@@ -211,8 +211,6 @@ static int add_host_bridge_uport(struct device *match, void *arg)
struct acpi_device *bridge = to_cxl_host_bridge(host, match);
struct acpi_pci_root *pci_root;
struct cxl_walk_context ctx;
- int single_port_map[1], rc;
- struct cxl_decoder *cxld;
struct cxl_dport *dport;
struct cxl_port *port;
@@ -246,38 +244,9 @@ static int add_host_bridge_uport(struct device *match, void *arg)
return -ENODEV;
if (ctx.error)
return ctx.error;
- if (ctx.count > 1)
- return 0;
- /* TODO: Scan CHBCR for HDM Decoder resources */
-
- /*
- * Per the CXL specification (8.2.5.12 CXL HDM Decoder Capability
- * Structure) single ported host-bridges need not publish a decoder
- * capability when a passthrough decode can be assumed, i.e. all
- * transactions that the uport sees are claimed and passed to the single
- * dport. Disable the range until the first CXL region is enumerated /
- * activated.
- */
- cxld = cxl_root_decoder_alloc(port, 1);
- if (IS_ERR(cxld))
- return PTR_ERR(cxld);
-
- device_lock(&port->dev);
- dport = list_first_entry(&port->dports, typeof(*dport), list);
- device_unlock(&port->dev);
-
- single_port_map[0] = dport->port_id;
-
- rc = cxl_decoder_add(cxld, single_port_map);
- if (rc)
- put_device(&cxld->dev);
- else
- rc = cxl_decoder_autoremove(host, cxld);
-
- if (rc == 0)
- dev_dbg(host, "add: %s\n", dev_name(&cxld->dev));
- return rc;
+ /* Host bridge ports are enumerated by the port driver. */
+ return 0;
}
struct cxl_chbs_context {
@@ -444,3 +413,4 @@ module_platform_driver(cxl_acpi_driver);
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(CXL);
MODULE_IMPORT_NS(ACPI);
+MODULE_SOFTDEP("pre: cxl_port");
@@ -62,7 +62,7 @@ void cxl_unregister_topology_host(struct device *host)
}
EXPORT_SYMBOL_NS_GPL(cxl_unregister_topology_host, CXL);
-static struct device *get_cxl_topology_host(void)
+struct device *get_cxl_topology_host(void)
{
down_read(&topology_host_sem);
if (cxl_topology_host)
@@ -70,12 +70,14 @@ static struct device *get_cxl_topology_host(void)
up_read(&topology_host_sem);
return NULL;
}
+EXPORT_SYMBOL_NS_GPL(get_cxl_topology_host, CXL);
-static void put_cxl_topology_host(struct device *dev)
+void put_cxl_topology_host(struct device *dev)
{
WARN_ON(dev != cxl_topology_host);
up_read(&topology_host_sem);
}
+EXPORT_SYMBOL_NS_GPL(put_cxl_topology_host, CXL);
static int decoder_match(struct device *dev, void *data)
{
@@ -170,6 +170,8 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
int cxl_register_topology_host(struct device *host);
void cxl_unregister_topology_host(struct device *host);
+struct device *get_cxl_topology_host(void);
+void put_cxl_topology_host(struct device *dev);
/*
* cxl_decoder flags that define the type of memory / devices this
@@ -221,12 +221,59 @@ static int enumerate_switch_decoders(struct cxl_port *port,
return 0;
}
+/*
+ * Per the CXL specification (8.2.5.12 CXL HDM Decoder Capability Structure)
+ * single ported host-bridges need not publish a decoder capability when a
+ * passthrough decode can be assumed, i.e. all transactions that the uport sees
+ * are claimed and passed to the single dport. Disable the range until the first
+ * CXL region is enumerated / activated.
+ */
+static int add_passthrough_decoder(struct cxl_port *port)
+{
+ int single_port_map[1], rc;
+ struct cxl_decoder *cxld;
+ struct cxl_dport *dport;
+
+ device_lock_assert(&port->dev);
+
+ cxld = cxl_switch_decoder_alloc(port, 1);
+ if (IS_ERR(cxld))
+ return PTR_ERR(cxld);
+
+ dport = list_first_entry(&port->dports, typeof(*dport), list);
+ single_port_map[0] = dport->port_id;
+
+ rc = cxl_decoder_add_locked(cxld, single_port_map);
+ if (rc)
+ put_device(&cxld->dev);
+ else
+ rc = cxl_decoder_autoremove(&port->dev, cxld);
+
+ if (rc == 0)
+ dev_dbg(&port->dev, "add: %s\n", dev_name(&cxld->dev));
+
+ return rc;
+}
+
static int cxl_port_probe(struct device *dev)
{
struct cxl_port *port = to_cxl_port(dev);
struct cxl_port_data *portdata;
void __iomem *crb;
- int rc;
+ int rc = 0;
+
+ if (list_is_singular(&port->dports)) {
+ struct device *host_dev = get_cxl_topology_host();
+
+ /*
+ * Root ports (single host bridge downstream) are handled by
+ * platform driver
+ */
+ if (port->uport != host_dev)
+ rc = add_passthrough_decoder(port);
+ put_cxl_topology_host(host_dev);
+ return rc;
+ }
/*
* All ports should have component registers except for the platform