diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2016-07-04 14:33:05 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2016-07-04 14:33:05 +0100 |
commit | 3173a1fd549b7fa0f7029b2c6a6b86ba6efa92aa (patch) | |
tree | 7ab44b3f4610cea6c687f0c127b83e09eb24f0ec | |
parent | 9b9611c85d810f8f94a007b7ed7103a417fd25ba (diff) | |
parent | e1ad9bc405afbd7581831ca1705f39e73c94c5ff (diff) |
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20160704' into staging
target-arm queue:
* fix semihosting SYS_HEAPINFO call for A64 guests
* fix crash if guest tries to write to ROM on imx boards
* armv7m_nvic: fix crash for debugger reads from some registers
* virt: mark PCIe host controller as dma-coherent in the DT
* add data-driven register API
* Xilinx Zynq: add devcfg device model
* m25p80: fix various bugs
* ast2400: add SMC controllers and SPI flash slaves
# gpg: Signature made Mon 04 Jul 2016 13:17:34 BST
# gpg: using RSA key 0x3C2525ED14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg: aka "Peter Maydell <pmaydell@gmail.com>"
# gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE
* remotes/pmaydell/tags/pull-target-arm-20160704: (23 commits)
ast2400: create SPI flash slaves
ast2400: add SPI flash slaves
ast2400: add SMC controllers (FMC and SPI)
m25p80: qdev-ify drive property
m25p80: change cur_addr to 32 bit integer
m25p80: avoid out of bounds accesses
m25p80: do not put iovec on the stack
ssi: change ssi_slave_init to be a realize ops
xilinx_zynq: Connect devcfg to the Zynq machine model
dma: Add Xilinx Zynq devcfg device model
register: Add block initialise helper
register: QOMify
register: Define REG and FIELD macros
register: Add Memory API glue
register: Add Register API
bitops: Add MAKE_64BIT_MASK macro
hw/arm/virt: mark the PCIe host controller as DMA coherent in the DT
armv7m_nvic: Use qemu_get_cpu(0) instead of current_cpu
memory: Assert that memory_region_init_rom_device() ops aren't NULL
imx: Use memory_region_init_rom() for ROMs
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
38 files changed, 1847 insertions, 132 deletions
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index c5bcba754a..7a19863b18 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -66,6 +66,7 @@ CONFIG_PXA2XX=y CONFIG_BITBANG_I2C=y CONFIG_FRAMEBUFFER=y CONFIG_XILINX_SPIPS=y +CONFIG_ZYNQ_DEVCFG=y CONFIG_ARM11SCU=y CONFIG_A9SCU=y diff --git a/docs/memory.txt b/docs/memory.txt index 431d9ca88f..811b1bd3c5 100644 --- a/docs/memory.txt +++ b/docs/memory.txt @@ -41,8 +41,13 @@ MemoryRegion): MemoryRegionOps structure describing the callbacks. - ROM: a ROM memory region works like RAM for reads (directly accessing - a region of host memory), but like MMIO for writes (invoking a callback). - You initialize these with memory_region_init_rom_device(). + a region of host memory), and forbids writes. You initialize these with + memory_region_init_rom(). + +- ROM device: a ROM device memory region works like RAM for reads + (directly accessing a region of host memory), but like MMIO for + writes (invoking a callback). You initialize these with + memory_region_init_rom_device(). - IOMMU region: an IOMMU region translates addresses of accesses made to it and forwards them to some other target memory region. As the name suggests, diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c index b14a82fcde..0555843620 100644 --- a/hw/arm/ast2400.c +++ b/hw/arm/ast2400.c @@ -23,11 +23,17 @@ #define AST2400_UART_5_BASE 0x00184000 #define AST2400_IOMEM_SIZE 0x00200000 #define AST2400_IOMEM_BASE 0x1E600000 +#define AST2400_SMC_BASE AST2400_IOMEM_BASE /* Legacy SMC */ +#define AST2400_FMC_BASE 0X1E620000 +#define AST2400_SPI_BASE 0X1E630000 #define AST2400_VIC_BASE 0x1E6C0000 #define AST2400_SCU_BASE 0x1E6E2000 #define AST2400_TIMER_BASE 0x1E782000 #define AST2400_I2C_BASE 0x1E78A000 +#define AST2400_FMC_FLASH_BASE 0x20000000 +#define AST2400_SPI_FLASH_BASE 0x30000000 + #define AST2400_A0_SILICON_REV 0x02000303 static const int uart_irqs[] = { 9, 32, 33, 34, 10 }; @@ -85,13 +91,21 @@ static void ast2400_init(Object *obj) "hw-strap1", &error_abort); object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), "hw-strap2", &error_abort); + + object_initialize(&s->smc, sizeof(s->smc), "aspeed.smc.fmc"); + object_property_add_child(obj, "smc", OBJECT(&s->smc), NULL); + qdev_set_parent_bus(DEVICE(&s->smc), sysbus_get_default()); + + object_initialize(&s->spi, sizeof(s->spi), "aspeed.smc.spi"); + object_property_add_child(obj, "spi", OBJECT(&s->spi), NULL); + qdev_set_parent_bus(DEVICE(&s->spi), sysbus_get_default()); } static void ast2400_realize(DeviceState *dev, Error **errp) { int i; AST2400State *s = AST2400(dev); - Error *err = NULL; + Error *err = NULL, *local_err = NULL; /* IO space */ memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL, @@ -147,6 +161,30 @@ static void ast2400_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, AST2400_I2C_BASE); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0, qdev_get_gpio_in(DEVICE(&s->vic), 12)); + + /* SMC */ + object_property_set_int(OBJECT(&s->smc), 1, "num-cs", &err); + object_property_set_bool(OBJECT(&s->smc), true, "realized", &local_err); + error_propagate(&err, local_err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 0, AST2400_FMC_BASE); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 1, AST2400_FMC_FLASH_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->smc), 0, + qdev_get_gpio_in(DEVICE(&s->vic), 19)); + + /* SPI */ + object_property_set_int(OBJECT(&s->spi), 1, "num-cs", &err); + object_property_set_bool(OBJECT(&s->spi), true, "realized", &local_err); + error_propagate(&err, local_err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, AST2400_SPI_BASE); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 1, AST2400_SPI_FLASH_BASE); } static void ast2400_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 1cd749aa4b..1a53e51cf1 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -249,16 +249,16 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) } /* initialize 2 x 16 KB ROM */ - memory_region_init_rom_device(&s->rom[0], NULL, NULL, NULL, - "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err); + memory_region_init_rom(&s->rom[0], NULL, + "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err); if (err) { error_propagate(errp, err); return; } memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM0_ADDR, &s->rom[0]); - memory_region_init_rom_device(&s->rom[1], NULL, NULL, NULL, - "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err); + memory_region_init_rom(&s->rom[1], NULL, + "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err); if (err) { error_propagate(errp, err); return; diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 31a3a87911..b283b71eb4 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -219,9 +219,8 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) } /* On a real system, the first 16k is a `secure boot rom' */ - memory_region_init_rom_device(&s->secure_rom, NULL, NULL, NULL, - "imx31.secure_rom", - FSL_IMX31_SECURE_ROM_SIZE, &err); + memory_region_init_rom(&s->secure_rom, NULL, "imx31.secure_rom", + FSL_IMX31_SECURE_ROM_SIZE, &err); if (err) { error_propagate(errp, err); return; @@ -230,8 +229,8 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) &s->secure_rom); /* There is also a 16k ROM */ - memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx31.rom", - FSL_IMX31_ROM_SIZE, &err); + memory_region_init_rom(&s->rom, NULL, "imx31.rom", + FSL_IMX31_ROM_SIZE, &err); if (err) { error_propagate(errp, err); return; diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 0c00e7a560..ed392a9e7f 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -399,8 +399,8 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) FSL_IMX6_ENET_MAC_1588_IRQ)); /* ROM memory */ - memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx6.rom", - FSL_IMX6_ROM_SIZE, &err); + memory_region_init_rom(&s->rom, NULL, "imx6.rom", + FSL_IMX6_ROM_SIZE, &err); if (err) { error_propagate(errp, err); return; @@ -409,8 +409,8 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) &s->rom); /* CAAM memory */ - memory_region_init_rom_device(&s->caam, NULL, NULL, NULL, "imx6.caam", - FSL_IMX6_CAAM_MEM_SIZE, &err); + memory_region_init_rom(&s->caam, NULL, "imx6.caam", + FSL_IMX6_CAAM_MEM_SIZE, &err); if (err) { error_propagate(errp, err); return; diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c index b8eed21348..54e29a865d 100644 --- a/hw/arm/palmetto-bmc.c +++ b/hw/arm/palmetto-bmc.c @@ -18,6 +18,8 @@ #include "hw/arm/ast2400.h" #include "hw/boards.h" #include "qemu/log.h" +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" static struct arm_boot_info palmetto_bmc_binfo = { .loader_start = AST2400_SDRAM_BASE, @@ -30,6 +32,32 @@ typedef struct PalmettoBMCState { MemoryRegion ram; } PalmettoBMCState; +static void palmetto_bmc_init_flashes(AspeedSMCState *s, const char *flashtype, + Error **errp) +{ + int i ; + + for (i = 0; i < s->num_cs; ++i) { + AspeedSMCFlash *fl = &s->flashes[i]; + DriveInfo *dinfo = drive_get_next(IF_MTD); + qemu_irq cs_line; + + /* + * FIXME: check that we are not using a flash module exceeding + * the controller segment size + */ + fl->flash = ssi_create_slave_no_init(s->spi, flashtype); + if (dinfo) { + qdev_prop_set_drive(fl->flash, "drive", blk_by_legacy_dinfo(dinfo), + errp); + } + qdev_init_nofail(fl->flash); + + cs_line = qdev_get_gpio_in_named(fl->flash, SSI_GPIO_CS, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(s), i + 1, cs_line); + } +} + static void palmetto_bmc_init(MachineState *machine) { PalmettoBMCState *bmc; @@ -49,6 +77,9 @@ static void palmetto_bmc_init(MachineState *machine) object_property_set_bool(OBJECT(&bmc->soc), true, "realized", &error_abort); + palmetto_bmc_init_flashes(&bmc->soc.smc, "n25q256a", &error_abort); + palmetto_bmc_init_flashes(&bmc->soc.spi, "mx25l25635e", &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; diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index 776c51e398..4e7ac8cc4f 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -86,13 +86,19 @@ static void sabrelite_init(MachineState *machine) spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(spi_dev), "spi"); if (spi_bus) { DeviceState *flash_dev; - - flash_dev = ssi_create_slave(spi_bus, "sst25vf016b"); - if (flash_dev) { - qemu_irq cs_line = qdev_get_gpio_in_named(flash_dev, - SSI_GPIO_CS, 0); - sysbus_connect_irq(SYS_BUS_DEVICE(spi_dev), 1, cs_line); + qemu_irq cs_line; + DriveInfo *dinfo = drive_get_next(IF_MTD); + + flash_dev = ssi_create_slave_no_init(spi_bus, "sst25vf016b"); + if (dinfo) { + qdev_prop_set_drive(flash_dev, "drive", + blk_by_legacy_dinfo(dinfo), + &error_fatal); } + qdev_init_nofail(flash_dev); + + cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(spi_dev), 1, cs_line); } } } diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index ba40f8302b..41cc2eeeb1 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -598,15 +598,13 @@ static uint32_t spitz_lcdtg_transfer(SSISlave *dev, uint32_t value) return 0; } -static int spitz_lcdtg_init(SSISlave *dev) +static void spitz_lcdtg_realize(SSISlave *dev, Error **errp) { SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev); spitz_lcdtg = s; s->bl_power = 0; s->bl_intensity = 0x20; - - return 0; } /* SSP devices */ @@ -666,7 +664,7 @@ static void spitz_adc_temp_on(void *opaque, int line, int level) max111x_set_input(max1111, MAX1111_BATT_TEMP, 0); } -static int corgi_ssp_init(SSISlave *d) +static void corgi_ssp_realize(SSISlave *d, Error **errp) { DeviceState *dev = DEVICE(d); CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, d); @@ -675,8 +673,6 @@ static int corgi_ssp_init(SSISlave *d) s->bus[0] = ssi_create_bus(dev, "ssi0"); s->bus[1] = ssi_create_bus(dev, "ssi1"); s->bus[2] = ssi_create_bus(dev, "ssi2"); - - return 0; } static void spitz_ssp_attach(PXA2xxState *cpu) @@ -1121,7 +1117,7 @@ static void corgi_ssp_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = corgi_ssp_init; + k->realize = corgi_ssp_realize; k->transfer = corgi_ssp_transfer; dc->vmsd = &vmstate_corgi_ssp_regs; } @@ -1150,7 +1146,7 @@ static void spitz_lcdtg_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = spitz_lcdtg_init; + k->realize = spitz_lcdtg_realize; k->transfer = spitz_lcdtg_transfer; dc->vmsd = &vmstate_spitz_lcdtg_regs; } diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index 4e9494f94c..2db66508b5 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -127,10 +127,9 @@ static uint32_t tosa_ssp_tansfer(SSISlave *dev, uint32_t value) return 0; } -static int tosa_ssp_init(SSISlave *dev) +static void tosa_ssp_realize(SSISlave *dev, Error **errp) { /* Nothing to do. */ - return 0; } #define TYPE_TOSA_DAC "tosa_dac" @@ -283,7 +282,7 @@ static void tosa_ssp_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = tosa_ssp_init; + k->realize = tosa_ssp_realize; k->transfer = tosa_ssp_tansfer; } diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c5c125e920..6e098afd1f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1021,6 +1021,7 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic, qemu_fdt_setprop_cell(vbi->fdt, nodename, "#size-cells", 2); qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0, nr_pcie_buses - 1); + qemu_fdt_setprop(vbi->fdt, nodename, "dma-coherent", NULL, 0); if (vbi->v2m_phandle) { qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index aefebcfa6d..7dac20d67d 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -138,7 +138,13 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq, spi = (SSIBus *)qdev_get_child_bus(dev, bus_name); for (j = 0; j < num_ss; ++j) { - flash_dev = ssi_create_slave(spi, "n25q128"); + DriveInfo *dinfo = drive_get_next(IF_MTD); + flash_dev = ssi_create_slave_no_init(spi, "n25q128"); + if (dinfo) { + qdev_prop_set_drive(flash_dev, "drive", + blk_by_legacy_dinfo(dinfo), &error_fatal); + } + qdev_init_nofail(flash_dev); cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); sysbus_connect_irq(busdev, i * num_ss + j + 1, cs_line); @@ -294,6 +300,12 @@ static void zynq_init(MachineState *machine) sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - IRQ_OFFSET]); } + dev = qdev_create(NULL, "xlnx.ps7-dev-cfg"); + qdev_init_nofail(dev); + busdev = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(busdev, 0, pic[40 - IRQ_OFFSET]); + sysbus_mmio_map(busdev, 0, 0xF8007000); + zynq_binfo.ram_size = ram_size; zynq_binfo.kernel_filename = kernel_filename; zynq_binfo.kernel_cmdline = kernel_cmdline; diff --git a/hw/arm/xlnx-ep108.c b/hw/arm/xlnx-ep108.c index 34b4641712..4ec590a25d 100644 --- a/hw/arm/xlnx-ep108.c +++ b/hw/arm/xlnx-ep108.c @@ -88,12 +88,19 @@ static void xlnx_ep108_init(MachineState *machine) SSIBus *spi_bus; DeviceState *flash_dev; qemu_irq cs_line; + DriveInfo *dinfo = drive_get_next(IF_MTD); gchar *bus_name = g_strdup_printf("spi%d", i); spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), bus_name); g_free(bus_name); - flash_dev = ssi_create_slave(spi_bus, "sst25wf080"); + flash_dev = ssi_create_slave_no_init(spi_bus, "sst25wf080"); + if (dinfo) { + qdev_prop_set_drive(flash_dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); + } + qdev_init_nofail(flash_dev); + cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi[i]), 1, cs_line); diff --git a/hw/arm/z2.c b/hw/arm/z2.c index aea895a500..68a92f3184 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -151,14 +151,12 @@ static void z2_lcd_cs(void *opaque, int line, int level) z2_lcd->selected = !level; } -static int zipit_lcd_init(SSISlave *dev) +static void zipit_lcd_realize(SSISlave *dev, Error **errp) { ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev); z->selected = 0; z->enabled = 0; z->pos = 0; - - return 0; } static VMStateDescription vmstate_zipit_lcd_state = { @@ -181,7 +179,7 @@ static void zipit_lcd_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = zipit_lcd_init; + k->realize = zipit_lcd_realize; k->transfer = zipit_lcd_transfer; dc->vmsd = &vmstate_zipit_lcd_state; } diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 326b688e83..d9b27939dd 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -28,6 +28,7 @@ #include "hw/ssi/ssi.h" #include "qemu/bitops.h" #include "qemu/log.h" +#include "qapi/error.h" #ifndef M25P80_ERR_DEBUG #define M25P80_ERR_DEBUG 0 @@ -389,7 +390,7 @@ typedef struct Flash { uint32_t pos; uint8_t needed_bytes; uint8_t cmd_in_progress; - uint64_t cur_addr; + uint32_t cur_addr; uint32_t nonvolatile_cfg; /* Configuration register for Macronix */ uint32_t volatile_cfg; @@ -446,6 +447,11 @@ static inline Manufacturer get_man(Flash *s) static void blk_sync_complete(void *opaque, int ret) { + QEMUIOVector *iov = opaque; + + qemu_iovec_destroy(iov); + g_free(iov); + /* do nothing. Masters do not directly interact with the backing store, * only the working copy so no mutexing required. */ @@ -453,31 +459,31 @@ static void blk_sync_complete(void *opaque, int ret) static void flash_sync_page(Flash *s, int page) { - QEMUIOVector iov; + QEMUIOVector *iov = g_new(QEMUIOVector, 1); if (!s->blk || blk_is_read_only(s->blk)) { return; } - qemu_iovec_init(&iov, 1); - qemu_iovec_add(&iov, s->storage + page * s->pi->page_size, + qemu_iovec_init(iov, 1); + qemu_iovec_add(iov, s->storage + page * s->pi->page_size, s->pi->page_size); - blk_aio_pwritev(s->blk, page * s->pi->page_size, &iov, 0, - blk_sync_complete, NULL); + blk_aio_pwritev(s->blk, page * s->pi->page_size, iov, 0, + blk_sync_complete, iov); } static inline void flash_sync_area(Flash *s, int64_t off, int64_t len) { - QEMUIOVector iov; + QEMUIOVector *iov = g_new(QEMUIOVector, 1); if (!s->blk || blk_is_read_only(s->blk)) { return; } assert(!(len % BDRV_SECTOR_SIZE)); - qemu_iovec_init(&iov, 1); - qemu_iovec_add(&iov, s->storage + off, len); - blk_aio_pwritev(s->blk, off, &iov, 0, blk_sync_complete, NULL); + qemu_iovec_init(iov, 1); + qemu_iovec_add(iov, s->storage + off, len); + blk_aio_pwritev(s->blk, off, iov, 0, blk_sync_complete, iov); } static void flash_erase(Flash *s, int offset, FlashCMD cmd) @@ -530,9 +536,9 @@ static inline void flash_sync_dirty(Flash *s, int64_t newpage) } static inline -void flash_write8(Flash *s, uint64_t addr, uint8_t data) +void flash_write8(Flash *s, uint32_t addr, uint8_t data) { - int64_t page = addr / s->pi->page_size; + uint32_t page = addr / s->pi->page_size; uint8_t prev = s->storage[s->cur_addr]; if (!s->write_enable) { @@ -540,7 +546,7 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data) } if ((prev ^ data) & data) { - DB_PRINT_L(1, "programming zero to one! addr=%" PRIx64 " %" PRIx8 + DB_PRINT_L(1, "programming zero to one! addr=%" PRIx32 " %" PRIx8 " -> %" PRIx8 "\n", addr, prev, data); } @@ -581,18 +587,16 @@ static inline int get_addr_length(Flash *s) static void complete_collecting_data(Flash *s) { - int i; - - s->cur_addr = 0; + int i, n; - for (i = 0; i < get_addr_length(s); ++i) { + n = get_addr_length(s); + s->cur_addr = (n == 3 ? s->ear : 0); + for (i = 0; i < n; ++i) { s->cur_addr <<= 8; s->cur_addr |= s->data[i]; } - if (get_addr_length(s) == 3) { - s->cur_addr += s->ear * MAX_3BYTES_SIZE; - } + s->cur_addr &= s->size - 1; s->state = STATE_IDLE; @@ -1091,17 +1095,17 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) switch (s->state) { case STATE_PAGE_PROGRAM: - DB_PRINT_L(1, "page program cur_addr=%#" PRIx64 " data=%" PRIx8 "\n", + DB_PRINT_L(1, "page program cur_addr=%#" PRIx32 " data=%" PRIx8 "\n", s->cur_addr, (uint8_t)tx); flash_write8(s, s->cur_addr, (uint8_t)tx); - s->cur_addr++; + s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; case STATE_READ: r = s->storage[s->cur_addr]; - DB_PRINT_L(1, "READ 0x%" PRIx64 "=%" PRIx8 "\n", s->cur_addr, + DB_PRINT_L(1, "READ 0x%" PRIx32 "=%" PRIx8 "\n", s->cur_addr, (uint8_t)r); - s->cur_addr = (s->cur_addr + 1) % s->size; + s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; case STATE_COLLECTING_DATA: @@ -1132,9 +1136,8 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) return r; } -static int m25p80_init(SSISlave *ss) +static void m25p80_realize(SSISlave *ss, Error **errp) { - DriveInfo *dinfo; Flash *s = M25P80(ss); M25P80Class *mc = M25P80_GET_CLASS(s); @@ -1143,28 +1146,19 @@ static int m25p80_init(SSISlave *ss) s->size = s->pi->sector_size * s->pi->n_sectors; s->dirty_page = -1; - /* FIXME use a qdev drive property instead of drive_get_next() */ - dinfo = drive_get_next(IF_MTD); - - if (dinfo) { + if (s->blk) { DB_PRINT_L(0, "Binding to IF_MTD drive\n"); - s->blk = blk_by_legacy_dinfo(dinfo); - blk_attach_dev_nofail(s->blk, s); - s->storage = blk_blockalign(s->blk, s->size); - /* FIXME: Move to late init */ if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { - fprintf(stderr, "Failed to initialize SPI flash!\n"); - return 1; + error_setg(errp, "failed to read the initial flash content"); + return; } } else { DB_PRINT_L(0, "No BDRV - binding to RAM\n"); s->storage = blk_blockalign(NULL, s->size); memset(s->storage, 0xFF, s->size); } - - return 0; } static void m25p80_reset(DeviceState *d) @@ -1186,6 +1180,7 @@ static Property m25p80_properties[] = { DEFINE_PROP_UINT8("spansion-cr2nv", Flash, spansion_cr2nv, 0x8), DEFINE_PROP_UINT8("spansion-cr3nv", Flash, spansion_cr3nv, 0x2), DEFINE_PROP_UINT8("spansion-cr4nv", Flash, spansion_cr4nv, 0x10), + DEFINE_PROP_DRIVE("drive", Flash, blk), DEFINE_PROP_END_OF_LIST(), }; @@ -1201,7 +1196,8 @@ static const VMStateDescription vmstate_m25p80 = { VMSTATE_UINT32(pos, Flash), VMSTATE_UINT8(needed_bytes, Flash), VMSTATE_UINT8(cmd_in_progress, Flash), - VMSTATE_UINT64(cur_addr, Flash), + VMSTATE_UNUSED(4), + VMSTATE_UINT32(cur_addr, Flash), VMSTATE_BOOL(write_enable, Flash), VMSTATE_BOOL_V(reset_enable, Flash, 2), VMSTATE_UINT8_V(ear, Flash, 2), @@ -1224,7 +1220,7 @@ static void m25p80_class_init(ObjectClass *klass, void *data) SSISlaveClass *k = SSI_SLAVE_CLASS(klass); M25P80Class *mc = M25P80_CLASS(klass); - k->init = m25p80_init; + k->realize = m25p80_realize; k->transfer = m25p80_transfer8; k->set_cs = m25p80_cs; k->cs_polarity = SSI_CS_LOW; diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index 82a9ef84f8..cfd4840397 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -15,4 +15,5 @@ common-obj-$(CONFIG_SOFTMMU) += machine.o common-obj-$(CONFIG_SOFTMMU) += null-machine.o common-obj-$(CONFIG_SOFTMMU) += loader.o common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o +common-obj-$(CONFIG_SOFTMMU) += register.o common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o diff --git a/hw/core/register.c b/hw/core/register.c new file mode 100644 index 0000000000..4bfbc508de --- /dev/null +++ b/hw/core/register.c @@ -0,0 +1,287 @@ +/* + * Register Definition API + * + * Copyright (c) 2016 Xilinx Inc. + * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "hw/register.h" +#include "hw/qdev.h" +#include "qemu/log.h" + +static inline void register_write_val(RegisterInfo *reg, uint64_t val) +{ + g_assert(reg->data); + + switch (reg->data_size) { + case 1: + *(uint8_t *)reg->data = val; + break; + case 2: + *(uint16_t *)reg->data = val; + break; + case 4: + *(uint32_t *)reg->data = val; + break; + case 8: + *(uint64_t *)reg->data = val; + break; + default: + g_assert_not_reached(); + } +} + +static inline uint64_t register_read_val(RegisterInfo *reg) +{ + switch (reg->data_size) { + case 1: + return *(uint8_t *)reg->data; + case 2: + return *(uint16_t *)reg->data; + case 4: + return *(uint32_t *)reg->data; + case 8: + return *(uint64_t *)reg->data; + default: + g_assert_not_reached(); + } + return 0; /* unreachable */ +} + +void register_write(RegisterInfo *reg, uint64_t val, uint64_t we, + const char *prefix, bool debug) +{ + uint64_t old_val, new_val, test, no_w_mask; + const RegisterAccessInfo *ac; + + assert(reg); + + ac = reg->access; + + if (!ac || !ac->name) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: write to undefined device state " + "(written value: %#" PRIx64 ")\n", prefix, val); + return; + } + + old_val = reg->data ? register_read_val(reg) : ac->reset; + + test = (old_val ^ val) & ac->rsvd; + if (test) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: change of value in reserved bit" + "fields: %#" PRIx64 ")\n", prefix, test); + } + + test = val & ac->unimp; + if (test) { + qemu_log_mask(LOG_UNIMP, + "%s:%s writing %#" PRIx64 " to unimplemented bits:" \ + " %#" PRIx64 "", + prefix, reg->access->name, val, ac->unimp); + } + + /* Create the no write mask based on the read only, write to clear and + * reserved bit masks. + */ + no_w_mask = ac->ro | ac->w1c | ac->rsvd | ~we; + new_val = (val & ~no_w_mask) | (old_val & no_w_mask); + new_val &= ~(val & ac->w1c); + + if (ac->pre_write) { + new_val = ac->pre_write(reg, new_val); + } + + if (debug) { + qemu_log("%s:%s: write of value %#" PRIx64 "\n", prefix, ac->name, + new_val); + } + + register_write_val(reg, new_val); + + if (ac->post_write) { + ac->post_write(reg, new_val); + } +} + +uint64_t register_read(RegisterInfo *reg, uint64_t re, const char* prefix, + bool debug) +{ + uint64_t ret; + const RegisterAccessInfo *ac; + + assert(reg); + + ac = reg->access; + if (!ac || !ac->name) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: read from undefined device state\n", + prefix); + return 0; + } + + ret = reg->data ? register_read_val(reg) : ac->reset; + + register_write_val(reg, ret & ~(ac->cor & re)); + + /* Mask based on the read enable size */ + ret &= re; + + if (ac->post_read) { + ret = ac->post_read(reg, ret); + } + + if (debug) { + qemu_log("%s:%s: read of value %#" PRIx64 "\n", prefix, + ac->name, ret); + } + + return ret; +} + +void register_reset(RegisterInfo *reg) +{ + g_assert(reg); + + if (!reg->data || !reg->access) { + return; + } + + register_write_val(reg, reg->access->reset); +} + +void register_init(RegisterInfo *reg) +{ + assert(reg); + + if (!reg->data || !reg->access) { + return; + } + + object_initialize((void *)reg, sizeof(*reg), TYPE_REGISTER); +} + +void register_write_memory(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + RegisterInfoArray *reg_array = opaque; + RegisterInfo *reg = NULL; + uint64_t we; + int i; + + for (i = 0; i < reg_array->num_elements; i++) { + if (reg_array->r[i]->access->addr == addr) { + reg = reg_array->r[i]; + break; + } + } + + if (!reg) { + qemu_log_mask(LOG_GUEST_ERROR, "Write to unimplemented register at " \ + "address: %#" PRIx64 "\n", addr); + return; + } + + /* Generate appropriate write enable mask */ + if (reg->data_size < size) { + we = MAKE_64BIT_MASK(0, reg->data_size * 8); + } else { + we = MAKE_64BIT_MASK(0, size * 8); + } + + register_write(reg, value, we, reg_array->prefix, + reg_array->debug); +} + +uint64_t register_read_memory(void *opaque, hwaddr addr, + unsigned size) +{ + RegisterInfoArray *reg_array = opaque; + RegisterInfo *reg = NULL; + uint64_t read_val; + int i; + + for (i = 0; i < reg_array->num_elements; i++) { + if (reg_array->r[i]->access->addr == addr) { + reg = reg_array->r[i]; + break; + } + } + + if (!reg) { + qemu_log_mask(LOG_GUEST_ERROR, "Read to unimplemented register at " \ + "address: %#" PRIx64 "\n", addr); + return 0; + } + + read_val = register_read(reg, size * 8, reg_array->prefix, + reg_array->debug); + + return extract64(read_val, 0, size * 8); +} + +RegisterInfoArray *register_init_block32(DeviceState *owner, + const RegisterAccessInfo *rae, + int num, RegisterInfo *ri, + uint32_t *data, + const MemoryRegionOps *ops, + bool debug_enabled, + uint64_t memory_size) +{ + const char *device_prefix = object_get_typename(OBJECT(owner)); + RegisterInfoArray *r_array = g_new0(RegisterInfoArray, 1); + int i; + + r_array->r = g_new0(RegisterInfo *, num); + r_array->num_elements = num; + r_array->debug = debug_enabled; + r_array->prefix = device_prefix; + + for (i = 0; i < num; i++) { + int index = rae[i].addr / 4; + RegisterInfo *r = &ri[index]; + + *r = (RegisterInfo) { + .data = &data[index], + .data_size = sizeof(uint32_t), + .access = &rae[i], + .opaque = owner, + }; + register_init(r); + + r_array->r[i] = r; + } + + memory_region_init_io(&r_array->mem, OBJECT(owner), ops, r_array, + device_prefix, memory_size); + + return r_array; +} + +void register_finalize_block(RegisterInfoArray *r_array) +{ + object_unparent(OBJECT(&r_array->mem)); + g_free(r_array->r); + g_free(r_array); +} + +static const TypeInfo register_info = { + .name = TYPE_REGISTER, + .parent = TYPE_DEVICE, +}; + +static void register_register_types(void) +{ + type_register_static(®ister_info); +} + +type_init(register_register_types) diff --git a/hw/display/ads7846.c b/hw/display/ads7846.c index 05aa2d1e6b..166edade7d 100644 --- a/hw/display/ads7846.c +++ b/hw/display/ads7846.c @@ -133,7 +133,7 @@ static const VMStateDescription vmstate_ads7846 = { } }; -static int ads7846_init(SSISlave *d) +static void ads7846_realize(SSISlave *d, Error **errp) { DeviceState *dev = DEVICE(d); ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, d); @@ -152,14 +152,13 @@ static int ads7846_init(SSISlave *d) ads7846_int_update(s); vmstate_register(NULL, -1, &vmstate_ads7846, s); - return 0; } static void ads7846_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = ads7846_init; + k->realize = ads7846_realize; k->transfer = ads7846_transfer; } diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c index 14c1bf339c..6d1faf44af 100644 --- a/hw/display/ssd0323.c +++ b/hw/display/ssd0323.c @@ -361,7 +361,7 @@ static const GraphicHwOps ssd0323_ops = { .gfx_update = ssd0323_update_display, }; -static int ssd0323_init(SSISlave *d) +static void ssd0323_realize(SSISlave *d, Error **errp) { DeviceState *dev = DEVICE(d); ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, d); @@ -375,14 +375,13 @@ static int ssd0323_init(SSISlave *d) register_savevm(dev, "ssd0323_oled", -1, 1, ssd0323_save, ssd0323_load, s); - return 0; } static void ssd0323_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = ssd0323_init; + k->realize = ssd0323_realize; k->transfer = ssd0323_transfer; k->cs_polarity = SSI_CS_HIGH; } diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs index 8b0823e593..087c8e6855 100644 --- a/hw/dma/Makefile.objs +++ b/hw/dma/Makefile.objs @@ -5,6 +5,7 @@ common-obj-$(CONFIG_PL330) += pl330.o common-obj-$(CONFIG_I82374) += i82374.o common-obj-$(CONFIG_I8257) += i8257.o common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o +common-obj-$(CONFIG_ZYNQ_DEVCFG) += xlnx-zynq-devcfg.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o common-obj-$(CONFIG_STP2000) += sparc32_dma.o common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o diff --git a/hw/dma/xlnx-zynq-devcfg.c b/hw/dma/xlnx-zynq-devcfg.c new file mode 100644 index 0000000000..3b10523430 --- /dev/null +++ b/hw/dma/xlnx-zynq-devcfg.c @@ -0,0 +1,400 @@ +/* + * QEMU model of the Xilinx Zynq Devcfg Interface + * + * (C) 2011 PetaLogix Pty Ltd + * (C) 2014 Xilinx Inc. + * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/dma/xlnx-zynq-devcfg.h" +#include "qemu/bitops.h" +#include "sysemu/sysemu.h" +#include "sysemu/dma.h" +#include "qemu/log.h" + +#define FREQ_HZ 900000000 + +#define BTT_MAX 0x400 + +#ifndef XLNX_ZYNQ_DEVCFG_ERR_DEBUG +#define XLNX_ZYNQ_DEVCFG_ERR_DEBUG 0 +#endif + +#define DB_PRINT(fmt, args...) do { \ + if (XLNX_ZYNQ_DEVCFG_ERR_DEBUG) { \ + qemu_log("%s: " fmt, __func__, ## args); \ + } \ +} while (0); + +REG32(CTRL, 0x00) + FIELD(CTRL, FORCE_RST, 31, 1) /* Not supported, wr ignored */ + FIELD(CTRL, PCAP_PR, 27, 1) /* Forced to 0 on bad unlock */ + FIELD(CTRL, PCAP_MODE, 26, 1) + FIELD(CTRL, MULTIBOOT_EN, 24, 1) + FIELD(CTRL, USER_MODE, 15, 1) + FIELD(CTRL, PCFG_AES_FUSE, 12, 1) + FIELD(CTRL, PCFG_AES_EN, 9, 3) + FIELD(CTRL, SEU_EN, 8, 1) + FIELD(CTRL, SEC_EN, 7, 1) + FIELD(CTRL, SPNIDEN, 6, 1) + FIELD(CTRL, SPIDEN, 5, 1) + FIELD(CTRL, NIDEN, 4, 1) + FIELD(CTRL, DBGEN, 3, 1) + FIELD(CTRL, DAP_EN, 0, 3) + +REG32(LOCK, 0x04) +#define AES_FUSE_LOCK 4 +#define AES_EN_LOCK 3 +#define SEU_LOCK 2 +#define SEC_LOCK 1 +#define DBG_LOCK 0 + +/* mapping bits in R_LOCK to what they lock in R_CTRL */ +static const uint32_t lock_ctrl_map[] = { + [AES_FUSE_LOCK] = R_CTRL_PCFG_AES_FUSE_MASK, + [AES_EN_LOCK] = R_CTRL_PCFG_AES_EN_MASK, + [SEU_LOCK] = R_CTRL_SEU_EN_MASK, + [SEC_LOCK] = R_CTRL_SEC_EN_MASK, + [DBG_LOCK] = R_CTRL_SPNIDEN_MASK | R_CTRL_SPIDEN_MASK | + R_CTRL_NIDEN_MASK | R_CTRL_DBGEN_MASK | + R_CTRL_DAP_EN_MASK, +}; + +REG32(CFG, 0x08) + FIELD(CFG, RFIFO_TH, 10, 2) + FIELD(CFG, WFIFO_TH, 8, 2) + FIELD(CFG, RCLK_EDGE, 7, 1) + FIELD(CFG, WCLK_EDGE, 6, 1) + FIELD(CFG, DISABLE_SRC_INC, 5, 1) + FIELD(CFG, DISABLE_DST_INC, 4, 1) +#define R_CFG_RESET 0x50B + +REG32(INT_STS, 0x0C) + FIELD(INT_STS, PSS_GTS_USR_B, 31, 1) + FIELD(INT_STS, PSS_FST_CFG_B, 30, 1) + FIELD(INT_STS, PSS_CFG_RESET_B, 27, 1) + FIELD(INT_STS, RX_FIFO_OV, 18, 1) + FIELD(INT_STS, WR_FIFO_LVL, 17, 1) + FIELD(INT_STS, RD_FIFO_LVL, 16, 1) + FIELD(INT_STS, DMA_CMD_ERR, 15, 1) + FIELD(INT_STS, DMA_Q_OV, 14, 1) + FIELD(INT_STS, DMA_DONE, 13, 1) + FIELD(INT_STS, DMA_P_DONE, 12, 1) + FIELD(INT_STS, P2D_LEN_ERR, 11, 1) + FIELD(INT_STS, PCFG_DONE, 2, 1) +#define R_INT_STS_RSVD ((0x7 << 24) | (0x1 << 19) | (0xF < 7)) + +REG32(INT_MASK, 0x10) + +REG32(STATUS, 0x14) + FIELD(STATUS, DMA_CMD_Q_F, 31, 1) + FIELD(STATUS, DMA_CMD_Q_E, 30, 1) + FIELD(STATUS, DMA_DONE_CNT, 28, 2) + FIELD(STATUS, RX_FIFO_LVL, 20, 5) + FIELD(STATUS, TX_FIFO_LVL, 12, 7) + FIELD(STATUS, PSS_GTS_USR_B, 11, 1) + FIELD(STATUS, PSS_FST_CFG_B, 10, 1) + FIELD(STATUS, PSS_CFG_RESET_B, 5, 1) + +REG32(DMA_SRC_ADDR, 0x18) +REG32(DMA_DST_ADDR, 0x1C) +REG32(DMA_SRC_LEN, 0x20) +REG32(DMA_DST_LEN, 0x24) +REG32(ROM_SHADOW, 0x28) +REG32(SW_ID, 0x30) +REG32(UNLOCK, 0x34) + +#define R_UNLOCK_MAGIC 0x757BDF0D + +REG32(MCTRL, 0x80) + FIELD(MCTRL, PS_VERSION, 28, 4) + FIELD(MCTRL, PCFG_POR_B, 8, 1) + FIELD(MCTRL, INT_PCAP_LPBK, 4, 1) + FIELD(MCTRL, QEMU, 3, 1) + +static void xlnx_zynq_devcfg_update_ixr(XlnxZynqDevcfg *s) +{ + qemu_set_irq(s->irq, ~s->regs[R_INT_MASK] & s->regs[R_INT_STS]); +} + +static void xlnx_zynq_devcfg_reset(DeviceState *dev) +{ + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(dev); + int i; + + for (i = 0; i < XLNX_ZYNQ_DEVCFG_R_MAX; ++i) { + register_reset(&s->regs_info[i]); + } +} + +static void xlnx_zynq_devcfg_dma_go(XlnxZynqDevcfg *s) +{ + do { + uint8_t buf[BTT_MAX]; + XlnxZynqDevcfgDMACmd *dmah = s->dma_cmd_fifo; + uint32_t btt = BTT_MAX; + bool loopback = s->regs[R_MCTRL] & R_MCTRL_INT_PCAP_LPBK_MASK; + + btt = MIN(btt, dmah->src_len); + if (loopback) { + btt = MIN(btt, dmah->dest_len); + } + DB_PRINT("reading %x bytes from %x\n", btt, dmah->src_addr); + dma_memory_read(&address_space_memory, dmah->src_addr, buf, btt); + dmah->src_len -= btt; + dmah->src_addr += btt; + if (loopback && (dmah->src_len || dmah->dest_len)) { + DB_PRINT("writing %x bytes from %x\n", btt, dmah->dest_addr); + dma_memory_write(&address_space_memory, dmah->dest_addr, buf, btt); + dmah->dest_len -= btt; + dmah->dest_addr += btt; + } + if (!dmah->src_len && !dmah->dest_len) { + DB_PRINT("dma operation finished\n"); + s->regs[R_INT_STS] |= R_INT_STS_DMA_DONE_MASK | + R_INT_STS_DMA_P_DONE_MASK; + s->dma_cmd_fifo_num--; + memmove(s->dma_cmd_fifo, &s->dma_cmd_fifo[1], + sizeof(s->dma_cmd_fifo) - sizeof(s->dma_cmd_fifo[0])); + } + xlnx_zynq_devcfg_update_ixr(s); + } while (s->dma_cmd_fifo_num); +} + +static void r_ixr_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque); + + xlnx_zynq_devcfg_update_ixr(s); +} + +static uint64_t r_ctrl_pre_write(RegisterInfo *reg, uint64_t val) +{ + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque); + int i; + + for (i = 0; i < ARRAY_SIZE(lock_ctrl_map); ++i) { + if (s->regs[R_LOCK] & 1 << i) { + val &= ~lock_ctrl_map[i]; + val |= lock_ctrl_map[i] & s->regs[R_CTRL]; + } + } + return val; +} + +static void r_ctrl_post_write(RegisterInfo *reg, uint64_t val) +{ + const char *device_prefix = object_get_typename(OBJECT(reg->opaque)); + uint32_t aes_en = FIELD_EX32(val, CTRL, PCFG_AES_EN); + + if (aes_en != 0 && aes_en != 7) { + qemu_log_mask(LOG_UNIMP, "%s: warning, aes-en bits inconsistent," + "unimplemented security reset should happen!\n", + device_prefix); + } +} + +static void r_unlock_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque); + const char *device_prefix = object_get_typename(OBJECT(s)); + + if (val == R_UNLOCK_MAGIC) { + DB_PRINT("successful unlock\n"); + s->regs[R_CTRL] |= R_CTRL_PCAP_PR_MASK; + s->regs[R_CTRL] |= R_CTRL_PCFG_AES_EN_MASK; + memory_region_set_enabled(&s->iomem, true); + } else { /* bad unlock attempt */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed unlock\n", device_prefix); + s->regs[R_CTRL] &= ~R_CTRL_PCAP_PR_MASK; + s->regs[R_CTRL] &= ~R_CTRL_PCFG_AES_EN_MASK; + /* core becomes inaccessible */ + memory_region_set_enabled(&s->iomem, false); + } +} + +static uint64_t r_lock_pre_write(RegisterInfo *reg, uint64_t val) +{ + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque); + + /* once bits are locked they stay locked */ + return s->regs[R_LOCK] | val; +} + +static void r_dma_dst_len_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque); + + s->dma_cmd_fifo[s->dma_cmd_fifo_num] = (XlnxZynqDevcfgDMACmd) { + .src_addr = s->regs[R_DMA_SRC_ADDR] & ~0x3UL, + .dest_addr = s->regs[R_DMA_DST_ADDR] & ~0x3UL, + .src_len = s->regs[R_DMA_SRC_LEN] << 2, + .dest_len = s->regs[R_DMA_DST_LEN] << 2, + }; + s->dma_cmd_fifo_num++; + DB_PRINT("dma transfer started; %d total transfers pending\n", + s->dma_cmd_fifo_num); + xlnx_zynq_devcfg_dma_go(s); +} + +static const RegisterAccessInfo xlnx_zynq_devcfg_regs_info[] = { + { .name = "CTRL", .addr = A_CTRL, + .reset = R_CTRL_PCAP_PR_MASK | R_CTRL_PCAP_MODE_MASK | 0x3 << 13, + .rsvd = 0x1 << 28 | 0x3ff << 13 | 0x3 << 13, + .pre_write = r_ctrl_pre_write, + .post_write = r_ctrl_post_write, + }, + { .name = "LOCK", .addr = A_LOCK, + .rsvd = MAKE_64BIT_MASK(5, 64 - 5), + .pre_write = r_lock_pre_write, + }, + { .name = "CFG", .addr = A_CFG, + .reset = R_CFG_RESET, + .rsvd = 0xfffff00f, + }, + { .name = "INT_STS", .addr = A_INT_STS, + .w1c = ~R_INT_STS_RSVD, + .reset = R_INT_STS_PSS_GTS_USR_B_MASK | + R_INT_STS_PSS_CFG_RESET_B_MASK | + R_INT_STS_WR_FIFO_LVL_MASK, + .rsvd = R_INT_STS_RSVD, + .post_write = r_ixr_post_write, + }, + { .name = "INT_MASK", .addr = A_INT_MASK, + .reset = ~0, + .rsvd = R_INT_STS_RSVD, + .post_write = r_ixr_post_write, + }, + { .name = "STATUS", .addr = A_STATUS, + .reset = R_STATUS_DMA_CMD_Q_E_MASK | + R_STATUS_PSS_GTS_USR_B_MASK | + R_STATUS_PSS_CFG_RESET_B_MASK, + .ro = ~0, + }, + { .name = "DMA_SRC_ADDR", .addr = A_DMA_SRC_ADDR, }, + { .name = "DMA_DST_ADDR", .addr = A_DMA_DST_ADDR, }, + { .name = "DMA_SRC_LEN", .addr = A_DMA_SRC_LEN, + .ro = MAKE_64BIT_MASK(27, 64 - 27) }, + { .name = "DMA_DST_LEN", .addr = A_DMA_DST_LEN, + .ro = MAKE_64BIT_MASK(27, 64 - 27), + .post_write = r_dma_dst_len_post_write, + }, + { .name = "ROM_SHADOW", .addr = A_ROM_SHADOW, + .rsvd = ~0ull, + }, + { .name = "SW_ID", .addr = A_SW_ID, }, + { .name = "UNLOCK", .addr = A_UNLOCK, + .post_write = r_unlock_post_write, + }, + { .name = "MCTRL", .addr = R_MCTRL * 4, + /* Silicon 3.0 for version field, the mysterious reserved bit 23 + * and QEMU platform identifier. + */ + .reset = 0x2 << R_MCTRL_PS_VERSION_SHIFT | 1 << 23 | R_MCTRL_QEMU_MASK, + .ro = ~R_MCTRL_INT_PCAP_LPBK_MASK, + .rsvd = 0x00f00303, + }, +}; + +static const MemoryRegionOps xlnx_zynq_devcfg_reg_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static const VMStateDescription vmstate_xlnx_zynq_devcfg_dma_cmd = { + .name = "xlnx_zynq_devcfg_dma_cmd", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(src_addr, XlnxZynqDevcfgDMACmd), + VMSTATE_UINT32(dest_addr, XlnxZynqDevcfgDMACmd), + VMSTATE_UINT32(src_len, XlnxZynqDevcfgDMACmd), + VMSTATE_UINT32(dest_len, XlnxZynqDevcfgDMACmd), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_xlnx_zynq_devcfg = { + .name = "xlnx_zynq_devcfg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(dma_cmd_fifo, XlnxZynqDevcfg, + XLNX_ZYNQ_DEVCFG_DMA_CMD_FIFO_LEN, 0, + vmstate_xlnx_zynq_devcfg_dma_cmd, + XlnxZynqDevcfgDMACmd), + VMSTATE_UINT8(dma_cmd_fifo_num, XlnxZynqDevcfg), + VMSTATE_UINT32_ARRAY(regs, XlnxZynqDevcfg, XLNX_ZYNQ_DEVCFG_R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static void xlnx_zynq_devcfg_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(obj); + RegisterInfoArray *reg_array; + + sysbus_init_irq(sbd, &s->irq); + + memory_region_init(&s->iomem, obj, "devcfg", XLNX_ZYNQ_DEVCFG_R_MAX * 4); + reg_array = + register_init_block32(DEVICE(obj), xlnx_zynq_devcfg_regs_info, + ARRAY_SIZE(xlnx_zynq_devcfg_regs_info), + s->regs_info, s->regs, + &xlnx_zynq_devcfg_reg_ops, + XLNX_ZYNQ_DEVCFG_ERR_DEBUG, + XLNX_ZYNQ_DEVCFG_R_MAX); + memory_region_add_subregion(&s->iomem, + A_CTRL, + ®_array->mem); + + sysbus_init_mmio(sbd, &s->iomem); +} + +static void xlnx_zynq_devcfg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = xlnx_zynq_devcfg_reset; + dc->vmsd = &vmstate_xlnx_zynq_devcfg; +} + +static const TypeInfo xlnx_zynq_devcfg_info = { + .name = TYPE_XLNX_ZYNQ_DEVCFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxZynqDevcfg), + .instance_init = xlnx_zynq_devcfg_init, + .class_init = xlnx_zynq_devcfg_class_init, +}; + +static void xlnx_zynq_devcfg_register_types(void) +{ + type_register_static(&xlnx_zynq_devcfg_info); +} + +type_init(xlnx_zynq_devcfg_register_types) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 890d5d7442..06d8db6bd6 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -187,11 +187,11 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) case 0x1c: /* SysTick Calibration Value. */ return 10000; case 0xd00: /* CPUID Base. */ - cpu = ARM_CPU(current_cpu); + cpu = ARM_CPU(qemu_get_cpu(0)); return cpu->midr; case 0xd04: /* Interrupt Control State. */ /* VECTACTIVE */ - cpu = ARM_CPU(current_cpu); + cpu = ARM_CPU(qemu_get_cpu(0)); val = cpu->env.v7m.exception; if (val == 1023) { val = 0; @@ -222,7 +222,7 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) val |= (1 << 31); return val; case 0xd08: /* Vector Table Offset. */ - cpu = ARM_CPU(current_cpu); + cpu = ARM_CPU(qemu_get_cpu(0)); return cpu->env.v7m.vecbase; case 0xd0c: /* Application Interrupt/Reset Control. */ return 0xfa050000; @@ -349,7 +349,7 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) } break; case 0xd08: /* Vector Table Offset. */ - cpu = ARM_CPU(current_cpu); + cpu = ARM_CPU(qemu_get_cpu(0)); cpu->env.v7m.vecbase = value & 0xffffff80; break; case 0xd0c: /* Application Interrupt/Reset Control. */ diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 07527b677b..4968bdbb28 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -191,9 +191,16 @@ petalogix_ml605_init(MachineState *machine) spi = (SSIBus *)qdev_get_child_bus(dev, "spi"); for (i = 0; i < NUM_SPI_FLASHES; i++) { + DriveInfo *dinfo = drive_get_next(IF_MTD); qemu_irq cs_line; - dev = ssi_create_slave(spi, "n25q128"); + dev = ssi_create_slave_no_init(spi, "n25q128"); + if (dinfo) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); + } + qdev_init_nofail(dev); + cs_line = qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0); sysbus_connect_irq(busdev, i+1, cs_line); } diff --git a/hw/misc/max111x.c b/hw/misc/max111x.c index 9014f0f705..2a277bdb86 100644 --- a/hw/misc/max111x.c +++ b/hw/misc/max111x.c @@ -147,14 +147,14 @@ static int max111x_init(SSISlave *d, int inputs) return 0; } -static int max1110_init(SSISlave *dev) +static void max1110_realize(SSISlave *dev, Error **errp) { - return max111x_init(dev, 8); + max111x_init(dev, 8); } -static int max1111_init(SSISlave *dev) +static void max1111_realize(SSISlave *dev, Error **errp) { - return max111x_init(dev, 4); + max111x_init(dev, 4); } void max111x_set_input(DeviceState *dev, int line, uint8_t value) @@ -183,7 +183,7 @@ static void max1110_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = max1110_init; + k->realize = max1110_realize; } static const TypeInfo max1110_info = { @@ -196,7 +196,7 @@ static void max1111_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = max1111_init; + k->realize = max1111_realize; } static const TypeInfo max1111_info = { diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 075e4ed5df..3ff0886dd5 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -15,6 +15,7 @@ #include "sysemu/blockdev.h" #include "hw/ssi/ssi.h" #include "hw/sd/sd.h" +#include "qapi/error.h" //#define DEBUG_SSI_SD 1 @@ -249,7 +250,7 @@ static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id) return 0; } -static int ssi_sd_init(SSISlave *d) +static void ssi_sd_realize(SSISlave *d, Error **errp) { DeviceState *dev = DEVICE(d); ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, d); @@ -260,17 +261,17 @@ static int ssi_sd_init(SSISlave *d) dinfo = drive_get_next(IF_SD); s->sd = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, true); if (s->sd == NULL) { - return -1; + error_setg(errp, "Device initialization failed."); + return; } register_savevm(dev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s); - return 0; } static void ssi_sd_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = ssi_sd_init; + k->realize = ssi_sd_realize; k->transfer = ssi_sd_transfer; k->cs_polarity = SSI_CS_LOW; } diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs index fcbb79ef01..c79a8dcd86 100644 --- a/hw/ssi/Makefile.objs +++ b/hw/ssi/Makefile.objs @@ -2,6 +2,7 @@ common-obj-$(CONFIG_PL022) += pl022.o common-obj-$(CONFIG_SSI) += ssi.o common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o +common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o obj-$(CONFIG_OMAP) += omap_spi.o obj-$(CONFIG_IMX) += imx_spi.o diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c new file mode 100644 index 0000000000..a371e302d4 --- /dev/null +++ b/hw/ssi/aspeed_smc.c @@ -0,0 +1,470 @@ +/* + * ASPEED AST2400 SMC Controller (SPI Flash Only) + * + * Copyright (C) 2016 IBM Corp. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "qemu/log.h" +#include "include/qemu/error-report.h" +#include "exec/address-spaces.h" + +#include "hw/ssi/aspeed_smc.h" + +/* CE Type Setting Register */ +#define R_CONF (0x00 / 4) +#define CONF_LEGACY_DISABLE (1 << 31) +#define CONF_ENABLE_W4 20 +#define CONF_ENABLE_W3 19 +#define CONF_ENABLE_W2 18 +#define CONF_ENABLE_W1 17 +#define CONF_ENABLE_W0 16 +#define CONF_FLASH_TYPE4 9 +#define CONF_FLASH_TYPE3 7 +#define CONF_FLASH_TYPE2 5 +#define CONF_FLASH_TYPE1 3 +#define CONF_FLASH_TYPE0 1 + +/* CE Control Register */ +#define R_CE_CTRL (0x04 / 4) +#define CTRL_EXTENDED4 4 /* 32 bit addressing for SPI */ +#define CTRL_EXTENDED3 3 /* 32 bit addressing for SPI */ +#define CTRL_EXTENDED2 2 /* 32 bit addressing for SPI */ +#define CTRL_EXTENDED1 1 /* 32 bit addressing for SPI */ +#define CTRL_EXTENDED0 0 /* 32 bit addressing for SPI */ + +/* Interrupt Control and Status Register */ +#define R_INTR_CTRL (0x08 / 4) +#define INTR_CTRL_DMA_STATUS (1 << 11) +#define INTR_CTRL_CMD_ABORT_STATUS (1 << 10) +#define INTR_CTRL_WRITE_PROTECT_STATUS (1 << 9) +#define INTR_CTRL_DMA_EN (1 << 3) +#define INTR_CTRL_CMD_ABORT_EN (1 << 2) +#define INTR_CTRL_WRITE_PROTECT_EN (1 << 1) + +/* CEx Control Register */ +#define R_CTRL0 (0x10 / 4) +#define CTRL_CMD_SHIFT 16 +#define CTRL_CMD_MASK 0xff +#define CTRL_CE_STOP_ACTIVE (1 << 2) +#define CTRL_CMD_MODE_MASK 0x3 +#define CTRL_READMODE 0x0 +#define CTRL_FREADMODE 0x1 +#define CTRL_WRITEMODE 0x2 +#define CTRL_USERMODE 0x3 +#define R_CTRL1 (0x14 / 4) +#define R_CTRL2 (0x18 / 4) +#define R_CTRL3 (0x1C / 4) +#define R_CTRL4 (0x20 / 4) + +/* CEx Segment Address Register */ +#define R_SEG_ADDR0 (0x30 / 4) +#define SEG_SIZE_SHIFT 24 /* 8MB units */ +#define SEG_SIZE_MASK 0x7f +#define SEG_START_SHIFT 16 /* address bit [A29-A23] */ +#define SEG_START_MASK 0x7f +#define R_SEG_ADDR1 (0x34 / 4) +#define R_SEG_ADDR2 (0x38 / 4) +#define R_SEG_ADDR3 (0x3C / 4) +#define R_SEG_ADDR4 (0x40 / 4) + +/* Misc Control Register #1 */ +#define R_MISC_CTRL1 (0x50 / 4) + +/* Misc Control Register #2 */ +#define R_MISC_CTRL2 (0x54 / 4) + +/* DMA Control/Status Register */ +#define R_DMA_CTRL (0x80 / 4) +#define DMA_CTRL_DELAY_MASK 0xf +#define DMA_CTRL_DELAY_SHIFT 8 +#define DMA_CTRL_FREQ_MASK 0xf +#define DMA_CTRL_FREQ_SHIFT 4 +#define DMA_CTRL_MODE (1 << 3) +#define DMA_CTRL_CKSUM (1 << 2) +#define DMA_CTRL_DIR (1 << 1) +#define DMA_CTRL_EN (1 << 0) + +/* DMA Flash Side Address */ +#define R_DMA_FLASH_ADDR (0x84 / 4) + +/* DMA DRAM Side Address */ +#define R_DMA_DRAM_ADDR (0x88 / 4) + +/* DMA Length Register */ +#define R_DMA_LEN (0x8C / 4) + +/* Checksum Calculation Result */ +#define R_DMA_CHECKSUM (0x90 / 4) + +/* Misc Control Register #2 */ +#define R_TIMINGS (0x94 / 4) + +/* SPI controller registers and bits */ +#define R_SPI_CONF (0x00 / 4) +#define SPI_CONF_ENABLE_W0 0 +#define R_SPI_CTRL0 (0x4 / 4) +#define R_SPI_MISC_CTRL (0x10 / 4) +#define R_SPI_TIMINGS (0x14 / 4) + +/* + * Default segments mapping addresses and size for each slave per + * controller. These can be changed when board is initialized with the + * Segment Address Registers but they don't seem do be used on the + * field. + */ +static const AspeedSegments aspeed_segments_legacy[] = { + { 0x10000000, 32 * 1024 * 1024 }, +}; + +static const AspeedSegments aspeed_segments_fmc[] = { + { 0x20000000, 64 * 1024 * 1024 }, + { 0x24000000, 32 * 1024 * 1024 }, + { 0x26000000, 32 * 1024 * 1024 }, + { 0x28000000, 32 * 1024 * 1024 }, + { 0x2A000000, 32 * 1024 * 1024 } +}; + +static const AspeedSegments aspeed_segments_spi[] = { + { 0x30000000, 64 * 1024 * 1024 }, +}; + +static const AspeedSMCController controllers[] = { + { "aspeed.smc.smc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS, + CONF_ENABLE_W0, 5, aspeed_segments_legacy, 0x6000000 }, + { "aspeed.smc.fmc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS, + CONF_ENABLE_W0, 5, aspeed_segments_fmc, 0x10000000 }, + { "aspeed.smc.spi", R_SPI_CONF, 0xff, R_SPI_CTRL0, R_SPI_TIMINGS, + SPI_CONF_ENABLE_W0, 1, aspeed_segments_spi, 0x10000000 }, +}; + +static uint64_t aspeed_smc_flash_default_read(void *opaque, hwaddr addr, + unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u" + PRIx64 "\n", __func__, addr, size); + return 0; +} + +static void aspeed_smc_flash_default_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u: 0x%" + PRIx64 "\n", __func__, addr, size, data); +} + +static const MemoryRegionOps aspeed_smc_flash_default_ops = { + .read = aspeed_smc_flash_default_read, + .write = aspeed_smc_flash_default_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static inline int aspeed_smc_flash_mode(const AspeedSMCState *s, int cs) +{ + return s->regs[s->r_ctrl0 + cs] & CTRL_CMD_MODE_MASK; +} + +static inline bool aspeed_smc_is_usermode(const AspeedSMCState *s, int cs) +{ + return aspeed_smc_flash_mode(s, cs) == CTRL_USERMODE; +} + +static inline bool aspeed_smc_is_writable(const AspeedSMCState *s, int cs) +{ + return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + cs)); +} + +static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) +{ + AspeedSMCFlash *fl = opaque; + const AspeedSMCState *s = fl->controller; + uint64_t ret = 0; + int i; + + if (aspeed_smc_is_usermode(s, fl->id)) { + for (i = 0; i < size; i++) { + ret |= ssi_transfer(s->spi, 0x0) << (8 * i); + } + } else { + qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n", + __func__); + ret = -1; + } + + return ret; +} + +static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + AspeedSMCFlash *fl = opaque; + const AspeedSMCState *s = fl->controller; + int i; + + if (!aspeed_smc_is_writable(s, fl->id)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: flash is not writable at 0x%" + HWADDR_PRIx "\n", __func__, addr); + return; + } + + if (!aspeed_smc_is_usermode(s, fl->id)) { + qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n", + __func__); + return; + } + + for (i = 0; i < size; i++) { + ssi_transfer(s->spi, (data >> (8 * i)) & 0xff); + } +} + +static const MemoryRegionOps aspeed_smc_flash_ops = { + .read = aspeed_smc_flash_read, + .write = aspeed_smc_flash_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static bool aspeed_smc_is_ce_stop_active(const AspeedSMCState *s, int cs) +{ + return s->regs[s->r_ctrl0 + cs] & CTRL_CE_STOP_ACTIVE; +} + +static void aspeed_smc_update_cs(const AspeedSMCState *s) +{ + int i; + + for (i = 0; i < s->num_cs; ++i) { + qemu_set_irq(s->cs_lines[i], aspeed_smc_is_ce_stop_active(s, i)); + } +} + +static void aspeed_smc_reset(DeviceState *d) +{ + AspeedSMCState *s = ASPEED_SMC(d); + int i; + + memset(s->regs, 0, sizeof s->regs); + + /* Unselect all slaves */ + for (i = 0; i < s->num_cs; ++i) { + s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE; + } + + aspeed_smc_update_cs(s); +} + +static bool aspeed_smc_is_implemented(AspeedSMCState *s, hwaddr addr) +{ + return (addr == s->r_conf || addr == s->r_timings || addr == s->r_ce_ctrl || + (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs)); +} + +static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) +{ + AspeedSMCState *s = ASPEED_SMC(opaque); + + addr >>= 2; + + if (addr >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; + } + + if (!aspeed_smc_is_implemented(s, addr)) { + qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; + } + + return s->regs[addr]; +} + +static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AspeedSMCState *s = ASPEED_SMC(opaque); + uint32_t value = data; + + addr >>= 2; + + if (addr >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at 0x%" HWADDR_PRIx "\n", + __func__, addr); + return; + } + + if (!aspeed_smc_is_implemented(s, addr)) { + qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", + __func__, addr); + return; + } + + /* + * Not much to do apart from storing the value and set the cs + * lines if the register is a controlling one. + */ + s->regs[addr] = value; + if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) { + aspeed_smc_update_cs(s); + } +} + +static const MemoryRegionOps aspeed_smc_ops = { + .read = aspeed_smc_read, + .write = aspeed_smc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.unaligned = true, +}; + +static void aspeed_smc_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedSMCState *s = ASPEED_SMC(dev); + AspeedSMCClass *mc = ASPEED_SMC_GET_CLASS(s); + int i; + char name[32]; + hwaddr offset = 0; + + s->ctrl = mc->ctrl; + + /* keep a copy under AspeedSMCState to speed up accesses */ + s->r_conf = s->ctrl->r_conf; + s->r_ce_ctrl = s->ctrl->r_ce_ctrl; + s->r_ctrl0 = s->ctrl->r_ctrl0; + s->r_timings = s->ctrl->r_timings; + s->conf_enable_w0 = s->ctrl->conf_enable_w0; + + /* Enforce some real HW limits */ + if (s->num_cs > s->ctrl->max_slaves) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: num_cs cannot exceed: %d\n", + __func__, s->ctrl->max_slaves); + s->num_cs = s->ctrl->max_slaves; + } + + s->spi = ssi_create_bus(dev, "spi"); + + /* Setup cs_lines for slaves */ + sysbus_init_irq(sbd, &s->irq); + s->cs_lines = g_new0(qemu_irq, s->num_cs); + ssi_auto_connect_slaves(dev, s->cs_lines, s->spi); + + for (i = 0; i < s->num_cs; ++i) { + sysbus_init_irq(sbd, &s->cs_lines[i]); + } + + aspeed_smc_reset(dev); + + memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s, + s->ctrl->name, ASPEED_SMC_R_MAX * 4); + sysbus_init_mmio(sbd, &s->mmio); + + /* + * Memory region where flash modules are remapped + */ + snprintf(name, sizeof(name), "%s.flash", s->ctrl->name); + + memory_region_init_io(&s->mmio_flash, OBJECT(s), + &aspeed_smc_flash_default_ops, s, name, + s->ctrl->mapping_window_size); + sysbus_init_mmio(sbd, &s->mmio_flash); + + s->flashes = g_new0(AspeedSMCFlash, s->num_cs); + + for (i = 0; i < s->num_cs; ++i) { + AspeedSMCFlash *fl = &s->flashes[i]; + + snprintf(name, sizeof(name), "%s.%d", s->ctrl->name, i); + + fl->id = i; + fl->controller = s; + fl->size = s->ctrl->segments[i].size; + memory_region_init_io(&fl->mmio, OBJECT(s), &aspeed_smc_flash_ops, + fl, name, fl->size); + memory_region_add_subregion(&s->mmio_flash, offset, &fl->mmio); + offset += fl->size; + } +} + +static const VMStateDescription vmstate_aspeed_smc = { + .name = "aspeed.smc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static Property aspeed_smc_properties[] = { + DEFINE_PROP_UINT32("num-cs", AspeedSMCState, num_cs, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void aspeed_smc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *mc = ASPEED_SMC_CLASS(klass); + + dc->realize = aspeed_smc_realize; + dc->reset = aspeed_smc_reset; + dc->props = aspeed_smc_properties; + dc->vmsd = &vmstate_aspeed_smc; + mc->ctrl = data; +} + +static const TypeInfo aspeed_smc_info = { + .name = TYPE_ASPEED_SMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedSMCState), + .class_size = sizeof(AspeedSMCClass), + .abstract = true, +}; + +static void aspeed_smc_register_types(void) +{ + int i; + + type_register_static(&aspeed_smc_info); + for (i = 0; i < ARRAY_SIZE(controllers); ++i) { + TypeInfo ti = { + .name = controllers[i].name, + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_smc_class_init, + .class_data = (void *)&controllers[i], + }; + type_register(&ti); + } +} + +type_init(aspeed_smc_register_types) diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c index 9791c0d947..7eaaf565fd 100644 --- a/hw/ssi/ssi.c +++ b/hw/ssi/ssi.c @@ -54,7 +54,7 @@ static uint32_t ssi_transfer_raw_default(SSISlave *dev, uint32_t val) return 0; } -static int ssi_slave_init(DeviceState *dev) +static void ssi_slave_realize(DeviceState *dev, Error **errp) { SSISlave *s = SSI_SLAVE(dev); SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s); @@ -64,7 +64,7 @@ static int ssi_slave_init(DeviceState *dev) qdev_init_gpio_in_named(dev, ssi_cs_default, SSI_GPIO_CS, 1); } - return ssc->init(s); + ssc->realize(s, errp); } static void ssi_slave_class_init(ObjectClass *klass, void *data) @@ -72,7 +72,7 @@ static void ssi_slave_class_init(ObjectClass *klass, void *data) SSISlaveClass *ssc = SSI_SLAVE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - dc->init = ssi_slave_init; + dc->realize = ssi_slave_realize; dc->bus_type = TYPE_SSI_BUS; if (!ssc->transfer_raw) { ssc->transfer_raw = ssi_transfer_raw_default; diff --git a/include/exec/memory.h b/include/exec/memory.h index 23c7399131..3e4d4164cd 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -445,15 +445,31 @@ void memory_region_init_alias(MemoryRegion *mr, uint64_t size); /** + * memory_region_init_rom: Initialize a ROM memory region. + * + * This has the same effect as calling memory_region_init_ram() + * and then marking the resulting region read-only with + * memory_region_set_readonly(). + * + * @mr: the #MemoryRegion to be initialized. + * @owner: the object that tracks the region's reference count + * @name: the name of the region. + * @size: size of the region. + * @errp: pointer to Error*, to store an error if it happens. + */ +void memory_region_init_rom(MemoryRegion *mr, + struct Object *owner, + const char *name, + uint64_t size, + Error **errp); + +/** * memory_region_init_rom_device: Initialize a ROM memory region. Writes are * handled via callbacks. * - * If NULL callbacks pointer is given, then I/O space is not supposed to be - * handled by QEMU itself. Any access via the memory API will cause an abort(). - * * @mr: the #MemoryRegion to be initialized. * @owner: the object that tracks the region's reference count - * @ops: callbacks for write access handling. + * @ops: callbacks for write access handling (must not be NULL). * @name: the name of the region. * @size: size of the region. * @errp: pointer to Error*, to store an error if it happens. diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h index f1a64fd389..7833bc716c 100644 --- a/include/hw/arm/ast2400.h +++ b/include/hw/arm/ast2400.h @@ -17,6 +17,7 @@ #include "hw/misc/aspeed_scu.h" #include "hw/timer/aspeed_timer.h" #include "hw/i2c/aspeed_i2c.h" +#include "hw/ssi/aspeed_smc.h" typedef struct AST2400State { /*< private >*/ @@ -29,6 +30,8 @@ typedef struct AST2400State { AspeedTimerCtrlState timerctrl; AspeedI2CState i2c; AspeedSCUState scu; + AspeedSMCState smc; + AspeedSMCState spi; } AST2400State; #define TYPE_AST2400 "ast2400" diff --git a/include/hw/dma/xlnx-zynq-devcfg.h b/include/hw/dma/xlnx-zynq-devcfg.h new file mode 100644 index 0000000000..d40e5c8df6 --- /dev/null +++ b/include/hw/dma/xlnx-zynq-devcfg.h @@ -0,0 +1,62 @@ +/* + * QEMU model of the Xilinx Devcfg Interface + * + * (C) 2011 PetaLogix Pty Ltd + * (C) 2014 Xilinx Inc. + * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef XLNX_ZYNQ_DEVCFG_H + +#include "hw/register.h" +#include "hw/sysbus.h" + +#define TYPE_XLNX_ZYNQ_DEVCFG "xlnx.ps7-dev-cfg" + +#define XLNX_ZYNQ_DEVCFG(obj) \ + OBJECT_CHECK(XlnxZynqDevcfg, (obj), TYPE_XLNX_ZYNQ_DEVCFG) + +#define XLNX_ZYNQ_DEVCFG_R_MAX 0x118 + +#define XLNX_ZYNQ_DEVCFG_DMA_CMD_FIFO_LEN 10 + +typedef struct XlnxZynqDevcfgDMACmd { + uint32_t src_addr; + uint32_t dest_addr; + uint32_t src_len; + uint32_t dest_len; +} XlnxZynqDevcfgDMACmd; + +typedef struct XlnxZynqDevcfg { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + + XlnxZynqDevcfgDMACmd dma_cmd_fifo[XLNX_ZYNQ_DEVCFG_DMA_CMD_FIFO_LEN]; + uint8_t dma_cmd_fifo_num; + + uint32_t regs[XLNX_ZYNQ_DEVCFG_R_MAX]; + RegisterInfo regs_info[XLNX_ZYNQ_DEVCFG_R_MAX]; +} XlnxZynqDevcfg; + +#define XLNX_ZYNQ_DEVCFG_H +#endif diff --git a/include/hw/register.h b/include/hw/register.h new file mode 100644 index 0000000000..8c12233b75 --- /dev/null +++ b/include/hw/register.h @@ -0,0 +1,255 @@ +/* + * Register Definition API + * + * Copyright (c) 2016 Xilinx Inc. + * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef REGISTER_H +#define REGISTER_H + +#include "hw/qdev-core.h" +#include "exec/memory.h" + +typedef struct RegisterInfo RegisterInfo; +typedef struct RegisterAccessInfo RegisterAccessInfo; +typedef struct RegisterInfoArray RegisterInfoArray; + +/** + * Access description for a register that is part of guest accessible device + * state. + * + * @name: String name of the register + * @ro: whether or not the bit is read-only + * @w1c: bits with the common write 1 to clear semantic. + * @reset: reset value. + * @cor: Bits that are clear on read + * @rsvd: Bits that are reserved and should not be changed + * + * @pre_write: Pre write callback. Passed the value that's to be written, + * immediately before the actual write. The returned value is what is written, + * giving the handler a chance to modify the written value. + * @post_write: Post write callback. Passed the written value. Most write side + * effects should be implemented here. + * + * @post_read: Post read callback. Passes the value that is about to be returned + * for a read. The return value from this function is what is ultimately read, + * allowing this function to modify the value before return to the client. + */ + +struct RegisterAccessInfo { + const char *name; + uint64_t ro; + uint64_t w1c; + uint64_t reset; + uint64_t cor; + uint64_t rsvd; + uint64_t unimp; + + uint64_t (*pre_write)(RegisterInfo *reg, uint64_t val); + void (*post_write)(RegisterInfo *reg, uint64_t val); + + uint64_t (*post_read)(RegisterInfo *reg, uint64_t val); + + hwaddr addr; +}; + +/** + * A register that is part of guest accessible state + * @data: pointer to the register data. Will be cast + * to the relevant uint type depending on data_size. + * @data_size: Size of the register in bytes. Must be + * 1, 2, 4 or 8 + * + * @access: Access description of this register + * + * @debug: Whether or not verbose debug is enabled + * @prefix: String prefix for log and debug messages + * + * @opaque: Opaque data for the register + */ + +struct RegisterInfo { + /* <private> */ + DeviceState parent_obj; + + /* <public> */ + void *data; + int data_size; + + const RegisterAccessInfo *access; + + void *opaque; +}; + +#define TYPE_REGISTER "qemu,register" +#define REGISTER(obj) OBJECT_CHECK(RegisterInfo, (obj), TYPE_REGISTER) + +/** + * This structure is used to group all of the individual registers which are + * modeled using the RegisterInfo structure. + * + * @r is an aray containing of all the relevent RegisterInfo structures. + * + * @num_elements is the number of elements in the array r + * + * @mem: optional Memory region for the register + */ + +struct RegisterInfoArray { + MemoryRegion mem; + + int num_elements; + RegisterInfo **r; + + bool debug; + const char *prefix; +}; + +/** + * write a value to a register, subject to its restrictions + * @reg: register to write to + * @val: value to write + * @we: write enable mask + * @prefix: The device prefix that should be printed before the register name + * @debug: Should the write operation debug information be printed? + */ + +void register_write(RegisterInfo *reg, uint64_t val, uint64_t we, + const char *prefix, bool debug); + +/** + * read a value from a register, subject to its restrictions + * @reg: register to read from + * @re: read enable mask + * @prefix: The device prefix that should be printed before the register name + * @debug: Should the read operation debug information be printed? + * returns: value read + */ + +uint64_t register_read(RegisterInfo *reg, uint64_t re, const char* prefix, + bool debug); + +/** + * reset a register + * @reg: register to reset + */ + +void register_reset(RegisterInfo *reg); + +/** + * Initialize a register. + * @reg: Register to initialize + */ + +void register_init(RegisterInfo *reg); + +/** + * Memory API MMIO write handler that will write to a Register API register. + * @opaque: RegisterInfo to write to + * @addr: Address to write + * @value: Value to write + * @size: Number of bytes to write + */ + +void register_write_memory(void *opaque, hwaddr addr, uint64_t value, + unsigned size); + +/** + * Memory API MMIO read handler that will read from a Register API register. + * @opaque: RegisterInfo to read from + * @addr: Address to read + * @size: Number of bytes to read + * returns: Value read from register + */ + +uint64_t register_read_memory(void *opaque, hwaddr addr, unsigned size); + +/** + * Init a block of registers into a container MemoryRegion. A + * number of constant register definitions are parsed to create a corresponding + * array of RegisterInfo's. + * + * @owner: device owning the registers + * @rae: Register definitions to init + * @num: number of registers to init (length of @rae) + * @ri: Register array to init, must already be allocated + * @data: Array to use for register data, must already be allocated + * @ops: Memory region ops to access registers. + * @debug enabled: turn on/off verbose debug information + * returns: A structure containing all of the registers and an initialized + * memory region (r_array->mem) the caller should add to a container. + */ + +RegisterInfoArray *register_init_block32(DeviceState *owner, + const RegisterAccessInfo *rae, + int num, RegisterInfo *ri, + uint32_t *data, + const MemoryRegionOps *ops, + bool debug_enabled, + uint64_t memory_size); + +/** + * This function should be called to cleanup the registers that were initialized + * when calling register_init_block32(). This function should only be called + * from the device's instance_finalize function. + * + * Any memory operations that the device performed that require cleanup (such + * as creating subregions) need to be called before calling this function. + * + * @r_array: A structure containing all of the registers, as returned by + * register_init_block32() + */ + +void register_finalize_block(RegisterInfoArray *r_array); + +/* Define constants for a 32 bit register */ + +/* This macro will define A_FOO, for the byte address of a register + * as well as R_FOO for the uint32_t[] register number (A_FOO / 4). + */ +#define REG32(reg, addr) \ + enum { A_ ## reg = (addr) }; \ + enum { R_ ## reg = (addr) / 4 }; + +/* Define SHIFT, LENGTH and MASK constants for a field within a register */ + +/* This macro will define FOO_BAR_MASK, FOO_BAR_SHIFT and FOO_BAR_LENGTH + * constants for field BAR in register FOO. + */ +#define FIELD(reg, field, shift, length) \ + enum { R_ ## reg ## _ ## field ## _SHIFT = (shift)}; \ + enum { R_ ## reg ## _ ## field ## _LENGTH = (length)}; \ + enum { R_ ## reg ## _ ## field ## _MASK = \ + MAKE_64BIT_MASK(shift, length)}; + +/* Extract a field from a register */ +#define FIELD_EX32(storage, reg, field) \ + extract32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) + +/* Extract a field from an array of registers */ +#define ARRAY_FIELD_EX32(regs, reg, field) \ + FIELD_EX32((regs)[R_ ## reg], reg, field) + +/* Deposit a register field. + * Assigning values larger then the target field will result in + * compilation warnings. + */ +#define FIELD_DP32(storage, reg, field, val) ({ \ + struct { \ + unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } v = { .v = val }; \ + uint32_t d; \ + d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, v.v); \ + d; }) + +/* Deposit a field to array of registers. */ +#define ARRAY_FIELD_DP32(regs, reg, field, val) \ + (regs)[R_ ## reg] = FIELD_DP32((regs)[R_ ## reg], reg, field, val); + +#endif diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h new file mode 100644 index 0000000000..def3b4507e --- /dev/null +++ b/include/hw/ssi/aspeed_smc.h @@ -0,0 +1,100 @@ +/* + * ASPEED AST2400 SMC Controller (SPI Flash Only) + * + * Copyright (C) 2016 IBM Corp. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef ASPEED_SMC_H +#define ASPEED_SMC_H + +#include "hw/ssi/ssi.h" + +typedef struct AspeedSegments { + hwaddr addr; + uint32_t size; +} AspeedSegments; + +struct AspeedSMCState; +typedef struct AspeedSMCController { + const char *name; + uint8_t r_conf; + uint8_t r_ce_ctrl; + uint8_t r_ctrl0; + uint8_t r_timings; + uint8_t conf_enable_w0; + uint8_t max_slaves; + const AspeedSegments *segments; + uint32_t mapping_window_size; +} AspeedSMCController; + +typedef struct AspeedSMCFlash { + const struct AspeedSMCState *controller; + + uint8_t id; + uint32_t size; + + MemoryRegion mmio; + DeviceState *flash; +} AspeedSMCFlash; + +#define TYPE_ASPEED_SMC "aspeed.smc" +#define ASPEED_SMC(obj) OBJECT_CHECK(AspeedSMCState, (obj), TYPE_ASPEED_SMC) +#define ASPEED_SMC_CLASS(klass) \ + OBJECT_CLASS_CHECK(AspeedSMCClass, (klass), TYPE_ASPEED_SMC) +#define ASPEED_SMC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AspeedSMCClass, (obj), TYPE_ASPEED_SMC) + +typedef struct AspeedSMCClass { + SysBusDevice parent_obj; + const AspeedSMCController *ctrl; +} AspeedSMCClass; + +#define ASPEED_SMC_R_MAX (0x100 / 4) + +typedef struct AspeedSMCState { + SysBusDevice parent_obj; + + const AspeedSMCController *ctrl; + + MemoryRegion mmio; + MemoryRegion mmio_flash; + + qemu_irq irq; + int irqline; + + uint32_t num_cs; + qemu_irq *cs_lines; + + SSIBus *spi; + + uint32_t regs[ASPEED_SMC_R_MAX]; + + /* depends on the controller type */ + uint8_t r_conf; + uint8_t r_ce_ctrl; + uint8_t r_ctrl0; + uint8_t r_timings; + uint8_t conf_enable_w0; + + AspeedSMCFlash *flashes; +} AspeedSMCState; + +#endif /* ASPEED_SMC_H */ diff --git a/include/hw/ssi/ssi.h b/include/hw/ssi/ssi.h index 4a0a53903c..6a0c3c3cdb 100644 --- a/include/hw/ssi/ssi.h +++ b/include/hw/ssi/ssi.h @@ -37,7 +37,7 @@ enum SSICSMode { struct SSISlaveClass { DeviceClass parent_class; - int (*init)(SSISlave *dev); + void (*realize)(SSISlave *dev, Error **errp); /* if you have standard or no CS behaviour, just override transfer. * This is called when the device cs is active (true by default). diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index 15418a86df..98fb005aba 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -24,6 +24,9 @@ #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) +#define MAKE_64BIT_MASK(shift, length) \ + (((~0ULL) >> (64 - (length))) << (shift)) + /** * set_bit - Set a bit in memory * @nr: the bit to set diff --git a/linux-user/qemu.h b/linux-user/qemu.h index e8a5aede95..cdf23a723a 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -116,10 +116,10 @@ typedef struct TaskState { #endif #if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32) /* Extra fields for semihosted binaries. */ - uint32_t heap_base; - uint32_t heap_limit; + abi_ulong heap_base; + abi_ulong heap_limit; #endif - uint32_t stack_base; + abi_ulong stack_base; int used; /* non zero if used */ struct image_info *info; struct linux_binprm *bprm; @@ -1376,6 +1376,21 @@ void memory_region_init_alias(MemoryRegion *mr, mr->alias_offset = offset; } +void memory_region_init_rom(MemoryRegion *mr, + struct Object *owner, + const char *name, + uint64_t size, + Error **errp) +{ + memory_region_init(mr, owner, name, size); + mr->ram = true; + mr->readonly = true; + mr->terminates = true; + mr->destructor = memory_region_destructor_ram; + mr->ram_block = qemu_ram_alloc(size, mr, errp); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; +} + void memory_region_init_rom_device(MemoryRegion *mr, Object *owner, const MemoryRegionOps *ops, @@ -1384,6 +1399,7 @@ void memory_region_init_rom_device(MemoryRegion *mr, uint64_t size, Error **errp) { + assert(ops); memory_region_init(mr, owner, name, size); mr->ops = ops; mr->opaque = opaque; diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c index 8be0645eb0..d50726f65d 100644 --- a/target-arm/arm-semi.c +++ b/target-arm/arm-semi.c @@ -564,8 +564,10 @@ target_ulong do_arm_semihosting(CPUARMState *env) } case TARGET_SYS_HEAPINFO: { - uint32_t *ptr; + target_ulong retvals[4]; uint32_t limit; + int i; + GET_ARG(0); #ifdef CONFIG_USER_ONLY @@ -587,30 +589,33 @@ target_ulong do_arm_semihosting(CPUARMState *env) ts->heap_limit = limit; } - ptr = lock_user(VERIFY_WRITE, arg0, 16, 0); - if (!ptr) { - /* FIXME - should this error code be -TARGET_EFAULT ? */ - return (uint32_t)-1; - } - ptr[0] = tswap32(ts->heap_base); - ptr[1] = tswap32(ts->heap_limit); - ptr[2] = tswap32(ts->stack_base); - ptr[3] = tswap32(0); /* Stack limit. */ - unlock_user(ptr, arg0, 16); + retvals[0] = ts->heap_base; + retvals[1] = ts->heap_limit; + retvals[2] = ts->stack_base; + retvals[3] = 0; /* Stack limit. */ #else limit = ram_size; - ptr = lock_user(VERIFY_WRITE, arg0, 16, 0); - if (!ptr) { - /* FIXME - should this error code be -TARGET_EFAULT ? */ - return (uint32_t)-1; - } /* TODO: Make this use the limit of the loaded application. */ - ptr[0] = tswap32(limit / 2); - ptr[1] = tswap32(limit); - ptr[2] = tswap32(limit); /* Stack base */ - ptr[3] = tswap32(0); /* Stack limit. */ - unlock_user(ptr, arg0, 16); + retvals[0] = limit / 2; + retvals[1] = limit; + retvals[2] = limit; /* Stack base */ + retvals[3] = 0; /* Stack limit. */ #endif + + for (i = 0; i < ARRAY_SIZE(retvals); i++) { + bool fail; + + if (is_a64(env)) { + fail = put_user_u64(retvals[i], arg0 + i * 8); + } else { + fail = put_user_u32(retvals[i], arg0 + i * 4); + } + + if (fail) { + /* Couldn't write back to argument block */ + return -1; + } + } return 0; } case TARGET_SYS_EXIT: |