qemu/hw/arm11mpcore.c
<<
>>
Prefs
   1/*
   2 * ARM11MPCore internal peripheral emulation.
   3 *
   4 * Copyright (c) 2006-2007 CodeSourcery.
   5 * Written by Paul Brook
   6 *
   7 * This code is licensed under the GPL.
   8 */
   9
  10#include "sysbus.h"
  11#include "qemu/timer.h"
  12
  13/* MPCore private memory region.  */
  14
  15typedef struct mpcore_priv_state {
  16    SysBusDevice busdev;
  17    uint32_t scu_control;
  18    int iomemtype;
  19    uint32_t old_timer_status[8];
  20    uint32_t num_cpu;
  21    MemoryRegion iomem;
  22    MemoryRegion container;
  23    DeviceState *mptimer;
  24    DeviceState *gic;
  25    uint32_t num_irq;
  26} mpcore_priv_state;
  27
  28/* Per-CPU private memory mapped IO.  */
  29
  30static uint64_t mpcore_scu_read(void *opaque, hwaddr offset,
  31                                unsigned size)
  32{
  33    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
  34    int id;
  35    /* SCU */
  36    switch (offset) {
  37    case 0x00: /* Control.  */
  38        return s->scu_control;
  39    case 0x04: /* Configuration.  */
  40        id = ((1 << s->num_cpu) - 1) << 4;
  41        return id | (s->num_cpu - 1);
  42    case 0x08: /* CPU status.  */
  43        return 0;
  44    case 0x0c: /* Invalidate all.  */
  45        return 0;
  46    default:
  47        qemu_log_mask(LOG_GUEST_ERROR,
  48                      "mpcore_priv_read: Bad offset %x\n", (int)offset);
  49        return 0;
  50    }
  51}
  52
  53static void mpcore_scu_write(void *opaque, hwaddr offset,
  54                             uint64_t value, unsigned size)
  55{
  56    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
  57    /* SCU */
  58    switch (offset) {
  59    case 0: /* Control register.  */
  60        s->scu_control = value & 1;
  61        break;
  62    case 0x0c: /* Invalidate all.  */
  63        /* This is a no-op as cache is not emulated.  */
  64        break;
  65    default:
  66        qemu_log_mask(LOG_GUEST_ERROR,
  67                      "mpcore_priv_read: Bad offset %x\n", (int)offset);
  68    }
  69}
  70
  71static const MemoryRegionOps mpcore_scu_ops = {
  72    .read = mpcore_scu_read,
  73    .write = mpcore_scu_write,
  74    .endianness = DEVICE_NATIVE_ENDIAN,
  75};
  76
  77static void mpcore_priv_set_irq(void *opaque, int irq, int level)
  78{
  79    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
  80    qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level);
  81}
  82
  83static void mpcore_priv_map_setup(mpcore_priv_state *s)
  84{
  85    int i;
  86    SysBusDevice *gicbusdev = SYS_BUS_DEVICE(s->gic);
  87    SysBusDevice *busdev = SYS_BUS_DEVICE(s->mptimer);
  88    memory_region_init(&s->container, "mpcode-priv-container", 0x2000);
  89    memory_region_init_io(&s->iomem, &mpcore_scu_ops, s, "mpcore-scu", 0x100);
  90    memory_region_add_subregion(&s->container, 0, &s->iomem);
  91    /* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs
  92     * at 0x200, 0x300...
  93     */
  94    for (i = 0; i < (s->num_cpu + 1); i++) {
  95        hwaddr offset = 0x100 + (i * 0x100);
  96        memory_region_add_subregion(&s->container, offset,
  97                                    sysbus_mmio_get_region(gicbusdev, i + 1));
  98    }
  99    /* Add the regions for timer and watchdog for "current CPU" and
 100     * for each specific CPU.
 101     */
 102    for (i = 0; i < (s->num_cpu + 1) * 2; i++) {
 103        /* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */
 104        hwaddr offset = 0x600 + (i >> 1) * 0x100 + (i & 1) * 0x20;
 105        memory_region_add_subregion(&s->container, offset,
 106                                    sysbus_mmio_get_region(busdev, i));
 107    }
 108    memory_region_add_subregion(&s->container, 0x1000,
 109                                sysbus_mmio_get_region(gicbusdev, 0));
 110    /* Wire up the interrupt from each watchdog and timer.
 111     * For each core the timer is PPI 29 and the watchdog PPI 30.
 112     */
 113    for (i = 0; i < s->num_cpu; i++) {
 114        int ppibase = (s->num_irq - 32) + i * 32;
 115        sysbus_connect_irq(busdev, i * 2,
 116                           qdev_get_gpio_in(s->gic, ppibase + 29));
 117        sysbus_connect_irq(busdev, i * 2 + 1,
 118                           qdev_get_gpio_in(s->gic, ppibase + 30));
 119    }
 120}
 121
 122static int mpcore_priv_init(SysBusDevice *dev)
 123{
 124    mpcore_priv_state *s = FROM_SYSBUS(mpcore_priv_state, dev);
 125
 126    s->gic = qdev_create(NULL, "arm_gic");
 127    qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu);
 128    qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq);
 129    /* Request the legacy 11MPCore GIC behaviour: */
 130    qdev_prop_set_uint32(s->gic, "revision", 0);
 131    qdev_init_nofail(s->gic);
 132
 133    /* Pass through outbound IRQ lines from the GIC */
 134    sysbus_pass_irq(dev, SYS_BUS_DEVICE(s->gic));
 135
 136    /* Pass through inbound GPIO lines to the GIC */
 137    qdev_init_gpio_in(&s->busdev.qdev, mpcore_priv_set_irq, s->num_irq - 32);
 138
 139    s->mptimer = qdev_create(NULL, "arm_mptimer");
 140    qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu);
 141    qdev_init_nofail(s->mptimer);
 142    mpcore_priv_map_setup(s);
 143    sysbus_init_mmio(dev, &s->container);
 144    return 0;
 145}
 146
 147/* Dummy PIC to route IRQ lines.  The baseboard has 4 independent IRQ
 148   controllers.  The output of these, plus some of the raw input lines
 149   are fed into a single SMP-aware interrupt controller on the CPU.  */
 150typedef struct {
 151    SysBusDevice busdev;
 152    SysBusDevice *priv;
 153    qemu_irq cpuic[32];
 154    qemu_irq rvic[4][64];
 155    uint32_t num_cpu;
 156} mpcore_rirq_state;
 157
 158/* Map baseboard IRQs onto CPU IRQ lines.  */
 159static const int mpcore_irq_map[32] = {
 160    -1, -1, -1, -1,  1,  2, -1, -1,
 161    -1, -1,  6, -1,  4,  5, -1, -1,
 162    -1, 14, 15,  0,  7,  8, -1, -1,
 163    -1, -1, -1, -1,  9,  3, -1, -1,
 164};
 165
 166static void mpcore_rirq_set_irq(void *opaque, int irq, int level)
 167{
 168    mpcore_rirq_state *s = (mpcore_rirq_state *)opaque;
 169    int i;
 170
 171    for (i = 0; i < 4; i++) {
 172        qemu_set_irq(s->rvic[i][irq], level);
 173    }
 174    if (irq < 32) {
 175        irq = mpcore_irq_map[irq];
 176        if (irq >= 0) {
 177            qemu_set_irq(s->cpuic[irq], level);
 178        }
 179    }
 180}
 181
 182static int realview_mpcore_init(SysBusDevice *dev)
 183{
 184    mpcore_rirq_state *s = FROM_SYSBUS(mpcore_rirq_state, dev);
 185    DeviceState *gic;
 186    DeviceState *priv;
 187    int n;
 188    int i;
 189
 190    priv = qdev_create(NULL, "arm11mpcore_priv");
 191    qdev_prop_set_uint32(priv, "num-cpu", s->num_cpu);
 192    qdev_init_nofail(priv);
 193    s->priv = SYS_BUS_DEVICE(priv);
 194    sysbus_pass_irq(dev, s->priv);
 195    for (i = 0; i < 32; i++) {
 196        s->cpuic[i] = qdev_get_gpio_in(priv, i);
 197    }
 198    /* ??? IRQ routing is hardcoded to "normal" mode.  */
 199    for (n = 0; n < 4; n++) {
 200        gic = sysbus_create_simple("realview_gic", 0x10040000 + n * 0x10000,
 201                                   s->cpuic[10 + n]);
 202        for (i = 0; i < 64; i++) {
 203            s->rvic[n][i] = qdev_get_gpio_in(gic, i);
 204        }
 205    }
 206    qdev_init_gpio_in(&dev->qdev, mpcore_rirq_set_irq, 64);
 207    sysbus_init_mmio(dev, sysbus_mmio_get_region(s->priv, 0));
 208    return 0;
 209}
 210
 211static Property mpcore_rirq_properties[] = {
 212    DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1),
 213    DEFINE_PROP_END_OF_LIST(),
 214};
 215
 216static void mpcore_rirq_class_init(ObjectClass *klass, void *data)
 217{
 218    DeviceClass *dc = DEVICE_CLASS(klass);
 219    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 220
 221    k->init = realview_mpcore_init;
 222    dc->props = mpcore_rirq_properties;
 223}
 224
 225static const TypeInfo mpcore_rirq_info = {
 226    .name          = "realview_mpcore",
 227    .parent        = TYPE_SYS_BUS_DEVICE,
 228    .instance_size = sizeof(mpcore_rirq_state),
 229    .class_init    = mpcore_rirq_class_init,
 230};
 231
 232static Property mpcore_priv_properties[] = {
 233    DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1),
 234    /* The ARM11 MPCORE TRM says the on-chip controller may have
 235     * anything from 0 to 224 external interrupt IRQ lines (with another
 236     * 32 internal). We default to 32+32, which is the number provided by
 237     * the ARM11 MPCore test chip in the Realview Versatile Express
 238     * coretile. Other boards may differ and should set this property
 239     * appropriately. Some Linux kernels may not boot if the hardware
 240     * has more IRQ lines than the kernel expects.
 241     */
 242    DEFINE_PROP_UINT32("num-irq", mpcore_priv_state, num_irq, 64),
 243    DEFINE_PROP_END_OF_LIST(),
 244};
 245
 246static void mpcore_priv_class_init(ObjectClass *klass, void *data)
 247{
 248    DeviceClass *dc = DEVICE_CLASS(klass);
 249    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 250
 251    k->init = mpcore_priv_init;
 252    dc->props = mpcore_priv_properties;
 253}
 254
 255static const TypeInfo mpcore_priv_info = {
 256    .name          = "arm11mpcore_priv",
 257    .parent        = TYPE_SYS_BUS_DEVICE,
 258    .instance_size = sizeof(mpcore_priv_state),
 259    .class_init    = mpcore_priv_class_init,
 260};
 261
 262static void arm11mpcore_register_types(void)
 263{
 264    type_register_static(&mpcore_rirq_info);
 265    type_register_static(&mpcore_priv_info);
 266}
 267
 268type_init(arm11mpcore_register_types)
 269