[PATCH 3/3] EDAC/loongson: Add CS4-7 timeout filter for cross-node access

From: Qunqin Zhao

Date: Thu Jul 30 2026 - 02:54:06 EST


On Loongson 3C6000 multi-node systems, when the EDAC driver running
on one node accesses the memory controller registers of another node,
the read operation may timeout and return random values. This causes
false CE error reports because the random values in CS0-3 (low 32 bits)
are misinterpreted as valid ECC error counts.

For cross-node accesses, check CS4-7 (high 32 bits) of the ECC
count register. If non-zero, it indicates a timeout/random value,
so skip the report and return the previous count.

Signed-off-by: Wusheng <wusheng@xxxxxxxxxxx>
Signed-off-by: Wang Jinwei <wangjinwei@xxxxxxxxxxx>
Signed-off-by: Qunqin Zhao <zhaoqunqin@xxxxxxxxxxx>
---
drivers/edac/loongson_edac.c | 23 +++++++++++++++++++----
1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/drivers/edac/loongson_edac.c b/drivers/edac/loongson_edac.c
index b458f81dfe..33ca2670d2 100644
--- a/drivers/edac/loongson_edac.c
+++ b/drivers/edac/loongson_edac.c
@@ -27,6 +27,7 @@ struct loongson_edac_pvt {
*/
int last_ce_count;
int mc_per_node;
+ int valid_cs_bits;
bool mc_idx_valid;
};

@@ -37,11 +38,21 @@ static int read_ecc(struct mem_ctl_info *mci)
int cs;

ecc = readq(pvt->ecc_base + ECC_CS_COUNT_REG);
- /* cs0 -- cs3 */
+ /* Discard the read value if any invalid bit is set to 1 */
+ if (ecc >> pvt->valid_cs_bits) {
+ edac_mc_printk(mci, KERN_DEBUG, "ECC read invalid, skip: 0x%llx\n", ecc);
+ return pvt->last_ce_count;
+ }
+
+ /* cs0 -- cs7 */
cs = ecc & 0xff;
cs += (ecc >> 8) & 0xff;
cs += (ecc >> 16) & 0xff;
cs += (ecc >> 24) & 0xff;
+ cs += (ecc >> 32) & 0xff;
+ cs += (ecc >> 40) & 0xff;
+ cs += (ecc >> 48) & 0xff;
+ cs += (ecc >> 56) & 0xff;

return cs;
}
@@ -114,7 +125,7 @@ static void dimm_config_init(struct mem_ctl_info *mci)
}

static void pvt_init(struct mem_ctl_info *mci, void __iomem *vbase,
- bool mc_idx_valid, int mc_per_node)
+ bool mc_idx_valid, int mc_per_node, int valid_cs_bits)
{
struct loongson_edac_pvt *pvt = mci->pvt_info;

@@ -122,6 +133,7 @@ static void pvt_init(struct mem_ctl_info *mci, void __iomem *vbase,
pvt->last_ce_count = read_ecc(mci);
pvt->mc_idx_valid = mc_idx_valid;
pvt->mc_per_node = mc_per_node;
+ pvt->valid_cs_bits = valid_cs_bits;
}

static int edac_probe(struct platform_device *pdev)
@@ -130,7 +142,7 @@ static int edac_probe(struct platform_device *pdev)
struct mem_ctl_info *mci;
struct device *dev = &pdev->dev;
void __iomem *vbase;
- u32 mc_per_node;
+ u32 mc_per_node, valid_cs_bits;
int ret;
bool mc_idx_valid;

@@ -170,7 +182,10 @@ static int edac_probe(struct platform_device *pdev)
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);
+ if (device_property_read_u32(dev, "valid_cs_bits", &valid_cs_bits))
+ valid_cs_bits = 32;
+
+ pvt_init(mci, vbase, mc_idx_valid, mc_per_node, valid_cs_bits);
dimm_config_init(mci);

ret = edac_mc_add_mc(mci);
--
2.47.2