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            | 42 +++++++++++++++++++++++++++++-----
 hw/timer/arm_mptimer.c         | 19 +++++++++++----
 include/hw/cpu/a9mpcore.h      |  1 +
 include/hw/timer/a9gtimer.h    |  1 +
 include/hw/timer/arm_mptimer.h |  2 ++
 7 files changed, 67 insertions(+), 11 deletions(-)

diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 31d5b5e5caa5..f7190118f7ff 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -265,6 +265,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 2127f9557892..5ffdc9277b4a 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 3a086915c817..6c6b7afb398c 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"
@@ -60,12 +63,30 @@ static inline int a9_gtimer_get_current_cpu(A9GTimerState 
*s)
     return current_cpu->cpu_index;
 }
 
-static inline uint64_t a9_gtimer_get_conv(A9GTimerState *s)
+static inline uint32_t a9_gtimer_get_prescale(A9GTimerState *s)
 {
-    uint64_t prescale = extract32(s->control, R_CONTROL_PRESCALER_SHIFT,
-                                  R_CONTROL_PRESCALER_LEN);
+    return extract32(s->control, R_CONTROL_PRESCALER_SHIFT,
+                     R_CONTROL_PRESCALER_LEN) + 1;
+}
 
-    return (prescale + 1) * 10;
+static inline uint64_t a9_gtimer_ns_to_ticks(A9GTimerState *s, uint64_t ns)
+{
+    assert(!(s->control & R_CONTROL_TIMER_ENABLE) || clock_get(s->clk) != 0);
+
+    return clock_ns_to_ticks(s->clk, ns) /
+           a9_gtimer_get_prescale(s);
+}
+
+static inline uint64_t a9_gtimer_ticks_to_ns(A9GTimerState *s, uint64_t ticks)
+{
+    uint32_t prescale = a9_gtimer_get_prescale(s);
+
+    assert(!(s->control & R_CONTROL_TIMER_ENABLE) || clock_get(s->clk) != 0);
+    if (ticks > UINT64_MAX / prescale) {
+        return INT64_MAX;
+    }
+
+    return clock_ticks_to_ns(s->clk, ticks * prescale);
 }
 
 static A9GTimerUpdate a9_gtimer_get_update(A9GTimerState *s)
@@ -74,7 +95,7 @@ static A9GTimerUpdate a9_gtimer_get_update(A9GTimerState *s)
 
     ret.now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     ret.new = s->ref_counter +
-              (ret.now - s->cpu_ref_time) / a9_gtimer_get_conv(s);
+              a9_gtimer_ns_to_ticks(s, ret.now - s->cpu_ref_time);
     return ret;
 }
 
@@ -117,7 +138,8 @@ static void a9_gtimer_update(A9GTimerState *s, bool sync)
     if (next_cdiff) {
         DB_PRINT("scheduling qemu_timer to fire again in %"
                  PRIx64 " cycles\n", next_cdiff);
-        timer_mod(s->timer, update.now + next_cdiff * a9_gtimer_get_conv(s));
+        timer_mod(s->timer, update.now +
+                  a9_gtimer_ticks_to_ns(s, next_cdiff));
     }
 
     if (s->control & R_CONTROL_TIMER_ENABLE) {
@@ -295,6 +317,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 +420,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 88158144b23f..6802ca29c927 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 void timerblock_set_period(TimerBlock *tb, uint32_t control)
 {
-    return (((control >> 8) & 0xff) + 1) * 10;
+    uint32_t prescale = (((control >> 8) & 0xff) + 1);
+
+    assert((control & 1) == 0 || clock_get(tb->clk) != 0);
+    ptimer_set_period_from_clock(tb->timer, tb->clk, prescale);
 }
 
 /* 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));
+            timerblock_set_period(tb, value);
         }
         if (value & 1) {
             uint64_t count = ptimer_get_count(tb->timer);
@@ -222,7 +228,7 @@ 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));
+        timerblock_set_period(tb, 0);
         ptimer_transaction_commit(tb->timer);
     }
 }
@@ -244,6 +250,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 +277,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 60765990247b..11b4b7cfe952 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 5c9b048b9545..ef198a969e75 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 4c6f5696319c..723f9b931997 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


Reply via email to