diff options
| author | Dave Jiang <dave.jiang@intel.com> | 2026-02-02 09:39:41 -0700 |
|---|---|---|
| committer | Dave Jiang <dave.jiang@intel.com> | 2026-02-02 09:39:41 -0700 |
| commit | 0da3050bdded5f121aaca6b5247ea50681d7129e (patch) | |
| tree | e2f6b0b3837c3aae0708375cceb1452c7e3a9078 | |
| parent | 63050be0bfe0b280cce5d701b31940fd84858609 (diff) | |
| parent | 2d2b3fe002797c8de2c71236662593bf36de834d (diff) | |
Merge branch 'for-7.0/cxl-aer-prep' into cxl-for-next
Fixup and refactor downstream port enumeration to prepare for CXL port
protocol error handling. Main motivation is to move endpoint
component register mapping to a port object.
cxl/port: Unify endpoint and switch port lookup
cxl/port: Move endpoint component register management to cxl_port
cxl/port: Map Port RAS registers
cxl/port: Move dport RAS setup to dport add time
cxl/port: Move dport probe operations to a driver event
cxl/port: Move decoder setup before dport creation
cxl/port: Cleanup dport removal with a devres group
cxl/port: Reduce number of @dport variables in cxl_port_add_dport()
cxl/port: Cleanup handling of the nr_dports 0 -> 1 transition
| -rw-r--r-- | drivers/cxl/core/core.h | 10 | ||||
| -rw-r--r-- | drivers/cxl/core/hdm.c | 6 | ||||
| -rw-r--r-- | drivers/cxl/core/pci.c | 8 | ||||
| -rw-r--r-- | drivers/cxl/core/port.c | 159 | ||||
| -rw-r--r-- | drivers/cxl/core/ras.c | 50 | ||||
| -rw-r--r-- | drivers/cxl/cxl.h | 33 | ||||
| -rw-r--r-- | drivers/cxl/cxlmem.h | 4 | ||||
| -rw-r--r-- | drivers/cxl/cxlpci.h | 12 | ||||
| -rw-r--r-- | drivers/cxl/mem.c | 2 | ||||
| -rw-r--r-- | drivers/cxl/pci.c | 56 | ||||
| -rw-r--r-- | drivers/cxl/port.c | 122 | ||||
| -rw-r--r-- | tools/testing/cxl/Kbuild | 3 | ||||
| -rw-r--r-- | tools/testing/cxl/cxl_core_exports.c | 22 | ||||
| -rw-r--r-- | tools/testing/cxl/exports.h | 13 | ||||
| -rw-r--r-- | tools/testing/cxl/test/mock.c | 36 |
15 files changed, 311 insertions, 225 deletions
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 422531799af2..be3c7b137115 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -144,6 +144,14 @@ int cxl_pci_get_bandwidth(struct pci_dev *pdev, struct access_coordinate *c); int cxl_port_get_switch_dport_bandwidth(struct cxl_port *port, struct access_coordinate *c); +static inline struct device *dport_to_host(struct cxl_dport *dport) +{ + struct cxl_port *port = dport->port; + + if (is_cxl_root(port)) + return port->uport_dev; + return &port->dev; +} #ifdef CONFIG_CXL_RAS int cxl_ras_init(void); void cxl_ras_exit(void); @@ -152,6 +160,7 @@ void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base); void cxl_dport_map_rch_aer(struct cxl_dport *dport); void cxl_disable_rch_root_ints(struct cxl_dport *dport); void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds); +void devm_cxl_dport_ras_setup(struct cxl_dport *dport); #else static inline int cxl_ras_init(void) { @@ -166,6 +175,7 @@ static inline void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base static inline void cxl_dport_map_rch_aer(struct cxl_dport *dport) { } static inline void cxl_disable_rch_root_ints(struct cxl_dport *dport) { } static inline void cxl_handle_rdport_errors(struct cxl_dev_state *cxlds) { } +static inline void devm_cxl_dport_ras_setup(struct cxl_dport *dport) { } #endif /* CONFIG_CXL_RAS */ int cxl_gpf_port_setup(struct cxl_dport *dport); diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c index 1097de03a2bb..b2db5967f5c0 100644 --- a/drivers/cxl/core/hdm.c +++ b/drivers/cxl/core/hdm.c @@ -1218,12 +1218,12 @@ static int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm, } /** - * __devm_cxl_switch_port_decoders_setup - allocate and setup switch decoders + * devm_cxl_switch_port_decoders_setup - allocate and setup switch decoders * @port: CXL port context * * Return 0 or -errno on error */ -int __devm_cxl_switch_port_decoders_setup(struct cxl_port *port) +int devm_cxl_switch_port_decoders_setup(struct cxl_port *port) { struct cxl_hdm *cxlhdm; @@ -1247,7 +1247,7 @@ int __devm_cxl_switch_port_decoders_setup(struct cxl_port *port) dev_err(&port->dev, "HDM decoder capability not found\n"); return -ENXIO; } -EXPORT_SYMBOL_NS_GPL(__devm_cxl_switch_port_decoders_setup, "CXL"); +EXPORT_SYMBOL_NS_GPL(devm_cxl_switch_port_decoders_setup, "CXL"); /** * devm_cxl_endpoint_decoders_setup - allocate and setup endpoint decoders diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index b838c59d7a3c..f96ce884a213 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -41,14 +41,14 @@ static int pci_get_port_num(struct pci_dev *pdev) } /** - * __devm_cxl_add_dport_by_dev - allocate a dport by dport device + * devm_cxl_add_dport_by_dev - allocate a dport by dport device * @port: cxl_port that hosts the dport * @dport_dev: 'struct device' of the dport * * Returns the allocated dport on success or ERR_PTR() of -errno on error */ -struct cxl_dport *__devm_cxl_add_dport_by_dev(struct cxl_port *port, - struct device *dport_dev) +struct cxl_dport *devm_cxl_add_dport_by_dev(struct cxl_port *port, + struct device *dport_dev) { struct cxl_register_map map; struct pci_dev *pdev; @@ -69,7 +69,7 @@ struct cxl_dport *__devm_cxl_add_dport_by_dev(struct cxl_port *port, device_lock_assert(&port->dev); return devm_cxl_add_dport(port, dport_dev, port_num, map.resource); } -EXPORT_SYMBOL_NS_GPL(__devm_cxl_add_dport_by_dev, "CXL"); +EXPORT_SYMBOL_NS_GPL(devm_cxl_add_dport_by_dev, "CXL"); static int cxl_dvsec_mem_range_valid(struct cxl_dev_state *cxlds, int id) { diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index 54f72452fb06..27ad30fe4fd2 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -778,7 +778,7 @@ static int cxl_setup_comp_regs(struct device *host, struct cxl_register_map *map return cxl_setup_regs(map); } -static int cxl_port_setup_regs(struct cxl_port *port, +int cxl_port_setup_regs(struct cxl_port *port, resource_size_t component_reg_phys) { if (dev_is_platform(port->uport_dev)) @@ -786,6 +786,7 @@ static int cxl_port_setup_regs(struct cxl_port *port, return cxl_setup_comp_regs(&port->dev, &port->reg_map, component_reg_phys); } +EXPORT_SYMBOL_NS_GPL(cxl_port_setup_regs, "CXL"); static int cxl_dport_setup_regs(struct device *host, struct cxl_dport *dport, resource_size_t component_reg_phys) @@ -1068,11 +1069,15 @@ static int add_dport(struct cxl_port *port, struct cxl_dport *dport) return -EBUSY; } + /* Arrange for dport_dev to be valid through remove_dport() */ + struct device *dev __free(put_device) = get_device(dport->dport_dev); + rc = xa_insert(&port->dports, (unsigned long)dport->dport_dev, dport, GFP_KERNEL); if (rc) return rc; + retain_and_null_ptr(dev); port->nr_dports++; return 0; } @@ -1101,6 +1106,7 @@ static void cxl_dport_remove(void *data) struct cxl_dport *dport = data; struct cxl_port *port = dport->port; + port->nr_dports--; xa_erase(&port->dports, (unsigned long) dport->dport_dev); put_device(dport->dport_dev); } @@ -1115,6 +1121,48 @@ static void cxl_dport_unlink(void *data) sysfs_remove_link(&port->dev.kobj, link_name); } +static void free_dport(void *dport) +{ + kfree(dport); +} + +/* + * Upon return either a group is established with one action (free_dport()), or + * no group established and @dport is freed. + */ +static void *cxl_dport_open_dr_group_or_free(struct cxl_dport *dport) +{ + int rc; + struct device *host = dport_to_host(dport); + void *group = devres_open_group(host, dport, GFP_KERNEL); + + if (!group) { + kfree(dport); + return NULL; + } + + rc = devm_add_action_or_reset(host, free_dport, dport); + if (rc) { + devres_release_group(host, group); + return NULL; + } + + return group; +} + +static void cxl_dport_close_dr_group(struct cxl_dport *dport, void *group) +{ + devres_close_group(dport_to_host(dport), group); +} + +static void del_dport(struct cxl_dport *dport) +{ + devres_release_group(dport_to_host(dport), dport); +} + +/* The dport group id is the dport */ +DEFINE_FREE(cxl_dport_release_dr_group, void *, if (_T) del_dport(_T)) + 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, @@ -1140,14 +1188,20 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev, CXL_TARGET_STRLEN) return ERR_PTR(-EINVAL); - dport = devm_kzalloc(host, sizeof(*dport), GFP_KERNEL); + dport = kzalloc(sizeof(*dport), GFP_KERNEL); if (!dport) return ERR_PTR(-ENOMEM); + /* Just enough init to manage the devres group */ dport->dport_dev = dport_dev; dport->port_id = port_id; dport->port = port; + void *dport_dr_group __free(cxl_dport_release_dr_group) = + cxl_dport_open_dr_group_or_free(dport); + if (!dport_dr_group) + return ERR_PTR(-ENOMEM); + if (rcrb == CXL_RESOURCE_NONE) { rc = cxl_dport_setup_regs(&port->dev, dport, component_reg_phys); @@ -1183,21 +1237,6 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev, if (rc) return ERR_PTR(rc); - /* - * Setup port register if this is the first dport showed up. Having - * a dport also means that there is at least 1 active link. - */ - if (port->nr_dports == 1 && - port->component_reg_phys != CXL_RESOURCE_NONE) { - rc = cxl_port_setup_regs(port, port->component_reg_phys); - if (rc) { - xa_erase(&port->dports, (unsigned long)dport->dport_dev); - return ERR_PTR(rc); - } - port->component_reg_phys = CXL_RESOURCE_NONE; - } - - get_device(dport_dev); rc = devm_add_action_or_reset(host, cxl_dport_remove, dport); if (rc) return ERR_PTR(rc); @@ -1215,6 +1254,12 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev, cxl_debugfs_create_dport_dir(dport); + if (!dport->rch) + devm_cxl_dport_ras_setup(dport); + + /* keep the group, and mark the end of devm actions */ + cxl_dport_close_dr_group(dport, no_free_ptr(dport_dr_group)); + return dport; } @@ -1441,15 +1486,6 @@ static void delete_switch_port(struct cxl_port *port) devm_release_action(port->dev.parent, unregister_port, port); } -static void del_dport(struct cxl_dport *dport) -{ - struct cxl_port *port = dport->port; - - devm_release_action(&port->dev, cxl_dport_unlink, dport); - devm_release_action(&port->dev, cxl_dport_remove, dport); - devm_kfree(&port->dev, dport); -} - static void del_dports(struct cxl_port *port) { struct cxl_dport *dport; @@ -1556,10 +1592,20 @@ static int match_port_by_uport(struct device *dev, const void *data) return 0; port = to_cxl_port(dev); + /* Endpoint ports are hosted by memdevs */ + if (is_cxl_memdev(port->uport_dev)) + return uport_dev == port->uport_dev->parent; return uport_dev == port->uport_dev; } -/* +/** + * find_cxl_port_by_uport - Find a CXL port device companion + * @uport_dev: Device that acts as a switch or endpoint in the CXL hierarchy + * + * In the case of endpoint ports recall that port->uport_dev points to a 'struct + * cxl_memdev' device. So, the @uport_dev argument is the parent device of the + * 'struct cxl_memdev' in that case. + * * Function takes a device reference on the port device. Caller should do a * put_device() when done. */ @@ -1599,47 +1645,44 @@ static int update_decoder_targets(struct device *dev, void *data) return 0; } -DEFINE_FREE(del_cxl_dport, struct cxl_dport *, if (!IS_ERR_OR_NULL(_T)) del_dport(_T)) -static struct cxl_dport *cxl_port_add_dport(struct cxl_port *port, - struct device *dport_dev) +void cxl_port_update_decoder_targets(struct cxl_port *port, + struct cxl_dport *dport) { - struct cxl_dport *dport; - int rc; + device_for_each_child(&port->dev, dport, update_decoder_targets); +} +EXPORT_SYMBOL_NS_GPL(cxl_port_update_decoder_targets, "CXL"); - device_lock_assert(&port->dev); - if (!port->dev.driver) - return ERR_PTR(-ENXIO); +static bool dport_exists(struct cxl_port *port, struct device *dport_dev) +{ + struct cxl_dport *dport = cxl_find_dport_by_dev(port, dport_dev); - dport = cxl_find_dport_by_dev(port, dport_dev); if (dport) { dev_dbg(&port->dev, "dport%d:%s already exists\n", dport->port_id, dev_name(dport_dev)); - return ERR_PTR(-EBUSY); + return true; } - struct cxl_dport *new_dport __free(del_cxl_dport) = - devm_cxl_add_dport_by_dev(port, dport_dev); - if (IS_ERR(new_dport)) - return new_dport; + return false; +} - cxl_switch_parse_cdat(new_dport); +static struct cxl_dport *probe_dport(struct cxl_port *port, + struct device *dport_dev) +{ + struct cxl_driver *drv; - if (ida_is_empty(&port->decoder_ida)) { - rc = devm_cxl_switch_port_decoders_setup(port); - if (rc) - return ERR_PTR(rc); - dev_dbg(&port->dev, "first dport%d:%s added with decoders\n", - new_dport->port_id, dev_name(dport_dev)); - return no_free_ptr(new_dport); - } + device_lock_assert(&port->dev); + if (!port->dev.driver) + return ERR_PTR(-ENXIO); - /* New dport added, update the decoder targets */ - device_for_each_child(&port->dev, new_dport, update_decoder_targets); + if (dport_exists(port, dport_dev)) + return ERR_PTR(-EBUSY); - dev_dbg(&port->dev, "dport%d:%s added\n", new_dport->port_id, - dev_name(dport_dev)); + drv = container_of(port->dev.driver, struct cxl_driver, drv); + if (!drv->add_dport) + return ERR_PTR(-ENXIO); - return no_free_ptr(new_dport); + /* see cxl_port_add_dport() */ + return drv->add_dport(port, dport_dev); } static struct cxl_dport *devm_cxl_create_port(struct device *ep_dev, @@ -1686,7 +1729,7 @@ static struct cxl_dport *devm_cxl_create_port(struct device *ep_dev, } guard(device)(&port->dev); - return cxl_port_add_dport(port, dport_dev); + return probe_dport(port, dport_dev); } static int add_port_attach_ep(struct cxl_memdev *cxlmd, @@ -1718,7 +1761,7 @@ static int add_port_attach_ep(struct cxl_memdev *cxlmd, scoped_guard(device, &parent_port->dev) { parent_dport = cxl_find_dport_by_dev(parent_port, dparent); if (!parent_dport) { - parent_dport = cxl_port_add_dport(parent_port, dparent); + parent_dport = probe_dport(parent_port, dparent); if (IS_ERR(parent_dport)) return PTR_ERR(parent_dport); } @@ -1754,7 +1797,7 @@ static struct cxl_dport *find_or_add_dport(struct cxl_port *port, device_lock_assert(&port->dev); dport = cxl_find_dport_by_dev(port, dport_dev); if (!dport) { - dport = cxl_port_add_dport(port, dport_dev); + dport = probe_dport(port, dport_dev); if (IS_ERR(dport)) return dport; diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c index 72908f3ced77..f6a8f4a355f1 100644 --- a/drivers/cxl/core/ras.c +++ b/drivers/cxl/core/ras.c @@ -139,26 +139,48 @@ static void cxl_dport_map_ras(struct cxl_dport *dport) } /** - * cxl_dport_init_ras_reporting - Setup CXL RAS report on this dport + * devm_cxl_dport_ras_setup - Setup CXL RAS report on this dport * @dport: the cxl_dport that needs to be initialized - * @host: host device for devm operations */ -void cxl_dport_init_ras_reporting(struct cxl_dport *dport, struct device *host) +void devm_cxl_dport_ras_setup(struct cxl_dport *dport) { - dport->reg_map.host = host; + dport->reg_map.host = dport_to_host(dport); cxl_dport_map_ras(dport); +} - if (dport->rch) { - struct pci_host_bridge *host_bridge = to_pci_host_bridge(dport->dport_dev); +void devm_cxl_dport_rch_ras_setup(struct cxl_dport *dport) +{ + struct pci_host_bridge *host_bridge; - if (!host_bridge->native_aer) - return; + if (!dev_is_pci(dport->dport_dev)) + return; - cxl_dport_map_rch_aer(dport); - cxl_disable_rch_root_ints(dport); + devm_cxl_dport_ras_setup(dport); + + host_bridge = to_pci_host_bridge(dport->dport_dev); + if (!host_bridge->native_aer) + return; + + cxl_dport_map_rch_aer(dport); + cxl_disable_rch_root_ints(dport); +} +EXPORT_SYMBOL_NS_GPL(devm_cxl_dport_rch_ras_setup, "CXL"); + +void devm_cxl_port_ras_setup(struct cxl_port *port) +{ + struct cxl_register_map *map = &port->reg_map; + + if (!map->component_map.ras.valid) { + dev_dbg(&port->dev, "RAS registers not found\n"); + return; } + + map->host = &port->dev; + if (cxl_map_component_regs(map, &port->regs, + BIT(CXL_CM_CAP_CAP_ID_RAS))) + dev_dbg(&port->dev, "Failed to map RAS capability\n"); } -EXPORT_SYMBOL_NS_GPL(cxl_dport_init_ras_reporting, "CXL"); +EXPORT_SYMBOL_NS_GPL(devm_cxl_port_ras_setup, "CXL"); void cxl_handle_cor_ras(struct device *dev, void __iomem *ras_base) { @@ -233,6 +255,7 @@ bool cxl_handle_ras(struct device *dev, void __iomem *ras_base) void cxl_cor_error_detected(struct pci_dev *pdev) { struct cxl_dev_state *cxlds = pci_get_drvdata(pdev); + struct cxl_memdev *cxlmd = cxlds->cxlmd; struct device *dev = &cxlds->cxlmd->dev; scoped_guard(device, dev) { @@ -246,7 +269,7 @@ void cxl_cor_error_detected(struct pci_dev *pdev) if (cxlds->rcd) cxl_handle_rdport_errors(cxlds); - cxl_handle_cor_ras(&cxlds->cxlmd->dev, cxlds->regs.ras); + cxl_handle_cor_ras(&cxlds->cxlmd->dev, cxlmd->endpoint->regs.ras); } } EXPORT_SYMBOL_NS_GPL(cxl_cor_error_detected, "CXL"); @@ -275,10 +298,9 @@ 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_handle_ras(&cxlds->cxlmd->dev, cxlds->regs.ras); + ue = cxl_handle_ras(&cxlds->cxlmd->dev, cxlmd->endpoint->regs.ras); } - switch (state) { case pci_channel_io_normal: if (ue) { diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index e1d47062e1d3..478d8803b464 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -607,6 +607,7 @@ struct cxl_dax_region { * @parent_dport: dport that points to this port in the parent * @decoder_ida: allocator for decoder ids * @reg_map: component and ras register mapping parameters + * @regs: mapped component registers * @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 @@ -628,6 +629,7 @@ struct cxl_port { struct cxl_dport *parent_dport; struct ida decoder_ida; struct cxl_register_map reg_map; + struct cxl_component_regs regs; int nr_dports; int hdm_end; int commit_end; @@ -842,8 +844,11 @@ struct cxl_endpoint_dvsec_info { }; int devm_cxl_switch_port_decoders_setup(struct cxl_port *port); -int __devm_cxl_switch_port_decoders_setup(struct cxl_port *port); int devm_cxl_endpoint_decoders_setup(struct cxl_port *port); +void cxl_port_update_decoder_targets(struct cxl_port *port, + struct cxl_dport *dport); +int cxl_port_setup_regs(struct cxl_port *port, + resource_size_t component_reg_phys); struct cxl_dev_state; int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, @@ -853,10 +858,18 @@ bool is_cxl_region(struct device *dev); extern const struct bus_type cxl_bus_type; +/* + * Note, add_dport() is expressly for the cxl_port driver. TODO: investigate a + * type-safe driver model where probe()/remove() take the type of object implied + * by @id and the add_dport() op only defined for the CXL_DEVICE_PORT driver + * template. + */ struct cxl_driver { const char *name; int (*probe)(struct device *dev); void (*remove)(struct device *dev); + struct cxl_dport *(*add_dport)(struct cxl_port *port, + struct device *dport_dev); struct device_driver drv; int id; }; @@ -941,8 +954,6 @@ void cxl_coordinates_combine(struct access_coordinate *out, bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port); struct cxl_dport *devm_cxl_add_dport_by_dev(struct cxl_port *port, struct device *dport_dev); -struct cxl_dport *__devm_cxl_add_dport_by_dev(struct cxl_port *port, - struct device *dport_dev); /* * Unit test builds overrides this to __weak, find the 'strong' version @@ -954,20 +965,4 @@ struct cxl_dport *__devm_cxl_add_dport_by_dev(struct cxl_port *port, u16 cxl_gpf_get_dvsec(struct device *dev); -/* - * Declaration for functions that are mocked by cxl_test that are called by - * cxl_core. The respective functions are defined as __foo() and called by - * cxl_core as foo(). The macros below ensures that those functions would - * exist as foo(). See tools/testing/cxl/cxl_core_exports.c and - * tools/testing/cxl/exports.h for setting up the mock functions. The dance - * is done to avoid a circular dependency where cxl_core calls a function that - * ends up being a mock function and goes to * cxl_test where it calls a - * cxl_core function. - */ -#ifndef CXL_TEST_ENABLE -#define DECLARE_TESTABLE(x) __##x -#define devm_cxl_add_dport_by_dev DECLARE_TESTABLE(devm_cxl_add_dport_by_dev) -#define devm_cxl_switch_port_decoders_setup DECLARE_TESTABLE(devm_cxl_switch_port_decoders_setup) -#endif - #endif /* __CXL_H__ */ diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index ef202b34e5ea..e21d744d639b 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -423,7 +423,7 @@ struct cxl_dpa_partition { * @dev: The device associated with this CXL state * @cxlmd: The device representing the CXL.mem capabilities of @dev * @reg_map: component and ras register mapping parameters - * @regs: Parsed register blocks + * @regs: Class device "Device" registers * @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) * @media_ready: Indicate whether the device media is usable @@ -439,7 +439,7 @@ struct cxl_dev_state { struct device *dev; struct cxl_memdev *cxlmd; struct cxl_register_map reg_map; - struct cxl_regs regs; + struct cxl_device_regs regs; int cxl_dvsec; bool rcd; bool media_ready; diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h index 6f9c78886fd9..0cf64218aa16 100644 --- a/drivers/cxl/cxlpci.h +++ b/drivers/cxl/cxlpci.h @@ -81,7 +81,8 @@ void read_cdat_data(struct cxl_port *port); void cxl_cor_error_detected(struct pci_dev *pdev); pci_ers_result_t cxl_error_detected(struct pci_dev *pdev, pci_channel_state_t state); -void cxl_dport_init_ras_reporting(struct cxl_dport *dport, struct device *host); +void devm_cxl_dport_rch_ras_setup(struct cxl_dport *dport); +void devm_cxl_port_ras_setup(struct cxl_port *port); #else static inline void cxl_cor_error_detected(struct pci_dev *pdev) { } @@ -91,8 +92,13 @@ static inline pci_ers_result_t cxl_error_detected(struct pci_dev *pdev, return PCI_ERS_RESULT_NONE; } -static inline void cxl_dport_init_ras_reporting(struct cxl_dport *dport, - struct device *host) { } +static inline void devm_cxl_dport_rch_ras_setup(struct cxl_dport *dport) +{ +} + +static inline void devm_cxl_port_ras_setup(struct cxl_port *port) +{ +} #endif #endif /* __CXL_PCI_H__ */ diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c index 0958bea915ac..fcffe24dcb42 100644 --- a/drivers/cxl/mem.c +++ b/drivers/cxl/mem.c @@ -128,8 +128,6 @@ static int cxl_mem_probe(struct device *dev) else endpoint_parent = &parent_port->dev; - cxl_dport_init_ras_reporting(dport, dev); - scoped_guard(device, endpoint_parent) { if (!endpoint_parent->driver) { dev_err(dev, "CXL port topology %s not enabled\n", diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index 1cf232220873..fbb300a01830 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -535,52 +535,6 @@ static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, return cxl_setup_regs(map); } -static int cxl_pci_ras_unmask(struct pci_dev *pdev) -{ - struct cxl_dev_state *cxlds = pci_get_drvdata(pdev); - void __iomem *addr; - u32 orig_val, val, mask; - u16 cap; - int rc; - - if (!cxlds->regs.ras) { - dev_dbg(&pdev->dev, "No RAS registers.\n"); - return 0; - } - - /* BIOS has PCIe AER error control */ - if (!pcie_aer_is_native(pdev)) - return 0; - - rc = pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &cap); - if (rc) - return rc; - - if (cap & PCI_EXP_DEVCTL_URRE) { - addr = cxlds->regs.ras + CXL_RAS_UNCORRECTABLE_MASK_OFFSET; - orig_val = readl(addr); - - mask = CXL_RAS_UNCORRECTABLE_MASK_MASK | - CXL_RAS_UNCORRECTABLE_MASK_F256B_MASK; - val = orig_val & ~mask; - writel(val, addr); - dev_dbg(&pdev->dev, - "Uncorrectable RAS Errors Mask: %#x -> %#x\n", - orig_val, val); - } - - if (cap & PCI_EXP_DEVCTL_CERE) { - addr = cxlds->regs.ras + CXL_RAS_CORRECTABLE_MASK_OFFSET; - orig_val = readl(addr); - val = orig_val & ~CXL_RAS_CORRECTABLE_MASK_MASK; - writel(val, addr); - dev_dbg(&pdev->dev, "Correctable RAS Errors Mask: %#x -> %#x\n", - orig_val, val); - } - - return 0; -} - static void free_event_buf(void *buf) { kvfree(buf); @@ -935,7 +889,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(&map, &cxlds->regs.device_regs); + rc = cxl_map_device_regs(&map, &cxlds->regs); if (rc) return rc; @@ -950,11 +904,6 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) else if (!cxlds->reg_map.component_map.ras.valid) dev_dbg(&pdev->dev, "RAS registers not found\n"); - rc = cxl_map_component_regs(&cxlds->reg_map, &cxlds->regs.component, - BIT(CXL_CM_CAP_CAP_ID_RAS)); - if (rc) - dev_dbg(&pdev->dev, "Failed to map RAS capability.\n"); - rc = cxl_pci_type3_init_mailbox(cxlds); if (rc) return rc; @@ -1045,9 +994,6 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (rc) return rc; - if (cxl_pci_ras_unmask(pdev)) - dev_dbg(&pdev->dev, "No RAS reporting unmasked\n"); - pci_save_state(pdev); return rc; diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c index 7937e7e53797..ada51948d52f 100644 --- a/drivers/cxl/port.c +++ b/drivers/cxl/port.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ +#include <linux/aer.h> #include <linux/device.h> #include <linux/module.h> #include <linux/slab.h> @@ -68,9 +69,59 @@ static int cxl_switch_port_probe(struct cxl_port *port) return 0; } +static int cxl_ras_unmask(struct cxl_port *port) +{ + struct pci_dev *pdev; + void __iomem *addr; + u32 orig_val, val, mask; + u16 cap; + int rc; + + if (!dev_is_pci(port->uport_dev)) + return 0; + pdev = to_pci_dev(port->uport_dev); + + if (!port->regs.ras) { + pci_dbg(pdev, "No RAS registers.\n"); + return 0; + } + + /* BIOS has PCIe AER error control */ + if (!pcie_aer_is_native(pdev)) + return 0; + + rc = pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &cap); + if (rc) + return rc; + + if (cap & PCI_EXP_DEVCTL_URRE) { + addr = port->regs.ras + CXL_RAS_UNCORRECTABLE_MASK_OFFSET; + orig_val = readl(addr); + + mask = CXL_RAS_UNCORRECTABLE_MASK_MASK | + CXL_RAS_UNCORRECTABLE_MASK_F256B_MASK; + val = orig_val & ~mask; + writel(val, addr); + pci_dbg(pdev, "Uncorrectable RAS Errors Mask: %#x -> %#x\n", + orig_val, val); + } + + if (cap & PCI_EXP_DEVCTL_CERE) { + addr = port->regs.ras + CXL_RAS_CORRECTABLE_MASK_OFFSET; + orig_val = readl(addr); + val = orig_val & ~CXL_RAS_CORRECTABLE_MASK_MASK; + writel(val, addr); + pci_dbg(pdev, "Correctable RAS Errors Mask: %#x -> %#x\n", + orig_val, val); + } + + return 0; +} + static int cxl_endpoint_port_probe(struct cxl_port *port) { struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev); + struct cxl_dport *dport = port->parent_dport; int rc; /* Cache the data early to ensure is_visible() works */ @@ -87,6 +138,21 @@ static int cxl_endpoint_port_probe(struct cxl_port *port) return rc; /* + * With VH (CXL Virtual Host) topology the cxl_port::add_dport() method + * handles RAS setup for downstream ports. With RCH (CXL Restricted CXL + * Host) topologies the downstream port is enumerated early by platform + * firmware, but the RCRB (root complex register block) is not mapped + * until after the cxl_pci driver attaches to the RCIeP (root complex + * integrated endpoint). + */ + if (dport->rch) + devm_cxl_dport_rch_ras_setup(dport); + + devm_cxl_port_ras_setup(port); + if (cxl_ras_unmask(port)) + dev_dbg(&port->dev, "failed to unmask RAS interrupts\n"); + + /* * Now that all endpoint decoders are successfully enumerated, try to * assemble regions from committed decoders */ @@ -151,9 +217,65 @@ static const struct attribute_group *cxl_port_attribute_groups[] = { NULL, }; +/* note this implicitly casts the group back to its @port */ +DEFINE_FREE(cxl_port_release_dr_group, struct cxl_port *, + if (_T) devres_release_group(&_T->dev, _T)) + +static struct cxl_dport *cxl_port_add_dport(struct cxl_port *port, + struct device *dport_dev) +{ + struct cxl_dport *dport; + int rc; + + /* Temp group for all "first dport" and "per dport" setup actions */ + void *port_dr_group __free(cxl_port_release_dr_group) = + devres_open_group(&port->dev, port, GFP_KERNEL); + if (!port_dr_group) + return ERR_PTR(-ENOMEM); + + if (port->nr_dports == 0) { + /* + * Some host bridges are known to not have component regsisters + * available until a root port has trained CXL. Perform that + * setup now. + */ + rc = cxl_port_setup_regs(port, port->component_reg_phys); + if (rc) + return ERR_PTR(rc); + + rc = devm_cxl_switch_port_decoders_setup(port); + if (rc) + return ERR_PTR(rc); + + /* + * RAS setup is optional, either driver operation can continue + * on failure, or the device does not implement RAS registers. + */ + devm_cxl_port_ras_setup(port); + } + + dport = devm_cxl_add_dport_by_dev(port, dport_dev); + if (IS_ERR(dport)) + return dport; + + /* This group was only needed for early exit above */ + devres_remove_group(&port->dev, no_free_ptr(port_dr_group)); + + cxl_switch_parse_cdat(dport); + + /* New dport added, update the decoder targets */ + cxl_port_update_decoder_targets(port, dport); + + dev_dbg(&port->dev, "dport%d:%s added\n", dport->port_id, + dev_name(dport_dev)); + + return dport; +} + static struct cxl_driver cxl_port_driver = { .name = "cxl_port", .probe = cxl_port_probe, + .add_dport = cxl_port_add_dport, .id = CXL_DEVICE_PORT, .drv = { .probe_type = PROBE_FORCE_SYNCHRONOUS, diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild index 6eceefefb0e0..982e8ea28b92 100644 --- a/tools/testing/cxl/Kbuild +++ b/tools/testing/cxl/Kbuild @@ -7,9 +7,10 @@ ldflags-y += --wrap=nvdimm_bus_register ldflags-y += --wrap=cxl_await_media_ready ldflags-y += --wrap=devm_cxl_add_rch_dport ldflags-y += --wrap=cxl_endpoint_parse_cdat -ldflags-y += --wrap=cxl_dport_init_ras_reporting ldflags-y += --wrap=devm_cxl_endpoint_decoders_setup ldflags-y += --wrap=hmat_get_extended_linear_cache_size +ldflags-y += --wrap=devm_cxl_add_dport_by_dev +ldflags-y += --wrap=devm_cxl_switch_port_decoders_setup DRIVERS := ../../../drivers CXL_SRC := $(DRIVERS)/cxl diff --git a/tools/testing/cxl/cxl_core_exports.c b/tools/testing/cxl/cxl_core_exports.c index 6754de35598d..f088792a8925 100644 --- a/tools/testing/cxl/cxl_core_exports.c +++ b/tools/testing/cxl/cxl_core_exports.c @@ -2,28 +2,6 @@ /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ #include "cxl.h" -#include "exports.h" /* Exporting of cxl_core symbols that are only used by cxl_test */ EXPORT_SYMBOL_NS_GPL(cxl_num_decoders_committed, "CXL"); - -cxl_add_dport_by_dev_fn _devm_cxl_add_dport_by_dev = - __devm_cxl_add_dport_by_dev; -EXPORT_SYMBOL_NS_GPL(_devm_cxl_add_dport_by_dev, "CXL"); - -struct cxl_dport *devm_cxl_add_dport_by_dev(struct cxl_port *port, - struct device *dport_dev) -{ - return _devm_cxl_add_dport_by_dev(port, dport_dev); -} -EXPORT_SYMBOL_NS_GPL(devm_cxl_add_dport_by_dev, "CXL"); - -cxl_switch_decoders_setup_fn _devm_cxl_switch_port_decoders_setup = - __devm_cxl_switch_port_decoders_setup; -EXPORT_SYMBOL_NS_GPL(_devm_cxl_switch_port_decoders_setup, "CXL"); - -int devm_cxl_switch_port_decoders_setup(struct cxl_port *port) -{ - return _devm_cxl_switch_port_decoders_setup(port); -} -EXPORT_SYMBOL_NS_GPL(devm_cxl_switch_port_decoders_setup, "CXL"); diff --git a/tools/testing/cxl/exports.h b/tools/testing/cxl/exports.h deleted file mode 100644 index 7ebee7c0bd67..000000000000 --- a/tools/testing/cxl/exports.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright(c) 2025 Intel Corporation */ -#ifndef __MOCK_CXL_EXPORTS_H_ -#define __MOCK_CXL_EXPORTS_H_ - -typedef struct cxl_dport *(*cxl_add_dport_by_dev_fn)(struct cxl_port *port, - struct device *dport_dev); -extern cxl_add_dport_by_dev_fn _devm_cxl_add_dport_by_dev; - -typedef int(*cxl_switch_decoders_setup_fn)(struct cxl_port *port); -extern cxl_switch_decoders_setup_fn _devm_cxl_switch_port_decoders_setup; - -#endif diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c index 44bce80ef3ff..b8fcb50c1027 100644 --- a/tools/testing/cxl/test/mock.c +++ b/tools/testing/cxl/test/mock.c @@ -10,21 +10,12 @@ #include <cxlmem.h> #include <cxlpci.h> #include "mock.h" -#include "../exports.h" static LIST_HEAD(mock); -static struct cxl_dport * -redirect_devm_cxl_add_dport_by_dev(struct cxl_port *port, - struct device *dport_dev); -static int redirect_devm_cxl_switch_port_decoders_setup(struct cxl_port *port); - void register_cxl_mock_ops(struct cxl_mock_ops *ops) { list_add_rcu(&ops->list, &mock); - _devm_cxl_add_dport_by_dev = redirect_devm_cxl_add_dport_by_dev; - _devm_cxl_switch_port_decoders_setup = - redirect_devm_cxl_switch_port_decoders_setup; } EXPORT_SYMBOL_GPL(register_cxl_mock_ops); @@ -32,9 +23,6 @@ DEFINE_STATIC_SRCU(cxl_mock_srcu); void unregister_cxl_mock_ops(struct cxl_mock_ops *ops) { - _devm_cxl_switch_port_decoders_setup = - __devm_cxl_switch_port_decoders_setup; - _devm_cxl_add_dport_by_dev = __devm_cxl_add_dport_by_dev; list_del_rcu(&ops->list); synchronize_srcu(&cxl_mock_srcu); } @@ -163,7 +151,7 @@ __wrap_nvdimm_bus_register(struct device *dev, } EXPORT_SYMBOL_GPL(__wrap_nvdimm_bus_register); -int redirect_devm_cxl_switch_port_decoders_setup(struct cxl_port *port) +int __wrap_devm_cxl_switch_port_decoders_setup(struct cxl_port *port) { int rc, index; struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); @@ -171,11 +159,12 @@ int redirect_devm_cxl_switch_port_decoders_setup(struct cxl_port *port) if (ops && ops->is_mock_port(port->uport_dev)) rc = ops->devm_cxl_switch_port_decoders_setup(port); else - rc = __devm_cxl_switch_port_decoders_setup(port); + rc = devm_cxl_switch_port_decoders_setup(port); put_cxl_mock_ops(index); return rc; } +EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_switch_port_decoders_setup, "CXL"); int __wrap_devm_cxl_endpoint_decoders_setup(struct cxl_port *port) { @@ -245,20 +234,8 @@ void __wrap_cxl_endpoint_parse_cdat(struct cxl_port *port) } EXPORT_SYMBOL_NS_GPL(__wrap_cxl_endpoint_parse_cdat, "CXL"); -void __wrap_cxl_dport_init_ras_reporting(struct cxl_dport *dport, struct device *host) -{ - int index; - struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); - - if (!ops || !ops->is_mock_port(dport->dport_dev)) - cxl_dport_init_ras_reporting(dport, host); - - put_cxl_mock_ops(index); -} -EXPORT_SYMBOL_NS_GPL(__wrap_cxl_dport_init_ras_reporting, "CXL"); - -struct cxl_dport *redirect_devm_cxl_add_dport_by_dev(struct cxl_port *port, - struct device *dport_dev) +struct cxl_dport *__wrap_devm_cxl_add_dport_by_dev(struct cxl_port *port, + struct device *dport_dev) { int index; struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); @@ -267,11 +244,12 @@ struct cxl_dport *redirect_devm_cxl_add_dport_by_dev(struct cxl_port *port, if (ops && ops->is_mock_port(port->uport_dev)) dport = ops->devm_cxl_add_dport_by_dev(port, dport_dev); else - dport = __devm_cxl_add_dport_by_dev(port, dport_dev); + dport = devm_cxl_add_dport_by_dev(port, dport_dev); put_cxl_mock_ops(index); return dport; } +EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_add_dport_by_dev, "CXL"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("cxl_test: emulation module"); |
