diff options
Diffstat (limited to 'arch/arm/mach-davinci/time.c')
-rw-r--r-- | arch/arm/mach-davinci/time.c | 141 |
1 files changed, 103 insertions, 38 deletions
diff --git a/arch/arm/mach-davinci/time.c b/arch/arm/mach-davinci/time.c index f80ae25a52e4..fc90d3ee0eba 100644 --- a/arch/arm/mach-davinci/time.c +++ b/arch/arm/mach-davinci/time.c | |||
@@ -46,14 +46,24 @@ enum { | |||
46 | }; | 46 | }; |
47 | 47 | ||
48 | /* Timer register offsets */ | 48 | /* Timer register offsets */ |
49 | #define PID12 0x0 | 49 | #define PID12 0x0 |
50 | #define TIM12 0x10 | 50 | #define TIM12 0x10 |
51 | #define TIM34 0x14 | 51 | #define TIM34 0x14 |
52 | #define PRD12 0x18 | 52 | #define PRD12 0x18 |
53 | #define PRD34 0x1c | 53 | #define PRD34 0x1c |
54 | #define TCR 0x20 | 54 | #define TCR 0x20 |
55 | #define TGCR 0x24 | 55 | #define TGCR 0x24 |
56 | #define WDTCR 0x28 | 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 | ||
57 | 67 | ||
58 | /* Timer register bitfields */ | 68 | /* Timer register bitfields */ |
59 | #define TCR_ENAMODE_DISABLE 0x0 | 69 | #define TCR_ENAMODE_DISABLE 0x0 |
@@ -85,6 +95,7 @@ struct timer_s { | |||
85 | unsigned int id; | 95 | unsigned int id; |
86 | unsigned long period; | 96 | unsigned long period; |
87 | unsigned long opts; | 97 | unsigned long opts; |
98 | unsigned long flags; | ||
88 | void __iomem *base; | 99 | void __iomem *base; |
89 | unsigned long tim_off; | 100 | unsigned long tim_off; |
90 | unsigned long prd_off; | 101 | unsigned long prd_off; |
@@ -94,9 +105,13 @@ struct timer_s { | |||
94 | static struct timer_s timers[]; | 105 | static struct timer_s timers[]; |
95 | 106 | ||
96 | /* values for 'opts' field of struct timer_s */ | 107 | /* values for 'opts' field of struct timer_s */ |
97 | #define TIMER_OPTS_DISABLED 0x00 | 108 | #define TIMER_OPTS_DISABLED 0x01 |
98 | #define TIMER_OPTS_ONESHOT 0x01 | 109 | #define TIMER_OPTS_ONESHOT 0x02 |
99 | #define TIMER_OPTS_PERIODIC 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) | ||
100 | 115 | ||
101 | static char *id_to_name[] = { | 116 | static char *id_to_name[] = { |
102 | [T0_BOT] = "timer0_0", | 117 | [T0_BOT] = "timer0_0", |
@@ -107,24 +122,41 @@ static char *id_to_name[] = { | |||
107 | 122 | ||
108 | static int timer32_config(struct timer_s *t) | 123 | static int timer32_config(struct timer_s *t) |
109 | { | 124 | { |
110 | u32 tcr = __raw_readl(t->base + TCR); | 125 | u32 tcr; |
111 | 126 | struct davinci_soc_info *soc_info = davinci_get_soc_info(); | |
112 | /* disable timer */ | 127 | |
113 | tcr &= ~(TCR_ENAMODE_MASK << t->enamode_shift); | 128 | if (USING_COMPARE(t)) { |
114 | __raw_writel(tcr, t->base + TCR); | 129 | struct davinci_timer_instance *dtip = |
115 | 130 | soc_info->timer_info->timers; | |
116 | /* reset counter to zero, set new period */ | 131 | int event_timer = ID_TO_TIMER(timers[TID_CLOCKEVENT].id); |
117 | __raw_writel(0, t->base + t->tim_off); | 132 | |
118 | __raw_writel(t->period, t->base + t->prd_off); | 133 | /* |
119 | 134 | * Next interrupt should be the current time reg value plus | |
120 | /* Set enable mode */ | 135 | * the new period (using 32-bit unsigned addition/wrapping |
121 | if (t->opts & TIMER_OPTS_ONESHOT) { | 136 | * to 0 on overflow). This assumes that the clocksource |
122 | tcr |= TCR_ENAMODE_ONESHOT << t->enamode_shift; | 137 | * is setup to count to 2^32-1 before wrapping around to 0. |
123 | } else if (t->opts & TIMER_OPTS_PERIODIC) { | 138 | */ |
124 | tcr |= TCR_ENAMODE_PERIODIC << t->enamode_shift; | 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); | ||
125 | } | 159 | } |
126 | |||
127 | __raw_writel(tcr, t->base + TCR); | ||
128 | return 0; | 160 | return 0; |
129 | } | 161 | } |
130 | 162 | ||
@@ -222,8 +254,11 @@ static void __init timer_init(void) | |||
222 | /* Register interrupt */ | 254 | /* Register interrupt */ |
223 | t->irqaction.name = t->name; | 255 | t->irqaction.name = t->name; |
224 | t->irqaction.dev_id = (void *)t; | 256 | t->irqaction.dev_id = (void *)t; |
225 | if (t->irqaction.handler != NULL) | 257 | |
258 | if (t->irqaction.handler != NULL) { | ||
259 | irq = USING_COMPARE(t) ? dtip[i].cmp_irq : irq; | ||
226 | setup_irq(irq, &t->irqaction); | 260 | setup_irq(irq, &t->irqaction); |
261 | } | ||
227 | 262 | ||
228 | timer32_config(&timers[i]); | 263 | timer32_config(&timers[i]); |
229 | } | 264 | } |
@@ -268,15 +303,18 @@ static void davinci_set_mode(enum clock_event_mode mode, | |||
268 | switch (mode) { | 303 | switch (mode) { |
269 | case CLOCK_EVT_MODE_PERIODIC: | 304 | case CLOCK_EVT_MODE_PERIODIC: |
270 | t->period = davinci_clock_tick_rate / (HZ); | 305 | t->period = davinci_clock_tick_rate / (HZ); |
271 | t->opts = TIMER_OPTS_PERIODIC; | 306 | t->opts &= ~TIMER_OPTS_STATE_MASK; |
307 | t->opts |= TIMER_OPTS_PERIODIC; | ||
272 | timer32_config(t); | 308 | timer32_config(t); |
273 | break; | 309 | break; |
274 | case CLOCK_EVT_MODE_ONESHOT: | 310 | case CLOCK_EVT_MODE_ONESHOT: |
275 | t->opts = TIMER_OPTS_ONESHOT; | 311 | t->opts &= ~TIMER_OPTS_STATE_MASK; |
312 | t->opts |= TIMER_OPTS_ONESHOT; | ||
276 | break; | 313 | break; |
277 | case CLOCK_EVT_MODE_UNUSED: | 314 | case CLOCK_EVT_MODE_UNUSED: |
278 | case CLOCK_EVT_MODE_SHUTDOWN: | 315 | case CLOCK_EVT_MODE_SHUTDOWN: |
279 | t->opts = TIMER_OPTS_DISABLED; | 316 | t->opts &= ~TIMER_OPTS_STATE_MASK; |
317 | t->opts |= TIMER_OPTS_DISABLED; | ||
280 | break; | 318 | break; |
281 | case CLOCK_EVT_MODE_RESUME: | 319 | case CLOCK_EVT_MODE_RESUME: |
282 | break; | 320 | break; |
@@ -295,12 +333,40 @@ static void __init davinci_timer_init(void) | |||
295 | { | 333 | { |
296 | struct clk *timer_clk; | 334 | struct clk *timer_clk; |
297 | struct davinci_soc_info *soc_info = &davinci_soc_info; | 335 | struct davinci_soc_info *soc_info = &davinci_soc_info; |
298 | 336 | unsigned int clockevent_id; | |
337 | unsigned int clocksource_id; | ||
299 | static char err[] __initdata = KERN_ERR | 338 | static char err[] __initdata = KERN_ERR |
300 | "%s: can't register clocksource!\n"; | 339 | "%s: can't register clocksource!\n"; |
301 | 340 | ||
302 | timers[TID_CLOCKEVENT].id = soc_info->timer_info->clockevent_id; | 341 | clockevent_id = soc_info->timer_info->clockevent_id; |
303 | timers[TID_CLOCKSOURCE].id = soc_info->timer_info->clocksource_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 | } | ||
304 | 370 | ||
305 | /* init timer hw */ | 371 | /* init timer hw */ |
306 | timer_init(); | 372 | timer_init(); |
@@ -312,7 +378,7 @@ static void __init davinci_timer_init(void) | |||
312 | davinci_clock_tick_rate = clk_get_rate(timer_clk); | 378 | davinci_clock_tick_rate = clk_get_rate(timer_clk); |
313 | 379 | ||
314 | /* setup clocksource */ | 380 | /* setup clocksource */ |
315 | clocksource_davinci.name = id_to_name[timers[TID_CLOCKSOURCE].id]; | 381 | clocksource_davinci.name = id_to_name[clocksource_id]; |
316 | clocksource_davinci.mult = | 382 | clocksource_davinci.mult = |
317 | clocksource_khz2mult(davinci_clock_tick_rate/1000, | 383 | clocksource_khz2mult(davinci_clock_tick_rate/1000, |
318 | clocksource_davinci.shift); | 384 | clocksource_davinci.shift); |
@@ -325,8 +391,7 @@ static void __init davinci_timer_init(void) | |||
325 | clockevent_davinci.shift); | 391 | clockevent_davinci.shift); |
326 | clockevent_davinci.max_delta_ns = | 392 | clockevent_davinci.max_delta_ns = |
327 | clockevent_delta2ns(0xfffffffe, &clockevent_davinci); | 393 | clockevent_delta2ns(0xfffffffe, &clockevent_davinci); |
328 | clockevent_davinci.min_delta_ns = | 394 | clockevent_davinci.min_delta_ns = 50000; /* 50 usec */ |
329 | clockevent_delta2ns(1, &clockevent_davinci); | ||
330 | 395 | ||
331 | clockevent_davinci.cpumask = cpumask_of(0); | 396 | clockevent_davinci.cpumask = cpumask_of(0); |
332 | clockevents_register_device(&clockevent_davinci); | 397 | clockevents_register_device(&clockevent_davinci); |