a9mpcore.c 7.2 KB
Newer Older
P
Paul Brook 已提交
1 2 3 4
/*
 * Cortex-A9MPCore internal peripheral emulation.
 *
 * Copyright (c) 2009 CodeSourcery.
5 6
 * Copyright (c) 2011 Linaro Limited.
 * Written by Paul Brook, Peter Maydell.
P
Paul Brook 已提交
7
 *
M
Matthew Fernandez 已提交
8
 * This code is licensed under the GPL.
P
Paul Brook 已提交
9 10
 */

11 12
#include "sysbus.h"

13
#define LEGACY_INCLUDED_GIC
14 15 16 17 18 19 20
#include "arm_gic.c"

/* A9MP private memory region.  */

typedef struct a9mp_priv_state {
    gic_state gic;
    uint32_t scu_control;
R
Rob Herring 已提交
21
    uint32_t scu_status;
22 23 24 25 26 27 28
    uint32_t old_timer_status[8];
    uint32_t num_cpu;
    qemu_irq *timer_irq;
    MemoryRegion scu_iomem;
    MemoryRegion ptimer_iomem;
    MemoryRegion container;
    DeviceState *mptimer;
29
    uint32_t num_irq;
30 31 32 33 34 35 36 37 38 39 40 41
} 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 */
R
Rob Herring 已提交
42 43 44 45 46 47 48
        return s->scu_status;
    case 0x09: /* CPU status.  */
        return s->scu_status >> 8;
    case 0x0a: /* CPU status.  */
        return s->scu_status >> 16;
    case 0x0b: /* CPU status.  */
        return s->scu_status >> 24;
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
    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;
R
Rob Herring 已提交
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
    uint32_t mask;
    uint32_t shift;
    switch (size) {
    case 1:
        mask = 0xff;
        break;
    case 2:
        mask = 0xffff;
        break;
    case 4:
        mask = 0xffffffff;
        break;
    default:
        fprintf(stderr, "Invalid size %u in write to a9 scu register %x\n",
                size, offset);
        return;
    }

85 86 87 88 89 90
    switch (offset) {
    case 0x00: /* Control */
        s->scu_control = value & 1;
        break;
    case 0x4: /* Configuration: RO */
        break;
R
Rob Herring 已提交
91 92 93 94 95
    case 0x08: case 0x09: case 0x0A: case 0x0B: /* Power Control */
        shift = (offset - 0x8) * 8;
        s->scu_status &= ~(mask << shift);
        s->scu_status |= ((value & mask) << shift);
        break;
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
    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 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;

142
    gic_init(&s->gic, s->num_cpu, s->num_irq);
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

    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",
R
Rob Herring 已提交
187
    .version_id = 2,
188 189 190 191
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32(scu_control, a9mp_priv_state),
        VMSTATE_UINT32_ARRAY(old_timer_status, a9mp_priv_state, 8),
R
Rob Herring 已提交
192
        VMSTATE_UINT32_V(scu_status, a9mp_priv_state, 2),
193 194 195
        VMSTATE_END_OF_LIST()
    }
};
P
Paul Brook 已提交
196

197 198 199 200 201 202 203 204 205 206 207 208
static Property a9mp_priv_properties[] = {
    DEFINE_PROP_UINT32("num-cpu", a9mp_priv_state, 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", a9mp_priv_state, num_irq, 96),
    DEFINE_PROP_END_OF_LIST(),
};

209 210
static void a9mp_priv_class_init(ObjectClass *klass, void *data)
{
211
    DeviceClass *dc = DEVICE_CLASS(klass);
212 213 214
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);

    k->init = a9mp_priv_init;
215 216 217
    dc->props = a9mp_priv_properties;
    dc->vmsd = &vmstate_a9mp_priv;
    dc->reset = a9mp_priv_reset;
218 219
}

220 221 222 223 224
static TypeInfo a9mp_priv_info = {
    .name          = "a9mpcore_priv",
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(a9mp_priv_state),
    .class_init    = a9mp_priv_class_init,
P
Paul Brook 已提交
225 226
};

A
Andreas Färber 已提交
227
static void a9mp_register_types(void)
P
Paul Brook 已提交
228
{
229
    type_register_static(&a9mp_priv_info);
P
Paul Brook 已提交
230 231
}

A
Andreas Färber 已提交
232
type_init(a9mp_register_types)