From: Mark A. Greer <[email protected]> The Timer64p timer has 8 compare registers that can be used to generate interrupts when the timer value matches the compare reg's value. It will not disturb the timer itself. This can be useful when there is only one timer available for both clock events and clocksource.
When enabled, the clocksource remains a continuous 32-bit counter but the clock event will no longer support periodic interrupts. Instead only oneshot timers will be supported and implemented by setting the compare register to the current timer value plus the period that the clock event subsystem is requesting. Compare registers support is enabled automatically when the following conditions are met: 1) The same timer is being used for clock events and clocksource. 2) The timer is the bottom half (32 bits) of the 64-bit timer (hardware limitation). 3) The the compare register offset and irq are not zero. Signed-off-by: Mark A. Greer <[email protected]> --- arch/arm/mach-davinci/include/mach/common.h | 2 + arch/arm/mach-davinci/include/mach/time.h | 10 +++ arch/arm/mach-davinci/time.c | 86 ++++++++++++++++++++------- 3 files changed, 77 insertions(+), 21 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h index 6df5f66..e56988b 100644 --- a/arch/arm/mach-davinci/include/mach/common.h +++ b/arch/arm/mach-davinci/include/mach/common.h @@ -31,6 +31,8 @@ struct davinci_timer_info { u32 *timer_irqs; unsigned int clockevent_id; unsigned int clocksource_id; + unsigned long compare_off; + unsigned int compare_irq; }; /* SoC specific init support */ diff --git a/arch/arm/mach-davinci/include/mach/time.h b/arch/arm/mach-davinci/include/mach/time.h index 16cc89f..e6b0944 100644 --- a/arch/arm/mach-davinci/include/mach/time.h +++ b/arch/arm/mach-davinci/include/mach/time.h @@ -28,6 +28,16 @@ enum { #define IS_TIMER_TOP(id) ((id & 0x1)) #define IS_TIMER_BOT(id) (!IS_TIMER_TOP(id)) +/* Offsets of the 8 compare registers */ +#define CMP12_0 0x60 +#define CMP12_1 0x64 +#define CMP12_2 0x68 +#define CMP12_3 0x6c +#define CMP12_4 0x70 +#define CMP12_5 0x74 +#define CMP12_6 0x78 +#define CMP12_7 0x7c + extern u32 davinci_timer_bases[]; extern u32 davinci_timer_irqs[]; diff --git a/arch/arm/mach-davinci/time.c b/arch/arm/mach-davinci/time.c index f07bac8..33ca090 100644 --- a/arch/arm/mach-davinci/time.c +++ b/arch/arm/mach-davinci/time.c @@ -84,6 +84,7 @@ struct timer_s { unsigned int id; unsigned long period; unsigned long opts; + unsigned long flags; void __iomem *base; unsigned long tim_off; unsigned long prd_off; @@ -97,6 +98,9 @@ static struct timer_s timers[]; #define TIMER_OPTS_ONESHOT 0x01 #define TIMER_OPTS_PERIODIC 0x02 +#define TIMER_FLAGS_USE_COMPARE 0x01 +#define USING_COMPARE(t) ((t)->flags & TIMER_FLAGS_USE_COMPARE) + static char *id_to_name[] = { [T0_BOT] = "timer0_0", [T0_TOP] = "timer0_1", @@ -106,24 +110,36 @@ static char *id_to_name[] = { static int timer32_config(struct timer_s *t) { - u32 tcr = __raw_readl(t->base + TCR); - - /* disable timer */ - tcr &= ~(TCR_ENAMODE_MASK << t->enamode_shift); - __raw_writel(tcr, t->base + TCR); - - /* reset counter to zero, set new period */ - __raw_writel(0, t->base + t->tim_off); - __raw_writel(t->period, t->base + t->prd_off); - - /* 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; + u32 tcr; + + if (USING_COMPARE(t)) + /* + * Next interrupt should be the current time reg value plus + * the new period (using 32 bit unsigned addition/wrapping + * to 0 on overflow). This assumes that the clocksource + * is setup to count to 2^32-1 before wrapping around to 0. + */ + __raw_writel(__raw_readl(t->base + t->tim_off) + t->period, + t->base + davinci_soc_info->timer_info->compare_off); + else { + tcr = __raw_readl(t->base + TCR); + + /* disable timer */ + tcr &= ~(TCR_ENAMODE_MASK << t->enamode_shift); + __raw_writel(tcr, t->base + TCR); + + /* reset counter to zero, set new period */ + __raw_writel(0, t->base + t->tim_off); + __raw_writel(t->period, t->base + t->prd_off); + + /* 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; + + __raw_writel(tcr, t->base + TCR); } - - __raw_writel(tcr, t->base + TCR); return 0; } @@ -201,6 +217,7 @@ static void __init timer_init(void) for (i=0; i< ARRAY_SIZE(timers); i++) { struct timer_s *t = &timers[i]; u32 phys_base; + unsigned int irq; phys_base = (IS_TIMER1(t->id) ? phys_bases[1] : phys_bases[0]); t->base = IO_ADDRESS(phys_base); @@ -218,9 +235,14 @@ static void __init timer_init(void) /* Register interrupt */ t->irqaction.name = t->name; t->irqaction.dev_id = (void *)t; - if (t->irqaction.handler != NULL) - setup_irq(davinci_soc_info->timer_info-> - timer_irqs[t->id], &t->irqaction); + + if (t->irqaction.handler != NULL) { + irq = USING_COMPARE(t) ? + davinci_soc_info->timer_info->compare_irq : + davinci_soc_info->timer_info->timer_irqs[t->id]; + + setup_irq(irq, &t->irqaction); + } timer32_config(&timers[i]); } @@ -291,7 +313,7 @@ static struct clock_event_device clockevent_davinci = { static void __init davinci_timer_init(void) { struct clk *timer_clk; - + unsigned int clockevent_id; static char err[] __initdata = KERN_ERR "%s: can't register clocksource!\n"; @@ -300,6 +322,28 @@ static void __init davinci_timer_init(void) timers[TID_CLOCKSOURCE].id = davinci_soc_info->timer_info->clocksource_id; + /* + * If using same timer for both clock events & clocksource, + * a compare register must be used to generate an event interrupt. + * This is equivalent to a oneshot timer only (not periodic). + */ + clockevent_id = davinci_soc_info->timer_info->clockevent_id; + + if (clockevent_id == davinci_soc_info->timer_info->clocksource_id) { + /* Only bottom timers can use compare regs */ + if (IS_TIMER_TOP(clockevent_id)) + printk(KERN_WARNING "davinci_timer_init: Invalid use " + "of system timers. Results unpredictable.\n"); + else if ((davinci_soc_info->timer_info->compare_off == 0) || + (davinci_soc_info->timer_info->compare_irq == 0)) + printk(KERN_WARNING "davinci_timer_init: Invalid " + "setup. Results unpredictable.\n"); + else { + timers[TID_CLOCKEVENT].flags = TIMER_FLAGS_USE_COMPARE; + clockevent_davinci.features = CLOCK_EVT_FEAT_ONESHOT; + } + } + /* init timer hw */ timer_init(); -- 1.6.0.3 _______________________________________________ Davinci-linux-open-source mailing list [email protected] http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
