[PATCH 4/7] hw/pci-host: add Nuvoton PCIe root port

Yubin Zou posted 7 patches 2 weeks, 4 days ago
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, Peter Maydell <peter.maydell@linaro.org>, Tyrone Ting <kfting@nuvoton.com>, Hao Wu <wuhaotsh@google.com>
[PATCH 4/7] hw/pci-host: add Nuvoton PCIe root port
Posted by Yubin Zou 2 weeks, 4 days ago
From: Titus Rwantare <titusr@google.com>

Signed-off-by: Titus Rwantare <titusr@google.com>
---
 hw/arm/npcm8xx.c                  |   3 +-
 hw/pci-host/npcm_pcierc.c         | 137 ++++++++++++++++++++++++++++++++++++++
 include/hw/pci-host/npcm_pcierc.h |  22 +++++-
 3 files changed, 160 insertions(+), 2 deletions(-)

diff --git a/hw/arm/npcm8xx.c b/hw/arm/npcm8xx.c
index 9b3e648e3fc3ca4352d5c8d310a19ea462e62cc7..f7a5ae2d121ffec99c519b484503e71dc8a43695 100644
--- a/hw/arm/npcm8xx.c
+++ b/hw/arm/npcm8xx.c
@@ -51,6 +51,7 @@
 #define NPCM8XX_MC_BA           0xf0824000
 #define NPCM8XX_RNG_BA          0xf000b000
 #define NPCM8XX_PCIERC_BA       0xe1000000
+#define NPCM8XX_PCIE_ROOT_BA    0xe8000000
 
 /* ADC Module */
 #define NPCM8XX_ADC_BA          0xf000c000
@@ -772,13 +773,13 @@ static void npcm8xx_realize(DeviceState *dev, Error **errp)
     /* PCIe RC */
     sysbus_realize(SYS_BUS_DEVICE(&s->pcierc), &error_abort);
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcierc), 0, NPCM8XX_PCIERC_BA);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcierc), 1, NPCM8XX_PCIE_ROOT_BA);
     sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcierc), 0,
                        npcm8xx_irq(s, NPCM8XX_PCIE_RC_IRQ));
 
     create_unimplemented_device("npcm8xx.shm",          0xc0001000,   4 * KiB);
     create_unimplemented_device("npcm8xx.gicextra",     0xdfffa000,  24 * KiB);
     create_unimplemented_device("npcm8xx.vdmx",         0xe0800000,   4 * KiB);
-    create_unimplemented_device("npcm8xx.rootc",        0xe8000000, 128 * MiB);
     create_unimplemented_device("npcm8xx.kcs",          0xf0007000,   4 * KiB);
     create_unimplemented_device("npcm8xx.gfxi",         0xf000e000,   4 * KiB);
     create_unimplemented_device("npcm8xx.fsw",          0xf000f000,   4 * KiB);
diff --git a/hw/pci-host/npcm_pcierc.c b/hw/pci-host/npcm_pcierc.c
index bffdec71acaba6562856b3bdd8aec07c3c153323..a1767335fe79a30464acf32ae94fc14e417c89eb 100644
--- a/hw/pci-host/npcm_pcierc.c
+++ b/hw/pci-host/npcm_pcierc.c
@@ -203,6 +203,7 @@ static void npcm_pcierc_write_window(NPCMPCIERCState *s, hwaddr addr,
     npcm_pcie_update_window_maps(s);
 }
 
+/* read root complex configuration registers */
 static uint64_t npcm_pcierc_cfg_read(void *opaque, hwaddr addr, unsigned size)
 {
     NPCMPCIERCState *s = NPCM_PCIERC(opaque);
@@ -248,6 +249,7 @@ static uint64_t npcm_pcierc_cfg_read(void *opaque, hwaddr addr, unsigned size)
     return ret;
 }
 
+/* write root complex configuration registers */
 static void npcm_pcierc_cfg_write(void *opaque, hwaddr addr, uint64_t data,
                                   unsigned size)
 {
@@ -291,6 +293,53 @@ static void npcm_pcierc_cfg_write(void *opaque, hwaddr addr, uint64_t data,
     }
 }
 
+/* read PCIe configuration space */
+static uint64_t npcm_pcie_host_config_read(void *opaque, hwaddr addr,
+                                           unsigned size)
+{
+    NPCMPCIERCState *s = NPCM_PCIERC(opaque);
+    PCIHostState *pcih = PCI_HOST_BRIDGE(opaque);
+    int bus = NPCM_PCIE_RCCFGNUM_BUS(s->rccfgnum);
+    uint8_t devfn = NPCM_PCIE_RCCFGNUM_DEVFN(s->rccfgnum);
+    PCIDevice *pcid = pci_find_device(pcih->bus, bus, devfn);
+
+    if (pcid) {
+        return pci_host_config_read_common(pcid, addr,
+                                           pci_config_size(pcid),
+                                           size);
+    }
+    return 0;
+}
+
+/* write PCIe configuration space */
+static void npcm_pcie_host_config_write(void *opaque, hwaddr addr,
+                                        uint64_t data, unsigned size)
+{
+    NPCMPCIERCState *s = NPCM_PCIERC(opaque);
+    PCIHostState *pcih = PCI_HOST_BRIDGE(opaque);
+    int bus = NPCM_PCIE_RCCFGNUM_BUS(s->rccfgnum);
+    uint8_t devfn = NPCM_PCIE_RCCFGNUM_DEVFN(s->rccfgnum);
+    PCIDevice *pcid = pci_find_device(pcih->bus, bus, devfn);
+
+    if (pcid) {
+        pci_host_config_write_common(pcid, addr,
+                                     pci_config_size(pcid),
+                                     data,
+                                     size);
+    }
+}
+
+static AddressSpace *npcm_pcierc_set_iommu(PCIBus *bus, void *opaque, int devfn)
+{
+    NPCMPCIERCState *s = NPCM_PCIERC(opaque);
+
+    return &s->pcie_space;
+}
+
+static const PCIIOMMUOps npcm_pcierc_iommu_ops = {
+    .get_address_space = npcm_pcierc_set_iommu,
+};
+
 static void npcm_pcierc_reset_pcie_windows(NPCMPCIERCState *s)
 {
     memset(s->axi2pcie, 0, sizeof(s->axi2pcie));
@@ -338,15 +387,73 @@ static const MemoryRegionOps npcm_pcierc_cfg_ops = {
     },
 };
 
+static const MemoryRegionOps npcm_pcie_cfg_space_ops = {
+    .read       = npcm_pcie_host_config_read,
+    .write      = npcm_pcie_host_config_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+        .unaligned = false,
+    },
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    }
+};
+
+static void npcm_pcie_set_irq(void *opaque, int irq_num, int level)
+{
+    NPCMPCIERCState *s = NPCM_PCIERC(opaque);
+
+    qemu_set_irq(s->irq, level);
+}
+
 static void npcm_pcierc_realize(DeviceState *dev, Error **errp)
 {
     NPCMPCIERCState *s = NPCM_PCIERC(dev);
     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    PCIHostState *pci = PCI_HOST_BRIDGE(dev);
+    PCIDevice *root = pci_new(PCI_DEVFN(0, 0), TYPE_NPCM_PCIE_ROOT_PORT);
 
     memory_region_init_io(&s->mmio, OBJECT(s), &npcm_pcierc_cfg_ops,
                           s, TYPE_NPCM_PCIERC, 4 * KiB);
     sysbus_init_mmio(sbd, &s->mmio);
     sysbus_init_irq(sbd, &s->irq);
+
+    /* IO memory region is needed to create a PCI bus, but is unused on ARM */
+    memory_region_init(&s->pcie_io, OBJECT(s), "npcm-pcie-io", 16);
+
+    /*
+     * pcie_root is a 128 MiB memory region in the BMC physical address space
+     * in which all PCIe windows must have their programmable source or
+     * destination address
+     */
+    memory_region_init_io(&s->pcie_root, OBJECT(s), &npcm_pcie_cfg_space_ops,
+                          s, "npcm-pcie-config", 128 * MiB);
+    sysbus_init_mmio(sbd, &s->pcie_root);
+
+    pci->bus = pci_register_root_bus(dev, "pcie",
+                                     npcm_pcie_set_irq,
+                                     pci_swizzle_map_irq_fn,
+                                     s, &s->pcie_root, &s->pcie_io,
+                                     0, 4, TYPE_PCIE_BUS);
+
+    address_space_init(&s->pcie_space, &s->pcie_root, "pcie-address-space");
+    pci_realize_and_unref(root, pci->bus, &error_fatal);
+    pci_setup_iommu(pci->bus, &npcm_pcierc_iommu_ops, s);
+}
+
+static void npcm_pcie_root_port_realize(DeviceState *dev, Error **errp)
+{
+    PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
+    Error *local_err = NULL;
+
+    rpc->parent_realize(dev, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
 }
 
 static void npcm_pcierc_instance_init(Object *obj)
@@ -369,6 +476,28 @@ static void npcm_pcierc_class_init(ObjectClass *klass, const void *data)
     dc->fw_name = "pci";
 }
 
+static void npcm_pcie_rp_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *pk = PCI_DEVICE_CLASS(klass);
+    PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
+
+    dc->desc = "Nuvoton PCIe Root Port";
+    dc->user_creatable = false;
+
+    device_class_set_parent_realize(dc,
+                                    npcm_pcie_root_port_realize,
+                                    &rpc->parent_realize);
+
+    /* TODO(b/229132071) replace with real values */
+    pk->vendor_id = PCI_VENDOR_ID_QEMU;
+    pk->device_id = 0;
+    pk->class_id = PCI_CLASS_BRIDGE_PCI;
+
+    rpc->exp_offset = NPCM_PCIE_HEADER_OFFSET; /* Express capabilities offset */
+    rpc->aer_offset = NPCM_PCIE_AER_OFFSET;
+}
+
 static const TypeInfo npcm_pcierc_type_info = {
     .name = TYPE_NPCM_PCIERC,
     .parent = TYPE_PCIE_HOST_BRIDGE,
@@ -377,9 +506,17 @@ static const TypeInfo npcm_pcierc_type_info = {
     .class_init = npcm_pcierc_class_init,
 };
 
+static const TypeInfo npcm_pcie_port_type_info = {
+    .name = TYPE_NPCM_PCIE_ROOT_PORT,
+    .parent = TYPE_PCIE_ROOT_PORT,
+    .instance_size = sizeof(NPCMPCIERootPort),
+    .class_init = npcm_pcie_rp_class_init,
+};
+
 static void npcm_pcierc_register_types(void)
 {
     type_register_static(&npcm_pcierc_type_info);
+    type_register_static(&npcm_pcie_port_type_info);
 }
 
 type_init(npcm_pcierc_register_types)
diff --git a/include/hw/pci-host/npcm_pcierc.h b/include/hw/pci-host/npcm_pcierc.h
index 410b34d1c1ced0e25f63fc7693d87bb625a80776..a47eae0084b88ba4388681b24ab97f77a4338594 100644
--- a/include/hw/pci-host/npcm_pcierc.h
+++ b/include/hw/pci-host/npcm_pcierc.h
@@ -23,11 +23,14 @@
 #include "hw/sysbus.h"
 #include "hw/pci/pci.h"
 #include "hw/pci/pcie_host.h"
+#include "hw/pci/pcie_port.h"
 #include "qom/object.h"
 
 /* PCIe Root Complex Registers */
-#define LINKSTAT                        0x92
+#define NPCM_PCIE_LINK_CTRL             0x90
 #define NPCM_PCIERC_RCCFGNUM            0x140 /* Configuration Number */
+#define     NPCM_PCIE_RCCFGNUM_BUS(a)   (((a) >> 8) & 0xFF)
+#define     NPCM_PCIE_RCCFGNUM_DEVFN(a) ((a) & 0xFF)
 #define NPCM_PCIERC_INTEN               0x180 /* Interrupt Enable */
 #define NPCM_PCIERC_INTST               0x184 /* Interrupt Status */
 #define NPCM_PCIERC_IMSI_ADDR           0x190
@@ -84,6 +87,10 @@
 #define NPCM_PCIERC_NUM_PA_WINDOWS          2
 #define NPCM_PCIERC_NUM_AP_WINDOWS          5
 
+/* PCIe extended config space offsets */
+#define NPCM_PCIE_HEADER_OFFSET             0x80
+#define NPCM_PCIE_AER_OFFSET                0x100
+
 #define TYPE_NPCM_PCIERC "npcm-pcie-root-complex"
 OBJECT_DECLARE_SIMPLE_TYPE(NPCMPCIERCState, NPCM_PCIERC)
 
@@ -105,6 +112,13 @@ typedef struct NPCMPCIEWindow {
     uint8_t id;
 } NPCMPCIEWindow;
 
+#define TYPE_NPCM_PCIE_ROOT_PORT "npcm-pcie-root-port"
+OBJECT_DECLARE_SIMPLE_TYPE(NPCMPCIERootPort, NPCM_PCIE_ROOT_PORT)
+
+struct NPCMPCIERootPort {
+    PCIESlot parent;
+};
+
 struct NPCMPCIERCState {
     PCIExpressHost parent;
 
@@ -118,6 +132,12 @@ struct NPCMPCIERCState {
     uint32_t rcimsiaddr;
     uint32_t rcmsisstat;
     uint32_t axierr;
+
+    /* Address translation state */
+    AddressSpace pcie_space;
+    MemoryRegion pcie_root;
+    MemoryRegion pcie_io; /* unused - but required for IO space PCI */
+    NPCMPCIERootPort port;
     /* PCIe to AXI Windows */
     NPCMPCIEWindow pcie2axi[NPCM_PCIERC_NUM_PA_WINDOWS];
 

-- 
2.51.0.384.g4c02a37b29-goog
Re: [PATCH 4/7] hw/pci-host: add Nuvoton PCIe root port
Posted by Peter Maydell 3 days ago
On Tue, 9 Sept 2025 at 23:11, Yubin Zou <yubinz@google.com> wrote:
>
> From: Titus Rwantare <titusr@google.com>
>
> Signed-off-by: Titus Rwantare <titusr@google.com>

> --- a/hw/pci-host/npcm_pcierc.c
> +++ b/hw/pci-host/npcm_pcierc.c
> @@ -203,6 +203,7 @@ static void npcm_pcierc_write_window(NPCMPCIERCState *s, hwaddr addr,
>      npcm_pcie_update_window_maps(s);
>  }
>
> +/* read root complex configuration registers */
>  static uint64_t npcm_pcierc_cfg_read(void *opaque, hwaddr addr, unsigned size)
>  {
>      NPCMPCIERCState *s = NPCM_PCIERC(opaque);
> @@ -248,6 +249,7 @@ static uint64_t npcm_pcierc_cfg_read(void *opaque, hwaddr addr, unsigned size)
>      return ret;
>  }
>
> +/* write root complex configuration registers */
>  static void npcm_pcierc_cfg_write(void *opaque, hwaddr addr, uint64_t data,
>                                    unsigned size)
>  {

These comments look like they should have been in
a previous patch where the functions were added.

thanks
-- PMM