arm11mpcore.c 8.3 KB
Newer Older
P
Paul Brook 已提交
1 2 3 4 5 6
/*
 * ARM11MPCore internal peripheral emulation.
 *
 * Copyright (c) 2006-2007 CodeSourcery.
 * Written by Paul Brook
 *
M
Matthew Fernandez 已提交
7
 * This code is licensed under the GPL.
P
Paul Brook 已提交
8 9
 */

10 11 12 13 14 15
#include "sysbus.h"
#include "qemu-timer.h"

/* MPCore private memory region.  */

typedef struct mpcore_priv_state {
16
    SysBusDevice busdev;
17 18 19 20 21 22 23
    uint32_t scu_control;
    int iomemtype;
    uint32_t old_timer_status[8];
    uint32_t num_cpu;
    MemoryRegion iomem;
    MemoryRegion container;
    DeviceState *mptimer;
24
    DeviceState *gic;
25
    uint32_t num_irq;
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
} 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;
    /* 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;
    /* 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,
};

74
static void mpcore_priv_set_irq(void *opaque, int irq, int level)
75 76
{
    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
77
    qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level);
78 79 80 81 82
}

static void mpcore_priv_map_setup(mpcore_priv_state *s)
{
    int i;
83
    SysBusDevice *gicbusdev = sysbus_from_qdev(s->gic);
84 85 86 87 88 89 90 91 92
    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);
93 94
        memory_region_add_subregion(&s->container, offset,
                                    sysbus_mmio_get_region(gicbusdev, i + 1));
95 96 97 98 99 100 101 102 103 104
    }
    /* Add the regions for timer and watchdog for "current CPU" and
     * for each specific CPU.
     */
    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));
    }
105 106 107 108 109 110 111 112 113 114 115
    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(busdev, i * 2,
                           qdev_get_gpio_in(s->gic, ppibase + 29));
        sysbus_connect_irq(busdev, i * 2 + 1,
                           qdev_get_gpio_in(s->gic, ppibase + 30));
116 117 118 119 120
    }
}

static int mpcore_priv_init(SysBusDevice *dev)
{
121 122 123 124 125 126 127 128 129 130 131 132
    mpcore_priv_state *s = FROM_SYSBUS(mpcore_priv_state, dev);

    s->gic = qdev_create(NULL, "arm_gic");
    qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu);
    qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq);
    qdev_init_nofail(s->gic);

    /* Pass through outbound IRQ lines from the GIC */
    sysbus_pass_irq(dev, sysbus_from_qdev(s->gic));

    /* Pass through inbound GPIO lines to the GIC */
    qdev_init_gpio_in(&s->busdev.qdev, mpcore_priv_set_irq, s->num_irq - 32);
133 134 135 136 137 138 139 140

    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;
}
P
Paul Brook 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201

/* Dummy PIC to route IRQ lines.  The baseboard has 4 independent IRQ
   controllers.  The output of these, plus some of the raw input lines
   are fed into a single SMP-aware interrupt controller on the CPU.  */
typedef struct {
    SysBusDevice busdev;
    SysBusDevice *priv;
    qemu_irq cpuic[32];
    qemu_irq rvic[4][64];
    uint32_t num_cpu;
} mpcore_rirq_state;

/* Map baseboard IRQs onto CPU IRQ lines.  */
static const int mpcore_irq_map[32] = {
    -1, -1, -1, -1,  1,  2, -1, -1,
    -1, -1,  6, -1,  4,  5, -1, -1,
    -1, 14, 15,  0,  7,  8, -1, -1,
    -1, -1, -1, -1,  9,  3, -1, -1,
};

static void mpcore_rirq_set_irq(void *opaque, int irq, int level)
{
    mpcore_rirq_state *s = (mpcore_rirq_state *)opaque;
    int i;

    for (i = 0; i < 4; i++) {
        qemu_set_irq(s->rvic[i][irq], level);
    }
    if (irq < 32) {
        irq = mpcore_irq_map[irq];
        if (irq >= 0) {
            qemu_set_irq(s->cpuic[irq], level);
        }
    }
}

static int realview_mpcore_init(SysBusDevice *dev)
{
    mpcore_rirq_state *s = FROM_SYSBUS(mpcore_rirq_state, dev);
    DeviceState *gic;
    DeviceState *priv;
    int n;
    int i;

    priv = qdev_create(NULL, "arm11mpcore_priv");
    qdev_prop_set_uint32(priv, "num-cpu", s->num_cpu);
    qdev_init_nofail(priv);
    s->priv = sysbus_from_qdev(priv);
    sysbus_pass_irq(dev, s->priv);
    for (i = 0; i < 32; i++) {
        s->cpuic[i] = qdev_get_gpio_in(priv, i);
    }
    /* ??? IRQ routing is hardcoded to "normal" mode.  */
    for (n = 0; n < 4; n++) {
        gic = sysbus_create_simple("realview_gic", 0x10040000 + n * 0x10000,
                                   s->cpuic[10 + n]);
        for (i = 0; i < 64; i++) {
            s->rvic[n][i] = qdev_get_gpio_in(gic, i);
        }
    }
    qdev_init_gpio_in(&dev->qdev, mpcore_rirq_set_irq, 64);
202
    sysbus_init_mmio(dev, sysbus_mmio_get_region(s->priv, 0));
P
Paul Brook 已提交
203 204 205
    return 0;
}

206
static Property mpcore_rirq_properties[] = {
207
    DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1),
208
    DEFINE_PROP_END_OF_LIST(),
P
Paul Brook 已提交
209 210
};

211 212
static void mpcore_rirq_class_init(ObjectClass *klass, void *data)
{
213
    DeviceClass *dc = DEVICE_CLASS(klass);
214 215 216
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);

    k->init = realview_mpcore_init;
217
    dc->props = mpcore_rirq_properties;
218 219
}

220 221 222 223 224
static TypeInfo mpcore_rirq_info = {
    .name          = "realview_mpcore",
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(mpcore_rirq_state),
    .class_init    = mpcore_rirq_class_init,
225 226 227 228
};

static Property mpcore_priv_properties[] = {
    DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1),
229 230 231 232 233 234 235 236 237
    /* The ARM11 MPCORE TRM says the on-chip controller may have
     * anything from 0 to 224 external interrupt IRQ lines (with another
     * 32 internal). We default to 32+32, which is the number provided by
     * the ARM11 MPCore test chip in the Realview Versatile Express
     * coretile. Other boards may differ and should set this property
     * appropriately. Some Linux kernels may not boot if the hardware
     * has more IRQ lines than the kernel expects.
     */
    DEFINE_PROP_UINT32("num-irq", mpcore_priv_state, num_irq, 64),
238 239 240 241 242
    DEFINE_PROP_END_OF_LIST(),
};

static void mpcore_priv_class_init(ObjectClass *klass, void *data)
{
243
    DeviceClass *dc = DEVICE_CLASS(klass);
244 245 246
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);

    k->init = mpcore_priv_init;
247
    dc->props = mpcore_priv_properties;
248 249
}

250 251 252 253 254
static TypeInfo mpcore_priv_info = {
    .name          = "arm11mpcore_priv",
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(mpcore_priv_state),
    .class_init    = mpcore_priv_class_init,
P
Paul Brook 已提交
255 256
};

A
Andreas Färber 已提交
257
static void arm11mpcore_register_types(void)
P
Paul Brook 已提交
258
{
259 260
    type_register_static(&mpcore_rirq_info);
    type_register_static(&mpcore_priv_info);
P
Paul Brook 已提交
261 262
}

A
Andreas Färber 已提交
263
type_init(arm11mpcore_register_types)