aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-davinci/time.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-davinci/time.c')
-rw-r--r--arch/arm/mach-davinci/time.c372
1 files changed, 372 insertions, 0 deletions
diff --git a/arch/arm/mach-davinci/time.c b/arch/arm/mach-davinci/time.c
new file mode 100644
index 000000000000..4d8425de6922
--- /dev/null
+++ b/arch/arm/mach-davinci/time.c
@@ -0,0 +1,372 @@
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>
18
19#include <asm/io.h>
20#include <asm/hardware.h>
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>
26#include <asm/arch/io.h>
27
28static 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
34enum {
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
43static 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
97struct 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};
108static 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
115static 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
138static inline u32 timer32_read(struct timer_s *t)
139{
140 return davinci_readl(t->tim_reg);
141}
142
143static 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 */
152static irqreturn_t freerun_interrupt(int irq, void *dev_id)
153{
154 return IRQ_HANDLED;
155}
156
157static 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
177static 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 */
241static cycle_t read_cycles(void)
242{
243 struct timer_s *t = &timers[TID_CLOCKSOURCE];
244
245 return (cycles_t)timer32_read(t);
246}
247
248static 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 */
260static 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
270static 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;
288 }
289}
290
291static struct clock_event_device clockevent_davinci = {
292 .name = "timer0_0",
293 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
294 .shift = 32,
295 .set_next_event = davinci_set_next_event,
296 .set_mode = davinci_set_mode,
297};
298
299
300static void __init davinci_timer_init(void)
301{
302 static char err[] __initdata = KERN_ERR
303 "%s: can't register clocksource!\n";
304
305 /* init timer hw */
306 timer_init();
307
308 /* setup clocksource */
309 clocksource_davinci.mult =
310 clocksource_khz2mult(CLOCK_TICK_RATE/1000,
311 clocksource_davinci.shift);
312 if (clocksource_register(&clocksource_davinci))
313 printk(err, clocksource_davinci.name);
314
315 /* setup clockevent */
316 clockevent_davinci.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC,
317 clockevent_davinci.shift);
318 clockevent_davinci.max_delta_ns =
319 clockevent_delta2ns(0xfffffffe, &clockevent_davinci);
320 clockevent_davinci.min_delta_ns =
321 clockevent_delta2ns(1, &clockevent_davinci);
322
323 clockevent_davinci.cpumask = cpumask_of_cpu(0);
324 clockevents_register_device(&clockevent_davinci);
325}
326
327struct sys_timer davinci_timer = {
328 .init = davinci_timer_init,
329};
330
331
332/* reset board using watchdog timer */
333void davinci_watchdog_reset(void) {
334 u32 tgcr, wdtcr, base = DAVINCI_WDOG_BASE;
335
336 /* disable, internal clock source */
337 davinci_writel(0, base + TCR);
338
339 /* reset timer, set mode to 64-bit watchdog, and unreset */
340 tgcr = 0;
341 davinci_writel(tgcr, base + TCR);
342 tgcr = TGCR_TIMMODE_64BIT_WDOG << TGCR_TIMMODE_SHIFT;
343 tgcr |= (TGCR_UNRESET << TGCR_TIM12RS_SHIFT) |
344 (TGCR_UNRESET << TGCR_TIM34RS_SHIFT);
345 davinci_writel(tgcr, base + TCR);
346
347 /* clear counter and period regs */
348 davinci_writel(0, base + TIM12);
349 davinci_writel(0, base + TIM34);
350 davinci_writel(0, base + PRD12);
351 davinci_writel(0, base + PRD34);
352
353 /* enable */
354 wdtcr = davinci_readl(base + WDTCR);
355 wdtcr |= WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT;
356 davinci_writel(wdtcr, base + WDTCR);
357
358 /* put watchdog in pre-active state */
359 wdtcr = (WDTCR_WDKEY_SEQ0 << WDTCR_WDKEY_SHIFT) |
360 (WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT);
361 davinci_writel(wdtcr, base + WDTCR);
362
363 /* put watchdog in active state */
364 wdtcr = (WDTCR_WDKEY_SEQ1 << WDTCR_WDKEY_SHIFT) |
365 (WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT);
366 davinci_writel(wdtcr, base + WDTCR);
367
368 /* write an invalid value to the WDKEY field to trigger
369 * a watchdog reset */
370 wdtcr = 0x00004000;
371 davinci_writel(wdtcr, base + WDTCR);
372}