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