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.
This commit is contained in:
Daniel Hellstrom
2012-06-19 08:34:37 +02:00
parent f00fee8eb1
commit 16d6e42c43
3 changed files with 59 additions and 12 deletions

View File

@@ -84,6 +84,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 */
@@ -274,8 +275,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)
@@ -301,6 +309,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.
*/
@@ -364,23 +376,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);
}
@@ -500,4 +522,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,
};