summaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/timer/bcm2835_systmr.c48
-rw-r--r--hw/timer/trace-events6
2 files changed, 35 insertions, 19 deletions
diff --git a/hw/timer/bcm2835_systmr.c b/hw/timer/bcm2835_systmr.c
index b234e83824..67669a57ff 100644
--- a/hw/timer/bcm2835_systmr.c
+++ b/hw/timer/bcm2835_systmr.c
@@ -28,20 +28,13 @@ REG32(COMPARE1, 0x10)
REG32(COMPARE2, 0x14)
REG32(COMPARE3, 0x18)
-static void bcm2835_systmr_update_irq(BCM2835SystemTimerState *s)
+static void bcm2835_systmr_timer_expire(void *opaque)
{
- bool enable = !!s->reg.ctrl_status;
+ BCM2835SystemTimerCompare *tmr = opaque;
- trace_bcm2835_systmr_irq(enable);
- qemu_set_irq(s->irq, enable);
-}
-
-static void bcm2835_systmr_update_compare(BCM2835SystemTimerState *s,
- unsigned timer_index)
-{
- /* TODO fow now, since neither Linux nor U-boot use these timers. */
- qemu_log_mask(LOG_UNIMP, "COMPARE register %u not implemented\n",
- timer_index);
+ trace_bcm2835_systmr_timer_expired(tmr->id);
+ tmr->state->reg.ctrl_status |= 1 << tmr->id;
+ qemu_set_irq(tmr->irq, 1);
}
static uint64_t bcm2835_systmr_read(void *opaque, hwaddr offset,
@@ -75,19 +68,33 @@ static uint64_t bcm2835_systmr_read(void *opaque, hwaddr offset,
}
static void bcm2835_systmr_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
+ uint64_t value64, unsigned size)
{
BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque);
+ int index;
+ uint32_t value = value64;
+ uint32_t triggers_delay_us;
+ uint64_t now;
trace_bcm2835_systmr_write(offset, value);
switch (offset) {
case A_CTRL_STATUS:
s->reg.ctrl_status &= ~value; /* Ack */
- bcm2835_systmr_update_irq(s);
+ for (index = 0; index < ARRAY_SIZE(s->tmr); index++) {
+ if (extract32(value, index, 1)) {
+ trace_bcm2835_systmr_irq_ack(index);
+ qemu_set_irq(s->tmr[index].irq, 0);
+ }
+ }
break;
case A_COMPARE0 ... A_COMPARE3:
- s->reg.compare[(offset - A_COMPARE0) >> 2] = value;
- bcm2835_systmr_update_compare(s, (offset - A_COMPARE0) >> 2);
+ index = (offset - A_COMPARE0) >> 2;
+ s->reg.compare[index] = value;
+ now = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
+ /* Compare lower 32-bits of the free-running counter. */
+ triggers_delay_us = value - now;
+ trace_bcm2835_systmr_run(index, triggers_delay_us);
+ timer_mod(&s->tmr[index].timer, now + triggers_delay_us);
break;
case A_COUNTER_LOW:
case A_COUNTER_HIGH:
@@ -125,7 +132,14 @@ static void bcm2835_systmr_realize(DeviceState *dev, Error **errp)
memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_systmr_ops,
s, "bcm2835-sys-timer", 0x20);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
- sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+
+ for (size_t i = 0; i < ARRAY_SIZE(s->tmr); i++) {
+ s->tmr[i].id = i;
+ s->tmr[i].state = s;
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->tmr[i].irq);
+ timer_init_us(&s->tmr[i].timer, QEMU_CLOCK_VIRTUAL,
+ bcm2835_systmr_timer_expire, &s->tmr[i]);
+ }
}
static const VMStateDescription bcm2835_systmr_vmstate = {
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index b996d99200..7a4326d956 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -77,9 +77,11 @@ nrf51_timer_write(uint8_t timer_id, uint64_t addr, uint32_t value, unsigned size
nrf51_timer_set_count(uint8_t timer_id, uint8_t counter_id, uint32_t value) "timer %u counter %u count 0x%" PRIx32
# bcm2835_systmr.c
-bcm2835_systmr_irq(bool enable) "timer irq state %u"
+bcm2835_systmr_timer_expired(unsigned id) "timer #%u expired"
+bcm2835_systmr_irq_ack(unsigned id) "timer #%u acked"
bcm2835_systmr_read(uint64_t offset, uint64_t data) "timer read: offset 0x%" PRIx64 " data 0x%" PRIx64
-bcm2835_systmr_write(uint64_t offset, uint64_t data) "timer write: offset 0x%" PRIx64 " data 0x%" PRIx64
+bcm2835_systmr_write(uint64_t offset, uint32_t data) "timer write: offset 0x%" PRIx64 " data 0x%" PRIx32
+bcm2835_systmr_run(unsigned id, uint64_t delay_us) "timer #%u expiring in %"PRIu64" us"
# avr_timer16.c
avr_timer16_read(uint8_t addr, uint8_t value) "timer16 read addr:%u value:%u"