From: <smadhavan@nvidia.com>
To: <dave@stgolabs.net>, <jonathan.cameron@huawei.com>,
<dave.jiang@intel.com>, <alison.schofield@intel.com>,
<vishal.l.verma@intel.com>, <ira.weiny@intel.com>,
<dan.j.williams@intel.com>, <bhelgaas@google.com>,
<ming.li@zohomail.com>, <rrichter@amd.com>,
<Smita.KoralahalliChannabasappa@amd.com>,
<huaisheng.ye@intel.com>, <linux-cxl@vger.kernel.org>,
<linux-pci@vger.kernel.org>
Cc: <smadhavan@nvidia.com>, <vaslot@nvidia.com>, <vsethi@nvidia.com>,
<sdonthineni@nvidia.com>, <vidyas@nvidia.com>, <mochs@nvidia.com>,
<jsequeira@nvidia.com>
Subject: [PATCH v4 10/10] cxl: add HDM decoder and IDE save/restore
Date: Tue, 20 Jan 2026 22:26:10 +0000 [thread overview]
Message-ID: <20260120222610.2227109-11-smadhavan@nvidia.com> (raw)
In-Reply-To: <20260120222610.2227109-1-smadhavan@nvidia.com>
From: Srirangan Madhavan <smadhavan@nvidia.com>
Extend state save/restore to HDM decoder and IDE registers for Type 2
devices. The HDM/IDE blocks are located via the component register map,
then preserved across reset to retain decoder configuration and IDE
policy. This avoids losing HDM/IDE programming when cxl_reset is issued.
Signed-off-by: Srirangan Madhavan <smadhavan@nvidia.com>
---
drivers/cxl/core/regs.c | 7 ++
drivers/cxl/cxl.h | 4 ++
drivers/cxl/pci.c | 153 ++++++++++++++++++++++++++++++++++++++--
include/cxl/pci.h | 43 +++++++++++
4 files changed, 201 insertions(+), 6 deletions(-)
diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index ecdb22ae6952..76d6869d82ea 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -93,6 +93,12 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
length = CXL_RAS_CAPABILITY_LENGTH;
rmap = &map->ras;
break;
+ case CXL_CM_CAP_CAP_ID_IDE:
+ dev_dbg(dev, "found IDE capability (0x%x)\n",
+ offset);
+ length = CXL_IDE_CAPABILITY_LENGTH;
+ rmap = &map->ide;
+ break;
default:
dev_dbg(dev, "Unknown CM cap ID: %d (0x%x)\n", cap_id,
offset);
@@ -212,6 +218,7 @@ int cxl_map_component_regs(const struct cxl_register_map *map,
} mapinfo[] = {
{ &map->component_map.hdm_decoder, ®s->hdm_decoder },
{ &map->component_map.ras, ®s->ras },
+ { &map->component_map.ide, ®s->ide },
};
int i;
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index ba17fa86d249..a7a6b79755b3 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -39,8 +39,10 @@ extern const struct nvdimm_security_ops *cxl_security_ops;
#define CXL_CM_CAP_PTR_MASK GENMASK(31, 20)
#define CXL_CM_CAP_CAP_ID_RAS 0x2
+#define CXL_CM_CAP_CAP_ID_IDE 0x4
#define CXL_CM_CAP_CAP_ID_HDM 0x5
#define CXL_CM_CAP_CAP_HDM_VERSION 1
+#define CXL_IDE_CAPABILITY_LENGTH 0x20
/* HDM decoders CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure */
#define CXL_HDM_DECODER_CAP_OFFSET 0x0
@@ -214,6 +216,7 @@ struct cxl_regs {
struct_group_tagged(cxl_component_regs, component,
void __iomem *hdm_decoder;
void __iomem *ras;
+ void __iomem *ide;
);
/*
* Common set of CXL Device register block base pointers
@@ -256,6 +259,7 @@ struct cxl_reg_map {
struct cxl_component_reg_map {
struct cxl_reg_map hdm_decoder;
struct cxl_reg_map ras;
+ struct cxl_reg_map ide;
};
struct cxl_device_reg_map {
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 7d6a0ef70b2d..eb735d0ae175 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -956,7 +956,8 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev_dbg(&pdev->dev, "RAS registers not found\n");
rc = cxl_map_component_regs(&cxlds->reg_map, &cxlds->regs.component,
- BIT(CXL_CM_CAP_CAP_ID_RAS));
+ BIT(CXL_CM_CAP_CAP_ID_RAS) |
+ BIT(CXL_CM_CAP_CAP_ID_IDE));
if (rc)
dev_dbg(&pdev->dev, "Failed to map RAS capability.\n");
@@ -1203,18 +1204,126 @@ static int cxl_restore_dvsec_state(struct pci_dev *pdev,
return rc;
}
+/*
+ * CXL HDM Decoder register save/restore
+ */
+static int cxl_save_hdm_state(struct cxl_dev_state *cxlds,
+ struct cxl_type2_saved_state *state)
+{
+ void __iomem *hdm = cxlds->regs.hdm_decoder;
+ u32 cap, ctrl;
+ int i, count;
+
+ if (!hdm)
+ return 0;
+
+ cap = readl(hdm + CXL_HDM_DECODER_CAP_OFFSET);
+ count = cap & CXL_HDM_DECODER_COUNT_MASK;
+ count = min(count, CXL_MAX_DECODERS);
+
+ state->hdm_decoder_count = count;
+ state->hdm_global_ctrl = readl(hdm + CXL_HDM_DECODER_GLOBAL_CTRL_OFFSET);
+
+ for (i = 0; i < count; i++) {
+ struct cxl_hdm_decoder_state *d = &state->decoders[i];
+ u32 base_low, base_high, size_low, size_high;
+ u32 dpa_skip_low, dpa_skip_high;
+
+ base_low = readl(hdm + CXL_HDM_DECODER_BASE_LOW(i));
+ base_high = readl(hdm + CXL_HDM_DECODER_BASE_HIGH(i));
+ size_low = readl(hdm + CXL_HDM_DECODER_SIZE_LOW(i));
+ size_high = readl(hdm + CXL_HDM_DECODER_SIZE_HIGH(i));
+ ctrl = readl(hdm + CXL_HDM_DECODER_CTRL(i));
+ dpa_skip_low = readl(hdm + CXL_HDM_DECODER_DPA_SKIP_LOW(i));
+ dpa_skip_high = readl(hdm + CXL_HDM_DECODER_DPA_SKIP_HIGH(i));
+
+ d->base = ((u64)base_high << 32) | base_low;
+ d->size = ((u64)size_high << 32) | size_low;
+ d->ctrl = ctrl;
+ d->dpa_skip = ((u64)dpa_skip_high << 32) | dpa_skip_low;
+ d->enabled = !!(ctrl & CXL_HDM_DECODER_ENABLE);
+ }
+
+ return 0;
+}
+
+static int cxl_restore_hdm_state(struct cxl_dev_state *cxlds,
+ const struct cxl_type2_saved_state *state)
+{
+ void __iomem *hdm = cxlds->regs.hdm_decoder;
+ int i;
+
+ if (!hdm || state->hdm_decoder_count == 0)
+ return 0;
+
+ writel(state->hdm_global_ctrl, hdm + CXL_HDM_DECODER_GLOBAL_CTRL_OFFSET);
+
+ for (i = 0; i < state->hdm_decoder_count; i++) {
+ const struct cxl_hdm_decoder_state *d = &state->decoders[i];
+
+ writel((u32)d->base, hdm + CXL_HDM_DECODER_BASE_LOW(i));
+ writel((u32)(d->base >> 32), hdm + CXL_HDM_DECODER_BASE_HIGH(i));
+ writel((u32)d->size, hdm + CXL_HDM_DECODER_SIZE_LOW(i));
+ writel((u32)(d->size >> 32), hdm + CXL_HDM_DECODER_SIZE_HIGH(i));
+ writel(d->ctrl, hdm + CXL_HDM_DECODER_CTRL(i));
+ writel((u32)d->dpa_skip, hdm + CXL_HDM_DECODER_DPA_SKIP_LOW(i));
+ writel((u32)(d->dpa_skip >> 32), hdm + CXL_HDM_DECODER_DPA_SKIP_HIGH(i));
+ }
+
+ return 0;
+}
+
+/*
+ * CXL IDE register save/restore
+ */
+static int cxl_save_ide_state(struct cxl_dev_state *cxlds,
+ struct cxl_type2_saved_state *state)
+{
+ void __iomem *ide = cxlds->regs.ide;
+ u32 cap;
+
+ if (!ide)
+ return 0;
+
+ cap = readl(ide + CXL_IDE_CAP_OFFSET);
+ if (!(cap & CXL_IDE_CAP_CAPABLE))
+ return 0;
+
+ state->ide_cap = cap;
+ state->ide_ctrl = readl(ide + CXL_IDE_CTRL_OFFSET);
+ state->ide_key_refresh_time = readl(ide + CXL_IDE_KEY_REFRESH_TIME_CTRL_OFFSET);
+ state->ide_truncation_delay = readl(ide + CXL_IDE_TRUNCATION_DELAY_CTRL_OFFSET);
+
+ return 0;
+}
+
+static int cxl_restore_ide_state(struct cxl_dev_state *cxlds,
+ const struct cxl_type2_saved_state *state)
+{
+ void __iomem *ide = cxlds->regs.ide;
+
+ if (!ide || !(state->ide_cap & CXL_IDE_CAP_CAPABLE))
+ return 0;
+
+ writel(state->ide_ctrl, ide + CXL_IDE_CTRL_OFFSET);
+ writel(state->ide_key_refresh_time, ide + CXL_IDE_KEY_REFRESH_TIME_CTRL_OFFSET);
+ writel(state->ide_truncation_delay, ide + CXL_IDE_TRUNCATION_DELAY_CTRL_OFFSET);
+
+ return 0;
+}
+
/**
* cxl_config_save_state - Save CXL configuration state
* @pdev: PCI device
* @state: Structure to store saved state
*
- * Saves CXL DVSEC state before reset.
+ * Saves CXL DVSEC, HDM decoder, and IDE state before reset.
*/
int cxl_config_save_state(struct pci_dev *pdev,
struct cxl_type2_saved_state *state)
{
struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
- int dvsec;
+ int rc, dvsec;
if (!cxlds || !state)
return -EINVAL;
@@ -1225,7 +1334,23 @@ int cxl_config_save_state(struct pci_dev *pdev,
if (!dvsec)
return -ENODEV;
- return cxl_save_dvsec_state(pdev, state, dvsec);
+ rc = cxl_save_dvsec_state(pdev, state, dvsec);
+ if (rc)
+ return rc;
+
+ if (cxlds->regs.hdm_decoder) {
+ rc = cxl_save_hdm_state(cxlds, state);
+ if (rc)
+ pci_warn(pdev, "Failed to save HDM state: %d\n", rc);
+ }
+
+ if (cxlds->regs.ide) {
+ rc = cxl_save_ide_state(cxlds, state);
+ if (rc)
+ pci_warn(pdev, "Failed to save IDE state: %d\n", rc);
+ }
+
+ return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_config_save_state, "CXL");
@@ -1234,7 +1359,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_config_save_state, "CXL");
* @pdev: PCI device
* @state: Previously saved state
*
- * Restores CXL DVSEC state after reset.
+ * Restores CXL DVSEC, HDM decoder, and IDE state after reset.
*/
int cxl_config_restore_state(struct pci_dev *pdev,
const struct cxl_type2_saved_state *state)
@@ -1257,7 +1382,23 @@ int cxl_config_restore_state(struct pci_dev *pdev,
config_locked = !!(lock_reg & CXL_DVSEC_LOCK_CONFIG_LOCK);
- return cxl_restore_dvsec_state(pdev, state, dvsec, config_locked);
+ rc = cxl_restore_dvsec_state(pdev, state, dvsec, config_locked);
+ if (rc)
+ return rc;
+
+ if (cxlds->regs.hdm_decoder && state->hdm_decoder_count > 0) {
+ rc = cxl_restore_hdm_state(cxlds, state);
+ if (rc)
+ pci_warn(pdev, "Failed to restore HDM state: %d\n", rc);
+ }
+
+ if (cxlds->regs.ide && (state->ide_cap & CXL_IDE_CAP_CAPABLE)) {
+ rc = cxl_restore_ide_state(cxlds, state);
+ if (rc)
+ pci_warn(pdev, "Failed to restore IDE state: %d\n", rc);
+ }
+
+ return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_config_restore_state, "CXL");
diff --git a/include/cxl/pci.h b/include/cxl/pci.h
index 2c629ded73cc..9f2a9ad10d75 100644
--- a/include/cxl/pci.h
+++ b/include/cxl/pci.h
@@ -4,11 +4,33 @@
#ifndef __CXL_ACCEL_PCI_H
#define __CXL_ACCEL_PCI_H
+/* HDM Decoder state for save/restore */
+struct cxl_hdm_decoder_state {
+ u64 base;
+ u64 size;
+ u32 ctrl;
+ u64 dpa_skip;
+ bool enabled;
+};
+
+#define CXL_MAX_DECODERS 10
+
/* CXL Type 2 device state for save/restore across reset */
struct cxl_type2_saved_state {
/* DVSEC registers */
u16 dvsec_ctrl;
u16 dvsec_ctrl2;
+
+ /* HDM Decoder registers */
+ u32 hdm_decoder_count;
+ u32 hdm_global_ctrl;
+ struct cxl_hdm_decoder_state decoders[CXL_MAX_DECODERS];
+
+ /* IDE registers */
+ u32 ide_cap;
+ u32 ide_ctrl;
+ u32 ide_key_refresh_time;
+ u32 ide_truncation_delay;
};
int cxl_config_save_state(struct pci_dev *pdev,
@@ -58,6 +80,27 @@ int cxl_config_restore_state(struct pci_dev *pdev,
#define CXL_DVSEC_RANGE_MAX 2
+/* CXL HDM Decoder Capability Structure (Section 8.2.4.20) */
+#define CXL_HDM_DECODER_CAP_OFFSET 0x0
+#define CXL_HDM_DECODER_COUNT_MASK GENMASK(3, 0)
+#define CXL_HDM_DECODER_GLOBAL_CTRL_OFFSET 0x4
+#define CXL_HDM_DECODER_ENABLE BIT(1)
+/* CXL HDM Decoder n registers (Offset 20h*n + base) */
+#define CXL_HDM_DECODER_BASE_LOW(n) (0x10 + ((n) * 0x20))
+#define CXL_HDM_DECODER_BASE_HIGH(n) (0x14 + ((n) * 0x20))
+#define CXL_HDM_DECODER_SIZE_LOW(n) (0x18 + ((n) * 0x20))
+#define CXL_HDM_DECODER_SIZE_HIGH(n) (0x1C + ((n) * 0x20))
+#define CXL_HDM_DECODER_CTRL(n) (0x20 + ((n) * 0x20))
+#define CXL_HDM_DECODER_DPA_SKIP_LOW(n) (0x24 + ((n) * 0x20))
+#define CXL_HDM_DECODER_DPA_SKIP_HIGH(n) (0x28 + ((n) * 0x20))
+
+/* CXL IDE Capability Structure (Section 8.2.4.22) */
+#define CXL_IDE_CAP_OFFSET 0x00
+#define CXL_IDE_CAP_CAPABLE BIT(0)
+#define CXL_IDE_CTRL_OFFSET 0x04
+#define CXL_IDE_KEY_REFRESH_TIME_CTRL_OFFSET 0x18
+#define CXL_IDE_TRUNCATION_DELAY_CTRL_OFFSET 0x1C
+
/* CXL 2.0 8.1.4: Non-CXL Function Map DVSEC */
#define CXL_DVSEC_FUNCTION_MAP 2
--
2.34.1
next prev parent reply other threads:[~2026-01-20 22:27 UTC|newest]
Thread overview: 48+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-20 22:26 [PATCH v4 0/10] CXL Reset support for Type 2 devices smadhavan
2026-01-20 22:26 ` [PATCH v4 01/10] cxl: move DVSEC defines to cxl pci header smadhavan
2026-01-21 10:31 ` Jonathan Cameron
2026-01-20 22:26 ` [PATCH v4 02/10] PCI: switch CXL port DVSEC defines smadhavan
2026-01-21 10:34 ` Jonathan Cameron
2026-01-20 22:26 ` [PATCH v4 03/10] cxl: add type 2 helper and reset DVSEC bits smadhavan
2026-01-20 23:27 ` Dave Jiang
2026-01-21 10:45 ` Jonathan Cameron
2026-01-20 22:26 ` [PATCH v4 04/10] PCI: add CXL reset method smadhavan
2026-01-21 0:08 ` Dave Jiang
2026-01-21 10:57 ` Jonathan Cameron
2026-01-23 13:54 ` kernel test robot
2026-01-20 22:26 ` [PATCH v4 05/10] cxl: add reset prepare and region teardown smadhavan
2026-01-21 11:09 ` Jonathan Cameron
2026-01-21 21:25 ` Dave Jiang
2026-01-20 22:26 ` [PATCH v4 06/10] PCI: wire CXL reset prepare/cleanup smadhavan
2026-01-21 22:13 ` Dave Jiang
2026-01-22 2:17 ` Srirangan Madhavan
2026-01-22 15:11 ` Dave Jiang
2026-01-24 7:54 ` kernel test robot
2026-01-20 22:26 ` [PATCH v4 07/10] cxl: add host cache flush and multi-function reset smadhavan
2026-01-21 11:20 ` Jonathan Cameron
2026-01-21 20:27 ` Davidlohr Bueso
2026-01-22 9:53 ` Jonathan Cameron
2026-01-21 22:19 ` Vikram Sethi
2026-01-22 9:40 ` Souvik Chakravarty
[not found] ` <PH7PR12MB9175CDFC163843BB497073CEBD96A@PH7PR12MB9175.namprd12.prod.outlook.com>
2026-01-22 10:31 ` Jonathan Cameron
2026-01-22 19:24 ` Vikram Sethi
2026-01-23 13:13 ` Jonathan Cameron
2026-01-21 23:59 ` Dave Jiang
2026-01-20 22:26 ` [PATCH v4 08/10] cxl: add DVSEC config save/restore smadhavan
2026-01-21 11:31 ` Jonathan Cameron
2026-01-20 22:26 ` [PATCH v4 09/10] PCI: save/restore CXL config around reset smadhavan
2026-01-21 22:32 ` Dave Jiang
2026-01-22 10:01 ` Lukas Wunner
2026-01-22 10:47 ` Jonathan Cameron
2026-01-26 22:34 ` Alex Williamson
2026-03-12 18:24 ` Jonathan Cameron
2026-01-20 22:26 ` smadhavan [this message]
2026-01-21 11:42 ` [PATCH v4 10/10] cxl: add HDM decoder and IDE save/restore Jonathan Cameron
2026-01-22 15:09 ` Dave Jiang
2026-01-21 1:19 ` [PATCH v4 0/10] CXL Reset support for Type 2 devices Alison Schofield
2026-01-22 0:00 ` Bjorn Helgaas
2026-01-27 16:33 ` Alex Williamson
2026-01-27 17:02 ` dan.j.williams
2026-01-27 18:07 ` Vikram Sethi
2026-01-28 3:42 ` dan.j.williams
2026-01-28 12:36 ` Jonathan Cameron
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=20260120222610.2227109-11-smadhavan@nvidia.com \
--to=smadhavan@nvidia.com \
--cc=Smita.KoralahalliChannabasappa@amd.com \
--cc=alison.schofield@intel.com \
--cc=bhelgaas@google.com \
--cc=dan.j.williams@intel.com \
--cc=dave.jiang@intel.com \
--cc=dave@stgolabs.net \
--cc=huaisheng.ye@intel.com \
--cc=ira.weiny@intel.com \
--cc=jonathan.cameron@huawei.com \
--cc=jsequeira@nvidia.com \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=ming.li@zohomail.com \
--cc=mochs@nvidia.com \
--cc=rrichter@amd.com \
--cc=sdonthineni@nvidia.com \
--cc=vaslot@nvidia.com \
--cc=vidyas@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