aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/core/ptimer.c19
1 files changed, 13 insertions, 6 deletions
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
index 16d7dd5a18..7e6fc2d2ce 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;