Commit 3abd5acf authored by Mark A. Greer's avatar Mark A. Greer Committed by Kevin Hilman

davinci: Add compare register support to timer code

The Timer64p timer has 8 compare registers that can
be used to generate interrupts when the timer value
matches the compare reg's value.  They do 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.

Since the timer is always running, there is a hardware
race in timer32_config() between reading the current
timer value, and adding the period to the current
timer value and writing the compare register.
Testing on a da830 evm board with the timer clocked
at 24 MHz and the processor clocked at 300 MHz,
showed the number of counter ticks to do this ranged
from 20-53 (~1-2.2 usecs) but usually around 41 ticks.
This includes some artifacts from collecting the
information.  So, the minimum period should be
at least 5 usecs to be safe.

There is also an non-critical lower limit that
the period should be since there is no point in
setting an event that is much shorter than the
time it takes to set the event, and get & handle
the timer interrupt for that event.  There can
also be all sorts of delays from activities
occuring elsewhere in the system (including
hardware activitis like cache & TLB management).
These are virtually impossible to quantify so a
minimum period of 50 usecs was chosen.  That will
certianly be enough to avoid the actual hardware
race but hopefully not large enough to cause
unreasonably course-grained timers.
Signed-off-by: default avatarMark A. Greer <mgreer@mvista.com>
Signed-off-by: default avatarKevin Hilman <khilman@deeprootsystems.com>
parent 0b0c4c2a
...@@ -30,6 +30,8 @@ struct davinci_timer_instance { ...@@ -30,6 +30,8 @@ struct davinci_timer_instance {
void __iomem *base; void __iomem *base;
u32 bottom_irq; u32 bottom_irq;
u32 top_irq; u32 top_irq;
unsigned long cmp_off;
unsigned int cmp_irq;
}; };
struct davinci_timer_info { struct davinci_timer_info {
......
...@@ -55,6 +55,16 @@ enum { ...@@ -55,6 +55,16 @@ enum {
#define TGCR 0x24 #define TGCR 0x24
#define WDTCR 0x28 #define WDTCR 0x28
/* 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
/* Timer register bitfields */ /* Timer register bitfields */
#define TCR_ENAMODE_DISABLE 0x0 #define TCR_ENAMODE_DISABLE 0x0
#define TCR_ENAMODE_ONESHOT 0x1 #define TCR_ENAMODE_ONESHOT 0x1
...@@ -85,6 +95,7 @@ struct timer_s { ...@@ -85,6 +95,7 @@ struct timer_s {
unsigned int id; unsigned int id;
unsigned long period; unsigned long period;
unsigned long opts; unsigned long opts;
unsigned long flags;
void __iomem *base; void __iomem *base;
unsigned long tim_off; unsigned long tim_off;
unsigned long prd_off; unsigned long prd_off;
...@@ -94,9 +105,13 @@ struct timer_s { ...@@ -94,9 +105,13 @@ struct timer_s {
static struct timer_s timers[]; static struct timer_s timers[];
/* values for 'opts' field of struct timer_s */ /* values for 'opts' field of struct timer_s */
#define TIMER_OPTS_DISABLED 0x00 #define TIMER_OPTS_DISABLED 0x01
#define TIMER_OPTS_ONESHOT 0x01 #define TIMER_OPTS_ONESHOT 0x02
#define TIMER_OPTS_PERIODIC 0x02 #define TIMER_OPTS_PERIODIC 0x04
#define TIMER_OPTS_STATE_MASK 0x07
#define TIMER_OPTS_USE_COMPARE 0x80000000
#define USING_COMPARE(t) ((t)->opts & TIMER_OPTS_USE_COMPARE)
static char *id_to_name[] = { static char *id_to_name[] = {
[T0_BOT] = "timer0_0", [T0_BOT] = "timer0_0",
...@@ -107,7 +122,24 @@ static char *id_to_name[] = { ...@@ -107,7 +122,24 @@ static char *id_to_name[] = {
static int timer32_config(struct timer_s *t) static int timer32_config(struct timer_s *t)
{ {
u32 tcr = __raw_readl(t->base + TCR); u32 tcr;
struct davinci_soc_info *soc_info = davinci_get_soc_info();
if (USING_COMPARE(t)) {
struct davinci_timer_instance *dtip =
soc_info->timer_info->timers;
int event_timer = ID_TO_TIMER(timers[TID_CLOCKEVENT].id);
/*
* 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 + dtip[event_timer].cmp_off);
} else {
tcr = __raw_readl(t->base + TCR);
/* disable timer */ /* disable timer */
tcr &= ~(TCR_ENAMODE_MASK << t->enamode_shift); tcr &= ~(TCR_ENAMODE_MASK << t->enamode_shift);
...@@ -118,13 +150,13 @@ static int timer32_config(struct timer_s *t) ...@@ -118,13 +150,13 @@ static int timer32_config(struct timer_s *t)
__raw_writel(t->period, t->base + t->prd_off); __raw_writel(t->period, t->base + t->prd_off);
/* Set enable mode */ /* Set enable mode */
if (t->opts & TIMER_OPTS_ONESHOT) { if (t->opts & TIMER_OPTS_ONESHOT)
tcr |= TCR_ENAMODE_ONESHOT << t->enamode_shift; tcr |= TCR_ENAMODE_ONESHOT << t->enamode_shift;
} else if (t->opts & TIMER_OPTS_PERIODIC) { else if (t->opts & TIMER_OPTS_PERIODIC)
tcr |= TCR_ENAMODE_PERIODIC << t->enamode_shift; tcr |= TCR_ENAMODE_PERIODIC << t->enamode_shift;
}
__raw_writel(tcr, t->base + TCR); __raw_writel(tcr, t->base + TCR);
}
return 0; return 0;
} }
...@@ -222,8 +254,11 @@ static void __init timer_init(void) ...@@ -222,8 +254,11 @@ static void __init timer_init(void)
/* Register interrupt */ /* Register interrupt */
t->irqaction.name = t->name; t->irqaction.name = t->name;
t->irqaction.dev_id = (void *)t; t->irqaction.dev_id = (void *)t;
if (t->irqaction.handler != NULL)
if (t->irqaction.handler != NULL) {
irq = USING_COMPARE(t) ? dtip[i].cmp_irq : irq;
setup_irq(irq, &t->irqaction); setup_irq(irq, &t->irqaction);
}
timer32_config(&timers[i]); timer32_config(&timers[i]);
} }
...@@ -268,15 +303,18 @@ static void davinci_set_mode(enum clock_event_mode mode, ...@@ -268,15 +303,18 @@ static void davinci_set_mode(enum clock_event_mode mode,
switch (mode) { switch (mode) {
case CLOCK_EVT_MODE_PERIODIC: case CLOCK_EVT_MODE_PERIODIC:
t->period = davinci_clock_tick_rate / (HZ); t->period = davinci_clock_tick_rate / (HZ);
t->opts = TIMER_OPTS_PERIODIC; t->opts &= ~TIMER_OPTS_STATE_MASK;
t->opts |= TIMER_OPTS_PERIODIC;
timer32_config(t); timer32_config(t);
break; break;
case CLOCK_EVT_MODE_ONESHOT: case CLOCK_EVT_MODE_ONESHOT:
t->opts = TIMER_OPTS_ONESHOT; t->opts &= ~TIMER_OPTS_STATE_MASK;
t->opts |= TIMER_OPTS_ONESHOT;
break; break;
case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN: case CLOCK_EVT_MODE_SHUTDOWN:
t->opts = TIMER_OPTS_DISABLED; t->opts &= ~TIMER_OPTS_STATE_MASK;
t->opts |= TIMER_OPTS_DISABLED;
break; break;
case CLOCK_EVT_MODE_RESUME: case CLOCK_EVT_MODE_RESUME:
break; break;
...@@ -295,12 +333,40 @@ static void __init davinci_timer_init(void) ...@@ -295,12 +333,40 @@ static void __init davinci_timer_init(void)
{ {
struct clk *timer_clk; struct clk *timer_clk;
struct davinci_soc_info *soc_info = &davinci_soc_info; struct davinci_soc_info *soc_info = &davinci_soc_info;
unsigned int clockevent_id;
unsigned int clocksource_id;
static char err[] __initdata = KERN_ERR static char err[] __initdata = KERN_ERR
"%s: can't register clocksource!\n"; "%s: can't register clocksource!\n";
timers[TID_CLOCKEVENT].id = soc_info->timer_info->clockevent_id; clockevent_id = soc_info->timer_info->clockevent_id;
timers[TID_CLOCKSOURCE].id = soc_info->timer_info->clocksource_id; clocksource_id = soc_info->timer_info->clocksource_id;
timers[TID_CLOCKEVENT].id = clockevent_id;
timers[TID_CLOCKSOURCE].id = 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).
*/
if (clockevent_id == clocksource_id) {
struct davinci_timer_instance *dtip =
soc_info->timer_info->timers;
int event_timer = ID_TO_TIMER(clockevent_id);
/* Only bottom timers can use compare regs */
if (IS_TIMER_TOP(clockevent_id))
pr_warning("davinci_timer_init: Invalid use"
" of system timers. Results unpredictable.\n");
else if ((dtip[event_timer].cmp_off == 0)
|| (dtip[event_timer].cmp_irq == 0))
pr_warning("davinci_timer_init: Invalid timer instance"
" setup. Results unpredictable.\n");
else {
timers[TID_CLOCKEVENT].opts |= TIMER_OPTS_USE_COMPARE;
clockevent_davinci.features = CLOCK_EVT_FEAT_ONESHOT;
}
}
/* init timer hw */ /* init timer hw */
timer_init(); timer_init();
...@@ -312,7 +378,7 @@ static void __init davinci_timer_init(void) ...@@ -312,7 +378,7 @@ static void __init davinci_timer_init(void)
davinci_clock_tick_rate = clk_get_rate(timer_clk); davinci_clock_tick_rate = clk_get_rate(timer_clk);
/* setup clocksource */ /* setup clocksource */
clocksource_davinci.name = id_to_name[timers[TID_CLOCKSOURCE].id]; clocksource_davinci.name = id_to_name[clocksource_id];
clocksource_davinci.mult = clocksource_davinci.mult =
clocksource_khz2mult(davinci_clock_tick_rate/1000, clocksource_khz2mult(davinci_clock_tick_rate/1000,
clocksource_davinci.shift); clocksource_davinci.shift);
...@@ -325,8 +391,7 @@ static void __init davinci_timer_init(void) ...@@ -325,8 +391,7 @@ static void __init davinci_timer_init(void)
clockevent_davinci.shift); clockevent_davinci.shift);
clockevent_davinci.max_delta_ns = clockevent_davinci.max_delta_ns =
clockevent_delta2ns(0xfffffffe, &clockevent_davinci); clockevent_delta2ns(0xfffffffe, &clockevent_davinci);
clockevent_davinci.min_delta_ns = clockevent_davinci.min_delta_ns = 50000; /* 50 usec */
clockevent_delta2ns(1, &clockevent_davinci);
clockevent_davinci.cpumask = cpumask_of(0); clockevent_davinci.cpumask = cpumask_of(0);
clockevents_register_device(&clockevent_davinci); clockevents_register_device(&clockevent_davinci);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment