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));
|
||||
}
|
||||
|
||||
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
|
||||
* @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 cxl_hdm *cxlhdm;
|
||||
|
@ -119,6 +142,9 @@ struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port)
|
|||
cxlhdm->port = port;
|
||||
crb = ioremap(port->component_reg_phys, CXL_COMPONENT_REG_BLOCK_SIZE);
|
||||
if (!crb) {
|
||||
if (info->mem_enabled)
|
||||
return devm_cxl_setup_emulated_hdm(port, info);
|
||||
|
||||
dev_err(dev, "No component registers mapped\n");
|
||||
return ERR_PTR(-ENXIO);
|
||||
}
|
||||
|
@ -814,19 +840,15 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
static void cxl_settle_decoders(struct cxl_hdm *cxlhdm)
|
||||
{
|
||||
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
|
||||
struct cxl_port *port = cxlhdm->port;
|
||||
int i, committed;
|
||||
u64 dpa_base = 0;
|
||||
int committed, i;
|
||||
u32 ctrl;
|
||||
|
||||
if (!hdm)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Since the register resource was recently claimed via request_region()
|
||||
* 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 */
|
||||
if (committed != cxlhdm->decoder_count)
|
||||
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++) {
|
||||
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 cxl_port *root;
|
||||
int i, rc, allowed;
|
||||
u32 global_ctrl;
|
||||
u32 global_ctrl = 0;
|
||||
|
||||
global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
|
||||
if (hdm)
|
||||
global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
|
||||
|
||||
/*
|
||||
* If the HDM Decoder Capability is already enabled then assume
|
||||
* 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);
|
||||
else if (!hdm)
|
||||
return -ENODEV;
|
||||
|
||||
root = to_cxl_port(port->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 *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,
|
||||
struct cxl_endpoint_dvsec_info *info);
|
||||
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);
|
||||
}
|
||||
|
||||
cxlhdm = devm_cxl_setup_hdm(port);
|
||||
cxlhdm = devm_cxl_setup_hdm(port, &info);
|
||||
if (IS_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)];
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -131,16 +131,18 @@ __wrap_nvdimm_bus_register(struct device *dev,
|
|||
}
|
||||
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;
|
||||
struct cxl_hdm *cxlhdm;
|
||||
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
|
||||
|
||||
if (ops && ops->is_mock_port(port->uport))
|
||||
cxlhdm = ops->devm_cxl_setup_hdm(port);
|
||||
cxlhdm = ops->devm_cxl_setup_hdm(port, info);
|
||||
else
|
||||
cxlhdm = devm_cxl_setup_hdm(port);
|
||||
cxlhdm = devm_cxl_setup_hdm(port, info);
|
||||
put_cxl_mock_ops(index);
|
||||
|
||||
return cxlhdm;
|
||||
|
|
|
@ -23,7 +23,8 @@ struct cxl_mock_ops {
|
|||
bool (*is_mock_port)(struct device *dev);
|
||||
bool (*is_mock_dev)(struct device *dev);
|
||||
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_enumerate_decoders)(
|
||||
struct cxl_hdm *hdm, struct cxl_endpoint_dvsec_info *info);
|
||||
|
|
Loading…
Add table
Reference in a new issue