diff options
Diffstat (limited to 'hw')
34 files changed, 2080 insertions, 410 deletions
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 78b56149b6..6088e53653 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -1,7 +1,10 @@ obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o +obj-$(CONFIG_DIGIC) += digic_boards.o obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o +obj-$(CONFIG_DIGIC) += digic.o obj-y += omap1.o omap2.o strongarm.o +obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c new file mode 100644 index 0000000000..4658e19504 --- /dev/null +++ b/hw/arm/allwinner-a10.c @@ -0,0 +1,103 @@ +/* + * Allwinner A10 SoC emulation + * + * Copyright (C) 2013 Li Guang + * Written by Li Guang <lig.fnst@cn.fujitsu.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "hw/sysbus.h" +#include "hw/devices.h" +#include "hw/arm/allwinner-a10.h" + +static void aw_a10_init(Object *obj) +{ + AwA10State *s = AW_A10(obj); + + object_initialize(&s->cpu, sizeof(s->cpu), "cortex-a8-" TYPE_ARM_CPU); + object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL); + + object_initialize(&s->intc, sizeof(s->intc), TYPE_AW_A10_PIC); + qdev_set_parent_bus(DEVICE(&s->intc), sysbus_get_default()); + + object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT); + qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default()); +} + +static void aw_a10_realize(DeviceState *dev, Error **errp) +{ + AwA10State *s = AW_A10(dev); + SysBusDevice *sysbusdev; + uint8_t i; + qemu_irq fiq, irq; + Error *err = NULL; + + object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + irq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ); + fiq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ); + + object_property_set_bool(OBJECT(&s->intc), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + sysbusdev = SYS_BUS_DEVICE(&s->intc); + sysbus_mmio_map(sysbusdev, 0, AW_A10_PIC_REG_BASE); + sysbus_connect_irq(sysbusdev, 0, irq); + sysbus_connect_irq(sysbusdev, 1, fiq); + for (i = 0; i < AW_A10_PIC_INT_NR; i++) { + s->irq[i] = qdev_get_gpio_in(DEVICE(&s->intc), i); + } + + object_property_set_bool(OBJECT(&s->timer), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + sysbusdev = SYS_BUS_DEVICE(&s->timer); + sysbus_mmio_map(sysbusdev, 0, AW_A10_PIT_REG_BASE); + sysbus_connect_irq(sysbusdev, 0, s->irq[22]); + sysbus_connect_irq(sysbusdev, 1, s->irq[23]); + sysbus_connect_irq(sysbusdev, 2, s->irq[24]); + sysbus_connect_irq(sysbusdev, 3, s->irq[25]); + sysbus_connect_irq(sysbusdev, 4, s->irq[67]); + sysbus_connect_irq(sysbusdev, 5, s->irq[68]); + + serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1], + 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); +} + +static void aw_a10_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = aw_a10_realize; +} + +static const TypeInfo aw_a10_type_info = { + .name = TYPE_AW_A10, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AwA10State), + .instance_init = aw_a10_init, + .class_init = aw_a10_class_init, +}; + +static void aw_a10_register_types(void) +{ + type_register_static(&aw_a10_type_info); +} + +type_init(aw_a10_register_types) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 55d552f3a8..1c1b0e5258 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -17,18 +17,55 @@ #include "sysemu/device_tree.h" #include "qemu/config-file.h" +/* Kernel boot protocol is specified in the kernel docs + * Documentation/arm/Booting and Documentation/arm64/booting.txt + * They have different preferred image load offsets from system RAM base. + */ #define KERNEL_ARGS_ADDR 0x100 #define KERNEL_LOAD_ADDR 0x00010000 +#define KERNEL64_LOAD_ADDR 0x00080000 + +typedef enum { + FIXUP_NONE = 0, /* do nothing */ + FIXUP_TERMINATOR, /* end of insns */ + FIXUP_BOARDID, /* overwrite with board ID number */ + FIXUP_ARGPTR, /* overwrite with pointer to kernel args */ + FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */ + FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */ + FIXUP_BOOTREG, /* overwrite with boot register address */ + FIXUP_DSB, /* overwrite with correct DSB insn for cpu */ + FIXUP_MAX, +} FixupType; + +typedef struct ARMInsnFixup { + uint32_t insn; + FixupType fixup; +} ARMInsnFixup; + +static const ARMInsnFixup bootloader_aarch64[] = { + { 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */ + { 0xaa1f03e1 }, /* mov x1, xzr */ + { 0xaa1f03e2 }, /* mov x2, xzr */ + { 0xaa1f03e3 }, /* mov x3, xzr */ + { 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */ + { 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */ + { 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */ + { 0 }, /* .word @DTB Higher 32-bits */ + { 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */ + { 0 }, /* .word @Kernel Entry Higher 32-bits */ + { 0, FIXUP_TERMINATOR } +}; /* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */ -static uint32_t bootloader[] = { - 0xe3a00000, /* mov r0, #0 */ - 0xe59f1004, /* ldr r1, [pc, #4] */ - 0xe59f2004, /* ldr r2, [pc, #4] */ - 0xe59ff004, /* ldr pc, [pc, #4] */ - 0, /* Board ID */ - 0, /* Address of kernel args. Set by integratorcp_init. */ - 0 /* Kernel entry point. Set by integratorcp_init. */ +static const ARMInsnFixup bootloader[] = { + { 0xe3a00000 }, /* mov r0, #0 */ + { 0xe59f1004 }, /* ldr r1, [pc, #4] */ + { 0xe59f2004 }, /* ldr r2, [pc, #4] */ + { 0xe59ff004 }, /* ldr pc, [pc, #4] */ + { 0, FIXUP_BOARDID }, + { 0, FIXUP_ARGPTR }, + { 0, FIXUP_ENTRYPOINT }, + { 0, FIXUP_TERMINATOR } }; /* Handling for secondary CPU boot in a multicore system. @@ -48,39 +85,83 @@ static uint32_t bootloader[] = { #define DSB_INSN 0xf57ff04f #define CP15_DSB_INSN 0xee070f9a /* mcr cp15, 0, r0, c7, c10, 4 */ -static uint32_t smpboot[] = { - 0xe59f2028, /* ldr r2, gic_cpu_if */ - 0xe59f0028, /* ldr r0, startaddr */ - 0xe3a01001, /* mov r1, #1 */ - 0xe5821000, /* str r1, [r2] - set GICC_CTLR.Enable */ - 0xe3a010ff, /* mov r1, #0xff */ - 0xe5821004, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */ - DSB_INSN, /* dsb */ - 0xe320f003, /* wfi */ - 0xe5901000, /* ldr r1, [r0] */ - 0xe1110001, /* tst r1, r1 */ - 0x0afffffb, /* beq <wfi> */ - 0xe12fff11, /* bx r1 */ - 0, /* gic_cpu_if: base address of GIC CPU interface */ - 0 /* bootreg: Boot register address is held here */ +static const ARMInsnFixup smpboot[] = { + { 0xe59f2028 }, /* ldr r2, gic_cpu_if */ + { 0xe59f0028 }, /* ldr r0, bootreg_addr */ + { 0xe3a01001 }, /* mov r1, #1 */ + { 0xe5821000 }, /* str r1, [r2] - set GICC_CTLR.Enable */ + { 0xe3a010ff }, /* mov r1, #0xff */ + { 0xe5821004 }, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */ + { 0, FIXUP_DSB }, /* dsb */ + { 0xe320f003 }, /* wfi */ + { 0xe5901000 }, /* ldr r1, [r0] */ + { 0xe1110001 }, /* tst r1, r1 */ + { 0x0afffffb }, /* beq <wfi> */ + { 0xe12fff11 }, /* bx r1 */ + { 0, FIXUP_GIC_CPU_IF }, /* gic_cpu_if: .word 0x.... */ + { 0, FIXUP_BOOTREG }, /* bootreg_addr: .word 0x.... */ + { 0, FIXUP_TERMINATOR } }; +static void write_bootloader(const char *name, hwaddr addr, + const ARMInsnFixup *insns, uint32_t *fixupcontext) +{ + /* Fix up the specified bootloader fragment and write it into + * guest memory using rom_add_blob_fixed(). fixupcontext is + * an array giving the values to write in for the fixup types + * which write a value into the code array. + */ + int i, len; + uint32_t *code; + + len = 0; + while (insns[len].fixup != FIXUP_TERMINATOR) { + len++; + } + + code = g_new0(uint32_t, len); + + for (i = 0; i < len; i++) { + uint32_t insn = insns[i].insn; + FixupType fixup = insns[i].fixup; + + switch (fixup) { + case FIXUP_NONE: + break; + case FIXUP_BOARDID: + case FIXUP_ARGPTR: + case FIXUP_ENTRYPOINT: + case FIXUP_GIC_CPU_IF: + case FIXUP_BOOTREG: + case FIXUP_DSB: + insn = fixupcontext[fixup]; + break; + default: + abort(); + } + code[i] = tswap32(insn); + } + + rom_add_blob_fixed(name, code, len * sizeof(uint32_t), addr); + + g_free(code); +} + static void default_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info) { - int n; - smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr; - smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr; - for (n = 0; n < ARRAY_SIZE(smpboot); n++) { - /* Replace DSB with the pre-v7 DSB if necessary. */ - if (!arm_feature(&cpu->env, ARM_FEATURE_V7) && - smpboot[n] == DSB_INSN) { - smpboot[n] = CP15_DSB_INSN; - } - smpboot[n] = tswap32(smpboot[n]); + uint32_t fixupcontext[FIXUP_MAX]; + + fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr; + fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr; + if (arm_feature(&cpu->env, ARM_FEATURE_V7)) { + fixupcontext[FIXUP_DSB] = DSB_INSN; + } else { + fixupcontext[FIXUP_DSB] = CP15_DSB_INSN; } - rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot), - info->smp_loader_start); + + write_bootloader("smpboot", info->smp_loader_start, + smpboot, fixupcontext); } static void default_reset_secondary(ARMCPU *cpu, @@ -254,8 +335,8 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo) } } - acells = qemu_devtree_getprop_cell(fdt, "/", "#address-cells"); - scells = qemu_devtree_getprop_cell(fdt, "/", "#size-cells"); + acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells"); + scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells"); if (acells == 0 || scells == 0) { fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n"); goto fail; @@ -270,17 +351,17 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo) goto fail; } - rc = qemu_devtree_setprop_sized_cells(fdt, "/memory", "reg", - acells, binfo->loader_start, - scells, binfo->ram_size); + rc = qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg", + acells, binfo->loader_start, + scells, binfo->ram_size); if (rc < 0) { fprintf(stderr, "couldn't set /memory/reg\n"); goto fail; } if (binfo->kernel_cmdline && *binfo->kernel_cmdline) { - rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", - binfo->kernel_cmdline); + rc = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", + binfo->kernel_cmdline); if (rc < 0) { fprintf(stderr, "couldn't set /chosen/bootargs\n"); goto fail; @@ -288,15 +369,15 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo) } if (binfo->initrd_size) { - rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", - binfo->initrd_start); + rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + binfo->initrd_start); if (rc < 0) { fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); goto fail; } - rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", - binfo->initrd_start + binfo->initrd_size); + rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + binfo->initrd_start + binfo->initrd_size); if (rc < 0) { fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); goto fail; @@ -307,7 +388,7 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo) binfo->modify_dtb(binfo, fdt); } - qemu_devtree_dumpdtb(fdt, size); + qemu_fdt_dumpdtb(fdt, size); cpu_physical_memory_write(addr, fdt, size); @@ -334,7 +415,12 @@ static void do_cpu_reset(void *opaque) env->thumb = info->entry & 1; } else { if (CPU(cpu) == first_cpu) { - env->regs[15] = info->loader_start; + if (env->aarch64) { + env->pc = info->loader_start; + } else { + env->regs[15] = info->loader_start; + } + if (!info->dtb_filename) { if (old_param) { set_kernel_args_old(info); @@ -354,11 +440,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) CPUState *cs = CPU(cpu); int kernel_size; int initrd_size; - int n; int is_linux = 0; uint64_t elf_entry; - hwaddr entry; + hwaddr entry, kernel_load_offset; int big_endian; + static const ARMInsnFixup *primary_loader; /* Load the kernel. */ if (!info->kernel_filename) { @@ -368,6 +454,14 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) return; } + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + primary_loader = bootloader_aarch64; + kernel_load_offset = KERNEL64_LOAD_ADDR; + } else { + primary_loader = bootloader; + kernel_load_offset = KERNEL_LOAD_ADDR; + } + info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); if (!info->secondary_cpu_reset_hook) { @@ -408,9 +502,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) &is_linux); } if (kernel_size < 0) { - entry = info->loader_start + KERNEL_LOAD_ADDR; + entry = info->loader_start + kernel_load_offset; kernel_size = load_image_targphys(info->kernel_filename, entry, - info->ram_size - KERNEL_LOAD_ADDR); + info->ram_size - kernel_load_offset); is_linux = 1; } if (kernel_size < 0) { @@ -420,6 +514,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) } info->entry = entry; if (is_linux) { + uint32_t fixupcontext[FIXUP_MAX]; + if (info->initrd_filename) { initrd_size = load_ramdisk(info->initrd_filename, info->initrd_start, @@ -441,7 +537,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) } info->initrd_size = initrd_size; - bootloader[4] = info->board_id; + fixupcontext[FIXUP_BOARDID] = info->board_id; /* for device tree boot, we pass the DTB directly in r2. Otherwise * we point to the kernel args. @@ -456,9 +552,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) if (load_dtb(dtb_start, info)) { exit(1); } - bootloader[5] = dtb_start; + fixupcontext[FIXUP_ARGPTR] = dtb_start; } else { - bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR; + fixupcontext[FIXUP_ARGPTR] = info->loader_start + KERNEL_ARGS_ADDR; if (info->ram_size >= (1ULL << 32)) { fprintf(stderr, "qemu: RAM size must be less than 4GB to boot" " Linux kernel using ATAGS (try passing a device tree" @@ -466,12 +562,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) exit(1); } } - bootloader[6] = entry; - for (n = 0; n < sizeof(bootloader) / 4; n++) { - bootloader[n] = tswap32(bootloader[n]); - } - rom_add_blob_fixed("bootloader", bootloader, sizeof(bootloader), - info->loader_start); + fixupcontext[FIXUP_ENTRYPOINT] = entry; + + write_bootloader("bootloader", info->loader_start, + primary_loader, fixupcontext); + if (info->nb_cpus > 1) { info->write_secondary_boot(cpu, info); } diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c new file mode 100644 index 0000000000..3fcb6d22f5 --- /dev/null +++ b/hw/arm/cubieboard.c @@ -0,0 +1,69 @@ +/* + * cubieboard emulation + * + * Copyright (C) 2013 Li Guang + * Written by Li Guang <lig.fnst@cn.fujitsu.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "hw/sysbus.h" +#include "hw/devices.h" +#include "hw/boards.h" +#include "hw/arm/allwinner-a10.h" + +static struct arm_boot_info cubieboard_binfo = { + .loader_start = AW_A10_SDRAM_BASE, + .board_id = 0x1008, +}; + +typedef struct CubieBoardState { + AwA10State *a10; + MemoryRegion sdram; +} CubieBoardState; + +static void cubieboard_init(QEMUMachineInitArgs *args) +{ + CubieBoardState *s = g_new(CubieBoardState, 1); + Error *err = NULL; + + s->a10 = AW_A10(object_new(TYPE_AW_A10)); + object_property_set_bool(OBJECT(s->a10), true, "realized", &err); + if (err != NULL) { + error_report("Couldn't realize Allwinner A10: %s\n", + error_get_pretty(err)); + exit(1); + } + + memory_region_init_ram(&s->sdram, NULL, "cubieboard.ram", args->ram_size); + vmstate_register_ram_global(&s->sdram); + memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE, + &s->sdram); + + cubieboard_binfo.ram_size = args->ram_size; + cubieboard_binfo.kernel_filename = args->kernel_filename; + cubieboard_binfo.kernel_cmdline = args->kernel_cmdline; + arm_load_kernel(&s->a10->cpu, &cubieboard_binfo); +} + +static QEMUMachine cubieboard_machine = { + .name = "cubieboard", + .desc = "cubietech cubieboard", + .init = cubieboard_init, +}; + + +static void cubieboard_machine_init(void) +{ + qemu_register_machine(&cubieboard_machine); +} + +machine_init(cubieboard_machine_init) diff --git a/hw/arm/digic.c b/hw/arm/digic.c new file mode 100644 index 0000000000..ec8c330602 --- /dev/null +++ b/hw/arm/digic.c @@ -0,0 +1,115 @@ +/* + * QEMU model of the Canon DIGIC SoC. + * + * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com> + * + * This model is based on reverse engineering efforts + * made by CHDK (http://chdk.wikia.com) and + * Magic Lantern (http://www.magiclantern.fm) projects + * contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "hw/arm/digic.h" + +#define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100) + +#define DIGIC_UART_BASE 0xc0800000 + +static void digic_init(Object *obj) +{ + DigicState *s = DIGIC(obj); + DeviceState *dev; + int i; + + object_initialize(&s->cpu, sizeof(s->cpu), "arm946-" TYPE_ARM_CPU); + object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL); + + for (i = 0; i < DIGIC4_NB_TIMERS; i++) { +#define DIGIC_TIMER_NAME_MLEN 11 + char name[DIGIC_TIMER_NAME_MLEN]; + + object_initialize(&s->timer[i], sizeof(s->timer[i]), TYPE_DIGIC_TIMER); + dev = DEVICE(&s->timer[i]); + qdev_set_parent_bus(dev, sysbus_get_default()); + snprintf(name, DIGIC_TIMER_NAME_MLEN, "timer[%d]", i); + object_property_add_child(obj, name, OBJECT(&s->timer[i]), NULL); + } + + object_initialize(&s->uart, sizeof(s->uart), TYPE_DIGIC_UART); + dev = DEVICE(&s->uart); + qdev_set_parent_bus(dev, sysbus_get_default()); + object_property_add_child(obj, "uart", OBJECT(&s->uart), NULL); +} + +static void digic_realize(DeviceState *dev, Error **errp) +{ + DigicState *s = DIGIC(dev); + Error *err = NULL; + SysBusDevice *sbd; + int i; + + object_property_set_bool(OBJECT(&s->cpu), true, "reset-hivecs", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + for (i = 0; i < DIGIC4_NB_TIMERS; i++) { + object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + sbd = SYS_BUS_DEVICE(&s->timer[i]); + sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i)); + } + + object_property_set_bool(OBJECT(&s->uart), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + sbd = SYS_BUS_DEVICE(&s->uart); + sysbus_mmio_map(sbd, 0, DIGIC_UART_BASE); +} + +static void digic_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = digic_realize; +} + +static const TypeInfo digic_type_info = { + .name = TYPE_DIGIC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(DigicState), + .instance_init = digic_init, + .class_init = digic_class_init, +}; + +static void digic_register_types(void) +{ + type_register_static(&digic_type_info); +} + +type_init(digic_register_types) diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c new file mode 100644 index 0000000000..32fc30a69d --- /dev/null +++ b/hw/arm/digic_boards.c @@ -0,0 +1,162 @@ +/* + * QEMU model of the Canon DIGIC boards (cameras indeed :). + * + * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com> + * + * This model is based on reverse engineering efforts + * made by CHDK (http://chdk.wikia.com) and + * Magic Lantern (http://www.magiclantern.fm) projects + * contributors. + * + * See docs here: + * http://magiclantern.wikia.com/wiki/Register_Map + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "hw/boards.h" +#include "exec/address-spaces.h" +#include "qemu/error-report.h" +#include "hw/arm/digic.h" +#include "hw/block/flash.h" +#include "hw/loader.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" + +#define DIGIC4_ROM0_BASE 0xf0000000 +#define DIGIC4_ROM1_BASE 0xf8000000 +#define DIGIC4_ROM_MAX_SIZE 0x08000000 + +typedef struct DigicBoardState { + DigicState *digic; + MemoryRegion ram; +} DigicBoardState; + +typedef struct DigicBoard { + hwaddr ram_size; + void (*add_rom0)(DigicBoardState *, hwaddr, const char *); + const char *rom0_def_filename; + void (*add_rom1)(DigicBoardState *, hwaddr, const char *); + const char *rom1_def_filename; +} DigicBoard; + +static void digic4_board_setup_ram(DigicBoardState *s, hwaddr ram_size) +{ + memory_region_init_ram(&s->ram, NULL, "ram", ram_size); + memory_region_add_subregion(get_system_memory(), 0, &s->ram); + vmstate_register_ram_global(&s->ram); +} + +static void digic4_board_init(DigicBoard *board) +{ + Error *err = NULL; + + DigicBoardState *s = g_new(DigicBoardState, 1); + + s->digic = DIGIC(object_new(TYPE_DIGIC)); + object_property_set_bool(OBJECT(s->digic), true, "realized", &err); + if (err != NULL) { + error_report("Couldn't realize DIGIC SoC: %s\n", + error_get_pretty(err)); + exit(1); + } + + digic4_board_setup_ram(s, board->ram_size); + + if (board->add_rom0) { + board->add_rom0(s, DIGIC4_ROM0_BASE, board->rom0_def_filename); + } + + if (board->add_rom1) { + board->add_rom1(s, DIGIC4_ROM1_BASE, board->rom1_def_filename); + } +} + +static void digic_load_rom(DigicBoardState *s, hwaddr addr, + hwaddr max_size, const char *def_filename) +{ + target_long rom_size; + const char *filename; + + if (qtest_enabled()) { + /* qtest runs no code so don't attempt a ROM load which + * could fail and result in a spurious test failure. + */ + return; + } + + if (bios_name) { + filename = bios_name; + } else { + filename = def_filename; + } + + if (filename) { + char *fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename); + + if (!fn) { + error_report("Couldn't find rom image '%s'.\n", filename); + exit(1); + } + + rom_size = load_image_targphys(fn, addr, max_size); + if (rom_size < 0 || rom_size > max_size) { + error_report("Couldn't load rom image '%s'.\n", filename); + exit(1); + } + } +} + +/* + * Samsung K8P3215UQB + * 64M Bit (4Mx16) Page Mode / Multi-Bank NOR Flash Memory + */ +static void digic4_add_k8p3215uqb_rom(DigicBoardState *s, hwaddr addr, + const char *def_filename) +{ +#define FLASH_K8P3215UQB_SIZE (4 * 1024 * 1024) +#define FLASH_K8P3215UQB_SECTOR_SIZE (64 * 1024) + + pflash_cfi02_register(addr, NULL, "pflash", FLASH_K8P3215UQB_SIZE, + NULL, FLASH_K8P3215UQB_SECTOR_SIZE, + FLASH_K8P3215UQB_SIZE / FLASH_K8P3215UQB_SECTOR_SIZE, + DIGIC4_ROM_MAX_SIZE / FLASH_K8P3215UQB_SIZE, + 4, + 0x00EC, 0x007E, 0x0003, 0x0001, + 0x0555, 0x2aa, 0); + + digic_load_rom(s, addr, FLASH_K8P3215UQB_SIZE, def_filename); +} + +static DigicBoard digic4_board_canon_a1100 = { + .ram_size = 64 * 1024 * 1024, + .add_rom1 = digic4_add_k8p3215uqb_rom, + .rom1_def_filename = "canon-a1100-rom1.bin", +}; + +static void canon_a1100_init(QEMUMachineInitArgs *args) +{ + digic4_board_init(&digic4_board_canon_a1100); +} + +static QEMUMachine canon_a1100 = { + .name = "canon-a1100", + .desc = "Canon PowerShot A1100 IS", + .init = &canon_a1100_init, +}; + +static void digic_register_machines(void) +{ + qemu_register_machine(&canon_a1100); +} + +machine_init(digic_register_machines) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index fe98ef10cb..c75b425c01 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -26,12 +26,13 @@ #include "hw/boards.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" +#include "qemu/error-report.h" -#define SMP_BOOT_ADDR 0x100 -#define SMP_BOOT_REG 0x40 -#define GIC_BASE_ADDR 0xfff10000 +#define SMP_BOOT_ADDR 0x100 +#define SMP_BOOT_REG 0x40 +#define MPCORE_PERIPHBASE 0xfff10000 -#define NIRQ_GIC 160 +#define NIRQ_GIC 160 /* Board init. */ @@ -54,7 +55,7 @@ static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info) 0xe1110001, /* tst r1, r1 */ 0x0afffffb, /* beq <wfi> */ 0xe12fff11, /* bx r1 */ - GIC_BASE_ADDR /* privbase: gic address. */ + MPCORE_PERIPHBASE /* privbase: MPCore peripheral base address. */ }; for (n = 0; n < ARRAY_SIZE(smpboot); n++) { smpboot[n] = tswap32(smpboot[n]); @@ -229,15 +230,23 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine) } for (n = 0; n < smp_cpus; n++) { + ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); ARMCPU *cpu; - cpu = cpu_arm_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); + Error *err = NULL; + + cpu = ARM_CPU(object_new(object_class_get_name(oc))); + + object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", + &err); + if (err) { + error_report("%s", error_get_pretty(err)); + exit(1); + } + object_property_set_bool(OBJECT(cpu), true, "realized", &err); + if (err) { + error_report("%s", error_get_pretty(err)); exit(1); } - - /* This will become a QOM property eventually */ - cpu->reset_cbar = GIC_BASE_ADDR; cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ); } @@ -279,7 +288,7 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine) qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC); qdev_init_nofail(dev); busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, GIC_BASE_ADDR); + sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); for (n = 0; n < smp_cpus; n++) { sysbus_connect_irq(busdev, n, cpu_irq[n]); } diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index f48de00a1a..ef1707aef0 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -419,13 +419,13 @@ static int add_virtio_mmio_node(void *fdt, uint32_t acells, uint32_t scells, int rc; char *nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, addr); - rc = qemu_devtree_add_subnode(fdt, nodename); - rc |= qemu_devtree_setprop_string(fdt, nodename, - "compatible", "virtio,mmio"); - rc |= qemu_devtree_setprop_sized_cells(fdt, nodename, "reg", - acells, addr, scells, size); - qemu_devtree_setprop_cells(fdt, nodename, "interrupt-parent", intc); - qemu_devtree_setprop_cells(fdt, nodename, "interrupts", 0, irq, 1); + rc = qemu_fdt_add_subnode(fdt, nodename); + rc |= qemu_fdt_setprop_string(fdt, nodename, + "compatible", "virtio,mmio"); + rc |= qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", + acells, addr, scells, size); + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", intc); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", 0, irq, 1); g_free(nodename); if (rc) { return -1; @@ -456,8 +456,8 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt) uint32_t acells, scells, intc; const VEDBoardInfo *daughterboard = (const VEDBoardInfo *)info; - acells = qemu_devtree_getprop_cell(fdt, "/", "#address-cells"); - scells = qemu_devtree_getprop_cell(fdt, "/", "#size-cells"); + acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells"); + scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells"); intc = find_int_controller(fdt); if (!intc) { /* Not fatal, we just won't provide virtio. This will @@ -480,6 +480,36 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt) } } + +/* Open code a private version of pflash registration since we + * need to set non-default device width for VExpress platform. + */ +static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, + DriveInfo *di) +{ + DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); + + if (di && qdev_prop_set_drive(dev, "drive", di->bdrv)) { + abort(); + } + + qdev_prop_set_uint32(dev, "num-blocks", + VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE); + qdev_prop_set_uint64(dev, "sector-length", VEXPRESS_FLASH_SECT_SIZE); + qdev_prop_set_uint8(dev, "width", 4); + qdev_prop_set_uint8(dev, "device-width", 2); + qdev_prop_set_uint8(dev, "big-endian", 0); + qdev_prop_set_uint16(dev, "id0", 0x89); + qdev_prop_set_uint16(dev, "id1", 0x18); + qdev_prop_set_uint16(dev, "id2", 0x00); + qdev_prop_set_uint16(dev, "id3", 0x00); + qdev_prop_set_string(dev, "name", name); + qdev_init_nofail(dev); + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01"); +} + static void vexpress_common_init(VEDBoardInfo *daughterboard, QEMUMachineInitArgs *args) { @@ -561,11 +591,8 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard, sysbus_create_simple("pl111", map[VE_CLCD], pic[14]); dinfo = drive_get_next(IF_PFLASH); - pflash0 = pflash_cfi01_register(map[VE_NORFLASH0], NULL, "vexpress.flash0", - VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL, - VEXPRESS_FLASH_SECT_SIZE, - VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4, - 0x00, 0x89, 0x00, 0x18, 0); + pflash0 = ve_pflash_cfi01_register(map[VE_NORFLASH0], "vexpress.flash0", + dinfo); if (!pflash0) { fprintf(stderr, "vexpress: error registering flash 0.\n"); exit(1); @@ -580,11 +607,8 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard, } dinfo = drive_get_next(IF_PFLASH); - if (!pflash_cfi01_register(map[VE_NORFLASH1], NULL, "vexpress.flash1", - VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL, - VEXPRESS_FLASH_SECT_SIZE, - VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4, - 0x00, 0x89, 0x00, 0x18, 0)) { + if (!ve_pflash_cfi01_register(map[VE_NORFLASH1], "vexpress.flash1", + dinfo)) { fprintf(stderr, "vexpress: error registering flash 1.\n"); exit(1); } diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 9531b5a574..517f2fe30f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -156,42 +156,42 @@ static void create_fdt(VirtBoardInfo *vbi) vbi->fdt = fdt; /* Header */ - qemu_devtree_setprop_string(fdt, "/", "compatible", "linux,dummy-virt"); - qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 0x2); - qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 0x2); + qemu_fdt_setprop_string(fdt, "/", "compatible", "linux,dummy-virt"); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); /* * /chosen and /memory nodes must exist for load_dtb * to fill in necessary properties later */ - qemu_devtree_add_subnode(fdt, "/chosen"); - qemu_devtree_add_subnode(fdt, "/memory"); - qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory"); + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_add_subnode(fdt, "/memory"); + qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory"); /* Clock node, for the benefit of the UART. The kernel device tree * binding documentation claims the PL011 node clock properties are * optional but in practice if you omit them the kernel refuses to * probe for the device. */ - vbi->clock_phandle = qemu_devtree_alloc_phandle(fdt); - qemu_devtree_add_subnode(fdt, "/apb-pclk"); - qemu_devtree_setprop_string(fdt, "/apb-pclk", "compatible", "fixed-clock"); - qemu_devtree_setprop_cell(fdt, "/apb-pclk", "#clock-cells", 0x0); - qemu_devtree_setprop_cell(fdt, "/apb-pclk", "clock-frequency", 24000000); - qemu_devtree_setprop_string(fdt, "/apb-pclk", "clock-output-names", + vbi->clock_phandle = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_add_subnode(fdt, "/apb-pclk"); + qemu_fdt_setprop_string(fdt, "/apb-pclk", "compatible", "fixed-clock"); + qemu_fdt_setprop_cell(fdt, "/apb-pclk", "#clock-cells", 0x0); + qemu_fdt_setprop_cell(fdt, "/apb-pclk", "clock-frequency", 24000000); + qemu_fdt_setprop_string(fdt, "/apb-pclk", "clock-output-names", "clk24mhz"); - qemu_devtree_setprop_cell(fdt, "/apb-pclk", "phandle", vbi->clock_phandle); + qemu_fdt_setprop_cell(fdt, "/apb-pclk", "phandle", vbi->clock_phandle); /* No PSCI for TCG yet */ if (kvm_enabled()) { - qemu_devtree_add_subnode(fdt, "/psci"); - qemu_devtree_setprop_string(fdt, "/psci", "compatible", "arm,psci"); - qemu_devtree_setprop_string(fdt, "/psci", "method", "hvc"); - qemu_devtree_setprop_cell(fdt, "/psci", "cpu_suspend", + qemu_fdt_add_subnode(fdt, "/psci"); + qemu_fdt_setprop_string(fdt, "/psci", "compatible", "arm,psci"); + qemu_fdt_setprop_string(fdt, "/psci", "method", "hvc"); + qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend", PSCI_FN_CPU_SUSPEND); - qemu_devtree_setprop_cell(fdt, "/psci", "cpu_off", PSCI_FN_CPU_OFF); - qemu_devtree_setprop_cell(fdt, "/psci", "cpu_on", PSCI_FN_CPU_ON); - qemu_devtree_setprop_cell(fdt, "/psci", "migrate", PSCI_FN_MIGRATE); + qemu_fdt_setprop_cell(fdt, "/psci", "cpu_off", PSCI_FN_CPU_OFF); + qemu_fdt_setprop_cell(fdt, "/psci", "cpu_on", PSCI_FN_CPU_ON); + qemu_fdt_setprop_cell(fdt, "/psci", "migrate", PSCI_FN_MIGRATE); } } @@ -206,10 +206,10 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi) irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vbi->smp_cpus) - 1); - qemu_devtree_add_subnode(vbi->fdt, "/timer"); - qemu_devtree_setprop_string(vbi->fdt, "/timer", + qemu_fdt_add_subnode(vbi->fdt, "/timer"); + qemu_fdt_setprop_string(vbi->fdt, "/timer", "compatible", "arm,armv7-timer"); - qemu_devtree_setprop_cells(vbi->fdt, "/timer", "interrupts", + qemu_fdt_setprop_cells(vbi->fdt, "/timer", "interrupts", GIC_FDT_IRQ_TYPE_PPI, 13, irqflags, GIC_FDT_IRQ_TYPE_PPI, 14, irqflags, GIC_FDT_IRQ_TYPE_PPI, 11, irqflags, @@ -220,25 +220,25 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) { int cpu; - qemu_devtree_add_subnode(vbi->fdt, "/cpus"); - qemu_devtree_setprop_cell(vbi->fdt, "/cpus", "#address-cells", 0x1); - qemu_devtree_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_add_subnode(vbi->fdt, "/cpus"); + qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#address-cells", 0x1); + qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0); for (cpu = vbi->smp_cpus - 1; cpu >= 0; cpu--) { char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu); ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); - qemu_devtree_add_subnode(vbi->fdt, nodename); - qemu_devtree_setprop_string(vbi->fdt, nodename, "device_type", "cpu"); - qemu_devtree_setprop_string(vbi->fdt, nodename, "compatible", + qemu_fdt_add_subnode(vbi->fdt, nodename); + qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "cpu"); + qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", armcpu->dtb_compatible); if (vbi->smp_cpus > 1) { - qemu_devtree_setprop_string(vbi->fdt, nodename, + qemu_fdt_setprop_string(vbi->fdt, nodename, "enable-method", "psci"); } - qemu_devtree_setprop_cell(vbi->fdt, nodename, "reg", cpu); + qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", cpu); g_free(nodename); } } @@ -247,20 +247,20 @@ static void fdt_add_gic_node(const VirtBoardInfo *vbi) { uint32_t gic_phandle; - gic_phandle = qemu_devtree_alloc_phandle(vbi->fdt); - qemu_devtree_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle); + gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt); + qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle); - qemu_devtree_add_subnode(vbi->fdt, "/intc"); - qemu_devtree_setprop_string(vbi->fdt, "/intc", "compatible", + qemu_fdt_add_subnode(vbi->fdt, "/intc"); + qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible", vbi->gic_compatible); - qemu_devtree_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3); - qemu_devtree_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0); - qemu_devtree_setprop_sized_cells(vbi->fdt, "/intc", "reg", + qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3); + qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0); + qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg", 2, vbi->memmap[VIRT_GIC_DIST].base, 2, vbi->memmap[VIRT_GIC_DIST].size, 2, vbi->memmap[VIRT_GIC_CPU].base, 2, vbi->memmap[VIRT_GIC_CPU].size); - qemu_devtree_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle); + qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle); } static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic) @@ -275,18 +275,18 @@ static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic) sysbus_create_simple("pl011", base, pic[irq]); nodename = g_strdup_printf("/pl011@%" PRIx64, base); - qemu_devtree_add_subnode(vbi->fdt, nodename); + qemu_fdt_add_subnode(vbi->fdt, nodename); /* Note that we can't use setprop_string because of the embedded NUL */ - qemu_devtree_setprop(vbi->fdt, nodename, "compatible", + qemu_fdt_setprop(vbi->fdt, nodename, "compatible", compat, sizeof(compat)); - qemu_devtree_setprop_sized_cells(vbi->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", 2, base, 2, size); - qemu_devtree_setprop_cells(vbi->fdt, nodename, "interrupts", + qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_SPI, irq, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); - qemu_devtree_setprop_cells(vbi->fdt, nodename, "clocks", + qemu_fdt_setprop_cells(vbi->fdt, nodename, "clocks", vbi->clock_phandle, vbi->clock_phandle); - qemu_devtree_setprop(vbi->fdt, nodename, "clock-names", + qemu_fdt_setprop(vbi->fdt, nodename, "clock-names", clocknames, sizeof(clocknames)); g_free(nodename); } @@ -314,14 +314,14 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) hwaddr base = vbi->memmap[VIRT_MMIO].base + i * size; nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base); - qemu_devtree_add_subnode(vbi->fdt, nodename); - qemu_devtree_setprop_string(vbi->fdt, nodename, - "compatible", "virtio,mmio"); - qemu_devtree_setprop_sized_cells(vbi->fdt, nodename, "reg", - 2, base, 2, size); - qemu_devtree_setprop_cells(vbi->fdt, nodename, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, irq, - GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + qemu_fdt_add_subnode(vbi->fdt, nodename); + qemu_fdt_setprop_string(vbi->fdt, nodename, + "compatible", "virtio,mmio"); + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + 2, base, 2, size); + qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq, + GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); g_free(nodename); } } diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 46924a0391..17251c7a65 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -25,6 +25,7 @@ #include "sysemu/blockdev.h" #include "hw/loader.h" #include "hw/ssi.h" +#include "qemu/error-report.h" #define NUM_SPI_FLASHES 4 #define NUM_QSPI_FLASHES 2 @@ -35,6 +36,8 @@ #define IRQ_OFFSET 32 /* pic interrupts start from index 32 */ +#define MPCORE_PERIPHBASE 0xF8F00000 + static const int dma_irqs[8] = { 46, 47, 48, 49, 72, 73, 74, 75 }; @@ -102,6 +105,7 @@ static void zynq_init(QEMUMachineInitArgs *args) const char *kernel_filename = args->kernel_filename; const char *kernel_cmdline = args->kernel_cmdline; const char *initrd_filename = args->initrd_filename; + ObjectClass *cpu_oc; ARMCPU *cpu; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ext_ram = g_new(MemoryRegion, 1); @@ -110,15 +114,24 @@ static void zynq_init(QEMUMachineInitArgs *args) SysBusDevice *busdev; qemu_irq pic[64]; NICInfo *nd; + Error *err = NULL; int n; if (!cpu_model) { cpu_model = "cortex-a9"; } + cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); + + cpu = ARM_CPU(object_new(object_class_get_name(cpu_oc))); - cpu = cpu_arm_init(cpu_model); - if (!cpu) { - fprintf(stderr, "Unable to find CPU definition\n"); + object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", &err); + if (err) { + error_report("%s", error_get_pretty(err)); + exit(1); + } + object_property_set_bool(OBJECT(cpu), true, "realized", &err); + if (err) { + error_report("%s", error_get_pretty(err)); exit(1); } @@ -154,7 +167,7 @@ static void zynq_init(QEMUMachineInitArgs *args) qdev_prop_set_uint32(dev, "num-cpu", 1); qdev_init_nofail(dev); busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, 0xF8F00000); + sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 018a9677ba..0c95d53dca 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -40,6 +40,7 @@ #include "hw/block/flash.h" #include "block/block.h" #include "qemu/timer.h" +#include "qemu/bitops.h" #include "exec/address-spaces.h" #include "qemu/host-utils.h" #include "hw/sysbus.h" @@ -71,7 +72,9 @@ struct pflash_t { BlockDriverState *bs; uint32_t nb_blocs; uint64_t sector_len; - uint8_t width; + uint8_t bank_width; + uint8_t device_width; /* If 0, device width not specified. */ + uint8_t max_device_width; /* max device width in bytes */ uint8_t be; uint8_t wcycle; /* if 0, the flash is read normally */ int ro; @@ -116,6 +119,119 @@ static void pflash_timer (void *opaque) pfl->cmd = 0; } +/* Perform a CFI query based on the bank width of the flash. + * If this code is called we know we have a device_width set for + * this flash. + */ +static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset) +{ + int i; + uint32_t resp = 0; + hwaddr boff; + + /* Adjust incoming offset to match expected device-width + * addressing. CFI query addresses are always specified in terms of + * the maximum supported width of the device. This means that x8 + * devices and x8/x16 devices in x8 mode behave differently. For + * devices that are not used at their max width, we will be + * provided with addresses that use higher address bits than + * expected (based on the max width), so we will shift them lower + * so that they will match the addresses used when + * device_width==max_device_width. + */ + boff = offset >> (ctz32(pfl->bank_width) + + ctz32(pfl->max_device_width) - ctz32(pfl->device_width)); + + if (boff > pfl->cfi_len) { + return 0; + } + /* Now we will construct the CFI response generated by a single + * device, then replicate that for all devices that make up the + * bus. For wide parts used in x8 mode, CFI query responses + * are different than native byte-wide parts. + */ + resp = pfl->cfi_table[boff]; + if (pfl->device_width != pfl->max_device_width) { + /* The only case currently supported is x8 mode for a + * wider part. + */ + if (pfl->device_width != 1 || pfl->bank_width > 4) { + DPRINTF("%s: Unsupported device configuration: " + "device_width=%d, max_device_width=%d\n", + __func__, pfl->device_width, + pfl->max_device_width); + return 0; + } + /* CFI query data is repeated, rather than zero padded for + * wide devices used in x8 mode. + */ + for (i = 1; i < pfl->max_device_width; i++) { + resp = deposit32(resp, 8 * i, 8, pfl->cfi_table[boff]); + } + } + /* Replicate responses for each device in bank. */ + if (pfl->device_width < pfl->bank_width) { + for (i = pfl->device_width; + i < pfl->bank_width; i += pfl->device_width) { + resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp); + } + } + + return resp; +} + + + +/* Perform a device id query based on the bank width of the flash. */ +static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset) +{ + int i; + uint32_t resp; + hwaddr boff; + + /* Adjust incoming offset to match expected device-width + * addressing. Device ID read addresses are always specified in + * terms of the maximum supported width of the device. This means + * that x8 devices and x8/x16 devices in x8 mode behave + * differently. For devices that are not used at their max width, + * we will be provided with addresses that use higher address bits + * than expected (based on the max width), so we will shift them + * lower so that they will match the addresses used when + * device_width==max_device_width. + */ + boff = offset >> (ctz32(pfl->bank_width) + + ctz32(pfl->max_device_width) - ctz32(pfl->device_width)); + + /* Mask off upper bits which may be used in to query block + * or sector lock status at other addresses. + * Offsets 2/3 are block lock status, is not emulated. + */ + switch (boff & 0xFF) { + case 0: + resp = pfl->ident0; + DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret); + break; + case 1: + resp = pfl->ident1; + DPRINTF("%s: Device ID Code %04x\n", __func__, ret); + break; + default: + DPRINTF("%s: Read Device Information offset=%x\n", __func__, + (unsigned)offset); + return 0; + break; + } + /* Replicate responses for each device in bank. */ + if (pfl->device_width < pfl->bank_width) { + for (i = pfl->device_width; + i < pfl->bank_width; i += pfl->device_width) { + resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp); + } + } + + return resp; +} + static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, int width, int be) { @@ -124,12 +240,6 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, uint8_t *p; ret = -1; - boff = offset & 0xFF; /* why this here ?? */ - - if (pfl->width == 2) - boff = boff >> 1; - else if (pfl->width == 4) - boff = boff >> 2; #if 0 DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n", @@ -190,35 +300,88 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, case 0x60: /* Block /un)lock */ case 0x70: /* Status Register */ case 0xe8: /* Write block */ - /* Status register read */ + /* Status register read. Return status from each device in + * bank. + */ ret = pfl->status; - if (width > 2) { + if (pfl->device_width && width > pfl->device_width) { + int shift = pfl->device_width * 8; + while (shift + pfl->device_width * 8 <= width * 8) { + ret |= pfl->status << shift; + shift += pfl->device_width * 8; + } + } else if (!pfl->device_width && width > 2) { + /* Handle 32 bit flash cases where device width is not + * set. (Existing behavior before device width added.) + */ ret |= pfl->status << 16; } DPRINTF("%s: status %x\n", __func__, ret); break; case 0x90: - switch (boff) { - case 0: - ret = pfl->ident0 << 8 | pfl->ident1; - DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret); - break; - case 1: - ret = pfl->ident2 << 8 | pfl->ident3; - DPRINTF("%s: Device ID Code %04x\n", __func__, ret); - break; - default: - DPRINTF("%s: Read Device Information boff=%x\n", __func__, - (unsigned)boff); - ret = 0; - break; + if (!pfl->device_width) { + /* Preserve old behavior if device width not specified */ + boff = offset & 0xFF; + if (pfl->bank_width == 2) { + boff = boff >> 1; + } else if (pfl->bank_width == 4) { + boff = boff >> 2; + } + + switch (boff) { + case 0: + ret = pfl->ident0 << 8 | pfl->ident1; + DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret); + break; + case 1: + ret = pfl->ident2 << 8 | pfl->ident3; + DPRINTF("%s: Device ID Code %04x\n", __func__, ret); + break; + default: + DPRINTF("%s: Read Device Information boff=%x\n", __func__, + (unsigned)boff); + ret = 0; + break; + } + } else { + /* If we have a read larger than the bank_width, combine multiple + * manufacturer/device ID queries into a single response. + */ + int i; + for (i = 0; i < width; i += pfl->bank_width) { + ret = deposit32(ret, i * 8, pfl->bank_width * 8, + pflash_devid_query(pfl, + offset + i * pfl->bank_width)); + } } break; case 0x98: /* Query mode */ - if (boff > pfl->cfi_len) - ret = 0; - else - ret = pfl->cfi_table[boff]; + if (!pfl->device_width) { + /* Preserve old behavior if device width not specified */ + boff = offset & 0xFF; + if (pfl->bank_width == 2) { + boff = boff >> 1; + } else if (pfl->bank_width == 4) { + boff = boff >> 2; + } + + if (boff > pfl->cfi_len) { + ret = 0; + } else { + ret = pfl->cfi_table[boff]; + } + } else { + /* If we have a read larger than the bank_width, combine multiple + * CFI queries into a single response. + */ + int i; + for (i = 0; i < width; i += pfl->bank_width) { + ret = deposit32(ret, i * 8, pfl->bank_width * 8, + pflash_cfi_query(pfl, + offset + i * pfl->bank_width)); + } + } + break; } return ret; @@ -378,6 +541,14 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, break; case 0xe8: + /* Mask writeblock size based on device width, or bank width if + * device width not specified. + */ + if (pfl->device_width) { + value = extract32(value, 0, pfl->device_width * 8); + } else { + value = extract32(value, 0, pfl->bank_width * 8); + } DPRINTF("%s: block write of %x bytes\n", __func__, value); pfl->counter = value; pfl->wcycle++; @@ -613,6 +784,13 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) pfl->ro = 0; } + /* Default to devices being used at their maximum device width. This was + * assumed before the device_width support was added. + */ + if (!pfl->max_device_width) { + pfl->max_device_width = pfl->device_width; + } + pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); pfl->wcycle = 0; pfl->cmd = 0; @@ -665,7 +843,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) pfl->cfi_table[0x28] = 0x02; pfl->cfi_table[0x29] = 0x00; /* Max number of bytes in multi-bytes write */ - if (pfl->width == 1) { + if (pfl->bank_width == 1) { pfl->cfi_table[0x2A] = 0x08; } else { pfl->cfi_table[0x2A] = 0x0B; @@ -706,7 +884,25 @@ static Property pflash_cfi01_properties[] = { DEFINE_PROP_DRIVE("drive", struct pflash_t, bs), DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0), - DEFINE_PROP_UINT8("width", struct pflash_t, width, 0), + /* width here is the overall width of this QEMU device in bytes. + * The QEMU device may be emulating a number of flash devices + * wired up in parallel; the width of each individual flash + * device should be specified via device-width. If the individual + * devices have a maximum width which is greater than the width + * they are being used for, this maximum width should be set via + * max-device-width (which otherwise defaults to device-width). + * So for instance a 32-bit wide QEMU flash device made from four + * 16-bit flash devices used in 8-bit wide mode would be configured + * with width = 4, device-width = 1, max-device-width = 2. + * + * If device-width is not specified we default to backwards + * compatible behaviour which is a bad emulation of two + * 16 bit devices making up a 32 bit wide QEMU device. This + * is deprecated for new uses of this device. + */ + DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0), + DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0), + DEFINE_PROP_UINT8("max-device-width", struct pflash_t, max_device_width, 0), DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0), DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), @@ -745,8 +941,8 @@ pflash_t *pflash_cfi01_register(hwaddr base, DeviceState *qdev, const char *name, hwaddr size, BlockDriverState *bs, - uint32_t sector_len, int nb_blocs, int width, - uint16_t id0, uint16_t id1, + uint32_t sector_len, int nb_blocs, + int bank_width, uint16_t id0, uint16_t id1, uint16_t id2, uint16_t id3, int be) { DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01); @@ -756,7 +952,7 @@ pflash_t *pflash_cfi01_register(hwaddr base, } qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); qdev_prop_set_uint64(dev, "sector-length", sector_len); - qdev_prop_set_uint8(dev, "width", width); + qdev_prop_set_uint8(dev, "width", bank_width); qdev_prop_set_uint8(dev, "big-endian", !!be); qdev_prop_set_uint16(dev, "id0", id0); qdev_prop_set_uint16(dev, "id1", id1); diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index cbd6a006f4..be2a7d953a 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -14,6 +14,7 @@ obj-$(CONFIG_COLDFIRE) += mcf_uart.o obj-$(CONFIG_OMAP) += omap_uart.o obj-$(CONFIG_SH4) += sh_serial.o obj-$(CONFIG_PSERIES) += spapr_vty.o +obj-$(CONFIG_DIGIC) += digic-uart.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c new file mode 100644 index 0000000000..fd8e07713d --- /dev/null +++ b/hw/char/digic-uart.c @@ -0,0 +1,195 @@ +/* + * QEMU model of the Canon DIGIC UART block. + * + * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com> + * + * This model is based on reverse engineering efforts + * made by CHDK (http://chdk.wikia.com) and + * Magic Lantern (http://www.magiclantern.fm) projects + * contributors. + * + * See "Serial terminal" docs here: + * http://magiclantern.wikia.com/wiki/Register_Map#Misc_Registers + * + * The QEMU model of the Milkymist UART block by Michael Walle + * is used as a template. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "sysemu/char.h" + +#include "hw/char/digic-uart.h" + +enum { + ST_RX_RDY = (1 << 0), + ST_TX_RDY = (1 << 1), +}; + +static uint64_t digic_uart_read(void *opaque, hwaddr addr, + unsigned size) +{ + DigicUartState *s = opaque; + uint64_t ret = 0; + + addr >>= 2; + + switch (addr) { + case R_RX: + s->reg_st &= ~(ST_RX_RDY); + ret = s->reg_rx; + break; + + case R_ST: + ret = s->reg_st; + break; + + default: + qemu_log_mask(LOG_UNIMP, + "digic-uart: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + } + + return ret; +} + +static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + DigicUartState *s = opaque; + unsigned char ch = value; + + addr >>= 2; + + switch (addr) { + case R_TX: + if (s->chr) { + qemu_chr_fe_write_all(s->chr, &ch, 1); + } + break; + + case R_ST: + /* + * Ignore write to R_ST. + * + * The point is that this register is actively used + * during receiving and transmitting symbols, + * but we don't know the function of most of bits. + * + * Ignoring writes to R_ST is only a simplification + * of the model. It has no perceptible side effects + * for existing guests. + */ + break; + + default: + qemu_log_mask(LOG_UNIMP, + "digic-uart: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + } +} + +static const MemoryRegionOps uart_mmio_ops = { + .read = digic_uart_read, + .write = digic_uart_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int uart_can_rx(void *opaque) +{ + DigicUartState *s = opaque; + + return !(s->reg_st & ST_RX_RDY); +} + +static void uart_rx(void *opaque, const uint8_t *buf, int size) +{ + DigicUartState *s = opaque; + + assert(uart_can_rx(opaque)); + + s->reg_st |= ST_RX_RDY; + s->reg_rx = *buf; +} + +static void uart_event(void *opaque, int event) +{ +} + +static void digic_uart_reset(DeviceState *d) +{ + DigicUartState *s = DIGIC_UART(d); + + s->reg_rx = 0; + s->reg_st = ST_TX_RDY; +} + +static void digic_uart_realize(DeviceState *dev, Error **errp) +{ + DigicUartState *s = DIGIC_UART(dev); + + s->chr = qemu_char_get_next_serial(); + if (s->chr) { + qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); + } +} + +static void digic_uart_init(Object *obj) +{ + DigicUartState *s = DIGIC_UART(obj); + + memory_region_init_io(&s->regs_region, OBJECT(s), &uart_mmio_ops, s, + TYPE_DIGIC_UART, 0x18); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->regs_region); +} + +static const VMStateDescription vmstate_digic_uart = { + .name = "digic-uart", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(reg_rx, DigicUartState), + VMSTATE_UINT32(reg_st, DigicUartState), + VMSTATE_END_OF_LIST() + } +}; + +static void digic_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = digic_uart_realize; + dc->reset = digic_uart_reset; + dc->vmsd = &vmstate_digic_uart; +} + +static const TypeInfo digic_uart_info = { + .name = TYPE_DIGIC_UART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(DigicUartState), + .instance_init = digic_uart_init, + .class_init = digic_uart_class_init, +}; + +static void digic_uart_register_types(void) +{ + type_register_static(&digic_uart_info); +} + +type_init(digic_uart_register_types) diff --git a/hw/core/loader.c b/hw/core/loader.c index 60d2ebd4ac..0634bee20c 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -785,6 +785,13 @@ static void rom_reset(void *unused) g_free(rom->data); rom->data = NULL; } + /* + * The rom loader is really on the same level as firmware in the guest + * shadowing a ROM into RAM. Such a shadowing mechanism needs to ensure + * that the instruction cache for that new region is clear, so that the + * CPU definitely fetches its instructions from the just written data. + */ + cpu_flush_icache_range(rom->addr, rom->datasize); } } diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 47ac44264c..60eb936e0d 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -24,3 +24,4 @@ obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o obj-$(CONFIG_SH4) += sh_intc.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_XICS_KVM) += xics_kvm.o +obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c new file mode 100644 index 0000000000..407d563514 --- /dev/null +++ b/hw/intc/allwinner-a10-pic.c @@ -0,0 +1,200 @@ +/* + * Allwinner A10 interrupt controller device emulation + * + * Copyright (C) 2013 Li Guang + * Written by Li Guang <lig.fnst@cn.fujitsu.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "hw/sysbus.h" +#include "hw/devices.h" +#include "sysemu/sysemu.h" +#include "hw/intc/allwinner-a10-pic.h" + +static void aw_a10_pic_update(AwA10PICState *s) +{ + uint8_t i; + int irq = 0, fiq = 0; + + for (i = 0; i < AW_A10_PIC_REG_NUM; i++) { + irq |= s->irq_pending[i] & ~s->mask[i]; + fiq |= s->select[i] & s->irq_pending[i] & ~s->mask[i]; + } + + qemu_set_irq(s->parent_irq, !!irq); + qemu_set_irq(s->parent_fiq, !!fiq); +} + +static void aw_a10_pic_set_irq(void *opaque, int irq, int level) +{ + AwA10PICState *s = opaque; + + if (level) { + set_bit(irq % 32, (void *)&s->irq_pending[irq / 32]); + } + aw_a10_pic_update(s); +} + +static uint64_t aw_a10_pic_read(void *opaque, hwaddr offset, unsigned size) +{ + AwA10PICState *s = opaque; + uint8_t index = (offset & 0xc) / 4; + + switch (offset) { + case AW_A10_PIC_VECTOR: + return s->vector; + case AW_A10_PIC_BASE_ADDR: + return s->base_addr; + case AW_A10_PIC_PROTECT: + return s->protect; + case AW_A10_PIC_NMI: + return s->nmi; + case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8: + return s->irq_pending[index]; + case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8: + return s->fiq_pending[index]; + case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8: + return s->select[index]; + case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8: + return s->enable[index]; + case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8: + return s->mask[index]; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + break; + } + + return 0; +} + +static void aw_a10_pic_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + AwA10PICState *s = opaque; + uint8_t index = (offset & 0xc) / 4; + + switch (offset) { + case AW_A10_PIC_VECTOR: + s->vector = value & ~0x3; + break; + case AW_A10_PIC_BASE_ADDR: + s->base_addr = value & ~0x3; + case AW_A10_PIC_PROTECT: + s->protect = value; + break; + case AW_A10_PIC_NMI: + s->nmi = value; + break; + case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8: + s->irq_pending[index] &= ~value; + break; + case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8: + s->fiq_pending[index] &= ~value; + break; + case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8: + s->select[index] = value; + break; + case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8: + s->enable[index] = value; + break; + case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8: + s->mask[index] = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + break; + } + + aw_a10_pic_update(s); +} + +static const MemoryRegionOps aw_a10_pic_ops = { + .read = aw_a10_pic_read, + .write = aw_a10_pic_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_aw_a10_pic = { + .name = "a10.pic", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(vector, AwA10PICState), + VMSTATE_UINT32(base_addr, AwA10PICState), + VMSTATE_UINT32(protect, AwA10PICState), + VMSTATE_UINT32(nmi, AwA10PICState), + VMSTATE_UINT32_ARRAY(irq_pending, AwA10PICState, AW_A10_PIC_REG_NUM), + VMSTATE_UINT32_ARRAY(fiq_pending, AwA10PICState, AW_A10_PIC_REG_NUM), + VMSTATE_UINT32_ARRAY(enable, AwA10PICState, AW_A10_PIC_REG_NUM), + VMSTATE_UINT32_ARRAY(select, AwA10PICState, AW_A10_PIC_REG_NUM), + VMSTATE_UINT32_ARRAY(mask, AwA10PICState, AW_A10_PIC_REG_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void aw_a10_pic_init(Object *obj) +{ + AwA10PICState *s = AW_A10_PIC(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); + + qdev_init_gpio_in(DEVICE(dev), aw_a10_pic_set_irq, AW_A10_PIC_INT_NR); + sysbus_init_irq(dev, &s->parent_irq); + sysbus_init_irq(dev, &s->parent_fiq); + memory_region_init_io(&s->iomem, OBJECT(s), &aw_a10_pic_ops, s, + TYPE_AW_A10_PIC, 0x400); + sysbus_init_mmio(dev, &s->iomem); +} + +static void aw_a10_pic_reset(DeviceState *d) +{ + AwA10PICState *s = AW_A10_PIC(d); + uint8_t i; + + s->base_addr = 0; + s->protect = 0; + s->nmi = 0; + s->vector = 0; + for (i = 0; i < AW_A10_PIC_REG_NUM; i++) { + s->irq_pending[i] = 0; + s->fiq_pending[i] = 0; + s->select[i] = 0; + s->enable[i] = 0; + s->mask[i] = 0; + } +} + +static void aw_a10_pic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = aw_a10_pic_reset; + dc->desc = "allwinner a10 pic"; + dc->vmsd = &vmstate_aw_a10_pic; + } + +static const TypeInfo aw_a10_pic_info = { + .name = TYPE_AW_A10_PIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AwA10PICState), + .instance_init = aw_a10_pic_init, + .class_init = aw_a10_pic_class_init, +}; + +static void aw_a10_register_types(void) +{ + type_register_static(&aw_a10_pic_info); +} + +type_init(aw_a10_register_types); diff --git a/hw/intc/xics.c b/hw/intc/xics.c index a333305d3d..b437563fb9 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -723,7 +723,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t nr, server, priority; if ((nargs != 3) || (nret != 1)) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -733,13 +733,13 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers) || (priority > 0xff)) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } ics_write_xive(ics, nr, server, priority, priority); - rtas_st(rets, 0, 0); /* Success */ + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_get_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -751,18 +751,18 @@ static void rtas_get_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t nr; if ((nargs != 1) || (nret != 3)) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } nr = rtas_ld(args, 0); if (!ics_valid_irq(ics, nr)) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } - rtas_st(rets, 0, 0); /* Success */ + rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, ics->irqs[nr - ics->offset].server); rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority); } @@ -776,21 +776,21 @@ static void rtas_int_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t nr; if ((nargs != 1) || (nret != 1)) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } nr = rtas_ld(args, 0); if (!ics_valid_irq(ics, nr)) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff, ics->irqs[nr - ics->offset].priority); - rtas_st(rets, 0, 0); /* Success */ + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -802,14 +802,14 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t nr; if ((nargs != 1) || (nret != 1)) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } nr = rtas_ld(args, 0); if (!ics_valid_irq(ics, nr)) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -817,7 +817,7 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr, ics->irqs[nr - ics->offset].saved_priority, ics->irqs[nr - ics->offset].saved_priority); - rtas_st(rets, 0, 0); /* Success */ + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } /* diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 2a7ea5c0f9..48d9e7afa4 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -79,19 +79,19 @@ static int microblaze_load_dtb(hwaddr addr, } if (kernel_cmdline) { - r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", - kernel_cmdline); + r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", + kernel_cmdline); if (r < 0) { fprintf(stderr, "couldn't set /chosen/bootargs\n"); } } if (initrd_start) { - qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_start); + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_start); - qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", - initrd_end); + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + initrd_end); } cpu_physical_memory_write(addr, fdt, fdt_size); diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index beaad682ac..635713e766 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -55,12 +55,12 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr, void *membuf; if ((nargs != 3) || (nret != 2)) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } if (!nvram) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); rtas_st(rets, 1, 0); return; } @@ -71,7 +71,7 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr, if (((offset + len) < offset) || ((offset + len) > nvram->size)) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); rtas_st(rets, 1, 0); return; } @@ -87,7 +87,7 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr, } cpu_physical_memory_unmap(membuf, len, 1, len); - rtas_st(rets, 0, (alen < len) ? -1 : 0); + rtas_st(rets, 0, (alen < len) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS); rtas_st(rets, 1, (alen < 0) ? 0 : alen); } @@ -102,12 +102,12 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr, void *membuf; if ((nargs != 3) || (nret != 2)) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } if (!nvram) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -117,7 +117,7 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr, if (((offset + len) < offset) || ((offset + len) > nvram->size)) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -132,7 +132,7 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr, } cpu_physical_memory_unmap(membuf, len, 0, len); - rtas_st(rets, 0, (alen < len) ? -1 : 0); + rtas_st(rets, 0, (alen < len) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS); rtas_st(rets, 1, (alen < 0) ? 0 : alen); } diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 4991ec44b0..75b60d36ac 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -82,7 +82,7 @@ PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, memory_region_add_subregion(address_space_mem, 0x80000000ULL, &d->pci_hole); - phb->bus = pci_register_bus(dev, "pci", + phb->bus = pci_register_bus(dev, NULL, pci_grackle_set_irq, pci_grackle_map_irq, pic, diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 91530cdd04..adc1d89010 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -234,7 +234,7 @@ PCIBus *pci_pmac_init(qemu_irq *pic, memory_region_add_subregion(address_space_mem, 0x80000000ULL, &d->pci_hole); - h->bus = pci_register_bus(dev, "pci", + h->bus = pci_register_bus(dev, NULL, pci_unin_set_irq, pci_unin_map_irq, pic, &d->pci_mmio, @@ -300,7 +300,7 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic, memory_region_add_subregion(address_space_mem, 0x80000000ULL, &d->pci_hole); - h->bus = pci_register_bus(dev, "pci", + h->bus = pci_register_bus(dev, NULL, pci_unin_set_irq, pci_unin_map_irq, pic, &d->pci_mmio, diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index cfdd84b969..b37ce9d633 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -108,18 +108,18 @@ static void dt_serial_create(void *fdt, unsigned long long offset, char ser[128]; snprintf(ser, sizeof(ser), "%s/serial@%llx", soc, offset); - qemu_devtree_add_subnode(fdt, ser); - qemu_devtree_setprop_string(fdt, ser, "device_type", "serial"); - qemu_devtree_setprop_string(fdt, ser, "compatible", "ns16550"); - qemu_devtree_setprop_cells(fdt, ser, "reg", offset, 0x100); - qemu_devtree_setprop_cell(fdt, ser, "cell-index", idx); - qemu_devtree_setprop_cell(fdt, ser, "clock-frequency", 0); - qemu_devtree_setprop_cells(fdt, ser, "interrupts", 42, 2); - qemu_devtree_setprop_phandle(fdt, ser, "interrupt-parent", mpic); - qemu_devtree_setprop_string(fdt, "/aliases", alias, ser); + qemu_fdt_add_subnode(fdt, ser); + qemu_fdt_setprop_string(fdt, ser, "device_type", "serial"); + qemu_fdt_setprop_string(fdt, ser, "compatible", "ns16550"); + qemu_fdt_setprop_cells(fdt, ser, "reg", offset, 0x100); + qemu_fdt_setprop_cell(fdt, ser, "cell-index", idx); + qemu_fdt_setprop_cell(fdt, ser, "clock-frequency", 0); + qemu_fdt_setprop_cells(fdt, ser, "interrupts", 42, 2); + qemu_fdt_setprop_phandle(fdt, ser, "interrupt-parent", mpic); + qemu_fdt_setprop_string(fdt, "/aliases", alias, ser); if (defcon) { - qemu_devtree_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); + qemu_fdt_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); } } @@ -183,30 +183,30 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args, } /* Manipulate device tree in memory. */ - qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 2); - qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 2); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 2); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 2); - qemu_devtree_add_subnode(fdt, "/memory"); - qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory"); - qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, - sizeof(mem_reg_property)); + qemu_fdt_add_subnode(fdt, "/memory"); + qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory"); + qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); - qemu_devtree_add_subnode(fdt, "/chosen"); + qemu_fdt_add_subnode(fdt, "/chosen"); if (initrd_size) { - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_base); + ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); if (ret < 0) { fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); } - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", - (initrd_base + initrd_size)); + ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); if (ret < 0) { fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); } } - ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", + ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", args->kernel_cmdline); if (ret < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); @@ -217,22 +217,22 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args, tb_freq = kvmppc_get_tbfreq(); /* indicate KVM hypercall interface */ - qemu_devtree_add_subnode(fdt, "/hypervisor"); - qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible", - "linux,kvm"); + qemu_fdt_add_subnode(fdt, "/hypervisor"); + qemu_fdt_setprop_string(fdt, "/hypervisor", "compatible", + "linux,kvm"); kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); - qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions", - hypercall, sizeof(hypercall)); + qemu_fdt_setprop(fdt, "/hypervisor", "hcall-instructions", + hypercall, sizeof(hypercall)); /* if KVM supports the idle hcall, set property indicating this */ if (kvmppc_get_hasidle(env)) { - qemu_devtree_setprop(fdt, "/hypervisor", "has-idle", NULL, 0); + qemu_fdt_setprop(fdt, "/hypervisor", "has-idle", NULL, 0); } } /* Create CPU nodes */ - qemu_devtree_add_subnode(fdt, "/cpus"); - qemu_devtree_setprop_cell(fdt, "/cpus", "#address-cells", 1); - qemu_devtree_setprop_cell(fdt, "/cpus", "#size-cells", 0); + qemu_fdt_add_subnode(fdt, "/cpus"); + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 1); + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0); /* We need to generate the cpu nodes in reverse order, so Linux can pick the first node as boot node and be happy */ @@ -249,55 +249,56 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args, snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", cpu->cpu_index); - qemu_devtree_add_subnode(fdt, cpu_name); - qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); - qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); - qemu_devtree_setprop_string(fdt, cpu_name, "device_type", "cpu"); - qemu_devtree_setprop_cell(fdt, cpu_name, "reg", cpu->cpu_index); - qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-line-size", - env->dcache_line_size); - qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-line-size", - env->icache_line_size); - qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000); - qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000); - qemu_devtree_setprop_cell(fdt, cpu_name, "bus-frequency", 0); + qemu_fdt_add_subnode(fdt, cpu_name); + qemu_fdt_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); + qemu_fdt_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); + qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); + qemu_fdt_setprop_cell(fdt, cpu_name, "reg", cpu->cpu_index); + qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-line-size", + env->dcache_line_size); + qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-line-size", + env->icache_line_size); + qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000); + qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000); + qemu_fdt_setprop_cell(fdt, cpu_name, "bus-frequency", 0); if (cpu->cpu_index) { - qemu_devtree_setprop_string(fdt, cpu_name, "status", "disabled"); - qemu_devtree_setprop_string(fdt, cpu_name, "enable-method", "spin-table"); - qemu_devtree_setprop_u64(fdt, cpu_name, "cpu-release-addr", - cpu_release_addr); + qemu_fdt_setprop_string(fdt, cpu_name, "status", "disabled"); + qemu_fdt_setprop_string(fdt, cpu_name, "enable-method", + "spin-table"); + qemu_fdt_setprop_u64(fdt, cpu_name, "cpu-release-addr", + cpu_release_addr); } else { - qemu_devtree_setprop_string(fdt, cpu_name, "status", "okay"); + qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); } } - qemu_devtree_add_subnode(fdt, "/aliases"); + qemu_fdt_add_subnode(fdt, "/aliases"); /* XXX These should go into their respective devices' code */ snprintf(soc, sizeof(soc), "/soc@%llx", MPC8544_CCSRBAR_BASE); - qemu_devtree_add_subnode(fdt, soc); - qemu_devtree_setprop_string(fdt, soc, "device_type", "soc"); - qemu_devtree_setprop(fdt, soc, "compatible", compatible_sb, - sizeof(compatible_sb)); - qemu_devtree_setprop_cell(fdt, soc, "#address-cells", 1); - qemu_devtree_setprop_cell(fdt, soc, "#size-cells", 1); - qemu_devtree_setprop_cells(fdt, soc, "ranges", 0x0, - MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE, - MPC8544_CCSRBAR_SIZE); + qemu_fdt_add_subnode(fdt, soc); + qemu_fdt_setprop_string(fdt, soc, "device_type", "soc"); + qemu_fdt_setprop(fdt, soc, "compatible", compatible_sb, + sizeof(compatible_sb)); + qemu_fdt_setprop_cell(fdt, soc, "#address-cells", 1); + qemu_fdt_setprop_cell(fdt, soc, "#size-cells", 1); + qemu_fdt_setprop_cells(fdt, soc, "ranges", 0x0, + MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE, + MPC8544_CCSRBAR_SIZE); /* XXX should contain a reasonable value */ - qemu_devtree_setprop_cell(fdt, soc, "bus-frequency", 0); + qemu_fdt_setprop_cell(fdt, soc, "bus-frequency", 0); snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET); - qemu_devtree_add_subnode(fdt, mpic); - qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic"); - qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); - qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET, - 0x40000); - qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0); - qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 2); - mpic_ph = qemu_devtree_alloc_phandle(fdt); - qemu_devtree_setprop_cell(fdt, mpic, "phandle", mpic_ph); - qemu_devtree_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph); - qemu_devtree_setprop(fdt, mpic, "interrupt-controller", NULL, 0); + qemu_fdt_add_subnode(fdt, mpic); + qemu_fdt_setprop_string(fdt, mpic, "device_type", "open-pic"); + qemu_fdt_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); + qemu_fdt_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET, + 0x40000); + qemu_fdt_setprop_cell(fdt, mpic, "#address-cells", 0); + qemu_fdt_setprop_cell(fdt, mpic, "#interrupt-cells", 2); + mpic_ph = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_setprop_cell(fdt, mpic, "phandle", mpic_ph); + qemu_fdt_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph); + qemu_fdt_setprop(fdt, mpic, "interrupt-controller", NULL, 0); /* * We have to generate ser1 first, because Linux takes the first @@ -311,19 +312,19 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args, snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc, MPC8544_UTIL_OFFSET); - qemu_devtree_add_subnode(fdt, gutil); - qemu_devtree_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts"); - qemu_devtree_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_OFFSET, 0x1000); - qemu_devtree_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0); + qemu_fdt_add_subnode(fdt, gutil); + qemu_fdt_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts"); + qemu_fdt_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_OFFSET, 0x1000); + qemu_fdt_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0); snprintf(msi, sizeof(msi), "/%s/msi@%llx", soc, MPC8544_MSI_REGS_OFFSET); - qemu_devtree_add_subnode(fdt, msi); - qemu_devtree_setprop_string(fdt, msi, "compatible", "fsl,mpic-msi"); - qemu_devtree_setprop_cells(fdt, msi, "reg", MPC8544_MSI_REGS_OFFSET, 0x200); - msi_ph = qemu_devtree_alloc_phandle(fdt); - qemu_devtree_setprop_cells(fdt, msi, "msi-available-ranges", 0x0, 0x100); - qemu_devtree_setprop_phandle(fdt, msi, "interrupt-parent", mpic); - qemu_devtree_setprop_cells(fdt, msi, "interrupts", + qemu_fdt_add_subnode(fdt, msi); + qemu_fdt_setprop_string(fdt, msi, "compatible", "fsl,mpic-msi"); + qemu_fdt_setprop_cells(fdt, msi, "reg", MPC8544_MSI_REGS_OFFSET, 0x200); + msi_ph = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_setprop_cells(fdt, msi, "msi-available-ranges", 0x0, 0x100); + qemu_fdt_setprop_phandle(fdt, msi, "interrupt-parent", mpic); + qemu_fdt_setprop_cells(fdt, msi, "interrupts", 0xe0, 0x0, 0xe1, 0x0, 0xe2, 0x0, @@ -332,46 +333,46 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args, 0xe5, 0x0, 0xe6, 0x0, 0xe7, 0x0); - qemu_devtree_setprop_cell(fdt, msi, "phandle", msi_ph); - qemu_devtree_setprop_cell(fdt, msi, "linux,phandle", msi_ph); + qemu_fdt_setprop_cell(fdt, msi, "phandle", msi_ph); + qemu_fdt_setprop_cell(fdt, msi, "linux,phandle", msi_ph); snprintf(pci, sizeof(pci), "/pci@%llx", MPC8544_PCI_REGS_BASE); - qemu_devtree_add_subnode(fdt, pci); - qemu_devtree_setprop_cell(fdt, pci, "cell-index", 0); - qemu_devtree_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); - qemu_devtree_setprop_string(fdt, pci, "device_type", "pci"); - qemu_devtree_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0, - 0x0, 0x7); - pci_map = pci_map_create(fdt, qemu_devtree_get_phandle(fdt, mpic), + qemu_fdt_add_subnode(fdt, pci); + qemu_fdt_setprop_cell(fdt, pci, "cell-index", 0); + qemu_fdt_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); + qemu_fdt_setprop_string(fdt, pci, "device_type", "pci"); + qemu_fdt_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0, + 0x0, 0x7); + pci_map = pci_map_create(fdt, qemu_fdt_get_phandle(fdt, mpic), params->pci_first_slot, params->pci_nr_slots, &len); - qemu_devtree_setprop(fdt, pci, "interrupt-map", pci_map, len); - qemu_devtree_setprop_phandle(fdt, pci, "interrupt-parent", mpic); - qemu_devtree_setprop_cells(fdt, pci, "interrupts", 24, 2); - qemu_devtree_setprop_cells(fdt, pci, "bus-range", 0, 255); + qemu_fdt_setprop(fdt, pci, "interrupt-map", pci_map, len); + qemu_fdt_setprop_phandle(fdt, pci, "interrupt-parent", mpic); + qemu_fdt_setprop_cells(fdt, pci, "interrupts", 24, 2); + qemu_fdt_setprop_cells(fdt, pci, "bus-range", 0, 255); for (i = 0; i < 14; i++) { pci_ranges[i] = cpu_to_be32(pci_ranges[i]); } - qemu_devtree_setprop_cell(fdt, pci, "fsl,msi", msi_ph); - qemu_devtree_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges)); - qemu_devtree_setprop_cells(fdt, pci, "reg", MPC8544_PCI_REGS_BASE >> 32, - MPC8544_PCI_REGS_BASE, 0, 0x1000); - qemu_devtree_setprop_cell(fdt, pci, "clock-frequency", 66666666); - qemu_devtree_setprop_cell(fdt, pci, "#interrupt-cells", 1); - qemu_devtree_setprop_cell(fdt, pci, "#size-cells", 2); - qemu_devtree_setprop_cell(fdt, pci, "#address-cells", 3); - qemu_devtree_setprop_string(fdt, "/aliases", "pci0", pci); + qemu_fdt_setprop_cell(fdt, pci, "fsl,msi", msi_ph); + qemu_fdt_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges)); + qemu_fdt_setprop_cells(fdt, pci, "reg", MPC8544_PCI_REGS_BASE >> 32, + MPC8544_PCI_REGS_BASE, 0, 0x1000); + qemu_fdt_setprop_cell(fdt, pci, "clock-frequency", 66666666); + qemu_fdt_setprop_cell(fdt, pci, "#interrupt-cells", 1); + qemu_fdt_setprop_cell(fdt, pci, "#size-cells", 2); + qemu_fdt_setprop_cell(fdt, pci, "#address-cells", 3); + qemu_fdt_setprop_string(fdt, "/aliases", "pci0", pci); params->fixup_devtree(params, fdt); if (toplevel_compat) { - qemu_devtree_setprop(fdt, "/", "compatible", toplevel_compat, - strlen(toplevel_compat) + 1); + qemu_fdt_setprop(fdt, "/", "compatible", toplevel_compat, + strlen(toplevel_compat) + 1); } done: if (!dry_run) { - qemu_devtree_dumpdtb(fdt, fdt_size); + qemu_fdt_dumpdtb(fdt, fdt_size); cpu_physical_memory_write(addr, fdt, fdt_size); } ret = fdt_size; diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 2e964b2474..7d5357e83b 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -23,9 +23,9 @@ static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt) const char model[] = "QEMU ppce500"; const char compatible[] = "fsl,qemu-e500"; - qemu_devtree_setprop(fdt, "/", "model", model, sizeof(model)); - qemu_devtree_setprop(fdt, "/", "compatible", compatible, - sizeof(compatible)); + qemu_fdt_setprop(fdt, "/", "model", model, sizeof(model)); + qemu_fdt_setprop(fdt, "/", "compatible", compatible, + sizeof(compatible)); } static void e500plat_init(QEMUMachineInitArgs *args) diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index edcc0be5f7..292c70953b 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -21,9 +21,9 @@ static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) const char model[] = "MPC8544DS"; const char compatible[] = "MPC8544DS\0MPC85xxDS"; - qemu_devtree_setprop(fdt, "/", "model", model, sizeof(model)); - qemu_devtree_setprop(fdt, "/", "compatible", compatible, - sizeof(compatible)); + qemu_fdt_setprop(fdt, "/", "model", model, sizeof(model)); + qemu_fdt_setprop(fdt, "/", "compatible", compatible, + sizeof(compatible)); } static void mpc8544ds_init(QEMUMachineInitArgs *args) diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 67597dfb88..ec15bab0b5 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -77,23 +77,23 @@ static int bamboo_load_device_tree(hwaddr addr, /* Manipulate device tree in memory. */ - ret = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, - sizeof(mem_reg_property)); + ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); if (ret < 0) fprintf(stderr, "couldn't set /memory/reg\n"); - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_base); + ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); if (ret < 0) fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", - (initrd_base + initrd_size)); + ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); if (ret < 0) fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); - ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", - kernel_cmdline); + ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", + kernel_cmdline); if (ret < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); @@ -105,10 +105,10 @@ static int bamboo_load_device_tree(hwaddr addr, clock_freq = kvmppc_get_clockfreq(); } - qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", - clock_freq); - qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", - tb_freq); + qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", + clock_freq); + qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", + tb_freq); rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 7e53a5f977..93d02c1e50 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -526,14 +526,15 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt) cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(0x0)}; char mem_name[32]; - hwaddr node0_size, mem_start; + hwaddr node0_size, mem_start, node_size; uint64_t mem_reg_property[2]; int i, off; /* memory node(s) */ - node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size; - if (spapr->rma_size > node0_size) { - spapr->rma_size = node0_size; + if (nb_numa_nodes > 1 && node_mem[0] < ram_size) { + node0_size = node_mem[0]; + } else { + node0_size = ram_size; } /* RMA */ @@ -566,7 +567,15 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt) mem_start = node0_size; for (i = 1; i < nb_numa_nodes; i++) { mem_reg_property[0] = cpu_to_be64(mem_start); - mem_reg_property[1] = cpu_to_be64(node_mem[i]); + if (mem_start >= ram_size) { + node_size = 0; + } else { + node_size = node_mem[i]; + if (node_size > ram_size - mem_start) { + node_size = ram_size - mem_start; + } + } + mem_reg_property[1] = cpu_to_be64(node_size); associativity[3] = associativity[4] = cpu_to_be32(i); sprintf(mem_name, "memory@" TARGET_FMT_lx, mem_start); off = fdt_add_subnode(fdt, 0, mem_name); @@ -576,7 +585,7 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt) sizeof(mem_reg_property)))); _FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity, sizeof(associativity)))); - mem_start += node_mem[i]; + mem_start += node_size; } return 0; @@ -688,7 +697,8 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) /* Update the RMA size if necessary */ if (spapr->vrma_adjust) { - spapr->rma_size = kvmppc_rma_size(ram_size, spapr->htab_shift); + hwaddr node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size; + spapr->rma_size = kvmppc_rma_size(node0_size, spapr->htab_shift); } } @@ -739,18 +749,10 @@ static void spapr_cpu_reset(void *opaque) static void spapr_create_nvram(sPAPREnvironment *spapr) { DeviceState *dev = qdev_create(&spapr->vio_bus->bus, "spapr-nvram"); - const char *drivename = qemu_opt_get(qemu_get_machine_opts(), "nvram"); - - if (drivename) { - BlockDriverState *bs; + DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); - bs = bdrv_find(drivename); - if (!bs) { - fprintf(stderr, "No such block device \"%s\" for nvram\n", - drivename); - exit(1); - } - qdev_prop_set_drive_nofail(dev, "drive", bs); + if (dinfo) { + qdev_prop_set_drive_nofail(dev, "drive", dinfo->bdrv); } qdev_init_nofail(dev); @@ -1113,6 +1115,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); hwaddr rma_alloc_size; + hwaddr node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size; uint32_t initrd_base = 0; long kernel_size = 0, initrd_size = 0; long load_limit, rtas_limit, fw_size; @@ -1134,10 +1137,10 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) exit(1); } - if (rma_alloc_size && (rma_alloc_size < ram_size)) { + if (rma_alloc_size && (rma_alloc_size < node0_size)) { spapr->rma_size = rma_alloc_size; } else { - spapr->rma_size = ram_size; + spapr->rma_size = node0_size; /* With KVM, we don't actually know whether KVM supports an * unbounded RMA (PR KVM) or is limited by the hash table size @@ -1154,6 +1157,12 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) } } + if (spapr->rma_size > node0_size) { + fprintf(stderr, "Error: Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")\n", + spapr->rma_size); + exit(1); + } + /* We place the device tree and RTAS just below either the top of the RMA, * or just below 2GB, whichever is lowere, so that it can be * processed with 32-bit real mode code if necessary */ diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index a69390e54e..16fa49e886 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -286,7 +286,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint64_t xinfo; if ((nargs < 6) || (nargs > 7) || nret != 1) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -306,9 +306,9 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr, cpu_physical_memory_write(buf, pending_epow, len); g_free(pending_epow); pending_epow = NULL; - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } else { - rtas_st(rets, 0, 1); + rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); } } diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 2beedd45e9..ec00300884 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -90,7 +90,7 @@ static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid, if ((size != 1) && (size != 2) && (size != 4)) { /* access must be 1, 2 or 4 bytes */ - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -100,14 +100,14 @@ static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid, if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) { /* Access must be to a valid device, within bounds and * naturally aligned */ - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } val = pci_host_config_read_common(pci_dev, addr, pci_config_size(pci_dev), size); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, val); } @@ -120,7 +120,7 @@ static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t size, addr; if ((nargs != 4) || (nret != 2)) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -139,7 +139,7 @@ static void rtas_read_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t size, addr; if ((nargs != 2) || (nret != 2)) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -157,7 +157,7 @@ static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid, if ((size != 1) && (size != 2) && (size != 4)) { /* access must be 1, 2 or 4 bytes */ - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -167,14 +167,14 @@ static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid, if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) { /* Access must be to a valid device, within bounds and * naturally aligned */ - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } pci_host_config_write_common(pci_dev, addr, pci_config_size(pci_dev), val, size); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -186,7 +186,7 @@ static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t val, size, addr; if ((nargs != 5) || (nret != 1)) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -206,7 +206,7 @@ static void rtas_write_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t val, size, addr; if ((nargs != 3) || (nret != 1)) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -293,7 +293,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, break; default: fprintf(stderr, "rtas_ibm_change_msi(%u) is not implemented\n", func); - rtas_st(rets, 0, -3); /* Parameter error */ + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -303,7 +303,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, pdev = find_dev(spapr, buid, config_addr); } if (!phb || !pdev) { - rtas_st(rets, 0, -3); /* Parameter error */ + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -312,11 +312,11 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, ndev = spapr_msicfg_find(phb, config_addr, false); if (ndev < 0) { trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr); - rtas_st(rets, 0, -1); /* Hardware error */ + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } trace_spapr_pci_msi("Released MSIs", ndev, config_addr); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, 0); return; } @@ -327,7 +327,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, ndev = spapr_msicfg_find(phb, config_addr, true); if (ndev >= SPAPR_MSIX_MAX_DEVS || ndev < 0) { fprintf(stderr, "No free entry for a new MSI device\n"); - rtas_st(rets, 0, -1); /* Hardware error */ + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } trace_spapr_pci_msi("Configuring MSI", ndev, config_addr); @@ -336,7 +336,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, if (phb->msi_table[ndev].nvec && (req_num != phb->msi_table[ndev].nvec)) { /* Unexpected behaviour */ fprintf(stderr, "Cannot reuse MSI config for device#%d", ndev); - rtas_st(rets, 0, -1); /* Hardware error */ + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -346,7 +346,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, ret_intr_type == RTAS_TYPE_MSI); if (irq < 0) { fprintf(stderr, "Cannot allocate MSIs for device#%d", ndev); - rtas_st(rets, 0, -1); /* Hardware error */ + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } phb->msi_table[ndev].irq = irq; @@ -358,7 +358,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, spapr_msi_setmsg(pdev, spapr->msi_win_addr, ret_intr_type == RTAS_TYPE_MSIX, phb->msi_table[ndev].irq, req_num); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, req_num); rtas_st(rets, 2, ++seq_num); rtas_st(rets, 3, ret_intr_type); @@ -383,7 +383,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, /* Fins sPAPRPHBState */ phb = find_phb(spapr, buid); if (!phb) { - rtas_st(rets, 0, -3); /* Parameter error */ + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -391,7 +391,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, ndev = spapr_msicfg_find(phb, config_addr, false); if (ndev < 0) { trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr); - rtas_st(rets, 0, -1); /* Hardware error */ + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -399,7 +399,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, trace_spapr_pci_rtas_ibm_query_interrupt_source_number(ioa_intr_num, intr_src_num); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, intr_src_num); rtas_st(rets, 2, 1);/* 0 == level; 1 == edge */ } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index eb542f218a..1cb276de05 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -47,10 +47,10 @@ static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr, VIOsPAPRDevice *sdev = vty_lookup(spapr, 0); if (!sdev) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); } else { vty_putchars(sdev, &c, sizeof(c)); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } } @@ -62,13 +62,13 @@ static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr, struct tm tm; if (nret != 8) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } qemu_get_timedate(&tm, spapr->rtc_offset); - rtas_st(rets, 0, 0); /* Success */ + rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, tm.tm_year + 1900); rtas_st(rets, 2, tm.tm_mon + 1); rtas_st(rets, 3, tm.tm_mday); @@ -96,7 +96,7 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr, rtc_change_mon_event(&tm); spapr->rtc_offset = qemu_timedate_diff(&tm); - rtas_st(rets, 0, 0); /* Success */ + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_power_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -104,11 +104,11 @@ static void rtas_power_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t nret, target_ulong rets) { if (nargs != 2 || nret != 1) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } qemu_system_shutdown_request(); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_system_reboot(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -117,11 +117,11 @@ static void rtas_system_reboot(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t nret, target_ulong rets) { if (nargs != 0 || nret != 1) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } qemu_system_reset_request(); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, @@ -134,7 +134,7 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, CPUState *cpu; if (nargs != 1 || nret != 2) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -147,12 +147,12 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, rtas_st(rets, 1, 2); } - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); return; } /* Didn't find a matching cpu */ - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); } static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, @@ -164,7 +164,7 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, CPUState *cs; if (nargs != 3 || nret != 1) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -178,7 +178,7 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, CPUPPCState *env = &cpu->env; if (!cs->halted) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -194,12 +194,12 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, qemu_cpu_kick(cs); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); return; } /* Didn't find a matching cpu */ - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); } static void rtas_stop_self(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -224,6 +224,49 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPREnvironment *spapr, env->msr = 0; } +#define DIAGNOSTICS_RUN_MODE 42 + +static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + target_ulong parameter = rtas_ld(args, 0); + target_ulong buffer = rtas_ld(args, 1); + target_ulong length = rtas_ld(args, 2); + target_ulong ret = RTAS_OUT_NOT_SUPPORTED; + + switch (parameter) { + case DIAGNOSTICS_RUN_MODE: + if (length == 1) { + rtas_st(buffer, 0, 0); + ret = RTAS_OUT_SUCCESS; + } + break; + } + + rtas_st(rets, 0, ret); +} + +static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + target_ulong parameter = rtas_ld(args, 0); + target_ulong ret = RTAS_OUT_NOT_SUPPORTED; + + switch (parameter) { + case DIAGNOSTICS_RUN_MODE: + ret = RTAS_OUT_NOT_AUTHORIZED; + break; + } + + rtas_st(rets, 0, ret); +} + static struct rtas_call { const char *name; spapr_rtas_fn fn; @@ -255,7 +298,7 @@ target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPREnvironment *spapr, } hcall_dprintf("Unknown RTAS token 0x%x\n", token); - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return H_PARAMETER; } @@ -291,24 +334,24 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, return ret; } - ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-base", - rtas_addr); + ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-base", + rtas_addr); if (ret < 0) { fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n", fdt_strerror(ret)); return ret; } - ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-entry", - rtas_addr); + ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-entry", + rtas_addr); if (ret < 0) { fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n", fdt_strerror(ret)); return ret; } - ret = qemu_devtree_setprop_cell(fdt, "/rtas", "rtas-size", - rtas_size); + ret = qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size", + rtas_size); if (ret < 0) { fprintf(stderr, "Couldn't add rtas-size property: %s\n", fdt_strerror(ret)); @@ -322,8 +365,8 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, continue; } - ret = qemu_devtree_setprop_cell(fdt, "/rtas", call->name, - i + TOKEN_BASE); + ret = qemu_fdt_setprop_cell(fdt, "/rtas", call->name, + i + TOKEN_BASE); if (ret < 0) { fprintf(stderr, "Couldn't add rtas token for %s: %s\n", call->name, fdt_strerror(ret)); @@ -345,6 +388,10 @@ static void core_rtas_register_types(void) rtas_query_cpu_stopped_state); spapr_rtas_register("start-cpu", rtas_start_cpu); spapr_rtas_register("stop-self", rtas_stop_self); + spapr_rtas_register("ibm,get-system-parameter", + rtas_ibm_get_system_parameter); + spapr_rtas_register("ibm,set-system-parameter", + rtas_ibm_set_system_parameter); } type_init(core_rtas_register_types) diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index a6a0a5113c..fee6195f95 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -331,25 +331,25 @@ static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t unit, enable; if (nargs != 2) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } unit = rtas_ld(args, 0); enable = rtas_ld(args, 1); dev = spapr_vio_find_by_reg(bus, unit); if (!dev) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } if (!dev->tcet) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } spapr_tce_set_bypass(dev->tcet, !!enable); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_quiesce(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -362,7 +362,7 @@ static void rtas_quiesce(PowerPCCPU *cpu, sPAPREnvironment *spapr, VIOsPAPRDevice *dev = NULL; if (nargs != 0) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -371,7 +371,7 @@ static void rtas_quiesce(PowerPCCPU *cpu, sPAPREnvironment *spapr, spapr_vio_quiesce_one(dev); } - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev) diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index fcfa678344..bdb057e36c 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -166,7 +166,7 @@ static int xilinx_load_device_tree(hwaddr addr, if (!fdt) { return 0; } - r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); + r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); if (r < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); cpu_physical_memory_write(addr, fdt, fdt_size); diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 3ae091c95e..2c86c3d412 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -26,5 +26,8 @@ obj-$(CONFIG_OMAP) += omap_synctimer.o obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o obj-$(CONFIG_SH4) += sh_timer.o obj-$(CONFIG_TUSB6010) += tusb6010.o +obj-$(CONFIG_DIGIC) += digic-timer.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o + +obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c new file mode 100644 index 0000000000..b27fce8cd2 --- /dev/null +++ b/hw/timer/allwinner-a10-pit.c @@ -0,0 +1,254 @@ +/* + * Allwinner A10 timer device emulation + * + * Copyright (C) 2013 Li Guang + * Written by Li Guang <lig.fnst@cn.fujitsu.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "hw/timer/allwinner-a10-pit.h" + +static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size) +{ + AwA10PITState *s = AW_A10_PIT(opaque); + uint8_t index; + + switch (offset) { + case AW_A10_PIT_TIMER_IRQ_EN: + return s->irq_enable; + case AW_A10_PIT_TIMER_IRQ_ST: + return s->irq_status; + case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END: + index = offset & 0xf0; + index >>= 4; + index -= 1; + switch (offset & 0x0f) { + case AW_A10_PIT_TIMER_CONTROL: + return s->control[index]; + case AW_A10_PIT_TIMER_INTERVAL: + return s->interval[index]; + case AW_A10_PIT_TIMER_COUNT: + s->count[index] = ptimer_get_count(s->timer[index]); + return s->count[index]; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + break; + } + case AW_A10_PIT_WDOG_CONTROL: + break; + case AW_A10_PIT_WDOG_MODE: + break; + case AW_A10_PIT_COUNT_LO: + return s->count_lo; + case AW_A10_PIT_COUNT_HI: + return s->count_hi; + case AW_A10_PIT_COUNT_CTL: + return s->count_ctl; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + break; + } + + return 0; +} + +static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + AwA10PITState *s = AW_A10_PIT(opaque); + uint8_t index; + + switch (offset) { + case AW_A10_PIT_TIMER_IRQ_EN: + s->irq_enable = value; + break; + case AW_A10_PIT_TIMER_IRQ_ST: + s->irq_status &= ~value; + break; + case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END: + index = offset & 0xf0; + index >>= 4; + index -= 1; + switch (offset & 0x0f) { + case AW_A10_PIT_TIMER_CONTROL: + s->control[index] = value; + if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) { + ptimer_set_count(s->timer[index], s->interval[index]); + } + if (s->control[index] & AW_A10_PIT_TIMER_EN) { + int oneshot = 0; + if (s->control[index] & AW_A10_PIT_TIMER_MODE) { + oneshot = 1; + } + ptimer_run(s->timer[index], oneshot); + } else { + ptimer_stop(s->timer[index]); + } + break; + case AW_A10_PIT_TIMER_INTERVAL: + s->interval[index] = value; + ptimer_set_limit(s->timer[index], s->interval[index], 1); + break; + case AW_A10_PIT_TIMER_COUNT: + s->count[index] = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + } + break; + case AW_A10_PIT_WDOG_CONTROL: + s->watch_dog_control = value; + break; + case AW_A10_PIT_WDOG_MODE: + s->watch_dog_mode = value; + break; + case AW_A10_PIT_COUNT_LO: + s->count_lo = value; + break; + case AW_A10_PIT_COUNT_HI: + s->count_hi = value; + break; + case AW_A10_PIT_COUNT_CTL: + s->count_ctl = value; + if (s->count_ctl & AW_A10_PIT_COUNT_RL_EN) { + uint64_t tmp_count = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + s->count_lo = tmp_count; + s->count_hi = tmp_count >> 32; + s->count_ctl &= ~AW_A10_PIT_COUNT_RL_EN; + } + if (s->count_ctl & AW_A10_PIT_COUNT_CLR_EN) { + s->count_lo = 0; + s->count_hi = 0; + s->count_ctl &= ~AW_A10_PIT_COUNT_CLR_EN; + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + break; + } +} + +static const MemoryRegionOps a10_pit_ops = { + .read = a10_pit_read, + .write = a10_pit_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_a10_pit = { + .name = "a10.pit", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(irq_enable, AwA10PITState), + VMSTATE_UINT32(irq_status, AwA10PITState), + VMSTATE_UINT32_ARRAY(control, AwA10PITState, AW_A10_PIT_TIMER_NR), + VMSTATE_UINT32_ARRAY(interval, AwA10PITState, AW_A10_PIT_TIMER_NR), + VMSTATE_UINT32_ARRAY(count, AwA10PITState, AW_A10_PIT_TIMER_NR), + VMSTATE_UINT32(watch_dog_mode, AwA10PITState), + VMSTATE_UINT32(watch_dog_control, AwA10PITState), + VMSTATE_UINT32(count_lo, AwA10PITState), + VMSTATE_UINT32(count_hi, AwA10PITState), + VMSTATE_UINT32(count_ctl, AwA10PITState), + VMSTATE_PTIMER_ARRAY(timer, AwA10PITState, AW_A10_PIT_TIMER_NR), + VMSTATE_END_OF_LIST() + } +}; + +static void a10_pit_reset(DeviceState *dev) +{ + AwA10PITState *s = AW_A10_PIT(dev); + uint8_t i; + + s->irq_enable = 0; + s->irq_status = 0; + for (i = 0; i < 6; i++) { + s->control[i] = AW_A10_PIT_DEFAULT_CLOCK; + s->interval[i] = 0; + s->count[i] = 0; + ptimer_stop(s->timer[i]); + } + s->watch_dog_mode = 0; + s->watch_dog_control = 0; + s->count_lo = 0; + s->count_hi = 0; + s->count_ctl = 0; +} + +static void a10_pit_timer_cb(void *opaque) +{ + AwA10PITState *s = AW_A10_PIT(opaque); + uint8_t i; + + for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { + if (s->control[i] & AW_A10_PIT_TIMER_EN) { + s->irq_status |= 1 << i; + if (s->control[i] & AW_A10_PIT_TIMER_MODE) { + ptimer_stop(s->timer[i]); + s->control[i] &= ~AW_A10_PIT_TIMER_EN; + } + qemu_irq_pulse(s->irq[i]); + } + } +} + +static void a10_pit_init(Object *obj) +{ + AwA10PITState *s = AW_A10_PIT(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + QEMUBH * bh[AW_A10_PIT_TIMER_NR]; + uint8_t i; + + for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } + memory_region_init_io(&s->iomem, OBJECT(s), &a10_pit_ops, s, + TYPE_AW_A10_PIT, 0x400); + sysbus_init_mmio(sbd, &s->iomem); + + for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { + bh[i] = qemu_bh_new(a10_pit_timer_cb, s); + s->timer[i] = ptimer_init(bh[i]); + ptimer_set_freq(s->timer[i], 240000); + } +} + +static void a10_pit_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = a10_pit_reset; + dc->desc = "allwinner a10 timer"; + dc->vmsd = &vmstate_a10_pit; +} + +static const TypeInfo a10_pit_info = { + .name = TYPE_AW_A10_PIT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AwA10PITState), + .instance_init = a10_pit_init, + .class_init = a10_pit_class_init, +}; + +static void a10_register_types(void) +{ + type_register_static(&a10_pit_info); +} + +type_init(a10_register_types); diff --git a/hw/timer/digic-timer.c b/hw/timer/digic-timer.c new file mode 100644 index 0000000000..1fde22c67f --- /dev/null +++ b/hw/timer/digic-timer.c @@ -0,0 +1,163 @@ +/* + * QEMU model of the Canon DIGIC timer block. + * + * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com> + * + * This model is based on reverse engineering efforts + * made by CHDK (http://chdk.wikia.com) and + * Magic Lantern (http://www.magiclantern.fm) projects + * contributors. + * + * See "Timer/Clock Module" docs here: + * http://magiclantern.wikia.com/wiki/Register_Map + * + * The QEMU model of the OSTimer in PKUnity SoC by Guan Xuetao + * is used as a template. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "hw/sysbus.h" +#include "hw/ptimer.h" +#include "qemu/main-loop.h" + +#include "hw/timer/digic-timer.h" + +static const VMStateDescription vmstate_digic_timer = { + .name = "digic.timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_PTIMER(ptimer, DigicTimerState), + VMSTATE_UINT32(control, DigicTimerState), + VMSTATE_UINT32(relvalue, DigicTimerState), + VMSTATE_END_OF_LIST() + } +}; + +static void digic_timer_reset(DeviceState *dev) +{ + DigicTimerState *s = DIGIC_TIMER(dev); + + ptimer_stop(s->ptimer); + s->control = 0; + s->relvalue = 0; +} + +static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size) +{ + DigicTimerState *s = opaque; + uint64_t ret = 0; + + switch (offset) { + case DIGIC_TIMER_CONTROL: + ret = s->control; + break; + case DIGIC_TIMER_RELVALUE: + ret = s->relvalue; + break; + case DIGIC_TIMER_VALUE: + ret = ptimer_get_count(s->ptimer) & 0xffff; + break; + default: + qemu_log_mask(LOG_UNIMP, + "digic-timer: read access to unknown register 0x" + TARGET_FMT_plx, offset); + } + + return ret; +} + +static void digic_timer_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + DigicTimerState *s = opaque; + + switch (offset) { + case DIGIC_TIMER_CONTROL: + if (value & DIGIC_TIMER_CONTROL_RST) { + digic_timer_reset((DeviceState *)s); + break; + } + + if (value & DIGIC_TIMER_CONTROL_EN) { + ptimer_run(s->ptimer, 0); + } + + s->control = (uint32_t)value; + break; + + case DIGIC_TIMER_RELVALUE: + s->relvalue = extract32(value, 0, 16); + ptimer_set_limit(s->ptimer, s->relvalue, 1); + break; + + case DIGIC_TIMER_VALUE: + break; + + default: + qemu_log_mask(LOG_UNIMP, + "digic-timer: read access to unknown register 0x" + TARGET_FMT_plx, offset); + } +} + +static const MemoryRegionOps digic_timer_ops = { + .read = digic_timer_read, + .write = digic_timer_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void digic_timer_init(Object *obj) +{ + DigicTimerState *s = DIGIC_TIMER(obj); + + s->ptimer = ptimer_init(NULL); + + /* + * FIXME: there is no documentation on Digic timer + * frequency setup so let it always run at 1 MHz + */ + ptimer_set_freq(s->ptimer, 1 * 1000 * 1000); + + memory_region_init_io(&s->iomem, OBJECT(s), &digic_timer_ops, s, + TYPE_DIGIC_TIMER, 0x100); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); +} + +static void digic_timer_class_init(ObjectClass *klass, void *class_data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = digic_timer_reset; + dc->vmsd = &vmstate_digic_timer; +} + +static const TypeInfo digic_timer_info = { + .name = TYPE_DIGIC_TIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(DigicTimerState), + .instance_init = digic_timer_init, + .class_init = digic_timer_class_init, +}; + +static void digic_timer_register_type(void) +{ + type_register_static(&digic_timer_info); +} + +type_init(digic_timer_register_type) |
