cxl/test: Fixup hdm init for auto region to support type2

Add support to setup initialization of decoders in order to support
type2 auto region.

Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Tested-by: Alison Schofield <alison.schofield@intel.com>
Link: https://patch.msgid.link/20260629221104.3891733-7-dave.jiang@intel.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
This commit is contained in:
Dave Jiang 2026-06-29 15:11:03 -07:00
parent 98ba41c323
commit b3df5a5a38

View File

@ -1084,74 +1084,26 @@ static int first_decoder(struct device *dev, const void *data)
return 0;
}
/*
* Initialize a decoder during HDM enumeration.
*
* If a saved registry entry exists:
* - enabled decoders are restored from the saved programming
* - disabled decoders are initialized in a clean disabled state
*
* If no registry entry exists the decoder follows the normal mock
* initialization path, including the special auto-region setup for
* the first endpoints under host-bridge0.
*
* Returns true if decoder state was restored from the registry. In
* that case the saved decode configuration (including target mapping)
* has already been applied and the map_targets() is skipped.
*/
static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
enum cxld_init_type {
MOCK_DECODER_INIT_DEFAULT,
MOCK_DECODER_INIT_SAVED,
MOCK_DECODER_INIT_TYPE3_AUTO,
MOCK_DECODER_INIT_TYPE2_AUTO,
};
static enum cxld_init_type get_decoder_init_type(struct cxl_decoder *cxld,
struct platform_device *pdev,
bool hb0,
struct cxl_test_decoder **td)
{
struct acpi_cedt_cfmws *window = mock_cfmws[0];
struct platform_device *pdev = NULL;
struct cxl_endpoint_decoder *cxled;
struct cxl_switch_decoder *cxlsd;
struct cxl_port *port, *iter;
struct cxl_test_decoder *td;
struct cxl_memdev *cxlmd;
struct cxl_dport *dport;
struct device *dev;
bool hb0 = false;
u64 base;
int i;
struct cxl_test_decoder *found_td = cxld_registry_find(cxld);
if (is_endpoint_decoder(&cxld->dev)) {
cxled = to_cxl_endpoint_decoder(&cxld->dev);
cxlmd = cxled_to_memdev(cxled);
WARN_ON(!dev_is_platform(cxlmd->dev.parent));
pdev = to_platform_device(cxlmd->dev.parent);
/* check is endpoint is attach to host-bridge0 */
port = cxled_to_port(cxled);
do {
if (port->uport_dev == &cxl_host_bridge[0]->dev) {
hb0 = true;
break;
}
if (is_cxl_port(port->dev.parent))
port = to_cxl_port(port->dev.parent);
else
port = NULL;
} while (port);
port = cxled_to_port(cxled);
} else {
port = to_cxl_port(cxld->dev.parent);
if (found_td) {
*td = found_td;
return MOCK_DECODER_INIT_SAVED;
}
td = cxld_registry_find(cxld);
if (td) {
bool enabled;
if (is_switch_decoder(&cxld->dev))
enabled = td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE;
else
enabled = td->cxled.cxld.flags & CXL_DECODER_F_ENABLE;
if (enabled)
return !cxld_registry_restore(cxld, td);
init_disabled_mock_decoder(cxld);
return false;
}
*td = NULL;
/*
* The first decoder on the first 2 devices on the first switch
@ -1162,15 +1114,119 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
* See 'cxl list -BMPu -m cxl_mem.0,cxl_mem.4'
*/
if (!is_endpoint_decoder(&cxld->dev) || !hb0 || pdev->id % 4 ||
pdev->id > 4 || cxld->id > 0) {
default_mock_decoder(cxld);
return false;
}
pdev->id > 4 || cxld->id > 0)
return MOCK_DECODER_INIT_DEFAULT;
return type2_test ? MOCK_DECODER_INIT_TYPE2_AUTO :
MOCK_DECODER_INIT_TYPE3_AUTO;
}
static bool mock_decoder_handle_saved(struct cxl_decoder *cxld, struct cxl_test_decoder *td)
{
bool enabled;
if (is_switch_decoder(&cxld->dev))
enabled = td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE;
else
enabled = td->cxled.cxld.flags & CXL_DECODER_F_ENABLE;
if (enabled)
return !cxld_registry_restore(cxld, td);
init_disabled_mock_decoder(cxld);
return false;
}
static void mock_init_hdm_type2_cxled(struct cxl_endpoint_decoder *cxled,
struct cxl_port *port)
{
struct acpi_cedt_cfmws *window = mock_cfmws[0];
struct cxl_decoder *cxld = &cxled->cxld;
struct cxl_switch_decoder *cxlsd;
struct cxl_dport *dport;
struct cxl_port *root_port;
struct device *dev;
u64 base;
base = window->base_hpa;
cxld->hpa_range = (struct range) {
.start = base,
.end = base + mock_auto_region_size - 1,
};
cxld->interleave_ways = 1;
eig_to_granularity(window->granularity, &cxld->interleave_granularity);
cxld->target_type = CXL_DECODER_DEVMEM;
cxld->flags = CXL_DECODER_F_ENABLE;
cxled->state = CXL_DECODER_STATE_AUTO;
port->commit_end = cxld->id;
devm_cxl_dpa_reserve(cxled, 0,
mock_auto_region_size / cxld->interleave_ways, 0);
cxld->commit = mock_decoder_commit;
cxld->reset = mock_decoder_reset;
WARN_ON_ONCE(!cxld_registry_new(cxld));
/*
* Now that endpoint decoder is set up, walk up the hierarchy
* and setup the root port decoder targeting @cxlmd.
*/
dport = port->parent_dport;
root_port = dport->port;
dev = device_find_child(&root_port->dev, NULL, first_decoder);
/*
* Ancestor ports are guaranteed to be enumerated before
* @port, and all ports have at least one decoder.
*/
if (WARN_ON(!dev))
return;
cxlsd = to_cxl_switch_decoder(dev);
cxld = &cxlsd->cxld;
cxld->target_type = CXL_DECODER_DEVMEM;
cxld->flags = CXL_DECODER_F_ENABLE;
root_port->commit_end = 0;
cxld->interleave_ways = 1;
cxld->interleave_granularity = 4096;
cxld->target_map[0] = dport->port_id;
cxld->hpa_range = (struct range) {
.start = base,
.end = base + mock_auto_region_size - 1,
};
cxld->commit = mock_decoder_commit;
cxld->reset = mock_decoder_reset;
/*
* Only target_map[] is programmed above, mimicking
* firmware. On real hardware target[] is populated as
* dports enumerate, via update_decoder_targets(). The
* mock's dports are already bound by now, so fire that
* resolution explicitly here rather than stamping
* target[] directly.
*/
cxl_port_update_decoder_targets(root_port, dport);
cxld_registry_update(cxld);
put_device(dev);
}
static void mock_init_hdm_type3_cxled(struct cxl_endpoint_decoder *cxled,
struct cxl_port *port,
struct platform_device *pdev,
bool hb0)
{
struct acpi_cedt_cfmws *window = mock_cfmws[0];
struct cxl_decoder *cxld = &cxled->cxld;
struct cxl_switch_decoder *cxlsd;
struct cxl_dport *dport;
struct cxl_port *iter;
struct device *dev;
u64 base;
int i;
/* Simulate missing cxl_mem.4 configuration */
if (hb0 && pdev->id == 4 && cxld->id == 0 && fail_autoassemble) {
default_mock_decoder(cxld);
return false;
return;
}
base = window->base_hpa;
@ -1252,7 +1308,76 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
cxld_registry_update(cxld);
put_device(dev);
}
}
/*
* Initialize a decoder during HDM enumeration.
*
* If a saved registry entry exists:
* - enabled decoders are restored from the saved programming
* - disabled decoders are initialized in a clean disabled state
*
* If no registry entry exists the decoder follows the normal mock
* initialization path, including the special auto-region setup for
* the first endpoints under host-bridge0.
*
* Returns true if decoder state was restored from the registry. In
* that case the saved decode configuration (including target mapping)
* has already been applied and the map_targets() is skipped.
*/
static bool mock_init_hdm_decoder(struct cxl_decoder *cxld)
{
struct cxl_endpoint_decoder *cxled = NULL;
struct platform_device *pdev = NULL;
struct cxl_test_decoder *td;
struct cxl_memdev *cxlmd;
struct cxl_port *port;
bool hb0 = false;
if (is_endpoint_decoder(&cxld->dev)) {
cxled = to_cxl_endpoint_decoder(&cxld->dev);
cxlmd = cxled_to_memdev(cxled);
WARN_ON(!dev_is_platform(cxlmd->dev.parent));
pdev = to_platform_device(cxlmd->dev.parent);
/* check is endpoint is attach to host-bridge0 */
port = cxled_to_port(cxled);
do {
if (port->uport_dev == &cxl_host_bridge[0]->dev) {
hb0 = true;
break;
}
if (is_cxl_port(port->dev.parent))
port = to_cxl_port(port->dev.parent);
else
port = NULL;
} while (port);
port = cxled_to_port(cxled);
} else {
port = to_cxl_port(cxld->dev.parent);
}
switch (get_decoder_init_type(cxld, pdev, hb0, &td)) {
case MOCK_DECODER_INIT_SAVED:
if (WARN_ON(!td))
return false;
return mock_decoder_handle_saved(cxld, td);
case MOCK_DECODER_INIT_DEFAULT:
/*
* The default path picks up all the decoders that are not
* endpoint.
*/
default_mock_decoder(cxld);
return false;
case MOCK_DECODER_INIT_TYPE3_AUTO:
mock_init_hdm_type3_cxled(cxled, port, pdev, hb0);
return false;
case MOCK_DECODER_INIT_TYPE2_AUTO:
mock_init_hdm_type2_cxled(cxled, port);
return false;
default:
return false;
}
return false;
}