summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Hellstrom <daniel@gaisler.com>2012-06-19 08:34:37 +0200
committerDaniel Hellstrom <daniel@gaisler.com>2012-06-19 08:47:58 +0200
commitf89be2c545028cc05343fd9b95e091be6b311df0 (patch)
treec362cea07670ad21c43a5922b2adf355e70d7155
parentc7dab042cfbc3b555ffd1448feef37cdc672346d (diff)
LEON3: fixed nano seconds support in TLIB
The _Watchdog_Nanoseconds_since_tick_handler() function caller does not take into account that the timer counter may wrap, underflow or overflow. Instead, the driver must take that into account. This GPTIMER DrvMgr driver patch makes use of the IRQ-Pending bit to determine if a underflow has happened. In that case a greater time than one tick is returned (even considering the function name..). The TLIB clock layer must also ACK the interrupt pending bit, otherwise we couldn't determine whether an IRQ is pending or if belongs to un old already handled tick IRQ. Note that this patch only fixes the DrvMgr GPTIMER driver and TLIB, the standard LEON3 GPTIMER driver still needs a fix. Reported-by: Rolf Schroedter <rolf.schroedter@dlr.de> Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
-rw-r--r--c/src/lib/libbsp/sparc/shared/include/tlib.h8
-rw-r--r--c/src/lib/libbsp/sparc/shared/timer/gptimer.c45
-rw-r--r--c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c18
3 files changed, 59 insertions, 12 deletions
diff --git a/c/src/lib/libbsp/sparc/shared/include/tlib.h b/c/src/lib/libbsp/sparc/shared/include/tlib.h
index 95be5efce4..7b253b7813 100644
--- a/c/src/lib/libbsp/sparc/shared/include/tlib.h
+++ b/c/src/lib/libbsp/sparc/shared/include/tlib.h
@@ -35,6 +35,7 @@ struct tlib_drv {
void (*restart)(struct tlib_dev *hand);
void (*get_counter)(struct tlib_dev *hand, unsigned int *counter);
int (*custom)(struct tlib_dev *hand, int cmd, void *arg);
+ int (*int_pend)(struct tlib_dev *hand, int ack);
};
struct tlib_dev {
@@ -167,3 +168,10 @@ static inline void tlib_custom(void *hand, int cmd, void *arg)
dev->drv->custom(dev, cmd, arg);
}
+
+static inline int tlib_interrupt_pending(void *hand, int ack)
+{
+ struct tlib_dev *dev = hand;
+
+ return dev->drv->int_pend(dev, ack);
+}
diff --git a/c/src/lib/libbsp/sparc/shared/timer/gptimer.c b/c/src/lib/libbsp/sparc/shared/timer/gptimer.c
index 957081b745..6479ecb834 100644
--- a/c/src/lib/libbsp/sparc/shared/timer/gptimer.c
+++ b/c/src/lib/libbsp/sparc/shared/timer/gptimer.c
@@ -87,6 +87,7 @@ struct gptimer_timer {
struct gptimer_timer_regs *tregs;
char index; /* Timer Index in this driver */
char tindex; /* Timer Index In Hardware */
+ unsigned char irq_ack_mask;
};
/* GPTIMER Core private */
@@ -277,8 +278,15 @@ int gptimer_init1(struct drvmgr_dev *dev)
timer->tregs = &regs->timer[(int)timer->tindex];
timer->tdev.drv = &gptimer_tlib_drv;
- /* Stop Timer */
- timer->tregs->ctrl = 0;
+ /* Stop Timer and probe Pending bit. In newer hardware the
+ * timer has pending bit is cleared by writing a one to it,
+ * whereas older versions it is cleared with a zero.
+ */
+ timer->tregs->ctrl = GPTIMER_CTRL_IP;
+ if ((timer->tregs->ctrl & GPTIMER_CTRL_IP) != 0)
+ timer->irq_ack_mask = ~GPTIMER_CTRL_IP;
+ else
+ timer->irq_ack_mask = ~0;
/* Register Timer at Timer Library */
#if defined(LEON3) && defined(RTEMS_DRVMGR_STARTUP)
@@ -304,6 +312,10 @@ int gptimer_init1(struct drvmgr_dev *dev)
priv);
}
+ /* Older HW */
+
+
+
/* If the user request a certain Timer to be the RTEMS Clock Timer,
* the timer must be registered at the Clock Driver.
*/
@@ -367,23 +379,33 @@ static inline struct gptimer_priv *priv_from_timer(struct gptimer_timer *t)
t->index * sizeof(struct gptimer_timer));
}
+int gptimer_tlib_int_pend(struct tlib_dev *hand, int ack)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+ unsigned int ctrl = timer->tregs->ctrl;
+
+ if ((ctrl & (GPTIMER_CTRL_IP | GPTIMER_CTRL_IE)) ==
+ (GPTIMER_CTRL_IP | GPTIMER_CTRL_IE)) {
+ /* clear Pending IRQ ? */
+ if (ack)
+ timer->tregs->ctrl = ctrl & timer->irq_ack_mask;
+ return 1; /* timer generated IRQ */
+ } else
+ return 0; /* was not timer causing IRQ */
+}
+
void gptimer_isr(void *data)
{
struct gptimer_priv *priv = data;
- struct gptimer_timer_regs *tregs;
int i;
- unsigned int ctrl;
/* Check all timers for IRQ */
for (i=0;i<priv->timer_cnt; i++) {
- tregs = priv->timers[i].tregs;
- ctrl = tregs->ctrl;
- if ( ctrl & GPTIMER_CTRL_IP ) {
- /* IRQ Was generated by Timer, Clear Pending flag
- * call ISR registered
+ if (gptimer_tlib_int_pend((void *)&priv->timers[i], 1)) {
+ /* IRQ Was generated by Timer and Pending flag has been
+ * cleared. Call ISR registered
*/
- tregs->ctrl = ctrl | GPTIMER_CTRL_IP;
- if ( priv->timers[i].tdev.isr_func ) {
+ if (priv->timers[i].tdev.isr_func) {
priv->timers[i].tdev.isr_func(
priv->timers[i].tdev.isr_data);
}
@@ -503,4 +525,5 @@ struct tlib_drv gptimer_tlib_drv =
.restart = gptimer_tlib_restart,
.get_counter = gptimer_tlib_get_counter,
.custom = NULL,
+ .int_pend = gptimer_tlib_int_pend,
};
diff --git a/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c b/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c
index ab57fba3d9..a67d31c5ba 100644
--- a/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c
+++ b/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c
@@ -72,6 +72,14 @@ rtems_device_minor_number rtems_clock_minor;
void Clock_isr(void *arg_unused)
{
/*
+ * Support for shared interrupts. Ack IRQ at source, only handle
+ * interrupts generated from the tick-timer. Clearing pending bit
+ * is also needed for Clock_nanoseconds_since_last_tick() to work.
+ */
+ if ( tlib_interrupt_pending(Clock_handle, 1) == 0 )
+ return;
+
+ /*
* Accurate count of ISRs
*/
@@ -144,22 +152,30 @@ void Clock_exit( void )
uint32_t Clock_nanoseconds_since_last_tick(void)
{
uint32_t clicks;
+ int ip;
+
if ( !Clock_handle )
return 0;
tlib_get_counter(Clock_handle, (unsigned int *)&clicks);
+ /* protect against timer counter underflow/overflow */
+ ip = tlib_interrupt_pending(Clock_handle, 0);
+ if (ip)
+ tlib_get_counter(Clock_handle, (unsigned int *)&clicks);
#ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ
{
/* Down counter. Calc from BaseFreq. */
uint64_t tmp;
+ if (ip)
+ clicks += Clock_basefreq;
tmp = ((uint64_t)clicks * 1000000000) / ((uint64_t)Clock_basefreq);
return (uint32_t)tmp;
}
#else
/* Down counter. Timer base frequency is initialized to 1 MHz */
return (uint32_t)
- (rtems_configuration_get_microseconds_per_tick() - clicks) * 1000;
+ ((rtems_configuration_get_microseconds_per_tick() << ip) - clicks) * 1000;
#endif
}