diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2021-08-12 10:33:55 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2021-09-01 11:08:20 +0100 |
commit | d18fdd69d0e417f15a388bd7a2e3d6bd2d3672a5 (patch) | |
tree | 056d8691429985659ec4399d75756b7d15a6517e | |
parent | f3eb7557284db7d9eba8843c5705b4dc90dc6fd3 (diff) |
hw/timer/stellaris-gptm: Use Clock input instead of system_clock_scale
The stellaris-gptm timer currently uses system_clock_scale for one of
its timer modes where the timer runs at the CPU clock rate. Make it
use a Clock input instead.
We don't try to make the timer handle changes in the clock frequency
while the downcounter is running. This is not a change in behaviour
from the previous system_clock_scale implementation -- we will pick
up the new frequency only when the downcounter hits zero. Handling
dynamic clock changes when the counter is running would require state
that the current gptm implementation doesn't have.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Damien Hedde <damien.hedde@greensocs.com>
Message-id: 20210812093356.1946-25-peter.maydell@linaro.org
-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 |