Skip to content

Commit

Permalink
timer/aspeed: fix timer enablement when a reload is not set
Browse files Browse the repository at this point in the history
When a timer is enabled before a reload value is set, the controller
waits for a reload value to be set before starting decrementing. This
fix tries to cover that case by changing the timer expiry only when
a reload value is valid.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
Message-id: 1496739312-32304-1-git-send-email-clg@kaod.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
  • Loading branch information
legoater authored and pm215 committed Jun 13, 2017
1 parent a87e81b commit 1403f36
Showing 1 changed file with 29 additions and 8 deletions.
37 changes: 29 additions & 8 deletions hw/timer/aspeed_timer.c
Expand Up @@ -130,15 +130,26 @@ static uint64_t calculate_next(struct AspeedTimer *t)
next = seq[1];
} else if (now < seq[2]) {
next = seq[2];
} else {
} else if (t->reload) {
reload_ns = muldiv64(t->reload, NANOSECONDS_PER_SECOND, rate);
t->start = now - ((now - t->start) % reload_ns);
} else {
/* no reload value, return 0 */
break;
}
}

return next;
}

static void aspeed_timer_mod(AspeedTimer *t)
{
uint64_t next = calculate_next(t);
if (next) {
timer_mod(&t->timer, next);
}
}

static void aspeed_timer_expire(void *opaque)
{
AspeedTimer *t = opaque;
Expand All @@ -164,7 +175,7 @@ static void aspeed_timer_expire(void *opaque)
qemu_set_irq(t->irq, t->level);
}

timer_mod(&t->timer, calculate_next(t));
aspeed_timer_mod(t);
}

static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg)
Expand Down Expand Up @@ -227,28 +238,38 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
uint32_t value)
{
AspeedTimer *t;
uint32_t old_reload;

trace_aspeed_timer_set_value(timer, reg, value);
t = &s->timers[timer];
switch (reg) {
case TIMER_REG_RELOAD:
old_reload = t->reload;
t->reload = value;

/* If the reload value was not previously set, or zero, and
* the current value is valid, try to start the timer if it is
* enabled.
*/
if (old_reload || !t->reload) {
break;
}

case TIMER_REG_STATUS:
if (timer_enabled(t)) {
uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int64_t delta = (int64_t) value - (int64_t) calculate_ticks(t, now);
uint32_t rate = calculate_rate(t);

t->start += muldiv64(delta, NANOSECONDS_PER_SECOND, rate);
timer_mod(&t->timer, calculate_next(t));
aspeed_timer_mod(t);
}
break;
case TIMER_REG_RELOAD:
t->reload = value;
break;
case TIMER_REG_MATCH_FIRST:
case TIMER_REG_MATCH_SECOND:
t->match[reg - 2] = value;
if (timer_enabled(t)) {
timer_mod(&t->timer, calculate_next(t));
aspeed_timer_mod(t);
}
break;
default:
Expand All @@ -268,7 +289,7 @@ static void aspeed_timer_ctrl_enable(AspeedTimer *t, bool enable)
trace_aspeed_timer_ctrl_enable(t->id, enable);
if (enable) {
t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
timer_mod(&t->timer, calculate_next(t));
aspeed_timer_mod(t);
} else {
timer_del(&t->timer);
}
Expand Down

0 comments on commit 1403f36

Please sign in to comment.