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/24/2026 3:11 PM, Li Ming wrote: > 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(). Oh, sorry, I saw you deleting this EXPORT_SYMBOL_FOR_MODULES() in PATCH #2. >> + >> +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 >