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