From: Srirangan Madhavan <smadhavan@nvidia.com>
To: Alison Schofield <alison.schofield@intel.com>,
Bjorn Helgaas <bhelgaas@google.com>,
Dave Jiang <dave.jiang@intel.com>,
Davidlohr Bueso <dave@stgolabs.net>,
Ira Weiny <iweiny@kernel.org>,
Jonathan Cameron <jic23@kernel.org>,
Vishal Verma <vishal.l.verma@intel.com>,
linux-cxl@vger.kernel.org, linux-pci@vger.kernel.org,
linux-kernel@vger.kernel.org
Cc: Li Ming <ming.li@zohomail.com>,
Joshua Machauer <machaujo@amazon.de>,
Alex Williamson <alex.williamson@redhat.com>,
vsethi@nvidia.com, alwilliamson@nvidia.com,
Sai Yashwanth Reddy Kancherla <skancherla@nvidia.com>,
Vishal Aslot <vaslot@nvidia.com>,
Manish Honap <mhonap@nvidia.com>, Jiandi An <jan@nvidia.com>,
Richard Cheng <icheng@nvidia.com>,
linux-tegra@vger.kernel.org,
Srirangan Madhavan <smadhavan@nvidia.com>
Subject: [PATCH v17 11/19] PCI/CXL: Cache endpoint HDM state during PCI enumeration
Date: Thu, 5 Nov 2026 18:59:09 +0000 [thread overview]
Message-ID: <20261105185917.2667656-12-smadhavan@nvidia.com> (raw)
In-Reply-To: <20261105185917.2667656-1-smadhavan@nvidia.com>
Cache HDM state in pci_dev->hdm after BAR resource setup and before driver
binding, so reset can restore it without a bound CXL driver.
Limit caching to devices that advertise CXL.cache, CXL.mem, CXL Reset,
and Reset Memory Clear. Save their HDM register location, decoder
settings, and Device DVSEC state.
Validate capability reads and decoder counts, and restore PCI_COMMAND
after temporary MMIO access, reporting restoration failures. Publish the
cache under the PCI device lock and protect later updates with the DPA
lock.
Signed-off-by: Srirangan Madhavan <smadhavan@nvidia.com>
Assisted-by: LLM
---
Changes in v17:
- Publish the cache under the PCI device lock without taking
cxl_rwsem.dpa, and drop the cxl_hdm_cache_present() and
cxl_hdm_cache_publish() helpers (Li Ming)
https://lore.kernel.org/linux-cxl/d581f2a0-dd9e-4d0a-bd19-639c1bd9b832@zohomail.com/
- Reject an all-ones DVSEC Control read in cxl_set_mem_enable() before
it is written back or cached (Sashiko)
https://sashiko.dev/#/patchset/20261029145703.2185231-1-smadhavan@nvidia.com?part=11
- Explain why cxl_set_mem_enable() refreshes the cached DVSEC Control
when MEM_ENABLE already matches (Richard)
https://lore.kernel.org/linux-cxl/auRQN6a3QVtqjd5a@MWDK4CY14F/
Reference branch: https://git.kernel.org/pub/scm/linux/kernel/git/djiang/linux.git/commit/?id=b12baa1105f33cc1a10f9c9c0c6376194397d9f3
---
MAINTAINERS | 1 +
drivers/cxl/core/hdm_regs.c | 96 +++++++++++++++
drivers/cxl/core/hdm_state.c | 37 +++++-
drivers/cxl/core/pci.c | 29 +++--
drivers/pci/Makefile | 1 +
drivers/pci/bus.c | 1 +
drivers/pci/cxl.c | 225 ++++++++++++++++++++++++++++++++++
drivers/pci/pci.h | 8 ++
drivers/pci/probe.c | 1 +
include/cxl/hdm.h | 9 ++
include/uapi/linux/pci_regs.h | 2 +
11 files changed, 399 insertions(+), 11 deletions(-)
create mode 100644 drivers/pci/cxl.c
diff --git a/MAINTAINERS b/MAINTAINERS
index c2414447892c..b874340b0265 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6572,6 +6572,7 @@ S: Maintained
F: Documentation/driver-api/cxl
F: Documentation/userspace-api/fwctl/fwctl-cxl.rst
F: drivers/cxl/
+F: drivers/pci/cxl.c
F: include/cxl/
F: include/uapi/linux/cxl_mem.h
F: tools/testing/cxl/
diff --git a/drivers/cxl/core/hdm_regs.c b/drivers/cxl/core/hdm_regs.c
index cc5d77c4abf5..c92dbb017162 100644
--- a/drivers/cxl/core/hdm_regs.c
+++ b/drivers/cxl/core/hdm_regs.c
@@ -2,6 +2,7 @@
/* Copyright (c) 2026 NVIDIA Corporation & Affiliates */
#include <linux/delay.h>
#include <linux/bug.h>
+#include <linux/device.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/kernel.h>
@@ -190,3 +191,98 @@ int cxl_hdm_read_decoder(struct cxl_decoder_config *config, void __iomem *hdm,
return cxl_hdm_unpack_decoder(config, id, ctrl, base, size);
}
EXPORT_SYMBOL_FOR_MODULES(cxl_hdm_read_decoder, "cxl_core");
+
+/**
+ * cxl_hdm_decoder_count_mapped() - read the decoder count of a mapped block
+ * @dev: device for error reporting
+ * @hdm: mapped HDM decoder capability
+ * @size: size of the mapping
+ *
+ * Return: the decoder count, or a negative errno if the count is reserved or
+ * the mapping cannot hold every decoder.
+ */
+int cxl_hdm_decoder_count_mapped(struct device *dev, void __iomem *hdm,
+ resource_size_t size)
+{
+ int count;
+
+ count = cxl_hdm_decoder_count(readl(hdm + CXL_HDM_DECODER_CAP_OFFSET));
+ if (count < 0)
+ return count;
+
+ /* The last decoder's target-list high register must fit in the mapping. */
+ if (CXL_HDM_DECODER0_TL_HIGH(count - 1) + sizeof(u32) > size) {
+ dev_err(dev, "CXL HDM decoder count exceeds mapped register block\n");
+ return -ENXIO;
+ }
+
+ return count;
+}
+
+/* Read one endpoint decoder; a disabled decoder keeps only its id. */
+static int cxl_hdm_read_decoder_settings(struct device *dev,
+ struct cxl_endpoint_decoder_settings *settings,
+ void __iomem *hdm, int id)
+{
+ u64 lo, hi;
+ u32 ctrl;
+ int rc;
+
+ ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
+ rc = cxl_hdm_read_decoder(&settings->config, hdm, id, ctrl);
+ if (rc) {
+ dev_err(dev,
+ "CXL HDM decoder %d has invalid configuration: %d\n",
+ id, rc);
+ return rc;
+ }
+
+ if (!(settings->config.flags & CXL_DECODER_F_ENABLE)) {
+ *settings = (struct cxl_endpoint_decoder_settings) {
+ .config.id = id,
+ };
+ } else {
+ lo = readl(hdm + CXL_HDM_DECODER0_SKIP_LOW(id));
+ hi = readl(hdm + CXL_HDM_DECODER0_SKIP_HIGH(id));
+ settings->skip = (hi << 32) | lo;
+ }
+
+ return 0;
+}
+
+/**
+ * cxl_hdm_read_state() - read endpoint HDM decoder state
+ * @dev: device for error messages
+ * @hdm: mapped HDM decoder capability
+ * @info: state to fill; @info->decoder_count must be set
+ *
+ * Return: 0 on success, or a negative errno if a decoder holds an invalid
+ * configuration or the decoder count changed while reading.
+ */
+int cxl_hdm_read_state(struct device *dev, void __iomem *hdm,
+ struct cxl_hdm_info *info)
+{
+ int count;
+ int rc;
+
+ info->global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
+ for (int i = 0; i < info->decoder_count; i++) {
+ rc = cxl_hdm_read_decoder_settings(dev, &info->settings[i],
+ hdm, i);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * A changed decoder count means the snapshot no longer describes the
+ * complete hardware state. Reject it rather than publish a partial cache.
+ */
+ count = cxl_hdm_decoder_count(readl(hdm + CXL_HDM_DECODER_CAP_OFFSET));
+ if (count != info->decoder_count) {
+ dev_err(dev, "CXL HDM decoder count changed from %d to %d\n",
+ info->decoder_count, count);
+ return -ENXIO;
+ }
+
+ return 0;
+}
diff --git a/drivers/cxl/core/hdm_state.c b/drivers/cxl/core/hdm_state.c
index d4c2be1ef833..9def4a7439c9 100644
--- a/drivers/cxl/core/hdm_state.c
+++ b/drivers/cxl/core/hdm_state.c
@@ -1,21 +1,37 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2026 NVIDIA Corporation & Affiliates */
+#include <linux/cleanup.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/rwsem.h>
+#include <linux/slab.h>
#include <cxl/hdm.h>
-
#include "cxl.h"
#include "core.h"
+/*
+ * CXL state that must stay available while no CXL driver is bound: the CXL
+ * locks and the per-device HDM cache filled during PCI enumeration.
+ */
struct cxl_rwsem cxl_rwsem = {
.region = __RWSEM_INITIALIZER(cxl_rwsem.region),
.dpa = __RWSEM_INITIALIZER(cxl_rwsem.dpa),
};
EXPORT_SYMBOL_FOR_MODULES(cxl_rwsem, "cxl_core");
+void cxl_hdm_cache_set_dvsec_ctrl(struct pci_dev *pdev, u16 ctrl)
+{
+ guard(rwsem_write)(&cxl_rwsem.dpa);
+ if (!pdev->hdm)
+ return;
+
+ pdev->hdm->dvsec_ctrl = ctrl;
+ pdev->hdm->dvsec_ctrl_valid = true;
+}
+EXPORT_SYMBOL_FOR_MODULES(cxl_hdm_cache_set_dvsec_ctrl, "cxl_core");
+
void cxl_hdm_cache_refresh_decoder(struct pci_dev *pdev, void __iomem *hdm,
struct cxl_endpoint_decoder *cxled)
{
@@ -70,3 +86,22 @@ int cxl_hdm_cache_check_decoder_count(struct pci_dev *pdev, int count)
return 0;
}
EXPORT_SYMBOL_FOR_MODULES(cxl_hdm_cache_check_decoder_count, "cxl_core");
+
+/**
+ * cxl_hdm_cache_release() - unpublish and free cached HDM state
+ * @slot: cache pointer owned by the device
+ *
+ * Take the DPA write lock to exclude readers while clearing @slot, then
+ * free the unpublished cache after releasing the lock.
+ */
+void cxl_hdm_cache_release(struct cxl_hdm_info **slot)
+{
+ struct cxl_hdm_info *info;
+
+ scoped_guard(rwsem_write, &cxl_rwsem.dpa) {
+ info = *slot;
+ /* Unpublish before freeing so readers cannot see stale state. */
+ *slot = NULL;
+ }
+ kfree(info);
+}
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 5272013548c7..ea5327cd627f 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -209,22 +209,31 @@ static int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val)
struct pci_dev *pdev = to_pci_dev(cxlds->dev);
int d = cxlds->cxl_dvsec;
u16 ctrl;
- int rc;
+ int rc, ret = 1;
rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, &ctrl);
if (rc)
return pcibios_err_to_errno(rc);
+ /* An all-ones read must not be written back or cached. */
+ if (PCI_POSSIBLE_ERROR(ctrl))
+ return -ENODEV;
- if ((ctrl & PCI_DVSEC_CXL_MEM_ENABLE) == val)
- return 1;
- ctrl &= ~PCI_DVSEC_CXL_MEM_ENABLE;
- ctrl |= val;
+ if ((ctrl & PCI_DVSEC_CXL_MEM_ENABLE) != val) {
+ ctrl &= ~PCI_DVSEC_CXL_MEM_ENABLE;
+ ctrl |= val;
- rc = pci_write_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, ctrl);
- if (rc)
- return pcibios_err_to_errno(rc);
+ rc = pci_write_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, ctrl);
+ if (rc)
+ return pcibios_err_to_errno(rc);
+ ret = 0;
+ }
- return 0;
+ /*
+ * Refresh the PCI-core cache from the live register even when
+ * MEM_ENABLE already matched: a reset restores the cached value.
+ */
+ cxl_hdm_cache_set_dvsec_ctrl(pdev, ctrl);
+ return ret;
}
static void clear_mem_enable(void *cxlds)
@@ -314,7 +323,7 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds,
* capable, there is no point in continuing to enable CXL.mem.
*/
hdm_count = FIELD_GET(PCI_DVSEC_CXL_HDM_COUNT, cap);
- if (!hdm_count || hdm_count > 2)
+ if (!cxl_dvsec_hdm_count_valid(hdm_count))
return -EINVAL;
/*
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 41ebc3b9a518..7a5d9f7297e9 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_PCI_P2PDMA) += p2pdma.o
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
obj-$(CONFIG_VGA_ARB) += vgaarb.o
obj-$(CONFIG_PCI_DOE) += doe.o
+obj-$(CONFIG_CXL_RESET) += cxl.o
obj-$(CONFIG_PCI_IDE) += ide.o
obj-$(CONFIG_PCI_TSM) += tsm.o
obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 655ed53436d3..aad567fb7c56 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -359,6 +359,7 @@ void pci_bus_add_device(struct pci_dev *dev)
/* Save config space for error recoverability */
pci_save_state(dev);
+ pci_cxl_hdm_cache_init(dev);
/*
* Enable runtime PM, which potentially allows the device to
diff --git a/drivers/pci/cxl.c b/drivers/pci/cxl.c
new file mode 100644
index 000000000000..a0d20f4210a5
--- /dev/null
+++ b/drivers/pci/cxl.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2026 NVIDIA Corporation & Affiliates */
+/*
+ * CXL reset support in the PCI core. Cache the HDM decoder state of a CXL
+ * memory device at enumeration, so it can be restored after a reset without
+ * a CXL driver. HDM register formats and programming live in drivers/cxl.
+ */
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <cxl/hdm.h>
+#include <cxl/pci.h>
+
+#include "pci.h"
+
+/* Find the CXL Device DVSEC and read its capability register. */
+static int cxl_pci_device_dvsec(struct pci_dev *pdev, u16 *dvsec, u16 *cap)
+{
+ int rc;
+
+ *dvsec = pci_find_dvsec_capability(pdev, PCI_VENDOR_ID_CXL,
+ PCI_DVSEC_CXL_DEVICE);
+ if (!*dvsec)
+ return -ENOTTY;
+
+ rc = pci_read_config_word(pdev, *dvsec + PCI_DVSEC_CXL_CAP, cap);
+ if (rc)
+ return pcibios_err_to_errno(rc);
+ if (PCI_POSSIBLE_ERROR(*cap))
+ return -ENODEV;
+
+ return 0;
+}
+
+static bool cxl_pci_bar_assigned(struct pci_dev *pdev, int bar)
+{
+ struct resource *res = &pdev->resource[bar];
+
+ return pci_resource_len(pdev, bar) &&
+ !(res->flags & (IORESOURCE_UNSET | IORESOURCE_DISABLED)) &&
+ resource_type(res) == IORESOURCE_MEM && res->start && res->end;
+}
+
+static void __iomem *cxl_pci_hdm_map_regs(struct pci_dev *pdev,
+ struct cxl_register_map *map,
+ resource_size_t *hdm_offset,
+ int *decoder_count)
+{
+ struct cxl_reg_map *hdm_map = &map->component_map.hdm_decoder;
+ void __iomem *hdm;
+ int rc;
+
+ /* cxl_setup_regs() populates hdm_map, including its valid bit. */
+ rc = cxl_setup_regs(map);
+ if (rc)
+ return ERR_PTR(rc);
+ if (!hdm_map->valid)
+ return ERR_PTR(-ENODEV);
+ if (hdm_map->offset > map->max_size ||
+ hdm_map->size > map->max_size - hdm_map->offset)
+ return ERR_PTR(-ENXIO);
+ if (check_add_overflow(map->bar_offset,
+ (resource_size_t)hdm_map->offset, hdm_offset))
+ return ERR_PTR(-EOVERFLOW);
+
+ hdm = ioremap(map->resource + hdm_map->offset, hdm_map->size);
+ if (!hdm)
+ return ERR_PTR(-ENOMEM);
+
+ *decoder_count = cxl_hdm_decoder_count_mapped(&pdev->dev, hdm,
+ hdm_map->size);
+ if (*decoder_count < 0) {
+ iounmap(hdm);
+ return ERR_PTR(*decoder_count);
+ }
+
+ return hdm;
+}
+
+static struct cxl_hdm_info *__cxl_pci_hdm_read_info(struct pci_dev *pdev,
+ struct cxl_register_map *map,
+ int dvsec)
+{
+ struct cxl_reg_map *hdm_map = &map->component_map.hdm_decoder;
+ struct cxl_hdm_info *info;
+ resource_size_t hdm_offset;
+ void __iomem *hdm;
+ int decoder_count;
+ u16 dvsec_ctrl;
+ int rc;
+
+ hdm = cxl_pci_hdm_map_regs(pdev, map, &hdm_offset, &decoder_count);
+ if (IS_ERR(hdm))
+ return ERR_CAST(hdm);
+
+ rc = pci_read_config_word(pdev, dvsec + PCI_DVSEC_CXL_CTRL,
+ &dvsec_ctrl);
+ if (rc) {
+ rc = pcibios_err_to_errno(rc);
+ goto out_unmap;
+ }
+
+ info = kzalloc_flex(*info, settings, decoder_count, GFP_KERNEL);
+ if (!info) {
+ rc = -ENOMEM;
+ goto out_unmap;
+ }
+
+ info->decoder_count = decoder_count;
+ info->hdm_bar = map->bar;
+ info->hdm_offset = hdm_offset;
+ info->hdm_size = hdm_map->size;
+ info->dvsec = dvsec;
+ info->dvsec_ctrl = dvsec_ctrl;
+ info->dvsec_ctrl_valid = true;
+
+ rc = cxl_hdm_read_state(&pdev->dev, hdm, info);
+ if (rc)
+ goto out_free;
+
+ iounmap(hdm);
+ return info;
+
+out_free:
+ kfree(info);
+out_unmap:
+ iounmap(hdm);
+ return ERR_PTR(rc);
+}
+
+static struct cxl_hdm_info *cxl_pci_hdm_read_info(struct pci_dev *pdev,
+ struct cxl_register_map *map,
+ int dvsec)
+{
+ struct cxl_hdm_info *info;
+ u16 command;
+ int rc;
+
+ rc = pci_read_config_word(pdev, PCI_COMMAND, &command);
+ if (rc)
+ return ERR_PTR(pcibios_err_to_errno(rc));
+
+ if (!(command & PCI_COMMAND_MEMORY)) {
+ rc = pci_write_config_word(pdev, PCI_COMMAND,
+ command | PCI_COMMAND_MEMORY);
+ if (rc)
+ return ERR_PTR(pcibios_err_to_errno(rc));
+ }
+
+ info = __cxl_pci_hdm_read_info(pdev, map, dvsec);
+ if (!(command & PCI_COMMAND_MEMORY)) {
+ rc = pci_write_config_word(pdev, PCI_COMMAND, command);
+ if (rc)
+ pci_err(pdev,
+ "failed to restore PCI_COMMAND after CXL HDM cache init: %d\n",
+ pcibios_err_to_errno(rc));
+ }
+ return info;
+}
+
+static int __pci_cxl_hdm_cache_init(struct pci_dev *pdev)
+{
+ struct cxl_register_map map = { };
+ struct cxl_hdm_info *read_info;
+ u16 dvsec, cap;
+ int rc;
+
+ guard(pci_dev)(pdev);
+
+ /* Avoid duplicate MMIO reads when the cache is already present. */
+ if (pdev->hdm)
+ return 0;
+
+ rc = cxl_pci_device_dvsec(pdev, &dvsec, &cap);
+ if (rc == -ENOTTY)
+ return 0;
+ if (rc)
+ return rc;
+ if (!(cap & PCI_DVSEC_CXL_CACHE_CAPABLE) ||
+ !(cap & PCI_DVSEC_CXL_MEM_CAPABLE) ||
+ !(cap & PCI_DVSEC_CXL_RST_CAPABLE) ||
+ !(cap & PCI_DVSEC_CXL_RST_MEM_CLR_CAPABLE) ||
+ !cxl_dvsec_hdm_count_valid(FIELD_GET(PCI_DVSEC_CXL_HDM_COUNT, cap)))
+ return 0;
+
+ rc = cxl_find_regblock(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
+ if (rc)
+ return rc;
+ if (map.bar < 0 || map.bar >= PCI_STD_NUM_BARS ||
+ !cxl_pci_bar_assigned(pdev, map.bar))
+ return -ENODEV;
+
+ read_info = cxl_pci_hdm_read_info(pdev, &map, dvsec);
+ if (IS_ERR(read_info))
+ return PTR_ERR(read_info);
+
+ /*
+ * The PCI device lock held since the cache check excludes other
+ * initializers. CXL drivers bind only after enumeration, so no cache
+ * mutator can run yet and cxl_rwsem.dpa is not needed to publish.
+ */
+ pdev->hdm = read_info;
+
+ return 0;
+}
+
+void pci_cxl_hdm_cache_init(struct pci_dev *pdev)
+{
+ int rc;
+
+ rc = __pci_cxl_hdm_cache_init(pdev);
+ if (rc)
+ pci_dbg(pdev, "CXL HDM cache init failed: %d\n", rc);
+}
+
+void pci_cxl_hdm_cache_release(struct pci_dev *pdev)
+{
+ cxl_hdm_cache_release(&pdev->hdm);
+}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index ba3c3fddddc2..9e218f236e66 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -730,6 +730,14 @@ static inline void pci_doe_destroy(struct pci_dev *pdev) { }
static inline void pci_doe_disconnected(struct pci_dev *pdev) { }
#endif
+#ifdef CONFIG_CXL_RESET
+void pci_cxl_hdm_cache_init(struct pci_dev *pdev);
+void pci_cxl_hdm_cache_release(struct pci_dev *pdev);
+#else
+static inline void pci_cxl_hdm_cache_init(struct pci_dev *pdev) { }
+static inline void pci_cxl_hdm_cache_release(struct pci_dev *pdev) { }
+#endif
+
#ifdef CONFIG_PCI_NPEM
void pci_npem_create(struct pci_dev *dev);
void pci_npem_remove(struct pci_dev *dev);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 27008e2ea5af..cb52357344ed 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2484,6 +2484,7 @@ static void pci_release_dev(struct device *dev)
struct pci_dev *pci_dev;
pci_dev = to_pci_dev(dev);
+ pci_cxl_hdm_cache_release(pci_dev);
pci_release_capabilities(pci_dev);
pci_release_of_node(pci_dev);
pcibios_release_device(pci_dev);
diff --git a/include/cxl/hdm.h b/include/cxl/hdm.h
index 06e9aa4479c2..0604dcc439c9 100644
--- a/include/cxl/hdm.h
+++ b/include/cxl/hdm.h
@@ -26,6 +26,7 @@ struct cxl_endpoint_decoder_settings {
* @hdm_offset: offset of the HDM decoder capability in @hdm_bar
* @hdm_size: size of the HDM decoder register block
* @global_ctrl: HDM decoder global control register
+ * @dvsec: config space offset of the CXL Device DVSEC
* @dvsec_ctrl: CXL DVSEC control register
* @dvsec_ctrl_valid: whether @dvsec_ctrl contains valid state
* @settings: per-endpoint-decoder programming state
@@ -39,13 +40,21 @@ struct cxl_hdm_info {
resource_size_t hdm_offset;
resource_size_t hdm_size;
u32 global_ctrl;
+ u16 dvsec;
u16 dvsec_ctrl;
bool dvsec_ctrl_valid;
struct cxl_endpoint_decoder_settings settings[] __counted_by(decoder_count);
};
+struct device;
+int cxl_hdm_decoder_count_mapped(struct device *dev, void __iomem *hdm,
+ resource_size_t size);
+int cxl_hdm_read_state(struct device *dev, void __iomem *hdm,
+ struct cxl_hdm_info *info);
+void cxl_hdm_cache_set_dvsec_ctrl(struct pci_dev *pdev, u16 ctrl);
void cxl_hdm_cache_refresh_decoder(struct pci_dev *pdev, void __iomem *hdm,
struct cxl_endpoint_decoder *cxled);
int cxl_hdm_cache_check_decoder_count(struct pci_dev *pdev, int count);
+void cxl_hdm_cache_release(struct cxl_hdm_info **slot);
#endif /* __CXL_HDM_H__ */
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index facaa324bd86..ca3e77b72d87 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -1352,6 +1352,8 @@
#define PCI_DVSEC_CXL_CACHE_CAPABLE _BITUL(0)
#define PCI_DVSEC_CXL_MEM_CAPABLE _BITUL(2)
#define PCI_DVSEC_CXL_HDM_COUNT __GENMASK(5, 4)
+#define PCI_DVSEC_CXL_RST_CAPABLE _BITUL(7)
+#define PCI_DVSEC_CXL_RST_MEM_CLR_CAPABLE _BITUL(11)
#define PCI_DVSEC_CXL_CTRL 0xC
#define PCI_DVSEC_CXL_MEM_ENABLE _BITUL(2)
#define PCI_DVSEC_CXL_RANGE_SIZE_HIGH(i) (0x18 + (i * 0x10))
--
2.43.0
next prev parent reply other threads:[~2026-11-05 18:59 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 18:58 [PATCH v17 00/19] PCI/CXL: Add CXL reset support for Type 2 devices Srirangan Madhavan
2026-11-05 18:58 ` [PATCH v17 01/19] cxl: Clarify decoder interleave limit Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 02/19] cxl: Share CXL port upstream PCI device lookup Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 03/19] cxl: Move decoder declarations to shared header Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 04/19] cxl: Embed decoder configuration in a standalone structure Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 05/19] cxl: Introduce endpoint HDM decoder settings Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 06/19] cxl: Move HDM decoder helpers to built-in code Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 07/19] cxl: Share HDM decoder register unpacking Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 08/19] cxl: Move cxl_rwsem into built-in code Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 09/19] cxl: Refresh cached PCI HDM decoder settings Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 10/19] cxl: Build register block helpers into the kernel Srirangan Madhavan
2026-11-05 18:59 ` Srirangan Madhavan [this message]
2026-11-05 18:59 ` [PATCH v17 12/19] PCI/CXL: Add CXL Device Reset sequencing Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 13/19] cxl: Validate and synchronize HDM ranges around reset Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 14/19] PCI/CXL: Reject reset with unsafe function scope Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 15/19] PCI/CXL: Restore CXL state after PCI reset Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 16/19] PCI/CXL: Expose CXL Reset as a PCI reset method Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 17/19] PCI/CXL: Restore CXL state after CXL bus reset Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 18/19] PCI/CXL: Flush host and device caches for " Srirangan Madhavan
2026-11-05 18:59 ` [PATCH v17 19/19] cxl: Add KUnit checks for HDM decoder unpacking Srirangan Madhavan
2026-11-05 19:31 ` [PATCH v17 00/19] PCI/CXL: Add CXL reset support for Type 2 devices Alison Schofield
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261105185917.2667656-12-smadhavan@nvidia.com \
--to=smadhavan@nvidia.com \
--cc=alex.williamson@redhat.com \
--cc=alison.schofield@intel.com \
--cc=alwilliamson@nvidia.com \
--cc=bhelgaas@google.com \
--cc=dave.jiang@intel.com \
--cc=dave@stgolabs.net \
--cc=icheng@nvidia.com \
--cc=iweiny@kernel.org \
--cc=jan@nvidia.com \
--cc=jic23@kernel.org \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=machaujo@amazon.de \
--cc=mhonap@nvidia.com \
--cc=ming.li@zohomail.com \
--cc=skancherla@nvidia.com \
--cc=vaslot@nvidia.com \
--cc=vishal.l.verma@intel.com \
--cc=vsethi@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®