On 24.11.22 10:35:32, Dan Williams wrote: > Unlike a CXL memory expander in a VH topology that has at least one > intervening 'struct cxl_port' instance between itself and the CXL root > device, an RCD attaches one-level higher. For example: > > VH > ┌──────────┐ > │ ACPI0017 │ > │ root0 │ > └─────┬────┘ > │ > ┌─────┴────┐ > │ dport0 │ > ┌─────┤ ACPI0016 ├─────┐ > │ │ port1 │ │ > │ └────┬─────┘ │ > │ │ │ > ┌──┴───┐ ┌──┴───┐ ┌───┴──┐ > │dport0│ │dport1│ │dport2│ > │ RP0 │ │ RP1 │ │ RP2 │ > └──────┘ └──┬───┘ └──────┘ > │ > ┌───┴─────┐ > │endpoint0│ > │ port2 │ > └─────────┘ > > ...vs: > > RCH > ┌──────────┐ > │ ACPI0017 │ > │ root0 │ > └────┬─────┘ > │ > ┌───┴────┐ > │ dport0 │ > │ACPI0016│ > └───┬────┘ > │ > ┌────┴─────┐ > │endpoint0 │ > │ port1 │ > └──────────┘ > > So arrange for endpoint port in the RCH/RCD case to appear directly > connected to the host-bridge in its singular role as a dport. Compare > that to the VH case where the host-bridge serves a dual role as a > 'cxl_dport' for the CXL root device *and* a 'cxl_port' upstream port for > the Root Ports in the Root Complex that are modeled as 'cxl_dport' > instances in the CXL topology. > > Another deviation from the VH case is that RCDs may need to look up > their component registers from the Root Complex Register Block (RCRB). > That platform firmware specified RCRB area is cached by the cxl_acpi > driver and conveyed via the host-bridge dport to the cxl_mem driver to > perform the cxl_rcrb_to_component() lookup for the endpoint port > (See 9.11.8 CXL Devices Attached to an RCH for the lookup of the > upstream port component registers). > > Signed-off-by: Dan Williams <dan.j.willi...@intel.com> > --- > drivers/cxl/core/port.c | 11 +++++++++-- > drivers/cxl/cxlmem.h | 2 ++ > drivers/cxl/mem.c | 31 ++++++++++++++++++++++++------- > drivers/cxl/pci.c | 10 ++++++++++ > 4 files changed, 45 insertions(+), 9 deletions(-) > > diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c > index c7f58282b2c1..2385ee00eb9a 100644 > --- a/drivers/cxl/core/port.c > +++ b/drivers/cxl/core/port.c > @@ -1358,8 +1358,17 @@ int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd) > { > struct device *dev = &cxlmd->dev; > struct device *iter; > + struct cxl_dport *dport; > + struct cxl_port *port;
There is no direct need to move that code here. If you want to clean that up in this patch too, then leave a comment in the change log? > int rc; > > + /* > + * Skip intermediate port enumeration in the RCH case, there > + * are no ports in between a host bridge and an endpoint. > + */ > + if (cxlmd->cxlds->rcd) > + return 0; > + > rc = devm_add_action_or_reset(&cxlmd->dev, cxl_detach_ep, cxlmd); > if (rc) > return rc; > @@ -1373,8 +1382,6 @@ int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd) > for (iter = dev; iter; iter = grandparent(iter)) { > struct device *dport_dev = grandparent(iter); > struct device *uport_dev; > - struct cxl_dport *dport; > - struct cxl_port *port; > > if (!dport_dev) > return 0; > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h > index e082991bc58c..35d485d041f0 100644 > --- a/drivers/cxl/cxlmem.h > +++ b/drivers/cxl/cxlmem.h > @@ -201,6 +201,7 @@ struct cxl_endpoint_dvsec_info { > * @dev: The device associated with this CXL state > * @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) > * @payload_size: Size of space for payload > * (CXL 2.0 8.2.8.4.3 Mailbox Capabilities Register) > * @lsa_size: Size of Label Storage Area > @@ -235,6 +236,7 @@ struct cxl_dev_state { > struct cxl_regs regs; > int cxl_dvsec; > > + bool rcd; > size_t payload_size; > size_t lsa_size; > struct mutex mbox_mutex; /* Protects device mailbox and firmware */ > diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c > index aa63ce8c7ca6..9a655b4b5e52 100644 > --- a/drivers/cxl/mem.c > +++ b/drivers/cxl/mem.c > @@ -45,12 +45,13 @@ static int cxl_mem_dpa_show(struct seq_file *file, void > *data) > return 0; > } > > -static int devm_cxl_add_endpoint(struct cxl_memdev *cxlmd, > +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; > + resource_size_t component_reg_phys; > int rc; > > /* > @@ -65,8 +66,18 @@ static int devm_cxl_add_endpoint(struct cxl_memdev *cxlmd, > ep->next = down; > } > > - endpoint = devm_cxl_add_port(&parent_port->dev, &cxlmd->dev, > - cxlds->component_reg_phys, parent_dport); > + /* > + * 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) > + component_reg_phys = cxl_rcrb_to_component( > + &cxlmd->dev, parent_dport->rcrb, CXL_RCRB_DOWNSTREAM); As already commented: this must be the upstream RCRB here. > + else > + component_reg_phys = cxlds->component_reg_phys; > + endpoint = devm_cxl_add_port(host, &cxlmd->dev, component_reg_phys, > + parent_dport); Looking at CXL 3.0 spec, table 8-22, there are the various sources of component registers listed. For RCD we need: D1, DP1, UP1 (optional R). D1: endpoint->component_reg_phys; UP1: parent_port-component_reg_phys; (missing in RCH topology) DP1: parent_dport->component_reg_phys; I don't see how all of them could be stored in this data layout as the cxl host port is missing. > if (IS_ERR(endpoint)) > return PTR_ERR(endpoint); > > @@ -87,6 +98,7 @@ static int cxl_mem_probe(struct device *dev) > { > struct cxl_memdev *cxlmd = to_cxl_memdev(dev); > struct cxl_dev_state *cxlds = cxlmd->cxlds; > + struct device *endpoint_parent; > struct cxl_port *parent_port; > struct cxl_dport *dport; > struct dentry *dentry; > @@ -119,17 +131,22 @@ static int cxl_mem_probe(struct device *dev) > return -ENXIO; > } > > - device_lock(&parent_port->dev); > - if (!parent_port->dev.driver) { > + if (dport->rch) > + endpoint_parent = parent_port->uport; > + else > + endpoint_parent = &parent_port->dev; > + > + device_lock(endpoint_parent); > + if (!endpoint_parent->driver) { > dev_err(dev, "CXL port topology %s not enabled\n", > dev_name(&parent_port->dev)); > rc = -ENXIO; > goto unlock; > } > > - rc = devm_cxl_add_endpoint(cxlmd, dport); > + rc = devm_cxl_add_endpoint(endpoint_parent, cxlmd, dport); > unlock: > - device_unlock(&parent_port->dev); > + device_unlock(endpoint_parent); > put_device(&parent_port->dev); > if (rc) > return rc; > diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c > index e15da405b948..73ff6c33a0c0 100644 > --- a/drivers/cxl/pci.c > +++ b/drivers/cxl/pci.c > @@ -433,6 +433,15 @@ static void devm_cxl_pci_create_doe(struct cxl_dev_state > *cxlds) > } > } > > +/* > + * 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_pci_probe(struct pci_dev *pdev, const struct pci_device_id > *id) > { > struct cxl_register_map map; > @@ -455,6 +464,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const > struct pci_device_id *id) > if (IS_ERR(cxlds)) > return PTR_ERR(cxlds); > > + cxlds->rcd = is_cxl_restricted(pdev); > cxlds->serial = pci_get_dsn(pdev); > cxlds->cxl_dvsec = pci_find_dvsec_capability( > pdev, PCI_DVSEC_VENDOR_ID_CXL, CXL_DVSEC_PCIE_DEVICE); >