blob: efbbc2ac63b4f82e1cf7e40fc17a530b4629c98c [file] [log] [blame]
Kevin Hilman7c6337e2007-04-30 19:37:19 +01001/*
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>
Russell Kingfced80c2008-09-06 12:10:45 +010018#include <linux/io.h>
Kevin Hilmanf5c122d2009-04-14 07:04:16 -050019#include <linux/clk.h>
20#include <linux/err.h>
21#include <linux/device.h>
Kevin Hilmanfb631382009-04-29 16:23:59 -070022#include <linux/platform_device.h>
Kevin Hilman7c6337e2007-04-30 19:37:19 +010023
Russell Kinga09e64f2008-08-05 16:14:15 +010024#include <mach/hardware.h>
Kevin Hilman7c6337e2007-04-30 19:37:19 +010025#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>
Russell Kinga09e64f2008-08-05 16:14:15 +010030#include <mach/io.h>
Kevin Hilmanf5c122d2009-04-14 07:04:16 -050031#include <mach/cputype.h>
32#include "clock.h"
Kevin Hilman7c6337e2007-04-30 19:37:19 +010033
34static struct clock_event_device clockevent_davinci;
Kevin Hilmane6099002009-04-14 07:06:37 -050035static unsigned int davinci_clock_tick_rate;
Kevin Hilman7c6337e2007-04-30 19:37:19 +010036
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
41enum {
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
50static 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
104struct timer_s {
105 char *name;
106 unsigned int id;
107 unsigned long period;
108 unsigned long opts;
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500109 void __iomem *base;
110 unsigned long tim_off;
111 unsigned long prd_off;
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100112 unsigned long enamode_shift;
113 struct irqaction irqaction;
114};
115static 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
122static int timer32_config(struct timer_s *t)
123{
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500124 u32 tcr = __raw_readl(t->base + TCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100125
126 /* disable timer */
127 tcr &= ~(TCR_ENAMODE_MASK << t->enamode_shift);
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500128 __raw_writel(tcr, t->base + TCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100129
130 /* reset counter to zero, set new period */
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500131 __raw_writel(0, t->base + t->tim_off);
132 __raw_writel(t->period, t->base + t->prd_off);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100133
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
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500141 __raw_writel(tcr, t->base + TCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100142 return 0;
143}
144
145static inline u32 timer32_read(struct timer_s *t)
146{
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500147 return __raw_readl(t->base + t->tim_off);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100148}
149
150static 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 */
159static irqreturn_t freerun_interrupt(int irq, void *dev_id)
160{
161 return IRQ_HANDLED;
162}
163
164static 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
184static void __init timer_init(void)
185{
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500186 u32 phys_bases[] = {DAVINCI_TIMER0_BASE, DAVINCI_TIMER1_BASE};
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100187 int i;
188
189 /* Global init of each 64-bit timer as a whole */
190 for(i=0; i<2; i++) {
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500191 u32 tgcr;
192 void __iomem *base = IO_ADDRESS(phys_bases[i]);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100193
194 /* Disabled, Internal clock source */
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500195 __raw_writel(0, base + TCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100196
197 /* reset both timers, no pre-scaler for timer34 */
198 tgcr = 0;
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500199 __raw_writel(tgcr, base + TGCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100200
201 /* Set both timers to unchained 32-bit */
202 tgcr = TGCR_TIMMODE_32BIT_UNCHAINED << TGCR_TIMMODE_SHIFT;
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500203 __raw_writel(tgcr, base + TGCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100204
205 /* Unreset timers */
206 tgcr |= (TGCR_UNRESET << TGCR_TIM12RS_SHIFT) |
207 (TGCR_UNRESET << TGCR_TIM34RS_SHIFT);
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500208 __raw_writel(tgcr, base + TGCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100209
210 /* Init both counters to zero */
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500211 __raw_writel(0, base + TIM12);
212 __raw_writel(0, base + TIM34);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100213 }
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];
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500218 u32 phys_base;
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100219
220 if (t->name) {
221 t->id = i;
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500222 phys_base = (IS_TIMER1(t->id) ?
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100223 DAVINCI_TIMER1_BASE : DAVINCI_TIMER0_BASE);
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500224 t->base = IO_ADDRESS(phys_base);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100225
226 if (IS_TIMER_BOT(t->id)) {
227 t->enamode_shift = 6;
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500228 t->tim_off = TIM12;
229 t->prd_off = PRD12;
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100230 } else {
231 t->enamode_shift = 22;
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500232 t->tim_off = TIM34;
233 t->prd_off = PRD34;
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100234 }
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 */
Magnus Damm8e196082009-04-21 12:24:00 -0700251static cycle_t read_cycles(struct clocksource *cs)
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100252{
253 struct timer_s *t = &timers[TID_CLOCKSOURCE];
254
255 return (cycles_t)timer32_read(t);
256}
257
258static 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 */
270static 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
280static 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:
Kevin Hilmane6099002009-04-14 07:06:37 -0500287 t->period = davinci_clock_tick_rate / (HZ);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100288 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;
Thomas Gleixner18de5bc2007-07-21 04:37:34 -0700298 case CLOCK_EVT_MODE_RESUME:
299 break;
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100300 }
301}
302
303static 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
312static void __init davinci_timer_init(void)
313{
Kevin Hilmane6099002009-04-14 07:06:37 -0500314 struct clk *timer_clk;
315
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100316 static char err[] __initdata = KERN_ERR
317 "%s: can't register clocksource!\n";
318
319 /* init timer hw */
320 timer_init();
321
Kevin Hilmane6099002009-04-14 07:06:37 -0500322 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
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100328 /* setup clocksource */
329 clocksource_davinci.mult =
Kevin Hilmane6099002009-04-14 07:06:37 -0500330 clocksource_khz2mult(davinci_clock_tick_rate/1000,
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100331 clocksource_davinci.shift);
332 if (clocksource_register(&clocksource_davinci))
333 printk(err, clocksource_davinci.name);
334
335 /* setup clockevent */
Kevin Hilmane6099002009-04-14 07:06:37 -0500336 clockevent_davinci.mult = div_sc(davinci_clock_tick_rate, NSEC_PER_SEC,
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100337 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
Rusty Russell320ab2b2008-12-13 21:20:26 +1030343 clockevent_davinci.cpumask = cpumask_of(0);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100344 clockevents_register_device(&clockevent_davinci);
345}
346
347struct sys_timer davinci_timer = {
348 .init = davinci_timer_init,
349};
350
351
352/* reset board using watchdog timer */
Kevin Hilmanfb631382009-04-29 16:23:59 -0700353void davinci_watchdog_reset(void)
354{
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500355 u32 tgcr, wdtcr;
356 void __iomem *base = IO_ADDRESS(DAVINCI_WDOG_BASE);
Kevin Hilmane6099002009-04-14 07:06:37 -0500357 struct clk *wd_clk;
Kevin Hilmane6099002009-04-14 07:06:37 -0500358
Kevin Hilmanfb631382009-04-29 16:23:59 -0700359 wd_clk = clk_get(&davinci_wdt_device.dev, NULL);
Kevin Hilmane6099002009-04-14 07:06:37 -0500360 if (WARN_ON(IS_ERR(wd_clk)))
361 return;
362 clk_enable(wd_clk);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100363
364 /* disable, internal clock source */
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500365 __raw_writel(0, base + TCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100366
367 /* reset timer, set mode to 64-bit watchdog, and unreset */
368 tgcr = 0;
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500369 __raw_writel(tgcr, base + TCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100370 tgcr = TGCR_TIMMODE_64BIT_WDOG << TGCR_TIMMODE_SHIFT;
371 tgcr |= (TGCR_UNRESET << TGCR_TIM12RS_SHIFT) |
372 (TGCR_UNRESET << TGCR_TIM34RS_SHIFT);
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500373 __raw_writel(tgcr, base + TCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100374
375 /* clear counter and period regs */
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500376 __raw_writel(0, base + TIM12);
377 __raw_writel(0, base + TIM34);
378 __raw_writel(0, base + PRD12);
379 __raw_writel(0, base + PRD34);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100380
381 /* enable */
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500382 wdtcr = __raw_readl(base + WDTCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100383 wdtcr |= WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT;
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500384 __raw_writel(wdtcr, base + WDTCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100385
386 /* put watchdog in pre-active state */
387 wdtcr = (WDTCR_WDKEY_SEQ0 << WDTCR_WDKEY_SHIFT) |
388 (WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT);
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500389 __raw_writel(wdtcr, base + WDTCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100390
391 /* put watchdog in active state */
392 wdtcr = (WDTCR_WDKEY_SEQ1 << WDTCR_WDKEY_SHIFT) |
393 (WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT);
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500394 __raw_writel(wdtcr, base + WDTCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100395
396 /* write an invalid value to the WDKEY field to trigger
397 * a watchdog reset */
398 wdtcr = 0x00004000;
Kevin Hilmanf5c122d2009-04-14 07:04:16 -0500399 __raw_writel(wdtcr, base + WDTCR);
Kevin Hilman7c6337e2007-04-30 19:37:19 +0100400}