aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2018-03-12 11:47:52 +0000
committerPeter Maydell <peter.maydell@linaro.org>2018-03-12 11:47:52 +0000
commit5df089564be6e6a6b1bc79207f74b5b7ed4e1277 (patch)
treeb0bacc6574524443680e0af4f2b27597d3c070de /hw
parent12c06d6f967a63515399b9e1f6a40f5ce871a8b7 (diff)
parent076a0fc32a73a9b960e0f73f04a531bc1bd94308 (diff)
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180309' into staging
target-arm queue: * i.MX: Add i.MX7 SOC implementation and i.MX7 Sabre board * Report the correct core count in A53 L2CTLR on the ZynqMP board * linux-user: preliminary SVE support work (signal handling) * hw/arm/boot: fix memory leak in case of error loading ELF file * hw/arm/boot: avoid reading off end of buffer if passed very small image file * hw/arm: Use more CONFIG switches for the object files * target/arm: Add "-cpu max" support * hw/arm/virt: Support -machine gic-version=max * hw/sd: improve debug tracing * hw/sd: sdcard: Add the Tuning Command (CMD 19) * MAINTAINERS: add Philippe as odd-fixes maintainer for SD # gpg: Signature made Fri 09 Mar 2018 17:24:23 GMT # gpg: using RSA key 3C2525ED14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20180309: (25 commits) MAINTAINERS: Add entries for SD (SDHCI, SDBus, SDCard) sdhci: Fix a typo in comment sdcard: Add the Tuning Command (CMD19) sdcard: Display which protocol is used when tracing (SD or SPI) sdcard: Display command name when tracing CMD/ACMD sdcard: Do not trace CMD55, except when we already expect an ACMD hw/arm/virt: Support -machine gic-version=max hw/arm/virt: Add "max" to the list of CPU types "virt" supports target/arm: Make 'any' CPU just an alias for 'max' target/arm: Add "-cpu max" support target/arm: Move definition of 'host' cpu type into cpu.c target/arm: Query host CPU features on-demand at instance init arm: avoid heap-buffer-overflow in load_aarch64_image arm: fix load ELF error leak hw/arm: Use more CONFIG switches for the object files aarch64-linux-user: Add support for SVE signal frame records aarch64-linux-user: Add support for EXTRA signal frame records aarch64-linux-user: Remove struct target_aux_context aarch64-linux-user: Split out helpers for guest signal handling linux-user: Implement aarch64 PR_SVE_SET/GET_VL ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/Makefile.objs31
-rw-r--r--hw/arm/boot.c4
-rw-r--r--hw/arm/fsl-imx7.c582
-rw-r--r--hw/arm/mcimx7d-sabre.c90
-rw-r--r--hw/arm/virt.c30
-rw-r--r--hw/arm/xlnx-zynqmp.c2
-rw-r--r--hw/pci-host/Makefile.objs2
-rw-r--r--hw/pci-host/designware.c754
-rw-r--r--hw/sd/Makefile.objs2
-rw-r--r--hw/sd/sd.c55
-rw-r--r--hw/sd/sdhci.c4
-rw-r--r--hw/sd/sdmmc-internal.c72
-rw-r--r--hw/sd/sdmmc-internal.h24
-rw-r--r--hw/sd/trace-events8
14 files changed, 1627 insertions, 33 deletions
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 232258160a..2885e3e234 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -1,15 +1,27 @@
-obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
-obj-$(CONFIG_DIGIC) += digic_boards.o
-obj-y += integratorcp.o mainstone.o musicpal.o nseries.o
-obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o
-obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
+obj-y += boot.o virt.o sysbus-fdt.o
obj-$(CONFIG_ACPI) += virt-acpi-build.o
-obj-y += netduino2.o
-obj-y += sysbus-fdt.o
+obj-$(CONFIG_DIGIC) += digic_boards.o
+obj-$(CONFIG_EXYNOS4) += exynos4_boards.o
+obj-$(CONFIG_HIGHBANK) += highbank.o
+obj-$(CONFIG_INTEGRATOR) += integratorcp.o
+obj-$(CONFIG_MAINSTONE) += mainstone.o
+obj-$(CONFIG_MUSICPAL) += musicpal.o
+obj-$(CONFIG_NETDUINO2) += netduino2.o
+obj-$(CONFIG_NSERIES) += nseries.o
+obj-$(CONFIG_OMAP) += omap_sx1.o palm.o
+obj-$(CONFIG_PXA2XX) += gumstix.o spitz.o tosa.o z2.o
+obj-$(CONFIG_REALVIEW) += realview.o
+obj-$(CONFIG_STELLARIS) += stellaris.o
+obj-$(CONFIG_STRONGARM) += collie.o
+obj-$(CONFIG_VERSATILE) += vexpress.o versatilepb.o
+obj-$(CONFIG_ZYNQ) += xilinx_zynq.o
-obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
+obj-$(CONFIG_ARM_V7M) += armv7m.o
+obj-$(CONFIG_EXYNOS4) += exynos4210.o
+obj-$(CONFIG_PXA2XX) += pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
obj-$(CONFIG_DIGIC) += digic.o
-obj-y += omap1.o omap2.o strongarm.o
+obj-$(CONFIG_OMAP) += omap1.o omap2.o
+obj-$(CONFIG_STRONGARM) += strongarm.o
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o
obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
@@ -22,3 +34,4 @@ obj-$(CONFIG_MPS2) += mps2.o
obj-$(CONFIG_MPS2) += mps2-tz.o
obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
obj-$(CONFIG_IOTKIT) += iotkit.o
+obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 6d0c92ab88..196c7fb242 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -829,6 +829,7 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err);
if (err) {
+ error_free(err);
return ret;
}
@@ -890,7 +891,8 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
}
/* check the arm64 magic header value -- very old kernels may not have it */
- if (memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
+ if (size > ARM64_MAGIC_OFFSET + 4 &&
+ memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
uint64_t hdrvals[2];
/* The arm64 Image header has text_offset and image_size fields at 8 and
diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c
new file mode 100644
index 0000000000..26ef36c79a
--- /dev/null
+++ b/hw/arm/fsl-imx7.c
@@ -0,0 +1,582 @@
+/*
+ * Copyright (c) 2018, Impinj, Inc.
+ *
+ * i.MX7 SoC definitions
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * Based on hw/arm/fsl-imx6.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "hw/arm/fsl-imx7.h"
+#include "hw/misc/unimp.h"
+#include "sysemu/sysemu.h"
+#include "qemu/error-report.h"
+
+#define NAME_SIZE 20
+
+static void fsl_imx7_init(Object *obj)
+{
+ BusState *sysbus = sysbus_get_default();
+ FslIMX7State *s = FSL_IMX7(obj);
+ char name[NAME_SIZE];
+ int i;
+
+ if (smp_cpus > FSL_IMX7_NUM_CPUS) {
+ error_report("%s: Only %d CPUs are supported (%d requested)",
+ TYPE_FSL_IMX7, FSL_IMX7_NUM_CPUS, smp_cpus);
+ exit(1);
+ }
+
+ for (i = 0; i < smp_cpus; i++) {
+ object_initialize(&s->cpu[i], sizeof(s->cpu[i]),
+ ARM_CPU_TYPE_NAME("cortex-a7"));
+ snprintf(name, NAME_SIZE, "cpu%d", i);
+ object_property_add_child(obj, name, OBJECT(&s->cpu[i]),
+ &error_fatal);
+ }
+
+ /*
+ * A7MPCORE
+ */
+ object_initialize(&s->a7mpcore, sizeof(s->a7mpcore), TYPE_A15MPCORE_PRIV);
+ qdev_set_parent_bus(DEVICE(&s->a7mpcore), sysbus);
+ object_property_add_child(obj, "a7mpcore",
+ OBJECT(&s->a7mpcore), &error_fatal);
+
+ /*
+ * GPIOs 1 to 7
+ */
+ for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) {
+ object_initialize(&s->gpio[i], sizeof(s->gpio[i]),
+ TYPE_IMX_GPIO);
+ qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus);
+ snprintf(name, NAME_SIZE, "gpio%d", i);
+ object_property_add_child(obj, name,
+ OBJECT(&s->gpio[i]), &error_fatal);
+ }
+
+ /*
+ * GPT1, 2, 3, 4
+ */
+ for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) {
+ object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX7_GPT);
+ qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus);
+ snprintf(name, NAME_SIZE, "gpt%d", i);
+ object_property_add_child(obj, name, OBJECT(&s->gpt[i]),
+ &error_fatal);
+ }
+
+ /*
+ * CCM
+ */
+ object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX7_CCM);
+ qdev_set_parent_bus(DEVICE(&s->ccm), sysbus);
+ object_property_add_child(obj, "ccm", OBJECT(&s->ccm), &error_fatal);
+
+ /*
+ * Analog
+ */
+ object_initialize(&s->analog, sizeof(s->analog), TYPE_IMX7_ANALOG);
+ qdev_set_parent_bus(DEVICE(&s->analog), sysbus);
+ object_property_add_child(obj, "analog", OBJECT(&s->analog), &error_fatal);
+
+ /*
+ * GPCv2
+ */
+ object_initialize(&s->gpcv2, sizeof(s->gpcv2), TYPE_IMX_GPCV2);
+ qdev_set_parent_bus(DEVICE(&s->gpcv2), sysbus);
+ object_property_add_child(obj, "gpcv2", OBJECT(&s->gpcv2), &error_fatal);
+
+ for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) {
+ object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI);
+ qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
+ snprintf(name, NAME_SIZE, "spi%d", i + 1);
+ object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL);
+ }
+
+
+ for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) {
+ object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C);
+ qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default());
+ snprintf(name, NAME_SIZE, "i2c%d", i + 1);
+ object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL);
+ }
+
+ /*
+ * UART
+ */
+ for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) {
+ object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL);
+ qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus);
+ snprintf(name, NAME_SIZE, "uart%d", i);
+ object_property_add_child(obj, name, OBJECT(&s->uart[i]),
+ &error_fatal);
+ }
+
+ /*
+ * Ethernet
+ */
+ for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) {
+ object_initialize(&s->eth[i], sizeof(s->eth[i]), TYPE_IMX_ENET);
+ qdev_set_parent_bus(DEVICE(&s->eth[i]), sysbus);
+ snprintf(name, NAME_SIZE, "eth%d", i);
+ object_property_add_child(obj, name, OBJECT(&s->eth[i]),
+ &error_fatal);
+ }
+
+ /*
+ * SDHCI
+ */
+ for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
+ object_initialize(&s->usdhc[i], sizeof(s->usdhc[i]),
+ TYPE_IMX_USDHC);
+ qdev_set_parent_bus(DEVICE(&s->usdhc[i]), sysbus);
+ snprintf(name, NAME_SIZE, "usdhc%d", i);
+ object_property_add_child(obj, name, OBJECT(&s->usdhc[i]),
+ &error_fatal);
+ }
+
+ /*
+ * SNVS
+ */
+ object_initialize(&s->snvs, sizeof(s->snvs), TYPE_IMX7_SNVS);
+ qdev_set_parent_bus(DEVICE(&s->snvs), sysbus);
+ object_property_add_child(obj, "snvs", OBJECT(&s->snvs), &error_fatal);
+
+ /*
+ * Watchdog
+ */
+ for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) {
+ object_initialize(&s->wdt[i], sizeof(s->wdt[i]), TYPE_IMX2_WDT);
+ qdev_set_parent_bus(DEVICE(&s->wdt[i]), sysbus);
+ snprintf(name, NAME_SIZE, "wdt%d", i);
+ object_property_add_child(obj, name, OBJECT(&s->wdt[i]),
+ &error_fatal);
+ }
+
+ /*
+ * GPR
+ */
+ object_initialize(&s->gpr, sizeof(s->gpr), TYPE_IMX7_GPR);
+ qdev_set_parent_bus(DEVICE(&s->gpr), sysbus);
+ object_property_add_child(obj, "gpr", OBJECT(&s->gpr), &error_fatal);
+
+ object_initialize(&s->pcie, sizeof(s->pcie), TYPE_DESIGNWARE_PCIE_HOST);
+ qdev_set_parent_bus(DEVICE(&s->pcie), sysbus);
+ object_property_add_child(obj, "pcie", OBJECT(&s->pcie), &error_fatal);
+
+ for (i = 0; i < FSL_IMX7_NUM_USBS; i++) {
+ object_initialize(&s->usb[i],
+ sizeof(s->usb[i]), TYPE_CHIPIDEA);
+ qdev_set_parent_bus(DEVICE(&s->usb[i]), sysbus);
+ snprintf(name, NAME_SIZE, "usb%d", i);
+ object_property_add_child(obj, name,
+ OBJECT(&s->usb[i]), &error_fatal);
+ }
+}
+
+static void fsl_imx7_realize(DeviceState *dev, Error **errp)
+{
+ FslIMX7State *s = FSL_IMX7(dev);
+ Object *o;
+ int i;
+ qemu_irq irq;
+ char name[NAME_SIZE];
+
+ for (i = 0; i < smp_cpus; i++) {
+ o = OBJECT(&s->cpu[i]);
+
+ object_property_set_int(o, QEMU_PSCI_CONDUIT_SMC,
+ "psci-conduit", &error_abort);
+
+ /* On uniprocessor, the CBAR is set to 0 */
+ if (smp_cpus > 1) {
+ object_property_set_int(o, FSL_IMX7_A7MPCORE_ADDR,
+ "reset-cbar", &error_abort);
+ }
+
+ if (i) {
+ /* Secondary CPUs start in PSCI powered-down state */
+ object_property_set_bool(o, true,
+ "start-powered-off", &error_abort);
+ }
+
+ object_property_set_bool(o, true, "realized", &error_abort);
+ }
+
+ /*
+ * A7MPCORE
+ */
+ object_property_set_int(OBJECT(&s->a7mpcore), smp_cpus, "num-cpu",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->a7mpcore),
+ FSL_IMX7_MAX_IRQ + GIC_INTERNAL,
+ "num-irq", &error_abort);
+
+ object_property_set_bool(OBJECT(&s->a7mpcore), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, FSL_IMX7_A7MPCORE_ADDR);
+
+ for (i = 0; i < smp_cpus; i++) {
+ SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore);
+ DeviceState *d = DEVICE(qemu_get_cpu(i));
+
+ irq = qdev_get_gpio_in(d, ARM_CPU_IRQ);
+ sysbus_connect_irq(sbd, i, irq);
+ irq = qdev_get_gpio_in(d, ARM_CPU_FIQ);
+ sysbus_connect_irq(sbd, i + smp_cpus, irq);
+ }
+
+ /*
+ * A7MPCORE DAP
+ */
+ create_unimplemented_device("a7mpcore-dap", FSL_IMX7_A7MPCORE_DAP_ADDR,
+ 0x100000);
+
+ /*
+ * GPT1, 2, 3, 4
+ */
+ for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) {
+ static const hwaddr FSL_IMX7_GPTn_ADDR[FSL_IMX7_NUM_GPTS] = {
+ FSL_IMX7_GPT1_ADDR,
+ FSL_IMX7_GPT2_ADDR,
+ FSL_IMX7_GPT3_ADDR,
+ FSL_IMX7_GPT4_ADDR,
+ };
+
+ s->gpt[i].ccm = IMX_CCM(&s->ccm);
+ object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, FSL_IMX7_GPTn_ADDR[i]);
+ }
+
+ for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) {
+ static const hwaddr FSL_IMX7_GPIOn_ADDR[FSL_IMX7_NUM_GPIOS] = {
+ FSL_IMX7_GPIO1_ADDR,
+ FSL_IMX7_GPIO2_ADDR,
+ FSL_IMX7_GPIO3_ADDR,
+ FSL_IMX7_GPIO4_ADDR,
+ FSL_IMX7_GPIO5_ADDR,
+ FSL_IMX7_GPIO6_ADDR,
+ FSL_IMX7_GPIO7_ADDR,
+ };
+
+ object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, FSL_IMX7_GPIOn_ADDR[i]);
+ }
+
+ /*
+ * IOMUXC and IOMUXC_LPSR
+ */
+ for (i = 0; i < FSL_IMX7_NUM_IOMUXCS; i++) {
+ static const hwaddr FSL_IMX7_IOMUXCn_ADDR[FSL_IMX7_NUM_IOMUXCS] = {
+ FSL_IMX7_IOMUXC_ADDR,
+ FSL_IMX7_IOMUXC_LPSR_ADDR,
+ };
+
+ snprintf(name, NAME_SIZE, "iomuxc%d", i);
+ create_unimplemented_device(name, FSL_IMX7_IOMUXCn_ADDR[i],
+ FSL_IMX7_IOMUXCn_SIZE);
+ }
+
+ /*
+ * CCM
+ */
+ object_property_set_bool(OBJECT(&s->ccm), true, "realized", &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX7_CCM_ADDR);
+
+ /*
+ * Analog
+ */
+ object_property_set_bool(OBJECT(&s->analog), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->analog), 0, FSL_IMX7_ANALOG_ADDR);
+
+ /*
+ * GPCv2
+ */
+ object_property_set_bool(OBJECT(&s->gpcv2), true,
+ "realized", &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpcv2), 0, FSL_IMX7_GPC_ADDR);
+
+ /* Initialize all ECSPI */
+ for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) {
+ static const hwaddr FSL_IMX7_SPIn_ADDR[FSL_IMX7_NUM_ECSPIS] = {
+ FSL_IMX7_ECSPI1_ADDR,
+ FSL_IMX7_ECSPI2_ADDR,
+ FSL_IMX7_ECSPI3_ADDR,
+ FSL_IMX7_ECSPI4_ADDR,
+ };
+
+ static const hwaddr FSL_IMX7_SPIn_IRQ[FSL_IMX7_NUM_ECSPIS] = {
+ FSL_IMX7_ECSPI1_IRQ,
+ FSL_IMX7_ECSPI2_IRQ,
+ FSL_IMX7_ECSPI3_IRQ,
+ FSL_IMX7_ECSPI4_IRQ,
+ };
+
+ /* Initialize the SPI */
+ object_property_set_bool(OBJECT(&s->spi[i]), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0,
+ FSL_IMX7_SPIn_ADDR[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->a7mpcore),
+ FSL_IMX7_SPIn_IRQ[i]));
+ }
+
+ for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) {
+ static const hwaddr FSL_IMX7_I2Cn_ADDR[FSL_IMX7_NUM_I2CS] = {
+ FSL_IMX7_I2C1_ADDR,
+ FSL_IMX7_I2C2_ADDR,
+ FSL_IMX7_I2C3_ADDR,
+ FSL_IMX7_I2C4_ADDR,
+ };
+
+ static const hwaddr FSL_IMX7_I2Cn_IRQ[FSL_IMX7_NUM_I2CS] = {
+ FSL_IMX7_I2C1_IRQ,
+ FSL_IMX7_I2C2_IRQ,
+ FSL_IMX7_I2C3_IRQ,
+ FSL_IMX7_I2C4_IRQ,
+ };
+
+ object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, FSL_IMX7_I2Cn_ADDR[i]);
+
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->a7mpcore),
+ FSL_IMX7_I2Cn_IRQ[i]));
+ }
+
+ /*
+ * UART
+ */
+ for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) {
+ static const hwaddr FSL_IMX7_UARTn_ADDR[FSL_IMX7_NUM_UARTS] = {
+ FSL_IMX7_UART1_ADDR,
+ FSL_IMX7_UART2_ADDR,
+ FSL_IMX7_UART3_ADDR,
+ FSL_IMX7_UART4_ADDR,
+ FSL_IMX7_UART5_ADDR,
+ FSL_IMX7_UART6_ADDR,
+ FSL_IMX7_UART7_ADDR,
+ };
+
+ static const int FSL_IMX7_UARTn_IRQ[FSL_IMX7_NUM_UARTS] = {
+ FSL_IMX7_UART1_IRQ,
+ FSL_IMX7_UART2_IRQ,
+ FSL_IMX7_UART3_IRQ,
+ FSL_IMX7_UART4_IRQ,
+ FSL_IMX7_UART5_IRQ,
+ FSL_IMX7_UART6_IRQ,
+ FSL_IMX7_UART7_IRQ,
+ };
+
+
+ if (i < MAX_SERIAL_PORTS) {
+ qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]);
+ }
+
+ object_property_set_bool(OBJECT(&s->uart[i]), true, "realized",
+ &error_abort);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, FSL_IMX7_UARTn_ADDR[i]);
+
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_UARTn_IRQ[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, irq);
+ }
+
+ /*
+ * Ethernet
+ */
+ for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) {
+ static const hwaddr FSL_IMX7_ENETn_ADDR[FSL_IMX7_NUM_ETHS] = {
+ FSL_IMX7_ENET1_ADDR,
+ FSL_IMX7_ENET2_ADDR,
+ };
+
+ object_property_set_uint(OBJECT(&s->eth[i]), FSL_IMX7_ETH_NUM_TX_RINGS,
+ "tx-ring-num", &error_abort);
+ qdev_set_nic_properties(DEVICE(&s->eth[i]), &nd_table[i]);
+ object_property_set_bool(OBJECT(&s->eth[i]), true, "realized",
+ &error_abort);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth[i]), 0, FSL_IMX7_ENETn_ADDR[i]);
+
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 0, irq);
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 3));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 1, irq);
+ }
+
+ /*
+ * USDHC
+ */
+ for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
+ static const hwaddr FSL_IMX7_USDHCn_ADDR[FSL_IMX7_NUM_USDHCS] = {
+ FSL_IMX7_USDHC1_ADDR,
+ FSL_IMX7_USDHC2_ADDR,
+ FSL_IMX7_USDHC3_ADDR,
+ };
+
+ static const int FSL_IMX7_USDHCn_IRQ[FSL_IMX7_NUM_USDHCS] = {
+ FSL_IMX7_USDHC1_IRQ,
+ FSL_IMX7_USDHC2_IRQ,
+ FSL_IMX7_USDHC3_IRQ,
+ };
+
+ object_property_set_bool(OBJECT(&s->usdhc[i]), true, "realized",
+ &error_abort);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0,
+ FSL_IMX7_USDHCn_ADDR[i]);
+
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USDHCn_IRQ[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, irq);
+ }
+
+ /*
+ * SNVS
+ */
+ object_property_set_bool(OBJECT(&s->snvs), true, "realized", &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, FSL_IMX7_SNVS_ADDR);
+
+ /*
+ * SRC
+ */
+ create_unimplemented_device("sdma", FSL_IMX7_SRC_ADDR, FSL_IMX7_SRC_SIZE);
+
+ /*
+ * Watchdog
+ */
+ for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) {
+ static const hwaddr FSL_IMX7_WDOGn_ADDR[FSL_IMX7_NUM_WDTS] = {
+ FSL_IMX7_WDOG1_ADDR,
+ FSL_IMX7_WDOG2_ADDR,
+ FSL_IMX7_WDOG3_ADDR,
+ FSL_IMX7_WDOG4_ADDR,
+ };
+
+ object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized",
+ &error_abort);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX7_WDOGn_ADDR[i]);
+ }
+
+ /*
+ * SDMA
+ */
+ create_unimplemented_device("sdma", FSL_IMX7_SDMA_ADDR, FSL_IMX7_SDMA_SIZE);
+
+
+ object_property_set_bool(OBJECT(&s->gpr), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpr), 0, FSL_IMX7_GPR_ADDR);
+
+ object_property_set_bool(OBJECT(&s->pcie), true,
+ "realized", &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, FSL_IMX7_PCIE_REG_ADDR);
+
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTA_IRQ);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, irq);
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTB_IRQ);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, irq);
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTC_IRQ);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, irq);
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTD_IRQ);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq);
+
+
+ for (i = 0; i < FSL_IMX7_NUM_USBS; i++) {
+ static const hwaddr FSL_IMX7_USBMISCn_ADDR[FSL_IMX7_NUM_USBS] = {
+ FSL_IMX7_USBMISC1_ADDR,
+ FSL_IMX7_USBMISC2_ADDR,
+ FSL_IMX7_USBMISC3_ADDR,
+ };
+
+ static const hwaddr FSL_IMX7_USBn_ADDR[FSL_IMX7_NUM_USBS] = {
+ FSL_IMX7_USB1_ADDR,
+ FSL_IMX7_USB2_ADDR,
+ FSL_IMX7_USB3_ADDR,
+ };
+
+ static const hwaddr FSL_IMX7_USBn_IRQ[FSL_IMX7_NUM_USBS] = {
+ FSL_IMX7_USB1_IRQ,
+ FSL_IMX7_USB2_IRQ,
+ FSL_IMX7_USB3_IRQ,
+ };
+
+ object_property_set_bool(OBJECT(&s->usb[i]), true, "realized",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0,
+ FSL_IMX7_USBn_ADDR[i]);
+
+ irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USBn_IRQ[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, irq);
+
+ snprintf(name, NAME_SIZE, "usbmisc%d", i);
+ create_unimplemented_device(name, FSL_IMX7_USBMISCn_ADDR[i],
+ FSL_IMX7_USBMISCn_SIZE);
+ }
+
+ /*
+ * ADCs
+ */
+ for (i = 0; i < FSL_IMX7_NUM_ADCS; i++) {
+ static const hwaddr FSL_IMX7_ADCn_ADDR[FSL_IMX7_NUM_ADCS] = {
+ FSL_IMX7_ADC1_ADDR,
+ FSL_IMX7_ADC2_ADDR,
+ };
+
+ snprintf(name, NAME_SIZE, "adc%d", i);
+ create_unimplemented_device(name, FSL_IMX7_ADCn_ADDR[i],
+ FSL_IMX7_ADCn_SIZE);
+ }
+
+ /*
+ * LCD
+ */
+ create_unimplemented_device("lcdif", FSL_IMX7_LCDIF_ADDR,
+ FSL_IMX7_LCDIF_SIZE);
+}
+
+static void fsl_imx7_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = fsl_imx7_realize;
+
+ /* Reason: Uses serial_hds and nd_table in realize() directly */
+ dc->user_creatable = false;
+ dc->desc = "i.MX7 SOC";
+}
+
+static const TypeInfo fsl_imx7_type_info = {
+ .name = TYPE_FSL_IMX7,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(FslIMX7State),
+ .instance_init = fsl_imx7_init,
+ .class_init = fsl_imx7_class_init,
+};
+
+static void fsl_imx7_register_types(void)
+{
+ type_register_static(&fsl_imx7_type_info);
+}
+type_init(fsl_imx7_register_types)
diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c
new file mode 100644
index 0000000000..95fb409d9c
--- /dev/null
+++ b/hw/arm/mcimx7d-sabre.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018, Impinj, Inc.
+ *
+ * MCIMX7D_SABRE Board System emulation.
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * This code is licensed under the GPL, version 2 or later.
+ * See the file `COPYING' in the top level directory.
+ *
+ * It (partially) emulates a mcimx7d_sabre board, with a Freescale
+ * i.MX7 SoC
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "hw/arm/fsl-imx7.h"
+#include "hw/boards.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/device_tree.h"
+#include "qemu/error-report.h"
+#include "sysemu/qtest.h"
+#include "net/net.h"
+
+typedef struct {
+ FslIMX7State soc;
+ MemoryRegion ram;
+} MCIMX7Sabre;
+
+static void mcimx7d_sabre_init(MachineState *machine)
+{
+ static struct arm_boot_info boot_info;
+ MCIMX7Sabre *s = g_new0(MCIMX7Sabre, 1);
+ Object *soc;
+ int i;
+
+ if (machine->ram_size > FSL_IMX7_MMDC_SIZE) {
+ error_report("RAM size " RAM_ADDR_FMT " above max supported (%08x)",
+ machine->ram_size, FSL_IMX7_MMDC_SIZE);
+ exit(1);
+ }
+
+ boot_info = (struct arm_boot_info) {
+ .loader_start = FSL_IMX7_MMDC_ADDR,
+ .board_id = -1,
+ .ram_size = machine->ram_size,
+ .kernel_filename = machine->kernel_filename,
+ .kernel_cmdline = machine->kernel_cmdline,
+ .initrd_filename = machine->initrd_filename,
+ .nb_cpus = smp_cpus,
+ };
+
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX7);
+ soc = OBJECT(&s->soc);
+ object_property_add_child(OBJECT(machine), "soc", soc, &error_fatal);
+ object_property_set_bool(soc, true, "realized", &error_fatal);
+
+ memory_region_allocate_system_memory(&s->ram, NULL, "mcimx7d-sabre.ram",
+ machine->ram_size);
+ memory_region_add_subregion(get_system_memory(),
+ FSL_IMX7_MMDC_ADDR, &s->ram);
+
+ for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) {
+ BusState *bus;
+ DeviceState *carddev;
+ DriveInfo *di;
+ BlockBackend *blk;
+
+ di = drive_get_next(IF_SD);
+ blk = di ? blk_by_legacy_dinfo(di) : NULL;
+ bus = qdev_get_child_bus(DEVICE(&s->soc.usdhc[i]), "sd-bus");
+ carddev = qdev_create(bus, TYPE_SD_CARD);
+ qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
+ object_property_set_bool(OBJECT(carddev), true,
+ "realized", &error_fatal);
+ }
+
+ if (!qtest_enabled()) {
+ arm_load_kernel(&s->soc.cpu[0], &boot_info);
+ }
+}
+
+static void mcimx7d_sabre_machine_init(MachineClass *mc)
+{
+ mc->desc = "Freescale i.MX7 DUAL SABRE (Cortex A7)";
+ mc->init = mcimx7d_sabre_init;
+ mc->max_cpus = FSL_IMX7_NUM_CPUS;
+}
+DEFINE_MACHINE("mcimx7d-sabre", mcimx7d_sabre_machine_init)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index dbb3c8036a..2c07245047 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -169,6 +169,7 @@ static const char *valid_cpus[] = {
ARM_CPU_TYPE_NAME("cortex-a53"),
ARM_CPU_TYPE_NAME("cortex-a57"),
ARM_CPU_TYPE_NAME("host"),
+ ARM_CPU_TYPE_NAME("max"),
};
static bool cpu_type_valid(const char *cpu)
@@ -1206,16 +1207,23 @@ static void machvirt_init(MachineState *machine)
/* We can probe only here because during property set
* KVM is not available yet
*/
- if (!vms->gic_version) {
+ if (vms->gic_version <= 0) {
+ /* "host" or "max" */
if (!kvm_enabled()) {
- error_report("gic-version=host requires KVM");
- exit(1);
- }
-
- vms->gic_version = kvm_arm_vgic_probe();
- if (!vms->gic_version) {
- error_report("Unable to determine GIC version supported by host");
- exit(1);
+ if (vms->gic_version == 0) {
+ error_report("gic-version=host requires KVM");
+ exit(1);
+ } else {
+ /* "max": currently means 3 for TCG */
+ vms->gic_version = 3;
+ }
+ } else {
+ vms->gic_version = kvm_arm_vgic_probe();
+ if (!vms->gic_version) {
+ error_report(
+ "Unable to determine GIC version supported by host");
+ exit(1);
+ }
}
}
@@ -1479,9 +1487,11 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
vms->gic_version = 2;
} else if (!strcmp(value, "host")) {
vms->gic_version = 0; /* Will probe later */
+ } else if (!strcmp(value, "max")) {
+ vms->gic_version = -1; /* Will probe later */
} else {
error_setg(errp, "Invalid gic-version value");
- error_append_hint(errp, "Valid values are 3, 2, host.\n");
+ error_append_hint(errp, "Valid values are 3, 2, host, max.\n");
}
}
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index 69227fd4c9..465796e97c 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -282,6 +282,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
s->virt, "has_el2", NULL);
object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR,
"reset-cbar", &error_abort);
+ object_property_set_int(OBJECT(&s->apu_cpu[i]), num_apus,
+ "core-count", &error_abort);
object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized",
&err);
if (err) {
diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
index 4b69f737b5..6d6597c065 100644
--- a/hw/pci-host/Makefile.objs
+++ b/hw/pci-host/Makefile.objs
@@ -17,3 +17,5 @@ common-obj-$(CONFIG_PCI_PIIX) += piix.o
common-obj-$(CONFIG_PCI_Q35) += q35.o
common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
common-obj-$(CONFIG_PCI_XILINX) += xilinx-pcie.o
+
+common-obj-$(CONFIG_PCI_DESIGNWARE) += designware.o
diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c
new file mode 100644
index 0000000000..29ea313798
--- /dev/null
+++ b/hw/pci-host/designware.c
@@ -0,0 +1,754 @@
+/*
+ * Copyright (c) 2018, Impinj, Inc.
+ *
+ * Designware PCIe IP block emulation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_host.h"
+#include "hw/pci/pcie_port.h"
+#include "hw/pci-host/designware.h"
+
+#define DESIGNWARE_PCIE_PORT_LINK_CONTROL 0x710
+#define DESIGNWARE_PCIE_PHY_DEBUG_R1 0x72C
+#define DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP BIT(4)
+#define DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
+#define DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE BIT(17)
+#define DESIGNWARE_PCIE_MSI_ADDR_LO 0x820
+#define DESIGNWARE_PCIE_MSI_ADDR_HI 0x824
+#define DESIGNWARE_PCIE_MSI_INTR0_ENABLE 0x828
+#define DESIGNWARE_PCIE_MSI_INTR0_MASK 0x82C
+#define DESIGNWARE_PCIE_MSI_INTR0_STATUS 0x830
+#define DESIGNWARE_PCIE_ATU_VIEWPORT 0x900
+#define DESIGNWARE_PCIE_ATU_REGION_INBOUND BIT(31)
+#define DESIGNWARE_PCIE_ATU_CR1 0x904
+#define DESIGNWARE_PCIE_ATU_TYPE_MEM (0x0 << 0)
+#define DESIGNWARE_PCIE_ATU_CR2 0x908
+#define DESIGNWARE_PCIE_ATU_ENABLE BIT(31)
+#define DESIGNWARE_PCIE_ATU_LOWER_BASE 0x90C
+#define DESIGNWARE_PCIE_ATU_UPPER_BASE 0x910
+#define DESIGNWARE_PCIE_ATU_LIMIT 0x914
+#define DESIGNWARE_PCIE_ATU_LOWER_TARGET 0x918
+#define DESIGNWARE_PCIE_ATU_BUS(x) (((x) >> 24) & 0xff)
+#define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff)
+#define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C
+
+static DesignwarePCIEHost *
+designware_pcie_root_to_host(DesignwarePCIERoot *root)
+{
+ BusState *bus = qdev_get_parent_bus(DEVICE(root));
+ return DESIGNWARE_PCIE_HOST(bus->parent);
+}
+
+static void designware_pcie_root_msi_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned len)
+{
+ DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
+ DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
+
+ 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);
+ }
+}
+
+static const MemoryRegionOps designware_pci_host_msi_ops = {
+ .write = designware_pcie_root_msi_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot *root)
+
+{
+ MemoryRegion *mem = &root->msi.iomem;
+ const uint64_t base = root->msi.base;
+ const bool enable = root->msi.intr[0].enable;
+
+ memory_region_set_address(mem, base);
+ memory_region_set_enabled(mem, enable);
+}
+
+static DesignwarePCIEViewport *
+designware_pcie_root_get_current_viewport(DesignwarePCIERoot *root)
+{
+ const unsigned int idx = root->atu_viewport & 0xF;
+ const unsigned int dir =
+ !!(root->atu_viewport & DESIGNWARE_PCIE_ATU_REGION_INBOUND);
+ return &root->viewports[dir][idx];
+}
+
+static uint32_t
+designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len)
+{
+ DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d);
+ DesignwarePCIEViewport *viewport =
+ designware_pcie_root_get_current_viewport(root);
+
+ uint32_t val;
+
+ switch (address) {
+ case DESIGNWARE_PCIE_PORT_LINK_CONTROL:
+ /*
+ * Linux guest uses this register only to configure number of
+ * PCIE lane (which in our case is irrelevant) and doesn't
+ * really care about the value it reads from this register
+ */
+ val = 0xDEADBEEF;
+ break;
+
+ case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL:
+ /*
+ * To make sure that any code in guest waiting for speed
+ * change does not time out we always report
+ * PORT_LOGIC_SPEED_CHANGE as set
+ */
+ val = DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_ADDR_LO:
+ val = root->msi.base;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_ADDR_HI:
+ val = root->msi.base >> 32;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_INTR0_ENABLE:
+ val = root->msi.intr[0].enable;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_INTR0_MASK:
+ val = root->msi.intr[0].mask;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_INTR0_STATUS:
+ val = root->msi.intr[0].status;
+ break;
+
+ case DESIGNWARE_PCIE_PHY_DEBUG_R1:
+ val = DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_VIEWPORT:
+ val = root->atu_viewport;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_LOWER_BASE:
+ val = viewport->base;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_UPPER_BASE:
+ val = viewport->base >> 32;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_LOWER_TARGET:
+ val = viewport->target;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_UPPER_TARGET:
+ val = viewport->target >> 32;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_LIMIT:
+ val = viewport->limit;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_CR1:
+ case DESIGNWARE_PCIE_ATU_CR2: /* FALLTHROUGH */
+ val = viewport->cr[(address - DESIGNWARE_PCIE_ATU_CR1) /
+ sizeof(uint32_t)];
+ break;
+
+ default:
+ val = pci_default_read_config(d, address, len);
+ break;
+ }
+
+ return val;
+}
+
+static uint64_t designware_pcie_root_data_access(void *opaque, hwaddr addr,
+ uint64_t *val, unsigned len)
+{
+ DesignwarePCIEViewport *viewport = opaque;
+ DesignwarePCIERoot *root = viewport->root;
+
+ const uint8_t busnum = DESIGNWARE_PCIE_ATU_BUS(viewport->target);
+ const uint8_t devfn = DESIGNWARE_PCIE_ATU_DEVFN(viewport->target);
+ PCIBus *pcibus = pci_get_bus(PCI_DEVICE(root));
+ PCIDevice *pcidev = pci_find_device(pcibus, busnum, devfn);
+
+ if (pcidev) {
+ addr &= pci_config_size(pcidev) - 1;
+
+ if (val) {
+ pci_host_config_write_common(pcidev, addr,
+ pci_config_size(pcidev),
+ *val, len);
+ } else {
+ return pci_host_config_read_common(pcidev, addr,
+ pci_config_size(pcidev),
+ len);
+ }
+ }
+
+ return UINT64_MAX;
+}
+
+static uint64_t designware_pcie_root_data_read(void *opaque, hwaddr addr,
+ unsigned len)
+{
+ return designware_pcie_root_data_access(opaque, addr, NULL, len);
+}
+
+static void designware_pcie_root_data_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned len)
+{
+ designware_pcie_root_data_access(opaque, addr, &val, len);
+}
+
+static const MemoryRegionOps designware_pci_host_conf_ops = {
+ .read = designware_pcie_root_data_read,
+ .write = designware_pcie_root_data_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+static void designware_pcie_update_viewport(DesignwarePCIERoot *root,
+ DesignwarePCIEViewport *viewport)
+{
+ const uint64_t target = viewport->target;
+ const uint64_t base = viewport->base;
+ const uint64_t size = (uint64_t)viewport->limit - base + 1;
+ const bool enabled = viewport->cr[1] & DESIGNWARE_PCIE_ATU_ENABLE;
+
+ MemoryRegion *current, *other;
+
+ if (viewport->cr[0] == DESIGNWARE_PCIE_ATU_TYPE_MEM) {
+ current = &viewport->mem;
+ other = &viewport->cfg;
+ memory_region_set_alias_offset(current, target);
+ } else {
+ current = &viewport->cfg;
+ other = &viewport->mem;
+ }
+
+ /*
+ * An outbound viewport can be reconfigure from being MEM to CFG,
+ * to account for that we disable the "other" memory region that
+ * becomes unused due to that fact.
+ */
+ memory_region_set_enabled(other, false);
+ if (enabled) {
+ memory_region_set_size(current, size);
+ memory_region_set_address(current, base);
+ }
+ memory_region_set_enabled(current, enabled);
+}
+
+static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d);
+ DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
+ DesignwarePCIEViewport *viewport =
+ designware_pcie_root_get_current_viewport(root);
+
+ switch (address) {
+ case DESIGNWARE_PCIE_PORT_LINK_CONTROL:
+ case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL:
+ case DESIGNWARE_PCIE_PHY_DEBUG_R1:
+ /* No-op */
+ break;
+
+ case DESIGNWARE_PCIE_MSI_ADDR_LO:
+ root->msi.base &= 0xFFFFFFFF00000000ULL;
+ root->msi.base |= val;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_ADDR_HI:
+ root->msi.base &= 0x00000000FFFFFFFFULL;
+ root->msi.base |= (uint64_t)val << 32;
+ break;
+
+ case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: {
+ const bool update_msi_mapping = !root->msi.intr[0].enable ^ !!val;
+
+ root->msi.intr[0].enable = val;
+
+ if (update_msi_mapping) {
+ designware_pcie_root_update_msi_mapping(root);
+ }
+ break;
+ }
+
+ case DESIGNWARE_PCIE_MSI_INTR0_MASK:
+ root->msi.intr[0].mask = val;
+ break;
+
+ 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);
+ }
+ break;
+
+ case DESIGNWARE_PCIE_ATU_VIEWPORT:
+ root->atu_viewport = val;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_LOWER_BASE:
+ viewport->base &= 0xFFFFFFFF00000000ULL;
+ viewport->base |= val;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_UPPER_BASE:
+ viewport->base &= 0x00000000FFFFFFFFULL;
+ viewport->base |= (uint64_t)val << 32;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_LOWER_TARGET:
+ viewport->target &= 0xFFFFFFFF00000000ULL;
+ viewport->target |= val;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_UPPER_TARGET:
+ viewport->target &= 0x00000000FFFFFFFFULL;
+ viewport->target |= val;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_LIMIT:
+ viewport->limit = val;
+ break;
+
+ case DESIGNWARE_PCIE_ATU_CR1:
+ viewport->cr[0] = val;
+ break;
+ case DESIGNWARE_PCIE_ATU_CR2:
+ viewport->cr[1] = val;
+ designware_pcie_update_viewport(root, viewport);
+ break;
+
+ default:
+ pci_bridge_write_config(d, address, val, len);
+ break;
+ }
+}
+
+static char *designware_pcie_viewport_name(const char *direction,
+ unsigned int i,
+ const char *type)
+{
+ return g_strdup_printf("PCI %s Viewport %u [%s]",
+ direction, i, type);
+}
+
+static void designware_pcie_root_realize(PCIDevice *dev, Error **errp)
+{
+ DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev);
+ DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
+ MemoryRegion *address_space = &host->pci.memory;
+ PCIBridge *br = PCI_BRIDGE(dev);
+ DesignwarePCIEViewport *viewport;
+ /*
+ * Dummy values used for initial configuration of MemoryRegions
+ * that belong to a given viewport
+ */
+ const hwaddr dummy_offset = 0;
+ const uint64_t dummy_size = 4;
+ size_t i;
+
+ br->bus_name = "dw-pcie";
+
+ pci_set_word(dev->config + PCI_COMMAND,
+ PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+
+ pci_config_set_interrupt_pin(dev->config, 1);
+ pci_bridge_initfn(dev, TYPE_PCIE_BUS);
+
+ pcie_port_init_reg(dev);
+
+ pcie_cap_init(dev, 0x70, PCI_EXP_TYPE_ROOT_PORT,
+ 0, &error_fatal);
+
+ msi_nonbroken = true;
+ msi_init(dev, 0x50, 32, true, true, &error_fatal);
+
+ for (i = 0; i < DESIGNWARE_PCIE_NUM_VIEWPORTS; i++) {
+ MemoryRegion *source, *destination, *mem;
+ const char *direction;
+ char *name;
+
+ viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i];
+ viewport->inbound = true;
+ viewport->base = 0x0000000000000000ULL;
+ viewport->target = 0x0000000000000000ULL;
+ viewport->limit = UINT32_MAX;
+ viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM;
+
+ source = &host->pci.address_space_root;
+ destination = get_system_memory();
+ direction = "Inbound";
+
+ /*
+ * Configure MemoryRegion implementing PCI -> CPU memory
+ * access
+ */
+ mem = &viewport->mem;
+ name = designware_pcie_viewport_name(direction, i, "MEM");
+ memory_region_init_alias(mem, OBJECT(root), name, destination,
+ dummy_offset, dummy_size);
+ memory_region_add_subregion_overlap(source, dummy_offset, mem, -1);
+ memory_region_set_enabled(mem, false);
+ g_free(name);
+
+ viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_OUTBOUND][i];
+ viewport->root = root;
+ viewport->inbound = false;
+ viewport->base = 0x0000000000000000ULL;
+ viewport->target = 0x0000000000000000ULL;
+ viewport->limit = UINT32_MAX;
+ viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM;
+
+ destination = &host->pci.memory;
+ direction = "Outbound";
+ source = get_system_memory();
+
+ /*
+ * Configure MemoryRegion implementing CPU -> PCI memory
+ * access
+ */
+ mem = &viewport->mem;
+ name = designware_pcie_viewport_name(direction, i, "MEM");
+ memory_region_init_alias(mem, OBJECT(root), name, destination,
+ dummy_offset, dummy_size);
+ memory_region_add_subregion(source, dummy_offset, mem);
+ memory_region_set_enabled(mem, false);
+ g_free(name);
+
+ /*
+ * Configure MemoryRegion implementing access to configuration
+ * space
+ */
+ mem = &viewport->cfg;
+ name = designware_pcie_viewport_name(direction, i, "CFG");
+ memory_region_init_io(&viewport->cfg, OBJECT(root),
+ &designware_pci_host_conf_ops,
+ viewport, name, dummy_size);
+ memory_region_add_subregion(source, dummy_offset, mem);
+ memory_region_set_enabled(mem, false);
+ g_free(name);
+ }
+
+ /*
+ * If no inbound iATU windows are configured, HW defaults to
+ * letting inbound TLPs to pass in. We emulate that by exlicitly
+ * configuring first inbound window to cover all of target's
+ * address space.
+ *
+ * NOTE: This will not work correctly for the case when first
+ * configured inbound window is window 0
+ */
+ viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][0];
+ viewport->cr[1] = DESIGNWARE_PCIE_ATU_ENABLE;
+ designware_pcie_update_viewport(root, viewport);
+
+ memory_region_init_io(&root->msi.iomem, OBJECT(root),
+ &designware_pci_host_msi_ops,
+ root, "pcie-msi", 0x4);
+ /*
+ * We initially place MSI interrupt I/O region a adress 0 and
+ * disable it. It'll be later moved to correct offset and enabled
+ * in designware_pcie_root_update_msi_mapping() as a part of
+ * initialization done by guest OS
+ */
+ memory_region_add_subregion(address_space, dummy_offset, &root->msi.iomem);
+ memory_region_set_enabled(&root->msi.iomem, false);
+}
+
+static void designware_pcie_set_irq(void *opaque, int irq_num, int level)
+{
+ DesignwarePCIEHost *host = DESIGNWARE_PCIE_HOST(opaque);
+
+ qemu_set_irq(host->pci.irqs[irq_num], level);
+}
+
+static const char *
+designware_pcie_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus)
+{
+ return "0000:00";
+}
+
+static const VMStateDescription vmstate_designware_pcie_msi_bank = {
+ .name = "designware-pcie-msi-bank",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(enable, DesignwarePCIEMSIBank),
+ VMSTATE_UINT32(mask, DesignwarePCIEMSIBank),
+ VMSTATE_UINT32(status, DesignwarePCIEMSIBank),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_designware_pcie_msi = {
+ .name = "designware-pcie-msi",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(base, DesignwarePCIEMSI),
+ VMSTATE_STRUCT_ARRAY(intr,
+ DesignwarePCIEMSI,
+ DESIGNWARE_PCIE_NUM_MSI_BANKS,
+ 1,
+ vmstate_designware_pcie_msi_bank,
+ DesignwarePCIEMSIBank),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_designware_pcie_viewport = {
+ .name = "designware-pcie-viewport",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(base, DesignwarePCIEViewport),
+ VMSTATE_UINT64(target, DesignwarePCIEViewport),
+ VMSTATE_UINT32(limit, DesignwarePCIEViewport),
+ VMSTATE_UINT32_ARRAY(cr, DesignwarePCIEViewport, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_designware_pcie_root = {
+ .name = "designware-pcie-root",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
+ VMSTATE_UINT32(atu_viewport, DesignwarePCIERoot),
+ VMSTATE_STRUCT_2DARRAY(viewports,
+ DesignwarePCIERoot,
+ 2,
+ DESIGNWARE_PCIE_NUM_VIEWPORTS,
+ 1,
+ vmstate_designware_pcie_viewport,
+ DesignwarePCIEViewport),
+ VMSTATE_STRUCT(msi,
+ DesignwarePCIERoot,
+ 1,
+ vmstate_designware_pcie_msi,
+ DesignwarePCIEMSI),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void designware_pcie_root_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+
+ k->vendor_id = PCI_VENDOR_ID_SYNOPSYS;
+ k->device_id = 0xABCD;
+ k->revision = 0;
+ k->class_id = PCI_CLASS_BRIDGE_PCI;
+ k->is_bridge = true;
+ k->exit = pci_bridge_exitfn;
+ k->realize = designware_pcie_root_realize;
+ k->config_read = designware_pcie_root_config_read;
+ k->config_write = designware_pcie_root_config_write;
+
+ dc->reset = pci_bridge_reset;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->user_creatable = false;
+ dc->vmsd = &vmstate_designware_pcie_root;
+}
+
+static uint64_t designware_pcie_host_mmio_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ PCIHostState *pci = PCI_HOST_BRIDGE(opaque);
+ PCIDevice *device = pci_find_device(pci->bus, 0, 0);
+
+ return pci_host_config_read_common(device,
+ addr,
+ pci_config_size(device),
+ size);
+}
+
+static void designware_pcie_host_mmio_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned int size)
+{
+ PCIHostState *pci = PCI_HOST_BRIDGE(opaque);
+ PCIDevice *device = pci_find_device(pci->bus, 0, 0);
+
+ return pci_host_config_write_common(device,
+ addr,
+ pci_config_size(device),
+ val, size);
+}
+
+static const MemoryRegionOps designware_pci_mmio_ops = {
+ .read = designware_pcie_host_mmio_read,
+ .write = designware_pcie_host_mmio_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static AddressSpace *designware_pcie_host_set_iommu(PCIBus *bus, void *opaque,
+ int devfn)
+{
+ DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(opaque);
+
+ return &s->pci.address_space;
+}
+
+static void designware_pcie_host_realize(DeviceState *dev, Error **errp)
+{
+ PCIHostState *pci = PCI_HOST_BRIDGE(dev);
+ DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(s->pci.irqs); i++) {
+ sysbus_init_irq(sbd, &s->pci.irqs[i]);
+ }
+
+ memory_region_init_io(&s->mmio,
+ OBJECT(s),
+ &designware_pci_mmio_ops,
+ s,
+ "pcie.reg", 4 * 1024);
+ sysbus_init_mmio(sbd, &s->mmio);
+
+ memory_region_init(&s->pci.io, OBJECT(s), "pcie-pio", 16);
+ memory_region_init(&s->pci.memory, OBJECT(s),
+ "pcie-bus-memory",
+ UINT64_MAX);
+
+ pci->bus = pci_register_root_bus(dev, "pcie",
+ designware_pcie_set_irq,
+ pci_swizzle_map_irq_fn,
+ s,
+ &s->pci.memory,
+ &s->pci.io,
+ 0, 4,
+ TYPE_PCIE_BUS);
+
+ memory_region_init(&s->pci.address_space_root,
+ OBJECT(s),
+ "pcie-bus-address-space-root",
+ UINT64_MAX);
+ memory_region_add_subregion(&s->pci.address_space_root,
+ 0x0, &s->pci.memory);
+ address_space_init(&s->pci.address_space,
+ &s->pci.address_space_root,
+ "pcie-bus-address-space");
+ pci_setup_iommu(pci->bus, designware_pcie_host_set_iommu, s);
+
+ qdev_set_parent_bus(DEVICE(&s->root), BUS(pci->bus));
+ qdev_init_nofail(DEVICE(&s->root));
+}
+
+static const VMStateDescription vmstate_designware_pcie_host = {
+ .name = "designware-pcie-host",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(root,
+ DesignwarePCIEHost,
+ 1,
+ vmstate_designware_pcie_root,
+ DesignwarePCIERoot),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void designware_pcie_host_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
+
+ hc->root_bus_path = designware_pcie_host_root_bus_path;
+ dc->realize = designware_pcie_host_realize;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ dc->fw_name = "pci";
+ dc->vmsd = &vmstate_designware_pcie_host;
+}
+
+static void designware_pcie_host_init(Object *obj)
+{
+ DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(obj);
+ DesignwarePCIERoot *root = &s->root;
+
+ object_initialize(root, sizeof(*root), TYPE_DESIGNWARE_PCIE_ROOT);
+ object_property_add_child(obj, "root", OBJECT(root), NULL);
+ qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0));
+ qdev_prop_set_bit(DEVICE(root), "multifunction", false);
+}
+
+static const TypeInfo designware_pcie_root_info = {
+ .name = TYPE_DESIGNWARE_PCIE_ROOT,
+ .parent = TYPE_PCI_BRIDGE,
+ .instance_size = sizeof(DesignwarePCIERoot),
+ .class_init = designware_pcie_root_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_PCIE_DEVICE },
+ { }
+ },
+};
+
+static const TypeInfo designware_pcie_host_info = {
+ .name = TYPE_DESIGNWARE_PCIE_HOST,
+ .parent = TYPE_PCI_HOST_BRIDGE,
+ .instance_size = sizeof(DesignwarePCIEHost),
+ .instance_init = designware_pcie_host_init,
+ .class_init = designware_pcie_host_class_init,
+};
+
+static void designware_pcie_register(void)
+{
+ type_register_static(&designware_pcie_root_info);
+ type_register_static(&designware_pcie_host_info);
+}
+type_init(designware_pcie_register)
diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs
index c2b7664264..a99d9fbb04 100644
--- a/hw/sd/Makefile.objs
+++ b/hw/sd/Makefile.objs
@@ -1,6 +1,6 @@
common-obj-$(CONFIG_PL181) += pl181.o
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
-common-obj-$(CONFIG_SD) += sd.o core.o
+common-obj-$(CONFIG_SD) += sd.o core.o sdmmc-internal.o
common-obj-$(CONFIG_SDHCI) += sdhci.o
obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 933890e86f..235e0518d6 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -120,6 +120,7 @@ struct SDState {
qemu_irq readonly_cb;
qemu_irq inserted_cb;
QEMUTimer *ocr_power_timer;
+ const char *proto_name;
bool enable;
uint8_t dat_lines;
bool cmd_line;
@@ -866,13 +867,19 @@ static void sd_lock_command(SDState *sd)
sd->card_status &= ~CARD_IS_LOCKED;
}
-static sd_rsp_type_t sd_normal_command(SDState *sd,
- SDRequest req)
+static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
{
uint32_t rca = 0x0000;
uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg;
- trace_sdcard_normal_command(req.cmd, req.arg, sd_state_name(sd->state));
+ /* CMD55 precedes an ACMD, so we are not interested in tracing it.
+ * However there is no ACMD55, so we want to trace this particular case.
+ */
+ if (req.cmd != 55 || sd->expecting_acmd) {
+ trace_sdcard_normal_command(sd->proto_name,
+ sd_cmd_name(req.cmd), req.cmd,
+ req.arg, sd_state_name(sd->state));
+ }
/* Not interpreting this as an app command */
sd->card_status &= ~APP_CMD;
@@ -1162,6 +1169,14 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
}
break;
+ case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */
+ if (sd->state == sd_transfer_state) {
+ sd->state = sd_sendingdata_state;
+ sd->data_offset = 0;
+ return sd_r1;
+ }
+ break;
+
case 23: /* CMD23: SET_BLOCK_COUNT */
switch (sd->state) {
case sd_transfer_state:
@@ -1450,7 +1465,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
static sd_rsp_type_t sd_app_command(SDState *sd,
SDRequest req)
{
- trace_sdcard_app_command(req.cmd, req.arg);
+ trace_sdcard_app_command(sd->proto_name, sd_acmd_name(req.cmd),
+ req.cmd, req.arg, sd_state_name(sd->state));
sd->card_status |= APP_CMD;
switch (req.cmd) {
case 6: /* ACMD6: SET_BUS_WIDTH */
@@ -1765,7 +1781,9 @@ void sd_write_data(SDState *sd, uint8_t value)
if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
return;
- trace_sdcard_write_data(sd->current_cmd, value);
+ trace_sdcard_write_data(sd->proto_name,
+ sd_acmd_name(sd->current_cmd),
+ sd->current_cmd, value);
switch (sd->current_cmd) {
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
sd->data[sd->data_offset ++] = value;
@@ -1883,6 +1901,20 @@ void sd_write_data(SDState *sd, uint8_t value)
}
}
+#define SD_TUNING_BLOCK_SIZE 64
+
+static const uint8_t sd_tuning_block_pattern[SD_TUNING_BLOCK_SIZE] = {
+ /* See: Physical Layer Simplified Specification Version 3.01, Table 4-2 */
+ 0xff, 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0xc3, 0xcc,
+ 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
+ 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
+ 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
+ 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
+ 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
+ 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
+ 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
+};
+
uint8_t sd_read_data(SDState *sd)
{
/* TODO: Append CRCs */
@@ -1903,7 +1935,9 @@ uint8_t sd_read_data(SDState *sd)
io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len;
- trace_sdcard_read_data(sd->current_cmd, io_len);
+ trace_sdcard_read_data(sd->proto_name,
+ sd_acmd_name(sd->current_cmd),
+ sd->current_cmd, io_len);
switch (sd->current_cmd) {
case 6: /* CMD6: SWITCH_FUNCTION */
ret = sd->data[sd->data_offset ++];
@@ -1960,6 +1994,13 @@ uint8_t sd_read_data(SDState *sd)
}
break;
+ case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */
+ if (sd->data_offset >= SD_TUNING_BLOCK_SIZE - 1) {
+ sd->state = sd_transfer_state;
+ }
+ ret = sd_tuning_block_pattern[sd->data_offset++];
+ break;
+
case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */
ret = sd->data[sd->data_offset ++];
@@ -2029,6 +2070,8 @@ static void sd_realize(DeviceState *dev, Error **errp)
SDState *sd = SD_CARD(dev);
int ret;
+ sd->proto_name = sd->spi ? "SPI" : "SD";
+
if (sd->blk && blk_is_read_only(sd->blk)) {
error_setg(errp, "Cannot use read-only drive as SD card");
return;
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index 97b4a473c8..1b828b104d 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -433,13 +433,13 @@ static void sdhci_read_block_from_card(SDHCIState *s)
for (index = 0; index < blk_size; index++) {
data = sdbus_read_data(&s->sdbus);
if (!FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
- /* Device is not in tunning */
+ /* Device is not in tuning */
s->fifo_buffer[index] = data;
}
}
if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
- /* Device is in tunning */
+ /* Device is in tuning */
s->hostctl2 &= ~R_SDHC_HOSTCTL2_EXECUTE_TUNING_MASK;
s->hostctl2 |= R_SDHC_HOSTCTL2_SAMPLING_CLKSEL_MASK;
s->prnsts &= ~(SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ |
diff --git a/hw/sd/sdmmc-internal.c b/hw/sd/sdmmc-internal.c
new file mode 100644
index 0000000000..2053def3f1
--- /dev/null
+++ b/hw/sd/sdmmc-internal.c
@@ -0,0 +1,72 @@
+/*
+ * SD/MMC cards common helpers
+ *
+ * Copyright (c) 2018 Philippe Mathieu-Daudé <f4bug@amsat.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "sdmmc-internal.h"
+
+const char *sd_cmd_name(uint8_t cmd)
+{
+ static const char *cmd_abbrev[SDMMC_CMD_MAX] = {
+ [0] = "GO_IDLE_STATE",
+ [2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR",
+ [4] = "SET_DSR", [5] = "IO_SEND_OP_COND",
+ [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD",
+ [8] = "SEND_IF_COND", [9] = "SEND_CSD",
+ [10] = "SEND_CID", [11] = "VOLTAGE_SWITCH",
+ [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS",
+ [15] = "GO_INACTIVE_STATE",
+ [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK",
+ [18] = "READ_MULTIPLE_BLOCK", [19] = "SEND_TUNING_BLOCK",
+ [20] = "SPEED_CLASS_CONTROL", [21] = "DPS_spec",
+ [23] = "SET_BLOCK_COUNT",
+ [24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK",
+ [26] = "MANUF_RSVD", [27] = "PROGRAM_CSD",
+ [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT",
+ [30] = "SEND_WRITE_PROT",
+ [32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END",
+ [34] = "SW_FUNC_RSVD", [35] = "SW_FUNC_RSVD",
+ [36] = "SW_FUNC_RSVD", [37] = "SW_FUNC_RSVD",
+ [38] = "ERASE",
+ [40] = "DPS_spec",
+ [42] = "LOCK_UNLOCK", [43] = "Q_MANAGEMENT",
+ [44] = "Q_TASK_INFO_A", [45] = "Q_TASK_INFO_B",
+ [46] = "Q_RD_TASK", [47] = "Q_WR_TASK",
+ [48] = "READ_EXTR_SINGLE", [49] = "WRITE_EXTR_SINGLE",
+ [50] = "SW_FUNC_RSVD",
+ [52] = "IO_RW_DIRECT", [53] = "IO_RW_EXTENDED",
+ [54] = "SDIO_RSVD", [55] = "APP_CMD",
+ [56] = "GEN_CMD", [57] = "SW_FUNC_RSVD",
+ [58] = "READ_EXTR_MULTI", [59] = "WRITE_EXTR_MULTI",
+ [60] = "MANUF_RSVD", [61] = "MANUF_RSVD",
+ [62] = "MANUF_RSVD", [63] = "MANUF_RSVD",
+ };
+ return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD";
+}
+
+const char *sd_acmd_name(uint8_t cmd)
+{
+ static const char *acmd_abbrev[SDMMC_CMD_MAX] = {
+ [6] = "SET_BUS_WIDTH",
+ [13] = "SD_STATUS",
+ [14] = "DPS_spec", [15] = "DPS_spec",
+ [16] = "DPS_spec",
+ [18] = "SECU_spec",
+ [22] = "SEND_NUM_WR_BLOCKS", [23] = "SET_WR_BLK_ERASE_COUNT",
+ [41] = "SD_SEND_OP_COND",
+ [42] = "SET_CLR_CARD_DETECT",
+ [51] = "SEND_SCR",
+ [52] = "SECU_spec", [53] = "SECU_spec",
+ [54] = "SECU_spec",
+ [56] = "SECU_spec", [57] = "SECU_spec",
+ [58] = "SECU_spec", [59] = "SECU_spec",
+ };
+
+ return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD";
+}
diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h
index 0e96cb0081..9aa04766fc 100644
--- a/hw/sd/sdmmc-internal.h
+++ b/hw/sd/sdmmc-internal.h
@@ -12,4 +12,28 @@
#define SDMMC_CMD_MAX 64
+/**
+ * sd_cmd_name:
+ * @cmd: A SD "normal" command, up to SDMMC_CMD_MAX.
+ *
+ * Returns a human-readable name describing the command.
+ * The return value is always a static string which does not need
+ * to be freed after use.
+ *
+ * Returns: The command name of @cmd or "UNKNOWN_CMD".
+ */
+const char *sd_cmd_name(uint8_t cmd);
+
+/**
+ * sd_acmd_name:
+ * @cmd: A SD "Application-Specific" command, up to SDMMC_CMD_MAX.
+ *
+ * Returns a human-readable name describing the application command.
+ * The return value is always a static string which does not need
+ * to be freed after use.
+ *
+ * Returns: The application command name of @cmd or "UNKNOWN_ACMD".
+ */
+const char *sd_acmd_name(uint8_t cmd);
+
#endif
diff --git a/hw/sd/trace-events b/hw/sd/trace-events
index 3040d32560..2059ace61f 100644
--- a/hw/sd/trace-events
+++ b/hw/sd/trace-events
@@ -24,8 +24,8 @@ sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of
sdhci_capareg(const char *desc, uint16_t val) "%s: %u"
# hw/sd/sd.c
-sdcard_normal_command(uint8_t cmd, uint32_t arg, const char *state) "CMD%d arg 0x%08x (state %s)"
-sdcard_app_command(uint8_t acmd, uint32_t arg) "ACMD%d arg 0x%08x"
+sdcard_normal_command(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t arg, const char *state) "%s %20s/ CMD%02d arg 0x%08x (state %s)"
+sdcard_app_command(const char *proto, const char *acmd_desc, uint8_t acmd, uint32_t arg, const char *state) "%s %23s/ACMD%02d arg 0x%08x (state %s)"
sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)"
sdcard_powerup(void) ""
sdcard_inquiry_cmd41(void) ""
@@ -39,8 +39,8 @@ sdcard_lock(void) ""
sdcard_unlock(void) ""
sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x"
-sdcard_write_data(uint8_t cmd, uint8_t value) "CMD%02d value 0x%02x"
-sdcard_read_data(uint8_t cmd, int length) "CMD%02d len %d"
+sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint8_t value) "%s %20s/ CMD%02d value 0x%02x"
+sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, int length) "%s %20s/ CMD%02d len %d"
sdcard_set_voltage(uint16_t millivolts) "%u mV"
# hw/sd/milkymist-memcard.c