cxl/hdm: Create emulated cxl_hdm for devices that do not have HDM decoders
authorDave Jiang <dave.jiang@intel.com>
Tue, 14 Feb 2023 19:41:30 +0000 (11:41 -0800)
committerDan Williams <dan.j.williams@intel.com>
Tue, 14 Feb 2023 23:45:21 +0000 (15:45 -0800)
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>
drivers/cxl/core/hdm.c
drivers/cxl/core/pci.c
drivers/cxl/cxl.h
drivers/cxl/port.c
tools/testing/cxl/test/cxl.c
tools/testing/cxl/test/mock.c
tools/testing/cxl/test/mock.h

index c0f224454447e56385749e44f6f8e56391297ef3..a49543f22dcac445ec26670247f0ee6318200e5e 100644 (file)
@@ -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 };
index 4df0b35c9b1ac8248cd9d92c99f4ff3820fb68af..4eb34dee7c950dffebfd4c92e4be0e576b2a9891 100644 (file)
@@ -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))
index fe9d75989c8ad698802dfe6b3f15f73ef21de4b4..f8cbc52754516822e72103493a05669df2ebf58a 100644 (file)
@@ -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);
index d3a708e32565951a11882ab3701f625fa9a673af..9f9cc268b5978b840a9e98e204d37942e730c413 100644 (file)
@@ -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);
 
index 3b4916adf29c02bcb5c0b1863daf2e3dbefa5cf0..94197abd44aa1e721c7b761a9a744ab4deb2d352 100644 (file)
@@ -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);
 
index 3116c9f07c5d7adbaca17055f326ab385381e689..c4e53f22e42159735cb14254f2cb464d86b9f085 100644 (file)
@@ -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;
index e377ced5f1b35a7a7354659926a4b539c25961b2..bef8817b01f20670c6940fc66426acc04d4805cd 100644 (file)
@@ -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);