2 * Moschip MCS814x timer routines
4 * Copyright (C) 2012, Florian Fainelli <florian@openwrt.org>
8 #include <linux/kernel.h>
9 #include <linux/interrupt.h>
10 #include <linux/time.h>
11 #include <linux/timex.h>
12 #include <linux/irq.h>
13 #include <linux/err.h>
14 #include <linux/clk.h>
17 #include <linux/of_address.h>
19 #include <asm/mach/time.h>
20 #include <mach/mcs814x.h>
22 /* Timer block registers */
23 #define TIMER_VAL 0x00
24 #define TIMER_CTL 0x04
26 static u32 last_reload
;
27 static u32 timer_correct
;
28 static u32 clock_rate
;
29 static u32 timer_reload_value
;
30 static void __iomem
*mcs814x_timer_base
;
32 static inline unsigned long ticks2usecs(u32 x
)
34 return x
/ (clock_rate
/ 1000000);
38 * Returns number of ms since last clock interrupt. Note that interrupts
39 * will have been disabled by do_gettimeoffset()
41 static unsigned long mcs814x_gettimeoffset(void)
43 u32 ticks
= __raw_readl(mcs814x_timer_base
+ TIMER_VAL
);
45 if (ticks
< last_reload
)
46 return ticks2usecs(ticks
+ (u32
)(0xffffffff - last_reload
));
48 return ticks2usecs(ticks
- last_reload
);
52 static irqreturn_t
mcs814x_timer_interrupt(int irq
, void *dev_id
)
54 u32 count
= __raw_readl(mcs814x_timer_base
+ TIMER_VAL
);
56 /* take into account delay up to this moment */
57 last_reload
= count
+ timer_correct
+ timer_reload_value
;
59 if (last_reload
< timer_reload_value
) {
60 last_reload
= timer_reload_value
;
62 if (timer_correct
== 0)
63 timer_correct
= __raw_readl(mcs814x_timer_base
+ TIMER_VAL
) - count
;
65 __raw_writel(last_reload
, mcs814x_timer_base
+ TIMER_VAL
);
72 static struct irqaction mcs814x_timer_irq
= {
73 .name
= "mcs814x-timer",
74 .flags
= IRQF_DISABLED
| IRQF_TIMER
| IRQF_IRQPOLL
,
75 .handler
= mcs814x_timer_interrupt
,
78 static struct of_device_id mcs814x_timer_ids
[] = {
79 { .compatible
= "moschip,mcs814x-timer" },
83 static void __init
mcs814x_of_timer_init(void)
85 struct device_node
*np
;
86 const unsigned int *intspec
;
88 np
= of_find_matching_node(NULL
, mcs814x_timer_ids
);
90 panic("unable to find compatible timer node in dtb");
92 mcs814x_timer_base
= of_iomap(np
, 0);
93 if (!mcs814x_timer_base
)
94 panic("unable to remap timer cpu registers");
96 intspec
= of_get_property(np
, "interrupts", NULL
);
98 panic("no interrupts property for timer");
100 mcs814x_timer_irq
.irq
= be32_to_cpup(intspec
);
103 static void __init
mcs814x_timer_init(void)
107 clk
= clk_get_sys("timer0", NULL
);
108 if (IS_ERR_OR_NULL(clk
))
109 panic("unable to get timer0 clock");
111 clock_rate
= clk_get_rate(clk
);
114 mcs814x_of_timer_init();
116 pr_info("Timer frequency: %d (kHz)\n", clock_rate
/ 1000);
118 timer_reload_value
= 0xffffffff - (clock_rate
/ HZ
);
121 __raw_writel(0, mcs814x_timer_base
+ TIMER_CTL
);
122 __raw_writel(timer_reload_value
, mcs814x_timer_base
+ TIMER_VAL
);
123 last_reload
= timer_reload_value
;
125 setup_irq(mcs814x_timer_irq
.irq
, &mcs814x_timer_irq
);
126 /* enable timer, stop timer in debug mode */
127 __raw_writel(0x03, mcs814x_timer_base
+ TIMER_CTL
);
130 struct sys_timer mcs814x_timer
= {
131 .init
= mcs814x_timer_init
,
132 .offset
= mcs814x_gettimeoffset
,