Commit | Line | Data |
---|---|---|
7c6337e2 KH |
1 | /* |
2 | * DaVinci timer subsystem | |
3 | * | |
4 | * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com> | |
5 | * | |
6 | * 2007 (c) MontaVista Software, Inc. This file is licensed under | |
7 | * the terms of the GNU General Public License version 2. This program | |
8 | * is licensed "as is" without any warranty of any kind, whether express | |
9 | * or implied. | |
10 | */ | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/types.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/clocksource.h> | |
16 | #include <linux/clockchips.h> | |
17 | #include <linux/spinlock.h> | |
fced80c7 | 18 | #include <linux/io.h> |
f5c122da KH |
19 | #include <linux/clk.h> |
20 | #include <linux/err.h> | |
21 | #include <linux/device.h> | |
fb631387 | 22 | #include <linux/platform_device.h> |
7c6337e2 | 23 | |
a09e64fb | 24 | #include <mach/hardware.h> |
7c6337e2 KH |
25 | #include <asm/system.h> |
26 | #include <asm/irq.h> | |
27 | #include <asm/mach/irq.h> | |
28 | #include <asm/mach/time.h> | |
29 | #include <asm/errno.h> | |
a09e64fb | 30 | #include <mach/io.h> |
f5c122da | 31 | #include <mach/cputype.h> |
f64691b3 | 32 | #include <mach/time.h> |
f5c122da | 33 | #include "clock.h" |
7c6337e2 KH |
34 | |
35 | static struct clock_event_device clockevent_davinci; | |
e6099002 | 36 | static unsigned int davinci_clock_tick_rate; |
7c6337e2 | 37 | |
7c6337e2 KH |
38 | /* |
39 | * This driver configures the 2 64-bit count-up timers as 4 independent | |
40 | * 32-bit count-up timers used as follows: | |
7c6337e2 | 41 | */ |
f64691b3 MG |
42 | |
43 | enum { | |
44 | TID_CLOCKEVENT, | |
45 | TID_CLOCKSOURCE, | |
46 | }; | |
7c6337e2 KH |
47 | |
48 | /* Timer register offsets */ | |
3abd5acf MG |
49 | #define PID12 0x0 |
50 | #define TIM12 0x10 | |
51 | #define TIM34 0x14 | |
52 | #define PRD12 0x18 | |
53 | #define PRD34 0x1c | |
54 | #define TCR 0x20 | |
55 | #define TGCR 0x24 | |
56 | #define WDTCR 0x28 | |
57 | ||
58 | /* Offsets of the 8 compare registers */ | |
59 | #define CMP12_0 0x60 | |
60 | #define CMP12_1 0x64 | |
61 | #define CMP12_2 0x68 | |
62 | #define CMP12_3 0x6c | |
63 | #define CMP12_4 0x70 | |
64 | #define CMP12_5 0x74 | |
65 | #define CMP12_6 0x78 | |
66 | #define CMP12_7 0x7c | |
7c6337e2 KH |
67 | |
68 | /* Timer register bitfields */ | |
69 | #define TCR_ENAMODE_DISABLE 0x0 | |
70 | #define TCR_ENAMODE_ONESHOT 0x1 | |
71 | #define TCR_ENAMODE_PERIODIC 0x2 | |
72 | #define TCR_ENAMODE_MASK 0x3 | |
73 | ||
74 | #define TGCR_TIMMODE_SHIFT 2 | |
75 | #define TGCR_TIMMODE_64BIT_GP 0x0 | |
76 | #define TGCR_TIMMODE_32BIT_UNCHAINED 0x1 | |
77 | #define TGCR_TIMMODE_64BIT_WDOG 0x2 | |
78 | #define TGCR_TIMMODE_32BIT_CHAINED 0x3 | |
79 | ||
80 | #define TGCR_TIM12RS_SHIFT 0 | |
81 | #define TGCR_TIM34RS_SHIFT 1 | |
82 | #define TGCR_RESET 0x0 | |
83 | #define TGCR_UNRESET 0x1 | |
84 | #define TGCR_RESET_MASK 0x3 | |
85 | ||
86 | #define WDTCR_WDEN_SHIFT 14 | |
87 | #define WDTCR_WDEN_DISABLE 0x0 | |
88 | #define WDTCR_WDEN_ENABLE 0x1 | |
89 | #define WDTCR_WDKEY_SHIFT 16 | |
90 | #define WDTCR_WDKEY_SEQ0 0xa5c6 | |
91 | #define WDTCR_WDKEY_SEQ1 0xda7e | |
92 | ||
93 | struct timer_s { | |
94 | char *name; | |
95 | unsigned int id; | |
96 | unsigned long period; | |
97 | unsigned long opts; | |
3abd5acf | 98 | unsigned long flags; |
f5c122da KH |
99 | void __iomem *base; |
100 | unsigned long tim_off; | |
101 | unsigned long prd_off; | |
7c6337e2 KH |
102 | unsigned long enamode_shift; |
103 | struct irqaction irqaction; | |
104 | }; | |
105 | static struct timer_s timers[]; | |
106 | ||
107 | /* values for 'opts' field of struct timer_s */ | |
3abd5acf MG |
108 | #define TIMER_OPTS_DISABLED 0x01 |
109 | #define TIMER_OPTS_ONESHOT 0x02 | |
110 | #define TIMER_OPTS_PERIODIC 0x04 | |
111 | #define TIMER_OPTS_STATE_MASK 0x07 | |
112 | ||
113 | #define TIMER_OPTS_USE_COMPARE 0x80000000 | |
114 | #define USING_COMPARE(t) ((t)->opts & TIMER_OPTS_USE_COMPARE) | |
7c6337e2 | 115 | |
f64691b3 MG |
116 | static char *id_to_name[] = { |
117 | [T0_BOT] = "timer0_0", | |
118 | [T0_TOP] = "timer0_1", | |
119 | [T1_BOT] = "timer1_0", | |
120 | [T1_TOP] = "timer1_1", | |
121 | }; | |
122 | ||
7c6337e2 KH |
123 | static int timer32_config(struct timer_s *t) |
124 | { | |
3abd5acf | 125 | u32 tcr; |
5570078c | 126 | struct davinci_soc_info *soc_info = &davinci_soc_info; |
3abd5acf MG |
127 | |
128 | if (USING_COMPARE(t)) { | |
129 | struct davinci_timer_instance *dtip = | |
130 | soc_info->timer_info->timers; | |
131 | int event_timer = ID_TO_TIMER(timers[TID_CLOCKEVENT].id); | |
132 | ||
133 | /* | |
134 | * Next interrupt should be the current time reg value plus | |
135 | * the new period (using 32-bit unsigned addition/wrapping | |
136 | * to 0 on overflow). This assumes that the clocksource | |
137 | * is setup to count to 2^32-1 before wrapping around to 0. | |
138 | */ | |
139 | __raw_writel(__raw_readl(t->base + t->tim_off) + t->period, | |
140 | t->base + dtip[event_timer].cmp_off); | |
141 | } else { | |
142 | tcr = __raw_readl(t->base + TCR); | |
143 | ||
144 | /* disable timer */ | |
145 | tcr &= ~(TCR_ENAMODE_MASK << t->enamode_shift); | |
146 | __raw_writel(tcr, t->base + TCR); | |
147 | ||
148 | /* reset counter to zero, set new period */ | |
149 | __raw_writel(0, t->base + t->tim_off); | |
150 | __raw_writel(t->period, t->base + t->prd_off); | |
151 | ||
152 | /* Set enable mode */ | |
153 | if (t->opts & TIMER_OPTS_ONESHOT) | |
154 | tcr |= TCR_ENAMODE_ONESHOT << t->enamode_shift; | |
155 | else if (t->opts & TIMER_OPTS_PERIODIC) | |
156 | tcr |= TCR_ENAMODE_PERIODIC << t->enamode_shift; | |
157 | ||
158 | __raw_writel(tcr, t->base + TCR); | |
7c6337e2 | 159 | } |
7c6337e2 KH |
160 | return 0; |
161 | } | |
162 | ||
163 | static inline u32 timer32_read(struct timer_s *t) | |
164 | { | |
f5c122da | 165 | return __raw_readl(t->base + t->tim_off); |
7c6337e2 KH |
166 | } |
167 | ||
168 | static irqreturn_t timer_interrupt(int irq, void *dev_id) | |
169 | { | |
170 | struct clock_event_device *evt = &clockevent_davinci; | |
171 | ||
172 | evt->event_handler(evt); | |
173 | return IRQ_HANDLED; | |
174 | } | |
175 | ||
176 | /* called when 32-bit counter wraps */ | |
177 | static irqreturn_t freerun_interrupt(int irq, void *dev_id) | |
178 | { | |
179 | return IRQ_HANDLED; | |
180 | } | |
181 | ||
182 | static struct timer_s timers[] = { | |
183 | [TID_CLOCKEVENT] = { | |
184 | .name = "clockevent", | |
185 | .opts = TIMER_OPTS_DISABLED, | |
186 | .irqaction = { | |
187 | .flags = IRQF_DISABLED | IRQF_TIMER, | |
188 | .handler = timer_interrupt, | |
189 | } | |
190 | }, | |
191 | [TID_CLOCKSOURCE] = { | |
192 | .name = "free-run counter", | |
193 | .period = ~0, | |
194 | .opts = TIMER_OPTS_PERIODIC, | |
195 | .irqaction = { | |
196 | .flags = IRQF_DISABLED | IRQF_TIMER, | |
197 | .handler = freerun_interrupt, | |
198 | } | |
199 | }, | |
200 | }; | |
201 | ||
202 | static void __init timer_init(void) | |
203 | { | |
f64691b3 MG |
204 | struct davinci_soc_info *soc_info = &davinci_soc_info; |
205 | struct davinci_timer_instance *dtip = soc_info->timer_info->timers; | |
7c6337e2 KH |
206 | int i; |
207 | ||
208 | /* Global init of each 64-bit timer as a whole */ | |
209 | for(i=0; i<2; i++) { | |
f5c122da | 210 | u32 tgcr; |
f64691b3 | 211 | void __iomem *base = dtip[i].base; |
7c6337e2 KH |
212 | |
213 | /* Disabled, Internal clock source */ | |
f5c122da | 214 | __raw_writel(0, base + TCR); |
7c6337e2 KH |
215 | |
216 | /* reset both timers, no pre-scaler for timer34 */ | |
217 | tgcr = 0; | |
f5c122da | 218 | __raw_writel(tgcr, base + TGCR); |
7c6337e2 KH |
219 | |
220 | /* Set both timers to unchained 32-bit */ | |
221 | tgcr = TGCR_TIMMODE_32BIT_UNCHAINED << TGCR_TIMMODE_SHIFT; | |
f5c122da | 222 | __raw_writel(tgcr, base + TGCR); |
7c6337e2 KH |
223 | |
224 | /* Unreset timers */ | |
225 | tgcr |= (TGCR_UNRESET << TGCR_TIM12RS_SHIFT) | | |
226 | (TGCR_UNRESET << TGCR_TIM34RS_SHIFT); | |
f5c122da | 227 | __raw_writel(tgcr, base + TGCR); |
7c6337e2 KH |
228 | |
229 | /* Init both counters to zero */ | |
f5c122da KH |
230 | __raw_writel(0, base + TIM12); |
231 | __raw_writel(0, base + TIM34); | |
7c6337e2 KH |
232 | } |
233 | ||
234 | /* Init of each timer as a 32-bit timer */ | |
235 | for (i=0; i< ARRAY_SIZE(timers); i++) { | |
236 | struct timer_s *t = &timers[i]; | |
f64691b3 MG |
237 | int timer = ID_TO_TIMER(t->id); |
238 | u32 irq; | |
239 | ||
240 | t->base = dtip[timer].base; | |
241 | ||
242 | if (IS_TIMER_BOT(t->id)) { | |
243 | t->enamode_shift = 6; | |
244 | t->tim_off = TIM12; | |
245 | t->prd_off = PRD12; | |
246 | irq = dtip[timer].bottom_irq; | |
247 | } else { | |
248 | t->enamode_shift = 22; | |
249 | t->tim_off = TIM34; | |
250 | t->prd_off = PRD34; | |
251 | irq = dtip[timer].top_irq; | |
7c6337e2 | 252 | } |
f64691b3 MG |
253 | |
254 | /* Register interrupt */ | |
255 | t->irqaction.name = t->name; | |
256 | t->irqaction.dev_id = (void *)t; | |
3abd5acf MG |
257 | |
258 | if (t->irqaction.handler != NULL) { | |
259 | irq = USING_COMPARE(t) ? dtip[i].cmp_irq : irq; | |
f64691b3 | 260 | setup_irq(irq, &t->irqaction); |
3abd5acf | 261 | } |
f64691b3 MG |
262 | |
263 | timer32_config(&timers[i]); | |
7c6337e2 KH |
264 | } |
265 | } | |
266 | ||
267 | /* | |
268 | * clocksource | |
269 | */ | |
8e19608e | 270 | static cycle_t read_cycles(struct clocksource *cs) |
7c6337e2 KH |
271 | { |
272 | struct timer_s *t = &timers[TID_CLOCKSOURCE]; | |
273 | ||
274 | return (cycles_t)timer32_read(t); | |
275 | } | |
276 | ||
277 | static struct clocksource clocksource_davinci = { | |
7c6337e2 KH |
278 | .rating = 300, |
279 | .read = read_cycles, | |
280 | .mask = CLOCKSOURCE_MASK(32), | |
281 | .shift = 24, | |
282 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
283 | }; | |
284 | ||
285 | /* | |
286 | * clockevent | |
287 | */ | |
288 | static int davinci_set_next_event(unsigned long cycles, | |
289 | struct clock_event_device *evt) | |
290 | { | |
291 | struct timer_s *t = &timers[TID_CLOCKEVENT]; | |
292 | ||
293 | t->period = cycles; | |
294 | timer32_config(t); | |
295 | return 0; | |
296 | } | |
297 | ||
298 | static void davinci_set_mode(enum clock_event_mode mode, | |
299 | struct clock_event_device *evt) | |
300 | { | |
301 | struct timer_s *t = &timers[TID_CLOCKEVENT]; | |
302 | ||
303 | switch (mode) { | |
304 | case CLOCK_EVT_MODE_PERIODIC: | |
e6099002 | 305 | t->period = davinci_clock_tick_rate / (HZ); |
3abd5acf MG |
306 | t->opts &= ~TIMER_OPTS_STATE_MASK; |
307 | t->opts |= TIMER_OPTS_PERIODIC; | |
7c6337e2 KH |
308 | timer32_config(t); |
309 | break; | |
310 | case CLOCK_EVT_MODE_ONESHOT: | |
3abd5acf MG |
311 | t->opts &= ~TIMER_OPTS_STATE_MASK; |
312 | t->opts |= TIMER_OPTS_ONESHOT; | |
7c6337e2 KH |
313 | break; |
314 | case CLOCK_EVT_MODE_UNUSED: | |
315 | case CLOCK_EVT_MODE_SHUTDOWN: | |
3abd5acf MG |
316 | t->opts &= ~TIMER_OPTS_STATE_MASK; |
317 | t->opts |= TIMER_OPTS_DISABLED; | |
7c6337e2 | 318 | break; |
18de5bc4 TG |
319 | case CLOCK_EVT_MODE_RESUME: |
320 | break; | |
7c6337e2 KH |
321 | } |
322 | } | |
323 | ||
324 | static struct clock_event_device clockevent_davinci = { | |
7c6337e2 KH |
325 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, |
326 | .shift = 32, | |
327 | .set_next_event = davinci_set_next_event, | |
328 | .set_mode = davinci_set_mode, | |
329 | }; | |
330 | ||
331 | ||
332 | static void __init davinci_timer_init(void) | |
333 | { | |
e6099002 | 334 | struct clk *timer_clk; |
f64691b3 | 335 | struct davinci_soc_info *soc_info = &davinci_soc_info; |
3abd5acf MG |
336 | unsigned int clockevent_id; |
337 | unsigned int clocksource_id; | |
7c6337e2 KH |
338 | static char err[] __initdata = KERN_ERR |
339 | "%s: can't register clocksource!\n"; | |
340 | ||
3abd5acf MG |
341 | clockevent_id = soc_info->timer_info->clockevent_id; |
342 | clocksource_id = soc_info->timer_info->clocksource_id; | |
343 | ||
344 | timers[TID_CLOCKEVENT].id = clockevent_id; | |
345 | timers[TID_CLOCKSOURCE].id = clocksource_id; | |
346 | ||
347 | /* | |
348 | * If using same timer for both clock events & clocksource, | |
349 | * a compare register must be used to generate an event interrupt. | |
350 | * This is equivalent to a oneshot timer only (not periodic). | |
351 | */ | |
352 | if (clockevent_id == clocksource_id) { | |
353 | struct davinci_timer_instance *dtip = | |
354 | soc_info->timer_info->timers; | |
355 | int event_timer = ID_TO_TIMER(clockevent_id); | |
356 | ||
357 | /* Only bottom timers can use compare regs */ | |
358 | if (IS_TIMER_TOP(clockevent_id)) | |
359 | pr_warning("davinci_timer_init: Invalid use" | |
360 | " of system timers. Results unpredictable.\n"); | |
361 | else if ((dtip[event_timer].cmp_off == 0) | |
362 | || (dtip[event_timer].cmp_irq == 0)) | |
363 | pr_warning("davinci_timer_init: Invalid timer instance" | |
364 | " setup. Results unpredictable.\n"); | |
365 | else { | |
366 | timers[TID_CLOCKEVENT].opts |= TIMER_OPTS_USE_COMPARE; | |
367 | clockevent_davinci.features = CLOCK_EVT_FEAT_ONESHOT; | |
368 | } | |
369 | } | |
f64691b3 | 370 | |
7c6337e2 KH |
371 | /* init timer hw */ |
372 | timer_init(); | |
373 | ||
e6099002 KH |
374 | timer_clk = clk_get(NULL, "timer0"); |
375 | BUG_ON(IS_ERR(timer_clk)); | |
376 | clk_enable(timer_clk); | |
377 | ||
378 | davinci_clock_tick_rate = clk_get_rate(timer_clk); | |
379 | ||
7c6337e2 | 380 | /* setup clocksource */ |
3abd5acf | 381 | clocksource_davinci.name = id_to_name[clocksource_id]; |
7c6337e2 | 382 | clocksource_davinci.mult = |
e6099002 | 383 | clocksource_khz2mult(davinci_clock_tick_rate/1000, |
7c6337e2 KH |
384 | clocksource_davinci.shift); |
385 | if (clocksource_register(&clocksource_davinci)) | |
386 | printk(err, clocksource_davinci.name); | |
387 | ||
388 | /* setup clockevent */ | |
f64691b3 | 389 | clockevent_davinci.name = id_to_name[timers[TID_CLOCKEVENT].id]; |
e6099002 | 390 | clockevent_davinci.mult = div_sc(davinci_clock_tick_rate, NSEC_PER_SEC, |
7c6337e2 KH |
391 | clockevent_davinci.shift); |
392 | clockevent_davinci.max_delta_ns = | |
393 | clockevent_delta2ns(0xfffffffe, &clockevent_davinci); | |
3abd5acf | 394 | clockevent_davinci.min_delta_ns = 50000; /* 50 usec */ |
7c6337e2 | 395 | |
320ab2b0 | 396 | clockevent_davinci.cpumask = cpumask_of(0); |
7c6337e2 KH |
397 | clockevents_register_device(&clockevent_davinci); |
398 | } | |
399 | ||
400 | struct sys_timer davinci_timer = { | |
401 | .init = davinci_timer_init, | |
402 | }; | |
403 | ||
404 | ||
405 | /* reset board using watchdog timer */ | |
fb631387 KH |
406 | void davinci_watchdog_reset(void) |
407 | { | |
f5c122da | 408 | u32 tgcr, wdtcr; |
951d6f6d MG |
409 | struct davinci_soc_info *soc_info = &davinci_soc_info; |
410 | void __iomem *base = soc_info->wdt_base; | |
e6099002 | 411 | struct clk *wd_clk; |
e6099002 | 412 | |
fb631387 | 413 | wd_clk = clk_get(&davinci_wdt_device.dev, NULL); |
e6099002 KH |
414 | if (WARN_ON(IS_ERR(wd_clk))) |
415 | return; | |
416 | clk_enable(wd_clk); | |
7c6337e2 KH |
417 | |
418 | /* disable, internal clock source */ | |
f5c122da | 419 | __raw_writel(0, base + TCR); |
7c6337e2 KH |
420 | |
421 | /* reset timer, set mode to 64-bit watchdog, and unreset */ | |
422 | tgcr = 0; | |
f5c122da | 423 | __raw_writel(tgcr, base + TCR); |
7c6337e2 KH |
424 | tgcr = TGCR_TIMMODE_64BIT_WDOG << TGCR_TIMMODE_SHIFT; |
425 | tgcr |= (TGCR_UNRESET << TGCR_TIM12RS_SHIFT) | | |
426 | (TGCR_UNRESET << TGCR_TIM34RS_SHIFT); | |
f5c122da | 427 | __raw_writel(tgcr, base + TCR); |
7c6337e2 KH |
428 | |
429 | /* clear counter and period regs */ | |
f5c122da KH |
430 | __raw_writel(0, base + TIM12); |
431 | __raw_writel(0, base + TIM34); | |
432 | __raw_writel(0, base + PRD12); | |
433 | __raw_writel(0, base + PRD34); | |
7c6337e2 KH |
434 | |
435 | /* enable */ | |
f5c122da | 436 | wdtcr = __raw_readl(base + WDTCR); |
7c6337e2 | 437 | wdtcr |= WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT; |
f5c122da | 438 | __raw_writel(wdtcr, base + WDTCR); |
7c6337e2 KH |
439 | |
440 | /* put watchdog in pre-active state */ | |
441 | wdtcr = (WDTCR_WDKEY_SEQ0 << WDTCR_WDKEY_SHIFT) | | |
442 | (WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT); | |
f5c122da | 443 | __raw_writel(wdtcr, base + WDTCR); |
7c6337e2 KH |
444 | |
445 | /* put watchdog in active state */ | |
446 | wdtcr = (WDTCR_WDKEY_SEQ1 << WDTCR_WDKEY_SHIFT) | | |
447 | (WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT); | |
f5c122da | 448 | __raw_writel(wdtcr, base + WDTCR); |
7c6337e2 KH |
449 | |
450 | /* write an invalid value to the WDKEY field to trigger | |
451 | * a watchdog reset */ | |
452 | wdtcr = 0x00004000; | |
f5c122da | 453 | __raw_writel(wdtcr, base + WDTCR); |
7c6337e2 | 454 | } |