From: YannickV <[email protected]> Add a clock input to a9gtimer, arm_mptimer and a9mpcore. a9mpcore distributes the clock to the other timers. If no clock is connected, falls back to 100 MHz for backward compatibility with existing machine types.
Frequency calculation now uses the input clock and prescaler values according to ARM Cortex-A9 MPCore Technical Reference Manual. More information about the A9MPCore timers can be found here: https://developer.arm.com/documentation/ddi0407/c/timers-and-watchdog-registers?lang=en Signed-off-by: YannickV <[email protected]> --- hw/arm/xilinx_zynq.c | 1 + hw/cpu/a9mpcore.c | 12 ++++++++++++ hw/timer/a9gtimer.c | 18 +++++++++++++++--- hw/timer/arm_mptimer.c | 20 +++++++++++++++----- include/hw/cpu/a9mpcore.h | 1 + include/hw/timer/a9gtimer.h | 1 + include/hw/timer/arm_mptimer.h | 2 ++ 7 files changed, 47 insertions(+), 8 deletions(-) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 52c9e41a76..5aac215c27 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -271,6 +271,7 @@ static void zynq_init(MachineState *machine) dev = qdev_new(TYPE_A9MPCORE_PRIV); qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL); + qdev_connect_clock_in(dev, "clk", qdev_get_clock_out(slcr, "cpu_3x2x")); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index 2127f95578..5ffdc9277b 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -14,6 +14,8 @@ #include "hw/cpu/a9mpcore.h" #include "hw/core/irq.h" #include "hw/core/qdev-properties.h" +#include "hw/core/qdev-clock.h" +#include "hw/core/clock.h" #include "hw/core/cpu.h" #include "target/arm/cpu-qom.h" @@ -42,6 +44,13 @@ static void a9mp_priv_initfn(Object *obj) object_initialize_child(obj, "mptimer", &s->mptimer, TYPE_ARM_MPTIMER); object_initialize_child(obj, "wdt", &s->wdt, TYPE_ARM_MPTIMER); + + /* + * Register a clock input. If no clock is connected the + * frequency falls back to 100 MHz for backward compatibility. + */ + s->clk = qdev_init_clock_in(DEVICE(obj), "clk", NULL, NULL, 0); + clock_set_ns(s->clk, 10); } static void a9mp_priv_realize(DeviceState *dev, Error **errp) @@ -103,6 +112,7 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) gtimerdev = DEVICE(&s->gtimer); qdev_prop_set_uint32(gtimerdev, "num-cpu", s->num_cpu); + qdev_connect_clock_in(gtimerdev, "clk", s->clk); if (!sysbus_realize(SYS_BUS_DEVICE(&s->gtimer), errp)) { return; } @@ -110,6 +120,7 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) mptimerdev = DEVICE(&s->mptimer); qdev_prop_set_uint32(mptimerdev, "num-cpu", s->num_cpu); + qdev_connect_clock_in(mptimerdev, "clk", s->clk); if (!sysbus_realize(SYS_BUS_DEVICE(&s->mptimer), errp)) { return; } @@ -117,6 +128,7 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) wdtdev = DEVICE(&s->wdt); qdev_prop_set_uint32(wdtdev, "num-cpu", s->num_cpu); + qdev_connect_clock_in(wdtdev, "clk", s->clk); if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt), errp)) { return; } diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index 3a086915c8..c9ffb4e2c1 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -24,6 +24,9 @@ #include "hw/core/hw-error.h" #include "hw/core/irq.h" #include "hw/core/qdev-properties.h" +#include "hw/core/qdev-properties-system.h" +#include "hw/core/qdev-clock.h" +#include "hw/core/clock.h" #include "hw/timer/a9gtimer.h" #include "migration/vmstate.h" #include "qapi/error.h" @@ -63,9 +66,10 @@ static inline int a9_gtimer_get_current_cpu(A9GTimerState *s) static inline uint64_t a9_gtimer_get_conv(A9GTimerState *s) { uint64_t prescale = extract32(s->control, R_CONTROL_PRESCALER_SHIFT, - R_CONTROL_PRESCALER_LEN); - - return (prescale + 1) * 10; + R_CONTROL_PRESCALER_LEN) + 1; + uint64_t clk_hz = clock_get_hz(s->clk); + assert(clk_hz != 0); + return muldiv64(prescale, NANOSECONDS_PER_SECOND, clk_hz); } static A9GTimerUpdate a9_gtimer_get_update(A9GTimerState *s) @@ -295,6 +299,13 @@ static void a9_gtimer_reset(DeviceState *dev) a9_gtimer_update(s, false); } +static void a9_gtimer_init(Object *obj) +{ + A9GTimerState *s = A9_GTIMER(obj); + + s->clk = qdev_init_clock_in(DEVICE(obj), "clk", NULL, NULL, 0); +} + static void a9_gtimer_realize(DeviceState *dev, Error **errp) { A9GTimerState *s = A9_GTIMER(dev); @@ -391,6 +402,7 @@ static const TypeInfo a9_gtimer_info = { .name = TYPE_A9_GTIMER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(A9GTimerState), + .instance_init = a9_gtimer_init, .class_init = a9_gtimer_class_init, }; diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index 88158144b2..eb40241ef6 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -24,9 +24,13 @@ #include "hw/core/irq.h" #include "hw/core/ptimer.h" #include "hw/core/qdev-properties.h" +#include "hw/core/qdev-properties-system.h" +#include "hw/core/qdev-clock.h" +#include "hw/core/clock.h" #include "hw/timer/arm_mptimer.h" #include "migration/vmstate.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qemu/module.h" #include "hw/core/cpu.h" @@ -58,10 +62,12 @@ static inline void timerblock_update_irq(TimerBlock *tb) qemu_set_irq(tb->irq, tb->status && (tb->control & 4)); } -/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ -static inline uint32_t timerblock_scale(uint32_t control) +static inline uint32_t timerblock_scale(TimerBlock *tb, uint32_t control) { - return (((control >> 8) & 0xff) + 1) * 10; + uint64_t prescale = (((control >> 8) & 0xff) + 1); + uint64_t clk_hz = clock_get_hz(tb->clk); + assert(clk_hz != 0); + return muldiv64(prescale, NANOSECONDS_PER_SECOND, clk_hz); } /* Must be called within a ptimer transaction block */ @@ -155,7 +161,7 @@ static void timerblock_write(void *opaque, hwaddr addr, ptimer_stop(tb->timer); } if ((control & 0xff00) != (value & 0xff00)) { - ptimer_set_period(tb->timer, timerblock_scale(value)); + ptimer_set_period(tb->timer, timerblock_scale(tb, value)); } if (value & 1) { uint64_t count = ptimer_get_count(tb->timer); @@ -222,7 +228,8 @@ static void timerblock_reset(TimerBlock *tb) ptimer_transaction_begin(tb->timer); ptimer_stop(tb->timer); ptimer_set_limit(tb->timer, 0, 1); - ptimer_set_period(tb->timer, timerblock_scale(0)); + ptimer_set_period(tb->timer, + timerblock_scale(tb, 0)); ptimer_transaction_commit(tb->timer); } } @@ -244,6 +251,8 @@ static void arm_mptimer_init(Object *obj) memory_region_init_io(&s->iomem, obj, &arm_thistimer_ops, s, "arm_mptimer_timer", 0x20); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); + + s->clk = qdev_init_clock_in(DEVICE(obj), "clk", NULL, NULL, 0); } static void arm_mptimer_realize(DeviceState *dev, Error **errp) @@ -269,6 +278,7 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp) */ for (i = 0; i < s->num_cpu; i++) { TimerBlock *tb = &s->timerblock[i]; + tb->clk = s->clk; tb->timer = ptimer_init(timerblock_tick, tb, PTIMER_POLICY); sysbus_init_irq(sbd, &tb->irq); memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb, diff --git a/include/hw/cpu/a9mpcore.h b/include/hw/cpu/a9mpcore.h index 6076599024..11b4b7cfe9 100644 --- a/include/hw/cpu/a9mpcore.h +++ b/include/hw/cpu/a9mpcore.h @@ -34,6 +34,7 @@ struct A9MPPrivState { A9GTimerState gtimer; ARMMPTimerState mptimer; ARMMPTimerState wdt; + Clock *clk; }; #endif diff --git a/include/hw/timer/a9gtimer.h b/include/hw/timer/a9gtimer.h index 5c9b048b95..ef198a969e 100644 --- a/include/hw/timer/a9gtimer.h +++ b/include/hw/timer/a9gtimer.h @@ -87,6 +87,7 @@ struct A9GTimerState { uint32_t control; /* only non per cpu banked bits valid */ A9GTimerPerCPU per_cpu[A9_GTIMER_MAX_CPUS]; + Clock *clk; }; typedef struct A9GTimerUpdate { diff --git a/include/hw/timer/arm_mptimer.h b/include/hw/timer/arm_mptimer.h index 4c6f569631..723f9b9319 100644 --- a/include/hw/timer/arm_mptimer.h +++ b/include/hw/timer/arm_mptimer.h @@ -33,6 +33,7 @@ typedef struct { struct ptimer_state *timer; qemu_irq irq; MemoryRegion iomem; + Clock *clk; } TimerBlock; #define TYPE_ARM_MPTIMER "arm_mptimer" @@ -44,6 +45,7 @@ struct ARMMPTimerState { /*< public >*/ uint32_t num_cpu; + Clock *clk; TimerBlock timerblock[ARM_MPTIMER_MAX_CPUS]; MemoryRegion iomem; }; -- 2.47.3
