Patches #1 to #18 are a rework of the Component Register setup. This
is needed to share multiple CXL capabilities (HDM and RAS) for the
same component, also there can be different components implementing
the same capability, finally RCH mode should be supported too. The
general approach to solve this is to:
* Unify code for components and capabilities in VH and RCH modes.
* Early setup of the Component Register base address.
* Create and store the register mappings to later use it for mapping
the capability I/O ranges.
Patches #19 to #27 enable CXL RCH error handling. These are needed because
RCH downstream port protocol error handling is implemented uniquely and not
currently supported. These patches address the following:
* Discovery and mapping of RCH downstream port AER registers.
* AER portdrv changes to support CXL RCH protocol errors.
* Interrupt setup specific to RCH mode: enabling RCEC internal
errors and disabling root port interrupts.
Changes in V6:
- Added patch for cxl test fixes: 'cxl: Update CXl Test to Work with
RCH'. Patch from Dan.
- Simplified: 'cxl/rch: Prepare for caching the MMIO mapped PCIe AER
capability'. Patch from Dan.
- Added patch: 'cxl: Rename 'uport' to 'uport_dev''
- Updated patch: 'cxl: Rename member @dport of struct cxl_dport to
@dport_dev'
- Updated *map assignment to use structure init in 'cxl/core/regs: Add
@dev to cxl_register_map'. Also fixed whitespace.
- Removed extra whitespace in 'cxl/core/regs: Add @dev to
cxl_register_map'
- Updated patch subject: 'cxl/acpi: Move add_host_bridge_uport() after
cxl_get_chbs()'
- Changes to work with CXL test. 'cxl/acpi: Directly bind the CEDT
detected CHBCR to the Host Bridge's port'
- 'cxl/pci: Early setup RCH dport component registers from RCRB'
- Removed parameter from cxl_rcrb_get_comp_regs().
- Changed return value to EPROBE_DEFER for retry during ACPI
initialization.
- Changed map to us struct initialization.
- Remove ENODEV check in 'cxl/port: Store the downstream port's
Component Register mappings in struct cxl_dport'
- 'cxl/port: Remove Component Register base address from struct
cxl_dport'
- Moved earlier with same removal for cxl_port.
- cxl/pci: Add RCH downstream port AER register discovery
- Flattened {request,release}_mem_region() and ioremap() into
cxl_rcrb_to_aer().
- Add check if OS is assigned AER handling before discovering AER.
- Added CXL namespace import to cxl_core (drivers/cxl/core/port.c).
Needed for using pci_print_aer(). In 'PCI/AER: Refactor
cper_print_aer() for use by CXL driver module'.
- cxl/pci: Map RCH downstream AER registers for logging protocol errors
- Changed dport device used in devm_cxl_iomap_block() call to be
port->dev.
- Removed ENODEV check.
- cxl/pci: Disable root port interrupts in RCH mode
- Removed unnecessary 'rch' check.
- Moved cxl_disable_rch_root_ints() into core/pci.c.
- Added OSC AER assignment check before accessing AER registers.
- cxl/pci: Update CXL error logging to use RAS register address
- Renamed function handlers.
- cxl/pci: Add RCH downstream port error logging
- Moved RCD check to caller.
- Added put_dev() after call to cxl_pci_find_port().
Changes in V5:
- Split 'cxl/rch: Prepare for logging RCH downstream port protocol
errors' patch into 2 patches.
- Added:
cxl/core/regs: Rename phys_addr in cxl_map_component_regs()
cxl/mem: Prepare for early RCH dport component register setup
- Correct comments CXL3.0 to CXL 3.0.
- changed cxl_port_get_comp_map() to static.
Changes in V4:
- Made port RAS register discovery common and called from
__devm_cxl_add_dport().
- Changed RCH AER register discovery to be called from
__devm_cxl_add_dport().
- Changed RAS and RCH AER register mapping to be called from
__devm_cxl_add_dport().
- Changed component register mapping to support all CXL component
mapping, cxl_map_component_regs().
- Added cxl_regs to 'struct cxl_dport' for providing RCH downstream port
mapped registers USED in error handler.
- PCI/AER:
- Improved description of PCIEAER_CXL option in Kconfig.
- Renamed function to pci_aer_unmask_internal_errors(), added
pcie_aer_is_native() check.
- Improved comments and added spec refs.
- Renamed functions to cxl_rch_handle_error*().
- Modified cxl_rch_handle_error_iter() to only call the handler
callbacks, this also simplifies refcounting of the pdev.
- Refactored handle_error_source(), created pci_aer_handle_error().
- Changed printk messages to pci_*() variants.
- Added check for pcie_aer_is_native() to the RCEC.
- Introduced function cxl_rch_enable_rcec().
- Updated patch description ("PCI/AER: Forward RCH downstream
port-detected errors to the CXL.mem dev handler").
Changes in V3:
- Correct base commit in cover sheet.
- Change hardcoded return 0 to NULL in regs.c.
- Remove calls to pci_disable_pcie_error_reporting(pdev) and
pci_enable_pcie_error_reporting(pdev) in mem.c;
- Move RCEC interrupt unmask to PCIe port AER driver's probe.
- Fixes missing PCIEAER and PCIEPORTBUS config option error.
- Rename cxl_rcrb_setup() to cxl_setup_rcrb() in mem.c.
- Update cper_mem_err_unpack() patch subject and description.
Changes in V2:
- Refactor RCH initialization into cxl_mem driver.
- Includes RCH RAS and AER register discovery and mapping.
- Add RCEC protocol error interrupt forwarding to CXL endpoint
handler.
- Change AER and RAS logging to use existing trace routines.
- Enable RCEC AER internal errors.
Dan Williams (3):
cxl: Updates for CXL Test to work with RCH
cxl/rch: Prepare for caching the MMIO mapped PCIe AER capability
cxl: Rename 'uport' to 'uport_dev'
Robert Richter (17):
cxl/port: Fix NULL pointer access in devm_cxl_add_port()
cxl/acpi: Probe RCRB later during RCH downstream port creation
cxl: Rename member @dport of struct cxl_dport to @dport_dev
cxl/core/regs: Add @dev to cxl_register_map
cxl/acpi: Move add_host_bridge_uport() after cxl_get_chbs()
cxl/acpi: Directly bind the CEDT detected CHBCR to the Host Bridge's
port
cxl/port: Remove Component Register base address from struct cxl_dport
cxl/regs: Remove early capability checks in Component Register setup
cxl/mem: Prepare for early RCH dport component register setup
cxl/pci: Early setup RCH dport component registers from RCRB
cxl/port: Store the port's Component Register mappings in struct
cxl_port
cxl/port: Store the downstream port's Component Register mappings in
struct cxl_dport
cxl/pci: Store the endpoint's Component Register mappings in struct
cxl_dev_state
cxl/hdm: Use stored Component Register mappings to map HDM decoder
capability
cxl/port: Remove Component Register base address from struct cxl_port
PCI/AER: Forward RCH downstream port-detected errors to the CXL.mem
dev handler
PCI/AER: Unmask RCEC internal errors to enable RCH downstream port
error handling
Terry Bowman (7):
cxl/pci: Refactor component register discovery for reuse
cxl/pci: Add RCH downstream port AER register discovery
PCI/AER: Refactor cper_print_aer() for use by CXL driver module
cxl/pci: Update CXL error logging to use RAS register address
cxl/pci: Map RCH downstream AER registers for logging protocol errors
cxl/pci: Add RCH downstream port error logging
cxl/pci: Disable root port interrupts in RCH mode
base-commit: a70fc4ed20a6118837b0aecbbf789074935f473b
drivers/cxl/acpi.c | 206 +++++++++++++++++++---------------
drivers/cxl/core/core.h | 17 +++
drivers/cxl/core/hdm.c | 59 +++++-----
drivers/cxl/core/pci.c | 178 ++++++++++++++++++++++++++---
drivers/cxl/core/port.c | 200 ++++++++++++++++++++++++---------
drivers/cxl/core/region.c | 52 ++++-----
drivers/cxl/core/regs.c | 139 +++++++++++++++++++++--
drivers/cxl/cxl.h | 70 +++++++-----
drivers/cxl/cxlmem.h | 9 +-
drivers/cxl/mem.c | 18 +--
drivers/cxl/pci.c | 125 ++++++++-------------
drivers/cxl/port.c | 7 +-
drivers/pci/pcie/Kconfig | 12 ++
drivers/pci/pcie/aer.c | 162 +++++++++++++++++++++++++-
include/linux/aer.h | 2 +-
tools/testing/cxl/Kbuild | 3 +-
tools/testing/cxl/test/cxl.c | 30 ++---
tools/testing/cxl/test/mem.c | 1 -
tools/testing/cxl/test/mock.c | 46 ++++++--
tools/testing/cxl/test/mock.h | 3 -
20 files changed, 949 insertions(+), 390 deletions(-)
--
2.34.1
From: Robert Richter <[email protected]>
In devm_cxl_add_port() the port creation may fail and its associated
pointer does not contain a valid address. During error message
generation this invalid port address is used. Fix that wrong address
access.
Fixes: f3cd264c4ec1 ("cxl: Unify debug messages when calling devm_cxl_add_port()")
Signed-off-by: Robert Richter <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Dan Williams <[email protected]>
---
drivers/cxl/core/port.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index da2068475fa2..e7c284c890bc 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -750,11 +750,10 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
parent_port = parent_dport ? parent_dport->port : NULL;
if (IS_ERR(port)) {
- dev_dbg(uport, "Failed to add %s%s%s%s: %ld\n",
- dev_name(&port->dev),
- parent_port ? " to " : "",
+ dev_dbg(uport, "Failed to add%s%s%s: %ld\n",
+ parent_port ? " port to " : "",
parent_port ? dev_name(&parent_port->dev) : "",
- parent_port ? "" : " (root port)",
+ parent_port ? "" : " root port",
PTR_ERR(port));
} else {
dev_dbg(uport, "%s added%s%s%s\n",
--
2.34.1
From: Robert Richter <[email protected]>
CXL capabilities are stored in the Component Registers. To use them,
the specific I/O ranges of the capabilities must be determined by
probing the registers. For this, the whole Component Register range
needs to be mapped temporarily to detect the offset and length of a
capability range.
In order to use more than one capability of a component (e.g. RAS and
HDM) the Component Register are probed and its mappings created
multiple times. This also causes overlapping I/O ranges as the whole
Component Register range must be mapped again while a capability's I/O
range is already mapped.
Different capabilities cannot be setup at the same time. E.g. the RAS
capability must be made available as soon as the PCI driver is bound,
the HDM decoder is setup later during port enumeration. Moreover,
during early setup it is still unknown if a certain capability is
needed. A central capability setup is therefore not possible,
capabilities must be individually enabled once needed during
initialization.
To avoid a duplicate register probe and overlapping I/O mappings, only
probe the Component Registers one time and store the Component
Register mapping in struct port. The stored mappings can be used later
to iomap the capability register range when enabling the capability,
which will be implemented in a follow-on patch.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/port.c | 26 ++++++++++++++++++++++++++
drivers/cxl/cxl.h | 2 ++
2 files changed, 28 insertions(+)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 679226023f0c..2e239fd33df9 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -688,6 +688,28 @@ static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
return ERR_PTR(rc);
}
+static int cxl_setup_comp_regs(struct device *dev, struct cxl_register_map *map,
+ resource_size_t component_reg_phys)
+{
+ if (component_reg_phys == CXL_RESOURCE_NONE)
+ return 0;
+
+ memset(map, 0, sizeof(*map));
+ map->dev = dev;
+ map->reg_type = CXL_REGLOC_RBI_COMPONENT;
+ map->resource = component_reg_phys;
+ map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE;
+
+ return cxl_setup_regs(map);
+}
+
+static inline int cxl_port_setup_regs(struct cxl_port *port,
+ resource_size_t component_reg_phys)
+{
+ return cxl_setup_comp_regs(&port->dev, &port->comp_map,
+ component_reg_phys);
+}
+
static struct cxl_port *__devm_cxl_add_port(struct device *host,
struct device *uport_dev,
resource_size_t component_reg_phys,
@@ -711,6 +733,10 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
if (rc)
goto err;
+ rc = cxl_port_setup_regs(port, component_reg_phys);
+ if (rc)
+ goto err;
+
rc = device_add(dev);
if (rc)
goto err;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index fe95f08acb69..37fa5b565362 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -547,6 +547,7 @@ struct cxl_dax_region {
* @regions: cxl_region_ref instances, regions mapped by this port
* @parent_dport: dport that points to this port in the parent
* @decoder_ida: allocator for decoder ids
+ * @comp_map: component register capability mappings
* @nr_dports: number of entries in @dports
* @hdm_end: track last allocated HDM decoder instance for allocation ordering
* @commit_end: cursor to track highest committed decoder for commit ordering
@@ -566,6 +567,7 @@ struct cxl_port {
struct xarray regions;
struct cxl_dport *parent_dport;
struct ida decoder_ida;
+ struct cxl_register_map comp_map;
int nr_dports;
int hdm_end;
int commit_end;
--
2.34.1
From: Dan Williams <[email protected]>
Prepare cxl_probe_rcrb() for retrieving more than just the component
register block. The RCH AER handling code wants to get back to the AER
capability that happens to be MMIO mapped rather then configuration
cycles.
Move RCRB specific downstream port data, like the RCRB base and the
AER capability offset, into its own data structure ('struct
cxl_rcrb_info') for cxl_probe_rcrb() to fill. Extend 'struct
cxl_dport' to include a 'struct cxl_rcrb_info' attribute.
This centralizes all RCRB scanning in one routine.
Signed-off-by: Dan Williams <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/core/core.h | 4 +++-
drivers/cxl/core/port.c | 4 ++--
drivers/cxl/core/regs.c | 5 +++--
drivers/cxl/cxl.h | 9 +++++++--
tools/testing/cxl/test/mock.c | 4 +++-
5 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index bd0a5788c696..b001669a5133 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -68,7 +68,9 @@ enum cxl_rcrb {
CXL_RCRB_DOWNSTREAM,
CXL_RCRB_UPSTREAM,
};
-resource_size_t __rcrb_to_component(struct device *dev, resource_size_t rcrb,
+struct cxl_rcrb_info;
+resource_size_t __rcrb_to_component(struct device *dev,
+ struct cxl_rcrb_info *ri,
enum cxl_rcrb which);
extern struct rw_semaphore cxl_dpa_rwsem;
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 45f5299af7a6..76888c75dae4 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -939,7 +939,8 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
return ERR_PTR(-ENOMEM);
if (rcrb != CXL_RESOURCE_NONE) {
- component_reg_phys = __rcrb_to_component(dport_dev, rcrb,
+ dport->rcrb.base = rcrb;
+ component_reg_phys = __rcrb_to_component(dport_dev, &dport->rcrb,
CXL_RCRB_DOWNSTREAM);
if (component_reg_phys == CXL_RESOURCE_NONE) {
dev_warn(dport_dev, "Invalid Component Registers in RCRB");
@@ -957,7 +958,6 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
dport->port_id = port_id;
dport->component_reg_phys = component_reg_phys;
dport->port = port;
- dport->rcrb = rcrb;
cond_cxl_root_lock(port);
rc = add_dport(port, dport);
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index 564dd430258a..6c4b33133918 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -332,10 +332,11 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
}
EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
-resource_size_t __rcrb_to_component(struct device *dev, resource_size_t rcrb,
+resource_size_t __rcrb_to_component(struct device *dev, struct cxl_rcrb_info *ri,
enum cxl_rcrb which)
{
resource_size_t component_reg_phys;
+ resource_size_t rcrb = ri->base;
void __iomem *addr;
u32 bar0, bar1;
u16 cmd;
@@ -400,6 +401,6 @@ resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
{
if (!dport->rch)
return CXL_RESOURCE_NONE;
- return __rcrb_to_component(dev, dport->rcrb, CXL_RCRB_UPSTREAM);
+ return __rcrb_to_component(dev, &dport->rcrb, CXL_RCRB_UPSTREAM);
}
EXPORT_SYMBOL_NS_GPL(cxl_rcd_component_reg_phys, CXL);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 28888bb0c088..7c8674079f1a 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -582,12 +582,17 @@ cxl_find_dport_by_dev(struct cxl_port *port, const struct device *dport_dev)
return xa_load(&port->dports, (unsigned long)dport_dev);
}
+struct cxl_rcrb_info {
+ resource_size_t base;
+ u16 aer_cap;
+};
+
/**
* struct cxl_dport - CXL downstream port
* @dport: PCI bridge or firmware device representing the downstream link
* @port_id: unique hardware identifier for dport in decoder target list
* @component_reg_phys: downstream port component registers
- * @rcrb: base address for the Root Complex Register Block
+ * @rcrb: Data about the Root Complex Register Block layout
* @rch: Indicate whether this dport was enumerated in RCH or VH mode
* @port: reference to cxl_port that contains this downstream port
*/
@@ -595,7 +600,7 @@ struct cxl_dport {
struct device *dport;
int port_id;
resource_size_t component_reg_phys;
- resource_size_t rcrb;
+ struct cxl_rcrb_info rcrb;
bool rch;
struct cxl_port *port;
};
diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c
index 30119a16ae85..dbeef5c6f606 100644
--- a/tools/testing/cxl/test/mock.c
+++ b/tools/testing/cxl/test/mock.c
@@ -271,8 +271,10 @@ struct cxl_dport *__wrap_devm_cxl_add_rch_dport(struct cxl_port *port,
if (ops && ops->is_mock_port(dport_dev)) {
dport = devm_cxl_add_dport(port, dport_dev, port_id,
CXL_RESOURCE_NONE);
- if (!IS_ERR(dport))
+ if (!IS_ERR(dport)) {
+ dport->rcrb.base = rcrb;
dport->rch = true;
+ }
} else
dport = devm_cxl_add_rch_dport(port, dport_dev, port_id, rcrb);
put_cxl_mock_ops(index);
--
2.34.1
From: Robert Richter <[email protected]>
Same as for ports, also store the downstream port's Component Register
mappings, use struct cxl_dport for that.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/core/port.c | 11 +++++++++++
drivers/cxl/cxl.h | 2 ++
2 files changed, 13 insertions(+)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 2e239fd33df9..45fe7d89f7f3 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -710,6 +710,13 @@ static inline int cxl_port_setup_regs(struct cxl_port *port,
component_reg_phys);
}
+static inline int cxl_dport_setup_regs(struct cxl_dport *dport,
+ resource_size_t component_reg_phys)
+{
+ return cxl_setup_comp_regs(dport->dport_dev, &dport->comp_map,
+ component_reg_phys);
+}
+
static struct cxl_port *__devm_cxl_add_port(struct device *host,
struct device *uport_dev,
resource_size_t component_reg_phys,
@@ -988,6 +995,10 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
dport->port_id = port_id;
dport->port = port;
+ rc = cxl_dport_setup_regs(dport, component_reg_phys);
+ if (rc)
+ return ERR_PTR(rc);
+
cond_cxl_root_lock(port);
rc = add_dport(port, dport);
cond_cxl_root_unlock(port);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 37fa5b565362..b1adca9b27ba 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -595,6 +595,7 @@ struct cxl_rcrb_info {
/**
* struct cxl_dport - CXL downstream port
* @dport_dev: PCI bridge or firmware device representing the downstream link
+ * @comp_map: component register capability mappings
* @port_id: unique hardware identifier for dport in decoder target list
* @rcrb: Data about the Root Complex Register Block layout
* @rch: Indicate whether this dport was enumerated in RCH or VH mode
@@ -602,6 +603,7 @@ struct cxl_rcrb_info {
*/
struct cxl_dport {
struct device *dport_dev;
+ struct cxl_register_map comp_map;
int port_id;
struct cxl_rcrb_info rcrb;
bool rch;
--
2.34.1
The endpoint implements component register setup code. Refactor it for
reuse with RCRB, downstream port, and upstream port setup.
Move PCI specifics from cxl_setup_regs() into cxl_pci_setup_regs().
Move cxl_setup_regs() into cxl/core/regs.c and export it. This also
includes supporting static functions cxl_map_registerblock(),
cxl_unmap_register_block() and cxl_probe_regs().
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/core/regs.c | 77 +++++++++++++++++++++++++++++++++++++++
drivers/cxl/cxl.h | 1 +
drivers/cxl/pci.c | 79 +++--------------------------------------
3 files changed, 83 insertions(+), 74 deletions(-)
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index 713e4a9ca35a..e035ad8827a4 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -338,6 +338,83 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
}
EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
+static int cxl_map_regblock(struct cxl_register_map *map)
+{
+ struct device *dev = map->dev;
+
+ map->base = ioremap(map->resource, map->max_size);
+ if (!map->base) {
+ dev_err(dev, "failed to map registers\n");
+ return -ENOMEM;
+ }
+
+ dev_dbg(dev, "Mapped CXL Memory Device resource %pa\n", &map->resource);
+ return 0;
+}
+
+static void cxl_unmap_regblock(struct cxl_register_map *map)
+{
+ iounmap(map->base);
+ map->base = NULL;
+}
+
+static int cxl_probe_regs(struct cxl_register_map *map)
+{
+ struct cxl_component_reg_map *comp_map;
+ struct cxl_device_reg_map *dev_map;
+ struct device *dev = map->dev;
+ void __iomem *base = map->base;
+
+ switch (map->reg_type) {
+ case CXL_REGLOC_RBI_COMPONENT:
+ comp_map = &map->component_map;
+ cxl_probe_component_regs(dev, base, comp_map);
+ if (!comp_map->hdm_decoder.valid) {
+ dev_err(dev, "HDM decoder registers not found\n");
+ return -ENXIO;
+ }
+
+ if (!comp_map->ras.valid)
+ dev_dbg(dev, "RAS registers not found\n");
+
+ dev_dbg(dev, "Set up component registers\n");
+ break;
+ case CXL_REGLOC_RBI_MEMDEV:
+ dev_map = &map->device_map;
+ cxl_probe_device_regs(dev, base, dev_map);
+ if (!dev_map->status.valid || !dev_map->mbox.valid ||
+ !dev_map->memdev.valid) {
+ dev_err(dev, "registers not found: %s%s%s\n",
+ !dev_map->status.valid ? "status " : "",
+ !dev_map->mbox.valid ? "mbox " : "",
+ !dev_map->memdev.valid ? "memdev " : "");
+ return -ENXIO;
+ }
+
+ dev_dbg(dev, "Probing device registers...\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int cxl_setup_regs(struct cxl_register_map *map)
+{
+ int rc;
+
+ rc = cxl_map_regblock(map);
+ if (rc)
+ return rc;
+
+ rc = cxl_probe_regs(map);
+ cxl_unmap_regblock(map);
+
+ return rc;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_setup_regs, CXL);
+
resource_size_t __rcrb_to_component(struct device *dev, struct cxl_rcrb_info *ri,
enum cxl_rcrb which)
{
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index bd68d5fabf21..ae265357170e 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -264,6 +264,7 @@ int cxl_map_device_regs(struct cxl_register_map *map,
enum cxl_regloc_type;
int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map);
+int cxl_setup_regs(struct cxl_register_map *map);
struct cxl_dport;
resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
struct cxl_dport *dport);
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 0a89b96e6a8d..ac17bc0430dc 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -274,70 +274,8 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
return 0;
}
-static int cxl_map_regblock(struct cxl_register_map *map)
-{
- struct device *dev = map->dev;
-
- map->base = ioremap(map->resource, map->max_size);
- if (!map->base) {
- dev_err(dev, "failed to map registers\n");
- return -ENOMEM;
- }
-
- dev_dbg(dev, "Mapped CXL Memory Device resource %pa\n", &map->resource);
- return 0;
-}
-
-static void cxl_unmap_regblock(struct cxl_register_map *map)
-{
- iounmap(map->base);
- map->base = NULL;
-}
-
-static int cxl_probe_regs(struct cxl_register_map *map)
-{
- struct cxl_component_reg_map *comp_map;
- struct cxl_device_reg_map *dev_map;
- struct device *dev = map->dev;
- void __iomem *base = map->base;
-
- switch (map->reg_type) {
- case CXL_REGLOC_RBI_COMPONENT:
- comp_map = &map->component_map;
- cxl_probe_component_regs(dev, base, comp_map);
- if (!comp_map->hdm_decoder.valid) {
- dev_err(dev, "HDM decoder registers not found\n");
- return -ENXIO;
- }
-
- if (!comp_map->ras.valid)
- dev_dbg(dev, "RAS registers not found\n");
-
- dev_dbg(dev, "Set up component registers\n");
- break;
- case CXL_REGLOC_RBI_MEMDEV:
- dev_map = &map->device_map;
- cxl_probe_device_regs(dev, base, dev_map);
- if (!dev_map->status.valid || !dev_map->mbox.valid ||
- !dev_map->memdev.valid) {
- dev_err(dev, "registers not found: %s%s%s\n",
- !dev_map->status.valid ? "status " : "",
- !dev_map->mbox.valid ? "mbox " : "",
- !dev_map->memdev.valid ? "memdev " : "");
- return -ENXIO;
- }
-
- dev_dbg(dev, "Probing device registers...\n");
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
- struct cxl_register_map *map)
+static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
+ struct cxl_register_map *map)
{
int rc;
@@ -345,14 +283,7 @@ static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
if (rc)
return rc;
- rc = cxl_map_regblock(map);
- if (rc)
- return rc;
-
- rc = cxl_probe_regs(map);
- cxl_unmap_regblock(map);
-
- return rc;
+ return cxl_setup_regs(map);
}
/*
@@ -683,7 +614,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev_warn(&pdev->dev,
"Device DVSEC not present, skip CXL.mem init\n");
- rc = cxl_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map);
+ rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map);
if (rc)
return rc;
@@ -696,7 +627,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* still be useful for management functions so don't return an error.
*/
cxlds->component_reg_phys = CXL_RESOURCE_NONE;
- rc = cxl_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
+ rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
if (rc)
dev_warn(&pdev->dev, "No component registers (%d)\n", rc);
--
2.34.1
From: Robert Richter <[email protected]>
When probing the Component Registers in function cxl_probe_regs()
there are also checks for the existence of the HDM and RAS
capabilities. The checks may fail for components that do not implement
the HDM capability causing the Component Registers setup to fail too.
Remove the checks for a generalized use of cxl_probe_regs() and check
them directly before mapping the RAS or HDM capabilities. This allows
it to setup other Component Registers esp. of an RCH Downstream Port,
which will be implemented in a follow-on patch.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/core/regs.c | 8 --------
drivers/cxl/pci.c | 2 ++
drivers/cxl/port.c | 5 ++++-
3 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index e035ad8827a4..e68848075bb6 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -369,14 +369,6 @@ static int cxl_probe_regs(struct cxl_register_map *map)
case CXL_REGLOC_RBI_COMPONENT:
comp_map = &map->component_map;
cxl_probe_component_regs(dev, base, comp_map);
- if (!comp_map->hdm_decoder.valid) {
- dev_err(dev, "HDM decoder registers not found\n");
- return -ENXIO;
- }
-
- if (!comp_map->ras.valid)
- dev_dbg(dev, "RAS registers not found\n");
-
dev_dbg(dev, "Set up component registers\n");
break;
case CXL_REGLOC_RBI_MEMDEV:
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index ac17bc0430dc..945ca0304d68 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -630,6 +630,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
if (rc)
dev_warn(&pdev->dev, "No component registers (%d)\n", rc);
+ else if (!map.component_map.ras.valid)
+ dev_dbg(&pdev->dev, "RAS registers not found\n");
cxlds->component_reg_phys = map.resource;
diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c
index 4cef2bf45ad2..01e84ea54f56 100644
--- a/drivers/cxl/port.c
+++ b/drivers/cxl/port.c
@@ -102,8 +102,11 @@ static int cxl_endpoint_port_probe(struct cxl_port *port)
return rc;
cxlhdm = devm_cxl_setup_hdm(port, &info);
- if (IS_ERR(cxlhdm))
+ if (IS_ERR(cxlhdm)) {
+ if (PTR_ERR(cxlhdm) == -ENODEV)
+ dev_err(&port->dev, "HDM decoder registers not found\n");
return PTR_ERR(cxlhdm);
+ }
/* Cache the data early to ensure is_visible() works */
read_cdat_data(port);
--
2.34.1
From: Robert Richter <[email protected]>
CXL RAS capabilities must be enabled and accessible as soon as the CXL
endpoint is detected in the PCI hierarchy and bound to the cxl_pci
driver. This needs to be independent of other modules such as cxl_port
or cxl_mem.
CXL RAS capabilities reside in the Component Registers. For an RCH
this is determined by probing RCRB which is implemented very late once
the CXL Memory Device is created.
Change this by moving the RCRB probe to the cxl_pci driver. Do this by
using a new introduced function cxl_pci_find_port() similar to
cxl_mem_find_port() to determine the involved dport by the endpoint's
PCI handle. Plug this into the existing cxl_pci_setup_regs() function
to setup Component Registers. Probe the RCRB in case the Component
Registers cannot be located through the CXL Register Locator
capability.
This unifies code and early sets up the Component Registers at the
same time for both, VH and RCH mode. Only the cxl_pci driver is
involved for this. This allows an early mapping of the CXL RAS
capability registers.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/core/port.c | 7 +++++
drivers/cxl/cxl.h | 2 ++
drivers/cxl/mem.c | 9 -------
drivers/cxl/pci.c | 57 ++++++++++++++++++++++++++++++++++-------
4 files changed, 57 insertions(+), 18 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index e0d2e7596440..679226023f0c 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -1480,6 +1480,13 @@ int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd)
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_ports, CXL);
+struct cxl_port *cxl_pci_find_port(struct pci_dev *pdev,
+ struct cxl_dport **dport)
+{
+ return find_cxl_port(pdev->dev.parent, dport);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_pci_find_port, CXL);
+
struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd,
struct cxl_dport **dport)
{
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 7fbc52b81554..fe95f08acb69 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -664,6 +664,8 @@ struct cxl_port *find_cxl_root(struct cxl_port *port);
int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd);
void cxl_bus_rescan(void);
void cxl_bus_drain(void);
+struct cxl_port *cxl_pci_find_port(struct pci_dev *pdev,
+ struct cxl_dport **dport);
struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd,
struct cxl_dport **dport);
bool schedule_cxl_memdev_detach(struct cxl_memdev *cxlmd);
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 7638a7f8f333..205e2e280aed 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -65,15 +65,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
ep->next = down;
}
- /*
- * The component registers for an RCD might come from the
- * host-bridge RCRB if they are not already mapped via the
- * typical register locator mechanism.
- */
- if (parent_dport->rch && cxlds->component_reg_phys == CXL_RESOURCE_NONE)
- cxlds->component_reg_phys =
- cxl_rcd_component_reg_phys(&cxlmd->dev, parent_dport);
-
endpoint = devm_cxl_add_port(host, &cxlmd->dev,
cxlds->component_reg_phys,
parent_dport);
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 945ca0304d68..99a75c54ee39 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -274,27 +274,66 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
return 0;
}
+/*
+ * Assume that any RCIEP that emits the CXL memory expander class code
+ * is an RCD
+ */
+static bool is_cxl_restricted(struct pci_dev *pdev)
+{
+ return pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END;
+}
+
+static int cxl_rcrb_get_comp_regs(struct pci_dev *pdev,
+ struct cxl_register_map *map)
+{
+ struct cxl_port *port;
+ struct cxl_dport *dport;
+ resource_size_t component_reg_phys;
+
+ *map = (struct cxl_register_map) {
+ .dev = &pdev->dev,
+ .resource = CXL_RESOURCE_NONE,
+ };
+
+ port = cxl_pci_find_port(pdev, &dport);
+ if (!port)
+ return -EPROBE_DEFER;
+
+ component_reg_phys = cxl_rcd_component_reg_phys(&pdev->dev, dport);
+
+ put_device(&port->dev);
+
+ if (component_reg_phys == CXL_RESOURCE_NONE)
+ return -ENXIO;
+
+ map->resource = component_reg_phys;
+ map->reg_type = CXL_REGLOC_RBI_COMPONENT;
+ map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE;
+
+ return 0;
+}
+
static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map)
{
int rc;
rc = cxl_find_regblock(pdev, type, map);
+
+ /*
+ * If the Register Locator DVSEC does not exist, check if it
+ * is an RCH and try to extract the Component Registers from
+ * an RCRB.
+ */
+ if (rc && type == CXL_REGLOC_RBI_COMPONENT && is_cxl_restricted(pdev))
+ rc = cxl_rcrb_get_comp_regs(pdev, map);
+
if (rc)
return rc;
return cxl_setup_regs(map);
}
-/*
- * Assume that any RCIEP that emits the CXL memory expander class code
- * is an RCD
- */
-static bool is_cxl_restricted(struct pci_dev *pdev)
-{
- return pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END;
-}
-
/*
* CXL v3.0 6.2.3 Table 6-4
* The table indicates that if PCIe Flit Mode is set, then CXL is in 256B flits
--
2.34.1
From: Robert Richter <[email protected]>
In Restricted CXL Device (RCD) mode a CXL device is exposed as an
RCiEP, but CXL downstream and upstream ports are not enumerated and
not visible in the PCIe hierarchy. [1] Protocol and link errors from
these non-enumerated ports are signaled as internal AER errors, either
Uncorrectable Internal Error (UIE) or Corrected Internal Errors (CIE)
via an RCEC.
Restricted CXL host (RCH) downstream port-detected errors have the
Requster ID of the RCEC set in the RCEC's AER Error Source ID
register. A CXL handler must then inspect the error status in various
CXL registers residing in the dport's component register space (CXL
RAS capability) or the dport's RCRB (PCIe AER extended
capability). [2]
Errors showing up in the RCEC's error handler must be handled and
connected to the CXL subsystem. Implement this by forwarding the error
to all CXL devices below the RCEC. Since the entire CXL device is
controlled only using PCIe Configuration Space of device 0, function
0, only pass it there [3]. The error handling is limited to currently
supported devices with the Memory Device class code set (CXL Type 3
Device, PCI_CLASS_MEMORY_CXL, 502h), handle downstream port errors in
the device's cxl_pci driver. Support for other CXL Device Types
(e.g. a CXL.cache Device) can be added later.
To handle downstream port errors in addition to errors directed to the
CXL endpoint device, a handler must also inspect the CXL RAS and PCIe
AER capabilities of the CXL downstream port the device is connected
to.
Since CXL downstream port errors are signaled using internal errors,
the handler requires those errors to be unmasked. This is subject of a
follow-on patch.
The reason for choosing this implementation is that the AER service
driver claims the RCEC device, but does not allow it to register a
custom specific handler to support CXL. Connecting the RCEC hard-wired
with a CXL handler does not work, as the CXL subsystem might not be
present all the time. The alternative to add an implementation to the
portdrv to allow the registration of a custom RCEC error handler isn't
worth doing it as CXL would be its only user. Instead, just check for
an CXL RCEC and pass it down to the connected CXL device's error
handler. With this approach the code can entirely be implemented in
the PCIe AER driver and is independent of the CXL subsystem. The CXL
driver only provides the handler.
[1] CXL 3.0 spec: 9.11.8 CXL Devices Attached to an RCH
[2] CXL 3.0 spec, 12.2.1.1 RCH Downstream Port-detected Errors
[3] CXL 3.0 spec, 8.1.3 PCIe DVSEC for CXL Devices
Co-developed-by: Terry Bowman <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Cc: "Oliver O'Halloran" <[email protected]>
Cc: Bjorn Helgaas <[email protected]>
Cc: [email protected]
Cc: [email protected]
Acked-by: Bjorn Helgaas <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/pci/pcie/Kconfig | 12 +++++
drivers/pci/pcie/aer.c | 96 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 106 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 228652a59f27..4f0e70fafe2d 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -49,6 +49,18 @@ config PCIEAER_INJECT
gotten from:
https://git.kernel.org/cgit/linux/kernel/git/gong.chen/aer-inject.git/
+config PCIEAER_CXL
+ bool "PCI Express CXL RAS support for Restricted Hosts (RCH)"
+ default y
+ depends on PCIEAER && CXL_PCI
+ help
+ Enables error handling of downstream ports of a CXL host
+ that is operating in RCD mode (Restricted CXL Host, RCH).
+ The downstream port reports AER errors to a given RCEC.
+ Errors are handled by the CXL memory device driver.
+
+ If unsure, say Y.
+
#
# PCI Express ECRC
#
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index d3344fcf1f79..c354ca5e8f2b 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -946,14 +946,100 @@ static bool find_source_device(struct pci_dev *parent,
return true;
}
+#ifdef CONFIG_PCIEAER_CXL
+
+static bool is_cxl_mem_dev(struct pci_dev *dev)
+{
+ /*
+ * The capability, status, and control fields in Device 0,
+ * Function 0 DVSEC control the CXL functionality of the
+ * entire device (CXL 3.0, 8.1.3).
+ */
+ if (dev->devfn != PCI_DEVFN(0, 0))
+ return false;
+
+ /*
+ * CXL Memory Devices must have the 502h class code set (CXL
+ * 3.0, 8.1.12.1).
+ */
+ if ((dev->class >> 8) != PCI_CLASS_MEMORY_CXL)
+ return false;
+
+ return true;
+}
+
+static bool cxl_error_is_native(struct pci_dev *dev)
+{
+ struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
+
+ if (pcie_ports_native)
+ return true;
+
+ return host->native_aer && host->native_cxl_error;
+}
+
+static bool is_internal_error(struct aer_err_info *info)
+{
+ if (info->severity == AER_CORRECTABLE)
+ return info->status & PCI_ERR_COR_INTERNAL;
+
+ return info->status & PCI_ERR_UNC_INTN;
+}
+
+static int cxl_rch_handle_error_iter(struct pci_dev *dev, void *data)
+{
+ struct aer_err_info *info = (struct aer_err_info *)data;
+ const struct pci_error_handlers *err_handler;
+
+ if (!is_cxl_mem_dev(dev) || !cxl_error_is_native(dev))
+ return 0;
+
+ /* protect dev->driver */
+ device_lock(&dev->dev);
+
+ err_handler = dev->driver ? dev->driver->err_handler : NULL;
+ if (!err_handler)
+ goto out;
+
+ if (info->severity == AER_CORRECTABLE) {
+ if (err_handler->cor_error_detected)
+ err_handler->cor_error_detected(dev);
+ } else if (err_handler->error_detected) {
+ if (info->severity == AER_NONFATAL)
+ err_handler->error_detected(dev, pci_channel_io_normal);
+ else if (info->severity == AER_FATAL)
+ err_handler->error_detected(dev, pci_channel_io_frozen);
+ }
+out:
+ device_unlock(&dev->dev);
+ return 0;
+}
+
+static void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info)
+{
+ /*
+ * Internal errors of an RCEC indicate an AER error in an
+ * RCH's downstream port. Check and handle them in the CXL.mem
+ * device driver.
+ */
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC &&
+ is_internal_error(info))
+ pcie_walk_rcec(dev, cxl_rch_handle_error_iter, info);
+}
+
+#else
+static inline void cxl_rch_handle_error(struct pci_dev *dev,
+ struct aer_err_info *info) { }
+#endif
+
/**
- * handle_error_source - handle logging error into an event log
+ * pci_aer_handle_error - handle logging error into an event log
* @dev: pointer to pci_dev data structure of error source device
* @info: comprehensive error information
*
* Invoked when an error being detected by Root Port.
*/
-static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
+static void pci_aer_handle_error(struct pci_dev *dev, struct aer_err_info *info)
{
int aer = dev->aer_cap;
@@ -977,6 +1063,12 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
pcie_do_recovery(dev, pci_channel_io_normal, aer_root_reset);
else if (info->severity == AER_FATAL)
pcie_do_recovery(dev, pci_channel_io_frozen, aer_root_reset);
+}
+
+static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
+{
+ cxl_rch_handle_error(dev, info);
+ pci_aer_handle_error(dev, info);
pci_dev_put(dev);
}
--
2.34.1
RCH downstream port error logging is missing in the current CXL driver. The
missing AER and RAS error logging is needed for communicating driver error
details to userspace. Update the driver to include PCIe AER and CXL RAS
error logging.
Add RCH downstream port error handling into the existing RCiEP handler.
The downstream port error handler is added to the RCiEP error handler
because the downstream port is implemented in a RCRB, is not PCI
enumerable, and as a result is not directly accessible to the PCI AER
root port driver. The AER root port driver calls the RCiEP handler for
handling RCD errors and RCH downstream port protocol errors.
Update existing RCiEP correctable and uncorrectable handlers to also call
the RCH handler. The RCH handler will read the RCH AER registers, check for
error severity, and if an error exists will log using an existing kernel
AER trace routine. The RCH handler will also log downstream port RAS errors
if they exist.
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/core/pci.c | 101 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 9cb39835e154..9e0eba5ccfc4 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -5,6 +5,7 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/pci-doe.h>
+#include <linux/aer.h>
#include <cxlpci.h>
#include <cxlmem.h>
#include <cxl.h>
@@ -747,10 +748,107 @@ static bool cxl_handle_endpoint_ras(struct cxl_dev_state *cxlds)
return __cxl_handle_ras(cxlds, cxlds->regs.ras);
}
+#ifdef CONFIG_PCIEAER_CXL
+
+static void cxl_handle_rdport_cor_ras(struct cxl_dev_state *cxlds,
+ struct cxl_dport *dport)
+{
+ return __cxl_handle_cor_ras(cxlds, dport->regs.ras);
+}
+
+static bool cxl_handle_rdport_ras(struct cxl_dev_state *cxlds,
+ struct cxl_dport *dport)
+{
+ return __cxl_handle_ras(cxlds, dport->regs.ras);
+}
+
+/*
+ * Copy the AER capability registers using 32 bit read accesses.
+ * This is necessary because RCRB AER capability is MMIO mapped. Clear the
+ * status after copying.
+ *
+ * @aer_base: base address of AER capability block in RCRB
+ * @aer_regs: destination for copying AER capability
+ */
+static bool cxl_rch_get_aer_info(void __iomem *aer_base,
+ struct aer_capability_regs *aer_regs)
+{
+ int read_cnt = sizeof(struct aer_capability_regs) / sizeof(u32);
+ u32 *aer_regs_buf = (u32 *)aer_regs;
+ int n;
+
+ if (!aer_base)
+ return false;
+
+ /* Use readl() to guarantee 32-bit accesses */
+ for (n = 0; n < read_cnt; n++)
+ aer_regs_buf[n] = readl(aer_base + n * sizeof(u32));
+
+ writel(aer_regs->uncor_status, aer_base + PCI_ERR_UNCOR_STATUS);
+ writel(aer_regs->cor_status, aer_base + PCI_ERR_COR_STATUS);
+
+ return true;
+}
+
+/* Get AER severity. Return false if there is no error. */
+static bool cxl_rch_get_aer_severity(struct aer_capability_regs *aer_regs,
+ int *severity)
+{
+ if (aer_regs->uncor_status & ~aer_regs->uncor_mask) {
+ if (aer_regs->uncor_status & PCI_ERR_ROOT_FATAL_RCV)
+ *severity = AER_FATAL;
+ else
+ *severity = AER_NONFATAL;
+ return true;
+ }
+
+ if (aer_regs->cor_status & ~aer_regs->cor_mask) {
+ *severity = AER_CORRECTABLE;
+ return true;
+ }
+
+ return false;
+}
+
+static void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds)
+{
+ struct pci_dev *pdev = to_pci_dev(cxlds->dev);
+ struct aer_capability_regs aer_regs;
+ struct cxl_dport *dport;
+ struct cxl_port *port;
+ int severity;
+
+ port = cxl_pci_find_port(pdev, &dport);
+ if (!port)
+ return;
+
+ put_device(&port->dev);
+
+ if (!cxl_rch_get_aer_info(dport->regs.dport_aer, &aer_regs))
+ return;
+
+ if (!cxl_rch_get_aer_severity(&aer_regs, &severity))
+ return;
+
+ pci_print_aer(pdev, severity, &aer_regs);
+
+ if (severity == AER_CORRECTABLE)
+ cxl_handle_rdport_cor_ras(cxlds, dport);
+ else
+ cxl_handle_rdport_ras(cxlds, dport);
+}
+
+#else
+static void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds) { }
+#endif
+
void cxl_cor_error_detected(struct pci_dev *pdev)
{
struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
+ if (cxlds->rcd)
+ cxl_handle_rdport_errors(cxlds);
+
cxl_handle_endpoint_cor_ras(cxlds);
}
EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, CXL);
@@ -763,6 +861,9 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
struct device *dev = &cxlmd->dev;
bool ue;
+ if (cxlds->rcd)
+ cxl_handle_rdport_errors(cxlds);
+
/*
* A frozen channel indicates an impending reset which is fatal to
* CXL.mem operation, and will likely crash the system. On the off
--
2.34.1
From: Robert Richter <[email protected]>
In order to move the RCH dport component register setup to cxl_pci the
base address must be stored in CXL device state (cxlds) for both
modes, RCH and VH. Store it in cxlds->component_reg_phys and use it
for endpoint creation.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/mem.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 4cc461c22b8b..7638a7f8f333 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -51,7 +51,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
struct cxl_port *parent_port = parent_dport->port;
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_port *endpoint, *iter, *down;
- resource_size_t component_reg_phys;
int rc;
/*
@@ -72,11 +71,11 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
* typical register locator mechanism.
*/
if (parent_dport->rch && cxlds->component_reg_phys == CXL_RESOURCE_NONE)
- component_reg_phys =
+ cxlds->component_reg_phys =
cxl_rcd_component_reg_phys(&cxlmd->dev, parent_dport);
- else
- component_reg_phys = cxlds->component_reg_phys;
- endpoint = devm_cxl_add_port(host, &cxlmd->dev, component_reg_phys,
+
+ endpoint = devm_cxl_add_port(host, &cxlmd->dev,
+ cxlds->component_reg_phys,
parent_dport);
if (IS_ERR(endpoint))
return PTR_ERR(endpoint);
--
2.34.1
From: Robert Richter <[email protected]>
The corresponding device of a register mapping is used for devm
operations and logging. For operations with struct cxl_register_map
the device needs to be kept track separately. To simpify the involved
function interfaces, add @dev to cxl_register_map.
While at it also reorder function arguments of cxl_map_device_regs()
and cxl_map_component_regs() to have the object @cxl_register_map
first.
In a result a bunch of functions are available to be used with a
@cxl_register_map object.
This patch is in preparation of reworking the component register setup
code.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/hdm.c | 4 ++--
drivers/cxl/core/regs.c | 18 ++++++++++++------
drivers/cxl/cxl.h | 10 ++++++----
drivers/cxl/pci.c | 23 +++++++++++------------
4 files changed, 31 insertions(+), 24 deletions(-)
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 7889ff203a34..5abfa9276dac 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -85,6 +85,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
struct cxl_component_regs *regs)
{
struct cxl_register_map map = {
+ .dev = &port->dev,
.resource = port->component_reg_phys,
.base = crb,
.max_size = CXL_COMPONENT_REG_BLOCK_SIZE,
@@ -97,8 +98,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
return -ENODEV;
}
- return cxl_map_component_regs(&port->dev, regs, &map,
- BIT(CXL_CM_CAP_CAP_ID_HDM));
+ return cxl_map_component_regs(&map, regs, BIT(CXL_CM_CAP_CAP_ID_HDM));
}
static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index 6c4b33133918..713e4a9ca35a 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -199,9 +199,11 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
return ret_val;
}
-int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
- struct cxl_register_map *map, unsigned long map_mask)
+int cxl_map_component_regs(struct cxl_register_map *map,
+ struct cxl_component_regs *regs,
+ unsigned long map_mask)
{
+ struct device *dev = map->dev;
struct mapinfo {
struct cxl_reg_map *rmap;
void __iomem **addr;
@@ -231,10 +233,10 @@ int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
}
EXPORT_SYMBOL_NS_GPL(cxl_map_component_regs, CXL);
-int cxl_map_device_regs(struct device *dev,
- struct cxl_device_regs *regs,
- struct cxl_register_map *map)
+int cxl_map_device_regs(struct cxl_register_map *map,
+ struct cxl_device_regs *regs)
{
+ struct device *dev = map->dev;
resource_size_t phys_addr = map->resource;
struct mapinfo {
struct cxl_reg_map *rmap;
@@ -302,7 +304,11 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
u32 regloc_size, regblocks;
int regloc, i;
- map->resource = CXL_RESOURCE_NONE;
+ *map = (struct cxl_register_map) {
+ .dev = &pdev->dev,
+ .resource = CXL_RESOURCE_NONE,
+ };
+
regloc = pci_find_dvsec_capability(pdev, PCI_DVSEC_VENDOR_ID_CXL,
CXL_DVSEC_REG_LOCATOR);
if (!regloc)
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 754cfe59ae37..bd68d5fabf21 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -231,6 +231,7 @@ struct cxl_device_reg_map {
/**
* struct cxl_register_map - DVSEC harvested register block mapping parameters
+ * @dev: device for devm operations and logging
* @base: virtual base of the register-block-BAR + @block_offset
* @resource: physical resource base of the register block
* @max_size: maximum mapping size to perform register search
@@ -239,6 +240,7 @@ struct cxl_device_reg_map {
* @device_map: cxl_reg_maps for device registers
*/
struct cxl_register_map {
+ struct device *dev;
void __iomem *base;
resource_size_t resource;
resource_size_t max_size;
@@ -253,11 +255,11 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
struct cxl_component_reg_map *map);
void cxl_probe_device_regs(struct device *dev, void __iomem *base,
struct cxl_device_reg_map *map);
-int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
- struct cxl_register_map *map,
+int cxl_map_component_regs(struct cxl_register_map *map,
+ struct cxl_component_regs *regs,
unsigned long map_mask);
-int cxl_map_device_regs(struct device *dev, struct cxl_device_regs *regs,
- struct cxl_register_map *map);
+int cxl_map_device_regs(struct cxl_register_map *map,
+ struct cxl_device_regs *regs);
enum cxl_regloc_type;
int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 0872f2233ed0..0a89b96e6a8d 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -274,9 +274,9 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
return 0;
}
-static int cxl_map_regblock(struct pci_dev *pdev, struct cxl_register_map *map)
+static int cxl_map_regblock(struct cxl_register_map *map)
{
- struct device *dev = &pdev->dev;
+ struct device *dev = map->dev;
map->base = ioremap(map->resource, map->max_size);
if (!map->base) {
@@ -288,18 +288,17 @@ static int cxl_map_regblock(struct pci_dev *pdev, struct cxl_register_map *map)
return 0;
}
-static void cxl_unmap_regblock(struct pci_dev *pdev,
- struct cxl_register_map *map)
+static void cxl_unmap_regblock(struct cxl_register_map *map)
{
iounmap(map->base);
map->base = NULL;
}
-static int cxl_probe_regs(struct pci_dev *pdev, struct cxl_register_map *map)
+static int cxl_probe_regs(struct cxl_register_map *map)
{
struct cxl_component_reg_map *comp_map;
struct cxl_device_reg_map *dev_map;
- struct device *dev = &pdev->dev;
+ struct device *dev = map->dev;
void __iomem *base = map->base;
switch (map->reg_type) {
@@ -346,12 +345,12 @@ static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
if (rc)
return rc;
- rc = cxl_map_regblock(pdev, map);
+ rc = cxl_map_regblock(map);
if (rc)
return rc;
- rc = cxl_probe_regs(pdev, map);
- cxl_unmap_regblock(pdev, map);
+ rc = cxl_probe_regs(map);
+ cxl_unmap_regblock(map);
return rc;
}
@@ -688,7 +687,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (rc)
return rc;
- rc = cxl_map_device_regs(&pdev->dev, &cxlds->regs.device_regs, &map);
+ rc = cxl_map_device_regs(&map, &cxlds->regs.device_regs);
if (rc)
return rc;
@@ -703,8 +702,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
cxlds->component_reg_phys = map.resource;
- rc = cxl_map_component_regs(&pdev->dev, &cxlds->regs.component,
- &map, BIT(CXL_CM_CAP_CAP_ID_RAS));
+ rc = cxl_map_component_regs(&map, &cxlds->regs.component,
+ BIT(CXL_CM_CAP_CAP_ID_RAS));
if (rc)
dev_dbg(&pdev->dev, "Failed to map RAS capability.\n");
--
2.34.1
The RCH root port contains root command AER registers that should not be
enabled.[1] Disable these to prevent root port interrupts.
[1] CXL 3.0 - 12.2.1.1 RCH Downstream Port-detected Errors
Signed-off-by: Terry Bowman <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
---
drivers/cxl/core/core.h | 6 ++++++
drivers/cxl/core/pci.c | 29 +++++++++++++++++++++++++++++
drivers/cxl/core/port.c | 3 +++
3 files changed, 38 insertions(+)
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 87467c633123..880bac9db376 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -86,4 +86,10 @@ enum cxl_poison_trace_type {
CXL_POISON_TRACE_CLEAR,
};
+#ifdef CONFIG_PCIEAER_CXL
+void cxl_disable_rch_root_ints(struct cxl_dport *dport);
+#else
+static inline void cxl_disable_rch_root_ints(struct cxl_dport *dport) { };
+#endif
+
#endif /* __CXL_CORE_H__ */
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 9e0eba5ccfc4..39a2f9f4f115 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -838,6 +838,35 @@ static void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds)
cxl_handle_rdport_ras(cxlds, dport);
}
+void cxl_disable_rch_root_ints(struct cxl_dport *dport)
+{
+ void __iomem *aer_base = dport->regs.dport_aer;
+ struct pci_host_bridge *bridge;
+ u32 aer_cmd_mask, aer_cmd;
+
+ if (!aer_base)
+ return;
+
+ bridge = to_pci_host_bridge(dport->dport_dev);
+
+ /*
+ * Disable RCH root port command interrupts.
+ * CXL 3.0 12.2.1.1 - RCH Downstream Port-detected Errors
+ *
+ * This sequnce may not be necessary. CXL spec states disabling
+ * the root cmd register's interrupts is required. But, PCI spec
+ * shows these are disabled by default on reset.
+ */
+ if (bridge->native_cxl_error) {
+ aer_cmd_mask = (PCI_ERR_ROOT_CMD_COR_EN |
+ PCI_ERR_ROOT_CMD_NONFATAL_EN |
+ PCI_ERR_ROOT_CMD_FATAL_EN);
+ aer_cmd = readl(aer_base + PCI_ERR_ROOT_COMMAND);
+ aer_cmd &= ~aer_cmd_mask;
+ writel(aer_cmd, aer_base + PCI_ERR_ROOT_COMMAND);
+ }
+}
+
#else
static void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds) { }
#endif
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 87a12e69aa8e..2d812bbaf05f 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -1035,6 +1035,9 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
cxl_dport_map_regs(dport);
+ if (dport->rch)
+ cxl_disable_rch_root_ints(dport);
+
cond_cxl_root_lock(port);
rc = add_dport(port, dport);
cond_cxl_root_unlock(port);
--
2.34.1
From: Robert Richter <[email protected]>
During a Host Bridge's downstream port enumeration the CHBS entries in
the CEDT table are parsed, its Component Register base address
extracted and then stored in struct cxl_dport. The CHBS may contain
either the RCRB (RCH mode) or the Host Bridge's Component Registers
(CHBCR, VH mode). The RCRB further contains the CXL downstream port
register base address, while in VH mode the CXL Downstream Switch
Ports are visible in the PCI hierarchy and the DP's component regs are
disovered using the CXL DVSEC register locator capability. The
Component Registers derived from the CHBS for both modes are different
and thus also must be treated differently. That is, in RCH mode, the
component regs base should be bound to the dport, but in VH mode to
the CXL host bridge's port object.
The current implementation stores the CHBCR in addition in struct
cxl_dport and copies it later from there to struct cxl_port. As a
result, the dport contains the wrong Component Registers base address
and, e.g. the RAS capability of a CXL Root Port cannot be detected.
To fix the CHBCR binding, attach it directly to the Host Bridge's
@cxl_port structure. Do this during port creation of the Host Bridge
in add_host_bridge_uport(). Factor out CHBS parsing code in
add_host_bridge_dport() and use it in both functions.
Co-developed-by: Dan Williams <[email protected]>
Signed-off-by: Dan Williams <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/acpi.c | 91 ++++++++++++++++++++++++++++++++--------------
1 file changed, 63 insertions(+), 28 deletions(-)
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 0c975ee684b0..89ee01323d43 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -335,13 +335,13 @@ struct cxl_chbs_context {
u32 cxl_version;
};
-static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
- const unsigned long end)
+static int cxl_get_chbs_iter(union acpi_subtable_headers *header, void *arg,
+ const unsigned long end)
{
struct cxl_chbs_context *ctx = arg;
struct acpi_cedt_chbs *chbs;
- if (ctx->base)
+ if (ctx->base != CXL_RESOURCE_NONE)
return 0;
chbs = (struct acpi_cedt_chbs *) header;
@@ -350,8 +350,6 @@ static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
return 0;
ctx->cxl_version = chbs->cxl_version;
- ctx->base = CXL_RESOURCE_NONE;
-
if (!chbs->base)
return 0;
@@ -364,11 +362,35 @@ static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
return 0;
}
+static int cxl_get_chbs(struct device *dev, struct acpi_device *hb,
+ struct cxl_chbs_context *ctx)
+{
+ unsigned long long uid;
+ int rc;
+
+ rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid);
+ if (rc != AE_OK) {
+ dev_err(dev, "unable to retrieve _UID\n");
+ return -ENOENT;
+ }
+
+ dev_dbg(dev, "UID found: %lld\n", uid);
+ *ctx = (struct cxl_chbs_context) {
+ .dev = dev,
+ .uid = uid,
+ .base = CXL_RESOURCE_NONE,
+ .cxl_version = UINT_MAX,
+ };
+
+ acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs_iter, ctx);
+
+ return 0;
+}
+
static int add_host_bridge_dport(struct device *match, void *arg)
{
acpi_status rc;
struct device *bridge;
- unsigned long long uid;
struct cxl_dport *dport;
struct cxl_chbs_context ctx;
struct acpi_pci_root *pci_root;
@@ -379,41 +401,38 @@ static int add_host_bridge_dport(struct device *match, void *arg)
if (!hb)
return 0;
- rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid);
- if (rc != AE_OK) {
- dev_err(match, "unable to retrieve _UID\n");
- return -ENODEV;
- }
-
- dev_dbg(match, "UID found: %lld\n", uid);
-
- ctx = (struct cxl_chbs_context) {
- .dev = match,
- .uid = uid,
- };
- acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs, &ctx);
+ rc = cxl_get_chbs(match, hb, &ctx);
+ if (rc)
+ return rc;
- if (!ctx.base) {
+ if (ctx.cxl_version == UINT_MAX) {
dev_warn(match, "No CHBS found for Host Bridge (UID %lld)\n",
- uid);
+ ctx.uid);
return 0;
}
if (ctx.base == CXL_RESOURCE_NONE) {
dev_warn(match, "CHBS invalid for Host Bridge (UID %lld)\n",
- uid);
+ ctx.uid);
return 0;
}
pci_root = acpi_pci_find_root(hb->handle);
bridge = pci_root->bus->bridge;
+ /*
+ * In RCH mode, bind the component regs base to the dport. In
+ * VH mode it will be bound to the CXL host bridge's port
+ * object later in add_host_bridge_uport().
+ */
if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) {
- dev_dbg(match, "RCRB found for UID %lld: %pa\n", uid, &ctx.base);
- dport = devm_cxl_add_rch_dport(root_port, bridge, uid, ctx.base);
+ dev_dbg(match, "RCRB found for UID %lld: %pa\n", ctx.uid,
+ &ctx.base);
+ dport = devm_cxl_add_rch_dport(root_port, bridge, ctx.uid,
+ ctx.base);
} else {
- dev_dbg(match, "CHBCR found for UID %lld: %pa\n", uid, &ctx.base);
- dport = devm_cxl_add_dport(root_port, bridge, uid, ctx.base);
+ dport = devm_cxl_add_dport(root_port, bridge, ctx.uid,
+ CXL_RESOURCE_NONE);
}
if (IS_ERR(dport))
@@ -435,6 +454,8 @@ static int add_host_bridge_uport(struct device *match, void *arg)
struct cxl_dport *dport;
struct cxl_port *port;
struct device *bridge;
+ struct cxl_chbs_context ctx;
+ resource_size_t component_reg_phys;
int rc;
if (!hb)
@@ -453,12 +474,26 @@ static int add_host_bridge_uport(struct device *match, void *arg)
return 0;
}
+ rc = cxl_get_chbs(match, hb, &ctx);
+ if (rc)
+ return rc;
+
+ if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) {
+ dev_warn(bridge,
+ "CXL CHBS version mismatch, skip port registration\n");
+ return 0;
+ }
+
+ component_reg_phys = ctx.base;
+ if (component_reg_phys != CXL_RESOURCE_NONE)
+ dev_dbg(match, "CHBCR found for UID %lld: %pa\n",
+ ctx.uid, &component_reg_phys);
+
rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
if (rc)
return rc;
- port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
- dport);
+ port = devm_cxl_add_port(host, bridge, component_reg_phys, dport);
if (IS_ERR(port))
return PTR_ERR(port);
--
2.34.1
From: Robert Richter <[email protected]>
Now, that the Component Register mappings are stored, use them to
enable and map the HDM decoder capabilities. The Component Registers
do not need to be probed again for this, remove probing code.
The HDM capability applies to Endpoints, USPs and VH Host Bridges. The
Endpoint's component register mappings are located in the cxlds and
else in the port's structure. Provide a helper function
cxl_port_get_comp_map() to locate the mappings depending on the
component's type.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/core/hdm.c | 59 +++++++++++++++++++++---------------------
1 file changed, 30 insertions(+), 29 deletions(-)
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 5abfa9276dac..8dcd9f0b22d8 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -81,26 +81,6 @@ static void parse_hdm_decoder_caps(struct cxl_hdm *cxlhdm)
cxlhdm->interleave_mask |= GENMASK(14, 12);
}
-static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
- struct cxl_component_regs *regs)
-{
- struct cxl_register_map map = {
- .dev = &port->dev,
- .resource = port->component_reg_phys,
- .base = crb,
- .max_size = CXL_COMPONENT_REG_BLOCK_SIZE,
- };
-
- cxl_probe_component_regs(&port->dev, crb, &map.component_map);
- if (!map.component_map.hdm_decoder.valid) {
- dev_dbg(&port->dev, "HDM decoder registers not implemented\n");
- /* unique error code to indicate no HDM decoder capability */
- return -ENODEV;
- }
-
- return cxl_map_component_regs(&map, regs, BIT(CXL_CM_CAP_CAP_ID_HDM));
-}
-
static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
{
struct cxl_hdm *cxlhdm;
@@ -145,6 +125,22 @@ static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
return true;
}
+static struct cxl_register_map *cxl_port_get_comp_map(struct cxl_port *port)
+{
+ /*
+ * HDM capability applies to Endpoints, USPs and VH Host
+ * Bridges. The Endpoint's component register mappings are
+ * located in the cxlds.
+ */
+ if (is_cxl_endpoint(port)) {
+ struct cxl_memdev *memdev = to_cxl_memdev(port->uport_dev);
+
+ return &memdev->cxlds->comp_map;
+ }
+
+ return &port->comp_map;
+}
+
/**
* devm_cxl_setup_hdm - map HDM decoder component registers
* @port: cxl_port to map
@@ -155,7 +151,7 @@ struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
{
struct device *dev = &port->dev;
struct cxl_hdm *cxlhdm;
- void __iomem *crb;
+ struct cxl_register_map *comp_map;
int rc;
cxlhdm = devm_kzalloc(dev, sizeof(*cxlhdm), GFP_KERNEL);
@@ -164,19 +160,24 @@ struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
cxlhdm->port = port;
dev_set_drvdata(dev, cxlhdm);
- crb = ioremap(port->component_reg_phys, CXL_COMPONENT_REG_BLOCK_SIZE);
- if (!crb && info && info->mem_enabled) {
- cxlhdm->decoder_count = info->ranges;
- return cxlhdm;
- } else if (!crb) {
+ comp_map = cxl_port_get_comp_map(port);
+
+ if (!comp_map->component_map.hdm_decoder.valid) {
+ dev_dbg(&port->dev, "HDM decoder registers not found\n");
+ if (info && info->mem_enabled) {
+ cxlhdm->decoder_count = info->ranges;
+ return cxlhdm;
+ }
dev_err(dev, "No component registers mapped\n");
return ERR_PTR(-ENXIO);
}
- rc = map_hdm_decoder_regs(port, crb, &cxlhdm->regs);
- iounmap(crb);
- if (rc)
+ rc = cxl_map_component_regs(comp_map, &cxlhdm->regs,
+ BIT(CXL_CM_CAP_CAP_ID_HDM));
+ if (rc) {
+ dev_dbg(dev, "Failed to map HDM capability.\n");
return ERR_PTR(rc);
+ }
parse_hdm_decoder_caps(cxlhdm);
if (cxlhdm->decoder_count == 0) {
--
2.34.1
From: Robert Richter <[email protected]>
Just moving code to reorder functions to later share cxl_get_chbs()
with add_host_bridge_uport().
This makes changes in the next patch visible. No other changes at all.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/acpi.c | 90 +++++++++++++++++++++++-----------------------
1 file changed, 45 insertions(+), 45 deletions(-)
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 70cd9ac73a8b..0c975ee684b0 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -327,51 +327,6 @@ __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
return NULL;
}
-/*
- * A host bridge is a dport to a CFMWS decode and it is a uport to the
- * dport (PCIe Root Ports) in the host bridge.
- */
-static int add_host_bridge_uport(struct device *match, void *arg)
-{
- struct cxl_port *root_port = arg;
- struct device *host = root_port->dev.parent;
- struct acpi_device *hb = to_cxl_host_bridge(host, match);
- struct acpi_pci_root *pci_root;
- struct cxl_dport *dport;
- struct cxl_port *port;
- struct device *bridge;
- int rc;
-
- if (!hb)
- return 0;
-
- pci_root = acpi_pci_find_root(hb->handle);
- bridge = pci_root->bus->bridge;
- dport = cxl_find_dport_by_dev(root_port, bridge);
- if (!dport) {
- dev_dbg(host, "host bridge expected and not found\n");
- return 0;
- }
-
- if (dport->rch) {
- dev_info(bridge, "host supports CXL (restricted)\n");
- return 0;
- }
-
- rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
- if (rc)
- return rc;
-
- port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
- dport);
- if (IS_ERR(port))
- return PTR_ERR(port);
-
- dev_info(bridge, "host supports CXL\n");
-
- return 0;
-}
-
/* Note, @dev is used by mock_acpi_table_parse_cedt() */
struct cxl_chbs_context {
struct device *dev;
@@ -467,6 +422,51 @@ static int add_host_bridge_dport(struct device *match, void *arg)
return 0;
}
+/*
+ * A host bridge is a dport to a CFMWS decode and it is a uport to the
+ * dport (PCIe Root Ports) in the host bridge.
+ */
+static int add_host_bridge_uport(struct device *match, void *arg)
+{
+ struct cxl_port *root_port = arg;
+ struct device *host = root_port->dev.parent;
+ struct acpi_device *hb = to_cxl_host_bridge(host, match);
+ struct acpi_pci_root *pci_root;
+ struct cxl_dport *dport;
+ struct cxl_port *port;
+ struct device *bridge;
+ int rc;
+
+ if (!hb)
+ return 0;
+
+ pci_root = acpi_pci_find_root(hb->handle);
+ bridge = pci_root->bus->bridge;
+ dport = cxl_find_dport_by_dev(root_port, bridge);
+ if (!dport) {
+ dev_dbg(host, "host bridge expected and not found\n");
+ return 0;
+ }
+
+ if (dport->rch) {
+ dev_info(bridge, "host supports CXL (restricted)\n");
+ return 0;
+ }
+
+ rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
+ if (rc)
+ return rc;
+
+ port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
+ dport);
+ if (IS_ERR(port))
+ return PTR_ERR(port);
+
+ dev_info(bridge, "host supports CXL\n");
+
+ return 0;
+}
+
static int add_root_nvdimm_bridge(struct device *match, void *data)
{
struct cxl_decoder *cxld;
--
2.34.1
From: Dan Williams <[email protected]>
For symmetry with the recent rename of ->dport_dev for a 'struct
cxl_dport', add the "_dev" suffix to the ->uport property of a 'struct
cxl_port'. These devices represent the downstream-port-device and
upstream-port-device respectively in the CXL/PCIe topology.
Signed-off-by: Dan Williams <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/pci.c | 4 +--
drivers/cxl/core/port.c | 61 +++++++++++++++++++----------------
drivers/cxl/core/region.c | 48 ++++++++++++++-------------
drivers/cxl/cxl.h | 13 ++++----
drivers/cxl/cxlmem.h | 4 +--
drivers/cxl/mem.c | 2 +-
drivers/cxl/port.c | 2 +-
tools/testing/cxl/test/cxl.c | 20 ++++++------
tools/testing/cxl/test/mock.c | 10 +++---
9 files changed, 86 insertions(+), 78 deletions(-)
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 67f4ab6daa34..375f01c6cad6 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -67,7 +67,7 @@ static int match_add_dports(struct pci_dev *pdev, void *data)
/**
* devm_cxl_port_enumerate_dports - enumerate downstream ports of the upstream port
- * @port: cxl_port whose ->uport is the upstream of dports to be enumerated
+ * @port: cxl_port whose ->uport_dev is the upstream of dports to be enumerated
*
* Returns a positive number of dports enumerated or a negative error
* code.
@@ -622,7 +622,7 @@ static int cxl_cdat_read_table(struct device *dev,
*/
void read_cdat_data(struct cxl_port *port)
{
- struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport);
+ struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev);
struct device *host = cxlmd->dev.parent;
struct device *dev = &port->dev;
struct pci_doe_mb *cdat_doe;
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 7d3079f5b7b5..cdfe0ea7a2e9 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -561,9 +561,9 @@ static void unregister_port(void *_port)
* unregistered while holding their parent port lock.
*/
if (!parent)
- lock_dev = port->uport;
+ lock_dev = port->uport_dev;
else if (is_cxl_root(parent))
- lock_dev = parent->uport;
+ lock_dev = parent->uport_dev;
else
lock_dev = &parent->dev;
@@ -583,7 +583,8 @@ static int devm_cxl_link_uport(struct device *host, struct cxl_port *port)
{
int rc;
- rc = sysfs_create_link(&port->dev.kobj, &port->uport->kobj, "uport");
+ rc = sysfs_create_link(&port->dev.kobj, &port->uport_dev->kobj,
+ "uport");
if (rc)
return rc;
return devm_add_action_or_reset(host, cxl_unlink_uport, port);
@@ -614,7 +615,7 @@ static int devm_cxl_link_parent_dport(struct device *host,
static struct lock_class_key cxl_port_key;
-static struct cxl_port *cxl_port_alloc(struct device *uport,
+static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
resource_size_t component_reg_phys,
struct cxl_dport *parent_dport)
{
@@ -630,7 +631,7 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
if (rc < 0)
goto err;
port->id = rc;
- port->uport = uport;
+ port->uport_dev = uport_dev;
/*
* The top-level cxl_port "cxl_root" does not have a cxl_port as
@@ -660,10 +661,11 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
else if (parent_dport->rch)
port->host_bridge = parent_dport->dport_dev;
else
- port->host_bridge = iter->uport;
- dev_dbg(uport, "host-bridge: %s\n", dev_name(port->host_bridge));
+ port->host_bridge = iter->uport_dev;
+ dev_dbg(uport_dev, "host-bridge: %s\n",
+ dev_name(port->host_bridge));
} else
- dev->parent = uport;
+ dev->parent = uport_dev;
port->component_reg_phys = component_reg_phys;
ida_init(&port->decoder_ida);
@@ -687,7 +689,7 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
}
static struct cxl_port *__devm_cxl_add_port(struct device *host,
- struct device *uport,
+ struct device *uport_dev,
resource_size_t component_reg_phys,
struct cxl_dport *parent_dport)
{
@@ -695,12 +697,12 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
struct device *dev;
int rc;
- port = cxl_port_alloc(uport, component_reg_phys, parent_dport);
+ port = cxl_port_alloc(uport_dev, component_reg_phys, parent_dport);
if (IS_ERR(port))
return port;
dev = &port->dev;
- if (is_cxl_memdev(uport))
+ if (is_cxl_memdev(uport_dev))
rc = dev_set_name(dev, "endpoint%d", port->id);
else if (parent_dport)
rc = dev_set_name(dev, "port%d", port->id);
@@ -735,28 +737,29 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
/**
* devm_cxl_add_port - register a cxl_port in CXL memory decode hierarchy
* @host: host device for devm operations
- * @uport: "physical" device implementing this upstream port
+ * @uport_dev: "physical" device implementing this upstream port
* @component_reg_phys: (optional) for configurable cxl_port instances
* @parent_dport: next hop up in the CXL memory decode hierarchy
*/
-struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
+struct cxl_port *devm_cxl_add_port(struct device *host,
+ struct device *uport_dev,
resource_size_t component_reg_phys,
struct cxl_dport *parent_dport)
{
struct cxl_port *port, *parent_port;
- port = __devm_cxl_add_port(host, uport, component_reg_phys,
+ port = __devm_cxl_add_port(host, uport_dev, component_reg_phys,
parent_dport);
parent_port = parent_dport ? parent_dport->port : NULL;
if (IS_ERR(port)) {
- dev_dbg(uport, "Failed to add%s%s%s: %ld\n",
+ dev_dbg(uport_dev, "Failed to add%s%s%s: %ld\n",
parent_port ? " port to " : "",
parent_port ? dev_name(&parent_port->dev) : "",
parent_port ? "" : " root port",
PTR_ERR(port));
} else {
- dev_dbg(uport, "%s added%s%s%s\n",
+ dev_dbg(uport_dev, "%s added%s%s%s\n",
dev_name(&port->dev),
parent_port ? " to " : "",
parent_port ? dev_name(&parent_port->dev) : "",
@@ -773,33 +776,34 @@ struct pci_bus *cxl_port_to_pci_bus(struct cxl_port *port)
if (is_cxl_root(port))
return NULL;
- if (dev_is_pci(port->uport)) {
- struct pci_dev *pdev = to_pci_dev(port->uport);
+ if (dev_is_pci(port->uport_dev)) {
+ struct pci_dev *pdev = to_pci_dev(port->uport_dev);
return pdev->subordinate;
}
- return xa_load(&cxl_root_buses, (unsigned long)port->uport);
+ return xa_load(&cxl_root_buses, (unsigned long)port->uport_dev);
}
EXPORT_SYMBOL_NS_GPL(cxl_port_to_pci_bus, CXL);
-static void unregister_pci_bus(void *uport)
+static void unregister_pci_bus(void *uport_dev)
{
- xa_erase(&cxl_root_buses, (unsigned long)uport);
+ xa_erase(&cxl_root_buses, (unsigned long)uport_dev);
}
-int devm_cxl_register_pci_bus(struct device *host, struct device *uport,
+int devm_cxl_register_pci_bus(struct device *host, struct device *uport_dev,
struct pci_bus *bus)
{
int rc;
- if (dev_is_pci(uport))
+ if (dev_is_pci(uport_dev))
return -EINVAL;
- rc = xa_insert(&cxl_root_buses, (unsigned long)uport, bus, GFP_KERNEL);
+ rc = xa_insert(&cxl_root_buses, (unsigned long)uport_dev, bus,
+ GFP_KERNEL);
if (rc)
return rc;
- return devm_add_action_or_reset(host, unregister_pci_bus, uport);
+ return devm_add_action_or_reset(host, unregister_pci_bus, uport_dev);
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_register_pci_bus, CXL);
@@ -920,7 +924,7 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
int rc;
if (is_cxl_root(port))
- host = port->uport;
+ host = port->uport_dev;
else
host = &port->dev;
@@ -1374,7 +1378,7 @@ static int add_port_attach_ep(struct cxl_memdev *cxlmd,
rc = PTR_ERR(port);
else {
dev_dbg(&cxlmd->dev, "add to new port %s:%s\n",
- dev_name(&port->dev), dev_name(port->uport));
+ dev_name(&port->dev), dev_name(port->uport_dev));
rc = cxl_add_ep(dport, &cxlmd->dev);
if (rc == -EBUSY) {
/*
@@ -1436,7 +1440,8 @@ int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd)
if (port) {
dev_dbg(&cxlmd->dev,
"found already registered port %s:%s\n",
- dev_name(&port->dev), dev_name(port->uport));
+ dev_name(&port->dev),
+ dev_name(port->uport_dev));
rc = cxl_add_ep(dport, &cxlmd->dev);
/*
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 13cda989d944..39825e5301d0 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -906,10 +906,10 @@ static int cxl_port_attach_region(struct cxl_port *port,
dev_dbg(&cxlr->dev,
"%s:%s %s add: %s:%s @ %d next: %s nr_eps: %d nr_targets: %d\n",
- dev_name(port->uport), dev_name(&port->dev),
+ dev_name(port->uport_dev), dev_name(&port->dev),
dev_name(&cxld->dev), dev_name(&cxlmd->dev),
dev_name(&cxled->cxld.dev), pos,
- ep ? ep->next ? dev_name(ep->next->uport) :
+ ep ? ep->next ? dev_name(ep->next->uport_dev) :
dev_name(&cxlmd->dev) :
"none",
cxl_rr->nr_eps, cxl_rr->nr_targets);
@@ -984,7 +984,7 @@ static int check_last_peer(struct cxl_endpoint_decoder *cxled,
*/
if (pos < distance) {
dev_dbg(&cxlr->dev, "%s:%s: cannot host %s:%s at %d\n",
- dev_name(port->uport), dev_name(&port->dev),
+ dev_name(port->uport_dev), dev_name(&port->dev),
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
return -ENXIO;
}
@@ -994,7 +994,7 @@ static int check_last_peer(struct cxl_endpoint_decoder *cxled,
if (ep->dport != ep_peer->dport) {
dev_dbg(&cxlr->dev,
"%s:%s: %s:%s pos %d mismatched peer %s:%s\n",
- dev_name(port->uport), dev_name(&port->dev),
+ dev_name(port->uport_dev), dev_name(&port->dev),
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos,
dev_name(&cxlmd_peer->dev),
dev_name(&cxled_peer->cxld.dev));
@@ -1026,7 +1026,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
*/
if (!is_power_of_2(cxl_rr->nr_targets)) {
dev_dbg(&cxlr->dev, "%s:%s: invalid target count %d\n",
- dev_name(port->uport), dev_name(&port->dev),
+ dev_name(port->uport_dev), dev_name(&port->dev),
cxl_rr->nr_targets);
return -EINVAL;
}
@@ -1076,7 +1076,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
rc = granularity_to_eig(parent_ig, &peig);
if (rc) {
dev_dbg(&cxlr->dev, "%s:%s: invalid parent granularity: %d\n",
- dev_name(parent_port->uport),
+ dev_name(parent_port->uport_dev),
dev_name(&parent_port->dev), parent_ig);
return rc;
}
@@ -1084,7 +1084,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
rc = ways_to_eiw(parent_iw, &peiw);
if (rc) {
dev_dbg(&cxlr->dev, "%s:%s: invalid parent interleave: %d\n",
- dev_name(parent_port->uport),
+ dev_name(parent_port->uport_dev),
dev_name(&parent_port->dev), parent_iw);
return rc;
}
@@ -1093,7 +1093,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
rc = ways_to_eiw(iw, &eiw);
if (rc) {
dev_dbg(&cxlr->dev, "%s:%s: invalid port interleave: %d\n",
- dev_name(port->uport), dev_name(&port->dev), iw);
+ dev_name(port->uport_dev), dev_name(&port->dev), iw);
return rc;
}
@@ -1113,7 +1113,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
rc = eig_to_granularity(eig, &ig);
if (rc) {
dev_dbg(&cxlr->dev, "%s:%s: invalid interleave: %d\n",
- dev_name(port->uport), dev_name(&port->dev),
+ dev_name(port->uport_dev), dev_name(&port->dev),
256 << eig);
return rc;
}
@@ -1126,11 +1126,11 @@ static int cxl_port_setup_targets(struct cxl_port *port,
((cxld->flags & CXL_DECODER_F_ENABLE) == 0)) {
dev_err(&cxlr->dev,
"%s:%s %s expected iw: %d ig: %d %pr\n",
- dev_name(port->uport), dev_name(&port->dev),
+ dev_name(port->uport_dev), dev_name(&port->dev),
__func__, iw, ig, p->res);
dev_err(&cxlr->dev,
"%s:%s %s got iw: %d ig: %d state: %s %#llx:%#llx\n",
- dev_name(port->uport), dev_name(&port->dev),
+ dev_name(port->uport_dev), dev_name(&port->dev),
__func__, cxld->interleave_ways,
cxld->interleave_granularity,
(cxld->flags & CXL_DECODER_F_ENABLE) ?
@@ -1147,20 +1147,20 @@ static int cxl_port_setup_targets(struct cxl_port *port,
.end = p->res->end,
};
}
- dev_dbg(&cxlr->dev, "%s:%s iw: %d ig: %d\n", dev_name(port->uport),
+ dev_dbg(&cxlr->dev, "%s:%s iw: %d ig: %d\n", dev_name(port->uport_dev),
dev_name(&port->dev), iw, ig);
add_target:
if (cxl_rr->nr_targets_set == cxl_rr->nr_targets) {
dev_dbg(&cxlr->dev,
"%s:%s: targets full trying to add %s:%s at %d\n",
- dev_name(port->uport), dev_name(&port->dev),
+ dev_name(port->uport_dev), dev_name(&port->dev),
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
return -ENXIO;
}
if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags)) {
if (cxlsd->target[cxl_rr->nr_targets_set] != ep->dport) {
dev_dbg(&cxlr->dev, "%s:%s: %s expected %s at %d\n",
- dev_name(port->uport), dev_name(&port->dev),
+ dev_name(port->uport_dev), dev_name(&port->dev),
dev_name(&cxlsd->cxld.dev),
dev_name(ep->dport->dport_dev),
cxl_rr->nr_targets_set);
@@ -1172,7 +1172,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
out_target_set:
cxl_rr->nr_targets_set += inc;
dev_dbg(&cxlr->dev, "%s:%s target[%d] = %s for %s:%s @ %d\n",
- dev_name(port->uport), dev_name(&port->dev),
+ dev_name(port->uport_dev), dev_name(&port->dev),
cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport_dev),
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
@@ -1492,7 +1492,7 @@ static int cmp_decode_pos(const void *a, const void *b)
if (!dev) {
struct range *range = &cxled_a->cxld.hpa_range;
- dev_err(port->uport,
+ dev_err(port->uport_dev,
"failed to find decoder that maps %#llx-%#llx\n",
range->start, range->end);
goto err;
@@ -1507,14 +1507,15 @@ static int cmp_decode_pos(const void *a, const void *b)
put_device(dev);
if (a_pos < 0 || b_pos < 0) {
- dev_err(port->uport,
+ dev_err(port->uport_dev,
"failed to find shared decoder for %s and %s\n",
dev_name(cxlmd_a->dev.parent),
dev_name(cxlmd_b->dev.parent));
goto err;
}
- dev_dbg(port->uport, "%s comes %s %s\n", dev_name(cxlmd_a->dev.parent),
+ dev_dbg(port->uport_dev, "%s comes %s %s\n",
+ dev_name(cxlmd_a->dev.parent),
a_pos - b_pos < 0 ? "before" : "after",
dev_name(cxlmd_b->dev.parent));
@@ -2059,11 +2060,11 @@ static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd,
if (rc)
goto err;
- rc = devm_add_action_or_reset(port->uport, unregister_region, cxlr);
+ rc = devm_add_action_or_reset(port->uport_dev, unregister_region, cxlr);
if (rc)
return ERR_PTR(rc);
- dev_dbg(port->uport, "%s: created %s\n",
+ dev_dbg(port->uport_dev, "%s: created %s\n",
dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev));
return cxlr;
@@ -2191,7 +2192,7 @@ static ssize_t delete_region_store(struct device *dev,
if (IS_ERR(cxlr))
return PTR_ERR(cxlr);
- devm_release_action(port->uport, unregister_region, cxlr);
+ devm_release_action(port->uport_dev, unregister_region, cxlr);
put_device(&cxlr->dev);
return len;
@@ -2356,7 +2357,8 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port)
rc = device_for_each_child(&port->dev, &ctx, poison_by_decoder);
if (rc == 1)
- rc = cxl_get_poison_unmapped(to_cxl_memdev(port->uport), &ctx);
+ rc = cxl_get_poison_unmapped(to_cxl_memdev(port->uport_dev),
+ &ctx);
up_read(&cxl_region_rwsem);
return rc;
@@ -2732,7 +2734,7 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
err:
up_write(&cxl_region_rwsem);
- devm_release_action(port->uport, unregister_region, cxlr);
+ devm_release_action(port->uport_dev, unregister_region, cxlr);
return ERR_PTR(rc);
}
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 7232c2a0e27c..754cfe59ae37 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -536,7 +536,7 @@ struct cxl_dax_region {
* downstream port devices to construct a CXL memory
* decode hierarchy.
* @dev: this port's device
- * @uport: PCI or platform device implementing the upstream port capability
+ * @uport_dev: PCI or platform device implementing the upstream port capability
* @host_bridge: Shortcut to the platform attach point for this port
* @id: id for port device-name
* @dports: cxl_dport instances referenced by decoders
@@ -555,7 +555,7 @@ struct cxl_dax_region {
*/
struct cxl_port {
struct device dev;
- struct device *uport;
+ struct device *uport_dev;
struct device *host_bridge;
int id;
struct xarray dports;
@@ -641,21 +641,22 @@ struct cxl_region_ref {
/*
* The platform firmware device hosting the root is also the top of the
* CXL port topology. All other CXL ports have another CXL port as their
- * parent and their ->uport / host device is out-of-line of the port
+ * parent and their ->uport_dev / host device is out-of-line of the port
* ancestry.
*/
static inline bool is_cxl_root(struct cxl_port *port)
{
- return port->uport == port->dev.parent;
+ return port->uport_dev == port->dev.parent;
}
bool is_cxl_port(const struct device *dev);
struct cxl_port *to_cxl_port(const struct device *dev);
struct pci_bus;
-int devm_cxl_register_pci_bus(struct device *host, struct device *uport,
+int devm_cxl_register_pci_bus(struct device *host, struct device *uport_dev,
struct pci_bus *bus);
struct pci_bus *cxl_port_to_pci_bus(struct cxl_port *port);
-struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
+struct cxl_port *devm_cxl_add_port(struct device *host,
+ struct device *uport_dev,
resource_size_t component_reg_phys,
struct cxl_dport *parent_dport);
struct cxl_port *find_cxl_root(struct cxl_port *port);
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index a2845a7a69d8..76743016b64c 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -72,13 +72,13 @@ cxled_to_memdev(struct cxl_endpoint_decoder *cxled)
{
struct cxl_port *port = to_cxl_port(cxled->cxld.dev.parent);
- return to_cxl_memdev(port->uport);
+ return to_cxl_memdev(port->uport_dev);
}
bool is_cxl_memdev(const struct device *dev);
static inline bool is_cxl_endpoint(struct cxl_port *port)
{
- return is_cxl_memdev(port->uport);
+ return is_cxl_memdev(port->uport_dev);
}
struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds);
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 45d4c32d78b0..4cc461c22b8b 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -163,7 +163,7 @@ static int cxl_mem_probe(struct device *dev)
}
if (dport->rch)
- endpoint_parent = parent_port->uport;
+ endpoint_parent = parent_port->uport_dev;
else
endpoint_parent = &parent_port->dev;
diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c
index c23b6164e1c0..4cef2bf45ad2 100644
--- a/drivers/cxl/port.c
+++ b/drivers/cxl/port.c
@@ -91,7 +91,7 @@ static int cxl_switch_port_probe(struct cxl_port *port)
static int cxl_endpoint_port_probe(struct cxl_port *port)
{
struct cxl_endpoint_dvsec_info info = { .port = port };
- struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport);
+ struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev);
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_hdm *cxlhdm;
struct cxl_port *root;
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index f5c04787bcc8..4f62eb55f8b8 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -754,7 +754,7 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
/* check is endpoint is attach to host-bridge0 */
port = cxled_to_port(cxled);
do {
- if (port->uport == &cxl_host_bridge[0]->dev) {
+ if (port->uport_dev == &cxl_host_bridge[0]->dev) {
hb0 = true;
break;
}
@@ -889,7 +889,7 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
mock_init_hdm_decoder(cxld);
if (target_count) {
- rc = device_for_each_child(port->uport, &ctx,
+ rc = device_for_each_child(port->uport_dev, &ctx,
map_targets);
if (rc) {
put_device(&cxld->dev);
@@ -919,29 +919,29 @@ static int mock_cxl_port_enumerate_dports(struct cxl_port *port)
int i, array_size;
if (port->depth == 1) {
- if (is_multi_bridge(port->uport)) {
+ if (is_multi_bridge(port->uport_dev)) {
array_size = ARRAY_SIZE(cxl_root_port);
array = cxl_root_port;
- } else if (is_single_bridge(port->uport)) {
+ } else if (is_single_bridge(port->uport_dev)) {
array_size = ARRAY_SIZE(cxl_root_single);
array = cxl_root_single;
} else {
dev_dbg(&port->dev, "%s: unknown bridge type\n",
- dev_name(port->uport));
+ dev_name(port->uport_dev));
return -ENXIO;
}
} else if (port->depth == 2) {
struct cxl_port *parent = to_cxl_port(port->dev.parent);
- if (is_multi_bridge(parent->uport)) {
+ if (is_multi_bridge(parent->uport_dev)) {
array_size = ARRAY_SIZE(cxl_switch_dport);
array = cxl_switch_dport;
- } else if (is_single_bridge(parent->uport)) {
+ } else if (is_single_bridge(parent->uport_dev)) {
array_size = ARRAY_SIZE(cxl_swd_single);
array = cxl_swd_single;
} else {
dev_dbg(&port->dev, "%s: unknown bridge type\n",
- dev_name(port->uport));
+ dev_name(port->uport_dev));
return -ENXIO;
}
} else {
@@ -954,9 +954,9 @@ static int mock_cxl_port_enumerate_dports(struct cxl_port *port)
struct platform_device *pdev = array[i];
struct cxl_dport *dport;
- if (pdev->dev.parent != port->uport) {
+ if (pdev->dev.parent != port->uport_dev) {
dev_dbg(&port->dev, "%s: mismatch parent %s\n",
- dev_name(port->uport),
+ dev_name(port->uport_dev),
dev_name(pdev->dev.parent));
continue;
}
diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c
index dbeef5c6f606..da554df50bac 100644
--- a/tools/testing/cxl/test/mock.c
+++ b/tools/testing/cxl/test/mock.c
@@ -139,7 +139,7 @@ struct cxl_hdm *__wrap_devm_cxl_setup_hdm(struct cxl_port *port,
struct cxl_hdm *cxlhdm;
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
- if (ops && ops->is_mock_port(port->uport))
+ if (ops && ops->is_mock_port(port->uport_dev))
cxlhdm = ops->devm_cxl_setup_hdm(port, info);
else
cxlhdm = devm_cxl_setup_hdm(port, info);
@@ -154,7 +154,7 @@ int __wrap_devm_cxl_enable_hdm(struct cxl_port *port, struct cxl_hdm *cxlhdm)
int index, rc;
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
- if (ops && ops->is_mock_port(port->uport))
+ if (ops && ops->is_mock_port(port->uport_dev))
rc = 0;
else
rc = devm_cxl_enable_hdm(port, cxlhdm);
@@ -169,7 +169,7 @@ int __wrap_devm_cxl_add_passthrough_decoder(struct cxl_port *port)
int rc, index;
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
- if (ops && ops->is_mock_port(port->uport))
+ if (ops && ops->is_mock_port(port->uport_dev))
rc = ops->devm_cxl_add_passthrough_decoder(port);
else
rc = devm_cxl_add_passthrough_decoder(port);
@@ -186,7 +186,7 @@ int __wrap_devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
struct cxl_port *port = cxlhdm->port;
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
- if (ops && ops->is_mock_port(port->uport))
+ if (ops && ops->is_mock_port(port->uport_dev))
rc = ops->devm_cxl_enumerate_decoders(cxlhdm, info);
else
rc = devm_cxl_enumerate_decoders(cxlhdm, info);
@@ -201,7 +201,7 @@ int __wrap_devm_cxl_port_enumerate_dports(struct cxl_port *port)
int rc, index;
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
- if (ops && ops->is_mock_port(port->uport))
+ if (ops && ops->is_mock_port(port->uport_dev))
rc = ops->devm_cxl_port_enumerate_dports(port);
else
rc = devm_cxl_port_enumerate_dports(port);
--
2.34.1
From: Robert Richter <[email protected]>
AER corrected and uncorrectable internal errors (CIE/UIE) are masked
in their corresponding mask registers per default once in power-up
state. [1][2] Enable internal errors for RCECs to receive CXL
downstream port errors of Restricted CXL Hosts (RCHs).
[1] CXL 3.0 Spec, 12.2.1.1 - RCH Downstream Port Detected Errors
[2] PCIe Base Spec r6.0, 7.8.4.3 Uncorrectable Error Mask Register,
7.8.4.6 Correctable Error Mask Register
Co-developed-by: Terry Bowman <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Acked-by: Bjorn Helgaas <[email protected]>
---
drivers/pci/pcie/aer.c | 57 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index c354ca5e8f2b..4f9203e27c62 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -948,6 +948,30 @@ static bool find_source_device(struct pci_dev *parent,
#ifdef CONFIG_PCIEAER_CXL
+/**
+ * pci_aer_unmask_internal_errors - unmask internal errors
+ * @dev: pointer to the pcie_dev data structure
+ *
+ * Unmasks internal errors in the Uncorrectable and Correctable Error
+ * Mask registers.
+ *
+ * Note: AER must be enabled and supported by the device which must be
+ * checked in advance, e.g. with pcie_aer_is_native().
+ */
+static void pci_aer_unmask_internal_errors(struct pci_dev *dev)
+{
+ int aer = dev->aer_cap;
+ u32 mask;
+
+ pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, &mask);
+ mask &= ~PCI_ERR_UNC_INTN;
+ pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, mask);
+
+ pci_read_config_dword(dev, aer + PCI_ERR_COR_MASK, &mask);
+ mask &= ~PCI_ERR_COR_INTERNAL;
+ pci_write_config_dword(dev, aer + PCI_ERR_COR_MASK, mask);
+}
+
static bool is_cxl_mem_dev(struct pci_dev *dev)
{
/*
@@ -1027,7 +1051,39 @@ static void cxl_rch_handle_error(struct pci_dev *dev, struct aer_err_info *info)
pcie_walk_rcec(dev, cxl_rch_handle_error_iter, info);
}
+static int handles_cxl_error_iter(struct pci_dev *dev, void *data)
+{
+ int *handles_cxl = data;
+
+ if (!*handles_cxl)
+ *handles_cxl = is_cxl_mem_dev(dev) && cxl_error_is_native(dev);
+
+ /* Non-zero terminates iteration */
+ return *handles_cxl;
+}
+
+static bool handles_cxl_errors(struct pci_dev *rcec)
+{
+ int handles_cxl = 0;
+
+ if (pci_pcie_type(rcec) == PCI_EXP_TYPE_RC_EC &&
+ pcie_aer_is_native(rcec))
+ pcie_walk_rcec(rcec, handles_cxl_error_iter, &handles_cxl);
+
+ return !!handles_cxl;
+}
+
+static void cxl_rch_enable_rcec(struct pci_dev *rcec)
+{
+ if (!handles_cxl_errors(rcec))
+ return;
+
+ pci_aer_unmask_internal_errors(rcec);
+ pci_info(rcec, "CXL: Internal errors unmasked");
+}
+
#else
+static inline void cxl_rch_enable_rcec(struct pci_dev *dev) { }
static inline void cxl_rch_handle_error(struct pci_dev *dev,
struct aer_err_info *info) { }
#endif
@@ -1428,6 +1484,7 @@ static int aer_probe(struct pcie_device *dev)
return status;
}
+ cxl_rch_enable_rcec(port);
aer_enable_rootport(rpc);
pci_info(port, "enabled with IRQ %d\n", dev->irq);
return 0;
--
2.34.1
From: Robert Richter <[email protected]>
Reading code like dport->dport does not immediately suggest that this
points to the corresponding device structure of the dport. Rename
struct member @dport to @dport_dev.
While at it, also rename @new argument of add_dport() to @dport. This
better describes the variable as a dport (e.g. new->dport becomes to
dport->dport_dev).
Co-developed-by: Terry Bowman <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
---
drivers/cxl/core/port.c | 20 ++++++++++----------
drivers/cxl/core/region.c | 4 ++--
drivers/cxl/cxl.h | 4 ++--
3 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 76888c75dae4..7d3079f5b7b5 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -605,7 +605,7 @@ static int devm_cxl_link_parent_dport(struct device *host,
if (!parent_dport)
return 0;
- rc = sysfs_create_link(&port->dev.kobj, &parent_dport->dport->kobj,
+ rc = sysfs_create_link(&port->dev.kobj, &parent_dport->dport_dev->kobj,
"parent_dport");
if (rc)
return rc;
@@ -658,7 +658,7 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
if (iter->host_bridge)
port->host_bridge = iter->host_bridge;
else if (parent_dport->rch)
- port->host_bridge = parent_dport->dport;
+ port->host_bridge = parent_dport->dport_dev;
else
port->host_bridge = iter->uport;
dev_dbg(uport, "host-bridge: %s\n", dev_name(port->host_bridge));
@@ -847,22 +847,22 @@ static struct cxl_dport *find_dport(struct cxl_port *port, int id)
return NULL;
}
-static int add_dport(struct cxl_port *port, struct cxl_dport *new)
+static int add_dport(struct cxl_port *port, struct cxl_dport *dport)
{
struct cxl_dport *dup;
int rc;
device_lock_assert(&port->dev);
- dup = find_dport(port, new->port_id);
+ dup = find_dport(port, dport->port_id);
if (dup) {
dev_err(&port->dev,
"unable to add dport%d-%s non-unique port id (%s)\n",
- new->port_id, dev_name(new->dport),
- dev_name(dup->dport));
+ dport->port_id, dev_name(dport->dport_dev),
+ dev_name(dup->dport_dev));
return -EBUSY;
}
- rc = xa_insert(&port->dports, (unsigned long)new->dport, new,
+ rc = xa_insert(&port->dports, (unsigned long)dport->dport_dev, dport,
GFP_KERNEL);
if (rc)
return rc;
@@ -895,8 +895,8 @@ static void cxl_dport_remove(void *data)
struct cxl_dport *dport = data;
struct cxl_port *port = dport->port;
- xa_erase(&port->dports, (unsigned long) dport->dport);
- put_device(dport->dport);
+ xa_erase(&port->dports, (unsigned long) dport->dport_dev);
+ put_device(dport->dport_dev);
}
static void cxl_dport_unlink(void *data)
@@ -954,7 +954,7 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
dev_dbg(dport_dev, "Component Registers found for dport: %pa\n",
&component_reg_phys);
- dport->dport = dport_dev;
+ dport->dport_dev = dport_dev;
dport->port_id = port_id;
dport->component_reg_phys = component_reg_phys;
dport->port = port;
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index f822de44bee0..13cda989d944 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -1162,7 +1162,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
dev_dbg(&cxlr->dev, "%s:%s: %s expected %s at %d\n",
dev_name(port->uport), dev_name(&port->dev),
dev_name(&cxlsd->cxld.dev),
- dev_name(ep->dport->dport),
+ dev_name(ep->dport->dport_dev),
cxl_rr->nr_targets_set);
return -ENXIO;
}
@@ -1173,7 +1173,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
cxl_rr->nr_targets_set += inc;
dev_dbg(&cxlr->dev, "%s:%s target[%d] = %s for %s:%s @ %d\n",
dev_name(port->uport), dev_name(&port->dev),
- cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport),
+ cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport_dev),
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
return 0;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 7c8674079f1a..7232c2a0e27c 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -589,7 +589,7 @@ struct cxl_rcrb_info {
/**
* struct cxl_dport - CXL downstream port
- * @dport: PCI bridge or firmware device representing the downstream link
+ * @dport_dev: PCI bridge or firmware device representing the downstream link
* @port_id: unique hardware identifier for dport in decoder target list
* @component_reg_phys: downstream port component registers
* @rcrb: Data about the Root Complex Register Block layout
@@ -597,7 +597,7 @@ struct cxl_rcrb_info {
* @port: reference to cxl_port that contains this downstream port
*/
struct cxl_dport {
- struct device *dport;
+ struct device *dport_dev;
int port_id;
resource_size_t component_reg_phys;
struct cxl_rcrb_info rcrb;
--
2.34.1
From: Robert Richter <[email protected]>
Same as for ports and dports, also store the endpoint's Component
Register mappings, use struct cxl_dev_state for that.
The Component Register base address @component_reg_phys is no longer
used after the rework of the Component Register setup which now uses
struct member @comp_map instead. Remove the base address.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/cxlmem.h | 5 ++---
drivers/cxl/mem.c | 4 ++--
drivers/cxl/pci.c | 10 ++++------
tools/testing/cxl/test/mem.c | 1 -
4 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 76743016b64c..8aee1a42d9af 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -263,6 +263,7 @@ struct cxl_poison_state {
*
* @dev: The device associated with this CXL state
* @cxlmd: The device representing the CXL.mem capabilities of @dev
+ * @comp_map: component register capability mappings
* @regs: Parsed register blocks
* @cxl_dvsec: Offset to the PCIe device DVSEC
* @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH)
@@ -286,7 +287,6 @@ struct cxl_poison_state {
* @active_persistent_bytes: sum of hard + soft persistent
* @next_volatile_bytes: volatile capacity change pending device reset
* @next_persistent_bytes: persistent capacity change pending device reset
- * @component_reg_phys: register base of component registers
* @info: Cached DVSEC information about the device.
* @serial: PCIe Device Serial Number
* @event: event log driver state
@@ -299,7 +299,7 @@ struct cxl_poison_state {
struct cxl_dev_state {
struct device *dev;
struct cxl_memdev *cxlmd;
-
+ struct cxl_register_map comp_map;
struct cxl_regs regs;
int cxl_dvsec;
@@ -325,7 +325,6 @@ struct cxl_dev_state {
u64 next_volatile_bytes;
u64 next_persistent_bytes;
- resource_size_t component_reg_phys;
u64 serial;
struct cxl_event_state event;
diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
index 205e2e280aed..92c6151b7206 100644
--- a/drivers/cxl/mem.c
+++ b/drivers/cxl/mem.c
@@ -49,7 +49,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
struct cxl_dport *parent_dport)
{
struct cxl_port *parent_port = parent_dport->port;
- struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_port *endpoint, *iter, *down;
int rc;
@@ -65,8 +64,9 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
ep->next = down;
}
+ /* The Endpoint's component regs are located in cxlds. */
endpoint = devm_cxl_add_port(host, &cxlmd->dev,
- cxlds->component_reg_phys,
+ CXL_RESOURCE_NONE,
parent_dport);
if (IS_ERR(endpoint))
return PTR_ERR(endpoint);
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 99a75c54ee39..ad4cfcd95e17 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -665,16 +665,14 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* If the component registers can't be found, the cxl_pci driver may
* still be useful for management functions so don't return an error.
*/
- cxlds->component_reg_phys = CXL_RESOURCE_NONE;
- rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
+ rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT,
+ &cxlds->comp_map);
if (rc)
dev_warn(&pdev->dev, "No component registers (%d)\n", rc);
- else if (!map.component_map.ras.valid)
+ else if (!cxlds->comp_map.component_map.ras.valid)
dev_dbg(&pdev->dev, "RAS registers not found\n");
- cxlds->component_reg_phys = map.resource;
-
- rc = cxl_map_component_regs(&map, &cxlds->regs.component,
+ rc = cxl_map_component_regs(&cxlds->comp_map, &cxlds->regs.component,
BIT(CXL_CM_CAP_CAP_ID_RAS));
if (rc)
dev_dbg(&pdev->dev, "Failed to map RAS capability.\n");
diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
index 34b48027b3de..fd562a5fa06f 100644
--- a/tools/testing/cxl/test/mem.c
+++ b/tools/testing/cxl/test/mem.c
@@ -1241,7 +1241,6 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
cxlds->event.buf = (struct cxl_get_event_payload *) mdata->event_buf;
if (is_rcd(pdev)) {
cxlds->rcd = true;
- cxlds->component_reg_phys = CXL_RESOURCE_NONE;
}
rc = cxl_enumerate_cmds(cxlds);
--
2.34.1
From: Robert Richter <[email protected]>
The Component Register base address @component_reg_phys is no longer
used after the rework of the Component Register setup which now uses
struct member @comp_map instead. Remove the base address.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/core/port.c | 1 -
drivers/cxl/cxl.h | 2 --
2 files changed, 3 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index cdfe0ea7a2e9..e0d2e7596440 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -960,7 +960,6 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
dport->dport_dev = dport_dev;
dport->port_id = port_id;
- dport->component_reg_phys = component_reg_phys;
dport->port = port;
cond_cxl_root_lock(port);
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index ae265357170e..7fbc52b81554 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -594,7 +594,6 @@ struct cxl_rcrb_info {
* struct cxl_dport - CXL downstream port
* @dport_dev: PCI bridge or firmware device representing the downstream link
* @port_id: unique hardware identifier for dport in decoder target list
- * @component_reg_phys: downstream port component registers
* @rcrb: Data about the Root Complex Register Block layout
* @rch: Indicate whether this dport was enumerated in RCH or VH mode
* @port: reference to cxl_port that contains this downstream port
@@ -602,7 +601,6 @@ struct cxl_rcrb_info {
struct cxl_dport {
struct device *dport_dev;
int port_id;
- resource_size_t component_reg_phys;
struct cxl_rcrb_info rcrb;
bool rch;
struct cxl_port *port;
--
2.34.1
The CXL driver plans to use cper_print_aer() for logging restricted CXL
host (RCH) AER errors. cper_print_aer() is not currently exported and
therefore not usable by the CXL drivers built as loadable modules. Export
the cper_print_aer() function. Use the EXPORT_SYMBOL_NS_GPL() variant
to restrict the export to CXL drivers.
The CONFIG_ACPI_APEI_PCIEAER kernel config is currently used to enable
cper_print_aer(). cper_print_aer() logs the AER registers and is
useful in PCIE AER logging outside of APEI. Remove the
CONFIG_ACPI_APEI_PCIEAER dependency to enable cper_print_aer().
The cper_print_aer() function name implies CPER specific use but is useful
in non-CPER cases as well. Rename cper_print_aer() to pci_print_aer().
Also, update cxl_core to import CXL namespace imports.
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Cc: Mahesh J Salgaonkar <[email protected]>
Cc: "Oliver O'Halloran" <[email protected]>
Cc: Bjorn Helgaas <[email protected]>
Cc: [email protected]
Reviewed-by: Jonathan Cameron <[email protected]>
Acked-by: Bjorn Helgaas <[email protected]>
---
drivers/cxl/core/port.c | 1 +
drivers/pci/pcie/aer.c | 9 +++++----
include/linux/aer.h | 2 +-
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 37ecd756b224..41e79a36c1ae 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -2056,3 +2056,4 @@ static void cxl_core_exit(void)
subsys_initcall(cxl_core_init);
module_exit(cxl_core_exit);
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(CXL);
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index f6c24ded134c..d3344fcf1f79 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -771,9 +771,10 @@ int cper_severity_to_aer(int cper_severity)
}
}
EXPORT_SYMBOL_GPL(cper_severity_to_aer);
+#endif
-void cper_print_aer(struct pci_dev *dev, int aer_severity,
- struct aer_capability_regs *aer)
+void pci_print_aer(struct pci_dev *dev, int aer_severity,
+ struct aer_capability_regs *aer)
{
int layer, agent, tlp_header_valid = 0;
u32 status, mask;
@@ -812,7 +813,7 @@ void cper_print_aer(struct pci_dev *dev, int aer_severity,
trace_aer_event(dev_name(&dev->dev), (status & ~mask),
aer_severity, tlp_header_valid, &aer->header_log);
}
-#endif
+EXPORT_SYMBOL_NS_GPL(pci_print_aer, CXL);
/**
* add_error_device - list device to be handled
@@ -1009,7 +1010,7 @@ static void aer_recover_work_func(struct work_struct *work)
PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn));
continue;
}
- cper_print_aer(pdev, entry.severity, entry.regs);
+ pci_print_aer(pdev, entry.severity, entry.regs);
if (entry.severity == AER_NONFATAL)
pcie_do_recovery(pdev, pci_channel_io_normal,
aer_root_reset);
diff --git a/include/linux/aer.h b/include/linux/aer.h
index 97f64ba1b34a..8f124b904314 100644
--- a/include/linux/aer.h
+++ b/include/linux/aer.h
@@ -64,7 +64,7 @@ static inline void pci_save_aer_state(struct pci_dev *dev) {}
static inline void pci_restore_aer_state(struct pci_dev *dev) {}
#endif
-void cper_print_aer(struct pci_dev *dev, int aer_severity,
+void pci_print_aer(struct pci_dev *dev, int aer_severity,
struct aer_capability_regs *aer);
int cper_severity_to_aer(int cper_severity);
void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
--
2.34.1
The CXL error handler currently only logs endpoint RAS status. The CXL
topology includes several components providing RAS details to be logged
during error handling.[1] Update the current handler's RAS logging to use a
RAS register address. Also, update the error handler function names to be
consistent with correctable and uncorrecable RAS. This will allow for
adding support to log other CXL component's RAS details in the future.
[1] CXL3.0 Table 8-22 CXL_Capability_ID Assignment
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/core/pci.c | 44 +++++++++++++++++++++++++++++-------------
1 file changed, 31 insertions(+), 13 deletions(-)
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 375f01c6cad6..9cb39835e154 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -665,32 +665,36 @@ void read_cdat_data(struct cxl_port *port)
}
EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);
-void cxl_cor_error_detected(struct pci_dev *pdev)
+static void __cxl_handle_cor_ras(struct cxl_dev_state *cxlds,
+ void __iomem *ras_base)
{
- struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
void __iomem *addr;
u32 status;
- if (!cxlds->regs.ras)
+ if (!ras_base)
return;
- addr = cxlds->regs.ras + CXL_RAS_CORRECTABLE_STATUS_OFFSET;
+ addr = ras_base + CXL_RAS_CORRECTABLE_STATUS_OFFSET;
status = readl(addr);
if (status & CXL_RAS_CORRECTABLE_STATUS_MASK) {
writel(status & CXL_RAS_CORRECTABLE_STATUS_MASK, addr);
trace_cxl_aer_correctable_error(cxlds->cxlmd, status);
}
}
-EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, CXL);
+
+static void cxl_handle_endpoint_cor_ras(struct cxl_dev_state *cxlds)
+{
+ return __cxl_handle_cor_ras(cxlds, cxlds->regs.ras);
+}
/* CXL spec rev3.0 8.2.4.16.1 */
-static void header_log_copy(struct cxl_dev_state *cxlds, u32 *log)
+static void header_log_copy(void __iomem *ras_base, u32 *log)
{
void __iomem *addr;
u32 *log_addr;
int i, log_u32_size = CXL_HEADERLOG_SIZE / sizeof(u32);
- addr = cxlds->regs.ras + CXL_RAS_HEADER_LOG_OFFSET;
+ addr = ras_base + CXL_RAS_HEADER_LOG_OFFSET;
log_addr = log;
for (i = 0; i < log_u32_size; i++) {
@@ -704,17 +708,18 @@ static void header_log_copy(struct cxl_dev_state *cxlds, u32 *log)
* Log the state of the RAS status registers and prepare them to log the
* next error status. Return 1 if reset needed.
*/
-static bool cxl_report_and_clear(struct cxl_dev_state *cxlds)
+static bool __cxl_handle_ras(struct cxl_dev_state *cxlds,
+ void __iomem *ras_base)
{
u32 hl[CXL_HEADERLOG_SIZE_U32];
void __iomem *addr;
u32 status;
u32 fe;
- if (!cxlds->regs.ras)
+ if (!ras_base)
return false;
- addr = cxlds->regs.ras + CXL_RAS_UNCORRECTABLE_STATUS_OFFSET;
+ addr = ras_base + CXL_RAS_UNCORRECTABLE_STATUS_OFFSET;
status = readl(addr);
if (!(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK))
return false;
@@ -722,7 +727,7 @@ static bool cxl_report_and_clear(struct cxl_dev_state *cxlds)
/* If multiple errors, log header points to first error from ctrl reg */
if (hweight32(status) > 1) {
void __iomem *rcc_addr =
- cxlds->regs.ras + CXL_RAS_CAP_CONTROL_OFFSET;
+ ras_base + CXL_RAS_CAP_CONTROL_OFFSET;
fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK,
readl(rcc_addr)));
@@ -730,13 +735,26 @@ static bool cxl_report_and_clear(struct cxl_dev_state *cxlds)
fe = status;
}
- header_log_copy(cxlds, hl);
+ header_log_copy(ras_base, hl);
trace_cxl_aer_uncorrectable_error(cxlds->cxlmd, status, fe, hl);
writel(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK, addr);
return true;
}
+static bool cxl_handle_endpoint_ras(struct cxl_dev_state *cxlds)
+{
+ return __cxl_handle_ras(cxlds, cxlds->regs.ras);
+}
+
+void cxl_cor_error_detected(struct pci_dev *pdev)
+{
+ struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
+
+ cxl_handle_endpoint_cor_ras(cxlds);
+}
+EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, CXL);
+
pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
pci_channel_state_t state)
{
@@ -751,7 +769,7 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
* chance the situation is recoverable dump the status of the RAS
* capability registers and bounce the active state of the memdev.
*/
- ue = cxl_report_and_clear(cxlds);
+ ue = cxl_handle_endpoint_ras(cxlds);
switch (state) {
case pci_channel_io_normal:
--
2.34.1
Restricted CXL host (RCH) downstream port AER information is not currently
logged while in the error state. One problem preventing the error logging
is the AER and RAS registers are not accessible. The CXL driver requires
changes to find RCH downstream port AER and RAS registers for purpose of
error logging.
RCH downstream ports are not enumerated during a PCI bus scan and are
instead discovered using system firmware, ACPI in this case.[1] The
downstream port is implemented as a Root Complex Register Block (RCRB).
The RCRB is a 4k memory block containing PCIe registers based on the PCIe
root port.[2] The RCRB includes AER extended capability registers used for
reporting errors. Note, the RCH's AER Capability is located in the RCRB
memory space instead of PCI configuration space, thus its register access
is different. Existing kernel PCIe AER functions can not be used to manage
the downstream port AER capabilities and RAS registers because the port was
not enumerated during PCI scan and the registers are not PCI config
accessible.
Discover RCH downstream port AER extended capability registers. Use MMIO
accesses to search for extended AER capability in RCRB register space.
[1] CXL 3.0 Spec, 9.11.2 - System Firmware View of CXL 1.1 Hierarchy
[2] CXL 3.0 Spec, 8.2.1.1 - RCH Downstream Port RCRB
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/core/core.h | 1 +
drivers/cxl/core/port.c | 7 ++++++-
drivers/cxl/core/regs.c | 35 +++++++++++++++++++++++++++++++++++
3 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index b001669a5133..87467c633123 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -72,6 +72,7 @@ struct cxl_rcrb_info;
resource_size_t __rcrb_to_component(struct device *dev,
struct cxl_rcrb_info *ri,
enum cxl_rcrb which);
+u16 cxl_rcrb_to_aer(struct device *dev, resource_size_t rcrb);
extern struct rw_semaphore cxl_dpa_rwsem;
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index cfae8307de90..37ecd756b224 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -678,7 +678,6 @@ static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
device_set_pm_not_required(dev);
dev->bus = &cxl_bus_type;
dev->type = &cxl_port_type;
-
return port;
err:
@@ -974,6 +973,8 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
return ERR_PTR(-ENOMEM);
if (rcrb != CXL_RESOURCE_NONE) {
+ struct pci_host_bridge *host_bridge;
+
dport->rcrb.base = rcrb;
component_reg_phys = __rcrb_to_component(dport_dev, &dport->rcrb,
CXL_RCRB_DOWNSTREAM);
@@ -982,6 +983,10 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
return ERR_PTR(-ENXIO);
}
+ host_bridge = to_pci_host_bridge(dport_dev);
+ if (host_bridge->native_cxl_error)
+ dport->rcrb.aer_cap = cxl_rcrb_to_aer(dport_dev, dport->rcrb.base);
+
dport->rch = true;
}
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index e68848075bb6..60b7ba7f4030 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -407,6 +407,41 @@ int cxl_setup_regs(struct cxl_register_map *map)
}
EXPORT_SYMBOL_NS_GPL(cxl_setup_regs, CXL);
+u16 cxl_rcrb_to_aer(struct device *dev, resource_size_t rcrb)
+{
+ void __iomem *addr;
+ u16 offset = 0;
+ u32 cap_hdr;
+
+ if (WARN_ON_ONCE(rcrb == CXL_RESOURCE_NONE))
+ return 0;
+
+ if (!request_mem_region(rcrb, SZ_4K, dev_name(dev)))
+ return 0;
+
+ addr = ioremap(rcrb, SZ_4K);
+ if (!addr) {
+ release_mem_region(rcrb, SZ_4K);
+ return 0;
+ }
+
+ cap_hdr = readl(addr + offset);
+ while (PCI_EXT_CAP_ID(cap_hdr) != PCI_EXT_CAP_ID_ERR) {
+ offset = PCI_EXT_CAP_NEXT(cap_hdr);
+ if (!offset)
+ break;
+ cap_hdr = readl(addr + offset);
+ }
+
+ if (offset)
+ dev_dbg(dev, "found AER extended capability (0x%x)\n", offset);
+
+ iounmap(addr);
+ release_mem_region(rcrb, SZ_4K);
+
+ return offset;
+}
+
resource_size_t __rcrb_to_component(struct device *dev, struct cxl_rcrb_info *ri,
enum cxl_rcrb which)
{
--
2.34.1
From: Robert Richter <[email protected]>
The Component Register base address @component_reg_phys is no longer
used after the rework of the Component Register setup which now uses
struct member @comp_map instead. Remove the base address.
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
---
drivers/cxl/core/port.c | 4 +---
drivers/cxl/cxl.h | 2 --
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 45fe7d89f7f3..cfae8307de90 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -616,7 +616,6 @@ static int devm_cxl_link_parent_dport(struct device *host,
static struct lock_class_key cxl_port_key;
static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
- resource_size_t component_reg_phys,
struct cxl_dport *parent_dport)
{
struct cxl_port *port;
@@ -667,7 +666,6 @@ static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
} else
dev->parent = uport_dev;
- port->component_reg_phys = component_reg_phys;
ida_init(&port->decoder_ida);
port->hdm_end = -1;
port->commit_end = -1;
@@ -726,7 +724,7 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
struct device *dev;
int rc;
- port = cxl_port_alloc(uport_dev, component_reg_phys, parent_dport);
+ port = cxl_port_alloc(uport_dev, parent_dport);
if (IS_ERR(port))
return port;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index b1adca9b27ba..9f46a4e1fbec 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -551,7 +551,6 @@ struct cxl_dax_region {
* @nr_dports: number of entries in @dports
* @hdm_end: track last allocated HDM decoder instance for allocation ordering
* @commit_end: cursor to track highest committed decoder for commit ordering
- * @component_reg_phys: component register capability base address (optional)
* @dead: last ep has been removed, force port re-creation
* @depth: How deep this port is relative to the root. depth 0 is the root.
* @cdat: Cached CDAT data
@@ -571,7 +570,6 @@ struct cxl_port {
int nr_dports;
int hdm_end;
int commit_end;
- resource_size_t component_reg_phys;
bool dead;
unsigned int depth;
struct cxl_cdat {
--
2.34.1
The restricted CXL host (RCH) error handler will log protocol errors
using AER and RAS status registers. The AER and RAS registers need
to be virtually memory mapped before enabling interrupts. Update
__devm_cxl_add_dport() to include RCH RAS and AER mapping.
Add 'struct cxl_regs' to 'struct cxl_dport' for saving a unique copy of
the RCH downstream port's mapped registers.
Co-developed-by: Robert Richter <[email protected]>
Signed-off-by: Robert Richter <[email protected]>
Signed-off-by: Terry Bowman <[email protected]>
---
drivers/cxl/core/port.c | 33 +++++++++++++++++++++++++++++++++
drivers/cxl/core/regs.c | 1 +
drivers/cxl/cxl.h | 11 +++++++++++
3 files changed, 45 insertions(+)
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 41e79a36c1ae..87a12e69aa8e 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -8,6 +8,7 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/idr.h>
+#include <linux/aer.h>
#include <cxlmem.h>
#include <cxlpci.h>
#include <cxl.h>
@@ -943,6 +944,36 @@ static void cxl_dport_unlink(void *data)
sysfs_remove_link(&port->dev.kobj, link_name);
}
+static void cxl_dport_map_rch_aer(struct cxl_dport *dport)
+{
+ struct cxl_rcrb_info *ri = &dport->rcrb;
+ struct cxl_port *port = dport->port;
+ void __iomem *dport_aer = NULL;
+ resource_size_t aer_phys;
+
+ if (dport->rch && ri->aer_cap) {
+ aer_phys = ri->aer_cap + ri->base;
+ dport_aer = devm_cxl_iomap_block(&port->dev, aer_phys,
+ sizeof(struct aer_capability_regs));
+ }
+
+ dport->regs.dport_aer = dport_aer;
+}
+
+static void cxl_dport_map_regs(struct cxl_dport *dport)
+{
+ struct cxl_register_map *map = &dport->comp_map;
+
+ if (!map->component_map.ras.valid)
+ dev_dbg(map->dev, "RAS registers not found\n");
+ else if (cxl_map_component_regs(map, &dport->regs.component,
+ BIT(CXL_CM_CAP_CAP_ID_RAS)))
+ dev_dbg(dport->dport_dev, "Failed to map RAS capability.\n");
+
+ if (dport->rch)
+ cxl_dport_map_rch_aer(dport);
+}
+
static struct cxl_dport *
__devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
int port_id, resource_size_t component_reg_phys,
@@ -1002,6 +1033,8 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
if (rc)
return ERR_PTR(rc);
+ cxl_dport_map_regs(dport);
+
cond_cxl_root_lock(port);
rc = add_dport(port, dport);
cond_cxl_root_unlock(port);
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index 60b7ba7f4030..982ef79670c7 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -198,6 +198,7 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
return ret_val;
}
+EXPORT_SYMBOL_NS_GPL(devm_cxl_iomap_block, CXL);
int cxl_map_component_regs(struct cxl_register_map *map,
struct cxl_component_regs *regs,
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 9f46a4e1fbec..12f11cf8e280 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -209,6 +209,13 @@ struct cxl_regs {
struct_group_tagged(cxl_device_regs, device_regs,
void __iomem *status, *mbox, *memdev;
);
+ /*
+ * RCH downstream port specific RAS register
+ * @aer: CXL 3.0 8.2.1.1 RCH Downstream Port RCRB
+ */
+ struct_group_tagged(cxl_rch_regs, rch_regs,
+ void __iomem *dport_aer;
+ );
};
struct cxl_reg_map {
@@ -255,6 +262,8 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
struct cxl_component_reg_map *map);
void cxl_probe_device_regs(struct device *dev, void __iomem *base,
struct cxl_device_reg_map *map);
+void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
+ resource_size_t length);
int cxl_map_component_regs(struct cxl_register_map *map,
struct cxl_component_regs *regs,
unsigned long map_mask);
@@ -598,6 +607,7 @@ struct cxl_rcrb_info {
* @rcrb: Data about the Root Complex Register Block layout
* @rch: Indicate whether this dport was enumerated in RCH or VH mode
* @port: reference to cxl_port that contains this downstream port
+ * @regs: Dport parsed register blocks
*/
struct cxl_dport {
struct device *dport_dev;
@@ -606,6 +616,7 @@ struct cxl_dport {
struct cxl_rcrb_info rcrb;
bool rch;
struct cxl_port *port;
+ struct cxl_regs regs;
};
/**
--
2.34.1
On 21.06.23 22:51:00, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> In devm_cxl_add_port() the port creation may fail and its associated
> pointer does not contain a valid address. During error message
> generation this invalid port address is used. Fix that wrong address
> access.
>
> Fixes: f3cd264c4ec1 ("cxl: Unify debug messages when calling devm_cxl_add_port()")
> Signed-off-by: Robert Richter <[email protected]>
> Reviewed-by: Dave Jiang <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]
> Signed-off-by: Dan Williams <[email protected]>
> ---
> drivers/cxl/core/port.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
This patch can be dropped as it is already upstream:
a70fc4ed20a6 cxl/port: Fix NULL pointer access in devm_cxl_add_port()
Instead, this is the base commit for the remaining patches of the
series.
-Robert
On Wed, 21 Jun 2023 22:51:05 -0500
Terry Bowman <[email protected]> wrote:
> From: Dan Williams <[email protected]>
>
> For symmetry with the recent rename of ->dport_dev for a 'struct
> cxl_dport', add the "_dev" suffix to the ->uport property of a 'struct
> cxl_port'. These devices represent the downstream-port-device and
> upstream-port-device respectively in the CXL/PCIe topology.
>
> Signed-off-by: Dan Williams <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Make sense.
If the later patches need more review time it might still be nice
to get these renames in place in the meantime to avoid merge conflict
mess for Dan.
Reviewed-by: Jonathan Cameron <[email protected]>
On Wed, 21 Jun 2023 22:51:04 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> Reading code like dport->dport does not immediately suggest that this
> points to the corresponding device structure of the dport. Rename
> struct member @dport to @dport_dev.
>
> While at it, also rename @new argument of add_dport() to @dport. This
> better describes the variable as a dport (e.g. new->dport becomes to
> dport->dport_dev).
>
> Co-developed-by: Terry Bowman <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
Excellent improvement in readability. I've fallen down this trap a few times
so glad to have it closed.
Reviewed-by: Jonathan Cameron <[email protected]>
> ---
> drivers/cxl/core/port.c | 20 ++++++++++----------
> drivers/cxl/core/region.c | 4 ++--
> drivers/cxl/cxl.h | 4 ++--
> 3 files changed, 14 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 76888c75dae4..7d3079f5b7b5 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -605,7 +605,7 @@ static int devm_cxl_link_parent_dport(struct device *host,
> if (!parent_dport)
> return 0;
>
> - rc = sysfs_create_link(&port->dev.kobj, &parent_dport->dport->kobj,
> + rc = sysfs_create_link(&port->dev.kobj, &parent_dport->dport_dev->kobj,
> "parent_dport");
> if (rc)
> return rc;
> @@ -658,7 +658,7 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
> if (iter->host_bridge)
> port->host_bridge = iter->host_bridge;
> else if (parent_dport->rch)
> - port->host_bridge = parent_dport->dport;
> + port->host_bridge = parent_dport->dport_dev;
> else
> port->host_bridge = iter->uport;
> dev_dbg(uport, "host-bridge: %s\n", dev_name(port->host_bridge));
> @@ -847,22 +847,22 @@ static struct cxl_dport *find_dport(struct cxl_port *port, int id)
> return NULL;
> }
>
> -static int add_dport(struct cxl_port *port, struct cxl_dport *new)
> +static int add_dport(struct cxl_port *port, struct cxl_dport *dport)
> {
> struct cxl_dport *dup;
> int rc;
>
> device_lock_assert(&port->dev);
> - dup = find_dport(port, new->port_id);
> + dup = find_dport(port, dport->port_id);
> if (dup) {
> dev_err(&port->dev,
> "unable to add dport%d-%s non-unique port id (%s)\n",
> - new->port_id, dev_name(new->dport),
> - dev_name(dup->dport));
> + dport->port_id, dev_name(dport->dport_dev),
> + dev_name(dup->dport_dev));
> return -EBUSY;
> }
>
> - rc = xa_insert(&port->dports, (unsigned long)new->dport, new,
> + rc = xa_insert(&port->dports, (unsigned long)dport->dport_dev, dport,
> GFP_KERNEL);
> if (rc)
> return rc;
> @@ -895,8 +895,8 @@ static void cxl_dport_remove(void *data)
> struct cxl_dport *dport = data;
> struct cxl_port *port = dport->port;
>
> - xa_erase(&port->dports, (unsigned long) dport->dport);
> - put_device(dport->dport);
> + xa_erase(&port->dports, (unsigned long) dport->dport_dev);
> + put_device(dport->dport_dev);
> }
>
> static void cxl_dport_unlink(void *data)
> @@ -954,7 +954,7 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> dev_dbg(dport_dev, "Component Registers found for dport: %pa\n",
> &component_reg_phys);
>
> - dport->dport = dport_dev;
> + dport->dport_dev = dport_dev;
> dport->port_id = port_id;
> dport->component_reg_phys = component_reg_phys;
> dport->port = port;
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index f822de44bee0..13cda989d944 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -1162,7 +1162,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> dev_dbg(&cxlr->dev, "%s:%s: %s expected %s at %d\n",
> dev_name(port->uport), dev_name(&port->dev),
> dev_name(&cxlsd->cxld.dev),
> - dev_name(ep->dport->dport),
> + dev_name(ep->dport->dport_dev),
> cxl_rr->nr_targets_set);
> return -ENXIO;
> }
> @@ -1173,7 +1173,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> cxl_rr->nr_targets_set += inc;
> dev_dbg(&cxlr->dev, "%s:%s target[%d] = %s for %s:%s @ %d\n",
> dev_name(port->uport), dev_name(&port->dev),
> - cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport),
> + cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport_dev),
> dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
>
> return 0;
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 7c8674079f1a..7232c2a0e27c 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -589,7 +589,7 @@ struct cxl_rcrb_info {
>
> /**
> * struct cxl_dport - CXL downstream port
> - * @dport: PCI bridge or firmware device representing the downstream link
> + * @dport_dev: PCI bridge or firmware device representing the downstream link
> * @port_id: unique hardware identifier for dport in decoder target list
> * @component_reg_phys: downstream port component registers
> * @rcrb: Data about the Root Complex Register Block layout
> @@ -597,7 +597,7 @@ struct cxl_rcrb_info {
> * @port: reference to cxl_port that contains this downstream port
> */
> struct cxl_dport {
> - struct device *dport;
> + struct device *dport_dev;
> int port_id;
> resource_size_t component_reg_phys;
> struct cxl_rcrb_info rcrb;
On Wed, 21 Jun 2023 22:51:06 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> The corresponding device of a register mapping is used for devm
> operations and logging. For operations with struct cxl_register_map
> the device needs to be kept track separately. To simpify the involved
> function interfaces, add @dev to cxl_register_map.
>
> While at it also reorder function arguments of cxl_map_device_regs()
> and cxl_map_component_regs() to have the object @cxl_register_map
> first.
>
> In a result a bunch of functions are available to be used with a
As a result? Not quite sure what this sentence means..
> @cxl_register_map object.
>
> This patch is in preparation of reworking the component register setup
> code.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
I've no strong opinion on the reordering though I slightly prefer it as
you have it after this. Will let Dan comment on whether he likes that or not.
Reviewed-by: Jonathan Cameron <[email protected]>
> ---
> drivers/cxl/core/hdm.c | 4 ++--
> drivers/cxl/core/regs.c | 18 ++++++++++++------
> drivers/cxl/cxl.h | 10 ++++++----
> drivers/cxl/pci.c | 23 +++++++++++------------
> 4 files changed, 31 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index 7889ff203a34..5abfa9276dac 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -85,6 +85,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
> struct cxl_component_regs *regs)
> {
> struct cxl_register_map map = {
> + .dev = &port->dev,
> .resource = port->component_reg_phys,
> .base = crb,
> .max_size = CXL_COMPONENT_REG_BLOCK_SIZE,
> @@ -97,8 +98,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
> return -ENODEV;
> }
>
> - return cxl_map_component_regs(&port->dev, regs, &map,
> - BIT(CXL_CM_CAP_CAP_ID_HDM));
> + return cxl_map_component_regs(&map, regs, BIT(CXL_CM_CAP_CAP_ID_HDM));
> }
>
> static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 6c4b33133918..713e4a9ca35a 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -199,9 +199,11 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> return ret_val;
> }
>
> -int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> - struct cxl_register_map *map, unsigned long map_mask)
> +int cxl_map_component_regs(struct cxl_register_map *map,
> + struct cxl_component_regs *regs,
> + unsigned long map_mask)
> {
> + struct device *dev = map->dev;
> struct mapinfo {
> struct cxl_reg_map *rmap;
> void __iomem **addr;
> @@ -231,10 +233,10 @@ int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> }
> EXPORT_SYMBOL_NS_GPL(cxl_map_component_regs, CXL);
>
> -int cxl_map_device_regs(struct device *dev,
> - struct cxl_device_regs *regs,
> - struct cxl_register_map *map)
> +int cxl_map_device_regs(struct cxl_register_map *map,
> + struct cxl_device_regs *regs)
> {
> + struct device *dev = map->dev;
> resource_size_t phys_addr = map->resource;
> struct mapinfo {
> struct cxl_reg_map *rmap;
> @@ -302,7 +304,11 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> u32 regloc_size, regblocks;
> int regloc, i;
>
> - map->resource = CXL_RESOURCE_NONE;
> + *map = (struct cxl_register_map) {
> + .dev = &pdev->dev,
> + .resource = CXL_RESOURCE_NONE,
> + };
> +
> regloc = pci_find_dvsec_capability(pdev, PCI_DVSEC_VENDOR_ID_CXL,
> CXL_DVSEC_REG_LOCATOR);
> if (!regloc)
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 754cfe59ae37..bd68d5fabf21 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -231,6 +231,7 @@ struct cxl_device_reg_map {
>
> /**
> * struct cxl_register_map - DVSEC harvested register block mapping parameters
> + * @dev: device for devm operations and logging
> * @base: virtual base of the register-block-BAR + @block_offset
> * @resource: physical resource base of the register block
> * @max_size: maximum mapping size to perform register search
> @@ -239,6 +240,7 @@ struct cxl_device_reg_map {
> * @device_map: cxl_reg_maps for device registers
> */
> struct cxl_register_map {
> + struct device *dev;
> void __iomem *base;
> resource_size_t resource;
> resource_size_t max_size;
> @@ -253,11 +255,11 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
> struct cxl_component_reg_map *map);
> void cxl_probe_device_regs(struct device *dev, void __iomem *base,
> struct cxl_device_reg_map *map);
> -int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> - struct cxl_register_map *map,
> +int cxl_map_component_regs(struct cxl_register_map *map,
> + struct cxl_component_regs *regs,
> unsigned long map_mask);
> -int cxl_map_device_regs(struct device *dev, struct cxl_device_regs *regs,
> - struct cxl_register_map *map);
> +int cxl_map_device_regs(struct cxl_register_map *map,
> + struct cxl_device_regs *regs);
>
> enum cxl_regloc_type;
> int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 0872f2233ed0..0a89b96e6a8d 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -274,9 +274,9 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
> return 0;
> }
>
> -static int cxl_map_regblock(struct pci_dev *pdev, struct cxl_register_map *map)
> +static int cxl_map_regblock(struct cxl_register_map *map)
> {
> - struct device *dev = &pdev->dev;
> + struct device *dev = map->dev;
>
> map->base = ioremap(map->resource, map->max_size);
> if (!map->base) {
> @@ -288,18 +288,17 @@ static int cxl_map_regblock(struct pci_dev *pdev, struct cxl_register_map *map)
> return 0;
> }
>
> -static void cxl_unmap_regblock(struct pci_dev *pdev,
> - struct cxl_register_map *map)
> +static void cxl_unmap_regblock(struct cxl_register_map *map)
> {
> iounmap(map->base);
> map->base = NULL;
> }
>
> -static int cxl_probe_regs(struct pci_dev *pdev, struct cxl_register_map *map)
> +static int cxl_probe_regs(struct cxl_register_map *map)
> {
> struct cxl_component_reg_map *comp_map;
> struct cxl_device_reg_map *dev_map;
> - struct device *dev = &pdev->dev;
> + struct device *dev = map->dev;
> void __iomem *base = map->base;
>
> switch (map->reg_type) {
> @@ -346,12 +345,12 @@ static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
> if (rc)
> return rc;
>
> - rc = cxl_map_regblock(pdev, map);
> + rc = cxl_map_regblock(map);
> if (rc)
> return rc;
>
> - rc = cxl_probe_regs(pdev, map);
> - cxl_unmap_regblock(pdev, map);
> + rc = cxl_probe_regs(map);
> + cxl_unmap_regblock(map);
>
> return rc;
> }
> @@ -688,7 +687,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> if (rc)
> return rc;
>
> - rc = cxl_map_device_regs(&pdev->dev, &cxlds->regs.device_regs, &map);
> + rc = cxl_map_device_regs(&map, &cxlds->regs.device_regs);
> if (rc)
> return rc;
>
> @@ -703,8 +702,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>
> cxlds->component_reg_phys = map.resource;
>
> - rc = cxl_map_component_regs(&pdev->dev, &cxlds->regs.component,
> - &map, BIT(CXL_CM_CAP_CAP_ID_RAS));
> + rc = cxl_map_component_regs(&map, &cxlds->regs.component,
> + BIT(CXL_CM_CAP_CAP_ID_RAS));
> if (rc)
> dev_dbg(&pdev->dev, "Failed to map RAS capability.\n");
>
On Wed, 21 Jun 2023 22:51:12 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> In order to move the RCH dport component register setup to cxl_pci the
> base address must be stored in CXL device state (cxlds) for both
> modes, RCH and VH. Store it in cxlds->component_reg_phys and use it
> for endpoint creation.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Seems reasonable.
Reviewed-by: Jonathan Cameron <[email protected]>
> ---
> drivers/cxl/mem.c | 9 ++++-----
> 1 file changed, 4 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 4cc461c22b8b..7638a7f8f333 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -51,7 +51,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> struct cxl_port *parent_port = parent_dport->port;
> struct cxl_dev_state *cxlds = cxlmd->cxlds;
> struct cxl_port *endpoint, *iter, *down;
> - resource_size_t component_reg_phys;
> int rc;
>
> /*
> @@ -72,11 +71,11 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> * typical register locator mechanism.
> */
> if (parent_dport->rch && cxlds->component_reg_phys == CXL_RESOURCE_NONE)
> - component_reg_phys =
> + cxlds->component_reg_phys =
> cxl_rcd_component_reg_phys(&cxlmd->dev, parent_dport);
> - else
> - component_reg_phys = cxlds->component_reg_phys;
> - endpoint = devm_cxl_add_port(host, &cxlmd->dev, component_reg_phys,
> +
> + endpoint = devm_cxl_add_port(host, &cxlmd->dev,
> + cxlds->component_reg_phys,
> parent_dport);
> if (IS_ERR(endpoint))
> return PTR_ERR(endpoint);
On Wed, 21 Jun 2023 22:51:24 -0500
Terry Bowman <[email protected]> wrote:
> The RCH root port contains root command AER registers that should not be
> enabled.[1] Disable these to prevent root port interrupts.
I'm a little dubious about a 'because the spec says' so argument.
If we can describe the path by which spurious interrupts turn up then
great - if not then fair enough.
One trivial spelling thing inline. With that fixed.
Reviewed-by: Jonathan Cameron <[email protected]>
>
> [1] CXL 3.0 - 12.2.1.1 RCH Downstream Port-detected Errors
>
> Signed-off-by: Terry Bowman <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> ---
> drivers/cxl/core/core.h | 6 ++++++
> drivers/cxl/core/pci.c | 29 +++++++++++++++++++++++++++++
> drivers/cxl/core/port.c | 3 +++
> 3 files changed, 38 insertions(+)
>
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 87467c633123..880bac9db376 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -86,4 +86,10 @@ enum cxl_poison_trace_type {
> CXL_POISON_TRACE_CLEAR,
> };
>
> +#ifdef CONFIG_PCIEAER_CXL
> +void cxl_disable_rch_root_ints(struct cxl_dport *dport);
> +#else
> +static inline void cxl_disable_rch_root_ints(struct cxl_dport *dport) { };
> +#endif
> +
> #endif /* __CXL_CORE_H__ */
> diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
> index 9e0eba5ccfc4..39a2f9f4f115 100644
> --- a/drivers/cxl/core/pci.c
> +++ b/drivers/cxl/core/pci.c
> @@ -838,6 +838,35 @@ static void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds)
> cxl_handle_rdport_ras(cxlds, dport);
> }
>
> +void cxl_disable_rch_root_ints(struct cxl_dport *dport)
> +{
> + void __iomem *aer_base = dport->regs.dport_aer;
> + struct pci_host_bridge *bridge;
> + u32 aer_cmd_mask, aer_cmd;
> +
> + if (!aer_base)
> + return;
> +
> + bridge = to_pci_host_bridge(dport->dport_dev);
> +
> + /*
> + * Disable RCH root port command interrupts.
> + * CXL 3.0 12.2.1.1 - RCH Downstream Port-detected Errors
> + *
> + * This sequnce may not be necessary. CXL spec states disabling
Spell check. (I often forget as well :(
> + * the root cmd register's interrupts is required. But, PCI spec
> + * shows these are disabled by default on reset.
> + */
> + if (bridge->native_cxl_error) {
> + aer_cmd_mask = (PCI_ERR_ROOT_CMD_COR_EN |
> + PCI_ERR_ROOT_CMD_NONFATAL_EN |
> + PCI_ERR_ROOT_CMD_FATAL_EN);
> + aer_cmd = readl(aer_base + PCI_ERR_ROOT_COMMAND);
> + aer_cmd &= ~aer_cmd_mask;
> + writel(aer_cmd, aer_base + PCI_ERR_ROOT_COMMAND);
> + }
> +}
> +
> #else
> static void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds) { }
> #endif
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 87a12e69aa8e..2d812bbaf05f 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -1035,6 +1035,9 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
>
> cxl_dport_map_regs(dport);
>
> + if (dport->rch)
> + cxl_disable_rch_root_ints(dport);
> +
> cond_cxl_root_lock(port);
> rc = add_dport(port, dport);
> cond_cxl_root_unlock(port);
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index cfae8307de90..37ecd756b224 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -678,7 +678,6 @@ static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
> device_set_pm_not_required(dev);
> dev->bus = &cxl_bus_type;
> dev->type = &cxl_port_type;
> -
:(
I can't see any argument that removing the blank line is an improvement.
> return port;
>
> err:
> @@ -974,6 +973,8 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> return ERR_PTR(-ENOMEM);
>
> if (rcrb != CXL_RESOURCE_NONE) {
> + struct pci_host_bridge *host_bridge;
> +
On Wed, 21 Jun 2023 22:51:26 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> AER corrected and uncorrectable internal errors (CIE/UIE) are masked
> in their corresponding mask registers per default once in power-up
> state. [1][2] Enable internal errors for RCECs to receive CXL
> downstream port errors of Restricted CXL Hosts (RCHs).
>
> [1] CXL 3.0 Spec, 12.2.1.1 - RCH Downstream Port Detected Errors
> [2] PCIe Base Spec r6.0, 7.8.4.3 Uncorrectable Error Mask Register,
> 7.8.4.6 Correctable Error Mask Register
>
> Co-developed-by: Terry Bowman <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Acked-by: Bjorn Helgaas <[email protected]>
Trivial suggestion inline. Either way
Reviewed-by: Jonathan Cameron <[email protected]>
>
> +static int handles_cxl_error_iter(struct pci_dev *dev, void *data)
> +{
> + int *handles_cxl = data;
> +
> + if (!*handles_cxl)
> + *handles_cxl = is_cxl_mem_dev(dev) && cxl_error_is_native(dev);
All bools, why not make handles_cxl a bool and avoid need for !! below?
> +
> + /* Non-zero terminates iteration */
> + return *handles_cxl;
> +}
> +
> +static bool handles_cxl_errors(struct pci_dev *rcec)
> +{
> + int handles_cxl = 0;
> +
> + if (pci_pcie_type(rcec) == PCI_EXP_TYPE_RC_EC &&
> + pcie_aer_is_native(rcec))
> + pcie_walk_rcec(rcec, handles_cxl_error_iter, &handles_cxl);
> +
> + return !!handles_cxl;
This is the one you can get rid of the !! on if you make handles_cxl a bool.
> +}
On Wed, 21 Jun 2023 22:51:22 -0500
Terry Bowman <[email protected]> wrote:
> The restricted CXL host (RCH) error handler will log protocol errors
> using AER and RAS status registers. The AER and RAS registers need
> to be virtually memory mapped before enabling interrupts. Update
> __devm_cxl_add_dport() to include RCH RAS and AER mapping.
>
> Add 'struct cxl_regs' to 'struct cxl_dport' for saving a unique copy of
> the RCH downstream port's mapped registers.
Copy of the address at which they are mapped, not the registers.
Probably worth tweaking description to make that clearer.
>
> Co-developed-by: Robert Richter <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Otherwise LGTM
Reviewed-by: Jonathan Cameron <[email protected]>
> ---
> drivers/cxl/core/port.c | 33 +++++++++++++++++++++++++++++++++
> drivers/cxl/core/regs.c | 1 +
> drivers/cxl/cxl.h | 11 +++++++++++
> 3 files changed, 45 insertions(+)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 41e79a36c1ae..87a12e69aa8e 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -8,6 +8,7 @@
> #include <linux/pci.h>
> #include <linux/slab.h>
> #include <linux/idr.h>
> +#include <linux/aer.h>
> #include <cxlmem.h>
> #include <cxlpci.h>
> #include <cxl.h>
> @@ -943,6 +944,36 @@ static void cxl_dport_unlink(void *data)
> sysfs_remove_link(&port->dev.kobj, link_name);
> }
>
> +static void cxl_dport_map_rch_aer(struct cxl_dport *dport)
> +{
> + struct cxl_rcrb_info *ri = &dport->rcrb;
> + struct cxl_port *port = dport->port;
> + void __iomem *dport_aer = NULL;
> + resource_size_t aer_phys;
> +
> + if (dport->rch && ri->aer_cap) {
> + aer_phys = ri->aer_cap + ri->base;
> + dport_aer = devm_cxl_iomap_block(&port->dev, aer_phys,
> + sizeof(struct aer_capability_regs));
> + }
> +
> + dport->regs.dport_aer = dport_aer;
> +}
> +
> +static void cxl_dport_map_regs(struct cxl_dport *dport)
> +{
> + struct cxl_register_map *map = &dport->comp_map;
> +
> + if (!map->component_map.ras.valid)
> + dev_dbg(map->dev, "RAS registers not found\n");
> + else if (cxl_map_component_regs(map, &dport->regs.component,
> + BIT(CXL_CM_CAP_CAP_ID_RAS)))
> + dev_dbg(dport->dport_dev, "Failed to map RAS capability.\n");
> +
> + if (dport->rch)
> + cxl_dport_map_rch_aer(dport);
> +}
> +
> static struct cxl_dport *
> __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> int port_id, resource_size_t component_reg_phys,
> @@ -1002,6 +1033,8 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> if (rc)
> return ERR_PTR(rc);
>
> + cxl_dport_map_regs(dport);
> +
> cond_cxl_root_lock(port);
> rc = add_dport(port, dport);
> cond_cxl_root_unlock(port);
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 60b7ba7f4030..982ef79670c7 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -198,6 +198,7 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
>
> return ret_val;
> }
> +EXPORT_SYMBOL_NS_GPL(devm_cxl_iomap_block, CXL);
>
> int cxl_map_component_regs(struct cxl_register_map *map,
> struct cxl_component_regs *regs,
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 9f46a4e1fbec..12f11cf8e280 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -209,6 +209,13 @@ struct cxl_regs {
> struct_group_tagged(cxl_device_regs, device_regs,
> void __iomem *status, *mbox, *memdev;
> );
> + /*
> + * RCH downstream port specific RAS register
> + * @aer: CXL 3.0 8.2.1.1 RCH Downstream Port RCRB
> + */
> + struct_group_tagged(cxl_rch_regs, rch_regs,
> + void __iomem *dport_aer;
> + );
> };
>
> struct cxl_reg_map {
> @@ -255,6 +262,8 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
> struct cxl_component_reg_map *map);
> void cxl_probe_device_regs(struct device *dev, void __iomem *base,
> struct cxl_device_reg_map *map);
> +void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> + resource_size_t length);
> int cxl_map_component_regs(struct cxl_register_map *map,
> struct cxl_component_regs *regs,
> unsigned long map_mask);
> @@ -598,6 +607,7 @@ struct cxl_rcrb_info {
> * @rcrb: Data about the Root Complex Register Block layout
> * @rch: Indicate whether this dport was enumerated in RCH or VH mode
> * @port: reference to cxl_port that contains this downstream port
> + * @regs: Dport parsed register blocks
> */
> struct cxl_dport {
> struct device *dport_dev;
> @@ -606,6 +616,7 @@ struct cxl_dport {
> struct cxl_rcrb_info rcrb;
> bool rch;
> struct cxl_port *port;
> + struct cxl_regs regs;
> };
>
> /**
On Wed, 21 Jun 2023 22:51:14 -0500
Terry Bowman <[email protected]> wrote:
> From: Robert Richter <[email protected]>
>
> CXL capabilities are stored in the Component Registers. To use them,
> the specific I/O ranges of the capabilities must be determined by
> probing the registers. For this, the whole Component Register range
> needs to be mapped temporarily to detect the offset and length of a
> capability range.
>
> In order to use more than one capability of a component (e.g. RAS and
> HDM) the Component Register are probed and its mappings created
> multiple times. This also causes overlapping I/O ranges as the whole
> Component Register range must be mapped again while a capability's I/O
> range is already mapped.
>
> Different capabilities cannot be setup at the same time. E.g. the RAS
> capability must be made available as soon as the PCI driver is bound,
> the HDM decoder is setup later during port enumeration. Moreover,
> during early setup it is still unknown if a certain capability is
> needed. A central capability setup is therefore not possible,
> capabilities must be individually enabled once needed during
> initialization.
>
> To avoid a duplicate register probe and overlapping I/O mappings, only
> probe the Component Registers one time and store the Component
> Register mapping in struct port. The stored mappings can be used later
> to iomap the capability register range when enabling the capability,
> which will be implemented in a follow-on patch.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Jonathan Cameron <[email protected]>
Hi Jonathan,
Thanks for the reviews.
On 6/22/23 08:16, Jonathan Cameron wrote:
> On Wed, 21 Jun 2023 22:51:22 -0500
> Terry Bowman <[email protected]> wrote:
>
>> The restricted CXL host (RCH) error handler will log protocol errors
>> using AER and RAS status registers. The AER and RAS registers need
>> to be virtually memory mapped before enabling interrupts. Update
>> __devm_cxl_add_dport() to include RCH RAS and AER mapping.
>>
>> Add 'struct cxl_regs' to 'struct cxl_dport' for saving a unique copy of
>> the RCH downstream port's mapped registers.
>
> Copy of the address at which they are mapped, not the registers.
> Probably worth tweaking description to make that clearer.
>
Good point. I'll change to the following:
"Add 'struct cxl_regs' to 'struct cxl_dport' for saving a pointer to
the RCH downstream port's AER and RAS registers."
Regards,
Terry
Hi Jonathan,
On 6/22/23 08:12, Jonathan Cameron wrote:
> On Wed, 21 Jun 2023 22:51:24 -0500
> Terry Bowman <[email protected]> wrote:
>
>> The RCH root port contains root command AER registers that should not be
>> enabled.[1] Disable these to prevent root port interrupts.
>
> I'm a little dubious about a 'because the spec says' so argument.
> If we can describe the path by which spurious interrupts turn up then
> great - if not then fair enough.
>
This was added to follow the spec. The RCH downstream port contains
root port control pci express capability for enabling and disabling root
port interrupts. The interrupts are (should be) disabled by default at
powerup according to the PCI spec. We know SW does not enable because
the RCH downstream port is not enumerated or managed by a port driver. I
cant say this patch is absolutely necessary but was not comfortable with
removing it either and want to avoid undefined behavior.
Regards,
Terry
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> The corresponding device of a register mapping is used for devm
> operations and logging. For operations with struct cxl_register_map
> the device needs to be kept track separately. To simpify the involved
> function interfaces, add @dev to cxl_register_map.
>
> While at it also reorder function arguments of cxl_map_device_regs()
> and cxl_map_component_regs() to have the object @cxl_register_map
> first.
>
> In a result a bunch of functions are available to be used with a
> @cxl_register_map object.
>
> This patch is in preparation of reworking the component register setup
> code.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/hdm.c | 4 ++--
> drivers/cxl/core/regs.c | 18 ++++++++++++------
> drivers/cxl/cxl.h | 10 ++++++----
> drivers/cxl/pci.c | 23 +++++++++++------------
> 4 files changed, 31 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index 7889ff203a34..5abfa9276dac 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -85,6 +85,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
> struct cxl_component_regs *regs)
> {
> struct cxl_register_map map = {
> + .dev = &port->dev,
> .resource = port->component_reg_phys,
> .base = crb,
> .max_size = CXL_COMPONENT_REG_BLOCK_SIZE,
> @@ -97,8 +98,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
> return -ENODEV;
> }
>
> - return cxl_map_component_regs(&port->dev, regs, &map,
> - BIT(CXL_CM_CAP_CAP_ID_HDM));
> + return cxl_map_component_regs(&map, regs, BIT(CXL_CM_CAP_CAP_ID_HDM));
> }
>
> static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 6c4b33133918..713e4a9ca35a 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -199,9 +199,11 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
> return ret_val;
> }
>
> -int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> - struct cxl_register_map *map, unsigned long map_mask)
> +int cxl_map_component_regs(struct cxl_register_map *map,
> + struct cxl_component_regs *regs,
> + unsigned long map_mask)
> {
> + struct device *dev = map->dev;
> struct mapinfo {
> struct cxl_reg_map *rmap;
> void __iomem **addr;
> @@ -231,10 +233,10 @@ int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> }
> EXPORT_SYMBOL_NS_GPL(cxl_map_component_regs, CXL);
>
> -int cxl_map_device_regs(struct device *dev,
> - struct cxl_device_regs *regs,
> - struct cxl_register_map *map)
> +int cxl_map_device_regs(struct cxl_register_map *map,
> + struct cxl_device_regs *regs)
> {
> + struct device *dev = map->dev;
> resource_size_t phys_addr = map->resource;
> struct mapinfo {
> struct cxl_reg_map *rmap;
> @@ -302,7 +304,11 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> u32 regloc_size, regblocks;
> int regloc, i;
>
> - map->resource = CXL_RESOURCE_NONE;
> + *map = (struct cxl_register_map) {
> + .dev = &pdev->dev,
> + .resource = CXL_RESOURCE_NONE,
> + };
> +
> regloc = pci_find_dvsec_capability(pdev, PCI_DVSEC_VENDOR_ID_CXL,
> CXL_DVSEC_REG_LOCATOR);
> if (!regloc)
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 754cfe59ae37..bd68d5fabf21 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -231,6 +231,7 @@ struct cxl_device_reg_map {
>
> /**
> * struct cxl_register_map - DVSEC harvested register block mapping parameters
> + * @dev: device for devm operations and logging
> * @base: virtual base of the register-block-BAR + @block_offset
> * @resource: physical resource base of the register block
> * @max_size: maximum mapping size to perform register search
> @@ -239,6 +240,7 @@ struct cxl_device_reg_map {
> * @device_map: cxl_reg_maps for device registers
> */
> struct cxl_register_map {
> + struct device *dev;
> void __iomem *base;
> resource_size_t resource;
> resource_size_t max_size;
> @@ -253,11 +255,11 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
> struct cxl_component_reg_map *map);
> void cxl_probe_device_regs(struct device *dev, void __iomem *base,
> struct cxl_device_reg_map *map);
> -int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
> - struct cxl_register_map *map,
> +int cxl_map_component_regs(struct cxl_register_map *map,
> + struct cxl_component_regs *regs,
> unsigned long map_mask);
> -int cxl_map_device_regs(struct device *dev, struct cxl_device_regs *regs,
> - struct cxl_register_map *map);
> +int cxl_map_device_regs(struct cxl_register_map *map,
> + struct cxl_device_regs *regs);
>
> enum cxl_regloc_type;
> int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 0872f2233ed0..0a89b96e6a8d 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -274,9 +274,9 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
> return 0;
> }
>
> -static int cxl_map_regblock(struct pci_dev *pdev, struct cxl_register_map *map)
> +static int cxl_map_regblock(struct cxl_register_map *map)
> {
> - struct device *dev = &pdev->dev;
> + struct device *dev = map->dev;
>
> map->base = ioremap(map->resource, map->max_size);
> if (!map->base) {
> @@ -288,18 +288,17 @@ static int cxl_map_regblock(struct pci_dev *pdev, struct cxl_register_map *map)
> return 0;
> }
>
> -static void cxl_unmap_regblock(struct pci_dev *pdev,
> - struct cxl_register_map *map)
> +static void cxl_unmap_regblock(struct cxl_register_map *map)
> {
> iounmap(map->base);
> map->base = NULL;
> }
>
> -static int cxl_probe_regs(struct pci_dev *pdev, struct cxl_register_map *map)
> +static int cxl_probe_regs(struct cxl_register_map *map)
> {
> struct cxl_component_reg_map *comp_map;
> struct cxl_device_reg_map *dev_map;
> - struct device *dev = &pdev->dev;
> + struct device *dev = map->dev;
> void __iomem *base = map->base;
>
> switch (map->reg_type) {
> @@ -346,12 +345,12 @@ static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
> if (rc)
> return rc;
>
> - rc = cxl_map_regblock(pdev, map);
> + rc = cxl_map_regblock(map);
> if (rc)
> return rc;
>
> - rc = cxl_probe_regs(pdev, map);
> - cxl_unmap_regblock(pdev, map);
> + rc = cxl_probe_regs(map);
> + cxl_unmap_regblock(map);
>
> return rc;
> }
> @@ -688,7 +687,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> if (rc)
> return rc;
>
> - rc = cxl_map_device_regs(&pdev->dev, &cxlds->regs.device_regs, &map);
> + rc = cxl_map_device_regs(&map, &cxlds->regs.device_regs);
> if (rc)
> return rc;
>
> @@ -703,8 +702,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>
> cxlds->component_reg_phys = map.resource;
>
> - rc = cxl_map_component_regs(&pdev->dev, &cxlds->regs.component,
> - &map, BIT(CXL_CM_CAP_CAP_ID_RAS));
> + rc = cxl_map_component_regs(&map, &cxlds->regs.component,
> + BIT(CXL_CM_CAP_CAP_ID_RAS));
> if (rc)
> dev_dbg(&pdev->dev, "Failed to map RAS capability.\n");
>
On 6/21/23 20:51, Terry Bowman wrote:
> From: Dan Williams <[email protected]>
>
> Prepare cxl_probe_rcrb() for retrieving more than just the component
> register block. The RCH AER handling code wants to get back to the AER
> capability that happens to be MMIO mapped rather then configuration
> cycles.
>
> Move RCRB specific downstream port data, like the RCRB base and the
> AER capability offset, into its own data structure ('struct
> cxl_rcrb_info') for cxl_probe_rcrb() to fill. Extend 'struct
> cxl_dport' to include a 'struct cxl_rcrb_info' attribute.
>
> This centralizes all RCRB scanning in one routine.
>
> Signed-off-by: Dan Williams <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/core.h | 4 +++-
> drivers/cxl/core/port.c | 4 ++--
> drivers/cxl/core/regs.c | 5 +++--
> drivers/cxl/cxl.h | 9 +++++++--
> tools/testing/cxl/test/mock.c | 4 +++-
> 5 files changed, 18 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index bd0a5788c696..b001669a5133 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -68,7 +68,9 @@ enum cxl_rcrb {
> CXL_RCRB_DOWNSTREAM,
> CXL_RCRB_UPSTREAM,
> };
> -resource_size_t __rcrb_to_component(struct device *dev, resource_size_t rcrb,
> +struct cxl_rcrb_info;
> +resource_size_t __rcrb_to_component(struct device *dev,
> + struct cxl_rcrb_info *ri,
> enum cxl_rcrb which);
>
> extern struct rw_semaphore cxl_dpa_rwsem;
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 45f5299af7a6..76888c75dae4 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -939,7 +939,8 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> return ERR_PTR(-ENOMEM);
>
> if (rcrb != CXL_RESOURCE_NONE) {
> - component_reg_phys = __rcrb_to_component(dport_dev, rcrb,
> + dport->rcrb.base = rcrb;
> + component_reg_phys = __rcrb_to_component(dport_dev, &dport->rcrb,
> CXL_RCRB_DOWNSTREAM);
> if (component_reg_phys == CXL_RESOURCE_NONE) {
> dev_warn(dport_dev, "Invalid Component Registers in RCRB");
> @@ -957,7 +958,6 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> dport->port_id = port_id;
> dport->component_reg_phys = component_reg_phys;
> dport->port = port;
> - dport->rcrb = rcrb;
>
> cond_cxl_root_lock(port);
> rc = add_dport(port, dport);
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 564dd430258a..6c4b33133918 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -332,10 +332,11 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> }
> EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
>
> -resource_size_t __rcrb_to_component(struct device *dev, resource_size_t rcrb,
> +resource_size_t __rcrb_to_component(struct device *dev, struct cxl_rcrb_info *ri,
> enum cxl_rcrb which)
> {
> resource_size_t component_reg_phys;
> + resource_size_t rcrb = ri->base;
> void __iomem *addr;
> u32 bar0, bar1;
> u16 cmd;
> @@ -400,6 +401,6 @@ resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
> {
> if (!dport->rch)
> return CXL_RESOURCE_NONE;
> - return __rcrb_to_component(dev, dport->rcrb, CXL_RCRB_UPSTREAM);
> + return __rcrb_to_component(dev, &dport->rcrb, CXL_RCRB_UPSTREAM);
> }
> EXPORT_SYMBOL_NS_GPL(cxl_rcd_component_reg_phys, CXL);
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 28888bb0c088..7c8674079f1a 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -582,12 +582,17 @@ cxl_find_dport_by_dev(struct cxl_port *port, const struct device *dport_dev)
> return xa_load(&port->dports, (unsigned long)dport_dev);
> }
>
> +struct cxl_rcrb_info {
> + resource_size_t base;
> + u16 aer_cap;
> +};
> +
> /**
> * struct cxl_dport - CXL downstream port
> * @dport: PCI bridge or firmware device representing the downstream link
> * @port_id: unique hardware identifier for dport in decoder target list
> * @component_reg_phys: downstream port component registers
> - * @rcrb: base address for the Root Complex Register Block
> + * @rcrb: Data about the Root Complex Register Block layout
> * @rch: Indicate whether this dport was enumerated in RCH or VH mode
> * @port: reference to cxl_port that contains this downstream port
> */
> @@ -595,7 +600,7 @@ struct cxl_dport {
> struct device *dport;
> int port_id;
> resource_size_t component_reg_phys;
> - resource_size_t rcrb;
> + struct cxl_rcrb_info rcrb;
> bool rch;
> struct cxl_port *port;
> };
> diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c
> index 30119a16ae85..dbeef5c6f606 100644
> --- a/tools/testing/cxl/test/mock.c
> +++ b/tools/testing/cxl/test/mock.c
> @@ -271,8 +271,10 @@ struct cxl_dport *__wrap_devm_cxl_add_rch_dport(struct cxl_port *port,
> if (ops && ops->is_mock_port(dport_dev)) {
> dport = devm_cxl_add_dport(port, dport_dev, port_id,
> CXL_RESOURCE_NONE);
> - if (!IS_ERR(dport))
> + if (!IS_ERR(dport)) {
> + dport->rcrb.base = rcrb;
> dport->rch = true;
> + }
> } else
> dport = devm_cxl_add_rch_dport(port, dport_dev, port_id, rcrb);
> put_cxl_mock_ops(index);
On 6/21/23 20:51, Terry Bowman wrote:
> From: Dan Williams <[email protected]>
>
> For symmetry with the recent rename of ->dport_dev for a 'struct
> cxl_dport', add the "_dev" suffix to the ->uport property of a 'struct
> cxl_port'. These devices represent the downstream-port-device and
> upstream-port-device respectively in the CXL/PCIe topology.
>
> Signed-off-by: Dan Williams <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/pci.c | 4 +--
> drivers/cxl/core/port.c | 61 +++++++++++++++++++----------------
> drivers/cxl/core/region.c | 48 ++++++++++++++-------------
> drivers/cxl/cxl.h | 13 ++++----
> drivers/cxl/cxlmem.h | 4 +--
> drivers/cxl/mem.c | 2 +-
> drivers/cxl/port.c | 2 +-
> tools/testing/cxl/test/cxl.c | 20 ++++++------
> tools/testing/cxl/test/mock.c | 10 +++---
> 9 files changed, 86 insertions(+), 78 deletions(-)
>
> diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
> index 67f4ab6daa34..375f01c6cad6 100644
> --- a/drivers/cxl/core/pci.c
> +++ b/drivers/cxl/core/pci.c
> @@ -67,7 +67,7 @@ static int match_add_dports(struct pci_dev *pdev, void *data)
>
> /**
> * devm_cxl_port_enumerate_dports - enumerate downstream ports of the upstream port
> - * @port: cxl_port whose ->uport is the upstream of dports to be enumerated
> + * @port: cxl_port whose ->uport_dev is the upstream of dports to be enumerated
> *
> * Returns a positive number of dports enumerated or a negative error
> * code.
> @@ -622,7 +622,7 @@ static int cxl_cdat_read_table(struct device *dev,
> */
> void read_cdat_data(struct cxl_port *port)
> {
> - struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport);
> + struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev);
> struct device *host = cxlmd->dev.parent;
> struct device *dev = &port->dev;
> struct pci_doe_mb *cdat_doe;
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 7d3079f5b7b5..cdfe0ea7a2e9 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -561,9 +561,9 @@ static void unregister_port(void *_port)
> * unregistered while holding their parent port lock.
> */
> if (!parent)
> - lock_dev = port->uport;
> + lock_dev = port->uport_dev;
> else if (is_cxl_root(parent))
> - lock_dev = parent->uport;
> + lock_dev = parent->uport_dev;
> else
> lock_dev = &parent->dev;
>
> @@ -583,7 +583,8 @@ static int devm_cxl_link_uport(struct device *host, struct cxl_port *port)
> {
> int rc;
>
> - rc = sysfs_create_link(&port->dev.kobj, &port->uport->kobj, "uport");
> + rc = sysfs_create_link(&port->dev.kobj, &port->uport_dev->kobj,
> + "uport");
> if (rc)
> return rc;
> return devm_add_action_or_reset(host, cxl_unlink_uport, port);
> @@ -614,7 +615,7 @@ static int devm_cxl_link_parent_dport(struct device *host,
>
> static struct lock_class_key cxl_port_key;
>
> -static struct cxl_port *cxl_port_alloc(struct device *uport,
> +static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
> resource_size_t component_reg_phys,
> struct cxl_dport *parent_dport)
> {
> @@ -630,7 +631,7 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
> if (rc < 0)
> goto err;
> port->id = rc;
> - port->uport = uport;
> + port->uport_dev = uport_dev;
>
> /*
> * The top-level cxl_port "cxl_root" does not have a cxl_port as
> @@ -660,10 +661,11 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
> else if (parent_dport->rch)
> port->host_bridge = parent_dport->dport_dev;
> else
> - port->host_bridge = iter->uport;
> - dev_dbg(uport, "host-bridge: %s\n", dev_name(port->host_bridge));
> + port->host_bridge = iter->uport_dev;
> + dev_dbg(uport_dev, "host-bridge: %s\n",
> + dev_name(port->host_bridge));
> } else
> - dev->parent = uport;
> + dev->parent = uport_dev;
>
> port->component_reg_phys = component_reg_phys;
> ida_init(&port->decoder_ida);
> @@ -687,7 +689,7 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
> }
>
> static struct cxl_port *__devm_cxl_add_port(struct device *host,
> - struct device *uport,
> + struct device *uport_dev,
> resource_size_t component_reg_phys,
> struct cxl_dport *parent_dport)
> {
> @@ -695,12 +697,12 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
> struct device *dev;
> int rc;
>
> - port = cxl_port_alloc(uport, component_reg_phys, parent_dport);
> + port = cxl_port_alloc(uport_dev, component_reg_phys, parent_dport);
> if (IS_ERR(port))
> return port;
>
> dev = &port->dev;
> - if (is_cxl_memdev(uport))
> + if (is_cxl_memdev(uport_dev))
> rc = dev_set_name(dev, "endpoint%d", port->id);
> else if (parent_dport)
> rc = dev_set_name(dev, "port%d", port->id);
> @@ -735,28 +737,29 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
> /**
> * devm_cxl_add_port - register a cxl_port in CXL memory decode hierarchy
> * @host: host device for devm operations
> - * @uport: "physical" device implementing this upstream port
> + * @uport_dev: "physical" device implementing this upstream port
> * @component_reg_phys: (optional) for configurable cxl_port instances
> * @parent_dport: next hop up in the CXL memory decode hierarchy
> */
> -struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
> +struct cxl_port *devm_cxl_add_port(struct device *host,
> + struct device *uport_dev,
> resource_size_t component_reg_phys,
> struct cxl_dport *parent_dport)
> {
> struct cxl_port *port, *parent_port;
>
> - port = __devm_cxl_add_port(host, uport, component_reg_phys,
> + port = __devm_cxl_add_port(host, uport_dev, component_reg_phys,
> parent_dport);
>
> parent_port = parent_dport ? parent_dport->port : NULL;
> if (IS_ERR(port)) {
> - dev_dbg(uport, "Failed to add%s%s%s: %ld\n",
> + dev_dbg(uport_dev, "Failed to add%s%s%s: %ld\n",
> parent_port ? " port to " : "",
> parent_port ? dev_name(&parent_port->dev) : "",
> parent_port ? "" : " root port",
> PTR_ERR(port));
> } else {
> - dev_dbg(uport, "%s added%s%s%s\n",
> + dev_dbg(uport_dev, "%s added%s%s%s\n",
> dev_name(&port->dev),
> parent_port ? " to " : "",
> parent_port ? dev_name(&parent_port->dev) : "",
> @@ -773,33 +776,34 @@ struct pci_bus *cxl_port_to_pci_bus(struct cxl_port *port)
> if (is_cxl_root(port))
> return NULL;
>
> - if (dev_is_pci(port->uport)) {
> - struct pci_dev *pdev = to_pci_dev(port->uport);
> + if (dev_is_pci(port->uport_dev)) {
> + struct pci_dev *pdev = to_pci_dev(port->uport_dev);
>
> return pdev->subordinate;
> }
>
> - return xa_load(&cxl_root_buses, (unsigned long)port->uport);
> + return xa_load(&cxl_root_buses, (unsigned long)port->uport_dev);
> }
> EXPORT_SYMBOL_NS_GPL(cxl_port_to_pci_bus, CXL);
>
> -static void unregister_pci_bus(void *uport)
> +static void unregister_pci_bus(void *uport_dev)
> {
> - xa_erase(&cxl_root_buses, (unsigned long)uport);
> + xa_erase(&cxl_root_buses, (unsigned long)uport_dev);
> }
>
> -int devm_cxl_register_pci_bus(struct device *host, struct device *uport,
> +int devm_cxl_register_pci_bus(struct device *host, struct device *uport_dev,
> struct pci_bus *bus)
> {
> int rc;
>
> - if (dev_is_pci(uport))
> + if (dev_is_pci(uport_dev))
> return -EINVAL;
>
> - rc = xa_insert(&cxl_root_buses, (unsigned long)uport, bus, GFP_KERNEL);
> + rc = xa_insert(&cxl_root_buses, (unsigned long)uport_dev, bus,
> + GFP_KERNEL);
> if (rc)
> return rc;
> - return devm_add_action_or_reset(host, unregister_pci_bus, uport);
> + return devm_add_action_or_reset(host, unregister_pci_bus, uport_dev);
> }
> EXPORT_SYMBOL_NS_GPL(devm_cxl_register_pci_bus, CXL);
>
> @@ -920,7 +924,7 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> int rc;
>
> if (is_cxl_root(port))
> - host = port->uport;
> + host = port->uport_dev;
> else
> host = &port->dev;
>
> @@ -1374,7 +1378,7 @@ static int add_port_attach_ep(struct cxl_memdev *cxlmd,
> rc = PTR_ERR(port);
> else {
> dev_dbg(&cxlmd->dev, "add to new port %s:%s\n",
> - dev_name(&port->dev), dev_name(port->uport));
> + dev_name(&port->dev), dev_name(port->uport_dev));
> rc = cxl_add_ep(dport, &cxlmd->dev);
> if (rc == -EBUSY) {
> /*
> @@ -1436,7 +1440,8 @@ int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd)
> if (port) {
> dev_dbg(&cxlmd->dev,
> "found already registered port %s:%s\n",
> - dev_name(&port->dev), dev_name(port->uport));
> + dev_name(&port->dev),
> + dev_name(port->uport_dev));
> rc = cxl_add_ep(dport, &cxlmd->dev);
>
> /*
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 13cda989d944..39825e5301d0 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -906,10 +906,10 @@ static int cxl_port_attach_region(struct cxl_port *port,
>
> dev_dbg(&cxlr->dev,
> "%s:%s %s add: %s:%s @ %d next: %s nr_eps: %d nr_targets: %d\n",
> - dev_name(port->uport), dev_name(&port->dev),
> + dev_name(port->uport_dev), dev_name(&port->dev),
> dev_name(&cxld->dev), dev_name(&cxlmd->dev),
> dev_name(&cxled->cxld.dev), pos,
> - ep ? ep->next ? dev_name(ep->next->uport) :
> + ep ? ep->next ? dev_name(ep->next->uport_dev) :
> dev_name(&cxlmd->dev) :
> "none",
> cxl_rr->nr_eps, cxl_rr->nr_targets);
> @@ -984,7 +984,7 @@ static int check_last_peer(struct cxl_endpoint_decoder *cxled,
> */
> if (pos < distance) {
> dev_dbg(&cxlr->dev, "%s:%s: cannot host %s:%s at %d\n",
> - dev_name(port->uport), dev_name(&port->dev),
> + dev_name(port->uport_dev), dev_name(&port->dev),
> dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
> return -ENXIO;
> }
> @@ -994,7 +994,7 @@ static int check_last_peer(struct cxl_endpoint_decoder *cxled,
> if (ep->dport != ep_peer->dport) {
> dev_dbg(&cxlr->dev,
> "%s:%s: %s:%s pos %d mismatched peer %s:%s\n",
> - dev_name(port->uport), dev_name(&port->dev),
> + dev_name(port->uport_dev), dev_name(&port->dev),
> dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos,
> dev_name(&cxlmd_peer->dev),
> dev_name(&cxled_peer->cxld.dev));
> @@ -1026,7 +1026,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> */
> if (!is_power_of_2(cxl_rr->nr_targets)) {
> dev_dbg(&cxlr->dev, "%s:%s: invalid target count %d\n",
> - dev_name(port->uport), dev_name(&port->dev),
> + dev_name(port->uport_dev), dev_name(&port->dev),
> cxl_rr->nr_targets);
> return -EINVAL;
> }
> @@ -1076,7 +1076,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> rc = granularity_to_eig(parent_ig, &peig);
> if (rc) {
> dev_dbg(&cxlr->dev, "%s:%s: invalid parent granularity: %d\n",
> - dev_name(parent_port->uport),
> + dev_name(parent_port->uport_dev),
> dev_name(&parent_port->dev), parent_ig);
> return rc;
> }
> @@ -1084,7 +1084,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> rc = ways_to_eiw(parent_iw, &peiw);
> if (rc) {
> dev_dbg(&cxlr->dev, "%s:%s: invalid parent interleave: %d\n",
> - dev_name(parent_port->uport),
> + dev_name(parent_port->uport_dev),
> dev_name(&parent_port->dev), parent_iw);
> return rc;
> }
> @@ -1093,7 +1093,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> rc = ways_to_eiw(iw, &eiw);
> if (rc) {
> dev_dbg(&cxlr->dev, "%s:%s: invalid port interleave: %d\n",
> - dev_name(port->uport), dev_name(&port->dev), iw);
> + dev_name(port->uport_dev), dev_name(&port->dev), iw);
> return rc;
> }
>
> @@ -1113,7 +1113,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> rc = eig_to_granularity(eig, &ig);
> if (rc) {
> dev_dbg(&cxlr->dev, "%s:%s: invalid interleave: %d\n",
> - dev_name(port->uport), dev_name(&port->dev),
> + dev_name(port->uport_dev), dev_name(&port->dev),
> 256 << eig);
> return rc;
> }
> @@ -1126,11 +1126,11 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)) {
> dev_err(&cxlr->dev,
> "%s:%s %s expected iw: %d ig: %d %pr\n",
> - dev_name(port->uport), dev_name(&port->dev),
> + dev_name(port->uport_dev), dev_name(&port->dev),
> __func__, iw, ig, p->res);
> dev_err(&cxlr->dev,
> "%s:%s %s got iw: %d ig: %d state: %s %#llx:%#llx\n",
> - dev_name(port->uport), dev_name(&port->dev),
> + dev_name(port->uport_dev), dev_name(&port->dev),
> __func__, cxld->interleave_ways,
> cxld->interleave_granularity,
> (cxld->flags & CXL_DECODER_F_ENABLE) ?
> @@ -1147,20 +1147,20 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> .end = p->res->end,
> };
> }
> - dev_dbg(&cxlr->dev, "%s:%s iw: %d ig: %d\n", dev_name(port->uport),
> + dev_dbg(&cxlr->dev, "%s:%s iw: %d ig: %d\n", dev_name(port->uport_dev),
> dev_name(&port->dev), iw, ig);
> add_target:
> if (cxl_rr->nr_targets_set == cxl_rr->nr_targets) {
> dev_dbg(&cxlr->dev,
> "%s:%s: targets full trying to add %s:%s at %d\n",
> - dev_name(port->uport), dev_name(&port->dev),
> + dev_name(port->uport_dev), dev_name(&port->dev),
> dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
> return -ENXIO;
> }
> if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags)) {
> if (cxlsd->target[cxl_rr->nr_targets_set] != ep->dport) {
> dev_dbg(&cxlr->dev, "%s:%s: %s expected %s at %d\n",
> - dev_name(port->uport), dev_name(&port->dev),
> + dev_name(port->uport_dev), dev_name(&port->dev),
> dev_name(&cxlsd->cxld.dev),
> dev_name(ep->dport->dport_dev),
> cxl_rr->nr_targets_set);
> @@ -1172,7 +1172,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> out_target_set:
> cxl_rr->nr_targets_set += inc;
> dev_dbg(&cxlr->dev, "%s:%s target[%d] = %s for %s:%s @ %d\n",
> - dev_name(port->uport), dev_name(&port->dev),
> + dev_name(port->uport_dev), dev_name(&port->dev),
> cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport_dev),
> dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
>
> @@ -1492,7 +1492,7 @@ static int cmp_decode_pos(const void *a, const void *b)
> if (!dev) {
> struct range *range = &cxled_a->cxld.hpa_range;
>
> - dev_err(port->uport,
> + dev_err(port->uport_dev,
> "failed to find decoder that maps %#llx-%#llx\n",
> range->start, range->end);
> goto err;
> @@ -1507,14 +1507,15 @@ static int cmp_decode_pos(const void *a, const void *b)
> put_device(dev);
>
> if (a_pos < 0 || b_pos < 0) {
> - dev_err(port->uport,
> + dev_err(port->uport_dev,
> "failed to find shared decoder for %s and %s\n",
> dev_name(cxlmd_a->dev.parent),
> dev_name(cxlmd_b->dev.parent));
> goto err;
> }
>
> - dev_dbg(port->uport, "%s comes %s %s\n", dev_name(cxlmd_a->dev.parent),
> + dev_dbg(port->uport_dev, "%s comes %s %s\n",
> + dev_name(cxlmd_a->dev.parent),
> a_pos - b_pos < 0 ? "before" : "after",
> dev_name(cxlmd_b->dev.parent));
>
> @@ -2059,11 +2060,11 @@ static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd,
> if (rc)
> goto err;
>
> - rc = devm_add_action_or_reset(port->uport, unregister_region, cxlr);
> + rc = devm_add_action_or_reset(port->uport_dev, unregister_region, cxlr);
> if (rc)
> return ERR_PTR(rc);
>
> - dev_dbg(port->uport, "%s: created %s\n",
> + dev_dbg(port->uport_dev, "%s: created %s\n",
> dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev));
> return cxlr;
>
> @@ -2191,7 +2192,7 @@ static ssize_t delete_region_store(struct device *dev,
> if (IS_ERR(cxlr))
> return PTR_ERR(cxlr);
>
> - devm_release_action(port->uport, unregister_region, cxlr);
> + devm_release_action(port->uport_dev, unregister_region, cxlr);
> put_device(&cxlr->dev);
>
> return len;
> @@ -2356,7 +2357,8 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port)
>
> rc = device_for_each_child(&port->dev, &ctx, poison_by_decoder);
> if (rc == 1)
> - rc = cxl_get_poison_unmapped(to_cxl_memdev(port->uport), &ctx);
> + rc = cxl_get_poison_unmapped(to_cxl_memdev(port->uport_dev),
> + &ctx);
>
> up_read(&cxl_region_rwsem);
> return rc;
> @@ -2732,7 +2734,7 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
>
> err:
> up_write(&cxl_region_rwsem);
> - devm_release_action(port->uport, unregister_region, cxlr);
> + devm_release_action(port->uport_dev, unregister_region, cxlr);
> return ERR_PTR(rc);
> }
>
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 7232c2a0e27c..754cfe59ae37 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -536,7 +536,7 @@ struct cxl_dax_region {
> * downstream port devices to construct a CXL memory
> * decode hierarchy.
> * @dev: this port's device
> - * @uport: PCI or platform device implementing the upstream port capability
> + * @uport_dev: PCI or platform device implementing the upstream port capability
> * @host_bridge: Shortcut to the platform attach point for this port
> * @id: id for port device-name
> * @dports: cxl_dport instances referenced by decoders
> @@ -555,7 +555,7 @@ struct cxl_dax_region {
> */
> struct cxl_port {
> struct device dev;
> - struct device *uport;
> + struct device *uport_dev;
> struct device *host_bridge;
> int id;
> struct xarray dports;
> @@ -641,21 +641,22 @@ struct cxl_region_ref {
> /*
> * The platform firmware device hosting the root is also the top of the
> * CXL port topology. All other CXL ports have another CXL port as their
> - * parent and their ->uport / host device is out-of-line of the port
> + * parent and their ->uport_dev / host device is out-of-line of the port
> * ancestry.
> */
> static inline bool is_cxl_root(struct cxl_port *port)
> {
> - return port->uport == port->dev.parent;
> + return port->uport_dev == port->dev.parent;
> }
>
> bool is_cxl_port(const struct device *dev);
> struct cxl_port *to_cxl_port(const struct device *dev);
> struct pci_bus;
> -int devm_cxl_register_pci_bus(struct device *host, struct device *uport,
> +int devm_cxl_register_pci_bus(struct device *host, struct device *uport_dev,
> struct pci_bus *bus);
> struct pci_bus *cxl_port_to_pci_bus(struct cxl_port *port);
> -struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
> +struct cxl_port *devm_cxl_add_port(struct device *host,
> + struct device *uport_dev,
> resource_size_t component_reg_phys,
> struct cxl_dport *parent_dport);
> struct cxl_port *find_cxl_root(struct cxl_port *port);
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index a2845a7a69d8..76743016b64c 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -72,13 +72,13 @@ cxled_to_memdev(struct cxl_endpoint_decoder *cxled)
> {
> struct cxl_port *port = to_cxl_port(cxled->cxld.dev.parent);
>
> - return to_cxl_memdev(port->uport);
> + return to_cxl_memdev(port->uport_dev);
> }
>
> bool is_cxl_memdev(const struct device *dev);
> static inline bool is_cxl_endpoint(struct cxl_port *port)
> {
> - return is_cxl_memdev(port->uport);
> + return is_cxl_memdev(port->uport_dev);
> }
>
> struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds);
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 45d4c32d78b0..4cc461c22b8b 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -163,7 +163,7 @@ static int cxl_mem_probe(struct device *dev)
> }
>
> if (dport->rch)
> - endpoint_parent = parent_port->uport;
> + endpoint_parent = parent_port->uport_dev;
> else
> endpoint_parent = &parent_port->dev;
>
> diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c
> index c23b6164e1c0..4cef2bf45ad2 100644
> --- a/drivers/cxl/port.c
> +++ b/drivers/cxl/port.c
> @@ -91,7 +91,7 @@ static int cxl_switch_port_probe(struct cxl_port *port)
> static int cxl_endpoint_port_probe(struct cxl_port *port)
> {
> struct cxl_endpoint_dvsec_info info = { .port = port };
> - struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport);
> + struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev);
> struct cxl_dev_state *cxlds = cxlmd->cxlds;
> struct cxl_hdm *cxlhdm;
> struct cxl_port *root;
> diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
> index f5c04787bcc8..4f62eb55f8b8 100644
> --- a/tools/testing/cxl/test/cxl.c
> +++ b/tools/testing/cxl/test/cxl.c
> @@ -754,7 +754,7 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
> /* check is endpoint is attach to host-bridge0 */
> port = cxled_to_port(cxled);
> do {
> - if (port->uport == &cxl_host_bridge[0]->dev) {
> + if (port->uport_dev == &cxl_host_bridge[0]->dev) {
> hb0 = true;
> break;
> }
> @@ -889,7 +889,7 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
> mock_init_hdm_decoder(cxld);
>
> if (target_count) {
> - rc = device_for_each_child(port->uport, &ctx,
> + rc = device_for_each_child(port->uport_dev, &ctx,
> map_targets);
> if (rc) {
> put_device(&cxld->dev);
> @@ -919,29 +919,29 @@ static int mock_cxl_port_enumerate_dports(struct cxl_port *port)
> int i, array_size;
>
> if (port->depth == 1) {
> - if (is_multi_bridge(port->uport)) {
> + if (is_multi_bridge(port->uport_dev)) {
> array_size = ARRAY_SIZE(cxl_root_port);
> array = cxl_root_port;
> - } else if (is_single_bridge(port->uport)) {
> + } else if (is_single_bridge(port->uport_dev)) {
> array_size = ARRAY_SIZE(cxl_root_single);
> array = cxl_root_single;
> } else {
> dev_dbg(&port->dev, "%s: unknown bridge type\n",
> - dev_name(port->uport));
> + dev_name(port->uport_dev));
> return -ENXIO;
> }
> } else if (port->depth == 2) {
> struct cxl_port *parent = to_cxl_port(port->dev.parent);
>
> - if (is_multi_bridge(parent->uport)) {
> + if (is_multi_bridge(parent->uport_dev)) {
> array_size = ARRAY_SIZE(cxl_switch_dport);
> array = cxl_switch_dport;
> - } else if (is_single_bridge(parent->uport)) {
> + } else if (is_single_bridge(parent->uport_dev)) {
> array_size = ARRAY_SIZE(cxl_swd_single);
> array = cxl_swd_single;
> } else {
> dev_dbg(&port->dev, "%s: unknown bridge type\n",
> - dev_name(port->uport));
> + dev_name(port->uport_dev));
> return -ENXIO;
> }
> } else {
> @@ -954,9 +954,9 @@ static int mock_cxl_port_enumerate_dports(struct cxl_port *port)
> struct platform_device *pdev = array[i];
> struct cxl_dport *dport;
>
> - if (pdev->dev.parent != port->uport) {
> + if (pdev->dev.parent != port->uport_dev) {
> dev_dbg(&port->dev, "%s: mismatch parent %s\n",
> - dev_name(port->uport),
> + dev_name(port->uport_dev),
> dev_name(pdev->dev.parent));
> continue;
> }
> diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c
> index dbeef5c6f606..da554df50bac 100644
> --- a/tools/testing/cxl/test/mock.c
> +++ b/tools/testing/cxl/test/mock.c
> @@ -139,7 +139,7 @@ struct cxl_hdm *__wrap_devm_cxl_setup_hdm(struct cxl_port *port,
> struct cxl_hdm *cxlhdm;
> struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
>
> - if (ops && ops->is_mock_port(port->uport))
> + if (ops && ops->is_mock_port(port->uport_dev))
> cxlhdm = ops->devm_cxl_setup_hdm(port, info);
> else
> cxlhdm = devm_cxl_setup_hdm(port, info);
> @@ -154,7 +154,7 @@ int __wrap_devm_cxl_enable_hdm(struct cxl_port *port, struct cxl_hdm *cxlhdm)
> int index, rc;
> struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
>
> - if (ops && ops->is_mock_port(port->uport))
> + if (ops && ops->is_mock_port(port->uport_dev))
> rc = 0;
> else
> rc = devm_cxl_enable_hdm(port, cxlhdm);
> @@ -169,7 +169,7 @@ int __wrap_devm_cxl_add_passthrough_decoder(struct cxl_port *port)
> int rc, index;
> struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
>
> - if (ops && ops->is_mock_port(port->uport))
> + if (ops && ops->is_mock_port(port->uport_dev))
> rc = ops->devm_cxl_add_passthrough_decoder(port);
> else
> rc = devm_cxl_add_passthrough_decoder(port);
> @@ -186,7 +186,7 @@ int __wrap_devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
> struct cxl_port *port = cxlhdm->port;
> struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
>
> - if (ops && ops->is_mock_port(port->uport))
> + if (ops && ops->is_mock_port(port->uport_dev))
> rc = ops->devm_cxl_enumerate_decoders(cxlhdm, info);
> else
> rc = devm_cxl_enumerate_decoders(cxlhdm, info);
> @@ -201,7 +201,7 @@ int __wrap_devm_cxl_port_enumerate_dports(struct cxl_port *port)
> int rc, index;
> struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
>
> - if (ops && ops->is_mock_port(port->uport))
> + if (ops && ops->is_mock_port(port->uport_dev))
> rc = ops->devm_cxl_port_enumerate_dports(port);
> else
> rc = devm_cxl_port_enumerate_dports(port);
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> Reading code like dport->dport does not immediately suggest that this
> points to the corresponding device structure of the dport. Rename
> struct member @dport to @dport_dev.
>
> While at it, also rename @new argument of add_dport() to @dport. This
> better describes the variable as a dport (e.g. new->dport becomes to
> dport->dport_dev).
>
> Co-developed-by: Terry Bowman <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/port.c | 20 ++++++++++----------
> drivers/cxl/core/region.c | 4 ++--
> drivers/cxl/cxl.h | 4 ++--
> 3 files changed, 14 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 76888c75dae4..7d3079f5b7b5 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -605,7 +605,7 @@ static int devm_cxl_link_parent_dport(struct device *host,
> if (!parent_dport)
> return 0;
>
> - rc = sysfs_create_link(&port->dev.kobj, &parent_dport->dport->kobj,
> + rc = sysfs_create_link(&port->dev.kobj, &parent_dport->dport_dev->kobj,
> "parent_dport");
> if (rc)
> return rc;
> @@ -658,7 +658,7 @@ static struct cxl_port *cxl_port_alloc(struct device *uport,
> if (iter->host_bridge)
> port->host_bridge = iter->host_bridge;
> else if (parent_dport->rch)
> - port->host_bridge = parent_dport->dport;
> + port->host_bridge = parent_dport->dport_dev;
> else
> port->host_bridge = iter->uport;
> dev_dbg(uport, "host-bridge: %s\n", dev_name(port->host_bridge));
> @@ -847,22 +847,22 @@ static struct cxl_dport *find_dport(struct cxl_port *port, int id)
> return NULL;
> }
>
> -static int add_dport(struct cxl_port *port, struct cxl_dport *new)
> +static int add_dport(struct cxl_port *port, struct cxl_dport *dport)
> {
> struct cxl_dport *dup;
> int rc;
>
> device_lock_assert(&port->dev);
> - dup = find_dport(port, new->port_id);
> + dup = find_dport(port, dport->port_id);
> if (dup) {
> dev_err(&port->dev,
> "unable to add dport%d-%s non-unique port id (%s)\n",
> - new->port_id, dev_name(new->dport),
> - dev_name(dup->dport));
> + dport->port_id, dev_name(dport->dport_dev),
> + dev_name(dup->dport_dev));
> return -EBUSY;
> }
>
> - rc = xa_insert(&port->dports, (unsigned long)new->dport, new,
> + rc = xa_insert(&port->dports, (unsigned long)dport->dport_dev, dport,
> GFP_KERNEL);
> if (rc)
> return rc;
> @@ -895,8 +895,8 @@ static void cxl_dport_remove(void *data)
> struct cxl_dport *dport = data;
> struct cxl_port *port = dport->port;
>
> - xa_erase(&port->dports, (unsigned long) dport->dport);
> - put_device(dport->dport);
> + xa_erase(&port->dports, (unsigned long) dport->dport_dev);
> + put_device(dport->dport_dev);
> }
>
> static void cxl_dport_unlink(void *data)
> @@ -954,7 +954,7 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> dev_dbg(dport_dev, "Component Registers found for dport: %pa\n",
> &component_reg_phys);
>
> - dport->dport = dport_dev;
> + dport->dport_dev = dport_dev;
> dport->port_id = port_id;
> dport->component_reg_phys = component_reg_phys;
> dport->port = port;
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index f822de44bee0..13cda989d944 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -1162,7 +1162,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> dev_dbg(&cxlr->dev, "%s:%s: %s expected %s at %d\n",
> dev_name(port->uport), dev_name(&port->dev),
> dev_name(&cxlsd->cxld.dev),
> - dev_name(ep->dport->dport),
> + dev_name(ep->dport->dport_dev),
> cxl_rr->nr_targets_set);
> return -ENXIO;
> }
> @@ -1173,7 +1173,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
> cxl_rr->nr_targets_set += inc;
> dev_dbg(&cxlr->dev, "%s:%s target[%d] = %s for %s:%s @ %d\n",
> dev_name(port->uport), dev_name(&port->dev),
> - cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport),
> + cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport_dev),
> dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), pos);
>
> return 0;
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 7c8674079f1a..7232c2a0e27c 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -589,7 +589,7 @@ struct cxl_rcrb_info {
>
> /**
> * struct cxl_dport - CXL downstream port
> - * @dport: PCI bridge or firmware device representing the downstream link
> + * @dport_dev: PCI bridge or firmware device representing the downstream link
> * @port_id: unique hardware identifier for dport in decoder target list
> * @component_reg_phys: downstream port component registers
> * @rcrb: Data about the Root Complex Register Block layout
> @@ -597,7 +597,7 @@ struct cxl_rcrb_info {
> * @port: reference to cxl_port that contains this downstream port
> */
> struct cxl_dport {
> - struct device *dport;
> + struct device *dport_dev;
> int port_id;
> resource_size_t component_reg_phys;
> struct cxl_rcrb_info rcrb;
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> Just moving code to reorder functions to later share cxl_get_chbs()
> with add_host_bridge_uport().
>
> This makes changes in the next patch visible. No other changes at all.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/acpi.c | 90 +++++++++++++++++++++++-----------------------
> 1 file changed, 45 insertions(+), 45 deletions(-)
>
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index 70cd9ac73a8b..0c975ee684b0 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -327,51 +327,6 @@ __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
> return NULL;
> }
>
> -/*
> - * A host bridge is a dport to a CFMWS decode and it is a uport to the
> - * dport (PCIe Root Ports) in the host bridge.
> - */
> -static int add_host_bridge_uport(struct device *match, void *arg)
> -{
> - struct cxl_port *root_port = arg;
> - struct device *host = root_port->dev.parent;
> - struct acpi_device *hb = to_cxl_host_bridge(host, match);
> - struct acpi_pci_root *pci_root;
> - struct cxl_dport *dport;
> - struct cxl_port *port;
> - struct device *bridge;
> - int rc;
> -
> - if (!hb)
> - return 0;
> -
> - pci_root = acpi_pci_find_root(hb->handle);
> - bridge = pci_root->bus->bridge;
> - dport = cxl_find_dport_by_dev(root_port, bridge);
> - if (!dport) {
> - dev_dbg(host, "host bridge expected and not found\n");
> - return 0;
> - }
> -
> - if (dport->rch) {
> - dev_info(bridge, "host supports CXL (restricted)\n");
> - return 0;
> - }
> -
> - rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
> - if (rc)
> - return rc;
> -
> - port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
> - dport);
> - if (IS_ERR(port))
> - return PTR_ERR(port);
> -
> - dev_info(bridge, "host supports CXL\n");
> -
> - return 0;
> -}
> -
> /* Note, @dev is used by mock_acpi_table_parse_cedt() */
> struct cxl_chbs_context {
> struct device *dev;
> @@ -467,6 +422,51 @@ static int add_host_bridge_dport(struct device *match, void *arg)
> return 0;
> }
>
> +/*
> + * A host bridge is a dport to a CFMWS decode and it is a uport to the
> + * dport (PCIe Root Ports) in the host bridge.
> + */
> +static int add_host_bridge_uport(struct device *match, void *arg)
> +{
> + struct cxl_port *root_port = arg;
> + struct device *host = root_port->dev.parent;
> + struct acpi_device *hb = to_cxl_host_bridge(host, match);
> + struct acpi_pci_root *pci_root;
> + struct cxl_dport *dport;
> + struct cxl_port *port;
> + struct device *bridge;
> + int rc;
> +
> + if (!hb)
> + return 0;
> +
> + pci_root = acpi_pci_find_root(hb->handle);
> + bridge = pci_root->bus->bridge;
> + dport = cxl_find_dport_by_dev(root_port, bridge);
> + if (!dport) {
> + dev_dbg(host, "host bridge expected and not found\n");
> + return 0;
> + }
> +
> + if (dport->rch) {
> + dev_info(bridge, "host supports CXL (restricted)\n");
> + return 0;
> + }
> +
> + rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
> + if (rc)
> + return rc;
> +
> + port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
> + dport);
> + if (IS_ERR(port))
> + return PTR_ERR(port);
> +
> + dev_info(bridge, "host supports CXL\n");
> +
> + return 0;
> +}
> +
> static int add_root_nvdimm_bridge(struct device *match, void *data)
> {
> struct cxl_decoder *cxld;
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> When probing the Component Registers in function cxl_probe_regs()
> there are also checks for the existence of the HDM and RAS
> capabilities. The checks may fail for components that do not implement
> the HDM capability causing the Component Registers setup to fail too.
>
> Remove the checks for a generalized use of cxl_probe_regs() and check
> them directly before mapping the RAS or HDM capabilities. This allows
> it to setup other Component Registers esp. of an RCH Downstream Port,
> which will be implemented in a follow-on patch.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/regs.c | 8 --------
> drivers/cxl/pci.c | 2 ++
> drivers/cxl/port.c | 5 ++++-
> 3 files changed, 6 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index e035ad8827a4..e68848075bb6 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -369,14 +369,6 @@ static int cxl_probe_regs(struct cxl_register_map *map)
> case CXL_REGLOC_RBI_COMPONENT:
> comp_map = &map->component_map;
> cxl_probe_component_regs(dev, base, comp_map);
> - if (!comp_map->hdm_decoder.valid) {
> - dev_err(dev, "HDM decoder registers not found\n");
> - return -ENXIO;
> - }
> -
> - if (!comp_map->ras.valid)
> - dev_dbg(dev, "RAS registers not found\n");
> -
> dev_dbg(dev, "Set up component registers\n");
> break;
> case CXL_REGLOC_RBI_MEMDEV:
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index ac17bc0430dc..945ca0304d68 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -630,6 +630,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
> if (rc)
> dev_warn(&pdev->dev, "No component registers (%d)\n", rc);
> + else if (!map.component_map.ras.valid)
> + dev_dbg(&pdev->dev, "RAS registers not found\n");
>
> cxlds->component_reg_phys = map.resource;
>
> diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c
> index 4cef2bf45ad2..01e84ea54f56 100644
> --- a/drivers/cxl/port.c
> +++ b/drivers/cxl/port.c
> @@ -102,8 +102,11 @@ static int cxl_endpoint_port_probe(struct cxl_port *port)
> return rc;
>
> cxlhdm = devm_cxl_setup_hdm(port, &info);
> - if (IS_ERR(cxlhdm))
> + if (IS_ERR(cxlhdm)) {
> + if (PTR_ERR(cxlhdm) == -ENODEV)
> + dev_err(&port->dev, "HDM decoder registers not found\n");
> return PTR_ERR(cxlhdm);
> + }
>
> /* Cache the data early to ensure is_visible() works */
> read_cdat_data(port);
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> During a Host Bridge's downstream port enumeration the CHBS entries in
> the CEDT table are parsed, its Component Register base address
> extracted and then stored in struct cxl_dport. The CHBS may contain
> either the RCRB (RCH mode) or the Host Bridge's Component Registers
> (CHBCR, VH mode). The RCRB further contains the CXL downstream port
> register base address, while in VH mode the CXL Downstream Switch
> Ports are visible in the PCI hierarchy and the DP's component regs are
> disovered using the CXL DVSEC register locator capability. The
s/disovered/discovered/
> Component Registers derived from the CHBS for both modes are different
> and thus also must be treated differently. That is, in RCH mode, the
> component regs base should be bound to the dport, but in VH mode to
> the CXL host bridge's port object.
>
> The current implementation stores the CHBCR in addition in struct
> cxl_dport and copies it later from there to struct cxl_port. As a
> result, the dport contains the wrong Component Registers base address
> and, e.g. the RAS capability of a CXL Root Port cannot be detected.
>
> To fix the CHBCR binding, attach it directly to the Host Bridge's
> @cxl_port structure. Do this during port creation of the Host Bridge
> in add_host_bridge_uport(). Factor out CHBS parsing code in
> add_host_bridge_dport() and use it in both functions.
>
> Co-developed-by: Dan Williams <[email protected]>
> Signed-off-by: Dan Williams <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/acpi.c | 91 ++++++++++++++++++++++++++++++++--------------
> 1 file changed, 63 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index 0c975ee684b0..89ee01323d43 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -335,13 +335,13 @@ struct cxl_chbs_context {
> u32 cxl_version;
> };
>
> -static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
> - const unsigned long end)
> +static int cxl_get_chbs_iter(union acpi_subtable_headers *header, void *arg,
> + const unsigned long end)
> {
> struct cxl_chbs_context *ctx = arg;
> struct acpi_cedt_chbs *chbs;
>
> - if (ctx->base)
> + if (ctx->base != CXL_RESOURCE_NONE)
> return 0;
>
> chbs = (struct acpi_cedt_chbs *) header;
> @@ -350,8 +350,6 @@ static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
> return 0;
>
> ctx->cxl_version = chbs->cxl_version;
> - ctx->base = CXL_RESOURCE_NONE;
> -
> if (!chbs->base)
> return 0;
>
> @@ -364,11 +362,35 @@ static int cxl_get_chbs(union acpi_subtable_headers *header, void *arg,
> return 0;
> }
>
> +static int cxl_get_chbs(struct device *dev, struct acpi_device *hb,
> + struct cxl_chbs_context *ctx)
> +{
> + unsigned long long uid;
> + int rc;
> +
> + rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid);
> + if (rc != AE_OK) {
> + dev_err(dev, "unable to retrieve _UID\n");
> + return -ENOENT;
> + }
> +
> + dev_dbg(dev, "UID found: %lld\n", uid);
> + *ctx = (struct cxl_chbs_context) {
> + .dev = dev,
> + .uid = uid,
> + .base = CXL_RESOURCE_NONE,
> + .cxl_version = UINT_MAX,
> + };
> +
> + acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs_iter, ctx);
> +
> + return 0;
> +}
> +
> static int add_host_bridge_dport(struct device *match, void *arg)
> {
> acpi_status rc;
> struct device *bridge;
> - unsigned long long uid;
> struct cxl_dport *dport;
> struct cxl_chbs_context ctx;
> struct acpi_pci_root *pci_root;
> @@ -379,41 +401,38 @@ static int add_host_bridge_dport(struct device *match, void *arg)
> if (!hb)
> return 0;
>
> - rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid);
> - if (rc != AE_OK) {
> - dev_err(match, "unable to retrieve _UID\n");
> - return -ENODEV;
> - }
> -
> - dev_dbg(match, "UID found: %lld\n", uid);
> -
> - ctx = (struct cxl_chbs_context) {
> - .dev = match,
> - .uid = uid,
> - };
> - acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs, &ctx);
> + rc = cxl_get_chbs(match, hb, &ctx);
> + if (rc)
> + return rc;
>
> - if (!ctx.base) {
> + if (ctx.cxl_version == UINT_MAX) {
> dev_warn(match, "No CHBS found for Host Bridge (UID %lld)\n",
> - uid);
> + ctx.uid);
> return 0;
> }
>
> if (ctx.base == CXL_RESOURCE_NONE) {
> dev_warn(match, "CHBS invalid for Host Bridge (UID %lld)\n",
> - uid);
> + ctx.uid);
> return 0;
> }
>
> pci_root = acpi_pci_find_root(hb->handle);
> bridge = pci_root->bus->bridge;
>
> + /*
> + * In RCH mode, bind the component regs base to the dport. In
> + * VH mode it will be bound to the CXL host bridge's port
> + * object later in add_host_bridge_uport().
> + */
> if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) {
> - dev_dbg(match, "RCRB found for UID %lld: %pa\n", uid, &ctx.base);
> - dport = devm_cxl_add_rch_dport(root_port, bridge, uid, ctx.base);
> + dev_dbg(match, "RCRB found for UID %lld: %pa\n", ctx.uid,
> + &ctx.base);
> + dport = devm_cxl_add_rch_dport(root_port, bridge, ctx.uid,
> + ctx.base);
> } else {
> - dev_dbg(match, "CHBCR found for UID %lld: %pa\n", uid, &ctx.base);
> - dport = devm_cxl_add_dport(root_port, bridge, uid, ctx.base);
> + dport = devm_cxl_add_dport(root_port, bridge, ctx.uid,
> + CXL_RESOURCE_NONE);
> }
>
> if (IS_ERR(dport))
> @@ -435,6 +454,8 @@ static int add_host_bridge_uport(struct device *match, void *arg)
> struct cxl_dport *dport;
> struct cxl_port *port;
> struct device *bridge;
> + struct cxl_chbs_context ctx;
> + resource_size_t component_reg_phys;
> int rc;
>
> if (!hb)
> @@ -453,12 +474,26 @@ static int add_host_bridge_uport(struct device *match, void *arg)
> return 0;
> }
>
> + rc = cxl_get_chbs(match, hb, &ctx);
> + if (rc)
> + return rc;
> +
> + if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) {
> + dev_warn(bridge,
> + "CXL CHBS version mismatch, skip port registration\n");
> + return 0;
> + }
> +
> + component_reg_phys = ctx.base;
> + if (component_reg_phys != CXL_RESOURCE_NONE)
> + dev_dbg(match, "CHBCR found for UID %lld: %pa\n",
> + ctx.uid, &component_reg_phys);
> +
> rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
> if (rc)
> return rc;
>
> - port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
> - dport);
> + port = devm_cxl_add_port(host, bridge, component_reg_phys, dport);
> if (IS_ERR(port))
> return PTR_ERR(port);
>
On 6/21/23 20:51, Terry Bowman wrote:
> The endpoint implements component register setup code. Refactor it for
> reuse with RCRB, downstream port, and upstream port setup.
>
> Move PCI specifics from cxl_setup_regs() into cxl_pci_setup_regs().
>
> Move cxl_setup_regs() into cxl/core/regs.c and export it. This also
> includes supporting static functions cxl_map_registerblock(),
> cxl_unmap_register_block() and cxl_probe_regs().
>
> Co-developed-by: Robert Richter <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/regs.c | 77 +++++++++++++++++++++++++++++++++++++++
> drivers/cxl/cxl.h | 1 +
> drivers/cxl/pci.c | 79 +++--------------------------------------
> 3 files changed, 83 insertions(+), 74 deletions(-)
>
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 713e4a9ca35a..e035ad8827a4 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -338,6 +338,83 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> }
> EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
>
> +static int cxl_map_regblock(struct cxl_register_map *map)
> +{
> + struct device *dev = map->dev;
> +
> + map->base = ioremap(map->resource, map->max_size);
> + if (!map->base) {
> + dev_err(dev, "failed to map registers\n");
> + return -ENOMEM;
> + }
> +
> + dev_dbg(dev, "Mapped CXL Memory Device resource %pa\n", &map->resource);
> + return 0;
> +}
> +
> +static void cxl_unmap_regblock(struct cxl_register_map *map)
> +{
> + iounmap(map->base);
> + map->base = NULL;
> +}
> +
> +static int cxl_probe_regs(struct cxl_register_map *map)
> +{
> + struct cxl_component_reg_map *comp_map;
> + struct cxl_device_reg_map *dev_map;
> + struct device *dev = map->dev;
> + void __iomem *base = map->base;
> +
> + switch (map->reg_type) {
> + case CXL_REGLOC_RBI_COMPONENT:
> + comp_map = &map->component_map;
> + cxl_probe_component_regs(dev, base, comp_map);
> + if (!comp_map->hdm_decoder.valid) {
> + dev_err(dev, "HDM decoder registers not found\n");
> + return -ENXIO;
> + }
> +
> + if (!comp_map->ras.valid)
> + dev_dbg(dev, "RAS registers not found\n");
> +
> + dev_dbg(dev, "Set up component registers\n");
> + break;
> + case CXL_REGLOC_RBI_MEMDEV:
> + dev_map = &map->device_map;
> + cxl_probe_device_regs(dev, base, dev_map);
> + if (!dev_map->status.valid || !dev_map->mbox.valid ||
> + !dev_map->memdev.valid) {
> + dev_err(dev, "registers not found: %s%s%s\n",
> + !dev_map->status.valid ? "status " : "",
> + !dev_map->mbox.valid ? "mbox " : "",
> + !dev_map->memdev.valid ? "memdev " : "");
> + return -ENXIO;
> + }
> +
> + dev_dbg(dev, "Probing device registers...\n");
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +int cxl_setup_regs(struct cxl_register_map *map)
> +{
> + int rc;
> +
> + rc = cxl_map_regblock(map);
> + if (rc)
> + return rc;
> +
> + rc = cxl_probe_regs(map);
> + cxl_unmap_regblock(map);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_setup_regs, CXL);
> +
> resource_size_t __rcrb_to_component(struct device *dev, struct cxl_rcrb_info *ri,
> enum cxl_rcrb which)
> {
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index bd68d5fabf21..ae265357170e 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -264,6 +264,7 @@ int cxl_map_device_regs(struct cxl_register_map *map,
> enum cxl_regloc_type;
> int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
> struct cxl_register_map *map);
> +int cxl_setup_regs(struct cxl_register_map *map);
> struct cxl_dport;
> resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
> struct cxl_dport *dport);
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 0a89b96e6a8d..ac17bc0430dc 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -274,70 +274,8 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
> return 0;
> }
>
> -static int cxl_map_regblock(struct cxl_register_map *map)
> -{
> - struct device *dev = map->dev;
> -
> - map->base = ioremap(map->resource, map->max_size);
> - if (!map->base) {
> - dev_err(dev, "failed to map registers\n");
> - return -ENOMEM;
> - }
> -
> - dev_dbg(dev, "Mapped CXL Memory Device resource %pa\n", &map->resource);
> - return 0;
> -}
> -
> -static void cxl_unmap_regblock(struct cxl_register_map *map)
> -{
> - iounmap(map->base);
> - map->base = NULL;
> -}
> -
> -static int cxl_probe_regs(struct cxl_register_map *map)
> -{
> - struct cxl_component_reg_map *comp_map;
> - struct cxl_device_reg_map *dev_map;
> - struct device *dev = map->dev;
> - void __iomem *base = map->base;
> -
> - switch (map->reg_type) {
> - case CXL_REGLOC_RBI_COMPONENT:
> - comp_map = &map->component_map;
> - cxl_probe_component_regs(dev, base, comp_map);
> - if (!comp_map->hdm_decoder.valid) {
> - dev_err(dev, "HDM decoder registers not found\n");
> - return -ENXIO;
> - }
> -
> - if (!comp_map->ras.valid)
> - dev_dbg(dev, "RAS registers not found\n");
> -
> - dev_dbg(dev, "Set up component registers\n");
> - break;
> - case CXL_REGLOC_RBI_MEMDEV:
> - dev_map = &map->device_map;
> - cxl_probe_device_regs(dev, base, dev_map);
> - if (!dev_map->status.valid || !dev_map->mbox.valid ||
> - !dev_map->memdev.valid) {
> - dev_err(dev, "registers not found: %s%s%s\n",
> - !dev_map->status.valid ? "status " : "",
> - !dev_map->mbox.valid ? "mbox " : "",
> - !dev_map->memdev.valid ? "memdev " : "");
> - return -ENXIO;
> - }
> -
> - dev_dbg(dev, "Probing device registers...\n");
> - break;
> - default:
> - break;
> - }
> -
> - return 0;
> -}
> -
> -static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
> - struct cxl_register_map *map)
> +static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
> + struct cxl_register_map *map)
> {
> int rc;
>
> @@ -345,14 +283,7 @@ static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
> if (rc)
> return rc;
>
> - rc = cxl_map_regblock(map);
> - if (rc)
> - return rc;
> -
> - rc = cxl_probe_regs(map);
> - cxl_unmap_regblock(map);
> -
> - return rc;
> + return cxl_setup_regs(map);
> }
>
> /*
> @@ -683,7 +614,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> dev_warn(&pdev->dev,
> "Device DVSEC not present, skip CXL.mem init\n");
>
> - rc = cxl_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map);
> + rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map);
> if (rc)
> return rc;
>
> @@ -696,7 +627,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> * still be useful for management functions so don't return an error.
> */
> cxlds->component_reg_phys = CXL_RESOURCE_NONE;
> - rc = cxl_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
> + rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
> if (rc)
> dev_warn(&pdev->dev, "No component registers (%d)\n", rc);
>
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> The Component Register base address @component_reg_phys is no longer
> used after the rework of the Component Register setup which now uses
> struct member @comp_map instead. Remove the base address.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/port.c | 1 -
> drivers/cxl/cxl.h | 2 --
> 2 files changed, 3 deletions(-)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index cdfe0ea7a2e9..e0d2e7596440 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -960,7 +960,6 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
>
> dport->dport_dev = dport_dev;
> dport->port_id = port_id;
> - dport->component_reg_phys = component_reg_phys;
> dport->port = port;
>
> cond_cxl_root_lock(port);
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index ae265357170e..7fbc52b81554 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -594,7 +594,6 @@ struct cxl_rcrb_info {
> * struct cxl_dport - CXL downstream port
> * @dport_dev: PCI bridge or firmware device representing the downstream link
> * @port_id: unique hardware identifier for dport in decoder target list
> - * @component_reg_phys: downstream port component registers
> * @rcrb: Data about the Root Complex Register Block layout
> * @rch: Indicate whether this dport was enumerated in RCH or VH mode
> * @port: reference to cxl_port that contains this downstream port
> @@ -602,7 +601,6 @@ struct cxl_rcrb_info {
> struct cxl_dport {
> struct device *dport_dev;
> int port_id;
> - resource_size_t component_reg_phys;
> struct cxl_rcrb_info rcrb;
> bool rch;
> struct cxl_port *port;
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> Same as for ports, also store the downstream port's Component Register
> mappings, use struct cxl_dport for that.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/port.c | 11 +++++++++++
> drivers/cxl/cxl.h | 2 ++
> 2 files changed, 13 insertions(+)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 2e239fd33df9..45fe7d89f7f3 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -710,6 +710,13 @@ static inline int cxl_port_setup_regs(struct cxl_port *port,
> component_reg_phys);
> }
>
> +static inline int cxl_dport_setup_regs(struct cxl_dport *dport,
> + resource_size_t component_reg_phys)
> +{
> + return cxl_setup_comp_regs(dport->dport_dev, &dport->comp_map,
> + component_reg_phys);
> +}
> +
> static struct cxl_port *__devm_cxl_add_port(struct device *host,
> struct device *uport_dev,
> resource_size_t component_reg_phys,
> @@ -988,6 +995,10 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> dport->port_id = port_id;
> dport->port = port;
>
> + rc = cxl_dport_setup_regs(dport, component_reg_phys);
> + if (rc)
> + return ERR_PTR(rc);
> +
> cond_cxl_root_lock(port);
> rc = add_dport(port, dport);
> cond_cxl_root_unlock(port);
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 37fa5b565362..b1adca9b27ba 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -595,6 +595,7 @@ struct cxl_rcrb_info {
> /**
> * struct cxl_dport - CXL downstream port
> * @dport_dev: PCI bridge or firmware device representing the downstream link
> + * @comp_map: component register capability mappings
> * @port_id: unique hardware identifier for dport in decoder target list
> * @rcrb: Data about the Root Complex Register Block layout
> * @rch: Indicate whether this dport was enumerated in RCH or VH mode
> @@ -602,6 +603,7 @@ struct cxl_rcrb_info {
> */
> struct cxl_dport {
> struct device *dport_dev;
> + struct cxl_register_map comp_map;
> int port_id;
> struct cxl_rcrb_info rcrb;
> bool rch;
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> Now, that the Component Register mappings are stored, use them to
> enable and map the HDM decoder capabilities. The Component Registers
> do not need to be probed again for this, remove probing code.
>
> The HDM capability applies to Endpoints, USPs and VH Host Bridges. The
> Endpoint's component register mappings are located in the cxlds and
> else in the port's structure. Provide a helper function
> cxl_port_get_comp_map() to locate the mappings depending on the
> component's type.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/hdm.c | 59 +++++++++++++++++++++---------------------
> 1 file changed, 30 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index 5abfa9276dac..8dcd9f0b22d8 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -81,26 +81,6 @@ static void parse_hdm_decoder_caps(struct cxl_hdm *cxlhdm)
> cxlhdm->interleave_mask |= GENMASK(14, 12);
> }
>
> -static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
> - struct cxl_component_regs *regs)
> -{
> - struct cxl_register_map map = {
> - .dev = &port->dev,
> - .resource = port->component_reg_phys,
> - .base = crb,
> - .max_size = CXL_COMPONENT_REG_BLOCK_SIZE,
> - };
> -
> - cxl_probe_component_regs(&port->dev, crb, &map.component_map);
> - if (!map.component_map.hdm_decoder.valid) {
> - dev_dbg(&port->dev, "HDM decoder registers not implemented\n");
> - /* unique error code to indicate no HDM decoder capability */
> - return -ENODEV;
> - }
> -
> - return cxl_map_component_regs(&map, regs, BIT(CXL_CM_CAP_CAP_ID_HDM));
> -}
> -
> static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
> {
> struct cxl_hdm *cxlhdm;
> @@ -145,6 +125,22 @@ static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
> return true;
> }
>
> +static struct cxl_register_map *cxl_port_get_comp_map(struct cxl_port *port)
> +{
> + /*
> + * HDM capability applies to Endpoints, USPs and VH Host
> + * Bridges. The Endpoint's component register mappings are
> + * located in the cxlds.
> + */
> + if (is_cxl_endpoint(port)) {
> + struct cxl_memdev *memdev = to_cxl_memdev(port->uport_dev);
> +
> + return &memdev->cxlds->comp_map;
> + }
> +
> + return &port->comp_map;
> +}
> +
> /**
> * devm_cxl_setup_hdm - map HDM decoder component registers
> * @port: cxl_port to map
> @@ -155,7 +151,7 @@ struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
> {
> struct device *dev = &port->dev;
> struct cxl_hdm *cxlhdm;
> - void __iomem *crb;
> + struct cxl_register_map *comp_map;
> int rc;
>
> cxlhdm = devm_kzalloc(dev, sizeof(*cxlhdm), GFP_KERNEL);
> @@ -164,19 +160,24 @@ struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
> cxlhdm->port = port;
> dev_set_drvdata(dev, cxlhdm);
>
> - crb = ioremap(port->component_reg_phys, CXL_COMPONENT_REG_BLOCK_SIZE);
> - if (!crb && info && info->mem_enabled) {
> - cxlhdm->decoder_count = info->ranges;
> - return cxlhdm;
> - } else if (!crb) {
> + comp_map = cxl_port_get_comp_map(port);
> +
> + if (!comp_map->component_map.hdm_decoder.valid) {
> + dev_dbg(&port->dev, "HDM decoder registers not found\n");
> + if (info && info->mem_enabled) {
> + cxlhdm->decoder_count = info->ranges;
> + return cxlhdm;
> + }
> dev_err(dev, "No component registers mapped\n");
> return ERR_PTR(-ENXIO);
> }
>
> - rc = map_hdm_decoder_regs(port, crb, &cxlhdm->regs);
> - iounmap(crb);
> - if (rc)
> + rc = cxl_map_component_regs(comp_map, &cxlhdm->regs,
> + BIT(CXL_CM_CAP_CAP_ID_HDM));
> + if (rc) {
> + dev_dbg(dev, "Failed to map HDM capability.\n");
> return ERR_PTR(rc);
> + }
>
> parse_hdm_decoder_caps(cxlhdm);
> if (cxlhdm->decoder_count == 0) {
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> CXL RAS capabilities must be enabled and accessible as soon as the CXL
> endpoint is detected in the PCI hierarchy and bound to the cxl_pci
> driver. This needs to be independent of other modules such as cxl_port
> or cxl_mem.
>
> CXL RAS capabilities reside in the Component Registers. For an RCH
> this is determined by probing RCRB which is implemented very late once
> the CXL Memory Device is created.
>
> Change this by moving the RCRB probe to the cxl_pci driver. Do this by
> using a new introduced function cxl_pci_find_port() similar to
> cxl_mem_find_port() to determine the involved dport by the endpoint's
> PCI handle. Plug this into the existing cxl_pci_setup_regs() function
> to setup Component Registers. Probe the RCRB in case the Component
> Registers cannot be located through the CXL Register Locator
> capability.
>
> This unifies code and early sets up the Component Registers at the
> same time for both, VH and RCH mode. Only the cxl_pci driver is
> involved for this. This allows an early mapping of the CXL RAS
> capability registers.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/port.c | 7 +++++
> drivers/cxl/cxl.h | 2 ++
> drivers/cxl/mem.c | 9 -------
> drivers/cxl/pci.c | 57 ++++++++++++++++++++++++++++++++++-------
> 4 files changed, 57 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index e0d2e7596440..679226023f0c 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -1480,6 +1480,13 @@ int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd)
> }
> EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_ports, CXL);
>
> +struct cxl_port *cxl_pci_find_port(struct pci_dev *pdev,
> + struct cxl_dport **dport)
> +{
> + return find_cxl_port(pdev->dev.parent, dport);
> +}
> +EXPORT_SYMBOL_NS_GPL(cxl_pci_find_port, CXL);
> +
> struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd,
> struct cxl_dport **dport)
> {
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 7fbc52b81554..fe95f08acb69 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -664,6 +664,8 @@ struct cxl_port *find_cxl_root(struct cxl_port *port);
> int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd);
> void cxl_bus_rescan(void);
> void cxl_bus_drain(void);
> +struct cxl_port *cxl_pci_find_port(struct pci_dev *pdev,
> + struct cxl_dport **dport);
> struct cxl_port *cxl_mem_find_port(struct cxl_memdev *cxlmd,
> struct cxl_dport **dport);
> bool schedule_cxl_memdev_detach(struct cxl_memdev *cxlmd);
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 7638a7f8f333..205e2e280aed 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -65,15 +65,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> ep->next = down;
> }
>
> - /*
> - * The component registers for an RCD might come from the
> - * host-bridge RCRB if they are not already mapped via the
> - * typical register locator mechanism.
> - */
> - if (parent_dport->rch && cxlds->component_reg_phys == CXL_RESOURCE_NONE)
> - cxlds->component_reg_phys =
> - cxl_rcd_component_reg_phys(&cxlmd->dev, parent_dport);
> -
> endpoint = devm_cxl_add_port(host, &cxlmd->dev,
> cxlds->component_reg_phys,
> parent_dport);
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 945ca0304d68..99a75c54ee39 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -274,27 +274,66 @@ static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
> return 0;
> }
>
> +/*
> + * Assume that any RCIEP that emits the CXL memory expander class code
> + * is an RCD
> + */
> +static bool is_cxl_restricted(struct pci_dev *pdev)
> +{
> + return pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END;
> +}
> +
> +static int cxl_rcrb_get_comp_regs(struct pci_dev *pdev,
> + struct cxl_register_map *map)
> +{
> + struct cxl_port *port;
> + struct cxl_dport *dport;
> + resource_size_t component_reg_phys;
> +
> + *map = (struct cxl_register_map) {
> + .dev = &pdev->dev,
> + .resource = CXL_RESOURCE_NONE,
> + };
> +
> + port = cxl_pci_find_port(pdev, &dport);
> + if (!port)
> + return -EPROBE_DEFER;
> +
> + component_reg_phys = cxl_rcd_component_reg_phys(&pdev->dev, dport);
> +
> + put_device(&port->dev);
> +
> + if (component_reg_phys == CXL_RESOURCE_NONE)
> + return -ENXIO;
> +
> + map->resource = component_reg_phys;
> + map->reg_type = CXL_REGLOC_RBI_COMPONENT;
> + map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE;
> +
> + return 0;
> +}
> +
> static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
> struct cxl_register_map *map)
> {
> int rc;
>
> rc = cxl_find_regblock(pdev, type, map);
> +
> + /*
> + * If the Register Locator DVSEC does not exist, check if it
> + * is an RCH and try to extract the Component Registers from
> + * an RCRB.
> + */
> + if (rc && type == CXL_REGLOC_RBI_COMPONENT && is_cxl_restricted(pdev))
> + rc = cxl_rcrb_get_comp_regs(pdev, map);
> +
> if (rc)
> return rc;
>
> return cxl_setup_regs(map);
> }
>
> -/*
> - * Assume that any RCIEP that emits the CXL memory expander class code
> - * is an RCD
> - */
> -static bool is_cxl_restricted(struct pci_dev *pdev)
> -{
> - return pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END;
> -}
> -
> /*
> * CXL v3.0 6.2.3 Table 6-4
> * The table indicates that if PCIe Flit Mode is set, then CXL is in 256B flits
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> Same as for ports and dports, also store the endpoint's Component
> Register mappings, use struct cxl_dev_state for that.
>
> The Component Register base address @component_reg_phys is no longer
> used after the rework of the Component Register setup which now uses
> struct member @comp_map instead. Remove the base address.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/cxlmem.h | 5 ++---
> drivers/cxl/mem.c | 4 ++--
> drivers/cxl/pci.c | 10 ++++------
> tools/testing/cxl/test/mem.c | 1 -
> 4 files changed, 8 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index 76743016b64c..8aee1a42d9af 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -263,6 +263,7 @@ struct cxl_poison_state {
> *
> * @dev: The device associated with this CXL state
> * @cxlmd: The device representing the CXL.mem capabilities of @dev
> + * @comp_map: component register capability mappings
> * @regs: Parsed register blocks
> * @cxl_dvsec: Offset to the PCIe device DVSEC
> * @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH)
> @@ -286,7 +287,6 @@ struct cxl_poison_state {
> * @active_persistent_bytes: sum of hard + soft persistent
> * @next_volatile_bytes: volatile capacity change pending device reset
> * @next_persistent_bytes: persistent capacity change pending device reset
> - * @component_reg_phys: register base of component registers
> * @info: Cached DVSEC information about the device.
> * @serial: PCIe Device Serial Number
> * @event: event log driver state
> @@ -299,7 +299,7 @@ struct cxl_poison_state {
> struct cxl_dev_state {
> struct device *dev;
> struct cxl_memdev *cxlmd;
> -
> + struct cxl_register_map comp_map;
> struct cxl_regs regs;
> int cxl_dvsec;
>
> @@ -325,7 +325,6 @@ struct cxl_dev_state {
> u64 next_volatile_bytes;
> u64 next_persistent_bytes;
>
> - resource_size_t component_reg_phys;
> u64 serial;
>
> struct cxl_event_state event;
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 205e2e280aed..92c6151b7206 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -49,7 +49,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> struct cxl_dport *parent_dport)
> {
> struct cxl_port *parent_port = parent_dport->port;
> - struct cxl_dev_state *cxlds = cxlmd->cxlds;
> struct cxl_port *endpoint, *iter, *down;
> int rc;
>
> @@ -65,8 +64,9 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> ep->next = down;
> }
>
> + /* The Endpoint's component regs are located in cxlds. */
> endpoint = devm_cxl_add_port(host, &cxlmd->dev,
> - cxlds->component_reg_phys,
> + CXL_RESOURCE_NONE,
> parent_dport);
> if (IS_ERR(endpoint))
> return PTR_ERR(endpoint);
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 99a75c54ee39..ad4cfcd95e17 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -665,16 +665,14 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> * If the component registers can't be found, the cxl_pci driver may
> * still be useful for management functions so don't return an error.
> */
> - cxlds->component_reg_phys = CXL_RESOURCE_NONE;
> - rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
> + rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT,
> + &cxlds->comp_map);
> if (rc)
> dev_warn(&pdev->dev, "No component registers (%d)\n", rc);
> - else if (!map.component_map.ras.valid)
> + else if (!cxlds->comp_map.component_map.ras.valid)
> dev_dbg(&pdev->dev, "RAS registers not found\n");
>
> - cxlds->component_reg_phys = map.resource;
> -
> - rc = cxl_map_component_regs(&map, &cxlds->regs.component,
> + rc = cxl_map_component_regs(&cxlds->comp_map, &cxlds->regs.component,
> BIT(CXL_CM_CAP_CAP_ID_RAS));
> if (rc)
> dev_dbg(&pdev->dev, "Failed to map RAS capability.\n");
> diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
> index 34b48027b3de..fd562a5fa06f 100644
> --- a/tools/testing/cxl/test/mem.c
> +++ b/tools/testing/cxl/test/mem.c
> @@ -1241,7 +1241,6 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
> cxlds->event.buf = (struct cxl_get_event_payload *) mdata->event_buf;
> if (is_rcd(pdev)) {
> cxlds->rcd = true;
> - cxlds->component_reg_phys = CXL_RESOURCE_NONE;
> }
>
> rc = cxl_enumerate_cmds(cxlds);
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> The Component Register base address @component_reg_phys is no longer
> used after the rework of the Component Register setup which now uses
> struct member @comp_map instead. Remove the base address.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/port.c | 4 +---
> drivers/cxl/cxl.h | 2 --
> 2 files changed, 1 insertion(+), 5 deletions(-)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 45fe7d89f7f3..cfae8307de90 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -616,7 +616,6 @@ static int devm_cxl_link_parent_dport(struct device *host,
> static struct lock_class_key cxl_port_key;
>
> static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
> - resource_size_t component_reg_phys,
> struct cxl_dport *parent_dport)
> {
> struct cxl_port *port;
> @@ -667,7 +666,6 @@ static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
> } else
> dev->parent = uport_dev;
>
> - port->component_reg_phys = component_reg_phys;
> ida_init(&port->decoder_ida);
> port->hdm_end = -1;
> port->commit_end = -1;
> @@ -726,7 +724,7 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
> struct device *dev;
> int rc;
>
> - port = cxl_port_alloc(uport_dev, component_reg_phys, parent_dport);
> + port = cxl_port_alloc(uport_dev, parent_dport);
> if (IS_ERR(port))
> return port;
>
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index b1adca9b27ba..9f46a4e1fbec 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -551,7 +551,6 @@ struct cxl_dax_region {
> * @nr_dports: number of entries in @dports
> * @hdm_end: track last allocated HDM decoder instance for allocation ordering
> * @commit_end: cursor to track highest committed decoder for commit ordering
> - * @component_reg_phys: component register capability base address (optional)
> * @dead: last ep has been removed, force port re-creation
> * @depth: How deep this port is relative to the root. depth 0 is the root.
> * @cdat: Cached CDAT data
> @@ -571,7 +570,6 @@ struct cxl_port {
> int nr_dports;
> int hdm_end;
> int commit_end;
> - resource_size_t component_reg_phys;
> bool dead;
> unsigned int depth;
> struct cxl_cdat {
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> In order to move the RCH dport component register setup to cxl_pci the
> base address must be stored in CXL device state (cxlds) for both
> modes, RCH and VH. Store it in cxlds->component_reg_phys and use it
> for endpoint creation.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/mem.c | 9 ++++-----
> 1 file changed, 4 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c
> index 4cc461c22b8b..7638a7f8f333 100644
> --- a/drivers/cxl/mem.c
> +++ b/drivers/cxl/mem.c
> @@ -51,7 +51,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> struct cxl_port *parent_port = parent_dport->port;
> struct cxl_dev_state *cxlds = cxlmd->cxlds;
> struct cxl_port *endpoint, *iter, *down;
> - resource_size_t component_reg_phys;
> int rc;
>
> /*
> @@ -72,11 +71,11 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
> * typical register locator mechanism.
> */
> if (parent_dport->rch && cxlds->component_reg_phys == CXL_RESOURCE_NONE)
> - component_reg_phys =
> + cxlds->component_reg_phys =
> cxl_rcd_component_reg_phys(&cxlmd->dev, parent_dport);
> - else
> - component_reg_phys = cxlds->component_reg_phys;
> - endpoint = devm_cxl_add_port(host, &cxlmd->dev, component_reg_phys,
> +
> + endpoint = devm_cxl_add_port(host, &cxlmd->dev,
> + cxlds->component_reg_phys,
> parent_dport);
> if (IS_ERR(endpoint))
> return PTR_ERR(endpoint);
On 6/21/23 20:51, Terry Bowman wrote:
> Restricted CXL host (RCH) downstream port AER information is not currently
> logged while in the error state. One problem preventing the error logging
> is the AER and RAS registers are not accessible. The CXL driver requires
> changes to find RCH downstream port AER and RAS registers for purpose of
> error logging.
>
> RCH downstream ports are not enumerated during a PCI bus scan and are
> instead discovered using system firmware, ACPI in this case.[1] The
> downstream port is implemented as a Root Complex Register Block (RCRB).
> The RCRB is a 4k memory block containing PCIe registers based on the PCIe
> root port.[2] The RCRB includes AER extended capability registers used for
> reporting errors. Note, the RCH's AER Capability is located in the RCRB
> memory space instead of PCI configuration space, thus its register access
> is different. Existing kernel PCIe AER functions can not be used to manage
> the downstream port AER capabilities and RAS registers because the port was
> not enumerated during PCI scan and the registers are not PCI config
> accessible.
>
> Discover RCH downstream port AER extended capability registers. Use MMIO
> accesses to search for extended AER capability in RCRB register space.
>
> [1] CXL 3.0 Spec, 9.11.2 - System Firmware View of CXL 1.1 Hierarchy
> [2] CXL 3.0 Spec, 8.2.1.1 - RCH Downstream Port RCRB
>
> Co-developed-by: Robert Richter <[email protected]>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
> Reviewed-by: Jonathan Cameron <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/core.h | 1 +
> drivers/cxl/core/port.c | 7 ++++++-
> drivers/cxl/core/regs.c | 35 +++++++++++++++++++++++++++++++++++
> 3 files changed, 42 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index b001669a5133..87467c633123 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -72,6 +72,7 @@ struct cxl_rcrb_info;
> resource_size_t __rcrb_to_component(struct device *dev,
> struct cxl_rcrb_info *ri,
> enum cxl_rcrb which);
> +u16 cxl_rcrb_to_aer(struct device *dev, resource_size_t rcrb);
>
> extern struct rw_semaphore cxl_dpa_rwsem;
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index cfae8307de90..37ecd756b224 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -678,7 +678,6 @@ static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
> device_set_pm_not_required(dev);
> dev->bus = &cxl_bus_type;
> dev->type = &cxl_port_type;
> -
As Jonathan pointed out, stray blank line
DJ
> return port;
>
> err:
> @@ -974,6 +973,8 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> return ERR_PTR(-ENOMEM);
>
> if (rcrb != CXL_RESOURCE_NONE) {
> + struct pci_host_bridge *host_bridge;
> +
> dport->rcrb.base = rcrb;
> component_reg_phys = __rcrb_to_component(dport_dev, &dport->rcrb,
> CXL_RCRB_DOWNSTREAM);
> @@ -982,6 +983,10 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
> return ERR_PTR(-ENXIO);
> }
>
> + host_bridge = to_pci_host_bridge(dport_dev);
> + if (host_bridge->native_cxl_error)
> + dport->rcrb.aer_cap = cxl_rcrb_to_aer(dport_dev, dport->rcrb.base);
> +
> dport->rch = true;
> }
>
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index e68848075bb6..60b7ba7f4030 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -407,6 +407,41 @@ int cxl_setup_regs(struct cxl_register_map *map)
> }
> EXPORT_SYMBOL_NS_GPL(cxl_setup_regs, CXL);
>
> +u16 cxl_rcrb_to_aer(struct device *dev, resource_size_t rcrb)
> +{
> + void __iomem *addr;
> + u16 offset = 0;
> + u32 cap_hdr;
> +
> + if (WARN_ON_ONCE(rcrb == CXL_RESOURCE_NONE))
> + return 0;
> +
> + if (!request_mem_region(rcrb, SZ_4K, dev_name(dev)))
> + return 0;
> +
> + addr = ioremap(rcrb, SZ_4K);
> + if (!addr) {
> + release_mem_region(rcrb, SZ_4K);
> + return 0;
> + }
> +
> + cap_hdr = readl(addr + offset);
> + while (PCI_EXT_CAP_ID(cap_hdr) != PCI_EXT_CAP_ID_ERR) {
> + offset = PCI_EXT_CAP_NEXT(cap_hdr);
> + if (!offset)
> + break;
> + cap_hdr = readl(addr + offset);
> + }
> +
> + if (offset)
> + dev_dbg(dev, "found AER extended capability (0x%x)\n", offset);
> +
> + iounmap(addr);
> + release_mem_region(rcrb, SZ_4K);
> +
> + return offset;
> +}
> +
> resource_size_t __rcrb_to_component(struct device *dev, struct cxl_rcrb_info *ri,
> enum cxl_rcrb which)
> {
On 6/21/23 20:51, Terry Bowman wrote:
> From: Robert Richter <[email protected]>
>
> CXL capabilities are stored in the Component Registers. To use them,
> the specific I/O ranges of the capabilities must be determined by
> probing the registers. For this, the whole Component Register range
> needs to be mapped temporarily to detect the offset and length of a
> capability range.
>
> In order to use more than one capability of a component (e.g. RAS and
> HDM) the Component Register are probed and its mappings created
> multiple times. This also causes overlapping I/O ranges as the whole
> Component Register range must be mapped again while a capability's I/O
> range is already mapped.
>
> Different capabilities cannot be setup at the same time. E.g. the RAS
> capability must be made available as soon as the PCI driver is bound,
> the HDM decoder is setup later during port enumeration. Moreover,
> during early setup it is still unknown if a certain capability is
> needed. A central capability setup is therefore not possible,
> capabilities must be individually enabled once needed during
> initialization.
>
> To avoid a duplicate register probe and overlapping I/O mappings, only
> probe the Component Registers one time and store the Component
> Register mapping in struct port. The stored mappings can be used later
> to iomap the capability register range when enabling the capability,
> which will be implemented in a follow-on patch.
>
> Signed-off-by: Robert Richter <[email protected]>
> Signed-off-by: Terry Bowman <[email protected]>
Reviewed-by: Dave Jiang <[email protected]>
> ---
> drivers/cxl/core/port.c | 26 ++++++++++++++++++++++++++
> drivers/cxl/cxl.h | 2 ++
> 2 files changed, 28 insertions(+)
>
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 679226023f0c..2e239fd33df9 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -688,6 +688,28 @@ static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
> return ERR_PTR(rc);
> }
>
> +static int cxl_setup_comp_regs(struct device *dev, struct cxl_register_map *map,
> + resource_size_t component_reg_phys)
> +{
> + if (component_reg_phys == CXL_RESOURCE_NONE)
> + return 0;
> +
> + memset(map, 0, sizeof(*map));
> + map->dev = dev;
> + map->reg_type = CXL_REGLOC_RBI_COMPONENT;
> + map->resource = component_reg_phys;
> + map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE;
> +
> + return cxl_setup_regs(map);
> +}
> +
> +static inline int cxl_port_setup_regs(struct cxl_port *port,
> + resource_size_t component_reg_phys)
> +{
> + return cxl_setup_comp_regs(&port->dev, &port->comp_map,
> + component_reg_phys);
> +}
> +
> static struct cxl_port *__devm_cxl_add_port(struct device *host,
> struct device *uport_dev,
> resource_size_t component_reg_phys,
> @@ -711,6 +733,10 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
> if (rc)
> goto err;
>
> + rc = cxl_port_setup_regs(port, component_reg_phys);
> + if (rc)
> + goto err;
> +
> rc = device_add(dev);
> if (rc)
> goto err;
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index fe95f08acb69..37fa5b565362 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -547,6 +547,7 @@ struct cxl_dax_region {
> * @regions: cxl_region_ref instances, regions mapped by this port
> * @parent_dport: dport that points to this port in the parent
> * @decoder_ida: allocator for decoder ids
> + * @comp_map: component register capability mappings
> * @nr_dports: number of entries in @dports
> * @hdm_end: track last allocated HDM decoder instance for allocation ordering
> * @commit_end: cursor to track highest committed decoder for commit ordering
> @@ -566,6 +567,7 @@ struct cxl_port {
> struct xarray regions;
> struct cxl_dport *parent_dport;
> struct ida decoder_ida;
> + struct cxl_register_map comp_map;
> int nr_dports;
> int hdm_end;
> int commit_end;
On Thu, 22 Jun 2023 11:33:30 -0500
Terry Bowman <[email protected]> wrote:
> Hi Jonathan,
>
> On 6/22/23 08:12, Jonathan Cameron wrote:
> > On Wed, 21 Jun 2023 22:51:24 -0500
> > Terry Bowman <[email protected]> wrote:
> >
> >> The RCH root port contains root command AER registers that should not be
> >> enabled.[1] Disable these to prevent root port interrupts.
> >
> > I'm a little dubious about a 'because the spec says' so argument.
> > If we can describe the path by which spurious interrupts turn up then
> > great - if not then fair enough.
> >
>
> This was added to follow the spec. The RCH downstream port contains
> root port control pci express capability for enabling and disabling root
> port interrupts. The interrupts are (should be) disabled by default at
> powerup according to the PCI spec. We know SW does not enable because
> the RCH downstream port is not enumerated or managed by a port driver. I
> cant say this patch is absolutely necessary but was not comfortable with
> removing it either and want to avoid undefined behavior.
>
Maybe we should just abuse firmware authors and blame them for potentially
having changed the default :)
Fine as is.
Jonathan
> Regards,
> Terry