timer/aspeed: fix timer enablement when a reload is not set

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>
This commit is contained in:
Cédric Le Goater 2017-06-13 14:57:00 +01:00 committed by Peter Maydell
parent a87e81b9b5
commit 1403f36447

View File

@ -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;
@ -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)
@ -227,10 +238,23 @@ 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);
@ -238,17 +262,14 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
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:
@ -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);
}