Skip to content

Commit

Permalink
cxl/acpi: Convert CFMWS parsing to ACPI sub-table helpers
Browse files Browse the repository at this point in the history
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: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://lore.kernel.org/r/163553710810.2509508.14686373989517930921.stgit@dwillia2-desk3.amr.corp.intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
  • Loading branch information
djbw committed Nov 15, 2021
1 parent 2d03e46 commit f4ce1f7
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 147 deletions.
1 change: 1 addition & 0 deletions drivers/cxl/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ config CXL_ACPI
tristate "CXL ACPI: Platform Support"
depends on ACPI
default CXL_BUS
select ACPI_TABLE_LIB
help
Enable support for host managed device memory (HDM) resources
published by a platform's ACPI CXL memory layout description. See
Expand Down
234 changes: 87 additions & 147 deletions drivers/cxl/acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -74,134 +72,63 @@ 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 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);
}

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)
Expand Down Expand Up @@ -355,12 +282,35 @@ static int add_host_bridge_uport(struct device *match, void *arg)
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)
{
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);
Expand All @@ -376,14 +326,18 @@ 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) {
.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));
Expand Down Expand Up @@ -417,65 +371,50 @@ 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
* for their role as CXL uports to their CXL-capable PCIe Root Ports.
*/
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);
Expand All @@ -491,3 +430,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);

0 comments on commit f4ce1f7

Please sign in to comment.