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 <ira.weiny@intel.com>,
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: 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 v10 02/12] cxl: Pass decoder settings to HDM commit helpers
Date: Tue, 4 Aug 2026 19:29:48 +0000 [thread overview]
Message-ID: <20260804192958.1823952-3-smadhavan@nvidia.com> (raw)
In-Reply-To: <20260804192958.1823952-1-smadhavan@nvidia.com>
Introduce struct cxl_decoder_settings as a plain snapshot of HDM decoder
programming state and pass it to the shared commit helpers. Keep endpoint
skip and switch target-list handling in hdm.c, where the endpoint and
switch decoder types are available.
Split the helper entry points into cxl_commit_start() and
cxl_commit_wait() so hdm.c keeps the existing DPA-lock and commit policy
flow while later reset restore code can reuse the register programming
sequence.
Signed-off-by: Srirangan Madhavan <smadhavan@nvidia.com>
---
drivers/cxl/core/core.h | 5 +-
drivers/cxl/core/hdm.c | 72 +++++++++++++++++-----
drivers/cxl/core/resource.c | 115 ++++++++++++++++--------------------
include/cxl/cxl.h | 20 +++++++
4 files changed, 133 insertions(+), 79 deletions(-)
diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index cb6853a92a93..1426254e6657 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -211,10 +211,11 @@ static inline void devm_cxl_dport_ras_setup(struct cxl_dport *dport) { }
int cxl_gpf_port_setup(struct cxl_dport *dport);
struct cxl_hdm;
+struct cxl_decoder_settings;
int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
struct cxl_endpoint_dvsec_info *info);
-void cxl_setup_hw_decoder(struct cxl_decoder *cxld, void __iomem *hdm);
-int cxld_await_commit(void __iomem *hdm, int id);
+int cxl_commit_start(struct cxl_decoder_settings *settings, void __iomem *hdm);
+int cxl_commit_wait(struct cxl_decoder_settings *settings, void __iomem *hdm);
int cxl_port_get_possible_dports(struct cxl_port *port);
#ifdef CONFIG_CXL_FEATURES
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 6ae0b9f46ac0..9047b190c35a 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -16,7 +16,6 @@
* for enumerating these registers and capabilities.
*/
-
static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld)
{
int rc;
@@ -675,12 +674,44 @@ int cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, u64 size)
return devm_add_action_or_reset(&port->dev, cxl_dpa_release, cxled);
}
+static void cxlsd_set_targets(struct cxl_switch_decoder *cxlsd, u64 *tgt)
+{
+ struct cxl_dport **t = &cxlsd->target[0];
+ int ways = cxlsd->cxld.interleave_ways;
+
+ *tgt = FIELD_PREP(GENMASK(7, 0), t[0]->port_id);
+ if (ways > 1)
+ *tgt |= FIELD_PREP(GENMASK(15, 8), t[1]->port_id);
+ if (ways > 2)
+ *tgt |= FIELD_PREP(GENMASK(23, 16), t[2]->port_id);
+ if (ways > 3)
+ *tgt |= FIELD_PREP(GENMASK(31, 24), t[3]->port_id);
+ if (ways > 4)
+ *tgt |= FIELD_PREP(GENMASK_ULL(39, 32), t[4]->port_id);
+ if (ways > 5)
+ *tgt |= FIELD_PREP(GENMASK_ULL(47, 40), t[5]->port_id);
+ if (ways > 6)
+ *tgt |= FIELD_PREP(GENMASK_ULL(55, 48), t[6]->port_id);
+ if (ways > 7)
+ *tgt |= FIELD_PREP(GENMASK_ULL(63, 56), t[7]->port_id);
+}
+
static int cxl_decoder_commit(struct cxl_decoder *cxld)
{
struct cxl_port *port = to_cxl_port(cxld->dev.parent);
struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
- int id = cxld->id, rc;
+ struct cxl_endpoint_decoder *cxled = NULL;
+ struct cxl_switch_decoder *cxlsd = NULL;
+ struct cxl_decoder_settings settings = {
+ .id = cxld->id,
+ .hpa_range = cxld->hpa_range,
+ .interleave_ways = cxld->interleave_ways,
+ .interleave_granularity = cxld->interleave_granularity,
+ .target_type = cxld->target_type,
+ .flags = cxld->flags,
+ };
+ int id = cxld->id, rc = 0;
if (cxld->flags & CXL_DECODER_F_ENABLE)
return 0;
@@ -693,29 +724,42 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
return -EBUSY;
}
- /*
- * For endpoint decoders hosted on CXL memory devices that
- * support the sanitize operation, make sure sanitize is not in-flight.
- */
if (is_endpoint_decoder(&cxld->dev)) {
- struct cxl_endpoint_decoder *cxled =
- to_cxl_endpoint_decoder(&cxld->dev);
- struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
- struct cxl_memdev_state *mds =
- to_cxl_memdev_state(cxlmd->cxlds);
+ struct cxl_memdev *cxlmd;
+ struct cxl_memdev_state *mds;
+ cxled = to_cxl_endpoint_decoder(&cxld->dev);
+ cxlmd = cxled_to_memdev(cxled);
+ mds = to_cxl_memdev_state(cxlmd->cxlds);
+ /*
+ * For endpoint decoders hosted on CXL memory devices that
+ * support the sanitize operation, make sure sanitize is not in-flight.
+ */
if (mds && mds->security.sanitize_active) {
dev_dbg(&cxlmd->dev,
"attempted to commit %s during sanitize\n",
dev_name(&cxld->dev));
return -EBUSY;
}
+ } else if (is_switch_decoder(&cxld->dev)) {
+ cxlsd = to_cxl_switch_decoder(&cxld->dev);
}
- scoped_guard(rwsem_read, &cxl_rwsem.dpa)
- cxl_setup_hw_decoder(cxld, hdm);
+ scoped_guard(rwsem_read, &cxl_rwsem.dpa) {
+ if (cxled)
+ settings.target_or_skip = cxled->skip;
+ else if (cxlsd)
+ cxlsd_set_targets(cxlsd, &settings.target_or_skip);
+
+ rc = cxl_commit_start(&settings, hdm);
+ }
+ if (rc) {
+ dev_dbg(&port->dev, "%s: error %d committing decoder\n",
+ dev_name(&cxld->dev), rc);
+ return rc;
+ }
- rc = cxld_await_commit(hdm, cxld->id);
+ rc = cxl_commit_wait(&settings, hdm);
if (rc) {
dev_dbg(&port->dev, "%s: error %d committing decoder\n",
dev_name(&cxld->dev), rc);
diff --git a/drivers/cxl/core/resource.c b/drivers/cxl/core/resource.c
index dd8bed3d3ff0..dd5e0cc82da4 100644
--- a/drivers/cxl/core/resource.c
+++ b/drivers/cxl/core/resource.c
@@ -15,21 +15,22 @@ struct cxl_rwsem cxl_rwsem = {
};
EXPORT_SYMBOL_FOR_MODULES(cxl_rwsem, "cxl_core");
-static void cxld_set_interleave(struct cxl_decoder *cxld, u32 *ctrl)
+static void cxld_set_interleave(struct cxl_decoder_settings *settings, u32 *ctrl)
{
u16 eig;
u8 eiw;
/*
* Input validation ensures these warns never fire, but otherwise
- * suppress unititalized variable usage warnings.
+ * suppress uninitialized variable usage warnings.
*/
- if (WARN_ONCE(ways_to_eiw(cxld->interleave_ways, &eiw),
- "invalid interleave_ways: %d\n", cxld->interleave_ways))
+ if (WARN_ONCE(ways_to_eiw(settings->interleave_ways, &eiw),
+ "invalid interleave_ways: %d\n",
+ settings->interleave_ways))
return;
- if (WARN_ONCE(granularity_to_eig(cxld->interleave_granularity, &eig),
+ if (WARN_ONCE(granularity_to_eig(settings->interleave_granularity, &eig),
"invalid interleave_granularity: %d\n",
- cxld->interleave_granularity))
+ settings->interleave_granularity))
return;
u32p_replace_bits(ctrl, eig, CXL_HDM_DECODER0_CTRL_IG_MASK);
@@ -37,42 +38,20 @@ static void cxld_set_interleave(struct cxl_decoder *cxld, u32 *ctrl)
*ctrl |= CXL_HDM_DECODER0_CTRL_COMMIT;
}
-static void cxld_set_type(struct cxl_decoder *cxld, u32 *ctrl)
+static void cxld_set_type(struct cxl_decoder_settings *settings, u32 *ctrl)
{
u32p_replace_bits(ctrl,
- !!(cxld->target_type == CXL_DECODER_HOSTONLYMEM),
+ !!(settings->target_type == CXL_DECODER_HOSTONLYMEM),
CXL_HDM_DECODER0_CTRL_HOSTONLY);
}
-static void cxlsd_set_targets(struct cxl_switch_decoder *cxlsd, u64 *tgt)
-{
- struct cxl_dport **t = &cxlsd->target[0];
- int ways = cxlsd->cxld.interleave_ways;
-
- *tgt = FIELD_PREP(GENMASK(7, 0), t[0]->port_id);
- if (ways > 1)
- *tgt |= FIELD_PREP(GENMASK(15, 8), t[1]->port_id);
- if (ways > 2)
- *tgt |= FIELD_PREP(GENMASK(23, 16), t[2]->port_id);
- if (ways > 3)
- *tgt |= FIELD_PREP(GENMASK(31, 24), t[3]->port_id);
- if (ways > 4)
- *tgt |= FIELD_PREP(GENMASK_ULL(39, 32), t[4]->port_id);
- if (ways > 5)
- *tgt |= FIELD_PREP(GENMASK_ULL(47, 40), t[5]->port_id);
- if (ways > 6)
- *tgt |= FIELD_PREP(GENMASK_ULL(55, 48), t[6]->port_id);
- if (ways > 7)
- *tgt |= FIELD_PREP(GENMASK_ULL(63, 56), t[7]->port_id);
-}
-
/*
* Per CXL 2.0 8.2.5.12.20 Committing Decoder Programming, hardware must set
* committed or error within 10ms, but just be generous with 20ms to account for
- * clock skew and other marginal behavior
+ * clock skew and other marginal behavior.
*/
#define COMMIT_TIMEOUT_MS 20
-int cxld_await_commit(void __iomem *hdm, int id)
+static int cxld_await_commit(void __iomem *hdm, int id)
{
u32 ctrl;
int i;
@@ -92,47 +71,57 @@ int cxld_await_commit(void __iomem *hdm, int id)
return -ETIMEDOUT;
}
-EXPORT_SYMBOL_FOR_MODULES(cxld_await_commit, "cxl_core");
-
-void cxl_setup_hw_decoder(struct cxl_decoder *cxld, void __iomem *hdm)
+static int setup_hw_decoder(struct cxl_decoder_settings *settings,
+ void __iomem *hdm)
{
- int id = cxld->id;
+ int id = settings->id;
+ u64 target_or_skip;
u64 base, size;
u32 ctrl;
- /* common decoder settings */
- ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id));
- cxld_set_interleave(cxld, &ctrl);
- cxld_set_type(cxld, &ctrl);
- base = cxld->hpa_range.start;
- size = range_len(&cxld->hpa_range);
+ ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
+ if (ctrl & CXL_HDM_DECODER0_CTRL_COMMITTED)
+ return -EBUSY;
+ if (ctrl & CXL_HDM_DECODER0_CTRL_COMMIT)
+ return -ETIMEDOUT;
+ if (ctrl & CXL_HDM_DECODER0_CTRL_COMMIT_ERROR)
+ return -EIO;
+ cxld_set_interleave(settings, &ctrl);
+ cxld_set_type(settings, &ctrl);
+ base = settings->hpa_range.start;
+ size = range_len(&settings->hpa_range);
+ target_or_skip = settings->target_or_skip;
writel(upper_32_bits(base), hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(id));
writel(lower_32_bits(base), hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id));
writel(upper_32_bits(size), hdm + CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(id));
writel(lower_32_bits(size), hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(id));
-
- if (is_switch_decoder(&cxld->dev)) {
- struct cxl_switch_decoder *cxlsd =
- to_cxl_switch_decoder(&cxld->dev);
- void __iomem *tl_hi = hdm + CXL_HDM_DECODER0_TL_HIGH(id);
- void __iomem *tl_lo = hdm + CXL_HDM_DECODER0_TL_LOW(id);
- u64 targets;
-
- cxlsd_set_targets(cxlsd, &targets);
- writel(upper_32_bits(targets), tl_hi);
- writel(lower_32_bits(targets), tl_lo);
- } else {
- struct cxl_endpoint_decoder *cxled =
- to_cxl_endpoint_decoder(&cxld->dev);
- void __iomem *sk_hi = hdm + CXL_HDM_DECODER0_SKIP_HIGH(id);
- void __iomem *sk_lo = hdm + CXL_HDM_DECODER0_SKIP_LOW(id);
-
- writel(upper_32_bits(cxled->skip), sk_hi);
- writel(lower_32_bits(cxled->skip), sk_lo);
- }
+ /* Target-list and endpoint-skip registers alias the same slot. */
+ writel(upper_32_bits(target_or_skip),
+ hdm + CXL_HDM_DECODER0_TL_HIGH(id));
+ writel(lower_32_bits(target_or_skip),
+ hdm + CXL_HDM_DECODER0_TL_LOW(id));
writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
+
+ return 0;
}
-EXPORT_SYMBOL_FOR_MODULES(cxl_setup_hw_decoder, "cxl_core");
+int cxl_commit_start(struct cxl_decoder_settings *settings, void __iomem *hdm)
+{
+ lockdep_assert_held(&cxl_rwsem.dpa);
+ return setup_hw_decoder(settings, hdm);
+}
+EXPORT_SYMBOL_FOR_MODULES(cxl_commit_start, "cxl_core");
+
+int cxl_commit_wait(struct cxl_decoder_settings *settings, void __iomem *hdm)
+{
+ int rc;
+
+ rc = cxld_await_commit(hdm, settings->id);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+EXPORT_SYMBOL_FOR_MODULES(cxl_commit_wait, "cxl_core");
diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h
index 05a8bc32138c..85e895d9b31c 100644
--- a/include/cxl/cxl.h
+++ b/include/cxl/cxl.h
@@ -32,6 +32,26 @@ enum cxl_decoder_type {
CXL_DECODER_HOSTONLYMEM = 3,
};
+/**
+ * struct cxl_decoder_settings - CXL HDM decoder programming snapshot
+ * @id: decoder id
+ * @hpa_range: Host physical address range mapped by this decoder
+ * @target_or_skip: switch target list or endpoint skip register value
+ * @interleave_ways: number of cxl_dports in this decode
+ * @interleave_granularity: data stride per dport
+ * @target_type: accelerator vs expander (type2 vs type3) selector
+ * @flags: memory type capabilities and locking
+ */
+struct cxl_decoder_settings {
+ int id;
+ struct range hpa_range;
+ u64 target_or_skip;
+ int interleave_ways;
+ int interleave_granularity;
+ enum cxl_decoder_type target_type;
+ unsigned long flags;
+};
+
/*
* Current specification goes up to 8, double that seems a reasonable
* software max for the foreseeable future
--
2.43.0
next prev parent reply other threads:[~2026-08-04 19:30 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-04 19:29 [PATCH v10 00/12] PCI/CXL: Add CXL reset support for Type 2 devices Srirangan Madhavan
2026-08-04 19:29 ` [PATCH v10 01/12] cxl: Move HDM decoder programming helpers Srirangan Madhavan
2026-08-04 19:46 ` sashiko-bot
2026-08-05 2:13 ` Alison Schofield
2026-08-04 19:29 ` Srirangan Madhavan [this message]
2026-08-04 19:49 ` [PATCH v10 02/12] cxl: Pass decoder settings to HDM commit helpers sashiko-bot
2026-08-04 19:29 ` [PATCH v10 03/12] cxl: Share HDM decoder decode logic Srirangan Madhavan
2026-08-04 19:45 ` sashiko-bot
2026-08-04 19:29 ` [PATCH v10 04/12] cxl: Cache decoder settings on PCI devices Srirangan Madhavan
2026-08-04 19:40 ` sashiko-bot
2026-08-04 19:29 ` [PATCH v10 05/12] cxl: Cache endpoint decoder settings during PCI enumeration Srirangan Madhavan
2026-08-04 19:51 ` sashiko-bot
2026-08-05 2:28 ` Alison Schofield
2026-08-04 19:29 ` [PATCH v10 06/12] cxl: Add CXL Device Reset helper Srirangan Madhavan
2026-08-04 19:42 ` sashiko-bot
2026-08-04 19:29 ` [PATCH v10 07/12] cxl: Validate HDM ranges before CXL reset Srirangan Madhavan
2026-08-04 19:38 ` sashiko-bot
2026-08-04 19:29 ` [PATCH v10 08/12] cxl: Reject CXL Reset on multifunction devices Srirangan Madhavan
2026-08-04 19:40 ` sashiko-bot
2026-08-04 19:29 ` [PATCH v10 09/12] cxl: Restore CXL HDM state after PCI reset Srirangan Madhavan
2026-08-04 19:44 ` sashiko-bot
2026-08-04 19:29 ` [PATCH v10 10/12] PCI/CXL: Expose CXL Reset as a PCI reset method Srirangan Madhavan
2026-08-04 20:00 ` sashiko-bot
2026-08-04 19:29 ` [PATCH v10 11/12] Documentation/ABI: Document CXL Reset " Srirangan Madhavan
2026-08-04 19:41 ` sashiko-bot
2026-08-04 19:29 ` [PATCH v10 12/12] PCI/CXL: Restore HDM state after CXL bus reset Srirangan Madhavan
2026-08-04 19:59 ` sashiko-bot
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=20260804192958.1823952-3-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=ira.weiny@intel.com \
--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=mhonap@nvidia.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.