diff options
-rw-r--r-- | hw/arm/stellaris.c | 12 | ||||
-rw-r--r-- | hw/timer/stellaris-gptm.c | 26 | ||||
-rw-r--r-- | include/hw/timer/stellaris-gptm.h | 3 |
3 files changed, 34 insertions, 7 deletions
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 8c547f146a..3e7d1dabad 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1090,9 +1090,15 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } for (i = 0; i < 4; i++) { if (board->dc2 & (0x10000 << i)) { - dev = sysbus_create_simple(TYPE_STELLARIS_GPTM, - 0x40030000 + i * 0x1000, - qdev_get_gpio_in(nvic, timer_irq[i])); + SysBusDevice *sbd; + + dev = qdev_new(TYPE_STELLARIS_GPTM); + sbd = SYS_BUS_DEVICE(dev); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(ssys_dev, "SYSCLK")); + sysbus_realize_and_unref(sbd, &error_fatal); + sysbus_mmio_map(sbd, 0, 0x40030000 + i * 0x1000); + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(nvic, timer_irq[i])); /* TODO: This is incorrect, but we get away with it because the ADC output is only ever pulsed. */ qdev_connect_gpio_out(dev, 0, adc); diff --git a/hw/timer/stellaris-gptm.c b/hw/timer/stellaris-gptm.c index 7846fe5f84..fd71c79be4 100644 --- a/hw/timer/stellaris-gptm.c +++ b/hw/timer/stellaris-gptm.c @@ -10,9 +10,10 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/timer.h" +#include "qapi/error.h" #include "migration/vmstate.h" +#include "hw/qdev-clock.h" #include "hw/timer/stellaris-gptm.h" -#include "hw/timer/armv7m_systick.h" /* Needed only for system_clock_scale */ static void gptm_update_irq(gptm_state *s) { @@ -39,7 +40,7 @@ static void gptm_reload(gptm_state *s, int n, int reset) /* 32-bit CountDown. */ uint32_t count; count = s->load[0] | (s->load[1] << 16); - tick += (int64_t)count * system_clock_scale; + tick += clock_ticks_to_ns(s->clk, count); } else if (s->config == 1) { /* 32-bit RTC. 1Hz tick. */ tick += NANOSECONDS_PER_SECOND; @@ -247,8 +248,8 @@ static const MemoryRegionOps gptm_ops = { static const VMStateDescription vmstate_stellaris_gptm = { .name = "stellaris_gptm", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT32(config, gptm_state), VMSTATE_UINT32_ARRAY(mode, gptm_state, 2), @@ -263,6 +264,7 @@ static const VMStateDescription vmstate_stellaris_gptm = { VMSTATE_UINT32(rtc, gptm_state), VMSTATE_INT64_ARRAY(tick, gptm_state, 2), VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2), + VMSTATE_CLOCK(clk, gptm_state), VMSTATE_END_OF_LIST() } }; @@ -281,11 +283,27 @@ static void stellaris_gptm_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); s->opaque[0] = s->opaque[1] = s; + + /* + * TODO: in an ideal world we would model the effects of changing + * the input clock frequency while the countdown timer is active. + * The best way to do this would be to convert the device to use + * ptimer instead of hand-rolling its own timer. This would also + * make it easy to implement reading the current count from the + * TAR and TBR registers. + */ + s->clk = qdev_init_clock_in(dev, "clk", NULL, NULL, 0); } static void stellaris_gptm_realize(DeviceState *dev, Error **errp) { gptm_state *s = STELLARIS_GPTM(dev); + + if (!clock_has_source(s->clk)) { + error_setg(errp, "stellaris-gptm: clk must be connected"); + return; + } + s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]); s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]); } diff --git a/include/hw/timer/stellaris-gptm.h b/include/hw/timer/stellaris-gptm.h index b8fa43c94b..fde1fc6f0c 100644 --- a/include/hw/timer/stellaris-gptm.h +++ b/include/hw/timer/stellaris-gptm.h @@ -13,6 +13,7 @@ #include "qom/object.h" #include "hw/sysbus.h" #include "hw/irq.h" +#include "hw/clock.h" #define TYPE_STELLARIS_GPTM "stellaris-gptm" OBJECT_DECLARE_SIMPLE_TYPE(gptm_state, STELLARIS_GPTM) @@ -22,6 +23,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(gptm_state, STELLARIS_GPTM) * + sysbus MMIO region 0: register bank * + sysbus IRQ 0: timer interrupt * + unnamed GPIO output 0: trigger output for the ADC + * + Clock input "clk": the 32-bit countdown timer runs at this speed */ struct gptm_state { SysBusDevice parent_obj; @@ -43,6 +45,7 @@ struct gptm_state { /* The timers have an alternate output used to trigger the ADC. */ qemu_irq trigger; qemu_irq irq; + Clock *clk; }; #endif |