qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials
@ 2013-11-28  6:19 Peter Crosthwaite
  2013-11-28  6:19 ` [Qemu-devel] [PATCH arm-devs v3 1/4] cpu/a9mpcore: rename timerbusdev variable Peter Crosthwaite
                   ` (4 more replies)
  0 siblings, 5 replies; 10+ messages in thread
From: Peter Crosthwaite @ 2013-11-28  6:19 UTC (permalink / raw)
  To: qemu-devel, peter.maydell; +Cc: peter.crosthwaite, afaerber

Hi Peter,

Another spin of the ARM MPCore global timer work. Patches 1 & 2 are some
trivial cleanup to MPCore I did along the way.

Regards,
Peter


François LEGAL (1):
  cpu/a9mpcore: Add Global Timer

Peter Crosthwaite (3):
  cpu/a9mpcore: rename timerbusdev variable
  cpu/a9mpcore: reorder operations/declarations
  hw/timer: Introduce ARM A9 Global Timer.

 hw/cpu/a9mpcore.c           |  44 ++++--
 hw/timer/Makefile.objs      |   2 +-
 hw/timer/a9gtimer.c         | 369 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/cpu/a9mpcore.h   |   4 +-
 include/hw/timer/a9gtimer.h |  97 ++++++++++++
 5 files changed, 500 insertions(+), 16 deletions(-)
 create mode 100644 hw/timer/a9gtimer.c
 create mode 100644 include/hw/timer/a9gtimer.h

-- 
1.8.4.4

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH arm-devs v3 1/4] cpu/a9mpcore: rename timerbusdev variable
  2013-11-28  6:19 [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials Peter Crosthwaite
@ 2013-11-28  6:19 ` Peter Crosthwaite
  2013-11-28 18:30   ` Peter Maydell
  2013-11-28  6:20 ` [Qemu-devel] [PATCH arm-devs v3 2/4] cpu/a9mpcore: reorder operations/declarations Peter Crosthwaite
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 10+ messages in thread
From: Peter Crosthwaite @ 2013-11-28  6:19 UTC (permalink / raw)
  To: qemu-devel, peter.maydell; +Cc: peter.crosthwaite, afaerber

Rename this variable for consistency with the above defined mptimerdev
variable.

Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
---

 hw/cpu/a9mpcore.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c
index 918a7d1..1123101 100644
--- a/hw/cpu/a9mpcore.c
+++ b/hw/cpu/a9mpcore.c
@@ -42,7 +42,7 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
     A9MPPrivState *s = A9MPCORE_PRIV(dev);
     DeviceState *gicdev, *scudev, *mptimerdev, *wdtdev;
-    SysBusDevice *timerbusdev, *wdtbusdev, *gicbusdev, *scubusdev;
+    SysBusDevice *mptimerbusdev, *wdtbusdev, *gicbusdev, *scubusdev;
     Error *err = NULL;
     int i;
 
@@ -78,7 +78,7 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
         error_propagate(errp, err);
         return;
     }
-    timerbusdev = SYS_BUS_DEVICE(&s->mptimer);
+    mptimerbusdev = SYS_BUS_DEVICE(&s->mptimer);
 
     wdtdev = DEVICE(&s->wdt);
     qdev_prop_set_uint32(wdtdev, "num-cpu", s->num_cpu);
@@ -109,7 +109,7 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
      * memory region, not the "timer/watchdog for core X" ones 11MPcore has.
      */
     memory_region_add_subregion(&s->container, 0x600,
-                                sysbus_mmio_get_region(timerbusdev, 0));
+                                sysbus_mmio_get_region(mptimerbusdev, 0));
     memory_region_add_subregion(&s->container, 0x620,
                                 sysbus_mmio_get_region(wdtbusdev, 0));
     memory_region_add_subregion(&s->container, 0x1000,
@@ -120,7 +120,7 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
      */
     for (i = 0; i < s->num_cpu; i++) {
         int ppibase = (s->num_irq - 32) + i * 32;
-        sysbus_connect_irq(timerbusdev, i,
+        sysbus_connect_irq(mptimerbusdev, i,
                            qdev_get_gpio_in(gicdev, ppibase + 29));
         sysbus_connect_irq(wdtbusdev, i,
                            qdev_get_gpio_in(gicdev, ppibase + 30));
-- 
1.8.4.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH arm-devs v3 2/4] cpu/a9mpcore: reorder operations/declarations
  2013-11-28  6:19 [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials Peter Crosthwaite
  2013-11-28  6:19 ` [Qemu-devel] [PATCH arm-devs v3 1/4] cpu/a9mpcore: rename timerbusdev variable Peter Crosthwaite
@ 2013-11-28  6:20 ` Peter Crosthwaite
  2013-11-28 18:30   ` Peter Maydell
  2013-11-28  6:20 ` [Qemu-devel] [PATCH arm-devs v3 3/4] hw/timer: Introduce ARM A9 Global Timer Peter Crosthwaite
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 10+ messages in thread
From: Peter Crosthwaite @ 2013-11-28  6:20 UTC (permalink / raw)
  To: qemu-devel, peter.maydell; +Cc: peter.crosthwaite, afaerber

To make it consistent for easier code reading. The order in which
variables are defined and functions are called is set to match the
address map ordering.

The new  consistent order of doing stuff is:

SCU -> GIC -> MPTimer -> WDT.

0 functional change.

Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
---

 hw/cpu/a9mpcore.c         | 28 ++++++++++++++--------------
 include/hw/cpu/a9mpcore.h |  2 +-
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c
index 1123101..a38464b 100644
--- a/hw/cpu/a9mpcore.c
+++ b/hw/cpu/a9mpcore.c
@@ -24,12 +24,12 @@ static void a9mp_priv_initfn(Object *obj)
     memory_region_init(&s->container, obj, "a9mp-priv-container", 0x2000);
     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->container);
 
-    object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC);
-    qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default());
-
     object_initialize(&s->scu, sizeof(s->scu), TYPE_A9_SCU);
     qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default());
 
+    object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC);
+    qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default());
+
     object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER);
     qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default());
 
@@ -41,11 +41,20 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
 {
     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
     A9MPPrivState *s = A9MPCORE_PRIV(dev);
-    DeviceState *gicdev, *scudev, *mptimerdev, *wdtdev;
-    SysBusDevice *mptimerbusdev, *wdtbusdev, *gicbusdev, *scubusdev;
+    DeviceState *scudev, *gicdev, *mptimerdev, *wdtdev;
+    SysBusDevice *scubusdev, *gicbusdev, *mptimerbusdev, *wdtbusdev;
     Error *err = NULL;
     int i;
 
+    scudev = DEVICE(&s->scu);
+    qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu);
+    object_property_set_bool(OBJECT(&s->scu), true, "realized", &err);
+    if (err != NULL) {
+        error_propagate(errp, err);
+        return;
+    }
+    scubusdev = SYS_BUS_DEVICE(&s->scu);
+
     gicdev = DEVICE(&s->gic);
     qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu);
     qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq);
@@ -62,15 +71,6 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
     /* Pass through inbound GPIO lines to the GIC */
     qdev_init_gpio_in(dev, a9mp_priv_set_irq, s->num_irq - 32);
 
-    scudev = DEVICE(&s->scu);
-    qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu);
-    object_property_set_bool(OBJECT(&s->scu), true, "realized", &err);
-    if (err != NULL) {
-        error_propagate(errp, err);
-        return;
-    }
-    scubusdev = SYS_BUS_DEVICE(&s->scu);
-
     mptimerdev = DEVICE(&s->mptimer);
     qdev_prop_set_uint32(mptimerdev, "num-cpu", s->num_cpu);
     object_property_set_bool(OBJECT(&s->mptimer), true, "realized", &err);
diff --git a/include/hw/cpu/a9mpcore.h b/include/hw/cpu/a9mpcore.h
index 010489b..8eece07 100644
--- a/include/hw/cpu/a9mpcore.h
+++ b/include/hw/cpu/a9mpcore.h
@@ -28,8 +28,8 @@ typedef struct A9MPPrivState {
     MemoryRegion container;
     uint32_t num_irq;
 
-    GICState gic;
     A9SCUState scu;
+    GICState gic;
     ARMMPTimerState mptimer;
     ARMMPTimerState wdt;
 } A9MPPrivState;
-- 
1.8.4.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH arm-devs v3 3/4] hw/timer: Introduce ARM A9 Global Timer.
  2013-11-28  6:19 [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials Peter Crosthwaite
  2013-11-28  6:19 ` [Qemu-devel] [PATCH arm-devs v3 1/4] cpu/a9mpcore: rename timerbusdev variable Peter Crosthwaite
  2013-11-28  6:20 ` [Qemu-devel] [PATCH arm-devs v3 2/4] cpu/a9mpcore: reorder operations/declarations Peter Crosthwaite
@ 2013-11-28  6:20 ` Peter Crosthwaite
  2013-11-28  6:21 ` [Qemu-devel] [PATCH arm-devs v3 4/4] cpu/a9mpcore: Add " Peter Crosthwaite
  2013-11-28 18:30 ` [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials Peter Maydell
  4 siblings, 0 replies; 10+ messages in thread
From: Peter Crosthwaite @ 2013-11-28  6:20 UTC (permalink / raw)
  To: qemu-devel, peter.maydell; +Cc: peter.crosthwaite, afaerber

The ARM A9 MPCore has a timer that is global to all CPUs in the mpcore.
The timer is shared but each CPU has a private independent comparator
and interrupt.

Based on version contributed by Francois LEGAL.

Signed-off-by: François LEGAL <devel@thom.fr.eu.org>
[PC changes:
 * New commit message
 * Re-implemented as single timer model
 * Fixed backwards counting issue in polled mode
 * completed VMSD fields
 * macroified magic numbers (and headerified reg definitions)
 * split of as device-model-only patch
 * use bitops for 64 bit register access
 * Fixed auto increment mode to check condition properly
 * general cleanup (names/style etc).
]
Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
---
Changed since v2 (PMM - review):
Refactored for container embedding
Made frequency scaler consistent with mptimer
Fixed missing VMSD ref_counter field
Fixed missing gtb->inc reset
Changed Memory region names
Changed since v1:
Added /*< private >*/ to struct definition.
Pulled register definitions out into a header (AF review)
SOB Francois LEGAL with PC changes indicated.

 hw/timer/Makefile.objs      |   2 +-
 hw/timer/a9gtimer.c         | 369 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/timer/a9gtimer.h |  97 ++++++++++++
 3 files changed, 467 insertions(+), 1 deletion(-)
 create mode 100644 hw/timer/a9gtimer.c
 create mode 100644 include/hw/timer/a9gtimer.h

diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index eca5905..42d96df 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -1,5 +1,5 @@
 common-obj-$(CONFIG_ARM_TIMER) += arm_timer.o
-common-obj-$(CONFIG_ARM_MPTIMER) += arm_mptimer.o
+common-obj-$(CONFIG_ARM_MPTIMER) += arm_mptimer.o a9gtimer.o
 common-obj-$(CONFIG_CADENCE) += cadence_ttc.o
 common-obj-$(CONFIG_DS1338) += ds1338.o
 common-obj-$(CONFIG_HPET) += hpet.o
diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c
new file mode 100644
index 0000000..0f08c67
--- /dev/null
+++ b/hw/timer/a9gtimer.c
@@ -0,0 +1,369 @@
+/*
+ * Global peripheral timer block for ARM A9MP
+ *
+ * (C) 2013 Xilinx Inc.
+ *
+ * Written by François LEGAL
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/timer/a9gtimer.h"
+#include "qemu/timer.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+
+#ifndef A9_GTIMER_ERR_DEBUG
+#define A9_GTIMER_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT_L(level, ...) do { \
+    if (A9_GTIMER_ERR_DEBUG > (level)) { \
+        fprintf(stderr,  ": %s: ", __func__); \
+        fprintf(stderr, ## __VA_ARGS__); \
+    } \
+} while (0);
+
+#define DB_PRINT(...) DB_PRINT_L(0, ## __VA_ARGS__)
+
+static inline int a9_gtimer_get_current_cpu(A9GTimerState *s)
+{
+    if (current_cpu->cpu_index >= s->num_cpu) {
+        hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
+                 s->num_cpu, current_cpu->cpu_index);
+    }
+    return current_cpu->cpu_index;
+}
+
+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;
+}
+
+static A9GTimerUpdate a9_gtimer_get_update(A9GTimerState *s)
+{
+    A9GTimerUpdate ret;
+
+    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);
+    return ret;
+}
+
+static void a9_gtimer_update(A9GTimerState *s, bool sync)
+{
+
+    A9GTimerUpdate update = a9_gtimer_get_update(s);
+    int i;
+    int64_t next_cdiff = 0;
+
+    for (i = 0; i < s->num_cpu; ++i) {
+        A9GTimerPerCPU *gtb = &s->per_cpu[i];
+        int64_t cdiff = 0;
+
+        if ((s->control & R_CONTROL_TIMER_ENABLE) &&
+                (gtb->control & R_CONTROL_COMP_ENABLE)) {
+            /* R2p0+, where the compare function is >= */
+            while (gtb->compare < update.new) {
+                DB_PRINT("Compare event happened for CPU %d\n", i);
+                gtb->status = 1;
+                if (gtb->control & R_CONTROL_AUTO_INCREMENT) {
+                    DB_PRINT("Auto incrementing timer compare by %" PRId32 "\n",
+                             gtb->inc);
+                    gtb->compare += gtb->inc;
+                } else {
+                    break;
+                }
+            }
+            cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1;
+            if (cdiff > 0 && (cdiff < next_cdiff || !next_cdiff)) {
+                next_cdiff = cdiff;
+            }
+        }
+
+        qemu_set_irq(gtb->irq,
+                     gtb->status && (gtb->control & R_CONTROL_IRQ_ENABLE));
+    }
+
+    timer_del(s->timer);
+    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));
+    }
+
+    if (s->control & R_CONTROL_TIMER_ENABLE) {
+        s->counter = update.new;
+    }
+
+    if (sync) {
+        s->cpu_ref_time = update.now;
+        s->ref_counter = s->counter;
+    }
+}
+
+static void a9_gtimer_update_no_sync(void *opaque)
+{
+    A9GTimerState *s = A9_GTIMER(opaque);
+
+    return a9_gtimer_update(s, false);
+}
+
+static uint64_t a9_gtimer_read(void *opaque, hwaddr addr, unsigned size)
+{
+    A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque;
+    A9GTimerState *s = gtb->parent;
+    A9GTimerUpdate update;
+    uint64_t ret = 0;
+    int shift = 0;
+
+    switch (addr) {
+    case R_COUNTER_HI:
+        shift = 32;
+        /* fallthrough */
+    case R_COUNTER_LO:
+        update = a9_gtimer_get_update(s);
+        ret = extract64(update.new, shift, 32);
+        break;
+    case R_CONTROL:
+        ret = s->control | gtb->control;
+        break;
+    case R_INTERRUPT_STATUS:
+        ret = gtb->status;
+        break;
+    case R_COMPARATOR_HI:
+        shift = 32;
+        /* fallthrough */
+    case R_COMPARATOR_LO:
+        ret = extract64(gtb->compare, shift, 32);
+        break;
+    case R_AUTO_INCREMENT:
+        ret =  gtb->inc;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "bad a9gtimer register: %x\n",
+                      (unsigned)addr);
+        return 0;
+    }
+
+    DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, ret);
+    return ret;
+}
+
+static void a9_gtimer_write(void *opaque, hwaddr addr, uint64_t value,
+                            unsigned size)
+{
+    A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque;
+    A9GTimerState *s = gtb->parent;
+    int shift = 0;
+
+    DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, value);
+
+    switch (addr) {
+    case R_COUNTER_HI:
+        shift = 32;
+        /* fallthrough */
+    case R_COUNTER_LO:
+        /*
+         * Keep it simple - ARM docco explicitly says to disable timer before
+         * modding it, so dont bother trying to do all the difficult on the fly
+         * timer modifications - (if they even work in real hardware??).
+         */
+        if (s->control & R_CONTROL_TIMER_ENABLE) {
+            qemu_log_mask(LOG_GUEST_ERROR, "Cannot mod running ARM gtimer\n");
+            return;
+        }
+        s->counter = deposit64(s->counter, shift, 32, value);
+        return;
+    case R_CONTROL:
+        a9_gtimer_update(s, (value ^ s->control) & R_CONTROL_NEEDS_SYNC);
+        gtb->control = value & R_CONTROL_BANKED;
+        s->control = value & ~R_CONTROL_BANKED;
+        break;
+    case R_INTERRUPT_STATUS:
+        a9_gtimer_update(s, false);
+        gtb->status &= ~value;
+        break;
+    case R_COMPARATOR_HI:
+        shift = 32;
+        /* fallthrough */
+    case R_COMPARATOR_LO:
+        a9_gtimer_update(s, false);
+        gtb->compare = deposit64(gtb->compare, shift, 32, value);
+        break;
+    case R_AUTO_INCREMENT:
+        gtb->inc = value;
+        return;
+    default:
+        return;
+    }
+
+    a9_gtimer_update(s, false);
+}
+
+/* Wrapper functions to implement the "read global timer for
+ * the current CPU" memory regions.
+ */
+static uint64_t a9_gtimer_this_read(void *opaque, hwaddr addr,
+                                    unsigned size)
+{
+    A9GTimerState *s = A9_GTIMER(opaque);
+    int id = a9_gtimer_get_current_cpu(s);
+
+    /* no \n so concatenates with message from read fn */
+    DB_PRINT("CPU:%d:", id);
+
+    return a9_gtimer_read(&s->per_cpu[id], addr, size);
+}
+
+static void a9_gtimer_this_write(void *opaque, hwaddr addr,
+                                 uint64_t value, unsigned size)
+{
+    A9GTimerState *s = A9_GTIMER(opaque);
+    int id = a9_gtimer_get_current_cpu(s);
+
+    /* no \n so concatentates with message from write fn */
+    DB_PRINT("CPU:%d:", id);
+
+    a9_gtimer_write(&s->per_cpu[id], addr, value, size);
+}
+
+static const MemoryRegionOps a9_gtimer_this_ops = {
+    .read = a9_gtimer_this_read,
+    .write = a9_gtimer_this_write,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps a9_gtimer_ops = {
+    .read = a9_gtimer_read,
+    .write = a9_gtimer_write,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void a9_gtimer_reset(DeviceState *dev)
+{
+    A9GTimerState *s = A9_GTIMER(dev);
+    int i;
+
+    s->counter = 0;
+    s->control = 0;
+
+    for (i = 0; i < s->num_cpu; i++) {
+        A9GTimerPerCPU *gtb = &s->per_cpu[i];
+
+        gtb->control = 0;
+        gtb->status = 0;
+        gtb->compare = 0;
+        gtb->inc = 0;
+    }
+    a9_gtimer_update(s, false);
+}
+
+static void a9_gtimer_realize(DeviceState *dev, Error **errp)
+{
+    A9GTimerState *s = A9_GTIMER(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    int i;
+
+    if (s->num_cpu < 1 || s->num_cpu > A9_GTIMER_MAX_CPUS) {
+        error_setg(errp, "%s: num-cpu must be between 1 and %d\n",
+                   __func__, A9_GTIMER_MAX_CPUS);
+    }
+
+    memory_region_init_io(&s->iomem, OBJECT(dev), &a9_gtimer_this_ops, s,
+                          "a9gtimer shared", 0x20);
+    sysbus_init_mmio(sbd, &s->iomem);
+    s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, a9_gtimer_update_no_sync, s);
+
+    for (i = 0; i < s->num_cpu; i++) {
+        A9GTimerPerCPU *gtb = &s->per_cpu[i];
+
+        gtb->parent = s;
+        sysbus_init_irq(sbd, &gtb->irq);
+        memory_region_init_io(&gtb->iomem, OBJECT(dev), &a9_gtimer_ops, gtb,
+                              "a9gtimer per cpu", 0x20);
+        sysbus_init_mmio(sbd, &gtb->iomem);
+    }
+}
+
+static const VMStateDescription vmstate_a9_gtimer_per_cpu = {
+    .name = "arm.cortex-a9-global-timer.percpu",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(control, A9GTimerPerCPU),
+        VMSTATE_UINT64(compare, A9GTimerPerCPU),
+        VMSTATE_UINT32(status, A9GTimerPerCPU),
+        VMSTATE_UINT32(inc, A9GTimerPerCPU),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_a9_gtimer = {
+    .name = "arm.cortex-a9-global-timer",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_TIMER(timer, A9GTimerState),
+        VMSTATE_UINT64(counter, A9GTimerState),
+        VMSTATE_UINT64(ref_counter, A9GTimerState),
+        VMSTATE_UINT64(cpu_ref_time, A9GTimerState),
+        VMSTATE_STRUCT_VARRAY_UINT32(per_cpu, A9GTimerState, num_cpu,
+                                     1, vmstate_a9_gtimer_per_cpu,
+                                     A9GTimerPerCPU),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property a9_gtimer_properties[] = {
+    DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void a9_gtimer_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = a9_gtimer_realize;
+    dc->vmsd = &vmstate_a9_gtimer;
+    dc->reset = a9_gtimer_reset;
+    dc->no_user = 1;
+    dc->props = a9_gtimer_properties;
+}
+
+static const TypeInfo a9_gtimer_info = {
+    .name          = TYPE_A9_GTIMER,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(A9GTimerState),
+    .class_init    = a9_gtimer_class_init,
+};
+
+static void a9_gtimer_register_types(void)
+{
+    type_register_static(&a9_gtimer_info);
+}
+
+type_init(a9_gtimer_register_types)
diff --git a/include/hw/timer/a9gtimer.h b/include/hw/timer/a9gtimer.h
new file mode 100644
index 0000000..b88c02a
--- /dev/null
+++ b/include/hw/timer/a9gtimer.h
@@ -0,0 +1,97 @@
+/*
+ * Global peripheral timer block for ARM A9MP
+ *
+ * (C) 2013 Xilinx Inc.
+ *
+ * Written by François LEGAL
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_TIMER_A9_GTIMER_H_H
+#define HW_TIMER_A9_GTIMER_H_H
+
+#include "hw/sysbus.h"
+
+#define A9_GTIMER_MAX_CPUS 4
+
+#define TYPE_A9_GTIMER "arm.cortex-a9-global-timer"
+#define A9_GTIMER(obj) OBJECT_CHECK(A9GTimerState, (obj), TYPE_A9_GTIMER)
+
+#define R_COUNTER_LO                0x00
+#define R_COUNTER_HI                0x04
+
+#define R_CONTROL                   0x08
+#define R_CONTROL_TIMER_ENABLE      (1 << 0)
+#define R_CONTROL_COMP_ENABLE       (1 << 1)
+#define R_CONTROL_IRQ_ENABLE        (1 << 2)
+#define R_CONTROL_AUTO_INCREMENT    (1 << 2)
+#define R_CONTROL_PRESCALER_SHIFT   8
+#define R_CONTROL_PRESCALER_LEN     8
+#define R_CONTROL_PRESCALER_MASK    (((1 << R_CONTROL_PRESCALER_LEN) - 1) << \
+                                     R_CONTROL_PRESCALER_SHIFT)
+
+#define R_CONTROL_BANKED            (R_CONTROL_COMP_ENABLE | \
+                                     R_CONTROL_IRQ_ENABLE | \
+                                     R_CONTROL_AUTO_INCREMENT)
+#define R_CONTROL_NEEDS_SYNC        (R_CONTROL_TIMER_ENABLE | \
+                                     R_CONTROL_PRESCALER_MASK)
+
+#define R_INTERRUPT_STATUS          0x0C
+#define R_COMPARATOR_LO             0x10
+#define R_COMPARATOR_HI             0x14
+#define R_AUTO_INCREMENT            0x18
+
+typedef struct A9GTimerPerCPU A9GTimerPerCPU;
+typedef struct A9GTimerState A9GTimerState;
+
+struct A9GTimerPerCPU {
+    A9GTimerState *parent;
+
+    uint32_t control; /* only per cpu banked bits valid */
+    uint64_t compare;
+    uint32_t status;
+    uint32_t inc;
+
+    MemoryRegion iomem;
+    qemu_irq irq; /* PPI interrupts */
+};
+
+struct A9GTimerState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    MemoryRegion iomem;
+    /* static props */
+    uint32_t num_cpu;
+
+    QEMUTimer *timer;
+
+    uint64_t counter; /* current timer value */
+
+    uint64_t ref_counter;
+    uint64_t cpu_ref_time; /* the cpu time as of last update of ref_counter */
+    uint32_t control; /* only non per cpu banked bits valid */
+
+    A9GTimerPerCPU per_cpu[A9_GTIMER_MAX_CPUS];
+};
+
+typedef struct A9GTimerUpdate {
+    uint64_t now;
+    uint64_t new;
+} A9GTimerUpdate;
+
+#endif /* #ifdef HW_TIMER_A9_GTIMER_H_H */
-- 
1.8.4.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH arm-devs v3 4/4] cpu/a9mpcore: Add Global Timer
  2013-11-28  6:19 [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials Peter Crosthwaite
                   ` (2 preceding siblings ...)
  2013-11-28  6:20 ` [Qemu-devel] [PATCH arm-devs v3 3/4] hw/timer: Introduce ARM A9 Global Timer Peter Crosthwaite
@ 2013-11-28  6:21 ` Peter Crosthwaite
  2013-11-28 18:31   ` Peter Maydell
  2013-11-28 18:30 ` [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials Peter Maydell
  4 siblings, 1 reply; 10+ messages in thread
From: Peter Crosthwaite @ 2013-11-28  6:21 UTC (permalink / raw)
  To: qemu-devel, peter.maydell; +Cc: peter.crosthwaite, afaerber

From: François LEGAL <devel@thom.fr.eu.org>

Add the global timer to A9 MPCore.

Signed-off-by: François LEGAL <devel@thom.fr.eu.org>
[PC Changes:
 * new commit message
 * split off original version as a separate patch
 * Rebased against new mpcore implementation (with struct embedding)
]
Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
---
changed from v2:
Rebased against major mpcore changed.
Changed from v1:
Set authorship to Francois and added SOB.

 hw/cpu/a9mpcore.c         | 26 +++++++++++++++++++++-----
 include/hw/cpu/a9mpcore.h |  2 ++
 2 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c
index a38464b..c09358c 100644
--- a/hw/cpu/a9mpcore.c
+++ b/hw/cpu/a9mpcore.c
@@ -30,6 +30,9 @@ static void a9mp_priv_initfn(Object *obj)
     object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC);
     qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default());
 
+    object_initialize(&s->gtimer, sizeof(s->gtimer), TYPE_A9_GTIMER);
+    qdev_set_parent_bus(DEVICE(&s->gtimer), sysbus_get_default());
+
     object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER);
     qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default());
 
@@ -41,8 +44,9 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
 {
     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
     A9MPPrivState *s = A9MPCORE_PRIV(dev);
-    DeviceState *scudev, *gicdev, *mptimerdev, *wdtdev;
-    SysBusDevice *scubusdev, *gicbusdev, *mptimerbusdev, *wdtbusdev;
+    DeviceState *scudev, *gicdev, *gtimerdev, *mptimerdev, *wdtdev;
+    SysBusDevice *scubusdev, *gicbusdev, *gtimerbusdev, *mptimerbusdev,
+                 *wdtbusdev;
     Error *err = NULL;
     int i;
 
@@ -71,6 +75,15 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
     /* Pass through inbound GPIO lines to the GIC */
     qdev_init_gpio_in(dev, a9mp_priv_set_irq, s->num_irq - 32);
 
+    gtimerdev = DEVICE(&s->gtimer);
+    qdev_prop_set_uint32(gtimerdev, "num-cpu", s->num_cpu);
+    object_property_set_bool(OBJECT(&s->gtimer), true, "realized", &err);
+    if (err != NULL) {
+        error_propagate(errp, err);
+        return;
+    }
+    gtimerbusdev = SYS_BUS_DEVICE(&s->gtimer);
+
     mptimerdev = DEVICE(&s->mptimer);
     qdev_prop_set_uint32(mptimerdev, "num-cpu", s->num_cpu);
     object_property_set_bool(OBJECT(&s->mptimer), true, "realized", &err);
@@ -97,14 +110,14 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
      *  0x0600-0x06ff -- private timers and watchdogs
      *  0x0700-0x0fff -- nothing
      *  0x1000-0x1fff -- GIC Distributor
-     *
-     * We should implement the global timer but don't currently do so.
      */
     memory_region_add_subregion(&s->container, 0,
                                 sysbus_mmio_get_region(scubusdev, 0));
     /* GIC CPU interface */
     memory_region_add_subregion(&s->container, 0x100,
                                 sysbus_mmio_get_region(gicbusdev, 1));
+    memory_region_add_subregion(&s->container, 0x200,
+                                sysbus_mmio_get_region(gtimerbusdev, 0));
     /* Note that the A9 exposes only the "timer/watchdog for this core"
      * memory region, not the "timer/watchdog for core X" ones 11MPcore has.
      */
@@ -116,10 +129,13 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
                                 sysbus_mmio_get_region(gicbusdev, 0));
 
     /* Wire up the interrupt from each watchdog and timer.
-     * For each core the timer is PPI 29 and the watchdog PPI 30.
+     * For each core the global timer is PPI 27, the private
+     * timer is PPI 29 and the watchdog PPI 30.
      */
     for (i = 0; i < s->num_cpu; i++) {
         int ppibase = (s->num_irq - 32) + i * 32;
+        sysbus_connect_irq(gtimerbusdev, i,
+                           qdev_get_gpio_in(gicdev, ppibase + 27));
         sysbus_connect_irq(mptimerbusdev, i,
                            qdev_get_gpio_in(gicdev, ppibase + 29));
         sysbus_connect_irq(wdtbusdev, i,
diff --git a/include/hw/cpu/a9mpcore.h b/include/hw/cpu/a9mpcore.h
index 8eece07..5d67ca2 100644
--- a/include/hw/cpu/a9mpcore.h
+++ b/include/hw/cpu/a9mpcore.h
@@ -14,6 +14,7 @@
 #include "hw/intc/arm_gic.h"
 #include "hw/misc/a9scu.h"
 #include "hw/timer/arm_mptimer.h"
+#include "hw/timer/a9gtimer.h"
 
 #define TYPE_A9MPCORE_PRIV "a9mpcore_priv"
 #define A9MPCORE_PRIV(obj) \
@@ -30,6 +31,7 @@ typedef struct A9MPPrivState {
 
     A9SCUState scu;
     GICState gic;
+    A9GTimerState gtimer;
     ARMMPTimerState mptimer;
     ARMMPTimerState wdt;
 } A9MPPrivState;
-- 
1.8.4.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials
  2013-11-28  6:19 [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials Peter Crosthwaite
                   ` (3 preceding siblings ...)
  2013-11-28  6:21 ` [Qemu-devel] [PATCH arm-devs v3 4/4] cpu/a9mpcore: Add " Peter Crosthwaite
@ 2013-11-28 18:30 ` Peter Maydell
  2013-11-29  0:12   ` Peter Crosthwaite
  4 siblings, 1 reply; 10+ messages in thread
From: Peter Maydell @ 2013-11-28 18:30 UTC (permalink / raw)
  To: Peter Crosthwaite; +Cc: QEMU Developers, Andreas Färber

On 28 November 2013 06:19, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> Hi Peter,
>
> Another spin of the ARM MPCore global timer work. Patches 1 & 2 are some
> trivial cleanup to MPCore I did along the way.

I'm happy with all of these, except that I think the
idea is that each device gets its own CONFIG_ define,
so we should have CONFIG_A9GTIMER or something rather
than piggybacking on CONFIG_ARM_MPTIMER.

I can just fix that nit as I put these into my target-arm
queue, assuming that nobody has any other review comments
that demand a respin for some other reason.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH arm-devs v3 1/4] cpu/a9mpcore: rename timerbusdev variable
  2013-11-28  6:19 ` [Qemu-devel] [PATCH arm-devs v3 1/4] cpu/a9mpcore: rename timerbusdev variable Peter Crosthwaite
@ 2013-11-28 18:30   ` Peter Maydell
  0 siblings, 0 replies; 10+ messages in thread
From: Peter Maydell @ 2013-11-28 18:30 UTC (permalink / raw)
  To: Peter Crosthwaite; +Cc: QEMU Developers, Andreas Färber

On 28 November 2013 06:19, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> Rename this variable for consistency with the above defined mptimerdev
> variable.
>
> Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

-- PMM

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH arm-devs v3 2/4] cpu/a9mpcore: reorder operations/declarations
  2013-11-28  6:20 ` [Qemu-devel] [PATCH arm-devs v3 2/4] cpu/a9mpcore: reorder operations/declarations Peter Crosthwaite
@ 2013-11-28 18:30   ` Peter Maydell
  0 siblings, 0 replies; 10+ messages in thread
From: Peter Maydell @ 2013-11-28 18:30 UTC (permalink / raw)
  To: Peter Crosthwaite; +Cc: QEMU Developers, Andreas Färber

On 28 November 2013 06:20, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> To make it consistent for easier code reading. The order in which
> variables are defined and functions are called is set to match the
> address map ordering.
>
> The new  consistent order of doing stuff is:
>
> SCU -> GIC -> MPTimer -> WDT.
>
> 0 functional change.
>
> Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

-- PMM

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH arm-devs v3 4/4] cpu/a9mpcore: Add Global Timer
  2013-11-28  6:21 ` [Qemu-devel] [PATCH arm-devs v3 4/4] cpu/a9mpcore: Add " Peter Crosthwaite
@ 2013-11-28 18:31   ` Peter Maydell
  0 siblings, 0 replies; 10+ messages in thread
From: Peter Maydell @ 2013-11-28 18:31 UTC (permalink / raw)
  To: Peter Crosthwaite; +Cc: QEMU Developers, Andreas Färber

On 28 November 2013 06:21, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> From: François LEGAL <devel@thom.fr.eu.org>
>
> Add the global timer to A9 MPCore.
>
> Signed-off-by: François LEGAL <devel@thom.fr.eu.org>
> [PC Changes:
>  * new commit message
>  * split off original version as a separate patch
>  * Rebased against new mpcore implementation (with struct embedding)
> ]
> Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

-- PMM

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials
  2013-11-28 18:30 ` [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials Peter Maydell
@ 2013-11-29  0:12   ` Peter Crosthwaite
  0 siblings, 0 replies; 10+ messages in thread
From: Peter Crosthwaite @ 2013-11-29  0:12 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers, Andreas Färber

On Fri, Nov 29, 2013 at 4:30 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 28 November 2013 06:19, Peter Crosthwaite
> <peter.crosthwaite@xilinx.com> wrote:
>> Hi Peter,
>>
>> Another spin of the ARM MPCore global timer work. Patches 1 & 2 are some
>> trivial cleanup to MPCore I did along the way.
>
> I'm happy with all of these, except that I think the
> idea is that each device gets its own CONFIG_ define,
> so we should have CONFIG_A9GTIMER or something rather
> than piggybacking on CONFIG_ARM_MPTIMER.
>

Doh! I saw your comment in V2 but it slipped thorugh the cracks when I
came to actioning your review. Sorry.

> I can just fix that nit as I put these into my target-arm
> queue, assuming that nobody has any other review comments
> that demand a respin for some other reason.
>

Ill let it cool for a few days then I can respin.

Regards,
Peter

> thanks
> -- PMM
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2013-11-29  0:12 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-11-28  6:19 [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials Peter Crosthwaite
2013-11-28  6:19 ` [Qemu-devel] [PATCH arm-devs v3 1/4] cpu/a9mpcore: rename timerbusdev variable Peter Crosthwaite
2013-11-28 18:30   ` Peter Maydell
2013-11-28  6:20 ` [Qemu-devel] [PATCH arm-devs v3 2/4] cpu/a9mpcore: reorder operations/declarations Peter Crosthwaite
2013-11-28 18:30   ` Peter Maydell
2013-11-28  6:20 ` [Qemu-devel] [PATCH arm-devs v3 3/4] hw/timer: Introduce ARM A9 Global Timer Peter Crosthwaite
2013-11-28  6:21 ` [Qemu-devel] [PATCH arm-devs v3 4/4] cpu/a9mpcore: Add " Peter Crosthwaite
2013-11-28 18:31   ` Peter Maydell
2013-11-28 18:30 ` [Qemu-devel] [PATCH arm-devs v3 0/4] A9 global timer + mpcore trivials Peter Maydell
2013-11-29  0:12   ` Peter Crosthwaite

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).