From 0f6b1aa6bece015a4d974f2cf9f655b7cfb19106 Mon Sep 17 00:00:00 2001 From: Juxin Gao Date: Tue, 12 Aug 2025 15:27:55 +0800 Subject: [PATCH] EDAC: Add EDAC driver for loongson memory controller ANBZ: #23545 commit 558aff7a63f67dc4723a4deed419a2dfd0fb14f2 upstream. Add ECC support for Loongson SoC DDR controller. This driver reports single bit errors (CE) only. Only ACPI firmware is supported. Signed-off-by: Qunqin Zhao Link: https://lore.kernel.org/r/20241219124846.1876-1-zhaoqunqin@loongson.cn Signed-off-by: Juxin Gao --- MAINTAINERS | 6 + .../L0-MANDATORY/loongarch/CONFIG_EDAC | 2 +- .../loongarch/CONFIG_EDAC_LOONGSON | 1 + arch/loongarch/Kconfig | 1 + drivers/edac/Kconfig | 9 ++ drivers/edac/Makefile | 1 + drivers/edac/loongson_edac.c | 152 ++++++++++++++++++ 7 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 anolis/configs/L0-MANDATORY/loongarch/CONFIG_EDAC_LOONGSON create mode 100644 drivers/edac/loongson_edac.c diff --git a/MAINTAINERS b/MAINTAINERS index 8e424a48d642..5065f48fc7fe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12450,6 +12450,12 @@ S: Maintained F: Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml F: drivers/thermal/loongson2_thermal.c +LOONGSON EDAC DRIVER +M: Zhao Qunqin +L: linux-edac@vger.kernel.org +S: Maintained +F: drivers/edac/loongson_edac.c + LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI) M: Sathya Prakash M: Sreekanth Reddy diff --git a/anolis/configs/L0-MANDATORY/loongarch/CONFIG_EDAC b/anolis/configs/L0-MANDATORY/loongarch/CONFIG_EDAC index 18521580af45..dcb32adb912e 100644 --- a/anolis/configs/L0-MANDATORY/loongarch/CONFIG_EDAC +++ b/anolis/configs/L0-MANDATORY/loongarch/CONFIG_EDAC @@ -1 +1 @@ -# CONFIG_EDAC is not set +CONFIG_EDAC=y diff --git a/anolis/configs/L0-MANDATORY/loongarch/CONFIG_EDAC_LOONGSON b/anolis/configs/L0-MANDATORY/loongarch/CONFIG_EDAC_LOONGSON new file mode 100644 index 000000000000..7c400d6a2bbb --- /dev/null +++ b/anolis/configs/L0-MANDATORY/loongarch/CONFIG_EDAC_LOONGSON @@ -0,0 +1 @@ +CONFIG_EDAC_LOONGSON=y diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 494d0deef45f..2a76a6c53089 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -68,6 +68,7 @@ config LOONGARCH select BUILDTIME_TABLE_SORT select COMMON_CLK select CPU_PM + select EDAC_SUPPORT select EFI select GENERIC_CLOCKEVENTS select GENERIC_CMOS_UPDATE diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 87feeb24b3ff..2094459a64e9 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -562,4 +562,13 @@ config EDAC_NPCM error detection (in-line ECC in which a section 1/8th of the memory device used to store data is used for ECC storage). +config EDAC_LOONGSON + tristate "Loongson Memory Controller" + depends on LOONGARCH && ACPI + help + Support for error detection and correction on the Loongson + family memory controller. This driver reports single bit + errors (CE) only. Loongson-3A5000/3C5000/3D5000/3A6000/3C6000 + are compatible. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 446364264e2b..699b818ac7cb 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -88,3 +88,4 @@ obj-$(CONFIG_EDAC_BLUEFIELD) += bluefield_edac.o obj-$(CONFIG_EDAC_DMC520) += dmc520_edac.o obj-$(CONFIG_EDAC_NPCM) += npcm_edac.o obj-$(CONFIG_EDAC_ZYNQMP) += zynqmp_edac.o +obj-$(CONFIG_EDAC_LOONGSON) += loongson_edac.o diff --git a/drivers/edac/loongson_edac.c b/drivers/edac/loongson_edac.c new file mode 100644 index 000000000000..46d6a3ab2ce2 --- /dev/null +++ b/drivers/edac/loongson_edac.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Loongson Technology Corporation Limited. + */ + +#include +#include +#include +#include +#include +#include +#include "edac_module.h" + +#define ECC_CS_COUNT_REG 0x18 + +struct loongson_edac_pvt { + void __iomem *ecc_base; + int last_ce_count; +}; + +static int read_ecc(struct mem_ctl_info *mci) +{ + struct loongson_edac_pvt *pvt = mci->pvt_info; + u64 ecc; + int cs; + + ecc = readq(pvt->ecc_base + ECC_CS_COUNT_REG); + /* cs0 -- cs3 */ + cs = ecc & 0xff; + cs += (ecc >> 8) & 0xff; + cs += (ecc >> 16) & 0xff; + cs += (ecc >> 24) & 0xff; + + return cs; +} + +static void edac_check(struct mem_ctl_info *mci) +{ + struct loongson_edac_pvt *pvt = mci->pvt_info; + int new, add; + + new = read_ecc(mci); + add = new - pvt->last_ce_count; + pvt->last_ce_count = new; + if (add <= 0) + return; + + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add, + 0, 0, 0, 0, 0, -1, "error", ""); +} + +static void dimm_config_init(struct mem_ctl_info *mci) +{ + struct dimm_info *dimm; + u32 size, npages; + + /* size not used */ + size = -1; + npages = MiB_TO_PAGES(size); + + dimm = edac_get_dimm(mci, 0, 0, 0); + dimm->nr_pages = npages; + snprintf(dimm->label, sizeof(dimm->label), + "MC#%uChannel#%u_DIMM#%u", mci->mc_idx, 0, 0); + dimm->grain = 8; +} + +static void pvt_init(struct mem_ctl_info *mci, void __iomem *vbase) +{ + struct loongson_edac_pvt *pvt = mci->pvt_info; + + pvt->ecc_base = vbase; + pvt->last_ce_count = read_ecc(mci); +} + +static int edac_probe(struct platform_device *pdev) +{ + struct edac_mc_layer layers[2]; + struct mem_ctl_info *mci; + void __iomem *vbase; + int ret; + + vbase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(vbase)) + return PTR_ERR(vbase); + + layers[0].type = EDAC_MC_LAYER_CHANNEL; + layers[0].size = 1; + layers[0].is_virt_csrow = false; + layers[1].type = EDAC_MC_LAYER_SLOT; + layers[1].size = 1; + layers[1].is_virt_csrow = true; + mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, + sizeof(struct loongson_edac_pvt)); + 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; + mci->mod_name = "loongson_edac.c"; + mci->ctl_name = "loongson_edac_ctl"; + mci->dev_name = "loongson_edac_dev"; + mci->ctl_page_to_phys = NULL; + mci->pdev = &pdev->dev; + mci->error_desc.grain = 8; + mci->edac_check = edac_check; + + pvt_init(mci, vbase); + dimm_config_init(mci); + + ret = edac_mc_add_mc(mci); + if (ret) { + edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); + edac_mc_free(mci); + return ret; + } + edac_op_state = EDAC_OPSTATE_POLL; + + return 0; +} + +static int edac_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = edac_mc_del_mc(&pdev->dev); + + if (mci) + edac_mc_free(mci); + + return 0; +} + +static const struct acpi_device_id loongson_edac_acpi_match[] = { + {"LOON0010", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, loongson_edac_acpi_match); + +static struct platform_driver loongson_edac_driver = { + .probe = edac_probe, + .remove = edac_remove, + .driver = { + .name = "loongson-mc-edac", + .acpi_match_table = loongson_edac_acpi_match, + }, +}; +module_platform_driver(loongson_edac_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhao Qunqin "); +MODULE_DESCRIPTION("EDAC driver for loongson memory controller"); -- Gitee