4c25f365ab
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> Message-id: 8f31398e6d9a93f57291399f269039da1a77a2b5.1385969450.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
164 lines
5.6 KiB
C
164 lines
5.6 KiB
C
/*
|
|
* Cortex-A9MPCore internal peripheral emulation.
|
|
*
|
|
* Copyright (c) 2009 CodeSourcery.
|
|
* Copyright (c) 2011 Linaro Limited.
|
|
* Written by Paul Brook, Peter Maydell.
|
|
*
|
|
* This code is licensed under the GPL.
|
|
*/
|
|
|
|
#include "hw/cpu/a9mpcore.h"
|
|
|
|
static void a9mp_priv_set_irq(void *opaque, int irq, int level)
|
|
{
|
|
A9MPPrivState *s = (A9MPPrivState *)opaque;
|
|
|
|
qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level);
|
|
}
|
|
|
|
static void a9mp_priv_initfn(Object *obj)
|
|
{
|
|
A9MPPrivState *s = A9MPCORE_PRIV(obj);
|
|
|
|
memory_region_init(&s->container, obj, "a9mp-priv-container", 0x2000);
|
|
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->container);
|
|
|
|
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());
|
|
|
|
object_initialize(&s->wdt, sizeof(s->wdt), TYPE_ARM_MPTIMER);
|
|
qdev_set_parent_bus(DEVICE(&s->wdt), sysbus_get_default());
|
|
}
|
|
|
|
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;
|
|
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);
|
|
object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
|
|
if (err != NULL) {
|
|
error_propagate(errp, err);
|
|
return;
|
|
}
|
|
gicbusdev = SYS_BUS_DEVICE(&s->gic);
|
|
|
|
/* Pass through outbound IRQ lines from the GIC */
|
|
sysbus_pass_irq(sbd, gicbusdev);
|
|
|
|
/* Pass through inbound GPIO lines to the GIC */
|
|
qdev_init_gpio_in(dev, a9mp_priv_set_irq, s->num_irq - 32);
|
|
|
|
mptimerdev = DEVICE(&s->mptimer);
|
|
qdev_prop_set_uint32(mptimerdev, "num-cpu", s->num_cpu);
|
|
object_property_set_bool(OBJECT(&s->mptimer), true, "realized", &err);
|
|
if (err != NULL) {
|
|
error_propagate(errp, err);
|
|
return;
|
|
}
|
|
mptimerbusdev = SYS_BUS_DEVICE(&s->mptimer);
|
|
|
|
wdtdev = DEVICE(&s->wdt);
|
|
qdev_prop_set_uint32(wdtdev, "num-cpu", s->num_cpu);
|
|
object_property_set_bool(OBJECT(&s->wdt), true, "realized", &err);
|
|
if (err != NULL) {
|
|
error_propagate(errp, err);
|
|
return;
|
|
}
|
|
wdtbusdev = SYS_BUS_DEVICE(&s->wdt);
|
|
|
|
/* 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_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));
|
|
/* 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(mptimerbusdev, 0));
|
|
memory_region_add_subregion(&s->container, 0x620,
|
|
sysbus_mmio_get_region(wdtbusdev, 0));
|
|
memory_region_add_subregion(&s->container, 0x1000,
|
|
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 (i = 0; i < s->num_cpu; i++) {
|
|
int ppibase = (s->num_irq - 32) + i * 32;
|
|
sysbus_connect_irq(mptimerbusdev, i,
|
|
qdev_get_gpio_in(gicdev, ppibase + 29));
|
|
sysbus_connect_irq(wdtbusdev, i,
|
|
qdev_get_gpio_in(gicdev, ppibase + 30));
|
|
}
|
|
}
|
|
|
|
static Property a9mp_priv_properties[] = {
|
|
DEFINE_PROP_UINT32("num-cpu", A9MPPrivState, num_cpu, 1),
|
|
/* The Cortex-A9MP may have anything from 0 to 224 external interrupt
|
|
* IRQ lines (with another 32 internal). We default to 64+32, which
|
|
* is the number provided by the Cortex-A9MP test chip in the
|
|
* Realview PBX-A9 and Versatile Express A9 development boards.
|
|
* Other boards may differ and should set this property appropriately.
|
|
*/
|
|
DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 96),
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
};
|
|
|
|
static void a9mp_priv_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
dc->realize = a9mp_priv_realize;
|
|
dc->props = a9mp_priv_properties;
|
|
}
|
|
|
|
static const TypeInfo a9mp_priv_info = {
|
|
.name = TYPE_A9MPCORE_PRIV,
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
.instance_size = sizeof(A9MPPrivState),
|
|
.instance_init = a9mp_priv_initfn,
|
|
.class_init = a9mp_priv_class_init,
|
|
};
|
|
|
|
static void a9mp_register_types(void)
|
|
{
|
|
type_register_static(&a9mp_priv_info);
|
|
}
|
|
|
|
type_init(a9mp_register_types)
|