qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
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



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