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
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
Loop in more Nuvoton folks.
-----Original Message-----
From: Peter Maydell <peter.maydell@linaro.org>
Sent: Friday, September 26, 2025 12:42 AM
To: Yubin Zou <yubinz@google.com>
Cc: qemu-devel@nongnu.org; Paolo Bonzini <pbonzini@redhat.com>; CS20 KFTing <KFTING@nuvoton.com>; Hao Wu <wuhaotsh@google.com>; qemu-arm@nongnu.org; Titus Rwantare <titusr@google.com>
Subject: Re: [PATCH 4/7] hw/pci-host: add Nuvoton PCIe root port
CAUTION - External Email: Do not click links or open attachments unless you acknowledge the sender and content.
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
________________________________
________________________________
The privileged confidential information contained in this email is intended for use only by the addressees as indicated by the original sender of this email. If you are not the addressee indicated in this email or are not responsible for delivery of the email to such a person, please kindly reply to the sender indicating this fact and delete all copies of it from your computer and network server immediately. Your cooperation is highly appreciated. It is advised that any unauthorized use of confidential information of Nuvoton is strictly prohibited; and any information in this email irrelevant to the official business of Nuvoton shall be deemed as neither given nor endorsed by Nuvoton.
© 2016 - 2026 Red Hat, Inc.