From: Dave Jiang <dave.jiang@intel.com>
To: linux-cxl@vger.kernel.org, linux-pci@vger.kernel.org
Cc: terry.bowman@amd.com, bhelgaas@google.com, jic23@kernel.org,
djbw@kernel.org
Subject: [RFC PATCH v2 1/2] PCI/CXL: Add RDPAS parsing support
Date: Thu, 18 Jun 2026 10:07:21 -0700 [thread overview]
Message-ID: <20260618170723.2010490-2-dave.jiang@intel.com> (raw)
In-Reply-To: <20260618170723.2010490-1-dave.jiang@intel.com>
Add parsing of the RCEC Downstream Port Association Structure (RDPAS),
which is a structure defined in the CXL spec r4.0 9.18.1.5. This
structure allows error handler to locate the downstream port(s)
that report errors to a given Root Complex Event Collector (RCEC).
The structure is part of CXL Early Discovery Table (CEDT) and can
be parsed like other CEDT tables.
A base address is provided in the RDPAS structure where depending on
the protocol field, it is the RCRB base associated with the downstream
port for CXL.io or the Component Base Register base associated with the
downstream port for CXL.cachemem.
Per the spec, "For every RCEC, zero or more entries of this type are
permitted", so a single (segment, BDF) maps to multiple downstream
ports. Each RDPAS structure is stored as a per-port list node hung off
a per-RCEC container in an xarray indexed by the combination of the
RCEC segment plus the BDF in a 32bit field. Both the base address and
protocol type are recorded for every entry so the error handler can
walk all ports associated with an RCEC and dispatch per protocol.
The parsed table is meant to live the entire life of the kernel, so the
xarray is not cleaned up when cxl_acpi unloads.
A helper is also added to retrieve the per-RCEC container based on the
segment and BDF of the RCEC.
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
RFC v2:
- verify table length (sashiko)
- store multiple downstream ports per RCEC in a list (sashiko)
- Add a gate to initialize the xarray only once per boot.
- Add support for multiple DSP per RCEC. (sashiko)
---
drivers/cxl/acpi.c | 5 ++
drivers/pci/pcie/aer_cxl_rch.c | 121 +++++++++++++++++++++++++++++++++
include/cxl/ras.h | 18 +++++
3 files changed, 144 insertions(+)
create mode 100644 include/cxl/ras.h
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 127537628817..e09706275d85 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -7,6 +7,7 @@
#include <linux/acpi.h>
#include <linux/pci.h>
#include <linux/node.h>
+#include <cxl/ras.h>
#include <asm/div64.h>
#include "cxlpci.h"
#include "cxl.h"
@@ -933,6 +934,10 @@ static int cxl_acpi_probe(struct platform_device *pdev)
if (rc < 0)
return -ENXIO;
+ rc = cxl_rdpas_init(host);
+ if (rc < 0)
+ dev_dbg(host, "No RDPAS entries found or failed to parse\n");
+
rc = add_cxl_resources(cxl_res);
if (rc)
return rc;
diff --git a/drivers/pci/pcie/aer_cxl_rch.c b/drivers/pci/pcie/aer_cxl_rch.c
index 83142eac0cab..eaab7698217e 100644
--- a/drivers/pci/pcie/aer_cxl_rch.c
+++ b/drivers/pci/pcie/aer_cxl_rch.c
@@ -4,9 +4,130 @@
#include <linux/pci.h>
#include <linux/aer.h>
#include <linux/bitfield.h>
+#include <linux/acpi.h>
+#include <linux/list.h>
+#include <cxl/ras.h>
#include "../pci.h"
#include "portdrv.h"
+/*
+ * CXL r4.0 9.18.1.5: "For every RCEC, zero or more entries of this type are
+ * permitted." A single (segment, bdf) therefore maps to multiple downstream
+ * ports, each with its own base address and protocol. The xarray value is a
+ * per-RCEC container holding the list of associated downstream ports.
+ */
+struct cxl_rdpas_rcec {
+ struct list_head ports;
+};
+
+/* One per RDPAS structure, i.e. per associated downstream port */
+struct cxl_rdpas_entry {
+ struct list_head list;
+ u64 address;
+ u8 protocol;
+};
+
+static DEFINE_XARRAY(cxl_rdpas);
+static bool rdpas_parsed;
+
+/* CXL r4.0 9.18.1.5 Table 9-24. The segment and the BDF belongs to the RCEC */
+static unsigned long __rdpas_index(u16 segment, u16 bdf)
+{
+ return FIELD_PREP(GENMASK(31, 16), segment) |
+ FIELD_PREP(GENMASK(15, 0), bdf);
+}
+
+static unsigned long rdpas_index(u16 segment, u8 bus, u8 device, u8 function)
+{
+ return __rdpas_index(segment,
+ FIELD_PREP(GENMASK(15, 8), bus) |
+ FIELD_PREP(GENMASK(7, 3), device) |
+ FIELD_PREP(GENMASK(2, 0), function));
+}
+
+static int __cxl_parse_rdpas(struct acpi_cedt_rdpas *rdpas, struct device *dev)
+{
+ struct cxl_rdpas_rcec *rdpas_rcec;
+ struct cxl_rdpas_entry *entry;
+ unsigned long index;
+ int rc;
+
+ if (rdpas->header.length < sizeof(struct acpi_cedt_rdpas))
+ return -EINVAL;
+
+ index = __rdpas_index(rdpas->segment, rdpas->bdf);
+
+ rdpas_rcec = xa_load(&cxl_rdpas, index);
+ if (!rdpas_rcec) {
+ rdpas_rcec = kzalloc(sizeof(*rdpas_rcec), GFP_KERNEL);
+ if (!rdpas_rcec)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&rdpas_rcec->ports);
+ rc = xa_insert(&cxl_rdpas, index, rdpas_rcec, GFP_KERNEL);
+ if (rc) {
+ kfree(rdpas_rcec);
+ return rc;
+ }
+ }
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->address = rdpas->address;
+ entry->protocol = rdpas->protocol;
+ list_add_tail(&entry->list, &rdpas_rcec->ports);
+
+ dev_dbg(dev,
+ "RDPAS entry: PCI %04x:%02lx:%02lx.%ld %s CXL addr %016llx\n",
+ rdpas->segment, FIELD_GET(GENMASK(15, 8), rdpas->bdf),
+ FIELD_GET(GENMASK(7, 3), rdpas->bdf),
+ FIELD_GET(GENMASK(2, 0), rdpas->bdf),
+ rdpas->protocol == ACPI_CEDT_RDPAS_PROTOCOL_IO ?
+ "CXL.io" : "CXL.cachemem",
+ rdpas->address);
+
+ return 0;
+}
+
+static int cxl_parse_rdpas(union acpi_subtable_headers *header, void *arg,
+ const unsigned long end)
+{
+ struct acpi_cedt_rdpas *rdpas = (struct acpi_cedt_rdpas *)header;
+ struct device *dev = arg;
+
+ return __cxl_parse_rdpas(rdpas, dev);
+}
+
+/*
+ * The CEDT is a single static system-wide firmware table, so RDPAS is parsed
+ * exactly once for the lifetime of the kernel. cxl_acpi may probe more than
+ * once (re-bind or multiple ACPI0017), but the global xarray is populated only
+ * on the first call; subsequent calls are no-ops. There is no teardown: the
+ * data describes the platform and remains valid until the kernel exits.
+ */
+int cxl_rdpas_init(struct device *host)
+{
+ if (rdpas_parsed)
+ return 0;
+
+ rdpas_parsed = true;
+
+ return acpi_table_parse_cedt(ACPI_CEDT_TYPE_RDPAS, cxl_parse_rdpas, host);
+}
+EXPORT_SYMBOL_FOR_MODULES(cxl_rdpas_init, "cxl_acpi");
+
+static struct cxl_rdpas_rcec __maybe_unused *cxl_get_rdpas_by_rcec(struct pci_dev *rcec)
+{
+ unsigned long index;
+
+ index = rdpas_index(pci_domain_nr(rcec->bus), rcec->bus->number,
+ PCI_SLOT(rcec->devfn), PCI_FUNC(rcec->devfn));
+
+ return xa_load(&cxl_rdpas, index);
+}
+
static bool is_cxl_mem_dev(struct pci_dev *dev)
{
/*
diff --git a/include/cxl/ras.h b/include/cxl/ras.h
new file mode 100644
index 000000000000..661307b0230a
--- /dev/null
+++ b/include/cxl/ras.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2026 Intel Corporation. */
+
+#ifndef __CXL_RAS_H__
+#define __CXL_RAS_H__
+
+#include <linux/acpi.h>
+
+#ifdef CONFIG_CXL_RAS
+int cxl_rdpas_init(struct device *host);
+#else
+static inline int cxl_rdpas_init(struct device *host)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+#endif
--
2.54.0
next prev parent reply other threads:[~2026-06-18 17:07 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-18 17:07 [RFC PATCH v2 0/2] PCI/CXL: Add RDPAS support for CXL.io Dave Jiang
2026-06-18 17:07 ` Dave Jiang [this message]
2026-06-18 17:19 ` [RFC PATCH v2 1/2] PCI/CXL: Add RDPAS parsing support sashiko-bot
2026-06-18 21:26 ` Bowman, Terry
2026-06-18 21:57 ` Dave Jiang
2026-06-18 17:07 ` [RFC PATCH v2 2/2] PCI/CXL: Enable usage of RDPAS to shortcut error device discovery Dave Jiang
2026-06-18 17:20 ` sashiko-bot
2026-06-18 21:26 ` Bowman, Terry
2026-06-18 22:04 ` Dave Jiang
2026-06-18 19:05 ` [RFC PATCH v2 0/2] PCI/CXL: Add RDPAS support for CXL.io Bowman, Terry
2026-06-18 20:12 ` Dave Jiang
2026-06-18 20:21 ` Dan Williams (nvidia)
2026-06-18 22:36 ` Dave Jiang
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=20260618170723.2010490-2-dave.jiang@intel.com \
--to=dave.jiang@intel.com \
--cc=bhelgaas@google.com \
--cc=djbw@kernel.org \
--cc=jic23@kernel.org \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=terry.bowman@amd.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.