Hello.
Mark A. Greer wrote:
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]>
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
Why not:
#define CMP12(n) (0x60 + (n) * 4)
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)
+
Why not add this flag to 'opts' field instead? I don't think we need
another 4-byte flag field, just add one more flag...
Also, why not add 'cmp_off' field since you've made it possible to use
different compare registers?
@@ -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
Shouldn't it be "32-bit"?
+ * 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];
Why 'compare_irq' isn't an array as well? TIMER1 has the compare registers
too, not only TIMER0.
+
+ 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");
Results are quite predictable -- it just won't work. :-)
WBR, Sergei
_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source