From: Yubin Zou <yubinz@google.com>
To: qemu-devel@nongnu.org
Cc: Paolo Bonzini <pbonzini@redhat.com>,
Tyrone Ting <kfting@nuvoton.com>, Hao Wu <wuhaotsh@google.com>,
qemu-arm@nongnu.org, Peter Maydell <peter.maydell@linaro.org>,
Yubin Zou <yubinz@google.com>,
Titus Rwantare <titusr@google.com>
Subject: [PATCH 2/7] hw/pci-host: add basic Nuvoton PCIe window support
Date: Tue, 09 Sep 2025 22:10:57 +0000 [thread overview]
Message-ID: <20250909-pcie-root-upstream-v1-2-d85883b2688d@google.com> (raw)
In-Reply-To: <20250909-pcie-root-upstream-v1-0-d85883b2688d@google.com>
From: Titus Rwantare <titusr@google.com>
Adds the windowing registers without address translation
Signed-off-by: Titus Rwantare <titusr@google.com>
---
hw/pci-host/npcm_pcierc.c | 223 +++++++++++++++++++++++++++++++++++++-
include/hw/pci-host/npcm_pcierc.h | 77 ++++++++++++-
2 files changed, 297 insertions(+), 3 deletions(-)
diff --git a/hw/pci-host/npcm_pcierc.c b/hw/pci-host/npcm_pcierc.c
index 3afe92e264f6ce4312e94f05b5e908840008df64..bffdec71acaba6562856b3bdd8aec07c3c153323 100644
--- a/hw/pci-host/npcm_pcierc.c
+++ b/hw/pci-host/npcm_pcierc.c
@@ -16,6 +16,193 @@
#include "qom/object.h"
#include "trace.h"
+/* Map enabled windows to a memory subregion */
+static void npcm_pcierc_map_enabled(NPCMPCIERCState *s, NPCMPCIEWindow *w)
+{
+ MemoryRegion *system = get_system_memory();
+ uint32_t size = NPCM_PCIERC_SAL_SIZE(w->sal);
+ hwaddr bar = ((uint64_t)w->sah) << 32 | (w->sal & 0xFFFFF000);
+ char name[26];
+
+ /* check if window is enabled */
+ if (!(w->sal & NPCM_PCIERC_SAL_EN)) {
+ return;
+ }
+
+ if (size > 2 * GiB || size < 4 * KiB) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Invalid PCI window size %d bytes\n",
+ __func__, size);
+ return;
+ }
+
+ if (w->type == AXI2PCIE) {
+ snprintf(name, sizeof(name), "npcm-axi2pcie-window-%d", w->id);
+ } else if (w->type == PCIE2AXI) {
+ snprintf(name, sizeof(name), "npcm-pcie2axi-window-%d", w->id);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: unable to map uninitialized PCIe window",
+ __func__);
+ return;
+ }
+
+ /* TODO: set subregion to target translation address */
+ /* add subregion starting at the window source address */
+ if (!memory_region_is_mapped(&w->mem)) {
+ memory_region_init(&w->mem, OBJECT(s), name, size);
+ memory_region_add_subregion(system, bar, &w->mem);
+ }
+}
+
+/* unmap windows marked as disabled */
+static void npcm_pcierc_unmap_disabled(NPCMPCIEWindow *w)
+{
+ MemoryRegion *system = get_system_memory();
+ /* Bit 0 in the Source address enables the window */
+ if (memory_region_is_mapped(&w->mem) && !(w->sal & NPCM_PCIERC_SAL_EN)) {
+ memory_region_del_subregion(system, &w->mem);
+ }
+}
+
+static void npcm_pcie_update_window_maps(NPCMPCIERCState *s)
+{
+ for (int i = 0; i < NPCM_PCIERC_NUM_PA_WINDOWS; i++) {
+ npcm_pcierc_unmap_disabled(&s->pcie2axi[i]);
+ }
+
+ for (int i = 0; i < NPCM_PCIERC_NUM_AP_WINDOWS; i++) {
+ npcm_pcierc_unmap_disabled(&s->axi2pcie[i]);
+ }
+
+ for (int i = 0; i < NPCM_PCIERC_NUM_AP_WINDOWS; i++) {
+ npcm_pcierc_map_enabled(s, &s->axi2pcie[i]);
+ }
+
+ for (int i = 0; i < NPCM_PCIERC_NUM_PA_WINDOWS; i++) {
+ npcm_pcierc_map_enabled(s, &s->pcie2axi[i]);
+ }
+}
+
+static NPCMPCIEWindow *npcm_pcierc_get_window(NPCMPCIERCState *s, hwaddr addr)
+{
+ NPCMPCIEWindow *window;
+
+ switch (addr) {
+ case NPCM_PCIERC_PAnSAL(0) ... NPCM_PCIERC_PAnTP(1):
+ window = &s->pcie2axi[NPCM_PCIERC_PA_WINDOW(addr)];
+ break;
+
+ case NPCM_PCIERC_APnSAL(0) ... NPCM_PCIERC_APnTP(4):
+ window = &s->axi2pcie[NPCM_PCIERC_AP_WINDOW(addr)];
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid window address 0x%lx\n",
+ __func__, addr);
+ return 0;
+ }
+ return window;
+}
+
+static int npcm_pcierc_get_window_offset(NPCMPCIEWindow *w, hwaddr addr)
+{
+ if (w->type == AXI2PCIE) {
+ return addr & NPCM_PCIERC_AP_OFFSET_MASK;
+ } else if (w->type == PCIE2AXI) {
+ return addr & NPCM_PCIERC_PA_OFFSET_MASK;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: unable to access uninitialized PCIe window",
+ __func__);
+ return -1;
+ }
+}
+
+static uint32_t npcm_pcierc_read_window(NPCMPCIERCState *s, hwaddr addr)
+{
+ NPCMPCIEWindow *window = npcm_pcierc_get_window(s, addr);
+ int offset;
+
+ if (!window) {
+ return 0;
+ }
+
+ offset = npcm_pcierc_get_window_offset(window, addr);
+ if (offset < 0) {
+ return 0;
+ }
+
+ switch (offset) {
+ case NPCM_PCIERC_SAL_OFFSET:
+ return window->sal;
+
+ case NPCM_PCIERC_SAH_OFFSET:
+ return window->sah;
+
+ case NPCM_PCIERC_TAL_OFFSET:
+ return window->tal;
+
+ case NPCM_PCIERC_TAH_OFFSET:
+ return window->tah;
+
+ case NPCM_PCIERC_PARAM_OFFSET:
+ return window->params;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid window offset 0x%x\n",
+ __func__, offset);
+ return 0;
+ }
+}
+
+static void npcm_pcierc_write_window(NPCMPCIERCState *s, hwaddr addr,
+ uint64_t data)
+{
+ NPCMPCIEWindow *window = npcm_pcierc_get_window(s, addr);
+ int offset;
+
+ if (!window) {
+ return;
+ }
+
+ offset = npcm_pcierc_get_window_offset(window, addr);
+ if (offset < 0) {
+ return;
+ }
+
+ switch (offset) {
+ case NPCM_PCIERC_SAL_OFFSET:
+ window->sal = data;
+ break;
+
+ case NPCM_PCIERC_SAH_OFFSET:
+ window->sah = data;
+ break;
+
+ case NPCM_PCIERC_TAL_OFFSET:
+ window->tal = data;
+ break;
+
+ case NPCM_PCIERC_TAH_OFFSET:
+ window->tah = data;
+ break;
+
+ case NPCM_PCIERC_PARAM_OFFSET:
+ window->params = data;
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid window offset 0x%x\n",
+ __func__, offset);
+ }
+
+ npcm_pcie_update_window_maps(s);
+}
+
static uint64_t npcm_pcierc_cfg_read(void *opaque, hwaddr addr, unsigned size)
{
NPCMPCIERCState *s = NPCM_PCIERC(opaque);
@@ -46,6 +233,10 @@ static uint64_t npcm_pcierc_cfg_read(void *opaque, hwaddr addr, unsigned size)
ret = s->axierr;
break;
+ case NPCM_PCIERC_PAnSAL(0) ... NPCM_PCIERC_APnTP(4):
+ ret = npcm_pcierc_read_window(s, addr);
+ break;
+
default:
qemu_log_mask(LOG_UNIMP,
"%s: read from unimplemented register 0x%04lx\n",
@@ -88,6 +279,10 @@ static void npcm_pcierc_cfg_write(void *opaque, hwaddr addr, uint64_t data,
s->axierr = data;
break;
+ case NPCM_PCIERC_PAnSAL(0) ... NPCM_PCIERC_APnTP(4):
+ npcm_pcierc_write_window(s, addr, data);
+ break;
+
default:
qemu_log_mask(LOG_UNIMP,
"%s: write to unimplemented reg 0x%04lx data: 0x%lx\n",
@@ -96,6 +291,22 @@ static void npcm_pcierc_cfg_write(void *opaque, hwaddr addr, uint64_t data,
}
}
+static void npcm_pcierc_reset_pcie_windows(NPCMPCIERCState *s)
+{
+ memset(s->axi2pcie, 0, sizeof(s->axi2pcie));
+ memset(s->pcie2axi, 0, sizeof(s->pcie2axi));
+
+ for (int i = 0; i < NPCM_PCIERC_NUM_PA_WINDOWS; i++) {
+ s->pcie2axi[i].id = i;
+ s->pcie2axi[i].type = PCIE2AXI;
+ }
+
+ for (int i = 0; i < NPCM_PCIERC_NUM_AP_WINDOWS; i++) {
+ s->axi2pcie[i].id = i;
+ s->axi2pcie[i].type = AXI2PCIE;
+ }
+}
+
static void npcm_pcierc_reset(Object *obj, ResetType type)
{
NPCMPCIERCState *s = NPCM_PCIERC(obj);
@@ -106,6 +317,8 @@ static void npcm_pcierc_reset(Object *obj, ResetType type)
s->rcimsiaddr = 0;
s->rcmsisstat = 0;
s->axierr = 0;
+
+ npcm_pcierc_reset_pcie_windows(s);
}
static const char *npcm_pcierc_root_bus_path(PCIHostState *host_bridge,
@@ -136,6 +349,13 @@ static void npcm_pcierc_realize(DeviceState *dev, Error **errp)
sysbus_init_irq(sbd, &s->irq);
}
+static void npcm_pcierc_instance_init(Object *obj)
+{
+ NPCMPCIERCState *s = NPCM_PCIERC(obj);
+
+ npcm_pcierc_reset_pcie_windows(s);
+}
+
static void npcm_pcierc_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -144,7 +364,7 @@ static void npcm_pcierc_class_init(ObjectClass *klass, const void *data)
hbc->root_bus_path = npcm_pcierc_root_bus_path;
dc->realize = npcm_pcierc_realize;
- rc->phases.enter = npcm_pcierc_reset;
+ rc->phases.exit = npcm_pcierc_reset;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->fw_name = "pci";
}
@@ -153,6 +373,7 @@ static const TypeInfo npcm_pcierc_type_info = {
.name = TYPE_NPCM_PCIERC,
.parent = TYPE_PCIE_HOST_BRIDGE,
.instance_size = sizeof(NPCMPCIERCState),
+ .instance_init = npcm_pcierc_instance_init,
.class_init = npcm_pcierc_class_init,
};
diff --git a/include/hw/pci-host/npcm_pcierc.h b/include/hw/pci-host/npcm_pcierc.h
index 2c817147d495fdc1d1fa4b389bad0469fd6a825e..410b34d1c1ced0e25f63fc7693d87bb625a80776 100644
--- a/include/hw/pci-host/npcm_pcierc.h
+++ b/include/hw/pci-host/npcm_pcierc.h
@@ -9,8 +9,8 @@
/*
* The PCIERC configuration registers must be initialised by the BMC kernel
* during boot for PCIe to function
- * - A single window from the PCIe to the Memory controller
- * - 4 windows from the BMC to the PCIe.
+ * - A single window from PCIe to the Memory controller
+ * - 4 windows from the BMC to PCIe.
* 1 of these five BMC-to-PCIe windows must be allocated for configuration
* transactions, the rest can be used for I/0 or memory transactions
* - All BMC-to-PCIe windows are mapped to address range
@@ -34,9 +34,77 @@
#define NPCM_PCIERC_MSISTAT 0x194 /* MSI Status Register */
#define NPCM_PCIERC_AXI_ERROR_REPORT 0x3E0
+/* PCIe-to-AXI Window 0 and 1 Registers */
+/* Source address low */
+#define NPCM_PCIERC_PAnSAL(n) (0x600 + (0x100 * (n)))
+/* Source address high */
+#define NPCM_PCIERC_PAnSAH(n) (0x604 + (0x100 * (n)))
+/* Translation address low */
+#define NPCM_PCIERC_PAnTAL(n) (0x608 + (0x100 * (n)))
+/* Translation address high */
+#define NPCM_PCIERC_PAnTAH(n) (0x60C + (0x100 * (n)))
+/* Translation parameters */
+#define NPCM_PCIERC_PAnTP(n) (0x610 + (0x100 * (n)))
+/* Get window number from address */
+#define NPCM_PCIERC_PA_WINDOW(addr) (((addr) - 0x600) / 0x100)
+#define NPCM_PCIERC_PA_OFFSET_MASK 0xff
+
+/* AXI-to-PCIe Window 1 to 5 Registers, n in range [0,4] */
+/* Source address low */
+#define NPCM_PCIERC_APnSAL(n) (0x820 + (0x20 * (n)))
+/* Source address high */
+#define NPCM_PCIERC_APnSAH(n) (0x824 + (0x20 * (n)))
+/* Translation address low */
+#define NPCM_PCIERC_APnTAL(n) (0x828 + (0x20 * (n)))
+/* Translation address high */
+#define NPCM_PCIERC_APnTAH(n) (0x82C + (0x20 * (n)))
+/* Translation parameters */
+#define NPCM_PCIERC_APnTP(n) (0x830 + (0x20 * (n)))
+/* Get window number from address */
+#define NPCM_PCIERC_AP_WINDOW(addr) (((addr) - 0x820) / 0x20)
+#define NPCM_PCIERC_AP_OFFSET_MASK 0x1f
+
+/* Translation window parameters */
+#define NPCM_PCIERC_TRSL_ID(p) ((p) & 0x7)
+#define NPCM_PCIERC_TRSL_ID_TX_RX 0
+#define NPCM_PCIERC_TRSL_ID_CONFIG 1
+#define NPCM_PCIERC_TRSF_PARAM(p) (((p) >> 16) & 0xFFF)
+#define NPCM_PCIERC_TRSF_PARAM_MEMORY 0
+#define NPCM_PCIERC_TRSF_PARAM_CONFIG 1
+#define NPCM_PCIERC_TRSF_PARAM_IO 2
+
+#define NPCM_PCIERC_SAL_OFFSET 0x0
+#define NPCM_PCIERC_SAL_EN 1
+#define NPCM_PCIERC_SAL_SIZE(addr) (2ull << (((addr) >> 1) & 0x1F))
+#define NPCM_PCIERC_SAH_OFFSET 0x4
+#define NPCM_PCIERC_TAL_OFFSET 0x8
+#define NPCM_PCIERC_TAH_OFFSET 0xC
+#define NPCM_PCIERC_PARAM_OFFSET 0x10
+
+#define NPCM_PCIERC_NUM_PA_WINDOWS 2
+#define NPCM_PCIERC_NUM_AP_WINDOWS 5
+
#define TYPE_NPCM_PCIERC "npcm-pcie-root-complex"
OBJECT_DECLARE_SIMPLE_TYPE(NPCMPCIERCState, NPCM_PCIERC)
+typedef enum {
+ AXI2PCIE = 1,
+ PCIE2AXI
+} NPCMPCIEWindowType;
+
+/* Nuvoton PCIe translation Window */
+typedef struct NPCMPCIEWindow {
+ uint32_t sal; /* source address low */
+ uint32_t sah; /* source address high */
+ uint32_t tal; /* translation address low */
+ uint32_t tah; /* translation address high */
+ uint32_t params; /* translation window parameters */
+
+ MemoryRegion mem; /* QEMU memory subregion per window */
+ NPCMPCIEWindowType type; /* translation direction */
+ uint8_t id;
+} NPCMPCIEWindow;
+
struct NPCMPCIERCState {
PCIExpressHost parent;
@@ -50,6 +118,11 @@ struct NPCMPCIERCState {
uint32_t rcimsiaddr;
uint32_t rcmsisstat;
uint32_t axierr;
+ /* PCIe to AXI Windows */
+ NPCMPCIEWindow pcie2axi[NPCM_PCIERC_NUM_PA_WINDOWS];
+
+ /* AXI to PCIe Windows */
+ NPCMPCIEWindow axi2pcie[NPCM_PCIERC_NUM_AP_WINDOWS];
};
#endif /* NPCM_PCIERC_H */
--
2.51.0.384.g4c02a37b29-goog
next prev parent reply other threads:[~2025-09-09 22:16 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-09 22:10 [PATCH 0/7] Introduce PCIE Root Complex on Nuvoton npcm8xx and npcm7xx Yubin Zou
2025-09-09 22:10 ` [PATCH 1/7] hw/pci-host: implement Nuvoton PCIE Root Complex stub Yubin Zou
2025-09-30 1:33 ` KFTING
2025-09-09 22:10 ` Yubin Zou [this message]
2025-09-25 16:38 ` [PATCH 2/7] hw/pci-host: add basic Nuvoton PCIe window support Peter Maydell
2025-09-25 19:39 ` Titus Rwantare
2025-09-26 9:07 ` Peter Maydell
2025-09-30 1:34 ` KFTING
2025-09-09 22:10 ` [PATCH 3/7] hw/arm: attach PCIe root complex to npmcm8xx Yubin Zou
2025-09-30 1:35 ` KFTING
2025-09-09 22:10 ` [PATCH 4/7] hw/pci-host: add Nuvoton PCIe root port Yubin Zou
2025-09-25 16:42 ` Peter Maydell
2025-09-30 1:34 ` KFTING
2025-09-09 22:11 ` [PATCH 5/7] hw/pci-host: enable MSI on npcm PCIe root complex Yubin Zou
2025-09-30 1:36 ` KFTING
2025-09-09 22:11 ` [PATCH 6/7] hw/pci-host: rework Nuvoton PCIe windowing and memory regions Yubin Zou
2025-09-25 16:40 ` Peter Maydell
2025-09-30 1:34 ` KFTING
2025-09-09 22:11 ` [PATCH 7/7] hw/arm: Add PCIERC to NPCM7xx SoC Yubin Zou
2025-09-30 1:36 ` KFTING
2025-09-25 16:43 ` [PATCH 0/7] Introduce PCIE Root Complex on Nuvoton npcm8xx and npcm7xx Peter Maydell
2025-09-30 1:32 ` KFTING
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250909-pcie-root-upstream-v1-2-d85883b2688d@google.com \
--to=yubinz@google.com \
--cc=kfting@nuvoton.com \
--cc=pbonzini@redhat.com \
--cc=peter.maydell@linaro.org \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=titusr@google.com \
--cc=wuhaotsh@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).