diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index 16d7dd5a187da89a00a6ec02148e7a0b0c8d0be5..7e6fc2d2ce7588f06fdef8efb1403d1962e5eee2 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -84,14 +84,16 @@ static void ptimer_tick(void *opaque) uint64_t ptimer_get_count(ptimer_state *s) { - int64_t now; uint64_t counter; if (s->enabled) { - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t next = s->next_event; + bool expired = (now - next >= 0); + bool oneshot = (s->enabled == 2); + /* Figure out the current counter value. */ - if (now - s->next_event > 0 - || s->period == 0) { + if (s->period == 0 || (expired && (oneshot || use_icount))) { /* Prevent timer underflowing if it should already have triggered. */ counter = 0; @@ -103,7 +105,7 @@ uint64_t ptimer_get_count(ptimer_state *s) uint32_t period_frac = s->period_frac; uint64_t period = s->period; - if ((s->enabled == 1) && !use_icount && (s->delta * period < 10000)) { + if (!oneshot && (s->delta * period < 10000) && !use_icount) { period = 10000 / s->delta; period_frac = 0; } @@ -118,7 +120,7 @@ uint64_t ptimer_get_count(ptimer_state *s) backwards. */ - rem = s->next_event - now; + rem = expired ? now - next : next - now; div = period; clz1 = clz64(rem); @@ -138,6 +140,11 @@ uint64_t ptimer_get_count(ptimer_state *s) div += 1; } counter = rem / div; + + if (expired && counter != 0) { + /* Wrap around periodic counter. */ + counter = s->limit - (counter - 1) % s->limit; + } } } else { counter = s->delta;