From nobody Fri Sep 25 06:03:14 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 A5D483D9549; Wed, 16 Sep 2026 05:30:44 +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=1789536647; cv=none; b=HW+pp6Fni1PEnZN9gDQYZMI7KhdyiwUgRVNvkQ8ver395naUqJGyBmeVbVNGi3EReu/mqol/A5dndXdPUouSZ2TpGnELsZFKnMctnoAfniNiiNYnmxjMP1L9bnoExwnpiGKGHQJBOvNwzwRSdBHevaxJ5dS6HZYKZGrFffG2sBU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789536647; c=relaxed/simple; bh=/rj5B9ojWffAdYkp2s4TIp84wfNqe/zSqKMEbEuHOX4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=vEIUZodl/ot5KxOR5iyJC42YIdtUpn+ydOIMvGaqA9Pmu9cj9/Dafgu2zbJffOy970kusaW7eWXub5tHGmcXOeM6S+dDdGss6Vzv2atzQTHwkxI2IfJsZNZ2eGetJo3AT0vpDYDluG1k4UgrNZWBRXEfA5vm3wbVI4R8fxdEOqc= 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, 16 Sep 2026 13:30:39 +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, 16 Sep 2026 13:30:39 +0800 From: Ryan Chen Date: Wed, 16 Sep 2026 13:30:38 +0800 Subject: [PATCH v3 1/9] 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: <20260916-edac-v3-1-23e9ed2fbdf9@aspeedtech.com> References: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> In-Reply-To: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Borislav Petkov , Ryan Chen , Krzysztof Kozlowski X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1789536639; l=1457; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=/rj5B9ojWffAdYkp2s4TIp84wfNqe/zSqKMEbEuHOX4=; b=GYkP/XZWtwiUUJWsJIWzmy8RkaKbHmY9uylzU6cltcIxAENQV5d84N21V4EbKHLNnveLuQxnp JAis4C6OwSAAyrM6rN+wWlRAxdB+67ehvhBV+Ut3MA6dpFsW7x33/pP 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 Fri Sep 25 06:03:14 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 10B113C1966; Wed, 16 Sep 2026 05:30:48 +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=1789536652; cv=none; b=Vvvh42/qsyWjbcIXjsRsuoAKg3in2RDFMiRy6eo9C45Z5S8MA9X7AisGCcYKH7bWDijqgyGU1TeGQUDy6ySKdAqS+WZmcPgScRBgnMIPkbMO8K7XP4aqTlpCWTgd1ly8caYg+W5hsasaFCHX9yOrcmVyEOiMoWqqvRcHfde+x7Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789536652; c=relaxed/simple; bh=HAKuO93Qq40zQKSxoIqd6AgYapN0t/tUgR+0VfcG6gw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=tJr8xsE/sszg+vMulOi8pIWnO3euzCfthJw/4jgDr4rl2s2ZX/OY7pcXK0vV0M8Tmp21zwJ6zr8w5fc+iVrjkVdi7GRpznNpOdGYmczrRzLDGj+yGOv5fzDUMcgVxa13Co/rLP/cdsRL0D0zXjHmgUBG6371iHpuzXZzkUfUwKM= 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, 16 Sep 2026 13:30:39 +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, 16 Sep 2026 13:30:39 +0800 From: Ryan Chen Date: Wed, 16 Sep 2026 13:30:39 +0800 Subject: [PATCH v3 2/9] 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: <20260916-edac-v3-2-23e9ed2fbdf9@aspeedtech.com> References: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> In-Reply-To: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Borislav Petkov , Ryan Chen , X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1789536639; l=1310; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=HAKuO93Qq40zQKSxoIqd6AgYapN0t/tUgR+0VfcG6gw=; b=k8peKwOmXOBFuHMQSK0ghXR+FYW/m7H/6AI2VzTrrO1sXHEl+b9KuvUGFHf/KFg4o9x+4jo3D tAkcYH1/ijECed9CA9sU6/Zj//DxFYpe7ddb6IbXhi2e+4P31sHPnu7 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 Fri Sep 25 06:03:14 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 8957C3E3170; Wed, 16 Sep 2026 05:30:52 +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=1789536655; cv=none; b=rOPS0lUHXpBX5itNnmJx74sY4rQif/WOxiOIWKchcsteEoQ/NTWaQRXXNy1keDll0np1HAghSRvN0ZIdNXPaQNUJRZoVYkAZSYQnidbOY6SNTdbAgVBfdvIj3uSZ0l+jNHCGaJUJiIFDmR21alJBUUh2vfPQaEz0+6c840DbSBY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789536655; c=relaxed/simple; bh=RlxiTc6ufbAPp2W9fkd50aRtYznC++m4JM40pI/YrqE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=Aeg1p9T7RCJYQkKi/zeR0u8gDSmoQz7BTBiVUK1SI5JOUrianSGrQ/6Tn+YHVHF8FnQOGUg2hdcwO+2kt3pqAMCdxplZnVoTdfSQb6Ykmc15Y2DxYH3q0jzMrE9n+Gohy2SuL/venOVZcxQHO+MZ0Ef1qO261xRDRKOoD6LgYYc= 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, 16 Sep 2026 13:30:39 +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, 16 Sep 2026 13:30:39 +0800 From: Ryan Chen Date: Wed, 16 Sep 2026 13:30:40 +0800 Subject: [PATCH v3 3/9] 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: <20260916-edac-v3-3-23e9ed2fbdf9@aspeedtech.com> References: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> In-Reply-To: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Borislav Petkov , Ryan Chen , X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1789536639; l=1363; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=RlxiTc6ufbAPp2W9fkd50aRtYznC++m4JM40pI/YrqE=; b=rnOoVpqXeg5E96TKBNdbFXJ2JZ79qG1hfXFi0b0vcir9V9JIJxZifMy1qJVZ6I5OzuSOUhYoP bBwdF7LEZ6lDxjgnPc5txFFuwkbfra70zaUq4Ac7WpE5/lSvoXssau1 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 Fri Sep 25 06:03:14 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 ECAFB3E3177; Wed, 16 Sep 2026 05:30: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=1789536658; cv=none; b=o//aoDj7ygoDPnY2imi8i9b6Rb4StgaJ3QO3zg1KGOr73u1dQkxA1JkcTxj2XvX/fbYNHe0w/2px9+cnAUfJRGoeeb5k/dZRBwQ3Y2usOho16rHLw6982e9cN+CQq6eD6w/jGzy0czadpqWk33Ko/85QXEk5/TkGyA2VIQDamwM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789536658; c=relaxed/simple; bh=FhGqCgC1qcv7kHcNVhXGaiiu15ASlL/TX82CfVZ7NtU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=DpBCDWVVFZBqokgrumlSSZBmV8XotPZCVz13jFTP5fd3FwoegcK6P6pO2dbxApx+3ePXYiDcdTT+wi5/ntWxn02um5iWPD+RPLU4sWZeH4P9IPvdfYHKJEd71y2MOy2hZROTPW5hFcHtxGefTqI1CJUx/eFAjuFQ6Rrh1w+FZLc= 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, 16 Sep 2026 13:30:39 +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, 16 Sep 2026 13:30:39 +0800 From: Ryan Chen Date: Wed, 16 Sep 2026 13:30:41 +0800 Subject: [PATCH v3 4/9] 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: <20260916-edac-v3-4-23e9ed2fbdf9@aspeedtech.com> References: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> In-Reply-To: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Borislav Petkov , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1789536639; l=4626; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=FhGqCgC1qcv7kHcNVhXGaiiu15ASlL/TX82CfVZ7NtU=; b=4Xo/osRwy3I1OWVSj7PlvVFVWiFO/6HzMd9tjo5CsuPIo9o+HF/zF65MGUc0r7J0dG6b2eyUE heUPiGa8yleCJirwrK3EqH2Gt5soyvuUWC0ykGbYg4AzvUp5izGwXke 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 Fri Sep 25 06:03:14 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 C7DFF3DB658; Wed, 16 Sep 2026 05:30:58 +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=1789536660; cv=none; b=eLXlcuAIw1XhDck1CwNEVisvUwcQki5C8tSchQUCU8GkvwX7zWmot8bT8sUiZJqp2hZAk95BFSrWCIk3GzSqxqTYUjzbvnJgBj4A+4c0wIL4MpEIWf1+Lbs8aWNM2/pegGlVaI/jDoEiBYZsQtoFjNYCJo+1d5wgXIXdEIY8ZRg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789536660; 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=Eq1nmo97t1hHR28Qwqxs8rxYuMw4SSk2uaq8A/dSlrTDd7+RrxdaHxzZ5JRNmFRs+K1Gx0UpMNkHWr9iJFz9tM00F1pZchA2aoj8ACDVRCeExNyB8mIT3Ma1pqhfF08V0L5tS8PaQ4WQCIKrkJ5Bi5JjDDoNRAX2G0kjWvNEt8Q= 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, 16 Sep 2026 13:30:40 +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, 16 Sep 2026 13:30:40 +0800 From: Ryan Chen Date: Wed, 16 Sep 2026 13:30:42 +0800 Subject: [PATCH v3 5/9] 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: <20260916-edac-v3-5-23e9ed2fbdf9@aspeedtech.com> References: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> In-Reply-To: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Borislav Petkov , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1789536639; l=1565; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=7kVBSW+iJq7iG0C/gaN+W7UHNDDfaeN9Q8zh7Gh7SjI=; b=XrA//NurIAmNGbHwpLeqb4LcWk7DgfaoKTQgijaRrS0YYzBv3TbSkIzes5dnSvfOHLbcmVPzr 3xlUELSmHDZCeV6Pg12WCsojIIrJ7MgjG4VlbtGpSRjalCO8mP/z2M+ 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 Fri Sep 25 06:03:14 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 064323BD638; Wed, 16 Sep 2026 05:31: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=1789536663; cv=none; b=WlJyZjEFkAQ8MeUExLsLTz1PPh80CL5r0xZ9LE65PiQqIVLywUy+QewiEpeVNnX10aPwEHQO0gyWDNOsIrLFBTfl+k2zWGPQfkHk//hZu/NZs5l8qd9c+nGpwYZkJvjgR+Veh/sqy07za0EfaBERA50Uf+a09aiyFn89qzjJUS8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789536663; c=relaxed/simple; bh=4vIvjd+wMbaMeekyBwMSPu6kmxJS0RjmBqXcVzKV5HY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=kuZEpRvZoHwAS7ld6M5JXSNVRz4RiKAWrdrhvJ0JdabjISbgpRT76H+TGOjX01yIzWBtsVnr5tNk32a6x5pyrD0tVzSP+2LD/22wDtODEHmb5FEqvVBmi/ToMl72uBGbzo6n7E/ihbbJXaJ8QMgz7Joxn355cfqJNzxAX62LSM4= 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, 16 Sep 2026 13:30:40 +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, 16 Sep 2026 13:30:40 +0800 From: Ryan Chen Date: Wed, 16 Sep 2026 13:30:43 +0800 Subject: [PATCH v3 6/9] 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: <20260916-edac-v3-6-23e9ed2fbdf9@aspeedtech.com> References: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> In-Reply-To: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Borislav Petkov , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1789536639; l=9581; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=4vIvjd+wMbaMeekyBwMSPu6kmxJS0RjmBqXcVzKV5HY=; b=9Hmeyg21uF4KlKRh+vk36hi17Q3CebFa2W4LywPCIVRIWpyxOZPedhAHkXo/cDbnqhojtEA7Q 4CAzKWRqSJxBHHLyi35TooNpMesczAUyWpU3qztcRrdyyqYuscd0SZW 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. - 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 Fri Sep 25 06:03:14 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 43D133E7653; Wed, 16 Sep 2026 05:31:03 +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=1789536665; cv=none; b=hfkz37s+KgaQU4HjvBg8/ZOUcESCEX8yVd66b1XKuW16A7nqI7HEWQUMebmkfYcpeZXicl5Q3FwTjxE7bDoqpiXwqQsIiiD6Tpw96OFhAaJLchuSdEW6z+QgBmCuTUKoZHW361p1+H7Vcx6a7K78qxzTB8WmQmQi+nOQ4vA+/tc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789536665; c=relaxed/simple; bh=9GcVj2fdxxCTsPwP9/o+MgofFZHMPAKymQnxcoTT3v0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=tMK3MbsMSECOIdiEHw4zQ7u+Fg7EqswiuBTse0M0KlYuO4LijhD8o76Mpv8XvPwhge37KSplPfH56RDRwafX3JfAj8xb3ccya8bYuOvWoBdyiPu5CSrc7cQOWqdnaKf5s01ku7/STFa/80dCqaSavnxnTHwfNP2jUgTKxD2e1NA= 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, 16 Sep 2026 13:30:40 +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, 16 Sep 2026 13:30:40 +0800 From: Ryan Chen Date: Wed, 16 Sep 2026 13:30:44 +0800 Subject: [PATCH v3 7/9] 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: <20260916-edac-v3-7-23e9ed2fbdf9@aspeedtech.com> References: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> In-Reply-To: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Borislav Petkov , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1789536639; l=12030; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=9GcVj2fdxxCTsPwP9/o+MgofFZHMPAKymQnxcoTT3v0=; b=S4C32eLjvBqBvrQHBRKPboO0xtuqIJM+z+c/PwsispYoHuUaLCksGnvOZjTikhbw17zlJqnn5 rloOduC/fnpB5xEdmqV8F5yJ3NhhCLuXerM9w3CpTvdZmT5uiFpL9QT 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 Fri Sep 25 06:03:14 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 658163E7BC4; Wed, 16 Sep 2026 05:31:05 +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=1789536667; cv=none; b=uhnYbdiXygv84Bny/COlMzGMyWpkbDmnVDx57K3SgFEyaAZK9/BQqd8l9DPuUTGBHQtlKqxM4rNDbZXS6FZ/X64UZlOf86ySQo2xHyd2bieJi16ykvs4S1/u4U+O/QouyoqMj5rocdJO1loASJBCyXEPjxdWA2yIQE+AJxx2Bes= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789536667; c=relaxed/simple; bh=pVNIBo5jD1DayuSl+51+0RH3NZwaV/1iDB0EjZVcUqc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=qEzb6MXmh3ljf4EyLrSWjNNnQ8mUcQI2yF0ey50Vw+A4UfqvA/lnbGOacEEsLm4eQPOcex3HH/LxOoHo0YXzWTbW+SaE0g8QTH3nkeWE0zU6At2eNS6HjlHlyPEW2AeyiXj6RdGAHIvyLGouN31aunOXleiyA+iFb0uxIIlAaDg= 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, 16 Sep 2026 13:30:40 +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, 16 Sep 2026 13:30:40 +0800 From: Ryan Chen Date: Wed, 16 Sep 2026 13:30:45 +0800 Subject: [PATCH v3 8/9] 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: <20260916-edac-v3-8-23e9ed2fbdf9@aspeedtech.com> References: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> In-Reply-To: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Borislav Petkov , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1789536639; l=11921; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=pVNIBo5jD1DayuSl+51+0RH3NZwaV/1iDB0EjZVcUqc=; b=wmXL3EQtb/0ZfVOTs/dW/iw5lCXBOjFhhrl+YYBtTTItq1uLV/TxHkIaBHFLmxyoKmu0AZzit OJcxgYL8ASIDN9NpQChUA+kJyDUawklDMz8m1w2TI3T5PxcFLzU5/EP 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 v3: - Write the AST2700 interrupt mask register outright rather than read-modify-write: mask every source and unmask only the ECC ones. The driver owns the line exclusively, so it should establish the mask itself instead of deriving it from the register's contents. - Mask the interrupts before requesting them rather than after, so the mask is established before the handler can run. 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..6fe6314cda72 100644 --- a/drivers/edac/aspeed_edac.c +++ b/drivers/edac/aspeed_edac.c @@ -32,6 +32,24 @@ #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_INT_ALL GENMASK(16, 0) +#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 +58,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 +70,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 +110,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 +196,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 +210,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 +275,19 @@ 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 =3D AST2700_INT_ALL; + + guard(raw_spinlock_irqsave)(&priv->lock); + + /* interrupts are enabled by clearing their mask bits */ + if (enable) + 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 +300,9 @@ 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, + priv->chip->set_irq(priv, false); + + 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 From nobody Fri Sep 25 06:03:14 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 DF3D23E95AE; Wed, 16 Sep 2026 05:31:07 +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=1789536669; cv=none; b=N2MPGMa3RDK/Wl7MndtXq9D34G/59il6aiQXe8e/JCnJesxk1A8dOphX6py4AZGcUVxA48DW2O3iXJg8kzTdi2EabftIG6Kd4zIc4VjS0BUUEsgsdDpzPNbgM4af6Jl1cG18bsGRRDpAkS7USAuyCHUIXwXxa+NllGm4zVOCZVU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789536669; c=relaxed/simple; bh=w8T0hgmEG8Qfrn667ljaWu8sAzWzj8Ca/Lo8h8/IWDA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=cah8XKD68xsNwQ0AQ9RAjf6EhccRbt8368Hp/Mu2kwzEQlwuZd95v2AAJV0yl9+GKlt0FOLAYD9BxLzibWPeCldQQuo/tCHvWjpaRR9bDJfsIKxfSEHTBuwRb83708zr4BhgHBOFrpM8kM7i+Rmyh3Y1jzZ02cPws11GGmOfwDA= 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, 16 Sep 2026 13:30:40 +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, 16 Sep 2026 13:30:40 +0800 From: Ryan Chen Date: Wed, 16 Sep 2026 13:30:46 +0800 Subject: [PATCH v3 9/9] MAINTAINERS: Add a reviewer for the Aspeed EDAC driver 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: <20260916-edac-v3-9-23e9ed2fbdf9@aspeedtech.com> References: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> In-Reply-To: <20260916-edac-v3-0-23e9ed2fbdf9@aspeedtech.com> To: Stefan Schaeckeler , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Borislav Petkov , Tony Luck CC: , , , , , Borislav Petkov , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1789536639; l=629; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=w8T0hgmEG8Qfrn667ljaWu8sAzWzj8Ca/Lo8h8/IWDA=; b=ldCLmciBR1ODuMPQ0Ij0lrTVtU6qNgoqih4XP2N1MgHt2Z5RuqAs50ybQ1LoKs48sOV2BCf4c rYcw5j0WwE7D0tQx8p8goZeRFFmIv7IA4rX6nYccL7snKNwjjMAmZCI X-Developer-Key: i=ryan_chen@aspeedtech.com; a=ed25519; pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc= Add myself as a reviewer to be copied on changes to the driver. Signed-off-by: Ryan Chen --- Changes in v3: - New patch. --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index d413dac5c8b3..5bd0109f0f1e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9284,6 +9284,7 @@ F: drivers/edac/armada_xp_* =20 EDAC-AST2500 M: Stefan Schaeckeler +R: Ryan Chen S: Supported F: Documentation/devicetree/bindings/edac/aspeed,ast2400-sdram-edac.yaml F: drivers/edac/aspeed_edac.c --=20 2.34.1