|  | /* | 
|  | * DaVinci timer subsystem | 
|  | * | 
|  | * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com> | 
|  | * | 
|  | * 2007 (c) MontaVista Software, Inc. This file is licensed under | 
|  | * the terms of the GNU General Public License version 2. This program | 
|  | * is licensed "as is" without any warranty of any kind, whether express | 
|  | * or implied. | 
|  | */ | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/types.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/clocksource.h> | 
|  | #include <linux/clockchips.h> | 
|  | #include <linux/spinlock.h> | 
|  |  | 
|  | #include <asm/io.h> | 
|  | #include <asm/hardware.h> | 
|  | #include <asm/system.h> | 
|  | #include <asm/irq.h> | 
|  | #include <asm/mach/irq.h> | 
|  | #include <asm/mach/time.h> | 
|  | #include <asm/errno.h> | 
|  | #include <asm/arch/io.h> | 
|  |  | 
|  | static struct clock_event_device clockevent_davinci; | 
|  |  | 
|  | #define DAVINCI_TIMER0_BASE (IO_PHYS + 0x21400) | 
|  | #define DAVINCI_TIMER1_BASE (IO_PHYS + 0x21800) | 
|  | #define DAVINCI_WDOG_BASE   (IO_PHYS + 0x21C00) | 
|  |  | 
|  | enum { | 
|  | T0_BOT = 0, T0_TOP, T1_BOT, T1_TOP, NUM_TIMERS, | 
|  | }; | 
|  |  | 
|  | #define IS_TIMER1(id)    (id & 0x2) | 
|  | #define IS_TIMER0(id)    (!IS_TIMER1(id)) | 
|  | #define IS_TIMER_TOP(id) ((id & 0x1)) | 
|  | #define IS_TIMER_BOT(id) (!IS_TIMER_TOP(id)) | 
|  |  | 
|  | static int timer_irqs[NUM_TIMERS] = { | 
|  | IRQ_TINT0_TINT12, | 
|  | IRQ_TINT0_TINT34, | 
|  | IRQ_TINT1_TINT12, | 
|  | IRQ_TINT1_TINT34, | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * This driver configures the 2 64-bit count-up timers as 4 independent | 
|  | * 32-bit count-up timers used as follows: | 
|  | * | 
|  | * T0_BOT: Timer 0, bottom:  clockevent source for hrtimers | 
|  | * T0_TOP: Timer 0, top   :  clocksource for generic timekeeping | 
|  | * T1_BOT: Timer 1, bottom:  (used by DSP in TI DSPLink code) | 
|  | * T1_TOP: Timer 1, top   :  <unused> | 
|  | */ | 
|  | #define TID_CLOCKEVENT  T0_BOT | 
|  | #define TID_CLOCKSOURCE T0_TOP | 
|  |  | 
|  | /* Timer register offsets */ | 
|  | #define PID12                        0x0 | 
|  | #define TIM12                        0x10 | 
|  | #define TIM34                        0x14 | 
|  | #define PRD12                        0x18 | 
|  | #define PRD34                        0x1c | 
|  | #define TCR                          0x20 | 
|  | #define TGCR                         0x24 | 
|  | #define WDTCR                        0x28 | 
|  |  | 
|  | /* Timer register bitfields */ | 
|  | #define TCR_ENAMODE_DISABLE          0x0 | 
|  | #define TCR_ENAMODE_ONESHOT          0x1 | 
|  | #define TCR_ENAMODE_PERIODIC         0x2 | 
|  | #define TCR_ENAMODE_MASK             0x3 | 
|  |  | 
|  | #define TGCR_TIMMODE_SHIFT           2 | 
|  | #define TGCR_TIMMODE_64BIT_GP        0x0 | 
|  | #define TGCR_TIMMODE_32BIT_UNCHAINED 0x1 | 
|  | #define TGCR_TIMMODE_64BIT_WDOG      0x2 | 
|  | #define TGCR_TIMMODE_32BIT_CHAINED   0x3 | 
|  |  | 
|  | #define TGCR_TIM12RS_SHIFT           0 | 
|  | #define TGCR_TIM34RS_SHIFT           1 | 
|  | #define TGCR_RESET                   0x0 | 
|  | #define TGCR_UNRESET                 0x1 | 
|  | #define TGCR_RESET_MASK              0x3 | 
|  |  | 
|  | #define WDTCR_WDEN_SHIFT             14 | 
|  | #define WDTCR_WDEN_DISABLE           0x0 | 
|  | #define WDTCR_WDEN_ENABLE            0x1 | 
|  | #define WDTCR_WDKEY_SHIFT            16 | 
|  | #define WDTCR_WDKEY_SEQ0             0xa5c6 | 
|  | #define WDTCR_WDKEY_SEQ1             0xda7e | 
|  |  | 
|  | struct timer_s { | 
|  | char *name; | 
|  | unsigned int id; | 
|  | unsigned long period; | 
|  | unsigned long opts; | 
|  | unsigned long reg_base; | 
|  | unsigned long tim_reg; | 
|  | unsigned long prd_reg; | 
|  | unsigned long enamode_shift; | 
|  | struct irqaction irqaction; | 
|  | }; | 
|  | static struct timer_s timers[]; | 
|  |  | 
|  | /* values for 'opts' field of struct timer_s */ | 
|  | #define TIMER_OPTS_DISABLED   0x00 | 
|  | #define TIMER_OPTS_ONESHOT    0x01 | 
|  | #define TIMER_OPTS_PERIODIC   0x02 | 
|  |  | 
|  | static int timer32_config(struct timer_s *t) | 
|  | { | 
|  | u32 tcr = davinci_readl(t->reg_base + TCR); | 
|  |  | 
|  | /* disable timer */ | 
|  | tcr &= ~(TCR_ENAMODE_MASK << t->enamode_shift); | 
|  | davinci_writel(tcr, t->reg_base + TCR); | 
|  |  | 
|  | /* reset counter to zero, set new period */ | 
|  | davinci_writel(0, t->tim_reg); | 
|  | davinci_writel(t->period, t->prd_reg); | 
|  |  | 
|  | /* Set enable mode */ | 
|  | if (t->opts & TIMER_OPTS_ONESHOT) { | 
|  | tcr |= TCR_ENAMODE_ONESHOT << t->enamode_shift; | 
|  | } else if (t->opts & TIMER_OPTS_PERIODIC) { | 
|  | tcr |= TCR_ENAMODE_PERIODIC << t->enamode_shift; | 
|  | } | 
|  |  | 
|  | davinci_writel(tcr, t->reg_base + TCR); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static inline u32 timer32_read(struct timer_s *t) | 
|  | { | 
|  | return davinci_readl(t->tim_reg); | 
|  | } | 
|  |  | 
|  | static irqreturn_t timer_interrupt(int irq, void *dev_id) | 
|  | { | 
|  | struct clock_event_device *evt = &clockevent_davinci; | 
|  |  | 
|  | evt->event_handler(evt); | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  |  | 
|  | /* called when 32-bit counter wraps */ | 
|  | static irqreturn_t freerun_interrupt(int irq, void *dev_id) | 
|  | { | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  |  | 
|  | static struct timer_s timers[] = { | 
|  | [TID_CLOCKEVENT] = { | 
|  | .name      = "clockevent", | 
|  | .opts      = TIMER_OPTS_DISABLED, | 
|  | .irqaction = { | 
|  | .flags   = IRQF_DISABLED | IRQF_TIMER, | 
|  | .handler = timer_interrupt, | 
|  | } | 
|  | }, | 
|  | [TID_CLOCKSOURCE] = { | 
|  | .name       = "free-run counter", | 
|  | .period     = ~0, | 
|  | .opts       = TIMER_OPTS_PERIODIC, | 
|  | .irqaction = { | 
|  | .flags   = IRQF_DISABLED | IRQF_TIMER, | 
|  | .handler = freerun_interrupt, | 
|  | } | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static void __init timer_init(void) | 
|  | { | 
|  | u32 bases[] = {DAVINCI_TIMER0_BASE, DAVINCI_TIMER1_BASE}; | 
|  | int i; | 
|  |  | 
|  | /* Global init of each 64-bit timer as a whole */ | 
|  | for(i=0; i<2; i++) { | 
|  | u32 tgcr, base = bases[i]; | 
|  |  | 
|  | /* Disabled, Internal clock source */ | 
|  | davinci_writel(0, base + TCR); | 
|  |  | 
|  | /* reset both timers, no pre-scaler for timer34 */ | 
|  | tgcr = 0; | 
|  | davinci_writel(tgcr, base + TGCR); | 
|  |  | 
|  | /* Set both timers to unchained 32-bit */ | 
|  | tgcr = TGCR_TIMMODE_32BIT_UNCHAINED << TGCR_TIMMODE_SHIFT; | 
|  | davinci_writel(tgcr, base + TGCR); | 
|  |  | 
|  | /* Unreset timers */ | 
|  | tgcr |= (TGCR_UNRESET << TGCR_TIM12RS_SHIFT) | | 
|  | (TGCR_UNRESET << TGCR_TIM34RS_SHIFT); | 
|  | davinci_writel(tgcr, base + TGCR); | 
|  |  | 
|  | /* Init both counters to zero */ | 
|  | davinci_writel(0, base + TIM12); | 
|  | davinci_writel(0, base + TIM34); | 
|  | } | 
|  |  | 
|  | /* Init of each timer as a 32-bit timer */ | 
|  | for (i=0; i< ARRAY_SIZE(timers); i++) { | 
|  | struct timer_s *t = &timers[i]; | 
|  |  | 
|  | if (t->name) { | 
|  | t->id = i; | 
|  | t->reg_base = (IS_TIMER1(t->id) ? | 
|  | DAVINCI_TIMER1_BASE : DAVINCI_TIMER0_BASE); | 
|  |  | 
|  | if (IS_TIMER_BOT(t->id)) { | 
|  | t->enamode_shift = 6; | 
|  | t->tim_reg = t->reg_base + TIM12; | 
|  | t->prd_reg = t->reg_base + PRD12; | 
|  | } else { | 
|  | t->enamode_shift = 22; | 
|  | t->tim_reg = t->reg_base + TIM34; | 
|  | t->prd_reg = t->reg_base + PRD34; | 
|  | } | 
|  |  | 
|  | /* Register interrupt */ | 
|  | t->irqaction.name = t->name; | 
|  | t->irqaction.dev_id = (void *)t; | 
|  | if (t->irqaction.handler != NULL) { | 
|  | setup_irq(timer_irqs[t->id], &t->irqaction); | 
|  | } | 
|  |  | 
|  | timer32_config(&timers[i]); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * clocksource | 
|  | */ | 
|  | static cycle_t read_cycles(void) | 
|  | { | 
|  | struct timer_s *t = &timers[TID_CLOCKSOURCE]; | 
|  |  | 
|  | return (cycles_t)timer32_read(t); | 
|  | } | 
|  |  | 
|  | static struct clocksource clocksource_davinci = { | 
|  | .name		= "timer0_1", | 
|  | .rating		= 300, | 
|  | .read		= read_cycles, | 
|  | .mask		= CLOCKSOURCE_MASK(32), | 
|  | .shift		= 24, | 
|  | .flags		= CLOCK_SOURCE_IS_CONTINUOUS, | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * clockevent | 
|  | */ | 
|  | static int davinci_set_next_event(unsigned long cycles, | 
|  | struct clock_event_device *evt) | 
|  | { | 
|  | struct timer_s *t = &timers[TID_CLOCKEVENT]; | 
|  |  | 
|  | t->period = cycles; | 
|  | timer32_config(t); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void davinci_set_mode(enum clock_event_mode mode, | 
|  | struct clock_event_device *evt) | 
|  | { | 
|  | struct timer_s *t = &timers[TID_CLOCKEVENT]; | 
|  |  | 
|  | switch (mode) { | 
|  | case CLOCK_EVT_MODE_PERIODIC: | 
|  | t->period = CLOCK_TICK_RATE / (HZ); | 
|  | t->opts = TIMER_OPTS_PERIODIC; | 
|  | timer32_config(t); | 
|  | break; | 
|  | case CLOCK_EVT_MODE_ONESHOT: | 
|  | t->opts = TIMER_OPTS_ONESHOT; | 
|  | break; | 
|  | case CLOCK_EVT_MODE_UNUSED: | 
|  | case CLOCK_EVT_MODE_SHUTDOWN: | 
|  | t->opts = TIMER_OPTS_DISABLED; | 
|  | break; | 
|  | case CLOCK_EVT_MODE_RESUME: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct clock_event_device clockevent_davinci = { | 
|  | .name		= "timer0_0", | 
|  | .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | 
|  | .shift		= 32, | 
|  | .set_next_event	= davinci_set_next_event, | 
|  | .set_mode	= davinci_set_mode, | 
|  | }; | 
|  |  | 
|  |  | 
|  | static void __init davinci_timer_init(void) | 
|  | { | 
|  | static char err[] __initdata = KERN_ERR | 
|  | "%s: can't register clocksource!\n"; | 
|  |  | 
|  | /* init timer hw */ | 
|  | timer_init(); | 
|  |  | 
|  | /* setup clocksource */ | 
|  | clocksource_davinci.mult = | 
|  | clocksource_khz2mult(CLOCK_TICK_RATE/1000, | 
|  | clocksource_davinci.shift); | 
|  | if (clocksource_register(&clocksource_davinci)) | 
|  | printk(err, clocksource_davinci.name); | 
|  |  | 
|  | /* setup clockevent */ | 
|  | clockevent_davinci.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, | 
|  | clockevent_davinci.shift); | 
|  | clockevent_davinci.max_delta_ns = | 
|  | clockevent_delta2ns(0xfffffffe, &clockevent_davinci); | 
|  | clockevent_davinci.min_delta_ns = | 
|  | clockevent_delta2ns(1, &clockevent_davinci); | 
|  |  | 
|  | clockevent_davinci.cpumask = cpumask_of_cpu(0); | 
|  | clockevents_register_device(&clockevent_davinci); | 
|  | } | 
|  |  | 
|  | struct sys_timer davinci_timer = { | 
|  | .init   = davinci_timer_init, | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* reset board using watchdog timer */ | 
|  | void davinci_watchdog_reset(void) { | 
|  | u32 tgcr, wdtcr, base = DAVINCI_WDOG_BASE; | 
|  |  | 
|  | /* disable, internal clock source */ | 
|  | davinci_writel(0, base + TCR); | 
|  |  | 
|  | /* reset timer, set mode to 64-bit watchdog, and unreset */ | 
|  | tgcr = 0; | 
|  | davinci_writel(tgcr, base + TCR); | 
|  | tgcr = TGCR_TIMMODE_64BIT_WDOG << TGCR_TIMMODE_SHIFT; | 
|  | tgcr |= (TGCR_UNRESET << TGCR_TIM12RS_SHIFT) | | 
|  | (TGCR_UNRESET << TGCR_TIM34RS_SHIFT); | 
|  | davinci_writel(tgcr, base + TCR); | 
|  |  | 
|  | /* clear counter and period regs */ | 
|  | davinci_writel(0, base + TIM12); | 
|  | davinci_writel(0, base + TIM34); | 
|  | davinci_writel(0, base + PRD12); | 
|  | davinci_writel(0, base + PRD34); | 
|  |  | 
|  | /* enable */ | 
|  | wdtcr = davinci_readl(base + WDTCR); | 
|  | wdtcr |= WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT; | 
|  | davinci_writel(wdtcr, base + WDTCR); | 
|  |  | 
|  | /* put watchdog in pre-active state */ | 
|  | wdtcr = (WDTCR_WDKEY_SEQ0 << WDTCR_WDKEY_SHIFT) | | 
|  | (WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT); | 
|  | davinci_writel(wdtcr, base + WDTCR); | 
|  |  | 
|  | /* put watchdog in active state */ | 
|  | wdtcr = (WDTCR_WDKEY_SEQ1 << WDTCR_WDKEY_SHIFT) | | 
|  | (WDTCR_WDEN_ENABLE << WDTCR_WDEN_SHIFT); | 
|  | davinci_writel(wdtcr, base + WDTCR); | 
|  |  | 
|  | /* write an invalid value to the WDKEY field to trigger | 
|  | * a watchdog reset */ | 
|  | wdtcr = 0x00004000; | 
|  | davinci_writel(wdtcr, base + WDTCR); | 
|  | } |