All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support
@ 2018-01-16  1:36 Andrey Smirnov
  2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 01/14] sdhci: Add i.MX specific subtype of SDHCI Andrey Smirnov
                   ` (15 more replies)
  0 siblings, 16 replies; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:36 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

Hi everyone,

This v4 of the patch series containing the work that I've done in
order to enable support for i.MX7 emulation in QEMU.

*NOTE*: Patches 1 and 2 are provided for the sake of completness and
	are going to have to be adapted once Philippe's SD changes
	land in master. As such, they are NOT ready to be
	accepted/merged.

As the one before last commit in the series states the supported i.MX7
features are:

    * up to 2 Cortex A9 cores (SMP works with PSCI)
    * A7 MPCORE (identical to A15 MPCORE)
    * 4 GPTs modules
    * 7 GPIO controllers
    * 2 IOMUXC controllers
    * 1 CCM module
    * 1 SVNS module
    * 1 SRC module
    * 1 GPCv2 controller
    * 4 eCSPI controllers
    * 4 I2C controllers
    * 7 i.MX UART controllers
    * 2 FlexCAN controllers
    * 2 Ethernet controllers (FEC)
    * 3 SD controllers (USDHC)
    * 4 WDT modules
    * 1 SDMA module
    * 1 GPR module
    * 2 USBMISC modules
    * 2 ADC modules
    * 1 PCIe controller
    * 3 USB controllers
    * 1 LCD controller
    * 1 ARMv7 DAP IP block

Feedback is welcome!

Changes since [v3]:

    - Changes to FEC were split into a separate set and merged to master

    - Patchest is rebased on latest master

    - Converted to use PSCI DT fixup code that is shared with virt
      platform (now relocated to live in arm/boot.c)

    - Large number of dummy block were converted to use
      create_unimplemented_device() as opposed to its own dedicated
      type

    - Incorporated varios small feedback items

    - Collected Reviewed-by tags from Peter

Changes since [v2]:

    - Added stubs for more blocks that were causing memory
      transactions when booting Linux guest as were revealed by
      additional testing of the patchest

    - Added proper USB emulation code, so now it should be possible to
      emulated guest's USB bus

Changes since [v1]:

    - Patchset no longer relies on "ignore_memory_transaction_failures = false"
      for its functionality

    - As a consequnce of implementing the above a number of patches
      implementing dummy IP block emulation as well as PCIe emulation
      patches that I alluded to in [v1] are now included in this patch
      series

    - "has_el3" property is no longer being set to "false" as a part
      of intialization of A7 CPU. I couldn't reproduce the issues that
      I thought I was having, so I just dropped that code.

    - A number of smaller feedback items from Peter and other has been
      incorporated into the patches.


Thanks,
Andrey Smirnov

[v3] https://lists.gnu.org/archive/html/qemu-devel/2017-11/msg04236.html
[v2] https://lists.gnu.org/archive/html/qemu-devel/2017-10/msg05516.html
[v1] https://lists.gnu.org/archive/html/qemu-devel/2017-09/msg04770.html

Andrey Smirnov (14):
  sdhci: Add i.MX specific subtype of SDHCI
  hw: i.MX: Convert i.MX6 to use TYPE_IMX_USDHC
  i.MX: Add code to emulate i.MX7 CCM, PMU and ANALOG IP blocks
  i.MX: Add code to emulate i.MX2 watchdog IP block
  i.MX: Add code to emulate i.MX7 SNVS IP-block
  i.MX: Add code to emulate GPCv2 IP block
  i.MX: Add i.MX7 GPT variant
  i.MX: Add implementation of i.MX7 GPR IP block
  pci: Add support for Designware IP block
  usb: Add basic code to emulate Chipidea USB IP
  ARM: Add basic code to emulate A7MPCore DAP block
  i.MX: Add i.MX7 SOC implementation.
  hw/arm: Move virt's PSCI DT fixup code to arm/boot.c
  Implement support for i.MX7 Sabre board

 default-configs/arm-softmmu.mak  |   3 +
 hw/arm/Makefile.objs             |   5 +-
 hw/arm/boot.c                    |  65 ++++
 hw/arm/coresight.c               | 120 ++++++++
 hw/arm/fsl-imx6.c                |   2 +-
 hw/arm/fsl-imx7.c                | 583 ++++++++++++++++++++++++++++++++++++
 hw/arm/mcimx7d-sabre.c           |  90 ++++++
 hw/arm/virt.c                    |  61 ----
 hw/intc/Makefile.objs            |   2 +-
 hw/intc/imx_gpcv2.c              | 125 ++++++++
 hw/misc/Makefile.objs            |   4 +
 hw/misc/imx2_wdt.c               |  89 ++++++
 hw/misc/imx7_ccm.c               | 277 ++++++++++++++++++
 hw/misc/imx7_gpr.c               | 119 ++++++++
 hw/misc/imx7_snvs.c              |  83 ++++++
 hw/pci-host/Makefile.objs        |   2 +
 hw/pci-host/designware.c         | 618 +++++++++++++++++++++++++++++++++++++++
 hw/sd/sdhci-internal.h           |  19 ++
 hw/sd/sdhci.c                    | 228 ++++++++++++++-
 hw/timer/imx_gpt.c               |  25 ++
 hw/usb/Makefile.objs             |   1 +
 hw/usb/chipidea.c                | 176 +++++++++++
 include/hw/arm/coresight.h       |  24 ++
 include/hw/arm/fsl-imx7.h        | 223 ++++++++++++++
 include/hw/intc/imx_gpcv2.h      |  22 ++
 include/hw/misc/imx2_wdt.h       |  33 +++
 include/hw/misc/imx7_ccm.h       | 139 +++++++++
 include/hw/misc/imx7_gpr.h       |  28 ++
 include/hw/misc/imx7_snvs.h      |  35 +++
 include/hw/pci-host/designware.h |  93 ++++++
 include/hw/pci/pci_ids.h         |   2 +
 include/hw/sd/sdhci.h            |  14 +
 include/hw/timer/imx_gpt.h       |   1 +
 include/hw/usb/chipidea.h        |  16 +
 34 files changed, 3261 insertions(+), 66 deletions(-)
 create mode 100644 hw/arm/coresight.c
 create mode 100644 hw/arm/fsl-imx7.c
 create mode 100644 hw/arm/mcimx7d-sabre.c
 create mode 100644 hw/intc/imx_gpcv2.c
 create mode 100644 hw/misc/imx2_wdt.c
 create mode 100644 hw/misc/imx7_ccm.c
 create mode 100644 hw/misc/imx7_gpr.c
 create mode 100644 hw/misc/imx7_snvs.c
 create mode 100644 hw/pci-host/designware.c
 create mode 100644 hw/usb/chipidea.c
 create mode 100644 include/hw/arm/coresight.h
 create mode 100644 include/hw/arm/fsl-imx7.h
 create mode 100644 include/hw/intc/imx_gpcv2.h
 create mode 100644 include/hw/misc/imx2_wdt.h
 create mode 100644 include/hw/misc/imx7_ccm.h
 create mode 100644 include/hw/misc/imx7_gpr.h
 create mode 100644 include/hw/misc/imx7_snvs.h
 create mode 100644 include/hw/pci-host/designware.h
 create mode 100644 include/hw/usb/chipidea.h

-- 
2.14.3

^ permalink raw reply	[flat|nested] 46+ messages in thread

* [Qemu-devel] [PATCH v4 01/14] sdhci: Add i.MX specific subtype of SDHCI
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
@ 2018-01-16  1:36 ` Andrey Smirnov
  2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 02/14] hw: i.MX: Convert i.MX6 to use TYPE_IMX_USDHC Andrey Smirnov
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:36 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

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 <peter.maydell@linaro.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org
Cc: yurovsky@gmail.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 hw/sd/sdhci-internal.h |  19 +++++
 hw/sd/sdhci.c          | 228 ++++++++++++++++++++++++++++++++++++++++++++++++-
 include/hw/sd/sdhci.h  |  14 +++
 3 files changed, 259 insertions(+), 2 deletions(-)

diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h
index 161177cf39..b86ac0791b 100644
--- a/hw/sd/sdhci-internal.h
+++ b/hw/sd/sdhci-internal.h
@@ -85,12 +85,18 @@
 
 /* R/W Host control Register 0x0 */
 #define SDHC_HOSTCTL                   0x28
+#define SDHC_CTRL_LED                  0x01
 #define SDHC_CTRL_DMA_CHECK_MASK       0x18
 #define SDHC_CTRL_SDMA                 0x00
 #define SDHC_CTRL_ADMA1_32             0x08
 #define SDHC_CTRL_ADMA2_32             0x10
 #define SDHC_CTRL_ADMA2_64             0x18
 #define SDHC_DMA_TYPE(x)               ((x) & SDHC_CTRL_DMA_CHECK_MASK)
+#define SDHC_CTRL_4BITBUS              0x02
+#define SDHC_CTRL_8BITBUS              0x20
+#define SDHC_CTRL_CDTEST_INS           0x40
+#define SDHC_CTRL_CDTEST_EN            0x80
+
 
 /* R/W Power Control Register 0x0 */
 #define SDHC_PWRCON                    0x29
@@ -229,4 +235,17 @@ enum {
 
 extern const VMStateDescription sdhci_vmstate;
 
+
+#define ESDHC_MIX_CTRL                  0x48
+#define ESDHC_VENDOR_SPEC               0xc0
+#define ESDHC_DLL_CTRL                  0x60
+
+#define ESDHC_TUNING_CTRL               0xcc
+#define ESDHC_TUNE_CTRL_STATUS          0x68
+#define ESDHC_WTMK_LVL                  0x44
+
+#define ESDHC_CTRL_4BITBUS              (0x1 << 1)
+#define ESDHC_CTRL_8BITBUS              (0x2 << 1)
+
+
 #endif
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index b23d1bfb97..a35e11e89d 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -265,7 +265,8 @@ static void sdhci_send_command(SDHCIState *s)
             }
         }
 
-        if ((s->norintstsen & SDHC_NISEN_TRSCMP) &&
+        if (!(s->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) &&
+            (s->norintstsen & SDHC_NISEN_TRSCMP) &&
             (s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY) {
             s->norintsts |= SDHC_NIS_TRSCMP;
         }
@@ -1194,6 +1195,8 @@ static void sdhci_initfn(SDHCIState *s)
 
     s->insert_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_raise_insertion_irq, s);
     s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_data_transfer, s);
+
+    s->io_ops = &sdhci_mmio_ops;
 }
 
 static void sdhci_uninitfn(SDHCIState *s)
@@ -1354,7 +1357,7 @@ static void sdhci_sysbus_realize(DeviceState *dev, Error ** errp)
     s->buf_maxsz = sdhci_get_fifolen(s);
     s->fifo_buffer = g_malloc0(s->buf_maxsz);
     sysbus_init_irq(sbd, &s->irq);
-    memory_region_init_io(&s->iomem, OBJECT(s), &sdhci_mmio_ops, s, "sdhci",
+    memory_region_init_io(&s->iomem, OBJECT(s), s->io_ops, s, "sdhci",
             SDHC_REGISTERS_MAP_SIZE);
     sysbus_init_mmio(sbd, &s->iomem);
 }
@@ -1393,11 +1396,232 @@ static const TypeInfo sdhci_bus_info = {
     .class_init = sdhci_bus_class_init,
 };
 
+static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
+{
+    SDHCIState *s = SYSBUS_SDHCI(opaque);
+    uint32_t ret;
+    uint16_t hostctl;
+
+    switch (offset) {
+    default:
+        return sdhci_read(opaque, offset, size);
+
+    case SDHC_HOSTCTL:
+        /*
+         * For a detailed explanation on the following bit
+         * manipulation code see comments in a similar part of
+         * usdhc_write()
+         */
+        hostctl = SDHC_DMA_TYPE(s->hostctl) << (8 - 3);
+
+        if (s->hostctl & SDHC_CTRL_8BITBUS) {
+            hostctl |= ESDHC_CTRL_8BITBUS;
+        }
+
+        if (s->hostctl & SDHC_CTRL_4BITBUS) {
+            hostctl |= ESDHC_CTRL_4BITBUS;
+        }
+
+        ret  = hostctl;
+        ret |= (uint32_t)s->blkgap << 16;
+        ret |= (uint32_t)s->wakcon << 24;
+
+        break;
+
+    case ESDHC_DLL_CTRL:
+    case ESDHC_TUNE_CTRL_STATUS:
+    case 0x6c:
+    case ESDHC_TUNING_CTRL:
+    case ESDHC_VENDOR_SPEC:
+    case ESDHC_MIX_CTRL:
+    case ESDHC_WTMK_LVL:
+        ret = 0;
+        break;
+    }
+
+    return ret;
+}
+
+static void
+usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
+{
+    SDHCIState *s = SYSBUS_SDHCI(opaque);
+    uint8_t hostctl;
+    uint32_t value = (uint32_t)val;
+
+    switch (offset) {
+    case ESDHC_DLL_CTRL:
+    case ESDHC_TUNE_CTRL_STATUS:
+    case 0x6c:
+    case ESDHC_TUNING_CTRL:
+    case ESDHC_WTMK_LVL:
+    case ESDHC_VENDOR_SPEC:
+        break;
+
+    case SDHC_HOSTCTL:
+        /*
+         * Here's What ESDHCI has at offset 0x28 (SDHC_HOSTCTL)
+         *
+         *       7         6     5      4      3      2        1      0
+         * |-----------+--------+--------+-----------+----------+---------|
+         * | Card      | Card   | Endian | DATA3     | Data     | Led     |
+         * | Detect    | Detect | Mode   | as Card   | Transfer | Control |
+         * | Signal    | Test   |        | Detection | Width    |         |
+         * | Selection | Level  |        | Pin       |          |         |
+         * |-----------+--------+--------+-----------+----------+---------|
+         *
+         * and 0x29
+         *
+         *  15      10 9    8
+         * |----------+------|
+         * | Reserved | DMA  |
+         * |          | Sel. |
+         * |          |      |
+         * |----------+------|
+         *
+         * and here's what SDCHI spec expects those offsets to be:
+         *
+         * 0x28 (Host Control Register)
+         *
+         *     7        6         5       4  3      2         1        0
+         * |--------+--------+----------+------+--------+----------+---------|
+         * | Card   | Card   | Extended | DMA  | High   | Data     | LED     |
+         * | Detect | Detect | Data     | Sel. | Speed  | Transfer | Control |
+         * | Signal | Test   | Transfer |      | Enable | Width    |         |
+         * | Sel.   | Level  | Width    |      |        |          |         |
+         * |--------+--------+----------+------+--------+----------+---------|
+         *
+         * and 0x29 (Power Control Register)
+         *
+         * |----------------------------------|
+         * | Power Control Register           |
+         * |                                  |
+         * | Description omitted,             |
+         * | since it has no analog in ESDHCI |
+         * |                                  |
+         * |----------------------------------|
+         *
+         * Since offsets 0x2A and 0x2B should be compatible between
+         * both IP specs we only need to reconcile least 16-bit of the
+         * word we've been given.
+         */
+
+        /*
+         * First, save bits 7 6 and 0 since they are identical
+         */
+        hostctl = value & (SDHC_CTRL_LED |
+                           SDHC_CTRL_CDTEST_INS |
+                           SDHC_CTRL_CDTEST_EN);
+        /*
+         * Second, split "Data Transfer Width" from bits 2 and 1 in to
+         * bits 5 and 1
+         */
+        if (value & ESDHC_CTRL_8BITBUS) {
+            hostctl |= SDHC_CTRL_8BITBUS;
+        }
+
+        if (value & ESDHC_CTRL_4BITBUS) {
+            hostctl |= ESDHC_CTRL_4BITBUS;
+        }
+
+        /*
+         * Third, move DMA select from bits 9 and 8 to bits 4 and 3
+         */
+        hostctl |= SDHC_DMA_TYPE(value >> (8 - 3));
+
+        /*
+         * Now place the corrected value into low 16-bit of the value
+         * we are going to give standard SDHCI write function
+         *
+         * NOTE: This transformation should be the inverse of what can
+         * be found in drivers/mmc/host/sdhci-esdhc-imx.c in Linux
+         * kernel
+         */
+        value &= ~UINT16_MAX;
+        value |= hostctl;
+        value |= (uint16_t)s->pwrcon << 8;
+
+        sdhci_write(opaque, offset, value, size);
+        break;
+
+    case ESDHC_MIX_CTRL:
+        /*
+         * So, when SD/MMC stack in Linux tries to write to "Transfer
+         * Mode Register", ESDHC i.MX quirk code will translate it
+         * into a write to ESDHC_MIX_CTRL, so we do the opposite in
+         * order to get where we started
+         *
+         * Note that Auto CMD23 Enable bit is located in a wrong place
+         * on i.MX, but since it is not used by QEMU we do not care.
+         *
+         * We don't want to call sdhci_write(.., SDHC_TRNMOD, ...)
+         * here becuase it will result in a call to
+         * sdhci_send_command(s) which we don't want.
+         *
+         */
+        s->trnmod = value & UINT16_MAX;
+        break;
+    case SDHC_TRNMOD:
+        /*
+         * Similar to above, but this time a write to "Command
+         * Register" will be translated into a 4-byte write to
+         * "Transfer Mode register" where lower 16-bit of value would
+         * be set to zero. So what we do is fill those bits with
+         * cached value from s->trnmod and let the SDHCI
+         * infrastructure handle the rest
+         */
+        sdhci_write(opaque, offset, val | s->trnmod, size);
+        break;
+    case SDHC_BLKSIZE:
+        /*
+         * ESDHCI does not implement "Host SDMA Buffer Boundary", and
+         * Linux driver will try to zero this field out which will
+         * break the rest of SDHCI emulation.
+         *
+         * Linux defaults to maximum possible setting (512K boundary)
+         * and it seems to be the only option that i.MX IP implements,
+         * so we artificially set it to that value.
+         */
+        val |= 0x7 << 12;
+        /* FALLTHROUGH */
+    default:
+        sdhci_write(opaque, offset, val, size);
+        break;
+    }
+}
+
+
+static const MemoryRegionOps usdhc_mmio_ops = {
+    .read = usdhc_read,
+    .write = usdhc_write,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+        .unaligned = false
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void imx_usdhc_init(Object *obj)
+{
+    SDHCIState *s = SYSBUS_SDHCI(obj);
+
+    s->io_ops = &usdhc_mmio_ops;
+    s->quirks = SDHCI_QUIRK_NO_BUSY_IRQ;
+}
+
+static const TypeInfo imx_usdhc_info = {
+    .name = TYPE_IMX_USDHC,
+    .parent = TYPE_SYSBUS_SDHCI,
+    .instance_init = imx_usdhc_init,
+};
+
 static void sdhci_register_types(void)
 {
     type_register_static(&sdhci_pci_info);
     type_register_static(&sdhci_sysbus_info);
     type_register_static(&sdhci_bus_info);
+    type_register_static(&imx_usdhc_info);
 }
 
 type_init(sdhci_register_types)
diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h
index 0f0c3f1e64..b56836a2fc 100644
--- a/include/hw/sd/sdhci.h
+++ b/include/hw/sd/sdhci.h
@@ -39,6 +39,7 @@ typedef struct SDHCIState {
     };
     SDBus sdbus;
     MemoryRegion iomem;
+    const MemoryRegionOps *io_ops;
 
     QEMUTimer *insert_timer;       /* timer for 'changing' sd card. */
     QEMUTimer *transfer_timer;
@@ -83,8 +84,19 @@ typedef struct SDHCIState {
     /* Force Event Auto CMD12 Error Interrupt Reg - write only */
     /* Force Event Error Interrupt Register- write only */
     /* RO Host Controller Version Register always reads as 0x2401 */
+
+    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)
 
@@ -92,4 +104,6 @@ typedef struct SDHCIState {
 #define SYSBUS_SDHCI(obj)                               \
      OBJECT_CHECK(SDHCIState, (obj), TYPE_SYSBUS_SDHCI)
 
+#define TYPE_IMX_USDHC "imx-usdhc"
+
 #endif /* SDHCI_H */
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [Qemu-devel] [PATCH v4 02/14] hw: i.MX: Convert i.MX6 to use TYPE_IMX_USDHC
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
  2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 01/14] sdhci: Add i.MX specific subtype of SDHCI Andrey Smirnov
@ 2018-01-16  1:36 ` Andrey Smirnov
  2018-01-31 17:04   ` Philippe Mathieu-Daudé
  2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 03/14] i.MX: Add code to emulate i.MX7 CCM, PMU and ANALOG IP blocks Andrey Smirnov
                   ` (13 subsequent siblings)
  15 siblings, 1 reply; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:36 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

Convert i.MX6 to use TYPE_IMX_USDHC since that's what real HW comes
with.

Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org
Cc: yurovsky@gmail.com
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 hw/arm/fsl-imx6.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c
index b0d4088290..e6559a8b12 100644
--- a/hw/arm/fsl-imx6.c
+++ b/hw/arm/fsl-imx6.c
@@ -93,7 +93,7 @@ static void fsl_imx6_init(Object *obj)
     }
 
     for (i = 0; i < FSL_IMX6_NUM_ESDHCS; i++) {
-        object_initialize(&s->esdhc[i], sizeof(s->esdhc[i]), TYPE_SYSBUS_SDHCI);
+        object_initialize(&s->esdhc[i], sizeof(s->esdhc[i]), TYPE_IMX_USDHC);
         qdev_set_parent_bus(DEVICE(&s->esdhc[i]), sysbus_get_default());
         snprintf(name, NAME_SIZE, "sdhc%d", i + 1);
         object_property_add_child(obj, name, OBJECT(&s->esdhc[i]), NULL);
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [Qemu-devel] [PATCH v4 03/14] i.MX: Add code to emulate i.MX7 CCM, PMU and ANALOG IP blocks
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
  2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 01/14] sdhci: Add i.MX specific subtype of SDHCI Andrey Smirnov
  2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 02/14] hw: i.MX: Convert i.MX6 to use TYPE_IMX_USDHC Andrey Smirnov
@ 2018-01-16  1:36 ` Andrey Smirnov
  2018-01-16 14:28   ` Peter Maydell
  2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 04/14] i.MX: Add code to emulate i.MX2 watchdog IP block Andrey Smirnov
                   ` (12 subsequent siblings)
  15 siblings, 1 reply; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:36 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

Add minimal code needed to allow upstream Linux guest to boot.

Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org
Cc: yurovsky@gmail.com
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 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

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..23855df02a
--- /dev/null
+++ b/hw/misc/imx7_ccm.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2017, Impinj, Inc.
+ *
+ * i.MX7 CCM, PMU and ANALOG IP blocks emulation code
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * 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 <andrew.smirnov@gmail.com>
+ *
+ * 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 {
+    /* <private> */
+    IMXCCMState parent_obj;
+
+    /* <public> */
+    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 {
+    /* <private> */
+    IMXCCMState parent_obj;
+
+    /* <public> */
+    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 */
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [Qemu-devel] [PATCH v4 04/14] i.MX: Add code to emulate i.MX2 watchdog IP block
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
                   ` (2 preceding siblings ...)
  2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 03/14] i.MX: Add code to emulate i.MX7 CCM, PMU and ANALOG IP blocks Andrey Smirnov
@ 2018-01-16  1:36 ` Andrey Smirnov
  2018-01-31 17:07   ` Philippe Mathieu-Daudé
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 05/14] i.MX: Add code to emulate i.MX7 SNVS IP-block Andrey Smirnov
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:36 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

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 <peter.maydell@linaro.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org
Cc: yurovsky@gmail.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 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

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..76bb98a525
--- /dev/null
+++ b/hw/misc/imx2_wdt.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017, Impinj, Inc.
+ *
+ * i.MX2 Watchdog IP block
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * 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/bitops.h"
+#include "qemu/osdep.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 <andrew.smirnov@gmail.com>
+ *
+ * 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 {
+    /* <private> */
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+} IMX2WdtState;
+
+#endif /* IMX7_SNVS_H */
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [Qemu-devel] [PATCH v4 05/14] i.MX: Add code to emulate i.MX7 SNVS IP-block
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
                   ` (3 preceding siblings ...)
  2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 04/14] i.MX: Add code to emulate i.MX2 watchdog IP block Andrey Smirnov
@ 2018-01-16  1:37 ` Andrey Smirnov
  2018-01-31 17:10   ` Philippe Mathieu-Daudé
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 06/14] i.MX: Add code to emulate GPCv2 IP block Andrey Smirnov
                   ` (10 subsequent siblings)
  15 siblings, 1 reply; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:37 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

Add code to emulate SNVS IP-block. Currently only the bits needed to
be able to emulate machine shutdown are implemented.

Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org
Cc: yurovsky@gmail.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 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

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..670b9f4639
--- /dev/null
+++ b/hw/misc/imx7_snvs.c
@@ -0,0 +1,83 @@
+/*
+ * IMX7 Secure Non-Volatile Storage
+ *
+ * Copyright (c) 2017, Impinj, Inc.
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * 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 <andrew.smirnov@gmail.com>
+ *
+ * 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 {
+    /* <private> */
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+} IMX7SNVSState;
+
+#endif /* IMX7_SNVS_H */
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [Qemu-devel] [PATCH v4 06/14] i.MX: Add code to emulate GPCv2 IP block
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
                   ` (4 preceding siblings ...)
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 05/14] i.MX: Add code to emulate i.MX7 SNVS IP-block Andrey Smirnov
@ 2018-01-16  1:37 ` Andrey Smirnov
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 08/14] i.MX: Add implementation of i.MX7 GPR " Andrey Smirnov
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:37 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

Add minimal code needed to allow upstream Linux guest to boot.

Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org
Cc: yurovsky@gmail.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 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

diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index ae358569a1..2252181ac9 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -4,7 +4,7 @@ common-obj-$(CONFIG_PL190) += pl190.o
 common-obj-$(CONFIG_PUV3) += puv3_intc.o
 common-obj-$(CONFIG_XILINX) += xilinx_intc.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..496ed31b78
--- /dev/null
+++ b/hw/intc/imx_gpcv2.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2017, Impinj, Inc.
+ *
+ * i.MX7 GPCv2 block emulation code
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * 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 */
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [Qemu-devel] [PATCH v4 08/14] i.MX: Add implementation of i.MX7 GPR IP block
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
                   ` (5 preceding siblings ...)
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 06/14] i.MX: Add code to emulate GPCv2 IP block Andrey Smirnov
@ 2018-01-16  1:37 ` Andrey Smirnov
  2018-01-16  4:45   ` Philippe Mathieu-Daudé
  2018-01-16 14:30   ` Peter Maydell
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware " Andrey Smirnov
                   ` (8 subsequent siblings)
  15 siblings, 2 replies; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:37 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

Add minimal code needed to allow upstream Linux guest to boot.

Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org
Cc: yurovsky@gmail.com
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 hw/misc/Makefile.objs      |   1 +
 hw/misc/imx7_gpr.c         | 119 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/misc/imx7_gpr.h |  28 +++++++++++
 3 files changed, 148 insertions(+)
 create mode 100644 hw/misc/imx7_gpr.c
 create mode 100644 include/hw/misc/imx7_gpr.h

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..9e8ccea9e8
--- /dev/null
+++ b/hw/misc/imx7_gpr.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2017, Impinj, Inc.
+ *
+ * i.MX7 GPR IP block emulation code
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * 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"
+
+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)
+{
+    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)
+{
+}
+
+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/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 <andrew.smirnov@gmail.com>
+ *
+ * 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 {
+    /* <private> */
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+} IMX7GPRState;
+
+#endif /* IMX7_GPR_H */
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware IP block
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
                   ` (6 preceding siblings ...)
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 08/14] i.MX: Add implementation of i.MX7 GPR " Andrey Smirnov
@ 2018-01-16  1:37 ` Andrey Smirnov
  2018-01-16 14:34   ` Peter Maydell
  2018-01-30 13:18   ` Marcel Apfelbaum
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 10/14] usb: Add basic code to emulate Chipidea USB IP Andrey Smirnov
                   ` (7 subsequent siblings)
  15 siblings, 2 replies; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:37 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

Add code needed to get a functional PCI subsytem when using in
conjunction with upstream Linux guest (4.13+). Tested to work against
"e1000e" (network adapter, using MSI interrupts) as well as
"usb-ehci" (USB controller, using legacy PCI interrupts).

Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org
Cc: yurovsky@gmail.com
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 default-configs/arm-softmmu.mak  |   2 +
 hw/pci-host/Makefile.objs        |   2 +
 hw/pci-host/designware.c         | 618 +++++++++++++++++++++++++++++++++++++++
 include/hw/pci-host/designware.h |  93 ++++++
 include/hw/pci/pci_ids.h         |   2 +
 5 files changed, 717 insertions(+)
 create mode 100644 hw/pci-host/designware.c
 create mode 100644 include/hw/pci-host/designware.h

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index b0d6e65038..0c5ae914ed 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -132,3 +132,5 @@ CONFIG_GPIO_KEY=y
 CONFIG_MSF2=y
 CONFIG_FW_CFG_DMA=y
 CONFIG_XILINX_AXI=y
+CONFIG_PCI_DESIGNWARE=y
+
diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
index 9c7909cf44..0e2c0a123b 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..98fff5e5f3
--- /dev/null
+++ b/hw/pci-host/designware.c
@@ -0,0 +1,618 @@
+/*
+ * Copyright (c) 2017, 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 PCIE_PORT_LINK_CONTROL          0x710
+
+#define PCIE_PHY_DEBUG_R1               0x72C
+#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP  BIT(4)
+
+#define PCIE_LINK_WIDTH_SPEED_CONTROL   0x80C
+
+#define PCIE_MSI_ADDR_LO                0x820
+#define PCIE_MSI_ADDR_HI                0x824
+#define PCIE_MSI_INTR0_ENABLE           0x828
+#define PCIE_MSI_INTR0_MASK             0x82C
+#define PCIE_MSI_INTR0_STATUS           0x830
+
+#define PCIE_ATU_VIEWPORT               0x900
+#define PCIE_ATU_REGION_INBOUND         (0x1 << 31)
+#define PCIE_ATU_REGION_OUTBOUND        (0x0 << 31)
+#define PCIE_ATU_REGION_INDEX2          (0x2 << 0)
+#define PCIE_ATU_REGION_INDEX1          (0x1 << 0)
+#define PCIE_ATU_REGION_INDEX0          (0x0 << 0)
+#define PCIE_ATU_CR1                    0x904
+#define PCIE_ATU_TYPE_MEM               (0x0 << 0)
+#define PCIE_ATU_TYPE_IO                (0x2 << 0)
+#define PCIE_ATU_TYPE_CFG0              (0x4 << 0)
+#define PCIE_ATU_TYPE_CFG1              (0x5 << 0)
+#define PCIE_ATU_CR2                    0x908
+#define PCIE_ATU_ENABLE                 (0x1 << 31)
+#define PCIE_ATU_BAR_MODE_ENABLE        (0x1 << 30)
+#define PCIE_ATU_LOWER_BASE             0x90C
+#define PCIE_ATU_UPPER_BASE             0x910
+#define PCIE_ATU_LIMIT                  0x914
+#define PCIE_ATU_LOWER_TARGET           0x918
+#define PCIE_ATU_BUS(x)                 (((x) >> 24) & 0xff)
+#define PCIE_ATU_DEVFN(x)               (((x) >> 16) & 0xff)
+#define 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 |= (1 << 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);
+    }
+}
+
+const MemoryRegionOps designware_pci_host_msi_ops = {
+    .write = designware_pcie_root_msi_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot *root)
+
+{
+    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
+    MemoryRegion *address_space = &host->pci.memory;
+    MemoryRegion *mem = &root->msi.iomem;
+    const uint64_t base = root->msi.base;
+    const bool enable = root->msi.intr[0].enable;
+
+    if (memory_region_is_mapped(mem)) {
+        memory_region_del_subregion(address_space, mem);
+        object_unparent(OBJECT(mem));
+    }
+
+    if (enable) {
+        memory_region_init_io(mem, OBJECT(root),
+                              &designware_pci_host_msi_ops,
+                              root, "pcie-msi", 0x1000);
+
+        memory_region_add_subregion(address_space, base, mem);
+    }
+}
+
+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 & 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 PCIE_PORT_LINK_CONTROL:
+    case PCIE_LINK_WIDTH_SPEED_CONTROL:
+        val = 0xDEADBEEF;
+        /* No-op */
+        break;
+
+    case PCIE_MSI_ADDR_LO:
+        val = root->msi.base;
+        break;
+
+    case PCIE_MSI_ADDR_HI:
+        val = root->msi.base >> 32;
+        break;
+
+    case PCIE_MSI_INTR0_ENABLE:
+        val = root->msi.intr[0].enable;
+        break;
+
+    case PCIE_MSI_INTR0_MASK:
+        val = root->msi.intr[0].mask;
+        break;
+
+    case PCIE_MSI_INTR0_STATUS:
+        val = root->msi.intr[0].status;
+        break;
+
+    case PCIE_PHY_DEBUG_R1:
+        val = PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
+        break;
+
+    case PCIE_ATU_VIEWPORT:
+        val = root->atu_viewport;
+        break;
+
+    case PCIE_ATU_LOWER_BASE:
+        val = viewport->base;
+        break;
+
+    case PCIE_ATU_UPPER_BASE:
+        val = viewport->base >> 32;
+        break;
+
+    case PCIE_ATU_LOWER_TARGET:
+        val = viewport->target;
+        break;
+
+    case PCIE_ATU_UPPER_TARGET:
+        val = viewport->target >> 32;
+        break;
+
+    case PCIE_ATU_LIMIT:
+        val = viewport->limit;
+        break;
+
+    case PCIE_ATU_CR1:
+    case PCIE_ATU_CR2:          /* FALLTHROUGH */
+        val = viewport->cr[(address - 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_read(void *opaque,
+                                               hwaddr addr, unsigned len)
+{
+    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
+    DesignwarePCIEViewport *viewport =
+        designware_pcie_root_get_current_viewport(root);
+
+    const uint8_t busnum = PCIE_ATU_BUS(viewport->target);
+    const uint8_t devfn  = 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_SPACE_SIZE - 1;
+
+        return pci_host_config_read_common(pcidev, addr,
+                                           PCI_CONFIG_SPACE_SIZE, len);
+    }
+
+    return UINT64_MAX;
+}
+
+static void designware_pcie_root_data_write(void *opaque, hwaddr addr,
+                                            uint64_t val, unsigned len)
+{
+    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
+    DesignwarePCIEViewport *viewport =
+        designware_pcie_root_get_current_viewport(root);
+    const uint8_t busnum = PCIE_ATU_BUS(viewport->target);
+    const uint8_t devfn  = 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_SPACE_SIZE - 1;
+        pci_host_config_write_common(pcidev, addr,
+                                     PCI_CONFIG_SPACE_SIZE,
+                                     val, len);
+    }
+}
+
+const MemoryRegionOps designware_pci_host_conf_ops = {
+    .read = designware_pcie_root_data_read,
+    .write = designware_pcie_root_data_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void designware_pcie_update_viewport(DesignwarePCIERoot *root,
+                                            DesignwarePCIEViewport *viewport)
+{
+    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
+
+    MemoryRegion *mem     = &viewport->memory;
+    const uint64_t target = viewport->target;
+    const uint64_t base   = viewport->base;
+    const uint64_t size   = (uint64_t)viewport->limit - base + 1;
+    const bool inbound    = viewport->inbound;
+
+    MemoryRegion *source, *destination;
+    const char *direction;
+    char *name;
+
+    if (inbound) {
+        source      = &host->pci.address_space_root;
+        destination = get_system_memory();
+        direction   = "Inbound";
+    } else {
+        source      = get_system_memory();
+        destination = &host->pci.memory;
+        direction   = "Outbound";
+    }
+
+    if (memory_region_is_mapped(mem)) {
+        /* Before we modify anything, unmap and destroy the region */
+        memory_region_del_subregion(source, mem);
+        object_unparent(OBJECT(mem));
+    }
+
+    name = g_strdup_printf("PCI %s Viewport %p", direction, viewport);
+
+    switch (viewport->cr[0]) {
+    case PCIE_ATU_TYPE_MEM:
+        memory_region_init_alias(mem, OBJECT(root), name,
+                                 destination, target, size);
+        break;
+    case PCIE_ATU_TYPE_CFG0:
+    case PCIE_ATU_TYPE_CFG1:    /* FALLTHROUGH */
+        if (inbound) {
+            goto exit;
+        }
+
+        memory_region_init_io(mem, OBJECT(root),
+                              &designware_pci_host_conf_ops,
+                              root, name, size);
+        break;
+    }
+
+    if (inbound) {
+        memory_region_add_subregion_overlap(source, base,
+                                            mem, -1);
+    } else {
+        memory_region_add_subregion(source, base, mem);
+    }
+
+ exit:
+    g_free(name);
+}
+
+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 PCIE_PORT_LINK_CONTROL:
+    case PCIE_LINK_WIDTH_SPEED_CONTROL:
+    case PCIE_PHY_DEBUG_R1:
+        /* No-op */
+        break;
+
+    case PCIE_MSI_ADDR_LO:
+        root->msi.base &= 0xFFFFFFFF00000000ULL;
+        root->msi.base |= val;
+        break;
+
+    case PCIE_MSI_ADDR_HI:
+        root->msi.base &= 0x00000000FFFFFFFFULL;
+        root->msi.base |= (uint64_t)val << 32;
+        break;
+
+    case 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 PCIE_MSI_INTR0_MASK:
+        root->msi.intr[0].mask = val;
+        break;
+
+    case 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 PCIE_ATU_VIEWPORT:
+        root->atu_viewport = val;
+        break;
+
+    case PCIE_ATU_LOWER_BASE:
+        viewport->base &= 0xFFFFFFFF00000000ULL;
+        viewport->base |= val;
+        break;
+
+    case PCIE_ATU_UPPER_BASE:
+        viewport->base &= 0x00000000FFFFFFFFULL;
+        viewport->base |= (uint64_t)val << 32;
+        break;
+
+    case PCIE_ATU_LOWER_TARGET:
+        viewport->target &= 0xFFFFFFFF00000000ULL;
+        viewport->target |= val;
+        break;
+
+    case PCIE_ATU_UPPER_TARGET:
+        viewport->target &= 0x00000000FFFFFFFFULL;
+        viewport->target |= val;
+        break;
+
+    case PCIE_ATU_LIMIT:
+        viewport->limit = val;
+        break;
+
+    case PCIE_ATU_CR1:
+        viewport->cr[0] = val;
+        break;
+    case PCIE_ATU_CR2:
+        viewport->cr[1] = val;
+
+        if (viewport->cr[1] & PCIE_ATU_ENABLE) {
+            designware_pcie_update_viewport(root, viewport);
+         }
+        break;
+
+    default:
+        pci_bridge_write_config(d, address, val, len);
+        break;
+    }
+}
+
+static int designware_pcie_root_init(PCIDevice *dev)
+{
+    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev);
+    PCIBridge *br = PCI_BRIDGE(dev);
+    DesignwarePCIEViewport *viewport;
+    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_PCI_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++) {
+        viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i];
+        viewport->inbound = true;
+    }
+
+    /*
+     * 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->base   = 0x0000000000000000ULL;
+    viewport->target = 0x0000000000000000ULL;
+    viewport->limit  = UINT32_MAX;
+    viewport->cr[0]  = PCIE_ATU_TYPE_MEM;
+
+    designware_pcie_update_viewport(root, viewport);
+
+    return 0;
+}
+
+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 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_HOST;
+    k->is_express = true;
+    k->is_bridge = true;
+    k->init = designware_pcie_root_init;
+    k->exit = pci_bridge_exitfn;
+    dc->reset = pci_bridge_reset;
+    k->config_read = designware_pcie_root_config_read;
+    k->config_write = designware_pcie_root_config_write;
+
+    /*
+     * 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;
+}
+
+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_NATIVE_ENDIAN,
+};
+
+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 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";
+}
+
+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)
+
+/* 00:00.0 Class 0604: 16c3:abcd */
diff --git a/include/hw/pci-host/designware.h b/include/hw/pci-host/designware.h
new file mode 100644
index 0000000000..55e45fcba0
--- /dev/null
+++ b/include/hw/pci-host/designware.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017, 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/>.
+ */
+
+#ifndef DESIGNWARE_H
+#define DESIGNWARE_H
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/pci/pcie_host.h"
+#include "hw/pci/pci_bridge.h"
+
+#define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host"
+#define DESIGNWARE_PCIE_HOST(obj) \
+     OBJECT_CHECK(DesignwarePCIEHost, (obj), TYPE_DESIGNWARE_PCIE_HOST)
+
+#define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root"
+#define DESIGNWARE_PCIE_ROOT(obj) \
+     OBJECT_CHECK(DesignwarePCIERoot, (obj), TYPE_DESIGNWARE_PCIE_ROOT)
+
+typedef struct DesignwarePCIEViewport {
+    MemoryRegion memory;
+
+    uint64_t base;
+    uint64_t target;
+    uint32_t limit;
+    uint32_t cr[2];
+
+    bool inbound;
+} DesignwarePCIEViewport;
+
+typedef struct DesignwarePCIERoot {
+    PCIBridge parent_obj;
+
+    uint32_t atu_viewport;
+
+#define DESIGNWARE_PCIE_VIEWPORT_OUTBOUND    0
+#define DESIGNWARE_PCIE_VIEWPORT_INBOUND     1
+#define DESIGNWARE_PCIE_NUM_VIEWPORTS        4
+
+    DesignwarePCIEViewport viewports[2][DESIGNWARE_PCIE_NUM_VIEWPORTS];
+
+    struct {
+        uint64_t     base;
+        MemoryRegion iomem;
+
+        struct {
+            uint32_t enable;
+            uint32_t mask;
+            uint32_t status;
+        } intr[1];
+    } msi;
+} DesignwarePCIERoot;
+
+typedef struct DesignwarePCIEHost {
+    PCIHostState parent_obj;
+
+    bool link_up;
+
+    DesignwarePCIERoot root;
+
+    struct {
+        AddressSpace address_space;
+        MemoryRegion address_space_root;
+
+        MemoryRegion memory;
+        MemoryRegion io;
+
+        qemu_irq     irqs[4];
+    } pci;
+
+    MemoryRegion mmio;
+} DesignwarePCIEHost;
+
+#endif  /* DESIGNWARE_H */
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index 35df1874a9..23fefe1bc6 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -266,4 +266,6 @@
 #define PCI_VENDOR_ID_TEWS               0x1498
 #define PCI_DEVICE_ID_TEWS_TPCI200       0x30C8
 
+#define PCI_VENDOR_ID_SYNOPSYS           0x16C3
+
 #endif
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [Qemu-devel] [PATCH v4 10/14] usb: Add basic code to emulate Chipidea USB IP
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
                   ` (7 preceding siblings ...)
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware " Andrey Smirnov
@ 2018-01-16  1:37 ` Andrey Smirnov
  2018-01-16 14:40   ` Peter Maydell
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 11/14] ARM: Add basic code to emulate A7MPCore DAP block Andrey Smirnov
                   ` (6 subsequent siblings)
  15 siblings, 1 reply; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:37 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

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 <peter.maydell@linaro.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org
Cc: yurovsky@gmail.com
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 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

diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index bdfead6701..5573c182d4 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..9bc8df448c
--- /dev/null
+++ b/hw/usb/chipidea.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2017, Impinj, Inc.
+ *
+ * Chipidea USB block emulation code
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * 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 */
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [Qemu-devel] [PATCH v4 11/14] ARM: Add basic code to emulate A7MPCore DAP block
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
                   ` (8 preceding siblings ...)
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 10/14] usb: Add basic code to emulate Chipidea USB IP Andrey Smirnov
@ 2018-01-16  1:37 ` Andrey Smirnov
  2018-01-16  4:32   ` Philippe Mathieu-Daudé
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 12/14] i.MX: Add i.MX7 SOC implementation Andrey Smirnov
                   ` (5 subsequent siblings)
  15 siblings, 1 reply; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:37 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

Add minimal code to emulate A7MPCore DAP block needed to boot Linux
guest.

Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org
Cc: yurovsky@gmail.com
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 hw/arm/Makefile.objs       |   2 +-
 hw/arm/coresight.c         | 120 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/coresight.h |  24 +++++++++
 3 files changed, 145 insertions(+), 1 deletion(-)
 create mode 100644 hw/arm/coresight.c
 create mode 100644 include/hw/arm/coresight.h

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 2794e086d6..692216e0cf 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -1,4 +1,4 @@
-obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
+obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o coresight.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
diff --git a/hw/arm/coresight.c b/hw/arm/coresight.c
new file mode 100644
index 0000000000..d0a8c1b005
--- /dev/null
+++ b/hw/arm/coresight.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2017, Impinj, Inc.
+ *
+ * CoreSight block emulation code
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * 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/arm/coresight.h"
+#include "qemu/log.h"
+
+static uint64_t coresight_read(void *opaque, hwaddr offset,
+                               unsigned size)
+{
+    return 0;
+}
+
+static void coresight_write(void *opaque, hwaddr offset,
+                            uint64_t value, unsigned size)
+{
+}
+
+static const struct MemoryRegionOps coresight_ops = {
+    .read = coresight_read,
+    .write = coresight_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 a7mpcore_dap_init(Object *obj)
+{
+    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+    A7MPCoreDAPState *s = A7MPCORE_DAP(obj);
+
+    memory_region_init(&s->container, obj, "a7mpcore-dap-container", 0x100000);
+    sysbus_init_mmio(sd, &s->container);
+
+    memory_region_init_io(&s->ca7_atb_funnel,
+                          obj,
+                          &coresight_ops,
+                          s,
+                          TYPE_A7MPCORE_DAP ".ca7-atb-funnel",
+                          0x1000);
+    memory_region_add_subregion(&s->container, 0x41000, &s->ca7_atb_funnel);
+
+    memory_region_init_io(&s->cpu0_etm,
+                          obj,
+                          &coresight_ops,
+                          s,
+                          TYPE_A7MPCORE_DAP ".cpu0-etm",
+                          0x1000);
+    memory_region_add_subregion(&s->container, 0x7C000, &s->cpu0_etm);
+
+    memory_region_init_io(&s->atb_funnel,
+                          obj,
+                          &coresight_ops,
+                          s,
+                          TYPE_A7MPCORE_DAP ".atb-funnel",
+                          0x1000);
+    memory_region_add_subregion(&s->container, 0x83000, &s->atb_funnel);
+
+    memory_region_init_io(&s->tmc_etb,
+                          obj,
+                          &coresight_ops,
+                          s,
+                          TYPE_A7MPCORE_DAP ".tmc-etb",
+                          0x1000);
+    memory_region_add_subregion(&s->container, 0x84000, &s->tmc_etb);
+
+    memory_region_init_io(&s->tmc_etr,
+                          obj,
+                          &coresight_ops,
+                          s,
+                          TYPE_A7MPCORE_DAP ".tmc-etr",
+                          0x1000);
+    memory_region_add_subregion(&s->container, 0x86000, &s->tmc_etr);
+
+    memory_region_init_io(&s->tpiu,
+                          obj,
+                          &coresight_ops,
+                          s,
+                          TYPE_A7MPCORE_DAP ".tpiu",
+                          0x1000);
+    memory_region_add_subregion(&s->container, 0x87000, &s->tpiu);
+}
+
+static void a7mpcore_dap_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc = "A7MPCore DAP Module";
+}
+
+static const TypeInfo a7mpcore_dap_info = {
+    .name          = TYPE_A7MPCORE_DAP,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(A7MPCoreDAPState),
+    .instance_init = a7mpcore_dap_init,
+    .class_init    = a7mpcore_dap_class_init,
+};
+
+static void coresight_register_type(void)
+{
+    type_register_static(&a7mpcore_dap_info);
+}
+type_init(coresight_register_type)
diff --git a/include/hw/arm/coresight.h b/include/hw/arm/coresight.h
new file mode 100644
index 0000000000..d1480e825b
--- /dev/null
+++ b/include/hw/arm/coresight.h
@@ -0,0 +1,24 @@
+#ifndef CORESIGHT_H
+#define CORESIGHT_H
+
+#include "hw/sysbus.h"
+
+typedef struct A7MPCoreDAPState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    MemoryRegion container;
+
+    MemoryRegion ca7_atb_funnel;
+    MemoryRegion cpu0_etm;
+    MemoryRegion atb_funnel;
+    MemoryRegion tmc_etb;
+    MemoryRegion tmc_etr;
+    MemoryRegion tpiu;
+
+} A7MPCoreDAPState;
+
+#define TYPE_A7MPCORE_DAP "a7mpcore-dap"
+#define A7MPCORE_DAP(obj) OBJECT_CHECK(A7MPCoreDAPState, (obj), TYPE_A7MPCORE_DAP)
+
+#endif /* CORESIGHT_H */
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [Qemu-devel] [PATCH v4 12/14] i.MX: Add i.MX7 SOC implementation.
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
                   ` (9 preceding siblings ...)
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 11/14] ARM: Add basic code to emulate A7MPCore DAP block Andrey Smirnov
@ 2018-01-16  1:37 ` Andrey Smirnov
  2018-01-16 14:42   ` Peter Maydell
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 13/14] hw/arm: Move virt's PSCI DT fixup code to arm/boot.c Andrey Smirnov
                   ` (4 subsequent siblings)
  15 siblings, 1 reply; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:37 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

The following interfaces are partially or fully emulated:

    * up to 2 Cortex A9 cores (SMP works with PSCI)
    * A7 MPCORE (identical to A15 MPCORE)
    * 4 GPTs modules
    * 7 GPIO controllers
    * 2 IOMUXC controllers
    * 1 CCM module
    * 1 SVNS module
    * 1 SRC module
    * 1 GPCv2 controller
    * 4 eCSPI controllers
    * 4 I2C controllers
    * 7 i.MX UART controllers
    * 2 FlexCAN controllers
    * 2 Ethernet controllers (FEC)
    * 3 SD controllers (USDHC)
    * 4 WDT modules
    * 1 SDMA module
    * 1 GPR module
    * 2 USBMISC modules
    * 2 ADC modules
    * 1 PCIe controller

Tested to boot and work with upstream Linux (4.13+) guest.

Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org
Cc: yurovsky@gmail.com
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 default-configs/arm-softmmu.mak |   1 +
 hw/arm/Makefile.objs            |   2 +
 hw/arm/fsl-imx7.c               | 583 ++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/fsl-imx7.h       | 223 +++++++++++++++
 4 files changed, 809 insertions(+)
 create mode 100644 hw/arm/fsl-imx7.c
 create mode 100644 include/hw/arm/fsl-imx7.h

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 0c5ae914ed..99fe1cd1fb 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -118,6 +118,7 @@ CONFIG_ALLWINNER_A10=y
 CONFIG_FSL_IMX6=y
 CONFIG_FSL_IMX31=y
 CONFIG_FSL_IMX25=y
+CONFIG_FSL_IMX7=y
 
 CONFIG_IMX_I2C=y
 
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 692216e0cf..ea47c8baed 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -20,3 +20,5 @@ obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o
 obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o
 obj-$(CONFIG_MPS2) += mps2.o
 obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
+obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o
+
diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c
new file mode 100644
index 0000000000..13f0aa2563
--- /dev/null
+++ b/hw/arm/fsl-imx7.c
@@ -0,0 +1,583 @@
+/*
+ * Copyright (c) 2017, 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);
+
+    object_initialize(&s->dap, sizeof(s->dap), TYPE_A7MPCORE_DAP);
+    qdev_set_parent_bus(DEVICE(&s->dap), sysbus);
+    object_property_add_child(obj, "a7mpcore-dap",
+                              OBJECT(&s->dap), &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);
+    }
+
+    object_property_set_bool(OBJECT(&s->dap), true, "realized",
+                             &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->dap), 0, FSL_IMX7_A7MPCORE_DAP_ADDR);
+
+    /*
+     * 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/include/hw/arm/fsl-imx7.h b/include/hw/arm/fsl-imx7.h
new file mode 100644
index 0000000000..11321f3e38
--- /dev/null
+++ b/include/hw/arm/fsl-imx7.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2017, Impinj, Inc.
+ *
+ * i.MX7 SoC definitions
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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.
+ */
+
+#ifndef FSL_IMX7_H
+#define FSL_IMX7_H
+
+#include "hw/arm/arm.h"
+#include "hw/arm/coresight.h"
+#include "hw/cpu/a15mpcore.h"
+#include "hw/intc/imx_gpcv2.h"
+#include "hw/misc/imx7_ccm.h"
+#include "hw/misc/imx7_snvs.h"
+#include "hw/misc/imx7_gpr.h"
+#include "hw/misc/imx6_src.h"
+#include "hw/misc/imx2_wdt.h"
+#include "hw/gpio/imx_gpio.h"
+#include "hw/char/imx_serial.h"
+#include "hw/timer/imx_gpt.h"
+#include "hw/timer/imx_epit.h"
+#include "hw/i2c/imx_i2c.h"
+#include "hw/gpio/imx_gpio.h"
+#include "hw/sd/sdhci.h"
+#include "hw/ssi/imx_spi.h"
+#include "hw/net/imx_fec.h"
+#include "hw/pci-host/designware.h"
+#include "hw/usb/chipidea.h"
+#include "exec/memory.h"
+#include "cpu.h"
+
+#define TYPE_FSL_IMX7 "fsl,imx7"
+#define FSL_IMX7(obj) OBJECT_CHECK(FslIMX7State, (obj), TYPE_FSL_IMX7)
+
+enum FslIMX7Configuration {
+    FSL_IMX7_NUM_CPUS         = 2,
+    FSL_IMX7_NUM_UARTS        = 7,
+    FSL_IMX7_NUM_ETHS         = 2,
+    FSL_IMX7_ETH_NUM_TX_RINGS = 3,
+    FSL_IMX7_NUM_USDHCS       = 3,
+    FSL_IMX7_NUM_WDTS         = 4,
+    FSL_IMX7_NUM_GPTS         = 4,
+    FSL_IMX7_NUM_IOMUXCS      = 2,
+    FSL_IMX7_NUM_GPIOS        = 7,
+    FSL_IMX7_NUM_I2CS         = 4,
+    FSL_IMX7_NUM_ECSPIS       = 4,
+    FSL_IMX7_NUM_USBS         = 3,
+    FSL_IMX7_NUM_ADCS         = 2,
+};
+
+typedef struct FslIMX7State {
+    /*< private >*/
+    DeviceState    parent_obj;
+
+    /*< public >*/
+    ARMCPU             cpu[FSL_IMX7_NUM_CPUS];
+    A15MPPrivState     a7mpcore;
+    A7MPCoreDAPState   dap;
+    IMXGPTState        gpt[FSL_IMX7_NUM_GPTS];
+    IMXGPIOState       gpio[FSL_IMX7_NUM_GPIOS];
+    IMX7CCMState       ccm;
+    IMX7AnalogState    analog;
+    IMX7SNVSState      snvs;
+    IMXGPCv2State      gpcv2;
+    IMXSPIState        spi[FSL_IMX7_NUM_ECSPIS];
+    IMXI2CState        i2c[FSL_IMX7_NUM_I2CS];
+    IMXSerialState     uart[FSL_IMX7_NUM_UARTS];
+    IMXFECState        eth[FSL_IMX7_NUM_ETHS];
+    SDHCIState         usdhc[FSL_IMX7_NUM_USDHCS];
+    IMX2WdtState       wdt[FSL_IMX7_NUM_WDTS];
+    IMX7GPRState       gpr;
+    ChipideaState      usb[FSL_IMX7_NUM_USBS];
+    DesignwarePCIEHost pcie;
+} FslIMX7State;
+
+enum FslIMX7MemoryMap {
+    FSL_IMX7_MMDC_ADDR            = 0x80000000,
+    FSL_IMX7_MMDC_SIZE            = 2 * 1024 * 1024 * 1024UL,
+
+    FSL_IMX7_GPIO1_ADDR           = 0x30200000,
+    FSL_IMX7_GPIO2_ADDR           = 0x30210000,
+    FSL_IMX7_GPIO3_ADDR           = 0x30220000,
+    FSL_IMX7_GPIO4_ADDR           = 0x30230000,
+    FSL_IMX7_GPIO5_ADDR           = 0x30240000,
+    FSL_IMX7_GPIO6_ADDR           = 0x30250000,
+    FSL_IMX7_GPIO7_ADDR           = 0x30260000,
+
+    FSL_IMX7_IOMUXC_LPSR_GPR_ADDR = 0x30270000,
+
+    FSL_IMX7_WDOG1_ADDR           = 0x30280000,
+    FSL_IMX7_WDOG2_ADDR           = 0x30290000,
+    FSL_IMX7_WDOG3_ADDR           = 0x302A0000,
+    FSL_IMX7_WDOG4_ADDR           = 0x302B0000,
+
+    FSL_IMX7_IOMUXC_LPSR_ADDR     = 0x302C0000,
+
+    FSL_IMX7_GPT1_ADDR            = 0x302D0000,
+    FSL_IMX7_GPT2_ADDR            = 0x302E0000,
+    FSL_IMX7_GPT3_ADDR            = 0x302F0000,
+    FSL_IMX7_GPT4_ADDR            = 0x30300000,
+
+    FSL_IMX7_IOMUXC_ADDR          = 0x30330000,
+    FSL_IMX7_IOMUXC_GPR_ADDR      = 0x30340000,
+    FSL_IMX7_IOMUXCn_SIZE         = 0x1000,
+
+    FSL_IMX7_ANALOG_ADDR          = 0x30360000,
+    FSL_IMX7_SNVS_ADDR            = 0x30370000,
+    FSL_IMX7_CCM_ADDR             = 0x30380000,
+
+    FSL_IMX7_SRC_ADDR             = 0x30390000,
+    FSL_IMX7_SRC_SIZE             = 0x1000,
+
+    FSL_IMX7_ADC1_ADDR            = 0x30610000,
+    FSL_IMX7_ADC2_ADDR            = 0x30620000,
+    FSL_IMX7_ADCn_SIZE            = 0x1000,
+
+    FSL_IMX7_GPC_ADDR             = 0x303A0000,
+
+    FSL_IMX7_I2C1_ADDR            = 0x30A20000,
+    FSL_IMX7_I2C2_ADDR            = 0x30A30000,
+    FSL_IMX7_I2C3_ADDR            = 0x30A40000,
+    FSL_IMX7_I2C4_ADDR            = 0x30A50000,
+
+    FSL_IMX7_ECSPI1_ADDR          = 0x30820000,
+    FSL_IMX7_ECSPI2_ADDR          = 0x30830000,
+    FSL_IMX7_ECSPI3_ADDR          = 0x30840000,
+    FSL_IMX7_ECSPI4_ADDR          = 0x30630000,
+
+    FSL_IMX7_LCDIF_ADDR           = 0x30730000,
+    FSL_IMX7_LCDIF_SIZE           = 0x1000,
+
+    FSL_IMX7_UART1_ADDR           = 0x30860000,
+    /*
+     * Some versions of the reference manual claim that UART2 is @
+     * 0x30870000, but experiments with HW + DT files in upstream
+     * Linux kernel show that not to be true and that block is
+     * acutally located @ 0x30890000
+     */
+    FSL_IMX7_UART2_ADDR           = 0x30890000,
+    FSL_IMX7_UART3_ADDR           = 0x30880000,
+    FSL_IMX7_UART4_ADDR           = 0x30A60000,
+    FSL_IMX7_UART5_ADDR           = 0x30A70000,
+    FSL_IMX7_UART6_ADDR           = 0x30A80000,
+    FSL_IMX7_UART7_ADDR           = 0x30A90000,
+
+    FSL_IMX7_ENET1_ADDR           = 0x30BE0000,
+    FSL_IMX7_ENET2_ADDR           = 0x30BF0000,
+
+    FSL_IMX7_USB1_ADDR            = 0x30B10000,
+    FSL_IMX7_USBMISC1_ADDR        = 0x30B10200,
+    FSL_IMX7_USB2_ADDR            = 0x30B20000,
+    FSL_IMX7_USBMISC2_ADDR        = 0x30B20200,
+    FSL_IMX7_USB3_ADDR            = 0x30B30000,
+    FSL_IMX7_USBMISC3_ADDR        = 0x30B30200,
+    FSL_IMX7_USBMISCn_SIZE        = 0x200,
+
+    FSL_IMX7_USDHC1_ADDR          = 0x30B40000,
+    FSL_IMX7_USDHC2_ADDR          = 0x30B50000,
+    FSL_IMX7_USDHC3_ADDR          = 0x30B60000,
+
+    FSL_IMX7_SDMA_ADDR            = 0x30BD0000,
+    FSL_IMX7_SDMA_SIZE            = 0x1000,
+
+    FSL_IMX7_A7MPCORE_ADDR        = 0x31000000,
+    FSL_IMX7_A7MPCORE_DAP_ADDR    = 0x30000000,
+
+    FSL_IMX7_PCIE_REG_ADDR        = 0x33800000,
+    FSL_IMX7_PCIE_REG_SIZE        = 16 * 1024,
+
+    FSL_IMX7_GPR_ADDR             = 0x30340000,
+};
+
+enum FslIMX7IRQs {
+    FSL_IMX7_USDHC1_IRQ   = 22,
+    FSL_IMX7_USDHC2_IRQ   = 23,
+    FSL_IMX7_USDHC3_IRQ   = 24,
+
+    FSL_IMX7_UART1_IRQ    = 26,
+    FSL_IMX7_UART2_IRQ    = 27,
+    FSL_IMX7_UART3_IRQ    = 28,
+    FSL_IMX7_UART4_IRQ    = 29,
+    FSL_IMX7_UART5_IRQ    = 30,
+    FSL_IMX7_UART6_IRQ    = 16,
+
+    FSL_IMX7_ECSPI1_IRQ   = 31,
+    FSL_IMX7_ECSPI2_IRQ   = 32,
+    FSL_IMX7_ECSPI3_IRQ   = 33,
+    FSL_IMX7_ECSPI4_IRQ   = 34,
+
+    FSL_IMX7_I2C1_IRQ     = 35,
+    FSL_IMX7_I2C2_IRQ     = 36,
+    FSL_IMX7_I2C3_IRQ     = 37,
+    FSL_IMX7_I2C4_IRQ     = 38,
+
+    FSL_IMX7_USB1_IRQ     = 43,
+    FSL_IMX7_USB2_IRQ     = 42,
+    FSL_IMX7_USB3_IRQ     = 40,
+
+    FSL_IMX7_PCI_INTA_IRQ = 122,
+    FSL_IMX7_PCI_INTB_IRQ = 123,
+    FSL_IMX7_PCI_INTC_IRQ = 124,
+    FSL_IMX7_PCI_INTD_IRQ = 125,
+
+    FSL_IMX7_UART7_IRQ    = 126,
+
+#define FSL_IMX7_ENET_IRQ(i, n)  ((n) + ((i) ? 100 : 118))
+
+    FSL_IMX7_MAX_IRQ      = 128,
+};
+
+#endif /* FSL_IMX7_H */
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [Qemu-devel] [PATCH v4 13/14] hw/arm: Move virt's PSCI DT fixup code to arm/boot.c
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
                   ` (10 preceding siblings ...)
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 12/14] i.MX: Add i.MX7 SOC implementation Andrey Smirnov
@ 2018-01-16  1:37 ` Andrey Smirnov
  2018-01-16 14:53   ` Peter Maydell
       [not found] ` <20180116013709.13830-8-andrew.smirnov@gmail.com>
                   ` (3 subsequent siblings)
  15 siblings, 1 reply; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16  1:37 UTC (permalink / raw)
  To: qemu-arm
  Cc: Andrey Smirnov, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

Move virt's PSCI DT fixup code to arm/boot.c and set this fixup to
happen automatically for every board that doesn't mark "psci-conduit"
as disabled. This way emulated boards other than "virt" that rely on
PSIC for SMP could benefit from that code.

Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org
Cc: yurovsky@gmail.com
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 hw/arm/boot.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/arm/virt.c | 61 -------------------------------------------------------
 2 files changed, 65 insertions(+), 61 deletions(-)

diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index c2720c8046..18ada9152c 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -384,6 +384,69 @@ static void set_kernel_args_old(const struct arm_boot_info *info)
     }
 }
 
+static void fdt_add_psci_node(void *fdt)
+{
+    uint32_t cpu_suspend_fn;
+    uint32_t cpu_off_fn;
+    uint32_t cpu_on_fn;
+    uint32_t migrate_fn;
+    ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0));
+    const char *psci_method;
+    int64_t psci_conduit;
+
+    psci_conduit = object_property_get_int(OBJECT(armcpu),
+                                           "psci-conduit",
+                                           &error_abort);
+    switch (psci_conduit) {
+    case QEMU_PSCI_CONDUIT_DISABLED:
+        return;
+    case QEMU_PSCI_CONDUIT_HVC:
+        psci_method = "hvc";
+        break;
+    case QEMU_PSCI_CONDUIT_SMC:
+        psci_method = "smc";
+        break;
+    default:
+        g_assert_not_reached();
+    }
+
+    qemu_fdt_add_subnode(fdt, "/psci");
+    if (armcpu->psci_version == 2) {
+        const char comp[] = "arm,psci-0.2\0arm,psci";
+        qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp));
+
+        cpu_off_fn = QEMU_PSCI_0_2_FN_CPU_OFF;
+        if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) {
+            cpu_suspend_fn = QEMU_PSCI_0_2_FN64_CPU_SUSPEND;
+            cpu_on_fn = QEMU_PSCI_0_2_FN64_CPU_ON;
+            migrate_fn = QEMU_PSCI_0_2_FN64_MIGRATE;
+        } else {
+            cpu_suspend_fn = QEMU_PSCI_0_2_FN_CPU_SUSPEND;
+            cpu_on_fn = QEMU_PSCI_0_2_FN_CPU_ON;
+            migrate_fn = QEMU_PSCI_0_2_FN_MIGRATE;
+        }
+    } else {
+        qemu_fdt_setprop_string(fdt, "/psci", "compatible", "arm,psci");
+
+        cpu_suspend_fn = QEMU_PSCI_0_1_FN_CPU_SUSPEND;
+        cpu_off_fn = QEMU_PSCI_0_1_FN_CPU_OFF;
+        cpu_on_fn = QEMU_PSCI_0_1_FN_CPU_ON;
+        migrate_fn = QEMU_PSCI_0_1_FN_MIGRATE;
+    }
+
+    /* We adopt the PSCI spec's nomenclature, and use 'conduit' to refer
+     * to the instruction that should be used to invoke PSCI functions.
+     * However, the device tree binding uses 'method' instead, so that is
+     * what we should use here.
+     */
+    qemu_fdt_setprop_string(fdt, "/psci", "method", psci_method);
+
+    qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend", cpu_suspend_fn);
+    qemu_fdt_setprop_cell(fdt, "/psci", "cpu_off", cpu_off_fn);
+    qemu_fdt_setprop_cell(fdt, "/psci", "cpu_on", cpu_on_fn);
+    qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
+}
+
 /**
  * load_dtb() - load a device tree binary image into memory
  * @addr:       the address to load the image at
@@ -540,6 +603,8 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
         }
     }
 
+    fdt_add_psci_node(fdt);
+
     if (binfo->modify_dtb) {
         binfo->modify_dtb(binfo, fdt);
     }
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 151592b1e5..5586aba01c 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -242,66 +242,6 @@ static void create_fdt(VirtMachineState *vms)
     }
 }
 
-static void fdt_add_psci_node(const VirtMachineState *vms)
-{
-    uint32_t cpu_suspend_fn;
-    uint32_t cpu_off_fn;
-    uint32_t cpu_on_fn;
-    uint32_t migrate_fn;
-    void *fdt = vms->fdt;
-    ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0));
-    const char *psci_method;
-
-    switch (vms->psci_conduit) {
-    case QEMU_PSCI_CONDUIT_DISABLED:
-        return;
-    case QEMU_PSCI_CONDUIT_HVC:
-        psci_method = "hvc";
-        break;
-    case QEMU_PSCI_CONDUIT_SMC:
-        psci_method = "smc";
-        break;
-    default:
-        g_assert_not_reached();
-    }
-
-    qemu_fdt_add_subnode(fdt, "/psci");
-    if (armcpu->psci_version == 2) {
-        const char comp[] = "arm,psci-0.2\0arm,psci";
-        qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp));
-
-        cpu_off_fn = QEMU_PSCI_0_2_FN_CPU_OFF;
-        if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) {
-            cpu_suspend_fn = QEMU_PSCI_0_2_FN64_CPU_SUSPEND;
-            cpu_on_fn = QEMU_PSCI_0_2_FN64_CPU_ON;
-            migrate_fn = QEMU_PSCI_0_2_FN64_MIGRATE;
-        } else {
-            cpu_suspend_fn = QEMU_PSCI_0_2_FN_CPU_SUSPEND;
-            cpu_on_fn = QEMU_PSCI_0_2_FN_CPU_ON;
-            migrate_fn = QEMU_PSCI_0_2_FN_MIGRATE;
-        }
-    } else {
-        qemu_fdt_setprop_string(fdt, "/psci", "compatible", "arm,psci");
-
-        cpu_suspend_fn = QEMU_PSCI_0_1_FN_CPU_SUSPEND;
-        cpu_off_fn = QEMU_PSCI_0_1_FN_CPU_OFF;
-        cpu_on_fn = QEMU_PSCI_0_1_FN_CPU_ON;
-        migrate_fn = QEMU_PSCI_0_1_FN_MIGRATE;
-    }
-
-    /* We adopt the PSCI spec's nomenclature, and use 'conduit' to refer
-     * to the instruction that should be used to invoke PSCI functions.
-     * However, the device tree binding uses 'method' instead, so that is
-     * what we should use here.
-     */
-    qemu_fdt_setprop_string(fdt, "/psci", "method", psci_method);
-
-    qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend", cpu_suspend_fn);
-    qemu_fdt_setprop_cell(fdt, "/psci", "cpu_off", cpu_off_fn);
-    qemu_fdt_setprop_cell(fdt, "/psci", "cpu_on", cpu_on_fn);
-    qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
-}
-
 static void fdt_add_timer_nodes(const VirtMachineState *vms)
 {
     /* On real hardware these interrupts are level-triggered.
@@ -1407,7 +1347,6 @@ static void machvirt_init(MachineState *machine)
     }
     fdt_add_timer_nodes(vms);
     fdt_add_cpu_nodes(vms);
-    fdt_add_psci_node(vms);
 
     memory_region_allocate_system_memory(ram, NULL, "mach-virt.ram",
                                          machine->ram_size);
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 11/14] ARM: Add basic code to emulate A7MPCore DAP block
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 11/14] ARM: Add basic code to emulate A7MPCore DAP block Andrey Smirnov
@ 2018-01-16  4:32   ` Philippe Mathieu-Daudé
  2018-01-16 14:41     ` Peter Maydell
  2018-01-16 15:04     ` Andrey Smirnov
  0 siblings, 2 replies; 46+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-01-16  4:32 UTC (permalink / raw)
  To: Andrey Smirnov, qemu-arm; +Cc: Peter Maydell, Jason Wang, qemu-devel, yurovsky

Hi Andrey,

On 01/15/2018 10:37 PM, Andrey Smirnov wrote:
> Add minimal code to emulate A7MPCore DAP block needed to boot Linux
> guest.

I was not aware the DAP is accessed by upstream Linux...

You sure this isn't rather part of some bootloader built-in self-test?

> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
>  hw/arm/Makefile.objs       |   2 +-
>  hw/arm/coresight.c         | 120 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/arm/coresight.h |  24 +++++++++
>  3 files changed, 145 insertions(+), 1 deletion(-)
>  create mode 100644 hw/arm/coresight.c
>  create mode 100644 include/hw/arm/coresight.h
> 
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 2794e086d6..692216e0cf 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -1,4 +1,4 @@
> -obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
> +obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o coresight.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
> diff --git a/hw/arm/coresight.c b/hw/arm/coresight.c
> new file mode 100644
> index 0000000000..d0a8c1b005
> --- /dev/null
> +++ b/hw/arm/coresight.c
> @@ -0,0 +1,120 @@
> +/*
> + * Copyright (c) 2017, Impinj, Inc.
> + *
> + * CoreSight block emulation code
> + *
> + * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
> + *
> + * 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/arm/coresight.h"
> +#include "qemu/log.h"
> +
> +static uint64_t coresight_read(void *opaque, hwaddr offset,
> +                               unsigned size)
> +{
> +    return 0;
> +}
> +
> +static void coresight_write(void *opaque, hwaddr offset,
> +                            uint64_t value, unsigned size)
> +{
> +}

I assume you had to add this to bypass the memory_transaction_failures
check.

> +
> +static const struct MemoryRegionOps coresight_ops = {
> +    .read = coresight_read,
> +    .write = coresight_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 a7mpcore_dap_init(Object *obj)
> +{
> +    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
> +    A7MPCoreDAPState *s = A7MPCORE_DAP(obj);
> +
> +    memory_region_init(&s->container, obj, "a7mpcore-dap-container", 0x100000);

You can just use add this in fsl_imx7_realize():

  create_unimplemented_device("a7mpcore-dap-container",
                              FSL_IMX7_A7MPCORE_DAP_ADDR,
                              0x100000);

to register a background region for the DAP (see "hw/misc/unimp.h")

as a bonus, running with "-d unimp" you can trace the DAP access.

So this model and those files are not necessary.

> +    sysbus_init_mmio(sd, &s->container);
> +
> +    memory_region_init_io(&s->ca7_atb_funnel,
> +                          obj,
> +                          &coresight_ops,
> +                          s,
> +                          TYPE_A7MPCORE_DAP ".ca7-atb-funnel",
> +                          0x1000);
> +    memory_region_add_subregion(&s->container, 0x41000, &s->ca7_atb_funnel);
> +
> +    memory_region_init_io(&s->cpu0_etm,
> +                          obj,
> +                          &coresight_ops,
> +                          s,
> +                          TYPE_A7MPCORE_DAP ".cpu0-etm",
> +                          0x1000);
> +    memory_region_add_subregion(&s->container, 0x7C000, &s->cpu0_etm);
> +
> +    memory_region_init_io(&s->atb_funnel,
> +                          obj,
> +                          &coresight_ops,
> +                          s,
> +                          TYPE_A7MPCORE_DAP ".atb-funnel",
> +                          0x1000);
> +    memory_region_add_subregion(&s->container, 0x83000, &s->atb_funnel);
> +
> +    memory_region_init_io(&s->tmc_etb,
> +                          obj,
> +                          &coresight_ops,
> +                          s,
> +                          TYPE_A7MPCORE_DAP ".tmc-etb",
> +                          0x1000);
> +    memory_region_add_subregion(&s->container, 0x84000, &s->tmc_etb);
> +
> +    memory_region_init_io(&s->tmc_etr,
> +                          obj,
> +                          &coresight_ops,
> +                          s,
> +                          TYPE_A7MPCORE_DAP ".tmc-etr",
> +                          0x1000);
> +    memory_region_add_subregion(&s->container, 0x86000, &s->tmc_etr);
> +
> +    memory_region_init_io(&s->tpiu,
> +                          obj,
> +                          &coresight_ops,
> +                          s,
> +                          TYPE_A7MPCORE_DAP ".tpiu",
> +                          0x1000);
> +    memory_region_add_subregion(&s->container, 0x87000, &s->tpiu);
> +}
> +
> +static void a7mpcore_dap_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->desc = "A7MPCore DAP Module";
> +}
> +
> +static const TypeInfo a7mpcore_dap_info = {
> +    .name          = TYPE_A7MPCORE_DAP,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(A7MPCoreDAPState),
> +    .instance_init = a7mpcore_dap_init,
> +    .class_init    = a7mpcore_dap_class_init,
> +};
> +
> +static void coresight_register_type(void)
> +{
> +    type_register_static(&a7mpcore_dap_info);
> +}
> +type_init(coresight_register_type)
> diff --git a/include/hw/arm/coresight.h b/include/hw/arm/coresight.h
> new file mode 100644
> index 0000000000..d1480e825b
> --- /dev/null
> +++ b/include/hw/arm/coresight.h
> @@ -0,0 +1,24 @@
> +#ifndef CORESIGHT_H
> +#define CORESIGHT_H
> +
> +#include "hw/sysbus.h"
> +
> +typedef struct A7MPCoreDAPState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion container;
> +
> +    MemoryRegion ca7_atb_funnel;
> +    MemoryRegion cpu0_etm;
> +    MemoryRegion atb_funnel;
> +    MemoryRegion tmc_etb;
> +    MemoryRegion tmc_etr;
> +    MemoryRegion tpiu;
> +
> +} A7MPCoreDAPState;
> +
> +#define TYPE_A7MPCORE_DAP "a7mpcore-dap"
> +#define A7MPCORE_DAP(obj) OBJECT_CHECK(A7MPCoreDAPState, (obj), TYPE_A7MPCORE_DAP)
> +
> +#endif /* CORESIGHT_H */
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 07/14] i.MX: Add i.MX7 GPT variant
       [not found] ` <20180116013709.13830-8-andrew.smirnov@gmail.com>
@ 2018-01-16  4:39   ` Philippe Mathieu-Daudé
  2018-01-16 14:29   ` Peter Maydell
  1 sibling, 0 replies; 46+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-01-16  4:39 UTC (permalink / raw)
  To: Andrey Smirnov, qemu-arm; +Cc: Peter Maydell, Jason Wang, qemu-devel, yurovsky

On 01/15/2018 10:37 PM, Andrey Smirnov wrote:
> Add minimal code needed to allow upstream Linux guest to boot.
> 
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
>  hw/timer/imx_gpt.c         | 25 +++++++++++++++++++++++++
>  include/hw/timer/imx_gpt.h |  1 +
>  2 files changed, 26 insertions(+)
> 
> 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,

ideally there should be an abstract TYPE_IMX_GPT you'd use as parent
rather than the TYPE_IMX25_GPT, anyway out of the scope of this series.

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +    .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
>  
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/14] i.MX: Add implementation of i.MX7 GPR IP block
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 08/14] i.MX: Add implementation of i.MX7 GPR " Andrey Smirnov
@ 2018-01-16  4:45   ` Philippe Mathieu-Daudé
  2018-01-16 15:05     ` Andrey Smirnov
  2018-01-16 14:30   ` Peter Maydell
  1 sibling, 1 reply; 46+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-01-16  4:45 UTC (permalink / raw)
  To: Andrey Smirnov, qemu-arm; +Cc: Peter Maydell, Jason Wang, qemu-devel, yurovsky

On 01/15/2018 10:37 PM, Andrey Smirnov wrote:
> Add minimal code needed to allow upstream Linux guest to boot.
> 
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
>  hw/misc/Makefile.objs      |   1 +
>  hw/misc/imx7_gpr.c         | 119 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/misc/imx7_gpr.h |  28 +++++++++++
>  3 files changed, 148 insertions(+)
>  create mode 100644 hw/misc/imx7_gpr.c
>  create mode 100644 include/hw/misc/imx7_gpr.h
> 
> 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..9e8ccea9e8
> --- /dev/null
> +++ b/hw/misc/imx7_gpr.c
> @@ -0,0 +1,119 @@
> +/*
> + * Copyright (c) 2017, Impinj, Inc.
> + *
> + * i.MX7 GPR IP block emulation code
> + *
> + * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
> + *
> + * 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"
> +
> +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)
> +{
> +    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)
> +{

If you ever respin, please add a trace point here (just copy/paste from
another file from the same directory), and in the read() function.

Linux will evolve and use more registers from this device (and the other
devices you are modelling), and a Linux driver busy loop is likely to
hang QEMU. A trace event will ease your board next user soon :)

That said,

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +}
> +
> +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/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 <andrew.smirnov@gmail.com>
> + *
> + * 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 {
> +    /* <private> */
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion mmio;
> +} IMX7GPRState;
> +
> +#endif /* IMX7_GPR_H */
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 03/14] i.MX: Add code to emulate i.MX7 CCM, PMU and ANALOG IP blocks
  2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 03/14] i.MX: Add code to emulate i.MX7 CCM, PMU and ANALOG IP blocks Andrey Smirnov
@ 2018-01-16 14:28   ` Peter Maydell
  0 siblings, 0 replies; 46+ messages in thread
From: Peter Maydell @ 2018-01-16 14:28 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: qemu-arm, Jason Wang, Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky

On 16 January 2018 at 01:36, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
> Add minimal code needed to allow upstream Linux guest to boot.
>
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
>  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
>

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 07/14] i.MX: Add i.MX7 GPT variant
       [not found] ` <20180116013709.13830-8-andrew.smirnov@gmail.com>
  2018-01-16  4:39   ` [Qemu-devel] [PATCH v4 07/14] i.MX: Add i.MX7 GPT variant Philippe Mathieu-Daudé
@ 2018-01-16 14:29   ` Peter Maydell
  1 sibling, 0 replies; 46+ messages in thread
From: Peter Maydell @ 2018-01-16 14:29 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: qemu-arm, Jason Wang, Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky

On 16 January 2018 at 01:37, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
> Add minimal code needed to allow upstream Linux guest to boot.
>
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/14] i.MX: Add implementation of i.MX7 GPR IP block
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 08/14] i.MX: Add implementation of i.MX7 GPR " Andrey Smirnov
  2018-01-16  4:45   ` Philippe Mathieu-Daudé
@ 2018-01-16 14:30   ` Peter Maydell
  1 sibling, 0 replies; 46+ messages in thread
From: Peter Maydell @ 2018-01-16 14:30 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: qemu-arm, Jason Wang, Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky

On 16 January 2018 at 01:37, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
> Add minimal code needed to allow upstream Linux guest to boot.
>
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware IP block
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware " Andrey Smirnov
@ 2018-01-16 14:34   ` Peter Maydell
  2018-01-17 15:23     ` Marcel Apfelbaum
  2018-01-30 13:18   ` Marcel Apfelbaum
  1 sibling, 1 reply; 46+ messages in thread
From: Peter Maydell @ 2018-01-16 14:34 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: qemu-arm, Jason Wang, Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky, Michael S. Tsirkin,
	Marcel Apfelbaum

On 16 January 2018 at 01:37, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
> Add code needed to get a functional PCI subsytem when using in
> conjunction with upstream Linux guest (4.13+). Tested to work against
> "e1000e" (network adapter, using MSI interrupts) as well as
> "usb-ehci" (USB controller, using legacy PCI interrupts).
>
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
>  default-configs/arm-softmmu.mak  |   2 +
>  hw/pci-host/Makefile.objs        |   2 +
>  hw/pci-host/designware.c         | 618 +++++++++++++++++++++++++++++++++++++++
>  include/hw/pci-host/designware.h |  93 ++++++
>  include/hw/pci/pci_ids.h         |   2 +
>  5 files changed, 717 insertions(+)
>  create mode 100644 hw/pci-host/designware.c
>  create mode 100644 include/hw/pci-host/designware.h

I'm not familiar enough with our PCI code to be able to review
this, I'm afraid. MST and Marcel are our PCI subsystem maintainers --
could one of you have a look at whether this seems to be a correct
implementation of a pcie host controller ?

I did notice it seems to be missing device state save/load support.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 10/14] usb: Add basic code to emulate Chipidea USB IP
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 10/14] usb: Add basic code to emulate Chipidea USB IP Andrey Smirnov
@ 2018-01-16 14:40   ` Peter Maydell
  0 siblings, 0 replies; 46+ messages in thread
From: Peter Maydell @ 2018-01-16 14:40 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: qemu-arm, Jason Wang, Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky

On 16 January 2018 at 01:37, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
> 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 <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---

This looked a little odd at first but I see it's how we're
implementing TYPE_FUSBH200_EHCI, so I guess it's ok.

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 11/14] ARM: Add basic code to emulate A7MPCore DAP block
  2018-01-16  4:32   ` Philippe Mathieu-Daudé
@ 2018-01-16 14:41     ` Peter Maydell
  2018-01-16 15:04     ` Andrey Smirnov
  1 sibling, 0 replies; 46+ messages in thread
From: Peter Maydell @ 2018-01-16 14:41 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Andrey Smirnov, qemu-arm, Jason Wang, QEMU Developers, Andrey Yurovsky

On 16 January 2018 at 04:32, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> You can just use add this in fsl_imx7_realize():
>
>   create_unimplemented_device("a7mpcore-dap-container",
>                               FSL_IMX7_A7MPCORE_DAP_ADDR,
>                               0x100000);
>
> to register a background region for the DAP (see "hw/misc/unimp.h")
>
> as a bonus, running with "-d unimp" you can trace the DAP access.
>
> So this model and those files are not necessary.

Yes, I think this is the best approach.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 12/14] i.MX: Add i.MX7 SOC implementation.
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 12/14] i.MX: Add i.MX7 SOC implementation Andrey Smirnov
@ 2018-01-16 14:42   ` Peter Maydell
  0 siblings, 0 replies; 46+ messages in thread
From: Peter Maydell @ 2018-01-16 14:42 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: qemu-arm, Jason Wang, Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky

On 16 January 2018 at 01:37, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
> The following interfaces are partially or fully emulated:
>
>     * up to 2 Cortex A9 cores (SMP works with PSCI)
>     * A7 MPCORE (identical to A15 MPCORE)
>     * 4 GPTs modules
>     * 7 GPIO controllers
>     * 2 IOMUXC controllers
>     * 1 CCM module
>     * 1 SVNS module
>     * 1 SRC module
>     * 1 GPCv2 controller
>     * 4 eCSPI controllers
>     * 4 I2C controllers
>     * 7 i.MX UART controllers
>     * 2 FlexCAN controllers
>     * 2 Ethernet controllers (FEC)
>     * 3 SD controllers (USDHC)
>     * 4 WDT modules
>     * 1 SDMA module
>     * 1 GPR module
>     * 2 USBMISC modules
>     * 2 ADC modules
>     * 1 PCIe controller
>
> Tested to boot and work with upstream Linux (4.13+) guest.
>
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 14/14] Implement support for i.MX7 Sabre board
       [not found] ` <20180116013709.13830-15-andrew.smirnov@gmail.com>
@ 2018-01-16 14:52   ` Peter Maydell
  0 siblings, 0 replies; 46+ messages in thread
From: Peter Maydell @ 2018-01-16 14:52 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: qemu-arm, Jason Wang, Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky

On 16 January 2018 at 01:37, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
> Implement code needed to set up emulation of MCIMX7SABRE board from
> NXP. For more info about the HW see:
>
> https://www.nxp.com/support/developer-resources/hardware-development-tools/sabre-development-system/sabre-board-for-smart-devices-based-on-the-i.mx-7dual-applications-processors:MCIMX7SABRE
>
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 13/14] hw/arm: Move virt's PSCI DT fixup code to arm/boot.c
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 13/14] hw/arm: Move virt's PSCI DT fixup code to arm/boot.c Andrey Smirnov
@ 2018-01-16 14:53   ` Peter Maydell
  0 siblings, 0 replies; 46+ messages in thread
From: Peter Maydell @ 2018-01-16 14:53 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: qemu-arm, Jason Wang, Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky

On 16 January 2018 at 01:37, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
> Move virt's PSCI DT fixup code to arm/boot.c and set this fixup to
> happen automatically for every board that doesn't mark "psci-conduit"
> as disabled. This way emulated boards other than "virt" that rely on
> PSIC for SMP could benefit from that code.
>
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
>  hw/arm/boot.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/arm/virt.c | 61 -------------------------------------------------------
>  2 files changed, 65 insertions(+), 61 deletions(-)
>

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 11/14] ARM: Add basic code to emulate A7MPCore DAP block
  2018-01-16  4:32   ` Philippe Mathieu-Daudé
  2018-01-16 14:41     ` Peter Maydell
@ 2018-01-16 15:04     ` Andrey Smirnov
  2018-01-16 16:47       ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16 15:04 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: open list:ARM, Peter Maydell, Jason Wang, QEMU Developers,
	Andrey Yurovsky

On Mon, Jan 15, 2018 at 8:32 PM, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> Hi Andrey,
>
> On 01/15/2018 10:37 PM, Andrey Smirnov wrote:
>> Add minimal code to emulate A7MPCore DAP block needed to boot Linux
>> guest.
>
> I was not aware the DAP is accessed by upstream Linux...
>
> You sure this isn't rather part of some bootloader built-in self-test?
>

Yes, I am positive:

a) I don't run any bootloader and boot directly into Linux, so it's
just physically impossible
b) Here's the code:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/hwtracing/coresight?h=v4.15-rc8


>> Cc: Peter Maydell <peter.maydell@linaro.org>
>> Cc: Jason Wang <jasowang@redhat.com>
>> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
>> Cc: qemu-devel@nongnu.org
>> Cc: qemu-arm@nongnu.org
>> Cc: yurovsky@gmail.com
>> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
>> ---
>>  hw/arm/Makefile.objs       |   2 +-
>>  hw/arm/coresight.c         | 120 +++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/arm/coresight.h |  24 +++++++++
>>  3 files changed, 145 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/arm/coresight.c
>>  create mode 100644 include/hw/arm/coresight.h
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index 2794e086d6..692216e0cf 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -1,4 +1,4 @@
>> -obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
>> +obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o coresight.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
>> diff --git a/hw/arm/coresight.c b/hw/arm/coresight.c
>> new file mode 100644
>> index 0000000000..d0a8c1b005
>> --- /dev/null
>> +++ b/hw/arm/coresight.c
>> @@ -0,0 +1,120 @@
>> +/*
>> + * Copyright (c) 2017, Impinj, Inc.
>> + *
>> + * CoreSight block emulation code
>> + *
>> + * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
>> + *
>> + * 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/arm/coresight.h"
>> +#include "qemu/log.h"
>> +
>> +static uint64_t coresight_read(void *opaque, hwaddr offset,
>> +                               unsigned size)
>> +{
>> +    return 0;
>> +}
>> +
>> +static void coresight_write(void *opaque, hwaddr offset,
>> +                            uint64_t value, unsigned size)
>> +{
>> +}
>
> I assume you had to add this to bypass the memory_transaction_failures
> check.
>
>> +
>> +static const struct MemoryRegionOps coresight_ops = {
>> +    .read = coresight_read,
>> +    .write = coresight_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 a7mpcore_dap_init(Object *obj)
>> +{
>> +    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
>> +    A7MPCoreDAPState *s = A7MPCORE_DAP(obj);
>> +
>> +    memory_region_init(&s->container, obj, "a7mpcore-dap-container", 0x100000);
>
> You can just use add this in fsl_imx7_realize():
>
>   create_unimplemented_device("a7mpcore-dap-container",
>                               FSL_IMX7_A7MPCORE_DAP_ADDR,
>                               0x100000);
>
> to register a background region for the DAP (see "hw/misc/unimp.h")
>
> as a bonus, running with "-d unimp" you can trace the DAP access.
>
> So this model and those files are not necessary.
>

I am aware of create_unimplemented_device(), my reasoning for keeping
this code as a standalone class was to make it reusable for other
systems while providing more granular mapping (not just covering the
whole 0x100000 bytes of memory). I'll convert the code to use
create_unimplemented_device() since we now have two votes in favor of
it.

Thanks,
Andrey Smirnov

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 08/14] i.MX: Add implementation of i.MX7 GPR IP block
  2018-01-16  4:45   ` Philippe Mathieu-Daudé
@ 2018-01-16 15:05     ` Andrey Smirnov
  0 siblings, 0 replies; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16 15:05 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: open list:ARM, Peter Maydell, Jason Wang, QEMU Developers,
	Andrey Yurovsky

On Mon, Jan 15, 2018 at 8:45 PM, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> On 01/15/2018 10:37 PM, Andrey Smirnov wrote:
>> Add minimal code needed to allow upstream Linux guest to boot.
>>
>> Cc: Peter Maydell <peter.maydell@linaro.org>
>> Cc: Jason Wang <jasowang@redhat.com>
>> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
>> Cc: qemu-devel@nongnu.org
>> Cc: qemu-arm@nongnu.org
>> Cc: yurovsky@gmail.com
>> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
>> ---
>>  hw/misc/Makefile.objs      |   1 +
>>  hw/misc/imx7_gpr.c         | 119 +++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/misc/imx7_gpr.h |  28 +++++++++++
>>  3 files changed, 148 insertions(+)
>>  create mode 100644 hw/misc/imx7_gpr.c
>>  create mode 100644 include/hw/misc/imx7_gpr.h
>>
>> 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..9e8ccea9e8
>> --- /dev/null
>> +++ b/hw/misc/imx7_gpr.c
>> @@ -0,0 +1,119 @@
>> +/*
>> + * Copyright (c) 2017, Impinj, Inc.
>> + *
>> + * i.MX7 GPR IP block emulation code
>> + *
>> + * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
>> + *
>> + * 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"
>> +
>> +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)
>> +{
>> +    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)
>> +{
>
> If you ever respin, please add a trace point here (just copy/paste from
> another file from the same directory), and in the read() function.
>
> Linux will evolve and use more registers from this device (and the other
> devices you are modelling), and a Linux driver busy loop is likely to
> hang QEMU. A trace event will ease your board next user soon :)
>

Sure, will do.

Thanks,
Andrey Smirnov

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
                   ` (13 preceding siblings ...)
       [not found] ` <20180116013709.13830-15-andrew.smirnov@gmail.com>
@ 2018-01-16 15:08 ` Peter Maydell
  2018-01-16 15:17   ` Andrey Smirnov
  2018-01-31 17:03 ` Philippe Mathieu-Daudé
  15 siblings, 1 reply; 46+ messages in thread
From: Peter Maydell @ 2018-01-16 15:08 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: qemu-arm, Jason Wang, Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky

On 16 January 2018 at 01:36, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
> Hi everyone,
>
> This v4 of the patch series containing the work that I've done in
> order to enable support for i.MX7 emulation in QEMU.
>
> *NOTE*: Patches 1 and 2 are provided for the sake of completness and
>         are going to have to be adapted once Philippe's SD changes
>         land in master. As such, they are NOT ready to be
>         accepted/merged.

I've just pushed a pullreq including Philippe's SD changes to master,
so you should be able to do the final versions of these i.MX SD patches
now. I've also I think now reviewed everything except the pci patch,
which is a bit out of my competence area.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support
  2018-01-16 15:08 ` [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Peter Maydell
@ 2018-01-16 15:17   ` Andrey Smirnov
  0 siblings, 0 replies; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-16 15:17 UTC (permalink / raw)
  To: Peter Maydell
  Cc: qemu-arm, Jason Wang, Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky

On Tue, Jan 16, 2018 at 7:08 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 16 January 2018 at 01:36, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
>> Hi everyone,
>>
>> This v4 of the patch series containing the work that I've done in
>> order to enable support for i.MX7 emulation in QEMU.
>>
>> *NOTE*: Patches 1 and 2 are provided for the sake of completness and
>>         are going to have to be adapted once Philippe's SD changes
>>         land in master. As such, they are NOT ready to be
>>         accepted/merged.
>
> I've just pushed a pullreq including Philippe's SD changes to master,
> so you should be able to do the final versions of these i.MX SD patches
> now. I've also I think now reviewed everything except the pci patch,
> which is a bit out of my competence area.

That's great news! I'll rebase on top of that as soon as I get a chance.

Thanks,
Andrey Smirnov

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 11/14] ARM: Add basic code to emulate A7MPCore DAP block
  2018-01-16 15:04     ` Andrey Smirnov
@ 2018-01-16 16:47       ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 46+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-01-16 16:47 UTC (permalink / raw)
  To: Andrey Smirnov, Alistair Francis
  Cc: open list:ARM, Peter Maydell, Jason Wang, QEMU Developers,
	Andrey Yurovsky

On 01/16/2018 12:04 PM, Andrey Smirnov wrote:
> On Mon, Jan 15, 2018 at 8:32 PM, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>> Hi Andrey,
>>
>> On 01/15/2018 10:37 PM, Andrey Smirnov wrote:
>>> Add minimal code to emulate A7MPCore DAP block needed to boot Linux
>>> guest.
>>
>> I was not aware the DAP is accessed by upstream Linux...
>>
>> You sure this isn't rather part of some bootloader built-in self-test?
>>
> 
> Yes, I am positive:
> 
> a) I don't run any bootloader and boot directly into Linux, so it's
> just physically impossible
> b) Here's the code:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/hwtracing/coresight?h=v4.15-rc8

Wow :) pretty interesting!

Alistair:
Some features might be interesting to add to the multicore-gdb project idea.

Thanks Andrey for updating me :)

Phil.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware IP block
  2018-01-16 14:34   ` Peter Maydell
@ 2018-01-17 15:23     ` Marcel Apfelbaum
  2018-01-17 15:35       ` Peter Maydell
  2018-01-17 16:12       ` Andrey Smirnov
  0 siblings, 2 replies; 46+ messages in thread
From: Marcel Apfelbaum @ 2018-01-17 15:23 UTC (permalink / raw)
  To: Peter Maydell, Andrey Smirnov
  Cc: Michael S. Tsirkin, Jason Wang, Philippe Mathieu-Daudé,
	QEMU Developers, qemu-arm, Marcel Apfelbaum, Andrey Yurovsky


Hi Peter,

On 16/01/2018 16:34, Peter Maydell wrote:
> On 16 January 2018 at 01:37, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
>> Add code needed to get a functional PCI subsytem when using in
>> conjunction with upstream Linux guest (4.13+). Tested to work against
>> "e1000e" (network adapter, using MSI interrupts) as well as
>> "usb-ehci" (USB controller, using legacy PCI interrupts).
>>
>> Cc: Peter Maydell <peter.maydell@linaro.org>
>> Cc: Jason Wang <jasowang@redhat.com>
>> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
>> Cc: qemu-devel@nongnu.org
>> Cc: qemu-arm@nongnu.org
>> Cc: yurovsky@gmail.com
>> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
>> ---
>>   default-configs/arm-softmmu.mak  |   2 +
>>   hw/pci-host/Makefile.objs        |   2 +
>>   hw/pci-host/designware.c         | 618 +++++++++++++++++++++++++++++++++++++++
>>   include/hw/pci-host/designware.h |  93 ++++++
>>   include/hw/pci/pci_ids.h         |   2 +
>>   5 files changed, 717 insertions(+)
>>   create mode 100644 hw/pci-host/designware.c
>>   create mode 100644 include/hw/pci-host/designware.h
> I'm not familiar enough with our PCI code to be able to review
> this, I'm afraid. MST and Marcel are our PCI subsystem maintainers --
> could one of you have a look at whether this seems to be a correct
> implementation of a pcie host controller ?

Sadly PCI Host bridges do not have a standard, each HW vendor
can do pretty much what they want.

That being said, if Andrey can point me to the PCI spec for the Designware
PCI host bridge and what parts they implemented for it I can have a look, sure.
(I will not be available for a week or so, but right after)

Thanks,
Marcel
> I did notice it seems to be missing device state save/load support.
>
> thanks
> -- PMM
>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware IP block
  2018-01-17 15:23     ` Marcel Apfelbaum
@ 2018-01-17 15:35       ` Peter Maydell
  2018-01-17 16:12         ` Marcel Apfelbaum
  2018-01-17 16:12       ` Andrey Smirnov
  1 sibling, 1 reply; 46+ messages in thread
From: Peter Maydell @ 2018-01-17 15:35 UTC (permalink / raw)
  To: Marcel Apfelbaum
  Cc: Andrey Smirnov, Michael S. Tsirkin, Jason Wang,
	Philippe Mathieu-Daudé,
	QEMU Developers, qemu-arm, Andrey Yurovsky

On 17 January 2018 at 15:23, Marcel Apfelbaum <marcel.apfelbaum@zoho.com> wrote:
>
> On 16/01/2018 16:34, Peter Maydell wrote:
>> On 16 January 2018 at 01:37, Andrey Smirnov <andrew.smirnov@gmail.com>
>> I'm not familiar enough with our PCI code to be able to review
>> this, I'm afraid. MST and Marcel are our PCI subsystem maintainers --
>> could one of you have a look at whether this seems to be a correct
>> implementation of a pcie host controller ?
>
>
> Sadly PCI Host bridges do not have a standard, each HW vendor
> can do pretty much what they want.
>
> That being said, if Andrey can point me to the PCI spec for the Designware
> PCI host bridge and what parts they implemented for it I can have a look,
> sure.

I'm not so worried about whether it's implementing the spec for
the hardware (I trust Andrey has done enough testing for that
side of things), but whether the code seems to be structured
the way we expect a QEMU pcie host controller to be structured,
is using the right APIs, and so on.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware IP block
  2018-01-17 15:23     ` Marcel Apfelbaum
  2018-01-17 15:35       ` Peter Maydell
@ 2018-01-17 16:12       ` Andrey Smirnov
  2018-01-17 16:17         ` Marcel Apfelbaum
  2018-01-17 16:45         ` Philippe Mathieu-Daudé
  1 sibling, 2 replies; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-17 16:12 UTC (permalink / raw)
  To: marcel
  Cc: Peter Maydell, Michael S. Tsirkin, Jason Wang,
	Philippe Mathieu-Daudé,
	QEMU Developers, qemu-arm, Andrey Yurovsky

On Wed, Jan 17, 2018 at 7:23 AM, Marcel Apfelbaum
<marcel.apfelbaum@zoho.com> wrote:
>
> Hi Peter,
>
>
> On 16/01/2018 16:34, Peter Maydell wrote:
>>
>> On 16 January 2018 at 01:37, Andrey Smirnov <andrew.smirnov@gmail.com>
>> wrote:
>>>
>>> Add code needed to get a functional PCI subsytem when using in
>>> conjunction with upstream Linux guest (4.13+). Tested to work against
>>> "e1000e" (network adapter, using MSI interrupts) as well as
>>> "usb-ehci" (USB controller, using legacy PCI interrupts).
>>>
>>> Cc: Peter Maydell <peter.maydell@linaro.org>
>>> Cc: Jason Wang <jasowang@redhat.com>
>>> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
>>> Cc: qemu-devel@nongnu.org
>>> Cc: qemu-arm@nongnu.org
>>> Cc: yurovsky@gmail.com
>>> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
>>> ---
>>>   default-configs/arm-softmmu.mak  |   2 +
>>>   hw/pci-host/Makefile.objs        |   2 +
>>>   hw/pci-host/designware.c         | 618
>>> +++++++++++++++++++++++++++++++++++++++
>>>   include/hw/pci-host/designware.h |  93 ++++++
>>>   include/hw/pci/pci_ids.h         |   2 +
>>>   5 files changed, 717 insertions(+)
>>>   create mode 100644 hw/pci-host/designware.c
>>>   create mode 100644 include/hw/pci-host/designware.h
>>
>> I'm not familiar enough with our PCI code to be able to review
>> this, I'm afraid. MST and Marcel are our PCI subsystem maintainers --
>> could one of you have a look at whether this seems to be a correct
>> implementation of a pcie host controller ?
>
>
> Sadly PCI Host bridges do not have a standard, each HW vendor
> can do pretty much what they want.
>
> That being said, if Andrey can point me to the PCI spec for the Designware
> PCI host bridge and what parts they implemented for it I can have a look,
> sure.
> (I will not be available for a week or so, but right after)
>

Just in case you still want this:

To the best of my knowledge, Synposys does not provide specification
for their PCIe IP to general public and I am in no way affiliated with
them, so I don't have any backchannels to get it any other way.

The next best thing to an actual spec, that I found to be pretty
useful, is PCIe chapter of i.MX6Q Reference Manual
(https://www.nxp.com/docs/en/reference-manual/IMX6DQRM.pdf   page
4049), which is what I used to implement the code in question.

Last, and probably the most important, "source of truth" was actual
Linux PCIe driver for i.MX/Designware which I used as a sort of
inverse reference implementation.

Thanks,
Andrey Smirnov

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware IP block
  2018-01-17 15:35       ` Peter Maydell
@ 2018-01-17 16:12         ` Marcel Apfelbaum
  0 siblings, 0 replies; 46+ messages in thread
From: Marcel Apfelbaum @ 2018-01-17 16:12 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrey Smirnov, Michael S. Tsirkin, Jason Wang,
	Philippe Mathieu-Daudé,
	QEMU Developers, qemu-arm, Andrey Yurovsky

On 17/01/2018 17:35, Peter Maydell wrote:
> On 17 January 2018 at 15:23, Marcel Apfelbaum <marcel.apfelbaum@zoho.com> wrote:
>>
>> On 16/01/2018 16:34, Peter Maydell wrote:
>>> On 16 January 2018 at 01:37, Andrey Smirnov <andrew.smirnov@gmail.com>
>>> I'm not familiar enough with our PCI code to be able to review
>>> this, I'm afraid. MST and Marcel are our PCI subsystem maintainers --
>>> could one of you have a look at whether this seems to be a correct
>>> implementation of a pcie host controller ?
>>
>>
>> Sadly PCI Host bridges do not have a standard, each HW vendor
>> can do pretty much what they want.
>>
>> That being said, if Andrey can point me to the PCI spec for the Designware
>> PCI host bridge and what parts they implemented for it I can have a look,
>> sure.
> 
> I'm not so worried about whether it's implementing the spec for
> the hardware (I trust Andrey has done enough testing for that
> side of things), but whether the code seems to be structured
> the way we expect a QEMU pcie host controller to be structured,
> is using the right APIs, and so on.
> 

Got it, I'll review in a week.

Thanks,
Marcel

> thanks
> -- PMM
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware IP block
  2018-01-17 16:12       ` Andrey Smirnov
@ 2018-01-17 16:17         ` Marcel Apfelbaum
  2018-01-17 16:45         ` Philippe Mathieu-Daudé
  1 sibling, 0 replies; 46+ messages in thread
From: Marcel Apfelbaum @ 2018-01-17 16:17 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: Peter Maydell, Michael S. Tsirkin, Jason Wang,
	Philippe Mathieu-Daudé,
	QEMU Developers, qemu-arm, Andrey Yurovsky

On 17/01/2018 18:12, Andrey Smirnov wrote:
> On Wed, Jan 17, 2018 at 7:23 AM, Marcel Apfelbaum
> <marcel.apfelbaum@zoho.com> wrote:
>>
>> Hi Peter,
>>
>>
>> On 16/01/2018 16:34, Peter Maydell wrote:
>>>
>>> On 16 January 2018 at 01:37, Andrey Smirnov <andrew.smirnov@gmail.com>
>>> wrote:
>>>>
>>>> Add code needed to get a functional PCI subsytem when using in
>>>> conjunction with upstream Linux guest (4.13+). Tested to work against
>>>> "e1000e" (network adapter, using MSI interrupts) as well as
>>>> "usb-ehci" (USB controller, using legacy PCI interrupts).
>>>>
>>>> Cc: Peter Maydell <peter.maydell@linaro.org>
>>>> Cc: Jason Wang <jasowang@redhat.com>
>>>> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
>>>> Cc: qemu-devel@nongnu.org
>>>> Cc: qemu-arm@nongnu.org
>>>> Cc: yurovsky@gmail.com
>>>> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
>>>> ---
>>>>    default-configs/arm-softmmu.mak  |   2 +
>>>>    hw/pci-host/Makefile.objs        |   2 +
>>>>    hw/pci-host/designware.c         | 618
>>>> +++++++++++++++++++++++++++++++++++++++
>>>>    include/hw/pci-host/designware.h |  93 ++++++
>>>>    include/hw/pci/pci_ids.h         |   2 +
>>>>    5 files changed, 717 insertions(+)
>>>>    create mode 100644 hw/pci-host/designware.c
>>>>    create mode 100644 include/hw/pci-host/designware.h
>>>
>>> I'm not familiar enough with our PCI code to be able to review
>>> this, I'm afraid. MST and Marcel are our PCI subsystem maintainers --
>>> could one of you have a look at whether this seems to be a correct
>>> implementation of a pcie host controller ?
>>
>>
>> Sadly PCI Host bridges do not have a standard, each HW vendor
>> can do pretty much what they want.
>>
>> That being said, if Andrey can point me to the PCI spec for the Designware
>> PCI host bridge and what parts they implemented for it I can have a look,
>> sure.
>> (I will not be available for a week or so, but right after)
>>
> 
> Just in case you still want this:
> 
> To the best of my knowledge, Synposys does not provide specification
> for their PCIe IP to general public and I am in no way affiliated with
> them, so I don't have any backchannels to get it any other way.
> 
> The next best thing to an actual spec, that I found to be pretty
> useful, is PCIe chapter of i.MX6Q Reference Manual
> (https://www.nxp.com/docs/en/reference-manual/IMX6DQRM.pdf   page
> 4049), which is what I used to implement the code in question.
> 

Appreciated.

> Last, and probably the most important, "source of truth" was actual
> Linux PCIe driver for i.MX/Designware which I used as a sort of
> inverse reference implementation.
> 

We did the same for our PVRDMA device implementation :)


Thanks,
Marcel

> Thanks,
> Andrey Smirnov
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware IP block
  2018-01-17 16:12       ` Andrey Smirnov
  2018-01-17 16:17         ` Marcel Apfelbaum
@ 2018-01-17 16:45         ` Philippe Mathieu-Daudé
  1 sibling, 0 replies; 46+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-01-17 16:45 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: Marcel Apfelbaum, Peter Maydell, Michael S. Tsirkin, Jason Wang,
	QEMU Developers, qemu-arm, Andrey Yurovsky

Hi Andrey,

On Wed, Jan 17, 2018 at 1:12 PM, Andrey Smirnov
<andrew.smirnov@gmail.com> wrote:
> On Wed, Jan 17, 2018 at 7:23 AM, Marcel Apfelbaum
> <marcel.apfelbaum@zoho.com> wrote:
>> That being said, if Andrey can point me to the PCI spec for the Designware
>> PCI host bridge and what parts they implemented for it I can have a look,
>> sure.
>> (I will not be available for a week or so, but right after)
>>
>
> Just in case you still want this:
>
> To the best of my knowledge, Synposys does not provide specification
> for their PCIe IP to general public and I am in no way affiliated with
> them, so I don't have any backchannels to get it any other way.
>
> The next best thing to an actual spec, that I found to be pretty
> useful, is PCIe chapter of i.MX6Q Reference Manual
> (https://www.nxp.com/docs/en/reference-manual/IMX6DQRM.pdf   page
> 4049), which is what I used to implement the code in question.

Please add a comment about it in your respin, such:

Based on "i.MX6 Applications Processor Reference Manual" (Document
Number: IMX6DQRM Rev. 4)

> Last, and probably the most important, "source of truth" was actual
> Linux PCIe driver for i.MX/Designware which I used as a sort of
> inverse reference implementation.

Same here:

Reversed from Linux v4.9 drivers/pci/host/pcie-designware.c

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware IP block
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware " Andrey Smirnov
  2018-01-16 14:34   ` Peter Maydell
@ 2018-01-30 13:18   ` Marcel Apfelbaum
  2018-01-30 17:49     ` Andrey Smirnov
  1 sibling, 1 reply; 46+ messages in thread
From: Marcel Apfelbaum @ 2018-01-30 13:18 UTC (permalink / raw)
  To: Andrey Smirnov, qemu-arm
  Cc: Peter Maydell, Jason Wang, Philippe Mathieu-Daudé,
	qemu-devel, yurovsky

Hi Andrei,

Sorry for letting you wait,
I have some comments/questions below.

On 16/01/2018 3:37, Andrey Smirnov wrote:
> Add code needed to get a functional PCI subsytem when using in
> conjunction with upstream Linux guest (4.13+). Tested to work against
> "e1000e" (network adapter, using MSI interrupts) as well as
> "usb-ehci" (USB controller, using legacy PCI interrupts).
>
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
>   default-configs/arm-softmmu.mak  |   2 +
>   hw/pci-host/Makefile.objs        |   2 +
>   hw/pci-host/designware.c         | 618 +++++++++++++++++++++++++++++++++++++++
>   include/hw/pci-host/designware.h |  93 ++++++
>   include/hw/pci/pci_ids.h         |   2 +
>   5 files changed, 717 insertions(+)
>   create mode 100644 hw/pci-host/designware.c
>   create mode 100644 include/hw/pci-host/designware.h
>
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index b0d6e65038..0c5ae914ed 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -132,3 +132,5 @@ CONFIG_GPIO_KEY=y
>   CONFIG_MSF2=y
>   CONFIG_FW_CFG_DMA=y
>   CONFIG_XILINX_AXI=y
> +CONFIG_PCI_DESIGNWARE=y
> +
> diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
> index 9c7909cf44..0e2c0a123b 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..98fff5e5f3
> --- /dev/null
> +++ b/hw/pci-host/designware.c
> @@ -0,0 +1,618 @@
> +/*
> + * Copyright (c) 2017, Impinj, Inc.
2018 :)
> + *
> + * 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 PCIE_PORT_LINK_CONTROL          0x710
> +
> +#define PCIE_PHY_DEBUG_R1               0x72C
> +#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP  BIT(4)
> +
> +#define PCIE_LINK_WIDTH_SPEED_CONTROL   0x80C
> +
> +#define PCIE_MSI_ADDR_LO                0x820
> +#define PCIE_MSI_ADDR_HI                0x824
> +#define PCIE_MSI_INTR0_ENABLE           0x828
> +#define PCIE_MSI_INTR0_MASK             0x82C
> +#define PCIE_MSI_INTR0_STATUS           0x830
> +
> +#define PCIE_ATU_VIEWPORT               0x900
> +#define PCIE_ATU_REGION_INBOUND         (0x1 << 31)
> +#define PCIE_ATU_REGION_OUTBOUND        (0x0 << 31)
> +#define PCIE_ATU_REGION_INDEX2          (0x2 << 0)
> +#define PCIE_ATU_REGION_INDEX1          (0x1 << 0)
> +#define PCIE_ATU_REGION_INDEX0          (0x0 << 0)
> +#define PCIE_ATU_CR1                    0x904
> +#define PCIE_ATU_TYPE_MEM               (0x0 << 0)
> +#define PCIE_ATU_TYPE_IO                (0x2 << 0)
> +#define PCIE_ATU_TYPE_CFG0              (0x4 << 0)
> +#define PCIE_ATU_TYPE_CFG1              (0x5 << 0)
> +#define PCIE_ATU_CR2                    0x908
> +#define PCIE_ATU_ENABLE                 (0x1 << 31)
> +#define PCIE_ATU_BAR_MODE_ENABLE        (0x1 << 30)
> +#define PCIE_ATU_LOWER_BASE             0x90C
> +#define PCIE_ATU_UPPER_BASE             0x910
> +#define PCIE_ATU_LIMIT                  0x914
> +#define PCIE_ATU_LOWER_TARGET           0x918
> +#define PCIE_ATU_BUS(x)                 (((x) >> 24) & 0xff)
> +#define PCIE_ATU_DEVFN(x)               (((x) >> 16) & 0xff)
> +#define 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 |= (1 << 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);

Sorry for the possibly dumb question, but "msi_write"
will result in raising an INTx ?
> +    }
> +}
> +
> +const MemoryRegionOps designware_pci_host_msi_ops = {
> +    .write = designware_pcie_root_msi_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,

You may want to limit the access size.
> +};
> +
> +static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot *root)
> +
> +{
> +    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
> +    MemoryRegion *address_space = &host->pci.memory;
> +    MemoryRegion *mem = &root->msi.iomem;
> +    const uint64_t base = root->msi.base;
> +    const bool enable = root->msi.intr[0].enable;
> +
> +    if (memory_region_is_mapped(mem)) {
> +        memory_region_del_subregion(address_space, mem);
> +        object_unparent(OBJECT(mem));
> +    }
> +
> +    if (enable) {
> +        memory_region_init_io(mem, OBJECT(root),
> +                              &designware_pci_host_msi_ops,
> +                              root, "pcie-msi", 0x1000);
> +
> +        memory_region_add_subregion(address_space, base, mem);

What happens if is enabled twice?
Is it possible to be also disabled?

> +    }
> +}
> +
> +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 & 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 PCIE_PORT_LINK_CONTROL:
> +    case PCIE_LINK_WIDTH_SPEED_CONTROL:
> +        val = 0xDEADBEEF;
> +        /* No-op */

Not really a no-op
> +        break;
> +
> +    case PCIE_MSI_ADDR_LO:
> +        val = root->msi.base;
> +        break;
> +
> +    case PCIE_MSI_ADDR_HI:
> +        val = root->msi.base >> 32;
> +        break;
> +
> +    case PCIE_MSI_INTR0_ENABLE:
> +        val = root->msi.intr[0].enable;
> +        break;
> +
> +    case PCIE_MSI_INTR0_MASK:
> +        val = root->msi.intr[0].mask;
> +        break;
> +
> +    case PCIE_MSI_INTR0_STATUS:
> +        val = root->msi.intr[0].status;
> +        break;
> +
> +    case PCIE_PHY_DEBUG_R1:
> +        val = PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
> +        break;
> +
> +    case PCIE_ATU_VIEWPORT:
> +        val = root->atu_viewport;
> +        break;
> +
> +    case PCIE_ATU_LOWER_BASE:
> +        val = viewport->base;
> +        break;
> +
> +    case PCIE_ATU_UPPER_BASE:
> +        val = viewport->base >> 32;
> +        break;
> +
> +    case PCIE_ATU_LOWER_TARGET:
> +        val = viewport->target;
> +        break;
> +
> +    case PCIE_ATU_UPPER_TARGET:
> +        val = viewport->target >> 32;
> +        break;
> +
> +    case PCIE_ATU_LIMIT:
> +        val = viewport->limit;
> +        break;
> +
> +    case PCIE_ATU_CR1:
> +    case PCIE_ATU_CR2:          /* FALLTHROUGH */
> +        val = viewport->cr[(address - 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_read(void *opaque,
> +                                               hwaddr addr, unsigned len)
> +{
> +    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
> +    DesignwarePCIEViewport *viewport =
> +        designware_pcie_root_get_current_viewport(root);
> +
> +    const uint8_t busnum = PCIE_ATU_BUS(viewport->target);
> +    const uint8_t devfn  = 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_SPACE_SIZE - 1;
> +
> +        return pci_host_config_read_common(pcidev, addr,
> +                                           PCI_CONFIG_SPACE_SIZE, len);
> +    }
You can use "pci_data_read" instead
> +
> +    return UINT64_MAX;
> +}
> +
> +static void designware_pcie_root_data_write(void *opaque, hwaddr addr,
> +                                            uint64_t val, unsigned len)
> +{
> +    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
> +    DesignwarePCIEViewport *viewport =
> +        designware_pcie_root_get_current_viewport(root);
> +    const uint8_t busnum = PCIE_ATU_BUS(viewport->target);
> +    const uint8_t devfn  = 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_SPACE_SIZE - 1;
> +        pci_host_config_write_common(pcidev, addr,
> +                                     PCI_CONFIG_SPACE_SIZE,
> +                                     val, len);
> +    }
You can use pci_data_write instead.

> +}
> +
> +const MemoryRegionOps designware_pci_host_conf_ops = {
> +    .read = designware_pcie_root_data_read,
> +    .write = designware_pcie_root_data_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,

Maybe you want to limit the access size also here.
> +};
> +
> +static void designware_pcie_update_viewport(DesignwarePCIERoot *root,
> +                                            DesignwarePCIEViewport *viewport)
> +{
> +    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
> +
> +    MemoryRegion *mem     = &viewport->memory;
> +    const uint64_t target = viewport->target;
> +    const uint64_t base   = viewport->base;
> +    const uint64_t size   = (uint64_t)viewport->limit - base + 1;
> +    const bool inbound    = viewport->inbound;
> +
> +    MemoryRegion *source, *destination;
> +    const char *direction;
> +    char *name;
> +
> +    if (inbound) {
> +        source      = &host->pci.address_space_root;
> +        destination = get_system_memory();
> +        direction   = "Inbound";
> +    } else {
> +        source      = get_system_memory();
> +        destination = &host->pci.memory;
> +        direction   = "Outbound";
> +    }
> +
> +    if (memory_region_is_mapped(mem)) {
> +        /* Before we modify anything, unmap and destroy the region */

I saw this also before. Can you please explain a little
why/when do you need to destroy prev mappings?
> +        memory_region_del_subregion(source, mem);
> +        object_unparent(OBJECT(mem));
> +    }
> +
> +    name = g_strdup_printf("PCI %s Viewport %p", direction, viewport);
> +
> +    switch (viewport->cr[0]) {
> +    case PCIE_ATU_TYPE_MEM:
> +        memory_region_init_alias(mem, OBJECT(root), name,
> +                                 destination, target, size);
> +        break;
> +    case PCIE_ATU_TYPE_CFG0:
> +    case PCIE_ATU_TYPE_CFG1:    /* FALLTHROUGH */
> +        if (inbound) {
> +            goto exit;
> +        }
> +
> +        memory_region_init_io(mem, OBJECT(root),
> +                              &designware_pci_host_conf_ops,
> +                              root, name, size);
> +        break;
> +    }
> +
> +    if (inbound) {
> +        memory_region_add_subregion_overlap(source, base,
> +                                            mem, -1);
> +    } else {
> +        memory_region_add_subregion(source, base, mem);
> +    }
> +
> + exit:
> +    g_free(name);
> +}
> +
> +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 PCIE_PORT_LINK_CONTROL:
> +    case PCIE_LINK_WIDTH_SPEED_CONTROL:
> +    case PCIE_PHY_DEBUG_R1:
> +        /* No-op */
> +        break;
> +
> +    case PCIE_MSI_ADDR_LO:
> +        root->msi.base &= 0xFFFFFFFF00000000ULL;
> +        root->msi.base |= val;
> +        break;
> +
> +    case PCIE_MSI_ADDR_HI:
> +        root->msi.base &= 0x00000000FFFFFFFFULL;
> +        root->msi.base |= (uint64_t)val << 32;
> +        break;
> +
> +    case 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 PCIE_MSI_INTR0_MASK:
> +        root->msi.intr[0].mask = val;
> +        break;
> +
> +    case 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 PCIE_ATU_VIEWPORT:
> +        root->atu_viewport = val;
> +        break;
> +
> +    case PCIE_ATU_LOWER_BASE:
> +        viewport->base &= 0xFFFFFFFF00000000ULL;
> +        viewport->base |= val;
> +        break;
> +
> +    case PCIE_ATU_UPPER_BASE:
> +        viewport->base &= 0x00000000FFFFFFFFULL;
> +        viewport->base |= (uint64_t)val << 32;
> +        break;
> +
> +    case PCIE_ATU_LOWER_TARGET:
> +        viewport->target &= 0xFFFFFFFF00000000ULL;
> +        viewport->target |= val;
> +        break;
> +
> +    case PCIE_ATU_UPPER_TARGET:
> +        viewport->target &= 0x00000000FFFFFFFFULL;
> +        viewport->target |= val;
> +        break;
> +
> +    case PCIE_ATU_LIMIT:
> +        viewport->limit = val;
> +        break;
> +
> +    case PCIE_ATU_CR1:
> +        viewport->cr[0] = val;
> +        break;
> +    case PCIE_ATU_CR2:
> +        viewport->cr[1] = val;
> +
> +        if (viewport->cr[1] & PCIE_ATU_ENABLE) {
> +            designware_pcie_update_viewport(root, viewport);
> +         }
> +        break;
> +
> +    default:
> +        pci_bridge_write_config(d, address, val, len);
> +        break;
> +    }
> +}
> +
> +static int designware_pcie_root_init(PCIDevice *dev)
> +{

Please use "realize" function rather than init.
We want to get rid of it eventually.
> +    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev);
> +    PCIBridge *br = PCI_BRIDGE(dev);
> +    DesignwarePCIEViewport *viewport;
> +    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_PCI_BUS);
So this is a PCI Express Root Port "sitting" on PCI 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++) {
> +        viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i];
> +        viewport->inbound = true;
> +    }
> +
> +    /*
> +     * 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->base   = 0x0000000000000000ULL;
> +    viewport->target = 0x0000000000000000ULL;
> +    viewport->limit  = UINT32_MAX;
> +    viewport->cr[0]  = PCIE_ATU_TYPE_MEM;
> +
> +    designware_pcie_update_viewport(root, viewport);
> +
> +    return 0;
> +}
> +
> +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 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_HOST;
So is a Root Port with call is "BRIDGE_HOST" ?

> +    k->is_express = true;
> +    k->is_bridge = true;
> +    k->init = designware_pcie_root_init;
> +    k->exit = pci_bridge_exitfn;
> +    dc->reset = pci_bridge_reset;
> +    k->config_read = designware_pcie_root_config_read;
> +    k->config_write = designware_pcie_root_config_write;

Please add category:
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> +
> +    /*
> +     * 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;
> +}
> +
> +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_NATIVE_ENDIAN,
> +};
> +
> +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 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";
> +}
> +
> +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)
> +
> +/* 00:00.0 Class 0604: 16c3:abcd */
> diff --git a/include/hw/pci-host/designware.h b/include/hw/pci-host/designware.h
> new file mode 100644
> index 0000000000..55e45fcba0
> --- /dev/null
> +++ b/include/hw/pci-host/designware.h
> @@ -0,0 +1,93 @@
> +/*
> + * Copyright (c) 2017, 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/>.
> + */
> +
> +#ifndef DESIGNWARE_H
> +#define DESIGNWARE_H
> +
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +#include "hw/pci/pci.h"
> +#include "hw/pci/pci_bus.h"
> +#include "hw/pci/pcie_host.h"
> +#include "hw/pci/pci_bridge.h"
> +
> +#define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host"
> +#define DESIGNWARE_PCIE_HOST(obj) \
> +     OBJECT_CHECK(DesignwarePCIEHost, (obj), TYPE_DESIGNWARE_PCIE_HOST)
> +
> +#define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root"
> +#define DESIGNWARE_PCIE_ROOT(obj) \
> +     OBJECT_CHECK(DesignwarePCIERoot, (obj), TYPE_DESIGNWARE_PCIE_ROOT)
> +
> +typedef struct DesignwarePCIEViewport {
> +    MemoryRegion memory;
> +
> +    uint64_t base;
> +    uint64_t target;
> +    uint32_t limit;
> +    uint32_t cr[2];
> +
> +    bool inbound;
> +} DesignwarePCIEViewport;
> +
> +typedef struct DesignwarePCIERoot {
> +    PCIBridge parent_obj;
> +
> +    uint32_t atu_viewport;
> +
> +#define DESIGNWARE_PCIE_VIEWPORT_OUTBOUND    0
> +#define DESIGNWARE_PCIE_VIEWPORT_INBOUND     1
> +#define DESIGNWARE_PCIE_NUM_VIEWPORTS        4
> +
> +    DesignwarePCIEViewport viewports[2][DESIGNWARE_PCIE_NUM_VIEWPORTS];
> +
> +    struct {
> +        uint64_t     base;
> +        MemoryRegion iomem;
> +
> +        struct {
> +            uint32_t enable;
> +            uint32_t mask;
> +            uint32_t status;
> +        } intr[1];
> +    } msi;

I think I didn't understand the msi struct above.
Is it some special msi implementation?
(related to the dumb question above)



> +} DesignwarePCIERoot;
> +
> +typedef struct DesignwarePCIEHost {
> +    PCIHostState parent_obj;
> +
> +    bool link_up;
> +
> +    DesignwarePCIERoot root;
> +
> +    struct {
> +        AddressSpace address_space;
> +        MemoryRegion address_space_root;
> +
> +        MemoryRegion memory;
> +        MemoryRegion io;
> +
> +        qemu_irq     irqs[4];
> +    } pci;
> +
> +    MemoryRegion mmio;
> +} DesignwarePCIEHost;
> +
> +#endif  /* DESIGNWARE_H */
> diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
> index 35df1874a9..23fefe1bc6 100644
> --- a/include/hw/pci/pci_ids.h
> +++ b/include/hw/pci/pci_ids.h
> @@ -266,4 +266,6 @@
>   #define PCI_VENDOR_ID_TEWS               0x1498
>   #define PCI_DEVICE_ID_TEWS_TPCI200       0x30C8
>   
> +#define PCI_VENDOR_ID_SYNOPSYS           0x16C3
> +
>   #endif


The host-bridge part looks OK to me, the Root Port part looks strange but it may

be because of the chipset.

Thanks,

Marcel

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware IP block
  2018-01-30 13:18   ` Marcel Apfelbaum
@ 2018-01-30 17:49     ` Andrey Smirnov
  2018-01-31 12:13       ` Marcel Apfelbaum
  0 siblings, 1 reply; 46+ messages in thread
From: Andrey Smirnov @ 2018-01-30 17:49 UTC (permalink / raw)
  To: marcel
  Cc: open list:ARM, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky

On Tue, Jan 30, 2018 at 5:18 AM, Marcel Apfelbaum
<marcel.apfelbaum@zoho.com> wrote:
> Hi Andrei,
>
> Sorry for letting you wait,
> I have some comments/questions below.
>
>
> On 16/01/2018 3:37, Andrey Smirnov wrote:
>>
>> Add code needed to get a functional PCI subsytem when using in
>> conjunction with upstream Linux guest (4.13+). Tested to work against
>> "e1000e" (network adapter, using MSI interrupts) as well as
>> "usb-ehci" (USB controller, using legacy PCI interrupts).
>>
>> Cc: Peter Maydell <peter.maydell@linaro.org>
>> Cc: Jason Wang <jasowang@redhat.com>
>> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
>> Cc: qemu-devel@nongnu.org
>> Cc: qemu-arm@nongnu.org
>> Cc: yurovsky@gmail.com
>> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
>> ---
>>   default-configs/arm-softmmu.mak  |   2 +
>>   hw/pci-host/Makefile.objs        |   2 +
>>   hw/pci-host/designware.c         | 618
>> +++++++++++++++++++++++++++++++++++++++
>>   include/hw/pci-host/designware.h |  93 ++++++
>>   include/hw/pci/pci_ids.h         |   2 +
>>   5 files changed, 717 insertions(+)
>>   create mode 100644 hw/pci-host/designware.c
>>   create mode 100644 include/hw/pci-host/designware.h
>>
>> diff --git a/default-configs/arm-softmmu.mak
>> b/default-configs/arm-softmmu.mak
>> index b0d6e65038..0c5ae914ed 100644
>> --- a/default-configs/arm-softmmu.mak
>> +++ b/default-configs/arm-softmmu.mak
>> @@ -132,3 +132,5 @@ CONFIG_GPIO_KEY=y
>>   CONFIG_MSF2=y
>>   CONFIG_FW_CFG_DMA=y
>>   CONFIG_XILINX_AXI=y
>> +CONFIG_PCI_DESIGNWARE=y
>> +
>> diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
>> index 9c7909cf44..0e2c0a123b 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..98fff5e5f3
>> --- /dev/null
>> +++ b/hw/pci-host/designware.c
>> @@ -0,0 +1,618 @@
>> +/*
>> + * Copyright (c) 2017, Impinj, Inc.
>
> 2018 :)
>
>> + *
>> + * 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 PCIE_PORT_LINK_CONTROL          0x710
>> +
>> +#define PCIE_PHY_DEBUG_R1               0x72C
>> +#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP  BIT(4)
>> +
>> +#define PCIE_LINK_WIDTH_SPEED_CONTROL   0x80C
>> +
>> +#define PCIE_MSI_ADDR_LO                0x820
>> +#define PCIE_MSI_ADDR_HI                0x824
>> +#define PCIE_MSI_INTR0_ENABLE           0x828
>> +#define PCIE_MSI_INTR0_MASK             0x82C
>> +#define PCIE_MSI_INTR0_STATUS           0x830
>> +
>> +#define PCIE_ATU_VIEWPORT               0x900
>> +#define PCIE_ATU_REGION_INBOUND         (0x1 << 31)
>> +#define PCIE_ATU_REGION_OUTBOUND        (0x0 << 31)
>> +#define PCIE_ATU_REGION_INDEX2          (0x2 << 0)
>> +#define PCIE_ATU_REGION_INDEX1          (0x1 << 0)
>> +#define PCIE_ATU_REGION_INDEX0          (0x0 << 0)
>> +#define PCIE_ATU_CR1                    0x904
>> +#define PCIE_ATU_TYPE_MEM               (0x0 << 0)
>> +#define PCIE_ATU_TYPE_IO                (0x2 << 0)
>> +#define PCIE_ATU_TYPE_CFG0              (0x4 << 0)
>> +#define PCIE_ATU_TYPE_CFG1              (0x5 << 0)
>> +#define PCIE_ATU_CR2                    0x908
>> +#define PCIE_ATU_ENABLE                 (0x1 << 31)
>> +#define PCIE_ATU_BAR_MODE_ENABLE        (0x1 << 30)
>> +#define PCIE_ATU_LOWER_BASE             0x90C
>> +#define PCIE_ATU_UPPER_BASE             0x910
>> +#define PCIE_ATU_LIMIT                  0x914
>> +#define PCIE_ATU_LOWER_TARGET           0x918
>> +#define PCIE_ATU_BUS(x)                 (((x) >> 24) & 0xff)
>> +#define PCIE_ATU_DEVFN(x)               (((x) >> 16) & 0xff)
>> +#define 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 |= (1 << 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);
>
>
> Sorry for the possibly dumb question, but "msi_write"
> will result in raising an INTx ?

Correct, that's the intention. I wouldn't be surprised if I missed a
better/canonical way to implement this.

>>
>> +    }
>> +}
>> +
>> +const MemoryRegionOps designware_pci_host_msi_ops = {
>> +    .write = designware_pcie_root_msi_write,
>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>
>
> You may want to limit the access size.

Good point, will do.

>>
>> +};
>> +
>> +static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot
>> *root)
>> +
>> +{
>> +    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
>> +    MemoryRegion *address_space = &host->pci.memory;
>> +    MemoryRegion *mem = &root->msi.iomem;
>> +    const uint64_t base = root->msi.base;
>> +    const bool enable = root->msi.intr[0].enable;
>> +
>> +    if (memory_region_is_mapped(mem)) {
>> +        memory_region_del_subregion(address_space, mem);
>> +        object_unparent(OBJECT(mem));
>> +    }
>> +
>> +    if (enable) {
>> +        memory_region_init_io(mem, OBJECT(root),
>> +                              &designware_pci_host_msi_ops,
>> +                              root, "pcie-msi", 0x1000);
>> +
>> +        memory_region_add_subregion(address_space, base, mem);
>
>
> What happens if is enabled twice?

Ideally this shouldn't happen since this function is only called when
PCIE_MSI_INTR0_ENABLE transitions from "All IRQs disabled" to "At
least one IRQ enabled" or the inverse (via "update_msi_mapping" in
designware_pcie_root_config_write).

But, assuming I got my logic wrong there, my thinking was that if it
gets enabled for the second time, first "if" statement in
designware_pcie_root_update_msi_mapping() would remove old
MemoryRegion and second one would add it back, so it end up being a
"no-op". I might be violating some API usage rules, so, please don't
hesitate to call me out on this if I do.

> Is it possible to be also disabled?
>

Yes, similar to the case above, except the "if (enabled)" is not going
to be executed and there'd be no MemoryRegion to trigger MSI
interrupt.

>
>> +    }
>> +}
>> +
>> +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 &
>> 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 PCIE_PORT_LINK_CONTROL:
>> +    case PCIE_LINK_WIDTH_SPEED_CONTROL:
>> +        val = 0xDEADBEEF;
>> +        /* No-op */
>
>
> Not really a no-op
>

What I meant by "no-op" is that those registers do not implement their
actual function and instead return obviously bogus value. I'll change
the comment to state that in a more explicit way.

>> +        break;
>> +
>> +    case PCIE_MSI_ADDR_LO:
>> +        val = root->msi.base;
>> +        break;
>> +
>> +    case PCIE_MSI_ADDR_HI:
>> +        val = root->msi.base >> 32;
>> +        break;
>> +
>> +    case PCIE_MSI_INTR0_ENABLE:
>> +        val = root->msi.intr[0].enable;
>> +        break;
>> +
>> +    case PCIE_MSI_INTR0_MASK:
>> +        val = root->msi.intr[0].mask;
>> +        break;
>> +
>> +    case PCIE_MSI_INTR0_STATUS:
>> +        val = root->msi.intr[0].status;
>> +        break;
>> +
>> +    case PCIE_PHY_DEBUG_R1:
>> +        val = PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
>> +        break;
>> +
>> +    case PCIE_ATU_VIEWPORT:
>> +        val = root->atu_viewport;
>> +        break;
>> +
>> +    case PCIE_ATU_LOWER_BASE:
>> +        val = viewport->base;
>> +        break;
>> +
>> +    case PCIE_ATU_UPPER_BASE:
>> +        val = viewport->base >> 32;
>> +        break;
>> +
>> +    case PCIE_ATU_LOWER_TARGET:
>> +        val = viewport->target;
>> +        break;
>> +
>> +    case PCIE_ATU_UPPER_TARGET:
>> +        val = viewport->target >> 32;
>> +        break;
>> +
>> +    case PCIE_ATU_LIMIT:
>> +        val = viewport->limit;
>> +        break;
>> +
>> +    case PCIE_ATU_CR1:
>> +    case PCIE_ATU_CR2:          /* FALLTHROUGH */
>> +        val = viewport->cr[(address - 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_read(void *opaque,
>> +                                               hwaddr addr, unsigned len)
>> +{
>> +    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
>> +    DesignwarePCIEViewport *viewport =
>> +        designware_pcie_root_get_current_viewport(root);
>> +
>> +    const uint8_t busnum = PCIE_ATU_BUS(viewport->target);
>> +    const uint8_t devfn  = 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_SPACE_SIZE - 1;
>> +
>> +        return pci_host_config_read_common(pcidev, addr,
>> +                                           PCI_CONFIG_SPACE_SIZE, len);
>> +    }
>
> You can use "pci_data_read" instead

Good to know, will change.

>>
>> +
>> +    return UINT64_MAX;
>> +}
>> +
>> +static void designware_pcie_root_data_write(void *opaque, hwaddr addr,
>> +                                            uint64_t val, unsigned len)
>> +{
>> +    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
>> +    DesignwarePCIEViewport *viewport =
>> +        designware_pcie_root_get_current_viewport(root);
>> +    const uint8_t busnum = PCIE_ATU_BUS(viewport->target);
>> +    const uint8_t devfn  = 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_SPACE_SIZE - 1;
>> +        pci_host_config_write_common(pcidev, addr,
>> +                                     PCI_CONFIG_SPACE_SIZE,
>> +                                     val, len);
>> +    }
>
> You can use pci_data_write instead.
>

Ditto.

>> +}
>> +
>> +const MemoryRegionOps designware_pci_host_conf_ops = {
>> +    .read = designware_pcie_root_data_read,
>> +    .write = designware_pcie_root_data_write,
>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>
>
> Maybe you want to limit the access size also here.
>

OK, will do.

>> +};
>> +
>> +static void designware_pcie_update_viewport(DesignwarePCIERoot *root,
>> +                                            DesignwarePCIEViewport
>> *viewport)
>> +{
>> +    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
>> +
>> +    MemoryRegion *mem     = &viewport->memory;
>> +    const uint64_t target = viewport->target;
>> +    const uint64_t base   = viewport->base;
>> +    const uint64_t size   = (uint64_t)viewport->limit - base + 1;
>> +    const bool inbound    = viewport->inbound;
>> +
>> +    MemoryRegion *source, *destination;
>> +    const char *direction;
>> +    char *name;
>> +
>> +    if (inbound) {
>> +        source      = &host->pci.address_space_root;
>> +        destination = get_system_memory();
>> +        direction   = "Inbound";
>> +    } else {
>> +        source      = get_system_memory();
>> +        destination = &host->pci.memory;
>> +        direction   = "Outbound";
>> +    }
>> +
>> +    if (memory_region_is_mapped(mem)) {
>> +        /* Before we modify anything, unmap and destroy the region */
>
>
> I saw this also before. Can you please explain a little
> why/when do you need to destroy prev mappings?
>

They are going to be updated every time a viewport (inbound or
outbound) in address translation unit (iATU) is reconfigured. Because
PCIE_ATU_*_TARGET register is used to configure which deivce/bus to
address outgoing configuration TLP to, they (viewports) get
reconfigured quite a bit. Corresponding functions in Linux kernel
would be dw_pcie_prog_outbound_atu() and dw_pcie_rd_other_conf(). I
wouldn't be surprised that the way I went about implementing it is far
from optimal, so let me know if it is.

>> +        memory_region_del_subregion(source, mem);
>> +        object_unparent(OBJECT(mem));
>> +    }
>> +
>> +    name = g_strdup_printf("PCI %s Viewport %p", direction, viewport);
>> +
>> +    switch (viewport->cr[0]) {
>> +    case PCIE_ATU_TYPE_MEM:
>> +        memory_region_init_alias(mem, OBJECT(root), name,
>> +                                 destination, target, size);
>> +        break;
>> +    case PCIE_ATU_TYPE_CFG0:
>> +    case PCIE_ATU_TYPE_CFG1:    /* FALLTHROUGH */
>> +        if (inbound) {
>> +            goto exit;
>> +        }
>> +
>> +        memory_region_init_io(mem, OBJECT(root),
>> +                              &designware_pci_host_conf_ops,
>> +                              root, name, size);
>> +        break;
>> +    }
>> +
>> +    if (inbound) {
>> +        memory_region_add_subregion_overlap(source, base,
>> +                                            mem, -1);
>> +    } else {
>> +        memory_region_add_subregion(source, base, mem);
>> +    }
>> +
>> + exit:
>> +    g_free(name);
>> +}
>> +
>> +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 PCIE_PORT_LINK_CONTROL:
>> +    case PCIE_LINK_WIDTH_SPEED_CONTROL:
>> +    case PCIE_PHY_DEBUG_R1:
>> +        /* No-op */
>> +        break;
>> +
>> +    case PCIE_MSI_ADDR_LO:
>> +        root->msi.base &= 0xFFFFFFFF00000000ULL;
>> +        root->msi.base |= val;
>> +        break;
>> +
>> +    case PCIE_MSI_ADDR_HI:
>> +        root->msi.base &= 0x00000000FFFFFFFFULL;
>> +        root->msi.base |= (uint64_t)val << 32;
>> +        break;
>> +
>> +    case 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 PCIE_MSI_INTR0_MASK:
>> +        root->msi.intr[0].mask = val;
>> +        break;
>> +
>> +    case 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 PCIE_ATU_VIEWPORT:
>> +        root->atu_viewport = val;
>> +        break;
>> +
>> +    case PCIE_ATU_LOWER_BASE:
>> +        viewport->base &= 0xFFFFFFFF00000000ULL;
>> +        viewport->base |= val;
>> +        break;
>> +
>> +    case PCIE_ATU_UPPER_BASE:
>> +        viewport->base &= 0x00000000FFFFFFFFULL;
>> +        viewport->base |= (uint64_t)val << 32;
>> +        break;
>> +
>> +    case PCIE_ATU_LOWER_TARGET:
>> +        viewport->target &= 0xFFFFFFFF00000000ULL;
>> +        viewport->target |= val;
>> +        break;
>> +
>> +    case PCIE_ATU_UPPER_TARGET:
>> +        viewport->target &= 0x00000000FFFFFFFFULL;
>> +        viewport->target |= val;
>> +        break;
>> +
>> +    case PCIE_ATU_LIMIT:
>> +        viewport->limit = val;
>> +        break;
>> +
>> +    case PCIE_ATU_CR1:
>> +        viewport->cr[0] = val;
>> +        break;
>> +    case PCIE_ATU_CR2:
>> +        viewport->cr[1] = val;
>> +
>> +        if (viewport->cr[1] & PCIE_ATU_ENABLE) {
>> +            designware_pcie_update_viewport(root, viewport);
>> +         }
>> +        break;
>> +
>> +    default:
>> +        pci_bridge_write_config(d, address, val, len);
>> +        break;
>> +    }
>> +}
>> +
>> +static int designware_pcie_root_init(PCIDevice *dev)
>> +{
>
>
> Please use "realize" function rather than init.
> We want to get rid of it eventually.

OK, will do.

>>
>> +    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev);
>> +    PCIBridge *br = PCI_BRIDGE(dev);
>> +    DesignwarePCIEViewport *viewport;
>> +    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_PCI_BUS);
>
> So this is a PCI Express Root Port "sitting" on PCI bus?

Yes, it is a built-in PCIe bridge, whose configuration space is mapped
into CPU's address space (designware_pci_host_conf_ops) and the rest
of PCIe hierarchy is presented through it.

>
>> +
>> +    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++) {
>> +        viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i];
>> +        viewport->inbound = true;
>> +    }
>> +
>> +    /*
>> +     * 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->base   = 0x0000000000000000ULL;
>> +    viewport->target = 0x0000000000000000ULL;
>> +    viewport->limit  = UINT32_MAX;
>> +    viewport->cr[0]  = PCIE_ATU_TYPE_MEM;
>> +
>> +    designware_pcie_update_viewport(root, viewport);
>> +
>> +    return 0;
>> +}
>> +
>> +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 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_HOST;
>
> So is a Root Port with call is "BRIDGE_HOST" ?
>

I think I am missing some PCI subsystem knowledge to understand that
question, would you mind re-phrasing it?

>> +    k->is_express = true;
>> +    k->is_bridge = true;
>> +    k->init = designware_pcie_root_init;
>> +    k->exit = pci_bridge_exitfn;
>> +    dc->reset = pci_bridge_reset;
>> +    k->config_read = designware_pcie_root_config_read;
>> +    k->config_write = designware_pcie_root_config_write;
>
>
> Please add category:
> set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);

It's already there, line 4 of the function.

>>
>> +
>> +    /*
>>
>> +     * 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;
>> +}
>> +
>> +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_NATIVE_ENDIAN,
>> +};
>> +
>> +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 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";
>> +}
>> +
>> +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)
>> +
>> +/* 00:00.0 Class 0604: 16c3:abcd */
>> diff --git a/include/hw/pci-host/designware.h
>> b/include/hw/pci-host/designware.h
>> new file mode 100644
>> index 0000000000..55e45fcba0
>> --- /dev/null
>> +++ b/include/hw/pci-host/designware.h
>> @@ -0,0 +1,93 @@
>> +/*
>> + * Copyright (c) 2017, 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/>.
>> + */
>> +
>> +#ifndef DESIGNWARE_H
>> +#define DESIGNWARE_H
>> +
>> +#include "hw/hw.h"
>> +#include "hw/sysbus.h"
>> +#include "hw/pci/pci.h"
>> +#include "hw/pci/pci_bus.h"
>> +#include "hw/pci/pcie_host.h"
>> +#include "hw/pci/pci_bridge.h"
>> +
>> +#define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host"
>> +#define DESIGNWARE_PCIE_HOST(obj) \
>> +     OBJECT_CHECK(DesignwarePCIEHost, (obj), TYPE_DESIGNWARE_PCIE_HOST)
>> +
>> +#define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root"
>> +#define DESIGNWARE_PCIE_ROOT(obj) \
>> +     OBJECT_CHECK(DesignwarePCIERoot, (obj), TYPE_DESIGNWARE_PCIE_ROOT)
>> +
>> +typedef struct DesignwarePCIEViewport {
>> +    MemoryRegion memory;
>> +
>> +    uint64_t base;
>> +    uint64_t target;
>> +    uint32_t limit;
>> +    uint32_t cr[2];
>> +
>> +    bool inbound;
>> +} DesignwarePCIEViewport;
>> +
>> +typedef struct DesignwarePCIERoot {
>> +    PCIBridge parent_obj;
>> +
>> +    uint32_t atu_viewport;
>> +
>> +#define DESIGNWARE_PCIE_VIEWPORT_OUTBOUND    0
>> +#define DESIGNWARE_PCIE_VIEWPORT_INBOUND     1
>> +#define DESIGNWARE_PCIE_NUM_VIEWPORTS        4
>> +
>> +    DesignwarePCIEViewport viewports[2][DESIGNWARE_PCIE_NUM_VIEWPORTS];
>> +
>> +    struct {
>> +        uint64_t     base;
>> +        MemoryRegion iomem;
>> +
>> +        struct {
>> +            uint32_t enable;
>> +            uint32_t mask;
>> +            uint32_t status;
>> +        } intr[1];
>> +    } msi;
>
>
> I think I didn't understand the msi struct above.
> Is it some special msi implementation?
> (related to the dumb question above)

Yes, it represents Designware specific MSI registers (part of a block
of vendor specific registers in configuration space of the root PCIe
bridge) as follows:

 - PCIE_PL_MSICA, PCIE_PL_MSICUA via msi.base
 - PCIE_PL_MSICI0_ENB via msi.intr[0].enable
 - PCIE_PL_MSICI0_MASK via msi.intr[0].mask
 -  PCIE_PL_MSICI0_STATUS via msi.intr[0].status

i.MX6/7 specifies 8 sets of PCIE_PL_MSICn_* registers, so I defined it
as an array, but since Linux only uses first set I limited the number
of elements to 1.

Thanks,
Andrey Smirnov

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware IP block
  2018-01-30 17:49     ` Andrey Smirnov
@ 2018-01-31 12:13       ` Marcel Apfelbaum
  2018-02-07  4:10         ` Andrey Smirnov
  0 siblings, 1 reply; 46+ messages in thread
From: Marcel Apfelbaum @ 2018-01-31 12:13 UTC (permalink / raw)
  To: Andrey Smirnov
  Cc: open list:ARM, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky

On 30/01/2018 19:49, Andrey Smirnov wrote:
> On Tue, Jan 30, 2018 at 5:18 AM, Marcel Apfelbaum
> <marcel.apfelbaum@zoho.com> wrote:
>> Hi Andrei,
>>
>> Sorry for letting you wait,
>> I have some comments/questions below.
>>
>>
>> On 16/01/2018 3:37, Andrey Smirnov wrote:
>>>
>>> Add code needed to get a functional PCI subsytem when using in
>>> conjunction with upstream Linux guest (4.13+). Tested to work against
>>> "e1000e" (network adapter, using MSI interrupts) as well as
>>> "usb-ehci" (USB controller, using legacy PCI interrupts).
>>>
>>> Cc: Peter Maydell <peter.maydell@linaro.org>
>>> Cc: Jason Wang <jasowang@redhat.com>
>>> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
>>> Cc: qemu-devel@nongnu.org
>>> Cc: qemu-arm@nongnu.org
>>> Cc: yurovsky@gmail.com
>>> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
>>> ---
>>>   default-configs/arm-softmmu.mak  |   2 +
>>>   hw/pci-host/Makefile.objs        |   2 +
>>>   hw/pci-host/designware.c         | 618
>>> +++++++++++++++++++++++++++++++++++++++
>>>   include/hw/pci-host/designware.h |  93 ++++++
>>>   include/hw/pci/pci_ids.h         |   2 +
>>>   5 files changed, 717 insertions(+)
>>>   create mode 100644 hw/pci-host/designware.c
>>>   create mode 100644 include/hw/pci-host/designware.h
>>>
>>> diff --git a/default-configs/arm-softmmu.mak
>>> b/default-configs/arm-softmmu.mak
>>> index b0d6e65038..0c5ae914ed 100644
>>> --- a/default-configs/arm-softmmu.mak
>>> +++ b/default-configs/arm-softmmu.mak
>>> @@ -132,3 +132,5 @@ CONFIG_GPIO_KEY=y
>>>   CONFIG_MSF2=y
>>>   CONFIG_FW_CFG_DMA=y
>>>   CONFIG_XILINX_AXI=y
>>> +CONFIG_PCI_DESIGNWARE=y
>>> +
>>> diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
>>> index 9c7909cf44..0e2c0a123b 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..98fff5e5f3
>>> --- /dev/null
>>> +++ b/hw/pci-host/designware.c
>>> @@ -0,0 +1,618 @@
>>> +/*
>>> + * Copyright (c) 2017, Impinj, Inc.
>>
>> 2018 :)
>>
>>> + *
>>> + * 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 PCIE_PORT_LINK_CONTROL          0x710
>>> +
>>> +#define PCIE_PHY_DEBUG_R1               0x72C
>>> +#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP  BIT(4)
>>> +
>>> +#define PCIE_LINK_WIDTH_SPEED_CONTROL   0x80C
>>> +
>>> +#define PCIE_MSI_ADDR_LO                0x820
>>> +#define PCIE_MSI_ADDR_HI                0x824
>>> +#define PCIE_MSI_INTR0_ENABLE           0x828
>>> +#define PCIE_MSI_INTR0_MASK             0x82C
>>> +#define PCIE_MSI_INTR0_STATUS           0x830
>>> +
>>> +#define PCIE_ATU_VIEWPORT               0x900
>>> +#define PCIE_ATU_REGION_INBOUND         (0x1 << 31)
>>> +#define PCIE_ATU_REGION_OUTBOUND        (0x0 << 31)
>>> +#define PCIE_ATU_REGION_INDEX2          (0x2 << 0)
>>> +#define PCIE_ATU_REGION_INDEX1          (0x1 << 0)
>>> +#define PCIE_ATU_REGION_INDEX0          (0x0 << 0)
>>> +#define PCIE_ATU_CR1                    0x904
>>> +#define PCIE_ATU_TYPE_MEM               (0x0 << 0)
>>> +#define PCIE_ATU_TYPE_IO                (0x2 << 0)
>>> +#define PCIE_ATU_TYPE_CFG0              (0x4 << 0)
>>> +#define PCIE_ATU_TYPE_CFG1              (0x5 << 0)
>>> +#define PCIE_ATU_CR2                    0x908
>>> +#define PCIE_ATU_ENABLE                 (0x1 << 31)
>>> +#define PCIE_ATU_BAR_MODE_ENABLE        (0x1 << 30)
>>> +#define PCIE_ATU_LOWER_BASE             0x90C
>>> +#define PCIE_ATU_UPPER_BASE             0x910
>>> +#define PCIE_ATU_LIMIT                  0x914
>>> +#define PCIE_ATU_LOWER_TARGET           0x918
>>> +#define PCIE_ATU_BUS(x)                 (((x) >> 24) & 0xff)
>>> +#define PCIE_ATU_DEVFN(x)               (((x) >> 16) & 0xff)
>>> +#define 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 |= (1 << 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);
>>
>>
>> Sorry for the possibly dumb question, but "msi_write"
>> will result in raising an INTx ?
> 
> Correct, that's the intention. I wouldn't be surprised if I missed a
> better/canonical way to implement this.
> 

Not sure of a "better" way. The point was the "msi" naming
that suggests edge interrupts, while resulting into level interrupts.
I was wondering about the naming, nothing more.

>>>
>>> +    }
>>> +}
>>> +
>>> +const MemoryRegionOps designware_pci_host_msi_ops = {
>>> +    .write = designware_pcie_root_msi_write,
>>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>>
>>
>> You may want to limit the access size.
> 
> Good point, will do.
> 
>>>
>>> +};
>>> +
>>> +static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot
>>> *root)
>>> +
>>> +{
>>> +    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
>>> +    MemoryRegion *address_space = &host->pci.memory;
>>> +    MemoryRegion *mem = &root->msi.iomem;
>>> +    const uint64_t base = root->msi.base;
>>> +    const bool enable = root->msi.intr[0].enable;
>>> +
>>> +    if (memory_region_is_mapped(mem)) {
>>> +        memory_region_del_subregion(address_space, mem);
>>> +        object_unparent(OBJECT(mem));
>>> +    }
>>> +
>>> +    if (enable) {
>>> +        memory_region_init_io(mem, OBJECT(root),
>>> +                              &designware_pci_host_msi_ops,
>>> +                              root, "pcie-msi", 0x1000);
>>> +
>>> +        memory_region_add_subregion(address_space, base, mem);
>>
>>
>> What happens if is enabled twice?
> 
> Ideally this shouldn't happen since this function is only called when
> PCIE_MSI_INTR0_ENABLE transitions from "All IRQs disabled" to "At
> least one IRQ enabled" or the inverse (via "update_msi_mapping" in
> designware_pcie_root_config_write).
> 
> But, assuming I got my logic wrong there, my thinking was that if it
> gets enabled for the second time, first "if" statement in
> designware_pcie_root_update_msi_mapping() would remove old
> MemoryRegion and second one would add it back, so it end up being a
> "no-op". I might be violating some API usage rules, so, please don't
> hesitate to call me out on this if I do.
> 

OK, so I am pretty sure we call "memory_region_init_io" only once
in the init/realize part.

Then, if the address mappings is getting changed between the calls
you can use memory_region_del_subregion/memory_region_add_subregion on update.

If the mappings remains the same you can use memory_region_set_enabled
to disable/enable the memory region.

>> Is it possible to be also disabled?
>>
> 
> Yes, similar to the case above, except the "if (enabled)" is not going
> to be executed and there'd be no MemoryRegion to trigger MSI
> interrupt.
> 
>>
>>> +    }
>>> +}
>>> +
>>> +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 &
>>> 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 PCIE_PORT_LINK_CONTROL:
>>> +    case PCIE_LINK_WIDTH_SPEED_CONTROL:
>>> +        val = 0xDEADBEEF;
>>> +        /* No-op */
>>
>>
>> Not really a no-op
>>
> 
> What I meant by "no-op" is that those registers do not implement their
> actual function and instead return obviously bogus value. I'll change
> the comment to state that in a more explicit way.
> 
>>> +        break;
>>> +
>>> +    case PCIE_MSI_ADDR_LO:
>>> +        val = root->msi.base;
>>> +        break;
>>> +
>>> +    case PCIE_MSI_ADDR_HI:
>>> +        val = root->msi.base >> 32;
>>> +        break;
>>> +
>>> +    case PCIE_MSI_INTR0_ENABLE:
>>> +        val = root->msi.intr[0].enable;
>>> +        break;
>>> +
>>> +    case PCIE_MSI_INTR0_MASK:
>>> +        val = root->msi.intr[0].mask;
>>> +        break;
>>> +
>>> +    case PCIE_MSI_INTR0_STATUS:
>>> +        val = root->msi.intr[0].status;
>>> +        break;
>>> +
>>> +    case PCIE_PHY_DEBUG_R1:
>>> +        val = PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_VIEWPORT:
>>> +        val = root->atu_viewport;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_LOWER_BASE:
>>> +        val = viewport->base;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_UPPER_BASE:
>>> +        val = viewport->base >> 32;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_LOWER_TARGET:
>>> +        val = viewport->target;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_UPPER_TARGET:
>>> +        val = viewport->target >> 32;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_LIMIT:
>>> +        val = viewport->limit;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_CR1:
>>> +    case PCIE_ATU_CR2:          /* FALLTHROUGH */
>>> +        val = viewport->cr[(address - 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_read(void *opaque,
>>> +                                               hwaddr addr, unsigned len)
>>> +{
>>> +    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
>>> +    DesignwarePCIEViewport *viewport =
>>> +        designware_pcie_root_get_current_viewport(root);
>>> +
>>> +    const uint8_t busnum = PCIE_ATU_BUS(viewport->target);
>>> +    const uint8_t devfn  = 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_SPACE_SIZE - 1;
>>> +
>>> +        return pci_host_config_read_common(pcidev, addr,
>>> +                                           PCI_CONFIG_SPACE_SIZE, len);
>>> +    }
>>
>> You can use "pci_data_read" instead
> 
> Good to know, will change.
> 
>>>
>>> +
>>> +    return UINT64_MAX;
>>> +}
>>> +
>>> +static void designware_pcie_root_data_write(void *opaque, hwaddr addr,
>>> +                                            uint64_t val, unsigned len)
>>> +{
>>> +    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
>>> +    DesignwarePCIEViewport *viewport =
>>> +        designware_pcie_root_get_current_viewport(root);
>>> +    const uint8_t busnum = PCIE_ATU_BUS(viewport->target);
>>> +    const uint8_t devfn  = 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_SPACE_SIZE - 1;
>>> +        pci_host_config_write_common(pcidev, addr,
>>> +                                     PCI_CONFIG_SPACE_SIZE,
>>> +                                     val, len);
>>> +    }
>>
>> You can use pci_data_write instead.
>>
> 
> Ditto.
> 
>>> +}
>>> +
>>> +const MemoryRegionOps designware_pci_host_conf_ops = {
>>> +    .read = designware_pcie_root_data_read,
>>> +    .write = designware_pcie_root_data_write,
>>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>>
>>
>> Maybe you want to limit the access size also here.
>>
> 
> OK, will do.
> 
>>> +};
>>> +
>>> +static void designware_pcie_update_viewport(DesignwarePCIERoot *root,
>>> +                                            DesignwarePCIEViewport
>>> *viewport)
>>> +{
>>> +    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
>>> +
>>> +    MemoryRegion *mem     = &viewport->memory;
>>> +    const uint64_t target = viewport->target;
>>> +    const uint64_t base   = viewport->base;
>>> +    const uint64_t size   = (uint64_t)viewport->limit - base + 1;
>>> +    const bool inbound    = viewport->inbound;
>>> +
>>> +    MemoryRegion *source, *destination;
>>> +    const char *direction;
>>> +    char *name;
>>> +
>>> +    if (inbound) {
>>> +        source      = &host->pci.address_space_root;
>>> +        destination = get_system_memory();
>>> +        direction   = "Inbound";
>>> +    } else {
>>> +        source      = get_system_memory();
>>> +        destination = &host->pci.memory;
>>> +        direction   = "Outbound";
>>> +    }
>>> +
>>> +    if (memory_region_is_mapped(mem)) {
>>> +        /* Before we modify anything, unmap and destroy the region */
>>
>>
>> I saw this also before. Can you please explain a little
>> why/when do you need to destroy prev mappings?
>>
> 
> They are going to be updated every time a viewport (inbound or
> outbound) in address translation unit (iATU) is reconfigured. Because
> PCIE_ATU_*_TARGET register is used to configure which deivce/bus to
> address outgoing configuration TLP to, they (viewports) get
> reconfigured quite a bit. Corresponding functions in Linux kernel
> would be dw_pcie_prog_outbound_atu() and dw_pcie_rd_other_conf(). I
> wouldn't be surprised that the way I went about implementing it is far
> from optimal, so let me know if it is.
> 

The same as above, I think memory_region_init_io should be used once.


>>> +        memory_region_del_subregion(source, mem);
>>> +        object_unparent(OBJECT(mem));
>>> +    }
>>> +
>>> +    name = g_strdup_printf("PCI %s Viewport %p", direction, viewport);
>>> +
>>> +    switch (viewport->cr[0]) {
>>> +    case PCIE_ATU_TYPE_MEM:
>>> +        memory_region_init_alias(mem, OBJECT(root), name,
>>> +                                 destination, target, size);
>>> +        break;
>>> +    case PCIE_ATU_TYPE_CFG0:
>>> +    case PCIE_ATU_TYPE_CFG1:    /* FALLTHROUGH */
>>> +        if (inbound) {
>>> +            goto exit;
>>> +        }
>>> +
>>> +        memory_region_init_io(mem, OBJECT(root),
>>> +                              &designware_pci_host_conf_ops,
>>> +                              root, name, size);
>>> +        break;
>>> +    }
>>> +
>>> +    if (inbound) {
>>> +        memory_region_add_subregion_overlap(source, base,
>>> +                                            mem, -1);
>>> +    } else {
>>> +        memory_region_add_subregion(source, base, mem);
>>> +    }
>>> +
>>> + exit:
>>> +    g_free(name);
>>> +}
>>> +
>>> +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 PCIE_PORT_LINK_CONTROL:
>>> +    case PCIE_LINK_WIDTH_SPEED_CONTROL:
>>> +    case PCIE_PHY_DEBUG_R1:
>>> +        /* No-op */
>>> +        break;
>>> +
>>> +    case PCIE_MSI_ADDR_LO:
>>> +        root->msi.base &= 0xFFFFFFFF00000000ULL;
>>> +        root->msi.base |= val;
>>> +        break;
>>> +
>>> +    case PCIE_MSI_ADDR_HI:
>>> +        root->msi.base &= 0x00000000FFFFFFFFULL;
>>> +        root->msi.base |= (uint64_t)val << 32;
>>> +        break;
>>> +
>>> +    case 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 PCIE_MSI_INTR0_MASK:
>>> +        root->msi.intr[0].mask = val;
>>> +        break;
>>> +
>>> +    case 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 PCIE_ATU_VIEWPORT:
>>> +        root->atu_viewport = val;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_LOWER_BASE:
>>> +        viewport->base &= 0xFFFFFFFF00000000ULL;
>>> +        viewport->base |= val;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_UPPER_BASE:
>>> +        viewport->base &= 0x00000000FFFFFFFFULL;
>>> +        viewport->base |= (uint64_t)val << 32;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_LOWER_TARGET:
>>> +        viewport->target &= 0xFFFFFFFF00000000ULL;
>>> +        viewport->target |= val;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_UPPER_TARGET:
>>> +        viewport->target &= 0x00000000FFFFFFFFULL;
>>> +        viewport->target |= val;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_LIMIT:
>>> +        viewport->limit = val;
>>> +        break;
>>> +
>>> +    case PCIE_ATU_CR1:
>>> +        viewport->cr[0] = val;
>>> +        break;
>>> +    case PCIE_ATU_CR2:
>>> +        viewport->cr[1] = val;
>>> +
>>> +        if (viewport->cr[1] & PCIE_ATU_ENABLE) {
>>> +            designware_pcie_update_viewport(root, viewport);
>>> +         }
>>> +        break;
>>> +
>>> +    default:
>>> +        pci_bridge_write_config(d, address, val, len);
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static int designware_pcie_root_init(PCIDevice *dev)
>>> +{
>>
>>
>> Please use "realize" function rather than init.
>> We want to get rid of it eventually.
> 
> OK, will do.
> 
>>>
>>> +    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev);
>>> +    PCIBridge *br = PCI_BRIDGE(dev);
>>> +    DesignwarePCIEViewport *viewport;
>>> +    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_PCI_BUS);
>>
>> So this is a PCI Express Root Port "sitting" on PCI bus?
> 
> Yes, it is a built-in PCIe bridge, whose configuration space is mapped
> into CPU's address space (designware_pci_host_conf_ops) and the rest
> of PCIe hierarchy is presented through it.

My point was: is the bus PCIe or PCI? Because you used:
  pci_bridge_initfn(dev, TYPE_PCI_BUS);
and not
  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++) {
>>> +        viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i];
>>> +        viewport->inbound = true;
>>> +    }
>>> +
>>> +    /*
>>> +     * 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->base   = 0x0000000000000000ULL;
>>> +    viewport->target = 0x0000000000000000ULL;
>>> +    viewport->limit  = UINT32_MAX;
>>> +    viewport->cr[0]  = PCIE_ATU_TYPE_MEM;
>>> +
>>> +    designware_pcie_update_viewport(root, viewport);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +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 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_HOST;
>>
>> So is a Root Port with call is "BRIDGE_HOST" ?
>>
> 
> I think I am missing some PCI subsystem knowledge to understand that
> question, would you mind re-phrasing it?

Sure, a Root Port is a type of PCI bridge, so I was expecting
the class id to be: PCI_CLASS_BRIDGE_PCI and not PCI_CLASS_BRIDGE_HOST.


> 
>>> +    k->is_express = true;
>>> +    k->is_bridge = true;
>>> +    k->init = designware_pcie_root_init;
>>> +    k->exit = pci_bridge_exitfn;
>>> +    dc->reset = pci_bridge_reset;
>>> +    k->config_read = designware_pcie_root_config_read;
>>> +    k->config_write = designware_pcie_root_config_write;
>>
>>
>> Please add category:
>> set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> 
> It's already there, line 4 of the function.
> 

Sorry for the noise

>>>
>>> +
>>> +    /*
>>>
>>> +     * 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;
>>> +}
>>> +
>>> +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_NATIVE_ENDIAN,
>>> +};
>>> +
>>> +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 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";
>>> +}
>>> +
>>> +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)
>>> +
>>> +/* 00:00.0 Class 0604: 16c3:abcd */
>>> diff --git a/include/hw/pci-host/designware.h
>>> b/include/hw/pci-host/designware.h
>>> new file mode 100644
>>> index 0000000000..55e45fcba0
>>> --- /dev/null
>>> +++ b/include/hw/pci-host/designware.h
>>> @@ -0,0 +1,93 @@
>>> +/*
>>> + * Copyright (c) 2017, 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/>.
>>> + */
>>> +
>>> +#ifndef DESIGNWARE_H
>>> +#define DESIGNWARE_H
>>> +
>>> +#include "hw/hw.h"
>>> +#include "hw/sysbus.h"
>>> +#include "hw/pci/pci.h"
>>> +#include "hw/pci/pci_bus.h"
>>> +#include "hw/pci/pcie_host.h"
>>> +#include "hw/pci/pci_bridge.h"
>>> +
>>> +#define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host"
>>> +#define DESIGNWARE_PCIE_HOST(obj) \
>>> +     OBJECT_CHECK(DesignwarePCIEHost, (obj), TYPE_DESIGNWARE_PCIE_HOST)
>>> +
>>> +#define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root"
>>> +#define DESIGNWARE_PCIE_ROOT(obj) \
>>> +     OBJECT_CHECK(DesignwarePCIERoot, (obj), TYPE_DESIGNWARE_PCIE_ROOT)
>>> +
>>> +typedef struct DesignwarePCIEViewport {
>>> +    MemoryRegion memory;
>>> +
>>> +    uint64_t base;
>>> +    uint64_t target;
>>> +    uint32_t limit;
>>> +    uint32_t cr[2];
>>> +
>>> +    bool inbound;
>>> +} DesignwarePCIEViewport;
>>> +
>>> +typedef struct DesignwarePCIERoot {
>>> +    PCIBridge parent_obj;
>>> +
>>> +    uint32_t atu_viewport;
>>> +
>>> +#define DESIGNWARE_PCIE_VIEWPORT_OUTBOUND    0
>>> +#define DESIGNWARE_PCIE_VIEWPORT_INBOUND     1
>>> +#define DESIGNWARE_PCIE_NUM_VIEWPORTS        4
>>> +
>>> +    DesignwarePCIEViewport viewports[2][DESIGNWARE_PCIE_NUM_VIEWPORTS];
>>> +
>>> +    struct {
>>> +        uint64_t     base;
>>> +        MemoryRegion iomem;
>>> +
>>> +        struct {
>>> +            uint32_t enable;
>>> +            uint32_t mask;
>>> +            uint32_t status;
>>> +        } intr[1];
>>> +    } msi;
>>
>>
>> I think I didn't understand the msi struct above.
>> Is it some special msi implementation?
>> (related to the dumb question above)
> 
> Yes, it represents Designware specific MSI registers (part of a block
> of vendor specific registers in configuration space of the root PCIe
> bridge) as follows:
> 
>  - PCIE_PL_MSICA, PCIE_PL_MSICUA via msi.base
>  - PCIE_PL_MSICI0_ENB via msi.intr[0].enable
>  - PCIE_PL_MSICI0_MASK via msi.intr[0].mask
>  -  PCIE_PL_MSICI0_STATUS via msi.intr[0].status
> 
> i.MX6/7 specifies 8 sets of PCIE_PL_MSICn_* registers, so I defined it
> as an array, but since Linux only uses first set I limited the number
> of elements to 1.
> 

Got it, thanks.
Marcel

> Thanks,
> Andrey Smirnov
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support
  2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
                   ` (14 preceding siblings ...)
  2018-01-16 15:08 ` [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Peter Maydell
@ 2018-01-31 17:03 ` Philippe Mathieu-Daudé
  2018-02-07  3:59   ` Andrey Smirnov
  15 siblings, 1 reply; 46+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-01-31 17:03 UTC (permalink / raw)
  To: Andrey Smirnov, Peter Maydell; +Cc: qemu-arm, Jason Wang, qemu-devel, yurovsky

Hi Peter, Andrey.

On 01/15/2018 10:36 PM, Andrey Smirnov wrote:
> Hi everyone,
> 
> This v4 of the patch series containing the work that I've done in
> order to enable support for i.MX7 emulation in QEMU.
> 
> *NOTE*: Patches 1 and 2 are provided for the sake of completness and
> 	are going to have to be adapted once Philippe's SD changes
> 	land in master. As such, they are NOT ready to be
> 	accepted/merged.

Peter:
Since my series are taking longer, if this series is ready it is
probably easier to apply Andrey series first and I'll adapt my SDHCI
series after.

Andrey:
I only plan to keep the sdhci.c file generic (dealing with quirks) and
split out the imx usdhci code, similar to this patch:
https://lists.gnu.org/archive/html/qemu-devel/2018-01/msg01265.html

> 
> As the one before last commit in the series states the supported i.MX7
> features are:
> 
>     * up to 2 Cortex A9 cores (SMP works with PSCI)
>     * A7 MPCORE (identical to A15 MPCORE)
>     * 4 GPTs modules
>     * 7 GPIO controllers
>     * 2 IOMUXC controllers
>     * 1 CCM module
>     * 1 SVNS module
>     * 1 SRC module
>     * 1 GPCv2 controller
>     * 4 eCSPI controllers
>     * 4 I2C controllers
>     * 7 i.MX UART controllers
>     * 2 FlexCAN controllers
>     * 2 Ethernet controllers (FEC)
>     * 3 SD controllers (USDHC)
>     * 4 WDT modules
>     * 1 SDMA module
>     * 1 GPR module
>     * 2 USBMISC modules
>     * 2 ADC modules
>     * 1 PCIe controller
>     * 3 USB controllers
>     * 1 LCD controller
>     * 1 ARMv7 DAP IP block
> 
> Feedback is welcome!
> 
> Changes since [v3]:
> 
>     - Changes to FEC were split into a separate set and merged to master
> 
>     - Patchest is rebased on latest master
> 
>     - Converted to use PSCI DT fixup code that is shared with virt
>       platform (now relocated to live in arm/boot.c)
> 
>     - Large number of dummy block were converted to use
>       create_unimplemented_device() as opposed to its own dedicated
>       type
> 
>     - Incorporated varios small feedback items
> 
>     - Collected Reviewed-by tags from Peter
> 
> Changes since [v2]:
> 
>     - Added stubs for more blocks that were causing memory
>       transactions when booting Linux guest as were revealed by
>       additional testing of the patchest
> 
>     - Added proper USB emulation code, so now it should be possible to
>       emulated guest's USB bus
> 
> Changes since [v1]:
> 
>     - Patchset no longer relies on "ignore_memory_transaction_failures = false"
>       for its functionality
> 
>     - As a consequnce of implementing the above a number of patches
>       implementing dummy IP block emulation as well as PCIe emulation
>       patches that I alluded to in [v1] are now included in this patch
>       series
> 
>     - "has_el3" property is no longer being set to "false" as a part
>       of intialization of A7 CPU. I couldn't reproduce the issues that
>       I thought I was having, so I just dropped that code.
> 
>     - A number of smaller feedback items from Peter and other has been
>       incorporated into the patches.
> 
> 
> Thanks,
> Andrey Smirnov
> 
> [v3] https://lists.gnu.org/archive/html/qemu-devel/2017-11/msg04236.html
> [v2] https://lists.gnu.org/archive/html/qemu-devel/2017-10/msg05516.html
> [v1] https://lists.gnu.org/archive/html/qemu-devel/2017-09/msg04770.html
> 
> Andrey Smirnov (14):
>   sdhci: Add i.MX specific subtype of SDHCI
>   hw: i.MX: Convert i.MX6 to use TYPE_IMX_USDHC
>   i.MX: Add code to emulate i.MX7 CCM, PMU and ANALOG IP blocks
>   i.MX: Add code to emulate i.MX2 watchdog IP block
>   i.MX: Add code to emulate i.MX7 SNVS IP-block
>   i.MX: Add code to emulate GPCv2 IP block
>   i.MX: Add i.MX7 GPT variant
>   i.MX: Add implementation of i.MX7 GPR IP block
>   pci: Add support for Designware IP block
>   usb: Add basic code to emulate Chipidea USB IP
>   ARM: Add basic code to emulate A7MPCore DAP block
>   i.MX: Add i.MX7 SOC implementation.
>   hw/arm: Move virt's PSCI DT fixup code to arm/boot.c
>   Implement support for i.MX7 Sabre board
> 
>  default-configs/arm-softmmu.mak  |   3 +
>  hw/arm/Makefile.objs             |   5 +-
>  hw/arm/boot.c                    |  65 ++++
>  hw/arm/coresight.c               | 120 ++++++++
>  hw/arm/fsl-imx6.c                |   2 +-
>  hw/arm/fsl-imx7.c                | 583 ++++++++++++++++++++++++++++++++++++
>  hw/arm/mcimx7d-sabre.c           |  90 ++++++
>  hw/arm/virt.c                    |  61 ----
>  hw/intc/Makefile.objs            |   2 +-
>  hw/intc/imx_gpcv2.c              | 125 ++++++++
>  hw/misc/Makefile.objs            |   4 +
>  hw/misc/imx2_wdt.c               |  89 ++++++
>  hw/misc/imx7_ccm.c               | 277 ++++++++++++++++++
>  hw/misc/imx7_gpr.c               | 119 ++++++++
>  hw/misc/imx7_snvs.c              |  83 ++++++
>  hw/pci-host/Makefile.objs        |   2 +
>  hw/pci-host/designware.c         | 618 +++++++++++++++++++++++++++++++++++++++
>  hw/sd/sdhci-internal.h           |  19 ++
>  hw/sd/sdhci.c                    | 228 ++++++++++++++-
>  hw/timer/imx_gpt.c               |  25 ++
>  hw/usb/Makefile.objs             |   1 +
>  hw/usb/chipidea.c                | 176 +++++++++++
>  include/hw/arm/coresight.h       |  24 ++
>  include/hw/arm/fsl-imx7.h        | 223 ++++++++++++++
>  include/hw/intc/imx_gpcv2.h      |  22 ++
>  include/hw/misc/imx2_wdt.h       |  33 +++
>  include/hw/misc/imx7_ccm.h       | 139 +++++++++
>  include/hw/misc/imx7_gpr.h       |  28 ++
>  include/hw/misc/imx7_snvs.h      |  35 +++
>  include/hw/pci-host/designware.h |  93 ++++++
>  include/hw/pci/pci_ids.h         |   2 +
>  include/hw/sd/sdhci.h            |  14 +
>  include/hw/timer/imx_gpt.h       |   1 +
>  include/hw/usb/chipidea.h        |  16 +
>  34 files changed, 3261 insertions(+), 66 deletions(-)
>  create mode 100644 hw/arm/coresight.c
>  create mode 100644 hw/arm/fsl-imx7.c
>  create mode 100644 hw/arm/mcimx7d-sabre.c
>  create mode 100644 hw/intc/imx_gpcv2.c
>  create mode 100644 hw/misc/imx2_wdt.c
>  create mode 100644 hw/misc/imx7_ccm.c
>  create mode 100644 hw/misc/imx7_gpr.c
>  create mode 100644 hw/misc/imx7_snvs.c
>  create mode 100644 hw/pci-host/designware.c
>  create mode 100644 hw/usb/chipidea.c
>  create mode 100644 include/hw/arm/coresight.h
>  create mode 100644 include/hw/arm/fsl-imx7.h
>  create mode 100644 include/hw/intc/imx_gpcv2.h
>  create mode 100644 include/hw/misc/imx2_wdt.h
>  create mode 100644 include/hw/misc/imx7_ccm.h
>  create mode 100644 include/hw/misc/imx7_gpr.h
>  create mode 100644 include/hw/misc/imx7_snvs.h
>  create mode 100644 include/hw/pci-host/designware.h
>  create mode 100644 include/hw/usb/chipidea.h
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 02/14] hw: i.MX: Convert i.MX6 to use TYPE_IMX_USDHC
  2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 02/14] hw: i.MX: Convert i.MX6 to use TYPE_IMX_USDHC Andrey Smirnov
@ 2018-01-31 17:04   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 46+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-01-31 17:04 UTC (permalink / raw)
  To: Andrey Smirnov, qemu-arm; +Cc: Peter Maydell, Jason Wang, qemu-devel, yurovsky

On 01/15/2018 10:36 PM, Andrey Smirnov wrote:
> Convert i.MX6 to use TYPE_IMX_USDHC since that's what real HW comes
> with.
> 
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> ---
>  hw/arm/fsl-imx6.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c
> index b0d4088290..e6559a8b12 100644
> --- a/hw/arm/fsl-imx6.c
> +++ b/hw/arm/fsl-imx6.c
> @@ -93,7 +93,7 @@ static void fsl_imx6_init(Object *obj)
>      }
>  
>      for (i = 0; i < FSL_IMX6_NUM_ESDHCS; i++) {
> -        object_initialize(&s->esdhc[i], sizeof(s->esdhc[i]), TYPE_SYSBUS_SDHCI);
> +        object_initialize(&s->esdhc[i], sizeof(s->esdhc[i]), TYPE_IMX_USDHC);
>          qdev_set_parent_bus(DEVICE(&s->esdhc[i]), sysbus_get_default());
>          snprintf(name, NAME_SIZE, "sdhc%d", i + 1);
>          object_property_add_child(obj, name, OBJECT(&s->esdhc[i]), NULL);
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 04/14] i.MX: Add code to emulate i.MX2 watchdog IP block
  2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 04/14] i.MX: Add code to emulate i.MX2 watchdog IP block Andrey Smirnov
@ 2018-01-31 17:07   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 46+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-01-31 17:07 UTC (permalink / raw)
  To: Andrey Smirnov, qemu-arm; +Cc: Peter Maydell, Jason Wang, qemu-devel, yurovsky

On 01/15/2018 10:36 PM, Andrey Smirnov wrote:
> 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 <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
>  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
> 
> 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..76bb98a525
> --- /dev/null
> +++ b/hw/misc/imx2_wdt.c
> @@ -0,0 +1,89 @@
> +/*
> + * Copyright (c) 2017, Impinj, Inc.
> + *
> + * i.MX2 Watchdog IP block
> + *
> + * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
> + *
> + * 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/bitops.h"
> +#include "qemu/osdep.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,

Maybe you can just drop the .read entry and 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 <andrew.smirnov@gmail.com>
> + *
> + * 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 {
> +    /* <private> */
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion mmio;
> +} IMX2WdtState;
> +
> +#endif /* IMX7_SNVS_H */
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 05/14] i.MX: Add code to emulate i.MX7 SNVS IP-block
  2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 05/14] i.MX: Add code to emulate i.MX7 SNVS IP-block Andrey Smirnov
@ 2018-01-31 17:10   ` Philippe Mathieu-Daudé
  2018-02-06 15:12     ` Andrey Smirnov
  0 siblings, 1 reply; 46+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-01-31 17:10 UTC (permalink / raw)
  To: Andrey Smirnov, qemu-arm; +Cc: Peter Maydell, Jason Wang, qemu-devel, yurovsky

On 01/15/2018 10:37 PM, Andrey Smirnov wrote:
> Add code to emulate SNVS IP-block. Currently only the bits needed to
> be able to emulate machine shutdown are implemented.
> 
> Cc: Peter Maydell <peter.maydell@linaro.org>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
> Cc: qemu-devel@nongnu.org
> Cc: qemu-arm@nongnu.org
> Cc: yurovsky@gmail.com
> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
>  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
> 
> 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..670b9f4639
> --- /dev/null
> +++ b/hw/misc/imx7_snvs.c
> @@ -0,0 +1,83 @@
> +/*
> + * IMX7 Secure Non-Volatile Storage
> + *
> + * Copyright (c) 2017, Impinj, Inc.
> + *
> + * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
> + *
> + * 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,

Same here, you can remove the imx7_snvs_read() function since
memory_region_dispatch_read() takes care of this and return 0.

> +    .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 <andrew.smirnov@gmail.com>
> + *
> + * 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 {
> +    /* <private> */
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion mmio;
> +} IMX7SNVSState;
> +
> +#endif /* IMX7_SNVS_H */
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 05/14] i.MX: Add code to emulate i.MX7 SNVS IP-block
  2018-01-31 17:10   ` Philippe Mathieu-Daudé
@ 2018-02-06 15:12     ` Andrey Smirnov
  0 siblings, 0 replies; 46+ messages in thread
From: Andrey Smirnov @ 2018-02-06 15:12 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: open list:ARM, Peter Maydell, Jason Wang, QEMU Developers,
	Andrey Yurovsky

On Wed, Jan 31, 2018 at 9:10 AM, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> On 01/15/2018 10:37 PM, Andrey Smirnov wrote:
>> Add code to emulate SNVS IP-block. Currently only the bits needed to
>> be able to emulate machine shutdown are implemented.
>>
>> Cc: Peter Maydell <peter.maydell@linaro.org>
>> Cc: Jason Wang <jasowang@redhat.com>
>> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
>> Cc: qemu-devel@nongnu.org
>> Cc: qemu-arm@nongnu.org
>> Cc: yurovsky@gmail.com
>> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
>> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
>> ---
>>  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
>>
>> 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..670b9f4639
>> --- /dev/null
>> +++ b/hw/misc/imx7_snvs.c
>> @@ -0,0 +1,83 @@
>> +/*
>> + * IMX7 Secure Non-Volatile Storage
>> + *
>> + * Copyright (c) 2017, Impinj, Inc.
>> + *
>> + * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
>> + *
>> + * 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,
>
> Same here, you can remove the imx7_snvs_read() function since
> memory_region_dispatch_read() takes care of this and return 0.
>

Hmm, I don't think I agree both from reading code and trying this out.
Without .read callback the call chain ends up being the following
memory_region_dispatch_read() -> memory_region_dispatch_read1() ->
access_with_adjusted_size(..., memory_region_oldmmio_read_accessor,
...) -> memory_region_oldmmio_read_accessor() ->
mr->ops->old_mmio.read[ctz32(size)]() -> SEGFAULT

As much as I'd love to get rid of dummy .read callback, I don't see a
way to do that, unfortunately.

Thanks,
Andrey Smirnov

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support
  2018-01-31 17:03 ` Philippe Mathieu-Daudé
@ 2018-02-07  3:59   ` Andrey Smirnov
  0 siblings, 0 replies; 46+ messages in thread
From: Andrey Smirnov @ 2018-02-07  3:59 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, open list:ARM, Jason Wang, QEMU Developers,
	Andrey Yurovsky

On Wed, Jan 31, 2018 at 9:03 AM, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> Hi Peter, Andrey.
>
> On 01/15/2018 10:36 PM, Andrey Smirnov wrote:
>> Hi everyone,
>>
>> This v4 of the patch series containing the work that I've done in
>> order to enable support for i.MX7 emulation in QEMU.
>>
>> *NOTE*: Patches 1 and 2 are provided for the sake of completness and
>>       are going to have to be adapted once Philippe's SD changes
>>       land in master. As such, they are NOT ready to be
>>       accepted/merged.
>
> Peter:
> Since my series are taking longer, if this series is ready it is
> probably easier to apply Andrey series first and I'll adapt my SDHCI
> series after.
>
> Andrey:
> I only plan to keep the sdhci.c file generic (dealing with quirks) and
> split out the imx usdhci code, similar to this patch:
> https://lists.gnu.org/archive/html/qemu-devel/2018-01/msg01265.html

Yes, I understand that, but I still am not clear how you propose
dealing with the fact that i.MX specific read/write functions need to
call similar functions from parent SDHC class. Are you planning on
adding overridable read()/write() methods to SDHCICommonClass?

Thanks,
Andrey Smirnov

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware IP block
  2018-01-31 12:13       ` Marcel Apfelbaum
@ 2018-02-07  4:10         ` Andrey Smirnov
  0 siblings, 0 replies; 46+ messages in thread
From: Andrey Smirnov @ 2018-02-07  4:10 UTC (permalink / raw)
  To: Marcel Apfelbaum
  Cc: open list:ARM, Peter Maydell, Jason Wang,
	Philippe Mathieu-Daudé,
	QEMU Developers, Andrey Yurovsky

On Wed, Jan 31, 2018 at 4:13 AM, Marcel Apfelbaum <marcel@redhat.com> wrote:
> On 30/01/2018 19:49, Andrey Smirnov wrote:
>> On Tue, Jan 30, 2018 at 5:18 AM, Marcel Apfelbaum
>> <marcel.apfelbaum@zoho.com> wrote:
>>> Hi Andrei,
>>>
>>> Sorry for letting you wait,
>>> I have some comments/questions below.
>>>
>>>
>>> On 16/01/2018 3:37, Andrey Smirnov wrote:
>>>>
>>>> Add code needed to get a functional PCI subsytem when using in
>>>> conjunction with upstream Linux guest (4.13+). Tested to work against
>>>> "e1000e" (network adapter, using MSI interrupts) as well as
>>>> "usb-ehci" (USB controller, using legacy PCI interrupts).
>>>>
>>>> Cc: Peter Maydell <peter.maydell@linaro.org>
>>>> Cc: Jason Wang <jasowang@redhat.com>
>>>> Cc: Philippe Mathieu-Daudé <f4bug@amsat.org>
>>>> Cc: qemu-devel@nongnu.org
>>>> Cc: qemu-arm@nongnu.org
>>>> Cc: yurovsky@gmail.com
>>>> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
>>>> ---
>>>>   default-configs/arm-softmmu.mak  |   2 +
>>>>   hw/pci-host/Makefile.objs        |   2 +
>>>>   hw/pci-host/designware.c         | 618
>>>> +++++++++++++++++++++++++++++++++++++++
>>>>   include/hw/pci-host/designware.h |  93 ++++++
>>>>   include/hw/pci/pci_ids.h         |   2 +
>>>>   5 files changed, 717 insertions(+)
>>>>   create mode 100644 hw/pci-host/designware.c
>>>>   create mode 100644 include/hw/pci-host/designware.h
>>>>
>>>> diff --git a/default-configs/arm-softmmu.mak
>>>> b/default-configs/arm-softmmu.mak
>>>> index b0d6e65038..0c5ae914ed 100644
>>>> --- a/default-configs/arm-softmmu.mak
>>>> +++ b/default-configs/arm-softmmu.mak
>>>> @@ -132,3 +132,5 @@ CONFIG_GPIO_KEY=y
>>>>   CONFIG_MSF2=y
>>>>   CONFIG_FW_CFG_DMA=y
>>>>   CONFIG_XILINX_AXI=y
>>>> +CONFIG_PCI_DESIGNWARE=y
>>>> +
>>>> diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
>>>> index 9c7909cf44..0e2c0a123b 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..98fff5e5f3
>>>> --- /dev/null
>>>> +++ b/hw/pci-host/designware.c
>>>> @@ -0,0 +1,618 @@
>>>> +/*
>>>> + * Copyright (c) 2017, Impinj, Inc.
>>>
>>> 2018 :)
>>>
>>>> + *
>>>> + * 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 PCIE_PORT_LINK_CONTROL          0x710
>>>> +
>>>> +#define PCIE_PHY_DEBUG_R1               0x72C
>>>> +#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP  BIT(4)
>>>> +
>>>> +#define PCIE_LINK_WIDTH_SPEED_CONTROL   0x80C
>>>> +
>>>> +#define PCIE_MSI_ADDR_LO                0x820
>>>> +#define PCIE_MSI_ADDR_HI                0x824
>>>> +#define PCIE_MSI_INTR0_ENABLE           0x828
>>>> +#define PCIE_MSI_INTR0_MASK             0x82C
>>>> +#define PCIE_MSI_INTR0_STATUS           0x830
>>>> +
>>>> +#define PCIE_ATU_VIEWPORT               0x900
>>>> +#define PCIE_ATU_REGION_INBOUND         (0x1 << 31)
>>>> +#define PCIE_ATU_REGION_OUTBOUND        (0x0 << 31)
>>>> +#define PCIE_ATU_REGION_INDEX2          (0x2 << 0)
>>>> +#define PCIE_ATU_REGION_INDEX1          (0x1 << 0)
>>>> +#define PCIE_ATU_REGION_INDEX0          (0x0 << 0)
>>>> +#define PCIE_ATU_CR1                    0x904
>>>> +#define PCIE_ATU_TYPE_MEM               (0x0 << 0)
>>>> +#define PCIE_ATU_TYPE_IO                (0x2 << 0)
>>>> +#define PCIE_ATU_TYPE_CFG0              (0x4 << 0)
>>>> +#define PCIE_ATU_TYPE_CFG1              (0x5 << 0)
>>>> +#define PCIE_ATU_CR2                    0x908
>>>> +#define PCIE_ATU_ENABLE                 (0x1 << 31)
>>>> +#define PCIE_ATU_BAR_MODE_ENABLE        (0x1 << 30)
>>>> +#define PCIE_ATU_LOWER_BASE             0x90C
>>>> +#define PCIE_ATU_UPPER_BASE             0x910
>>>> +#define PCIE_ATU_LIMIT                  0x914
>>>> +#define PCIE_ATU_LOWER_TARGET           0x918
>>>> +#define PCIE_ATU_BUS(x)                 (((x) >> 24) & 0xff)
>>>> +#define PCIE_ATU_DEVFN(x)               (((x) >> 16) & 0xff)
>>>> +#define 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 |= (1 << 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);
>>>
>>>
>>> Sorry for the possibly dumb question, but "msi_write"
>>> will result in raising an INTx ?
>>
>> Correct, that's the intention. I wouldn't be surprised if I missed a
>> better/canonical way to implement this.
>>
>
> Not sure of a "better" way. The point was the "msi" naming
> that suggests edge interrupts, while resulting into level interrupts.
> I was wondering about the naming, nothing more.
>
>>>>
>>>> +    }
>>>> +}
>>>> +
>>>> +const MemoryRegionOps designware_pci_host_msi_ops = {
>>>> +    .write = designware_pcie_root_msi_write,
>>>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>>>
>>>
>>> You may want to limit the access size.
>>
>> Good point, will do.
>>
>>>>
>>>> +};
>>>> +
>>>> +static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot
>>>> *root)
>>>> +
>>>> +{
>>>> +    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
>>>> +    MemoryRegion *address_space = &host->pci.memory;
>>>> +    MemoryRegion *mem = &root->msi.iomem;
>>>> +    const uint64_t base = root->msi.base;
>>>> +    const bool enable = root->msi.intr[0].enable;
>>>> +
>>>> +    if (memory_region_is_mapped(mem)) {
>>>> +        memory_region_del_subregion(address_space, mem);
>>>> +        object_unparent(OBJECT(mem));
>>>> +    }
>>>> +
>>>> +    if (enable) {
>>>> +        memory_region_init_io(mem, OBJECT(root),
>>>> +                              &designware_pci_host_msi_ops,
>>>> +                              root, "pcie-msi", 0x1000);
>>>> +
>>>> +        memory_region_add_subregion(address_space, base, mem);
>>>
>>>
>>> What happens if is enabled twice?
>>
>> Ideally this shouldn't happen since this function is only called when
>> PCIE_MSI_INTR0_ENABLE transitions from "All IRQs disabled" to "At
>> least one IRQ enabled" or the inverse (via "update_msi_mapping" in
>> designware_pcie_root_config_write).
>>
>> But, assuming I got my logic wrong there, my thinking was that if it
>> gets enabled for the second time, first "if" statement in
>> designware_pcie_root_update_msi_mapping() would remove old
>> MemoryRegion and second one would add it back, so it end up being a
>> "no-op". I might be violating some API usage rules, so, please don't
>> hesitate to call me out on this if I do.
>>
>
> OK, so I am pretty sure we call "memory_region_init_io" only once
> in the init/realize part.
>
> Then, if the address mappings is getting changed between the calls
> you can use memory_region_del_subregion/memory_region_add_subregion on update.
>
> If the mappings remains the same you can use memory_region_set_enabled
> to disable/enable the memory region.
>

Sounds good. Will do in v5.

>>> Is it possible to be also disabled?
>>>
>>
>> Yes, similar to the case above, except the "if (enabled)" is not going
>> to be executed and there'd be no MemoryRegion to trigger MSI
>> interrupt.
>>
>>>
>>>> +    }
>>>> +}
>>>> +
>>>> +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 &
>>>> 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 PCIE_PORT_LINK_CONTROL:
>>>> +    case PCIE_LINK_WIDTH_SPEED_CONTROL:
>>>> +        val = 0xDEADBEEF;
>>>> +        /* No-op */
>>>
>>>
>>> Not really a no-op
>>>
>>
>> What I meant by "no-op" is that those registers do not implement their
>> actual function and instead return obviously bogus value. I'll change
>> the comment to state that in a more explicit way.
>>
>>>> +        break;
>>>> +
>>>> +    case PCIE_MSI_ADDR_LO:
>>>> +        val = root->msi.base;
>>>> +        break;
>>>> +
>>>> +    case PCIE_MSI_ADDR_HI:
>>>> +        val = root->msi.base >> 32;
>>>> +        break;
>>>> +
>>>> +    case PCIE_MSI_INTR0_ENABLE:
>>>> +        val = root->msi.intr[0].enable;
>>>> +        break;
>>>> +
>>>> +    case PCIE_MSI_INTR0_MASK:
>>>> +        val = root->msi.intr[0].mask;
>>>> +        break;
>>>> +
>>>> +    case PCIE_MSI_INTR0_STATUS:
>>>> +        val = root->msi.intr[0].status;
>>>> +        break;
>>>> +
>>>> +    case PCIE_PHY_DEBUG_R1:
>>>> +        val = PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_VIEWPORT:
>>>> +        val = root->atu_viewport;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_LOWER_BASE:
>>>> +        val = viewport->base;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_UPPER_BASE:
>>>> +        val = viewport->base >> 32;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_LOWER_TARGET:
>>>> +        val = viewport->target;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_UPPER_TARGET:
>>>> +        val = viewport->target >> 32;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_LIMIT:
>>>> +        val = viewport->limit;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_CR1:
>>>> +    case PCIE_ATU_CR2:          /* FALLTHROUGH */
>>>> +        val = viewport->cr[(address - 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_read(void *opaque,
>>>> +                                               hwaddr addr, unsigned len)
>>>> +{
>>>> +    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
>>>> +    DesignwarePCIEViewport *viewport =
>>>> +        designware_pcie_root_get_current_viewport(root);
>>>> +
>>>> +    const uint8_t busnum = PCIE_ATU_BUS(viewport->target);
>>>> +    const uint8_t devfn  = 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_SPACE_SIZE - 1;
>>>> +
>>>> +        return pci_host_config_read_common(pcidev, addr,
>>>> +                                           PCI_CONFIG_SPACE_SIZE, len);
>>>> +    }
>>>
>>> You can use "pci_data_read" instead
>>
>> Good to know, will change.
>>
>>>>
>>>> +
>>>> +    return UINT64_MAX;
>>>> +}
>>>> +
>>>> +static void designware_pcie_root_data_write(void *opaque, hwaddr addr,
>>>> +                                            uint64_t val, unsigned len)
>>>> +{
>>>> +    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
>>>> +    DesignwarePCIEViewport *viewport =
>>>> +        designware_pcie_root_get_current_viewport(root);
>>>> +    const uint8_t busnum = PCIE_ATU_BUS(viewport->target);
>>>> +    const uint8_t devfn  = 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_SPACE_SIZE - 1;
>>>> +        pci_host_config_write_common(pcidev, addr,
>>>> +                                     PCI_CONFIG_SPACE_SIZE,
>>>> +                                     val, len);
>>>> +    }
>>>
>>> You can use pci_data_write instead.
>>>
>>
>> Ditto.
>>
>>>> +}
>>>> +
>>>> +const MemoryRegionOps designware_pci_host_conf_ops = {
>>>> +    .read = designware_pcie_root_data_read,
>>>> +    .write = designware_pcie_root_data_write,
>>>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>>>
>>>
>>> Maybe you want to limit the access size also here.
>>>
>>
>> OK, will do.
>>
>>>> +};
>>>> +
>>>> +static void designware_pcie_update_viewport(DesignwarePCIERoot *root,
>>>> +                                            DesignwarePCIEViewport
>>>> *viewport)
>>>> +{
>>>> +    DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
>>>> +
>>>> +    MemoryRegion *mem     = &viewport->memory;
>>>> +    const uint64_t target = viewport->target;
>>>> +    const uint64_t base   = viewport->base;
>>>> +    const uint64_t size   = (uint64_t)viewport->limit - base + 1;
>>>> +    const bool inbound    = viewport->inbound;
>>>> +
>>>> +    MemoryRegion *source, *destination;
>>>> +    const char *direction;
>>>> +    char *name;
>>>> +
>>>> +    if (inbound) {
>>>> +        source      = &host->pci.address_space_root;
>>>> +        destination = get_system_memory();
>>>> +        direction   = "Inbound";
>>>> +    } else {
>>>> +        source      = get_system_memory();
>>>> +        destination = &host->pci.memory;
>>>> +        direction   = "Outbound";
>>>> +    }
>>>> +
>>>> +    if (memory_region_is_mapped(mem)) {
>>>> +        /* Before we modify anything, unmap and destroy the region */
>>>
>>>
>>> I saw this also before. Can you please explain a little
>>> why/when do you need to destroy prev mappings?
>>>
>>
>> They are going to be updated every time a viewport (inbound or
>> outbound) in address translation unit (iATU) is reconfigured. Because
>> PCIE_ATU_*_TARGET register is used to configure which deivce/bus to
>> address outgoing configuration TLP to, they (viewports) get
>> reconfigured quite a bit. Corresponding functions in Linux kernel
>> would be dw_pcie_prog_outbound_atu() and dw_pcie_rd_other_conf(). I
>> wouldn't be surprised that the way I went about implementing it is far
>> from optimal, so let me know if it is.
>>
>
> The same as above, I think memory_region_init_io should be used once.
>

Will do in v5.

>
>>>> +        memory_region_del_subregion(source, mem);
>>>> +        object_unparent(OBJECT(mem));
>>>> +    }
>>>> +
>>>> +    name = g_strdup_printf("PCI %s Viewport %p", direction, viewport);
>>>> +
>>>> +    switch (viewport->cr[0]) {
>>>> +    case PCIE_ATU_TYPE_MEM:
>>>> +        memory_region_init_alias(mem, OBJECT(root), name,
>>>> +                                 destination, target, size);
>>>> +        break;
>>>> +    case PCIE_ATU_TYPE_CFG0:
>>>> +    case PCIE_ATU_TYPE_CFG1:    /* FALLTHROUGH */
>>>> +        if (inbound) {
>>>> +            goto exit;
>>>> +        }
>>>> +
>>>> +        memory_region_init_io(mem, OBJECT(root),
>>>> +                              &designware_pci_host_conf_ops,
>>>> +                              root, name, size);
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    if (inbound) {
>>>> +        memory_region_add_subregion_overlap(source, base,
>>>> +                                            mem, -1);
>>>> +    } else {
>>>> +        memory_region_add_subregion(source, base, mem);
>>>> +    }
>>>> +
>>>> + exit:
>>>> +    g_free(name);
>>>> +}
>>>> +
>>>> +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 PCIE_PORT_LINK_CONTROL:
>>>> +    case PCIE_LINK_WIDTH_SPEED_CONTROL:
>>>> +    case PCIE_PHY_DEBUG_R1:
>>>> +        /* No-op */
>>>> +        break;
>>>> +
>>>> +    case PCIE_MSI_ADDR_LO:
>>>> +        root->msi.base &= 0xFFFFFFFF00000000ULL;
>>>> +        root->msi.base |= val;
>>>> +        break;
>>>> +
>>>> +    case PCIE_MSI_ADDR_HI:
>>>> +        root->msi.base &= 0x00000000FFFFFFFFULL;
>>>> +        root->msi.base |= (uint64_t)val << 32;
>>>> +        break;
>>>> +
>>>> +    case 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 PCIE_MSI_INTR0_MASK:
>>>> +        root->msi.intr[0].mask = val;
>>>> +        break;
>>>> +
>>>> +    case 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 PCIE_ATU_VIEWPORT:
>>>> +        root->atu_viewport = val;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_LOWER_BASE:
>>>> +        viewport->base &= 0xFFFFFFFF00000000ULL;
>>>> +        viewport->base |= val;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_UPPER_BASE:
>>>> +        viewport->base &= 0x00000000FFFFFFFFULL;
>>>> +        viewport->base |= (uint64_t)val << 32;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_LOWER_TARGET:
>>>> +        viewport->target &= 0xFFFFFFFF00000000ULL;
>>>> +        viewport->target |= val;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_UPPER_TARGET:
>>>> +        viewport->target &= 0x00000000FFFFFFFFULL;
>>>> +        viewport->target |= val;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_LIMIT:
>>>> +        viewport->limit = val;
>>>> +        break;
>>>> +
>>>> +    case PCIE_ATU_CR1:
>>>> +        viewport->cr[0] = val;
>>>> +        break;
>>>> +    case PCIE_ATU_CR2:
>>>> +        viewport->cr[1] = val;
>>>> +
>>>> +        if (viewport->cr[1] & PCIE_ATU_ENABLE) {
>>>> +            designware_pcie_update_viewport(root, viewport);
>>>> +         }
>>>> +        break;
>>>> +
>>>> +    default:
>>>> +        pci_bridge_write_config(d, address, val, len);
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static int designware_pcie_root_init(PCIDevice *dev)
>>>> +{
>>>
>>>
>>> Please use "realize" function rather than init.
>>> We want to get rid of it eventually.
>>
>> OK, will do.
>>
>>>>
>>>> +    DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev);
>>>> +    PCIBridge *br = PCI_BRIDGE(dev);
>>>> +    DesignwarePCIEViewport *viewport;
>>>> +    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_PCI_BUS);
>>>
>>> So this is a PCI Express Root Port "sitting" on PCI bus?
>>
>> Yes, it is a built-in PCIe bridge, whose configuration space is mapped
>> into CPU's address space (designware_pci_host_conf_ops) and the rest
>> of PCIe hierarchy is presented through it.
>
> My point was: is the bus PCIe or PCI? Because you used:
>   pci_bridge_initfn(dev, TYPE_PCI_BUS);
> and not
>   pci_bridge_initfn(dev, TYPE_PCIE_BUS);
>

Oops, my bad. Will fix in v5.

>>
>>>
>>>> +
>>>> +    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++) {
>>>> +        viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i];
>>>> +        viewport->inbound = true;
>>>> +    }
>>>> +
>>>> +    /*
>>>> +     * 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->base   = 0x0000000000000000ULL;
>>>> +    viewport->target = 0x0000000000000000ULL;
>>>> +    viewport->limit  = UINT32_MAX;
>>>> +    viewport->cr[0]  = PCIE_ATU_TYPE_MEM;
>>>> +
>>>> +    designware_pcie_update_viewport(root, viewport);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +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 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_HOST;
>>>
>>> So is a Root Port with call is "BRIDGE_HOST" ?
>>>
>>
>> I think I am missing some PCI subsystem knowledge to understand that
>> question, would you mind re-phrasing it?
>
> Sure, a Root Port is a type of PCI bridge, so I was expecting
> the class id to be: PCI_CLASS_BRIDGE_PCI and not PCI_CLASS_BRIDGE_HOST.
>

Yeah, that just mistake on my part since real HW reports
0604/PCI_CLASS_BRIDGE_PCI. Will fix in v5.

Thanks,
Andrey Smirnov

^ permalink raw reply	[flat|nested] 46+ messages in thread

end of thread, other threads:[~2018-02-07  4:10 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-16  1:36 [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Andrey Smirnov
2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 01/14] sdhci: Add i.MX specific subtype of SDHCI Andrey Smirnov
2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 02/14] hw: i.MX: Convert i.MX6 to use TYPE_IMX_USDHC Andrey Smirnov
2018-01-31 17:04   ` Philippe Mathieu-Daudé
2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 03/14] i.MX: Add code to emulate i.MX7 CCM, PMU and ANALOG IP blocks Andrey Smirnov
2018-01-16 14:28   ` Peter Maydell
2018-01-16  1:36 ` [Qemu-devel] [PATCH v4 04/14] i.MX: Add code to emulate i.MX2 watchdog IP block Andrey Smirnov
2018-01-31 17:07   ` Philippe Mathieu-Daudé
2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 05/14] i.MX: Add code to emulate i.MX7 SNVS IP-block Andrey Smirnov
2018-01-31 17:10   ` Philippe Mathieu-Daudé
2018-02-06 15:12     ` Andrey Smirnov
2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 06/14] i.MX: Add code to emulate GPCv2 IP block Andrey Smirnov
2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 08/14] i.MX: Add implementation of i.MX7 GPR " Andrey Smirnov
2018-01-16  4:45   ` Philippe Mathieu-Daudé
2018-01-16 15:05     ` Andrey Smirnov
2018-01-16 14:30   ` Peter Maydell
2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 09/14] pci: Add support for Designware " Andrey Smirnov
2018-01-16 14:34   ` Peter Maydell
2018-01-17 15:23     ` Marcel Apfelbaum
2018-01-17 15:35       ` Peter Maydell
2018-01-17 16:12         ` Marcel Apfelbaum
2018-01-17 16:12       ` Andrey Smirnov
2018-01-17 16:17         ` Marcel Apfelbaum
2018-01-17 16:45         ` Philippe Mathieu-Daudé
2018-01-30 13:18   ` Marcel Apfelbaum
2018-01-30 17:49     ` Andrey Smirnov
2018-01-31 12:13       ` Marcel Apfelbaum
2018-02-07  4:10         ` Andrey Smirnov
2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 10/14] usb: Add basic code to emulate Chipidea USB IP Andrey Smirnov
2018-01-16 14:40   ` Peter Maydell
2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 11/14] ARM: Add basic code to emulate A7MPCore DAP block Andrey Smirnov
2018-01-16  4:32   ` Philippe Mathieu-Daudé
2018-01-16 14:41     ` Peter Maydell
2018-01-16 15:04     ` Andrey Smirnov
2018-01-16 16:47       ` Philippe Mathieu-Daudé
2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 12/14] i.MX: Add i.MX7 SOC implementation Andrey Smirnov
2018-01-16 14:42   ` Peter Maydell
2018-01-16  1:37 ` [Qemu-devel] [PATCH v4 13/14] hw/arm: Move virt's PSCI DT fixup code to arm/boot.c Andrey Smirnov
2018-01-16 14:53   ` Peter Maydell
     [not found] ` <20180116013709.13830-8-andrew.smirnov@gmail.com>
2018-01-16  4:39   ` [Qemu-devel] [PATCH v4 07/14] i.MX: Add i.MX7 GPT variant Philippe Mathieu-Daudé
2018-01-16 14:29   ` Peter Maydell
     [not found] ` <20180116013709.13830-15-andrew.smirnov@gmail.com>
2018-01-16 14:52   ` [Qemu-devel] [PATCH v4 14/14] Implement support for i.MX7 Sabre board Peter Maydell
2018-01-16 15:08 ` [Qemu-devel] [PATCH v4 00/14] Initial i.MX7 support Peter Maydell
2018-01-16 15:17   ` Andrey Smirnov
2018-01-31 17:03 ` Philippe Mathieu-Daudé
2018-02-07  3:59   ` Andrey Smirnov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.