aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2011-12-12 10:08:57 -0600
committerAnthony Liguori <aliguori@us.ibm.com>2011-12-12 10:08:57 -0600
commitab23ebf48510c12d80f95b56f628afde3ca2c1c2 (patch)
treec8040778cc5ea6d81c5beabd471d81b7485e99f1
parent9bf4896e5d565785316d2c179be91fba11fbf3fb (diff)
parent2a6ab1e368c07569b0d884634dcf9e9f7cbb6fca (diff)
Merge remote-tracking branch 'pmaydell/arm-devs.for-upstream' into staging
-rw-r--r--Makefile.target1
-rw-r--r--hw/a9mpcore.c189
-rw-r--r--hw/arm11mpcore.c130
-rw-r--r--hw/arm_gic.c75
-rw-r--r--hw/arm_mptimer.c332
-rw-r--r--hw/arm_timer.c41
-rw-r--r--hw/mpcore.c283
-rw-r--r--hw/realview_gic.c25
8 files changed, 751 insertions, 325 deletions
diff --git a/Makefile.target b/Makefile.target
index a111521dbf..39b2e5a01b 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/a9mpcore.c b/hw/a9mpcore.c
index 6f108f4ce2..cd2985f421 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)
diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c
index 32ecf98309..bc0457e58b 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/arm_gic.c b/hw/arm_gic.c
index 527c9cec39..66c48fd2d9 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. */
@@ -633,6 +640,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)
@@ -752,6 +807,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);
}
diff --git a/hw/arm_mptimer.c b/hw/arm_mptimer.c
new file mode 100644
index 0000000000..455a0aa55a
--- /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/arm_timer.c b/hw/arm_timer.c
index 518bad20ae..0a5b9d2cd3 100644
--- a/hw/arm_timer.c
+++ b/hw/arm_timer.c
@@ -170,9 +170,9 @@ static arm_timer_state *arm_timer_init(uint32_t freq)
}
/* ARM PrimeCell SP804 dual timer module.
- Docs for this device don't seem to be publicly available. This
- implementation is based on guesswork, the linux kernel sources and the
- Integrator/CP timer modules. */
+ * Docs at
+ * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0271d/index.html
+*/
typedef struct {
SysBusDevice busdev;
@@ -182,6 +182,13 @@ typedef struct {
qemu_irq irq;
} sp804_state;
+static const uint8_t sp804_ids[] = {
+ /* Timer ID */
+ 0x04, 0x18, 0x14, 0,
+ /* PrimeCell ID */
+ 0xd, 0xf0, 0x05, 0xb1
+};
+
/* Merge the IRQs from the two component devices. */
static void sp804_set_irq(void *opaque, int irq, int level)
{
@@ -196,12 +203,27 @@ static uint64_t sp804_read(void *opaque, target_phys_addr_t offset,
{
sp804_state *s = (sp804_state *)opaque;
- /* ??? Don't know the PrimeCell ID for this device. */
if (offset < 0x20) {
return arm_timer_read(s->timer[0], offset);
- } else {
+ }
+ if (offset < 0x40) {
return arm_timer_read(s->timer[1], offset - 0x20);
}
+
+ /* TimerPeriphID */
+ if (offset >= 0xfe0 && offset <= 0xffc) {
+ return sp804_ids[(offset - 0xfe0) >> 2];
+ }
+
+ switch (offset) {
+ /* Integration Test control registers, which we won't support */
+ case 0xf00: /* TimerITCR */
+ case 0xf04: /* TimerITOP (strictly write only but..) */
+ return 0;
+ }
+
+ hw_error("%s: Bad offset %x\n", __func__, (int)offset);
+ return 0;
}
static void sp804_write(void *opaque, target_phys_addr_t offset,
@@ -211,9 +233,16 @@ static void sp804_write(void *opaque, target_phys_addr_t offset,
if (offset < 0x20) {
arm_timer_write(s->timer[0], offset, value);
- } else {
+ return;
+ }
+
+ if (offset < 0x40) {
arm_timer_write(s->timer[1], offset - 0x20, value);
+ return;
}
+
+ /* Technically we could be writing to the Test Registers, but not likely */
+ hw_error("%s: Bad offset %x\n", __func__, (int)offset);
}
static const MemoryRegionOps sp804_ops = {
diff --git a/hw/mpcore.c b/hw/mpcore.c
deleted file mode 100644
index 4357d12217..0000000000
--- a/hw/mpcore.c
+++ /dev/null
@@ -1,283 +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 {
- 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 num_cpu;
- MemoryRegion iomem;
- MemoryRegion container;
-} 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,
- unsigned size)
-{
- mpcore_priv_state *s = (mpcore_priv_state *)opaque;
- int id;
- offset &= 0xfff;
- 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;
- }
- } 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);
- } 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);
- return 0;
-}
-
-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;
- 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;
- }
- } 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);
- }
- } 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:
- 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,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void mpcore_priv_map_setup(mpcore_priv_state *s)
-{
- 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);
- memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem);
-}
-
-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);
- 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;
-}
diff --git a/hw/realview_gic.c b/hw/realview_gic.c
index 479f939553..8c4d509ee7 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);
}