From fd1e5c81796454b6459f44bef8759a970eb9e92a Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 9 Feb 2018 10:40:29 +0000 Subject: sdhci: Add i.MX specific subtype of SDHCI IP block found on several generations of i.MX family does not use vanilla SDHCI implementation and it comes with a number of quirks. Introduce i.MX SDHCI subtype of SDHCI block to add code necessary to support unmodified Linux guest driver. Cc: Peter Maydell Cc: Jason Wang Cc: Philippe Mathieu-Daudé Cc: Marcel Apfelbaum Cc: Michael S. Tsirkin Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Cc: yurovsky@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Andrey Smirnov Reviewed-by: Philippe Mathieu-Daudé [PMM: define and use ESDHC_UNDOCUMENTED_REG27] Signed-off-by: Peter Maydell --- include/hw/sd/sdhci.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/hw') diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h index 1cf70f8c23..f8d1ba3538 100644 --- a/include/hw/sd/sdhci.h +++ b/include/hw/sd/sdhci.h @@ -44,6 +44,7 @@ typedef struct SDHCIState { AddressSpace sysbus_dma_as; AddressSpace *dma_as; MemoryRegion *dma_mr; + const MemoryRegionOps *io_ops; QEMUTimer *insert_timer; /* timer for 'changing' sd card. */ QEMUTimer *transfer_timer; @@ -91,8 +92,18 @@ typedef struct SDHCIState { /* Configurable properties */ bool pending_insert_quirk; /* Quirk for Raspberry Pi card insert int */ + uint32_t quirks; } SDHCIState; +/* + * Controller does not provide transfer-complete interrupt when not + * busy. + * + * NOTE: This definition is taken out of Linux kernel and so the + * original bit number is preserved + */ +#define SDHCI_QUIRK_NO_BUSY_IRQ BIT(14) + #define TYPE_PCI_SDHCI "sdhci-pci" #define PCI_SDHCI(obj) OBJECT_CHECK(SDHCIState, (obj), TYPE_PCI_SDHCI) @@ -100,4 +111,6 @@ typedef struct SDHCIState { #define SYSBUS_SDHCI(obj) \ OBJECT_CHECK(SDHCIState, (obj), TYPE_SYSBUS_SDHCI) +#define TYPE_IMX_USDHC "imx-usdhc" + #endif /* SDHCI_H */ -- cgit v1.2.3-55-g7522 From e9e0ef15d2357eca058af71c35eba51093b0c0fb Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 9 Feb 2018 10:40:29 +0000 Subject: i.MX: Add code to emulate i.MX7 CCM, PMU and ANALOG IP blocks Add minimal code needed to allow upstream Linux guest to boot. Cc: Peter Maydell Cc: Jason Wang Cc: Philippe Mathieu-Daudé Cc: Marcel Apfelbaum Cc: Michael S. Tsirkin Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Cc: yurovsky@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Andrey Smirnov Signed-off-by: Peter Maydell --- hw/misc/Makefile.objs | 1 + hw/misc/imx7_ccm.c | 277 +++++++++++++++++++++++++++++++++++++++++++++ include/hw/misc/imx7_ccm.h | 139 +++++++++++++++++++++++ 3 files changed, 417 insertions(+) create mode 100644 hw/misc/imx7_ccm.c create mode 100644 include/hw/misc/imx7_ccm.h (limited to 'include/hw') diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index d517f83e81..a28e5e49b0 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -33,6 +33,7 @@ obj-$(CONFIG_IMX) += imx31_ccm.o obj-$(CONFIG_IMX) += imx25_ccm.o obj-$(CONFIG_IMX) += imx6_ccm.o obj-$(CONFIG_IMX) += imx6_src.o +obj-$(CONFIG_IMX) += imx7_ccm.o obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o obj-$(CONFIG_MAINSTONE) += mst_fpga.o diff --git a/hw/misc/imx7_ccm.c b/hw/misc/imx7_ccm.c new file mode 100644 index 0000000000..d90c48bfec --- /dev/null +++ b/hw/misc/imx7_ccm.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2018, Impinj, Inc. + * + * i.MX7 CCM, PMU and ANALOG IP blocks emulation code + * + * Author: Andrey Smirnov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" + +#include "hw/misc/imx7_ccm.h" + +static void imx7_analog_reset(DeviceState *dev) +{ + IMX7AnalogState *s = IMX7_ANALOG(dev); + + memset(s->pmu, 0, sizeof(s->pmu)); + memset(s->analog, 0, sizeof(s->analog)); + + s->analog[ANALOG_PLL_ARM] = 0x00002042; + s->analog[ANALOG_PLL_DDR] = 0x0060302c; + s->analog[ANALOG_PLL_DDR_SS] = 0x00000000; + s->analog[ANALOG_PLL_DDR_NUM] = 0x06aaac4d; + s->analog[ANALOG_PLL_DDR_DENOM] = 0x100003ec; + s->analog[ANALOG_PLL_480] = 0x00002000; + s->analog[ANALOG_PLL_480A] = 0x52605a56; + s->analog[ANALOG_PLL_480B] = 0x52525216; + s->analog[ANALOG_PLL_ENET] = 0x00001fc0; + s->analog[ANALOG_PLL_AUDIO] = 0x0001301b; + s->analog[ANALOG_PLL_AUDIO_SS] = 0x00000000; + s->analog[ANALOG_PLL_AUDIO_NUM] = 0x05f5e100; + s->analog[ANALOG_PLL_AUDIO_DENOM] = 0x2964619c; + s->analog[ANALOG_PLL_VIDEO] = 0x0008201b; + s->analog[ANALOG_PLL_VIDEO_SS] = 0x00000000; + s->analog[ANALOG_PLL_VIDEO_NUM] = 0x0000f699; + s->analog[ANALOG_PLL_VIDEO_DENOM] = 0x000f4240; + s->analog[ANALOG_PLL_MISC0] = 0x00000000; + + /* all PLLs need to be locked */ + s->analog[ANALOG_PLL_ARM] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_PLL_DDR] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_PLL_480] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_PLL_480A] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_PLL_480B] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_PLL_ENET] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_PLL_AUDIO] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_PLL_VIDEO] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_PLL_MISC0] |= ANALOG_PLL_LOCK; + + /* + * Since I couldn't find any info about this in the reference + * manual the value of this register is based strictly on matching + * what Linux kernel expects it to be. + */ + s->analog[ANALOG_DIGPROG] = 0x720000; + /* + * Set revision to be 1.0 (Arbitrary choice, no particular + * reason). + */ + s->analog[ANALOG_DIGPROG] |= 0x000010; +} + +static void imx7_ccm_reset(DeviceState *dev) +{ + IMX7CCMState *s = IMX7_CCM(dev); + + memset(s->ccm, 0, sizeof(s->ccm)); +} + +#define CCM_INDEX(offset) (((offset) & ~(hwaddr)0xF) / sizeof(uint32_t)) +#define CCM_BITOP(offset) ((offset) & (hwaddr)0xF) + +enum { + CCM_BITOP_NONE = 0x00, + CCM_BITOP_SET = 0x04, + CCM_BITOP_CLR = 0x08, + CCM_BITOP_TOG = 0x0C, +}; + +static uint64_t imx7_set_clr_tog_read(void *opaque, hwaddr offset, + unsigned size) +{ + const uint32_t *mmio = opaque; + + return mmio[CCM_INDEX(offset)]; +} + +static void imx7_set_clr_tog_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + const uint8_t bitop = CCM_BITOP(offset); + const uint32_t index = CCM_INDEX(offset); + uint32_t *mmio = opaque; + + switch (bitop) { + case CCM_BITOP_NONE: + mmio[index] = value; + break; + case CCM_BITOP_SET: + mmio[index] |= value; + break; + case CCM_BITOP_CLR: + mmio[index] &= ~value; + break; + case CCM_BITOP_TOG: + mmio[index] ^= value; + break; + }; +} + +static const struct MemoryRegionOps imx7_set_clr_tog_ops = { + .read = imx7_set_clr_tog_read, + .write = imx7_set_clr_tog_write, + .endianness = DEVICE_NATIVE_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 const struct MemoryRegionOps imx7_digprog_ops = { + .read = imx7_set_clr_tog_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx7_ccm_init(Object *obj) +{ + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX7CCMState *s = IMX7_CCM(obj); + + memory_region_init_io(&s->iomem, + obj, + &imx7_set_clr_tog_ops, + s->ccm, + TYPE_IMX7_CCM ".ccm", + sizeof(s->ccm)); + + sysbus_init_mmio(sd, &s->iomem); +} + +static void imx7_analog_init(Object *obj) +{ + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX7AnalogState *s = IMX7_ANALOG(obj); + + memory_region_init(&s->mmio.container, obj, TYPE_IMX7_ANALOG, + 0x10000); + + memory_region_init_io(&s->mmio.analog, + obj, + &imx7_set_clr_tog_ops, + s->analog, + TYPE_IMX7_ANALOG, + sizeof(s->analog)); + + memory_region_add_subregion(&s->mmio.container, + 0x60, &s->mmio.analog); + + memory_region_init_io(&s->mmio.pmu, + obj, + &imx7_set_clr_tog_ops, + s->pmu, + TYPE_IMX7_ANALOG ".pmu", + sizeof(s->pmu)); + + memory_region_add_subregion(&s->mmio.container, + 0x200, &s->mmio.pmu); + + memory_region_init_io(&s->mmio.digprog, + obj, + &imx7_digprog_ops, + &s->analog[ANALOG_DIGPROG], + TYPE_IMX7_ANALOG ".digprog", + sizeof(uint32_t)); + + memory_region_add_subregion_overlap(&s->mmio.container, + 0x800, &s->mmio.digprog, 10); + + + sysbus_init_mmio(sd, &s->mmio.container); +} + +static const VMStateDescription vmstate_imx7_ccm = { + .name = TYPE_IMX7_CCM, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(ccm, IMX7CCMState, CCM_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static uint32_t imx7_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) +{ + /* + * This function is "consumed" by GPT emulation code, however on + * i.MX7 each GPT block can have their own clock root. This means + * that this functions needs somehow to know requester's identity + * and the way to pass it: be it via additional IMXClk constants + * or by adding another argument to this method needs to be + * figured out + */ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Not implemented\n", + TYPE_IMX7_CCM, __func__); + return 0; +} + +static void imx7_ccm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IMXCCMClass *ccm = IMX_CCM_CLASS(klass); + + dc->reset = imx7_ccm_reset; + dc->vmsd = &vmstate_imx7_ccm; + dc->desc = "i.MX7 Clock Control Module"; + + ccm->get_clock_frequency = imx7_ccm_get_clock_frequency; +} + +static const TypeInfo imx7_ccm_info = { + .name = TYPE_IMX7_CCM, + .parent = TYPE_IMX_CCM, + .instance_size = sizeof(IMX7CCMState), + .instance_init = imx7_ccm_init, + .class_init = imx7_ccm_class_init, +}; + +static const VMStateDescription vmstate_imx7_analog = { + .name = TYPE_IMX7_ANALOG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(analog, IMX7AnalogState, ANALOG_MAX), + VMSTATE_UINT32_ARRAY(pmu, IMX7AnalogState, PMU_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx7_analog_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = imx7_analog_reset; + dc->vmsd = &vmstate_imx7_analog; + dc->desc = "i.MX7 Analog Module"; +} + +static const TypeInfo imx7_analog_info = { + .name = TYPE_IMX7_ANALOG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX7AnalogState), + .instance_init = imx7_analog_init, + .class_init = imx7_analog_class_init, +}; + +static void imx7_ccm_register_type(void) +{ + type_register_static(&imx7_ccm_info); + type_register_static(&imx7_analog_info); +} +type_init(imx7_ccm_register_type) diff --git a/include/hw/misc/imx7_ccm.h b/include/hw/misc/imx7_ccm.h new file mode 100644 index 0000000000..9538f37d98 --- /dev/null +++ b/include/hw/misc/imx7_ccm.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2017, Impinj, Inc. + * + * i.MX7 CCM, PMU and ANALOG IP blocks emulation code + * + * Author: Andrey Smirnov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef IMX7_CCM_H +#define IMX7_CCM_H + +#include "hw/misc/imx_ccm.h" +#include "qemu/bitops.h" + +enum IMX7AnalogRegisters { + ANALOG_PLL_ARM, + ANALOG_PLL_ARM_SET, + ANALOG_PLL_ARM_CLR, + ANALOG_PLL_ARM_TOG, + ANALOG_PLL_DDR, + ANALOG_PLL_DDR_SET, + ANALOG_PLL_DDR_CLR, + ANALOG_PLL_DDR_TOG, + ANALOG_PLL_DDR_SS, + ANALOG_PLL_DDR_SS_SET, + ANALOG_PLL_DDR_SS_CLR, + ANALOG_PLL_DDR_SS_TOG, + ANALOG_PLL_DDR_NUM, + ANALOG_PLL_DDR_NUM_SET, + ANALOG_PLL_DDR_NUM_CLR, + ANALOG_PLL_DDR_NUM_TOG, + ANALOG_PLL_DDR_DENOM, + ANALOG_PLL_DDR_DENOM_SET, + ANALOG_PLL_DDR_DENOM_CLR, + ANALOG_PLL_DDR_DENOM_TOG, + ANALOG_PLL_480, + ANALOG_PLL_480_SET, + ANALOG_PLL_480_CLR, + ANALOG_PLL_480_TOG, + ANALOG_PLL_480A, + ANALOG_PLL_480A_SET, + ANALOG_PLL_480A_CLR, + ANALOG_PLL_480A_TOG, + ANALOG_PLL_480B, + ANALOG_PLL_480B_SET, + ANALOG_PLL_480B_CLR, + ANALOG_PLL_480B_TOG, + ANALOG_PLL_ENET, + ANALOG_PLL_ENET_SET, + ANALOG_PLL_ENET_CLR, + ANALOG_PLL_ENET_TOG, + ANALOG_PLL_AUDIO, + ANALOG_PLL_AUDIO_SET, + ANALOG_PLL_AUDIO_CLR, + ANALOG_PLL_AUDIO_TOG, + ANALOG_PLL_AUDIO_SS, + ANALOG_PLL_AUDIO_SS_SET, + ANALOG_PLL_AUDIO_SS_CLR, + ANALOG_PLL_AUDIO_SS_TOG, + ANALOG_PLL_AUDIO_NUM, + ANALOG_PLL_AUDIO_NUM_SET, + ANALOG_PLL_AUDIO_NUM_CLR, + ANALOG_PLL_AUDIO_NUM_TOG, + ANALOG_PLL_AUDIO_DENOM, + ANALOG_PLL_AUDIO_DENOM_SET, + ANALOG_PLL_AUDIO_DENOM_CLR, + ANALOG_PLL_AUDIO_DENOM_TOG, + ANALOG_PLL_VIDEO, + ANALOG_PLL_VIDEO_SET, + ANALOG_PLL_VIDEO_CLR, + ANALOG_PLL_VIDEO_TOG, + ANALOG_PLL_VIDEO_SS, + ANALOG_PLL_VIDEO_SS_SET, + ANALOG_PLL_VIDEO_SS_CLR, + ANALOG_PLL_VIDEO_SS_TOG, + ANALOG_PLL_VIDEO_NUM, + ANALOG_PLL_VIDEO_NUM_SET, + ANALOG_PLL_VIDEO_NUM_CLR, + ANALOG_PLL_VIDEO_NUM_TOG, + ANALOG_PLL_VIDEO_DENOM, + ANALOG_PLL_VIDEO_DENOM_SET, + ANALOG_PLL_VIDEO_DENOM_CLR, + ANALOG_PLL_VIDEO_DENOM_TOG, + ANALOG_PLL_MISC0, + ANALOG_PLL_MISC0_SET, + ANALOG_PLL_MISC0_CLR, + ANALOG_PLL_MISC0_TOG, + + ANALOG_DIGPROG = 0x800 / sizeof(uint32_t), + ANALOG_MAX, + + ANALOG_PLL_LOCK = BIT(31) +}; + +enum IMX7CCMRegisters { + CCM_MAX = 0xBE00 / sizeof(uint32_t) + 1, +}; + +enum IMX7PMURegisters { + PMU_MAX = 0x140 / sizeof(uint32_t), +}; + +#define TYPE_IMX7_CCM "imx7.ccm" +#define IMX7_CCM(obj) OBJECT_CHECK(IMX7CCMState, (obj), TYPE_IMX7_CCM) + +typedef struct IMX7CCMState { + /* */ + IMXCCMState parent_obj; + + /* */ + MemoryRegion iomem; + + uint32_t ccm[CCM_MAX]; +} IMX7CCMState; + + +#define TYPE_IMX7_ANALOG "imx7.analog" +#define IMX7_ANALOG(obj) OBJECT_CHECK(IMX7AnalogState, (obj), TYPE_IMX7_ANALOG) + +typedef struct IMX7AnalogState { + /* */ + IMXCCMState parent_obj; + + /* */ + struct { + MemoryRegion container; + MemoryRegion analog; + MemoryRegion digprog; + MemoryRegion pmu; + } mmio; + + uint32_t analog[ANALOG_MAX]; + uint32_t pmu[PMU_MAX]; +} IMX7AnalogState; + +#endif /* IMX7_CCM_H */ -- cgit v1.2.3-55-g7522 From 067e68e7041ec6eff73dd053876e22eaf1d4cb00 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 9 Feb 2018 10:40:29 +0000 Subject: i.MX: Add code to emulate i.MX2 watchdog IP block Add enough code to emulate i.MX2 watchdog IP block so it would be possible to reboot the machine running Linux Guest. Cc: Peter Maydell Cc: Jason Wang Cc: Philippe Mathieu-Daudé Cc: Marcel Apfelbaum Cc: Michael S. Tsirkin Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Cc: yurovsky@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Andrey Smirnov Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- hw/misc/Makefile.objs | 1 + hw/misc/imx2_wdt.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++ include/hw/misc/imx2_wdt.h | 33 +++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 hw/misc/imx2_wdt.c create mode 100644 include/hw/misc/imx2_wdt.h (limited to 'include/hw') diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index a28e5e49b0..4b2b705a6c 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -34,6 +34,7 @@ obj-$(CONFIG_IMX) += imx25_ccm.o obj-$(CONFIG_IMX) += imx6_ccm.o obj-$(CONFIG_IMX) += imx6_src.o obj-$(CONFIG_IMX) += imx7_ccm.o +obj-$(CONFIG_IMX) += imx2_wdt.o obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o obj-$(CONFIG_MAINSTONE) += mst_fpga.o diff --git a/hw/misc/imx2_wdt.c b/hw/misc/imx2_wdt.c new file mode 100644 index 0000000000..e47e442592 --- /dev/null +++ b/hw/misc/imx2_wdt.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018, Impinj, Inc. + * + * i.MX2 Watchdog IP block + * + * Author: Andrey Smirnov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "sysemu/watchdog.h" + +#include "hw/misc/imx2_wdt.h" + +#define IMX2_WDT_WCR_WDA BIT(5) /* -> External Reset WDOG_B */ +#define IMX2_WDT_WCR_SRS BIT(4) /* -> Software Reset Signal */ + +static uint64_t imx2_wdt_read(void *opaque, hwaddr addr, + unsigned int size) +{ + return 0; +} + +static void imx2_wdt_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + if (addr == IMX2_WDT_WCR && + (value & (IMX2_WDT_WCR_WDA | IMX2_WDT_WCR_SRS))) { + watchdog_perform_action(); + } +} + +static const MemoryRegionOps imx2_wdt_ops = { + .read = imx2_wdt_read, + .write = imx2_wdt_write, + .endianness = DEVICE_NATIVE_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 void imx2_wdt_realize(DeviceState *dev, Error **errp) +{ + IMX2WdtState *s = IMX2_WDT(dev); + + memory_region_init_io(&s->mmio, OBJECT(dev), + &imx2_wdt_ops, s, + TYPE_IMX2_WDT".mmio", + IMX2_WDT_REG_NUM * sizeof(uint16_t)); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); +} + +static void imx2_wdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx2_wdt_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo imx2_wdt_info = { + .name = TYPE_IMX2_WDT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX2WdtState), + .class_init = imx2_wdt_class_init, +}; + +static WatchdogTimerModel model = { + .wdt_name = "imx2-watchdog", + .wdt_description = "i.MX2 Watchdog", +}; + +static void imx2_wdt_register_type(void) +{ + watchdog_add_model(&model); + type_register_static(&imx2_wdt_info); +} +type_init(imx2_wdt_register_type) diff --git a/include/hw/misc/imx2_wdt.h b/include/hw/misc/imx2_wdt.h new file mode 100644 index 0000000000..8afc99a10e --- /dev/null +++ b/include/hw/misc/imx2_wdt.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017, Impinj, Inc. + * + * i.MX2 Watchdog IP block + * + * Author: Andrey Smirnov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef IMX2_WDT_H +#define IMX2_WDT_H + +#include "hw/sysbus.h" + +#define TYPE_IMX2_WDT "imx2.wdt" +#define IMX2_WDT(obj) OBJECT_CHECK(IMX2WdtState, (obj), TYPE_IMX2_WDT) + +enum IMX2WdtRegisters { + IMX2_WDT_WCR = 0x0000, + IMX2_WDT_REG_NUM = 0x0008 / sizeof(uint16_t) + 1, +}; + + +typedef struct IMX2WdtState { + /* */ + SysBusDevice parent_obj; + + MemoryRegion mmio; +} IMX2WdtState; + +#endif /* IMX7_SNVS_H */ -- cgit v1.2.3-55-g7522 From 0a7bc1c0457a803a89d6506e97d5b035992c7dff Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 9 Feb 2018 10:40:30 +0000 Subject: i.MX: Add code to emulate i.MX7 SNVS IP-block Add code to emulate SNVS IP-block. Currently only the bits needed to be able to emulate machine shutdown are implemented. Cc: Peter Maydell Cc: Jason Wang Cc: Philippe Mathieu-Daudé Cc: Marcel Apfelbaum Cc: Michael S. Tsirkin Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Cc: yurovsky@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Andrey Smirnov Signed-off-by: Peter Maydell --- hw/misc/Makefile.objs | 1 + hw/misc/imx7_snvs.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ include/hw/misc/imx7_snvs.h | 35 +++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 hw/misc/imx7_snvs.c create mode 100644 include/hw/misc/imx7_snvs.h (limited to 'include/hw') diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 4b2b705a6c..019886912c 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -35,6 +35,7 @@ obj-$(CONFIG_IMX) += imx6_ccm.o obj-$(CONFIG_IMX) += imx6_src.o obj-$(CONFIG_IMX) += imx7_ccm.o obj-$(CONFIG_IMX) += imx2_wdt.o +obj-$(CONFIG_IMX) += imx7_snvs.o obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o obj-$(CONFIG_MAINSTONE) += mst_fpga.o diff --git a/hw/misc/imx7_snvs.c b/hw/misc/imx7_snvs.c new file mode 100644 index 0000000000..4df482b282 --- /dev/null +++ b/hw/misc/imx7_snvs.c @@ -0,0 +1,83 @@ +/* + * IMX7 Secure Non-Volatile Storage + * + * Copyright (c) 2018, Impinj, Inc. + * + * Author: Andrey Smirnov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Bare minimum emulation code needed to support being able to shut + * down linux guest gracefully. + */ + +#include "qemu/osdep.h" +#include "hw/misc/imx7_snvs.h" +#include "qemu/log.h" +#include "sysemu/sysemu.h" + +static uint64_t imx7_snvs_read(void *opaque, hwaddr offset, unsigned size) +{ + return 0; +} + +static void imx7_snvs_write(void *opaque, hwaddr offset, + uint64_t v, unsigned size) +{ + const uint32_t value = v; + const uint32_t mask = SNVS_LPCR_TOP | SNVS_LPCR_DP_EN; + + if (offset == SNVS_LPCR && ((value & mask) == mask)) { + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } +} + +static const struct MemoryRegionOps imx7_snvs_ops = { + .read = imx7_snvs_read, + .write = imx7_snvs_write, + .endianness = DEVICE_NATIVE_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 void imx7_snvs_init(Object *obj) +{ + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX7SNVSState *s = IMX7_SNVS(obj); + + memory_region_init_io(&s->mmio, obj, &imx7_snvs_ops, s, + TYPE_IMX7_SNVS, 0x1000); + + sysbus_init_mmio(sd, &s->mmio); +} + +static void imx7_snvs_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "i.MX7 Secure Non-Volatile Storage Module"; +} + +static const TypeInfo imx7_snvs_info = { + .name = TYPE_IMX7_SNVS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX7SNVSState), + .instance_init = imx7_snvs_init, + .class_init = imx7_snvs_class_init, +}; + +static void imx7_snvs_register_type(void) +{ + type_register_static(&imx7_snvs_info); +} +type_init(imx7_snvs_register_type) diff --git a/include/hw/misc/imx7_snvs.h b/include/hw/misc/imx7_snvs.h new file mode 100644 index 0000000000..255f8f26f9 --- /dev/null +++ b/include/hw/misc/imx7_snvs.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017, Impinj, Inc. + * + * i.MX7 SNVS block emulation code + * + * Author: Andrey Smirnov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef IMX7_SNVS_H +#define IMX7_SNVS_H + +#include "qemu/bitops.h" +#include "hw/sysbus.h" + + +enum IMX7SNVSRegisters { + SNVS_LPCR = 0x38, + SNVS_LPCR_TOP = BIT(6), + SNVS_LPCR_DP_EN = BIT(5) +}; + +#define TYPE_IMX7_SNVS "imx7.snvs" +#define IMX7_SNVS(obj) OBJECT_CHECK(IMX7SNVSState, (obj), TYPE_IMX7_SNVS) + +typedef struct IMX7SNVSState { + /* */ + SysBusDevice parent_obj; + + MemoryRegion mmio; +} IMX7SNVSState; + +#endif /* IMX7_SNVS_H */ -- cgit v1.2.3-55-g7522 From 0999e87fa5b45f579670a48f168c77f6235e996f Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 9 Feb 2018 10:40:30 +0000 Subject: i.MX: Add code to emulate GPCv2 IP block Add minimal code needed to allow upstream Linux guest to boot. Cc: Peter Maydell Cc: Jason Wang Cc: Philippe Mathieu-Daudé Cc: Marcel Apfelbaum Cc: Michael S. Tsirkin Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Cc: yurovsky@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Andrey Smirnov Signed-off-by: Peter Maydell --- hw/intc/Makefile.objs | 2 +- hw/intc/imx_gpcv2.c | 125 ++++++++++++++++++++++++++++++++++++++++++++ include/hw/intc/imx_gpcv2.h | 22 ++++++++ 3 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 hw/intc/imx_gpcv2.c create mode 100644 include/hw/intc/imx_gpcv2.h (limited to 'include/hw') diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 571e094a14..0e9963f5ee 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -6,7 +6,7 @@ common-obj-$(CONFIG_XILINX) += xilinx_intc.o common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-pmu-iomod-intc.o common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-ipi.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_pic.o -common-obj-$(CONFIG_IMX) += imx_avic.o +common-obj-$(CONFIG_IMX) += imx_avic.o imx_gpcv2.o common-obj-$(CONFIG_LM32) += lm32_pic.o common-obj-$(CONFIG_REALVIEW) += realview_gic.o common-obj-$(CONFIG_SLAVIO) += slavio_intctl.o diff --git a/hw/intc/imx_gpcv2.c b/hw/intc/imx_gpcv2.c new file mode 100644 index 0000000000..4eb9ce2668 --- /dev/null +++ b/hw/intc/imx_gpcv2.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2018, Impinj, Inc. + * + * i.MX7 GPCv2 block emulation code + * + * Author: Andrey Smirnov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/intc/imx_gpcv2.h" +#include "qemu/log.h" + +#define GPC_PU_PGC_SW_PUP_REQ 0x0f8 +#define GPC_PU_PGC_SW_PDN_REQ 0x104 + +#define USB_HSIC_PHY_SW_Pxx_REQ BIT(4) +#define USB_OTG2_PHY_SW_Pxx_REQ BIT(3) +#define USB_OTG1_PHY_SW_Pxx_REQ BIT(2) +#define PCIE_PHY_SW_Pxx_REQ BIT(1) +#define MIPI_PHY_SW_Pxx_REQ BIT(0) + + +static void imx_gpcv2_reset(DeviceState *dev) +{ + IMXGPCv2State *s = IMX_GPCV2(dev); + + memset(s->regs, 0, sizeof(s->regs)); +} + +static uint64_t imx_gpcv2_read(void *opaque, hwaddr offset, + unsigned size) +{ + IMXGPCv2State *s = opaque; + + return s->regs[offset / sizeof(uint32_t)]; +} + +static void imx_gpcv2_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IMXGPCv2State *s = opaque; + const size_t idx = offset / sizeof(uint32_t); + + s->regs[idx] = value; + + /* + * Real HW will clear those bits once as a way to indicate that + * power up request is complete + */ + if (offset == GPC_PU_PGC_SW_PUP_REQ || + offset == GPC_PU_PGC_SW_PDN_REQ) { + s->regs[idx] &= ~(USB_HSIC_PHY_SW_Pxx_REQ | + USB_OTG2_PHY_SW_Pxx_REQ | + USB_OTG1_PHY_SW_Pxx_REQ | + PCIE_PHY_SW_Pxx_REQ | + MIPI_PHY_SW_Pxx_REQ); + } +} + +static const struct MemoryRegionOps imx_gpcv2_ops = { + .read = imx_gpcv2_read, + .write = imx_gpcv2_write, + .endianness = DEVICE_NATIVE_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 void imx_gpcv2_init(Object *obj) +{ + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMXGPCv2State *s = IMX_GPCV2(obj); + + memory_region_init_io(&s->iomem, + obj, + &imx_gpcv2_ops, + s, + TYPE_IMX_GPCV2 ".iomem", + sizeof(s->regs)); + sysbus_init_mmio(sd, &s->iomem); +} + +static const VMStateDescription vmstate_imx_gpcv2 = { + .name = TYPE_IMX_GPCV2, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, IMXGPCv2State, GPC_NUM), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx_gpcv2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = imx_gpcv2_reset; + dc->vmsd = &vmstate_imx_gpcv2; + dc->desc = "i.MX GPCv2 Module"; +} + +static const TypeInfo imx_gpcv2_info = { + .name = TYPE_IMX_GPCV2, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXGPCv2State), + .instance_init = imx_gpcv2_init, + .class_init = imx_gpcv2_class_init, +}; + +static void imx_gpcv2_register_type(void) +{ + type_register_static(&imx_gpcv2_info); +} +type_init(imx_gpcv2_register_type) diff --git a/include/hw/intc/imx_gpcv2.h b/include/hw/intc/imx_gpcv2.h new file mode 100644 index 0000000000..ed978b24bb --- /dev/null +++ b/include/hw/intc/imx_gpcv2.h @@ -0,0 +1,22 @@ +#ifndef IMX_GPCV2_H +#define IMX_GPCV2_H + +#include "hw/sysbus.h" + +enum IMXGPCv2Registers { + GPC_NUM = 0xE00 / sizeof(uint32_t), +}; + +typedef struct IMXGPCv2State { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + uint32_t regs[GPC_NUM]; +} IMXGPCv2State; + +#define TYPE_IMX_GPCV2 "imx-gpcv2" +#define IMX_GPCV2(obj) OBJECT_CHECK(IMXGPCv2State, (obj), TYPE_IMX_GPCV2) + +#endif /* IMX_GPCV2_H */ -- cgit v1.2.3-55-g7522 From a62bf59fd97c15e95e271adb318135fbe0277730 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 9 Feb 2018 10:40:30 +0000 Subject: i.MX: Add i.MX7 GPT variant Add minimal code needed to allow upstream Linux guest to boot. Cc: Peter Maydell Cc: Jason Wang Cc: Philippe Mathieu-Daudé Cc: Marcel Apfelbaum Cc: Michael S. Tsirkin Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Cc: yurovsky@gmail.com Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Andrey Smirnov Signed-off-by: Peter Maydell --- hw/timer/imx_gpt.c | 25 +++++++++++++++++++++++++ include/hw/timer/imx_gpt.h | 1 + 2 files changed, 26 insertions(+) (limited to 'include/hw') diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index 4b9b54bf2e..65e4ee6bcf 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -113,6 +113,17 @@ static const IMXClk imx6_gpt_clocks[] = { CLK_HIGH, /* 111 reference clock */ }; +static const IMXClk imx7_gpt_clocks[] = { + CLK_NONE, /* 000 No clock source */ + CLK_IPG, /* 001 ipg_clk, 532MHz*/ + CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ + CLK_EXT, /* 011 External clock */ + CLK_32k, /* 100 ipg_clk_32k */ + CLK_HIGH, /* 101 reference clock */ + CLK_NONE, /* 110 not defined */ + CLK_NONE, /* 111 not defined */ +}; + static void imx_gpt_set_freq(IMXGPTState *s) { uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3); @@ -512,6 +523,13 @@ static void imx6_gpt_init(Object *obj) s->clocks = imx6_gpt_clocks; } +static void imx7_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx7_gpt_clocks; +} + static const TypeInfo imx25_gpt_info = { .name = TYPE_IMX25_GPT, .parent = TYPE_SYS_BUS_DEVICE, @@ -532,11 +550,18 @@ static const TypeInfo imx6_gpt_info = { .instance_init = imx6_gpt_init, }; +static const TypeInfo imx7_gpt_info = { + .name = TYPE_IMX7_GPT, + .parent = TYPE_IMX25_GPT, + .instance_init = imx7_gpt_init, +}; + static void imx_gpt_register_types(void) { type_register_static(&imx25_gpt_info); type_register_static(&imx31_gpt_info); type_register_static(&imx6_gpt_info); + type_register_static(&imx7_gpt_info); } type_init(imx_gpt_register_types) diff --git a/include/hw/timer/imx_gpt.h b/include/hw/timer/imx_gpt.h index eac59b2a70..20ccb327c4 100644 --- a/include/hw/timer/imx_gpt.h +++ b/include/hw/timer/imx_gpt.h @@ -77,6 +77,7 @@ #define TYPE_IMX25_GPT "imx25.gpt" #define TYPE_IMX31_GPT "imx31.gpt" #define TYPE_IMX6_GPT "imx6.gpt" +#define TYPE_IMX7_GPT "imx7.gpt" #define TYPE_IMX_GPT TYPE_IMX25_GPT -- cgit v1.2.3-55-g7522 From 30b2f8709d16b1ed2255552ac90935ebab542385 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 9 Feb 2018 10:40:30 +0000 Subject: i.MX: Add implementation of i.MX7 GPR IP block Add minimal code needed to allow upstream Linux guest to boot. Cc: Peter Maydell Cc: Jason Wang Cc: Philippe Mathieu-Daudé Cc: Marcel Apfelbaum Cc: Michael S. Tsirkin Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Cc: yurovsky@gmail.com Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Andrey Smirnov Signed-off-by: Peter Maydell --- hw/misc/Makefile.objs | 1 + hw/misc/imx7_gpr.c | 124 +++++++++++++++++++++++++++++++++++++++++++++ hw/misc/trace-events | 4 ++ include/hw/misc/imx7_gpr.h | 28 ++++++++++ 4 files changed, 157 insertions(+) create mode 100644 hw/misc/imx7_gpr.c create mode 100644 include/hw/misc/imx7_gpr.h (limited to 'include/hw') diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 019886912c..fce426eb75 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -36,6 +36,7 @@ obj-$(CONFIG_IMX) += imx6_src.o obj-$(CONFIG_IMX) += imx7_ccm.o obj-$(CONFIG_IMX) += imx2_wdt.o obj-$(CONFIG_IMX) += imx7_snvs.o +obj-$(CONFIG_IMX) += imx7_gpr.o obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o obj-$(CONFIG_MAINSTONE) += mst_fpga.o diff --git a/hw/misc/imx7_gpr.c b/hw/misc/imx7_gpr.c new file mode 100644 index 0000000000..c2a9df29c6 --- /dev/null +++ b/hw/misc/imx7_gpr.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2018, Impinj, Inc. + * + * i.MX7 GPR IP block emulation code + * + * Author: Andrey Smirnov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Bare minimum emulation code needed to support being able to shut + * down linux guest gracefully. + */ + +#include "qemu/osdep.h" +#include "hw/misc/imx7_gpr.h" +#include "qemu/log.h" +#include "sysemu/sysemu.h" + +#include "trace.h" + +enum IMX7GPRRegisters { + IOMUXC_GPR0 = 0x00, + IOMUXC_GPR1 = 0x04, + IOMUXC_GPR2 = 0x08, + IOMUXC_GPR3 = 0x0c, + IOMUXC_GPR4 = 0x10, + IOMUXC_GPR5 = 0x14, + IOMUXC_GPR6 = 0x18, + IOMUXC_GPR7 = 0x1c, + IOMUXC_GPR8 = 0x20, + IOMUXC_GPR9 = 0x24, + IOMUXC_GPR10 = 0x28, + IOMUXC_GPR11 = 0x2c, + IOMUXC_GPR12 = 0x30, + IOMUXC_GPR13 = 0x34, + IOMUXC_GPR14 = 0x38, + IOMUXC_GPR15 = 0x3c, + IOMUXC_GPR16 = 0x40, + IOMUXC_GPR17 = 0x44, + IOMUXC_GPR18 = 0x48, + IOMUXC_GPR19 = 0x4c, + IOMUXC_GPR20 = 0x50, + IOMUXC_GPR21 = 0x54, + IOMUXC_GPR22 = 0x58, +}; + +#define IMX7D_GPR1_IRQ_MASK BIT(12) +#define IMX7D_GPR1_ENET1_TX_CLK_SEL_MASK BIT(13) +#define IMX7D_GPR1_ENET2_TX_CLK_SEL_MASK BIT(14) +#define IMX7D_GPR1_ENET_TX_CLK_SEL_MASK (0x3 << 13) +#define IMX7D_GPR1_ENET1_CLK_DIR_MASK BIT(17) +#define IMX7D_GPR1_ENET2_CLK_DIR_MASK BIT(18) +#define IMX7D_GPR1_ENET_CLK_DIR_MASK (0x3 << 17) + +#define IMX7D_GPR5_CSI_MUX_CONTROL_MIPI BIT(4) +#define IMX7D_GPR12_PCIE_PHY_REFCLK_SEL BIT(5) +#define IMX7D_GPR22_PCIE_PHY_PLL_LOCKED BIT(31) + + +static uint64_t imx7_gpr_read(void *opaque, hwaddr offset, unsigned size) +{ + trace_imx7_gpr_read(offset); + + if (offset == IOMUXC_GPR22) { + return IMX7D_GPR22_PCIE_PHY_PLL_LOCKED; + } + + return 0; +} + +static void imx7_gpr_write(void *opaque, hwaddr offset, + uint64_t v, unsigned size) +{ + trace_imx7_gpr_write(offset, v); +} + +static const struct MemoryRegionOps imx7_gpr_ops = { + .read = imx7_gpr_read, + .write = imx7_gpr_write, + .endianness = DEVICE_NATIVE_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 void imx7_gpr_init(Object *obj) +{ + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX7GPRState *s = IMX7_GPR(obj); + + memory_region_init_io(&s->mmio, obj, &imx7_gpr_ops, s, + TYPE_IMX7_GPR, 64 * 1024); + sysbus_init_mmio(sd, &s->mmio); +} + +static void imx7_gpr_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "i.MX7 General Purpose Registers Module"; +} + +static const TypeInfo imx7_gpr_info = { + .name = TYPE_IMX7_GPR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX7GPRState), + .instance_init = imx7_gpr_init, + .class_init = imx7_gpr_class_init, +}; + +static void imx7_gpr_register_type(void) +{ + type_register_static(&imx7_gpr_info); +} +type_init(imx7_gpr_register_type) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 616579a403..e6070f280d 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -66,3 +66,7 @@ mps2_scc_cfg_read(unsigned function, unsigned device, uint32_t value) "MPS2 SCC msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" HWADDR_PRIx " data 0x%" PRIx32 " prev 0x%" PRIx32 msf2_sysreg_read(uint64_t offset, uint32_t val) "msf2-sysreg read: addr 0x%08" HWADDR_PRIx " data 0x%08" PRIx32 msf2_sysreg_write_pll_status(void) "Invalid write to read only PLL status register" + +#hw/misc/imx7_gpr.c +imx7_gpr_read(uint64_t offset) "addr 0x%08" HWADDR_PRIx +imx7_gpr_write(uint64_t offset, uint64_t value) "addr 0x%08" HWADDR_PRIx "value 0x%08" HWADDR_PRIx diff --git a/include/hw/misc/imx7_gpr.h b/include/hw/misc/imx7_gpr.h new file mode 100644 index 0000000000..e19373d274 --- /dev/null +++ b/include/hw/misc/imx7_gpr.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017, Impinj, Inc. + * + * i.MX7 GPR IP block emulation code + * + * Author: Andrey Smirnov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef IMX7_GPR_H +#define IMX7_GPR_H + +#include "qemu/bitops.h" +#include "hw/sysbus.h" + +#define TYPE_IMX7_GPR "imx7.gpr" +#define IMX7_GPR(obj) OBJECT_CHECK(IMX7GPRState, (obj), TYPE_IMX7_GPR) + +typedef struct IMX7GPRState { + /* */ + SysBusDevice parent_obj; + + MemoryRegion mmio; +} IMX7GPRState; + +#endif /* IMX7_GPR_H */ -- cgit v1.2.3-55-g7522 From a24273bba858d61f75ab0e98871f7691988fd0ce Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 9 Feb 2018 10:40:30 +0000 Subject: usb: Add basic code to emulate Chipidea USB IP Add code to emulate Chipidea USB IP (used in i.MX SoCs). Tested to work against: -usb -drive if=none,id=stick,file=usb.img,format=raw -device \ usb-storage,bus=usb-bus.0,drive=stick Cc: Peter Maydell Cc: Jason Wang Cc: Philippe Mathieu-Daudé Cc: Marcel Apfelbaum Cc: Michael S. Tsirkin Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Cc: yurovsky@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Andrey Smirnov Signed-off-by: Peter Maydell --- hw/usb/Makefile.objs | 1 + hw/usb/chipidea.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++ include/hw/usb/chipidea.h | 16 +++++ 3 files changed, 193 insertions(+) create mode 100644 hw/usb/chipidea.c create mode 100644 include/hw/usb/chipidea.h (limited to 'include/hw') diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index fbcd498c59..41be700812 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -12,6 +12,7 @@ common-obj-$(CONFIG_USB_XHCI_NEC) += hcd-xhci-nec.o common-obj-$(CONFIG_USB_MUSB) += hcd-musb.o obj-$(CONFIG_TUSB6010) += tusb6010.o +obj-$(CONFIG_IMX) += chipidea.o # emulated usb devices common-obj-$(CONFIG_USB) += dev-hub.o diff --git a/hw/usb/chipidea.c b/hw/usb/chipidea.c new file mode 100644 index 0000000000..60d67f88b8 --- /dev/null +++ b/hw/usb/chipidea.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2018, Impinj, Inc. + * + * Chipidea USB block emulation code + * + * Author: Andrey Smirnov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/usb/hcd-ehci.h" +#include "hw/usb/chipidea.h" +#include "qemu/log.h" + +enum { + CHIPIDEA_USBx_DCIVERSION = 0x000, + CHIPIDEA_USBx_DCCPARAMS = 0x004, + CHIPIDEA_USBx_DCCPARAMS_HC = BIT(8), +}; + +static uint64_t chipidea_read(void *opaque, hwaddr offset, + unsigned size) +{ + return 0; +} + +static void chipidea_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ +} + +static const struct MemoryRegionOps chipidea_ops = { + .read = chipidea_read, + .write = chipidea_write, + .endianness = DEVICE_NATIVE_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 uint64_t chipidea_dc_read(void *opaque, hwaddr offset, + unsigned size) +{ + switch (offset) { + case CHIPIDEA_USBx_DCIVERSION: + return 0x1; + case CHIPIDEA_USBx_DCCPARAMS: + /* + * Real hardware (at least i.MX7) will also report the + * controller as "Device Capable" (and 8 supported endpoints), + * but there doesn't seem to be much point in doing so, since + * we don't emulate that part. + */ + return CHIPIDEA_USBx_DCCPARAMS_HC; + } + + return 0; +} + +static void chipidea_dc_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ +} + +static const struct MemoryRegionOps chipidea_dc_ops = { + .read = chipidea_dc_read, + .write = chipidea_dc_write, + .endianness = DEVICE_NATIVE_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 void chipidea_init(Object *obj) +{ + EHCIState *ehci = &SYS_BUS_EHCI(obj)->ehci; + ChipideaState *ci = CHIPIDEA(obj); + int i; + + for (i = 0; i < ARRAY_SIZE(ci->iomem); i++) { + const struct { + const char *name; + hwaddr offset; + uint64_t size; + const struct MemoryRegionOps *ops; + } regions[ARRAY_SIZE(ci->iomem)] = { + /* + * Registers located between offsets 0x000 and 0xFC + */ + { + .name = TYPE_CHIPIDEA ".misc", + .offset = 0x000, + .size = 0x100, + .ops = &chipidea_ops, + }, + /* + * Registers located between offsets 0x1A4 and 0x1DC + */ + { + .name = TYPE_CHIPIDEA ".endpoints", + .offset = 0x1A4, + .size = 0x1DC - 0x1A4 + 4, + .ops = &chipidea_ops, + }, + /* + * USB_x_DCIVERSION and USB_x_DCCPARAMS + */ + { + .name = TYPE_CHIPIDEA ".dc", + .offset = 0x120, + .size = 8, + .ops = &chipidea_dc_ops, + }, + }; + + memory_region_init_io(&ci->iomem[i], + obj, + regions[i].ops, + ci, + regions[i].name, + regions[i].size); + + memory_region_add_subregion(&ehci->mem, + regions[i].offset, + &ci->iomem[i]); + } +} + +static void chipidea_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(klass); + + /* + * Offsets used were taken from i.MX7Dual Applications Processor + * Reference Manual, Rev 0.1, p. 3177, Table 11-59 + */ + sec->capsbase = 0x100; + sec->opregbase = 0x140; + sec->portnr = 1; + + set_bit(DEVICE_CATEGORY_USB, dc->categories); + dc->desc = "Chipidea USB Module"; +} + +static const TypeInfo chipidea_info = { + .name = TYPE_CHIPIDEA, + .parent = TYPE_SYS_BUS_EHCI, + .instance_size = sizeof(ChipideaState), + .instance_init = chipidea_init, + .class_init = chipidea_class_init, +}; + +static void chipidea_register_type(void) +{ + type_register_static(&chipidea_info); +} +type_init(chipidea_register_type) diff --git a/include/hw/usb/chipidea.h b/include/hw/usb/chipidea.h new file mode 100644 index 0000000000..1ec2e9dbda --- /dev/null +++ b/include/hw/usb/chipidea.h @@ -0,0 +1,16 @@ +#ifndef CHIPIDEA_H +#define CHIPIDEA_H + +#include "hw/usb/hcd-ehci.h" + +typedef struct ChipideaState { + /*< private >*/ + EHCISysBusState parent_obj; + + MemoryRegion iomem[3]; +} ChipideaState; + +#define TYPE_CHIPIDEA "usb-chipidea" +#define CHIPIDEA(obj) OBJECT_CHECK(ChipideaState, (obj), TYPE_CHIPIDEA) + +#endif /* CHIPIDEA_H */ -- cgit v1.2.3-55-g7522