Linux EDAC development
 help / color / mirror / Atom feed
* [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers
@ 2026-05-21  7:31 Qiuxu Zhuo
  2026-05-21  7:31 ` [PATCH 1/8] EDAC/{skx_common,i10nm,imh}: Move MC register access helpers to skx_common Qiuxu Zhuo
                   ` (8 more replies)
  0 siblings, 9 replies; 12+ messages in thread
From: Qiuxu Zhuo @ 2026-05-21  7:31 UTC (permalink / raw)
  To: Tony Luck, Borislav Petkov; +Cc: Qiuxu Zhuo, Yi Lai, linux-edac, linux-kernel

This patch set adds RRL (Retry Read Error Log) support for Intel Diamond Rapids.

RRL provides detailed information about retry read errors reported by the memory controller,
which helps diagnose memory reliability issues. Intel Diamond Rapids introduces sub-channel
granularity and requires updates to the existing EDAC RRL handling.

To avoid code duplication and ensure consistent behavior across platforms, the existing i10nm
RRL implementation is refactored and moved into common code shared by i10nm_edac and imh_edac.


The patch set is organized as follows:

  Patch 1-5: Refactor i10nm RRL code and move it into common helpers.

  Patch 6-7: Prepare ADXL sub-channel decoding and extend RRL handling for sub-channel granularity.

  Patch 8: Add RRL support for Diamond Rapids.


The patch set is based on:

  Tree: https://git.kernel.org/pub/scm/linux/kernel/git/ras/ras.git
  Branch: edac-for-next
  Commit: 4b049cd5e519

Qiuxu Zhuo (8):
  EDAC/{skx_common,i10nm,imh}: Move MC register access helpers to skx_common
  EDAC/{skx_common,skx,i10nm}: Split skx_set_decode()
  EDAC/{skx_common,i10nm}: Rename rrl_mode to rrl_source_type
  EDAC/{skx_common,i10nm}: Introduce rrl_ctrl_mode
  EDAC/{skx_common,i10nm}: Move RRL handling to common code
  EDAC/skx_common: Add SubChannel support to ADXL decode
  EDAC/{skx_common,i10nm}: Prepare RRL for sub-channel granularity
  EDAC/imh: Add RRL support for Intel Diamond Rapids server

 drivers/edac/i10nm_base.c | 255 +++------------------------------
 drivers/edac/imh_base.c   |  81 +++++++----
 drivers/edac/skx_base.c   |   6 +-
 drivers/edac/skx_common.c | 294 +++++++++++++++++++++++++++++++++++++-
 drivers/edac/skx_common.h |  44 ++++--
 5 files changed, 401 insertions(+), 279 deletions(-)


base-commit: 4b049cd5e5194c8e58cd978d2dc9d3bcf67c84f6
-- 
2.43.0


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/8] EDAC/{skx_common,i10nm,imh}: Move MC register access helpers to skx_common
  2026-05-21  7:31 [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Qiuxu Zhuo
@ 2026-05-21  7:31 ` Qiuxu Zhuo
  2026-06-15  2:26   ` Borislav Petkov
  2026-05-21  7:31 ` [PATCH 2/8] EDAC/{skx_common,skx,i10nm}: Split skx_set_decode() Qiuxu Zhuo
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 12+ messages in thread
From: Qiuxu Zhuo @ 2026-05-21  7:31 UTC (permalink / raw)
  To: Tony Luck, Borislav Petkov; +Cc: Qiuxu Zhuo, Yi Lai, linux-edac, linux-kernel

Both i10nm_basic.c and imh_basic.c use identical helpers for accessing
memory controller MMIO-based registers. Move these helpers to skx_common.c
to eliminate code duplication. This change also prepares for an upcoming
patch that will move RRL(retry_rd_err_log) code from i10nm_basic.c to
skx_common.c, which requires these helpers to be available in skx_common.c.

Additionally, prefix these function names with 'skx_' to maintain naming
consistency within the file.

No functional changes intended.

Tested-by: Yi Lai <yi1.lai@intel.com>
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
---
 drivers/edac/i10nm_base.c | 39 ++++--------------------------
 drivers/edac/imh_base.c   | 33 ++++----------------------
 drivers/edac/skx_common.c | 50 +++++++++++++++++++++++++++++++++++++++
 drivers/edac/skx_common.h |  3 +++
 4 files changed, 63 insertions(+), 62 deletions(-)

diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c
index de6c52dbd9d2..0a0236583eb7 100644
--- a/drivers/edac/i10nm_base.c
+++ b/drivers/edac/i10nm_base.c
@@ -47,12 +47,6 @@
 	readl((m)->mbase + ((m)->hbm_mc ? 0xef8 :	\
 	(res_cfg->type == GNR ? 0xaf8 : 0x20ef8)) +	\
 	(i) * (m)->chan_mmio_sz)
-#define I10NM_GET_REG32(m, i, offset)	\
-	readl((m)->mbase + (i) * (m)->chan_mmio_sz + (offset))
-#define I10NM_GET_REG64(m, i, offset)	\
-	readq((m)->mbase + (i) * (m)->chan_mmio_sz + (offset))
-#define I10NM_SET_REG32(m, i, offset, v)	\
-	writel(v, (m)->mbase + (i) * (m)->chan_mmio_sz + (offset))
 
 #define I10NM_GET_SCK_MMIO_BASE(reg)	(GET_BITFIELD(reg, 0, 28) << 23)
 #define I10NM_GET_IMC_MMIO_OFFSET(reg)	(GET_BITFIELD(reg, 0, 10) << 12)
@@ -189,29 +183,6 @@ static struct reg_rrl gnr_reg_rrl_ddr = {
 	.cecnt_widths	= {4, 4, 4, 4, 4, 4, 4, 4},
 };
 
-static u64 read_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width)
-{
-	switch (width) {
-	case 4:
-		return I10NM_GET_REG32(imc, chan, offset);
-	case 8:
-		return I10NM_GET_REG64(imc, chan, offset);
-	default:
-		i10nm_printk(KERN_ERR, "Invalid read RRL 0x%x width %d\n", offset, width);
-		return 0;
-	}
-}
-
-static void write_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width, u64 val)
-{
-	switch (width) {
-	case 4:
-		return I10NM_SET_REG32(imc, chan, offset, (u32)val);
-	default:
-		i10nm_printk(KERN_ERR, "Invalid write RRL 0x%x width %d\n", offset, width);
-	}
-}
-
 static void enable_rrl(struct skx_imc *imc, int chan, struct reg_rrl *rrl,
 		       int rrl_set, bool enable, u32 *rrl_ctl)
 {
@@ -225,7 +196,7 @@ static void enable_rrl(struct skx_imc *imc, int chan, struct reg_rrl *rrl,
 	/* Patrol scrub or on-demand read error. */
 	scrub = (mode == FRE_SCRUB || mode == LRE_SCRUB);
 
-	v = read_imc_reg(imc, chan, offset, width);
+	v = skx_read_imc_reg(imc, chan, offset, width);
 
 	if (enable) {
 		/* Save default configurations. */
@@ -268,7 +239,7 @@ static void enable_rrl(struct skx_imc *imc, int chan, struct reg_rrl *rrl,
 			v &= ~rrl->en_mask;
 	}
 
-	write_imc_reg(imc, chan, offset, width, v);
+	skx_write_imc_reg(imc, chan, offset, width, v);
 }
 
 static void enable_rrls(struct skx_imc *imc, int chan, struct reg_rrl *rrl,
@@ -354,7 +325,7 @@ static void show_retry_rd_err_log(struct decoded_addr *res, char *msg,
 		for (j = 0; j < rrl->reg_num && len - n > 0; j++) {
 			offset = rrl->offsets[i][j];
 			width = rrl->widths[j];
-			log = read_imc_reg(imc, ch, offset, width);
+			log = skx_read_imc_reg(imc, ch, offset, width);
 
 			if (width == 4)
 				n += scnprintf(msg + n, len - n, "%.8llx ", log);
@@ -363,7 +334,7 @@ static void show_retry_rd_err_log(struct decoded_addr *res, char *msg,
 
 			/* Clear RRL status if RRL in Linux control mode. */
 			if (retry_rd_err_log == 2 && !j && (log & status_mask))
-				write_imc_reg(imc, ch, offset, width, log & ~status_mask);
+				skx_write_imc_reg(imc, ch, offset, width, log & ~status_mask);
 		}
 	}
 
@@ -376,7 +347,7 @@ static void show_retry_rd_err_log(struct decoded_addr *res, char *msg,
 		for (i = 0; i < rrl->cecnt_num && len - n > 0; i++) {
 			offset = rrl->cecnt_offsets[i];
 			width = rrl->cecnt_widths[i];
-			corr = read_imc_reg(imc, ch, offset, width);
+			corr = skx_read_imc_reg(imc, ch, offset, width);
 
 			/* CPUs {ICX,SPR} encode two counters per 4-byte CORRERRCNT register. */
 			if (res_cfg->type <= SPR) {
diff --git a/drivers/edac/imh_base.c b/drivers/edac/imh_base.c
index 40082ba45e62..dfdcfa127ce7 100644
--- a/drivers/edac/imh_base.c
+++ b/drivers/edac/imh_base.c
@@ -71,28 +71,11 @@ struct local_reg {
 		.width	= (cfg)->ip_name##_reg_##reg_name##_width,		\
 	}
 
-static u64 readx(void __iomem *addr, u8 width)
-{
-	switch (width) {
-	case 1:
-		return readb(addr);
-	case 2:
-		return readw(addr);
-	case 4:
-		return readl(addr);
-	case 8:
-		return readq(addr);
-	default:
-		imh_printk(KERN_ERR, "Invalid reg 0x%p width %d\n", addr, width);
-		return 0;
-	}
-}
-
 static void __read_local_reg(void *reg)
 {
 	struct local_reg *r = (struct local_reg *)reg;
 
-	r->val = readx(r->vbase + r->offset, r->width);
+	r->val = skx_readx(r->vbase + r->offset, r->width);
 }
 
 /* Read a local-view register. */
@@ -378,22 +361,16 @@ static bool imh_2lm_enabled(struct res_config *cfg, struct list_head *head)
 	return false;
 }
 
-/* Helpers to read memory controller registers */
-static u64 read_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width)
-{
-	return readx(imc->mbase + imc->chan_mmio_sz * chan + offset, width);
-}
-
 static u32 read_imc_mcmtr(struct res_config *cfg, struct skx_imc *imc, int chan)
 {
-	return (u32)read_imc_reg(imc, chan, cfg->ddr_reg_mcmtr_offset, cfg->ddr_reg_mcmtr_width);
+	return (u32)skx_read_imc_reg(imc, chan, cfg->ddr_reg_mcmtr_offset, cfg->ddr_reg_mcmtr_width);
 }
 
 static u32 read_imc_dimmmtr(struct res_config *cfg, struct skx_imc *imc, int chan, int dimm)
 {
-	return (u32)read_imc_reg(imc, chan, cfg->ddr_reg_dimmmtr_offset +
-				 cfg->ddr_reg_dimmmtr_width * dimm,
-				 cfg->ddr_reg_dimmmtr_width);
+	return (u32)skx_read_imc_reg(imc, chan, cfg->ddr_reg_dimmmtr_offset +
+				     cfg->ddr_reg_dimmmtr_width * dimm,
+				     cfg->ddr_reg_dimmmtr_width);
 }
 
 static bool ecc_enabled(u32 mcmtr)
diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c
index f15de0ea96c8..1c4cc21679bc 100644
--- a/drivers/edac/skx_common.c
+++ b/drivers/edac/skx_common.c
@@ -52,6 +52,56 @@ static LIST_HEAD(dev_edac_list);
 static bool skx_mem_cfg_2lm;
 static struct res_config *skx_res_cfg;
 
+u64 skx_readx(void __iomem *addr, u8 width)
+{
+	switch (width) {
+	case 1:
+		return readb(addr);
+	case 2:
+		return readw(addr);
+	case 4:
+		return readl(addr);
+	case 8:
+		return readq(addr);
+	default:
+		skx_printk(KERN_ERR, "Invalid reg 0x%p width %u to read.\n", addr, width);
+		return 0;
+	}
+}
+EXPORT_SYMBOL_GPL(skx_readx);
+
+static void skx_writex(void __iomem *addr, u8 width, u64 val)
+{
+	switch (width) {
+	case 1:
+		writeb((u8)val, addr);
+		return;
+	case 2:
+		writew((u16)val, addr);
+		return;
+	case 4:
+		writel((u32)val, addr);
+		return;
+	case 8:
+		writeq(val, addr);
+		return;
+	default:
+		skx_printk(KERN_ERR, "Invalid reg 0x%p width %u to write 0x%llx.\n", addr, width, val);
+	}
+}
+
+u64 skx_read_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width)
+{
+	return skx_readx(imc->mbase + imc->chan_mmio_sz * chan + offset, width);
+}
+EXPORT_SYMBOL_GPL(skx_read_imc_reg);
+
+void skx_write_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width, u64 val)
+{
+	skx_writex(imc->mbase + imc->chan_mmio_sz * chan + offset, width, val);
+}
+EXPORT_SYMBOL_GPL(skx_write_imc_reg);
+
 int skx_adxl_get(void)
 {
 	const char * const *names;
diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h
index f88038e5b18c..95412459a84f 100644
--- a/drivers/edac/skx_common.h
+++ b/drivers/edac/skx_common.h
@@ -326,6 +326,9 @@ typedef int (*get_dimm_config_f)(struct mem_ctl_info *mci,
 typedef bool (*skx_decode_f)(struct decoded_addr *res);
 typedef void (*skx_show_retry_log_f)(struct decoded_addr *res, char *msg, int len, bool scrub_err);
 
+u64 skx_readx(void __iomem *addr, u8 width);
+u64 skx_read_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width);
+void skx_write_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width, u64 val);
 int skx_adxl_get(void);
 void skx_adxl_put(void);
 void skx_set_decode(skx_decode_f decode, skx_show_retry_log_f show_retry_log);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/8] EDAC/{skx_common,skx,i10nm}: Split skx_set_decode()
  2026-05-21  7:31 [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Qiuxu Zhuo
  2026-05-21  7:31 ` [PATCH 1/8] EDAC/{skx_common,i10nm,imh}: Move MC register access helpers to skx_common Qiuxu Zhuo
@ 2026-05-21  7:31 ` Qiuxu Zhuo
  2026-05-21  7:31 ` [PATCH 3/8] EDAC/{skx_common,i10nm}: Rename rrl_mode to rrl_source_type Qiuxu Zhuo
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Qiuxu Zhuo @ 2026-05-21  7:31 UTC (permalink / raw)
  To: Tony Luck, Borislav Petkov; +Cc: Qiuxu Zhuo, Yi Lai, linux-edac, linux-kernel

skx_set_decode() currently handles both address decoding and Retry
Read error Log (RRL) reporting, coupling two independent functions
in a single API. This complicates setup/teardown and forces callers
to update unrelated state.

Introduce skx_set_show_rrl() and keep skx_set_decode() focused on
decode setup, allowing decode and RRL handling to be managed
independently.

Also rename the callback type and variable to skx_show_rrl_f and
show_rrl for clearer RRL terminology and consistency.

No functional changes intended.

Tested-by: Yi Lai <yi1.lai@intel.com>
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
---
 drivers/edac/i10nm_base.c | 10 ++++++----
 drivers/edac/skx_base.c   |  6 +++---
 drivers/edac/skx_common.c | 15 ++++++++++-----
 drivers/edac/skx_common.h |  5 +++--
 4 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c
index 0a0236583eb7..c09790d5b95e 100644
--- a/drivers/edac/i10nm_base.c
+++ b/drivers/edac/i10nm_base.c
@@ -1208,13 +1208,13 @@ static int __init i10nm_init(void)
 	skx_setup_debug("i10nm_test");
 
 	if (retry_rd_err_log && res_cfg->reg_rrl_ddr) {
-		skx_set_decode(i10nm_mc_decode, show_retry_rd_err_log);
+		skx_set_show_rrl(show_retry_rd_err_log);
 		if (retry_rd_err_log == 2)
 			enable_retry_rd_err_log(true);
-	} else {
-		skx_set_decode(i10nm_mc_decode, NULL);
 	}
 
+	skx_set_decode(i10nm_mc_decode);
+
 	i10nm_printk(KERN_INFO, "%s\n", I10NM_REVISION);
 
 	return 0;
@@ -1227,10 +1227,12 @@ static void __exit i10nm_exit(void)
 {
 	edac_dbg(2, "\n");
 
+	skx_set_decode(NULL);
+
 	if (retry_rd_err_log && res_cfg->reg_rrl_ddr) {
-		skx_set_decode(NULL, NULL);
 		if (retry_rd_err_log == 2)
 			enable_retry_rd_err_log(false);
+		skx_set_show_rrl(NULL);
 	}
 
 	skx_teardown_debug();
diff --git a/drivers/edac/skx_base.c b/drivers/edac/skx_base.c
index aa6593ccda2d..de749413ff9a 100644
--- a/drivers/edac/skx_base.c
+++ b/drivers/edac/skx_base.c
@@ -671,14 +671,14 @@ static int __init skx_init(void)
 		}
 	}
 
-	skx_set_decode(skx_decode, skx_show_retry_rd_err_log);
+	skx_set_show_rrl(skx_show_retry_rd_err_log);
 
 	if (nvdimm_count && skx_adxl_get() != -ENODEV) {
-		skx_set_decode(NULL, skx_show_retry_rd_err_log);
+		skx_set_decode(NULL);
 	} else {
 		if (nvdimm_count)
 			skx_printk(KERN_NOTICE, "Only decoding DDR4 address!\n");
-		skx_set_decode(skx_decode, skx_show_retry_rd_err_log);
+		skx_set_decode(skx_decode);
 	}
 
 	/* Ensure that the OPSTATE is set correctly for POLL or NMI */
diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c
index 1c4cc21679bc..2cdef2e69d71 100644
--- a/drivers/edac/skx_common.c
+++ b/drivers/edac/skx_common.c
@@ -46,7 +46,7 @@ static unsigned long adxl_nm_bitmap;
 
 static char skx_msg[MSG_SIZE];
 static skx_decode_f driver_decode;
-static skx_show_retry_log_f skx_show_retry_rd_err_log;
+static skx_show_rrl_f show_rrl;
 static u64 skx_tolm, skx_tohm;
 static LIST_HEAD(dev_edac_list);
 static bool skx_mem_cfg_2lm;
@@ -312,13 +312,18 @@ void skx_set_res_cfg(struct res_config *cfg)
 }
 EXPORT_SYMBOL_GPL(skx_set_res_cfg);
 
-void skx_set_decode(skx_decode_f decode, skx_show_retry_log_f show_retry_log)
+void skx_set_decode(skx_decode_f decode)
 {
 	driver_decode = decode;
-	skx_show_retry_rd_err_log = show_retry_log;
 }
 EXPORT_SYMBOL_GPL(skx_set_decode);
 
+void skx_set_show_rrl(skx_show_rrl_f rrl)
+{
+	show_rrl = rrl;
+}
+EXPORT_SYMBOL_GPL(skx_set_show_rrl);
+
 static int skx_get_pkg_id(struct skx_dev *d, u8 *id)
 {
 	int node;
@@ -767,8 +772,8 @@ static void skx_mce_output_error(struct mem_ctl_info *mci,
 			 res->row, res->column, res->bank_address, res->bank_group);
 	}
 
-	if (skx_show_retry_rd_err_log)
-		skx_show_retry_rd_err_log(res, skx_msg + len, MSG_SIZE - len, scrub_err);
+	if (show_rrl)
+		show_rrl(res, skx_msg + len, MSG_SIZE - len, scrub_err);
 
 	edac_dbg(0, "%s\n", skx_msg);
 
diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h
index 95412459a84f..5a08f219e46d 100644
--- a/drivers/edac/skx_common.h
+++ b/drivers/edac/skx_common.h
@@ -324,14 +324,15 @@ struct res_config {
 typedef int (*get_dimm_config_f)(struct mem_ctl_info *mci,
 				 struct res_config *cfg);
 typedef bool (*skx_decode_f)(struct decoded_addr *res);
-typedef void (*skx_show_retry_log_f)(struct decoded_addr *res, char *msg, int len, bool scrub_err);
+typedef void (*skx_show_rrl_f)(struct decoded_addr *res, char *msg, int len, bool scrub_err);
 
 u64 skx_readx(void __iomem *addr, u8 width);
 u64 skx_read_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width);
 void skx_write_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width, u64 val);
 int skx_adxl_get(void);
 void skx_adxl_put(void);
-void skx_set_decode(skx_decode_f decode, skx_show_retry_log_f show_retry_log);
+void skx_set_decode(skx_decode_f decode);
+void skx_set_show_rrl(skx_show_rrl_f rrl);
 void skx_set_mem_cfg(bool mem_cfg_2lm);
 void skx_set_res_cfg(struct res_config *cfg);
 void skx_init_mc_mapping(struct skx_dev *d);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 3/8] EDAC/{skx_common,i10nm}: Rename rrl_mode to rrl_source_type
  2026-05-21  7:31 [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Qiuxu Zhuo
  2026-05-21  7:31 ` [PATCH 1/8] EDAC/{skx_common,i10nm,imh}: Move MC register access helpers to skx_common Qiuxu Zhuo
  2026-05-21  7:31 ` [PATCH 2/8] EDAC/{skx_common,skx,i10nm}: Split skx_set_decode() Qiuxu Zhuo
@ 2026-05-21  7:31 ` Qiuxu Zhuo
  2026-05-21  7:31 ` [PATCH 4/8] EDAC/{skx_common,i10nm}: Introduce rrl_ctrl_mode Qiuxu Zhuo
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Qiuxu Zhuo @ 2026-05-21  7:31 UTC (permalink / raw)
  To: Tony Luck, Borislav Petkov; +Cc: Qiuxu Zhuo, Yi Lai, linux-edac, linux-kernel

The RRL (Retry Read error Log) values describe where an error was logged
from (first/last read and scrub/demand), not an operating mode.

Rename rrl_mode to rrl_source_type and "modes" to "sources" to better
reflect their meaning and improve code readability.

No functional changes intended.

Tested-by: Yi Lai <yi1.lai@intel.com>
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
---
 drivers/edac/i10nm_base.c | 18 +++++++++---------
 drivers/edac/skx_common.h | 14 +++++++-------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c
index c09790d5b95e..01cc86f697c8 100644
--- a/drivers/edac/i10nm_base.c
+++ b/drivers/edac/i10nm_base.c
@@ -78,7 +78,7 @@ static bool no_adxl;
 static struct reg_rrl icx_reg_rrl_ddr = {
 	.set_num = 2,
 	.reg_num = 6,
-	.modes = {LRE_SCRUB, LRE_DEMAND},
+	.sources = {RRL_SRC_LRE_SCRUB, RRL_SRC_LRE_DEMAND},
 	.offsets = {
 		{0x22c60, 0x22c54, 0x22c5c, 0x22c58, 0x22c28, 0x20ed8},
 		{0x22e54, 0x22e60, 0x22e64, 0x22e58, 0x22e5c, 0x20ee0},
@@ -99,7 +99,7 @@ static struct reg_rrl icx_reg_rrl_ddr = {
 static struct reg_rrl spr_reg_rrl_ddr = {
 	.set_num = 3,
 	.reg_num = 6,
-	.modes = {LRE_SCRUB, LRE_DEMAND, FRE_DEMAND},
+	.sources = {RRL_SRC_LRE_SCRUB, RRL_SRC_LRE_DEMAND, RRL_SRC_FRE_DEMAND},
 	.offsets = {
 		{0x22c60, 0x22c54, 0x22f08, 0x22c58, 0x22c28, 0x20ed8},
 		{0x22e54, 0x22e60, 0x22f10, 0x22e58, 0x22e5c, 0x20ee0},
@@ -121,7 +121,7 @@ static struct reg_rrl spr_reg_rrl_ddr = {
 static struct reg_rrl spr_reg_rrl_hbm_pch0 = {
 	.set_num = 2,
 	.reg_num = 6,
-	.modes = {LRE_SCRUB, LRE_DEMAND},
+	.sources = {RRL_SRC_LRE_SCRUB, RRL_SRC_LRE_DEMAND},
 	.offsets = {
 		{0x2860, 0x2854, 0x2b08, 0x2858, 0x2828, 0x0ed8},
 		{0x2a54, 0x2a60, 0x2b10, 0x2a58, 0x2a5c, 0x0ee0},
@@ -142,7 +142,7 @@ static struct reg_rrl spr_reg_rrl_hbm_pch0 = {
 static struct reg_rrl spr_reg_rrl_hbm_pch1 = {
 	.set_num = 2,
 	.reg_num = 6,
-	.modes = {LRE_SCRUB, LRE_DEMAND},
+	.sources = {RRL_SRC_LRE_SCRUB, RRL_SRC_LRE_DEMAND},
 	.offsets = {
 		{0x2c60, 0x2c54, 0x2f08, 0x2c58, 0x2c28, 0x0fa8},
 		{0x2e54, 0x2e60, 0x2f10, 0x2e58, 0x2e5c, 0x0fb0},
@@ -163,7 +163,7 @@ static struct reg_rrl spr_reg_rrl_hbm_pch1 = {
 static struct reg_rrl gnr_reg_rrl_ddr = {
 	.set_num = 4,
 	.reg_num = 6,
-	.modes = {FRE_SCRUB, FRE_DEMAND, LRE_SCRUB, LRE_DEMAND},
+	.sources = {RRL_SRC_FRE_SCRUB, RRL_SRC_FRE_DEMAND, RRL_SRC_LRE_SCRUB, RRL_SRC_LRE_DEMAND},
 	.offsets = {
 		{0x2f10, 0x2f20, 0x2f30, 0x2f50, 0x2f60, 0xba0},
 		{0x2f14, 0x2f24, 0x2f38, 0x2f54, 0x2f64, 0xba8},
@@ -186,15 +186,15 @@ static struct reg_rrl gnr_reg_rrl_ddr = {
 static void enable_rrl(struct skx_imc *imc, int chan, struct reg_rrl *rrl,
 		       int rrl_set, bool enable, u32 *rrl_ctl)
 {
-	enum rrl_mode mode = rrl->modes[rrl_set];
+	enum rrl_source_type source = rrl->sources[rrl_set];
 	u32 offset = rrl->offsets[rrl_set][0], v;
 	u8 width = rrl->widths[0];
 	bool first, scrub;
 
 	/* First or last read error. */
-	first = (mode == FRE_SCRUB || mode == FRE_DEMAND);
+	first = (source == RRL_SRC_FRE_SCRUB || source == RRL_SRC_FRE_DEMAND);
 	/* Patrol scrub or on-demand read error. */
-	scrub = (mode == FRE_SCRUB || mode == LRE_SCRUB);
+	scrub = (source == RRL_SRC_FRE_SCRUB || source == RRL_SRC_LRE_SCRUB);
 
 	v = skx_read_imc_reg(imc, chan, offset, width);
 
@@ -318,7 +318,7 @@ static void show_retry_rd_err_log(struct decoded_addr *res, char *msg,
 
 	n = scnprintf(msg, len, " retry_rd_err_log[");
 	for (i = 0; i < rrl->set_num; i++) {
-		scrub = (rrl->modes[i] == FRE_SCRUB || rrl->modes[i] == LRE_SCRUB);
+		scrub = (rrl->sources[i] == RRL_SRC_FRE_SCRUB || rrl->sources[i] == RRL_SRC_LRE_SCRUB);
 		if (scrub_err != scrub)
 			continue;
 
diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h
index 5a08f219e46d..f7f016db122f 100644
--- a/drivers/edac/skx_common.h
+++ b/drivers/edac/skx_common.h
@@ -81,23 +81,23 @@
 /* Max correctable error count registers. */
 #define NUM_CECNT_REG		8
 
-/* Modes of RRL register set. */
-enum rrl_mode {
+/* Error source from which the RRL registers log errors. */
+enum rrl_source_type {
 	/* Last read error from patrol scrub. */
-	LRE_SCRUB,
+	RRL_SRC_LRE_SCRUB,
 	/* Last read error from demand. */
-	LRE_DEMAND,
+	RRL_SRC_LRE_DEMAND,
 	/* First read error from patrol scrub. */
-	FRE_SCRUB,
+	RRL_SRC_FRE_SCRUB,
 	/* First read error from demand. */
-	FRE_DEMAND,
+	RRL_SRC_FRE_DEMAND,
 };
 
 /* RRL registers per {,sub-,pseudo-}channel. */
 struct reg_rrl {
 	/* RRL register parts. */
 	int set_num, reg_num;
-	enum rrl_mode modes[NUM_RRL_SET];
+	enum rrl_source_type sources[NUM_RRL_SET];
 	u32 offsets[NUM_RRL_SET][NUM_RRL_REG];
 	/* RRL register widths in byte per set. */
 	u8 widths[NUM_RRL_REG];
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 4/8] EDAC/{skx_common,i10nm}: Introduce rrl_ctrl_mode
  2026-05-21  7:31 [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Qiuxu Zhuo
                   ` (2 preceding siblings ...)
  2026-05-21  7:31 ` [PATCH 3/8] EDAC/{skx_common,i10nm}: Rename rrl_mode to rrl_source_type Qiuxu Zhuo
@ 2026-05-21  7:31 ` Qiuxu Zhuo
  2026-05-21  7:31 ` [PATCH 5/8] EDAC/{skx_common,i10nm}: Move RRL handling to common code Qiuxu Zhuo
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Qiuxu Zhuo @ 2026-05-21  7:31 UTC (permalink / raw)
  To: Tony Luck, Borislav Petkov; +Cc: Qiuxu Zhuo, Yi Lai, linux-edac, linux-kernel

RRL (Retry Read error Log) ownership is currently inferred from
retry_rd_err_log magic values, making control semantics implicit
and harder to understand.

Introduce rrl_ctrl_mode to explicitly describe whether RRL is
controlled by none, BIOS, or Linux, and replace direct checks with
named control states to improve readability and maintainability.

No functional change intended.

Tested-by: Yi Lai <yi1.lai@intel.com>
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
---
 drivers/edac/i10nm_base.c |  7 ++++---
 drivers/edac/skx_common.h | 11 +++++++++++
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c
index 01cc86f697c8..fe148f1f2319 100644
--- a/drivers/edac/i10nm_base.c
+++ b/drivers/edac/i10nm_base.c
@@ -333,7 +333,7 @@ static void show_retry_rd_err_log(struct decoded_addr *res, char *msg,
 				n += scnprintf(msg + n, len - n, "%.16llx ", log);
 
 			/* Clear RRL status if RRL in Linux control mode. */
-			if (retry_rd_err_log == 2 && !j && (log & status_mask))
+			if (res_cfg->rrl_ctrl_mode == RRL_CTRL_LINUX && !j && (log & status_mask))
 				skx_write_imc_reg(imc, ch, offset, width, log & ~status_mask);
 		}
 	}
@@ -1207,9 +1207,10 @@ static int __init i10nm_init(void)
 	mce_register_decode_chain(&i10nm_mce_dec);
 	skx_setup_debug("i10nm_test");
 
+	res_cfg->rrl_ctrl_mode = retry_rd_err_log;
 	if (retry_rd_err_log && res_cfg->reg_rrl_ddr) {
 		skx_set_show_rrl(show_retry_rd_err_log);
-		if (retry_rd_err_log == 2)
+		if (retry_rd_err_log == RRL_CTRL_LINUX)
 			enable_retry_rd_err_log(true);
 	}
 
@@ -1230,7 +1231,7 @@ static void __exit i10nm_exit(void)
 	skx_set_decode(NULL);
 
 	if (retry_rd_err_log && res_cfg->reg_rrl_ddr) {
-		if (retry_rd_err_log == 2)
+		if (retry_rd_err_log == RRL_CTRL_LINUX)
 			enable_retry_rd_err_log(false);
 		skx_set_show_rrl(NULL);
 	}
diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h
index f7f016db122f..4091431356d6 100644
--- a/drivers/edac/skx_common.h
+++ b/drivers/edac/skx_common.h
@@ -93,6 +93,15 @@ enum rrl_source_type {
 	RRL_SRC_FRE_DEMAND,
 };
 
+enum rrl_ctrl_mode {
+	/* Linux does not control RRL or reports values. */
+	RRL_CTRL_NONE,
+	/* Firmware retains control. Linux only reports values. */
+	RRL_CTRL_BIOS,
+	/* Linux takes control, resets mode bits, and clears valid/UC bits; reports values. */
+	RRL_CTRL_LINUX,
+};
+
 /* RRL registers per {,sub-,pseudo-}channel. */
 struct reg_rrl {
 	/* RRL register parts. */
@@ -272,6 +281,8 @@ struct res_config {
 	struct reg_rrl *reg_rrl_ddr;
 	/* RRL register sets per HBM channel */
 	struct reg_rrl *reg_rrl_hbm[2];
+	/* RRL control mode */
+	enum rrl_ctrl_mode rrl_ctrl_mode;
 	union {
 		/* {skx,i10nm}_edac */
 		struct {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 5/8] EDAC/{skx_common,i10nm}: Move RRL handling to common code
  2026-05-21  7:31 [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Qiuxu Zhuo
                   ` (3 preceding siblings ...)
  2026-05-21  7:31 ` [PATCH 4/8] EDAC/{skx_common,i10nm}: Introduce rrl_ctrl_mode Qiuxu Zhuo
@ 2026-05-21  7:31 ` Qiuxu Zhuo
  2026-05-21  7:31 ` [PATCH 6/8] EDAC/skx_common: Add SubChannel support to ADXL decode Qiuxu Zhuo
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Qiuxu Zhuo @ 2026-05-21  7:31 UTC (permalink / raw)
  To: Tony Luck, Borislav Petkov; +Cc: Qiuxu Zhuo, Yi Lai, linux-edac, linux-kernel

Move RRL (Retry Read error Log)  handling from i10nm_edac to skx_common
so it can be shared across EDAC drivers (e.g. imh_edac).

- Move RRL enable/disable and log dumping helpers to skx_common
  to avoid code duplication and enable reuse by other drivers.

- Export skx_enable_rrl() and skx_show_rrl()
  so common RRL handling can be used by i10nm_edac and imh_edac.

No functional change intended.

Tested-by: Yi Lai <yi1.lai@intel.com>
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
---
 drivers/edac/i10nm_base.c | 191 +-------------------------------------
 drivers/edac/skx_common.c | 186 +++++++++++++++++++++++++++++++++++++
 drivers/edac/skx_common.h |   2 +
 3 files changed, 191 insertions(+), 188 deletions(-)

diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c
index fe148f1f2319..2e21eefbb4f5 100644
--- a/drivers/edac/i10nm_base.c
+++ b/drivers/edac/i10nm_base.c
@@ -183,191 +183,6 @@ static struct reg_rrl gnr_reg_rrl_ddr = {
 	.cecnt_widths	= {4, 4, 4, 4, 4, 4, 4, 4},
 };
 
-static void enable_rrl(struct skx_imc *imc, int chan, struct reg_rrl *rrl,
-		       int rrl_set, bool enable, u32 *rrl_ctl)
-{
-	enum rrl_source_type source = rrl->sources[rrl_set];
-	u32 offset = rrl->offsets[rrl_set][0], v;
-	u8 width = rrl->widths[0];
-	bool first, scrub;
-
-	/* First or last read error. */
-	first = (source == RRL_SRC_FRE_SCRUB || source == RRL_SRC_FRE_DEMAND);
-	/* Patrol scrub or on-demand read error. */
-	scrub = (source == RRL_SRC_FRE_SCRUB || source == RRL_SRC_LRE_SCRUB);
-
-	v = skx_read_imc_reg(imc, chan, offset, width);
-
-	if (enable) {
-		/* Save default configurations. */
-		*rrl_ctl = v;
-		v &= ~rrl->uc_mask;
-
-		if (first)
-			v |= rrl->noover_mask;
-		else
-			v &= ~rrl->noover_mask;
-
-		if (scrub)
-			v |= rrl->en_patspr_mask;
-		else
-			v &= ~rrl->en_patspr_mask;
-
-		v |= rrl->en_mask;
-	} else {
-		/* Restore default configurations. */
-		if (*rrl_ctl & rrl->uc_mask)
-			v |= rrl->uc_mask;
-
-		if (first) {
-			if (!(*rrl_ctl & rrl->noover_mask))
-				v &= ~rrl->noover_mask;
-		} else {
-			if (*rrl_ctl & rrl->noover_mask)
-				v |= rrl->noover_mask;
-		}
-
-		if (scrub) {
-			if (!(*rrl_ctl & rrl->en_patspr_mask))
-				v &= ~rrl->en_patspr_mask;
-		} else {
-			if (*rrl_ctl & rrl->en_patspr_mask)
-				v |= rrl->en_patspr_mask;
-		}
-
-		if (!(*rrl_ctl & rrl->en_mask))
-			v &= ~rrl->en_mask;
-	}
-
-	skx_write_imc_reg(imc, chan, offset, width, v);
-}
-
-static void enable_rrls(struct skx_imc *imc, int chan, struct reg_rrl *rrl,
-			bool enable, u32 *rrl_ctl)
-{
-	for (int i = 0; i < rrl->set_num; i++)
-		enable_rrl(imc, chan, rrl, i, enable, rrl_ctl + i);
-}
-
-static void enable_rrls_ddr(struct skx_imc *imc, bool enable)
-{
-	struct reg_rrl *rrl_ddr = res_cfg->reg_rrl_ddr;
-	int i, chan_num = res_cfg->ddr_chan_num;
-	struct skx_channel *chan = imc->chan;
-
-	if (!imc->mbase)
-		return;
-
-	for (i = 0; i < chan_num; i++)
-		enable_rrls(imc, i, rrl_ddr, enable, chan[i].rrl_ctl[0]);
-}
-
-static void enable_rrls_hbm(struct skx_imc *imc, bool enable)
-{
-	struct reg_rrl **rrl_hbm = res_cfg->reg_rrl_hbm;
-	int i, chan_num = res_cfg->hbm_chan_num;
-	struct skx_channel *chan = imc->chan;
-
-	if (!imc->mbase || !imc->hbm_mc || !rrl_hbm[0] || !rrl_hbm[1])
-		return;
-
-	for (i = 0; i < chan_num; i++) {
-		enable_rrls(imc, i, rrl_hbm[0], enable, chan[i].rrl_ctl[0]);
-		enable_rrls(imc, i, rrl_hbm[1], enable, chan[i].rrl_ctl[1]);
-	}
-}
-
-static void enable_retry_rd_err_log(bool enable)
-{
-	struct skx_dev *d;
-	int i, imc_num;
-
-	edac_dbg(2, "\n");
-
-	list_for_each_entry(d, i10nm_edac_list, list) {
-		imc_num  = res_cfg->ddr_imc_num;
-		for (i = 0; i < imc_num; i++)
-			enable_rrls_ddr(&d->imc[i], enable);
-
-		imc_num += res_cfg->hbm_imc_num;
-		for (; i < imc_num; i++)
-			enable_rrls_hbm(&d->imc[i], enable);
-	}
-}
-
-static void show_retry_rd_err_log(struct decoded_addr *res, char *msg,
-				  int len, bool scrub_err)
-{
-	int i, j, n, ch = res->channel, pch = res->cs & 1;
-	struct skx_imc *imc = &res->dev->imc[res->imc];
-	u64 log, corr, status_mask;
-	struct reg_rrl *rrl;
-	bool scrub;
-	u32 offset;
-	u8 width;
-
-	if (!imc->mbase)
-		return;
-
-	rrl = imc->hbm_mc ? res_cfg->reg_rrl_hbm[pch] : res_cfg->reg_rrl_ddr;
-
-	if (!rrl)
-		return;
-
-	status_mask = rrl->over_mask | rrl->uc_mask | rrl->v_mask;
-
-	n = scnprintf(msg, len, " retry_rd_err_log[");
-	for (i = 0; i < rrl->set_num; i++) {
-		scrub = (rrl->sources[i] == RRL_SRC_FRE_SCRUB || rrl->sources[i] == RRL_SRC_LRE_SCRUB);
-		if (scrub_err != scrub)
-			continue;
-
-		for (j = 0; j < rrl->reg_num && len - n > 0; j++) {
-			offset = rrl->offsets[i][j];
-			width = rrl->widths[j];
-			log = skx_read_imc_reg(imc, ch, offset, width);
-
-			if (width == 4)
-				n += scnprintf(msg + n, len - n, "%.8llx ", log);
-			else
-				n += scnprintf(msg + n, len - n, "%.16llx ", log);
-
-			/* Clear RRL status if RRL in Linux control mode. */
-			if (res_cfg->rrl_ctrl_mode == RRL_CTRL_LINUX && !j && (log & status_mask))
-				skx_write_imc_reg(imc, ch, offset, width, log & ~status_mask);
-		}
-	}
-
-	/* Move back one space. */
-	n--;
-	n += scnprintf(msg + n, len - n, "]");
-
-	if (len - n > 0) {
-		n += scnprintf(msg + n, len - n, " correrrcnt[");
-		for (i = 0; i < rrl->cecnt_num && len - n > 0; i++) {
-			offset = rrl->cecnt_offsets[i];
-			width = rrl->cecnt_widths[i];
-			corr = skx_read_imc_reg(imc, ch, offset, width);
-
-			/* CPUs {ICX,SPR} encode two counters per 4-byte CORRERRCNT register. */
-			if (res_cfg->type <= SPR) {
-				n += scnprintf(msg + n, len - n, "%.4llx %.4llx ",
-					      corr & 0xffff, corr >> 16);
-			} else {
-			/* CPUs {GNR} encode one counter per CORRERRCNT register. */
-				if (width == 4)
-					n += scnprintf(msg + n, len - n, "%.8llx ", corr);
-				else
-					n += scnprintf(msg + n, len - n, "%.16llx ", corr);
-			}
-		}
-
-		/* Move back one space. */
-		n--;
-		n += scnprintf(msg + n, len - n, "]");
-	}
-}
-
 static struct pci_dev *pci_get_dev_wrapper(int dom, unsigned int bus,
 					   unsigned int dev, unsigned int fun)
 {
@@ -1209,9 +1024,9 @@ static int __init i10nm_init(void)
 
 	res_cfg->rrl_ctrl_mode = retry_rd_err_log;
 	if (retry_rd_err_log && res_cfg->reg_rrl_ddr) {
-		skx_set_show_rrl(show_retry_rd_err_log);
+		skx_set_show_rrl(skx_show_rrl);
 		if (retry_rd_err_log == RRL_CTRL_LINUX)
-			enable_retry_rd_err_log(true);
+			skx_enable_rrl(true);
 	}
 
 	skx_set_decode(i10nm_mc_decode);
@@ -1232,7 +1047,7 @@ static void __exit i10nm_exit(void)
 
 	if (retry_rd_err_log && res_cfg->reg_rrl_ddr) {
 		if (retry_rd_err_log == RRL_CTRL_LINUX)
-			enable_retry_rd_err_log(false);
+			skx_enable_rrl(false);
 		skx_set_show_rrl(NULL);
 	}
 
diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c
index 2cdef2e69d71..f769a354ea45 100644
--- a/drivers/edac/skx_common.c
+++ b/drivers/edac/skx_common.c
@@ -102,6 +102,192 @@ void skx_write_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width, u64
 }
 EXPORT_SYMBOL_GPL(skx_write_imc_reg);
 
+static void enable_rrl(struct skx_imc *imc, int chan, struct reg_rrl *rrl,
+		       int rrl_set, bool enable, u32 *rrl_ctl)
+{
+	enum rrl_source_type source = rrl->sources[rrl_set];
+	u32 offset = rrl->offsets[rrl_set][0], v;
+	u8 width = rrl->widths[0];
+	bool first, scrub;
+
+	/* First or last read error. */
+	first = (source == RRL_SRC_FRE_SCRUB || source == RRL_SRC_FRE_DEMAND);
+	/* Patrol scrub or on-demand read error. */
+	scrub = (source == RRL_SRC_FRE_SCRUB || source == RRL_SRC_LRE_SCRUB);
+
+	v = skx_read_imc_reg(imc, chan, offset, width);
+
+	if (enable) {
+		/* Save default configurations. */
+		*rrl_ctl = v;
+		v &= ~rrl->uc_mask;
+
+		if (first)
+			v |= rrl->noover_mask;
+		else
+			v &= ~rrl->noover_mask;
+
+		if (scrub)
+			v |= rrl->en_patspr_mask;
+		else
+			v &= ~rrl->en_patspr_mask;
+
+		v |= rrl->en_mask;
+	} else {
+		/* Restore default configurations. */
+		if (*rrl_ctl & rrl->uc_mask)
+			v |= rrl->uc_mask;
+
+		if (first) {
+			if (!(*rrl_ctl & rrl->noover_mask))
+				v &= ~rrl->noover_mask;
+		} else {
+			if (*rrl_ctl & rrl->noover_mask)
+				v |= rrl->noover_mask;
+		}
+
+		if (scrub) {
+			if (!(*rrl_ctl & rrl->en_patspr_mask))
+				v &= ~rrl->en_patspr_mask;
+		} else {
+			if (*rrl_ctl & rrl->en_patspr_mask)
+				v |= rrl->en_patspr_mask;
+		}
+
+		if (!(*rrl_ctl & rrl->en_mask))
+			v &= ~rrl->en_mask;
+	}
+
+	skx_write_imc_reg(imc, chan, offset, width, v);
+}
+
+static void enable_rrls(struct skx_imc *imc, int chan, struct reg_rrl *rrl,
+			bool enable, u32 *rrl_ctl)
+{
+	for (int i = 0; i < rrl->set_num; i++)
+		enable_rrl(imc, chan, rrl, i, enable, rrl_ctl + i);
+}
+
+static void enable_rrls_ddr(struct skx_imc *imc, bool enable)
+{
+	struct reg_rrl *rrl_ddr = skx_res_cfg->reg_rrl_ddr;
+	int i, chan_num = skx_res_cfg->ddr_chan_num;
+	struct skx_channel *chan = imc->chan;
+
+	if (!imc->mbase)
+		return;
+
+	for (i = 0; i < chan_num; i++)
+		enable_rrls(imc, i, rrl_ddr, enable, chan[i].rrl_ctl[0]);
+}
+
+static void enable_rrls_hbm(struct skx_imc *imc, bool enable)
+{
+	struct reg_rrl **rrl_hbm = skx_res_cfg->reg_rrl_hbm;
+	int i, chan_num = skx_res_cfg->hbm_chan_num;
+	struct skx_channel *chan = imc->chan;
+
+	if (!imc->mbase || !imc->hbm_mc || !rrl_hbm[0] || !rrl_hbm[1])
+		return;
+
+	for (i = 0; i < chan_num; i++) {
+		enable_rrls(imc, i, rrl_hbm[0], enable, chan[i].rrl_ctl[0]);
+		enable_rrls(imc, i, rrl_hbm[1], enable, chan[i].rrl_ctl[1]);
+	}
+}
+
+void skx_enable_rrl(bool enable)
+{
+	struct skx_dev *d;
+	int i, imc_num;
+
+	edac_dbg(2, "\n");
+
+	list_for_each_entry(d, &dev_edac_list, list) {
+		imc_num  = skx_res_cfg->ddr_imc_num;
+		for (i = 0; i < imc_num; i++)
+			enable_rrls_ddr(&d->imc[i], enable);
+
+		imc_num += skx_res_cfg->hbm_imc_num;
+		for (; i < imc_num; i++)
+			enable_rrls_hbm(&d->imc[i], enable);
+	}
+}
+EXPORT_SYMBOL_GPL(skx_enable_rrl);
+
+void skx_show_rrl(struct decoded_addr *res, char *msg, int len, bool scrub_err)
+{
+	int i, j, n, ch = res->channel, pch = res->cs & 1;
+	struct skx_imc *imc = &res->dev->imc[res->imc];
+	u64 log, corr, status_mask;
+	struct reg_rrl *rrl;
+	bool scrub;
+	u32 offset;
+	u8 width;
+
+	if (!imc->mbase)
+		return;
+
+	rrl = imc->hbm_mc ? skx_res_cfg->reg_rrl_hbm[pch] : skx_res_cfg->reg_rrl_ddr;
+
+	if (!rrl)
+		return;
+
+	status_mask = rrl->over_mask | rrl->uc_mask | rrl->v_mask;
+
+	n = scnprintf(msg, len, " retry_rd_err_log[");
+	for (i = 0; i < rrl->set_num; i++) {
+		scrub = (rrl->sources[i] == RRL_SRC_FRE_SCRUB || rrl->sources[i] == RRL_SRC_LRE_SCRUB);
+		if (scrub_err != scrub)
+			continue;
+
+		for (j = 0; j < rrl->reg_num && len - n > 0; j++) {
+			offset = rrl->offsets[i][j];
+			width = rrl->widths[j];
+			log = skx_read_imc_reg(imc, ch, offset, width);
+
+			if (width == 4)
+				n += scnprintf(msg + n, len - n, "%.8llx ", log);
+			else
+				n += scnprintf(msg + n, len - n, "%.16llx ", log);
+
+			/* Clear RRL status if RRL in Linux control mode. */
+			if (skx_res_cfg->rrl_ctrl_mode == RRL_CTRL_LINUX && !j && (log & status_mask))
+				skx_write_imc_reg(imc, ch, offset, width, log & ~status_mask);
+		}
+	}
+
+	/* Move back one space. */
+	n--;
+	n += scnprintf(msg + n, len - n, "]");
+
+	if (len - n > 0) {
+		n += scnprintf(msg + n, len - n, " correrrcnt[");
+		for (i = 0; i < rrl->cecnt_num && len - n > 0; i++) {
+			offset = rrl->cecnt_offsets[i];
+			width = rrl->cecnt_widths[i];
+			corr = skx_read_imc_reg(imc, ch, offset, width);
+
+			/* CPUs {ICX,SPR} encode two counters per 4-byte CORRERRCNT register. */
+			if (skx_res_cfg->type <= SPR) {
+				n += scnprintf(msg + n, len - n, "%.4llx %.4llx ",
+					      corr & 0xffff, corr >> 16);
+			} else {
+			/* CPUs {GNR} encode one counter per CORRERRCNT register. */
+				if (width == 4)
+					n += scnprintf(msg + n, len - n, "%.8llx ", corr);
+				else
+					n += scnprintf(msg + n, len - n, "%.16llx ", corr);
+			}
+		}
+
+		/* Move back one space. */
+		n--;
+		n += scnprintf(msg + n, len - n, "]");
+	}
+}
+EXPORT_SYMBOL_GPL(skx_show_rrl);
+
 int skx_adxl_get(void)
 {
 	const char * const *names;
diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h
index 4091431356d6..eea2d95cc0ac 100644
--- a/drivers/edac/skx_common.h
+++ b/drivers/edac/skx_common.h
@@ -344,6 +344,8 @@ int skx_adxl_get(void);
 void skx_adxl_put(void);
 void skx_set_decode(skx_decode_f decode);
 void skx_set_show_rrl(skx_show_rrl_f rrl);
+void skx_show_rrl(struct decoded_addr *res, char *msg, int len, bool scrub_err);
+void skx_enable_rrl(bool enable);
 void skx_set_mem_cfg(bool mem_cfg_2lm);
 void skx_set_res_cfg(struct res_config *cfg);
 void skx_init_mc_mapping(struct skx_dev *d);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 6/8] EDAC/skx_common: Add SubChannel support to ADXL decode
  2026-05-21  7:31 [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Qiuxu Zhuo
                   ` (4 preceding siblings ...)
  2026-05-21  7:31 ` [PATCH 5/8] EDAC/{skx_common,i10nm}: Move RRL handling to common code Qiuxu Zhuo
@ 2026-05-21  7:31 ` Qiuxu Zhuo
  2026-05-21  7:31 ` [PATCH 7/8] EDAC/{skx_common,i10nm}: Prepare RRL for sub-channel granularity Qiuxu Zhuo
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Qiuxu Zhuo @ 2026-05-21  7:31 UTC (permalink / raw)
  To: Tony Luck, Borislav Petkov; +Cc: Qiuxu Zhuo, Yi Lai, linux-edac, linux-kernel

Diamond Rapids server RRL (Retry Read error Log) operates at sub-channel
granularity. Add SubChannel support to ADXL decoding in preparation for
enabling this feature.

Also introduce adxl_component_required() to validate mandatory ADXL
components to improve code readability.

Tested-by: Yi Lai <yi1.lai@intel.com>
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
---
 drivers/edac/skx_common.c | 20 +++++++++++++++++++-
 drivers/edac/skx_common.h |  5 +++++
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c
index f769a354ea45..4d832f02fb59 100644
--- a/drivers/edac/skx_common.c
+++ b/drivers/edac/skx_common.c
@@ -31,10 +31,12 @@ static const char * const component_names[] = {
 	[INDEX_CHANNEL]		= "ChannelId",
 	[INDEX_DIMM]		= "DimmSlotId",
 	[INDEX_CS]		= "ChipSelect",
+	[INDEX_SUBCH]		= "SubChId",
 	[INDEX_NM_MEMCTRL]	= "NmMemoryControllerId",
 	[INDEX_NM_CHANNEL]	= "NmChannelId",
 	[INDEX_NM_DIMM]		= "NmDimmSlotId",
 	[INDEX_NM_CS]		= "NmChipSelect",
+	[INDEX_NM_SUBCH]	= "NmSubChId",
 };
 
 static int component_indices[ARRAY_SIZE(component_names)];
@@ -43,6 +45,7 @@ static const char * const *adxl_component_names;
 static u64 *adxl_values;
 static char *adxl_msg;
 static unsigned long adxl_nm_bitmap;
+static unsigned long adxl_bitmap;
 
 static char skx_msg[MSG_SIZE];
 static skx_decode_f driver_decode;
@@ -288,6 +291,15 @@ void skx_show_rrl(struct decoded_addr *res, char *msg, int len, bool scrub_err)
 }
 EXPORT_SYMBOL_GPL(skx_show_rrl);
 
+static bool adxl_component_required(int idx)
+{
+	return idx == INDEX_SOCKET ||
+	       idx == INDEX_MEMCTRL ||
+	       idx == INDEX_CHANNEL ||
+	       idx == INDEX_DIMM ||
+	       idx == INDEX_CS;
+}
+
 int skx_adxl_get(void)
 {
 	const char * const *names;
@@ -306,12 +318,14 @@ int skx_adxl_get(void)
 
 				if (i >= INDEX_NM_FIRST)
 					adxl_nm_bitmap |= 1 << i;
+				else
+					adxl_bitmap |= 1 << i;
 
 				break;
 			}
 		}
 
-		if (!names[j] && i < INDEX_NM_FIRST)
+		if (!names[j] && adxl_component_required(i))
 			goto err;
 	}
 
@@ -438,11 +452,15 @@ static bool skx_adxl_decode(struct decoded_addr *res, enum error_source err_src)
 			       (int)adxl_values[component_indices[INDEX_NM_DIMM]] : -1;
 		res->cs      = (adxl_nm_bitmap & BIT_NM_CS) ?
 			       (int)adxl_values[component_indices[INDEX_NM_CS]] : -1;
+		res->subch   = (adxl_nm_bitmap & BIT_NM_SUBCH) ?
+			       (int)adxl_values[component_indices[INDEX_NM_SUBCH]] : -1;
 	} else {
 		res->imc     = (int)adxl_values[component_indices[INDEX_MEMCTRL]];
 		res->channel = (int)adxl_values[component_indices[INDEX_CHANNEL]];
 		res->dimm    = (int)adxl_values[component_indices[INDEX_DIMM]];
 		res->cs      = (int)adxl_values[component_indices[INDEX_CS]];
+		res->subch   = (adxl_bitmap & BIT_SUBCH) ?
+			       (int)adxl_values[component_indices[INDEX_SUBCH]] : -1;
 	}
 
 	if (res->imc < 0) {
diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h
index eea2d95cc0ac..38d96bf71fd9 100644
--- a/drivers/edac/skx_common.h
+++ b/drivers/edac/skx_common.h
@@ -210,11 +210,13 @@ enum {
 	INDEX_CHANNEL,
 	INDEX_DIMM,
 	INDEX_CS,
+	INDEX_SUBCH,
 	INDEX_NM_FIRST,
 	INDEX_NM_MEMCTRL = INDEX_NM_FIRST,
 	INDEX_NM_CHANNEL,
 	INDEX_NM_DIMM,
 	INDEX_NM_CS,
+	INDEX_NM_SUBCH,
 	INDEX_MAX
 };
 
@@ -225,10 +227,12 @@ enum error_source {
 	ERR_SRC_NOT_MEMORY,
 };
 
+#define BIT_SUBCH	BIT_ULL(INDEX_SUBCH)
 #define BIT_NM_MEMCTRL	BIT_ULL(INDEX_NM_MEMCTRL)
 #define BIT_NM_CHANNEL	BIT_ULL(INDEX_NM_CHANNEL)
 #define BIT_NM_DIMM	BIT_ULL(INDEX_NM_DIMM)
 #define BIT_NM_CS	BIT_ULL(INDEX_NM_CS)
+#define BIT_NM_SUBCH	BIT_ULL(INDEX_NM_SUBCH)
 
 struct decoded_addr {
 	struct mce *mce;
@@ -242,6 +246,7 @@ struct decoded_addr {
 	int	chanways;
 	int	dimm;
 	int	cs;
+	int	subch;
 	int	rank;
 	int	channel_rank;
 	u64	rank_address;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 7/8] EDAC/{skx_common,i10nm}: Prepare RRL for sub-channel granularity
  2026-05-21  7:31 [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Qiuxu Zhuo
                   ` (5 preceding siblings ...)
  2026-05-21  7:31 ` [PATCH 6/8] EDAC/skx_common: Add SubChannel support to ADXL decode Qiuxu Zhuo
@ 2026-05-21  7:31 ` Qiuxu Zhuo
  2026-05-21  7:31 ` [PATCH 8/8] EDAC/imh: Add RRL support for Intel Diamond Rapids server Qiuxu Zhuo
  2026-05-27 16:18 ` [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Luck, Tony
  8 siblings, 0 replies; 12+ messages in thread
From: Qiuxu Zhuo @ 2026-05-21  7:31 UTC (permalink / raw)
  To: Tony Luck, Borislav Petkov; +Cc: Qiuxu Zhuo, Yi Lai, linux-edac, linux-kernel

To prepare for enabling Diamond Rapids server RRL (Retry Read error Log),
which operates at sub-channel granularity by converting struct
res_config::reg_rrl_ddr from a single pointer to an array (reg_rrl_ddr[2])
and updating all users in i10nm_edac and skx_common accordingly.

Initialize only reg_rrl_ddr[0] for existing platforms and prepare for
supporting two RRL set groups per DDR channel (one per sub-channel)
when present.

Tested-by: Yi Lai <yi1.lai@intel.com>
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
---
 drivers/edac/i10nm_base.c | 12 ++++++------
 drivers/edac/skx_common.c | 35 +++++++++++++++++++++++++++++------
 drivers/edac/skx_common.h |  2 +-
 3 files changed, 36 insertions(+), 13 deletions(-)

diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c
index 2e21eefbb4f5..fa1853f252b0 100644
--- a/drivers/edac/i10nm_base.c
+++ b/drivers/edac/i10nm_base.c
@@ -767,7 +767,7 @@ static struct res_config i10nm_cfg0 = {
 	.ddr_mdev_bdf		= {0, 12, 0},
 	.hbm_mdev_bdf		= {0, 12, 1},
 	.sad_all_offset		= 0x108,
-	.reg_rrl_ddr		= &icx_reg_rrl_ddr,
+	.reg_rrl_ddr[0]		= &icx_reg_rrl_ddr,
 };
 
 static struct res_config i10nm_cfg1 = {
@@ -785,7 +785,7 @@ static struct res_config i10nm_cfg1 = {
 	.ddr_mdev_bdf		= {0, 12, 0},
 	.hbm_mdev_bdf		= {0, 12, 1},
 	.sad_all_offset		= 0x108,
-	.reg_rrl_ddr		= &icx_reg_rrl_ddr,
+	.reg_rrl_ddr[0]		= &icx_reg_rrl_ddr,
 };
 
 static struct res_config spr_cfg = {
@@ -808,7 +808,7 @@ static struct res_config spr_cfg = {
 	.ddr_mdev_bdf		= {0, 12, 0},
 	.hbm_mdev_bdf		= {0, 12, 1},
 	.sad_all_offset		= 0x300,
-	.reg_rrl_ddr		= &spr_reg_rrl_ddr,
+	.reg_rrl_ddr[0]		= &spr_reg_rrl_ddr,
 	.reg_rrl_hbm[0]		= &spr_reg_rrl_hbm_pch0,
 	.reg_rrl_hbm[1]		= &spr_reg_rrl_hbm_pch1,
 };
@@ -828,7 +828,7 @@ static struct res_config gnr_cfg = {
 	.uracu_bdf		= {0, 0, 1},
 	.ddr_mdev_bdf		= {0, 5, 1},
 	.sad_all_offset		= 0x300,
-	.reg_rrl_ddr		= &gnr_reg_rrl_ddr,
+	.reg_rrl_ddr[0]		= &gnr_reg_rrl_ddr,
 };
 
 static const struct x86_cpu_id i10nm_cpuids[] = {
@@ -1023,7 +1023,7 @@ static int __init i10nm_init(void)
 	skx_setup_debug("i10nm_test");
 
 	res_cfg->rrl_ctrl_mode = retry_rd_err_log;
-	if (retry_rd_err_log && res_cfg->reg_rrl_ddr) {
+	if (retry_rd_err_log && res_cfg->reg_rrl_ddr[0]) {
 		skx_set_show_rrl(skx_show_rrl);
 		if (retry_rd_err_log == RRL_CTRL_LINUX)
 			skx_enable_rrl(true);
@@ -1045,7 +1045,7 @@ static void __exit i10nm_exit(void)
 
 	skx_set_decode(NULL);
 
-	if (retry_rd_err_log && res_cfg->reg_rrl_ddr) {
+	if (retry_rd_err_log && res_cfg->reg_rrl_ddr[0]) {
 		if (retry_rd_err_log == RRL_CTRL_LINUX)
 			skx_enable_rrl(false);
 		skx_set_show_rrl(NULL);
diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c
index 4d832f02fb59..bfd0cd1689ed 100644
--- a/drivers/edac/skx_common.c
+++ b/drivers/edac/skx_common.c
@@ -173,15 +173,18 @@ static void enable_rrls(struct skx_imc *imc, int chan, struct reg_rrl *rrl,
 
 static void enable_rrls_ddr(struct skx_imc *imc, bool enable)
 {
-	struct reg_rrl *rrl_ddr = skx_res_cfg->reg_rrl_ddr;
+	struct reg_rrl **rrl_ddr = skx_res_cfg->reg_rrl_ddr;
 	int i, chan_num = skx_res_cfg->ddr_chan_num;
 	struct skx_channel *chan = imc->chan;
 
 	if (!imc->mbase)
 		return;
 
-	for (i = 0; i < chan_num; i++)
-		enable_rrls(imc, i, rrl_ddr, enable, chan[i].rrl_ctl[0]);
+	for (i = 0; i < chan_num; i++) {
+		enable_rrls(imc, i, rrl_ddr[0], enable, chan[i].rrl_ctl[0]);
+		if (rrl_ddr[1])
+			enable_rrls(imc, i, rrl_ddr[1], enable, chan[i].rrl_ctl[1]);
+	}
 }
 
 static void enable_rrls_hbm(struct skx_imc *imc, bool enable)
@@ -218,10 +221,31 @@ void skx_enable_rrl(bool enable)
 }
 EXPORT_SYMBOL_GPL(skx_enable_rrl);
 
+static struct reg_rrl *get_rrl_reg(struct decoded_addr *res, struct res_config *cfg)
+{
+	struct skx_imc *imc = &res->dev->imc[res->imc];
+
+	/* HBM has two groups of RRL sets, one per pseudo-channel. */
+	if (imc->hbm_mc)
+		return cfg->reg_rrl_hbm[res->cs & 1];
+
+	/* One group of RRL sets per DDR channel. */
+	if (!cfg->reg_rrl_ddr[1])
+		return cfg->reg_rrl_ddr[0];
+
+	if (res->subch == -1) {
+		skx_printk(KERN_ERR, "Invalid sub-channel id (-1), possibly missing %s ADXL component.\n", component_names[INDEX_SUBCH]);
+		return NULL;
+	}
+
+	/* Two groups of RRL sets per DDR channel (e.g., DMR: one group per sub-channel). */
+	return cfg->reg_rrl_ddr[res->subch & 1];
+}
+
 void skx_show_rrl(struct decoded_addr *res, char *msg, int len, bool scrub_err)
 {
-	int i, j, n, ch = res->channel, pch = res->cs & 1;
 	struct skx_imc *imc = &res->dev->imc[res->imc];
+	int i, j, n, ch = res->channel;
 	u64 log, corr, status_mask;
 	struct reg_rrl *rrl;
 	bool scrub;
@@ -231,8 +255,7 @@ void skx_show_rrl(struct decoded_addr *res, char *msg, int len, bool scrub_err)
 	if (!imc->mbase)
 		return;
 
-	rrl = imc->hbm_mc ? skx_res_cfg->reg_rrl_hbm[pch] : skx_res_cfg->reg_rrl_ddr;
-
+	rrl = get_rrl_reg(res, skx_res_cfg);
 	if (!rrl)
 		return;
 
diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h
index 38d96bf71fd9..6d4cf0dd412a 100644
--- a/drivers/edac/skx_common.h
+++ b/drivers/edac/skx_common.h
@@ -283,7 +283,7 @@ struct res_config {
 	int hbm_chan_mmio_sz;
 	bool support_ddr5;
 	/* RRL register sets per DDR channel */
-	struct reg_rrl *reg_rrl_ddr;
+	struct reg_rrl *reg_rrl_ddr[2];
 	/* RRL register sets per HBM channel */
 	struct reg_rrl *reg_rrl_hbm[2];
 	/* RRL control mode */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 8/8] EDAC/imh: Add RRL support for Intel Diamond Rapids server
  2026-05-21  7:31 [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Qiuxu Zhuo
                   ` (6 preceding siblings ...)
  2026-05-21  7:31 ` [PATCH 7/8] EDAC/{skx_common,i10nm}: Prepare RRL for sub-channel granularity Qiuxu Zhuo
@ 2026-05-21  7:31 ` Qiuxu Zhuo
  2026-05-27 16:18 ` [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Luck, Tony
  8 siblings, 0 replies; 12+ messages in thread
From: Qiuxu Zhuo @ 2026-05-21  7:31 UTC (permalink / raw)
  To: Tony Luck, Borislav Petkov; +Cc: Qiuxu Zhuo, Yi Lai, linux-edac, linux-kernel

Compared to previous generations, Diamond Rapids RRL (Retry Read error Log)
operates at DDR sub-channel granularity and adds an extra register per set.
It also increases the CORRERRCNT register width from 4 to 8 bytes while
reducing the number of registers from 8 to 4.

Add the Diamond Rapids RRL register configuration table and enable support.

Tested-by: Yi Lai <yi1.lai@intel.com>
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
---
 drivers/edac/imh_base.c   | 52 +++++++++++++++++++++++++++++++++++++++
 drivers/edac/skx_common.h |  2 +-
 2 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/drivers/edac/imh_base.c b/drivers/edac/imh_base.c
index dfdcfa127ce7..6ca0df031bf5 100644
--- a/drivers/edac/imh_base.c
+++ b/drivers/edac/imh_base.c
@@ -71,6 +71,39 @@ struct local_reg {
 		.width	= (cfg)->ip_name##_reg_##reg_name##_width,		\
 	}
 
+static struct res_config *res_cfg;
+static int retry_rd_err_log;
+
+#define REG_RRL_DEFINE(a0, a1, a2, a3, a4, a5, a6, b0, b1, b2, b3)	\
+	{								\
+		.set_num = 4,						\
+		.reg_num = 7,						\
+		.sources = {RRL_SRC_FRE_SCRUB, RRL_SRC_FRE_DEMAND, RRL_SRC_LRE_SCRUB, RRL_SRC_LRE_DEMAND},	\
+		.offsets = {									\
+			{a0,      a1,      a2,      a3,      a4,      a5,      a6},		\
+			{a0 + 4,  a1 + 4,  a2 + 8,  a3 + 4,  a4 + 4,  a5 + 8,  a6 + 8},		\
+			{a0 + 8,  a1 + 8,  a2 + 16, a3 + 8,  a4 + 8,  a5 + 16, a6 + 16},	\
+			{a0 + 12, a1 + 12, a2 + 24, a3 + 12, a4 + 12, a5 + 24, a6 + 24},	\
+		},										\
+		.widths		= {4, 4, 8, 4, 4, 8, 8},		\
+		.v_mask		= BIT(0),				\
+		.uc_mask	= BIT(1),				\
+		.over_mask	= BIT(2),				\
+		.en_mask	= BIT(12),				\
+		.en_patspr_mask	= BIT(14),				\
+		.noover_mask	= BIT(15),				\
+		.cecnt_num	= 4,					\
+		.cecnt_offsets	= {b0, b1, b2, b3},			\
+		.cecnt_widths	= {8, 8, 8, 8},				\
+}
+
+static struct reg_rrl dmr_reg_rrl_ddr_subch0 = REG_RRL_DEFINE(
+	0x2dc0, 0x2dd0, 0x2de0, 0x2e00, 0x2e10, 0x2f70, 0x0200,
+	0x2c10, 0x2c18, 0x2c20, 0x2c28);
+static struct reg_rrl dmr_reg_rrl_ddr_subch1 = REG_RRL_DEFINE(
+	0x6dc0, 0x6dd0, 0x6de0, 0x6e00, 0x6e10, 0x6f70, 0x4200,
+	0x6c10, 0x6c18, 0x6c20, 0x6c28);
+
 static void __read_local_reg(void *reg)
 {
 	struct local_reg *r = (struct local_reg *)reg;
@@ -480,6 +513,8 @@ static struct res_config dmr_cfg = {
 	.ha_size			= 0x1000,
 	.ha_reg_mode_offset		= 0x4a0,
 	.ha_reg_mode_width		= 4,
+	.reg_rrl_ddr[0]			= &dmr_reg_rrl_ddr_subch0,
+	.reg_rrl_ddr[1]			= &dmr_reg_rrl_ddr_subch1,
 };
 
 static const struct x86_cpu_id imh_cpuids[] = {
@@ -519,6 +554,7 @@ static int __init imh_init(void)
 		return -ENODEV;
 	cfg = (struct res_config *)id->driver_data;
 	skx_set_res_cfg(cfg);
+	res_cfg = cfg;
 
 	if (!imh_get_tolm_tohm(cfg, &tolm, &tohm))
 		return -ENODEV;
@@ -553,6 +589,13 @@ static int __init imh_init(void)
 	mce_register_decode_chain(&imh_mce_dec);
 	skx_setup_debug("imh_test");
 
+	cfg->rrl_ctrl_mode = retry_rd_err_log;
+	if (retry_rd_err_log && cfg->reg_rrl_ddr[0]) {
+		skx_set_show_rrl(skx_show_rrl);
+		if (retry_rd_err_log == RRL_CTRL_LINUX)
+			skx_enable_rrl(true);
+	}
+
 	imh_printk(KERN_INFO, "%s\n", IMH_REVISION);
 
 	return 0;
@@ -565,6 +608,12 @@ static void __exit imh_exit(void)
 {
 	edac_dbg(2, "\n");
 
+	if (retry_rd_err_log && res_cfg->reg_rrl_ddr[0]) {
+		if (retry_rd_err_log == RRL_CTRL_LINUX)
+			skx_enable_rrl(false);
+		skx_set_show_rrl(NULL);
+	}
+
 	skx_teardown_debug();
 	mce_unregister_decode_chain(&imh_mce_dec);
 	skx_adxl_put();
@@ -574,6 +623,9 @@ static void __exit imh_exit(void)
 module_init(imh_init);
 module_exit(imh_exit);
 
+module_param(retry_rd_err_log, int, 0444);
+MODULE_PARM_DESC(retry_rd_err_log, "retry_rd_err_log: 0=off(default), 1=bios(Linux doesn't reset any control bits, but just reports values.), 2=linux(Linux tries to take control and resets mode bits, clear valid/UC bits after reading.)");
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Qiuxu Zhuo");
 MODULE_DESCRIPTION("MC Driver for Intel servers using IMH-based memory controller");
diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h
index 6d4cf0dd412a..777252cca809 100644
--- a/drivers/edac/skx_common.h
+++ b/drivers/edac/skx_common.h
@@ -77,7 +77,7 @@
 /* Max RRL register sets per {,sub-,pseudo-}channel. */
 #define NUM_RRL_SET		4
 /* Max RRL registers per set. */
-#define NUM_RRL_REG		6
+#define NUM_RRL_REG		7
 /* Max correctable error count registers. */
 #define NUM_CECNT_REG		8
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers
  2026-05-21  7:31 [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Qiuxu Zhuo
                   ` (7 preceding siblings ...)
  2026-05-21  7:31 ` [PATCH 8/8] EDAC/imh: Add RRL support for Intel Diamond Rapids server Qiuxu Zhuo
@ 2026-05-27 16:18 ` Luck, Tony
  8 siblings, 0 replies; 12+ messages in thread
From: Luck, Tony @ 2026-05-27 16:18 UTC (permalink / raw)
  To: Qiuxu Zhuo; +Cc: Borislav Petkov, Yi Lai, linux-edac, linux-kernel

On Thu, May 21, 2026 at 03:31:04PM +0800, Qiuxu Zhuo wrote:
> This patch set adds RRL (Retry Read Error Log) support for Intel Diamond Rapids.
> 
> RRL provides detailed information about retry read errors reported by the memory controller,
> which helps diagnose memory reliability issues. Intel Diamond Rapids introduces sub-channel
> granularity and requires updates to the existing EDAC RRL handling.
> 
> To avoid code duplication and ensure consistent behavior across platforms, the existing i10nm
> RRL implementation is refactored and moved into common code shared by i10nm_edac and imh_edac.
> 
> 
> The patch set is organized as follows:
> 
>   Patch 1-5: Refactor i10nm RRL code and move it into common helpers.
> 
>   Patch 6-7: Prepare ADXL sub-channel decoding and extend RRL handling for sub-channel granularity.
> 
>   Patch 8: Add RRL support for Diamond Rapids.

Applied for 7.2 merge window. Thanks.

-Tony

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/8] EDAC/{skx_common,i10nm,imh}: Move MC register access helpers to skx_common
  2026-05-21  7:31 ` [PATCH 1/8] EDAC/{skx_common,i10nm,imh}: Move MC register access helpers to skx_common Qiuxu Zhuo
@ 2026-06-15  2:26   ` Borislav Petkov
  2026-06-16  2:02     ` Zhuo, Qiuxu
  0 siblings, 1 reply; 12+ messages in thread
From: Borislav Petkov @ 2026-06-15  2:26 UTC (permalink / raw)
  To: Qiuxu Zhuo; +Cc: Tony Luck, Yi Lai, linux-edac, linux-kernel

On Thu, May 21, 2026 at 03:31:05PM +0800, Qiuxu Zhuo wrote:
> Both i10nm_basic.c and imh_basic.c use identical helpers for accessing
> memory controller MMIO-based registers. Move these helpers to skx_common.c
> to eliminate code duplication. This change also prepares for an upcoming
> patch that will move RRL(retry_rd_err_log) code from i10nm_basic.c to

For the future:

Avoid having "This patch" or "This commit" in the commit message. It is
tautologically useless.

Also, do

$ git grep 'This patch' Documentation/process

for more details.

Also, there's no upcoming patch in git history either.

So please formulate your commit messages without referencing some patches
which no one would know about when reading this commit message in the future.

Also, in all your commit messages, do not explain what the code does - that's
visible from the diff itself. Rather explain why the patch exists.

Just several of things that I noticed while preparing the pull request.

We have a bunch of good suggestions here

https://docs.kernel.org/process/maintainer-tip.html

and in Documentation/process/submitting-patches.rst

Please read them and pay attention to that stuff in your future submissions.

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette

^ permalink raw reply	[flat|nested] 12+ messages in thread

* RE: [PATCH 1/8] EDAC/{skx_common,i10nm,imh}: Move MC register access helpers to skx_common
  2026-06-15  2:26   ` Borislav Petkov
@ 2026-06-16  2:02     ` Zhuo, Qiuxu
  0 siblings, 0 replies; 12+ messages in thread
From: Zhuo, Qiuxu @ 2026-06-16  2:02 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Luck, Tony, Lai, Yi1, linux-edac@vger.kernel.org,
	linux-kernel@vger.kernel.org

> From: Borislav Petkov <bp@alien8.de>
> Sent: Monday, June 15, 2026 10:26 AM
> To: Zhuo, Qiuxu <qiuxu.zhuo@intel.com>
> Cc: Luck, Tony <tony.luck@intel.com>; Lai, Yi1 <yi1.lai@intel.com>; linux-
> edac@vger.kernel.org; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH 1/8] EDAC/{skx_common,i10nm,imh}: Move MC register
> access helpers to skx_common
> 
> On Thu, May 21, 2026 at 03:31:05PM +0800, Qiuxu Zhuo wrote:
> > Both i10nm_basic.c and imh_basic.c use identical helpers for accessing
> > memory controller MMIO-based registers. Move these helpers to
> > skx_common.c to eliminate code duplication. This change also prepares
> > for an upcoming patch that will move RRL(retry_rd_err_log) code from
> > i10nm_basic.c to
> 
> For the future:
> 
> Avoid having "This patch" or "This commit" in the commit message. It is
> tautologically useless.
> 
> Also, do
> 
> $ git grep 'This patch' Documentation/process
> 
> for more details.
> 
> Also, there's no upcoming patch in git history either.
> 
> So please formulate your commit messages without referencing some
> patches which no one would know about when reading this commit message
> in the future.
> 
> Also, in all your commit messages, do not explain what the code does - that's
> visible from the diff itself. Rather explain why the patch exists.
> 
> Just several of things that I noticed while preparing the pull request.
> 
> We have a bunch of good suggestions here
> 
> https://docs.kernel.org/process/maintainer-tip.html
> 
> and in Documentation/process/submitting-patches.rst
> 
> Please read them and pay attention to that stuff in your future submissions.

Thanks, Boris, for pointing these out.

I'll pay closer attention to the points you raised. I'll also review the documentation you referenced again.

Thanks!
-Qiuxu


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-06-16  2:02 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-21  7:31 [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Qiuxu Zhuo
2026-05-21  7:31 ` [PATCH 1/8] EDAC/{skx_common,i10nm,imh}: Move MC register access helpers to skx_common Qiuxu Zhuo
2026-06-15  2:26   ` Borislav Petkov
2026-06-16  2:02     ` Zhuo, Qiuxu
2026-05-21  7:31 ` [PATCH 2/8] EDAC/{skx_common,skx,i10nm}: Split skx_set_decode() Qiuxu Zhuo
2026-05-21  7:31 ` [PATCH 3/8] EDAC/{skx_common,i10nm}: Rename rrl_mode to rrl_source_type Qiuxu Zhuo
2026-05-21  7:31 ` [PATCH 4/8] EDAC/{skx_common,i10nm}: Introduce rrl_ctrl_mode Qiuxu Zhuo
2026-05-21  7:31 ` [PATCH 5/8] EDAC/{skx_common,i10nm}: Move RRL handling to common code Qiuxu Zhuo
2026-05-21  7:31 ` [PATCH 6/8] EDAC/skx_common: Add SubChannel support to ADXL decode Qiuxu Zhuo
2026-05-21  7:31 ` [PATCH 7/8] EDAC/{skx_common,i10nm}: Prepare RRL for sub-channel granularity Qiuxu Zhuo
2026-05-21  7:31 ` [PATCH 8/8] EDAC/imh: Add RRL support for Intel Diamond Rapids server Qiuxu Zhuo
2026-05-27 16:18 ` [PATCH 0/8] EDAC: Add RRL support for Intel Diamond Rapids servers Luck, Tony

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox