diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-03-12 11:47:52 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-03-12 11:47:52 +0000 |
commit | 5df089564be6e6a6b1bc79207f74b5b7ed4e1277 (patch) | |
tree | b0bacc6574524443680e0af4f2b27597d3c070de /hw | |
parent | 12c06d6f967a63515399b9e1f6a40f5ce871a8b7 (diff) | |
parent | 076a0fc32a73a9b960e0f73f04a531bc1bd94308 (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.objs | 31 | ||||
-rw-r--r-- | hw/arm/boot.c | 4 | ||||
-rw-r--r-- | hw/arm/fsl-imx7.c | 582 | ||||
-rw-r--r-- | hw/arm/mcimx7d-sabre.c | 90 | ||||
-rw-r--r-- | hw/arm/virt.c | 30 | ||||
-rw-r--r-- | hw/arm/xlnx-zynqmp.c | 2 | ||||
-rw-r--r-- | hw/pci-host/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/pci-host/designware.c | 754 | ||||
-rw-r--r-- | hw/sd/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/sd/sd.c | 55 | ||||
-rw-r--r-- | hw/sd/sdhci.c | 4 | ||||
-rw-r--r-- | hw/sd/sdmmc-internal.c | 72 | ||||
-rw-r--r-- | hw/sd/sdmmc-internal.h | 24 | ||||
-rw-r--r-- | hw/sd/trace-events | 8 |
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 |