From f7c703250cef231085f07352b719bb91d71ea8a1 Mon Sep 17 00:00:00 2001 From: Paul Brook Date: Thu, 19 Nov 2009 16:45:21 +0000 Subject: [PATCH] ARM PBX-A9 board support Implement ARM RealView PBX-A9 board support. Signed-off-by: Paul Brook --- Makefile.target | 2 +- hw/a9mpcore.c | 29 ++++++++++++ hw/arm-misc.h | 1 + hw/arm11mpcore.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++ hw/arm_boot.c | 7 +-- hw/arm_gic.c | 2 +- hw/mpcore.c | 99 +---------------------------------------- hw/realview.c | 65 ++++++++++++++++++++++++--- qemu-doc.texi | 2 +- 9 files changed, 209 insertions(+), 110 deletions(-) create mode 100644 hw/a9mpcore.c create mode 100644 hw/arm11mpcore.c diff --git a/Makefile.target b/Makefile.target index b9888f025f..ab774e529b 100644 --- a/Makefile.target +++ b/Makefile.target @@ -270,7 +270,7 @@ endif 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 mpcore.o +obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.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 new file mode 100644 index 0000000000..b5e5328395 --- /dev/null +++ b/hw/a9mpcore.c @@ -0,0 +1,29 @@ +/* + * Cortex-A9MPCore internal peripheral emulation. + * + * Copyright (c) 2009 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL. + */ + +/* 64 external IRQ lines. */ +#define GIC_NIRQ 96 +#include "mpcore.c" + +static SysBusDeviceInfo mpcore_priv_info = { + .init = mpcore_priv_init, + .qdev.name = "a9mpcore_priv", + .qdev.size = sizeof(mpcore_priv_state), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void a9mpcore_register_devices(void) +{ + sysbus_register_withprop(&mpcore_priv_info); +} + +device_init(a9mpcore_register_devices) diff --git a/hw/arm-misc.h b/hw/arm-misc.h index e584073cb2..010acb4cf9 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -28,6 +28,7 @@ struct arm_boot_info { const char *initrd_filename; target_phys_addr_t loader_start; target_phys_addr_t smp_loader_start; + target_phys_addr_t smp_priv_base; int nb_cpus; int board_id; int (*atag_board)(struct arm_boot_info *info, void *p); diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c new file mode 100644 index 0000000000..3bbd8856cf --- /dev/null +++ b/hw/arm11mpcore.c @@ -0,0 +1,112 @@ +/* + * ARM11MPCore internal peripheral emulation. + * + * Copyright (c) 2006-2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL. + */ + +/* ??? 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" + +/* 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 void mpcore_rirq_map(SysBusDevice *dev, target_phys_addr_t base) +{ + mpcore_rirq_state *s = FROM_SYSBUS(mpcore_rirq_state, dev); + sysbus_mmio_map(s->priv, 0, base); +} + +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); + sysbus_init_mmio_cb(dev, 0x2000, mpcore_rirq_map); + return 0; +} + +static SysBusDeviceInfo mpcore_rirq_info = { + .init = realview_mpcore_init, + .qdev.name = "realview_mpcore", + .qdev.size = sizeof(mpcore_rirq_state), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static SysBusDeviceInfo mpcore_priv_info = { + .init = mpcore_priv_init, + .qdev.name = "arm11mpcore_priv", + .qdev.size = sizeof(mpcore_priv_state), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void arm11mpcore_register_devices(void) +{ + sysbus_register_withprop(&mpcore_rirq_info); + sysbus_register_withprop(&mpcore_priv_info); +} + +device_init(arm11mpcore_register_devices) diff --git a/hw/arm_boot.c b/hw/arm_boot.c index e273803645..30a76a56d1 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -31,8 +31,7 @@ static uint32_t bootloader[] = { /* Entry point for secondary CPUs. Enable interrupt controller and Issue WFI until start address is written to system controller. */ static uint32_t smpboot[] = { - 0xe3a00201, /* mov r0, #0x10000000 */ - 0xe3800601, /* orr r0, r0, #0x001000000 */ + 0xe59f0020, /* ldr r0, privbase */ 0xe3a01001, /* mov r1, #1 */ 0xe5801100, /* str r1, [r0, #0x100] */ 0xe3a00201, /* mov r0, #0x10000000 */ @@ -41,7 +40,8 @@ static uint32_t smpboot[] = { 0xe5901000, /* ldr r1, [r0] */ 0xe1110001, /* tst r1, r1 */ 0x0afffffb, /* beq */ - 0xe12fff11 /* bx r1 */ + 0xe12fff11, /* bx r1 */ + 0 /* privbase: Private memory region base address. */ }; #define WRITE_WORD(p, value) do { \ @@ -268,6 +268,7 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info) rom_add_blob_fixed("bootloader", bootloader, sizeof(bootloader), info->loader_start); if (info->nb_cpus > 1) { + smpboot[10] = info->smp_priv_base; for (n = 0; n < sizeof(smpboot) / 4; n++) { smpboot[n] = tswap32(smpboot[n]); } diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 536112bff5..c4afc6aeb5 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -607,7 +607,7 @@ static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value) switch (offset) { case 0x00: /* Control */ s->cpu_enabled[cpu] = (value & 1); - DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis"); + DPRINTF("CPU %d %sabled\n", cpu, s->cpu_enabled ? "En" : "Dis"); break; case 0x04: /* Priority mask */ s->priority_mask[cpu] = (value & 0xff); diff --git a/hw/mpcore.c b/hw/mpcore.c index eccb2e188d..b4db1914b7 100644 --- a/hw/mpcore.c +++ b/hw/mpcore.c @@ -1,5 +1,5 @@ /* - * ARM MPCore internal peripheral emulation. + * ARM MPCore internal peripheral emulation (common code). * * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook @@ -10,12 +10,7 @@ #include "sysbus.h" #include "qemu-timer.h" -#define MPCORE_PRIV_BASE 0x10100000 #define NCPU 4 -/* ??? 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 static inline int gic_get_current_cpu(void) @@ -288,95 +283,3 @@ static int mpcore_priv_init(SysBusDevice *dev) } 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 - are fed into a single SMP-aware interrupt controller on the CPU. */ -typedef struct { - SysBusDevice busdev; - 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; - SysBusDevice *bus_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); - bus_priv = sysbus_from_qdev(priv); - sysbus_mmio_map(bus_priv, 0, MPCORE_PRIV_BASE); - sysbus_pass_irq(dev, bus_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); - return 0; -} - -static SysBusDeviceInfo mpcore_rirq_info = { - .init = realview_mpcore_init, - .qdev.name = "realview_mpcore", - .qdev.size = sizeof(mpcore_rirq_state), - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), - } -}; - -static SysBusDeviceInfo mpcore_priv_info = { - .init = mpcore_priv_init, - .qdev.name = "arm11mpcore_priv", - .qdev.size = sizeof(mpcore_priv_state), - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), - } -}; - -static void mpcore_register_devices(void) -{ - sysbus_register_withprop(&mpcore_rirq_info); - sysbus_register_withprop(&mpcore_priv_info); -} - -device_init(mpcore_register_devices) diff --git a/hw/realview.c b/hw/realview.c index 50386f8e0a..7aa127ee2d 100644 --- a/hw/realview.c +++ b/hw/realview.c @@ -34,10 +34,19 @@ static void secondary_cpu_reset(void *opaque) env->regs[15] = SMP_BOOT_ADDR; } +/* The following two lists must be consistent. */ enum realview_board_type { BOARD_EB, BOARD_EB_MPCORE, - BOARD_PB_A8 + BOARD_PB_A8, + BOARD_PBX_A9, +}; + +int realview_board_id[] = { + 0x33b, + 0x33b, + 0x769, + 0x76d }; static void realview_init(ram_addr_t ram_size, @@ -57,12 +66,26 @@ static void realview_init(ram_addr_t ram_size, int n; int done_nic = 0; qemu_irq cpu_irq[4]; - int is_mpcore = (board_type == BOARD_EB_MPCORE); - int is_pb = (board_type == BOARD_PB_A8); + int is_mpcore = 0; + int is_pb = 0; uint32_t proc_id = 0; uint32_t sys_id; ram_addr_t low_ram_size; + switch (board_type) { + case BOARD_EB: + break; + case BOARD_EB_MPCORE: + is_mpcore = 1; + break; + case BOARD_PB_A8: + is_pb = 1; + break; + case BOARD_PBX_A9: + is_mpcore = 1; + is_pb = 1; + break; + } for (n = 0; n < smp_cpus; n++) { env = cpu_init(cpu_model); if (!env) { @@ -76,7 +99,11 @@ static void realview_init(ram_addr_t ram_size, } } if (arm_feature(env, ARM_FEATURE_V7)) { - proc_id = 0x0e000000; + if (is_mpcore) { + proc_id = 0x0c000000; + } else { + proc_id = 0x0e000000; + } } else if (arm_feature(env, ARM_FEATURE_V6K)) { proc_id = 0x06000000; } else if (arm_feature(env, ARM_FEATURE_V6)) { @@ -104,10 +131,16 @@ static void realview_init(ram_addr_t ram_size, arm_sysctl_init(0x10000000, sys_id, proc_id); if (is_mpcore) { - dev = qdev_create(NULL, "realview_mpcore"); + dev = qdev_create(NULL, is_pb ? "a9mpcore_priv": "realview_mpcore"); qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); qdev_init_nofail(dev); busdev = sysbus_from_qdev(dev); + if (is_pb) { + realview_binfo.smp_priv_base = 0x1f000000; + } else { + realview_binfo.smp_priv_base = 0x10100000; + } + sysbus_mmio_map(busdev, 0, realview_binfo.smp_priv_base); for (n = 0; n < smp_cpus; n++) { sysbus_connect_irq(busdev, n, cpu_irq[n]); } @@ -238,7 +271,7 @@ static void realview_init(ram_addr_t ram_size, realview_binfo.kernel_cmdline = kernel_cmdline; realview_binfo.initrd_filename = initrd_filename; realview_binfo.nb_cpus = smp_cpus; - realview_binfo.board_id = is_pb ? 0x769 : 0x33b; + realview_binfo.board_id = realview_board_id[board_type]; realview_binfo.loader_start = is_pb ? 0x70000000 : 0; arm_load_kernel(first_cpu, &realview_binfo); } @@ -279,6 +312,18 @@ static void realview_pb_a8_init(ram_addr_t ram_size, initrd_filename, cpu_model, BOARD_PB_A8); } +static void realview_pbx_a9_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + if (!cpu_model) { + cpu_model = "cortex-a9"; + } + realview_init(ram_size, boot_device, kernel_filename, kernel_cmdline, + initrd_filename, cpu_model, BOARD_PBX_A9); +} + static QEMUMachine realview_eb_machine = { .name = "realview-eb", .desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)", @@ -298,7 +343,14 @@ static QEMUMachine realview_pb_a8_machine = { .name = "realview-pb-a8", .desc = "ARM RealView Platform Baseboard for Cortex-A8", .init = realview_pb_a8_init, +}; + +static QEMUMachine realview_pbx_a9_machine = { + .name = "realview-pbx-a9", + .desc = "ARM RealView Platform Baseboard Explore for Cortex-A9", + .init = realview_pbx_a9_init, .use_scsi = 1, + .max_cpus = 4, }; static void realview_machine_init(void) @@ -306,6 +358,7 @@ static void realview_machine_init(void) qemu_register_machine(&realview_eb_machine); qemu_register_machine(&realview_eb_mpcore_machine); qemu_register_machine(&realview_pb_a8_machine); + qemu_register_machine(&realview_pbx_a9_machine); } machine_init(realview_machine_init); diff --git a/qemu-doc.texi b/qemu-doc.texi index eca43715bf..7b8488f7f7 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -1664,7 +1664,7 @@ devices: @itemize @minus @item -ARM926E, ARM1136, ARM11MPCORE or Cortex-A8 CPU +ARM926E, ARM1136, ARM11MPCore, Cortex-A8 or Cortex-A9 MPCore CPU @item ARM AMBA Generic/Distributed Interrupt Controller @item -- GitLab