cxl/hdm: Create emulated cxl_hdm for devices that do not have HDM decoders
CXL rev3 spec 8.1.3 RCDs may not have HDM register blocks. Create a fake HDM with information from the CXL PCIe DVSEC registers. The decoder count will be set to the HDM count retrieved from the DVSEC cap register. Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Dave Jiang <dave.jiang@intel.com> Link: https://lore.kernel.org/r/167640368994.935665.15831225724059704620.stgit@dwillia2-xfh.jf.intel.com Signed-off-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
parent
b777e9bec9
commit
4474ce565e
7 changed files with 66 additions and 20 deletions
|
@ -101,11 +101,34 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
|
||||||
BIT(CXL_CM_CAP_CAP_ID_HDM));
|
BIT(CXL_CM_CAP_CAP_ID_HDM));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct cxl_hdm *devm_cxl_setup_emulated_hdm(struct cxl_port *port,
|
||||||
|
struct cxl_endpoint_dvsec_info *info)
|
||||||
|
{
|
||||||
|
struct device *dev = &port->dev;
|
||||||
|
struct cxl_hdm *cxlhdm;
|
||||||
|
|
||||||
|
if (!info->mem_enabled)
|
||||||
|
return ERR_PTR(-ENODEV);
|
||||||
|
|
||||||
|
cxlhdm = devm_kzalloc(dev, sizeof(*cxlhdm), GFP_KERNEL);
|
||||||
|
if (!cxlhdm)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
cxlhdm->port = port;
|
||||||
|
cxlhdm->decoder_count = info->ranges;
|
||||||
|
cxlhdm->target_count = info->ranges;
|
||||||
|
dev_set_drvdata(&port->dev, cxlhdm);
|
||||||
|
|
||||||
|
return cxlhdm;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* devm_cxl_setup_hdm - map HDM decoder component registers
|
* devm_cxl_setup_hdm - map HDM decoder component registers
|
||||||
* @port: cxl_port to map
|
* @port: cxl_port to map
|
||||||
|
* @info: cached DVSEC range register info
|
||||||
*/
|
*/
|
||||||
struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port)
|
struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
|
||||||
|
struct cxl_endpoint_dvsec_info *info)
|
||||||
{
|
{
|
||||||
struct device *dev = &port->dev;
|
struct device *dev = &port->dev;
|
||||||
struct cxl_hdm *cxlhdm;
|
struct cxl_hdm *cxlhdm;
|
||||||
|
@ -119,6 +142,9 @@ struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port)
|
||||||
cxlhdm->port = port;
|
cxlhdm->port = port;
|
||||||
crb = ioremap(port->component_reg_phys, CXL_COMPONENT_REG_BLOCK_SIZE);
|
crb = ioremap(port->component_reg_phys, CXL_COMPONENT_REG_BLOCK_SIZE);
|
||||||
if (!crb) {
|
if (!crb) {
|
||||||
|
if (info->mem_enabled)
|
||||||
|
return devm_cxl_setup_emulated_hdm(port, info);
|
||||||
|
|
||||||
dev_err(dev, "No component registers mapped\n");
|
dev_err(dev, "No component registers mapped\n");
|
||||||
return ERR_PTR(-ENXIO);
|
return ERR_PTR(-ENXIO);
|
||||||
}
|
}
|
||||||
|
@ -814,19 +840,15 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void cxl_settle_decoders(struct cxl_hdm *cxlhdm)
|
||||||
* devm_cxl_enumerate_decoders - add decoder objects per HDM register set
|
|
||||||
* @cxlhdm: Structure to populate with HDM capabilities
|
|
||||||
*/
|
|
||||||
int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
|
|
||||||
struct cxl_endpoint_dvsec_info *info)
|
|
||||||
{
|
{
|
||||||
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
|
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
|
||||||
struct cxl_port *port = cxlhdm->port;
|
int committed, i;
|
||||||
int i, committed;
|
|
||||||
u64 dpa_base = 0;
|
|
||||||
u32 ctrl;
|
u32 ctrl;
|
||||||
|
|
||||||
|
if (!hdm)
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since the register resource was recently claimed via request_region()
|
* Since the register resource was recently claimed via request_region()
|
||||||
* be careful about trusting the "not-committed" status until the commit
|
* be careful about trusting the "not-committed" status until the commit
|
||||||
|
@ -843,6 +865,22 @@ int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
|
||||||
/* ensure that future checks of committed can be trusted */
|
/* ensure that future checks of committed can be trusted */
|
||||||
if (committed != cxlhdm->decoder_count)
|
if (committed != cxlhdm->decoder_count)
|
||||||
msleep(20);
|
msleep(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* devm_cxl_enumerate_decoders - add decoder objects per HDM register set
|
||||||
|
* @cxlhdm: Structure to populate with HDM capabilities
|
||||||
|
* @info: cached DVSEC range register info
|
||||||
|
*/
|
||||||
|
int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
|
||||||
|
struct cxl_endpoint_dvsec_info *info)
|
||||||
|
{
|
||||||
|
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
|
||||||
|
struct cxl_port *port = cxlhdm->port;
|
||||||
|
int i;
|
||||||
|
u64 dpa_base = 0;
|
||||||
|
|
||||||
|
cxl_settle_decoders(cxlhdm);
|
||||||
|
|
||||||
for (i = 0; i < cxlhdm->decoder_count; i++) {
|
for (i = 0; i < cxlhdm->decoder_count; i++) {
|
||||||
int target_map[CXL_DECODER_MAX_INTERLEAVE] = { 0 };
|
int target_map[CXL_DECODER_MAX_INTERLEAVE] = { 0 };
|
||||||
|
|
|
@ -378,16 +378,19 @@ int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
|
||||||
struct device *dev = cxlds->dev;
|
struct device *dev = cxlds->dev;
|
||||||
struct cxl_port *root;
|
struct cxl_port *root;
|
||||||
int i, rc, allowed;
|
int i, rc, allowed;
|
||||||
u32 global_ctrl;
|
u32 global_ctrl = 0;
|
||||||
|
|
||||||
|
if (hdm)
|
||||||
global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
|
global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the HDM Decoder Capability is already enabled then assume
|
* If the HDM Decoder Capability is already enabled then assume
|
||||||
* that some other agent like platform firmware set it up.
|
* that some other agent like platform firmware set it up.
|
||||||
*/
|
*/
|
||||||
if (global_ctrl & CXL_HDM_DECODER_ENABLE)
|
if (global_ctrl & CXL_HDM_DECODER_ENABLE || (!hdm && info->mem_enabled))
|
||||||
return devm_cxl_enable_mem(&port->dev, cxlds);
|
return devm_cxl_enable_mem(&port->dev, cxlds);
|
||||||
|
else if (!hdm)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
root = to_cxl_port(port->dev.parent);
|
root = to_cxl_port(port->dev.parent);
|
||||||
while (!is_cxl_root(root) && is_cxl_port(root->dev.parent))
|
while (!is_cxl_root(root) && is_cxl_port(root->dev.parent))
|
||||||
|
|
|
@ -643,7 +643,8 @@ struct cxl_endpoint_dvsec_info {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cxl_hdm;
|
struct cxl_hdm;
|
||||||
struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port);
|
struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
|
||||||
|
struct cxl_endpoint_dvsec_info *info);
|
||||||
int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
|
int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
|
||||||
struct cxl_endpoint_dvsec_info *info);
|
struct cxl_endpoint_dvsec_info *info);
|
||||||
int devm_cxl_add_passthrough_decoder(struct cxl_port *port);
|
int devm_cxl_add_passthrough_decoder(struct cxl_port *port);
|
||||||
|
|
|
@ -54,7 +54,7 @@ static int cxl_port_probe(struct device *dev)
|
||||||
return devm_cxl_add_passthrough_decoder(port);
|
return devm_cxl_add_passthrough_decoder(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
cxlhdm = devm_cxl_setup_hdm(port);
|
cxlhdm = devm_cxl_setup_hdm(port, &info);
|
||||||
if (IS_ERR(cxlhdm))
|
if (IS_ERR(cxlhdm))
|
||||||
return PTR_ERR(cxlhdm);
|
return PTR_ERR(cxlhdm);
|
||||||
|
|
||||||
|
|
|
@ -618,7 +618,8 @@ static struct acpi_pci_root *mock_acpi_pci_find_root(acpi_handle handle)
|
||||||
return &mock_pci_root[host_bridge_index(adev)];
|
return &mock_pci_root[host_bridge_index(adev)];
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct cxl_hdm *mock_cxl_setup_hdm(struct cxl_port *port)
|
static struct cxl_hdm *mock_cxl_setup_hdm(struct cxl_port *port,
|
||||||
|
struct cxl_endpoint_dvsec_info *info)
|
||||||
{
|
{
|
||||||
struct cxl_hdm *cxlhdm = devm_kzalloc(&port->dev, sizeof(*cxlhdm), GFP_KERNEL);
|
struct cxl_hdm *cxlhdm = devm_kzalloc(&port->dev, sizeof(*cxlhdm), GFP_KERNEL);
|
||||||
|
|
||||||
|
|
|
@ -131,16 +131,18 @@ __wrap_nvdimm_bus_register(struct device *dev,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(__wrap_nvdimm_bus_register);
|
EXPORT_SYMBOL_GPL(__wrap_nvdimm_bus_register);
|
||||||
|
|
||||||
struct cxl_hdm *__wrap_devm_cxl_setup_hdm(struct cxl_port *port)
|
struct cxl_hdm *__wrap_devm_cxl_setup_hdm(struct cxl_port *port,
|
||||||
|
struct cxl_endpoint_dvsec_info *info)
|
||||||
|
|
||||||
{
|
{
|
||||||
int index;
|
int index;
|
||||||
struct cxl_hdm *cxlhdm;
|
struct cxl_hdm *cxlhdm;
|
||||||
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
|
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
|
||||||
|
|
||||||
if (ops && ops->is_mock_port(port->uport))
|
if (ops && ops->is_mock_port(port->uport))
|
||||||
cxlhdm = ops->devm_cxl_setup_hdm(port);
|
cxlhdm = ops->devm_cxl_setup_hdm(port, info);
|
||||||
else
|
else
|
||||||
cxlhdm = devm_cxl_setup_hdm(port);
|
cxlhdm = devm_cxl_setup_hdm(port, info);
|
||||||
put_cxl_mock_ops(index);
|
put_cxl_mock_ops(index);
|
||||||
|
|
||||||
return cxlhdm;
|
return cxlhdm;
|
||||||
|
|
|
@ -23,7 +23,8 @@ struct cxl_mock_ops {
|
||||||
bool (*is_mock_port)(struct device *dev);
|
bool (*is_mock_port)(struct device *dev);
|
||||||
bool (*is_mock_dev)(struct device *dev);
|
bool (*is_mock_dev)(struct device *dev);
|
||||||
int (*devm_cxl_port_enumerate_dports)(struct cxl_port *port);
|
int (*devm_cxl_port_enumerate_dports)(struct cxl_port *port);
|
||||||
struct cxl_hdm *(*devm_cxl_setup_hdm)(struct cxl_port *port);
|
struct cxl_hdm *(*devm_cxl_setup_hdm)(
|
||||||
|
struct cxl_port *port, struct cxl_endpoint_dvsec_info *info);
|
||||||
int (*devm_cxl_add_passthrough_decoder)(struct cxl_port *port);
|
int (*devm_cxl_add_passthrough_decoder)(struct cxl_port *port);
|
||||||
int (*devm_cxl_enumerate_decoders)(
|
int (*devm_cxl_enumerate_decoders)(
|
||||||
struct cxl_hdm *hdm, struct cxl_endpoint_dvsec_info *info);
|
struct cxl_hdm *hdm, struct cxl_endpoint_dvsec_info *info);
|
||||||
|
|
Loading…
Add table
Reference in a new issue