[PATCH 1/3] EDAC/loongson: Encode node and MC info into mc_idx

Qunqin Zhao <[email protected]> Thu, 30 Jul 2026 14:30:33 +0800
Newsgroups dev.linux.lists.loongarch,org.kernel.vger.linux-edac,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
From: Wang Jinwei <[email protected]>

On Loongson multi-node systems, memory controllers are distributed
across multiple nodes. Encode node and MC information into mc_idx
so that sysfs entries uniquely identify each controller.

The driver gets mc-idx from ACPI _DSD property 'mc-idx' if firmware
provides it. If not, it falls back to edac_device_alloc_index() for
legacy firmware compatibility.

The number of memory controllers per node (mc-per-node) is obtained
from firmware via ACPI _DSD property, with a default of 4 for
backward compatibility. The node and MC IDs are decoded from mc_idx:
     node = mc_idx / mc_per_node
     mc = mc_idx % mc_per_node

For example, with mc-per-node=4:
     mc-idx 0-3   -> node0 mc0-mc3
     mc-idx 4-7   -> node1 mc0-mc3
     ...
     mc-idx 28-31 -> node7 mc0-mc3

The dmesg output looks like this:
Before: (no _DSD mc-idx)
EDAC MC3: 510 CE error on MC#3Channel#0_DIMM#0 (channel:0 slot:0
page:0x0 offset:0x0 grain:8 syndrome:0x0)

After: (_DSD mc-idx)
EDAC MC3: 510 CE error on MC#3Channel#0_DIMM#0 (channel:0 slot:0
page:0x0 offset:0x0 grain:8 syndrome:0x0 - node:0 mc:3)

Cc: Yulong Wang <[email protected]>
Cc: Dongyan Qian <[email protected]>
Cc: Chao Li <[email protected]>
Signed-off-by: Wang Jinwei <[email protected]>
Signed-off-by: Qunqin Zhao <[email protected]>
---
 drivers/edac/loongson_edac.c | 38 +++++++++++++++++++++++++++++++-----
 1 file changed, 33 insertions(+), 5 deletions(-)

diff --git a/drivers/edac/loongson_edac.c b/drivers/edac/loongson_edac.c
index 38745800ed..f2452e98f5 100644
--- a/drivers/edac/loongson_edac.c
+++ b/drivers/edac/loongson_edac.c
@@ -9,6 +9,7 @@
 #include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include "edac_module.h"
 
 #define ECC_CS_COUNT_REG	0x18
@@ -23,6 +24,8 @@ struct loongson_edac_pvt {
 	 * register state.
 	 */
 	int last_ce_count;
+	int mc_per_node;
+	bool mc_idx_valid;
 };
 
 static int read_ecc(struct mem_ctl_info *mci)
@@ -44,7 +47,8 @@ static int read_ecc(struct mem_ctl_info *mci)
 static void edac_check(struct mem_ctl_info *mci)
 {
 	struct loongson_edac_pvt *pvt = mci->pvt_info;
-	int new, add;
+	char other_detail[64];
+	int new, add, node, mc;
 
 	new = read_ecc(mci);
 	add = new - pvt->last_ce_count;
@@ -52,8 +56,17 @@ static void edac_check(struct mem_ctl_info *mci)
 	if (add <= 0)
 		return;
 
+	if (pvt->mc_idx_valid) {
+		node = mci->mc_idx / pvt->mc_per_node;
+		mc = mci->mc_idx % pvt->mc_per_node;
+		snprintf(other_detail, sizeof(other_detail),
+				"node:%d mc:%d", node, mc);
+	} else {
+		other_detail[0] = '\0';
+	}
+
 	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add,
-			     0, 0, 0, 0, 0, -1, "error", "");
+			     0, 0, 0, 0, 0, -1, "error", other_detail);
 }
 
 static void dimm_config_init(struct mem_ctl_info *mci)
@@ -72,20 +85,26 @@ static void dimm_config_init(struct mem_ctl_info *mci)
 	dimm->grain = 8;
 }
 
-static void pvt_init(struct mem_ctl_info *mci, void __iomem *vbase)
+static void pvt_init(struct mem_ctl_info *mci, void __iomem *vbase,
+		bool mc_idx_valid, int mc_per_node)
 {
 	struct loongson_edac_pvt *pvt = mci->pvt_info;
 
 	pvt->ecc_base = vbase;
 	pvt->last_ce_count = read_ecc(mci);
+	pvt->mc_idx_valid = mc_idx_valid;
+	pvt->mc_per_node = mc_per_node;
 }
 
 static int edac_probe(struct platform_device *pdev)
 {
 	struct edac_mc_layer layers[2];
 	struct mem_ctl_info *mci;
+	struct device *dev = &pdev->dev;
 	void __iomem *vbase;
+	u32 mc_per_node;
 	int ret;
+	bool mc_idx_valid;
 
 	vbase = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(vbase))
@@ -102,7 +121,6 @@ static int edac_probe(struct platform_device *pdev)
 	if (mci == NULL)
 		return -ENOMEM;
 
-	mci->mc_idx = edac_device_alloc_index();
 	mci->mtype_cap = MEM_FLAG_RDDR4;
 	mci->edac_ctl_cap = EDAC_FLAG_NONE;
 	mci->edac_cap = EDAC_FLAG_NONE;
@@ -114,7 +132,17 @@ static int edac_probe(struct platform_device *pdev)
 	mci->error_desc.grain = 8;
 	mci->edac_check = edac_check;
 
-	pvt_init(mci, vbase);
+	if (device_property_read_u32(dev, "mc-idx", &mci->mc_idx)) {
+		mci->mc_idx = edac_device_alloc_index();
+		mc_idx_valid = false;
+	} else {
+		mc_idx_valid = true;
+	}
+
+	if (device_property_read_u32(dev, "mc-per-node", &mc_per_node))
+		mc_per_node = 4;
+
+	pvt_init(mci, vbase, mc_idx_valid, mc_per_node);
 	dimm_config_init(mci);
 
 	ret = edac_mc_add_mc(mci);

base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
-- 
2.47.2