Commit f4ce1f76 authored by Dan Williams's avatar Dan Williams

cxl/acpi: Convert CFMWS parsing to ACPI sub-table helpers

The cxl_acpi driver originally open-coded its table parsing since the
ACPI subtable helpers were marked __init and only used in early NUMA
initialization.  Now that those helpers have been exported for driver
usage replace the open-coded solution with the common one.

Cc: Alison Schofield <alison.schofield@intel.com>
Acked-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://lore.kernel.org/r/163553710810.2509508.14686373989517930921.stgit@dwillia2-desk3.amr.corp.intel.comSigned-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent 2d03e46a
...@@ -51,6 +51,7 @@ config CXL_ACPI ...@@ -51,6 +51,7 @@ config CXL_ACPI
tristate "CXL ACPI: Platform Support" tristate "CXL ACPI: Platform Support"
depends on ACPI depends on ACPI
default CXL_BUS default CXL_BUS
select ACPI_TABLE_LIB
help help
Enable support for host managed device memory (HDM) resources Enable support for host managed device memory (HDM) resources
published by a platform's ACPI CXL memory layout description. See published by a platform's ACPI CXL memory layout description. See
......
...@@ -8,8 +8,6 @@ ...@@ -8,8 +8,6 @@
#include <linux/pci.h> #include <linux/pci.h>
#include "cxl.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 */ /* 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_WAYS(x) (1 << (x)->interleave_ways)
#define CFMWS_INTERLEAVE_GRANULARITY(x) ((x)->granularity + 8) #define CFMWS_INTERLEAVE_GRANULARITY(x) ((x)->granularity + 8)
...@@ -74,134 +72,63 @@ static int cxl_acpi_cfmws_verify(struct device *dev, ...@@ -74,134 +72,63 @@ static int cxl_acpi_cfmws_verify(struct device *dev,
return 0; return 0;
} }
static void cxl_add_cfmws_decoders(struct device *dev, struct cxl_cfmws_context {
struct cxl_port *root_port) 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]; 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 acpi_cedt_cfmws *cfmws;
struct cxl_decoder *cxld; struct cxl_decoder *cxld;
acpi_size len, cur = 0; int rc, i;
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;
}
cfmws = cedt_subtable + cur; cfmws = (struct acpi_cedt_cfmws *) header;
if (cfmws->header.length < sizeof(*cfmws)) { rc = cxl_acpi_cfmws_verify(dev, cfmws);
dev_warn_once(dev, if (rc) {
"CFMWS entry skipped:invalid length:%u\n", dev_err(dev, "CFMWS range %#llx-%#llx not registered\n",
cfmws->header.length); cfmws->base_hpa,
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,
cfmws->base_hpa + cfmws->window_size - 1); cfmws->base_hpa + cfmws->window_size - 1);
next: return 0;
cur += c->length;
} }
}
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); for (i = 0; i < CFMWS_INTERLEAVE_WAYS(cfmws); i++)
cedt_subtable = acpi_cedt + 1; target_map[i] = cfmws->interleave_targets[i];
while (cur < len) { cxld = cxl_decoder_alloc(root_port, CFMWS_INTERLEAVE_WAYS(cfmws));
struct acpi_cedt_header *c = cedt_subtable + cur; if (IS_ERR(cxld))
return 0;
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;
}
if (chbs_match) { cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
dev_warn_once(dev, cxld->target_type = CXL_DECODER_EXPANDER;
"CHBS entry skipped: duplicate UID:%u\n", cxld->range = (struct range){
uid); .start = cfmws->base_hpa,
cur += c->length; .end = cfmws->base_hpa + cfmws->window_size - 1,
continue; };
} cxld->interleave_ways = CFMWS_INTERLEAVE_WAYS(cfmws);
cxld->interleave_granularity = CFMWS_INTERLEAVE_GRANULARITY(cfmws);
chbs_match = chbs; rc = cxl_decoder_add(cxld, target_map);
cur += c->length; 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 range %#llx-%#llx\n", dev_name(&cxld->dev),
cfmws->base_hpa, cfmws->base_hpa + cfmws->window_size - 1);
return chbs_match ? chbs_match : ERR_PTR(-ENODEV); return 0;
}
static resource_size_t get_chbcr(struct acpi_cedt_chbs *chbs)
{
return IS_ERR(chbs) ? CXL_RESOURCE_NONE : chbs->base;
} }
__mock int match_add_root_ports(struct pci_dev *pdev, void *data) __mock int match_add_root_ports(struct pci_dev *pdev, void *data)
...@@ -355,12 +282,35 @@ static int add_host_bridge_uport(struct device *match, void *arg) ...@@ -355,12 +282,35 @@ static int add_host_bridge_uport(struct device *match, void *arg)
return rc; return rc;
} }
struct cxl_chbs_context {
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) static int add_host_bridge_dport(struct device *match, void *arg)
{ {
int rc; int rc;
acpi_status status; acpi_status status;
unsigned long long uid; unsigned long long uid;
struct acpi_cedt_chbs *chbs; struct cxl_chbs_context ctx;
struct cxl_port *root_port = arg; struct cxl_port *root_port = arg;
struct device *host = root_port->dev.parent; struct device *host = root_port->dev.parent;
struct acpi_device *bridge = to_cxl_host_bridge(host, match); struct acpi_device *bridge = to_cxl_host_bridge(host, match);
...@@ -376,14 +326,18 @@ static int add_host_bridge_dport(struct device *match, void *arg) ...@@ -376,14 +326,18 @@ static int add_host_bridge_dport(struct device *match, void *arg)
return -ENODEV; return -ENODEV;
} }
chbs = cxl_acpi_match_chbs(host, uid); ctx = (struct cxl_chbs_context) {
if (IS_ERR(chbs)) { .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_warn(host, "No CHBS found for Host Bridge: %s\n",
dev_name(match)); dev_name(match));
return 0; 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) { if (rc) {
dev_err(host, "failed to add downstream port: %s\n", dev_err(host, "failed to add downstream port: %s\n",
dev_name(match)); dev_name(match));
...@@ -417,40 +371,29 @@ static int add_root_nvdimm_bridge(struct device *match, void *data) ...@@ -417,40 +371,29 @@ static int add_root_nvdimm_bridge(struct device *match, void *data)
return 1; 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) static int cxl_acpi_probe(struct platform_device *pdev)
{ {
int rc; int rc;
acpi_status status;
struct cxl_port *root_port; struct cxl_port *root_port;
struct device *host = &pdev->dev; struct device *host = &pdev->dev;
struct acpi_device *adev = ACPI_COMPANION(host); struct acpi_device *adev = ACPI_COMPANION(host);
struct cxl_cfmws_context ctx;
root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL); root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL);
if (IS_ERR(root_port)) if (IS_ERR(root_port))
return PTR_ERR(root_port); return PTR_ERR(root_port);
dev_dbg(host, "add: %s\n", dev_name(&root_port->dev)); 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, rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
add_host_bridge_dport); add_host_bridge_dport);
if (rc) if (rc < 0)
goto out; 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 * Root level scanned with host-bridge as dports, now scan host-bridges
...@@ -458,24 +401,20 @@ static int cxl_acpi_probe(struct platform_device *pdev) ...@@ -458,24 +401,20 @@ static int cxl_acpi_probe(struct platform_device *pdev)
*/ */
rc = bus_for_each_dev(adev->dev.bus, NULL, root_port, rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
add_host_bridge_uport); add_host_bridge_uport);
if (rc) if (rc < 0)
goto out; return rc;
if (IS_ENABLED(CONFIG_CXL_PMEM)) if (IS_ENABLED(CONFIG_CXL_PMEM))
rc = device_for_each_child(&root_port->dev, root_port, rc = device_for_each_child(&root_port->dev, root_port,
add_root_nvdimm_bridge); add_root_nvdimm_bridge);
out:
acpi_put_table(acpi_cedt);
if (rc < 0) if (rc < 0)
return rc; return rc;
return 0; return 0;
} }
static bool native_acpi0017 = true;
static const struct acpi_device_id cxl_acpi_ids[] = { static const struct acpi_device_id cxl_acpi_ids[] = {
{ "ACPI0017", (unsigned long) &native_acpi0017 }, { "ACPI0017" },
{ }, { },
}; };
MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids); MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids);
...@@ -491,3 +430,4 @@ static struct platform_driver cxl_acpi_driver = { ...@@ -491,3 +430,4 @@ static struct platform_driver cxl_acpi_driver = {
module_platform_driver(cxl_acpi_driver); module_platform_driver(cxl_acpi_driver);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(CXL); MODULE_IMPORT_NS(CXL);
MODULE_IMPORT_NS(ACPI);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment