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 KH |
31 | #include <mach/cputype.h> |
32 | #include "clock.h" | |
7c6337e2 KH |
33 | |
34 | static struct clock_event_device clockevent_davinci; | |
e6099002 | 35 | static unsigned int davinci_clock_tick_rate; |
7c6337e2 KH |
36 | |
37 | #define DAVINCI_TIMER0_BASE (IO_PHYS + 0x21400) | |
38 | #define DAVINCI_TIMER1_BASE (IO_PHYS + 0x21800) | |
39 | #define DAVINCI_WDOG_BASE (IO_PHYS + 0x21C00) | |
40 | ||
41 | enum { | |
42 | T0_BOT = 0, T0_TOP, T1_BOT, T1_TOP, NUM_TIMERS, | |
43 | }; | |
44 | ||
45 | #define IS_TIMER1(id) (id & 0x2) | |
46 | #define IS_TIMER0(id) (!IS_TIMER1(id)) | |
47 | #define IS_TIMER_TOP(id) ((id & 0x1)) | |
48 | #define IS_TIMER_BOT(id) (!IS_TIMER_TOP(id)) | |
49 | ||
50 | static int timer_irqs[NUM_TIMERS] = { | |
51 | IRQ_TINT0_TINT12, | |
52 | IRQ_TINT0_TINT34, | |
53 | IRQ_TINT1_TINT12, | |
54 | IRQ_TINT1_TINT34, | |
55 | }; | |
56 | ||
57 | /* | |
58 | * This driver configures the 2 64-bit count-up timers as 4 independent | |
59 | * 32-bit count-up timers used as follows: | |
60 | * | |
61 | * T0_BOT: Timer 0, bottom: clockevent source for hrtimers | |
62 | * T0_TOP: Timer 0, top : clocksource for generic timekeeping | |
63 | * T1_BOT: Timer 1, bottom: (used by DSP in TI DSPLink code) | |
64 | * T1_TOP: Timer 1, top : <unused> | |
65 | */ | |
66 | #define TID_CLOCKEVENT T0_BOT | |
67 | #define TID_CLOCKSOURCE T0_TOP | |
68 | ||
69 | /* Timer register offsets */ | |
70 | #define PID12 0x0 | |
71 | #define TIM12 0x10 | |
72 | #define TIM34 0x14 | |
73 | #define PRD12 0x18 | |
74 | #define PRD34 0x1c | |
75 | #define TCR 0x20 | |
76 | #define TGCR 0x24 | |
77 | #define WDTCR 0x28 | |
78 | ||
79 | /* Timer register bitfields */ | |
80 | #define TCR_ENAMODE_DISABLE 0x0 | |
81 | #define TCR_ENAMODE_ONESHOT 0x1 | |
82 | #define TCR_ENAMODE_PERIODIC 0x2 | |
83 | #define TCR_ENAMODE_MASK 0x3 | |
84 | ||
85 | #define TGCR_TIMMODE_SHIFT 2 | |
86 | #define TGCR_TIMMODE_64BIT_GP 0x0 | |
87 | #define TGCR_TIMMODE_32BIT_UNCHAINED 0x1 | |
88 | #define TGCR_TIMMODE_64BIT_WDOG 0x2 | |
89 | #define TGCR_TIMMODE_32BIT_CHAINED 0x3 | |
90 | ||
91 | #define TGCR_TIM12RS_SHIFT 0 | |
92 | #define TGCR_TIM34RS_SHIFT 1 | |
93 | #define TGCR_RESET 0x0 | |
94 | #define TGCR_UNRESET 0x1 | |
95 | #define TGCR_RESET_MASK 0x3 | |
96 | ||
97 | #define WDTCR_WDEN_SHIFT 14 | |
98 | #define WDTCR_WDEN_DISABLE 0x0 | |
99 | #define WDTCR_WDEN_ENABLE 0x1 | |
100 | #define WDTCR_WDKEY_SHIFT 16 | |
101 | #define WDTCR_WDKEY_SEQ0 0xa5c6 | |
102 | #define WDTCR_WDKEY_SEQ1 0xda7e | |
103 | ||
104 | struct timer_s { | |
105 | char *name; | |
106 | unsigned int id; | |
107 | unsigned long period; | |
108 | unsigned long opts; | |
f5c122da KH |
109 | void __iomem *base; |
110 | unsigned long tim_off; | |
111 | unsigned long prd_off; | |
7c6337e2 KH |
112 | unsigned long enamode_shift; |
113 | struct irqaction irqaction; | |
114 | }; | |
115 | static struct timer_s timers[]; | |
116 | ||
117 | /* values for 'opts' field of struct timer_s */ | |
118 | #define TIMER_OPTS_DISABLED 0x00 | |
119 | #define TIMER_OPTS_ONESHOT 0x01 | |
120 | #define TIMER_OPTS_PERIODIC 0x02 | |
121 | ||
122 | static int timer32_config(struct timer_s *t) | |
123 | { | |
f5c122da | 124 | u32 tcr = __raw_readl(t->base + TCR); |
7c6337e2 KH |
125 | |
126 | /* disable timer */ | |
127 | tcr &= ~(TCR_ENAMODE_MASK << t->enamode_shift); | |
f5c122da | 128 | __raw_writel(tcr, t->base + TCR); |
7c6337e2 KH |
129 | |
130 | /* reset counter to zero, set new period */ | |
f5c122da KH |
131 | __raw_writel(0, t->base + t->tim_off); |
132 | __raw_writel(t->period, t->base + t->prd_off); | |
7c6337e2 KH |
133 | |
134 | /* Set enable mode */ | |
135 | if (t->opts & TIMER_OPTS_ONESHOT) { | |
136 | tcr |= TCR_ENAMODE_ONESHOT << t->enamode_shift; | |
137 | } else if (t->opts & TIMER_OPTS_PERIODIC) { | |
138 | tcr |= TCR_ENAMODE_PERIODIC << t->enamode_shift; | |
139 | } | |
140 | ||
f5c122da | 141 | __raw_writel(tcr, t->base + TCR); |
7c6337e2 KH |
142 | return 0; |
143 | } | |
144 | ||
145 | static inline u32 timer32_read(struct timer_s *t) | |
146 | { | |
f5c122da | 147 | return __raw_readl(t->base + t->tim_off); |
7c6337e2 KH |
148 | } |
149 | ||
150 | static irqreturn_t timer_interrupt(int irq, void *dev_id) | |
151 | { | |
152 | struct clock_event_device *evt = &clockevent_davinci; | |
153 | ||
154 | evt->event_handler(evt); | |
155 | return IRQ_HANDLED; | |
156 | } | |
157 | ||
158 | /* called when 32-bit counter wraps */ | |
159 | static irqreturn_t freerun_interrupt(int irq, void *dev_id) | |
160 | { | |
161 | return IRQ_HANDLED; | |
162 | } | |
163 | ||
164 | static struct timer_s timers[] = { | |
165 | [TID_CLOCKEVENT] = { | |
166 | .name = "clockevent", | |
167 | .opts = TIMER_OPTS_DISABLED, | |
168 | .irqaction = { | |
169 | .flags = IRQF_DISABLED | IRQF_TIMER, | |
170 | .handler = timer_interrupt, | |
171 | } | |
172 | }, | |
173 | [TID_CLOCKSOURCE] = { | |
174 | .name = "free-run counter", | |
175 | .period = ~0, | |
176 | .opts = TIMER_OPTS_PERIODIC, | |
177 | .irqaction = { | |
178 | .flags = IRQF_DISABLED | IRQF_TIMER, | |
179 | .handler = freerun_interrupt, | |
180 | } | |
181 | }, | |
182 | }; | |
183 | ||
184 | static void __init timer_init(void) | |
185 | { | |
f5c122da | 186 | u32 phys_bases[] = {DAVINCI_TIMER0_BASE, DAVINCI_TIMER1_BASE}; |
7c6337e2 KH |
187 | int i; |
188 | ||
189 | /* Global init of each 64-bit timer as a whole */ | |
190 | for(i=0; i<2; i++) { | |
f5c122da KH |
191 | u32 tgcr; |
192 | void __iomem *base = IO_ADDRESS(phys_bases[i]); | |
7c6337e2 KH |
193 | |
194 | /* Disabled, Internal clock source */ | |
f5c122da | 195 | __raw_writel(0, base + TCR); |
7c6337e2 KH |
196 | |
197 | /* reset both timers, no pre-scaler for timer34 */ | |
198 | tgcr = 0; | |
f5c122da | 199 | __raw_writel(tgcr, base + TGCR); |
7c6337e2 KH |
200 | |
201 | /* Set both timers to unchained 32-bit */ | |
202 | tgcr = TGCR_TIMMODE_32BIT_UNCHAINED << TGCR_TIMMODE_SHIFT; | |
f5c122da | 203 | __raw_writel(tgcr, base + TGCR); |
7c6337e2 KH |
204 | |
205 | /* Unreset timers */ | |
206 | tgcr |= (TGCR_UNRESET << TGCR_TIM12RS_SHIFT) | | |
207 | (TGCR_UNRESET << TGCR_TIM34RS_SHIFT); | |
f5c122da | 208 | __raw_writel(tgcr, base + TGCR); |
7c6337e2 KH |
209 | |
210 | /* Init both counters to zero */ | |
f5c122da KH |
211 | __raw_writel(0, base + TIM12); |
212 | __raw_writel(0, base + TIM34); | |
7c6337e2 KH |
213 | } |
214 | ||
215 | /* Init of each timer as a 32-bit timer */ | |
216 | for (i=0; i< ARRAY_SIZE(timers); i++) { | |
217 | struct timer_s *t = &timers[i]; | |
f5c122da | 218 | u32 phys_base; |
7c6337e2 KH |
219 | |
220 | if (t->name) { | |
221 | t->id = i; | |
f5c122da | 222 | phys_base = (IS_TIMER1(t->id) ? |
7c6337e2 | 223 | DAVINCI_TIMER1_BASE : DAVINCI_TIMER0_BASE); |
f5c122da | 224 | t->base = IO_ADDRESS(phys_base); |
7c6337e2 KH |
225 | |
226 | if (IS_TIMER_BOT(t->id)) { | |
227 | t->enamode_shift = 6; | |
f5c122da KH |
228 | t->tim_off = TIM12; |
229 | t->prd_off = PRD12; | |
7c6337e2 KH |
230 | } else { |
231 | t->enamode_shift = 22; | |
f5c122da KH |
232 | t->tim_off = TIM34; |
233 | t->prd_off = PRD34; | |
7c6337e2 KH |
234 | } |
235 | ||
236 | /* Register interrupt */ | |
237 | t->irqaction.name = t->name; | |
238 | t->irqaction.dev_id = (void *)t; | |
239 | if (t->irqaction.handler != NULL) { | |
240 | setup_irq(timer_irqs[t->id], &t->irqaction); | |
241 | } | |
242 | ||
243 | timer32_config(&timers[i]); | |
244 | } | |
245 | } | |
246 | } | |
247 | ||
248 | /* | |
249 | * clocksource | |
250 | */ | |
8e19608e | 251 | static cycle_t read_cycles(struct clocksource *cs) |
7c6337e2 KH |
252 | { |
253 | struct timer_s *t = &timers[TID_CLOCKSOURCE]; | |
254 | ||
255 | return (cycles_t)timer32_read(t); | |
256 | } | |
257 | ||
258 | static struct clocksource clocksource_davinci = { | |
259 | .name = "timer0_1", | |
260 | .rating = 300, | |
261 | .read = read_cycles, | |
262 | .mask = CLOCKSOURCE_MASK(32), | |
263 | .shift = 24, | |
264 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
265 | }; | |
266 | ||
267 | /* | |
268 | * clockevent | |
269 | */ | |
270 | static int davinci_set_next_event(unsigned long cycles, | |
271 | struct clock_event_device *evt) | |
272 | { | |
273 | struct timer_s *t = &timers[TID_CLOCKEVENT]; | |
274 | ||
275 | t->period = cycles; | |
276 | timer32_config(t); | |
277 | return 0; | |
278 | } | |
279 | ||
280 | static void davinci_set_mode(enum clock_event_mode mode, | |
281 | struct clock_event_device *evt) | |
282 | { | |
283 | struct timer_s *t = &timers[TID_CLOCKEVENT]; | |
284 | ||
285 | switch (mode) { | |
286 | case CLOCK_EVT_MODE_PERIODIC: | |
e6099002 | 287 | t->period = davinci_clock_tick_rate / (HZ); |
7c6337e2 KH |
288 | t->opts = TIMER_OPTS_PERIODIC; |
289 | timer32_config(t); | |
290 | break; | |
291 | case CLOCK_EVT_MODE_ONESHOT: | |
292 | t->opts = TIMER_OPTS_ONESHOT; | |
293 | break; | |
294 | case CLOCK_EVT_MODE_UNUSED: | |
295 | case CLOCK_EVT_MODE_SHUTDOWN: | |
296 | t->opts = TIMER_OPTS_DISABLED; | |
297 | break; | |
18de5bc4 TG |
298 | case CLOCK_EVT_MODE_RESUME: |
299 | break; | |
7c6337e2 KH |
300 | } |
301 | } | |
302 | ||
303 | static struct clock_event_device clockevent_davinci = { | |
304 | .name = "timer0_0", | |
305 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | |
306 | .shift = 32, | |
307 | .set_next_event = davinci_set_next_event, | |
308 | .set_mode = davinci_set_mode, | |
309 | }; | |
310 | ||
311 | ||
312 | static void __init davinci_timer_init(void) | |
313 | { | |
e6099002 KH |
314 | struct clk *timer_clk; |
315 | ||
7c6337e2 KH |
316 | static char err[] __initdata = KERN_ERR |
317 | "%s: can't register clocksource!\n"; | |
318 | ||
319 | /* init timer hw */ | |
320 | timer_init(); | |
321 | ||
e6099002 KH |
322 | timer_clk = clk_get(NULL, "timer0"); |
323 | BUG_ON(IS_ERR(timer_clk)); | |
324 | clk_enable(timer_clk); | |
325 | ||
326 | davinci_clock_tick_rate = clk_get_rate(timer_clk); | |
327 | ||
7c6337e2 KH |
328 | /* setup clocksource */ |
329 | clocksource_davinci.mult = | |
e6099002 | 330 | clocksource_khz2mult(davinci_clock_tick_rate/1000, |
7c6337e2 KH |
331 | clocksource_davinci.shift); |
332 | if (clocksource_register(&clocksource_davinci)) | |
333 | printk(err, clocksource_davinci.name); | |
334 | ||
335 | /* setup clockevent */ | |
e6099002 | 336 | clockevent_davinci.mult = div_sc(davinci_clock_tick_rate, NSEC_PER_SEC, |
7c6337e2 KH |
337 | clockevent_davinci.shift); |
338 | clockevent_davinci.max_delta_ns = | |
339 | clockevent_delta2ns(0xfffffffe, &clockevent_davinci); | |
340 | clockevent_davinci.min_delta_ns = | |
341 | clockevent_delta2ns(1, &clockevent_davinci); | |
342 | ||
320ab2b0 | 343 | clockevent_davinci.cpumask = cpumask_of(0); |
7c6337e2 KH |
344 | clockevents_register_device(&clockevent_davinci); |
345 | } | |
346 | ||
347 | struct sys_timer davinci_timer = { | |
348 | .init = davinci_timer_init, | |
349 | }; | |
350 | ||
351 | ||
352 | /* reset board using watchdog timer */ | |
fb631387 KH |
353 | void davinci_watchdog_reset(void) |
354 | { | |
f5c122da KH |
355 | u32 tgcr, wdtcr; |
356 | void __iomem *base = IO_ADDRESS(DAVINCI_WDOG_BASE); | |
e6099002 | 357 | struct clk *wd_clk; |
e6099002 | 358 | |
fb631387 | 359 | wd_clk = clk_get(&davinci_wdt_device.dev, NULL); |
e6099002 KH |
360 | if (WARN_ON(IS_ERR(wd_clk))) |
361 | return; | |
362 | clk_enable(wd_clk); | |
7c6337e2 KH |
363 | |
364 | /* disable, internal clock source */ | |
f5c122da | 365 | __raw_writel(0, base + TCR); |
7c6337e2 KH |
366 | |
367 | /* reset timer, set mode to 64-bit watchdog, and unreset */ | |
368 | tgcr = 0; | |
f5c122da | 369 | __raw_writel(tgcr, base + TCR); |
7c6337e2 KH |
370 | tgcr = TGCR_TIMMODE_64BIT_WDOG << TGCR_TIMMODE_SHIFT; |
371 | tgcr |= (TGCR_UNRESET << TGCR_TIM12RS_SHIFT) | | |
372 | (TGCR_UNRESET << TGCR_TIM34RS_SHIFT); | |
f5c122da | 373 | __raw_writel(tgcr, base + TCR); |
7c6337e2 KH |
374 | |
375 | /* clear counter and period regs */ | |
f5c122da KH |
376 | __raw_writel(0, base + TIM12); |
377 | __raw_writel(0, base + TIM34); | |
378 | __raw_writel(0, base + PRD12); | |
379 | __raw_writel(0, base + PRD34); | |
7c6337e2 KH |
380 | |
381 | /* enable */ | |
f5c122da | 382 | wdtcr = __raw_readl(base + WDTCR); |
7c6337e2 | 383 | wdtcr |= WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT; |
f5c122da | 384 | __raw_writel(wdtcr, base + WDTCR); |
7c6337e2 KH |
385 | |
386 | /* put watchdog in pre-active state */ | |
387 | wdtcr = (WDTCR_WDKEY_SEQ0 << WDTCR_WDKEY_SHIFT) | | |
388 | (WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT); | |
f5c122da | 389 | __raw_writel(wdtcr, base + WDTCR); |
7c6337e2 KH |
390 | |
391 | /* put watchdog in active state */ | |
392 | wdtcr = (WDTCR_WDKEY_SEQ1 << WDTCR_WDKEY_SHIFT) | | |
393 | (WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT); | |
f5c122da | 394 | __raw_writel(wdtcr, base + WDTCR); |
7c6337e2 KH |
395 | |
396 | /* write an invalid value to the WDKEY field to trigger | |
397 | * a watchdog reset */ | |
398 | wdtcr = 0x00004000; | |
f5c122da | 399 | __raw_writel(wdtcr, base + WDTCR); |
7c6337e2 | 400 | } |