diff options
46 files changed, 4114 insertions, 140 deletions
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index a9f82a1032..2bcd23656b 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -110,3 +110,4 @@ CONFIG_IOH3420=y CONFIG_I82801B11=y CONFIG_ACPI=y CONFIG_SMBIOS=y +CONFIG_ASPEED_SOC=y diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index a711e4df61..954c9fe15e 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -16,3 +16,4 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o +obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c new file mode 100644 index 0000000000..daa5518c92 --- /dev/null +++ b/hw/arm/ast2400.c @@ -0,0 +1,137 @@ +/* + * AST2400 SoC + * + * Andrew Jeffery <andrew@aj.id.au> + * Jeremy Kerr <jk@ozlabs.org> + * + * Copyright 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "hw/arm/ast2400.h" +#include "hw/char/serial.h" + +#define AST2400_UART_5_BASE 0x00184000 +#define AST2400_IOMEM_SIZE 0x00200000 +#define AST2400_IOMEM_BASE 0x1E600000 +#define AST2400_VIC_BASE 0x1E6C0000 +#define AST2400_TIMER_BASE 0x1E782000 + +static const int uart_irqs[] = { 9, 32, 33, 34, 10 }; +static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, }; + +/* + * IO handlers: simply catch any reads/writes to IO addresses that aren't + * handled by a device mapping. + */ + +static uint64_t ast2400_io_read(void *p, hwaddr offset, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u]\n", + __func__, offset, size); + return 0; +} + +static void ast2400_io_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n", + __func__, offset, value, size); +} + +static const MemoryRegionOps ast2400_io_ops = { + .read = ast2400_io_read, + .write = ast2400_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ast2400_init(Object *obj) +{ + AST2400State *s = AST2400(obj); + + s->cpu = cpu_arm_init("arm926"); + + object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC); + object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL); + qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default()); + + object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER); + object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL); + qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default()); +} + +static void ast2400_realize(DeviceState *dev, Error **errp) +{ + int i; + AST2400State *s = AST2400(dev); + Error *err = NULL; + + /* IO space */ + memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL, + "ast2400.io", AST2400_IOMEM_SIZE); + memory_region_add_subregion_overlap(get_system_memory(), AST2400_IOMEM_BASE, + &s->iomem, -1); + + /* VIC */ + object_property_set_bool(OBJECT(&s->vic), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, AST2400_VIC_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0, + qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1, + qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ)); + + /* Timer */ + object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, AST2400_TIMER_BASE); + for (i = 0; i < ARRAY_SIZE(timer_irqs); i++) { + qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->vic), timer_irqs[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); + } + + /* UART - attach an 8250 to the IO space as our UART5 */ + if (serial_hds[0]) { + qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]); + serial_mm_init(&s->iomem, AST2400_UART_5_BASE, 2, + uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN); + } +} + +static void ast2400_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ast2400_realize; + + /* + * Reason: creates an ARM CPU, thus use after free(), see + * arm_cpu_class_init() + */ + dc->cannot_destroy_with_object_finalize_yet = true; +} + +static const TypeInfo ast2400_type_info = { + .name = TYPE_AST2400, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AST2400State), + .instance_init = ast2400_init, + .class_init = ast2400_class_init, +}; + +static void ast2400_register_types(void) +{ + type_register_static(&ast2400_type_info); +} + +type_init(ast2400_register_types) diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 6d66fa0280..8099a8a92e 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -12,6 +12,7 @@ #include "hw/arm/bcm2835_peripherals.h" #include "hw/misc/bcm2835_mbox_defs.h" #include "hw/arm/raspi_platform.h" +#include "sysemu/char.h" /* Peripheral base address on the VC (GPU) system bus */ #define BCM2835_VC_PERI_BASE 0x7e000000 @@ -48,6 +49,11 @@ static void bcm2835_peripherals_init(Object *obj) object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL); qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default()); + /* AUX / UART1 */ + object_initialize(&s->aux, sizeof(s->aux), TYPE_BCM2835_AUX); + object_property_add_child(obj, "aux", OBJECT(&s->aux), NULL); + qdev_set_parent_bus(DEVICE(&s->aux), sysbus_get_default()); + /* Mailboxes */ object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX); object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL); @@ -56,6 +62,16 @@ static void bcm2835_peripherals_init(Object *obj) object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr", OBJECT(&s->mbox_mr), &error_abort); + /* Framebuffer */ + object_initialize(&s->fb, sizeof(s->fb), TYPE_BCM2835_FB); + object_property_add_child(obj, "fb", OBJECT(&s->fb), NULL); + object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size", + &error_abort); + qdev_set_parent_bus(DEVICE(&s->fb), sysbus_get_default()); + + object_property_add_const_link(OBJECT(&s->fb), "dma-mr", + OBJECT(&s->gpu_bus_mr), &error_abort); + /* Property channel */ object_initialize(&s->property, sizeof(s->property), TYPE_BCM2835_PROPERTY); object_property_add_child(obj, "property", OBJECT(&s->property), NULL); @@ -63,6 +79,8 @@ static void bcm2835_peripherals_init(Object *obj) "board-rev", &error_abort); qdev_set_parent_bus(DEVICE(&s->property), sysbus_get_default()); + object_property_add_const_link(OBJECT(&s->property), "fb", + OBJECT(&s->fb), &error_abort); object_property_add_const_link(OBJECT(&s->property), "dma-mr", OBJECT(&s->gpu_bus_mr), &error_abort); @@ -70,6 +88,14 @@ static void bcm2835_peripherals_init(Object *obj) object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI); object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL); qdev_set_parent_bus(DEVICE(&s->sdhci), sysbus_get_default()); + + /* DMA Channels */ + object_initialize(&s->dma, sizeof(s->dma), TYPE_BCM2835_DMA); + object_property_add_child(obj, "dma", OBJECT(&s->dma), NULL); + qdev_set_parent_bus(DEVICE(&s->dma), sysbus_get_default()); + + object_property_add_const_link(OBJECT(&s->dma), "dma-mr", + OBJECT(&s->gpu_bus_mr), &error_abort); } static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) @@ -78,7 +104,8 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) Object *obj; MemoryRegion *ram; Error *err = NULL; - uint32_t ram_size; + uint32_t ram_size, vcram_size; + CharDriverState *chr; int n; obj = object_property_get_link(OBJECT(dev), "ram", &err); @@ -131,6 +158,29 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, INTERRUPT_UART)); + /* AUX / UART1 */ + /* TODO: don't call qemu_char_get_next_serial() here, instead set + * chardev properties for each uart at the board level, once pl011 + * (uart0) has been updated to avoid qemu_char_get_next_serial() + */ + chr = qemu_char_get_next_serial(); + if (chr == NULL) { + chr = qemu_chr_new("bcm2835.uart1", "null", NULL); + } + qdev_prop_set_chr(DEVICE(&s->aux), "chardev", chr); + + object_property_set_bool(OBJECT(&s->aux), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, UART1_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0, + qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, + INTERRUPT_AUX)); + /* Mailboxes */ object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err); if (err) { @@ -144,13 +194,33 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ, INTERRUPT_ARM_MAILBOX)); - /* Property channel */ - object_property_set_int(OBJECT(&s->property), ram_size, "ram-size", &err); + /* Framebuffer */ + vcram_size = (uint32_t)object_property_get_int(OBJECT(s), "vcram-size", + &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_int(OBJECT(&s->fb), ram_size - vcram_size, + "vcram-base", &err); if (err) { error_propagate(errp, err); return; } + object_property_set_bool(OBJECT(&s->fb), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->mbox_mr, MBOX_CHAN_FB << MBOX_AS_CHAN_SHIFT, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->fb), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0, + qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB)); + + /* Property channel */ object_property_set_bool(OBJECT(&s->property), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -171,6 +241,13 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) return; } + object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk", + &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -189,6 +266,24 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) return; } + /* DMA Channels */ + object_property_set_bool(OBJECT(&s->dma), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, DMA_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 0)); + memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1)); + + for (n = 0; n <= 12; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n, + qdev_get_gpio_in_named(DEVICE(&s->ic), + BCM2835_IC_GPU_IRQ, + INTERRUPT_DMA0 + n)); + } } static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) @@ -196,6 +291,8 @@ static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = bcm2835_peripherals_realize; + /* Reason: realize() method uses qemu_char_get_next_serial() */ + dc->cannot_instantiate_with_device_add_yet = true; } static const TypeInfo bcm2835_peripherals_type_info = { diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 032143905e..89a6b35b81 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -42,6 +42,8 @@ static void bcm2836_init(Object *obj) &error_abort); object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals), "board-rev", &error_abort); + object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals), + "vcram-size", &error_abort); qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default()); } diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index fb743bfbd0..1fbc317b74 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -291,6 +291,7 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data) * arm_cpu_class_init() */ dc->cannot_destroy_with_object_finalize_yet = true; + dc->desc = "i.MX25 SOC"; } static const TypeInfo fsl_imx25_type_info = { diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index f2c2ce56f6..0d69a2c70b 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -265,6 +265,7 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data) * arm_cpu_class_init() */ dc->cannot_destroy_with_object_finalize_yet = true; + dc->desc = "i.MX31 SOC"; } static const TypeInfo fsl_imx31_type_info = { diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c new file mode 100644 index 0000000000..55d741918b --- /dev/null +++ b/hw/arm/palmetto-bmc.c @@ -0,0 +1,65 @@ +/* + * OpenPOWER Palmetto BMC + * + * Andrew Jeffery <andrew@aj.id.au> + * + * Copyright 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "hw/arm/arm.h" +#include "hw/arm/ast2400.h" +#include "hw/boards.h" + +static struct arm_boot_info palmetto_bmc_binfo = { + .loader_start = AST2400_SDRAM_BASE, + .board_id = 0, + .nb_cpus = 1, +}; + +typedef struct PalmettoBMCState { + AST2400State soc; + MemoryRegion ram; +} PalmettoBMCState; + +static void palmetto_bmc_init(MachineState *machine) +{ + PalmettoBMCState *bmc; + + bmc = g_new0(PalmettoBMCState, 1); + object_initialize(&bmc->soc, (sizeof(bmc->soc)), TYPE_AST2400); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&bmc->soc), + &error_abort); + + memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size); + memory_region_add_subregion(get_system_memory(), AST2400_SDRAM_BASE, + &bmc->ram); + object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram), + &error_abort); + object_property_set_bool(OBJECT(&bmc->soc), true, "realized", + &error_abort); + + palmetto_bmc_binfo.kernel_filename = machine->kernel_filename; + palmetto_bmc_binfo.initrd_filename = machine->initrd_filename; + palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline; + palmetto_bmc_binfo.ram_size = ram_size; + arm_load_kernel(ARM_CPU(first_cpu), &palmetto_bmc_binfo); +} + +static void palmetto_bmc_machine_init(MachineClass *mc) +{ + mc->desc = "OpenPOWER Palmetto BMC"; + mc->init = palmetto_bmc_init; + mc->max_cpus = 1; + mc->no_sdcard = 1; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->no_sdcard = 1; + mc->no_parallel = 1; +} + +DEFINE_MACHINE("palmetto-bmc", palmetto_bmc_machine_init); diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 65822792fe..83fe8097e5 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -113,6 +113,7 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size) static void raspi2_init(MachineState *machine) { RasPiState *s = g_new0(RasPiState, 1); + uint32_t vcram_size; DriveInfo *di; BlockBackend *blk; BusState *bus; @@ -149,7 +150,9 @@ static void raspi2_init(MachineState *machine) qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); - setup_boot(machine, 2, machine->ram_size); + vcram_size = object_property_get_int(OBJECT(&s->soc), "vcram-size", + &error_abort); + setup_boot(machine, 2, machine->ram_size - vcram_size); } static void raspi2_machine_init(MachineClass *mc) @@ -161,11 +164,6 @@ static void raspi2_machine_init(MachineClass *mc) mc->no_floppy = 1; mc->no_cdrom = 1; mc->max_cpus = BCM2836_NCPUS; - - /* XXX: Temporary restriction in RAM size from the full 1GB. Since - * we do not yet support the framebuffer / GPU, we need to limit - * RAM usable by the OS to sit below the peripherals. - */ - mc->default_ram_size = 0x3F000000; /* BCM2836_PERI_BASE */ + mc->default_ram_size = 1024 * 1024 * 1024; }; DEFINE_MACHINE("raspi2", raspi2_machine_init) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 8c6c99625f..8c3ac0d952 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1345,7 +1345,32 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp) } } -static void virt_instance_init(Object *obj) +static void virt_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = machvirt_init; + /* Start max_cpus at the maximum QEMU supports. We'll further restrict + * it later in machvirt_init, where we have more information about the + * configuration of the particular instance. + */ + mc->max_cpus = MAX_CPUMASK_BITS; + mc->has_dynamic_sysbus = true; + mc->block_default_type = IF_VIRTIO; + mc->no_cdrom = 1; + mc->pci_allow_0_address = true; +} + +static const TypeInfo virt_machine_info = { + .name = TYPE_VIRT_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(VirtMachineState), + .class_size = sizeof(VirtMachineClass), + .class_init = virt_machine_class_init, +}; + +static void virt_2_6_instance_init(Object *obj) { VirtMachineState *vms = VIRT_MACHINE(obj); @@ -1378,34 +1403,28 @@ static void virt_instance_init(Object *obj) "Valid values are 2, 3 and host", NULL); } -static void virt_class_init(ObjectClass *oc, void *data) +static void virt_2_6_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + static GlobalProperty compat_props[] = { + { /* end of list */ } + }; - mc->desc = "ARM Virtual Machine", - mc->init = machvirt_init; - /* Start max_cpus at the maximum QEMU supports. We'll further restrict - * it later in machvirt_init, where we have more information about the - * configuration of the particular instance. - */ - mc->max_cpus = MAX_CPUMASK_BITS; - mc->has_dynamic_sysbus = true; - mc->block_default_type = IF_VIRTIO; - mc->no_cdrom = 1; - mc->pci_allow_0_address = true; + mc->desc = "QEMU 2.6 ARM Virtual Machine"; + mc->alias = "virt"; + mc->compat_props = compat_props; } static const TypeInfo machvirt_info = { - .name = TYPE_VIRT_MACHINE, - .parent = TYPE_MACHINE, - .instance_size = sizeof(VirtMachineState), - .instance_init = virt_instance_init, - .class_size = sizeof(VirtMachineClass), - .class_init = virt_class_init, + .name = MACHINE_TYPE_NAME("virt-2.6"), + .parent = TYPE_VIRT_MACHINE, + .instance_init = virt_2_6_instance_init, + .class_init = virt_2_6_class_init, }; static void machvirt_machine_init(void) { + type_register_static(&virt_machine_info); type_register_static(&machvirt_info); } diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 5931cc8400..69a553cd8d 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -16,6 +16,7 @@ obj-$(CONFIG_SH4) += sh_serial.o obj-$(CONFIG_PSERIES) += spapr_vty.o obj-$(CONFIG_DIGIC) += digic-uart.o obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o +obj-$(CONFIG_RASPI) += bcm2835_aux.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c new file mode 100644 index 0000000000..0394d11a88 --- /dev/null +++ b/hw/char/bcm2835_aux.c @@ -0,0 +1,316 @@ +/* + * BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI). + * Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * Based on pl011.c, copyright terms below: + * + * Arm PrimeCell PL011 UART + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + * + * At present only the core UART functions (data path for tx/rx) are + * implemented. The following features/registers are unimplemented: + * - Line/modem control + * - Scratch register + * - Extra control + * - Baudrate + * - SPI interfaces + */ + +#include "qemu/osdep.h" +#include "hw/char/bcm2835_aux.h" + +#define AUX_IRQ 0x0 +#define AUX_ENABLES 0x4 +#define AUX_MU_IO_REG 0x40 +#define AUX_MU_IER_REG 0x44 +#define AUX_MU_IIR_REG 0x48 +#define AUX_MU_LCR_REG 0x4c +#define AUX_MU_MCR_REG 0x50 +#define AUX_MU_LSR_REG 0x54 +#define AUX_MU_MSR_REG 0x58 +#define AUX_MU_SCRATCH 0x5c +#define AUX_MU_CNTL_REG 0x60 +#define AUX_MU_STAT_REG 0x64 +#define AUX_MU_BAUD_REG 0x68 + +/* bits in IER/IIR registers */ +#define TX_INT 0x1 +#define RX_INT 0x2 + +static void bcm2835_aux_update(BCM2835AuxState *s) +{ + /* signal an interrupt if either: + * 1. rx interrupt is enabled and we have a non-empty rx fifo, or + * 2. the tx interrupt is enabled (since we instantly drain the tx fifo) + */ + s->iir = 0; + if ((s->ier & RX_INT) && s->read_count != 0) { + s->iir |= RX_INT; + } + if (s->ier & TX_INT) { + s->iir |= TX_INT; + } + qemu_set_irq(s->irq, s->iir != 0); +} + +static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835AuxState *s = opaque; + uint32_t c, res; + + switch (offset) { + case AUX_IRQ: + return s->iir != 0; + + case AUX_ENABLES: + return 1; /* mini UART permanently enabled */ + + case AUX_MU_IO_REG: + /* "DLAB bit set means access baudrate register" is NYI */ + c = s->read_fifo[s->read_pos]; + if (s->read_count > 0) { + s->read_count--; + if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) { + s->read_pos = 0; + } + } + if (s->chr) { + qemu_chr_accept_input(s->chr); + } + bcm2835_aux_update(s); + return c; + + case AUX_MU_IER_REG: + /* "DLAB bit set means access baudrate register" is NYI */ + return 0xc0 | s->ier; /* FIFO enables always read 1 */ + + case AUX_MU_IIR_REG: + res = 0xc0; /* FIFO enables */ + /* The spec is unclear on what happens when both tx and rx + * interrupts are active, besides that this cannot occur. At + * present, we choose to prioritise the rx interrupt, since + * the tx fifo is always empty. */ + if (s->read_count != 0) { + res |= 0x4; + } else { + res |= 0x2; + } + if (s->iir == 0) { + res |= 0x1; + } + return res; + + case AUX_MU_LCR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__); + return 0; + + case AUX_MU_MCR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__); + return 0; + + case AUX_MU_LSR_REG: + res = 0x60; /* tx idle, empty */ + if (s->read_count != 0) { + res |= 0x1; + } + return res; + + case AUX_MU_MSR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__); + return 0; + + case AUX_MU_SCRATCH: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__); + return 0; + + case AUX_MU_CNTL_REG: + return 0x3; /* tx, rx enabled */ + + case AUX_MU_STAT_REG: + res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */ + if (s->read_count > 0) { + res |= 0x1; /* data in input buffer */ + assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN); + res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */ + } + return res; + + case AUX_MU_BAUD_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__); + return 0; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } +} + +static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + BCM2835AuxState *s = opaque; + unsigned char ch; + + switch (offset) { + case AUX_ENABLES: + if (value != 1) { + qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI " + "or disable UART\n", __func__); + } + break; + + case AUX_MU_IO_REG: + /* "DLAB bit set means access baudrate register" is NYI */ + ch = value; + if (s->chr) { + qemu_chr_fe_write(s->chr, &ch, 1); + } + break; + + case AUX_MU_IER_REG: + /* "DLAB bit set means access baudrate register" is NYI */ + s->ier = value & (TX_INT | RX_INT); + bcm2835_aux_update(s); + break; + + case AUX_MU_IIR_REG: + if (value & 0x2) { + s->read_count = 0; + } + break; + + case AUX_MU_LCR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__); + break; + + case AUX_MU_MCR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__); + break; + + case AUX_MU_SCRATCH: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__); + break; + + case AUX_MU_CNTL_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__); + break; + + case AUX_MU_BAUD_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + } + + bcm2835_aux_update(s); +} + +static int bcm2835_aux_can_receive(void *opaque) +{ + BCM2835AuxState *s = opaque; + + return s->read_count < BCM2835_AUX_RX_FIFO_LEN; +} + +static void bcm2835_aux_put_fifo(void *opaque, uint8_t value) +{ + BCM2835AuxState *s = opaque; + int slot; + + slot = s->read_pos + s->read_count; + if (slot >= BCM2835_AUX_RX_FIFO_LEN) { + slot -= BCM2835_AUX_RX_FIFO_LEN; + } + s->read_fifo[slot] = value; + s->read_count++; + if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) { + /* buffer full */ + } + bcm2835_aux_update(s); +} + +static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size) +{ + bcm2835_aux_put_fifo(opaque, *buf); +} + +static const MemoryRegionOps bcm2835_aux_ops = { + .read = bcm2835_aux_read, + .write = bcm2835_aux_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_bcm2835_aux = { + .name = TYPE_BCM2835_AUX, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState, + BCM2835_AUX_RX_FIFO_LEN), + VMSTATE_UINT8(read_pos, BCM2835AuxState), + VMSTATE_UINT8(read_count, BCM2835AuxState), + VMSTATE_UINT8(ier, BCM2835AuxState), + VMSTATE_UINT8(iir, BCM2835AuxState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_aux_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + BCM2835AuxState *s = BCM2835_AUX(obj); + + memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s, + TYPE_BCM2835_AUX, 0x100); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static void bcm2835_aux_realize(DeviceState *dev, Error **errp) +{ + BCM2835AuxState *s = BCM2835_AUX(dev); + + if (s->chr) { + qemu_chr_add_handlers(s->chr, bcm2835_aux_can_receive, + bcm2835_aux_receive, NULL, s); + } +} + +static Property bcm2835_aux_props[] = { + DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void bcm2835_aux_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = bcm2835_aux_realize; + dc->vmsd = &vmstate_bcm2835_aux; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + dc->props = bcm2835_aux_props; +} + +static const TypeInfo bcm2835_aux_info = { + .name = TYPE_BCM2835_AUX, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835AuxState), + .instance_init = bcm2835_aux_init, + .class_init = bcm2835_aux_class_init, +}; + +static void bcm2835_aux_register_types(void) +{ + type_register_static(&bcm2835_aux_info); +} + +type_init(bcm2835_aux_register_types) diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index f0cf431a0f..d99780eeb9 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -27,6 +27,7 @@ endif obj-$(CONFIG_OMAP) += omap_dss.o obj-$(CONFIG_OMAP) += omap_lcdc.o obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o +obj-$(CONFIG_RASPI) += bcm2835_fb.o obj-$(CONFIG_SM501) += sm501.o obj-$(CONFIG_TCX) += tcx.o obj-$(CONFIG_CG3) += cg3.o diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c new file mode 100644 index 0000000000..779b56f07b --- /dev/null +++ b/hw/display/bcm2835_fb.c @@ -0,0 +1,424 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann. + * This code is licensed under the GNU GPLv2 and later. + * + * Heavily based on milkymist-vgafb.c, copyright terms below: + * QEMU model of the Milkymist VGA framebuffer. + * + * Copyright (c) 2010-2012 Michael Walle <michael@walle.cc> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "hw/display/bcm2835_fb.h" +#include "hw/display/framebuffer.h" +#include "ui/pixel_ops.h" +#include "hw/misc/bcm2835_mbox_defs.h" + +#define DEFAULT_VCRAM_SIZE 0x4000000 +#define BCM2835_FB_OFFSET 0x00100000 + +static void fb_invalidate_display(void *opaque) +{ + BCM2835FBState *s = BCM2835_FB(opaque); + + s->invalidate = true; +} + +static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, + int width, int deststep) +{ + BCM2835FBState *s = opaque; + uint16_t rgb565; + uint32_t rgb888; + uint8_t r, g, b; + DisplaySurface *surface = qemu_console_surface(s->con); + int bpp = surface_bits_per_pixel(surface); + + while (width--) { + switch (s->bpp) { + case 8: + /* lookup palette starting at video ram base + * TODO: cache translation, rather than doing this each time! + */ + rgb888 = ldl_le_phys(&s->dma_as, s->vcram_base + (*src << 2)); + r = (rgb888 >> 0) & 0xff; + g = (rgb888 >> 8) & 0xff; + b = (rgb888 >> 16) & 0xff; + src++; + break; + case 16: + rgb565 = lduw_le_p(src); + r = ((rgb565 >> 11) & 0x1f) << 3; + g = ((rgb565 >> 5) & 0x3f) << 2; + b = ((rgb565 >> 0) & 0x1f) << 3; + src += 2; + break; + case 24: + rgb888 = ldl_le_p(src); + r = (rgb888 >> 0) & 0xff; + g = (rgb888 >> 8) & 0xff; + b = (rgb888 >> 16) & 0xff; + src += 3; + break; + case 32: + rgb888 = ldl_le_p(src); + r = (rgb888 >> 0) & 0xff; + g = (rgb888 >> 8) & 0xff; + b = (rgb888 >> 16) & 0xff; + src += 4; + break; + default: + r = 0; + g = 0; + b = 0; + break; + } + + if (s->pixo == 0) { + /* swap to BGR pixel format */ + uint8_t tmp = r; + r = b; + b = tmp; + } + + switch (bpp) { + case 8: + *dst++ = rgb_to_pixel8(r, g, b); + break; + case 15: + *(uint16_t *)dst = rgb_to_pixel15(r, g, b); + dst += 2; + break; + case 16: + *(uint16_t *)dst = rgb_to_pixel16(r, g, b); + dst += 2; + break; + case 24: + rgb888 = rgb_to_pixel24(r, g, b); + *dst++ = rgb888 & 0xff; + *dst++ = (rgb888 >> 8) & 0xff; + *dst++ = (rgb888 >> 16) & 0xff; + break; + case 32: + *(uint32_t *)dst = rgb_to_pixel32(r, g, b); + dst += 4; + break; + default: + return; + } + } +} + +static void fb_update_display(void *opaque) +{ + BCM2835FBState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + int first = 0; + int last = 0; + int src_width = 0; + int dest_width = 0; + + if (s->lock || !s->xres) { + return; + } + + src_width = s->xres * (s->bpp >> 3); + dest_width = s->xres; + + switch (surface_bits_per_pixel(surface)) { + case 0: + return; + case 8: + break; + case 15: + dest_width *= 2; + break; + case 16: + dest_width *= 2; + break; + case 24: + dest_width *= 3; + break; + case 32: + dest_width *= 4; + break; + default: + hw_error("bcm2835_fb: bad color depth\n"); + break; + } + + if (s->invalidate) { + framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base, + s->yres, src_width); + } + + framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, + src_width, dest_width, 0, s->invalidate, + draw_line_src16, s, &first, &last); + + if (first >= 0) { + dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1); + } + + s->invalidate = false; +} + +static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) +{ + value &= ~0xf; + + s->lock = true; + + s->xres = ldl_le_phys(&s->dma_as, value); + s->yres = ldl_le_phys(&s->dma_as, value + 4); + s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8); + s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12); + s->bpp = ldl_le_phys(&s->dma_as, value + 20); + s->xoffset = ldl_le_phys(&s->dma_as, value + 24); + s->yoffset = ldl_le_phys(&s->dma_as, value + 28); + + s->base = s->vcram_base | (value & 0xc0000000); + s->base += BCM2835_FB_OFFSET; + + /* TODO - Manage properly virtual resolution */ + + s->pitch = s->xres * (s->bpp >> 3); + s->size = s->yres * s->pitch; + + stl_le_phys(&s->dma_as, value + 16, s->pitch); + stl_le_phys(&s->dma_as, value + 32, s->base); + stl_le_phys(&s->dma_as, value + 36, s->size); + + s->invalidate = true; + qemu_console_resize(s->con, s->xres, s->yres); + s->lock = false; +} + +void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres, + uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp, + uint32_t *pixo, uint32_t *alpha) +{ + s->lock = true; + + /* TODO: input validation! */ + if (xres) { + s->xres = *xres; + } + if (yres) { + s->yres = *yres; + } + if (xoffset) { + s->xoffset = *xoffset; + } + if (yoffset) { + s->yoffset = *yoffset; + } + if (bpp) { + s->bpp = *bpp; + } + if (pixo) { + s->pixo = *pixo; + } + if (alpha) { + s->alpha = *alpha; + } + + /* TODO - Manage properly virtual resolution */ + + s->pitch = s->xres * (s->bpp >> 3); + s->size = s->yres * s->pitch; + + s->invalidate = true; + qemu_console_resize(s->con, s->xres, s->yres); + s->lock = false; +} + +static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835FBState *s = opaque; + uint32_t res = 0; + + switch (offset) { + case MBOX_AS_DATA: + res = MBOX_CHAN_FB; + s->pending = false; + qemu_set_irq(s->mbox_irq, 0); + break; + + case MBOX_AS_PENDING: + res = s->pending; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } + + return res; +} + +static void bcm2835_fb_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + BCM2835FBState *s = opaque; + + switch (offset) { + case MBOX_AS_DATA: + /* bcm2835_mbox should check our pending status before pushing */ + assert(!s->pending); + s->pending = true; + bcm2835_fb_mbox_push(s, value); + qemu_set_irq(s->mbox_irq, 1); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return; + } +} + +static const MemoryRegionOps bcm2835_fb_ops = { + .read = bcm2835_fb_read, + .write = bcm2835_fb_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_bcm2835_fb = { + .name = TYPE_BCM2835_FB, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(lock, BCM2835FBState), + VMSTATE_BOOL(invalidate, BCM2835FBState), + VMSTATE_BOOL(pending, BCM2835FBState), + VMSTATE_UINT32(xres, BCM2835FBState), + VMSTATE_UINT32(yres, BCM2835FBState), + VMSTATE_UINT32(xres_virtual, BCM2835FBState), + VMSTATE_UINT32(yres_virtual, BCM2835FBState), + VMSTATE_UINT32(xoffset, BCM2835FBState), + VMSTATE_UINT32(yoffset, BCM2835FBState), + VMSTATE_UINT32(bpp, BCM2835FBState), + VMSTATE_UINT32(base, BCM2835FBState), + VMSTATE_UINT32(pitch, BCM2835FBState), + VMSTATE_UINT32(size, BCM2835FBState), + VMSTATE_UINT32(pixo, BCM2835FBState), + VMSTATE_UINT32(alpha, BCM2835FBState), + VMSTATE_END_OF_LIST() + } +}; + +static const GraphicHwOps vgafb_ops = { + .invalidate = fb_invalidate_display, + .gfx_update = fb_update_display, +}; + +static void bcm2835_fb_init(Object *obj) +{ + BCM2835FBState *s = BCM2835_FB(obj); + + memory_region_init_io(&s->iomem, obj, &bcm2835_fb_ops, s, TYPE_BCM2835_FB, + 0x10); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq); +} + +static void bcm2835_fb_reset(DeviceState *dev) +{ + BCM2835FBState *s = BCM2835_FB(dev); + + s->pending = false; + + s->xres_virtual = s->xres; + s->yres_virtual = s->yres; + s->xoffset = 0; + s->yoffset = 0; + s->base = s->vcram_base + BCM2835_FB_OFFSET; + s->pitch = s->xres * (s->bpp >> 3); + s->size = s->yres * s->pitch; + + s->invalidate = true; + s->lock = false; +} + +static void bcm2835_fb_realize(DeviceState *dev, Error **errp) +{ + BCM2835FBState *s = BCM2835_FB(dev); + Error *err = NULL; + Object *obj; + + if (s->vcram_base == 0) { + error_setg(errp, "%s: required vcram-base property not set", __func__); + return; + } + + obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); + if (obj == NULL) { + error_setg(errp, "%s: required dma-mr link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->dma_mr = MEMORY_REGION(obj); + address_space_init(&s->dma_as, s->dma_mr, NULL); + + bcm2835_fb_reset(dev); + + s->con = graphic_console_init(dev, 0, &vgafb_ops, s); + qemu_console_resize(s->con, s->xres, s->yres); +} + +static Property bcm2835_fb_props[] = { + DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/ + DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size, + DEFAULT_VCRAM_SIZE), + DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640), + DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480), + DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16), + DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */ + DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */ + DEFINE_PROP_END_OF_LIST() +}; + +static void bcm2835_fb_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = bcm2835_fb_props; + dc->realize = bcm2835_fb_realize; + dc->reset = bcm2835_fb_reset; + dc->vmsd = &vmstate_bcm2835_fb; +} + +static TypeInfo bcm2835_fb_info = { + .name = TYPE_BCM2835_FB, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835FBState), + .class_init = bcm2835_fb_class_init, + .instance_init = bcm2835_fb_init, +}; + +static void bcm2835_fb_register_types(void) +{ + type_register_static(&bcm2835_fb_info); +} + +type_init(bcm2835_fb_register_types) diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs index 0e65ed0d74..a1abbcf746 100644 --- a/hw/dma/Makefile.objs +++ b/hw/dma/Makefile.objs @@ -11,3 +11,4 @@ common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o +obj-$(CONFIG_RASPI) += bcm2835_dma.o diff --git a/hw/dma/bcm2835_dma.c b/hw/dma/bcm2835_dma.c new file mode 100644 index 0000000000..c7ce4e4881 --- /dev/null +++ b/hw/dma/bcm2835_dma.c @@ -0,0 +1,408 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#include "qemu/osdep.h" +#include "hw/dma/bcm2835_dma.h" + +/* DMA CS Control and Status bits */ +#define BCM2708_DMA_ACTIVE (1 << 0) +#define BCM2708_DMA_END (1 << 1) /* GE */ +#define BCM2708_DMA_INT (1 << 2) +#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */ +#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */ +#define BCM2708_DMA_ERR (1 << 8) +#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */ +#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */ + +/* DMA control block "info" field bits */ +#define BCM2708_DMA_INT_EN (1 << 0) +#define BCM2708_DMA_TDMODE (1 << 1) +#define BCM2708_DMA_WAIT_RESP (1 << 3) +#define BCM2708_DMA_D_INC (1 << 4) +#define BCM2708_DMA_D_WIDTH (1 << 5) +#define BCM2708_DMA_D_DREQ (1 << 6) +#define BCM2708_DMA_D_IGNORE (1 << 7) +#define BCM2708_DMA_S_INC (1 << 8) +#define BCM2708_DMA_S_WIDTH (1 << 9) +#define BCM2708_DMA_S_DREQ (1 << 10) +#define BCM2708_DMA_S_IGNORE (1 << 11) + +/* Register offsets */ +#define BCM2708_DMA_CS 0x00 /* Control and Status */ +#define BCM2708_DMA_ADDR 0x04 /* Control block address */ +/* the current control block appears in the following registers - read only */ +#define BCM2708_DMA_INFO 0x08 +#define BCM2708_DMA_SOURCE_AD 0x0c +#define BCM2708_DMA_DEST_AD 0x10 +#define BCM2708_DMA_TXFR_LEN 0x14 +#define BCM2708_DMA_STRIDE 0x18 +#define BCM2708_DMA_NEXTCB 0x1C +#define BCM2708_DMA_DEBUG 0x20 + +#define BCM2708_DMA_INT_STATUS 0xfe0 /* Interrupt status of each channel */ +#define BCM2708_DMA_ENABLE 0xff0 /* Global enable bits for each channel */ + +#define BCM2708_DMA_CS_RW_MASK 0x30ff0001 /* All RW bits in DMA_CS */ + +static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c) +{ + BCM2835DMAChan *ch = &s->chan[c]; + uint32_t data, xlen, ylen; + int16_t dst_stride, src_stride; + + if (!(s->enable & (1 << c))) { + return; + } + + while ((s->enable & (1 << c)) && (ch->conblk_ad != 0)) { + /* CB fetch */ + ch->ti = ldl_le_phys(&s->dma_as, ch->conblk_ad); + ch->source_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 4); + ch->dest_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 8); + ch->txfr_len = ldl_le_phys(&s->dma_as, ch->conblk_ad + 12); + ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16); + ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20); + + if (ch->ti & BCM2708_DMA_TDMODE) { + /* 2D transfer mode */ + ylen = (ch->txfr_len >> 16) & 0x3fff; + xlen = ch->txfr_len & 0xffff; + dst_stride = ch->stride >> 16; + src_stride = ch->stride & 0xffff; + } else { + ylen = 1; + xlen = ch->txfr_len; + dst_stride = 0; + src_stride = 0; + } + + while (ylen != 0) { + /* Normal transfer mode */ + while (xlen != 0) { + if (ch->ti & BCM2708_DMA_S_IGNORE) { + /* Ignore reads */ + data = 0; + } else { + data = ldl_le_phys(&s->dma_as, ch->source_ad); + } + if (ch->ti & BCM2708_DMA_S_INC) { + ch->source_ad += 4; + } + + if (ch->ti & BCM2708_DMA_D_IGNORE) { + /* Ignore writes */ + } else { + stl_le_phys(&s->dma_as, ch->dest_ad, data); + } + if (ch->ti & BCM2708_DMA_D_INC) { + ch->dest_ad += 4; + } + + /* update remaining transfer length */ + xlen -= 4; + if (ch->ti & BCM2708_DMA_TDMODE) { + ch->txfr_len = (ylen << 16) | xlen; + } else { + ch->txfr_len = xlen; + } + } + + if (--ylen != 0) { + ch->source_ad += src_stride; + ch->dest_ad += dst_stride; + } + } + ch->cs |= BCM2708_DMA_END; + if (ch->ti & BCM2708_DMA_INT_EN) { + ch->cs |= BCM2708_DMA_INT; + s->int_status |= (1 << c); + qemu_set_irq(ch->irq, 1); + } + + /* Process next CB */ + ch->conblk_ad = ch->nextconbk; + } + + ch->cs &= ~BCM2708_DMA_ACTIVE; + ch->cs |= BCM2708_DMA_ISPAUSED; +} + +static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch) +{ + ch->cs = 0; + ch->conblk_ad = 0; +} + +static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset, + unsigned size, unsigned c) +{ + BCM2835DMAChan *ch; + uint32_t res = 0; + + assert(size == 4); + assert(c < BCM2835_DMA_NCHANS); + + ch = &s->chan[c]; + + switch (offset) { + case BCM2708_DMA_CS: + res = ch->cs; + break; + case BCM2708_DMA_ADDR: + res = ch->conblk_ad; + break; + case BCM2708_DMA_INFO: + res = ch->ti; + break; + case BCM2708_DMA_SOURCE_AD: + res = ch->source_ad; + break; + case BCM2708_DMA_DEST_AD: + res = ch->dest_ad; + break; + case BCM2708_DMA_TXFR_LEN: + res = ch->txfr_len; + break; + case BCM2708_DMA_STRIDE: + res = ch->stride; + break; + case BCM2708_DMA_NEXTCB: + res = ch->nextconbk; + break; + case BCM2708_DMA_DEBUG: + res = ch->debug; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + break; + } + return res; +} + +static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset, + uint64_t value, unsigned size, unsigned c) +{ + BCM2835DMAChan *ch; + uint32_t oldcs; + + assert(size == 4); + assert(c < BCM2835_DMA_NCHANS); + + ch = &s->chan[c]; + + switch (offset) { + case BCM2708_DMA_CS: + oldcs = ch->cs; + if (value & BCM2708_DMA_RESET) { + bcm2835_dma_chan_reset(ch); + } + if (value & BCM2708_DMA_ABORT) { + /* abort is a no-op, since we always run to completion */ + } + if (value & BCM2708_DMA_END) { + ch->cs &= ~BCM2708_DMA_END; + } + if (value & BCM2708_DMA_INT) { + ch->cs &= ~BCM2708_DMA_INT; + s->int_status &= ~(1 << c); + qemu_set_irq(ch->irq, 0); + } + ch->cs &= ~BCM2708_DMA_CS_RW_MASK; + ch->cs |= (value & BCM2708_DMA_CS_RW_MASK); + if (!(oldcs & BCM2708_DMA_ACTIVE) && (ch->cs & BCM2708_DMA_ACTIVE)) { + bcm2835_dma_update(s, c); + } + break; + case BCM2708_DMA_ADDR: + ch->conblk_ad = value; + break; + case BCM2708_DMA_DEBUG: + ch->debug = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + break; + } +} + +static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835DMAState *s = opaque; + + if (offset < 0xf00) { + return bcm2835_dma_read(s, (offset & 0xff), size, (offset >> 8) & 0xf); + } else { + switch (offset) { + case BCM2708_DMA_INT_STATUS: + return s->int_status; + case BCM2708_DMA_ENABLE: + return s->enable; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } + } +} + +static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size) +{ + return bcm2835_dma_read(opaque, (offset & 0xff), size, 15); +} + +static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + BCM2835DMAState *s = opaque; + + if (offset < 0xf00) { + bcm2835_dma_write(s, (offset & 0xff), value, size, (offset >> 8) & 0xf); + } else { + switch (offset) { + case BCM2708_DMA_INT_STATUS: + break; + case BCM2708_DMA_ENABLE: + s->enable = (value & 0xffff); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + } + } + +} + +static void bcm2835_dma15_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + bcm2835_dma_write(opaque, (offset & 0xff), value, size, 15); +} + +static const MemoryRegionOps bcm2835_dma0_ops = { + .read = bcm2835_dma0_read, + .write = bcm2835_dma0_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const MemoryRegionOps bcm2835_dma15_ops = { + .read = bcm2835_dma15_read, + .write = bcm2835_dma15_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_bcm2835_dma_chan = { + .name = TYPE_BCM2835_DMA "-chan", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cs, BCM2835DMAChan), + VMSTATE_UINT32(conblk_ad, BCM2835DMAChan), + VMSTATE_UINT32(ti, BCM2835DMAChan), + VMSTATE_UINT32(source_ad, BCM2835DMAChan), + VMSTATE_UINT32(dest_ad, BCM2835DMAChan), + VMSTATE_UINT32(txfr_len, BCM2835DMAChan), + VMSTATE_UINT32(stride, BCM2835DMAChan), + VMSTATE_UINT32(nextconbk, BCM2835DMAChan), + VMSTATE_UINT32(debug, BCM2835DMAChan), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_bcm2835_dma = { + .name = TYPE_BCM2835_DMA, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1, + vmstate_bcm2835_dma_chan, BCM2835DMAChan), + VMSTATE_UINT32(int_status, BCM2835DMAState), + VMSTATE_UINT32(enable, BCM2835DMAState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_dma_init(Object *obj) +{ + BCM2835DMAState *s = BCM2835_DMA(obj); + int n; + + /* DMA channels 0-14 occupy a contiguous block of IO memory, along + * with the global enable and interrupt status bits. Channel 15 + * has the same register map, but is mapped at a discontiguous + * address in a separate IO block. + */ + memory_region_init_io(&s->iomem0, OBJECT(s), &bcm2835_dma0_ops, s, + TYPE_BCM2835_DMA, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem0); + + memory_region_init_io(&s->iomem15, OBJECT(s), &bcm2835_dma15_ops, s, + TYPE_BCM2835_DMA "-chan15", 0x100); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem15); + + for (n = 0; n < 16; n++) { + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->chan[n].irq); + } +} + +static void bcm2835_dma_reset(DeviceState *dev) +{ + BCM2835DMAState *s = BCM2835_DMA(dev); + int n; + + s->enable = 0xffff; + s->int_status = 0; + for (n = 0; n < BCM2835_DMA_NCHANS; n++) { + bcm2835_dma_chan_reset(&s->chan[n]); + } +} + +static void bcm2835_dma_realize(DeviceState *dev, Error **errp) +{ + BCM2835DMAState *s = BCM2835_DMA(dev); + Error *err = NULL; + Object *obj; + + obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); + if (obj == NULL) { + error_setg(errp, "%s: required dma-mr link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->dma_mr = MEMORY_REGION(obj); + address_space_init(&s->dma_as, s->dma_mr, NULL); + + bcm2835_dma_reset(dev); +} + +static void bcm2835_dma_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = bcm2835_dma_realize; + dc->reset = bcm2835_dma_reset; + dc->vmsd = &vmstate_bcm2835_dma; +} + +static TypeInfo bcm2835_dma_info = { + .name = TYPE_BCM2835_DMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835DMAState), + .class_init = bcm2835_dma_class_init, + .instance_init = bcm2835_dma_init, +}; + +static void bcm2835_dma_register_types(void) +{ + type_register_static(&bcm2835_dma_info); +} + +type_init(bcm2835_dma_register_types) diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c index e43544876b..a01e43ebeb 100644 --- a/hw/i2c/imx_i2c.c +++ b/hw/i2c/imx_i2c.c @@ -319,6 +319,7 @@ static void imx_i2c_class_init(ObjectClass *klass, void *data) dc->vmsd = &imx_i2c_vmstate; dc->reset = imx_i2c_reset; dc->realize = imx_i2c_realize; + dc->desc = "i.MX I2C Controller"; } static const TypeInfo imx_i2c_type_info = { diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 6a13a39519..0e47f0f9ec 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -31,3 +31,4 @@ obj-$(CONFIG_XICS_KVM) += xics_kvm.o obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o obj-$(CONFIG_S390_FLIC) += s390_flic.o obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o +obj-$(CONFIG_ASPEED_SOC) += aspeed_vic.o diff --git a/hw/intc/aspeed_vic.c b/hw/intc/aspeed_vic.c new file mode 100644 index 0000000000..19a0ff7486 --- /dev/null +++ b/hw/intc/aspeed_vic.c @@ -0,0 +1,339 @@ +/* + * ASPEED Interrupt Controller (New) + * + * Andrew Jeffery <andrew@aj.id.au> + * + * Copyright 2015, 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +/* The hardware exposes two register sets, a legacy set and a 'new' set. The + * model implements the 'new' register set, and logs warnings on accesses to + * the legacy IO space. + * + * The hardware uses 32bit registers to manage 51 IRQs, with low and high + * registers for each conceptual register. The device model's implementation + * uses 64bit data types to store both low and high register values (in the one + * member), but must cope with access offset values in multiples of 4 passed to + * the callbacks. As such the read() and write() implementations process the + * provided offset to understand whether the access is requesting the lower or + * upper 32 bits of the 64bit member. + * + * Additionally, the "Interrupt Enable", "Edge Status" and "Software Interrupt" + * fields have separate "enable"/"status" and "clear" registers, where set bits + * are written to one or the other to change state (avoiding a + * read-modify-write sequence). + */ + +#include "qemu/osdep.h" +#include <inttypes.h> +#include "hw/intc/aspeed_vic.h" +#include "qemu/bitops.h" +#include "trace.h" + +#define AVIC_NEW_BASE_OFFSET 0x80 + +#define AVIC_L_MASK 0xFFFFFFFFU +#define AVIC_H_MASK 0x0007FFFFU +#define AVIC_EVENT_W_MASK (0x78000ULL << 32) + +static void aspeed_vic_update(AspeedVICState *s) +{ + uint64_t new = (s->raw & s->enable); + uint64_t flags; + + flags = new & s->select; + trace_aspeed_vic_update_fiq(!!flags); + qemu_set_irq(s->fiq, !!flags); + + flags = new & ~s->select; + trace_aspeed_vic_update_irq(!!flags); + qemu_set_irq(s->irq, !!flags); +} + +static void aspeed_vic_set_irq(void *opaque, int irq, int level) +{ + uint64_t irq_mask; + bool raise; + AspeedVICState *s = (AspeedVICState *)opaque; + + if (irq > ASPEED_VIC_NR_IRQS) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + trace_aspeed_vic_set_irq(irq, level); + + irq_mask = BIT(irq); + if (s->sense & irq_mask) { + /* level-triggered */ + if (s->event & irq_mask) { + /* high-sensitive */ + raise = level; + } else { + /* low-sensitive */ + raise = !level; + } + s->raw = deposit64(s->raw, irq, 1, raise); + } else { + uint64_t old_level = s->level & irq_mask; + + /* edge-triggered */ + if (s->dual_edge & irq_mask) { + raise = (!!old_level) != (!!level); + } else { + if (s->event & irq_mask) { + /* rising-sensitive */ + raise = !old_level && level; + } else { + /* falling-sensitive */ + raise = old_level && !level; + } + } + if (raise) { + s->raw = deposit64(s->raw, irq, 1, raise); + } + } + s->level = deposit64(s->level, irq, 1, level); + aspeed_vic_update(s); +} + +static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size) +{ + uint64_t val; + const bool high = !!(offset & 0x4); + hwaddr n_offset = (offset & ~0x4); + AspeedVICState *s = (AspeedVICState *)opaque; + + if (offset < AVIC_NEW_BASE_OFFSET) { + qemu_log_mask(LOG_UNIMP, "%s: Ignoring read from legacy registers " + "at 0x%" HWADDR_PRIx "[%u]\n", __func__, offset, size); + return 0; + } + + n_offset -= AVIC_NEW_BASE_OFFSET; + + switch (n_offset) { + case 0x0: /* IRQ Status */ + val = s->raw & ~s->select & s->enable; + break; + case 0x08: /* FIQ Status */ + val = s->raw & s->select & s->enable; + break; + case 0x10: /* Raw Interrupt Status */ + val = s->raw; + break; + case 0x18: /* Interrupt Selection */ + val = s->select; + break; + case 0x20: /* Interrupt Enable */ + val = s->enable; + break; + case 0x30: /* Software Interrupt */ + val = s->trigger; + break; + case 0x40: /* Interrupt Sensitivity */ + val = s->sense; + break; + case 0x48: /* Interrupt Both Edge Trigger Control */ + val = s->dual_edge; + break; + case 0x50: /* Interrupt Event */ + val = s->event; + break; + case 0x60: /* Edge Triggered Interrupt Status */ + val = s->raw & ~s->sense; + break; + /* Illegal */ + case 0x28: /* Interrupt Enable Clear */ + case 0x38: /* Software Interrupt Clear */ + case 0x58: /* Edge Triggered Interrupt Clear */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Read of write-only register with offset 0x%" + HWADDR_PRIx "\n", __func__, offset); + val = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad register at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + val = 0; + break; + } + if (high) { + val = extract64(val, 32, 19); + } + trace_aspeed_vic_read(offset, size, val); + return val; +} + +static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + const bool high = !!(offset & 0x4); + hwaddr n_offset = (offset & ~0x4); + AspeedVICState *s = (AspeedVICState *)opaque; + + if (offset < AVIC_NEW_BASE_OFFSET) { + qemu_log_mask(LOG_UNIMP, + "%s: Ignoring write to legacy registers at 0x%" + HWADDR_PRIx "[%u] <- 0x%" PRIx64 "\n", __func__, offset, + size, data); + return; + } + + n_offset -= AVIC_NEW_BASE_OFFSET; + trace_aspeed_vic_write(offset, size, data); + + /* Given we have members using separate enable/clear registers, deposit64() + * isn't quite the tool for the job. Instead, relocate the incoming bits to + * the required bit offset based on the provided access address + */ + if (high) { + data &= AVIC_H_MASK; + data <<= 32; + } else { + data &= AVIC_L_MASK; + } + + switch (n_offset) { + case 0x18: /* Interrupt Selection */ + /* Register has deposit64() semantics - overwrite requested 32 bits */ + if (high) { + s->select &= AVIC_L_MASK; + } else { + s->select &= ((uint64_t) AVIC_H_MASK) << 32; + } + s->select |= data; + break; + case 0x20: /* Interrupt Enable */ + s->enable |= data; + break; + case 0x28: /* Interrupt Enable Clear */ + s->enable &= ~data; + break; + case 0x30: /* Software Interrupt */ + qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. " + "IRQs requested: 0x%016" PRIx64 "\n", __func__, data); + break; + case 0x38: /* Software Interrupt Clear */ + qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. " + "IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data); + break; + case 0x50: /* Interrupt Event */ + /* Register has deposit64() semantics - overwrite the top four valid + * IRQ bits, as only the top four IRQs (GPIOs) can change their event + * type */ + if (high) { + s->event &= ~AVIC_EVENT_W_MASK; + s->event |= (data & AVIC_EVENT_W_MASK); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "Ignoring invalid write to interrupt event register"); + } + break; + case 0x58: /* Edge Triggered Interrupt Clear */ + s->raw &= ~(data & ~s->sense); + break; + case 0x00: /* IRQ Status */ + case 0x08: /* FIQ Status */ + case 0x10: /* Raw Interrupt Status */ + case 0x40: /* Interrupt Sensitivity */ + case 0x48: /* Interrupt Both Edge Trigger Control */ + case 0x60: /* Edge Triggered Interrupt Status */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write of read-only register with offset 0x%" + HWADDR_PRIx "\n", __func__, offset); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad register at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } + aspeed_vic_update(s); +} + +static const MemoryRegionOps aspeed_vic_ops = { + .read = aspeed_vic_read, + .write = aspeed_vic_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static void aspeed_vic_reset(DeviceState *dev) +{ + AspeedVICState *s = ASPEED_VIC(dev); + + s->level = 0; + s->raw = 0; + s->select = 0; + s->enable = 0; + s->trigger = 0; + s->sense = 0x1F07FFF8FFFFULL; + s->dual_edge = 0xF800070000ULL; + s->event = 0x5F07FFF8FFFFULL; +} + +#define AVIC_IO_REGION_SIZE 0x20000 + +static void aspeed_vic_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedVICState *s = ASPEED_VIC(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s, + TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE); + + sysbus_init_mmio(sbd, &s->iomem); + + qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS); + sysbus_init_irq(sbd, &s->irq); + sysbus_init_irq(sbd, &s->fiq); +} + +static const VMStateDescription vmstate_aspeed_vic = { + .name = "aspeed.new-vic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(level, AspeedVICState), + VMSTATE_UINT64(raw, AspeedVICState), + VMSTATE_UINT64(select, AspeedVICState), + VMSTATE_UINT64(enable, AspeedVICState), + VMSTATE_UINT64(trigger, AspeedVICState), + VMSTATE_UINT64(sense, AspeedVICState), + VMSTATE_UINT64(dual_edge, AspeedVICState), + VMSTATE_UINT64(event, AspeedVICState), + VMSTATE_END_OF_LIST() + } +}; + +static void aspeed_vic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = aspeed_vic_realize; + dc->reset = aspeed_vic_reset; + dc->desc = "ASPEED Interrupt Controller (New)"; + dc->vmsd = &vmstate_aspeed_vic; +} + +static const TypeInfo aspeed_vic_info = { + .name = TYPE_ASPEED_VIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedVICState), + .class_init = aspeed_vic_class_init, +}; + +static void aspeed_vic_register_types(void) +{ + type_register_static(&aspeed_vic_info); +} + +type_init(aspeed_vic_register_types); diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index ea6cd3c9ff..44ac2e14a3 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -28,6 +28,7 @@ obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o obj-$(CONFIG_IMX) += imx_ccm.o obj-$(CONFIG_IMX) += imx31_ccm.o obj-$(CONFIG_IMX) += imx25_ccm.o +obj-$(CONFIG_IMX) += imx6_ccm.o obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o obj-$(CONFIG_MAINSTONE) += mst_fpga.o diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 41fbbe3e7f..15dcc02f99 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -17,6 +17,11 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) uint32_t tot_len; size_t resplen; uint32_t tmp; + int n; + uint32_t offset, length, color; + uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha; + uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL, + *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL; value &= ~0xf; @@ -60,7 +65,14 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* base */ stl_le_phys(&s->dma_as, value + 12, 0); /* size */ - stl_le_phys(&s->dma_as, value + 16, s->ram_size); + stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base); + resplen = 8; + break; + case 0x00010006: /* Get VC memory */ + /* base */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base); + /* size */ + stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size); resplen = 8; break; case 0x00028001: /* Set power state */ @@ -122,6 +134,114 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 8; break; + /* Frame buffer */ + + case 0x00040001: /* Allocate buffer */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->base); + stl_le_phys(&s->dma_as, value + 16, s->fbdev->size); + resplen = 8; + break; + case 0x00048001: /* Release buffer */ + resplen = 0; + break; + case 0x00040002: /* Blank screen */ + resplen = 4; + break; + case 0x00040003: /* Get display width/height */ + case 0x00040004: + stl_le_phys(&s->dma_as, value + 12, s->fbdev->xres); + stl_le_phys(&s->dma_as, value + 16, s->fbdev->yres); + resplen = 8; + break; + case 0x00044003: /* Test display width/height */ + case 0x00044004: + resplen = 8; + break; + case 0x00048003: /* Set display width/height */ + case 0x00048004: + xres = ldl_le_phys(&s->dma_as, value + 12); + newxres = &xres; + yres = ldl_le_phys(&s->dma_as, value + 16); + newyres = &yres; + resplen = 8; + break; + case 0x00040005: /* Get depth */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->bpp); + resplen = 4; + break; + case 0x00044005: /* Test depth */ + resplen = 4; + break; + case 0x00048005: /* Set depth */ + bpp = ldl_le_phys(&s->dma_as, value + 12); + newbpp = &bpp; + resplen = 4; + break; + case 0x00040006: /* Get pixel order */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->pixo); + resplen = 4; + break; + case 0x00044006: /* Test pixel order */ + resplen = 4; + break; + case 0x00048006: /* Set pixel order */ + pixo = ldl_le_phys(&s->dma_as, value + 12); + newpixo = &pixo; + resplen = 4; + break; + case 0x00040007: /* Get alpha */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->alpha); + resplen = 4; + break; + case 0x00044007: /* Test pixel alpha */ + resplen = 4; + break; + case 0x00048007: /* Set alpha */ + alpha = ldl_le_phys(&s->dma_as, value + 12); + newalpha = α + resplen = 4; + break; + case 0x00040008: /* Get pitch */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->pitch); + resplen = 4; + break; + case 0x00040009: /* Get virtual offset */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->xoffset); + stl_le_phys(&s->dma_as, value + 16, s->fbdev->yoffset); + resplen = 8; + break; + case 0x00044009: /* Test virtual offset */ + resplen = 8; + break; + case 0x00048009: /* Set virtual offset */ + xoffset = ldl_le_phys(&s->dma_as, value + 12); + newxoffset = &xoffset; + yoffset = ldl_le_phys(&s->dma_as, value + 16); + newyoffset = &yoffset; + resplen = 8; + break; + case 0x0004000a: /* Get/Test/Set overscan */ + case 0x0004400a: + case 0x0004800a: + stl_le_phys(&s->dma_as, value + 12, 0); + stl_le_phys(&s->dma_as, value + 16, 0); + stl_le_phys(&s->dma_as, value + 20, 0); + stl_le_phys(&s->dma_as, value + 24, 0); + resplen = 16; + break; + case 0x0004800b: /* Set palette */ + offset = ldl_le_phys(&s->dma_as, value + 12); + length = ldl_le_phys(&s->dma_as, value + 16); + n = 0; + while (n < length - offset) { + color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2)); + stl_le_phys(&s->dma_as, + s->fbdev->vcram_base + ((offset + n) << 2), color); + n++; + } + stl_le_phys(&s->dma_as, value + 12, 0); + resplen = 4; + break; case 0x00060001: /* Get DMA channels */ /* channels 2-5 */ @@ -147,6 +267,13 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) value += bufsize + 12; } + /* Reconfigure framebuffer if required */ + if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo + || newalpha) { + bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset, + newyoffset, newbpp, newpixo, newalpha); + } + /* Buffer response code */ stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31)); } @@ -241,6 +368,15 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp) Object *obj; Error *err = NULL; + obj = object_property_get_link(OBJECT(dev), "fb", &err); + if (obj == NULL) { + error_setg(errp, "%s: required fb link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->fbdev = BCM2835_FB(obj); + obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); if (obj == NULL) { error_setg(errp, "%s: required dma-mr link not found: %s", @@ -259,7 +395,6 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp) static Property bcm2835_property_props[] = { DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0), - DEFINE_PROP_UINT32("ram-size", BCM2835PropertyState, ram_size, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c index 498e84c907..225604d823 100644 --- a/hw/misc/imx25_ccm.c +++ b/hw/misc/imx25_ccm.c @@ -120,20 +120,6 @@ static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev) return freq; } -static uint32_t imx25_ccm_get_upll_clk(IMXCCMState *dev) -{ - uint32_t freq = 0; - IMX25CCMState *s = IMX25_CCM(dev); - - if (!EXTRACT(s->reg[IMX25_CCM_CCTL_REG], UPLL_DIS)) { - freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_UPCTL_REG], CKIH_FREQ); - } - - DPRINTF("freq = %d\n", freq); - - return freq; -} - static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev) { uint32_t freq; @@ -182,21 +168,10 @@ static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) DPRINTF("Clock = %d)\n", clock); switch (clock) { - case NOCLK: - break; - case CLK_MPLL: - freq = imx25_ccm_get_mpll_clk(dev); - break; - case CLK_UPLL: - freq = imx25_ccm_get_upll_clk(dev); - break; - case CLK_MCU: - freq = imx25_ccm_get_mcu_clk(dev); - break; - case CLK_AHB: - freq = imx25_ccm_get_ahb_clk(dev); + case CLK_NONE: break; case CLK_IPG: + case CLK_IPG_HIGH: freq = imx25_ccm_get_ipg_clk(dev); break; case CLK_32k: diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c index 17640bf2ec..80c1647166 100644 --- a/hw/misc/imx31_ccm.c +++ b/hw/misc/imx31_ccm.c @@ -152,32 +152,6 @@ static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev) return freq; } -static uint32_t imx31_ccm_get_mcu_clk(IMXCCMState *dev) -{ - uint32_t freq; - IMX31CCMState *s = IMX31_CCM(dev); - - freq = imx31_ccm_get_mcu_main_clk(dev) - / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MCU)); - - DPRINTF("freq = %d\n", freq); - - return freq; -} - -static uint32_t imx31_ccm_get_hsp_clk(IMXCCMState *dev) -{ - uint32_t freq; - IMX31CCMState *s = IMX31_CCM(dev); - - freq = imx31_ccm_get_mcu_main_clk(dev) - / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], HSP)); - - DPRINTF("freq = %d\n", freq); - - return freq; -} - static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev) { uint32_t freq; @@ -209,15 +183,10 @@ static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) uint32_t freq = 0; switch (clock) { - case NOCLK: - break; - case CLK_MCU: - freq = imx31_ccm_get_mcu_clk(dev); - break; - case CLK_HSP: - freq = imx31_ccm_get_hsp_clk(dev); + case CLK_NONE: break; case CLK_IPG: + case CLK_IPG_HIGH: freq = imx31_ccm_get_ipg_clk(dev); break; case CLK_32k: diff --git a/hw/misc/imx6_ccm.c b/hw/misc/imx6_ccm.c new file mode 100644 index 0000000000..4e1d49da69 --- /dev/null +++ b/hw/misc/imx6_ccm.c @@ -0,0 +1,774 @@ +/* + * IMX6 Clock Control Module + * + * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * To get the timer frequencies right, we need to emulate at least part of + * the CCM. + */ + +#include "qemu/osdep.h" +#include "hw/misc/imx6_ccm.h" + +#ifndef DEBUG_IMX6_CCM +#define DEBUG_IMX6_CCM 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX6_CCM) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_CCM, \ + __func__, ##args); \ + } \ + } while (0) + +static char const *imx6_ccm_reg_name(uint32_t reg) +{ + static char unknown[20]; + + switch (reg) { + case CCM_CCR: + return "CCR"; + case CCM_CCDR: + return "CCDR"; + case CCM_CSR: + return "CSR"; + case CCM_CCSR: + return "CCSR"; + case CCM_CACRR: + return "CACRR"; + case CCM_CBCDR: + return "CBCDR"; + case CCM_CBCMR: + return "CBCMR"; + case CCM_CSCMR1: + return "CSCMR1"; + case CCM_CSCMR2: + return "CSCMR2"; + case CCM_CSCDR1: + return "CSCDR1"; + case CCM_CS1CDR: + return "CS1CDR"; + case CCM_CS2CDR: + return "CS2CDR"; + case CCM_CDCDR: + return "CDCDR"; + case CCM_CHSCCDR: + return "CHSCCDR"; + case CCM_CSCDR2: + return "CSCDR2"; + case CCM_CSCDR3: + return "CSCDR3"; + case CCM_CDHIPR: + return "CDHIPR"; + case CCM_CTOR: + return "CTOR"; + case CCM_CLPCR: + return "CLPCR"; + case CCM_CISR: + return "CISR"; + case CCM_CIMR: + return "CIMR"; + case CCM_CCOSR: + return "CCOSR"; + case CCM_CGPR: + return "CGPR"; + case CCM_CCGR0: + return "CCGR0"; + case CCM_CCGR1: + return "CCGR1"; + case CCM_CCGR2: + return "CCGR2"; + case CCM_CCGR3: + return "CCGR3"; + case CCM_CCGR4: + return "CCGR4"; + case CCM_CCGR5: + return "CCGR5"; + case CCM_CCGR6: + return "CCGR6"; + case CCM_CMEOR: + return "CMEOR"; + default: + sprintf(unknown, "%d ?", reg); + return unknown; + } +} + +static char const *imx6_analog_reg_name(uint32_t reg) +{ + static char unknown[20]; + + switch (reg) { + case CCM_ANALOG_PLL_ARM: + return "PLL_ARM"; + case CCM_ANALOG_PLL_ARM_SET: + return "PLL_ARM_SET"; + case CCM_ANALOG_PLL_ARM_CLR: + return "PLL_ARM_CLR"; + case CCM_ANALOG_PLL_ARM_TOG: + return "PLL_ARM_TOG"; + case CCM_ANALOG_PLL_USB1: + return "PLL_USB1"; + case CCM_ANALOG_PLL_USB1_SET: + return "PLL_USB1_SET"; + case CCM_ANALOG_PLL_USB1_CLR: + return "PLL_USB1_CLR"; + case CCM_ANALOG_PLL_USB1_TOG: + return "PLL_USB1_TOG"; + case CCM_ANALOG_PLL_USB2: + return "PLL_USB2"; + case CCM_ANALOG_PLL_USB2_SET: + return "PLL_USB2_SET"; + case CCM_ANALOG_PLL_USB2_CLR: + return "PLL_USB2_CLR"; + case CCM_ANALOG_PLL_USB2_TOG: + return "PLL_USB2_TOG"; + case CCM_ANALOG_PLL_SYS: + return "PLL_SYS"; + case CCM_ANALOG_PLL_SYS_SET: + return "PLL_SYS_SET"; + case CCM_ANALOG_PLL_SYS_CLR: + return "PLL_SYS_CLR"; + case CCM_ANALOG_PLL_SYS_TOG: + return "PLL_SYS_TOG"; + case CCM_ANALOG_PLL_SYS_SS: + return "PLL_SYS_SS"; + case CCM_ANALOG_PLL_SYS_NUM: + return "PLL_SYS_NUM"; + case CCM_ANALOG_PLL_SYS_DENOM: + return "PLL_SYS_DENOM"; + case CCM_ANALOG_PLL_AUDIO: + return "PLL_AUDIO"; + case CCM_ANALOG_PLL_AUDIO_SET: + return "PLL_AUDIO_SET"; + case CCM_ANALOG_PLL_AUDIO_CLR: + return "PLL_AUDIO_CLR"; + case CCM_ANALOG_PLL_AUDIO_TOG: + return "PLL_AUDIO_TOG"; + case CCM_ANALOG_PLL_AUDIO_NUM: + return "PLL_AUDIO_NUM"; + case CCM_ANALOG_PLL_AUDIO_DENOM: + return "PLL_AUDIO_DENOM"; + case CCM_ANALOG_PLL_VIDEO: + return "PLL_VIDEO"; + case CCM_ANALOG_PLL_VIDEO_SET: + return "PLL_VIDEO_SET"; + case CCM_ANALOG_PLL_VIDEO_CLR: + return "PLL_VIDEO_CLR"; + case CCM_ANALOG_PLL_VIDEO_TOG: + return "PLL_VIDEO_TOG"; + case CCM_ANALOG_PLL_VIDEO_NUM: + return "PLL_VIDEO_NUM"; + case CCM_ANALOG_PLL_VIDEO_DENOM: + return "PLL_VIDEO_DENOM"; + case CCM_ANALOG_PLL_MLB: + return "PLL_MLB"; + case CCM_ANALOG_PLL_MLB_SET: + return "PLL_MLB_SET"; + case CCM_ANALOG_PLL_MLB_CLR: + return "PLL_MLB_CLR"; + case CCM_ANALOG_PLL_MLB_TOG: + return "PLL_MLB_TOG"; + case CCM_ANALOG_PLL_ENET: + return "PLL_ENET"; + case CCM_ANALOG_PLL_ENET_SET: + return "PLL_ENET_SET"; + case CCM_ANALOG_PLL_ENET_CLR: + return "PLL_ENET_CLR"; + case CCM_ANALOG_PLL_ENET_TOG: + return "PLL_ENET_TOG"; + case CCM_ANALOG_PFD_480: + return "PFD_480"; + case CCM_ANALOG_PFD_480_SET: + return "PFD_480_SET"; + case CCM_ANALOG_PFD_480_CLR: + return "PFD_480_CLR"; + case CCM_ANALOG_PFD_480_TOG: + return "PFD_480_TOG"; + case CCM_ANALOG_PFD_528: + return "PFD_528"; + case CCM_ANALOG_PFD_528_SET: + return "PFD_528_SET"; + case CCM_ANALOG_PFD_528_CLR: + return "PFD_528_CLR"; + case CCM_ANALOG_PFD_528_TOG: + return "PFD_528_TOG"; + case CCM_ANALOG_MISC0: + return "MISC0"; + case CCM_ANALOG_MISC0_SET: + return "MISC0_SET"; + case CCM_ANALOG_MISC0_CLR: + return "MISC0_CLR"; + case CCM_ANALOG_MISC0_TOG: + return "MISC0_TOG"; + case CCM_ANALOG_MISC2: + return "MISC2"; + case CCM_ANALOG_MISC2_SET: + return "MISC2_SET"; + case CCM_ANALOG_MISC2_CLR: + return "MISC2_CLR"; + case CCM_ANALOG_MISC2_TOG: + return "MISC2_TOG"; + case PMU_REG_1P1: + return "PMU_REG_1P1"; + case PMU_REG_3P0: + return "PMU_REG_3P0"; + case PMU_REG_2P5: + return "PMU_REG_2P5"; + case PMU_REG_CORE: + return "PMU_REG_CORE"; + case PMU_MISC1: + return "PMU_MISC1"; + case PMU_MISC1_SET: + return "PMU_MISC1_SET"; + case PMU_MISC1_CLR: + return "PMU_MISC1_CLR"; + case PMU_MISC1_TOG: + return "PMU_MISC1_TOG"; + case USB_ANALOG_DIGPROG: + return "USB_ANALOG_DIGPROG"; + default: + sprintf(unknown, "%d ?", reg); + return unknown; + } +} + +#define CKIH_FREQ 24000000 /* 24MHz crystal input */ + +static const VMStateDescription vmstate_imx6_ccm = { + .name = TYPE_IMX6_CCM, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(ccm, IMX6CCMState, CCM_MAX), + VMSTATE_UINT32_ARRAY(analog, IMX6CCMState, CCM_ANALOG_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev) +{ + uint64_t freq = 24000000; + + if (EXTRACT(dev->analog[CCM_ANALOG_PLL_SYS], DIV_SELECT)) { + freq *= 22; + } else { + freq *= 20; + } + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6_analog_get_pll2_clk(dev) * 18 + / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD0_FRAC); + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint64_t imx6_analog_get_pll2_pfd2_clk(IMX6CCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6_analog_get_pll2_clk(dev) * 18 + / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD2_FRAC); + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev) +{ + uint64_t freq = 0; + + switch (EXTRACT(dev->ccm[CCM_CBCMR], PRE_PERIPH_CLK_SEL)) { + case 0: + freq = imx6_analog_get_pll2_clk(dev); + break; + case 1: + freq = imx6_analog_get_pll2_pfd2_clk(dev); + break; + case 2: + freq = imx6_analog_get_pll2_pfd0_clk(dev); + break; + case 3: + freq = imx6_analog_get_pll2_pfd2_clk(dev) / 2; + break; + default: + /* We should never get there */ + g_assert_not_reached(); + break; + } + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint64_t imx6_ccm_get_ahb_clk(IMX6CCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6_analog_get_periph_clk(dev) + / (1 + EXTRACT(dev->ccm[CCM_CBCDR], AHB_PODF)); + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint64_t imx6_ccm_get_ipg_clk(IMX6CCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6_ccm_get_ahb_clk(dev) + / (1 + EXTRACT(dev->ccm[CCM_CBCDR], IPG_PODF));; + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6_ccm_get_ipg_clk(dev) + / (1 + EXTRACT(dev->ccm[CCM_CSCMR1], PERCLK_PODF)); + + DPRINTF("freq = %d\n", (uint32_t)freq); + + return freq; +} + +static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) +{ + uint32_t freq = 0; + IMX6CCMState *s = IMX6_CCM(dev); + + switch (clock) { + case CLK_NONE: + break; + case CLK_IPG: + freq = imx6_ccm_get_ipg_clk(s); + break; + case CLK_IPG_HIGH: + freq = imx6_ccm_get_per_clk(s); + break; + case CLK_32k: + freq = CKIL_FREQ; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", + TYPE_IMX6_CCM, __func__, clock); + break; + } + + DPRINTF("Clock = %d) = %d\n", clock, freq); + + return freq; +} + +static void imx6_ccm_reset(DeviceState *dev) +{ + IMX6CCMState *s = IMX6_CCM(dev); + + DPRINTF("\n"); + + s->ccm[CCM_CCR] = 0x040116FF; + s->ccm[CCM_CCDR] = 0x00000000; + s->ccm[CCM_CSR] = 0x00000010; + s->ccm[CCM_CCSR] = 0x00000100; + s->ccm[CCM_CACRR] = 0x00000000; + s->ccm[CCM_CBCDR] = 0x00018D40; + s->ccm[CCM_CBCMR] = 0x00022324; + s->ccm[CCM_CSCMR1] = 0x00F00000; + s->ccm[CCM_CSCMR2] = 0x02B92F06; + s->ccm[CCM_CSCDR1] = 0x00490B00; + s->ccm[CCM_CS1CDR] = 0x0EC102C1; + s->ccm[CCM_CS2CDR] = 0x000736C1; + s->ccm[CCM_CDCDR] = 0x33F71F92; + s->ccm[CCM_CHSCCDR] = 0x0002A150; + s->ccm[CCM_CSCDR2] = 0x0002A150; + s->ccm[CCM_CSCDR3] = 0x00014841; + s->ccm[CCM_CDHIPR] = 0x00000000; + s->ccm[CCM_CTOR] = 0x00000000; + s->ccm[CCM_CLPCR] = 0x00000079; + s->ccm[CCM_CISR] = 0x00000000; + s->ccm[CCM_CIMR] = 0xFFFFFFFF; + s->ccm[CCM_CCOSR] = 0x000A0001; + s->ccm[CCM_CGPR] = 0x0000FE62; + s->ccm[CCM_CCGR0] = 0xFFFFFFFF; + s->ccm[CCM_CCGR1] = 0xFFFFFFFF; + s->ccm[CCM_CCGR2] = 0xFC3FFFFF; + s->ccm[CCM_CCGR3] = 0xFFFFFFFF; + s->ccm[CCM_CCGR4] = 0xFFFFFFFF; + s->ccm[CCM_CCGR5] = 0xFFFFFFFF; + s->ccm[CCM_CCGR6] = 0xFFFFFFFF; + s->ccm[CCM_CMEOR] = 0xFFFFFFFF; + + s->analog[CCM_ANALOG_PLL_ARM] = 0x00013042; + s->analog[CCM_ANALOG_PLL_USB1] = 0x00012000; + s->analog[CCM_ANALOG_PLL_USB2] = 0x00012000; + s->analog[CCM_ANALOG_PLL_SYS] = 0x00013001; + s->analog[CCM_ANALOG_PLL_SYS_SS] = 0x00000000; + s->analog[CCM_ANALOG_PLL_SYS_NUM] = 0x00000000; + s->analog[CCM_ANALOG_PLL_SYS_DENOM] = 0x00000012; + s->analog[CCM_ANALOG_PLL_AUDIO] = 0x00011006; + s->analog[CCM_ANALOG_PLL_AUDIO_NUM] = 0x05F5E100; + s->analog[CCM_ANALOG_PLL_AUDIO_DENOM] = 0x2964619C; + s->analog[CCM_ANALOG_PLL_VIDEO] = 0x0001100C; + s->analog[CCM_ANALOG_PLL_VIDEO_NUM] = 0x05F5E100; + s->analog[CCM_ANALOG_PLL_VIDEO_DENOM] = 0x10A24447; + s->analog[CCM_ANALOG_PLL_MLB] = 0x00010000; + s->analog[CCM_ANALOG_PLL_ENET] = 0x00011001; + s->analog[CCM_ANALOG_PFD_480] = 0x1311100C; + s->analog[CCM_ANALOG_PFD_528] = 0x1018101B; + + s->analog[PMU_REG_1P1] = 0x00001073; + s->analog[PMU_REG_3P0] = 0x00000F74; + s->analog[PMU_REG_2P5] = 0x00005071; + s->analog[PMU_REG_CORE] = 0x00402010; + s->analog[PMU_MISC0] = 0x04000000; + s->analog[PMU_MISC1] = 0x00000000; + s->analog[PMU_MISC2] = 0x00272727; + + s->analog[USB_ANALOG_USB1_VBUS_DETECT] = 0x00000004; + s->analog[USB_ANALOG_USB1_CHRG_DETECT] = 0x00000000; + s->analog[USB_ANALOG_USB1_VBUS_DETECT_STAT] = 0x00000000; + s->analog[USB_ANALOG_USB1_CHRG_DETECT_STAT] = 0x00000000; + s->analog[USB_ANALOG_USB1_MISC] = 0x00000002; + s->analog[USB_ANALOG_USB2_VBUS_DETECT] = 0x00000004; + s->analog[USB_ANALOG_USB2_CHRG_DETECT] = 0x00000000; + s->analog[USB_ANALOG_USB2_MISC] = 0x00000002; + s->analog[USB_ANALOG_DIGPROG] = 0x00000000; + + /* all PLLs need to be locked */ + s->analog[CCM_ANALOG_PLL_ARM] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_USB1] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_USB2] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_SYS] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_AUDIO] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_VIDEO] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_MLB] |= CCM_ANALOG_PLL_LOCK; + s->analog[CCM_ANALOG_PLL_ENET] |= CCM_ANALOG_PLL_LOCK; +} + +static uint64_t imx6_ccm_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value = 0; + uint32_t index = offset >> 2; + IMX6CCMState *s = (IMX6CCMState *)opaque; + + value = s->ccm[index]; + + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), value); + + return (uint64_t)value; +} + +static void imx6_ccm_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + uint32_t index = offset >> 2; + IMX6CCMState *s = (IMX6CCMState *)opaque; + + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), + (uint32_t)value); + + /* + * We will do a better implementation later. In particular some bits + * cannot be written to. + */ + s->ccm[index] = (uint32_t)value; +} + +static uint64_t imx6_analog_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value; + uint32_t index = offset >> 2; + IMX6CCMState *s = (IMX6CCMState *)opaque; + + switch (index) { + case CCM_ANALOG_PLL_ARM_SET: + case CCM_ANALOG_PLL_USB1_SET: + case CCM_ANALOG_PLL_USB2_SET: + case CCM_ANALOG_PLL_SYS_SET: + case CCM_ANALOG_PLL_AUDIO_SET: + case CCM_ANALOG_PLL_VIDEO_SET: + case CCM_ANALOG_PLL_MLB_SET: + case CCM_ANALOG_PLL_ENET_SET: + case CCM_ANALOG_PFD_480_SET: + case CCM_ANALOG_PFD_528_SET: + case CCM_ANALOG_MISC0_SET: + case PMU_MISC1_SET: + case CCM_ANALOG_MISC2_SET: + case USB_ANALOG_USB1_VBUS_DETECT_SET: + case USB_ANALOG_USB1_CHRG_DETECT_SET: + case USB_ANALOG_USB1_MISC_SET: + case USB_ANALOG_USB2_VBUS_DETECT_SET: + case USB_ANALOG_USB2_CHRG_DETECT_SET: + case USB_ANALOG_USB2_MISC_SET: + /* + * All REG_NAME_SET register access are in fact targeting the + * the REG_NAME register. + */ + value = s->analog[index - 1]; + break; + case CCM_ANALOG_PLL_ARM_CLR: + case CCM_ANALOG_PLL_USB1_CLR: + case CCM_ANALOG_PLL_USB2_CLR: + case CCM_ANALOG_PLL_SYS_CLR: + case CCM_ANALOG_PLL_AUDIO_CLR: + case CCM_ANALOG_PLL_VIDEO_CLR: + case CCM_ANALOG_PLL_MLB_CLR: + case CCM_ANALOG_PLL_ENET_CLR: + case CCM_ANALOG_PFD_480_CLR: + case CCM_ANALOG_PFD_528_CLR: + case CCM_ANALOG_MISC0_CLR: + case PMU_MISC1_CLR: + case CCM_ANALOG_MISC2_CLR: + case USB_ANALOG_USB1_VBUS_DETECT_CLR: + case USB_ANALOG_USB1_CHRG_DETECT_CLR: + case USB_ANALOG_USB1_MISC_CLR: + case USB_ANALOG_USB2_VBUS_DETECT_CLR: + case USB_ANALOG_USB2_CHRG_DETECT_CLR: + case USB_ANALOG_USB2_MISC_CLR: + /* + * All REG_NAME_CLR register access are in fact targeting the + * the REG_NAME register. + */ + value = s->analog[index - 2]; + break; + case CCM_ANALOG_PLL_ARM_TOG: + case CCM_ANALOG_PLL_USB1_TOG: + case CCM_ANALOG_PLL_USB2_TOG: + case CCM_ANALOG_PLL_SYS_TOG: + case CCM_ANALOG_PLL_AUDIO_TOG: + case CCM_ANALOG_PLL_VIDEO_TOG: + case CCM_ANALOG_PLL_MLB_TOG: + case CCM_ANALOG_PLL_ENET_TOG: + case CCM_ANALOG_PFD_480_TOG: + case CCM_ANALOG_PFD_528_TOG: + case CCM_ANALOG_MISC0_TOG: + case PMU_MISC1_TOG: + case CCM_ANALOG_MISC2_TOG: + case USB_ANALOG_USB1_VBUS_DETECT_TOG: + case USB_ANALOG_USB1_CHRG_DETECT_TOG: + case USB_ANALOG_USB1_MISC_TOG: + case USB_ANALOG_USB2_VBUS_DETECT_TOG: + case USB_ANALOG_USB2_CHRG_DETECT_TOG: + case USB_ANALOG_USB2_MISC_TOG: + /* + * All REG_NAME_TOG register access are in fact targeting the + * the REG_NAME register. + */ + value = s->analog[index - 3]; + break; + default: + value = s->analog[index]; + break; + } + + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_analog_reg_name(index), value); + + return (uint64_t)value; +} + +static void imx6_analog_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + uint32_t index = offset >> 2; + IMX6CCMState *s = (IMX6CCMState *)opaque; + + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_analog_reg_name(index), + (uint32_t)value); + + switch (index) { + case CCM_ANALOG_PLL_ARM_SET: + case CCM_ANALOG_PLL_USB1_SET: + case CCM_ANALOG_PLL_USB2_SET: + case CCM_ANALOG_PLL_SYS_SET: + case CCM_ANALOG_PLL_AUDIO_SET: + case CCM_ANALOG_PLL_VIDEO_SET: + case CCM_ANALOG_PLL_MLB_SET: + case CCM_ANALOG_PLL_ENET_SET: + case CCM_ANALOG_PFD_480_SET: + case CCM_ANALOG_PFD_528_SET: + case CCM_ANALOG_MISC0_SET: + case PMU_MISC1_SET: + case CCM_ANALOG_MISC2_SET: + case USB_ANALOG_USB1_VBUS_DETECT_SET: + case USB_ANALOG_USB1_CHRG_DETECT_SET: + case USB_ANALOG_USB1_MISC_SET: + case USB_ANALOG_USB2_VBUS_DETECT_SET: + case USB_ANALOG_USB2_CHRG_DETECT_SET: + case USB_ANALOG_USB2_MISC_SET: + /* + * All REG_NAME_SET register access are in fact targeting the + * the REG_NAME register. So we change the value of the + * REG_NAME register, setting bits passed in the value. + */ + s->analog[index - 1] |= value; + break; + case CCM_ANALOG_PLL_ARM_CLR: + case CCM_ANALOG_PLL_USB1_CLR: + case CCM_ANALOG_PLL_USB2_CLR: + case CCM_ANALOG_PLL_SYS_CLR: + case CCM_ANALOG_PLL_AUDIO_CLR: + case CCM_ANALOG_PLL_VIDEO_CLR: + case CCM_ANALOG_PLL_MLB_CLR: + case CCM_ANALOG_PLL_ENET_CLR: + case CCM_ANALOG_PFD_480_CLR: + case CCM_ANALOG_PFD_528_CLR: + case CCM_ANALOG_MISC0_CLR: + case PMU_MISC1_CLR: + case CCM_ANALOG_MISC2_CLR: + case USB_ANALOG_USB1_VBUS_DETECT_CLR: + case USB_ANALOG_USB1_CHRG_DETECT_CLR: + case USB_ANALOG_USB1_MISC_CLR: + case USB_ANALOG_USB2_VBUS_DETECT_CLR: + case USB_ANALOG_USB2_CHRG_DETECT_CLR: + case USB_ANALOG_USB2_MISC_CLR: + /* + * All REG_NAME_CLR register access are in fact targeting the + * the REG_NAME register. So we change the value of the + * REG_NAME register, unsetting bits passed in the value. + */ + s->analog[index - 2] &= ~value; + break; + case CCM_ANALOG_PLL_ARM_TOG: + case CCM_ANALOG_PLL_USB1_TOG: + case CCM_ANALOG_PLL_USB2_TOG: + case CCM_ANALOG_PLL_SYS_TOG: + case CCM_ANALOG_PLL_AUDIO_TOG: + case CCM_ANALOG_PLL_VIDEO_TOG: + case CCM_ANALOG_PLL_MLB_TOG: + case CCM_ANALOG_PLL_ENET_TOG: + case CCM_ANALOG_PFD_480_TOG: + case CCM_ANALOG_PFD_528_TOG: + case CCM_ANALOG_MISC0_TOG: + case PMU_MISC1_TOG: + case CCM_ANALOG_MISC2_TOG: + case USB_ANALOG_USB1_VBUS_DETECT_TOG: + case USB_ANALOG_USB1_CHRG_DETECT_TOG: + case USB_ANALOG_USB1_MISC_TOG: + case USB_ANALOG_USB2_VBUS_DETECT_TOG: + case USB_ANALOG_USB2_CHRG_DETECT_TOG: + case USB_ANALOG_USB2_MISC_TOG: + /* + * All REG_NAME_TOG register access are in fact targeting the + * the REG_NAME register. So we change the value of the + * REG_NAME register, toggling bits passed in the value. + */ + s->analog[index - 3] ^= value; + break; + default: + /* + * We will do a better implementation later. In particular some bits + * cannot be written to. + */ + s->analog[index] = value; + break; + } +} + +static const struct MemoryRegionOps imx6_ccm_ops = { + .read = imx6_ccm_read, + .write = imx6_ccm_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const struct MemoryRegionOps imx6_analog_ops = { + .read = imx6_analog_read, + .write = imx6_analog_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx6_ccm_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX6CCMState *s = IMX6_CCM(obj); + + /* initialize a container for the all memory range */ + memory_region_init(&s->container, OBJECT(dev), TYPE_IMX6_CCM, 0x5000); + + /* We initialize an IO memory region for the CCM part */ + memory_region_init_io(&s->ioccm, OBJECT(dev), &imx6_ccm_ops, s, + TYPE_IMX6_CCM ".ccm", CCM_MAX * sizeof(uint32_t)); + + /* Add the CCM as a subregion at offset 0 */ + memory_region_add_subregion(&s->container, 0, &s->ioccm); + + /* We initialize an IO memory region for the ANALOG part */ + memory_region_init_io(&s->ioanalog, OBJECT(dev), &imx6_analog_ops, s, + TYPE_IMX6_CCM ".analog", + CCM_ANALOG_MAX * sizeof(uint32_t)); + + /* Add the ANALOG as a subregion at offset 0x4000 */ + memory_region_add_subregion(&s->container, 0x4000, &s->ioanalog); + + sysbus_init_mmio(sd, &s->container); +} + +static void imx6_ccm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IMXCCMClass *ccm = IMX_CCM_CLASS(klass); + + dc->reset = imx6_ccm_reset; + dc->vmsd = &vmstate_imx6_ccm; + dc->desc = "i.MX6 Clock Control Module"; + + ccm->get_clock_frequency = imx6_ccm_get_clock_frequency; +} + +static const TypeInfo imx6_ccm_info = { + .name = TYPE_IMX6_CCM, + .parent = TYPE_IMX_CCM, + .instance_size = sizeof(IMX6CCMState), + .instance_init = imx6_ccm_init, + .class_init = imx6_ccm_class_init, +}; + +static void imx6_ccm_register_types(void) +{ + type_register_static(&imx6_ccm_info); +} + +type_init(imx6_ccm_register_types) diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 3bd551736d..e60e3380e4 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -693,6 +693,7 @@ static void imx_fec_class_init(ObjectClass *klass, void *data) dc->reset = imx_fec_reset; dc->props = imx_fec_properties; dc->realize = imx_fec_realize; + dc->desc = "i.MX FEC Ethernet Controller"; } static const TypeInfo imx_fec_info = { diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 00c320d00b..1568057e4f 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -563,17 +563,19 @@ static const VMStateDescription sd_vmstate = { /* Legacy initialization function for use by non-qdevified callers */ SDState *sd_init(BlockBackend *blk, bool is_spi) { + Object *obj; DeviceState *dev; Error *err = NULL; - dev = qdev_create(NULL, TYPE_SD_CARD); + obj = object_new(TYPE_SD_CARD); + dev = DEVICE(obj); qdev_prop_set_drive(dev, "drive", blk, &err); if (err) { error_report("sd_init failed: %s", error_get_pretty(err)); return NULL; } qdev_prop_set_bit(dev, "spi", is_spi); - object_property_set_bool(OBJECT(dev), true, "realized", &err); + object_property_set_bool(obj, true, "realized", &err); if (err) { error_report("sd_init failed: %s", error_get_pretty(err)); return NULL; diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 5cfea6e0da..003c14fa26 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -32,3 +32,4 @@ obj-$(CONFIG_MC146818RTC) += mc146818rtc.o obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o +common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c new file mode 100644 index 0000000000..51e8303cda --- /dev/null +++ b/hw/timer/aspeed_timer.c @@ -0,0 +1,449 @@ +/* + * ASPEED AST2400 Timer + * + * Andrew Jeffery <andrew@aj.id.au> + * + * Copyright (C) 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/ptimer.h" +#include "hw/sysbus.h" +#include "hw/timer/aspeed_timer.h" +#include "qemu-common.h" +#include "qemu/bitops.h" +#include "qemu/main-loop.h" +#include "qemu/timer.h" +#include "trace.h" + +#define TIMER_NR_REGS 4 + +#define TIMER_CTRL_BITS 4 +#define TIMER_CTRL_MASK ((1 << TIMER_CTRL_BITS) - 1) + +#define TIMER_CLOCK_USE_EXT true +#define TIMER_CLOCK_EXT_HZ 1000000 +#define TIMER_CLOCK_USE_APB false +#define TIMER_CLOCK_APB_HZ 24000000 + +#define TIMER_REG_STATUS 0 +#define TIMER_REG_RELOAD 1 +#define TIMER_REG_MATCH_FIRST 2 +#define TIMER_REG_MATCH_SECOND 3 + +#define TIMER_FIRST_CAP_PULSE 4 + +enum timer_ctrl_op { + op_enable = 0, + op_external_clock, + op_overflow_interrupt, + op_pulse_enable +}; + +/** + * Avoid mutual references between AspeedTimerCtrlState and AspeedTimer + * structs, as it's a waste of memory. The ptimer BH callback needs to know + * whether a specific AspeedTimer is enabled, but this information is held in + * AspeedTimerCtrlState. So, provide a helper to hoist ourselves from an + * arbitrary AspeedTimer to AspeedTimerCtrlState. + */ +static inline AspeedTimerCtrlState *timer_to_ctrl(AspeedTimer *t) +{ + const AspeedTimer (*timers)[] = (void *)t - (t->id * sizeof(*t)); + return container_of(timers, AspeedTimerCtrlState, timers); +} + +static inline bool timer_ctrl_status(AspeedTimer *t, enum timer_ctrl_op op) +{ + return !!(timer_to_ctrl(t)->ctrl & BIT(t->id * TIMER_CTRL_BITS + op)); +} + +static inline bool timer_enabled(AspeedTimer *t) +{ + return timer_ctrl_status(t, op_enable); +} + +static inline bool timer_overflow_interrupt(AspeedTimer *t) +{ + return timer_ctrl_status(t, op_overflow_interrupt); +} + +static inline bool timer_can_pulse(AspeedTimer *t) +{ + return t->id >= TIMER_FIRST_CAP_PULSE; +} + +static void aspeed_timer_expire(void *opaque) +{ + AspeedTimer *t = opaque; + + /* Only support interrupts on match values of zero for the moment - this is + * sufficient to boot an aspeed_defconfig Linux kernel. + * + * TODO: matching on arbitrary values (see e.g. hw/timer/a9gtimer.c) + */ + bool match = !(t->match[0] && t->match[1]); + bool interrupt = timer_overflow_interrupt(t) || match; + if (timer_enabled(t) && interrupt) { + t->level = !t->level; + qemu_set_irq(t->irq, t->level); + } +} + +static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg) +{ + uint64_t value; + + switch (reg) { + case TIMER_REG_STATUS: + value = ptimer_get_count(t->timer); + break; + case TIMER_REG_RELOAD: + value = t->reload; + break; + case TIMER_REG_MATCH_FIRST: + case TIMER_REG_MATCH_SECOND: + value = t->match[reg - 2]; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Programming error: unexpected reg: %d\n", + __func__, reg); + value = 0; + break; + } + return value; +} + +static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size) +{ + AspeedTimerCtrlState *s = opaque; + const int reg = (offset & 0xf) / 4; + uint64_t value; + + switch (offset) { + case 0x30: /* Control Register */ + value = s->ctrl; + break; + case 0x34: /* Control Register 2 */ + value = s->ctrl2; + break; + case 0x00 ... 0x2c: /* Timers 1 - 4 */ + value = aspeed_timer_get_value(&s->timers[(offset >> 4)], reg); + break; + case 0x40 ... 0x8c: /* Timers 5 - 8 */ + value = aspeed_timer_get_value(&s->timers[(offset >> 4) - 1], reg); + break; + /* Illegal */ + case 0x38: + case 0x3C: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + value = 0; + break; + } + trace_aspeed_timer_read(offset, size, value); + return value; +} + +static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg, + uint32_t value) +{ + AspeedTimer *t; + + trace_aspeed_timer_set_value(timer, reg, value); + t = &s->timers[timer]; + switch (reg) { + case TIMER_REG_STATUS: + if (timer_enabled(t)) { + ptimer_set_count(t->timer, value); + } + break; + case TIMER_REG_RELOAD: + t->reload = value; + ptimer_set_limit(t->timer, value, 1); + break; + case TIMER_REG_MATCH_FIRST: + case TIMER_REG_MATCH_SECOND: + if (value) { + /* Non-zero match values are unsupported. As such an interrupt will + * always be triggered when the timer reaches zero even if the + * overflow interrupt control bit is clear. + */ + qemu_log_mask(LOG_UNIMP, "%s: Match value unsupported by device: " + "0x%" PRIx32 "\n", __func__, value); + } else { + t->match[reg - 2] = value; + } + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Programming error: unexpected reg: %d\n", + __func__, reg); + break; + } +} + +/* Control register operations are broken out into helpers that can be + * explictly called on aspeed_timer_reset(), but also from + * aspeed_timer_ctrl_op(). + */ + +static void aspeed_timer_ctrl_enable(AspeedTimer *t, bool enable) +{ + trace_aspeed_timer_ctrl_enable(t->id, enable); + if (enable) { + ptimer_run(t->timer, 0); + } else { + ptimer_stop(t->timer); + ptimer_set_limit(t->timer, t->reload, 1); + } +} + +static void aspeed_timer_ctrl_external_clock(AspeedTimer *t, bool enable) +{ + trace_aspeed_timer_ctrl_external_clock(t->id, enable); + if (enable) { + ptimer_set_freq(t->timer, TIMER_CLOCK_EXT_HZ); + } else { + ptimer_set_freq(t->timer, TIMER_CLOCK_APB_HZ); + } +} + +static void aspeed_timer_ctrl_overflow_interrupt(AspeedTimer *t, bool enable) +{ + trace_aspeed_timer_ctrl_overflow_interrupt(t->id, enable); +} + +static void aspeed_timer_ctrl_pulse_enable(AspeedTimer *t, bool enable) +{ + if (timer_can_pulse(t)) { + trace_aspeed_timer_ctrl_pulse_enable(t->id, enable); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Timer does not support pulse mode\n", __func__); + } +} + +/** + * Given the actions are fixed in number and completely described in helper + * functions, dispatch with a lookup table rather than manage control flow with + * a switch statement. + */ +static void (*const ctrl_ops[])(AspeedTimer *, bool) = { + [op_enable] = aspeed_timer_ctrl_enable, + [op_external_clock] = aspeed_timer_ctrl_external_clock, + [op_overflow_interrupt] = aspeed_timer_ctrl_overflow_interrupt, + [op_pulse_enable] = aspeed_timer_ctrl_pulse_enable, +}; + +/** + * Conditionally affect changes chosen by a timer's control bit. + * + * The aspeed_timer_ctrl_op() interface is convenient for the + * aspeed_timer_set_ctrl() function as the "no change" early exit can be + * calculated for all operations, which cleans up the caller code. However the + * interface isn't convenient for the reset function where we want to enter a + * specific state without artificially constructing old and new values that + * will fall through the change guard (and motivates extracting the actions + * out to helper functions). + * + * @t: The timer to manipulate + * @op: The type of operation to be performed + * @old: The old state of the timer's control bits + * @new: The incoming state for the timer's control bits + */ +static void aspeed_timer_ctrl_op(AspeedTimer *t, enum timer_ctrl_op op, + uint8_t old, uint8_t new) +{ + const uint8_t mask = BIT(op); + const bool enable = !!(new & mask); + const bool changed = ((old ^ new) & mask); + if (!changed) { + return; + } + ctrl_ops[op](t, enable); +} + +static void aspeed_timer_set_ctrl(AspeedTimerCtrlState *s, uint32_t reg) +{ + int i; + int shift; + uint8_t t_old, t_new; + AspeedTimer *t; + const uint8_t enable_mask = BIT(op_enable); + + /* Handle a dependency between the 'enable' and remaining three + * configuration bits - i.e. if more than one bit in the control set has + * changed, including the 'enable' bit, then we want either disable the + * timer and perform configuration, or perform configuration and then + * enable the timer + */ + for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { + t = &s->timers[i]; + shift = (i * TIMER_CTRL_BITS); + t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK; + t_new = (reg >> shift) & TIMER_CTRL_MASK; + + /* If we are disabling, do so first */ + if ((t_old & enable_mask) && !(t_new & enable_mask)) { + aspeed_timer_ctrl_enable(t, false); + } + aspeed_timer_ctrl_op(t, op_external_clock, t_old, t_new); + aspeed_timer_ctrl_op(t, op_overflow_interrupt, t_old, t_new); + aspeed_timer_ctrl_op(t, op_pulse_enable, t_old, t_new); + /* If we are enabling, do so last */ + if (!(t_old & enable_mask) && (t_new & enable_mask)) { + aspeed_timer_ctrl_enable(t, true); + } + } + s->ctrl = reg; +} + +static void aspeed_timer_set_ctrl2(AspeedTimerCtrlState *s, uint32_t value) +{ + trace_aspeed_timer_set_ctrl2(value); +} + +static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF); + const int reg = (offset & 0xf) / 4; + AspeedTimerCtrlState *s = opaque; + + switch (offset) { + /* Control Registers */ + case 0x30: + aspeed_timer_set_ctrl(s, tv); + break; + case 0x34: + aspeed_timer_set_ctrl2(s, tv); + break; + /* Timer Registers */ + case 0x00 ... 0x2c: + aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS), reg, tv); + break; + case 0x40 ... 0x8c: + aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS) - 1, reg, tv); + break; + /* Illegal */ + case 0x38: + case 0x3C: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } +} + +static const MemoryRegionOps aspeed_timer_ops = { + .read = aspeed_timer_read, + .write = aspeed_timer_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static void aspeed_init_one_timer(AspeedTimerCtrlState *s, uint8_t id) +{ + QEMUBH *bh; + AspeedTimer *t = &s->timers[id]; + + t->id = id; + bh = qemu_bh_new(aspeed_timer_expire, t); + t->timer = ptimer_init(bh); +} + +static void aspeed_timer_realize(DeviceState *dev, Error **errp) +{ + int i; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedTimerCtrlState *s = ASPEED_TIMER(dev); + + for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { + aspeed_init_one_timer(s, i); + sysbus_init_irq(sbd, &s->timers[i].irq); + } + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_timer_ops, s, + TYPE_ASPEED_TIMER, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void aspeed_timer_reset(DeviceState *dev) +{ + int i; + AspeedTimerCtrlState *s = ASPEED_TIMER(dev); + + for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { + AspeedTimer *t = &s->timers[i]; + /* Explictly call helpers to avoid any conditional behaviour through + * aspeed_timer_set_ctrl(). + */ + aspeed_timer_ctrl_enable(t, false); + aspeed_timer_ctrl_external_clock(t, TIMER_CLOCK_USE_APB); + aspeed_timer_ctrl_overflow_interrupt(t, false); + aspeed_timer_ctrl_pulse_enable(t, false); + t->level = 0; + t->reload = 0; + t->match[0] = 0; + t->match[1] = 0; + } + s->ctrl = 0; + s->ctrl2 = 0; +} + +static const VMStateDescription vmstate_aspeed_timer = { + .name = "aspeed.timer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(id, AspeedTimer), + VMSTATE_INT32(level, AspeedTimer), + VMSTATE_PTIMER(timer, AspeedTimer), + VMSTATE_UINT32(reload, AspeedTimer), + VMSTATE_UINT32_ARRAY(match, AspeedTimer, 2), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_aspeed_timer_state = { + .name = "aspeed.timerctrl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ctrl, AspeedTimerCtrlState), + VMSTATE_UINT32(ctrl2, AspeedTimerCtrlState), + VMSTATE_STRUCT_ARRAY(timers, AspeedTimerCtrlState, + ASPEED_TIMER_NR_TIMERS, 1, vmstate_aspeed_timer, + AspeedTimer), + VMSTATE_END_OF_LIST() + } +}; + +static void timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = aspeed_timer_realize; + dc->reset = aspeed_timer_reset; + dc->desc = "ASPEED Timer"; + dc->vmsd = &vmstate_aspeed_timer_state; +} + +static const TypeInfo aspeed_timer_info = { + .name = TYPE_ASPEED_TIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedTimerCtrlState), + .class_init = timer_class_init, +}; + +static void aspeed_timer_register_types(void) +{ + type_register_static(&aspeed_timer_info); +} + +type_init(aspeed_timer_register_types) diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c index 38fbf278fa..f5836e21f4 100644 --- a/hw/timer/imx_epit.c +++ b/hw/timer/imx_epit.c @@ -52,10 +52,10 @@ static char const *imx_epit_reg_name(uint32_t reg) * These are typical. */ static const IMXClk imx_epit_clocks[] = { - NOCLK, /* 00 disabled */ - CLK_IPG, /* 01 ipg_clk, ~532MHz */ - CLK_IPG, /* 10 ipg_clk_highfreq */ - CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */ + CLK_NONE, /* 00 disabled */ + CLK_IPG, /* 01 ipg_clk, ~532MHz */ + CLK_IPG_HIGH, /* 10 ipg_clk_highfreq */ + CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */ }; /* diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index 5cdc3b3c8d..ab2e213a19 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -81,14 +81,14 @@ static const VMStateDescription vmstate_imx_timer_gpt = { }; static const IMXClk imx_gpt_clocks[] = { - NOCLK, /* 000 No clock source */ - CLK_IPG, /* 001 ipg_clk, 532MHz*/ - CLK_IPG, /* 010 ipg_clk_highfreq */ - NOCLK, /* 011 not defined */ - CLK_32k, /* 100 ipg_clk_32k */ - NOCLK, /* 101 not defined */ - NOCLK, /* 110 not defined */ - NOCLK, /* 111 not defined */ + CLK_NONE, /* 000 No clock source */ + CLK_IPG, /* 001 ipg_clk, 532MHz*/ + CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ + CLK_NONE, /* 011 not defined */ + CLK_32k, /* 100 ipg_clk_32k */ + CLK_NONE, /* 101 not defined */ + CLK_NONE, /* 110 not defined */ + CLK_NONE, /* 111 not defined */ }; static void imx_gpt_set_freq(IMXGPTState *s) @@ -134,7 +134,7 @@ static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg, static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event) { uint32_t timeout = GPT_TIMER_MAX; - uint32_t count = 0; + uint32_t count; long long limit; if (!(s->cr & GPT_CR_EN)) { @@ -142,20 +142,23 @@ static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event) return; } - if (event) { - /* This is a timer event */ + /* update the count */ + count = imx_gpt_update_count(s); - if ((s->cr & GPT_CR_FRR) && (s->next_timeout != GPT_TIMER_MAX)) { - /* - * if we are in free running mode and we have not reached - * the GPT_TIMER_MAX limit, then update the count + if (event) { + /* + * This is an event (the ptimer reached 0 and stopped), and the + * timer counter is now equal to s->next_timeout. + */ + if (!(s->cr & GPT_CR_FRR) && (count == s->ocr1)) { + /* We are in restart mode and we crossed the compare channel 1 + * value. We need to reset the counter to 0. */ - count = imx_gpt_update_count(s); + count = s->cnt = s->next_timeout = 0; + } else if (count == GPT_TIMER_MAX) { + /* We reached GPT_TIMER_MAX so we need to rollover */ + count = s->cnt = s->next_timeout = 0; } - } else { - /* not a timer event, then just update the count */ - - count = imx_gpt_update_count(s); } /* now, find the next timeout related to count */ diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h new file mode 100644 index 0000000000..f16a1ed25c --- /dev/null +++ b/include/hw/arm/ast2400.h @@ -0,0 +1,35 @@ +/* + * ASPEED AST2400 SoC + * + * Andrew Jeffery <andrew@aj.id.au> + * + * Copyright 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#ifndef AST2400_H +#define AST2400_H + +#include "hw/arm/arm.h" +#include "hw/intc/aspeed_vic.h" +#include "hw/timer/aspeed_timer.h" + +typedef struct AST2400State { + /*< private >*/ + DeviceState parent; + + /*< public >*/ + ARMCPU *cpu; + MemoryRegion iomem; + AspeedVICState vic; + AspeedTimerCtrlState timerctrl; +} AST2400State; + +#define TYPE_AST2400 "ast2400" +#define AST2400(obj) OBJECT_CHECK(AST2400State, (obj), TYPE_AST2400) + +#define AST2400_SDRAM_BASE 0x40000000 + +#endif /* AST2400_H */ diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index 5d888dca53..e12ae3721a 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -14,6 +14,9 @@ #include "qemu-common.h" #include "exec/address-spaces.h" #include "hw/sysbus.h" +#include "hw/char/bcm2835_aux.h" +#include "hw/display/bcm2835_fb.h" +#include "hw/dma/bcm2835_dma.h" #include "hw/intc/bcm2835_ic.h" #include "hw/misc/bcm2835_property.h" #include "hw/misc/bcm2835_mbox.h" @@ -33,6 +36,9 @@ typedef struct BCM2835PeripheralState { qemu_irq irq, fiq; SysBusDevice *uart0; + BCM2835AuxState aux; + BCM2835FBState fb; + BCM2835DMAState dma; BCM2835ICState ic; BCM2835PropertyState property; BCM2835MboxState mboxes; diff --git a/include/hw/char/bcm2835_aux.h b/include/hw/char/bcm2835_aux.h new file mode 100644 index 0000000000..42f0ee7a92 --- /dev/null +++ b/include/hw/char/bcm2835_aux.h @@ -0,0 +1,33 @@ +/* + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_AUX_H +#define BCM2835_AUX_H + +#include "hw/sysbus.h" +#include "sysemu/char.h" + +#define TYPE_BCM2835_AUX "bcm2835-aux" +#define BCM2835_AUX(obj) OBJECT_CHECK(BCM2835AuxState, (obj), TYPE_BCM2835_AUX) + +#define BCM2835_AUX_RX_FIFO_LEN 8 + +typedef struct { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + CharDriverState *chr; + qemu_irq irq; + + uint8_t read_fifo[BCM2835_AUX_RX_FIFO_LEN]; + uint8_t read_pos, read_count; + uint8_t ier, iir; +} BCM2835AuxState; + +#endif diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h new file mode 100644 index 0000000000..9a12d7afa2 --- /dev/null +++ b/include/hw/display/bcm2835_fb.h @@ -0,0 +1,47 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_FB_H +#define BCM2835_FB_H + +#include "hw/sysbus.h" +#include "exec/address-spaces.h" +#include "ui/console.h" + +#define TYPE_BCM2835_FB "bcm2835-fb" +#define BCM2835_FB(obj) OBJECT_CHECK(BCM2835FBState, (obj), TYPE_BCM2835_FB) + +typedef struct { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + uint32_t vcram_base, vcram_size; + MemoryRegion *dma_mr; + AddressSpace dma_as; + MemoryRegion iomem; + MemoryRegionSection fbsection; + QemuConsole *con; + qemu_irq mbox_irq; + + bool lock, invalidate, pending; + uint32_t xres, yres; + uint32_t xres_virtual, yres_virtual; + uint32_t xoffset, yoffset; + uint32_t bpp; + uint32_t base, pitch, size; + uint32_t pixo, alpha; +} BCM2835FBState; + +void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres, + uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp, + uint32_t *pixo, uint32_t *alpha); + +#endif diff --git a/include/hw/dma/bcm2835_dma.h b/include/hw/dma/bcm2835_dma.h new file mode 100644 index 0000000000..75312e2e17 --- /dev/null +++ b/include/hw/dma/bcm2835_dma.h @@ -0,0 +1,47 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_DMA_H +#define BCM2835_DMA_H + +#include "qemu-common.h" +#include "exec/address-spaces.h" +#include "hw/sysbus.h" + +typedef struct { + uint32_t cs; + uint32_t conblk_ad; + uint32_t ti; + uint32_t source_ad; + uint32_t dest_ad; + uint32_t txfr_len; + uint32_t stride; + uint32_t nextconbk; + uint32_t debug; + + qemu_irq irq; +} BCM2835DMAChan; + +#define TYPE_BCM2835_DMA "bcm2835-dma" +#define BCM2835_DMA(obj) \ + OBJECT_CHECK(BCM2835DMAState, (obj), TYPE_BCM2835_DMA) + +#define BCM2835_DMA_NCHANS 16 + +typedef struct { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + MemoryRegion iomem0, iomem15; + MemoryRegion *dma_mr; + AddressSpace dma_as; + + BCM2835DMAChan chan[BCM2835_DMA_NCHANS]; + uint32_t int_status; + uint32_t enable; +} BCM2835DMAState; + +#endif diff --git a/include/hw/intc/aspeed_vic.h b/include/hw/intc/aspeed_vic.h new file mode 100644 index 0000000000..107ff17c3b --- /dev/null +++ b/include/hw/intc/aspeed_vic.h @@ -0,0 +1,48 @@ +/* + * ASPEED Interrupt Controller (New) + * + * Andrew Jeffery <andrew@aj.id.au> + * + * Copyright 2016 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + * Need to add SVIC and CVIC support + */ +#ifndef ASPEED_VIC_H +#define ASPEED_VIC_H + +#include "hw/sysbus.h" + +#define TYPE_ASPEED_VIC "aspeed.vic" +#define ASPEED_VIC(obj) OBJECT_CHECK(AspeedVICState, (obj), TYPE_ASPEED_VIC) + +#define ASPEED_VIC_NR_IRQS 51 + +typedef struct AspeedVICState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + qemu_irq irq; + qemu_irq fiq; + + uint64_t level; + uint64_t raw; + uint64_t select; + uint64_t enable; + uint64_t trigger; + + /* 0=edge, 1=level */ + uint64_t sense; + + /* 0=single-edge, 1=dual-edge */ + uint64_t dual_edge; + + /* 0=low-sensitive/falling-edge, 1=high-sensitive/rising-edge */ + uint64_t event; +} AspeedVICState; + +#endif /* ASPEED_VIC_H */ diff --git a/include/hw/loader.h b/include/hw/loader.h index 0ba780852c..b3d1358d9c 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -137,7 +137,7 @@ void hmp_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_blob_fixed(_f, _b, _l, _a) \ rom_add_blob(_f, _b, _l, _l, _a, NULL, NULL, NULL) #define rom_add_file_mr(_f, _mr, _i) \ - rom_add_file(_f, NULL, 0, _i, false, mr) + rom_add_file(_f, NULL, 0, _i, false, _mr) #define PC_ROM_MIN_VGA 0xc0000 #define PC_ROM_MIN_OPTION 0xc8000 diff --git a/include/hw/misc/bcm2835_property.h b/include/hw/misc/bcm2835_property.h index df889eaa08..edcab603ce 100644 --- a/include/hw/misc/bcm2835_property.h +++ b/include/hw/misc/bcm2835_property.h @@ -9,6 +9,7 @@ #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "net/net.h" +#include "hw/display/bcm2835_fb.h" #define TYPE_BCM2835_PROPERTY "bcm2835-property" #define BCM2835_PROPERTY(obj) \ @@ -18,13 +19,15 @@ typedef struct { /*< private >*/ SysBusDevice busdev; /*< public >*/ + MemoryRegion *dma_mr; AddressSpace dma_as; MemoryRegion iomem; qemu_irq mbox_irq; + BCM2835FBState *fbdev; + MACAddr macaddr; uint32_t board_rev; - uint32_t ram_size; uint32_t addr; bool pending; } BCM2835PropertyState; diff --git a/include/hw/misc/imx6_ccm.h b/include/hw/misc/imx6_ccm.h new file mode 100644 index 0000000000..80505809b4 --- /dev/null +++ b/include/hw/misc/imx6_ccm.h @@ -0,0 +1,197 @@ +/* + * IMX6 Clock Control Module + * + * Copyright (C) 2012 NICTA + * Updated by Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef IMX6_CCM_H +#define IMX6_CCM_H + +#include "hw/misc/imx_ccm.h" +#include "qemu/bitops.h" + +#define CCM_CCR 0 +#define CCM_CCDR 1 +#define CCM_CSR 2 +#define CCM_CCSR 3 +#define CCM_CACRR 4 +#define CCM_CBCDR 5 +#define CCM_CBCMR 6 +#define CCM_CSCMR1 7 +#define CCM_CSCMR2 8 +#define CCM_CSCDR1 9 +#define CCM_CS1CDR 10 +#define CCM_CS2CDR 11 +#define CCM_CDCDR 12 +#define CCM_CHSCCDR 13 +#define CCM_CSCDR2 14 +#define CCM_CSCDR3 15 +#define CCM_CDHIPR 18 +#define CCM_CTOR 20 +#define CCM_CLPCR 21 +#define CCM_CISR 22 +#define CCM_CIMR 23 +#define CCM_CCOSR 24 +#define CCM_CGPR 25 +#define CCM_CCGR0 26 +#define CCM_CCGR1 27 +#define CCM_CCGR2 28 +#define CCM_CCGR3 29 +#define CCM_CCGR4 30 +#define CCM_CCGR5 31 +#define CCM_CCGR6 32 +#define CCM_CMEOR 34 +#define CCM_MAX 35 + +#define CCM_ANALOG_PLL_ARM 0 +#define CCM_ANALOG_PLL_ARM_SET 1 +#define CCM_ANALOG_PLL_ARM_CLR 2 +#define CCM_ANALOG_PLL_ARM_TOG 3 +#define CCM_ANALOG_PLL_USB1 4 +#define CCM_ANALOG_PLL_USB1_SET 5 +#define CCM_ANALOG_PLL_USB1_CLR 6 +#define CCM_ANALOG_PLL_USB1_TOG 7 +#define CCM_ANALOG_PLL_USB2 8 +#define CCM_ANALOG_PLL_USB2_SET 9 +#define CCM_ANALOG_PLL_USB2_CLR 10 +#define CCM_ANALOG_PLL_USB2_TOG 11 +#define CCM_ANALOG_PLL_SYS 12 +#define CCM_ANALOG_PLL_SYS_SET 13 +#define CCM_ANALOG_PLL_SYS_CLR 14 +#define CCM_ANALOG_PLL_SYS_TOG 15 +#define CCM_ANALOG_PLL_SYS_SS 16 +#define CCM_ANALOG_PLL_SYS_NUM 20 +#define CCM_ANALOG_PLL_SYS_DENOM 24 +#define CCM_ANALOG_PLL_AUDIO 28 +#define CCM_ANALOG_PLL_AUDIO_SET 29 +#define CCM_ANALOG_PLL_AUDIO_CLR 30 +#define CCM_ANALOG_PLL_AUDIO_TOG 31 +#define CCM_ANALOG_PLL_AUDIO_NUM 32 +#define CCM_ANALOG_PLL_AUDIO_DENOM 36 +#define CCM_ANALOG_PLL_VIDEO 40 +#define CCM_ANALOG_PLL_VIDEO_SET 41 +#define CCM_ANALOG_PLL_VIDEO_CLR 42 +#define CCM_ANALOG_PLL_VIDEO_TOG 44 +#define CCM_ANALOG_PLL_VIDEO_NUM 46 +#define CCM_ANALOG_PLL_VIDEO_DENOM 48 +#define CCM_ANALOG_PLL_MLB 52 +#define CCM_ANALOG_PLL_MLB_SET 53 +#define CCM_ANALOG_PLL_MLB_CLR 54 +#define CCM_ANALOG_PLL_MLB_TOG 55 +#define CCM_ANALOG_PLL_ENET 56 +#define CCM_ANALOG_PLL_ENET_SET 57 +#define CCM_ANALOG_PLL_ENET_CLR 58 +#define CCM_ANALOG_PLL_ENET_TOG 59 +#define CCM_ANALOG_PFD_480 60 +#define CCM_ANALOG_PFD_480_SET 61 +#define CCM_ANALOG_PFD_480_CLR 62 +#define CCM_ANALOG_PFD_480_TOG 63 +#define CCM_ANALOG_PFD_528 64 +#define CCM_ANALOG_PFD_528_SET 65 +#define CCM_ANALOG_PFD_528_CLR 66 +#define CCM_ANALOG_PFD_528_TOG 67 + +/* PMU registers */ +#define PMU_REG_1P1 68 +#define PMU_REG_3P0 72 +#define PMU_REG_2P5 76 +#define PMU_REG_CORE 80 + +#define CCM_ANALOG_MISC0 84 +#define PMU_MISC0 84 +#define CCM_ANALOG_MISC0_SET 85 +#define CCM_ANALOG_MISC0_CLR 86 +#define CCM_ANALOG_MISC0_TOG 87 + +#define PMU_MISC1 88 +#define PMU_MISC1_SET 89 +#define PMU_MISC1_CLR 90 +#define PMU_MISC1_TOG 91 + +#define CCM_ANALOG_MISC2 92 +#define PMU_MISC2 92 +#define CCM_ANALOG_MISC2_SET 93 +#define CCM_ANALOG_MISC2_CLR 94 +#define CCM_ANALOG_MISC2_TOG 95 + +#define USB_ANALOG_USB1_VBUS_DETECT 104 +#define USB_ANALOG_USB1_VBUS_DETECT_SET 105 +#define USB_ANALOG_USB1_VBUS_DETECT_CLR 106 +#define USB_ANALOG_USB1_VBUS_DETECT_TOG 107 +#define USB_ANALOG_USB1_CHRG_DETECT 108 +#define USB_ANALOG_USB1_CHRG_DETECT_SET 109 +#define USB_ANALOG_USB1_CHRG_DETECT_CLR 110 +#define USB_ANALOG_USB1_CHRG_DETECT_TOG 111 +#define USB_ANALOG_USB1_VBUS_DETECT_STAT 112 +#define USB_ANALOG_USB1_CHRG_DETECT_STAT 116 +#define USB_ANALOG_USB1_MISC 124 +#define USB_ANALOG_USB1_MISC_SET 125 +#define USB_ANALOG_USB1_MISC_CLR 126 +#define USB_ANALOG_USB1_MISC_TOG 127 +#define USB_ANALOG_USB2_VBUS_DETECT 128 +#define USB_ANALOG_USB2_VBUS_DETECT_SET 129 +#define USB_ANALOG_USB2_VBUS_DETECT_CLR 130 +#define USB_ANALOG_USB2_VBUS_DETECT_TOG 131 +#define USB_ANALOG_USB2_CHRG_DETECT 132 +#define USB_ANALOG_USB2_CHRG_DETECT_SET 133 +#define USB_ANALOG_USB2_CHRG_DETECT_CLR 134 +#define USB_ANALOG_USB2_CHRG_DETECT_TOG 135 +#define USB_ANALOG_USB2_VBUS_DETECT_STAT 136 +#define USB_ANALOG_USB2_CHRG_DETECT_STAT 140 +#define USB_ANALOG_USB2_MISC 148 +#define USB_ANALOG_USB2_MISC_SET 149 +#define USB_ANALOG_USB2_MISC_CLR 150 +#define USB_ANALOG_USB2_MISC_TOG 151 +#define USB_ANALOG_DIGPROG 152 +#define CCM_ANALOG_MAX 153 + +/* CCM_CBCMR */ +#define PRE_PERIPH_CLK_SEL_SHIFT (18) +#define PRE_PERIPH_CLK_SEL_LENGTH (2) + +/* CCM_CBCDR */ +#define AHB_PODF_SHIFT (10) +#define AHB_PODF_LENGTH (3) +#define IPG_PODF_SHIFT (8) +#define IPG_PODF_LENGTH (2) + +/* CCM_CSCMR1 */ +#define PERCLK_PODF_SHIFT (0) +#define PERCLK_PODF_LENGTH (6) + +/* CCM_ANALOG_PFD_528 */ +#define PFD0_FRAC_SHIFT (0) +#define PFD0_FRAC_LENGTH (6) +#define PFD2_FRAC_SHIFT (16) +#define PFD2_FRAC_LENGTH (6) + +/* CCM_ANALOG_PLL_SYS */ +#define DIV_SELECT_SHIFT (0) +#define DIV_SELECT_LENGTH (1) + +#define CCM_ANALOG_PLL_LOCK (1 << 31); + +#define EXTRACT(value, name) extract32(value, name##_SHIFT, name##_LENGTH) + +#define TYPE_IMX6_CCM "imx6.ccm" +#define IMX6_CCM(obj) OBJECT_CHECK(IMX6CCMState, (obj), TYPE_IMX6_CCM) + +typedef struct IMX6CCMState { + /* <private> */ + IMXCCMState parent_obj; + + /* <public> */ + MemoryRegion container; + MemoryRegion ioccm; + MemoryRegion ioanalog; + + uint32_t ccm[CCM_MAX]; + uint32_t analog[CCM_ANALOG_MAX]; + +} IMX6CCMState; + +#endif /* IMX6_CCM_H */ diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h index 5c4b7958bd..48a7afad5e 100644 --- a/include/hw/misc/imx_ccm.h +++ b/include/hw/misc/imx_ccm.h @@ -43,15 +43,9 @@ typedef struct IMXCCMState { } IMXCCMState; typedef enum { - NOCLK, - CLK_MPLL, - CLK_UPLL, - CLK_MCU, - CLK_HSP, - CLK_MAX, - CLK_AHB, + CLK_NONE, CLK_IPG, - CLK_PER, + CLK_IPG_HIGH, CLK_32k } IMXClk; diff --git a/include/hw/timer/aspeed_timer.h b/include/hw/timer/aspeed_timer.h new file mode 100644 index 0000000000..44dc2f89d5 --- /dev/null +++ b/include/hw/timer/aspeed_timer.h @@ -0,0 +1,59 @@ +/* + * ASPEED AST2400 Timer + * + * Andrew Jeffery <andrew@aj.id.au> + * + * Copyright (C) 2016 IBM Corp. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef ASPEED_TIMER_H +#define ASPEED_TIMER_H + +#include "hw/ptimer.h" + +#define ASPEED_TIMER(obj) \ + OBJECT_CHECK(AspeedTimerCtrlState, (obj), TYPE_ASPEED_TIMER); +#define TYPE_ASPEED_TIMER "aspeed.timer" +#define ASPEED_TIMER_NR_TIMERS 8 + +typedef struct AspeedTimer { + qemu_irq irq; + + uint8_t id; + + /** + * Track the line level as the ASPEED timers implement edge triggered + * interrupts, signalling with both the rising and falling edge. + */ + int32_t level; + ptimer_state *timer; + uint32_t reload; + uint32_t match[2]; +} AspeedTimer; + +typedef struct AspeedTimerCtrlState { + /*< private >*/ + SysBusDevice parent; + + /*< public >*/ + MemoryRegion iomem; + + uint32_t ctrl; + uint32_t ctrl2; + AspeedTimer timers[ASPEED_TIMER_NR_TIMERS]; +} AspeedTimerCtrlState; + +#endif /* ASPEED_TIMER_H */ diff --git a/target-arm/helper.c b/target-arm/helper.c index eaded41969..19d5d525f3 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -7237,7 +7237,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, CPUState *cs = CPU(cpu); /* Read an LPAE long-descriptor translation table. */ MMUFaultType fault_type = translation_fault; - uint32_t level = 1; + uint32_t level; uint32_t epd = 0; int32_t t0sz, t1sz; uint32_t tg; @@ -7248,7 +7248,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, target_ulong page_size; uint32_t attrs; int32_t stride = 9; - int32_t va_size = 32; + int32_t va_size; int inputsize; int32_t tbi = 0; TCR *tcr = regime_tcr(env, mmu_idx); @@ -7264,6 +7264,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, * support for those page table walks. */ if (arm_el_is_aa64(env, el)) { + level = 0; va_size = 64; if (el > 1) { if (mmu_idx != ARMMMUIdx_S2NS) { @@ -7285,6 +7286,8 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, ttbr1_valid = false; } } else { + level = 1; + va_size = 32; /* There is no TTBR1 for EL2 */ if (el == 2) { ttbr1_valid = false; @@ -7407,27 +7410,26 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, /* For stage 2 translations the starting level is specified by the * VTCR_EL2.SL0 field (whose interpretation depends on the page size) */ - int startlevel = extract32(tcr->raw_tcr, 6, 2); + uint32_t sl0 = extract32(tcr->raw_tcr, 6, 2); + uint32_t startlevel; bool ok; if (va_size == 32 || stride == 9) { /* AArch32 or 4KB pages */ - level = 2 - startlevel; + startlevel = 2 - sl0; } else { /* 16KB or 64KB pages */ - level = 3 - startlevel; + startlevel = 3 - sl0; } /* Check that the starting level is valid. */ - ok = check_s2_mmu_setup(cpu, va_size == 64, level, inputsize, stride); + ok = check_s2_mmu_setup(cpu, va_size == 64, startlevel, + inputsize, stride); if (!ok) { - /* AArch64 reports these as level 0 faults. - * AArch32 reports these as level 1 faults. - */ - level = va_size == 64 ? 0 : 1; fault_type = translation_fault; goto do_fault; } + level = startlevel; } /* Clear the vaddr bits which aren't part of the within-region address, diff --git a/target-arm/helper.h b/target-arm/helper.h index e3d09d93bd..84aa637629 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -77,6 +77,9 @@ DEF_HELPER_1(exception_return, void, env) DEF_HELPER_2(get_r13_banked, i32, env, i32) DEF_HELPER_3(set_r13_banked, void, env, i32, i32) +DEF_HELPER_3(mrs_banked, i32, env, i32, i32) +DEF_HELPER_4(msr_banked, void, env, i32, i32, i32) + DEF_HELPER_2(get_user_reg, i32, env, i32) DEF_HELPER_3(set_user_reg, void, env, i32, i32) diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 92fde0a68c..d626ff1a20 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -494,6 +494,126 @@ uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode) } } +static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode, + uint32_t regno) +{ + /* Raise an exception if the requested access is one of the UNPREDICTABLE + * cases; otherwise return. This broadly corresponds to the pseudocode + * BankedRegisterAccessValid() and SPSRAccessValid(), + * except that we have already handled some cases at translate time. + */ + int curmode = env->uncached_cpsr & CPSR_M; + + if (curmode == tgtmode) { + goto undef; + } + + if (tgtmode == ARM_CPU_MODE_USR) { + switch (regno) { + case 8 ... 12: + if (curmode != ARM_CPU_MODE_FIQ) { + goto undef; + } + break; + case 13: + if (curmode == ARM_CPU_MODE_SYS) { + goto undef; + } + break; + case 14: + if (curmode == ARM_CPU_MODE_HYP || curmode == ARM_CPU_MODE_SYS) { + goto undef; + } + break; + default: + break; + } + } + + if (tgtmode == ARM_CPU_MODE_HYP) { + switch (regno) { + case 17: /* ELR_Hyp */ + if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) { + goto undef; + } + break; + default: + if (curmode != ARM_CPU_MODE_MON) { + goto undef; + } + break; + } + } + + return; + +undef: + raise_exception(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env)); +} + +void HELPER(msr_banked)(CPUARMState *env, uint32_t value, uint32_t tgtmode, + uint32_t regno) +{ + msr_mrs_banked_exc_checks(env, tgtmode, regno); + + switch (regno) { + case 16: /* SPSRs */ + env->banked_spsr[bank_number(tgtmode)] = value; + break; + case 17: /* ELR_Hyp */ + env->elr_el[2] = value; + break; + case 13: + env->banked_r13[bank_number(tgtmode)] = value; + break; + case 14: + env->banked_r14[bank_number(tgtmode)] = value; + break; + case 8 ... 12: + switch (tgtmode) { + case ARM_CPU_MODE_USR: + env->usr_regs[regno - 8] = value; + break; + case ARM_CPU_MODE_FIQ: + env->fiq_regs[regno - 8] = value; + break; + default: + g_assert_not_reached(); + } + break; + default: + g_assert_not_reached(); + } +} + +uint32_t HELPER(mrs_banked)(CPUARMState *env, uint32_t tgtmode, uint32_t regno) +{ + msr_mrs_banked_exc_checks(env, tgtmode, regno); + + switch (regno) { + case 16: /* SPSRs */ + return env->banked_spsr[bank_number(tgtmode)]; + case 17: /* ELR_Hyp */ + return env->elr_el[2]; + case 13: + return env->banked_r13[bank_number(tgtmode)]; + case 14: + return env->banked_r14[bank_number(tgtmode)]; + case 8 ... 12: + switch (tgtmode) { + case ARM_CPU_MODE_USR: + return env->usr_regs[regno - 8]; + case ARM_CPU_MODE_FIQ: + return env->fiq_regs[regno - 8]; + default: + g_assert_not_reached(); + } + default: + g_assert_not_reached(); + } +} + void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, uint32_t isread) { diff --git a/target-arm/translate.c b/target-arm/translate.c index 025c7a53eb..5a818afc02 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -4160,6 +4160,195 @@ static int gen_set_psr_im(DisasContext *s, uint32_t mask, int spsr, uint32_t val return gen_set_psr(s, mask, spsr, tmp); } +static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, + int *tgtmode, int *regno) +{ + /* Decode the r and sysm fields of MSR/MRS banked accesses into + * the target mode and register number, and identify the various + * unpredictable cases. + * MSR (banked) and MRS (banked) are CONSTRAINED UNPREDICTABLE if: + * + executed in user mode + * + using R15 as the src/dest register + * + accessing an unimplemented register + * + accessing a register that's inaccessible at current PL/security state* + * + accessing a register that you could access with a different insn + * We choose to UNDEF in all these cases. + * Since we don't know which of the various AArch32 modes we are in + * we have to defer some checks to runtime. + * Accesses to Monitor mode registers from Secure EL1 (which implies + * that EL3 is AArch64) must trap to EL3. + * + * If the access checks fail this function will emit code to take + * an exception and return false. Otherwise it will return true, + * and set *tgtmode and *regno appropriately. + */ + int exc_target = default_exception_el(s); + + /* These instructions are present only in ARMv8, or in ARMv7 with the + * Virtualization Extensions. + */ + if (!arm_dc_feature(s, ARM_FEATURE_V8) && + !arm_dc_feature(s, ARM_FEATURE_EL2)) { + goto undef; + } + + if (IS_USER(s) || rn == 15) { + goto undef; + } + + /* The table in the v8 ARM ARM section F5.2.3 describes the encoding + * of registers into (r, sysm). + */ + if (r) { + /* SPSRs for other modes */ + switch (sysm) { + case 0xe: /* SPSR_fiq */ + *tgtmode = ARM_CPU_MODE_FIQ; + break; + case 0x10: /* SPSR_irq */ + *tgtmode = ARM_CPU_MODE_IRQ; + break; + case 0x12: /* SPSR_svc */ + *tgtmode = ARM_CPU_MODE_SVC; + break; + case 0x14: /* SPSR_abt */ + *tgtmode = ARM_CPU_MODE_ABT; + break; + case 0x16: /* SPSR_und */ + *tgtmode = ARM_CPU_MODE_UND; + break; + case 0x1c: /* SPSR_mon */ + *tgtmode = ARM_CPU_MODE_MON; + break; + case 0x1e: /* SPSR_hyp */ + *tgtmode = ARM_CPU_MODE_HYP; + break; + default: /* unallocated */ + goto undef; + } + /* We arbitrarily assign SPSR a register number of 16. */ + *regno = 16; + } else { + /* general purpose registers for other modes */ + switch (sysm) { + case 0x0 ... 0x6: /* 0b00xxx : r8_usr ... r14_usr */ + *tgtmode = ARM_CPU_MODE_USR; + *regno = sysm + 8; + break; + case 0x8 ... 0xe: /* 0b01xxx : r8_fiq ... r14_fiq */ + *tgtmode = ARM_CPU_MODE_FIQ; + *regno = sysm; + break; + case 0x10 ... 0x11: /* 0b1000x : r14_irq, r13_irq */ + *tgtmode = ARM_CPU_MODE_IRQ; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x12 ... 0x13: /* 0b1001x : r14_svc, r13_svc */ + *tgtmode = ARM_CPU_MODE_SVC; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x14 ... 0x15: /* 0b1010x : r14_abt, r13_abt */ + *tgtmode = ARM_CPU_MODE_ABT; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x16 ... 0x17: /* 0b1011x : r14_und, r13_und */ + *tgtmode = ARM_CPU_MODE_UND; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x1c ... 0x1d: /* 0b1110x : r14_mon, r13_mon */ + *tgtmode = ARM_CPU_MODE_MON; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x1e ... 0x1f: /* 0b1111x : elr_hyp, r13_hyp */ + *tgtmode = ARM_CPU_MODE_HYP; + /* Arbitrarily pick 17 for ELR_Hyp (which is not a banked LR!) */ + *regno = sysm & 1 ? 13 : 17; + break; + default: /* unallocated */ + goto undef; + } + } + + /* Catch the 'accessing inaccessible register' cases we can detect + * at translate time. + */ + switch (*tgtmode) { + case ARM_CPU_MODE_MON: + if (!arm_dc_feature(s, ARM_FEATURE_EL3) || s->ns) { + goto undef; + } + if (s->current_el == 1) { + /* If we're in Secure EL1 (which implies that EL3 is AArch64) + * then accesses to Mon registers trap to EL3 + */ + exc_target = 3; + goto undef; + } + break; + case ARM_CPU_MODE_HYP: + /* Note that we can forbid accesses from EL2 here because they + * must be from Hyp mode itself + */ + if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 3) { + goto undef; + } + break; + default: + break; + } + + return true; + +undef: + /* If we get here then some access check did not pass */ + gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), exc_target); + return false; +} + +static void gen_msr_banked(DisasContext *s, int r, int sysm, int rn) +{ + TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno; + int tgtmode = 0, regno = 0; + + if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) { + return; + } + + /* Sync state because msr_banked() can raise exceptions */ + gen_set_condexec(s); + gen_set_pc_im(s, s->pc - 4); + tcg_reg = load_reg(s, rn); + tcg_tgtmode = tcg_const_i32(tgtmode); + tcg_regno = tcg_const_i32(regno); + gen_helper_msr_banked(cpu_env, tcg_reg, tcg_tgtmode, tcg_regno); + tcg_temp_free_i32(tcg_tgtmode); + tcg_temp_free_i32(tcg_regno); + tcg_temp_free_i32(tcg_reg); + s->is_jmp = DISAS_UPDATE; +} + +static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn) +{ + TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno; + int tgtmode = 0, regno = 0; + + if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) { + return; + } + + /* Sync state because mrs_banked() can raise exceptions */ + gen_set_condexec(s); + gen_set_pc_im(s, s->pc - 4); + tcg_reg = tcg_temp_new_i32(); + tcg_tgtmode = tcg_const_i32(tgtmode); + tcg_regno = tcg_const_i32(regno); + gen_helper_mrs_banked(tcg_reg, cpu_env, tcg_tgtmode, tcg_regno); + tcg_temp_free_i32(tcg_tgtmode); + tcg_temp_free_i32(tcg_regno); + store_reg(s, rn, tcg_reg); + s->is_jmp = DISAS_UPDATE; +} + /* Generate an old-style exception return. Marks pc as dead. */ static void gen_exception_return(DisasContext *s, TCGv_i32 pc) { @@ -8022,7 +8211,26 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) sh = (insn >> 4) & 0xf; rm = insn & 0xf; switch (sh) { - case 0x0: /* move program status register */ + case 0x0: /* MSR, MRS */ + if (insn & (1 << 9)) { + /* MSR (banked) and MRS (banked) */ + int sysm = extract32(insn, 16, 4) | + (extract32(insn, 8, 1) << 4); + int r = extract32(insn, 22, 1); + + if (op1 & 1) { + /* MSR (banked) */ + gen_msr_banked(s, r, sysm, rm); + } else { + /* MRS (banked) */ + int rd = extract32(insn, 12, 4); + + gen_mrs_banked(s, r, sysm, rd); + } + break; + } + + /* MSR, MRS (for PSRs) */ if (op1 & 1) { /* PSR = reg */ tmp = load_reg(s, rm); @@ -10133,6 +10341,18 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw if (arm_dc_feature(s, ARM_FEATURE_M)) { goto illegal_op; } + + if (extract32(insn, 5, 1)) { + /* MSR (banked) */ + int sysm = extract32(insn, 8, 4) | + (extract32(insn, 4, 1) << 4); + int r = op & 1; + + gen_msr_banked(s, r, sysm, rm); + break; + } + + /* MSR (for PSRs) */ tmp = load_reg(s, rn); if (gen_set_psr(s, msr_mask(s, (insn >> 8) & 0xf, op == 1), @@ -10205,7 +10425,17 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tcg_gen_subi_i32(tmp, tmp, insn & 0xff); gen_exception_return(s, tmp); break; - case 6: /* mrs cpsr. */ + case 6: /* MRS */ + if (extract32(insn, 5, 1)) { + /* MRS (banked) */ + int sysm = extract32(insn, 16, 4) | + (extract32(insn, 4, 1) << 4); + + gen_mrs_banked(s, 0, sysm, rd); + break; + } + + /* mrs cpsr */ tmp = tcg_temp_new_i32(); if (arm_dc_feature(s, ARM_FEATURE_M)) { addr = tcg_const_i32(insn & 0xff); @@ -10216,7 +10446,17 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } store_reg(s, rd, tmp); break; - case 7: /* mrs spsr. */ + case 7: /* MRS */ + if (extract32(insn, 5, 1)) { + /* MRS (banked) */ + int sysm = extract32(insn, 16, 4) | + (extract32(insn, 4, 1) << 4); + + gen_mrs_banked(s, 1, sysm, rd); + break; + } + + /* mrs spsr. */ /* Not accessible in user mode. */ if (IS_USER(s) || arm_dc_feature(s, ARM_FEATURE_M)) { goto illegal_op; diff --git a/trace-events b/trace-events index 0ad8a1c85f..d494de1446 100644 --- a/trace-events +++ b/trace-events @@ -1892,3 +1892,19 @@ qio_channel_command_new_pid(void *ioc, int writefd, int readfd, int pid) "Comman qio_channel_command_new_spawn(void *ioc, const char *binary, int flags) "Command new spawn ioc=%p binary=%s flags=%d" qio_channel_command_abort(void *ioc, int pid) "Command abort ioc=%p pid=%d" qio_channel_command_wait(void *ioc, int pid, int ret, int status) "Command abort ioc=%p pid=%d ret=%d status=%d" + +# hw/timer/aspeed_timer.c +aspeed_timer_ctrl_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" +aspeed_timer_ctrl_external_clock(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" +aspeed_timer_ctrl_overflow_interrupt(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" +aspeed_timer_ctrl_pulse_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" +aspeed_timer_set_ctrl2(uint32_t value) "Value: 0x%" PRIx32 +aspeed_timer_set_value(int timer, int reg, uint32_t value) "Timer %d register %d: 0x%" PRIx32 +aspeed_timer_read(uint64_t offset, unsigned size, uint64_t value) "From 0x%" PRIx64 ": of size %u: 0x%" PRIx64 + +# hw/intc/aspeed_vic.c +aspeed_vic_set_irq(int irq, int level) "Enabling IRQ %d: %d" +aspeed_vic_update_fiq(int flags) "Raising FIQ: %d" +aspeed_vic_update_irq(int flags) "Raising IRQ: %d" +aspeed_vic_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 |