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
© 2016 - 2025 Red Hat, Inc.