[PATCH v3 3/3] EDAC/loongson: Add CS4-7 timeout filter for cross-node access
From: Qunqin Zhao
Date: Wed Aug 12 2026 - 02:58:16 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 the valid-cs-bits firmware property is set and
the high bits are non-zero, it indicates a timeout or random value,
so skip the report and return the previous count.
To avoid undefined behavior on 8-CS systems where valid_cs_bits=64,
guard the shift with a bounds check (valid_cs_bits < 64).
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 | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/drivers/edac/loongson_edac.c b/drivers/edac/loongson_edac.c
index a3776a8501..eb36b708e6 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 mcs_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 (pvt->valid_cs_bits < 64 && (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;
}
@@ -111,14 +122,15 @@ 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 mcs_per_node)
+ bool mc_idx_valid, int mcs_per_node, int valid_cs_bits)
{
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->mcs_per_node = mcs_per_node;
+ pvt->valid_cs_bits = valid_cs_bits;
+ pvt->last_ce_count = read_ecc(mci);
}
static int edac_probe(struct platform_device *pdev)
@@ -127,7 +139,7 @@ static int edac_probe(struct platform_device *pdev)
struct mem_ctl_info *mci;
struct device *dev = &pdev->dev;
void __iomem *vbase;
- u32 mcs_per_node;
+ u32 mcs_per_node, valid_cs_bits;
int ret;
bool mc_idx_valid = true;
@@ -165,7 +177,10 @@ static int edac_probe(struct platform_device *pdev)
if (device_property_read_u32(dev, "mcs-per-node", &mcs_per_node) || mcs_per_node == 0)
mcs_per_node = 4;
- pvt_init(mci, vbase, mc_idx_valid, mcs_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, mcs_per_node, valid_cs_bits);
dimm_config_init(mci);
ret = edac_mc_add_mc(mci);
--
2.47.3