From nobody Tue Sep 29 06:08:02 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9902732B10A; Wed, 12 Aug 2026 05:53:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786514019; cv=none; b=jRNht4o5nHM87bqjaY9mO/curZ7qDCEzx1IOz9BePhOUnOwTW/kq7GVyEVPqU2fb2tkZzqvqwGP+8ms8U4cCZKMGxMX3KYxuPk4kEqXdJs/pxVvR94iZVTdQtPDFBYA1U5jZ4XVkPZvQtHVHYjvyf5DmFYMxtrQODFtIZ4CJ538= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786514019; c=relaxed/simple; bh=z9SqsVTupF6vkjCZpgGoS377DCcrh5k1AI9S/5F9788=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=QMxG2xogSp7eNyCSiFDMIVej9J//LQWeTKlyV0RlU9DeEVPtSf5CSHXc3crE/ekp8hxNHSwfCPcQtrtt9XAolmznDmlb63QLelAxbSHmGFcAzHrE3dpjC4ssxlbjCMlysltNuOuCAlvSyyr54z6BFuEXi4c0ocGS+5lodENfUzI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 12 Aug 2026 13:48:26 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 12 Aug 2026 13:48:26 +0800 From: Ryan Chen Date: Wed, 12 Aug 2026 13:48:25 +0800 Subject: [PATCH 1/7] dt-bindings: edac: aspeed: Add AST2700 SDRAM EDAC Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20260812-edac-v1-1-03992edea297@aspeedtech.com> References: <20260812-edac-v1-0-03992edea297@aspeedtech.com> In-Reply-To: <20260812-edac-v1-0-03992edea297@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck , "Sebastian Andrzej Siewior" , Clark Williams , Steven Rostedt CC: , , , , , Borislav Petkov , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786513706; l=1387; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=z9SqsVTupF6vkjCZpgGoS377DCcrh5k1AI9S/5F9788=; b=G01N5R7+EbAExh/xiJj8baWQ66EJngCPmhBj+mvEAm6w44/buhkUU6kIZAAKLyZ5YhYuXPpae fJ/1iQBhWCkD9YnfO+l5NADP/X2H36c3D2fzXW6l10l/F87q0aea1xf X-Developer-Key: i=ryan_chen@aspeedtech.com; a=ed25519; pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc= Add the "aspeed,ast2700-sdram-edac" compatible and note DDR5 support. Signed-off-by: Ryan Chen Acked-by: Krzysztof Kozlowski --- .../devicetree/bindings/edac/aspeed,ast2400-sdram-edac.yaml | 6 ++= ++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/edac/aspeed,ast2400-sdram-ed= ac.yaml b/Documentation/devicetree/bindings/edac/aspeed,ast2400-sdram-edac.= yaml index 09735826d707..685b6815f293 100644 --- a/Documentation/devicetree/bindings/edac/aspeed,ast2400-sdram-edac.yaml +++ b/Documentation/devicetree/bindings/edac/aspeed,ast2400-sdram-edac.yaml @@ -10,8 +10,9 @@ maintainers: - Stefan Schaeckeler =20 description: > - The Aspeed BMC SoC supports DDR3 and DDR4 memory with and without ECC (e= rror - correction check). + The Aspeed BMC SoCs support DDR memory with and without ECC (error + correction check): DDR3 and DDR4 on the AST2400, AST2500 and AST2600, + and DDR4 and DDR5 on the AST2700. =20 The memory controller supports SECDED (single bit error correction, doub= le bit error detection) and single bit error auto scrubbing by reserving 8 bits= for @@ -25,6 +26,7 @@ properties: - aspeed,ast2400-sdram-edac - aspeed,ast2500-sdram-edac - aspeed,ast2600-sdram-edac + - aspeed,ast2700-sdram-edac =20 reg: maxItems: 1 --=20 2.34.1 From nobody Tue Sep 29 06:08:02 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 542CC329C6D; Wed, 12 Aug 2026 05:53:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786514020; cv=none; b=WEQBC49jJlGtkmxkMMys0IUaNmbrjU8bCZKw60zWf80iN3I5h6hlFo3y5x3H5CpMKQq+VBhUbJcrhvglIdoH+CJkYGjNW0BGwdugItqWxStI+UHmeZukFDF8PWHVFV9XC4xf2ztfZC0Or2ftm4iIXU+R9BZyeg38nzzKZm3F5xw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786514020; c=relaxed/simple; bh=ghwuXIUkCe0r9jImgksJuGTgCehdPwUB1sU47hl7ksA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=kwQRhXml3rGDxrxDjsOINp3AFzILKab7nPCHOGvpgdDG4l9m+XhkAuDYPpAiHSwtwgdLnpYO1XWKk8aKFHTSvQKiWMNPkQHsgiOH1pJTh39V8x4B83pRFx91d/dxuo8th22mqQ/Gn0wm2aC+CkAXMHvDTYhp+67plOuDytsHk4E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 12 Aug 2026 13:48:26 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 12 Aug 2026 13:48:26 +0800 From: Ryan Chen Date: Wed, 12 Aug 2026 13:48:26 +0800 Subject: [PATCH 2/7] EDAC/aspeed: Set the DIMM grain Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20260812-edac-v1-2-03992edea297@aspeedtech.com> References: <20260812-edac-v1-0-03992edea297@aspeedtech.com> In-Reply-To: <20260812-edac-v1-0-03992edea297@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck , "Sebastian Andrzej Siewior" , Clark Williams , Steven Rostedt CC: , , , , , Borislav Petkov , , Ryan Chen , X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786513706; l=1310; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=ghwuXIUkCe0r9jImgksJuGTgCehdPwUB1sU47hl7ksA=; b=ta3t3OOxlRiWkwMAqIK/N+WRybGbaOUJqQg0PEnxU+ucrYPyhNHsveCEa50qfJPjIFuCdvrvB JKdn2wbwYSlD5+7XNJ8o56qPVH+USWyAHMRG3mZ0zzsDkqhJppHYiDM X-Developer-Key: i=ryan_chen@aspeedtech.com; a=ed25519; pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc= The driver never sets dimm->grain, leaving it zero. Since commit 3724ace582d9 ("EDAC/mc: Fix grain_bits calculation") edac_raw_mc_handle_error() runs WARN_ON_ONCE(!e->grain) and forces the grain to 1, so the first ECC error reported on any Aspeed BMC SoC emits a warning splat, e.g. on the AST2600: WARNING: CPU: 0 PID: 0 at drivers/edac/edac_mc.c:924 edac_raw_mc_handle_e= rror+0x4b4/0x604 ... edac_raw_mc_handle_error from edac_mc_handle_error+0x364/0x4a8 edac_mc_handle_error from count_rec+0xdc/0x124 count_rec from mcr_isr+0x110/0x1e8 Fixes: 9b7e6242ee4e ("EDAC, aspeed: Add an Aspeed AST2500 EDAC driver") Cc: stable@vger.kernel.org Signed-off-by: Ryan Chen --- drivers/edac/aspeed_edac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index 6e069b255595..83d60414f89a 100644 --- a/drivers/edac/aspeed_edac.c +++ b/drivers/edac/aspeed_edac.c @@ -266,6 +266,7 @@ static int init_csrows(struct mem_ctl_info *mci) dimm->mtype =3D dram_type; dimm->edac_mode =3D EDAC_SECDED; dimm->nr_pages =3D nr_pages / csrow->nr_channels; + dimm->grain =3D 16; =20 dev_dbg(mci->pdev, "initialized dimm with first_page=3D0x%lx and nr_pages= =3D0x%x\n", csrow->first_page, nr_pages); --=20 2.34.1 From nobody Tue Sep 29 06:08:02 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 307B43191D0; Wed, 12 Aug 2026 05:53:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786514025; cv=none; b=TvEAG10c5KOV3280/X7QD7HFOiDXsAZC8Hqrd63JQYt6ntan9kt88piwQkBO6OxzMm6C62xYrHotsDscmO7tGo63uHtYi1cSYucl41Wvo8SoQxprepSqpLTIONef6gus8IQJ0/r2cLGNVO6ZhZjQp/lGOTupYygPCWzOON7ElZg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786514025; c=relaxed/simple; bh=bvQkmqZviZh6mQkAspugiuA92hDaOOpScX0KpDjjs8U=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=Xlffyg4PAb7VlLiDdNsax2LK7AdJXA6zCvurKP4NP55JYr+l+z51/vUl0+VNMg/IHjnpxxy+Yh/BLlZzw+lF6BXeO4F/6EOcgGM490E9wloOs04KRFjeiDpFKfq4F10e30k1RQveGR2Qm33p9V3NeJnv8UGzXBEEn5y/Z5ENHeo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 12 Aug 2026 13:48:26 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 12 Aug 2026 13:48:26 +0800 From: Ryan Chen Date: Wed, 12 Aug 2026 13:48:27 +0800 Subject: [PATCH 3/7] EDAC/aspeed: Clean up whitespace and include ordering Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20260812-edac-v1-3-03992edea297@aspeedtech.com> References: <20260812-edac-v1-0-03992edea297@aspeedtech.com> In-Reply-To: <20260812-edac-v1-0-03992edea297@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck , "Sebastian Andrzej Siewior" , Clark Williams , Steven Rostedt CC: , , , , , Borislav Petkov , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786513706; l=4626; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=bvQkmqZviZh6mQkAspugiuA92hDaOOpScX0KpDjjs8U=; b=mFBd1S06YShfr1QL0DFEXqfb9cHFx4O5l4gCcwyncY/PEMP9RIu0Azwysb/Nk+6MgQ9qf0+XM Pxrlk9wkEYqC0oBDidIuc+je+QD6/awBeWwV1u3RW8JaY38kcwB2WoF X-Developer-Key: i=ryan_chen@aspeedtech.com; a=ed25519; pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc= The driver separates functions and definition groups with two blank lines where the kernel style uses one, its headers are not sorted, and it includes linux/stop_machine.h without using it. Collapse the double blank lines, drop the unused include and sort the rest alphabetically so the following changes start from a consistent style. Signed-off-by: Ryan Chen --- drivers/edac/aspeed_edac.c | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index 83d60414f89a..71535e0b6bad 100644 --- a/drivers/edac/aspeed_edac.c +++ b/drivers/edac/aspeed_edac.c @@ -4,20 +4,17 @@ */ =20 #include -#include #include #include -#include -#include #include +#include #include +#include #include #include "edac_module.h" =20 - #define DRV_NAME "aspeed-edac" =20 - #define ASPEED_MCR_PROT 0x00 /* protection key register */ #define ASPEED_MCR_CONF 0x04 /* configuration register */ #define ASPEED_MCR_INTR_CTRL 0x50 /* interrupt control/status register */ @@ -25,19 +22,16 @@ #define ASPEED_MCR_ADDR_REC 0x5c /* address of last recoverable error */ #define ASPEED_MCR_LAST ASPEED_MCR_ADDR_REC =20 - -#define ASPEED_MCR_PROT_PASSWD 0xfc600309 -#define ASPEED_MCR_CONF_DRAM_TYPE BIT(4) -#define ASPEED_MCR_CONF_ECC BIT(7) -#define ASPEED_MCR_INTR_CTRL_CLEAR BIT(31) -#define ASPEED_MCR_INTR_CTRL_CNT_REC GENMASK(23, 16) -#define ASPEED_MCR_INTR_CTRL_CNT_UNREC GENMASK(15, 12) -#define ASPEED_MCR_INTR_CTRL_ENABLE (BIT(0) | BIT(1)) - +#define ASPEED_MCR_PROT_PASSWD 0xfc600309 +#define ASPEED_MCR_CONF_DRAM_TYPE BIT(4) +#define ASPEED_MCR_CONF_ECC BIT(7) +#define ASPEED_MCR_INTR_CTRL_CLEAR BIT(31) +#define ASPEED_MCR_INTR_CTRL_CNT_REC GENMASK(23, 16) +#define ASPEED_MCR_INTR_CTRL_CNT_UNREC GENMASK(15, 12) +#define ASPEED_MCR_INTR_CTRL_ENABLE (BIT(0) | BIT(1)) =20 static struct regmap *aspeed_regmap; =20 - static int regmap_reg_write(void *context, unsigned int reg, unsigned int = val) { void __iomem *regs =3D (void __iomem *)context; @@ -53,7 +47,6 @@ static int regmap_reg_write(void *context, unsigned int r= eg, unsigned int val) return 0; } =20 - static int regmap_reg_read(void *context, unsigned int reg, unsigned int *= val) { void __iomem *regs =3D (void __iomem *)context; @@ -76,7 +69,6 @@ static bool regmap_is_volatile(struct device *dev, unsign= ed int reg) } } =20 - static const struct regmap_config aspeed_regmap_config =3D { .reg_bits =3D 32, .val_bits =3D 32, @@ -88,7 +80,6 @@ static const struct regmap_config aspeed_regmap_config = =3D { .fast_io =3D true, }; =20 - static void count_rec(struct mem_ctl_info *mci, u8 rec_cnt, u32 rec_addr) { struct csrow_info *csrow =3D mci->csrows[0]; @@ -120,7 +111,6 @@ static void count_rec(struct mem_ctl_info *mci, u8 rec_= cnt, u32 rec_addr) 0, 0, -1, "", ""); } =20 - static void count_un_rec(struct mem_ctl_info *mci, u8 un_rec_cnt, u32 un_rec_addr) { @@ -153,7 +143,6 @@ static void count_un_rec(struct mem_ctl_info *mci, u8 u= n_rec_cnt, } } =20 - static irqreturn_t mcr_isr(int irq, void *arg) { struct mem_ctl_info *mci =3D arg; @@ -200,7 +189,6 @@ static irqreturn_t mcr_isr(int irq, void *arg) return IRQ_HANDLED; } =20 - static int config_irq(void *ctx, struct platform_device *pdev) { int irq; @@ -225,7 +213,6 @@ static int config_irq(void *ctx, struct platform_device= *pdev) return 0; } =20 - static int init_csrows(struct mem_ctl_info *mci) { struct csrow_info *csrow =3D mci->csrows[0]; @@ -274,7 +261,6 @@ static int init_csrows(struct mem_ctl_info *mci) return 0; } =20 - static int aspeed_probe(struct platform_device *pdev) { struct device *dev =3D &pdev->dev; @@ -355,7 +341,6 @@ static int aspeed_probe(struct platform_device *pdev) return rc; } =20 - static void aspeed_remove(struct platform_device *pdev) { struct mem_ctl_info *mci; @@ -370,7 +355,6 @@ static void aspeed_remove(struct platform_device *pdev) edac_mc_free(mci); } =20 - static const struct of_device_id aspeed_of_match[] =3D { { .compatible =3D "aspeed,ast2400-sdram-edac" }, { .compatible =3D "aspeed,ast2500-sdram-edac" }, --=20 2.34.1 From nobody Tue Sep 29 06:08:02 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B98C6326927; Wed, 12 Aug 2026 05:49:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786513787; cv=none; b=b96g7819eY2NyeyyDSvYKtlBF+h0FoX++slqU9DJadJHY84UJtTLJerByjHAt2DPmuzjd9AdBo0/KQJRXB5nN/bYs0HhxW3hqUMVHf2lfz7Zows019LM6Fx/J10uoez+xYragwERczvOSBRst2OfED/zObhRlD365LCsJu0wIhw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786513787; c=relaxed/simple; bh=yRn6gRtMLiLqMDInORTTYh92hg9HUS6B25U23zwFj3o=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=WLxGfHiUsN0Ib00pOVQDqPjk3IWRuH1OxjDWbD9uJh88yZc/H6dDxq44cO3HthZBQgbZ13gpCuRh0RhBCBveacQ6heXrFgs5eSU0UP5kzh+kRBb1YdbHO7uRIiff2HzeC2OyHPwpHumCQfEExHHd5O8SfYlnzzSQ8tMVQCJhX6Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 12 Aug 2026 13:48:26 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 12 Aug 2026 13:48:26 +0800 From: Ryan Chen Date: Wed, 12 Aug 2026 13:48:28 +0800 Subject: [PATCH 4/7] EDAC/aspeed: Free the mem_ctl_info unconditionally on remove Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20260812-edac-v1-4-03992edea297@aspeedtech.com> References: <20260812-edac-v1-0-03992edea297@aspeedtech.com> In-Reply-To: <20260812-edac-v1-0-03992edea297@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck , "Sebastian Andrzej Siewior" , Clark Williams , Steven Rostedt CC: , , , , , Borislav Petkov , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786513706; l=1411; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=yRn6gRtMLiLqMDInORTTYh92hg9HUS6B25U23zwFj3o=; b=ssnqIGmszPrfSMHYrqQ49TZhae/h/sTZ3GwA+RPn09RCCbhAP8Z4KVb+OO82ZhXy0cfSsUYep CUzsPZknQkDBRQ3tiym0RwK1UG3pJ08zptxQkKqRDJFr1s955CD26o3 X-Developer-Key: i=ryan_chen@aspeedtech.com; a=ed25519; pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc= aspeed_remove() is a driver .remove callback, so it only runs for a device that has already probed successfully and registered its mem_ctl_info. In that case edac_mc_del_mc() always returns the same, valid pointer, and the NULL check on its return value can never be false. Fetch the mem_ctl_info from the platform device's driver data instead and free it unconditionally, dropping the redundant check. This also decouples the teardown from the return value of edac_mc_del_mc(). Signed-off-by: Ryan Chen --- drivers/edac/aspeed_edac.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index 71535e0b6bad..8bfeb21d3204 100644 --- a/drivers/edac/aspeed_edac.c +++ b/drivers/edac/aspeed_edac.c @@ -343,16 +343,15 @@ static int aspeed_probe(struct platform_device *pdev) =20 static void aspeed_remove(struct platform_device *pdev) { - struct mem_ctl_info *mci; + struct mem_ctl_info *mci =3D platform_get_drvdata(pdev); =20 /* disable interrupts */ regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, ASPEED_MCR_INTR_CTRL_ENABLE, 0); =20 /* free resources */ - mci =3D edac_mc_del_mc(&pdev->dev); - if (mci) - edac_mc_free(mci); + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); } =20 static const struct of_device_id aspeed_of_match[] =3D { --=20 2.34.1 From nobody Tue Sep 29 06:08:02 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B071E482D4; Wed, 12 Aug 2026 05:49:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786513780; cv=none; b=mjYTFKLmsOfkwQyZ8tfj6yl2jzVTAKwi4hhesfVpsN/yCSQIcvmdsYi3CAr20JIkzcMXJInsuOg5M6xD1jDsRF1lBWliAHtQwl8NBLRwsia4o4yFsjvUnL2Eqlw5Q2FOROCTpdVhgIaSdp8nd4ILOjQkFNLCSOxUFV4UYUtV8KI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786513780; c=relaxed/simple; bh=Me8mi0NrSdfydNawCYFOI5PqxoSJA8FxxW2gT79Fc4E=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=gZjK+6zgLmE0xf86KBQQWohViYVKDtORVbV1Jxnlgit2zyLWcmTZa4DuWr65eN0hnTDlmGFnbgrYcVMF0lrXDSqRldD2mF8Mo9Kuu9J03E0GHiudR2wE+Ge46ihwiLfJPXYpa/Wn6SBRdb4FfqKr+QZGBPSc8gcRq5T+VUk8lCU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 12 Aug 2026 13:48:26 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 12 Aug 2026 13:48:26 +0800 From: Ryan Chen Date: Wed, 12 Aug 2026 13:48:29 +0800 Subject: [PATCH 5/7] EDAC/aspeed: Replace regmap with direct register access Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20260812-edac-v1-5-03992edea297@aspeedtech.com> References: <20260812-edac-v1-0-03992edea297@aspeedtech.com> In-Reply-To: <20260812-edac-v1-0-03992edea297@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck , "Sebastian Andrzej Siewior" , Clark Williams , Steven Rostedt CC: , , , , , Borislav Petkov , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786513706; l=8681; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=Me8mi0NrSdfydNawCYFOI5PqxoSJA8FxxW2gT79Fc4E=; b=2PU7W0YGm6//PwLZbta9GqrvS/IUAj0KX1k+Ge63pDjVRmctMKCfK/TyoAstFxKlihE7fwtTf FInx/eaBkYGAjxMvKUgrv16OgH5utanqePr0bUFwK99Xu6C5n818RZD X-Developer-Key: i=ryan_chen@aspeedtech.com; a=ed25519; pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc= The driver instantiates its own regmap purely as an MMIO wrapper: it has no register cache, uses custom reg_read/reg_write callbacks, and is not shared as a syscon with other drivers. So it brings nothing here beyond the spinlock that regmap takes around each access when fast_io is set. Drop the regmap and access the registers directly with readl()/writel() under an explicit raw spinlock, held across the whole read-modify-write so the controller is unlocked once around the grouped writes rather than on every register write. The lock is a raw_spinlock_t because the ECC interrupt handler runs in hardirq context, where under PREEMPT_RT a sleeping spinlock could not be acquired. Annotate the register base with __guarded_by() so that, under CONFIG_WARN_CONTEXT_ANALYSIS, the compiler checks at build time that every hardware register access is performed while holding the lock. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Ryan Chen --- drivers/edac/aspeed_edac.c | 132 +++++++++++++++++------------------------= ---- 1 file changed, 48 insertions(+), 84 deletions(-) diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index 8bfeb21d3204..7bd552ee9a61 100644 --- a/drivers/edac/aspeed_edac.c +++ b/drivers/edac/aspeed_edac.c @@ -3,6 +3,7 @@ * Copyright 2018, 2019 Cisco Systems */ =20 +#include #include #include #include @@ -10,7 +11,7 @@ #include #include #include -#include +#include #include "edac_module.h" =20 #define DRV_NAME "aspeed-edac" @@ -20,7 +21,6 @@ #define ASPEED_MCR_INTR_CTRL 0x50 /* interrupt control/status register */ #define ASPEED_MCR_ADDR_UNREC 0x58 /* address of first un-recoverable err= or */ #define ASPEED_MCR_ADDR_REC 0x5c /* address of last recoverable error */ -#define ASPEED_MCR_LAST ASPEED_MCR_ADDR_REC =20 #define ASPEED_MCR_PROT_PASSWD 0xfc600309 #define ASPEED_MCR_CONF_DRAM_TYPE BIT(4) @@ -30,55 +30,8 @@ #define ASPEED_MCR_INTR_CTRL_CNT_UNREC GENMASK(15, 12) #define ASPEED_MCR_INTR_CTRL_ENABLE (BIT(0) | BIT(1)) =20 -static struct regmap *aspeed_regmap; - -static int regmap_reg_write(void *context, unsigned int reg, unsigned int = val) -{ - void __iomem *regs =3D (void __iomem *)context; - - /* enable write to MCR register set */ - writel(ASPEED_MCR_PROT_PASSWD, regs + ASPEED_MCR_PROT); - - writel(val, regs + reg); - - /* disable write to MCR register set */ - writel(~ASPEED_MCR_PROT_PASSWD, regs + ASPEED_MCR_PROT); - - return 0; -} - -static int regmap_reg_read(void *context, unsigned int reg, unsigned int *= val) -{ - void __iomem *regs =3D (void __iomem *)context; - - *val =3D readl(regs + reg); - - return 0; -} - -static bool regmap_is_volatile(struct device *dev, unsigned int reg) -{ - switch (reg) { - case ASPEED_MCR_PROT: - case ASPEED_MCR_INTR_CTRL: - case ASPEED_MCR_ADDR_UNREC: - case ASPEED_MCR_ADDR_REC: - return true; - default: - return false; - } -} - -static const struct regmap_config aspeed_regmap_config =3D { - .reg_bits =3D 32, - .val_bits =3D 32, - .reg_stride =3D 4, - .max_register =3D ASPEED_MCR_LAST, - .reg_write =3D regmap_reg_write, - .reg_read =3D regmap_reg_read, - .volatile_reg =3D regmap_is_volatile, - .fast_io =3D true, -}; +static DEFINE_RAW_SPINLOCK(aspeed_lock); +static void __iomem *aspeed_regs __guarded_by(&aspeed_lock); =20 static void count_rec(struct mem_ctl_info *mci, u8 rec_cnt, u32 rec_addr) { @@ -147,12 +100,24 @@ static irqreturn_t mcr_isr(int irq, void *arg) { struct mem_ctl_info *mci =3D arg; u32 rec_addr, un_rec_addr; - u32 reg50, reg5c, reg58; - u8 rec_cnt, un_rec_cnt; - - regmap_read(aspeed_regmap, ASPEED_MCR_INTR_CTRL, ®50); - dev_dbg(mci->pdev, "received edac interrupt w/ mcr register 50: 0x%x\n", - reg50); + u8 rec_cnt, un_rec_cnt; + u32 reg50; + + scoped_guard(raw_spinlock, &aspeed_lock) { + reg50 =3D readl(aspeed_regs + ASPEED_MCR_INTR_CTRL); + dev_dbg(mci->pdev, "received edac interrupt w/ mcr register 50: 0x%x\n", + reg50); + un_rec_addr =3D readl(aspeed_regs + ASPEED_MCR_ADDR_UNREC); + rec_addr =3D readl(aspeed_regs + ASPEED_MCR_ADDR_REC); + + /* clearing the counters needs a set-then-clear of CLEAR */ + writel(ASPEED_MCR_PROT_PASSWD, aspeed_regs + ASPEED_MCR_PROT); + writel(reg50 | ASPEED_MCR_INTR_CTRL_CLEAR, + aspeed_regs + ASPEED_MCR_INTR_CTRL); + writel(reg50 & ~ASPEED_MCR_INTR_CTRL_CLEAR, + aspeed_regs + ASPEED_MCR_INTR_CTRL); + writel(~ASPEED_MCR_PROT_PASSWD, aspeed_regs + ASPEED_MCR_PROT); + } =20 /* collect data about recoverable and unrecoverable errors */ rec_cnt =3D (reg50 & ASPEED_MCR_INTR_CTRL_CNT_REC) >> 16; @@ -161,20 +126,6 @@ static irqreturn_t mcr_isr(int irq, void *arg) dev_dbg(mci->pdev, "%d recoverable interrupts and %d unrecoverable interr= upts\n", rec_cnt, un_rec_cnt); =20 - regmap_read(aspeed_regmap, ASPEED_MCR_ADDR_UNREC, ®58); - un_rec_addr =3D reg58; - - regmap_read(aspeed_regmap, ASPEED_MCR_ADDR_REC, ®5c); - rec_addr =3D reg5c; - - /* clear interrupt flags and error counters: */ - regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, - ASPEED_MCR_INTR_CTRL_CLEAR, - ASPEED_MCR_INTR_CTRL_CLEAR); - - regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, - ASPEED_MCR_INTR_CTRL_CLEAR, 0); - /* process recoverable and unrecoverable errors */ count_rec(mci, rec_cnt, rec_addr); count_un_rec(mci, un_rec_cnt, un_rec_addr); @@ -182,13 +133,31 @@ static irqreturn_t mcr_isr(int irq, void *arg) if (!rec_cnt && !un_rec_cnt) dev_dbg(mci->pdev, "received edac interrupt, but did not find any ECC co= unters\n"); =20 - regmap_read(aspeed_regmap, ASPEED_MCR_INTR_CTRL, ®50); + scoped_guard(raw_spinlock, &aspeed_lock) + reg50 =3D readl(aspeed_regs + ASPEED_MCR_INTR_CTRL); dev_dbg(mci->pdev, "edac interrupt handled. mcr reg 50 is now: 0x%x\n", reg50); =20 return IRQ_HANDLED; } =20 +static void aspeed_set_irq(bool enable) +{ + u32 val; + + guard(raw_spinlock_irqsave)(&aspeed_lock); + + val =3D readl(aspeed_regs + ASPEED_MCR_INTR_CTRL); + if (enable) + val |=3D ASPEED_MCR_INTR_CTRL_ENABLE; + else + val &=3D ~ASPEED_MCR_INTR_CTRL_ENABLE; + + writel(ASPEED_MCR_PROT_PASSWD, aspeed_regs + ASPEED_MCR_PROT); + writel(val, aspeed_regs + ASPEED_MCR_INTR_CTRL); + writel(~ASPEED_MCR_PROT_PASSWD, aspeed_regs + ASPEED_MCR_PROT); +} + static int config_irq(void *ctx, struct platform_device *pdev) { int irq; @@ -206,9 +175,7 @@ static int config_irq(void *ctx, struct platform_device= *pdev) return rc; =20 /* enable interrupts */ - regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, - ASPEED_MCR_INTR_CTRL_ENABLE, - ASPEED_MCR_INTR_CTRL_ENABLE); + aspeed_set_irq(true); =20 return 0; } @@ -246,7 +213,8 @@ static int init_csrows(struct mem_ctl_info *mci) nr_pages =3D resource_size(&r) >> PAGE_SHIFT; csrow->last_page =3D csrow->first_page + nr_pages - 1; =20 - regmap_read(aspeed_regmap, ASPEED_MCR_CONF, ®04); + scoped_guard(raw_spinlock, &aspeed_lock) + reg04 =3D readl(aspeed_regs + ASPEED_MCR_CONF); dram_type =3D (reg04 & ASPEED_MCR_CONF_DRAM_TYPE) ? MEM_DDR4 : MEM_DDR3; =20 dimm =3D csrow->channels[0]->dimm; @@ -263,7 +231,6 @@ static int init_csrows(struct mem_ctl_info *mci) =20 static int aspeed_probe(struct platform_device *pdev) { - struct device *dev =3D &pdev->dev; struct edac_mc_layer layers[2]; struct mem_ctl_info *mci; void __iomem *regs; @@ -274,13 +241,11 @@ static int aspeed_probe(struct platform_device *pdev) if (IS_ERR(regs)) return PTR_ERR(regs); =20 - aspeed_regmap =3D devm_regmap_init(dev, NULL, (__force void *)regs, - &aspeed_regmap_config); - if (IS_ERR(aspeed_regmap)) - return PTR_ERR(aspeed_regmap); + scoped_guard(raw_spinlock, &aspeed_lock) + aspeed_regs =3D regs; =20 /* bail out if ECC mode is not configured */ - regmap_read(aspeed_regmap, ASPEED_MCR_CONF, ®04); + reg04 =3D readl(regs + ASPEED_MCR_CONF); if (!(reg04 & ASPEED_MCR_CONF_ECC)) { dev_err(&pdev->dev, "ECC mode is not configured in u-boot\n"); return -EPERM; @@ -346,8 +311,7 @@ static void aspeed_remove(struct platform_device *pdev) struct mem_ctl_info *mci =3D platform_get_drvdata(pdev); =20 /* disable interrupts */ - regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, - ASPEED_MCR_INTR_CTRL_ENABLE, 0); + aspeed_set_irq(false); =20 /* free resources */ edac_mc_del_mc(&pdev->dev); --=20 2.34.1 From nobody Tue Sep 29 06:08:02 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A8CA6294A10; Wed, 12 Aug 2026 05:49:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786513783; cv=none; b=UV78ObF3X5nCHhUniHK3xo2NGsyLlSboDPckPsYsI7ufIIz8MZkE962PqLOPuMlbL86SnxByzgnsyJK0XaHtznCrWBvDkX4IqdX3P6IeinclU5VwDN1wi5mpvk7fJ7hgx+QHx5b0Ksx/4FbPSSeADLxR8hIucHk9O4XIGbIEC1k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786513783; c=relaxed/simple; bh=zZcq6bjSenPSfX4fEIs8h1/Nsf9aZ5N5Y6/Xg/m3iDY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=sKmC3RyVPsMiskh8Ks6mzy5TX7VaJSaFgbTX068UhI3LzElHFs1LoUCWlEVrPHx16xB2fpitAosrV/jjMnbt9XoHqTKD0etmf6XcNjhRC6fPltMuYPfEGalXtcFdfE/QI35h/PE1ORBTGpwXJTeMP+vqmZGDLy1Gq1XmWFB6HBE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 12 Aug 2026 13:48:26 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 12 Aug 2026 13:48:26 +0800 From: Ryan Chen Date: Wed, 12 Aug 2026 13:48:30 +0800 Subject: [PATCH 6/7] EDAC/aspeed: Abstract SoC differences behind chip data Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20260812-edac-v1-6-03992edea297@aspeedtech.com> References: <20260812-edac-v1-0-03992edea297@aspeedtech.com> In-Reply-To: <20260812-edac-v1-0-03992edea297@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck , "Sebastian Andrzej Siewior" , Clark Williams , Steven Rostedt CC: , , , , , Borislav Petkov , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786513706; l=11471; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=zZcq6bjSenPSfX4fEIs8h1/Nsf9aZ5N5Y6/Xg/m3iDY=; b=lEYfUjJ0NHh9vI79AzD2PL+/bqWiuBS6EYNXMoCm5T9sCAAzem/P88yLtoL2CA2NDerVbyHYN YCyLzsz1l8YA/tHesZRtGGz9LRDIexe+j+8PU5BaBOZbG7qiTusBSrR X-Developer-Key: i=ryan_chen@aspeedtech.com; a=ed25519; pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc= The driver hard-codes the AST2400/2500/2600 register layout, ECC and DRAM-type bits, memory types and write-protection key. Abstract these SoC-specific details behind a per-SoC struct aspeed_edac_chip selected by the compatible, and move the per-instance state (register base, lock) into mci->pvt_info instead of globals, so controller variants that differ in these details can be added as table data rather than by forking the driver. The __guarded_by() annotation on the register base moves with it, so the build-time check that every access holds the lock is kept across the conversion. Only the AST2400 and AST2500 key-protect the interrupt control register (MCR50); the AST2600 does not. Gate the unlock/relock on the chip carrying a protection key and split the shared entry into keyed (AST2400/2500) and unkeyed (AST2600) variants, so the AST2600 no longer performs the unnecessary unlock. Tested on an AST2600: A correctable error was injected from the console by unlocking the controller and writing its ECC error inject test register: # mw 1e6e0000 fc600309 # mw 1e6e00b0 81 EDAC MC0: 1 CE address(es) not available on mc#0csrow#0channel#0 (csrow:0= channel:0 page:0x0 offset:0x0 grain:16 syndrome:0x0) EDAC MC0: 1 CE on mc#0csrow#0channel#0 (csrow:0 channel:0 page:0x8a543 of= fset:0xec0 grain:16 syndrome:0x0) Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Ryan Chen --- drivers/edac/aspeed_edac.c | 154 ++++++++++++++++++++++++++++++++---------= ---- 1 file changed, 109 insertions(+), 45 deletions(-) diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index 7bd552ee9a61..91df5d2df5f1 100644 --- a/drivers/edac/aspeed_edac.c +++ b/drivers/edac/aspeed_edac.c @@ -3,12 +3,14 @@ * Copyright 2018, 2019 Cisco Systems */ =20 +#include #include #include #include #include #include #include +#include #include #include #include @@ -30,8 +32,22 @@ #define ASPEED_MCR_INTR_CTRL_CNT_UNREC GENMASK(15, 12) #define ASPEED_MCR_INTR_CTRL_ENABLE (BIT(0) | BIT(1)) =20 -static DEFINE_RAW_SPINLOCK(aspeed_lock); -static void __iomem *aspeed_regs __guarded_by(&aspeed_lock); +struct aspeed_edac_chip { + unsigned int conf_reg; + u32 conf_ecc; + u32 conf_dram_type; + enum mem_type dram_type[2]; + unsigned long mtype_cap; + unsigned int prot_reg; + u32 prot_key; +}; + +struct aspeed_edac { + raw_spinlock_t lock; + + void __iomem *regs __guarded_by(&lock); + const struct aspeed_edac_chip *chip; +}; =20 static void count_rec(struct mem_ctl_info *mci, u8 rec_cnt, u32 rec_addr) { @@ -96,32 +112,49 @@ static void count_un_rec(struct mem_ctl_info *mci, u8 = un_rec_cnt, } } =20 -static irqreturn_t mcr_isr(int irq, void *arg) +static void aspeed_mcr_irq_update_enter(struct aspeed_edac *priv) + __must_hold(&priv->lock) +{ + if (priv->chip->prot_key) + writel(priv->chip->prot_key, priv->regs + priv->chip->prot_reg); +} + +static void aspeed_mcr_irq_update_exit(struct aspeed_edac *priv) + __must_hold(&priv->lock) +{ + if (priv->chip->prot_key) + writel(~priv->chip->prot_key, priv->regs + priv->chip->prot_reg); +} + +static irqreturn_t aspeed_mcr_isr(int irq, void *arg) { struct mem_ctl_info *mci =3D arg; u32 rec_addr, un_rec_addr; + struct aspeed_edac *priv; u8 rec_cnt, un_rec_cnt; u32 reg50; =20 - scoped_guard(raw_spinlock, &aspeed_lock) { - reg50 =3D readl(aspeed_regs + ASPEED_MCR_INTR_CTRL); + priv =3D mci->pvt_info; + + scoped_guard(raw_spinlock, &priv->lock) { + reg50 =3D readl(priv->regs + ASPEED_MCR_INTR_CTRL); dev_dbg(mci->pdev, "received edac interrupt w/ mcr register 50: 0x%x\n", reg50); - un_rec_addr =3D readl(aspeed_regs + ASPEED_MCR_ADDR_UNREC); - rec_addr =3D readl(aspeed_regs + ASPEED_MCR_ADDR_REC); + un_rec_addr =3D readl(priv->regs + ASPEED_MCR_ADDR_UNREC); + rec_addr =3D readl(priv->regs + ASPEED_MCR_ADDR_REC); =20 /* clearing the counters needs a set-then-clear of CLEAR */ - writel(ASPEED_MCR_PROT_PASSWD, aspeed_regs + ASPEED_MCR_PROT); + aspeed_mcr_irq_update_enter(priv); writel(reg50 | ASPEED_MCR_INTR_CTRL_CLEAR, - aspeed_regs + ASPEED_MCR_INTR_CTRL); + priv->regs + ASPEED_MCR_INTR_CTRL); writel(reg50 & ~ASPEED_MCR_INTR_CTRL_CLEAR, - aspeed_regs + ASPEED_MCR_INTR_CTRL); - writel(~ASPEED_MCR_PROT_PASSWD, aspeed_regs + ASPEED_MCR_PROT); + priv->regs + ASPEED_MCR_INTR_CTRL); + aspeed_mcr_irq_update_exit(priv); } =20 /* collect data about recoverable and unrecoverable errors */ - rec_cnt =3D (reg50 & ASPEED_MCR_INTR_CTRL_CNT_REC) >> 16; - un_rec_cnt =3D (reg50 & ASPEED_MCR_INTR_CTRL_CNT_UNREC) >> 12; + rec_cnt =3D FIELD_GET(ASPEED_MCR_INTR_CTRL_CNT_REC, reg50); + un_rec_cnt =3D FIELD_GET(ASPEED_MCR_INTR_CTRL_CNT_UNREC, reg50); =20 dev_dbg(mci->pdev, "%d recoverable interrupts and %d unrecoverable interr= upts\n", rec_cnt, un_rec_cnt); @@ -131,34 +164,35 @@ static irqreturn_t mcr_isr(int irq, void *arg) count_un_rec(mci, un_rec_cnt, un_rec_addr); =20 if (!rec_cnt && !un_rec_cnt) - dev_dbg(mci->pdev, "received edac interrupt, but did not find any ECC co= unters\n"); + dev_dbg_ratelimited(mci->pdev, "received edac interrupt, but did not fin= d any ECC counters\n"); =20 - scoped_guard(raw_spinlock, &aspeed_lock) - reg50 =3D readl(aspeed_regs + ASPEED_MCR_INTR_CTRL); + scoped_guard(raw_spinlock, &priv->lock) + reg50 =3D readl(priv->regs + ASPEED_MCR_INTR_CTRL); dev_dbg(mci->pdev, "edac interrupt handled. mcr reg 50 is now: 0x%x\n", reg50); =20 return IRQ_HANDLED; } =20 -static void aspeed_set_irq(bool enable) +static void aspeed_set_irq(struct mem_ctl_info *mci, bool enable) { + struct aspeed_edac *priv =3D mci->pvt_info; u32 val; =20 - guard(raw_spinlock_irqsave)(&aspeed_lock); + guard(raw_spinlock_irqsave)(&priv->lock); =20 - val =3D readl(aspeed_regs + ASPEED_MCR_INTR_CTRL); + val =3D readl(priv->regs + ASPEED_MCR_INTR_CTRL); if (enable) val |=3D ASPEED_MCR_INTR_CTRL_ENABLE; else val &=3D ~ASPEED_MCR_INTR_CTRL_ENABLE; =20 - writel(ASPEED_MCR_PROT_PASSWD, aspeed_regs + ASPEED_MCR_PROT); - writel(val, aspeed_regs + ASPEED_MCR_INTR_CTRL); - writel(~ASPEED_MCR_PROT_PASSWD, aspeed_regs + ASPEED_MCR_PROT); + aspeed_mcr_irq_update_enter(priv); + writel(val, priv->regs + ASPEED_MCR_INTR_CTRL); + aspeed_mcr_irq_update_exit(priv); } =20 -static int config_irq(void *ctx, struct platform_device *pdev) +static int config_irq(struct mem_ctl_info *mci, struct platform_device *pd= ev) { int irq; int rc; @@ -169,13 +203,13 @@ static int config_irq(void *ctx, struct platform_devi= ce *pdev) if (irq < 0) return irq; =20 - rc =3D devm_request_irq(&pdev->dev, irq, mcr_isr, IRQF_TRIGGER_HIGH, - DRV_NAME, ctx); + rc =3D devm_request_irq(&pdev->dev, irq, aspeed_mcr_isr, IRQF_TRIGGER_HIG= H, + DRV_NAME, mci); if (rc) return rc; =20 /* enable interrupts */ - aspeed_set_irq(true); + aspeed_set_irq(mci, true); =20 return 0; } @@ -183,11 +217,13 @@ static int config_irq(void *ctx, struct platform_devi= ce *pdev) static int init_csrows(struct mem_ctl_info *mci) { struct csrow_info *csrow =3D mci->csrows[0]; - u32 nr_pages, dram_type; - struct dimm_info *dimm; + struct aspeed_edac *priv =3D mci->pvt_info; struct device_node *np; + struct dimm_info *dimm; struct resource r; - u32 reg04; + unsigned int type; + u32 nr_pages; + u32 conf; int rc; =20 /* retrieve info about physical memory from device tree */ @@ -213,12 +249,12 @@ static int init_csrows(struct mem_ctl_info *mci) nr_pages =3D resource_size(&r) >> PAGE_SHIFT; csrow->last_page =3D csrow->first_page + nr_pages - 1; =20 - scoped_guard(raw_spinlock, &aspeed_lock) - reg04 =3D readl(aspeed_regs + ASPEED_MCR_CONF); - dram_type =3D (reg04 & ASPEED_MCR_CONF_DRAM_TYPE) ? MEM_DDR4 : MEM_DDR3; + scoped_guard(raw_spinlock, &priv->lock) + conf =3D readl(priv->regs + priv->chip->conf_reg); + type =3D field_get(priv->chip->conf_dram_type, conf); =20 dimm =3D csrow->channels[0]->dimm; - dimm->mtype =3D dram_type; + dimm->mtype =3D priv->chip->dram_type[type]; dimm->edac_mode =3D EDAC_SECDED; dimm->nr_pages =3D nr_pages / csrow->nr_channels; dimm->grain =3D 16; @@ -231,22 +267,26 @@ static int init_csrows(struct mem_ctl_info *mci) =20 static int aspeed_probe(struct platform_device *pdev) { + const struct aspeed_edac_chip *chip; + struct device *dev =3D &pdev->dev; struct edac_mc_layer layers[2]; + struct aspeed_edac *priv; struct mem_ctl_info *mci; void __iomem *regs; - u32 reg04; + u32 conf; int rc; =20 + chip =3D of_device_get_match_data(dev); + if (!chip) + return -EINVAL; + regs =3D devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); =20 - scoped_guard(raw_spinlock, &aspeed_lock) - aspeed_regs =3D regs; - /* bail out if ECC mode is not configured */ - reg04 =3D readl(regs + ASPEED_MCR_CONF); - if (!(reg04 & ASPEED_MCR_CONF_ECC)) { + conf =3D readl(regs + chip->conf_reg); + if (!field_get(chip->conf_ecc, conf)) { dev_err(&pdev->dev, "ECC mode is not configured in u-boot\n"); return -EPERM; } @@ -261,12 +301,17 @@ static int aspeed_probe(struct platform_device *pdev) layers[1].size =3D 1; layers[1].is_virt_csrow =3D false; =20 - mci =3D edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0); + mci =3D edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*priv)); if (!mci) return -ENOMEM; =20 + priv =3D mci->pvt_info; + priv->chip =3D chip; + scoped_guard(raw_spinlock_init, &priv->lock) + priv->regs =3D regs; + mci->pdev =3D &pdev->dev; - mci->mtype_cap =3D MEM_FLAG_DDR3 | MEM_FLAG_DDR4; + mci->mtype_cap =3D chip->mtype_cap; mci->edac_ctl_cap =3D EDAC_FLAG_SECDED; mci->edac_cap =3D EDAC_FLAG_SECDED; mci->scrub_cap =3D SCRUB_FLAG_HW_SRC; @@ -311,17 +356,36 @@ static void aspeed_remove(struct platform_device *pde= v) struct mem_ctl_info *mci =3D platform_get_drvdata(pdev); =20 /* disable interrupts */ - aspeed_set_irq(false); + aspeed_set_irq(mci, false); =20 /* free resources */ edac_mc_del_mc(&pdev->dev); edac_mc_free(mci); } =20 +static const struct aspeed_edac_chip ast2400_edac =3D { + .conf_reg =3D ASPEED_MCR_CONF, + .conf_ecc =3D ASPEED_MCR_CONF_ECC, + .conf_dram_type =3D ASPEED_MCR_CONF_DRAM_TYPE, + .dram_type =3D { MEM_DDR3, MEM_DDR4 }, + .mtype_cap =3D MEM_FLAG_DDR3 | MEM_FLAG_DDR4, + .prot_reg =3D ASPEED_MCR_PROT, + .prot_key =3D ASPEED_MCR_PROT_PASSWD, +}; + +/* The AST2600 does not key-protect the interrupt control register (MCR50)= . */ +static const struct aspeed_edac_chip ast2600_edac =3D { + .conf_reg =3D ASPEED_MCR_CONF, + .conf_ecc =3D ASPEED_MCR_CONF_ECC, + .conf_dram_type =3D ASPEED_MCR_CONF_DRAM_TYPE, + .dram_type =3D { MEM_DDR3, MEM_DDR4 }, + .mtype_cap =3D MEM_FLAG_DDR3 | MEM_FLAG_DDR4, +}; + static const struct of_device_id aspeed_of_match[] =3D { - { .compatible =3D "aspeed,ast2400-sdram-edac" }, - { .compatible =3D "aspeed,ast2500-sdram-edac" }, - { .compatible =3D "aspeed,ast2600-sdram-edac" }, + { .compatible =3D "aspeed,ast2400-sdram-edac", .data =3D &ast2400_edac }, + { .compatible =3D "aspeed,ast2500-sdram-edac", .data =3D &ast2400_edac }, + { .compatible =3D "aspeed,ast2600-sdram-edac", .data =3D &ast2600_edac }, {}, }; =20 --=20 2.34.1 From nobody Tue Sep 29 06:08:02 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 374C43128DF; Wed, 12 Aug 2026 05:49:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786513785; cv=none; b=cPE6+svydWmt+eJN88U8lBzPq3iW61RQf6/l8lIuIGvmg+7cK0FogSMnghf01H2pTYmAqrioKZCljvzw6z9dKEsqmxTa2t4bu/LwEfgMjy0OQvWp+z0ZDNvt4JDToxBuaAhXSqecmfIHN9+4guggn6NSN5W2YErEWb3PMaonUnA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786513785; c=relaxed/simple; bh=EOVfr2ok6rxbBdxN7bYBtEviOJjGs6G3RxZU8KfK1eo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=q+EDarJQvstxVAwC2zyqtN/790TZEYd581FDXFnH6Px+lx7Rk+Z5zoujs+Xem3tHwBf0Lvtp0LOl4AZUwYNxkL8LSGT+qwyHT5sU0c+XhlbjyV8pIGAAOCWU68yLoLFINBjyyB3BvT6wSQ8V8wWPR/UzyZ0fZfyX+WF1PDTI9z0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 12 Aug 2026 13:48:27 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 12 Aug 2026 13:48:27 +0800 From: Ryan Chen Date: Wed, 12 Aug 2026 13:48:31 +0800 Subject: [PATCH 7/7] EDAC/aspeed: Add AST2700 support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20260812-edac-v1-7-03992edea297@aspeedtech.com> References: <20260812-edac-v1-0-03992edea297@aspeedtech.com> In-Reply-To: <20260812-edac-v1-0-03992edea297@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck , "Sebastian Andrzej Siewior" , Clark Williams , Steven Rostedt CC: , , , , , Borislav Petkov , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786513706; l=10873; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=EOVfr2ok6rxbBdxN7bYBtEviOJjGs6G3RxZU8KfK1eo=; b=8xYSioI2uWnPZREYmRnWEyTIdXS0zjZ+TlyEg5XE6IsciKW7kUGokXwO724O50Q/rJTYXWUlR DW4p++Hr2d1Beh3dCNI+P14TUfGYmrJbbtWTvqqIKGmkdHAhtWVgxZZ X-Developer-Key: i=ryan_chen@aspeedtech.com; a=ed25519; pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc= Add SDRAM ECC reporting for the Aspeed AST2700. Its DRAMC has a different register layout, a split interrupt status/clear/mask scheme, DDR4/DDR5 memory and interrupt registers that are not key-protected. Its interrupt status/clear and enable sequences differ from the earlier SoCs, so add per-chip isr() and set_irq() hooks and route the request_irq and enable/disable paths through them, keeping the existing AST2400/2500/ 2600 behaviour under the shared aspeed_mcr_isr()/aspeed_set_irq(). Unlike the earlier SoCs it records a single failure address shared by both error types, so extend the shared count_rec()/count_un_rec() helpers with a have_addr flag to report an error without an address (existing SoCs pass have_addr =3D true, unchanged) and widen their address argument to phys_addr_t as the AST2700 address can exceed 32 bits. Tested on an AST2700: A correctable error was injected from the console by unlocking the controller and writing its ECC error inject test register: # mw 12c00000 1688a8a8 # mw 12c00080 31 EDAC MC0: 1 CE on mc#0csrow#0channel#0 (csrow:0 channel:0 page:0x40f6da o= ffset:0xdb0 grain:16 syndrome:0x0) Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Ryan Chen --- drivers/edac/aspeed_edac.c | 154 +++++++++++++++++++++++++++++++++++++----= ---- 1 file changed, 128 insertions(+), 26 deletions(-) diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index 91df5d2df5f1..8b48044a00d1 100644 --- a/drivers/edac/aspeed_edac.c +++ b/drivers/edac/aspeed_edac.c @@ -32,6 +32,19 @@ #define ASPEED_MCR_INTR_CTRL_CNT_UNREC GENMASK(15, 12) #define ASPEED_MCR_INTR_CTRL_ENABLE (BIT(0) | BIT(1)) =20 +#define AST2700_INT_STS 0x04 +#define AST2700_INT_CLR 0x08 +#define AST2700_INT_MASK 0x0c +#define AST2700_INT_ECC_RECOVERABLE BIT(5) +#define AST2700_INT_ECC_UNRECOVERABLE BIT(4) +#define AST2700_MCFG 0x10 +#define AST2700_MCFG_ECC BIT(6) +#define AST2700_MCFG_DRAM_TYPE BIT(0) /* 0=3DDDR4, 1=3DDDR5 */ +#define AST2700_ECC_STS 0x78 +#define AST2700_ECC_REC_CNT GENMASK(15, 8) +#define AST2700_ECC_UNREC_CNT GENMASK(7, 0) +#define AST2700_ECC_FAIL_ADDR 0x7c + struct aspeed_edac_chip { unsigned int conf_reg; u32 conf_ecc; @@ -40,6 +53,8 @@ struct aspeed_edac_chip { unsigned long mtype_cap; unsigned int prot_reg; u32 prot_key; + irqreturn_t (*isr)(int irq, void *arg); + void (*set_irq)(struct mem_ctl_info *mci, bool enable); }; =20 struct aspeed_edac { @@ -49,26 +64,34 @@ struct aspeed_edac { const struct aspeed_edac_chip *chip; }; =20 -static void count_rec(struct mem_ctl_info *mci, u8 rec_cnt, u32 rec_addr) +static void count_rec(struct mem_ctl_info *mci, u8 rec_cnt, phys_addr_t re= c_addr, + bool have_addr) { struct csrow_info *csrow =3D mci->csrows[0]; - u32 page, offset, syndrome; + unsigned long page, offset, syndrome; =20 if (!rec_cnt) return; =20 - /* report first few errors (if there are) */ - /* note: no addresses are recorded */ - if (rec_cnt > 1) { + /* + * Report the errors whose address is not recorded: all of them when + * no address is available, otherwise all but the last one (reported + * with its address below). + */ + if (rec_cnt > 1 || !have_addr) { /* page, offset and syndrome are not available */ page =3D 0; offset =3D 0; syndrome =3D 0; - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, rec_cnt-1, + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + have_addr ? rec_cnt - 1 : rec_cnt, page, offset, syndrome, 0, 0, -1, "address(es) not available", ""); } =20 + if (!have_addr) + return; + /* report last error */ /* note: rec_addr is the last recoverable error addr */ page =3D rec_addr >> PAGE_SHIFT; @@ -81,32 +104,34 @@ static void count_rec(struct mem_ctl_info *mci, u8 rec= _cnt, u32 rec_addr) } =20 static void count_un_rec(struct mem_ctl_info *mci, u8 un_rec_cnt, - u32 un_rec_addr) + phys_addr_t un_rec_addr, bool have_addr) { struct csrow_info *csrow =3D mci->csrows[0]; - u32 page, offset, syndrome; + unsigned long page, offset, syndrome; =20 if (!un_rec_cnt) return; =20 - /* report 1. error */ - /* note: un_rec_addr is the first unrecoverable error addr */ - page =3D un_rec_addr >> PAGE_SHIFT; - offset =3D un_rec_addr & ~PAGE_MASK; - /* syndrome is not available */ - syndrome =3D 0; - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, - csrow->first_page + page, offset, syndrome, - 0, 0, -1, "", ""); + /* report the first error with its address when one is available */ + if (have_addr) { + /* note: un_rec_addr is the first unrecoverable error addr */ + page =3D un_rec_addr >> PAGE_SHIFT; + offset =3D un_rec_addr & ~PAGE_MASK; + /* syndrome is not available */ + syndrome =3D 0; + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, + csrow->first_page + page, offset, syndrome, + 0, 0, -1, "", ""); + } =20 - /* report further errors (if there are) */ - /* note: no addresses are recorded */ - if (un_rec_cnt > 1) { + /* report the remaining errors without a recorded address */ + if (un_rec_cnt > 1 || !have_addr) { /* page, offset and syndrome are not available */ page =3D 0; offset =3D 0; syndrome =3D 0; - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, un_rec_cnt-1, + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + have_addr ? un_rec_cnt - 1 : un_rec_cnt, page, offset, syndrome, 0, 0, -1, "address(es) not available", ""); } @@ -160,8 +185,8 @@ static irqreturn_t aspeed_mcr_isr(int irq, void *arg) rec_cnt, un_rec_cnt); =20 /* process recoverable and unrecoverable errors */ - count_rec(mci, rec_cnt, rec_addr); - count_un_rec(mci, un_rec_cnt, un_rec_addr); + count_rec(mci, rec_cnt, rec_addr, true); + count_un_rec(mci, un_rec_cnt, un_rec_addr, true); =20 if (!rec_cnt && !un_rec_cnt) dev_dbg_ratelimited(mci->pdev, "received edac interrupt, but did not fin= d any ECC counters\n"); @@ -174,6 +199,52 @@ static irqreturn_t aspeed_mcr_isr(int irq, void *arg) return IRQ_HANDLED; } =20 +static irqreturn_t ast2700_dramc_isr(int irq, void *arg) +{ + u32 int_sts, ecc_sts, fail_addr; + struct mem_ctl_info *mci =3D arg; + struct aspeed_edac *priv; + u8 rec_cnt, un_rec_cnt; + phys_addr_t addr; + + priv =3D mci->pvt_info; + + scoped_guard(raw_spinlock, &priv->lock) { + int_sts =3D readl(priv->regs + AST2700_INT_STS); + ecc_sts =3D readl(priv->regs + AST2700_ECC_STS); + fail_addr =3D readl(priv->regs + AST2700_ECC_FAIL_ADDR); + + /* the interrupt registers are not key-protected; clear only ECC */ + writel(int_sts & (AST2700_INT_ECC_RECOVERABLE | AST2700_INT_ECC_UNRECOVE= RABLE), + priv->regs + AST2700_INT_CLR); + } + + rec_cnt =3D FIELD_GET(AST2700_ECC_REC_CNT, ecc_sts); + un_rec_cnt =3D FIELD_GET(AST2700_ECC_UNREC_CNT, ecc_sts); + + /* the register holds address bits [35:4], in units of 16 bytes */ + addr =3D (phys_addr_t)fail_addr << 4; + + /* + * The controller records only the address of the latest failure, + * shared by both error types. When only one type occurred it owns + * that address; when both occurred attribute it to the uncorrectable + * error and report the corrected ones without an address. + */ + if (un_rec_cnt && !rec_cnt) { + count_un_rec(mci, un_rec_cnt, addr, true); + } else if (!un_rec_cnt && rec_cnt) { + count_rec(mci, rec_cnt, addr, true); + } else if (un_rec_cnt && rec_cnt) { + count_un_rec(mci, un_rec_cnt, addr, true); + count_rec(mci, rec_cnt, 0, false); + } else { + dev_dbg_ratelimited(mci->pdev, "received interrupt with no ECC counters = set\n"); + } + + return IRQ_HANDLED; +} + static void aspeed_set_irq(struct mem_ctl_info *mci, bool enable) { struct aspeed_edac *priv =3D mci->pvt_info; @@ -192,8 +263,22 @@ static void aspeed_set_irq(struct mem_ctl_info *mci, b= ool enable) aspeed_mcr_irq_update_exit(priv); } =20 +static void ast2700_set_irq(struct mem_ctl_info *mci, bool enable) +{ + u32 mask =3D AST2700_INT_ECC_RECOVERABLE | AST2700_INT_ECC_UNRECOVERABLE; + struct aspeed_edac *priv =3D mci->pvt_info; + u32 val; + + guard(raw_spinlock_irqsave)(&priv->lock); + + /* interrupts are enabled by clearing their mask bits */ + val =3D readl(priv->regs + AST2700_INT_MASK); + writel(enable ? (val & ~mask) : (val | mask), priv->regs + AST2700_INT_MA= SK); +} + static int config_irq(struct mem_ctl_info *mci, struct platform_device *pd= ev) { + struct aspeed_edac *priv =3D mci->pvt_info; int irq; int rc; =20 @@ -203,13 +288,13 @@ static int config_irq(struct mem_ctl_info *mci, struc= t platform_device *pdev) if (irq < 0) return irq; =20 - rc =3D devm_request_irq(&pdev->dev, irq, aspeed_mcr_isr, IRQF_TRIGGER_HIG= H, + rc =3D devm_request_irq(&pdev->dev, irq, priv->chip->isr, IRQF_TRIGGER_HI= GH, DRV_NAME, mci); if (rc) return rc; =20 /* enable interrupts */ - aspeed_set_irq(mci, true); + priv->chip->set_irq(mci, true); =20 return 0; } @@ -354,9 +439,10 @@ static int aspeed_probe(struct platform_device *pdev) static void aspeed_remove(struct platform_device *pdev) { struct mem_ctl_info *mci =3D platform_get_drvdata(pdev); + struct aspeed_edac *priv =3D mci->pvt_info; =20 /* disable interrupts */ - aspeed_set_irq(mci, false); + priv->chip->set_irq(mci, false); =20 /* free resources */ edac_mc_del_mc(&pdev->dev); @@ -371,6 +457,8 @@ static const struct aspeed_edac_chip ast2400_edac =3D { .mtype_cap =3D MEM_FLAG_DDR3 | MEM_FLAG_DDR4, .prot_reg =3D ASPEED_MCR_PROT, .prot_key =3D ASPEED_MCR_PROT_PASSWD, + .isr =3D aspeed_mcr_isr, + .set_irq =3D aspeed_set_irq, }; =20 /* The AST2600 does not key-protect the interrupt control register (MCR50)= . */ @@ -380,12 +468,26 @@ static const struct aspeed_edac_chip ast2600_edac =3D= { .conf_dram_type =3D ASPEED_MCR_CONF_DRAM_TYPE, .dram_type =3D { MEM_DDR3, MEM_DDR4 }, .mtype_cap =3D MEM_FLAG_DDR3 | MEM_FLAG_DDR4, + .isr =3D aspeed_mcr_isr, + .set_irq =3D aspeed_set_irq, +}; + +/* The AST2700 interrupt registers are not key-protected either. */ +static const struct aspeed_edac_chip ast2700_edac =3D { + .conf_reg =3D AST2700_MCFG, + .conf_ecc =3D AST2700_MCFG_ECC, + .conf_dram_type =3D AST2700_MCFG_DRAM_TYPE, + .dram_type =3D { MEM_DDR4, MEM_DDR5 }, + .mtype_cap =3D MEM_FLAG_DDR4 | MEM_FLAG_DDR5, + .isr =3D ast2700_dramc_isr, + .set_irq =3D ast2700_set_irq, }; =20 static const struct of_device_id aspeed_of_match[] =3D { { .compatible =3D "aspeed,ast2400-sdram-edac", .data =3D &ast2400_edac }, { .compatible =3D "aspeed,ast2500-sdram-edac", .data =3D &ast2400_edac }, { .compatible =3D "aspeed,ast2600-sdram-edac", .data =3D &ast2600_edac }, + { .compatible =3D "aspeed,ast2700-sdram-edac", .data =3D &ast2700_edac }, {}, }; =20 --=20 2.34.1