diff options
Diffstat (limited to 'drivers/cxl/acpi.c')
| -rw-r--r-- | drivers/cxl/acpi.c | 237 | 
1 files changed, 90 insertions, 147 deletions
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c index dadc7f64b9ff..3163167ecc3a 100644 --- a/drivers/cxl/acpi.c +++ b/drivers/cxl/acpi.c @@ -8,8 +8,6 @@  #include <linux/pci.h>  #include "cxl.h" -static struct acpi_table_header *acpi_cedt; -  /* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */  #define CFMWS_INTERLEAVE_WAYS(x)	(1 << (x)->interleave_ways)  #define CFMWS_INTERLEAVE_GRANULARITY(x)	((x)->granularity + 8) @@ -74,134 +72,64 @@ static int cxl_acpi_cfmws_verify(struct device *dev,  	return 0;  } -static void cxl_add_cfmws_decoders(struct device *dev, -				   struct cxl_port *root_port) +struct cxl_cfmws_context { +	struct device *dev; +	struct cxl_port *root_port; +}; + +static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg, +			   const unsigned long end)  {  	int target_map[CXL_DECODER_MAX_INTERLEAVE]; +	struct cxl_cfmws_context *ctx = arg; +	struct cxl_port *root_port = ctx->root_port; +	struct device *dev = ctx->dev;  	struct acpi_cedt_cfmws *cfmws;  	struct cxl_decoder *cxld; -	acpi_size len, cur = 0; -	void *cedt_subtable; -	int rc; - -	len = acpi_cedt->length - sizeof(*acpi_cedt); -	cedt_subtable = acpi_cedt + 1; - -	while (cur < len) { -		struct acpi_cedt_header *c = cedt_subtable + cur; -		int i; - -		if (c->type != ACPI_CEDT_TYPE_CFMWS) { -			cur += c->length; -			continue; -		} +	int rc, i; -		cfmws = cedt_subtable + cur; +	cfmws = (struct acpi_cedt_cfmws *) header; -		if (cfmws->header.length < sizeof(*cfmws)) { -			dev_warn_once(dev, -				      "CFMWS entry skipped:invalid length:%u\n", -				      cfmws->header.length); -			cur += c->length; -			continue; -		} - -		rc = cxl_acpi_cfmws_verify(dev, cfmws); -		if (rc) { -			dev_err(dev, "CFMWS range %#llx-%#llx not registered\n", -				cfmws->base_hpa, cfmws->base_hpa + -				cfmws->window_size - 1); -			cur += c->length; -			continue; -		} - -		for (i = 0; i < CFMWS_INTERLEAVE_WAYS(cfmws); i++) -			target_map[i] = cfmws->interleave_targets[i]; - -		cxld = cxl_decoder_alloc(root_port, -					 CFMWS_INTERLEAVE_WAYS(cfmws)); -		if (IS_ERR(cxld)) -			goto next; - -		cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions); -		cxld->target_type = CXL_DECODER_EXPANDER; -		cxld->range = (struct range) { -			.start = cfmws->base_hpa, -			.end = cfmws->base_hpa + cfmws->window_size - 1, -		}; -		cxld->interleave_ways = CFMWS_INTERLEAVE_WAYS(cfmws); -		cxld->interleave_granularity = -			CFMWS_INTERLEAVE_GRANULARITY(cfmws); - -		rc = cxl_decoder_add(cxld, target_map); -		if (rc) -			put_device(&cxld->dev); -		else -			rc = cxl_decoder_autoremove(dev, cxld); -		if (rc) { -			dev_err(dev, "Failed to add decoder for %#llx-%#llx\n", -				cfmws->base_hpa, cfmws->base_hpa + -				cfmws->window_size - 1); -			goto next; -		} -		dev_dbg(dev, "add: %s range %#llx-%#llx\n", -			dev_name(&cxld->dev), cfmws->base_hpa, +	rc = cxl_acpi_cfmws_verify(dev, cfmws); +	if (rc) { +		dev_err(dev, "CFMWS range %#llx-%#llx not registered\n", +			cfmws->base_hpa,  			cfmws->base_hpa + cfmws->window_size - 1); -next: -		cur += c->length; +		return 0;  	} -} - -static struct acpi_cedt_chbs *cxl_acpi_match_chbs(struct device *dev, u32 uid) -{ -	struct acpi_cedt_chbs *chbs, *chbs_match = NULL; -	acpi_size len, cur = 0; -	void *cedt_subtable; -	len = acpi_cedt->length - sizeof(*acpi_cedt); -	cedt_subtable = acpi_cedt + 1; +	for (i = 0; i < CFMWS_INTERLEAVE_WAYS(cfmws); i++) +		target_map[i] = cfmws->interleave_targets[i]; -	while (cur < len) { -		struct acpi_cedt_header *c = cedt_subtable + cur; - -		if (c->type != ACPI_CEDT_TYPE_CHBS) { -			cur += c->length; -			continue; -		} - -		chbs = cedt_subtable + cur; - -		if (chbs->header.length < sizeof(*chbs)) { -			dev_warn_once(dev, -				      "CHBS entry skipped: invalid length:%u\n", -				      chbs->header.length); -			cur += c->length; -			continue; -		} - -		if (chbs->uid != uid) { -			cur += c->length; -			continue; -		} +	cxld = cxl_decoder_alloc(root_port, CFMWS_INTERLEAVE_WAYS(cfmws)); +	if (IS_ERR(cxld)) +		return 0; -		if (chbs_match) { -			dev_warn_once(dev, -				      "CHBS entry skipped: duplicate UID:%u\n", -				      uid); -			cur += c->length; -			continue; -		} +	cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions); +	cxld->target_type = CXL_DECODER_EXPANDER; +	cxld->range = (struct range){ +		.start = cfmws->base_hpa, +		.end = cfmws->base_hpa + cfmws->window_size - 1, +	}; +	cxld->interleave_ways = CFMWS_INTERLEAVE_WAYS(cfmws); +	cxld->interleave_granularity = CFMWS_INTERLEAVE_GRANULARITY(cfmws); -		chbs_match = chbs; -		cur += c->length; +	rc = cxl_decoder_add(cxld, target_map); +	if (rc) +		put_device(&cxld->dev); +	else +		rc = cxl_decoder_autoremove(dev, cxld); +	if (rc) { +		dev_err(dev, "Failed to add decoder for %#llx-%#llx\n", +			cfmws->base_hpa, +			cfmws->base_hpa + cfmws->window_size - 1); +		return 0;  	} +	dev_dbg(dev, "add: %s node: %d range %#llx-%#llx\n", +		dev_name(&cxld->dev), phys_to_target_node(cxld->range.start), +		cfmws->base_hpa, cfmws->base_hpa + cfmws->window_size - 1); -	return chbs_match ? chbs_match : ERR_PTR(-ENODEV); -} - -static resource_size_t get_chbcr(struct acpi_cedt_chbs *chbs) -{ -	return IS_ERR(chbs) ? CXL_RESOURCE_NONE : chbs->base; +	return 0;  }  __mock int match_add_root_ports(struct pci_dev *pdev, void *data) @@ -355,12 +283,36 @@ static int add_host_bridge_uport(struct device *match, void *arg)  	return rc;  } +struct cxl_chbs_context { +	struct device *dev; +	unsigned long long uid; +	resource_size_t chbcr; +}; + +static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg, +			 const unsigned long end) +{ +	struct cxl_chbs_context *ctx = arg; +	struct acpi_cedt_chbs *chbs; + +	if (ctx->chbcr) +		return 0; + +	chbs = (struct acpi_cedt_chbs *) header; + +	if (ctx->uid != chbs->uid) +		return 0; +	ctx->chbcr = chbs->base; + +	return 0; +} +  static int add_host_bridge_dport(struct device *match, void *arg)  {  	int rc;  	acpi_status status;  	unsigned long long uid; -	struct acpi_cedt_chbs *chbs; +	struct cxl_chbs_context ctx;  	struct cxl_port *root_port = arg;  	struct device *host = root_port->dev.parent;  	struct acpi_device *bridge = to_cxl_host_bridge(host, match); @@ -376,14 +328,19 @@ static int add_host_bridge_dport(struct device *match, void *arg)  		return -ENODEV;  	} -	chbs = cxl_acpi_match_chbs(host, uid); -	if (IS_ERR(chbs)) { +	ctx = (struct cxl_chbs_context) { +		.dev = host, +		.uid = uid, +	}; +	acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbcr, &ctx); + +	if (ctx.chbcr == 0) {  		dev_warn(host, "No CHBS found for Host Bridge: %s\n",  			 dev_name(match));  		return 0;  	} -	rc = cxl_add_dport(root_port, match, uid, get_chbcr(chbs)); +	rc = cxl_add_dport(root_port, match, uid, ctx.chbcr);  	if (rc) {  		dev_err(host, "failed to add downstream port: %s\n",  			dev_name(match)); @@ -417,40 +374,29 @@ static int add_root_nvdimm_bridge(struct device *match, void *data)  	return 1;  } -static u32 cedt_instance(struct platform_device *pdev) -{ -	const bool *native_acpi0017 = acpi_device_get_match_data(&pdev->dev); - -	if (native_acpi0017 && *native_acpi0017) -		return 0; - -	/* for cxl_test request a non-canonical instance */ -	return U32_MAX; -} -  static int cxl_acpi_probe(struct platform_device *pdev)  {  	int rc; -	acpi_status status;  	struct cxl_port *root_port;  	struct device *host = &pdev->dev;  	struct acpi_device *adev = ACPI_COMPANION(host); +	struct cxl_cfmws_context ctx;  	root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL);  	if (IS_ERR(root_port))  		return PTR_ERR(root_port);  	dev_dbg(host, "add: %s\n", dev_name(&root_port->dev)); -	status = acpi_get_table(ACPI_SIG_CEDT, cedt_instance(pdev), &acpi_cedt); -	if (ACPI_FAILURE(status)) -		return -ENXIO; -  	rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,  			      add_host_bridge_dport); -	if (rc) -		goto out; +	if (rc < 0) +		return rc; -	cxl_add_cfmws_decoders(host, root_port); +	ctx = (struct cxl_cfmws_context) { +		.dev = host, +		.root_port = root_port, +	}; +	acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, cxl_parse_cfmws, &ctx);  	/*  	 * Root level scanned with host-bridge as dports, now scan host-bridges @@ -458,24 +404,20 @@ static int cxl_acpi_probe(struct platform_device *pdev)  	 */  	rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,  			      add_host_bridge_uport); -	if (rc) -		goto out; +	if (rc < 0) +		return rc;  	if (IS_ENABLED(CONFIG_CXL_PMEM))  		rc = device_for_each_child(&root_port->dev, root_port,  					   add_root_nvdimm_bridge); - -out: -	acpi_put_table(acpi_cedt);  	if (rc < 0)  		return rc; +  	return 0;  } -static bool native_acpi0017 = true; -  static const struct acpi_device_id cxl_acpi_ids[] = { -	{ "ACPI0017", (unsigned long) &native_acpi0017 }, +	{ "ACPI0017" },  	{ },  };  MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids); @@ -491,3 +433,4 @@ static struct platform_driver cxl_acpi_driver = {  module_platform_driver(cxl_acpi_driver);  MODULE_LICENSE("GPL v2");  MODULE_IMPORT_NS(CXL); +MODULE_IMPORT_NS(ACPI);  |