Commit f9db85bf authored by Alison Schofield's avatar Alison Schofield Committed by Dan Williams

cxl/acpi: Support CXL XOR Interleave Math (CXIMS)

When the CFMWS is using XOR math, parse the corresponding
CXIMS structure and store the xormaps in the root decoder
structure. Use the xormaps in a new lookup, cxl_hb_xor(),
to find a targets entry in the host bridge interleave
target list.

Defined in CXL Specfication 3.0 Section: 9.17.1
Signed-off-by: default avatarAlison Schofield <alison.schofield@intel.com>
Reviewed-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
Link: https://lore.kernel.org/r/5794813acdf7b67cfba3609c6aaff46932fa38d0.1669847017.git.alison.schofield@intel.comSigned-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent 7db0aa8c
...@@ -6,9 +6,118 @@ ...@@ -6,9 +6,118 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <asm/div64.h>
#include "cxlpci.h" #include "cxlpci.h"
#include "cxl.h" #include "cxl.h"
struct cxl_cxims_data {
int nr_maps;
u64 xormaps[];
};
/*
* Find a targets entry (n) in the host bridge interleave list.
* CXL Specfication 3.0 Table 9-22
*/
static int cxl_xor_calc_n(u64 hpa, struct cxl_cxims_data *cximsd, int iw,
int ig)
{
int i = 0, n = 0;
u8 eiw;
/* IW: 2,4,6,8,12,16 begin building 'n' using xormaps */
if (iw != 3) {
for (i = 0; i < cximsd->nr_maps; i++)
n |= (hweight64(hpa & cximsd->xormaps[i]) & 1) << i;
}
/* IW: 3,6,12 add a modulo calculation to 'n' */
if (!is_power_of_2(iw)) {
if (ways_to_cxl(iw, &eiw))
return -1;
hpa &= GENMASK_ULL(51, eiw + ig);
n |= do_div(hpa, 3) << i;
}
return n;
}
static struct cxl_dport *cxl_hb_xor(struct cxl_root_decoder *cxlrd, int pos)
{
struct cxl_cxims_data *cximsd = cxlrd->platform_data;
struct cxl_switch_decoder *cxlsd = &cxlrd->cxlsd;
struct cxl_decoder *cxld = &cxlsd->cxld;
int ig = cxld->interleave_granularity;
int iw = cxld->interleave_ways;
int n = 0;
u64 hpa;
if (dev_WARN_ONCE(&cxld->dev,
cxld->interleave_ways != cxlsd->nr_targets,
"misconfigured root decoder\n"))
return NULL;
hpa = cxlrd->res->start + pos * ig;
/* Entry (n) is 0 for no interleave (iw == 1) */
if (iw != 1)
n = cxl_xor_calc_n(hpa, cximsd, iw, ig);
if (n < 0)
return NULL;
return cxlrd->cxlsd.target[n];
}
struct cxl_cxims_context {
struct device *dev;
struct cxl_root_decoder *cxlrd;
};
static int cxl_parse_cxims(union acpi_subtable_headers *header, void *arg,
const unsigned long end)
{
struct acpi_cedt_cxims *cxims = (struct acpi_cedt_cxims *)header;
struct cxl_cxims_context *ctx = arg;
struct cxl_root_decoder *cxlrd = ctx->cxlrd;
struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
struct device *dev = ctx->dev;
struct cxl_cxims_data *cximsd;
unsigned int hbig, nr_maps;
int rc;
rc = cxl_to_granularity(cxims->hbig, &hbig);
if (rc)
return rc;
/* Does this CXIMS entry apply to the given CXL Window? */
if (hbig != cxld->interleave_granularity)
return 0;
/* IW 1,3 do not use xormaps and skip this parsing entirely */
if (is_power_of_2(cxld->interleave_ways))
/* 2, 4, 8, 16 way */
nr_maps = ilog2(cxld->interleave_ways);
else
/* 6, 12 way */
nr_maps = ilog2(cxld->interleave_ways / 3);
if (cxims->nr_xormaps < nr_maps) {
dev_dbg(dev, "CXIMS nr_xormaps[%d] expected[%d]\n",
cxims->nr_xormaps, nr_maps);
return -ENXIO;
}
cximsd = devm_kzalloc(dev, struct_size(cximsd, xormaps, nr_maps),
GFP_KERNEL);
if (!cximsd)
return -ENOMEM;
memcpy(cximsd->xormaps, cxims->xormap_list,
nr_maps * sizeof(*cximsd->xormaps));
cximsd->nr_maps = nr_maps;
cxlrd->platform_data = cximsd;
return 0;
}
static unsigned long cfmws_to_decoder_flags(int restrictions) static unsigned long cfmws_to_decoder_flags(int restrictions)
{ {
unsigned long flags = CXL_DECODER_F_ENABLE; unsigned long flags = CXL_DECODER_F_ENABLE;
...@@ -33,8 +142,10 @@ static int cxl_acpi_cfmws_verify(struct device *dev, ...@@ -33,8 +142,10 @@ static int cxl_acpi_cfmws_verify(struct device *dev,
int rc, expected_len; int rc, expected_len;
unsigned int ways; unsigned int ways;
if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO) { if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO &&
dev_err(dev, "CFMWS Unsupported Interleave Arithmetic\n"); cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_XOR) {
dev_err(dev, "CFMWS Unknown Interleave Arithmetic: %d\n",
cfmws->interleave_arithmetic);
return -EINVAL; return -EINVAL;
} }
...@@ -84,9 +195,11 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg, ...@@ -84,9 +195,11 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
struct cxl_cfmws_context *ctx = arg; struct cxl_cfmws_context *ctx = arg;
struct cxl_port *root_port = ctx->root_port; struct cxl_port *root_port = ctx->root_port;
struct resource *cxl_res = ctx->cxl_res; struct resource *cxl_res = ctx->cxl_res;
struct cxl_cxims_context cxims_ctx;
struct cxl_root_decoder *cxlrd; struct cxl_root_decoder *cxlrd;
struct device *dev = ctx->dev; struct device *dev = ctx->dev;
struct acpi_cedt_cfmws *cfmws; struct acpi_cedt_cfmws *cfmws;
cxl_calc_hb_fn cxl_calc_hb;
struct cxl_decoder *cxld; struct cxl_decoder *cxld;
unsigned int ways, i, ig; unsigned int ways, i, ig;
struct resource *res; struct resource *res;
...@@ -128,7 +241,12 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg, ...@@ -128,7 +241,12 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
if (rc) if (rc)
goto err_insert; goto err_insert;
cxlrd = cxl_root_decoder_alloc(root_port, ways); if (cfmws->interleave_arithmetic == ACPI_CEDT_CFMWS_ARITHMETIC_MODULO)
cxl_calc_hb = cxl_hb_modulo;
else
cxl_calc_hb = cxl_hb_xor;
cxlrd = cxl_root_decoder_alloc(root_port, ways, cxl_calc_hb);
if (IS_ERR(cxlrd)) if (IS_ERR(cxlrd))
return 0; return 0;
...@@ -148,7 +266,20 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg, ...@@ -148,7 +266,20 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
ig = CXL_DECODER_MIN_GRANULARITY; ig = CXL_DECODER_MIN_GRANULARITY;
cxld->interleave_granularity = ig; cxld->interleave_granularity = ig;
if (cfmws->interleave_arithmetic == ACPI_CEDT_CFMWS_ARITHMETIC_XOR) {
if (ways != 1 && ways != 3) {
cxims_ctx = (struct cxl_cxims_context) {
.dev = dev,
.cxlrd = cxlrd,
};
rc = acpi_table_parse_cedt(ACPI_CEDT_TYPE_CXIMS,
cxl_parse_cxims, &cxims_ctx);
if (rc < 0)
goto err_xormap;
}
}
rc = cxl_decoder_add(cxld, target_map); rc = cxl_decoder_add(cxld, target_map);
err_xormap:
if (rc) if (rc)
put_device(&cxld->dev); put_device(&cxld->dev);
else else
......
...@@ -1428,7 +1428,7 @@ static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd, ...@@ -1428,7 +1428,7 @@ static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd,
return rc; return rc;
} }
static struct cxl_dport *cxl_hb_modulo(struct cxl_root_decoder *cxlrd, int pos) struct cxl_dport *cxl_hb_modulo(struct cxl_root_decoder *cxlrd, int pos)
{ {
struct cxl_switch_decoder *cxlsd = &cxlrd->cxlsd; struct cxl_switch_decoder *cxlsd = &cxlrd->cxlsd;
struct cxl_decoder *cxld = &cxlsd->cxld; struct cxl_decoder *cxld = &cxlsd->cxld;
...@@ -1441,6 +1441,7 @@ static struct cxl_dport *cxl_hb_modulo(struct cxl_root_decoder *cxlrd, int pos) ...@@ -1441,6 +1441,7 @@ static struct cxl_dport *cxl_hb_modulo(struct cxl_root_decoder *cxlrd, int pos)
return cxlrd->cxlsd.target[pos % iw]; return cxlrd->cxlsd.target[pos % iw];
} }
EXPORT_SYMBOL_NS_GPL(cxl_hb_modulo, CXL);
static struct lock_class_key cxl_decoder_key; static struct lock_class_key cxl_decoder_key;
...@@ -1502,6 +1503,7 @@ static int cxl_switch_decoder_init(struct cxl_port *port, ...@@ -1502,6 +1503,7 @@ static int cxl_switch_decoder_init(struct cxl_port *port,
* cxl_root_decoder_alloc - Allocate a root level decoder * cxl_root_decoder_alloc - Allocate a root level decoder
* @port: owning CXL root of this decoder * @port: owning CXL root of this decoder
* @nr_targets: static number of downstream targets * @nr_targets: static number of downstream targets
* @calc_hb: which host bridge covers the n'th position by granularity
* *
* Return: A new cxl decoder to be registered by cxl_decoder_add(). A * Return: A new cxl decoder to be registered by cxl_decoder_add(). A
* 'CXL root' decoder is one that decodes from a top-level / static platform * 'CXL root' decoder is one that decodes from a top-level / static platform
...@@ -1509,7 +1511,8 @@ static int cxl_switch_decoder_init(struct cxl_port *port, ...@@ -1509,7 +1511,8 @@ static int cxl_switch_decoder_init(struct cxl_port *port,
* topology. * topology.
*/ */
struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port, struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
unsigned int nr_targets) unsigned int nr_targets,
cxl_calc_hb_fn calc_hb)
{ {
struct cxl_root_decoder *cxlrd; struct cxl_root_decoder *cxlrd;
struct cxl_switch_decoder *cxlsd; struct cxl_switch_decoder *cxlsd;
...@@ -1531,7 +1534,7 @@ struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port, ...@@ -1531,7 +1534,7 @@ struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
return ERR_PTR(rc); return ERR_PTR(rc);
} }
cxlrd->calc_hb = cxl_hb_modulo; cxlrd->calc_hb = calc_hb;
cxld = &cxlsd->cxld; cxld = &cxlsd->cxld;
cxld->dev.type = &cxl_decoder_root_type; cxld->dev.type = &cxl_decoder_root_type;
......
...@@ -324,18 +324,23 @@ struct cxl_switch_decoder { ...@@ -324,18 +324,23 @@ struct cxl_switch_decoder {
struct cxl_dport *target[]; struct cxl_dport *target[];
}; };
struct cxl_root_decoder;
typedef struct cxl_dport *(*cxl_calc_hb_fn)(struct cxl_root_decoder *cxlrd,
int pos);
/** /**
* struct cxl_root_decoder - Static platform CXL address decoder * struct cxl_root_decoder - Static platform CXL address decoder
* @res: host / parent resource for region allocations * @res: host / parent resource for region allocations
* @region_id: region id for next region provisioning event * @region_id: region id for next region provisioning event
* @calc_hb: which host bridge covers the n'th position by granularity * @calc_hb: which host bridge covers the n'th position by granularity
* @platform_data: platform specific configuration data
* @cxlsd: base cxl switch decoder * @cxlsd: base cxl switch decoder
*/ */
struct cxl_root_decoder { struct cxl_root_decoder {
struct resource *res; struct resource *res;
atomic_t region_id; atomic_t region_id;
struct cxl_dport *(*calc_hb)(struct cxl_root_decoder *cxlrd, int pos); cxl_calc_hb_fn calc_hb;
void *platform_data;
struct cxl_switch_decoder cxlsd; struct cxl_switch_decoder cxlsd;
}; };
...@@ -581,7 +586,9 @@ struct cxl_endpoint_decoder *to_cxl_endpoint_decoder(struct device *dev); ...@@ -581,7 +586,9 @@ struct cxl_endpoint_decoder *to_cxl_endpoint_decoder(struct device *dev);
bool is_root_decoder(struct device *dev); bool is_root_decoder(struct device *dev);
bool is_endpoint_decoder(struct device *dev); bool is_endpoint_decoder(struct device *dev);
struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port, struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
unsigned int nr_targets); unsigned int nr_targets,
cxl_calc_hb_fn calc_hb);
struct cxl_dport *cxl_hb_modulo(struct cxl_root_decoder *cxlrd, int pos);
struct cxl_switch_decoder *cxl_switch_decoder_alloc(struct cxl_port *port, struct cxl_switch_decoder *cxl_switch_decoder_alloc(struct cxl_port *port,
unsigned int nr_targets); unsigned int nr_targets);
int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map); int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map);
......
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