From nobody Mon Sep 28 09:58:17 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 1F866374185; Mon, 24 Aug 2026 02:42: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=1787539367; cv=none; b=kwvRuUkV8g3KUoeNkyImRK6R0ntO3HQko2bLrbXm6j41YWwdJLfHRZFFVN5wWDsswPbxTY8R94dTKCBzChEZPPvU7uG7aHMwfrFyMDFLuGO0KM8nV/MB+GSdVVTOgWmLa/35AcmYjoDLwbAI2K59JhZZoQTzbShxqH/bz78iAMc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787539367; c=relaxed/simple; bh=/rj5B9ojWffAdYkp2s4TIp84wfNqe/zSqKMEbEuHOX4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=Mr++enst8S8jLfGTvLz3ztFzGfKXOBwwStFuB2QXxBimhd7R527OypjTft/UpRGlS3KYfHnMoWM/xAYkucTva7ZUQTp4a2HwfDc6hVSlL5y1dyhyQMEE4iZDUFLsNC92/OD+cnq5l4yhf7qWX07xdkQxoWdHbv9WqxAzbtuY2bY= 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; Mon, 24 Aug 2026 10:42:29 +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; Mon, 24 Aug 2026 10:42:29 +0800 From: Ryan Chen Date: Mon, 24 Aug 2026 10:42:28 +0800 Subject: [PATCH v2 1/8] 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: <20260824-edac-v2-1-c8d8bb693586@aspeedtech.com> References: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> In-Reply-To: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Ryan Chen , "Krzysztof Kozlowski" X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1787539349; l=1457; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=/rj5B9ojWffAdYkp2s4TIp84wfNqe/zSqKMEbEuHOX4=; b=TaibQvv4TpzRvvyoXVzgOjoAI303zuv8TcSaD2VQghxWh6N8J2Ejb/YKubUDy2LwQlCUnU9Tz kHqYF4tiE2rAeuS52ZOANT7tGZfw1zUsDxd0E3XsChaWawY4LDnRyqW 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 Mon Sep 28 09:58:17 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 94DC73749E2; Mon, 24 Aug 2026 02:42:47 +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=1787539371; cv=none; b=T2QCXdULv5MF3HBag2gmr6H+R/2baQvld+MKJMoNEpU+s6H/Gyn5Dpfd91JowPYrJIBhEcDkdF8g9E4spndwLLP7Y7xHF405HKphqIh+5QN4DmYQWXf4hmYFmACjJsNOLd4bJyIZ9Fs04niPi+0dTXPfuTeADyyODpja5wxcYXg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787539371; c=relaxed/simple; bh=HAKuO93Qq40zQKSxoIqd6AgYapN0t/tUgR+0VfcG6gw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=OVmoCbVapgIDVZIRaSY0XxD0NRflX9CZ4gx+dkJynq6SCIqIH+UgCNarsWyIhwht70PN50LOlsBkoqKE063RhVjrf02feV8f7ufgsTh/MgMCMwh0sKJw6ahe+7s0hAANhOjI0cg4qEsoBVGLLzEcRfEYXSD0FG5mjjtyDiZisBY= 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; Mon, 24 Aug 2026 10:42:29 +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; Mon, 24 Aug 2026 10:42:29 +0800 From: Ryan Chen Date: Mon, 24 Aug 2026 10:42:29 +0800 Subject: [PATCH v2 2/8] 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: <20260824-edac-v2-2-c8d8bb693586@aspeedtech.com> References: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> In-Reply-To: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Ryan Chen , X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1787539349; l=1310; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=HAKuO93Qq40zQKSxoIqd6AgYapN0t/tUgR+0VfcG6gw=; b=pWMGHcJsIcNVW6fGk94Ijg8TINjEYsz1FD6Ls8QarH71DmsGHNYs0v+MeawD+m42IUSfAQtqJ Rx6vngxHGfQDWICRK+3nRef9Xsm4UkjhAOQp7WRYgmaaN6DkI5RH2vE 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") Signed-off-by: Ryan Chen Cc: stable@vger.kernel.org --- 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 Mon Sep 28 09:58:17 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 8C49A36492D; Mon, 24 Aug 2026 02:42:51 +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=1787539375; cv=none; b=gdmQ1dYp3UqGtfuiLw3DX9H2yBg6zLc8nyMI0KlOhsGKFDElmqjgeFdcUA0DqDKpPN/8ubpde1AsoSTrNYf+ihUV76j26+2S798hPCUnmLIaSszzuI2uokZFRTsV1y1lnkiEVvWHUWJfeg23PgMGbiIhCDA2gvmR7DEPflpYUiA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787539375; c=relaxed/simple; bh=RlxiTc6ufbAPp2W9fkd50aRtYznC++m4JM40pI/YrqE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=Et9wE66ve0xuquxPlhLCF1tqCgdqPPW2QRJzzD1oV51S2GqG8f63MCuoHVDhBXwPZeCxF7eVWrmF9jYjp9gKv+oKYY/l1yi8cbIRTLIkavrIPWzMGPz0rDL9OD1V3kWd+FlcNKrFhHiWpT7iA8DcmLHoS5vmrju4rqX/Fro7f4k= 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; Mon, 24 Aug 2026 10:42:29 +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; Mon, 24 Aug 2026 10:42:29 +0800 From: Ryan Chen Date: Mon, 24 Aug 2026 10:42:30 +0800 Subject: [PATCH v2 3/8] EDAC/aspeed: Free the interrupt before the mem_ctl_info 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: <20260824-edac-v2-3-c8d8bb693586@aspeedtech.com> References: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> In-Reply-To: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Ryan Chen , X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1787539349; l=1363; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=RlxiTc6ufbAPp2W9fkd50aRtYznC++m4JM40pI/YrqE=; b=gpm27cUcRMtuWzCYAMZDJ+ZRjHayMCjYlYb1GvBw7j/JjEkz5DoJugDkwdsgHS1YKJBXoglg0 LrgCW0p3PfhD0Ihn/PVxqB2zZL+Dut7Vd3Y6FJ9p6NtwOpzsuoeFiW4 X-Developer-Key: i=ryan_chen@aspeedtech.com; a=ed25519; pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc= The ECC interrupt is devm-managed, so it is only released after .remove() has returned, and masking the controller does not wait for a handler already running on another CPU. edac_mc_free() can therefore free the mem_ctl_info the handler uses as its context while it is still running. Fix the ordering and synchronise by freeing the interrupt prior to releasing related memory. Fixes: 9b7e6242ee4e ("EDAC, aspeed: Add an Aspeed AST2500 EDAC driver") Signed-off-by: Ryan Chen Cc: stable@vger.kernel.org --- Changes in v2: - New patch. --- drivers/edac/aspeed_edac.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index 83d60414f89a..e05ebed5c2f2 100644 --- a/drivers/edac/aspeed_edac.c +++ b/drivers/edac/aspeed_edac.c @@ -359,11 +359,16 @@ static int aspeed_probe(struct platform_device *pdev) static void aspeed_remove(struct platform_device *pdev) { struct mem_ctl_info *mci; + int irq; =20 /* disable interrupts */ regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, ASPEED_MCR_INTR_CTRL_ENABLE, 0); =20 + irq =3D platform_get_irq(pdev, 0); + WARN_ON(irq < 0); + devm_free_irq(&pdev->dev, irq, platform_get_drvdata(pdev)); + /* free resources */ mci =3D edac_mc_del_mc(&pdev->dev); if (mci) --=20 2.34.1 From nobody Mon Sep 28 09:58:17 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 9E023376BD0; Mon, 24 Aug 2026 02:42:55 +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=1787539379; cv=none; b=p/62WoqP0le3pc4gS+K9lFH8rVJ6CeAHvY1aM405RbbC4BPfgDwqwDE4RONRLWW/YS4kw84TTgL+8mC0F6djUkTHll0ST/qxOKFIcAWUjkPG2Wn0+5P7MahbLbkhTPAyQ6io0I0U0/9HPHXnr0va919PPWwmUQvzhuBsJhoWd+o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787539379; c=relaxed/simple; bh=FhGqCgC1qcv7kHcNVhXGaiiu15ASlL/TX82CfVZ7NtU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=dRe1vRC36327wvKfOEMu2fyMZkDCPdW1HWoF1FfRshXxWzVdziBwgI7DrQp0Txt1cKbgsKYScG9/ol6puYxd7S3ZCl8TCfh90g2TuNRk3duZfswq6TfZMmOw1qJ9mxD/bw7+QEfzsXsyJ3tubUqlPBb+p3RrwofunA8l9XVdffk= 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; Mon, 24 Aug 2026 10:42:29 +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; Mon, 24 Aug 2026 10:42:29 +0800 From: Ryan Chen Date: Mon, 24 Aug 2026 10:42:31 +0800 Subject: [PATCH v2 4/8] 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: <20260824-edac-v2-4-c8d8bb693586@aspeedtech.com> References: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> In-Reply-To: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1787539349; l=4626; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=FhGqCgC1qcv7kHcNVhXGaiiu15ASlL/TX82CfVZ7NtU=; b=GoygC2E3grvnPd/Rs5Ery9rOw8CZ2YdTHWs1JTllbAufHdcDhqmniCvIr3gty9x43Vlyq1Pzb X3NhroraJhdCdJOYlFiEs3dh3IsGOe5FHGgeBVNvzZoIfwSoJOtCGuK 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 e05ebed5c2f2..cd2a6fcca355 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; @@ -375,7 +360,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 Mon Sep 28 09:58:17 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 19FAB3749FB; Mon, 24 Aug 2026 02:43:00 +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=1787539383; cv=none; b=byZ9YOnzqKSTGSLDV/FB1OKViS1kQrfbmYTuHCMLUbnHDZW9Owx9EHw/N4Z5nPtaiDSGKjxRfrmZrwgLp9jCR5yI9zahUG3k++2ZFQXHOYWVf5b5JdLNijzmRbusb6QS2yk7z3MrSCRznWN5afSIjiMnNlcQAz/9umWfUEkSAX0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787539383; c=relaxed/simple; bh=7kVBSW+iJq7iG0C/gaN+W7UHNDDfaeN9Q8zh7Gh7SjI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=pmBVUXrjmtDP+v6eV5O3W42zFZc0v4Su1fdL9FDWmDlr9Ev5zPB41HwClOdM9oSOV5Q3KS4OObwlDWxx4HbUgC+OWDqduyqemEjJnfH4aZmFDGVf6ppqBokLw+CqwaMyHsTvP06byM3WuY2uBdi72n3s5cyM+zxaIyB/EP3d3sY= 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; Mon, 24 Aug 2026 10:42:29 +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; Mon, 24 Aug 2026 10:42:29 +0800 From: Ryan Chen Date: Mon, 24 Aug 2026 10:42:32 +0800 Subject: [PATCH v2 5/8] 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: <20260824-edac-v2-5-c8d8bb693586@aspeedtech.com> References: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> In-Reply-To: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1787539349; l=1565; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=7kVBSW+iJq7iG0C/gaN+W7UHNDDfaeN9Q8zh7Gh7SjI=; b=tmzcNbO6l4v/ZjpLF1sRvvnABHILvPaf2m5qsCrYx7e1Xvo3KWS7sazk+K6B0sEq/XNQbH7A6 4oPY8pE0WSJCTkKMfdhwd+asMen7kmrQbIYF9UuVD/Bt79CialmOoxG 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 | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index cd2a6fcca355..352910e1defc 100644 --- a/drivers/edac/aspeed_edac.c +++ b/drivers/edac/aspeed_edac.c @@ -343,7 +343,7 @@ 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); int irq; =20 /* disable interrupts */ @@ -352,12 +352,11 @@ static void aspeed_remove(struct platform_device *pde= v) =20 irq =3D platform_get_irq(pdev, 0); WARN_ON(irq < 0); - devm_free_irq(&pdev->dev, irq, platform_get_drvdata(pdev)); + devm_free_irq(&pdev->dev, irq, mci); =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 Mon Sep 28 09:58:17 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 25A17378D8D; Mon, 24 Aug 2026 02:43:04 +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=1787539387; cv=none; b=SZ1uGmkRxvFnuWmAXMDeVV8wEqvir6mhWRmflBvghuWiBEwhA+sSIpf9HCbdOKsOHvYuVyPJ10GS7wNrgEkW1Z4/MBJ8gN8iEA9Ep49TIV+pMEXvB85mMdDs2wwAQWRLNdkAmsyd54G3N50dpOz5/CuhN13xPOJdmhOSJ7hJJW8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787539387; c=relaxed/simple; bh=psxRIoxnIOnKjSdmh+wrPYhekj1Z2QyYL+Qf6j/9Bq0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=HgSR3W+++tGC37zPsuFXGsLdJVMsnIv1UPwPSjyynFtihyDiIMmSCu7CLcPFU0xWdI7Mwh3Q6J2EpsB/EmG+0eIzm7+FiZmoYFjeimMUK0mssA2N+qTCANUujfPoedtwzCYRVtmrM0wyDLhFia58YaymdHsBSJJgn+vx1iO3s2E= 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; Mon, 24 Aug 2026 10:42:29 +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; Mon, 24 Aug 2026 10:42:29 +0800 From: Ryan Chen Date: Mon, 24 Aug 2026 10:42:33 +0800 Subject: [PATCH v2 6/8] 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: <20260824-edac-v2-6-c8d8bb693586@aspeedtech.com> References: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> In-Reply-To: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1787539349; l=9641; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=psxRIoxnIOnKjSdmh+wrPYhekj1Z2QyYL+Qf6j/9Bq0=; b=FTiurfxA95Wui5itLcd9MtzO1dXWHIpGoLUst5PY0wDUGWIJV/haaBeerkcZ1x4t8B2iuwbTi Y2OyeAYmQX4CfYuDRQF4lHPfM53rFcPDMRjC7synJiDfucmaS2ac7n/ 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. 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 --- Changes in v2: - Take the register lock with the irqsave variant in init_csrows() and in aspeed_probe(); the interrupt handler takes the same lock in hardirq context, so acquiring it with interrupts enabled would trip lockdep. - Move the dev_dbg() of the interrupt status register out of the raw_spinlock critical section in the interrupt handler. - Opt aspeed_edac.o into context analysis in drivers/edac/Makefile, so that the __guarded_by() annotation is actually checked. - Note in the interrupt handler that the counter and interrupt flag fields are read-only, so writing back the read value is harmless. --- drivers/edac/Makefile | 1 + drivers/edac/aspeed_edac.c | 131 +++++++++++++++++------------------------= ---- 2 files changed, 51 insertions(+), 81 deletions(-) diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index a37534300ab9..9215dd0bb835 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_EDAC_SYNOPSYS) +=3D synopsys_edac.o obj-$(CONFIG_EDAC_XGENE) +=3D xgene_edac.o obj-$(CONFIG_EDAC_TI) +=3D ti_edac.o obj-$(CONFIG_EDAC_QCOM) +=3D qcom_edac.o +CONTEXT_ANALYSIS_aspeed_edac.o :=3D y obj-$(CONFIG_EDAC_ASPEED) +=3D aspeed_edac.o obj-$(CONFIG_EDAC_BLUEFIELD) +=3D bluefield_edac.o obj-$(CONFIG_EDAC_DMC520) +=3D dmc520_edac.o diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index 352910e1defc..26d2c456cc0d 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,10 +100,27 @@ 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; + u8 rec_cnt, un_rec_cnt; + u32 reg50; + + scoped_guard(raw_spinlock, &aspeed_lock) { + reg50 =3D readl(aspeed_regs + ASPEED_MCR_INTR_CTRL); + 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. The + * counter and interrupt flag fields are read-only, so writing + * back the values read above leaves them unaffected. + */ + 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 - regmap_read(aspeed_regmap, ASPEED_MCR_INTR_CTRL, ®50); dev_dbg(mci->pdev, "received edac interrupt w/ mcr register 50: 0x%x\n", reg50); =20 @@ -161,20 +131,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 +138,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 +180,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 +218,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_irqsave, &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 +236,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 +246,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_irqsave, &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; @@ -347,8 +317,7 @@ static void aspeed_remove(struct platform_device *pdev) int irq; =20 /* disable interrupts */ - regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, - ASPEED_MCR_INTR_CTRL_ENABLE, 0); + aspeed_set_irq(false); =20 irq =3D platform_get_irq(pdev, 0); WARN_ON(irq < 0); --=20 2.34.1 From nobody Mon Sep 28 09:58:17 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 DB5F186329; Mon, 24 Aug 2026 02:43:08 +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=1787539393; cv=none; b=LJr90FqDl8eCTkB6KxcyZ7pyg5/z3WL2MtRlqq5xt/BbNalQEM1LsDFJqBpvW8bem6E8vTwRQ+7fl+B2IbM/PAS1X5+r4/d9sWAxkGVSIlUr1oPjN7PiCS5Wb4xpakPEsPbzvlTYObksY8NgjubfQK0EgBuLvRHDs5yAgwwaOIw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787539393; c=relaxed/simple; bh=9GcVj2fdxxCTsPwP9/o+MgofFZHMPAKymQnxcoTT3v0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=JA0RuXt5nxAe3v8QV7rU7rtllP4Cng/xWM8NQc2ctWo5ygF3glcY06QwM37JNmS4jA93I52o1sQPuaAuMZgdXisaIUY4IPEt0xkrEDLlOzRWoGz4Goq+cVav2Y8r9MZkcYqJ6Bq/D5/ryi11/9NtShqDOPlQEaeGLvl2hQgUhdE= 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; Mon, 24 Aug 2026 10:42:30 +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; Mon, 24 Aug 2026 10:42:30 +0800 From: Ryan Chen Date: Mon, 24 Aug 2026 10:42:34 +0800 Subject: [PATCH v2 7/8] 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: <20260824-edac-v2-7-c8d8bb693586@aspeedtech.com> References: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> In-Reply-To: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1787539349; l=12030; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=9GcVj2fdxxCTsPwP9/o+MgofFZHMPAKymQnxcoTT3v0=; b=c/OnQKprV1QP9PTzVx+YeuvAxi0JKRV7IL7evjt5tyyVcLaP7vxt9fmuVPwlCVtxDSXMVENgD KS/plaRXDpsD0dAAoXy/ud1vOs7c79JCNHZM2H4o0j80/M1B56/H8IW 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. 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 --- Changes in v2: - Carry over the irqsave lock acquisition in init_csrows() and the dev_dbg() moved out of the raw_spinlock critical section. - Store the interrupt number in struct aspeed_edac so that aspeed_remove() can free the interrupt without looking it up again. --- drivers/edac/aspeed_edac.c | 163 +++++++++++++++++++++++++++++++----------= ---- 1 file changed, 114 insertions(+), 49 deletions(-) diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index 26d2c456cc0d..e88d9d2646de 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,23 @@ #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; + int irq; +}; =20 static void count_rec(struct mem_ctl_info *mci, u8 rec_cnt, u32 rec_addr) { @@ -96,37 +113,54 @@ 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); - un_rec_addr =3D readl(aspeed_regs + ASPEED_MCR_ADDR_UNREC); - rec_addr =3D readl(aspeed_regs + ASPEED_MCR_ADDR_REC); + priv =3D mci->pvt_info; + + scoped_guard(raw_spinlock, &priv->lock) { + reg50 =3D readl(priv->regs + ASPEED_MCR_INTR_CTRL); + 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. The * counter and interrupt flag fields are read-only, so writing * back the values read above leaves them unaffected. */ - 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 dev_dbg(mci->pdev, "received edac interrupt w/ mcr register 50: 0x%x\n", reg50); =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); @@ -136,35 +170,36 @@ 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 aspeed_edac *priv, bool enable) { 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) { + struct aspeed_edac *priv =3D mci->pvt_info; int irq; int rc; =20 @@ -174,13 +209,15 @@ 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 + priv->irq =3D irq; + /* enable interrupts */ - aspeed_set_irq(true); + aspeed_set_irq(priv, true); =20 return 0; } @@ -188,11 +225,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 */ @@ -218,12 +257,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_irqsave, &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_irqsave, &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; @@ -236,22 +275,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_irqsave, &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; } @@ -266,12 +309,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; @@ -314,24 +362,41 @@ 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); - int irq; + struct aspeed_edac *priv =3D mci->pvt_info; =20 /* disable interrupts */ - aspeed_set_irq(false); + aspeed_set_irq(priv, false); =20 - irq =3D platform_get_irq(pdev, 0); - WARN_ON(irq < 0); - devm_free_irq(&pdev->dev, irq, mci); + devm_free_irq(&pdev->dev, priv->irq, mci); =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 Mon Sep 28 09:58:17 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 69858377018; Mon, 24 Aug 2026 02:43:13 +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=1787539397; cv=none; b=cBmM8spx3w2D1Cv8ArZDuGpcnoO6LUZC+ZEk7agC7AiFWp+ShasCV9zl87l2argOpOAur1SWp5pygakY7+06KnNOjnvQZKeeYaw/6MV2gTRisC08jOEiwB9NvQDKwjLQFPubUMUvoCy87Ue5I29wPj/iUI6w6VyMxXcbbAulsYM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787539397; c=relaxed/simple; bh=HlelczrZse4itqbntkw1AflaRc+t0N11SCs7ovp7nzw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=srUtvRt69ZIBFqwShB3kGlM93iugUkGKbpaQWmg1QwjL0mj8BIDfIVNc5v1zgpmBn0num3ZbhFs3wfGWx/bYE+cHbEh7jbWYOj10zCPNO3xCpb8QnKBtPU7uDv8btrTERWQvQyC61TEDtSC3cj/YwYUBsHNXI7DPScrahM0NfKw= 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; Mon, 24 Aug 2026 10:42:30 +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; Mon, 24 Aug 2026 10:42:30 +0800 From: Ryan Chen Date: Mon, 24 Aug 2026 10:42:35 +0800 Subject: [PATCH v2 8/8] 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: <20260824-edac-v2-8-c8d8bb693586@aspeedtech.com> References: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> In-Reply-To: <20260824-edac-v2-0-c8d8bb693586@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1787539349; l=11489; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=HlelczrZse4itqbntkw1AflaRc+t0N11SCs7ovp7nzw=; b=LNccIVppdqVKTXHSnMaJ1c1YboG4AOpHa0LIPmIpA2GiOjZIZunNiFehQVzos/URnOHZsUYH8 eArPWn00qQcBrxCePZvWxFGHZM47cHj6RRS5u/FpeMzUsGrFDquD4RD 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 devm_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 --- Changes in v2: - Return IRQ_NONE from ast2700_dramc_isr() when no ECC interrupt status bit is set. The handler clears only the ECC bits, so unconditionally claiming the interrupt could livelock the level-triggered line if another DRAMC source was left unmasked. - Add AST2700_INT_ECC for the pair of ECC interrupt bits, now used by both the handler and the enable path. - Expand the enable/disable ternary in ast2700_set_irq() into if/else, matching the shape of aspeed_set_irq(). - Pass struct aspeed_edac to the set_irq() hooks instead of struct mem_ctl_info, which the callees only used to reach pvt_info. --- drivers/edac/aspeed_edac.c | 161 +++++++++++++++++++++++++++++++++++++----= ---- 1 file changed, 135 insertions(+), 26 deletions(-) diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index e88d9d2646de..88b7f5ff3727 100644 --- a/drivers/edac/aspeed_edac.c +++ b/drivers/edac/aspeed_edac.c @@ -32,6 +32,23 @@ #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_INT_ECC (AST2700_INT_ECC_RECOVERABLE | \ + AST2700_INT_ECC_UNRECOVERABLE) +#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; + struct aspeed_edac_chip { unsigned int conf_reg; u32 conf_ecc; @@ -40,6 +57,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 aspeed_edac *priv, bool enable); }; =20 struct aspeed_edac { @@ -50,26 +69,34 @@ struct aspeed_edac { int irq; }; =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; @@ -82,32 +109,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", ""); } @@ -166,8 +195,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"); @@ -180,6 +209,54 @@ 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); + if (!(int_sts & AST2700_INT_ECC)) + return IRQ_NONE; + + 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, 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 aspeed_edac *priv, bool enable) { u32 val; @@ -197,6 +274,22 @@ static void aspeed_set_irq(struct aspeed_edac *priv, b= ool enable) aspeed_mcr_irq_update_exit(priv); } =20 +static void ast2700_set_irq(struct aspeed_edac *priv, bool enable) +{ + u32 val; + + guard(raw_spinlock_irqsave)(&priv->lock); + + /* interrupts are enabled by clearing their mask bits */ + val =3D readl(priv->regs + AST2700_INT_MASK); + if (enable) + val &=3D ~AST2700_INT_ECC; + else + val |=3D AST2700_INT_ECC; + + writel(val, priv->regs + AST2700_INT_MASK); +} + static int config_irq(struct mem_ctl_info *mci, struct platform_device *pd= ev) { struct aspeed_edac *priv =3D mci->pvt_info; @@ -209,7 +302,7 @@ static int config_irq(struct mem_ctl_info *mci, struct = 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; @@ -217,7 +310,7 @@ static int config_irq(struct mem_ctl_info *mci, struct = platform_device *pdev) priv->irq =3D irq; =20 /* enable interrupts */ - aspeed_set_irq(priv, true); + priv->chip->set_irq(priv, true); =20 return 0; } @@ -365,7 +458,7 @@ static void aspeed_remove(struct platform_device *pdev) struct aspeed_edac *priv =3D mci->pvt_info; =20 /* disable interrupts */ - aspeed_set_irq(priv, false); + priv->chip->set_irq(priv, false); =20 devm_free_irq(&pdev->dev, priv->irq, mci); =20 @@ -382,6 +475,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)= . */ @@ -391,12 +486,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