* [Qemu-devel] [PATCH 0/7] ARM: clean up mpcore.c and separate out A9
@ 2011-12-05 16:40 Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 1/7] hw/arm_mptimer.c: Turn ARM MPcore private timers into qdev devices Peter Maydell
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Peter Maydell @ 2011-12-05 16:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Bill Carson, patches
This patch series cleans up hw/mpcore.c by splitting out the various
parts of the 11MPCore private peripheral region so they are separate
memory regions which are all mapped into the right places with a
container. This then allows us to split out the A9 private peripheral
region into its own entirely distinct implementation so we can
actually implement the A9 view of the world rather than the 11MPCore
one. Finally, since mpcore.c is then purely 11MPCore we can just
roll it into arm11mpcore.c.
(This is a kind of preliminary cleanup prior to introducing the A15,
which has a different set of private peripherals again.)
Peter Maydell (7):
hw/arm_mptimer.c: Turn ARM MPcore private timers into qdev devices
hw/arm_gic: Expose GIC CPU interfaces as sysbus memory regions
hw/mpcore.c: Use the GIC memory regions for the CPU interface
hw/realview_gic: Use GIC memory region for the CPU interface
hw/mpcore: Clean up mpcore_priv_read/write as they are now SCU only
hw/a9mpcore.c: Implement A9MP peripherals rather than 11MPcore ones
hw/mpcore.c: Merge with hw/arm11mpcore.c
Makefile.target | 1 +
hw/a9mpcore.c | 189 ++++++++++++++++++++++++++++--
hw/arm11mpcore.c | 130 +++++++++++++++++++++-
hw/arm_gic.c | 75 ++++++++++++-
hw/arm_mptimer.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/mpcore.c | 283 ---------------------------------------------
hw/realview_gic.c | 25 +----
7 files changed, 716 insertions(+), 319 deletions(-)
create mode 100644 hw/arm_mptimer.c
delete mode 100644 hw/mpcore.c
^ permalink raw reply [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 1/7] hw/arm_mptimer.c: Turn ARM MPcore private timers into qdev devices
2011-12-05 16:40 [Qemu-devel] [PATCH 0/7] ARM: clean up mpcore.c and separate out A9 Peter Maydell
@ 2011-12-05 16:40 ` Peter Maydell
2012-01-13 7:18 ` Evgeny Voevodin
2011-12-05 16:40 ` [Qemu-devel] [PATCH 2/7] hw/arm_gic: Expose GIC CPU interfaces as sysbus memory regions Peter Maydell
` (5 subsequent siblings)
6 siblings, 1 reply; 9+ messages in thread
From: Peter Maydell @ 2011-12-05 16:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Bill Carson, patches
Turn the ARM MPcore private timer/watchdog blocks into separate
qdev devices. This will allow us to share them neatly between
11MPCore and A9MPcore.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
Makefile.target | 1 +
hw/arm_mptimer.c | 332 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/mpcore.c | 184 +++++-------------------------
3 files changed, 365 insertions(+), 152 deletions(-)
create mode 100644 hw/arm_mptimer.c
diff --git a/Makefile.target b/Makefile.target
index a111521..39b2e5a 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -344,6 +344,7 @@ obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
obj-arm-y += versatile_pci.o
obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
+obj-arm-y += arm_mptimer.o
obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
obj-arm-y += pl061.o
obj-arm-y += arm-semi.o
diff --git a/hw/arm_mptimer.c b/hw/arm_mptimer.c
new file mode 100644
index 0000000..455a0aa
--- /dev/null
+++ b/hw/arm_mptimer.c
@@ -0,0 +1,332 @@
+/*
+ * Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Copyright (c) 2011 Linaro Limited
+ * Written by Paul Brook, Peter Maydell
+ *
+ * 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 "sysbus.h"
+#include "qemu-timer.h"
+
+/* This device implements the per-cpu private timer and watchdog block
+ * which is used in both the ARM11MPCore and Cortex-A9MP.
+ */
+
+#define MAX_CPUS 4
+
+/* State of a single timer or watchdog block */
+typedef struct {
+ uint32_t count;
+ uint32_t load;
+ uint32_t control;
+ uint32_t status;
+ int64_t tick;
+ QEMUTimer *timer;
+ qemu_irq irq;
+ MemoryRegion iomem;
+} timerblock;
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t num_cpu;
+ timerblock timerblock[MAX_CPUS * 2];
+ MemoryRegion iomem[2];
+} arm_mptimer_state;
+
+static inline int get_current_cpu(arm_mptimer_state *s)
+{
+ if (cpu_single_env->cpu_index >= s->num_cpu) {
+ hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
+ s->num_cpu, cpu_single_env->cpu_index);
+ }
+ return cpu_single_env->cpu_index;
+}
+
+static inline void timerblock_update_irq(timerblock *tb)
+{
+ qemu_set_irq(tb->irq, tb->status);
+}
+
+/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
+static inline uint32_t timerblock_scale(timerblock *tb)
+{
+ return (((tb->control >> 8) & 0xff) + 1) * 10;
+}
+
+static void timerblock_reload(timerblock *tb, int restart)
+{
+ if (tb->count == 0) {
+ return;
+ }
+ if (restart) {
+ tb->tick = qemu_get_clock_ns(vm_clock);
+ }
+ tb->tick += (int64_t)tb->count * timerblock_scale(tb);
+ qemu_mod_timer(tb->timer, tb->tick);
+}
+
+static void timerblock_tick(void *opaque)
+{
+ timerblock *tb = (timerblock *)opaque;
+ tb->status = 1;
+ if (tb->control & 2) {
+ tb->count = tb->load;
+ timerblock_reload(tb, 0);
+ } else {
+ tb->count = 0;
+ }
+ timerblock_update_irq(tb);
+}
+
+static uint64_t timerblock_read(void *opaque, target_phys_addr_t addr,
+ unsigned size)
+{
+ timerblock *tb = (timerblock *)opaque;
+ int64_t val;
+ addr &= 0x1f;
+ switch (addr) {
+ case 0: /* Load */
+ return tb->load;
+ case 4: /* Counter. */
+ if (((tb->control & 1) == 0) || (tb->count == 0)) {
+ return 0;
+ }
+ /* Slow and ugly, but hopefully won't happen too often. */
+ val = tb->tick - qemu_get_clock_ns(vm_clock);
+ val /= timerblock_scale(tb);
+ if (val < 0) {
+ val = 0;
+ }
+ return val;
+ case 8: /* Control. */
+ return tb->control;
+ case 12: /* Interrupt status. */
+ return tb->status;
+ default:
+ return 0;
+ }
+}
+
+static void timerblock_write(void *opaque, target_phys_addr_t addr,
+ uint64_t value, unsigned size)
+{
+ timerblock *tb = (timerblock *)opaque;
+ int64_t old;
+ addr &= 0x1f;
+ switch (addr) {
+ case 0: /* Load */
+ tb->load = value;
+ /* Fall through. */
+ case 4: /* Counter. */
+ if ((tb->control & 1) && tb->count) {
+ /* Cancel the previous timer. */
+ qemu_del_timer(tb->timer);
+ }
+ tb->count = value;
+ if (tb->control & 1) {
+ timerblock_reload(tb, 1);
+ }
+ break;
+ case 8: /* Control. */
+ old = tb->control;
+ tb->control = value;
+ if (((old & 1) == 0) && (value & 1)) {
+ if (tb->count == 0 && (tb->control & 2)) {
+ tb->count = tb->load;
+ }
+ timerblock_reload(tb, 1);
+ }
+ break;
+ case 12: /* Interrupt status. */
+ tb->status &= ~value;
+ timerblock_update_irq(tb);
+ break;
+ }
+}
+
+/* Wrapper functions to implement the "read timer/watchdog for
+ * the current CPU" memory regions.
+ */
+static uint64_t arm_thistimer_read(void *opaque, target_phys_addr_t addr,
+ unsigned size)
+{
+ arm_mptimer_state *s = (arm_mptimer_state *)opaque;
+ int id = get_current_cpu(s);
+ return timerblock_read(&s->timerblock[id * 2], addr, size);
+}
+
+static void arm_thistimer_write(void *opaque, target_phys_addr_t addr,
+ uint64_t value, unsigned size)
+{
+ arm_mptimer_state *s = (arm_mptimer_state *)opaque;
+ int id = get_current_cpu(s);
+ timerblock_write(&s->timerblock[id * 2], addr, value, size);
+}
+
+static uint64_t arm_thiswdog_read(void *opaque, target_phys_addr_t addr,
+ unsigned size)
+{
+ arm_mptimer_state *s = (arm_mptimer_state *)opaque;
+ int id = get_current_cpu(s);
+ return timerblock_read(&s->timerblock[id * 2 + 1], addr, size);
+}
+
+static void arm_thiswdog_write(void *opaque, target_phys_addr_t addr,
+ uint64_t value, unsigned size)
+{
+ arm_mptimer_state *s = (arm_mptimer_state *)opaque;
+ int id = get_current_cpu(s);
+ timerblock_write(&s->timerblock[id * 2 + 1], addr, value, size);
+}
+
+static const MemoryRegionOps arm_thistimer_ops = {
+ .read = arm_thistimer_read,
+ .write = arm_thistimer_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps arm_thiswdog_ops = {
+ .read = arm_thiswdog_read,
+ .write = arm_thiswdog_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps timerblock_ops = {
+ .read = timerblock_read,
+ .write = timerblock_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void timerblock_reset(timerblock *tb)
+{
+ tb->count = 0;
+ tb->load = 0;
+ tb->control = 0;
+ tb->status = 0;
+ tb->tick = 0;
+}
+
+static void arm_mptimer_reset(DeviceState *dev)
+{
+ arm_mptimer_state *s =
+ FROM_SYSBUS(arm_mptimer_state, sysbus_from_qdev(dev));
+ int i;
+ /* We reset every timer in the array, not just the ones we're using,
+ * because vmsave will look at every array element.
+ */
+ for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) {
+ timerblock_reset(&s->timerblock[i]);
+ }
+}
+
+static int arm_mptimer_init(SysBusDevice *dev)
+{
+ arm_mptimer_state *s = FROM_SYSBUS(arm_mptimer_state, dev);
+ int i;
+ if (s->num_cpu < 1 || s->num_cpu > MAX_CPUS) {
+ hw_error("%s: num-cpu must be between 1 and %d\n", __func__, MAX_CPUS);
+ }
+ /* We implement one timer and one watchdog block per CPU, and
+ * expose multiple MMIO regions:
+ * * region 0 is "timer for this core"
+ * * region 1 is "watchdog for this core"
+ * * region 2 is "timer for core 0"
+ * * region 3 is "watchdog for core 0"
+ * * region 4 is "timer for core 1"
+ * * region 5 is "watchdog for core 1"
+ * and so on.
+ * The outgoing interrupt lines are
+ * * timer for core 0
+ * * watchdog for core 0
+ * * timer for core 1
+ * * watchdog for core 1
+ * and so on.
+ */
+ memory_region_init_io(&s->iomem[0], &arm_thistimer_ops, s,
+ "arm_mptimer_timer", 0x20);
+ sysbus_init_mmio(dev, &s->iomem[0]);
+ memory_region_init_io(&s->iomem[1], &arm_thiswdog_ops, s,
+ "arm_mptimer_wdog", 0x20);
+ sysbus_init_mmio(dev, &s->iomem[1]);
+ for (i = 0; i < (s->num_cpu * 2); i++) {
+ timerblock *tb = &s->timerblock[i];
+ tb->timer = qemu_new_timer_ns(vm_clock, timerblock_tick, tb);
+ sysbus_init_irq(dev, &tb->irq);
+ memory_region_init_io(&tb->iomem, &timerblock_ops, tb,
+ "arm_mptimer_timerblock", 0x20);
+ sysbus_init_mmio(dev, &tb->iomem);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_timerblock = {
+ .name = "arm_mptimer_timerblock",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(count, timerblock),
+ VMSTATE_UINT32(load, timerblock),
+ VMSTATE_UINT32(control, timerblock),
+ VMSTATE_UINT32(status, timerblock),
+ VMSTATE_INT64(tick, timerblock),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_arm_mptimer = {
+ .name = "arm_mptimer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(timerblock, arm_mptimer_state, (MAX_CPUS * 2),
+ 1, vmstate_timerblock, timerblock),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static SysBusDeviceInfo arm_mptimer_info = {
+ .init = arm_mptimer_init,
+ .qdev.name = "arm_mptimer",
+ .qdev.size = sizeof(arm_mptimer_state),
+ .qdev.vmsd = &vmstate_arm_mptimer,
+ .qdev.reset = arm_mptimer_reset,
+ .qdev.no_user = 1,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("num-cpu", arm_mptimer_state, num_cpu, 0),
+ DEFINE_PROP_END_OF_LIST()
+ }
+};
+
+static void arm_mptimer_register_devices(void)
+{
+ sysbus_register_withprop(&arm_mptimer_info);
+}
+
+device_init(arm_mptimer_register_devices)
diff --git a/hw/mpcore.c b/hw/mpcore.c
index 4357d12..3d64609 100644
--- a/hw/mpcore.c
+++ b/hw/mpcore.c
@@ -22,135 +22,18 @@ gic_get_current_cpu(void)
/* MPCore private memory region. */
-typedef struct {
- uint32_t count;
- uint32_t load;
- uint32_t control;
- uint32_t status;
- uint32_t old_status;
- int64_t tick;
- QEMUTimer *timer;
- struct mpcore_priv_state *mpcore;
- int id; /* Encodes both timer/watchdog and CPU. */
-} mpcore_timer_state;
-
typedef struct mpcore_priv_state {
gic_state gic;
uint32_t scu_control;
int iomemtype;
- mpcore_timer_state timer[8];
+ uint32_t old_timer_status[8];
uint32_t num_cpu;
+ qemu_irq *timer_irq;
MemoryRegion iomem;
MemoryRegion container;
+ DeviceState *mptimer;
} mpcore_priv_state;
-/* Per-CPU Timers. */
-
-static inline void mpcore_timer_update_irq(mpcore_timer_state *s)
-{
- if (s->status & ~s->old_status) {
- gic_set_pending_private(&s->mpcore->gic, s->id >> 1, 29 + (s->id & 1));
- }
- s->old_status = s->status;
-}
-
-/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
-static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s)
-{
- return (((s->control >> 8) & 0xff) + 1) * 10;
-}
-
-static void mpcore_timer_reload(mpcore_timer_state *s, int restart)
-{
- if (s->count == 0)
- return;
- if (restart)
- s->tick = qemu_get_clock_ns(vm_clock);
- s->tick += (int64_t)s->count * mpcore_timer_scale(s);
- qemu_mod_timer(s->timer, s->tick);
-}
-
-static void mpcore_timer_tick(void *opaque)
-{
- mpcore_timer_state *s = (mpcore_timer_state *)opaque;
- s->status = 1;
- if (s->control & 2) {
- s->count = s->load;
- mpcore_timer_reload(s, 0);
- } else {
- s->count = 0;
- }
- mpcore_timer_update_irq(s);
-}
-
-static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset)
-{
- int64_t val;
- switch (offset) {
- case 0: /* Load */
- return s->load;
- /* Fall through. */
- case 4: /* Counter. */
- if (((s->control & 1) == 0) || (s->count == 0))
- return 0;
- /* Slow and ugly, but hopefully won't happen too often. */
- val = s->tick - qemu_get_clock_ns(vm_clock);
- val /= mpcore_timer_scale(s);
- if (val < 0)
- val = 0;
- return val;
- case 8: /* Control. */
- return s->control;
- case 12: /* Interrupt status. */
- return s->status;
- default:
- return 0;
- }
-}
-
-static void mpcore_timer_write(mpcore_timer_state *s, int offset,
- uint32_t value)
-{
- int64_t old;
- switch (offset) {
- case 0: /* Load */
- s->load = value;
- /* Fall through. */
- case 4: /* Counter. */
- if ((s->control & 1) && s->count) {
- /* Cancel the previous timer. */
- qemu_del_timer(s->timer);
- }
- s->count = value;
- if (s->control & 1) {
- mpcore_timer_reload(s, 1);
- }
- break;
- case 8: /* Control. */
- old = s->control;
- s->control = value;
- if (((old & 1) == 0) && (value & 1)) {
- if (s->count == 0 && (s->control & 2))
- s->count = s->load;
- mpcore_timer_reload(s, 1);
- }
- break;
- case 12: /* Interrupt status. */
- s->status &= ~value;
- mpcore_timer_update_irq(s);
- break;
- }
-}
-
-static void mpcore_timer_init(mpcore_priv_state *mpcore,
- mpcore_timer_state *s, int id)
-{
- s->id = id;
- s->mpcore = mpcore;
- s->timer = qemu_new_timer_ns(vm_clock, mpcore_timer_tick, s);
-}
-
-
/* Per-CPU private memory mapped IO. */
static uint64_t mpcore_priv_read(void *opaque, target_phys_addr_t offset,
@@ -185,20 +68,6 @@ static uint64_t mpcore_priv_read(void *opaque, target_phys_addr_t offset,
}
}
return gic_cpu_read(&s->gic, id, offset & 0xff);
- } else if (offset < 0xb00) {
- /* Timers. */
- if (offset < 0x700) {
- id = gic_get_current_cpu();
- } else {
- id = (offset - 0x700) >> 8;
- if (id >= s->num_cpu) {
- return 0;
- }
- }
- id <<= 1;
- if (offset & 0x20)
- id++;
- return mpcore_timer_read(&s->timer[id], offset & 0xf);
}
bad_reg:
hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
@@ -233,20 +102,6 @@ static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
if (id < s->num_cpu) {
gic_cpu_write(&s->gic, id, offset & 0xff, value);
}
- } else if (offset < 0xb00) {
- /* Timers. */
- if (offset < 0x700) {
- id = gic_get_current_cpu();
- } else {
- id = (offset - 0x700) >> 8;
- }
- if (id < s->num_cpu) {
- id <<= 1;
- if (offset & 0x20)
- id++;
- mpcore_timer_write(&s->timer[id], offset & 0xf, value);
- }
- return;
}
return;
bad_reg:
@@ -259,25 +114,50 @@ static const MemoryRegionOps mpcore_priv_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
+static void mpcore_timer_irq_handler(void *opaque, int irq, int level)
+{
+ mpcore_priv_state *s = (mpcore_priv_state *)opaque;
+ if (level && !s->old_timer_status[irq]) {
+ gic_set_pending_private(&s->gic, irq >> 1, 29 + (irq & 1));
+ }
+ s->old_timer_status[irq] = level;
+}
+
static void mpcore_priv_map_setup(mpcore_priv_state *s)
{
+ int i;
+ SysBusDevice *busdev = sysbus_from_qdev(s->mptimer);
memory_region_init(&s->container, "mpcode-priv-container", 0x2000);
memory_region_init_io(&s->iomem, &mpcore_priv_ops, s, "mpcode-priv",
0x1000);
memory_region_add_subregion(&s->container, 0, &s->iomem);
+ /* Add the regions for timer and watchdog for "current CPU" and
+ * for each specific CPU.
+ */
+ s->timer_irq = qemu_allocate_irqs(mpcore_timer_irq_handler,
+ s, (s->num_cpu + 1) * 2);
+ for (i = 0; i < (s->num_cpu + 1) * 2; i++) {
+ /* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */
+ target_phys_addr_t offset = 0x600 + (i >> 1) * 0x100 + (i & 1) * 0x20;
+ memory_region_add_subregion(&s->container, offset,
+ sysbus_mmio_get_region(busdev, i));
+ }
memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem);
+ /* Wire up the interrupt from each watchdog and timer. */
+ for (i = 0; i < s->num_cpu * 2; i++) {
+ sysbus_connect_irq(busdev, i, s->timer_irq[i]);
+ }
}
static int mpcore_priv_init(SysBusDevice *dev)
{
mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
- int i;
gic_init(&s->gic, s->num_cpu);
+ s->mptimer = qdev_create(NULL, "arm_mptimer");
+ qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu);
+ qdev_init_nofail(s->mptimer);
mpcore_priv_map_setup(s);
sysbus_init_mmio(dev, &s->container);
- for (i = 0; i < s->num_cpu * 2; i++) {
- mpcore_timer_init(s, &s->timer[i], i);
- }
return 0;
}
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 2/7] hw/arm_gic: Expose GIC CPU interfaces as sysbus memory regions
2011-12-05 16:40 [Qemu-devel] [PATCH 0/7] ARM: clean up mpcore.c and separate out A9 Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 1/7] hw/arm_mptimer.c: Turn ARM MPcore private timers into qdev devices Peter Maydell
@ 2011-12-05 16:40 ` Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 3/7] hw/mpcore.c: Use the GIC memory regions for the CPU interface Peter Maydell
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Peter Maydell @ 2011-12-05 16:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Bill Carson, patches
Expose the ARM GIC CPU interfaces as memory regions, rather than
just providing read and write functions for them.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm_gic.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 74 insertions(+), 1 deletions(-)
diff --git a/hw/arm_gic.c b/hw/arm_gic.c
index f3f3516..10d4028 100644
--- a/hw/arm_gic.c
+++ b/hw/arm_gic.c
@@ -103,7 +103,14 @@ typedef struct gic_state
int num_cpu;
#endif
- MemoryRegion iomem;
+ MemoryRegion iomem; /* Distributor */
+#ifndef NVIC
+ /* This is just so we can have an opaque pointer which identifies
+ * both this GIC and which CPU interface we should be accessing.
+ */
+ struct gic_state *backref[NCPU];
+ MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */
+#endif
} gic_state;
/* TODO: Many places that call this routine could be optimized. */
@@ -624,6 +631,54 @@ static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value)
}
gic_update(s);
}
+
+/* Wrappers to read/write the GIC CPU interface for the current CPU */
+static uint64_t gic_thiscpu_read(void *opaque, target_phys_addr_t addr,
+ unsigned size)
+{
+ gic_state *s = (gic_state *)opaque;
+ return gic_cpu_read(s, gic_get_current_cpu(), addr & 0xff);
+}
+
+static void gic_thiscpu_write(void *opaque, target_phys_addr_t addr,
+ uint64_t value, unsigned size)
+{
+ gic_state *s = (gic_state *)opaque;
+ gic_cpu_write(s, gic_get_current_cpu(), addr & 0xff, value);
+}
+
+/* Wrappers to read/write the GIC CPU interface for a specific CPU.
+ * These just decode the opaque pointer into gic_state* + cpu id.
+ */
+static uint64_t gic_do_cpu_read(void *opaque, target_phys_addr_t addr,
+ unsigned size)
+{
+ gic_state **backref = (gic_state **)opaque;
+ gic_state *s = *backref;
+ int id = (backref - s->backref);
+ return gic_cpu_read(s, id, addr & 0xff);
+}
+
+static void gic_do_cpu_write(void *opaque, target_phys_addr_t addr,
+ uint64_t value, unsigned size)
+{
+ gic_state **backref = (gic_state **)opaque;
+ gic_state *s = *backref;
+ int id = (backref - s->backref);
+ gic_cpu_write(s, id, addr & 0xff, value);
+}
+
+static const MemoryRegionOps gic_thiscpu_ops = {
+ .read = gic_thiscpu_read,
+ .write = gic_thiscpu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps gic_cpu_ops = {
+ .read = gic_do_cpu_read,
+ .write = gic_do_cpu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
#endif
static void gic_reset(gic_state *s)
@@ -743,6 +798,24 @@ static void gic_init(gic_state *s)
sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
}
memory_region_init_io(&s->iomem, &gic_dist_ops, s, "gic_dist", 0x1000);
+#ifndef NVIC
+ /* Memory regions for the CPU interfaces (NVIC doesn't have these):
+ * a region for "CPU interface for this core", then a region for
+ * "CPU interface for core 0", "for core 1", ...
+ * NB that the memory region size of 0x100 applies for the 11MPCore
+ * and also cores following the GIC v1 spec (ie A9).
+ * GIC v2 defines a larger memory region (0x1000) so this will need
+ * to be extended when we implement A15.
+ */
+ memory_region_init_io(&s->cpuiomem[0], &gic_thiscpu_ops, s,
+ "gic_cpu", 0x100);
+ for (i = 0; i < NUM_CPU(s); i++) {
+ s->backref[i] = s;
+ memory_region_init_io(&s->cpuiomem[i+1], &gic_cpu_ops, &s->backref[i],
+ "gic_cpu", 0x100);
+ }
+#endif
+
gic_reset(s);
register_savevm(NULL, "arm_gic", -1, 2, gic_save, gic_load, s);
}
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 3/7] hw/mpcore.c: Use the GIC memory regions for the CPU interface
2011-12-05 16:40 [Qemu-devel] [PATCH 0/7] ARM: clean up mpcore.c and separate out A9 Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 1/7] hw/arm_mptimer.c: Turn ARM MPcore private timers into qdev devices Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 2/7] hw/arm_gic: Expose GIC CPU interfaces as sysbus memory regions Peter Maydell
@ 2011-12-05 16:40 ` Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 4/7] hw/realview_gic: Use GIC memory region " Peter Maydell
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Peter Maydell @ 2011-12-05 16:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Bill Carson, patches
Switch to using the GIC memory regions for the CPU interface
rather than hand implementing them as a subcase of mpcore_priv_read()
and mpcore_priv_write().
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/mpcore.c | 35 ++++++++++-------------------------
1 files changed, 10 insertions(+), 25 deletions(-)
diff --git a/hw/mpcore.c b/hw/mpcore.c
index 3d64609..a0af1ad 100644
--- a/hw/mpcore.c
+++ b/hw/mpcore.c
@@ -41,7 +41,7 @@ static uint64_t mpcore_priv_read(void *opaque, target_phys_addr_t offset,
{
mpcore_priv_state *s = (mpcore_priv_state *)opaque;
int id;
- offset &= 0xfff;
+ offset &= 0xff;
if (offset < 0x100) {
/* SCU */
switch (offset) {
@@ -57,17 +57,6 @@ static uint64_t mpcore_priv_read(void *opaque, target_phys_addr_t offset,
default:
goto bad_reg;
}
- } else if (offset < 0x600) {
- /* Interrupt controller. */
- if (offset < 0x200) {
- id = gic_get_current_cpu();
- } else {
- id = (offset - 0x200) >> 8;
- if (id >= s->num_cpu) {
- return 0;
- }
- }
- return gic_cpu_read(&s->gic, id, offset & 0xff);
}
bad_reg:
hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
@@ -78,8 +67,7 @@ static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
uint64_t value, unsigned size)
{
mpcore_priv_state *s = (mpcore_priv_state *)opaque;
- int id;
- offset &= 0xfff;
+ offset &= 0xff;
if (offset < 0x100) {
/* SCU */
switch (offset) {
@@ -92,16 +80,6 @@ static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
default:
goto bad_reg;
}
- } else if (offset < 0x600) {
- /* Interrupt controller. */
- if (offset < 0x200) {
- id = gic_get_current_cpu();
- } else {
- id = (offset - 0x200) >> 8;
- }
- if (id < s->num_cpu) {
- gic_cpu_write(&s->gic, id, offset & 0xff, value);
- }
}
return;
bad_reg:
@@ -129,8 +107,15 @@ static void mpcore_priv_map_setup(mpcore_priv_state *s)
SysBusDevice *busdev = sysbus_from_qdev(s->mptimer);
memory_region_init(&s->container, "mpcode-priv-container", 0x2000);
memory_region_init_io(&s->iomem, &mpcore_priv_ops, s, "mpcode-priv",
- 0x1000);
+ 0x100);
memory_region_add_subregion(&s->container, 0, &s->iomem);
+ /* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs
+ * at 0x200, 0x300...
+ */
+ for (i = 0; i < (s->num_cpu + 1); i++) {
+ target_phys_addr_t offset = 0x100 + (i * 0x100);
+ memory_region_add_subregion(&s->container, offset, &s->gic.cpuiomem[i]);
+ }
/* Add the regions for timer and watchdog for "current CPU" and
* for each specific CPU.
*/
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 4/7] hw/realview_gic: Use GIC memory region for the CPU interface
2011-12-05 16:40 [Qemu-devel] [PATCH 0/7] ARM: clean up mpcore.c and separate out A9 Peter Maydell
` (2 preceding siblings ...)
2011-12-05 16:40 ` [Qemu-devel] [PATCH 3/7] hw/mpcore.c: Use the GIC memory regions for the CPU interface Peter Maydell
@ 2011-12-05 16:40 ` Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 5/7] hw/mpcore: Clean up mpcore_priv_read/write as they are now SCU only Peter Maydell
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Peter Maydell @ 2011-12-05 16:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Bill Carson, patches
Use the GIC provided memory region for the CPU interface rather
than implementing our own.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/realview_gic.c | 25 +------------------------
1 files changed, 1 insertions(+), 24 deletions(-)
diff --git a/hw/realview_gic.c b/hw/realview_gic.c
index 479f939..8c4d509 100644
--- a/hw/realview_gic.c
+++ b/hw/realview_gic.c
@@ -23,36 +23,13 @@ gic_get_current_cpu(void)
typedef struct {
gic_state gic;
- MemoryRegion iomem;
MemoryRegion container;
} RealViewGICState;
-static uint64_t realview_gic_cpu_read(void *opaque, target_phys_addr_t offset,
- unsigned size)
-{
- gic_state *s = (gic_state *)opaque;
- return gic_cpu_read(s, gic_get_current_cpu(), offset);
-}
-
-static void realview_gic_cpu_write(void *opaque, target_phys_addr_t offset,
- uint64_t value, unsigned size)
-{
- gic_state *s = (gic_state *)opaque;
- gic_cpu_write(s, gic_get_current_cpu(), offset, value);
-}
-
-static const MemoryRegionOps realview_gic_cpu_ops = {
- .read = realview_gic_cpu_read,
- .write = realview_gic_cpu_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
static void realview_gic_map_setup(RealViewGICState *s)
{
memory_region_init(&s->container, "realview-gic-container", 0x2000);
- memory_region_init_io(&s->iomem, &realview_gic_cpu_ops, &s->gic,
- "realview-gic", 0x1000);
- memory_region_add_subregion(&s->container, 0, &s->iomem);
+ memory_region_add_subregion(&s->container, 0, &s->gic.cpuiomem[0]);
memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem);
}
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 5/7] hw/mpcore: Clean up mpcore_priv_read/write as they are now SCU only
2011-12-05 16:40 [Qemu-devel] [PATCH 0/7] ARM: clean up mpcore.c and separate out A9 Peter Maydell
` (3 preceding siblings ...)
2011-12-05 16:40 ` [Qemu-devel] [PATCH 4/7] hw/realview_gic: Use GIC memory region " Peter Maydell
@ 2011-12-05 16:40 ` Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 6/7] hw/a9mpcore.c: Implement A9MP peripherals rather than 11MPcore ones Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 7/7] hw/mpcore.c: Merge with hw/arm11mpcore.c Peter Maydell
6 siblings, 0 replies; 9+ messages in thread
From: Peter Maydell @ 2011-12-05 16:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Bill Carson, patches
The only code left in mpcore_priv_read and mpcore_priv_write is now
the implementation of the SCU registers. Clean up by renaming functions
and removing some unnecessary conditionals to make this clearer.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/mpcore.c | 73 +++++++++++++++++++++++++----------------------------------
1 files changed, 31 insertions(+), 42 deletions(-)
diff --git a/hw/mpcore.c b/hw/mpcore.c
index a0af1ad..670d7e5 100644
--- a/hw/mpcore.c
+++ b/hw/mpcore.c
@@ -36,59 +36,49 @@ typedef struct mpcore_priv_state {
/* Per-CPU private memory mapped IO. */
-static uint64_t mpcore_priv_read(void *opaque, target_phys_addr_t offset,
- unsigned size)
+static uint64_t mpcore_scu_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
{
mpcore_priv_state *s = (mpcore_priv_state *)opaque;
int id;
offset &= 0xff;
- if (offset < 0x100) {
- /* SCU */
- switch (offset) {
- case 0x00: /* Control. */
- return s->scu_control;
- case 0x04: /* Configuration. */
- id = ((1 << s->num_cpu) - 1) << 4;
- return id | (s->num_cpu - 1);
- case 0x08: /* CPU status. */
- return 0;
- case 0x0c: /* Invalidate all. */
- return 0;
- default:
- goto bad_reg;
- }
+ /* SCU */
+ switch (offset) {
+ case 0x00: /* Control. */
+ return s->scu_control;
+ case 0x04: /* Configuration. */
+ id = ((1 << s->num_cpu) - 1) << 4;
+ return id | (s->num_cpu - 1);
+ case 0x08: /* CPU status. */
+ return 0;
+ case 0x0c: /* Invalidate all. */
+ return 0;
+ default:
+ hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
}
-bad_reg:
- hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
- return 0;
}
-static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
- uint64_t value, unsigned size)
+static void mpcore_scu_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
{
mpcore_priv_state *s = (mpcore_priv_state *)opaque;
offset &= 0xff;
- if (offset < 0x100) {
- /* SCU */
- switch (offset) {
- case 0: /* Control register. */
- s->scu_control = value & 1;
- break;
- case 0x0c: /* Invalidate all. */
- /* This is a no-op as cache is not emulated. */
- break;
- default:
- goto bad_reg;
- }
+ /* SCU */
+ switch (offset) {
+ case 0: /* Control register. */
+ s->scu_control = value & 1;
+ break;
+ case 0x0c: /* Invalidate all. */
+ /* This is a no-op as cache is not emulated. */
+ break;
+ default:
+ hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
}
- return;
-bad_reg:
- hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
}
-static const MemoryRegionOps mpcore_priv_ops = {
- .read = mpcore_priv_read,
- .write = mpcore_priv_write,
+static const MemoryRegionOps mpcore_scu_ops = {
+ .read = mpcore_scu_read,
+ .write = mpcore_scu_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
@@ -106,8 +96,7 @@ static void mpcore_priv_map_setup(mpcore_priv_state *s)
int i;
SysBusDevice *busdev = sysbus_from_qdev(s->mptimer);
memory_region_init(&s->container, "mpcode-priv-container", 0x2000);
- memory_region_init_io(&s->iomem, &mpcore_priv_ops, s, "mpcode-priv",
- 0x100);
+ memory_region_init_io(&s->iomem, &mpcore_scu_ops, s, "mpcore-scu", 0x100);
memory_region_add_subregion(&s->container, 0, &s->iomem);
/* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs
* at 0x200, 0x300...
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 6/7] hw/a9mpcore.c: Implement A9MP peripherals rather than 11MPcore ones
2011-12-05 16:40 [Qemu-devel] [PATCH 0/7] ARM: clean up mpcore.c and separate out A9 Peter Maydell
` (4 preceding siblings ...)
2011-12-05 16:40 ` [Qemu-devel] [PATCH 5/7] hw/mpcore: Clean up mpcore_priv_read/write as they are now SCU only Peter Maydell
@ 2011-12-05 16:40 ` Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 7/7] hw/mpcore.c: Merge with hw/arm11mpcore.c Peter Maydell
6 siblings, 0 replies; 9+ messages in thread
From: Peter Maydell @ 2011-12-05 16:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Bill Carson, patches
Implement the A9MP private peripheral region correctly, rather
than piggybacking on the 11MPCore code; the two CPUs are not the
same in this area.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/a9mpcore.c | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 179 insertions(+), 10 deletions(-)
diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c
index 6f108f4..cd2985f 100644
--- a/hw/a9mpcore.c
+++ b/hw/a9mpcore.c
@@ -2,28 +2,197 @@
* Cortex-A9MPCore internal peripheral emulation.
*
* Copyright (c) 2009 CodeSourcery.
- * Written by Paul Brook
+ * Copyright (c) 2011 Linaro Limited.
+ * Written by Paul Brook, Peter Maydell.
*
* This code is licensed under the GPL.
*/
-/* 64 external IRQ lines. */
+#include "sysbus.h"
+
+/* Configuration for arm_gic.c:
+ * number of external IRQ lines, max number of CPUs, how to ID current CPU
+ */
#define GIC_NIRQ 96
-#include "mpcore.c"
+#define NCPU 4
+
+static inline int
+gic_get_current_cpu(void)
+{
+ return cpu_single_env->cpu_index;
+}
+
+#include "arm_gic.c"
+
+/* A9MP private memory region. */
+
+typedef struct a9mp_priv_state {
+ gic_state gic;
+ uint32_t scu_control;
+ uint32_t old_timer_status[8];
+ uint32_t num_cpu;
+ qemu_irq *timer_irq;
+ MemoryRegion scu_iomem;
+ MemoryRegion ptimer_iomem;
+ MemoryRegion container;
+ DeviceState *mptimer;
+} a9mp_priv_state;
+
+static uint64_t a9_scu_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ a9mp_priv_state *s = (a9mp_priv_state *)opaque;
+ switch (offset) {
+ case 0x00: /* Control */
+ return s->scu_control;
+ case 0x04: /* Configuration */
+ return (((1 << s->num_cpu) - 1) << 4) | (s->num_cpu - 1);
+ case 0x08: /* CPU Power Status */
+ return 0;
+ case 0x0c: /* Invalidate All Registers In Secure State */
+ return 0;
+ case 0x40: /* Filtering Start Address Register */
+ case 0x44: /* Filtering End Address Register */
+ /* RAZ/WI, like an implementation with only one AXI master */
+ return 0;
+ case 0x50: /* SCU Access Control Register */
+ case 0x54: /* SCU Non-secure Access Control Register */
+ /* unimplemented, fall through */
+ default:
+ return 0;
+ }
+}
+
+static void a9_scu_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ a9mp_priv_state *s = (a9mp_priv_state *)opaque;
+ switch (offset) {
+ case 0x00: /* Control */
+ s->scu_control = value & 1;
+ break;
+ case 0x4: /* Configuration: RO */
+ break;
+ case 0x0c: /* Invalidate All Registers In Secure State */
+ /* no-op as we do not implement caches */
+ break;
+ case 0x40: /* Filtering Start Address Register */
+ case 0x44: /* Filtering End Address Register */
+ /* RAZ/WI, like an implementation with only one AXI master */
+ break;
+ case 0x8: /* CPU Power Status */
+ case 0x50: /* SCU Access Control Register */
+ case 0x54: /* SCU Non-secure Access Control Register */
+ /* unimplemented, fall through */
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps a9_scu_ops = {
+ .read = a9_scu_read,
+ .write = a9_scu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void a9mpcore_timer_irq_handler(void *opaque, int irq, int level)
+{
+ a9mp_priv_state *s = (a9mp_priv_state *)opaque;
+ if (level && !s->old_timer_status[irq]) {
+ gic_set_pending_private(&s->gic, irq >> 1, 29 + (irq & 1));
+ }
+ s->old_timer_status[irq] = level;
+}
+
+static void a9mp_priv_reset(DeviceState *dev)
+{
+ a9mp_priv_state *s = FROM_SYSBUSGIC(a9mp_priv_state, sysbus_from_qdev(dev));
+ int i;
+ s->scu_control = 0;
+ for (i = 0; i < ARRAY_SIZE(s->old_timer_status); i++) {
+ s->old_timer_status[i] = 0;
+ }
+}
+
+static int a9mp_priv_init(SysBusDevice *dev)
+{
+ a9mp_priv_state *s = FROM_SYSBUSGIC(a9mp_priv_state, dev);
+ SysBusDevice *busdev;
+ int i;
+
+ if (s->num_cpu > NCPU) {
+ hw_error("a9mp_priv_init: num-cpu may not be more than %d\n", NCPU);
+ }
+
+ gic_init(&s->gic, s->num_cpu);
+
+ s->mptimer = qdev_create(NULL, "arm_mptimer");
+ qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu);
+ qdev_init_nofail(s->mptimer);
+ busdev = sysbus_from_qdev(s->mptimer);
+
+ /* Memory map (addresses are offsets from PERIPHBASE):
+ * 0x0000-0x00ff -- Snoop Control Unit
+ * 0x0100-0x01ff -- GIC CPU interface
+ * 0x0200-0x02ff -- Global Timer
+ * 0x0300-0x05ff -- nothing
+ * 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_init(&s->container, "a9mp-priv-container", 0x2000);
+ memory_region_init_io(&s->scu_iomem, &a9_scu_ops, s, "a9mp-scu", 0x100);
+ memory_region_add_subregion(&s->container, 0, &s->scu_iomem);
+ /* GIC CPU interface */
+ memory_region_add_subregion(&s->container, 0x100, &s->gic.cpuiomem[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.
+ */
+ memory_region_add_subregion(&s->container, 0x600,
+ sysbus_mmio_get_region(busdev, 0));
+ memory_region_add_subregion(&s->container, 0x620,
+ sysbus_mmio_get_region(busdev, 1));
+ memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem);
+
+ sysbus_init_mmio(dev, &s->container);
+
+ /* Wire up the interrupt from each watchdog and timer. */
+ s->timer_irq = qemu_allocate_irqs(a9mpcore_timer_irq_handler,
+ s, (s->num_cpu + 1) * 2);
+ for (i = 0; i < s->num_cpu * 2; i++) {
+ sysbus_connect_irq(busdev, i, s->timer_irq[i]);
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_a9mp_priv = {
+ .name = "a9mpcore_priv",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(scu_control, a9mp_priv_state),
+ VMSTATE_UINT32_ARRAY(old_timer_status, a9mp_priv_state, 8),
+ VMSTATE_END_OF_LIST()
+ }
+};
-static SysBusDeviceInfo mpcore_priv_info = {
- .init = mpcore_priv_init,
+static SysBusDeviceInfo a9mp_priv_info = {
+ .init = a9mp_priv_init,
.qdev.name = "a9mpcore_priv",
- .qdev.size = sizeof(mpcore_priv_state),
+ .qdev.size = sizeof(a9mp_priv_state),
+ .qdev.vmsd = &vmstate_a9mp_priv,
+ .qdev.reset = a9mp_priv_reset,
.qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1),
+ DEFINE_PROP_UINT32("num-cpu", a9mp_priv_state, num_cpu, 1),
DEFINE_PROP_END_OF_LIST(),
}
};
-static void a9mpcore_register_devices(void)
+static void a9mp_register_devices(void)
{
- sysbus_register_withprop(&mpcore_priv_info);
+ sysbus_register_withprop(&a9mp_priv_info);
}
-device_init(a9mpcore_register_devices)
+device_init(a9mp_register_devices)
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 7/7] hw/mpcore.c: Merge with hw/arm11mpcore.c
2011-12-05 16:40 [Qemu-devel] [PATCH 0/7] ARM: clean up mpcore.c and separate out A9 Peter Maydell
` (5 preceding siblings ...)
2011-12-05 16:40 ` [Qemu-devel] [PATCH 6/7] hw/a9mpcore.c: Implement A9MP peripherals rather than 11MPcore ones Peter Maydell
@ 2011-12-05 16:40 ` Peter Maydell
6 siblings, 0 replies; 9+ messages in thread
From: Peter Maydell @ 2011-12-05 16:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Bill Carson, patches
hw/mpcore.c is now implementing only ARM11MPCore specific peripherals,
and is #included only from hw/arm11mpcore.c, so just merge it into that
file.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm11mpcore.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++-
hw/mpcore.c | 137 ------------------------------------------------------
2 files changed, 129 insertions(+), 138 deletions(-)
delete mode 100644 hw/mpcore.c
diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c
index 32ecf98..bc0457e 100644
--- a/hw/arm11mpcore.c
+++ b/hw/arm11mpcore.c
@@ -7,11 +7,139 @@
* This code is licensed under the GPL.
*/
+#include "sysbus.h"
+#include "qemu-timer.h"
+
/* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines
(+ 32 internal). However my test chip only exposes/reports 32.
More importantly Linux falls over if more than 32 are present! */
#define GIC_NIRQ 64
-#include "mpcore.c"
+
+#define NCPU 4
+
+static inline int
+gic_get_current_cpu(void)
+{
+ return cpu_single_env->cpu_index;
+}
+
+#include "arm_gic.c"
+
+/* MPCore private memory region. */
+
+typedef struct mpcore_priv_state {
+ gic_state gic;
+ uint32_t scu_control;
+ int iomemtype;
+ uint32_t old_timer_status[8];
+ uint32_t num_cpu;
+ qemu_irq *timer_irq;
+ MemoryRegion iomem;
+ MemoryRegion container;
+ DeviceState *mptimer;
+} mpcore_priv_state;
+
+/* Per-CPU private memory mapped IO. */
+
+static uint64_t mpcore_scu_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ mpcore_priv_state *s = (mpcore_priv_state *)opaque;
+ int id;
+ offset &= 0xff;
+ /* SCU */
+ switch (offset) {
+ case 0x00: /* Control. */
+ return s->scu_control;
+ case 0x04: /* Configuration. */
+ id = ((1 << s->num_cpu) - 1) << 4;
+ return id | (s->num_cpu - 1);
+ case 0x08: /* CPU status. */
+ return 0;
+ case 0x0c: /* Invalidate all. */
+ return 0;
+ default:
+ hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
+ }
+}
+
+static void mpcore_scu_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ mpcore_priv_state *s = (mpcore_priv_state *)opaque;
+ offset &= 0xff;
+ /* SCU */
+ switch (offset) {
+ case 0: /* Control register. */
+ s->scu_control = value & 1;
+ break;
+ case 0x0c: /* Invalidate all. */
+ /* This is a no-op as cache is not emulated. */
+ break;
+ default:
+ hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
+ }
+}
+
+static const MemoryRegionOps mpcore_scu_ops = {
+ .read = mpcore_scu_read,
+ .write = mpcore_scu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void mpcore_timer_irq_handler(void *opaque, int irq, int level)
+{
+ mpcore_priv_state *s = (mpcore_priv_state *)opaque;
+ if (level && !s->old_timer_status[irq]) {
+ gic_set_pending_private(&s->gic, irq >> 1, 29 + (irq & 1));
+ }
+ s->old_timer_status[irq] = level;
+}
+
+static void mpcore_priv_map_setup(mpcore_priv_state *s)
+{
+ int i;
+ SysBusDevice *busdev = sysbus_from_qdev(s->mptimer);
+ memory_region_init(&s->container, "mpcode-priv-container", 0x2000);
+ memory_region_init_io(&s->iomem, &mpcore_scu_ops, s, "mpcore-scu", 0x100);
+ memory_region_add_subregion(&s->container, 0, &s->iomem);
+ /* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs
+ * at 0x200, 0x300...
+ */
+ for (i = 0; i < (s->num_cpu + 1); i++) {
+ target_phys_addr_t offset = 0x100 + (i * 0x100);
+ memory_region_add_subregion(&s->container, offset, &s->gic.cpuiomem[i]);
+ }
+ /* Add the regions for timer and watchdog for "current CPU" and
+ * for each specific CPU.
+ */
+ s->timer_irq = qemu_allocate_irqs(mpcore_timer_irq_handler,
+ s, (s->num_cpu + 1) * 2);
+ for (i = 0; i < (s->num_cpu + 1) * 2; i++) {
+ /* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */
+ target_phys_addr_t offset = 0x600 + (i >> 1) * 0x100 + (i & 1) * 0x20;
+ memory_region_add_subregion(&s->container, offset,
+ sysbus_mmio_get_region(busdev, i));
+ }
+ memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem);
+ /* Wire up the interrupt from each watchdog and timer. */
+ for (i = 0; i < s->num_cpu * 2; i++) {
+ sysbus_connect_irq(busdev, i, s->timer_irq[i]);
+ }
+}
+
+static int mpcore_priv_init(SysBusDevice *dev)
+{
+ mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
+
+ gic_init(&s->gic, s->num_cpu);
+ s->mptimer = qdev_create(NULL, "arm_mptimer");
+ qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu);
+ qdev_init_nofail(s->mptimer);
+ mpcore_priv_map_setup(s);
+ sysbus_init_mmio(dev, &s->container);
+ return 0;
+}
/* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ
controllers. The output of these, plus some of the raw input lines
diff --git a/hw/mpcore.c b/hw/mpcore.c
deleted file mode 100644
index 670d7e5..0000000
--- a/hw/mpcore.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * ARM MPCore internal peripheral emulation (common code).
- *
- * Copyright (c) 2006-2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-
-#include "sysbus.h"
-#include "qemu-timer.h"
-
-#define NCPU 4
-
-static inline int
-gic_get_current_cpu(void)
-{
- return cpu_single_env->cpu_index;
-}
-
-#include "arm_gic.c"
-
-/* MPCore private memory region. */
-
-typedef struct mpcore_priv_state {
- gic_state gic;
- uint32_t scu_control;
- int iomemtype;
- uint32_t old_timer_status[8];
- uint32_t num_cpu;
- qemu_irq *timer_irq;
- MemoryRegion iomem;
- MemoryRegion container;
- DeviceState *mptimer;
-} mpcore_priv_state;
-
-/* Per-CPU private memory mapped IO. */
-
-static uint64_t mpcore_scu_read(void *opaque, target_phys_addr_t offset,
- unsigned size)
-{
- mpcore_priv_state *s = (mpcore_priv_state *)opaque;
- int id;
- offset &= 0xff;
- /* SCU */
- switch (offset) {
- case 0x00: /* Control. */
- return s->scu_control;
- case 0x04: /* Configuration. */
- id = ((1 << s->num_cpu) - 1) << 4;
- return id | (s->num_cpu - 1);
- case 0x08: /* CPU status. */
- return 0;
- case 0x0c: /* Invalidate all. */
- return 0;
- default:
- hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
- }
-}
-
-static void mpcore_scu_write(void *opaque, target_phys_addr_t offset,
- uint64_t value, unsigned size)
-{
- mpcore_priv_state *s = (mpcore_priv_state *)opaque;
- offset &= 0xff;
- /* SCU */
- switch (offset) {
- case 0: /* Control register. */
- s->scu_control = value & 1;
- break;
- case 0x0c: /* Invalidate all. */
- /* This is a no-op as cache is not emulated. */
- break;
- default:
- hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
- }
-}
-
-static const MemoryRegionOps mpcore_scu_ops = {
- .read = mpcore_scu_read,
- .write = mpcore_scu_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void mpcore_timer_irq_handler(void *opaque, int irq, int level)
-{
- mpcore_priv_state *s = (mpcore_priv_state *)opaque;
- if (level && !s->old_timer_status[irq]) {
- gic_set_pending_private(&s->gic, irq >> 1, 29 + (irq & 1));
- }
- s->old_timer_status[irq] = level;
-}
-
-static void mpcore_priv_map_setup(mpcore_priv_state *s)
-{
- int i;
- SysBusDevice *busdev = sysbus_from_qdev(s->mptimer);
- memory_region_init(&s->container, "mpcode-priv-container", 0x2000);
- memory_region_init_io(&s->iomem, &mpcore_scu_ops, s, "mpcore-scu", 0x100);
- memory_region_add_subregion(&s->container, 0, &s->iomem);
- /* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs
- * at 0x200, 0x300...
- */
- for (i = 0; i < (s->num_cpu + 1); i++) {
- target_phys_addr_t offset = 0x100 + (i * 0x100);
- memory_region_add_subregion(&s->container, offset, &s->gic.cpuiomem[i]);
- }
- /* Add the regions for timer and watchdog for "current CPU" and
- * for each specific CPU.
- */
- s->timer_irq = qemu_allocate_irqs(mpcore_timer_irq_handler,
- s, (s->num_cpu + 1) * 2);
- for (i = 0; i < (s->num_cpu + 1) * 2; i++) {
- /* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */
- target_phys_addr_t offset = 0x600 + (i >> 1) * 0x100 + (i & 1) * 0x20;
- memory_region_add_subregion(&s->container, offset,
- sysbus_mmio_get_region(busdev, i));
- }
- memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem);
- /* Wire up the interrupt from each watchdog and timer. */
- for (i = 0; i < s->num_cpu * 2; i++) {
- sysbus_connect_irq(busdev, i, s->timer_irq[i]);
- }
-}
-
-static int mpcore_priv_init(SysBusDevice *dev)
-{
- mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
-
- gic_init(&s->gic, s->num_cpu);
- s->mptimer = qdev_create(NULL, "arm_mptimer");
- qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu);
- qdev_init_nofail(s->mptimer);
- mpcore_priv_map_setup(s);
- sysbus_init_mmio(dev, &s->container);
- return 0;
-}
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH 1/7] hw/arm_mptimer.c: Turn ARM MPcore private timers into qdev devices
2011-12-05 16:40 ` [Qemu-devel] [PATCH 1/7] hw/arm_mptimer.c: Turn ARM MPcore private timers into qdev devices Peter Maydell
@ 2012-01-13 7:18 ` Evgeny Voevodin
0 siblings, 0 replies; 9+ messages in thread
From: Evgeny Voevodin @ 2012-01-13 7:18 UTC (permalink / raw)
To: Peter Maydell; +Cc: Bill Carson, Dmitry Solodkiy, qemu-devel, patches
On 12/05/2011 08:40 PM, Peter Maydell wrote:
> -/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
> -static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s)
> -{
> - return (((s->control>> 8)& 0xff) + 1) * 10;
> -}
Dear Peter, could you please explain why such a conversion used to gain
qemu ticks from mpcore timer ticks, actually why to multiply by 10?
AFAIK by default to get timer ticks QEMU uses host's time. Also cortex
documentation says that mpcore timer tick interval should be calculated
in this way: ((prescaler + 1) * (load + 1))/freq
Later mpcore_timer_reload uses this code to update QEMU ticks:
> - s->tick += (int64_t)s->count * mpcore_timer_scale(s);
I see that equation has transformed to: (prescaler * load) * 10
As I understand, this means that arm core internal timer is working at
frequency 10 times less (in ideal) then host.
--
Kind regards,
Evgeny Voevodin,
Leading Software Engineer,
ASWG, Moscow R&D center, Samsung Electronics
e-mail: e.voevodin@samsung.com
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2012-01-13 7:18 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-12-05 16:40 [Qemu-devel] [PATCH 0/7] ARM: clean up mpcore.c and separate out A9 Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 1/7] hw/arm_mptimer.c: Turn ARM MPcore private timers into qdev devices Peter Maydell
2012-01-13 7:18 ` Evgeny Voevodin
2011-12-05 16:40 ` [Qemu-devel] [PATCH 2/7] hw/arm_gic: Expose GIC CPU interfaces as sysbus memory regions Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 3/7] hw/mpcore.c: Use the GIC memory regions for the CPU interface Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 4/7] hw/realview_gic: Use GIC memory region " Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 5/7] hw/mpcore: Clean up mpcore_priv_read/write as they are now SCU only Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 6/7] hw/a9mpcore.c: Implement A9MP peripherals rather than 11MPcore ones Peter Maydell
2011-12-05 16:40 ` [Qemu-devel] [PATCH 7/7] hw/mpcore.c: Merge with hw/arm11mpcore.c Peter Maydell
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).