aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/Kconfig14
-rw-r--r--hw/arm/Makefile.objs1
-rw-r--r--hw/arm/aspeed.c76
-rw-r--r--hw/arm/aspeed_soc.c268
-rw-r--r--hw/arm/boot.c3
-rw-r--r--hw/arm/fsl-imx7.c11
-rw-r--r--hw/arm/msf2-som.c1
-rw-r--r--hw/arm/sbsa-ref.c806
-rw-r--r--hw/arm/virt.c1
-rw-r--r--hw/display/Kconfig2
-rw-r--r--hw/display/ati.c99
-rw-r--r--hw/display/ati_2d.c26
-rw-r--r--hw/display/ati_dbg.c2
-rw-r--r--hw/display/ati_int.h6
-rw-r--r--hw/display/ati_regs.h6
-rw-r--r--hw/i2c/bitbang_i2c.c2
-rw-r--r--hw/i2c/bitbang_i2c.h12
-rw-r--r--hw/i2c/ppc4xx_i2c.c1
-rw-r--r--hw/i2c/versatile_i2c.c2
-rw-r--r--hw/intc/aspeed_vic.c105
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/aspeed_xdma.c165
-rw-r--r--hw/misc/trace-events3
-rw-r--r--hw/net/ftgmac100.c2
-rw-r--r--hw/net/virtio-net.c4
-rw-r--r--hw/pci-host/designware.c18
-rw-r--r--hw/s390x/css.c27
-rw-r--r--hw/s390x/s390-ccw.c20
-rw-r--r--hw/ssi/aspeed_smc.c1
-rw-r--r--hw/timer/Makefile.objs2
-rw-r--r--hw/timer/aspeed_rtc.c180
-rw-r--r--hw/timer/aspeed_timer.c78
-rw-r--r--hw/timer/trace-events4
-rw-r--r--hw/vfio/ccw.c162
-rw-r--r--hw/watchdog/wdt_aspeed.c20
35 files changed, 1884 insertions, 247 deletions
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 9aced9d54d..ab65ecd216 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -184,6 +184,20 @@ config REALVIEW
select DS1338 # I2C RTC+NVRAM
select USB_OHCI
+config SBSA_REF
+ bool
+ imply PCI_DEVICES
+ select AHCI
+ select ARM_SMMUV3
+ select GPIO_KEY
+ select PCI_EXPRESS
+ select PCI_EXPRESS_GENERIC_BRIDGE
+ select PFLASH_CFI01
+ select PL011 # UART
+ select PL031 # RTC
+ select PL061 # GPIO
+ select USB_EHCI_SYSBUS
+
config SABRELITE
bool
select FSL_IMX6
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 994e67dd0d..43ce8d5b19 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -19,6 +19,7 @@ obj-$(CONFIG_SPITZ) += spitz.o
obj-$(CONFIG_TOSA) += tosa.o
obj-$(CONFIG_Z2) += z2.o
obj-$(CONFIG_REALVIEW) += realview.o
+obj-$(CONFIG_SBSA_REF) += sbsa-ref.o
obj-$(CONFIG_STELLARIS) += stellaris.o
obj-$(CONFIG_COLLIE) += collie.o
obj-$(CONFIG_VERSATILE) += versatilepb.o
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index d2ad2da24b..8b6d304247 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -22,17 +22,18 @@
#include "hw/misc/tmp105.h"
#include "qemu/log.h"
#include "sysemu/block-backend.h"
+#include "sysemu/sysemu.h"
#include "hw/loader.h"
#include "qemu/error-report.h"
#include "qemu/units.h"
static struct arm_boot_info aspeed_board_binfo = {
.board_id = -1, /* device-tree-only board */
- .nb_cpus = 1,
};
struct AspeedBoardState {
AspeedSoCState soc;
+ MemoryRegion ram_container;
MemoryRegion ram;
MemoryRegion max_ram;
};
@@ -72,6 +73,17 @@ struct AspeedBoardState {
SCU_AST2500_HW_STRAP_ACPI_ENABLE | \
SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER))
+/* Swift hardware value: 0xF11AD206 */
+#define SWIFT_BMC_HW_STRAP1 ( \
+ AST2500_HW_STRAP1_DEFAULTS | \
+ SCU_AST2500_HW_STRAP_SPI_AUTOFETCH_ENABLE | \
+ SCU_AST2500_HW_STRAP_GPIO_STRAP_ENABLE | \
+ SCU_AST2500_HW_STRAP_UART_DEBUG | \
+ SCU_AST2500_HW_STRAP_DDR4_ENABLE | \
+ SCU_H_PLL_BYPASS_EN | \
+ SCU_AST2500_HW_STRAP_ACPI_ENABLE | \
+ SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER))
+
/* Witherspoon hardware value: 0xF10AD216 (but use romulus definition) */
#define WITHERSPOON_BMC_HW_STRAP1 ROMULUS_BMC_HW_STRAP1
@@ -159,6 +171,10 @@ static void aspeed_board_init(MachineState *machine,
ram_addr_t max_ram_size;
bmc = g_new0(AspeedBoardState, 1);
+
+ memory_region_init(&bmc->ram_container, NULL, "aspeed-ram-container",
+ UINT32_MAX);
+
object_initialize_child(OBJECT(machine), "soc", &bmc->soc,
(sizeof(bmc->soc)), cfg->soc_name, &error_abort,
NULL);
@@ -171,6 +187,8 @@ static void aspeed_board_init(MachineState *machine,
&error_abort);
object_property_set_int(OBJECT(&bmc->soc), cfg->num_cs, "num-cs",
&error_abort);
+ object_property_set_int(OBJECT(&bmc->soc), smp_cpus, "num-cpus",
+ &error_abort);
if (machine->kernel_filename) {
/*
* When booting with a -kernel command line there is no u-boot
@@ -191,18 +209,16 @@ static void aspeed_board_init(MachineState *machine,
&error_abort);
memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size);
- memory_region_add_subregion(get_system_memory(), sc->info->sdram_base,
- &bmc->ram);
- object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram),
- &error_abort);
+ memory_region_add_subregion(&bmc->ram_container, 0, &bmc->ram);
+ memory_region_add_subregion(get_system_memory(),
+ sc->info->memmap[ASPEED_SDRAM],
+ &bmc->ram_container);
max_ram_size = object_property_get_uint(OBJECT(&bmc->soc), "max-ram-size",
&error_abort);
memory_region_init_io(&bmc->max_ram, NULL, &max_ram_ops, NULL,
"max_ram", max_ram_size - ram_size);
- memory_region_add_subregion(get_system_memory(),
- sc->info->sdram_base + ram_size,
- &bmc->max_ram);
+ memory_region_add_subregion(&bmc->ram_container, ram_size, &bmc->max_ram);
aspeed_board_init_flashes(&bmc->soc.fmc, cfg->fmc_model, &error_abort);
aspeed_board_init_flashes(&bmc->soc.spi[0], cfg->spi_model, &error_abort);
@@ -229,7 +245,8 @@ static void aspeed_board_init(MachineState *machine,
aspeed_board_binfo.initrd_filename = machine->initrd_filename;
aspeed_board_binfo.kernel_cmdline = machine->kernel_cmdline;
aspeed_board_binfo.ram_size = ram_size;
- aspeed_board_binfo.loader_start = sc->info->sdram_base;
+ aspeed_board_binfo.loader_start = sc->info->memmap[ASPEED_SDRAM];
+ aspeed_board_binfo.nb_cpus = bmc->soc.num_cpus;
if (cfg->i2c_init) {
cfg->i2c_init(bmc);
@@ -286,6 +303,35 @@ static void romulus_bmc_i2c_init(AspeedBoardState *bmc)
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "ds1338", 0x32);
}
+static void swift_bmc_i2c_init(AspeedBoardState *bmc)
+{
+ AspeedSoCState *soc = &bmc->soc;
+
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 3), "pca9552", 0x60);
+
+ /* The swift board expects a TMP275 but a TMP105 is compatible */
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 7), "tmp105", 0x48);
+ /* The swift board expects a pca9551 but a pca9552 is compatible */
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 7), "pca9552", 0x60);
+
+ /* The swift board expects an Epson RX8900 RTC but a ds1338 is compatible */
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 8), "ds1338", 0x32);
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 8), "pca9552", 0x60);
+
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 9), "tmp423", 0x4c);
+ /* The swift board expects a pca9539 but a pca9552 is compatible */
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 9), "pca9552", 0x74);
+
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 10), "tmp423", 0x4c);
+ /* The swift board expects a pca9539 but a pca9552 is compatible */
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 10), "pca9552",
+ 0x74);
+
+ /* The swift board expects a TMP275 but a TMP105 is compatible */
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 12), "tmp105", 0x48);
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 12), "tmp105", 0x4a);
+}
+
static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc)
{
AspeedSoCState *soc = &bmc->soc;
@@ -326,7 +372,7 @@ static void aspeed_machine_class_init(ObjectClass *oc, void *data)
mc->desc = board->desc;
mc->init = aspeed_machine_init;
- mc->max_cpus = 1;
+ mc->max_cpus = ASPEED_CPUS_NUM;
mc->no_sdcard = 1;
mc->no_floppy = 1;
mc->no_cdrom = 1;
@@ -377,6 +423,16 @@ static const AspeedBoardConfig aspeed_boards[] = {
.i2c_init = romulus_bmc_i2c_init,
.ram = 512 * MiB,
}, {
+ .name = MACHINE_TYPE_NAME("swift-bmc"),
+ .desc = "OpenPOWER Swift BMC (ARM1176)",
+ .soc_name = "ast2500-a1",
+ .hw_strap1 = SWIFT_BMC_HW_STRAP1,
+ .fmc_model = "mx66l1g45g",
+ .spi_model = "mx66l1g45g",
+ .num_cs = 2,
+ .i2c_init = swift_bmc_i2c_init,
+ .ram = 512 * MiB,
+ }, {
.name = MACHINE_TYPE_NAME("witherspoon-bmc"),
.desc = "OpenPOWER Witherspoon BMC (ARM1176)",
.soc_name = "ast2500-a1",
diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c
index a2ea8c7484..c6fb3700f2 100644
--- a/hw/arm/aspeed_soc.c
+++ b/hw/arm/aspeed_soc.c
@@ -19,36 +19,99 @@
#include "hw/char/serial.h"
#include "qemu/log.h"
#include "qemu/module.h"
+#include "qemu/error-report.h"
#include "hw/i2c/aspeed_i2c.h"
#include "net/net.h"
-#define ASPEED_SOC_UART_5_BASE 0x00184000
#define ASPEED_SOC_IOMEM_SIZE 0x00200000
-#define ASPEED_SOC_IOMEM_BASE 0x1E600000
-#define ASPEED_SOC_FMC_BASE 0x1E620000
-#define ASPEED_SOC_SPI_BASE 0x1E630000
-#define ASPEED_SOC_SPI2_BASE 0x1E631000
-#define ASPEED_SOC_VIC_BASE 0x1E6C0000
-#define ASPEED_SOC_SDMC_BASE 0x1E6E0000
-#define ASPEED_SOC_SCU_BASE 0x1E6E2000
-#define ASPEED_SOC_SRAM_BASE 0x1E720000
-#define ASPEED_SOC_TIMER_BASE 0x1E782000
-#define ASPEED_SOC_WDT_BASE 0x1E785000
-#define ASPEED_SOC_I2C_BASE 0x1E78A000
-#define ASPEED_SOC_ETH1_BASE 0x1E660000
-#define ASPEED_SOC_ETH2_BASE 0x1E680000
-
-static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
-static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
-
-#define AST2400_SDRAM_BASE 0x40000000
-#define AST2500_SDRAM_BASE 0x80000000
-
-static const hwaddr aspeed_soc_ast2400_spi_bases[] = { ASPEED_SOC_SPI_BASE };
-static const char *aspeed_soc_ast2400_typenames[] = { "aspeed.smc.spi" };
-static const hwaddr aspeed_soc_ast2500_spi_bases[] = { ASPEED_SOC_SPI_BASE,
- ASPEED_SOC_SPI2_BASE};
+static const hwaddr aspeed_soc_ast2400_memmap[] = {
+ [ASPEED_IOMEM] = 0x1E600000,
+ [ASPEED_FMC] = 0x1E620000,
+ [ASPEED_SPI1] = 0x1E630000,
+ [ASPEED_VIC] = 0x1E6C0000,
+ [ASPEED_SDMC] = 0x1E6E0000,
+ [ASPEED_SCU] = 0x1E6E2000,
+ [ASPEED_XDMA] = 0x1E6E7000,
+ [ASPEED_ADC] = 0x1E6E9000,
+ [ASPEED_SRAM] = 0x1E720000,
+ [ASPEED_GPIO] = 0x1E780000,
+ [ASPEED_RTC] = 0x1E781000,
+ [ASPEED_TIMER1] = 0x1E782000,
+ [ASPEED_WDT] = 0x1E785000,
+ [ASPEED_PWM] = 0x1E786000,
+ [ASPEED_LPC] = 0x1E789000,
+ [ASPEED_IBT] = 0x1E789140,
+ [ASPEED_I2C] = 0x1E78A000,
+ [ASPEED_ETH1] = 0x1E660000,
+ [ASPEED_ETH2] = 0x1E680000,
+ [ASPEED_UART1] = 0x1E783000,
+ [ASPEED_UART5] = 0x1E784000,
+ [ASPEED_VUART] = 0x1E787000,
+ [ASPEED_SDRAM] = 0x40000000,
+};
+
+static const hwaddr aspeed_soc_ast2500_memmap[] = {
+ [ASPEED_IOMEM] = 0x1E600000,
+ [ASPEED_FMC] = 0x1E620000,
+ [ASPEED_SPI1] = 0x1E630000,
+ [ASPEED_SPI2] = 0x1E631000,
+ [ASPEED_VIC] = 0x1E6C0000,
+ [ASPEED_SDMC] = 0x1E6E0000,
+ [ASPEED_SCU] = 0x1E6E2000,
+ [ASPEED_XDMA] = 0x1E6E7000,
+ [ASPEED_ADC] = 0x1E6E9000,
+ [ASPEED_SRAM] = 0x1E720000,
+ [ASPEED_GPIO] = 0x1E780000,
+ [ASPEED_RTC] = 0x1E781000,
+ [ASPEED_TIMER1] = 0x1E782000,
+ [ASPEED_WDT] = 0x1E785000,
+ [ASPEED_PWM] = 0x1E786000,
+ [ASPEED_LPC] = 0x1E789000,
+ [ASPEED_IBT] = 0x1E789140,
+ [ASPEED_I2C] = 0x1E78A000,
+ [ASPEED_ETH1] = 0x1E660000,
+ [ASPEED_ETH2] = 0x1E680000,
+ [ASPEED_UART1] = 0x1E783000,
+ [ASPEED_UART5] = 0x1E784000,
+ [ASPEED_VUART] = 0x1E787000,
+ [ASPEED_SDRAM] = 0x80000000,
+};
+
+static const int aspeed_soc_ast2400_irqmap[] = {
+ [ASPEED_UART1] = 9,
+ [ASPEED_UART2] = 32,
+ [ASPEED_UART3] = 33,
+ [ASPEED_UART4] = 34,
+ [ASPEED_UART5] = 10,
+ [ASPEED_VUART] = 8,
+ [ASPEED_FMC] = 19,
+ [ASPEED_SDMC] = 0,
+ [ASPEED_SCU] = 21,
+ [ASPEED_ADC] = 31,
+ [ASPEED_GPIO] = 20,
+ [ASPEED_RTC] = 22,
+ [ASPEED_TIMER1] = 16,
+ [ASPEED_TIMER2] = 17,
+ [ASPEED_TIMER3] = 18,
+ [ASPEED_TIMER4] = 35,
+ [ASPEED_TIMER5] = 36,
+ [ASPEED_TIMER6] = 37,
+ [ASPEED_TIMER7] = 38,
+ [ASPEED_TIMER8] = 39,
+ [ASPEED_WDT] = 27,
+ [ASPEED_PWM] = 28,
+ [ASPEED_LPC] = 8,
+ [ASPEED_IBT] = 8, /* LPC */
+ [ASPEED_I2C] = 12,
+ [ASPEED_ETH1] = 2,
+ [ASPEED_ETH2] = 3,
+ [ASPEED_XDMA] = 6,
+};
+
+#define aspeed_soc_ast2500_irqmap aspeed_soc_ast2400_irqmap
+
+static const char *aspeed_soc_ast2400_typenames[] = { "aspeed.smc.spi" };
static const char *aspeed_soc_ast2500_typenames[] = {
"aspeed.smc.ast2500-spi1", "aspeed.smc.ast2500-spi2" };
@@ -57,57 +120,71 @@ static const AspeedSoCInfo aspeed_socs[] = {
.name = "ast2400-a0",
.cpu_type = ARM_CPU_TYPE_NAME("arm926"),
.silicon_rev = AST2400_A0_SILICON_REV,
- .sdram_base = AST2400_SDRAM_BASE,
.sram_size = 0x8000,
.spis_num = 1,
- .spi_bases = aspeed_soc_ast2400_spi_bases,
.fmc_typename = "aspeed.smc.fmc",
.spi_typename = aspeed_soc_ast2400_typenames,
.wdts_num = 2,
+ .irqmap = aspeed_soc_ast2400_irqmap,
+ .memmap = aspeed_soc_ast2400_memmap,
+ .num_cpus = 1,
}, {
.name = "ast2400-a1",
.cpu_type = ARM_CPU_TYPE_NAME("arm926"),
.silicon_rev = AST2400_A1_SILICON_REV,
- .sdram_base = AST2400_SDRAM_BASE,
.sram_size = 0x8000,
.spis_num = 1,
- .spi_bases = aspeed_soc_ast2400_spi_bases,
.fmc_typename = "aspeed.smc.fmc",
.spi_typename = aspeed_soc_ast2400_typenames,
.wdts_num = 2,
+ .irqmap = aspeed_soc_ast2400_irqmap,
+ .memmap = aspeed_soc_ast2400_memmap,
+ .num_cpus = 1,
}, {
.name = "ast2400",
.cpu_type = ARM_CPU_TYPE_NAME("arm926"),
.silicon_rev = AST2400_A0_SILICON_REV,
- .sdram_base = AST2400_SDRAM_BASE,
.sram_size = 0x8000,
.spis_num = 1,
- .spi_bases = aspeed_soc_ast2400_spi_bases,
.fmc_typename = "aspeed.smc.fmc",
.spi_typename = aspeed_soc_ast2400_typenames,
.wdts_num = 2,
+ .irqmap = aspeed_soc_ast2400_irqmap,
+ .memmap = aspeed_soc_ast2400_memmap,
+ .num_cpus = 1,
}, {
.name = "ast2500-a1",
.cpu_type = ARM_CPU_TYPE_NAME("arm1176"),
.silicon_rev = AST2500_A1_SILICON_REV,
- .sdram_base = AST2500_SDRAM_BASE,
.sram_size = 0x9000,
.spis_num = 2,
- .spi_bases = aspeed_soc_ast2500_spi_bases,
.fmc_typename = "aspeed.smc.ast2500-fmc",
.spi_typename = aspeed_soc_ast2500_typenames,
.wdts_num = 3,
+ .irqmap = aspeed_soc_ast2500_irqmap,
+ .memmap = aspeed_soc_ast2500_memmap,
+ .num_cpus = 1,
},
};
+static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl)
+{
+ AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
+
+ return qdev_get_gpio_in(DEVICE(&s->vic), sc->info->irqmap[ctrl]);
+}
+
static void aspeed_soc_init(Object *obj)
{
AspeedSoCState *s = ASPEED_SOC(obj);
AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
int i;
- object_initialize_child(obj, "cpu", OBJECT(&s->cpu), sizeof(s->cpu),
- sc->info->cpu_type, &error_abort, NULL);
+ for (i = 0; i < sc->info->num_cpus; i++) {
+ object_initialize_child(obj, "cpu[*]", OBJECT(&s->cpu[i]),
+ sizeof(s->cpu[i]), sc->info->cpu_type,
+ &error_abort, NULL);
+ }
sysbus_init_child_obj(obj, "scu", OBJECT(&s->scu), sizeof(s->scu),
TYPE_ASPEED_SCU);
@@ -123,6 +200,9 @@ static void aspeed_soc_init(Object *obj)
sysbus_init_child_obj(obj, "vic", OBJECT(&s->vic), sizeof(s->vic),
TYPE_ASPEED_VIC);
+ sysbus_init_child_obj(obj, "rtc", OBJECT(&s->rtc), sizeof(s->rtc),
+ TYPE_ASPEED_RTC);
+
sysbus_init_child_obj(obj, "timerctrl", OBJECT(&s->timerctrl),
sizeof(s->timerctrl), TYPE_ASPEED_TIMER);
object_property_add_const_link(OBJECT(&s->timerctrl), "scu",
@@ -155,10 +235,17 @@ static void aspeed_soc_init(Object *obj)
sizeof(s->wdt[i]), TYPE_ASPEED_WDT);
qdev_prop_set_uint32(DEVICE(&s->wdt[i]), "silicon-rev",
sc->info->silicon_rev);
+ object_property_add_const_link(OBJECT(&s->wdt[i]), "scu",
+ OBJECT(&s->scu), &error_abort);
}
- sysbus_init_child_obj(obj, "ftgmac100", OBJECT(&s->ftgmac100),
- sizeof(s->ftgmac100), TYPE_FTGMAC100);
+ for (i = 0; i < ASPEED_MACS_NUM; i++) {
+ sysbus_init_child_obj(obj, "ftgmac100[*]", OBJECT(&s->ftgmac100[i]),
+ sizeof(s->ftgmac100[i]), TYPE_FTGMAC100);
+ }
+
+ sysbus_init_child_obj(obj, "xdma", OBJECT(&s->xdma), sizeof(s->xdma),
+ TYPE_ASPEED_XDMA);
}
static void aspeed_soc_realize(DeviceState *dev, Error **errp)
@@ -169,14 +256,22 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
Error *err = NULL, *local_err = NULL;
/* IO space */
- create_unimplemented_device("aspeed_soc.io",
- ASPEED_SOC_IOMEM_BASE, ASPEED_SOC_IOMEM_SIZE);
+ create_unimplemented_device("aspeed_soc.io", sc->info->memmap[ASPEED_IOMEM],
+ ASPEED_SOC_IOMEM_SIZE);
+
+ if (s->num_cpus > sc->info->num_cpus) {
+ warn_report("%s: invalid number of CPUs %d, using default %d",
+ sc->info->name, s->num_cpus, sc->info->num_cpus);
+ s->num_cpus = sc->info->num_cpus;
+ }
/* CPU */
- object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
+ for (i = 0; i < s->num_cpus; i++) {
+ object_property_set_bool(OBJECT(&s->cpu[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
}
/* SRAM */
@@ -186,8 +281,8 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
error_propagate(errp, err);
return;
}
- memory_region_add_subregion(get_system_memory(), ASPEED_SOC_SRAM_BASE,
- &s->sram);
+ memory_region_add_subregion(get_system_memory(),
+ sc->info->memmap[ASPEED_SRAM], &s->sram);
/* SCU */
object_property_set_bool(OBJECT(&s->scu), true, "realized", &err);
@@ -195,7 +290,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, ASPEED_SOC_SCU_BASE);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, sc->info->memmap[ASPEED_SCU]);
/* VIC */
object_property_set_bool(OBJECT(&s->vic), true, "realized", &err);
@@ -203,29 +298,39 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, ASPEED_SOC_VIC_BASE);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, sc->info->memmap[ASPEED_VIC]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0,
qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ));
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1,
qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ));
+ /* RTC */
+ object_property_set_bool(OBJECT(&s->rtc), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, sc->info->memmap[ASPEED_RTC]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0,
+ aspeed_soc_get_irq(s, ASPEED_RTC));
+
/* Timer */
object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, ASPEED_SOC_TIMER_BASE);
- for (i = 0; i < ARRAY_SIZE(timer_irqs); i++) {
- qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->vic), timer_irqs[i]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0,
+ sc->info->memmap[ASPEED_TIMER1]);
+ for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
+ qemu_irq irq = aspeed_soc_get_irq(s, ASPEED_TIMER1 + i);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq);
}
/* UART - attach an 8250 to the IO space as our UART5 */
if (serial_hd(0)) {
- qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]);
- serial_mm_init(get_system_memory(),
- ASPEED_SOC_IOMEM_BASE + ASPEED_SOC_UART_5_BASE, 2,
+ qemu_irq uart5 = aspeed_soc_get_irq(s, ASPEED_UART5);
+ serial_mm_init(get_system_memory(), sc->info->memmap[ASPEED_UART5], 2,
uart5, 38400, serial_hd(0), DEVICE_LITTLE_ENDIAN);
}
@@ -235,21 +340,27 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, ASPEED_SOC_I2C_BASE);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, sc->info->memmap[ASPEED_I2C]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0,
- qdev_get_gpio_in(DEVICE(&s->vic), 12));
+ aspeed_soc_get_irq(s, ASPEED_I2C));
/* FMC, The number of CS is set at the board level */
+ object_property_set_int(OBJECT(&s->fmc), sc->info->memmap[ASPEED_SDRAM],
+ "sdram-base", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
object_property_set_bool(OBJECT(&s->fmc), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, ASPEED_SOC_FMC_BASE);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, sc->info->memmap[ASPEED_FMC]);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1,
s->fmc.ctrl->flash_window_base);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0,
- qdev_get_gpio_in(DEVICE(&s->vic), 19));
+ aspeed_soc_get_irq(s, ASPEED_FMC));
/* SPI */
for (i = 0; i < sc->info->spis_num; i++) {
@@ -261,7 +372,8 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, sc->info->spi_bases[i]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0,
+ sc->info->memmap[ASPEED_SPI1 + i]);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1,
s->spi[i].ctrl->flash_window_base);
}
@@ -272,7 +384,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, ASPEED_SOC_SDMC_BASE);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, sc->info->memmap[ASPEED_SDMC]);
/* Watch dog */
for (i = 0; i < sc->info->wdts_num; i++) {
@@ -282,23 +394,42 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0,
- ASPEED_SOC_WDT_BASE + i * 0x20);
+ sc->info->memmap[ASPEED_WDT] + i * 0x20);
}
/* Net */
- qdev_set_nic_properties(DEVICE(&s->ftgmac100), &nd_table[0]);
- object_property_set_bool(OBJECT(&s->ftgmac100), true, "aspeed", &err);
- object_property_set_bool(OBJECT(&s->ftgmac100), true, "realized",
- &local_err);
- error_propagate(&err, local_err);
+ for (i = 0; i < nb_nics; i++) {
+ qdev_set_nic_properties(DEVICE(&s->ftgmac100[i]), &nd_table[i]);
+ object_property_set_bool(OBJECT(&s->ftgmac100[i]), true, "aspeed",
+ &err);
+ object_property_set_bool(OBJECT(&s->ftgmac100[i]), true, "realized",
+ &local_err);
+ error_propagate(&err, local_err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0,
+ sc->info->memmap[ASPEED_ETH1 + i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0,
+ aspeed_soc_get_irq(s, ASPEED_ETH1 + i));
+ }
+
+ /* XDMA */
+ object_property_set_bool(OBJECT(&s->xdma), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100), 0, ASPEED_SOC_ETH1_BASE);
- sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100), 0,
- qdev_get_gpio_in(DEVICE(&s->vic), 2));
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->xdma), 0,
+ sc->info->memmap[ASPEED_XDMA]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0,
+ aspeed_soc_get_irq(s, ASPEED_XDMA));
}
+static Property aspeed_soc_properties[] = {
+ DEFINE_PROP_UINT32("num-cpus", AspeedSoCState, num_cpus, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
static void aspeed_soc_class_init(ObjectClass *oc, void *data)
{
@@ -309,6 +440,7 @@ static void aspeed_soc_class_init(ObjectClass *oc, void *data)
dc->realize = aspeed_soc_realize;
/* Reason: Uses serial_hds and nd_table in realize() directly */
dc->user_creatable = false;
+ dc->props = aspeed_soc_properties;
}
static const TypeInfo aspeed_soc_type_info = {
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index b2f93f6bef..1fb24fbef2 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -1109,10 +1109,11 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
info->initrd_filename);
exit(1);
}
- if (info->initrd_start + initrd_size > info->ram_size) {
+ if (info->initrd_start + initrd_size > ram_end) {
error_report("could not load initrd '%s': "
"too big to fit into RAM after the kernel",
info->initrd_filename);
+ exit(1);
}
} else {
initrd_size = 0;
diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c
index b7e3526b4f..2eddf3f25c 100644
--- a/hw/arm/fsl-imx7.c
+++ b/hw/arm/fsl-imx7.c
@@ -526,6 +526,17 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp)
*/
create_unimplemented_device("lcdif", FSL_IMX7_LCDIF_ADDR,
FSL_IMX7_LCDIF_SIZE);
+
+ /*
+ * DMA APBH
+ */
+ create_unimplemented_device("dma-apbh", FSL_IMX7_DMA_APBH_ADDR,
+ FSL_IMX7_DMA_APBH_SIZE);
+ /*
+ * PCIe PHY
+ */
+ create_unimplemented_device("pcie-phy", FSL_IMX7_PCIE_PHY_ADDR,
+ FSL_IMX7_PCIE_PHY_SIZE);
}
static void fsl_imx7_class_init(ObjectClass *oc, void *data)
diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c
index 8c550a8bdd..2c9984bb3b 100644
--- a/hw/arm/msf2-som.c
+++ b/hw/arm/msf2-som.c
@@ -53,6 +53,7 @@ static void emcraft_sf2_s2s010_init(MachineState *machine)
if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
error_report("This board can only be used with CPU %s",
mc->default_cpu_type);
+ exit(1);
}
memory_region_init_ram(ddr, NULL, "ddr-ram", DDR_SIZE,
diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c
new file mode 100644
index 0000000000..ee53f0ff60
--- /dev/null
+++ b/hw/arm/sbsa-ref.c
@@ -0,0 +1,806 @@
+/*
+ * ARM SBSA Reference Platform emulation
+ *
+ * Copyright (c) 2018 Linaro Limited
+ * Written by Hongbo Zhang <hongbo.zhang@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/units.h"
+#include "sysemu/device_tree.h"
+#include "sysemu/numa.h"
+#include "sysemu/sysemu.h"
+#include "exec/address-spaces.h"
+#include "exec/hwaddr.h"
+#include "kvm_arm.h"
+#include "hw/arm/boot.h"
+#include "hw/block/flash.h"
+#include "hw/boards.h"
+#include "hw/ide/internal.h"
+#include "hw/ide/ahci_internal.h"
+#include "hw/intc/arm_gicv3_common.h"
+#include "hw/loader.h"
+#include "hw/pci-host/gpex.h"
+#include "hw/usb.h"
+#include "net/net.h"
+
+#define RAMLIMIT_GB 8192
+#define RAMLIMIT_BYTES (RAMLIMIT_GB * GiB)
+
+#define NUM_IRQS 256
+#define NUM_SMMU_IRQS 4
+#define NUM_SATA_PORTS 6
+
+#define VIRTUAL_PMU_IRQ 7
+#define ARCH_GIC_MAINT_IRQ 9
+#define ARCH_TIMER_VIRT_IRQ 11
+#define ARCH_TIMER_S_EL1_IRQ 13
+#define ARCH_TIMER_NS_EL1_IRQ 14
+#define ARCH_TIMER_NS_EL2_IRQ 10
+
+enum {
+ SBSA_FLASH,
+ SBSA_MEM,
+ SBSA_CPUPERIPHS,
+ SBSA_GIC_DIST,
+ SBSA_GIC_REDIST,
+ SBSA_SMMU,
+ SBSA_UART,
+ SBSA_RTC,
+ SBSA_PCIE,
+ SBSA_PCIE_MMIO,
+ SBSA_PCIE_MMIO_HIGH,
+ SBSA_PCIE_PIO,
+ SBSA_PCIE_ECAM,
+ SBSA_GPIO,
+ SBSA_SECURE_UART,
+ SBSA_SECURE_UART_MM,
+ SBSA_SECURE_MEM,
+ SBSA_AHCI,
+ SBSA_EHCI,
+};
+
+typedef struct MemMapEntry {
+ hwaddr base;
+ hwaddr size;
+} MemMapEntry;
+
+typedef struct {
+ MachineState parent;
+ struct arm_boot_info bootinfo;
+ int smp_cpus;
+ void *fdt;
+ int fdt_size;
+ int psci_conduit;
+ PFlashCFI01 *flash[2];
+} SBSAMachineState;
+
+#define TYPE_SBSA_MACHINE MACHINE_TYPE_NAME("sbsa-ref")
+#define SBSA_MACHINE(obj) \
+ OBJECT_CHECK(SBSAMachineState, (obj), TYPE_SBSA_MACHINE)
+
+static const MemMapEntry sbsa_ref_memmap[] = {
+ /* 512M boot ROM */
+ [SBSA_FLASH] = { 0, 0x20000000 },
+ /* 512M secure memory */
+ [SBSA_SECURE_MEM] = { 0x20000000, 0x20000000 },
+ /* Space reserved for CPU peripheral devices */
+ [SBSA_CPUPERIPHS] = { 0x40000000, 0x00040000 },
+ [SBSA_GIC_DIST] = { 0x40060000, 0x00010000 },
+ [SBSA_GIC_REDIST] = { 0x40080000, 0x04000000 },
+ [SBSA_UART] = { 0x60000000, 0x00001000 },
+ [SBSA_RTC] = { 0x60010000, 0x00001000 },
+ [SBSA_GPIO] = { 0x60020000, 0x00001000 },
+ [SBSA_SECURE_UART] = { 0x60030000, 0x00001000 },
+ [SBSA_SECURE_UART_MM] = { 0x60040000, 0x00001000 },
+ [SBSA_SMMU] = { 0x60050000, 0x00020000 },
+ /* Space here reserved for more SMMUs */
+ [SBSA_AHCI] = { 0x60100000, 0x00010000 },
+ [SBSA_EHCI] = { 0x60110000, 0x00010000 },
+ /* Space here reserved for other devices */
+ [SBSA_PCIE_PIO] = { 0x7fff0000, 0x00010000 },
+ /* 32-bit address PCIE MMIO space */
+ [SBSA_PCIE_MMIO] = { 0x80000000, 0x70000000 },
+ /* 256M PCIE ECAM space */
+ [SBSA_PCIE_ECAM] = { 0xf0000000, 0x10000000 },
+ /* ~1TB PCIE MMIO space (4GB to 1024GB boundary) */
+ [SBSA_PCIE_MMIO_HIGH] = { 0x100000000ULL, 0xFF00000000ULL },
+ [SBSA_MEM] = { 0x10000000000ULL, RAMLIMIT_BYTES },
+};
+
+static const int sbsa_ref_irqmap[] = {
+ [SBSA_UART] = 1,
+ [SBSA_RTC] = 2,
+ [SBSA_PCIE] = 3, /* ... to 6 */
+ [SBSA_GPIO] = 7,
+ [SBSA_SECURE_UART] = 8,
+ [SBSA_SECURE_UART_MM] = 9,
+ [SBSA_AHCI] = 10,
+ [SBSA_EHCI] = 11,
+};
+
+/*
+ * Firmware on this machine only uses ACPI table to load OS, these limited
+ * device tree nodes are just to let firmware know the info which varies from
+ * command line parameters, so it is not necessary to be fully compatible
+ * with the kernel CPU and NUMA binding rules.
+ */
+static void create_fdt(SBSAMachineState *sms)
+{
+ void *fdt = create_device_tree(&sms->fdt_size);
+ const MachineState *ms = MACHINE(sms);
+ int cpu;
+
+ if (!fdt) {
+ error_report("create_device_tree() failed");
+ exit(1);
+ }
+
+ sms->fdt = fdt;
+
+ qemu_fdt_setprop_string(fdt, "/", "compatible", "linux,sbsa-ref");
+ qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
+
+ if (have_numa_distance) {
+ int size = nb_numa_nodes * nb_numa_nodes * 3 * sizeof(uint32_t);
+ uint32_t *matrix = g_malloc0(size);
+ int idx, i, j;
+
+ for (i = 0; i < nb_numa_nodes; i++) {
+ for (j = 0; j < nb_numa_nodes; j++) {
+ idx = (i * nb_numa_nodes + j) * 3;
+ matrix[idx + 0] = cpu_to_be32(i);
+ matrix[idx + 1] = cpu_to_be32(j);
+ matrix[idx + 2] = cpu_to_be32(numa_info[i].distance[j]);
+ }
+ }
+
+ qemu_fdt_add_subnode(fdt, "/distance-map");
+ qemu_fdt_setprop(fdt, "/distance-map", "distance-matrix",
+ matrix, size);
+ g_free(matrix);
+ }
+
+ qemu_fdt_add_subnode(sms->fdt, "/cpus");
+
+ for (cpu = sms->smp_cpus - 1; cpu >= 0; cpu--) {
+ char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
+ ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu));
+ CPUState *cs = CPU(armcpu);
+
+ qemu_fdt_add_subnode(sms->fdt, nodename);
+
+ if (ms->possible_cpus->cpus[cs->cpu_index].props.has_node_id) {
+ qemu_fdt_setprop_cell(sms->fdt, nodename, "numa-node-id",
+ ms->possible_cpus->cpus[cs->cpu_index].props.node_id);
+ }
+
+ g_free(nodename);
+ }
+}
+
+#define SBSA_FLASH_SECTOR_SIZE (256 * KiB)
+
+static PFlashCFI01 *sbsa_flash_create1(SBSAMachineState *sms,
+ const char *name,
+ const char *alias_prop_name)
+{
+ /*
+ * Create a single flash device. We use the same parameters as
+ * the flash devices on the Versatile Express board.
+ */
+ DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01);
+
+ qdev_prop_set_uint64(dev, "sector-length", SBSA_FLASH_SECTOR_SIZE);
+ qdev_prop_set_uint8(dev, "width", 4);
+ qdev_prop_set_uint8(dev, "device-width", 2);
+ qdev_prop_set_bit(dev, "big-endian", false);
+ qdev_prop_set_uint16(dev, "id0", 0x89);
+ qdev_prop_set_uint16(dev, "id1", 0x18);
+ qdev_prop_set_uint16(dev, "id2", 0x00);
+ qdev_prop_set_uint16(dev, "id3", 0x00);
+ qdev_prop_set_string(dev, "name", name);
+ object_property_add_child(OBJECT(sms), name, OBJECT(dev),
+ &error_abort);
+ object_property_add_alias(OBJECT(sms), alias_prop_name,
+ OBJECT(dev), "drive", &error_abort);
+ return PFLASH_CFI01(dev);
+}
+
+static void sbsa_flash_create(SBSAMachineState *sms)
+{
+ sms->flash[0] = sbsa_flash_create1(sms, "sbsa.flash0", "pflash0");
+ sms->flash[1] = sbsa_flash_create1(sms, "sbsa.flash1", "pflash1");
+}
+
+static void sbsa_flash_map1(PFlashCFI01 *flash,
+ hwaddr base, hwaddr size,
+ MemoryRegion *sysmem)
+{
+ DeviceState *dev = DEVICE(flash);
+
+ assert(size % SBSA_FLASH_SECTOR_SIZE == 0);
+ assert(size / SBSA_FLASH_SECTOR_SIZE <= UINT32_MAX);
+ qdev_prop_set_uint32(dev, "num-blocks", size / SBSA_FLASH_SECTOR_SIZE);
+ qdev_init_nofail(dev);
+
+ memory_region_add_subregion(sysmem, base,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(dev),
+ 0));
+}
+
+static void sbsa_flash_map(SBSAMachineState *sms,
+ MemoryRegion *sysmem,
+ MemoryRegion *secure_sysmem)
+{
+ /*
+ * Map two flash devices to fill the SBSA_FLASH space in the memmap.
+ * sysmem is the system memory space. secure_sysmem is the secure view
+ * of the system, and the first flash device should be made visible only
+ * there. The second flash device is visible to both secure and nonsecure.
+ * If sysmem == secure_sysmem this means there is no separate Secure
+ * address space and both flash devices are generally visible.
+ */
+ hwaddr flashsize = sbsa_ref_memmap[SBSA_FLASH].size / 2;
+ hwaddr flashbase = sbsa_ref_memmap[SBSA_FLASH].base;
+
+ sbsa_flash_map1(sms->flash[0], flashbase, flashsize,
+ secure_sysmem);
+ sbsa_flash_map1(sms->flash[1], flashbase + flashsize, flashsize,
+ sysmem);
+}
+
+static bool sbsa_firmware_init(SBSAMachineState *sms,
+ MemoryRegion *sysmem,
+ MemoryRegion *secure_sysmem)
+{
+ int i;
+ BlockBackend *pflash_blk0;
+
+ /* Map legacy -drive if=pflash to machine properties */
+ for (i = 0; i < ARRAY_SIZE(sms->flash); i++) {
+ pflash_cfi01_legacy_drive(sms->flash[i],
+ drive_get(IF_PFLASH, 0, i));
+ }
+
+ sbsa_flash_map(sms, sysmem, secure_sysmem);
+
+ pflash_blk0 = pflash_cfi01_get_blk(sms->flash[0]);
+
+ if (bios_name) {
+ char *fname;
+ MemoryRegion *mr;
+ int image_size;
+
+ if (pflash_blk0) {
+ error_report("The contents of the first flash device may be "
+ "specified with -bios or with -drive if=pflash... "
+ "but you cannot use both options at once");
+ exit(1);
+ }
+
+ /* Fall back to -bios */
+
+ fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (!fname) {
+ error_report("Could not find ROM image '%s'", bios_name);
+ exit(1);
+ }
+ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(sms->flash[0]), 0);
+ image_size = load_image_mr(fname, mr);
+ g_free(fname);
+ if (image_size < 0) {
+ error_report("Could not load ROM image '%s'", bios_name);
+ exit(1);
+ }
+ }
+
+ return pflash_blk0 || bios_name;
+}
+
+static void create_secure_ram(SBSAMachineState *sms,
+ MemoryRegion *secure_sysmem)
+{
+ MemoryRegion *secram = g_new(MemoryRegion, 1);
+ hwaddr base = sbsa_ref_memmap[SBSA_SECURE_MEM].base;
+ hwaddr size = sbsa_ref_memmap[SBSA_SECURE_MEM].size;
+
+ memory_region_init_ram(secram, NULL, "sbsa-ref.secure-ram", size,
+ &error_fatal);
+ memory_region_add_subregion(secure_sysmem, base, secram);
+}
+
+static void create_gic(SBSAMachineState *sms, qemu_irq *pic)
+{
+ DeviceState *gicdev;
+ SysBusDevice *gicbusdev;
+ const char *gictype;
+ uint32_t redist0_capacity, redist0_count;
+ int i;
+
+ gictype = gicv3_class_name();
+
+ gicdev = qdev_create(NULL, gictype);
+ qdev_prop_set_uint32(gicdev, "revision", 3);
+ qdev_prop_set_uint32(gicdev, "num-cpu", smp_cpus);
+ /*
+ * Note that the num-irq property counts both internal and external
+ * interrupts; there are always 32 of the former (mandated by GIC spec).
+ */
+ qdev_prop_set_uint32(gicdev, "num-irq", NUM_IRQS + 32);
+ qdev_prop_set_bit(gicdev, "has-security-extensions", true);
+
+ redist0_capacity =
+ sbsa_ref_memmap[SBSA_GIC_REDIST].size / GICV3_REDIST_SIZE;
+ redist0_count = MIN(smp_cpus, redist0_capacity);
+
+ qdev_prop_set_uint32(gicdev, "len-redist-region-count", 1);
+ qdev_prop_set_uint32(gicdev, "redist-region-count[0]", redist0_count);
+
+ qdev_init_nofail(gicdev);
+ gicbusdev = SYS_BUS_DEVICE(gicdev);
+ sysbus_mmio_map(gicbusdev, 0, sbsa_ref_memmap[SBSA_GIC_DIST].base);
+ sysbus_mmio_map(gicbusdev, 1, sbsa_ref_memmap[SBSA_GIC_REDIST].base);
+
+ /*
+ * Wire the outputs from each CPU's generic timer and the GICv3
+ * maintenance interrupt signal to the appropriate GIC PPI inputs,
+ * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs.
+ */
+ for (i = 0; i < smp_cpus; i++) {
+ DeviceState *cpudev = DEVICE(qemu_get_cpu(i));
+ int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS;
+ int irq;
+ /*
+ * Mapping from the output timer irq lines from the CPU to the
+ * GIC PPI inputs used for this board.
+ */
+ const int timer_irq[] = {
+ [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ,
+ [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ,
+ [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ,
+ [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ,
+ };
+
+ for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
+ qdev_connect_gpio_out(cpudev, irq,
+ qdev_get_gpio_in(gicdev,
+ ppibase + timer_irq[irq]));
+ }
+
+ qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", 0,
+ qdev_get_gpio_in(gicdev, ppibase
+ + ARCH_GIC_MAINT_IRQ));
+ qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0,
+ qdev_get_gpio_in(gicdev, ppibase
+ + VIRTUAL_PMU_IRQ));
+
+ sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
+ sysbus_connect_irq(gicbusdev, i + smp_cpus,
+ qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
+ sysbus_connect_irq(gicbusdev, i + 2 * smp_cpus,
+ qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
+ sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus,
+ qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
+ }
+
+ for (i = 0; i < NUM_IRQS; i++) {
+ pic[i] = qdev_get_gpio_in(gicdev, i);
+ }
+}
+
+static void create_uart(const SBSAMachineState *sms, qemu_irq *pic, int uart,
+ MemoryRegion *mem, Chardev *chr)
+{
+ hwaddr base = sbsa_ref_memmap[uart].base;
+ int irq = sbsa_ref_irqmap[uart];
+ DeviceState *dev = qdev_create(NULL, "pl011");
+ SysBusDevice *s = SYS_BUS_DEVICE(dev);
+
+ qdev_prop_set_chr(dev, "chardev", chr);
+ qdev_init_nofail(dev);
+ memory_region_add_subregion(mem, base,
+ sysbus_mmio_get_region(s, 0));
+ sysbus_connect_irq(s, 0, pic[irq]);
+}
+
+static void create_rtc(const SBSAMachineState *sms, qemu_irq *pic)
+{
+ hwaddr base = sbsa_ref_memmap[SBSA_RTC].base;
+ int irq = sbsa_ref_irqmap[SBSA_RTC];
+
+ sysbus_create_simple("pl031", base, pic[irq]);
+}
+
+static DeviceState *gpio_key_dev;
+static void sbsa_ref_powerdown_req(Notifier *n, void *opaque)
+{
+ /* use gpio Pin 3 for power button event */
+ qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1);
+}
+
+static Notifier sbsa_ref_powerdown_notifier = {
+ .notify = sbsa_ref_powerdown_req
+};
+
+static void create_gpio(const SBSAMachineState *sms, qemu_irq *pic)
+{
+ DeviceState *pl061_dev;
+ hwaddr base = sbsa_ref_memmap[SBSA_GPIO].base;
+ int irq = sbsa_ref_irqmap[SBSA_GPIO];
+
+ pl061_dev = sysbus_create_simple("pl061", base, pic[irq]);
+
+ gpio_key_dev = sysbus_create_simple("gpio-key", -1,
+ qdev_get_gpio_in(pl061_dev, 3));
+
+ /* connect powerdown request */
+ qemu_register_powerdown_notifier(&sbsa_ref_powerdown_notifier);
+}
+
+static void create_ahci(const SBSAMachineState *sms, qemu_irq *pic)
+{
+ hwaddr base = sbsa_ref_memmap[SBSA_AHCI].base;
+ int irq = sbsa_ref_irqmap[SBSA_AHCI];
+ DeviceState *dev;
+ DriveInfo *hd[NUM_SATA_PORTS];
+ SysbusAHCIState *sysahci;
+ AHCIState *ahci;
+ int i;
+
+ dev = qdev_create(NULL, "sysbus-ahci");
+ qdev_prop_set_uint32(dev, "num-ports", NUM_SATA_PORTS);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[irq]);
+
+ sysahci = SYSBUS_AHCI(dev);
+ ahci = &sysahci->ahci;
+ ide_drive_get(hd, ARRAY_SIZE(hd));
+ for (i = 0; i < ahci->ports; i++) {
+ if (hd[i] == NULL) {
+ continue;
+ }
+ ide_create_drive(&ahci->dev[i].port, 0, hd[i]);
+ }
+}
+
+static void create_ehci(const SBSAMachineState *sms, qemu_irq *pic)
+{
+ hwaddr base = sbsa_ref_memmap[SBSA_EHCI].base;
+ int irq = sbsa_ref_irqmap[SBSA_EHCI];
+
+ sysbus_create_simple("platform-ehci-usb", base, pic[irq]);
+}
+
+static void create_smmu(const SBSAMachineState *sms, qemu_irq *pic,
+ PCIBus *bus)
+{
+ hwaddr base = sbsa_ref_memmap[SBSA_SMMU].base;
+ int irq = sbsa_ref_irqmap[SBSA_SMMU];
+ DeviceState *dev;
+ int i;
+
+ dev = qdev_create(NULL, "arm-smmuv3");
+
+ object_property_set_link(OBJECT(dev), OBJECT(bus), "primary-bus",
+ &error_abort);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
+ for (i = 0; i < NUM_SMMU_IRQS; i++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]);
+ }
+}
+
+static void create_pcie(SBSAMachineState *sms, qemu_irq *pic)
+{
+ hwaddr base_ecam = sbsa_ref_memmap[SBSA_PCIE_ECAM].base;
+ hwaddr size_ecam = sbsa_ref_memmap[SBSA_PCIE_ECAM].size;
+ hwaddr base_mmio = sbsa_ref_memmap[SBSA_PCIE_MMIO].base;
+ hwaddr size_mmio = sbsa_ref_memmap[SBSA_PCIE_MMIO].size;
+ hwaddr base_mmio_high = sbsa_ref_memmap[SBSA_PCIE_MMIO_HIGH].base;
+ hwaddr size_mmio_high = sbsa_ref_memmap[SBSA_PCIE_MMIO_HIGH].size;
+ hwaddr base_pio = sbsa_ref_memmap[SBSA_PCIE_PIO].base;
+ int irq = sbsa_ref_irqmap[SBSA_PCIE];
+ MemoryRegion *mmio_alias, *mmio_alias_high, *mmio_reg;
+ MemoryRegion *ecam_alias, *ecam_reg;
+ DeviceState *dev;
+ PCIHostState *pci;
+ int i;
+
+ dev = qdev_create(NULL, TYPE_GPEX_HOST);
+ qdev_init_nofail(dev);
+
+ /* Map ECAM space */
+ ecam_alias = g_new0(MemoryRegion, 1);
+ ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
+ memory_region_init_alias(ecam_alias, OBJECT(dev), "pcie-ecam",
+ ecam_reg, 0, size_ecam);
+ memory_region_add_subregion(get_system_memory(), base_ecam, ecam_alias);
+
+ /* Map the MMIO space */
+ mmio_alias = g_new0(MemoryRegion, 1);
+ mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
+ memory_region_init_alias(mmio_alias, OBJECT(dev), "pcie-mmio",
+ mmio_reg, base_mmio, size_mmio);
+ memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias);
+
+ /* Map the MMIO_HIGH space */
+ mmio_alias_high = g_new0(MemoryRegion, 1);
+ memory_region_init_alias(mmio_alias_high, OBJECT(dev), "pcie-mmio-high",
+ mmio_reg, base_mmio_high, size_mmio_high);
+ memory_region_add_subregion(get_system_memory(), base_mmio_high,
+ mmio_alias_high);
+
+ /* Map IO port space */
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio);
+
+ for (i = 0; i < GPEX_NUM_IRQS; i++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]);
+ gpex_set_irq_num(GPEX_HOST(dev), i, irq + i);
+ }
+
+ pci = PCI_HOST_BRIDGE(dev);
+ if (pci->bus) {
+ for (i = 0; i < nb_nics; i++) {
+ NICInfo *nd = &nd_table[i];
+
+ if (!nd->model) {
+ nd->model = g_strdup("e1000e");
+ }
+
+ pci_nic_init_nofail(nd, pci->bus, nd->model, NULL);
+ }
+ }
+
+ pci_create_simple(pci->bus, -1, "VGA");
+
+ create_smmu(sms, pic, pci->bus);
+}
+
+static void *sbsa_ref_dtb(const struct arm_boot_info *binfo, int *fdt_size)
+{
+ const SBSAMachineState *board = container_of(binfo, SBSAMachineState,
+ bootinfo);
+
+ *fdt_size = board->fdt_size;
+ return board->fdt;
+}
+
+static void sbsa_ref_init(MachineState *machine)
+{
+ SBSAMachineState *sms = SBSA_MACHINE(machine);
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ MemoryRegion *sysmem = get_system_memory();
+ MemoryRegion *secure_sysmem = NULL;
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ bool firmware_loaded;
+ const CPUArchIdList *possible_cpus;
+ int n, sbsa_max_cpus;
+ qemu_irq pic[NUM_IRQS];
+
+ if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a57"))) {
+ error_report("sbsa-ref: CPU type other than the built-in "
+ "cortex-a57 not supported");
+ exit(1);
+ }
+
+ if (kvm_enabled()) {
+ error_report("sbsa-ref: KVM is not supported for this machine");
+ exit(1);
+ }
+
+ /*
+ * The Secure view of the world is the same as the NonSecure,
+ * but with a few extra devices. Create it as a container region
+ * containing the system memory at low priority; any secure-only
+ * devices go in at higher priority and take precedence.
+ */
+ secure_sysmem = g_new(MemoryRegion, 1);
+ memory_region_init(secure_sysmem, OBJECT(machine), "secure-memory",
+ UINT64_MAX);
+ memory_region_add_subregion_overlap(secure_sysmem, 0, sysmem, -1);
+
+ firmware_loaded = sbsa_firmware_init(sms, sysmem,
+ secure_sysmem ?: sysmem);
+
+ if (machine->kernel_filename && firmware_loaded) {
+ error_report("sbsa-ref: No fw_cfg device on this machine, "
+ "so -kernel option is not supported when firmware loaded, "
+ "please load OS from hard disk instead");
+ exit(1);
+ }
+
+ /*
+ * This machine has EL3 enabled, external firmware should supply PSCI
+ * implementation, so the QEMU's internal PSCI is disabled.
+ */
+ sms->psci_conduit = QEMU_PSCI_CONDUIT_DISABLED;
+
+ sbsa_max_cpus = sbsa_ref_memmap[SBSA_GIC_REDIST].size / GICV3_REDIST_SIZE;
+
+ if (max_cpus > sbsa_max_cpus) {
+ error_report("Number of SMP CPUs requested (%d) exceeds max CPUs "
+ "supported by machine 'sbsa-ref' (%d)",
+ max_cpus, sbsa_max_cpus);
+ exit(1);
+ }
+
+ sms->smp_cpus = smp_cpus;
+
+ if (machine->ram_size > sbsa_ref_memmap[SBSA_MEM].size) {
+ error_report("sbsa-ref: cannot model more than %dGB RAM", RAMLIMIT_GB);
+ exit(1);
+ }
+
+ possible_cpus = mc->possible_cpu_arch_ids(machine);
+ for (n = 0; n < possible_cpus->len; n++) {
+ Object *cpuobj;
+ CPUState *cs;
+
+ if (n >= smp_cpus) {
+ break;
+ }
+
+ cpuobj = object_new(possible_cpus->cpus[n].type);
+ object_property_set_int(cpuobj, possible_cpus->cpus[n].arch_id,
+ "mp-affinity", NULL);
+
+ cs = CPU(cpuobj);
+ cs->cpu_index = n;
+
+ numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj),
+ &error_fatal);
+
+ if (object_property_find(cpuobj, "reset-cbar", NULL)) {
+ object_property_set_int(cpuobj,
+ sbsa_ref_memmap[SBSA_CPUPERIPHS].base,
+ "reset-cbar", &error_abort);
+ }
+
+ object_property_set_link(cpuobj, OBJECT(sysmem), "memory",
+ &error_abort);
+
+ object_property_set_link(cpuobj, OBJECT(secure_sysmem),
+ "secure-memory", &error_abort);
+
+ object_property_set_bool(cpuobj, true, "realized", &error_fatal);
+ object_unref(cpuobj);
+ }
+
+ memory_region_allocate_system_memory(ram, NULL, "sbsa-ref.ram",
+ machine->ram_size);
+ memory_region_add_subregion(sysmem, sbsa_ref_memmap[SBSA_MEM].base, ram);
+
+ create_fdt(sms);
+
+ create_secure_ram(sms, secure_sysmem);
+
+ create_gic(sms, pic);
+
+ create_uart(sms, pic, SBSA_UART, sysmem, serial_hd(0));
+ create_uart(sms, pic, SBSA_SECURE_UART, secure_sysmem, serial_hd(1));
+ /* Second secure UART for RAS and MM from EL0 */
+ create_uart(sms, pic, SBSA_SECURE_UART_MM, secure_sysmem, serial_hd(2));
+
+ create_rtc(sms, pic);
+
+ create_gpio(sms, pic);
+
+ create_ahci(sms, pic);
+
+ create_ehci(sms, pic);
+
+ create_pcie(sms, pic);
+
+ sms->bootinfo.ram_size = machine->ram_size;
+ sms->bootinfo.kernel_filename = machine->kernel_filename;
+ sms->bootinfo.nb_cpus = smp_cpus;
+ sms->bootinfo.board_id = -1;
+ sms->bootinfo.loader_start = sbsa_ref_memmap[SBSA_MEM].base;
+ sms->bootinfo.get_dtb = sbsa_ref_dtb;
+ sms->bootinfo.firmware_loaded = firmware_loaded;
+ arm_load_kernel(ARM_CPU(first_cpu), &sms->bootinfo);
+}
+
+static uint64_t sbsa_ref_cpu_mp_affinity(SBSAMachineState *sms, int idx)
+{
+ uint8_t clustersz = ARM_DEFAULT_CPUS_PER_CLUSTER;
+ return arm_cpu_mp_affinity(idx, clustersz);
+}
+
+static const CPUArchIdList *sbsa_ref_possible_cpu_arch_ids(MachineState *ms)
+{
+ SBSAMachineState *sms = SBSA_MACHINE(ms);
+ int n;
+
+ if (ms->possible_cpus) {
+ assert(ms->possible_cpus->len == max_cpus);
+ return ms->possible_cpus;
+ }
+
+ ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
+ sizeof(CPUArchId) * max_cpus);
+ ms->possible_cpus->len = max_cpus;
+ for (n = 0; n < ms->possible_cpus->len; n++) {
+ ms->possible_cpus->cpus[n].type = ms->cpu_type;
+ ms->possible_cpus->cpus[n].arch_id =
+ sbsa_ref_cpu_mp_affinity(sms, n);
+ ms->possible_cpus->cpus[n].props.has_thread_id = true;
+ ms->possible_cpus->cpus[n].props.thread_id = n;
+ }
+ return ms->possible_cpus;
+}
+
+static CpuInstanceProperties
+sbsa_ref_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
+
+ assert(cpu_index < possible_cpus->len);
+ return possible_cpus->cpus[cpu_index].props;
+}
+
+static int64_t
+sbsa_ref_get_default_cpu_node_id(const MachineState *ms, int idx)
+{
+ return idx % nb_numa_nodes;
+}
+
+static void sbsa_ref_instance_init(Object *obj)
+{
+ SBSAMachineState *sms = SBSA_MACHINE(obj);
+
+ sbsa_flash_create(sms);
+}
+
+static void sbsa_ref_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->init = sbsa_ref_init;
+ mc->desc = "QEMU 'SBSA Reference' ARM Virtual Machine";
+ mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a57");
+ mc->max_cpus = 512;
+ mc->pci_allow_0_address = true;
+ mc->minimum_page_bits = 12;
+ mc->block_default_type = IF_IDE;
+ mc->no_cdrom = 1;
+ mc->default_ram_size = 1 * GiB;
+ mc->default_cpus = 4;
+ mc->possible_cpu_arch_ids = sbsa_ref_possible_cpu_arch_ids;
+ mc->cpu_index_to_instance_props = sbsa_ref_cpu_index_to_props;
+ mc->get_default_cpu_node_id = sbsa_ref_get_default_cpu_node_id;
+}
+
+static const TypeInfo sbsa_ref_info = {
+ .name = TYPE_SBSA_MACHINE,
+ .parent = TYPE_MACHINE,
+ .instance_init = sbsa_ref_instance_init,
+ .class_init = sbsa_ref_class_init,
+ .instance_size = sizeof(SBSAMachineState),
+};
+
+static void sbsa_ref_machine_init(void)
+{
+ type_register_static(&sbsa_ref_info);
+}
+
+type_init(sbsa_ref_machine_init);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 431e2900fd..ed009fa447 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -176,6 +176,7 @@ static const int a15irqmap[] = {
};
static const char *valid_cpus[] = {
+ ARM_CPU_TYPE_NAME("cortex-a7"),
ARM_CPU_TYPE_NAME("cortex-a15"),
ARM_CPU_TYPE_NAME("cortex-a53"),
ARM_CPU_TYPE_NAME("cortex-a57"),
diff --git a/hw/display/Kconfig b/hw/display/Kconfig
index 910dccb2f7..cbdf7b1a67 100644
--- a/hw/display/Kconfig
+++ b/hw/display/Kconfig
@@ -130,3 +130,5 @@ config ATI_VGA
default y if PCI_DEVICES
depends on PCI
select VGA
+ select BITBANG_I2C
+ select DDC
diff --git a/hw/display/ati.c b/hw/display/ati.c
index 76595d9511..0cb1173848 100644
--- a/hw/display/ati.c
+++ b/hw/display/ati.c
@@ -26,6 +26,7 @@
#include "qapi/error.h"
#include "hw/hw.h"
#include "ui/console.h"
+#include "hw/display/i2c-ddc.h"
#include "trace.h"
#define ATI_DEBUG_HW_CURSOR 0
@@ -215,6 +216,24 @@ static void ati_cursor_draw_line(VGACommonState *vga, uint8_t *d, int scr_y)
}
}
+static uint64_t ati_i2c(bitbang_i2c_interface *i2c, uint64_t data, int base)
+{
+ bool c = (data & BIT(base + 17) ? !!(data & BIT(base + 1)) : 1);
+ bool d = (data & BIT(base + 16) ? !!(data & BIT(base)) : 1);
+
+ bitbang_i2c_set(i2c, BITBANG_I2C_SCL, c);
+ d = bitbang_i2c_set(i2c, BITBANG_I2C_SDA, d);
+
+ data &= ~0xf00ULL;
+ if (c) {
+ data |= BIT(base + 9);
+ }
+ if (d) {
+ data |= BIT(base + 8);
+ }
+ return data;
+}
+
static inline uint64_t ati_reg_read_offs(uint32_t reg, int offs,
unsigned int size)
{
@@ -266,7 +285,16 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
case DAC_CNTL:
val = s->regs.dac_cntl;
break;
-/* case GPIO_MONID: FIXME hook up DDC I2C here */
+ case GPIO_VGA_DDC:
+ val = s->regs.gpio_vga_ddc;
+ break;
+ case GPIO_DVI_DDC:
+ val = s->regs.gpio_dvi_ddc;
+ break;
+ case GPIO_MONID ... GPIO_MONID + 3:
+ val = ati_reg_read_offs(s->regs.gpio_monid,
+ addr - GPIO_MONID, size);
+ break;
case PALETTE_INDEX:
/* FIXME unaligned access */
val = vga_ioport_read(&s->vga, VGA_PEL_IR) << 16;
@@ -391,9 +419,15 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
break;
case DEFAULT_OFFSET:
val = s->regs.default_offset;
+ if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) {
+ val >>= 10;
+ val |= s->regs.default_pitch << 16;
+ val |= s->regs.default_tile << 30;
+ }
break;
case DEFAULT_PITCH:
val = s->regs.default_pitch;
+ val |= s->regs.default_tile << 16;
break;
case DEFAULT_SC_BOTTOM_RIGHT:
val = s->regs.default_sc_bottom_right;
@@ -497,7 +531,33 @@ static void ati_mm_write(void *opaque, hwaddr addr,
s->regs.dac_cntl = data & 0xffffe3ff;
s->vga.dac_8bit = !!(data & DAC_8BIT_EN);
break;
-/* case GPIO_MONID: FIXME hook up DDC I2C here */
+ case GPIO_VGA_DDC:
+ if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) {
+ /* FIXME: Maybe add a property to select VGA or DVI port? */
+ }
+ break;
+ case GPIO_DVI_DDC:
+ if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) {
+ s->regs.gpio_dvi_ddc = ati_i2c(s->bbi2c, data, 0);
+ }
+ break;
+ case GPIO_MONID ... GPIO_MONID + 3:
+ /* FIXME What does Radeon have here? */
+ if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
+ ati_reg_write_offs(&s->regs.gpio_monid,
+ addr - GPIO_MONID, data, size);
+ /*
+ * Rage128p accesses DDC used to get EDID via these bits.
+ * Only touch i2c when write overlaps 3rd byte because some
+ * drivers access this reg via multiple partial writes and
+ * without this spurious bits would be sent.
+ */
+ if ((s->regs.gpio_monid & BIT(25)) &&
+ addr <= GPIO_MONID + 2 && addr + size > GPIO_MONID + 2) {
+ s->regs.gpio_monid = ati_i2c(s->bbi2c, s->regs.gpio_monid, 1);
+ }
+ }
+ break;
case PALETTE_INDEX ... PALETTE_INDEX + 3:
if (size == 4) {
vga_ioport_write(&s->vga, VGA_PEL_IR, (data >> 16) & 0xff);
@@ -628,22 +688,22 @@ static void ati_mm_write(void *opaque, hwaddr addr,
break;
case SRC_PITCH_OFFSET:
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
- s->regs.src_offset = (data & 0x1fffff) << 5;
- s->regs.src_pitch = (data >> 21) & 0x3ff;
+ s->regs.src_offset = (data & 0x1fffff) << 4;
+ s->regs.src_pitch = (data & 0x7fe00000) >> 21;
s->regs.src_tile = data >> 31;
} else {
- s->regs.src_offset = (data & 0x3fffff) << 11;
+ s->regs.src_offset = (data & 0x3fffff) << 10;
s->regs.src_pitch = (data & 0x3fc00000) >> 16;
s->regs.src_tile = (data >> 30) & 1;
}
break;
case DST_PITCH_OFFSET:
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
- s->regs.dst_offset = (data & 0x1fffff) << 5;
- s->regs.dst_pitch = (data >> 21) & 0x3ff;
+ s->regs.dst_offset = (data & 0x1fffff) << 4;
+ s->regs.dst_pitch = (data & 0x7fe00000) >> 21;
s->regs.dst_tile = data >> 31;
} else {
- s->regs.dst_offset = (data & 0x3fffff) << 11;
+ s->regs.dst_offset = (data & 0x3fffff) << 10;
s->regs.dst_pitch = (data & 0x3fc00000) >> 16;
s->regs.dst_tile = data >> 30;
}
@@ -723,13 +783,19 @@ static void ati_mm_write(void *opaque, hwaddr addr,
s->regs.dp_write_mask = data;
break;
case DEFAULT_OFFSET:
- data &= (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF ?
- 0x03fffc00 : 0xfffffc00);
- s->regs.default_offset = data;
+ if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
+ s->regs.default_offset = data & 0xfffffff0;
+ } else {
+ /* Radeon has DEFAULT_PITCH_OFFSET here like DST_PITCH_OFFSET */
+ s->regs.default_offset = (data & 0x3fffff) << 10;
+ s->regs.default_pitch = (data & 0x3fc00000) >> 16;
+ s->regs.default_tile = data >> 30;
+ }
break;
case DEFAULT_PITCH:
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
- s->regs.default_pitch = data & 0x103ff;
+ s->regs.default_pitch = data & 0x3fff;
+ s->regs.default_tile = (data >> 16) & 1;
}
break;
case DEFAULT_SC_BOTTOM_RIGHT:
@@ -788,6 +854,12 @@ static void ati_vga_realize(PCIDevice *dev, Error **errp)
vga->cursor_draw_line = ati_cursor_draw_line;
}
+ /* ddc, edid */
+ I2CBus *i2cbus = i2c_init_bus(DEVICE(s), "ati-vga.ddc");
+ s->bbi2c = bitbang_i2c_init(i2cbus);
+ I2CSlave *i2cddc = I2C_SLAVE(qdev_create(BUS(i2cbus), TYPE_I2CDDC));
+ i2c_set_slave_address(i2cddc, 0x50);
+
/* mmio register space */
memory_region_init_io(&s->mm, OBJECT(s), &ati_mm_ops, s,
"ati.mmregs", 0x4000);
@@ -813,6 +885,7 @@ static void ati_vga_exit(PCIDevice *dev)
ATIVGAState *s = ATI_VGA(dev);
graphic_console_close(s->vga.con);
+ g_free(s->bbi2c);
}
static Property ati_vga_properties[] = {
@@ -837,7 +910,7 @@ static void ati_vga_class_init(ObjectClass *klass, void *data)
k->class_id = PCI_CLASS_DISPLAY_VGA;
k->vendor_id = PCI_VENDOR_ID_ATI;
k->device_id = PCI_DEVICE_ID_ATI_RAGE128_PF;
- k->romfile = "vgabios-stdvga.bin";
+ k->romfile = "vgabios-ati.bin";
k->realize = ati_vga_realize;
k->exit = ati_vga_exit;
}
diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c
index d83c29c6d9..2dbf53f039 100644
--- a/hw/display/ati_2d.c
+++ b/hw/display/ati_2d.c
@@ -51,8 +51,9 @@ void ati_2d_blt(ATIVGAState *s)
s->vga.vbe_start_addr, surface_data(ds), surface_stride(ds),
surface_bits_per_pixel(ds),
(s->regs.dp_mix & GMC_ROP3_MASK) >> 16);
- DPRINTF("%d %d, %d %d, (%d,%d) -> (%d,%d) %dx%d\n", s->regs.src_offset,
- s->regs.dst_offset, s->regs.src_pitch, s->regs.dst_pitch,
+ DPRINTF("%d %d %d, %d %d %d, (%d,%d) -> (%d,%d) %dx%d\n",
+ s->regs.src_offset, s->regs.dst_offset, s->regs.default_offset,
+ s->regs.src_pitch, s->regs.dst_pitch, s->regs.default_pitch,
s->regs.src_x, s->regs.src_y, s->regs.dst_x, s->regs.dst_y,
s->regs.dst_width, s->regs.dst_height);
switch (s->regs.dp_mix & GMC_ROP3_MASK) {
@@ -60,10 +61,16 @@ void ati_2d_blt(ATIVGAState *s)
{
uint8_t *src_bits, *dst_bits, *end;
int src_stride, dst_stride, bpp = ati_bpp_from_datatype(s);
- src_bits = s->vga.vram_ptr + s->regs.src_offset;
- dst_bits = s->vga.vram_ptr + s->regs.dst_offset;
- src_stride = s->regs.src_pitch;
- dst_stride = s->regs.dst_pitch;
+ src_bits = s->vga.vram_ptr +
+ (s->regs.dp_gui_master_cntl & GMC_SRC_PITCH_OFFSET_CNTL ?
+ s->regs.src_offset : s->regs.default_offset);
+ dst_bits = s->vga.vram_ptr +
+ (s->regs.dp_gui_master_cntl & GMC_DST_PITCH_OFFSET_CNTL ?
+ s->regs.dst_offset : s->regs.default_offset);
+ src_stride = (s->regs.dp_gui_master_cntl & GMC_SRC_PITCH_OFFSET_CNTL ?
+ s->regs.src_pitch : s->regs.default_pitch);
+ dst_stride = (s->regs.dp_gui_master_cntl & GMC_DST_PITCH_OFFSET_CNTL ?
+ s->regs.dst_pitch : s->regs.default_pitch);
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
src_bits += s->regs.crtc_offset & 0x07ffffff;
@@ -111,8 +118,11 @@ void ati_2d_blt(ATIVGAState *s)
uint8_t *dst_bits, *end;
int dst_stride, bpp = ati_bpp_from_datatype(s);
uint32_t filler = 0;
- dst_bits = s->vga.vram_ptr + s->regs.dst_offset;
- dst_stride = s->regs.dst_pitch;
+ dst_bits = s->vga.vram_ptr +
+ (s->regs.dp_gui_master_cntl & GMC_DST_PITCH_OFFSET_CNTL ?
+ s->regs.dst_offset : s->regs.default_offset);
+ dst_stride = (s->regs.dp_gui_master_cntl & GMC_DST_PITCH_OFFSET_CNTL ?
+ s->regs.dst_pitch : s->regs.default_pitch);
if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
dst_bits += s->regs.crtc_offset & 0x07ffffff;
diff --git a/hw/display/ati_dbg.c b/hw/display/ati_dbg.c
index b045f81d06..88b3a11315 100644
--- a/hw/display/ati_dbg.c
+++ b/hw/display/ati_dbg.c
@@ -19,6 +19,8 @@ static struct ati_regdesc ati_reg_names[] = {
{"CRTC_GEN_CNTL", 0x0050},
{"CRTC_EXT_CNTL", 0x0054},
{"DAC_CNTL", 0x0058},
+ {"GPIO_VGA_DDC", 0x0060},
+ {"GPIO_DVI_DDC", 0x0064},
{"GPIO_MONID", 0x0068},
{"I2C_CNTL_1", 0x0094},
{"PALETTE_INDEX", 0x00b0},
diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h
index 2f426064cf..9b67d0022a 100644
--- a/hw/display/ati_int.h
+++ b/hw/display/ati_int.h
@@ -10,6 +10,7 @@
#define ATI_INT_H
#include "hw/pci/pci.h"
+#include "hw/i2c/bitbang_i2c.h"
#include "vga_int.h"
/*#define DEBUG_ATI*/
@@ -35,6 +36,9 @@ typedef struct ATIVGARegs {
uint32_t crtc_gen_cntl;
uint32_t crtc_ext_cntl;
uint32_t dac_cntl;
+ uint32_t gpio_vga_ddc;
+ uint32_t gpio_dvi_ddc;
+ uint32_t gpio_monid;
uint32_t crtc_h_total_disp;
uint32_t crtc_h_sync_strt_wid;
uint32_t crtc_v_total_disp;
@@ -70,6 +74,7 @@ typedef struct ATIVGARegs {
uint32_t dp_write_mask;
uint32_t default_offset;
uint32_t default_pitch;
+ uint32_t default_tile;
uint32_t default_sc_bottom_right;
} ATIVGARegs;
@@ -83,6 +88,7 @@ typedef struct ATIVGAState {
uint16_t cursor_size;
uint32_t cursor_offset;
QEMUCursor *cursor;
+ bitbang_i2c_interface *bbi2c;
MemoryRegion io;
MemoryRegion mm;
ATIVGARegs regs;
diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h
index 923bfd33ce..d7155c93d5 100644
--- a/hw/display/ati_regs.h
+++ b/hw/display/ati_regs.h
@@ -37,6 +37,8 @@
#define CRTC_GEN_CNTL 0x0050
#define CRTC_EXT_CNTL 0x0054
#define DAC_CNTL 0x0058
+#define GPIO_VGA_DDC 0x0060
+#define GPIO_DVI_DDC 0x0064
#define GPIO_MONID 0x0068
#define I2C_CNTL_1 0x0094
#define PALETTE_INDEX 0x00b0
@@ -368,8 +370,8 @@
#define BRUSH_SOLIDCOLOR 0x00000d00
/* DP_GUI_MASTER_CNTL bit constants */
-#define GMC_SRC_PITCH_OFFSET_DEFAULT 0x00000000
-#define GMC_DST_PITCH_OFFSET_DEFAULT 0x00000000
+#define GMC_SRC_PITCH_OFFSET_CNTL 0x00000001
+#define GMC_DST_PITCH_OFFSET_CNTL 0x00000002
#define GMC_SRC_CLIP_DEFAULT 0x00000000
#define GMC_DST_CLIP_DEFAULT 0x00000000
#define GMC_BRUSH_SOLIDCOLOR 0x000000d0
diff --git a/hw/i2c/bitbang_i2c.c b/hw/i2c/bitbang_i2c.c
index 5dfc72d9d7..3cb0509b02 100644
--- a/hw/i2c/bitbang_i2c.c
+++ b/hw/i2c/bitbang_i2c.c
@@ -12,7 +12,7 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
-#include "bitbang_i2c.h"
+#include "hw/i2c/bitbang_i2c.h"
#include "hw/sysbus.h"
#include "qemu/module.h"
diff --git a/hw/i2c/bitbang_i2c.h b/hw/i2c/bitbang_i2c.h
deleted file mode 100644
index 9443021710..0000000000
--- a/hw/i2c/bitbang_i2c.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef BITBANG_I2C_H
-#define BITBANG_I2C_H
-
-#include "hw/i2c/i2c.h"
-
-#define BITBANG_I2C_SDA 0
-#define BITBANG_I2C_SCL 1
-
-bitbang_i2c_interface *bitbang_i2c_init(I2CBus *bus);
-int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level);
-
-#endif
diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c
index d606d3dbeb..5fb4f86c38 100644
--- a/hw/i2c/ppc4xx_i2c.c
+++ b/hw/i2c/ppc4xx_i2c.c
@@ -30,7 +30,6 @@
#include "cpu.h"
#include "hw/hw.h"
#include "hw/i2c/ppc4xx_i2c.h"
-#include "bitbang_i2c.h"
#define PPC4xx_I2C_MEM_SIZE 18
diff --git a/hw/i2c/versatile_i2c.c b/hw/i2c/versatile_i2c.c
index e07be9890c..24b6e36b6d 100644
--- a/hw/i2c/versatile_i2c.c
+++ b/hw/i2c/versatile_i2c.c
@@ -23,7 +23,7 @@
#include "qemu/osdep.h"
#include "hw/sysbus.h"
-#include "bitbang_i2c.h"
+#include "hw/i2c/bitbang_i2c.h"
#include "qemu/log.h"
#include "qemu/module.h"
diff --git a/hw/intc/aspeed_vic.c b/hw/intc/aspeed_vic.c
index 927638d532..266a309f3b 100644
--- a/hw/intc/aspeed_vic.c
+++ b/hw/intc/aspeed_vic.c
@@ -104,54 +104,63 @@ static void aspeed_vic_set_irq(void *opaque, int irq, int level)
static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
{
- uint64_t val;
- const bool high = !!(offset & 0x4);
- hwaddr n_offset = (offset & ~0x4);
AspeedVICState *s = (AspeedVICState *)opaque;
+ hwaddr n_offset;
+ uint64_t val;
+ bool high;
if (offset < AVIC_NEW_BASE_OFFSET) {
- qemu_log_mask(LOG_UNIMP, "%s: Ignoring read from legacy registers "
- "at 0x%" HWADDR_PRIx "[%u]\n", __func__, offset, size);
- return 0;
+ high = false;
+ n_offset = offset;
+ } else {
+ high = !!(offset & 0x4);
+ n_offset = (offset & ~0x4);
}
- n_offset -= AVIC_NEW_BASE_OFFSET;
-
switch (n_offset) {
- case 0x0: /* IRQ Status */
+ case 0x80: /* IRQ Status */
+ case 0x00:
val = s->raw & ~s->select & s->enable;
break;
- case 0x08: /* FIQ Status */
+ case 0x88: /* FIQ Status */
+ case 0x04:
val = s->raw & s->select & s->enable;
break;
- case 0x10: /* Raw Interrupt Status */
+ case 0x90: /* Raw Interrupt Status */
+ case 0x08:
val = s->raw;
break;
- case 0x18: /* Interrupt Selection */
+ case 0x98: /* Interrupt Selection */
+ case 0x0c:
val = s->select;
break;
- case 0x20: /* Interrupt Enable */
+ case 0xa0: /* Interrupt Enable */
+ case 0x10:
val = s->enable;
break;
- case 0x30: /* Software Interrupt */
+ case 0xb0: /* Software Interrupt */
+ case 0x18:
val = s->trigger;
break;
- case 0x40: /* Interrupt Sensitivity */
+ case 0xc0: /* Interrupt Sensitivity */
+ case 0x24:
val = s->sense;
break;
- case 0x48: /* Interrupt Both Edge Trigger Control */
+ case 0xc8: /* Interrupt Both Edge Trigger Control */
+ case 0x28:
val = s->dual_edge;
break;
- case 0x50: /* Interrupt Event */
+ case 0xd0: /* Interrupt Event */
+ case 0x2c:
val = s->event;
break;
- case 0x60: /* Edge Triggered Interrupt Status */
+ case 0xe0: /* Edge Triggered Interrupt Status */
val = s->raw & ~s->sense;
break;
/* Illegal */
- case 0x28: /* Interrupt Enable Clear */
- case 0x38: /* Software Interrupt Clear */
- case 0x58: /* Edge Triggered Interrupt Clear */
+ case 0xa8: /* Interrupt Enable Clear */
+ case 0xb8: /* Software Interrupt Clear */
+ case 0xd8: /* Edge Triggered Interrupt Clear */
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Read of write-only register with offset 0x%"
HWADDR_PRIx "\n", __func__, offset);
@@ -166,6 +175,8 @@ static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
}
if (high) {
val = extract64(val, 32, 19);
+ } else {
+ val = extract64(val, 0, 32);
}
trace_aspeed_vic_read(offset, size, val);
return val;
@@ -174,19 +185,18 @@ static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
unsigned size)
{
- const bool high = !!(offset & 0x4);
- hwaddr n_offset = (offset & ~0x4);
AspeedVICState *s = (AspeedVICState *)opaque;
+ hwaddr n_offset;
+ bool high;
if (offset < AVIC_NEW_BASE_OFFSET) {
- qemu_log_mask(LOG_UNIMP,
- "%s: Ignoring write to legacy registers at 0x%"
- HWADDR_PRIx "[%u] <- 0x%" PRIx64 "\n", __func__, offset,
- size, data);
- return;
+ high = false;
+ n_offset = offset;
+ } else {
+ high = !!(offset & 0x4);
+ n_offset = (offset & ~0x4);
}
- n_offset -= AVIC_NEW_BASE_OFFSET;
trace_aspeed_vic_write(offset, size, data);
/* Given we have members using separate enable/clear registers, deposit64()
@@ -201,7 +211,8 @@ static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
}
switch (n_offset) {
- case 0x18: /* Interrupt Selection */
+ case 0x98: /* Interrupt Selection */
+ case 0x0c:
/* Register has deposit64() semantics - overwrite requested 32 bits */
if (high) {
s->select &= AVIC_L_MASK;
@@ -210,21 +221,25 @@ static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
}
s->select |= data;
break;
- case 0x20: /* Interrupt Enable */
+ case 0xa0: /* Interrupt Enable */
+ case 0x10:
s->enable |= data;
break;
- case 0x28: /* Interrupt Enable Clear */
+ case 0xa8: /* Interrupt Enable Clear */
+ case 0x14:
s->enable &= ~data;
break;
- case 0x30: /* Software Interrupt */
+ case 0xb0: /* Software Interrupt */
+ case 0x18:
qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
"IRQs requested: 0x%016" PRIx64 "\n", __func__, data);
break;
- case 0x38: /* Software Interrupt Clear */
+ case 0xb8: /* Software Interrupt Clear */
+ case 0x1c:
qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
"IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data);
break;
- case 0x50: /* Interrupt Event */
+ case 0xd0: /* Interrupt Event */
/* Register has deposit64() semantics - overwrite the top four valid
* IRQ bits, as only the top four IRQs (GPIOs) can change their event
* type */
@@ -236,15 +251,21 @@ static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
"Ignoring invalid write to interrupt event register");
}
break;
- case 0x58: /* Edge Triggered Interrupt Clear */
+ case 0xd8: /* Edge Triggered Interrupt Clear */
+ case 0x38:
s->raw &= ~(data & ~s->sense);
break;
- case 0x00: /* IRQ Status */
- case 0x08: /* FIQ Status */
- case 0x10: /* Raw Interrupt Status */
- case 0x40: /* Interrupt Sensitivity */
- case 0x48: /* Interrupt Both Edge Trigger Control */
- case 0x60: /* Edge Triggered Interrupt Status */
+ case 0x80: /* IRQ Status */
+ case 0x00:
+ case 0x88: /* FIQ Status */
+ case 0x04:
+ case 0x90: /* Raw Interrupt Status */
+ case 0x08:
+ case 0xc0: /* Interrupt Sensitivity */
+ case 0x24:
+ case 0xc8: /* Interrupt Both Edge Trigger Control */
+ case 0x28:
+ case 0xe0: /* Edge Triggered Interrupt Status */
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Write of read-only register with offset 0x%"
HWADDR_PRIx "\n", __func__, offset);
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 77b9df9796..e9aab519a1 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -74,6 +74,7 @@ obj-$(CONFIG_ARMSSE_MHU) += armsse-mhu.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_AUX) += auxbus.o
+obj-$(CONFIG_ASPEED_SOC) += aspeed_xdma.o
obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o
obj-$(CONFIG_MSF2) += msf2-sysreg.o
obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o
diff --git a/hw/misc/aspeed_xdma.c b/hw/misc/aspeed_xdma.c
new file mode 100644
index 0000000000..eebd4ad540
--- /dev/null
+++ b/hw/misc/aspeed_xdma.c
@@ -0,0 +1,165 @@
+/*
+ * ASPEED XDMA Controller
+ * Eddie James <eajames@linux.ibm.com>
+ *
+ * Copyright (C) 2019 IBM Corp
+ * SPDX-License-Identifer: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "hw/misc/aspeed_xdma.h"
+#include "qapi/error.h"
+
+#include "trace.h"
+
+#define XDMA_BMC_CMDQ_ADDR 0x10
+#define XDMA_BMC_CMDQ_ENDP 0x14
+#define XDMA_BMC_CMDQ_WRP 0x18
+#define XDMA_BMC_CMDQ_W_MASK 0x0003FFFF
+#define XDMA_BMC_CMDQ_RDP 0x1C
+#define XDMA_BMC_CMDQ_RDP_MAGIC 0xEE882266
+#define XDMA_IRQ_ENG_CTRL 0x20
+#define XDMA_IRQ_ENG_CTRL_US_COMP BIT(4)
+#define XDMA_IRQ_ENG_CTRL_DS_COMP BIT(5)
+#define XDMA_IRQ_ENG_CTRL_W_MASK 0xBFEFF07F
+#define XDMA_IRQ_ENG_STAT 0x24
+#define XDMA_IRQ_ENG_STAT_US_COMP BIT(4)
+#define XDMA_IRQ_ENG_STAT_DS_COMP BIT(5)
+#define XDMA_IRQ_ENG_STAT_RESET 0xF8000000
+#define XDMA_MEM_SIZE 0x1000
+
+#define TO_REG(addr) ((addr) / sizeof(uint32_t))
+
+static uint64_t aspeed_xdma_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ uint32_t val = 0;
+ AspeedXDMAState *xdma = opaque;
+
+ if (addr < ASPEED_XDMA_REG_SIZE) {
+ val = xdma->regs[TO_REG(addr)];
+ }
+
+ return (uint64_t)val;
+}
+
+static void aspeed_xdma_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned int size)
+{
+ unsigned int idx;
+ uint32_t val32 = (uint32_t)val;
+ AspeedXDMAState *xdma = opaque;
+
+ if (addr >= ASPEED_XDMA_REG_SIZE) {
+ return;
+ }
+
+ switch (addr) {
+ case XDMA_BMC_CMDQ_ENDP:
+ xdma->regs[TO_REG(addr)] = val32 & XDMA_BMC_CMDQ_W_MASK;
+ break;
+ case XDMA_BMC_CMDQ_WRP:
+ idx = TO_REG(addr);
+ xdma->regs[idx] = val32 & XDMA_BMC_CMDQ_W_MASK;
+ xdma->regs[TO_REG(XDMA_BMC_CMDQ_RDP)] = xdma->regs[idx];
+
+ trace_aspeed_xdma_write(addr, val);
+
+ if (xdma->bmc_cmdq_readp_set) {
+ xdma->bmc_cmdq_readp_set = 0;
+ } else {
+ xdma->regs[TO_REG(XDMA_IRQ_ENG_STAT)] |=
+ XDMA_IRQ_ENG_STAT_US_COMP | XDMA_IRQ_ENG_STAT_DS_COMP;
+
+ if (xdma->regs[TO_REG(XDMA_IRQ_ENG_CTRL)] &
+ (XDMA_IRQ_ENG_CTRL_US_COMP | XDMA_IRQ_ENG_CTRL_DS_COMP))
+ qemu_irq_raise(xdma->irq);
+ }
+ break;
+ case XDMA_BMC_CMDQ_RDP:
+ trace_aspeed_xdma_write(addr, val);
+
+ if (val32 == XDMA_BMC_CMDQ_RDP_MAGIC) {
+ xdma->bmc_cmdq_readp_set = 1;
+ }
+ break;
+ case XDMA_IRQ_ENG_CTRL:
+ xdma->regs[TO_REG(addr)] = val32 & XDMA_IRQ_ENG_CTRL_W_MASK;
+ break;
+ case XDMA_IRQ_ENG_STAT:
+ trace_aspeed_xdma_write(addr, val);
+
+ idx = TO_REG(addr);
+ if (val32 & (XDMA_IRQ_ENG_STAT_US_COMP | XDMA_IRQ_ENG_STAT_DS_COMP)) {
+ xdma->regs[idx] &=
+ ~(XDMA_IRQ_ENG_STAT_US_COMP | XDMA_IRQ_ENG_STAT_DS_COMP);
+ qemu_irq_lower(xdma->irq);
+ }
+ break;
+ default:
+ xdma->regs[TO_REG(addr)] = val32;
+ break;
+ }
+}
+
+static const MemoryRegionOps aspeed_xdma_ops = {
+ .read = aspeed_xdma_read,
+ .write = aspeed_xdma_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static void aspeed_xdma_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedXDMAState *xdma = ASPEED_XDMA(dev);
+
+ sysbus_init_irq(sbd, &xdma->irq);
+ memory_region_init_io(&xdma->iomem, OBJECT(xdma), &aspeed_xdma_ops, xdma,
+ TYPE_ASPEED_XDMA, XDMA_MEM_SIZE);
+ sysbus_init_mmio(sbd, &xdma->iomem);
+}
+
+static void aspeed_xdma_reset(DeviceState *dev)
+{
+ AspeedXDMAState *xdma = ASPEED_XDMA(dev);
+
+ xdma->bmc_cmdq_readp_set = 0;
+ memset(xdma->regs, 0, ASPEED_XDMA_REG_SIZE);
+ xdma->regs[TO_REG(XDMA_IRQ_ENG_STAT)] = XDMA_IRQ_ENG_STAT_RESET;
+
+ qemu_irq_lower(xdma->irq);
+}
+
+static const VMStateDescription aspeed_xdma_vmstate = {
+ .name = TYPE_ASPEED_XDMA,
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, AspeedXDMAState, ASPEED_XDMA_NUM_REGS),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static void aspeed_xdma_class_init(ObjectClass *classp, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(classp);
+
+ dc->realize = aspeed_xdma_realize;
+ dc->reset = aspeed_xdma_reset;
+ dc->vmsd = &aspeed_xdma_vmstate;
+}
+
+static const TypeInfo aspeed_xdma_info = {
+ .name = TYPE_ASPEED_XDMA,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedXDMAState),
+ .class_init = aspeed_xdma_class_init,
+};
+
+static void aspeed_xdma_register_type(void)
+{
+ type_register_static(&aspeed_xdma_info);
+}
+type_init(aspeed_xdma_register_type);
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 47e1bccf71..c1ea1aa437 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -140,3 +140,6 @@ armsse_cpuid_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_I
# armsse-mhu.c
armsse_mhu_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
armsse_mhu_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+
+# aspeed_xdma.c
+aspeed_xdma_write(uint64_t offset, uint64_t data) "XDMA write: offset 0x%" PRIx64 " data 0x%" PRIx64
diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c
index eb760472e5..d2cded5e94 100644
--- a/hw/net/ftgmac100.c
+++ b/hw/net/ftgmac100.c
@@ -1017,8 +1017,6 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp)
sysbus_init_irq(sbd, &s->irq);
qemu_macaddr_default_if_unset(&s->conf.macaddr);
- s->conf.peers.ncs[0] = nd_table[0].netdev;
-
s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf,
object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
s);
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index c3f5fccfd1..b9e1cd71cf 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -2360,7 +2360,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
timer_mod(n->announce_timer.tm,
qemu_clock_get_ms(n->announce_timer.type));
} else {
- qemu_announce_timer_del(&n->announce_timer);
+ qemu_announce_timer_del(&n->announce_timer, false);
}
}
@@ -2784,7 +2784,7 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
virtio_net_del_queue(n, i);
}
- qemu_announce_timer_del(&n->announce_timer);
+ qemu_announce_timer_del(&n->announce_timer, false);
g_free(n->vqs);
qemu_del_nic(n->nic);
virtio_net_rsc_cleanup(n);
diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c
index 0fdfff5784..9ae8c0deb7 100644
--- a/hw/pci-host/designware.c
+++ b/hw/pci-host/designware.c
@@ -51,6 +51,8 @@
#define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff)
#define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C
+#define DESIGNWARE_PCIE_IRQ_MSI 3
+
static DesignwarePCIEHost *
designware_pcie_root_to_host(DesignwarePCIERoot *root)
{
@@ -67,7 +69,7 @@ static void designware_pcie_root_msi_write(void *opaque, hwaddr addr,
root->msi.intr[0].status |= BIT(val) & root->msi.intr[0].enable;
if (root->msi.intr[0].status & ~root->msi.intr[0].mask) {
- qemu_set_irq(host->pci.irqs[0], 1);
+ qemu_set_irq(host->pci.irqs[DESIGNWARE_PCIE_IRQ_MSI], 1);
}
}
@@ -290,23 +292,19 @@ static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address,
case DESIGNWARE_PCIE_MSI_ADDR_LO:
root->msi.base &= 0xFFFFFFFF00000000ULL;
root->msi.base |= val;
+ designware_pcie_root_update_msi_mapping(root);
break;
case DESIGNWARE_PCIE_MSI_ADDR_HI:
root->msi.base &= 0x00000000FFFFFFFFULL;
root->msi.base |= (uint64_t)val << 32;
+ designware_pcie_root_update_msi_mapping(root);
break;
- case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: {
- const bool update_msi_mapping = !root->msi.intr[0].enable ^ !!val;
-
+ case DESIGNWARE_PCIE_MSI_INTR0_ENABLE:
root->msi.intr[0].enable = val;
-
- if (update_msi_mapping) {
- designware_pcie_root_update_msi_mapping(root);
- }
+ designware_pcie_root_update_msi_mapping(root);
break;
- }
case DESIGNWARE_PCIE_MSI_INTR0_MASK:
root->msi.intr[0].mask = val;
@@ -315,7 +313,7 @@ static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address,
case DESIGNWARE_PCIE_MSI_INTR0_STATUS:
root->msi.intr[0].status ^= val;
if (!root->msi.intr[0].status) {
- qemu_set_irq(host->pci.irqs[0], 0);
+ qemu_set_irq(host->pci.irqs[DESIGNWARE_PCIE_IRQ_MSI], 0);
}
break;
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index ad310b9f94..b92395f165 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -22,6 +22,7 @@
#include "trace.h"
#include "hw/s390x/s390_flic.h"
#include "hw/s390x/s390-virtio-ccw.h"
+#include "hw/s390x/s390-ccw.h"
typedef struct CrwContainer {
CRW crw;
@@ -1205,6 +1206,26 @@ static void sch_handle_start_func_virtual(SubchDev *sch)
}
+static void sch_handle_halt_func_passthrough(SubchDev *sch)
+{
+ int ret;
+
+ ret = s390_ccw_halt(sch);
+ if (ret == -ENOSYS) {
+ sch_handle_halt_func(sch);
+ }
+}
+
+static void sch_handle_clear_func_passthrough(SubchDev *sch)
+{
+ int ret;
+
+ ret = s390_ccw_clear(sch);
+ if (ret == -ENOSYS) {
+ sch_handle_clear_func(sch);
+ }
+}
+
static IOInstEnding sch_handle_start_func_passthrough(SubchDev *sch)
{
SCHIB *schib = &sch->curr_status;
@@ -1244,11 +1265,9 @@ IOInstEnding do_subchannel_work_passthrough(SubchDev *sch)
SCHIB *schib = &sch->curr_status;
if (schib->scsw.ctrl & SCSW_FCTL_CLEAR_FUNC) {
- /* TODO: Clear handling */
- sch_handle_clear_func(sch);
+ sch_handle_clear_func_passthrough(sch);
} else if (schib->scsw.ctrl & SCSW_FCTL_HALT_FUNC) {
- /* TODO: Halt handling */
- sch_handle_halt_func(sch);
+ sch_handle_halt_func_passthrough(sch);
} else if (schib->scsw.ctrl & SCSW_FCTL_START_FUNC) {
return sch_handle_start_func_passthrough(sch);
}
diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c
index 8403f0e3e9..22c6878b84 100644
--- a/hw/s390x/s390-ccw.c
+++ b/hw/s390x/s390-ccw.c
@@ -30,6 +30,26 @@ IOInstEnding s390_ccw_cmd_request(SubchDev *sch)
return cdc->handle_request(sch);
}
+int s390_ccw_halt(SubchDev *sch)
+{
+ S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data);
+
+ if (!cdc->handle_halt) {
+ return -ENOSYS;
+ }
+ return cdc->handle_halt(sch);
+}
+
+int s390_ccw_clear(SubchDev *sch)
+{
+ S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data);
+
+ if (!cdc->handle_clear) {
+ return -ENOSYS;
+ }
+ return cdc->handle_clear(sch);
+}
+
static void s390_ccw_get_dev_info(S390CCWDevice *cdev,
char *sysfsdev,
Error **errp)
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 9eda0d720b..81f2fb7f70 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -913,6 +913,7 @@ static const VMStateDescription vmstate_aspeed_smc = {
static Property aspeed_smc_properties[] = {
DEFINE_PROP_UINT32("num-cs", AspeedSMCState, num_cs, 1),
+ DEFINE_PROP_UINT64("sdram-base", AspeedSMCState, sdram_base, 0),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 0e9a4530f8..123d92c969 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -41,7 +41,7 @@ obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o
common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o
-common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o
+common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o aspeed_rtc.o
common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o
common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o
diff --git a/hw/timer/aspeed_rtc.c b/hw/timer/aspeed_rtc.c
new file mode 100644
index 0000000000..19f061c846
--- /dev/null
+++ b/hw/timer/aspeed_rtc.c
@@ -0,0 +1,180 @@
+/*
+ * ASPEED Real Time Clock
+ * Joel Stanley <joel@jms.id.au>
+ *
+ * Copyright 2019 IBM Corp
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "hw/timer/aspeed_rtc.h"
+#include "qemu/log.h"
+#include "qemu/timer.h"
+
+#include "trace.h"
+
+#define COUNTER1 (0x00 / 4)
+#define COUNTER2 (0x04 / 4)
+#define ALARM (0x08 / 4)
+#define CONTROL (0x10 / 4)
+#define ALARM_STATUS (0x14 / 4)
+
+#define RTC_UNLOCKED BIT(1)
+#define RTC_ENABLED BIT(0)
+
+static void aspeed_rtc_calc_offset(AspeedRtcState *rtc)
+{
+ struct tm tm;
+ uint32_t year, cent;
+ uint32_t reg1 = rtc->reg[COUNTER1];
+ uint32_t reg2 = rtc->reg[COUNTER2];
+
+ tm.tm_mday = (reg1 >> 24) & 0x1f;
+ tm.tm_hour = (reg1 >> 16) & 0x1f;
+ tm.tm_min = (reg1 >> 8) & 0x3f;
+ tm.tm_sec = (reg1 >> 0) & 0x3f;
+
+ cent = (reg2 >> 16) & 0x1f;
+ year = (reg2 >> 8) & 0x7f;
+ tm.tm_mon = ((reg2 >> 0) & 0x0f) - 1;
+ tm.tm_year = year + (cent * 100) - 1900;
+
+ rtc->offset = qemu_timedate_diff(&tm);
+}
+
+static uint32_t aspeed_rtc_get_counter(AspeedRtcState *rtc, int r)
+{
+ uint32_t year, cent;
+ struct tm now;
+
+ qemu_get_timedate(&now, rtc->offset);
+
+ switch (r) {
+ case COUNTER1:
+ return (now.tm_mday << 24) | (now.tm_hour << 16) |
+ (now.tm_min << 8) | now.tm_sec;
+ case COUNTER2:
+ cent = (now.tm_year + 1900) / 100;
+ year = now.tm_year % 100;
+ return ((cent & 0x1f) << 16) | ((year & 0x7f) << 8) |
+ ((now.tm_mon + 1) & 0xf);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static uint64_t aspeed_rtc_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ AspeedRtcState *rtc = opaque;
+ uint64_t val;
+ uint32_t r = addr >> 2;
+
+ switch (r) {
+ case COUNTER1:
+ case COUNTER2:
+ if (rtc->reg[CONTROL] & RTC_ENABLED) {
+ rtc->reg[r] = aspeed_rtc_get_counter(rtc, r);
+ }
+ /* fall through */
+ case CONTROL:
+ val = rtc->reg[r];
+ break;
+ case ALARM:
+ case ALARM_STATUS:
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr);
+ return 0;
+ }
+
+ trace_aspeed_rtc_read(addr, val);
+
+ return val;
+}
+
+static void aspeed_rtc_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ AspeedRtcState *rtc = opaque;
+ uint32_t r = addr >> 2;
+
+ switch (r) {
+ case COUNTER1:
+ case COUNTER2:
+ if (!(rtc->reg[CONTROL] & RTC_UNLOCKED)) {
+ break;
+ }
+ /* fall through */
+ case CONTROL:
+ rtc->reg[r] = val;
+ aspeed_rtc_calc_offset(rtc);
+ break;
+ case ALARM:
+ case ALARM_STATUS:
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr);
+ break;
+ }
+ trace_aspeed_rtc_write(addr, val);
+}
+
+static void aspeed_rtc_reset(DeviceState *d)
+{
+ AspeedRtcState *rtc = ASPEED_RTC(d);
+
+ rtc->offset = 0;
+ memset(rtc->reg, 0, sizeof(rtc->reg));
+}
+
+static const MemoryRegionOps aspeed_rtc_ops = {
+ .read = aspeed_rtc_read,
+ .write = aspeed_rtc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_aspeed_rtc = {
+ .name = TYPE_ASPEED_RTC,
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(reg, AspeedRtcState, 0x18),
+ VMSTATE_INT32(offset, AspeedRtcState),
+ VMSTATE_INT32(offset, AspeedRtcState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void aspeed_rtc_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedRtcState *s = ASPEED_RTC(dev);
+
+ sysbus_init_irq(sbd, &s->irq);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_rtc_ops, s,
+ "aspeed-rtc", 0x18ULL);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void aspeed_rtc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = aspeed_rtc_realize;
+ dc->vmsd = &vmstate_aspeed_rtc;
+ dc->reset = aspeed_rtc_reset;
+}
+
+static const TypeInfo aspeed_rtc_info = {
+ .name = TYPE_ASPEED_RTC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedRtcState),
+ .class_init = aspeed_rtc_class_init,
+};
+
+static void aspeed_rtc_register_types(void)
+{
+ type_register_static(&aspeed_rtc_info);
+}
+
+type_init(aspeed_rtc_register_types)
diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
index 2c3a4d0fe7..29cc5e8070 100644
--- a/hw/timer/aspeed_timer.c
+++ b/hw/timer/aspeed_timer.c
@@ -107,39 +107,49 @@ static inline uint64_t calculate_time(struct AspeedTimer *t, uint32_t ticks)
return t->start + delta_ns;
}
+static inline uint32_t calculate_match(struct AspeedTimer *t, int i)
+{
+ return t->match[i] < t->reload ? t->match[i] : 0;
+}
+
static uint64_t calculate_next(struct AspeedTimer *t)
{
- uint64_t next = 0;
- uint32_t rate = calculate_rate(t);
+ uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ uint64_t next;
- while (!next) {
- /* We don't know the relationship between the values in the match
- * registers, so sort using MAX/MIN/zero. We sort in that order as the
- * timer counts down to zero. */
- uint64_t seq[] = {
- calculate_time(t, MAX(t->match[0], t->match[1])),
- calculate_time(t, MIN(t->match[0], t->match[1])),
- calculate_time(t, 0),
- };
- uint64_t reload_ns;
- uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-
- if (now < seq[0]) {
- next = seq[0];
- } else if (now < seq[1]) {
- next = seq[1];
- } else if (now < seq[2]) {
- next = seq[2];
- } else if (t->reload) {
- reload_ns = muldiv64(t->reload, NANOSECONDS_PER_SECOND, rate);
- t->start = now - ((now - t->start) % reload_ns);
- } else {
- /* no reload value, return 0 */
- break;
- }
+ /*
+ * We don't know the relationship between the values in the match
+ * registers, so sort using MAX/MIN/zero. We sort in that order as
+ * the timer counts down to zero.
+ */
+
+ next = calculate_time(t, MAX(calculate_match(t, 0), calculate_match(t, 1)));
+ if (now < next) {
+ return next;
+ }
+
+ next = calculate_time(t, MIN(calculate_match(t, 0), calculate_match(t, 1)));
+ if (now < next) {
+ return next;
}
- return next;
+ next = calculate_time(t, 0);
+ if (now < next) {
+ return next;
+ }
+
+ /* We've missed all deadlines, fire interrupt and try again */
+ timer_del(&t->timer);
+
+ if (timer_overflow_interrupt(t)) {
+ t->level = !t->level;
+ qemu_set_irq(t->irq, t->level);
+ }
+
+ next = MAX(MAX(calculate_match(t, 0), calculate_match(t, 1)), 0);
+ t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+ return calculate_time(t, next);
}
static void aspeed_timer_mod(AspeedTimer *t)
@@ -184,7 +194,11 @@ static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg)
switch (reg) {
case TIMER_REG_STATUS:
- value = calculate_ticks(t, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+ if (timer_enabled(t)) {
+ value = calculate_ticks(t, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+ } else {
+ value = t->reload;
+ }
break;
case TIMER_REG_RELOAD:
value = t->reload;
@@ -261,7 +275,11 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
int64_t delta = (int64_t) value - (int64_t) calculate_ticks(t, now);
uint32_t rate = calculate_rate(t);
- t->start += muldiv64(delta, NANOSECONDS_PER_SECOND, rate);
+ if (delta >= 0) {
+ t->start += muldiv64(delta, NANOSECONDS_PER_SECOND, rate);
+ } else {
+ t->start -= muldiv64(-delta, NANOSECONDS_PER_SECOND, rate);
+ }
aspeed_timer_mod(t);
}
break;
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index dcaf3d6da6..db02a9142c 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -66,6 +66,10 @@ cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK A
cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset"
+# hw/timer/aspeed-rtc.c
+aspeed_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64
+aspeed_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64
+
# sun4v-rtc.c
sun4v_rtc_read(uint64_t addr, uint64_t value) "read: addr 0x%" PRIx64 " value 0x%" PRIx64
sun4v_rtc_write(uint64_t addr, uint64_t value) "write: addr 0x%" PRIx64 " value 0x%" PRIx64
diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c
index 03a2becb3e..6d0296fe4d 100644
--- a/hw/vfio/ccw.c
+++ b/hw/vfio/ccw.c
@@ -2,9 +2,12 @@
* vfio based subchannel assignment support
*
* Copyright 2017 IBM Corp.
+ * Copyright 2019 Red Hat, Inc.
+ *
* Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
* Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
* Pierre Morel <pmorel@linux.vnet.ibm.com>
+ * Cornelia Huck <cohuck@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
@@ -33,6 +36,9 @@ struct VFIOCCWDevice {
uint64_t io_region_size;
uint64_t io_region_offset;
struct ccw_io_region *io_region;
+ uint64_t async_cmd_region_size;
+ uint64_t async_cmd_region_offset;
+ struct ccw_cmd_region *async_cmd_region;
EventNotifier io_notifier;
bool force_orb_pfch;
bool warned_orb_pfch;
@@ -115,6 +121,87 @@ again:
}
}
+static int vfio_ccw_handle_clear(SubchDev *sch)
+{
+ S390CCWDevice *cdev = sch->driver_data;
+ VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
+ struct ccw_cmd_region *region = vcdev->async_cmd_region;
+ int ret;
+
+ if (!vcdev->async_cmd_region) {
+ /* Async command region not available, fall back to emulation */
+ return -ENOSYS;
+ }
+
+ memset(region, 0, sizeof(*region));
+ region->command = VFIO_CCW_ASYNC_CMD_CSCH;
+
+again:
+ ret = pwrite(vcdev->vdev.fd, region,
+ vcdev->async_cmd_region_size, vcdev->async_cmd_region_offset);
+ if (ret != vcdev->async_cmd_region_size) {
+ if (errno == EAGAIN) {
+ goto again;
+ }
+ error_report("vfio-ccw: write cmd region failed with errno=%d", errno);
+ ret = -errno;
+ } else {
+ ret = region->ret_code;
+ }
+ switch (ret) {
+ case 0:
+ case -ENODEV:
+ case -EACCES:
+ return 0;
+ case -EFAULT:
+ default:
+ sch_gen_unit_exception(sch);
+ css_inject_io_interrupt(sch);
+ return 0;
+ }
+}
+
+static int vfio_ccw_handle_halt(SubchDev *sch)
+{
+ S390CCWDevice *cdev = sch->driver_data;
+ VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
+ struct ccw_cmd_region *region = vcdev->async_cmd_region;
+ int ret;
+
+ if (!vcdev->async_cmd_region) {
+ /* Async command region not available, fall back to emulation */
+ return -ENOSYS;
+ }
+
+ memset(region, 0, sizeof(*region));
+ region->command = VFIO_CCW_ASYNC_CMD_HSCH;
+
+again:
+ ret = pwrite(vcdev->vdev.fd, region,
+ vcdev->async_cmd_region_size, vcdev->async_cmd_region_offset);
+ if (ret != vcdev->async_cmd_region_size) {
+ if (errno == EAGAIN) {
+ goto again;
+ }
+ error_report("vfio-ccw: write cmd region failed with errno=%d", errno);
+ ret = -errno;
+ } else {
+ ret = region->ret_code;
+ }
+ switch (ret) {
+ case 0:
+ case -EBUSY:
+ case -ENODEV:
+ case -EACCES:
+ return 0;
+ case -EFAULT:
+ default:
+ sch_gen_unit_exception(sch);
+ css_inject_io_interrupt(sch);
+ return 0;
+ }
+}
+
static void vfio_ccw_reset(DeviceState *dev)
{
CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev);
@@ -198,9 +285,8 @@ static void vfio_ccw_register_io_notifier(VFIOCCWDevice *vcdev, Error **errp)
{
VFIODevice *vdev = &vcdev->vdev;
struct vfio_irq_info *irq_info;
- struct vfio_irq_set *irq_set;
size_t argsz;
- int32_t *pfd;
+ int fd;
if (vdev->num_irqs < VFIO_CCW_IO_IRQ_INDEX + 1) {
error_setg(errp, "vfio: unexpected number of io irqs %u",
@@ -224,56 +310,32 @@ static void vfio_ccw_register_io_notifier(VFIOCCWDevice *vcdev, Error **errp)
goto out_free_info;
}
- argsz = sizeof(*irq_set) + sizeof(*pfd);
- irq_set = g_malloc0(argsz);
- irq_set->argsz = argsz;
- irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
- VFIO_IRQ_SET_ACTION_TRIGGER;
- irq_set->index = VFIO_CCW_IO_IRQ_INDEX;
- irq_set->start = 0;
- irq_set->count = 1;
- pfd = (int32_t *) &irq_set->data;
-
- *pfd = event_notifier_get_fd(&vcdev->io_notifier);
- qemu_set_fd_handler(*pfd, vfio_ccw_io_notifier_handler, NULL, vcdev);
- if (ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
- error_setg(errp, "vfio: Failed to set up io notification");
- qemu_set_fd_handler(*pfd, NULL, NULL, vcdev);
+ fd = event_notifier_get_fd(&vcdev->io_notifier);
+ qemu_set_fd_handler(fd, vfio_ccw_io_notifier_handler, NULL, vcdev);
+
+ if (vfio_set_irq_signaling(vdev, VFIO_CCW_IO_IRQ_INDEX, 0,
+ VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) {
+ qemu_set_fd_handler(fd, NULL, NULL, vcdev);
event_notifier_cleanup(&vcdev->io_notifier);
}
- g_free(irq_set);
-
out_free_info:
g_free(irq_info);
}
static void vfio_ccw_unregister_io_notifier(VFIOCCWDevice *vcdev)
{
- struct vfio_irq_set *irq_set;
- size_t argsz;
- int32_t *pfd;
-
- argsz = sizeof(*irq_set) + sizeof(*pfd);
- irq_set = g_malloc0(argsz);
- irq_set->argsz = argsz;
- irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
- VFIO_IRQ_SET_ACTION_TRIGGER;
- irq_set->index = VFIO_CCW_IO_IRQ_INDEX;
- irq_set->start = 0;
- irq_set->count = 1;
- pfd = (int32_t *) &irq_set->data;
- *pfd = -1;
+ Error *err = NULL;
- if (ioctl(vcdev->vdev.fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
- error_report("vfio: Failed to de-assign device io fd: %m");
+ vfio_set_irq_signaling(&vcdev->vdev, VFIO_CCW_IO_IRQ_INDEX, 0,
+ VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err);
+ if (err) {
+ error_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name);
}
qemu_set_fd_handler(event_notifier_get_fd(&vcdev->io_notifier),
NULL, NULL, vcdev);
event_notifier_cleanup(&vcdev->io_notifier);
-
- g_free(irq_set);
}
static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
@@ -288,9 +350,13 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
return;
}
+ /*
+ * We always expect at least the I/O region to be present. We also
+ * may have a variable number of regions governed by capabilities.
+ */
if (vdev->num_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) {
- error_setg(errp, "vfio: Unexpected number of the I/O region %u",
- vdev->num_regions);
+ error_setg(errp, "vfio: too few regions (%u), expected at least %u",
+ vdev->num_regions, VFIO_CCW_CONFIG_REGION_INDEX + 1);
return;
}
@@ -310,11 +376,27 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
vcdev->io_region_offset = info->offset;
vcdev->io_region = g_malloc0(info->size);
+ /* check for the optional async command region */
+ ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW,
+ VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD, &info);
+ if (!ret) {
+ vcdev->async_cmd_region_size = info->size;
+ if (sizeof(*vcdev->async_cmd_region) != vcdev->async_cmd_region_size) {
+ error_setg(errp, "vfio: Unexpected size of the async cmd region");
+ g_free(vcdev->io_region);
+ g_free(info);
+ return;
+ }
+ vcdev->async_cmd_region_offset = info->offset;
+ vcdev->async_cmd_region = g_malloc0(info->size);
+ }
+
g_free(info);
}
static void vfio_ccw_put_region(VFIOCCWDevice *vcdev)
{
+ g_free(vcdev->async_cmd_region);
g_free(vcdev->io_region);
}
@@ -487,6 +569,8 @@ static void vfio_ccw_class_init(ObjectClass *klass, void *data)
dc->reset = vfio_ccw_reset;
cdc->handle_request = vfio_ccw_handle_request;
+ cdc->handle_halt = vfio_ccw_handle_halt;
+ cdc->handle_clear = vfio_ccw_handle_clear;
}
static const TypeInfo vfio_ccw_info = {
diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c
index 4a8409f0da..57fe24ae6b 100644
--- a/hw/watchdog/wdt_aspeed.c
+++ b/hw/watchdog/wdt_aspeed.c
@@ -44,6 +44,9 @@
#define WDT_RESTART_MAGIC 0x4755
+#define SCU_RESET_CONTROL1 (0x04 / 4)
+#define SCU_RESET_SDRAM BIT(0)
+
static bool aspeed_wdt_is_enabled(const AspeedWDTState *s)
{
return s->regs[WDT_CTRL] & WDT_CTRL_ENABLE;
@@ -222,6 +225,13 @@ static void aspeed_wdt_timer_expired(void *dev)
{
AspeedWDTState *s = ASPEED_WDT(dev);
+ /* Do not reset on SDRAM controller reset */
+ if (s->scu->regs[SCU_RESET_CONTROL1] & SCU_RESET_SDRAM) {
+ timer_del(s->timer);
+ s->regs[WDT_CTRL] = 0;
+ return;
+ }
+
qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
watchdog_perform_action();
timer_del(s->timer);
@@ -233,6 +243,16 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
AspeedWDTState *s = ASPEED_WDT(dev);
+ Error *err = NULL;
+ Object *obj;
+
+ obj = object_property_get_link(OBJECT(dev), "scu", &err);
+ if (!obj) {
+ error_propagate(errp, err);
+ error_prepend(errp, "required link 'scu' not found: ");
+ return;
+ }
+ s->scu = ASPEED_SCU(obj);
if (!is_supported_silicon_rev(s->silicon_rev)) {
error_setg(errp, "Unknown silicon revision: 0x%" PRIx32,