From: Mark A. Greer <mgr...@mvista.com>

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 <mgr...@mvista.com>
---
 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
Davinci-linux-open-source@linux.davincidsp.com
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to