Re: [PATCH v10 01/12] cxl: Move HDM decoder programming helpers
Li Ming <[email protected]>
| Newsgroups | org.kernel.vger.linux-cxl,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci,org.kernel.vger.linux-tegra |
|---|---|
| Message-ID | <[email protected]> |
On 8/5/2026 3:29 AM, Srirangan Madhavan wrote: > Move the low-level HDM decoder programming helpers from hdm.c into > resource.c so they can be shared by later reset restore code. > > Keep the existing decoder commit flow and register programming sequence > unchanged. This patch is intended to be a mechanical move. Follow-on > patches update the helper interface for reset restore. > > Signed-off-by: Srirangan Madhavan <[email protected]> > --- > drivers/cxl/Kconfig | 4 ++ > drivers/cxl/core/Makefile | 1 + > drivers/cxl/core/core.h | 2 + > drivers/cxl/core/hdm.c | 124 +------------------------------- > drivers/cxl/core/resource.c | 138 ++++++++++++++++++++++++++++++++++++ > drivers/cxl/cxl.h | 41 ----------- > include/cxl/cxl.h | 45 +++++++++++- > tools/testing/cxl/Kbuild | 1 + > 8 files changed, 191 insertions(+), 165 deletions(-) > create mode 100644 drivers/cxl/core/resource.c > > diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig > index 80aeb0d556bd..87d719ea1e14 100644 > --- a/drivers/cxl/Kconfig > +++ b/drivers/cxl/Kconfig > @@ -6,6 +6,7 @@ menuconfig CXL_BUS > select FW_UPLOAD > select PCI_DOE > select FIRMWARE_TABLE > + select CXL_HDM > select NUMA_KEEP_MEMINFO if NUMA_MEMBLKS > select FWCTL if CXL_FEATURES > help > @@ -243,4 +244,7 @@ config CXL_ATL > depends on CXL_REGION > depends on ACPI_PRMT && AMD_NB > > +config CXL_HDM > + bool > + > endif > diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile > index ce7213818d3c..0df4178bbbaa 100644 > --- a/drivers/cxl/core/Makefile > +++ b/drivers/cxl/core/Makefile > @@ -1,5 +1,6 @@ > # SPDX-License-Identifier: GPL-2.0 > obj-$(CONFIG_CXL_BUS) += cxl_core.o > +obj-$(CONFIG_CXL_HDM) += resource.o > obj-$(CONFIG_CXL_SUSPEND) += suspend.o > > ccflags-y += -I$(srctree)/drivers/cxl > diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h > index 07555ae63859..cb6853a92a93 100644 > --- a/drivers/cxl/core/core.h > +++ b/drivers/cxl/core/core.h > @@ -213,6 +213,8 @@ int cxl_gpf_port_setup(struct cxl_dport *dport); > struct cxl_hdm; > 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_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 0c80b76a5f9b..6ae0b9f46ac0 100644 > --- a/drivers/cxl/core/hdm.c > +++ b/drivers/cxl/core/hdm.c > @@ -16,10 +16,6 @@ > * for enumerating these registers and capabilities. > */ > > -struct cxl_rwsem cxl_rwsem = { > - .region = __RWSEM_INITIALIZER(cxl_rwsem.region), > - .dpa = __RWSEM_INITIALIZER(cxl_rwsem.dpa), > -}; > > static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld) > { > @@ -679,124 +675,6 @@ 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 cxld_set_interleave(struct cxl_decoder *cxld, u32 *ctrl) > -{ > - u16 eig; > - u8 eiw; > - > - /* > - * Input validation ensures these warns never fire, but otherwise > - * suppress unititalized variable usage warnings. > - */ > - if (WARN_ONCE(ways_to_eiw(cxld->interleave_ways, &eiw), > - "invalid interleave_ways: %d\n", cxld->interleave_ways)) > - return; > - if (WARN_ONCE(granularity_to_eig(cxld->interleave_granularity, &eig), > - "invalid interleave_granularity: %d\n", > - cxld->interleave_granularity)) > - return; > - > - u32p_replace_bits(ctrl, eig, CXL_HDM_DECODER0_CTRL_IG_MASK); > - u32p_replace_bits(ctrl, eiw, CXL_HDM_DECODER0_CTRL_IW_MASK); > - *ctrl |= CXL_HDM_DECODER0_CTRL_COMMIT; > -} > - > -static void cxld_set_type(struct cxl_decoder *cxld, u32 *ctrl) > -{ > - u32p_replace_bits(ctrl, > - !!(cxld->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 > - */ > -#define COMMIT_TIMEOUT_MS 20 > -static int cxld_await_commit(void __iomem *hdm, int id) > -{ > - u32 ctrl; > - int i; > - > - for (i = 0; i < COMMIT_TIMEOUT_MS; i++) { > - ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); > - if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMIT_ERROR, ctrl)) { > - ctrl &= ~CXL_HDM_DECODER0_CTRL_COMMIT; > - writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); > - return -EIO; > - } > - if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl)) > - return 0; > - fsleep(1000); > - } > - > - return -ETIMEDOUT; > -} > - > -static void setup_hw_decoder(struct cxl_decoder *cxld, void __iomem *hdm) > -{ > - int id = cxld->id; > - 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); > - > - 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); > - } > - > - writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); > -} > - > static int cxl_decoder_commit(struct cxl_decoder *cxld) > { > struct cxl_port *port = to_cxl_port(cxld->dev.parent); > @@ -835,7 +713,7 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld) > } > > scoped_guard(rwsem_read, &cxl_rwsem.dpa) > - setup_hw_decoder(cxld, hdm); > + cxl_setup_hw_decoder(cxld, hdm); > > rc = cxld_await_commit(hdm, cxld->id); > if (rc) { > diff --git a/drivers/cxl/core/resource.c b/drivers/cxl/core/resource.c > new file mode 100644 > index 000000000000..dd8bed3d3ff0 > --- /dev/null > +++ b/drivers/cxl/core/resource.c > @@ -0,0 +1,138 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* Copyright (c) 2026 NVIDIA Corporation & Affiliates */ > +#include <linux/delay.h> > +#include <linux/bug.h> > +#include <linux/errno.h> > +#include <linux/export.h> > +#include <linux/kernel.h> > + > +#include "cxl.h" > +#include "core.h" > + > +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"); > + > +static void cxld_set_interleave(struct cxl_decoder *cxld, u32 *ctrl) > +{ > + u16 eig; > + u8 eiw; > + > + /* > + * Input validation ensures these warns never fire, but otherwise > + * suppress unititalized variable usage warnings. > + */ > + if (WARN_ONCE(ways_to_eiw(cxld->interleave_ways, &eiw), > + "invalid interleave_ways: %d\n", cxld->interleave_ways)) > + return; > + if (WARN_ONCE(granularity_to_eig(cxld->interleave_granularity, &eig), > + "invalid interleave_granularity: %d\n", > + cxld->interleave_granularity)) > + return; > + > + u32p_replace_bits(ctrl, eig, CXL_HDM_DECODER0_CTRL_IG_MASK); > + u32p_replace_bits(ctrl, eiw, CXL_HDM_DECODER0_CTRL_IW_MASK); > + *ctrl |= CXL_HDM_DECODER0_CTRL_COMMIT; > +} > + > +static void cxld_set_type(struct cxl_decoder *cxld, u32 *ctrl) > +{ > + u32p_replace_bits(ctrl, > + !!(cxld->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 > + */ > +#define COMMIT_TIMEOUT_MS 20 > +int cxld_await_commit(void __iomem *hdm, int id) > +{ > + u32 ctrl; > + int i; > + > + for (i = 0; i < COMMIT_TIMEOUT_MS; i++) { > + ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); > + if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMIT_ERROR, ctrl)) { > + ctrl &= ~CXL_HDM_DECODER0_CTRL_COMMIT; > + writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); > + return -EIO; > + } > + if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl)) > + return 0; > + fsleep(1000); > + } > + > + return -ETIMEDOUT; > +} > + > +EXPORT_SYMBOL_FOR_MODULES(cxld_await_commit, "cxl_core"); Redundant blank line between function implementation and EXPORT_SYMBOL_FOR_MODULES(). > + > +void cxl_setup_hw_decoder(struct cxl_decoder *cxld, void __iomem *hdm) > +{ > + int id = cxld->id; > + 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); > + > + 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); > + } > + > + writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); > +} > + > +EXPORT_SYMBOL_FOR_MODULES(cxl_setup_hw_decoder, "cxl_core"); Same as above > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h > index c0e5308e4d1b..c795cce0fcbc 100644 > --- a/drivers/cxl/cxl.h > +++ b/drivers/cxl/cxl.h > @@ -264,49 +264,8 @@ int cxl_dport_map_rcd_linkcap(struct pci_dev *pdev, struct cxl_dport *dport); > #define CXL_DECODER_F_NORMALIZED_ADDRESSING BIT(6) > #define CXL_DECODER_F_RESET_MASK (CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK) > > -enum cxl_decoder_type { > - CXL_DECODER_DEVMEM = 2, > - CXL_DECODER_HOSTONLYMEM = 3, > -}; > - > -/* > - * Current specification goes up to 8, double that seems a reasonable > - * software max for the foreseeable future > - */ > -#define CXL_DECODER_MAX_INTERLEAVE 16 > - > #define CXL_QOS_CLASS_INVALID -1 > > -/** > - * struct cxl_decoder - Common CXL HDM Decoder Attributes > - * @dev: this decoder's device > - * @id: kernel device name id > - * @hpa_range: Host physical address range mapped by this decoder > - * @interleave_ways: number of cxl_dports in this decode > - * @interleave_granularity: data stride per dport > - * @target_type: accelerator vs expander (type2 vs type3) selector > - * @region: currently assigned region for this decoder > - * @flags: memory type capabilities and locking > - * @target_map: cached copy of hardware port-id list, available at init > - * before all @dport objects have been instantiated. While > - * dport id is 8bit, CFMWS interleave targets are 32bits. > - * @commit: device/decoder-type specific callback to commit settings to hw > - * @reset: device/decoder-type specific callback to reset hw settings > -*/ > -struct cxl_decoder { > - struct device dev; > - int id; > - struct range hpa_range; > - int interleave_ways; > - int interleave_granularity; > - enum cxl_decoder_type target_type; > - struct cxl_region *region; > - unsigned long flags; > - u32 target_map[CXL_DECODER_MAX_INTERLEAVE]; > - int (*commit)(struct cxl_decoder *cxld); > - void (*reset)(struct cxl_decoder *cxld); > -}; > - > /* > * Track whether this decoder is free for userspace provisioning, reserved for > * region autodiscovery, whether it is started connecting (awaiting other > diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h > index 016c74fb747c..05a8bc32138c 100644 > --- a/include/cxl/cxl.h > +++ b/include/cxl/cxl.h > @@ -5,8 +5,10 @@ > #ifndef __CXL_CXL_H__ > #define __CXL_CXL_H__ > > +#include <linux/device.h> > #include <linux/node.h> > #include <linux/ioport.h> > +#include <linux/range.h> > #include <cxl/mailbox.h> > > /** > @@ -23,7 +25,48 @@ enum cxl_devtype { > CXL_DEVTYPE_CLASSMEM, > }; > > -struct device; > +struct cxl_region; > + > +enum cxl_decoder_type { > + CXL_DECODER_DEVMEM = 2, > + CXL_DECODER_HOSTONLYMEM = 3, > +}; > + > +/* > + * Current specification goes up to 8, double that seems a reasonable > + * software max for the foreseeable future > + */ > +#define CXL_DECODER_MAX_INTERLEAVE 16 > + > +/** > + * struct cxl_decoder - Common CXL HDM Decoder Attributes > + * @dev: this decoder's device > + * @id: kernel device name id > + * @hpa_range: Host physical address range mapped by this decoder > + * @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 > + * @region: currently assigned region for this decoder > + * @target_map: cached copy of hardware port-id list, available at init > + * before all @dport objects have been instantiated. While > + * dport id is 8bit, CFMWS interleave targets are 32bits. > + * @commit: device/decoder-type specific callback to commit settings to hw > + * @reset: device/decoder-type specific callback to reset hw settings > + */ > +struct cxl_decoder { > + struct device dev; > + int id; > + struct range hpa_range; > + int interleave_ways; > + int interleave_granularity; > + enum cxl_decoder_type target_type; > + unsigned long flags; > + struct cxl_region *region; > + u32 target_map[CXL_DECODER_MAX_INTERLEAVE]; > + int (*commit)(struct cxl_decoder *cxld); > + void (*reset)(struct cxl_decoder *cxld); > +}; > > /* > * Using struct_group() allows for per register-block-type helper routines, > diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild > index 2be1df80fcc9..6c09932587ce 100644 > --- a/tools/testing/cxl/Kbuild > +++ b/tools/testing/cxl/Kbuild > @@ -60,6 +60,7 @@ cxl_core-y += $(CXL_CORE_SRC)/memdev.o > cxl_core-y += $(CXL_CORE_SRC)/mbox.o > cxl_core-y += $(CXL_CORE_SRC)/pci.o > cxl_core-y += $(CXL_CORE_SRC)/hdm.o > +cxl_core-y += $(CXL_CORE_SRC)/resource.o > cxl_core-y += $(CXL_CORE_SRC)/pmu.o > cxl_core-y += $(CXL_CORE_SRC)/cdat.o > cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o