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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJXelPeAAoJEDwlJe0UNgzejHUP/0if6Q7A4F7MKcrhWpdmOxap FzAd7SL/kHt1GWbXKQzcwlwMk0oWKQYxpHDE4k/GF82cbGgA6HWZ6LGd8H0H/lc8 7LpCZUkOZeQJImueoqyJJ5NOKCzQv1ta+oh5DE3Ru4JQmNdPRoqmH/527/sAajd7 FNfxJI9OT13eQSoXRPBbZNwGDoIwvRu/adr2H4p0mtqJ2LSw4S4Hy0DT3Ag3gtEq a4U1VRbe6LpVlGciTx3kqQhr4qMGv4YIdcCHjt6jmegNaXpy8GnyuQJBvkqRgESH Ksj1npzQhI0Odvm3wd5fmcse0Yz0DZ1H056QNNd89To9Bp//Mmb9B1tZXFV+icHU 7Uz+Z6dj/9PFJ1UUea+rNR4VMN6vuVjfZVtQGkUzfLB/r1mfyBafXrEielObE7gQ nszzx/IODpw4m8T0LdkVey7o4Ocz25mSQ7iPS0xeH5WVLUsj8Mq2c4sboVJ9VQHu b1ULOlrzB0MCLYrkI52/wp+FNvK+s4LfDuiUypQdMYjYAZz4blCdgfe9sbOGxU0K rbukTdpKnZEF1DFjMSRjJAmoYhU+lNLgyCjThVqX9D+MQA1UMvVFZAZYr8TS5F9+ Q+yvTCKFGg4s+oV7CdAeRAEjNh9Mx7VHg8LogVcbBVBaVLRTlfSkUFe5EkyGNIMt LtaS6lpIS0jK3SaYiCKy =Du+u -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
3173a1fd54
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
qemu_irq cs_line;
|
||||
DriveInfo *dinfo = drive_get_next(IF_MTD);
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
int i, n;
|
||||
|
||||
s->cur_addr = 0;
|
||||
|
||||
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;
|
||||
|
@ -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
|
||||
|
287
hw/core/register.c
Normal file
287
hw/core/register.c
Normal file
@ -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)
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
400
hw/dma/xlnx-zynq-devcfg.c
Normal file
400
hw/dma/xlnx-zynq-devcfg.c
Normal file
@ -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)
|
@ -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. */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
470
hw/ssi/aspeed_smc.c
Normal file
470
hw/ssi/aspeed_smc.c
Normal file
@ -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)
|
@ -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;
|
||||
|
@ -445,15 +445,31 @@ void memory_region_init_alias(MemoryRegion *mr,
|
||||
uint64_t size);
|
||||
|
||||
/**
|
||||
* memory_region_init_rom_device: Initialize a ROM memory region. Writes are
|
||||
* handled via callbacks.
|
||||
* memory_region_init_rom: Initialize a ROM memory region.
|
||||
*
|
||||
* 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().
|
||||
* 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
|
||||
* @ops: callbacks for write access handling.
|
||||
* @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.
|
||||
*
|
||||
* @mr: the #MemoryRegion to be initialized.
|
||||
* @owner: the object that tracks the region's reference count
|
||||
* @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.
|
||||
|
@ -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"
|
||||
|
62
include/hw/dma/xlnx-zynq-devcfg.h
Normal file
62
include/hw/dma/xlnx-zynq-devcfg.h
Normal file
@ -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
|
255
include/hw/register.h
Normal file
255
include/hw/register.h
Normal file
@ -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
|
100
include/hw/ssi/aspeed_smc.h
Normal file
100
include/hw/ssi/aspeed_smc.h
Normal file
@ -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 */
|
@ -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).
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
16
memory.c
16
memory.c
@ -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;
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user