Commit 46037455 authored by Dave Jiang's avatar Dave Jiang
Browse files

Merge branch 'for-6.18/cxl-delay-dport' into cxl-for-next

Add changes to delay the allocation and setup of dports until when the
endpoint device is being probed. At this point, the CXL link is
established from endpoint to host bridge. Addresses issues seen on
some platforms when dports are probed earlier.

Link: https://lore.kernel.org/linux-cxl/20250829180928.842707-1-dave.jiang@intel.com/
parents c5dca386 f6ee2491
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -401,7 +401,6 @@ DEFINE_FREE(del_cxl_resource, struct resource *, if (_T) del_cxl_resource(_T))
static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws,
			     struct cxl_cfmws_context *ctx)
{
	int target_map[CXL_DECODER_MAX_INTERLEAVE];
	struct cxl_port *root_port = ctx->root_port;
	struct cxl_cxims_context cxims_ctx;
	struct device *dev = ctx->dev;
@@ -419,8 +418,6 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws,
	rc = eig_to_granularity(cfmws->granularity, &ig);
	if (rc)
		return rc;
	for (i = 0; i < ways; i++)
		target_map[i] = cfmws->interleave_targets[i];

	struct resource *res __free(del_cxl_resource) = alloc_cxl_resource(
		cfmws->base_hpa, cfmws->window_size, ctx->id++);
@@ -446,6 +443,8 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws,
		.end = cfmws->base_hpa + cfmws->window_size - 1,
	};
	cxld->interleave_ways = ways;
	for (i = 0; i < ways; i++)
		cxld->target_map[i] = cfmws->interleave_targets[i];
	/*
	 * Minimize the x1 granularity to advertise support for any
	 * valid region granularity
@@ -484,7 +483,7 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws,
		cxlrd->ops->spa_to_hpa = cxl_apply_xor_maps;
	}

	rc = cxl_decoder_add(cxld, target_map);
	rc = cxl_decoder_add(cxld);
	if (rc)
		return rc;

+11 −14
Original line number Diff line number Diff line
@@ -338,7 +338,7 @@ static int match_cxlrd_hb(struct device *dev, void *data)

	guard(rwsem_read)(&cxl_rwsem.region);
	for (int i = 0; i < cxlsd->nr_targets; i++) {
		if (host_bridge == cxlsd->target[i]->dport_dev)
		if (cxlsd->target[i] && host_bridge == cxlsd->target[i]->dport_dev)
			return 1;
	}

@@ -440,8 +440,8 @@ static int cdat_sslbis_handler(union acpi_subtable_headers *header, void *arg,
	} *tbl = (struct acpi_cdat_sslbis_table *)header;
	int size = sizeof(header->cdat) + sizeof(tbl->sslbis_header);
	struct acpi_cdat_sslbis *sslbis;
	struct cxl_port *port = arg;
	struct device *dev = &port->dev;
	struct cxl_dport *dport = arg;
	struct device *dev = &dport->port->dev;
	int remain, entries, i;
	u16 len;

@@ -467,8 +467,6 @@ static int cdat_sslbis_handler(union acpi_subtable_headers *header, void *arg,
		u16 y = le16_to_cpu((__force __le16)tbl->entries[i].porty_id);
		__le64 le_base;
		__le16 le_val;
		struct cxl_dport *dport;
		unsigned long index;
		u16 dsp_id;
		u64 val;

@@ -499,28 +497,27 @@ static int cdat_sslbis_handler(union acpi_subtable_headers *header, void *arg,
		val = cdat_normalize(le16_to_cpu(le_val), le64_to_cpu(le_base),
				     sslbis->data_type);

		xa_for_each(&port->dports, index, dport) {
		if (dsp_id == ACPI_CDAT_SSLBIS_ANY_PORT ||
		    dsp_id == dport->port_id) {
			cxl_access_coordinate_set(dport->coord,
							  sslbis->data_type,
							  val);
			}
						  sslbis->data_type, val);
			return 0;
		}
	}

	return 0;
}

void cxl_switch_parse_cdat(struct cxl_port *port)
void cxl_switch_parse_cdat(struct cxl_dport *dport)
{
	struct cxl_port *port = dport->port;
	int rc;

	if (!port->cdat.table)
		return;

	rc = cdat_table_parse(ACPI_CDAT_TYPE_SSLBIS, cdat_sslbis_handler,
			      port, port->cdat.table, port->cdat.length);
			      dport, port->cdat.table, port->cdat.length);
	rc = cdat_table_parse_output(rc);
	if (rc)
		dev_dbg(&port->dev, "Failed to parse SSLBIS: %d\n", rc);
+5 −0
Original line number Diff line number Diff line
@@ -148,6 +148,11 @@ int cxl_ras_init(void);
void cxl_ras_exit(void);
int cxl_gpf_port_setup(struct cxl_dport *dport);

struct cxl_hdm;
int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
			struct cxl_endpoint_dvsec_info *info);
int cxl_port_get_possible_dports(struct cxl_port *port);

#ifdef CONFIG_CXL_FEATURES
struct cxl_feat_entry *
cxl_feature_info(struct cxl_features_state *cxlfs, const uuid_t *uuid);
+80 −25
Original line number Diff line number Diff line
@@ -21,12 +21,11 @@ struct cxl_rwsem cxl_rwsem = {
	.dpa = __RWSEM_INITIALIZER(cxl_rwsem.dpa),
};

static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
			   int *target_map)
static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld)
{
	int rc;

	rc = cxl_decoder_add_locked(cxld, target_map);
	rc = cxl_decoder_add_locked(cxld);
	if (rc) {
		put_device(&cxld->dev);
		dev_err(&port->dev, "Failed to add decoder\n");
@@ -50,12 +49,9 @@ static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
 * are claimed and passed to the single dport. Disable the range until the first
 * CXL region is enumerated / activated.
 */
int devm_cxl_add_passthrough_decoder(struct cxl_port *port)
static int devm_cxl_add_passthrough_decoder(struct cxl_port *port)
{
	struct cxl_switch_decoder *cxlsd;
	struct cxl_dport *dport = NULL;
	int single_port_map[1];
	unsigned long index;
	struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);

	/*
@@ -71,13 +67,8 @@ int devm_cxl_add_passthrough_decoder(struct cxl_port *port)

	device_lock_assert(&port->dev);

	xa_for_each(&port->dports, index, dport)
		break;
	single_port_map[0] = dport->port_id;

	return add_hdm_decoder(port, &cxlsd->cxld, single_port_map);
	return add_hdm_decoder(port, &cxlsd->cxld);
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_passthrough_decoder, "CXL");

static void parse_hdm_decoder_caps(struct cxl_hdm *cxlhdm)
{
@@ -147,7 +138,7 @@ static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
 * @port: cxl_port to map
 * @info: cached DVSEC range register info
 */
struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
static struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
					  struct cxl_endpoint_dvsec_info *info)
{
	struct cxl_register_map *reg_map = &port->reg_map;
@@ -203,7 +194,6 @@ struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,

	return cxlhdm;
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_setup_hdm, "CXL");

static void __cxl_dpa_debug(struct seq_file *file, struct resource *r, int depth)
{
@@ -984,7 +974,7 @@ static int cxl_setup_hdm_decoder_from_dvsec(
}

static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
			    int *target_map, void __iomem *hdm, int which,
			    void __iomem *hdm, int which,
			    u64 *dpa_base, struct cxl_endpoint_dvsec_info *info)
{
	struct cxl_endpoint_decoder *cxled = NULL;
@@ -1103,7 +1093,7 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
		hi = readl(hdm + CXL_HDM_DECODER0_TL_HIGH(which));
		target_list.value = (hi << 32) + lo;
		for (i = 0; i < cxld->interleave_ways; i++)
			target_map[i] = target_list.target_id[i];
			cxld->target_map[i] = target_list.target_id[i];

		return 0;
	}
@@ -1168,7 +1158,7 @@ static void cxl_settle_decoders(struct cxl_hdm *cxlhdm)
 * @cxlhdm: Structure to populate with HDM capabilities
 * @info: cached DVSEC range register info
 */
int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
static int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
				       struct cxl_endpoint_dvsec_info *info)
{
	void __iomem *hdm = cxlhdm->regs.hdm_decoder;
@@ -1179,7 +1169,6 @@ int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
	cxl_settle_decoders(cxlhdm);

	for (i = 0; i < cxlhdm->decoder_count; i++) {
		int target_map[CXL_DECODER_MAX_INTERLEAVE] = { 0 };
		int rc, target_count = cxlhdm->target_count;
		struct cxl_decoder *cxld;

@@ -1207,8 +1196,7 @@ int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
			cxld = &cxlsd->cxld;
		}

		rc = init_hdm_decoder(port, cxld, target_map, hdm, i,
				      &dpa_base, info);
		rc = init_hdm_decoder(port, cxld, hdm, i, &dpa_base, info);
		if (rc) {
			dev_warn(&port->dev,
				 "Failed to initialize decoder%d.%d\n",
@@ -1216,7 +1204,7 @@ int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
			put_device(&cxld->dev);
			return rc;
		}
		rc = add_hdm_decoder(port, cxld, target_map);
		rc = add_hdm_decoder(port, cxld);
		if (rc) {
			dev_warn(&port->dev,
				 "Failed to add decoder%d.%d\n", port->id, i);
@@ -1226,4 +1214,71 @@ int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,

	return 0;
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_decoders, "CXL");

/**
 * __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)
{
	struct cxl_hdm *cxlhdm;

	if (is_cxl_root(port) || is_cxl_endpoint(port))
		return -EOPNOTSUPP;

	cxlhdm = devm_cxl_setup_hdm(port, NULL);
	if (!IS_ERR(cxlhdm))
		return devm_cxl_enumerate_decoders(cxlhdm, NULL);

	if (PTR_ERR(cxlhdm) != -ENODEV) {
		dev_err(&port->dev, "Failed to map HDM decoder capability\n");
		return PTR_ERR(cxlhdm);
	}

	if (cxl_port_get_possible_dports(port) == 1) {
		dev_dbg(&port->dev, "Fallback to passthrough decoder\n");
		return devm_cxl_add_passthrough_decoder(port);
	}

	dev_err(&port->dev, "HDM decoder capability not found\n");
	return -ENXIO;
}
EXPORT_SYMBOL_NS_GPL(__devm_cxl_switch_port_decoders_setup, "CXL");

/**
 * devm_cxl_endpoint_decoders_setup - allocate and setup endpoint decoders
 * @port: CXL port context
 *
 * Return 0 or -errno on error
 */
int devm_cxl_endpoint_decoders_setup(struct cxl_port *port)
{
	struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev);
	struct cxl_endpoint_dvsec_info info = { .port = port };
	struct cxl_dev_state *cxlds = cxlmd->cxlds;
	struct cxl_hdm *cxlhdm;
	int rc;

	if (!is_cxl_endpoint(port))
		return -EOPNOTSUPP;

	rc = cxl_dvsec_rr_decode(cxlds, &info);
	if (rc < 0)
		return rc;

	cxlhdm = devm_cxl_setup_hdm(port, &info);
	if (IS_ERR(cxlhdm)) {
		if (PTR_ERR(cxlhdm) == -ENODEV)
			dev_err(&port->dev, "HDM decoder registers not found\n");
		return PTR_ERR(cxlhdm);
	}

	rc = cxl_hdm_decode_init(cxlds, cxlhdm, &info);
	if (rc)
		return rc;

	return devm_cxl_enumerate_decoders(cxlhdm, &info);
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_endpoint_decoders_setup, "CXL");
+89 −0
Original line number Diff line number Diff line
@@ -24,6 +24,53 @@ static unsigned short media_ready_timeout = 60;
module_param(media_ready_timeout, ushort, 0644);
MODULE_PARM_DESC(media_ready_timeout, "seconds to wait for media ready");

static int pci_get_port_num(struct pci_dev *pdev)
{
	u32 lnkcap;
	int type;

	type = pci_pcie_type(pdev);
	if (type != PCI_EXP_TYPE_DOWNSTREAM && type != PCI_EXP_TYPE_ROOT_PORT)
		return -EINVAL;

	if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP,
				  &lnkcap))
		return -ENXIO;

	return FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
}

/**
 * __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_register_map map;
	struct pci_dev *pdev;
	int port_num, rc;

	if (!dev_is_pci(dport_dev))
		return ERR_PTR(-EINVAL);

	pdev = to_pci_dev(dport_dev);
	port_num = pci_get_port_num(pdev);
	if (port_num < 0)
		return ERR_PTR(port_num);

	rc = cxl_find_regblock(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
	if (rc)
		return ERR_PTR(rc);

	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");

struct cxl_walk_context {
	struct pci_bus *bus;
	struct cxl_port *port;
@@ -1169,3 +1216,45 @@ int cxl_gpf_port_setup(struct cxl_dport *dport)

	return 0;
}

static int count_dports(struct pci_dev *pdev, void *data)
{
	struct cxl_walk_context *ctx = data;
	int type = pci_pcie_type(pdev);

	if (pdev->bus != ctx->bus)
		return 0;
	if (!pci_is_pcie(pdev))
		return 0;
	if (type != ctx->type)
		return 0;

	ctx->count++;
	return 0;
}

int cxl_port_get_possible_dports(struct cxl_port *port)
{
	struct pci_bus *bus = cxl_port_to_pci_bus(port);
	struct cxl_walk_context ctx;
	int type;

	if (!bus) {
		dev_err(&port->dev, "No PCI bus found for port %s\n",
			dev_name(&port->dev));
		return -ENXIO;
	}

	if (pci_is_root_bus(bus))
		type = PCI_EXP_TYPE_ROOT_PORT;
	else
		type = PCI_EXP_TYPE_DOWNSTREAM;

	ctx = (struct cxl_walk_context) {
		.bus = bus,
		.type = type,
	};
	pci_walk_bus(bus, count_dports, &ctx);

	return ctx.count;
}
Loading