From patchwork Mon Aug 22 19:19:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 599299 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7D63AC32789 for ; Mon, 22 Aug 2022 19:21:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238506AbiHVTVl (ORCPT ); Mon, 22 Aug 2022 15:21:41 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48694 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238705AbiHVTU6 (ORCPT ); Mon, 22 Aug 2022 15:20:58 -0400 Received: from mail.baikalelectronics.com (mail.baikalelectronics.com [87.245.175.230]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 55AF8FD17; Mon, 22 Aug 2022 12:20:17 -0700 (PDT) Received: from mail (mail.baikal.int [192.168.51.25]) by mail.baikalelectronics.com (Postfix) with ESMTP id 9495DDA3; Mon, 22 Aug 2022 22:23:28 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.com 9495DDA3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1661196208; bh=IGQU0ypa1PF4qcU+f0QqtuUCDe4v6kRCgIcvDETuAXs=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=FqpLIxXjIw/4SRaZkPtgL+Rx0x7c1NPfQO0dYCv7PIy/VP8QVJ9dKvzqjR3o5fste k+aW6TbAgH7X1gFp4kf8R7fHtVNLLm+D49fZejVkz1ORUHRrkyegJa5kNVLI3S+atc EHMohewPQxKEIFwHaIQouweD4S4BFLgCtuA6Emis= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 22 Aug 2022 22:20:14 +0300 From: Serge Semin To: Michal Simek , Borislav Petkov , Mauro Carvalho Chehab , Tony Luck , Krzysztof Kozlowski , Rob Herring , Manish Narani CC: Serge Semin , Serge Semin , Alexey Malahov , Michail Ivanov , Pavel Parkhomenko , Punnaiah Choudary Kalluri , Dinh Nguyen , James Morse , Robert Richter , Krzysztof Kozlowski , , , , , Krzysztof Kozlowski Subject: [PATCH 01/13] dt-bindings: memory: snps: Extend schema with IRQs/resets/clocks props Date: Mon, 22 Aug 2022 22:19:44 +0300 Message-ID: <20220822191957.28546-2-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> References: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org First of all the DW uMCTL2 DDRC IP-core supports the individual IRQ lines for each standard event: ECC Corrected Error, ECC Uncorrected Error, ECC Address Protection, Scrubber-Done signal, DFI Parity/CRC Error. It's possible that the platform engineers merge them up in the IRQ controller level. So let's add both configuration support to the DT-schema. Secondly each IP-core interface is supplied with a clock source like APB reference clock, AXI-ports clock, main DDRC core reference clock and Scrubber low-power clock. In addition to that each clock domain can have a dedicated reset signal. Let's add the properties for at least the denoted clock sources and the corresponding reset controls. Signed-off-by: Serge Semin --- .../snps,dw-umctl2-ddrc.yaml | 65 +++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml b/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml index 787d91d64eee..8db92210cfe1 100644 --- a/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/snps,dw-umctl2-ddrc.yaml @@ -13,13 +13,13 @@ maintainers: description: | Synopsys DesignWare Enhanced uMCTL2 DDR Memory Controller is cappable of - working with DDR devices up to (LP)DDR4 protocol. It can be equipped + working with DDR devices upporting to (LP)DDR4 protocol. It can be equipped with SEC/DEC ECC feature if DRAM data bus width is either 16-bits or 32-bits or 64-bits wide. - The ZynqMP DDR controller is based on the DW uMCTL2 v2.40a controller. - It has an optional SEC/DEC ECC support in 64-bit and 32-bit bus width - configurations. + For instance the ZynqMP DDR controller is based on the DW uMCTL2 v2.40a + controller. It has an optional SEC/DEC ECC support in 64-bit and 32-bit + bus width configurations. properties: compatible: @@ -28,11 +28,55 @@ properties: - xlnx,zynqmp-ddrc-2.40a interrupts: - maxItems: 1 + description: + DW uMCTL2 DDRC IP-core provides individual IRQ signal for each event":" + ECC Corrected Error, ECC Uncorrected Error, ECC Address Protection, + Scrubber-Done signal, DFI Parity/CRC Error. Some platforms may have the + signals merged before they reach the IRQ controller or have some of them + absent in case if the corresponding feature is unavailable/disabled. + minItems: 1 + maxItems: 5 + + interrupt-names: + minItems: 1 + maxItems: 5 + oneOf: + - description: Common ECC CE/UE/Scrubber/DFI Errors IRQ + items: + - const: ecc + - description: Individual ECC CE/UE/Scrubber/DFI Errors IRQs + items: + enum: [ ecc_ce, ecc_ue, ecc_ap, ecc_sbr, dfi_e ] reg: maxItems: 1 + clocks: + description: + A standard set of the clock sources contains CSRs bus clock, AXI-ports + reference clock, DDRC core clock, Scrubber standalone clock + (synchronous to the DDRC clock). + minItems: 1 + maxItems: 4 + + clock-names: + minItems: 1 + maxItems: 4 + items: + enum: [ pclk, aclk, core, sbr ] + + resets: + description: + Each clock domain can have separate reset signal. + minItems: 1 + maxItems: 4 + + reset-names: + minItems: 1 + maxItems: 4 + items: + enum: [ prst, arst, core, sbr ] + required: - compatible - reg @@ -48,4 +92,15 @@ examples: interrupt-parent = <&gic>; interrupts = <0 112 4>; }; + - | + memory-controller@fd070000 { + compatible = "snps,ddrc-3.80a"; + reg = <0x3d400000 0x400000>; + + interrupts = <0 147 4>, <0 148 4>, <0 149 4>, <0 150 4>; + interrupt-names = "ecc_ce", "ecc_ue", "ecc_sbr", "dfi_e"; + + clocks = <&rcu 0>, <&rcu 5>, <&rcu 6>, <&rcu 7>; + clock-names = "pclk", "aclk", "core", "sbr"; + }; ... From patchwork Mon Aug 22 19:19:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 599298 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 076E5C28D13 for ; Mon, 22 Aug 2022 19:21:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238523AbiHVTVo (ORCPT ); Mon, 22 Aug 2022 15:21:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47634 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238699AbiHVTU5 (ORCPT ); Mon, 22 Aug 2022 15:20:57 -0400 Received: from mail.baikalelectronics.com (mail.baikalelectronics.com [87.245.175.230]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 0CFC613D08; Mon, 22 Aug 2022 12:20:21 -0700 (PDT) Received: from mail (mail.baikal.int [192.168.51.25]) by mail.baikalelectronics.com (Postfix) with ESMTP id F3AD4DA5; Mon, 22 Aug 2022 22:23:29 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.com F3AD4DA5 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1661196210; bh=hYISXOPwelQMZEnoI/XHwiZ9SYWjZeM4wKuUzRG7AZg=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=CVqMr9a2i8OyAqPdjse8KdEXMb7CJiqMEPrqqZnHa83BaoCXuPH1Vqw/Pc8Ei87x4 zmAhXhx+IN+58j1KxMEiaJ47jHVCfoMEjwwjo77b5dxMU20QbkNSm19ArqSFu2wS6n /p3p5/1qKZtUVG3102H1WmvwppI32aas0TxXsT44= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 22 Aug 2022 22:20:15 +0300 From: Serge Semin To: Michal Simek , Borislav Petkov , Mauro Carvalho Chehab , Tony Luck , James Morse , Robert Richter CC: Serge Semin , Serge Semin , Alexey Malahov , Michail Ivanov , Pavel Parkhomenko , Punnaiah Choudary Kalluri , Manish Narani , Dinh Nguyen , Rob Herring , Krzysztof Kozlowski , , , , Subject: [PATCH 03/13] EDAC/synopsys: Add multi-ranked memory support Date: Mon, 22 Aug 2022 22:19:46 +0300 Message-ID: <20220822191957.28546-4-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> References: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org DW uMCTL2 DDRC supports multi-rank memory attached to the controller. If so the MSTR.active_ranks field will be set with the populated ranks bitfield. It is permitted to have one, two or four ranks activated at a time [1]. Since we now have the number of ranks determined in the controller configuration detection procedure, it can be easily used for accordingly extending the MCI chip-select layer. In case of the ECC errors the affected rank will be read from the CE/UE address CSRs [2]. Note we need to drop the ranks from the total memory size calculation since multiple ranks are taken into account by means of the layer[0] definition. [1] DesignWare® Cores Enhanced Universal DDR Memory Controller (uMCTL2) Databook, Version 3.91a, October 2020, p.739 [2] DesignWare® Cores Enhanced Universal DDR Memory Controller (uMCTL2) Databook, Version 3.91a, October 2020, p.821, p.832 Signed-off-by: Serge Semin --- drivers/edac/synopsys_edac.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index 90b57986a9b5..872ad9a164a7 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -23,9 +23,6 @@ #include "edac_module.h" -/* Number of cs_rows needed per memory controller */ -#define SNPS_EDAC_NR_CSROWS 1 - /* Number of channels per memory controller */ #define SNPS_EDAC_NR_CHANS 1 @@ -795,7 +792,7 @@ static void snps_handle_error(struct mem_ctl_info *mci, struct snps_ecc_status * edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, p->ce_cnt, PHYS_PFN(sys), offset_in_page(sys), - pinf->syndrome, 0, 0, -1, + pinf->syndrome, pinf->sdram.rank, 0, -1, priv->message, ""); } @@ -812,7 +809,7 @@ static void snps_handle_error(struct mem_ctl_info *mci, struct snps_ecc_status * edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, p->ue_cnt, PHYS_PFN(sys), offset_in_page(sys), - pinf->syndrome, 0, 0, -1, + pinf->syndrome, pinf->sdram.rank, 0, -1, priv->message, ""); } @@ -1411,10 +1408,7 @@ static u64 snps_get_sdram_size(struct snps_edac_priv *priv) size++; } - for (i = 0; i < DDR_MAX_RANK_WIDTH; i++) { - if (map->rank[i] != DDR_ADDRMAP_UNUSED) - size++; - } + /* Skip the ranks since the multi-rankness is determined by layer[0] */ return 1ULL << (size + priv->info.dq_width); } @@ -1468,7 +1462,7 @@ static struct mem_ctl_info *snps_mc_create(struct snps_edac_priv *priv) struct mem_ctl_info *mci; layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; - layers[0].size = SNPS_EDAC_NR_CSROWS; + layers[0].size = priv->info.ranks; layers[0].is_virt_csrow = true; layers[1].type = EDAC_MC_LAYER_CHANNEL; layers[1].size = SNPS_EDAC_NR_CHANS; From patchwork Mon Aug 22 19:19:47 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 599300 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C6BD2C3F6B0 for ; Mon, 22 Aug 2022 19:21:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238276AbiHVTV1 (ORCPT ); Mon, 22 Aug 2022 15:21:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47624 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238691AbiHVTU4 (ORCPT ); Mon, 22 Aug 2022 15:20:56 -0400 Received: from mail.baikalelectronics.com (mail.baikalelectronics.com [87.245.175.230]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 0732913D14; Mon, 22 Aug 2022 12:20:23 -0700 (PDT) Received: from mail (mail.baikal.int [192.168.51.25]) by mail.baikalelectronics.com (Postfix) with ESMTP id 9D50CDA6; Mon, 22 Aug 2022 22:23:30 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.com 9D50CDA6 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1661196210; bh=LzgKLA2Es3MVjHYo8FGWSLKo8lr8OalQ3g2hasfSC+k=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=YlzCnLA1UyogqEyTz5GoGP8fEomf1wvjDDnWhNAzjskiCrSVudf8ovRgwV2EyI87t 9aa0j/EtmY5KKQFwDrXEiHh9yV26f2zsbOnLUzg6wARVRpv6v3BVDt6gPE+BAysizW ISrqu6Y/F6sElTkPQOATQU49INUv6to2bCKe0HfE= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 22 Aug 2022 22:20:16 +0300 From: Serge Semin To: Michal Simek , Borislav Petkov , Mauro Carvalho Chehab , Tony Luck , James Morse , Robert Richter CC: Serge Semin , Serge Semin , Alexey Malahov , Michail Ivanov , Pavel Parkhomenko , Punnaiah Choudary Kalluri , Manish Narani , Dinh Nguyen , Rob Herring , Krzysztof Kozlowski , , , , Subject: [PATCH 04/13] EDAC/synopsys: Add optional ECC Scrub support Date: Mon, 22 Aug 2022 22:19:47 +0300 Message-ID: <20220822191957.28546-5-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> References: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org DW uMCTL2 DDRC ECC has a so called ECC Scrub feature in case if an single-bit error is detected. The scrub is executed as a new RMW operation to the location that resulted in a single-bit error thus fixing the ECC code preserved in the SDRAM. But that feature not only optional, but also runtime switchable. So there can be platforms with DW uMCTL2 DDRC not supporting hardware-base scrub. In those cases the single-bit errors will still be detected but won't be fixed until the next SDRAM write commands to the erroneous location. Since the ECC Scrub feature availability is detectable by means of the ECCCFG0.dis_scrub flag state we can use it to tune the MCI core up so one would automatically execute the platform-specific the platform-specific scrubbing to the affected SDRAM location. It's now possible to be done since the DW uMCTL2 DDRC driver supports the actual system address reported to the MCI core. The only thing left to do is to auto-detect the ECC Scrub feature availability and set the mem_ctl.info.scrub_mode mode with SCRUB_SW_SRC if the feature is unavailable. The rest will be done by the MCI core when the single-bit errors happen. Signed-off-by: Serge Semin --- drivers/edac/synopsys_edac.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index 872ad9a164a7..2b8de7e8fae1 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -32,6 +32,7 @@ #define SNPS_EDAC_MOD_VER "1" /* DDR capabilities */ +#define SNPS_CAP_ECC_SCRUB BIT(0) #define SNPS_CAP_ZYNQMP BIT(31) /* Synopsys uMCTL2 DDR controller registers that are relevant to ECC */ @@ -114,6 +115,7 @@ #define DDR_MSTR_MEM_LPDDR4 BIT(5) /* ECC CFG0 register definitions */ +#define ECC_CFG0_DIS_SCRUB BIT(4) #define ECC_CFG0_MODE_MASK GENMASK(2, 0) /* ECC status register definitions */ @@ -1008,6 +1010,10 @@ static int snps_get_ddrc_info(struct snps_edac_priv *priv) return -ENXIO; } + /* Assume HW-src scrub is always available if it isn't disabled */ + if (!(regval & ECC_CFG0_DIS_SCRUB)) + priv->info.caps |= SNPS_CAP_ECC_SCRUB; + /* Auto-detect the basic HIF/SDRAM bus parameters */ regval = readl(priv->baseaddr + DDR_MSTR_OFST); @@ -1484,8 +1490,14 @@ static struct mem_ctl_info *snps_mc_create(struct snps_edac_priv *priv) MEM_FLAG_DDR3 | MEM_FLAG_LPDDR3 | MEM_FLAG_DDR4 | MEM_FLAG_LPDDR4; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; - mci->scrub_cap = SCRUB_FLAG_HW_SRC; - mci->scrub_mode = SCRUB_NONE; + + if (priv->info.caps & SNPS_CAP_ECC_SCRUB) { + mci->scrub_mode = SCRUB_HW_SRC; + mci->scrub_cap = SCRUB_FLAG_HW_SRC; + } else { + mci->scrub_mode = SCRUB_SW_SRC; + mci->scrub_cap = SCRUB_FLAG_SW_SRC; + } mci->edac_cap = EDAC_FLAG_SECDED; mci->ctl_name = "snps_umctl2_ddrc"; @@ -1578,6 +1590,8 @@ static int snps_ddrc_info_show(struct seq_file *s, void *data) seq_puts(s, "Caps:"); if (priv->info.caps) { + if (priv->info.caps & SNPS_CAP_ECC_SCRUB) + seq_puts(s, " +Scrub"); if (priv->info.caps & SNPS_CAP_ZYNQMP) seq_puts(s, " +ZynqMP"); } else { From patchwork Mon Aug 22 19:19:50 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 599297 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1106AC28D13 for ; Mon, 22 Aug 2022 19:21:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238535AbiHVTVs (ORCPT ); Mon, 22 Aug 2022 15:21:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46344 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238706AbiHVTU6 (ORCPT ); Mon, 22 Aug 2022 15:20:58 -0400 Received: from mail.baikalelectronics.com (mail.baikalelectronics.com [87.245.175.230]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id ADB2013D45; Mon, 22 Aug 2022 12:20:28 -0700 (PDT) Received: from mail (mail.baikal.int [192.168.51.25]) by mail.baikalelectronics.com (Postfix) with ESMTP id 8E82FDA9; Mon, 22 Aug 2022 22:23:34 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.com 8E82FDA9 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1661196214; bh=hvldx64g2L6X+9yrJcqAyNeVPAaiuKHUOlWSlc8zeO8=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=WeZ2BLvqXNo+AAaS9wfvHlBXhL/wXkZbhSJmII9yFzjov6etCql5ZVFpn3RuuSGT6 NrxbCP9tUBlLyRql4JPWTOIBGx3ey1A9QxTSoVb6T9LZX2tvZmXeNpo+7efoEscA7K 66k/JH38zRBdJhAB8Sau+lpBRDvVMJ6+qy1mbFTk= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 22 Aug 2022 22:20:20 +0300 From: Serge Semin To: Michal Simek , Borislav Petkov , Mauro Carvalho Chehab , Tony Luck , James Morse , Robert Richter CC: Serge Semin , Serge Semin , Alexey Malahov , Michail Ivanov , Pavel Parkhomenko , Punnaiah Choudary Kalluri , Manish Narani , Dinh Nguyen , Rob Herring , Krzysztof Kozlowski , , , , Subject: [PATCH 07/13] EDAC/synopsys: Split up ECC UE/CE IRQs handler Date: Mon, 22 Aug 2022 22:19:50 +0300 Message-ID: <20220822191957.28546-8-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> References: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org DW uMCTL2 DDRC IP-core doesn't have common IRQ line. Instead it provides individual IRQ output signals for each controller event like: corrected error, uncorrected error, DFI parity error, address protection, scrubber done, and so on. So the common IRQ handler implemented in the Synopsys EDAC driver isn't device-specific but is a particular platform specific. Obviously it won't be suitable for the generic devices which are added to the platforms with the original individual IRQs as it has happened in our case. So let's split up the common IRQ handler into two ones handling ECC corrected and uncorrected errors. It won't be that hard since both sub-methods it calls are already logically divided into two CE/UE parts. What we need to do is to move these parts into the dedicated methods and redefine the local variables a bit. The new methods will be simply called from the common IRQs handler if one is utilized on the particular platform. Otherwise each new IRQ handler will be called on particular interrupt request (the IRQ handlers registration will be added a bit later). Note we now can discard the snps_ecc_status structure as unneeded since the error data is collected and reported now within a single method. Signed-off-by: Serge Semin --- drivers/edac/synopsys_edac.c | 272 +++++++++++++++++------------------ 1 file changed, 135 insertions(+), 137 deletions(-) diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index 028a9ad70d49..ac0123ff4595 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -143,7 +143,6 @@ #define DDR_QOS_IRQ_STAT_OFST 0x20200 #define DDR_QOSUE_MASK BIT(2) #define DDR_QOSCE_MASK BIT(1) -#define ECC_CE_UE_INTR_MASK (DDR_QOSUE_MASK | DDR_QOSCE_MASK) #define DDR_QOS_IRQ_EN_OFST 0x20208 #define DDR_QOS_IRQ_DB_OFST 0x2020C @@ -372,31 +371,19 @@ struct snps_sdram_addr { /** * struct snps_ecc_error_info - ECC error log information. * @sdram: SDRAM address. + * @ecnt: Number of detected errors. * @bitpos: Bit position. * @data: Data causing the error. * @syndrome: Erroneous data syndrome. */ struct snps_ecc_error_info { struct snps_sdram_addr sdram; + u16 ecnt; u32 bitpos; u64 data; u32 syndrome; }; -/** - * struct snps_ecc_status - ECC status information to report. - * @ce_cnt: Correctable error count. - * @ue_cnt: Uncorrectable error count. - * @ceinfo: Correctable error log information. - * @ueinfo: Uncorrectable error log information. - */ -struct snps_ecc_status { - u32 ce_cnt; - u32 ue_cnt; - struct snps_ecc_error_info ceinfo; - struct snps_ecc_error_info ueinfo; -}; - /** * struct snps_edac_priv - DDR memory controller private data. * @info: DDR controller config info. @@ -406,7 +393,6 @@ struct snps_ecc_status { * @baseaddr: Base address of the DDR controller. * @lock: Concurrent CSRs access lock. * @message: Buffer for framing the event specific info. - * @stat: ECC status information. */ struct snps_edac_priv { struct snps_ddrc_info info; @@ -416,7 +402,6 @@ struct snps_edac_priv { void __iomem *baseaddr; spinlock_t lock; char message[SNPS_EDAC_MSG_SIZE]; - struct snps_ecc_status stat; }; /** @@ -688,130 +673,178 @@ static inline u32 snps_get_bitpos(u32 bitnum, enum snps_dq_width dq_width) } /** - * snps_get_error_info - Get the current ECC error info. - * @priv: DDR memory controller private instance data. + * snps_ce_irq_handler - Corrected error interrupt handler. + * @irq: IRQ number. + * @dev_id: Device ID. * - * Return: one if there is no error otherwise returns zero. + * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise. */ -static int snps_get_error_info(struct snps_edac_priv *priv) +static irqreturn_t snps_ce_irq_handler(int irq, void *dev_id) { - struct snps_ecc_status *p; - u32 regval, clearval; + struct mem_ctl_info *mci = dev_id; + struct snps_edac_priv *priv = mci->pvt_info; + struct snps_ecc_error_info einfo; unsigned long flags; - void __iomem *base; + u32 qosval, regval; + dma_addr_t sys; - base = priv->baseaddr; - p = &priv->stat; + /* Make sure IRQ is caused by a corrected ECC error */ + if (priv->info.caps & SNPS_CAP_ZYNQMP) { + qosval = readl(priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); + if (!(qosval & DDR_QOSCE_MASK)) + return IRQ_NONE; - regval = readl(base + ECC_STAT_OFST); - if (!regval) - return 1; + qosval &= DDR_QOSCE_MASK; + } - p->ceinfo.bitpos = FIELD_GET(ECC_STAT_BITNUM_MASK, regval); + regval = readl(priv->baseaddr + ECC_STAT_OFST); + if (!FIELD_GET(ECC_STAT_CE_MASK, regval)) + return IRQ_NONE; - regval = readl(base + ECC_ERRCNT_OFST); - p->ce_cnt = FIELD_GET(ECC_ERRCNT_CECNT_MASK, regval); - p->ue_cnt = FIELD_GET(ECC_ERRCNT_UECNT_MASK, regval); - if (!p->ce_cnt) - goto ue_err; + /* Read error info like bit position, SDRAM address, data, syndrome */ + einfo.bitpos = FIELD_GET(ECC_STAT_BITNUM_MASK, regval); + einfo.bitpos = snps_get_bitpos(einfo.bitpos, priv->info.dq_width); - p->ceinfo.bitpos = snps_get_bitpos(p->ceinfo.bitpos, priv->info.dq_width); + regval = readl(priv->baseaddr + ECC_ERRCNT_OFST); + einfo.ecnt = FIELD_GET(ECC_ERRCNT_CECNT_MASK, regval); - regval = readl(base + ECC_CEADDR0_OFST); - p->ceinfo.sdram.rank = FIELD_GET(ECC_CEADDR0_RANK_MASK, regval); - p->ceinfo.sdram.row = FIELD_GET(ECC_CEADDR0_ROW_MASK, regval); + regval = readl(priv->baseaddr + ECC_CEADDR0_OFST); + einfo.sdram.rank = FIELD_GET(ECC_CEADDR0_RANK_MASK, regval); + einfo.sdram.row = FIELD_GET(ECC_CEADDR0_ROW_MASK, regval); - regval = readl(base + ECC_CEADDR1_OFST); - p->ceinfo.sdram.bankgrp = FIELD_GET(ECC_CEADDR1_BANKGRP_MASK, regval); - p->ceinfo.sdram.bank = FIELD_GET(ECC_CEADDR1_BANK_MASK, regval); - p->ceinfo.sdram.col = FIELD_GET(ECC_CEADDR1_COL_MASK, regval); + regval = readl(priv->baseaddr + ECC_CEADDR1_OFST); + einfo.sdram.bankgrp = FIELD_GET(ECC_CEADDR1_BANKGRP_MASK, regval); + einfo.sdram.bank = FIELD_GET(ECC_CEADDR1_BANK_MASK, regval); + einfo.sdram.col = FIELD_GET(ECC_CEADDR1_COL_MASK, regval); - p->ceinfo.data = readl(base + ECC_CSYND0_OFST); + einfo.data = readl(priv->baseaddr + ECC_CSYND0_OFST); if (priv->info.dq_width == SNPS_DQ_64) - p->ceinfo.data |= (u64)readl(base + ECC_CSYND1_OFST) << 32; - - p->ceinfo.syndrome = readl(base + ECC_CSYND2_OFST); + einfo.data |= (u64)readl(priv->baseaddr + ECC_CSYND1_OFST) << 32; -ue_err: - if (!p->ue_cnt) - goto out; + einfo.syndrome = readl(priv->baseaddr + ECC_CSYND2_OFST); - regval = readl(base + ECC_UEADDR0_OFST); - p->ueinfo.sdram.rank = FIELD_GET(ECC_CEADDR0_RANK_MASK, regval); - p->ueinfo.sdram.row = FIELD_GET(ECC_CEADDR0_ROW_MASK, regval); + /* Report the detected errors with the corresponding sys address */ + snps_map_sdram_to_sys(priv, &einfo.sdram, &sys); - regval = readl(base + ECC_UEADDR1_OFST); - p->ueinfo.sdram.bankgrp = FIELD_GET(ECC_CEADDR1_BANKGRP_MASK, regval); - p->ueinfo.sdram.bank = FIELD_GET(ECC_CEADDR1_BANK_MASK, regval); - p->ueinfo.sdram.col = FIELD_GET(ECC_CEADDR1_COL_MASK, regval); + snprintf(priv->message, SNPS_EDAC_MSG_SIZE, + "Row %hu Col %hu Bank %hhu Bank Group %hhu Rank %hhu Bit %d Data 0x%08llx", + einfo.sdram.row, einfo.sdram.col, einfo.sdram.bank, + einfo.sdram.bankgrp, einfo.sdram.rank, + einfo.bitpos, einfo.data); - p->ueinfo.data = readl(base + ECC_UESYND0_OFST); - if (priv->info.dq_width == SNPS_DQ_64) - p->ueinfo.data |= (u64)readl(base + ECC_UESYND1_OFST) << 32; - - p->ueinfo.syndrome = readl(base + ECC_UESYND2_OFST); + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, einfo.ecnt, + PHYS_PFN(sys), offset_in_page(sys), + einfo.syndrome, einfo.sdram.rank, 0, -1, + priv->message, ""); -out: + /* Make sure the CE IRQ status is cleared */ spin_lock_irqsave(&priv->lock, flags); - clearval = readl(base + ECC_CLR_OFST) | - ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT | - ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT; - writel(clearval, base + ECC_CLR_OFST); + regval = readl(priv->baseaddr + ECC_CLR_OFST) | + ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT; + writel(regval, priv->baseaddr + ECC_CLR_OFST); spin_unlock_irqrestore(&priv->lock, flags); - return 0; + if (priv->info.caps & SNPS_CAP_ZYNQMP) + writel(qosval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); + + return IRQ_HANDLED; } /** - * snps_handle_error - Handle Correctable and Uncorrectable errors. - * @mci: EDAC memory controller instance. - * @p: Synopsys ECC status structure. + * snps_ue_irq_handler - Uncorrected error interrupt handler. + * @irq: IRQ number. + * @dev_id: Device ID. * - * Handles ECC correctable and uncorrectable errors. + * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise. */ -static void snps_handle_error(struct mem_ctl_info *mci, struct snps_ecc_status *p) +static irqreturn_t snps_ue_irq_handler(int irq, void *dev_id) { + struct mem_ctl_info *mci = dev_id; struct snps_edac_priv *priv = mci->pvt_info; - struct snps_ecc_error_info *pinf; + struct snps_ecc_error_info einfo; + unsigned long flags; + u32 qosval, regval; dma_addr_t sys; - if (p->ce_cnt) { - pinf = &p->ceinfo; + /* Make sure IRQ is caused by an uncorrected ECC error */ + if (priv->info.caps & SNPS_CAP_ZYNQMP) { + qosval = readl(priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); + if (!(regval & DDR_QOSUE_MASK)) + return IRQ_NONE; + + qosval &= DDR_QOSUE_MASK; + } - snprintf(priv->message, SNPS_EDAC_MSG_SIZE, - "Row %hu Col %hu Bank %hhu Bank Group %hhu Rank %hhu Bit %d Data 0x%08llx", - pinf->sdram.row, pinf->sdram.col, pinf->sdram.bank, - pinf->sdram.bankgrp, pinf->sdram.rank, - pinf->bitpos, pinf->data); + regval = readl(priv->baseaddr + ECC_STAT_OFST); + if (!FIELD_GET(ECC_STAT_UE_MASK, regval)) + return IRQ_NONE; - snps_map_sdram_to_sys(priv, &pinf->sdram, &sys); + /* Read error info like SDRAM address, data and syndrome */ + regval = readl(priv->baseaddr + ECC_ERRCNT_OFST); + einfo.ecnt = FIELD_GET(ECC_ERRCNT_UECNT_MASK, regval); - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, p->ce_cnt, - PHYS_PFN(sys), offset_in_page(sys), - pinf->syndrome, pinf->sdram.rank, 0, -1, - priv->message, ""); - } + regval = readl(priv->baseaddr + ECC_UEADDR0_OFST); + einfo.sdram.rank = FIELD_GET(ECC_CEADDR0_RANK_MASK, regval); + einfo.sdram.row = FIELD_GET(ECC_CEADDR0_ROW_MASK, regval); - if (p->ue_cnt) { - pinf = &p->ueinfo; + regval = readl(priv->baseaddr + ECC_UEADDR1_OFST); + einfo.sdram.bankgrp = FIELD_GET(ECC_CEADDR1_BANKGRP_MASK, regval); + einfo.sdram.bank = FIELD_GET(ECC_CEADDR1_BANK_MASK, regval); + einfo.sdram.col = FIELD_GET(ECC_CEADDR1_COL_MASK, regval); - snprintf(priv->message, SNPS_EDAC_MSG_SIZE, - "Row %hu Col %hu Bank %hhu Bank Group %hhu Rank %hhu Data 0x%08llx", - pinf->sdram.row, pinf->sdram.col, pinf->sdram.bank, - pinf->sdram.bankgrp, pinf->sdram.rank, - pinf->data); + einfo.data = readl(priv->baseaddr + ECC_UESYND0_OFST); + if (priv->info.dq_width == SNPS_DQ_64) + einfo.data |= (u64)readl(priv->baseaddr + ECC_UESYND1_OFST) << 32; - snps_map_sdram_to_sys(priv, &pinf->sdram, &sys); + einfo.syndrome = readl(priv->baseaddr + ECC_UESYND2_OFST); - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, p->ue_cnt, - PHYS_PFN(sys), offset_in_page(sys), - pinf->syndrome, pinf->sdram.rank, 0, -1, - priv->message, ""); - } + /* Report the detected errors with the corresponding sys address */ + snps_map_sdram_to_sys(priv, &einfo.sdram, &sys); + + snprintf(priv->message, SNPS_EDAC_MSG_SIZE, + "Row %hu Col %hu Bank %hhu Bank Group %hhu Rank %hhu Data 0x%08llx", + einfo.sdram.row, einfo.sdram.col, einfo.sdram.bank, + einfo.sdram.bankgrp, einfo.sdram.rank, + einfo.data); + + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, einfo.ecnt, + PHYS_PFN(sys), offset_in_page(sys), + einfo.syndrome, einfo.sdram.rank, 0, -1, + priv->message, ""); + + /* Make sure the UE IRQ status is cleared */ + spin_lock_irqsave(&priv->lock, flags); + + regval = readl(priv->baseaddr + ECC_CLR_OFST) | + ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT; + writel(regval, priv->baseaddr + ECC_CLR_OFST); - memset(p, 0, sizeof(*p)); + spin_unlock_irqrestore(&priv->lock, flags); + + if (priv->info.caps & SNPS_CAP_ZYNQMP) + writel(qosval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); + + return IRQ_HANDLED; +} + +/** + * snps_com_irq_handler - Interrupt IRQ signal handler. + * @irq: IRQ number. + * @dev_id: Device ID. + * + * Return: IRQ_NONE, if interrupts not set or IRQ_HANDLED otherwise. + */ +static irqreturn_t snps_com_irq_handler(int irq, void *dev_id) +{ + irqreturn_t rc = IRQ_NONE; + + rc |= snps_ce_irq_handler(irq, dev_id); + + rc |= snps_ue_irq_handler(irq, dev_id); + + return rc; } static void snps_enable_irq(struct snps_edac_priv *priv) @@ -854,41 +887,6 @@ static void snps_disable_irq(struct snps_edac_priv *priv) spin_unlock_irqrestore(&priv->lock, flags); } -/** - * snps_irq_handler - Interrupt Handler for ECC interrupts. - * @irq: IRQ number. - * @dev_id: Device ID. - * - * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise. - */ -static irqreturn_t snps_irq_handler(int irq, void *dev_id) -{ - struct mem_ctl_info *mci = dev_id; - struct snps_edac_priv *priv; - int status, regval; - - priv = mci->pvt_info; - - if (priv->info.caps & SNPS_CAP_ZYNQMP) { - regval = readl(priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); - regval &= (DDR_QOSCE_MASK | DDR_QOSUE_MASK); - if (!(regval & ECC_CE_UE_INTR_MASK)) - return IRQ_NONE; - } - - status = snps_get_error_info(priv); - if (status) - return IRQ_NONE; - - snps_handle_error(mci, &priv->stat); - - - if (priv->info.caps & SNPS_CAP_ZYNQMP) - writel(regval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); - - return IRQ_HANDLED; -} - /** * snps_create_data - Create private data. * @pdev: platform device. @@ -1538,7 +1536,7 @@ static int snps_setup_irq(struct mem_ctl_info *mci) return irq; } - ret = devm_request_irq(&priv->pdev->dev, irq, snps_irq_handler, + ret = devm_request_irq(&priv->pdev->dev, irq, snps_com_irq_handler, 0, dev_name(&priv->pdev->dev), mci); if (ret < 0) { edac_printk(KERN_ERR, EDAC_MC, "Failed to request IRQ\n"); From patchwork Mon Aug 22 19:19:53 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 599296 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1F69CC32772 for ; Mon, 22 Aug 2022 19:22:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238550AbiHVTV7 (ORCPT ); Mon, 22 Aug 2022 15:21:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46342 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238716AbiHVTU6 (ORCPT ); Mon, 22 Aug 2022 15:20:58 -0400 Received: from mail.baikalelectronics.com (mail.baikalelectronics.com [87.245.175.230]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 6A68E15820; Mon, 22 Aug 2022 12:20:34 -0700 (PDT) Received: from mail (mail.baikal.int [192.168.51.25]) by mail.baikalelectronics.com (Postfix) with ESMTP id 90C11DA3; Mon, 22 Aug 2022 22:23:36 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.com 90C11DA3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1661196216; bh=wku7u/iSUztYZ4YyKZErSj2fMZzBfaaRtgq0auiZEuw=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=LNGdKf0DgN4f+vqoqZliJum63i8RSPJ2aUqctRt3UrkcZxGErEzwr/EtOx/BKlI8Y 5qc6XXC80ZUxdfOwAcTMRK6Ntc3TV/gXFhtPzmR6c9s3XW2yDzXDMdtNYK8yBSzZYU 0KT+k6kElM9LVG1NYw0UkOPz01ytTfGjNqoiDbLM= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 22 Aug 2022 22:20:22 +0300 From: Serge Semin To: Michal Simek , Borislav Petkov , Mauro Carvalho Chehab , Tony Luck , James Morse , Robert Richter CC: Serge Semin , Serge Semin , Alexey Malahov , Michail Ivanov , Pavel Parkhomenko , Punnaiah Choudary Kalluri , Manish Narani , Dinh Nguyen , Rob Herring , Krzysztof Kozlowski , , , , Subject: [PATCH 10/13] EDAC/synopsys: Add reference clocks support Date: Mon, 22 Aug 2022 22:19:53 +0300 Message-ID: <20220822191957.28546-11-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> References: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Currently the driver doesn't support any clock-related resources request and handling, fairly assuming that all of them are supposed to be enabled anyway in order for the system to work correctly. It's true for the Core and AXI Ports reference clocks, but the CSR (APB) and Scrubber clocks might still be disabled in case if the system firmware doesn't imply any other software touching the DDR controller internals. Since the DW uMCTL2 DDRC driver does access the controller registers at the very least we need to make sure the APB clock is enabled. Let's add the reference clocks support then. First of all the driver will request all the clocks possibly defined for the controller (Core, AXI, APB and Scrubber). Secondly the APB clock will be enabled/disabled only since the Scrubber is currently unsupported by the driver, and the Core and AXI clocks feed the critical system parts so we need to avoid touching them with a risk to de-stabilize the system memory. Please note the clocks connection IDs have been chosen in accordance with the DT-bindings. Signed-off-by: Serge Semin --- drivers/edac/synopsys_edac.c | 101 +++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 3 deletions(-) diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index cdfa0e16bc5c..d6d5dfabddf5 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -301,6 +302,25 @@ enum snps_ecc_mode { SNPS_ECC_ADVX4X8 = 5, }; +/** + * enum snps_ref_clk - DW uMCTL2 DDR controller clocks. + * @SNPS_CSR_CLK: CSR/APB interface clock. + * @SNPS_AXI_CLK: AXI (AHB) Port reference clock. + * @SNPS_CORE_CLK: DDR controller (including DFI) clock. SDRAM clock + * matches runs with this freq in 1:1 ratio mode and + * with twice of this freq in case of 1:2 ratio mode. + * @SNPS_SBR_CLK: Scrubber port reference clock (synchronous to + * the core clock). + * @SNPS_MAX_NCLK: Total number of clocks. + */ +enum snps_ref_clk { + SNPS_CSR_CLK, + SNPS_AXI_CLK, + SNPS_CORE_CLK, + SNPS_SBR_CLK, + SNPS_MAX_NCLK +}; + /** * struct snps_ddrc_info - DDR controller platform parameters. * @caps: DDR controller capabilities. @@ -405,6 +425,7 @@ struct snps_ecc_error_info { * @pdev: Platform device. * @baseaddr: Base address of the DDR controller. * @lock: Concurrent CSRs access lock. + * @clks: Controller reference clocks. * @message: Buffer for framing the event specific info. */ struct snps_edac_priv { @@ -414,6 +435,7 @@ struct snps_edac_priv { struct platform_device *pdev; void __iomem *baseaddr; spinlock_t lock; + struct clk_bulk_data clks[SNPS_MAX_NCLK]; char message[SNPS_EDAC_MSG_SIZE]; }; @@ -974,6 +996,60 @@ static struct snps_edac_priv *snps_create_data(struct platform_device *pdev) return priv; } +/** + * snps_get_res - Get platform device resources. + * @priv: DDR memory controller private instance data. + * + * It's supposed to request all the controller resources available for the + * particular platform and enable all the required for the driver normal + * work. Note only the CSR and Scrubber clocks are supposed to be switched + * on/off by the driver. + * + * Return: negative errno if failed to get the resources, otherwise - zero. + */ +static int snps_get_res(struct snps_edac_priv *priv) +{ + const char * const ids[] = { + [SNPS_CSR_CLK] = "pclk", + [SNPS_AXI_CLK] = "aclk", + [SNPS_CORE_CLK] = "core", + [SNPS_SBR_CLK] = "sbr", + }; + int i, rc; + + for (i = 0; i < SNPS_MAX_NCLK; i++) + priv->clks[i].id = ids[i]; + + rc = devm_clk_bulk_get_optional(&priv->pdev->dev, SNPS_MAX_NCLK, + priv->clks); + if (rc) { + edac_printk(KERN_INFO, EDAC_MC, "Failed to get ref clocks\n"); + return rc; + } + + /* + * Don't touch the Core and AXI clocks since they are critical for the + * stable system functioning and are supposed to have been enabled + * anyway. + */ + rc = clk_prepare_enable(priv->clks[SNPS_CSR_CLK].clk); + if (rc) { + edac_printk(KERN_INFO, EDAC_MC, "Couldn't enable CSR clock\n"); + return rc; + } + + return 0; +} + +/** + * snps_put_res - Put platform device resources. + * @priv: DDR memory controller private instance data. + */ +static void snps_put_res(struct snps_edac_priv *priv) +{ + clk_disable_unprepare(priv->clks[SNPS_CSR_CLK].clk); +} + /* * zynqmp_init_plat - ZynqMP-specific platform initialization. * @priv: DDR memory controller private data. @@ -1707,9 +1783,17 @@ static int snps_ddrc_info_show(struct seq_file *s, void *data) { struct mem_ctl_info *mci = s->private; struct snps_edac_priv *priv = mci->pvt_info; + unsigned long rate; seq_printf(s, "SDRAM: %s\n", edac_mem_types[priv->info.sdram_mode]); + rate = clk_get_rate(priv->clks[SNPS_CORE_CLK].clk); + if (rate) { + rate = rate / HZ_PER_MHZ; + seq_printf(s, "Clock: Core %luMHz SDRAM %luMHz\n", + rate, priv->info.freq_ratio * rate); + } + seq_printf(s, "DQ bus: %u/%s\n", (BITS_PER_BYTE << priv->info.dq_width), priv->info.dq_mode == SNPS_DQ_FULL ? "Full" : priv->info.dq_mode == SNPS_DQ_HALF ? "Half" : @@ -2018,15 +2102,21 @@ static int snps_mc_probe(struct platform_device *pdev) if (IS_ERR(priv)) return PTR_ERR(priv); - rc = snps_get_ddrc_info(priv); + rc = snps_get_res(priv); if (rc) return rc; + rc = snps_get_ddrc_info(priv); + if (rc) + goto put_res; + snps_get_addr_map(priv); mci = snps_mc_create(priv); - if (IS_ERR(mci)) - return PTR_ERR(mci); + if (IS_ERR(mci)) { + rc = PTR_ERR(mci); + goto put_res; + } rc = snps_setup_irq(mci); if (rc) @@ -2046,6 +2136,9 @@ static int snps_mc_probe(struct platform_device *pdev) free_edac_mc: snps_mc_free(mci); +put_res: + snps_put_res(priv); + return rc; } @@ -2066,6 +2159,8 @@ static int snps_mc_remove(struct platform_device *pdev) snps_mc_free(mci); + snps_put_res(priv); + return 0; } From patchwork Mon Aug 22 19:19:54 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 599295 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CFF13C32789 for ; Mon, 22 Aug 2022 19:22:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237611AbiHVTWE (ORCPT ); Mon, 22 Aug 2022 15:22:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47656 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238719AbiHVTU7 (ORCPT ); Mon, 22 Aug 2022 15:20:59 -0400 Received: from mail.baikalelectronics.com (mail.baikalelectronics.com [87.245.175.230]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 91CCA17E34; Mon, 22 Aug 2022 12:20:36 -0700 (PDT) Received: from mail (mail.baikal.int [192.168.51.25]) by mail.baikalelectronics.com (Postfix) with ESMTP id 46C92DAC; Mon, 22 Aug 2022 22:23:37 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.com 46C92DAC DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1661196217; bh=6X1mO4mb7/Zp5I3lTkCpiXqoxPYOO7/fRXGZUo4wY8k=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=e9VQ1bctq/FtjWc0Wiu/f2kN4JzKzYha0E7QfvvVfL2abY9WUHbnj88g5g/ty+DQQ fenCLqqW9WkIBgm3bMLQ0MQzUw4AeMLFlmY4rCR56cpel7ZmeRA0ewnWzpEGAbdi62 h1cRmSaEd4jZz1jExiJqgG5p93atr9tXtNiJGDDs= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 22 Aug 2022 22:20:23 +0300 From: Serge Semin To: Michal Simek , Borislav Petkov , Mauro Carvalho Chehab , Tony Luck , James Morse , Robert Richter CC: Serge Semin , Serge Semin , Alexey Malahov , Michail Ivanov , Pavel Parkhomenko , Punnaiah Choudary Kalluri , Manish Narani , Dinh Nguyen , Rob Herring , Krzysztof Kozlowski , , , , Subject: [PATCH 11/13] EDAC/synopsys: Add ECC Scrubber support Date: Mon, 22 Aug 2022 22:19:54 +0300 Message-ID: <20220822191957.28546-12-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> References: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org DW uMCTL2 DDR controller IP-core can by synthesized with an embedded Scrubber engine. The ECC Scrubber (SBR) is a block which initiates periodic background burst read commands to the DDRC and further towards the DDR memory in an attempt to trigger Correctable or Uncorrectable errors. If a Correctable error is detected the ECC Scrub feature will execute the Read-Modify-Write (RMW) procedure in order to fix the ECC. In case of the Uncorrectable error it will be just reported as the corresponding IRQ event. So it's definitely very useful feature. Let's add it to the driver then especially seeing the MCI core already has some infrastructure for it. First of all even though the Core clock rate is only used for the Scrub rate calculations we need to have the Scrubber clock enabled if one is supplied otherwise the engine won't work. Secondly the Scrubber engine support needs to be detected. Alas there is no any special CSR indicating whether the DW uMCTL2 DDRC IP-core has been synthesized with one embedded. Instead we suggest to implement the detection procedure based on the Scrubber-specific CSRs writability. So if the SBRWDATA0 CSR is writable then the CSR exists, which means the Scrubber is available, otherwise the capability will be considered as absent. Thirdly the MCI core provides two callbacks utilized for the Scrubber tuning: set the Scrubber bandwidth in bytes, which can also be used to disable the periodic scrubbing, and get the Scrubber bandwidth (zero if disabled). We can implement both of them by using the Scrubber CSRs the controller provides. In particular aside with the back-to-back periodic reads the Scrubber provides a way to delay the next read command for the predefined set of 512's Core/Scrubber clock cycles. It can be used to change the Scrubber bandwidth from the DDR maximal bandwidth (no delay) to up to (0x1FFF * 512) Core/Scrubber clock cycles (see the inline comments for details and utilized formulae). Note the Scrubber clock must be synchronous to the Core clock by the controller design so here we get to use the Core clock rate for the calculations. Pleas also note if no Core clock specified the Scrubber will still be supported, but the bandwidth will be used directly to calculate the Scrubber reads interval. The back-to-back reads mode in this case will be indicated by the INT_MAX bandwidth. Fourthly the back-to-back scrubbing most likely will cause the significant system performance drop. The manual says that it has been added to the controller for the initial SDRAM initialization and the fast SDRAM scrubbing after getting out of the low-power state. In anyway it is supposed to be enabled only for a single SDRAM pass. We get to preserve that semantic here so the back-to-back scrubbing will be disabled in the Scrubber Done IRQ handler. Finally the denoted scrub-rate callbacks and the SCRUB_FLAG_HW_PROG and SCRUB_FLAG_HW_TUN flags will set to the MCI descriptor based on the detected Scrubber capability. So no capability - no flags and no callbacks. Signed-off-by: Serge Semin --- drivers/edac/synopsys_edac.c | 304 +++++++++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+) diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index d6d5dfabddf5..f86d1be2702a 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ /* DDR capabilities */ #define SNPS_CAP_ECC_SCRUB BIT(0) +#define SNPS_CAP_ECC_SCRUBBER BIT(1) #define SNPS_CAP_ZYNQMP BIT(31) /* Synopsys uMCTL2 DDR controller registers that are relevant to ECC */ @@ -102,6 +104,12 @@ #define DDR_SARBASE0_OFST 0xF04 #define DDR_SARSIZE0_OFST 0xF08 +/* ECC Scrubber registers */ +#define ECC_SBRCTL_OFST 0xF24 +#define ECC_SBRSTAT_OFST 0xF28 +#define ECC_SBRWDATA0_OFST 0xF2C +#define ECC_SBRWDATA1_OFST 0xF30 + /* DDR Master Register 0 definitions */ #define DDR_MSTR_DEV_CFG_MASK GENMASK(31, 30) #define DDR_MSTR_DEV_X4 0x0 @@ -244,6 +252,18 @@ #define DDR_MAX_NSAR 4 #define DDR_MIN_SARSIZE SZ_256M +/* ECC Scrubber registers definitions */ +#define ECC_SBRCTL_SCRUB_INTERVAL GENMASK(20, 8) +#define ECC_SBRCTL_INTERVAL_STEP 512 +#define ECC_SBRCTL_INTERVAL_MIN 0 +#define ECC_SBRCTL_INTERVAL_SAFE 1 +#define ECC_SBRCTL_INTERVAL_MAX (ECC_SBRCTL_SCRUB_INTERVAL >> 8) +#define ECC_SBRCTL_SCRUB_BURST GENMASK(6, 4) +#define ECC_SBRCTL_SCRUB_MODE_WR BIT(2) +#define ECC_SBRCTL_SCRUB_EN BIT(0) +#define ECC_SBRSTAT_SCRUB_DONE BIT(1) +#define ECC_SBRSTAT_SCRUB_BUSY BIT(0) + /** * enum snps_dq_width - SDRAM DQ bus width (ECC capable). * SNPS_DQ_32: 32-bit memory data width. @@ -906,6 +926,47 @@ static irqreturn_t snps_dfi_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +/** + * snps_sbr_irq_handler - Scrubber Done interrupt handler. + * @irq: IRQ number. + * @dev_id: Device ID. + * + * It just checks whether the IRQ has been caused by the Scrubber Done event + * and disables the back-to-back scrubbing by falling back to the smallest + * delay between the Scrubber read commands. + * + * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise. + */ +static irqreturn_t snps_sbr_irq_handler(int irq, void *dev_id) +{ + struct mem_ctl_info *mci = dev_id; + struct snps_edac_priv *priv = mci->pvt_info; + unsigned long flags; + u32 regval, en; + + /* Make sure IRQ is caused by the Scrubber Done event */ + regval = readl(priv->baseaddr + ECC_SBRSTAT_OFST); + if (!(regval & ECC_SBRSTAT_SCRUB_DONE)) + return IRQ_NONE; + + spin_lock_irqsave(&priv->lock, flags); + + regval = readl(priv->baseaddr + ECC_SBRCTL_OFST); + en = regval & ECC_SBRCTL_SCRUB_EN; + writel(regval & ~en, priv->baseaddr + ECC_SBRCTL_OFST); + + regval = FIELD_PREP(ECC_SBRCTL_SCRUB_INTERVAL, ECC_SBRCTL_INTERVAL_SAFE); + writel(regval, priv->baseaddr + ECC_SBRCTL_OFST); + + writel(regval | en, priv->baseaddr + ECC_SBRCTL_OFST); + + spin_unlock_irqrestore(&priv->lock, flags); + + edac_mc_printk(mci, KERN_WARNING, "Back-to-back scrubbing disabled\n"); + + return IRQ_HANDLED; +} + /** * snps_com_irq_handler - Interrupt IRQ signal handler. * @irq: IRQ number. @@ -915,6 +976,8 @@ static irqreturn_t snps_dfi_irq_handler(int irq, void *dev_id) */ static irqreturn_t snps_com_irq_handler(int irq, void *dev_id) { + struct mem_ctl_info *mci = dev_id; + struct snps_edac_priv *priv = mci->pvt_info; irqreturn_t rc = IRQ_NONE; rc |= snps_ce_irq_handler(irq, dev_id); @@ -923,6 +986,9 @@ static irqreturn_t snps_com_irq_handler(int irq, void *dev_id) rc |= snps_dfi_irq_handler(irq, dev_id); + if (priv->info.caps & SNPS_CAP_ECC_SCRUBBER) + rc |= snps_sbr_irq_handler(irq, dev_id); + return rc; } @@ -972,6 +1038,205 @@ static void snps_disable_irq(struct snps_edac_priv *priv) spin_unlock_irqrestore(&priv->lock, flags); } +/** + * snps_get_sdram_bw - Get SDRAM bandwidth. + * @priv: DDR memory controller private instance data. + * + * The SDRAM interface bandwidth is calculated based on the DDRC Core clock rate + * and the DW uMCTL2 IP-core parameters like DQ-bus width and mode and + * Core/SDRAM clocks frequency ratio. Note it returns the theoretical bandwidth + * which in reality is hardly possible to reach. + * + * Return: SDRAM bandwidth or zero if no Core clock specified. + */ +static u64 snps_get_sdram_bw(struct snps_edac_priv *priv) +{ + unsigned long rate; + + /* + * Depending on the ratio mode the SDRAM clock either matches the Core + * clock or runs with the twice its frequency. + */ + rate = clk_get_rate(priv->clks[SNPS_CORE_CLK].clk); + rate *= priv->info.freq_ratio; + + /* + * Scale up by 2 since it's DDR (Double Data Rate) and subtract the + * DQ-mode since in non-Full mode only a part of the DQ-bus is utilised + * on each SDRAM clock edge. + */ + return (2U << (priv->info.dq_width - priv->info.dq_mode)) * (u64)rate; +} + +/** + * snps_get_scrub_bw - Get Scrubber bandwidth. + * @priv: DDR memory controller private instance data. + * @interval: Scrub interval. + * + * DW uMCTL2 DDRC Scrubber performs periodical progressive burst reads (RMW if + * ECC CE is detected) commands from the whole memory space. The read commands + * can be delayed by means of the SBRCTL.scrub_interval field. The Scrubber + * cycles look as follows: + * + * |---HIF-burst-read---|-------delay-------|-HIF-burst-read-| etc + * + * Tb = Bl*[DQ]/Bw[RAM] Td = 512*interval/Fc - periods of the stages, where + * Bl - HIF burst length, [DQ] - Full DQ-bus width, Bw[RAM] - SDRAM bandwidth, + * Fc - Core clock frequency (Scrubber and Core clocks are synchronous). + * + * After some simple calculations the expressions above can be used to get the + * next Scrubber bandwidth formulae: + * + * Bw[Sbr] = Bw[RAM] / (1 + F * interval), where + * F = 2 * 512 * Fr * Fc * [DQ]e - interval scale factor with + * Fr - HIF/SDRAM clock frequency ratio (1 or 2), [DQ]e - DQ-bus width mode. + * + * Return: Scrubber bandwidth or zero if no Core clock specified. + */ +static u64 snps_get_scrub_bw(struct snps_edac_priv *priv, u32 interval) +{ + unsigned long fac; + u64 bw_ram; + + fac = (2 * ECC_SBRCTL_INTERVAL_STEP * priv->info.freq_ratio) / + (priv->info.hif_burst_len * (1UL << priv->info.dq_mode)); + + bw_ram = snps_get_sdram_bw(priv); + + do_div(bw_ram, 1 + fac * interval); + + return bw_ram; +} + +/** + * snps_get_scrub_interval - Get Scrubber delay interval. + * @priv: DDR memory controller private instance data. + * @bw: Scrubber bandwidth. + * + * Similarly to the Scrubber bandwidth the interval formulae can be inferred + * from the same expressions: + * + * interval = (Bw[RAM] - Bw[Sbr]) / (F * Bw[Sbr]) + * + * Return: Scrubber delay interval or zero if no Core clock specified. + */ +static u32 snps_get_scrub_interval(struct snps_edac_priv *priv, u32 bw) +{ + unsigned long fac; + u64 bw_ram; + + fac = (2 * priv->info.freq_ratio * ECC_SBRCTL_INTERVAL_STEP) / + (priv->info.hif_burst_len * (1UL << priv->info.dq_mode)); + + bw_ram = snps_get_sdram_bw(priv); + + /* Divide twice so not to cause the integer overflow in (fac * bw) */ + bw_ram -= bw; + do_div(bw_ram, bw); + do_div(bw_ram, fac); + + return bw_ram; +} + +/** + * snps_set_sdram_scrub_rate - Set the Scrubber bandwidth. + * @mci: EDAC memory controller instance. + * @bw: Bandwidth. + * + * It calculates the delay between the Scrubber read commands based on the + * specified bandwidth and the Core clock rate. If the Core clock is unavailable + * the passed bandwidth will be directly used as the interval value. + * + * Note the method warns about the back-to-back scrubbing since it may + * significantly degrade the system performance. This mode is supposed to be + * used for a single SDRAM scrubbing pass only. So it will be turned off in the + * Scrubber Done IRQ handler. + * + * Return: Actually set bandwidth (interval-based approximated bandwidth if the + * Core clock is unavailable) or zero if the Scrubber was disabled. + */ +static int snps_set_sdram_scrub_rate(struct mem_ctl_info *mci, u32 bw) +{ + struct snps_edac_priv *priv = mci->pvt_info; + u32 regval, interval; + unsigned long flags; + u64 bw_min, bw_max; + + /* Don't bother with the calculations just disable and return. */ + if (!bw) { + spin_lock_irqsave(&priv->lock, flags); + + regval = readl(priv->baseaddr + ECC_SBRCTL_OFST); + regval &= ~ECC_SBRCTL_SCRUB_EN; + writel(regval, priv->baseaddr + ECC_SBRCTL_OFST); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; + } + + /* If no Core clock specified fallback to the direct interval setup. */ + bw_max = snps_get_scrub_bw(priv, ECC_SBRCTL_INTERVAL_MIN); + if (bw_max) { + bw_min = snps_get_scrub_bw(priv, ECC_SBRCTL_INTERVAL_MAX); + bw = clamp_t(u64, bw, bw_min, bw_max); + + interval = snps_get_scrub_interval(priv, bw); + } else { + bw = clamp_val(bw, ECC_SBRCTL_INTERVAL_MIN, ECC_SBRCTL_INTERVAL_MAX); + + interval = ECC_SBRCTL_INTERVAL_MAX - bw; + } + + /* + * SBRCTL.scrub_en bitfield must be accessed separately from the other + * CSR bitfields. It means the flag must be set/clear with no updates + * to the rest of the fields. + */ + spin_lock_irqsave(&priv->lock, flags); + + regval = FIELD_PREP(ECC_SBRCTL_SCRUB_INTERVAL, interval); + writel(regval, priv->baseaddr + ECC_SBRCTL_OFST); + + writel(regval | ECC_SBRCTL_SCRUB_EN, priv->baseaddr + ECC_SBRCTL_OFST); + + spin_unlock_irqrestore(&priv->lock, flags); + + if (!interval) + edac_mc_printk(mci, KERN_WARNING, "Back-to-back scrubbing enabled\n"); + + if (!bw_max) + return interval ? bw : INT_MAX; + + return snps_get_scrub_bw(priv, interval); +} + +/** + * snps_get_sdram_scrub_rate - Get the Scrubber bandwidth. + * @mci: EDAC memory controller instance. + * + * Return: Scrubber bandwidth (interval-based approximated bandwidth if the + * Core clock is unavailable) or zero if the Scrubber was disabled. + */ +static int snps_get_sdram_scrub_rate(struct mem_ctl_info *mci) +{ + struct snps_edac_priv *priv = mci->pvt_info; + u32 regval; + u64 bw; + + regval = readl(priv->baseaddr + ECC_SBRCTL_OFST); + if (!(regval & ECC_SBRCTL_SCRUB_EN)) + return 0; + + regval = FIELD_GET(ECC_SBRCTL_SCRUB_INTERVAL, regval); + + bw = snps_get_scrub_bw(priv, regval); + if (!bw) + return regval ? ECC_SBRCTL_INTERVAL_MAX - regval : INT_MAX; + + return bw; +} + /** * snps_create_data - Create private data. * @pdev: platform device. @@ -1038,7 +1303,18 @@ static int snps_get_res(struct snps_edac_priv *priv) return rc; } + rc = clk_prepare_enable(priv->clks[SNPS_SBR_CLK].clk); + if (rc) { + edac_printk(KERN_INFO, EDAC_MC, "Couldn't enable Scrubber clock\n"); + goto err_disable_pclk; + } + return 0; + +err_disable_pclk: + clk_disable_unprepare(priv->clks[SNPS_CSR_CLK].clk); + + return rc; } /** @@ -1047,6 +1323,8 @@ static int snps_get_res(struct snps_edac_priv *priv) */ static void snps_put_res(struct snps_edac_priv *priv) { + clk_disable_unprepare(priv->clks[SNPS_SBR_CLK].clk); + clk_disable_unprepare(priv->clks[SNPS_CSR_CLK].clk); } @@ -1147,6 +1425,14 @@ static int snps_get_ddrc_info(struct snps_edac_priv *priv) if (!(regval & ECC_CFG0_DIS_SCRUB)) priv->info.caps |= SNPS_CAP_ECC_SCRUB; + /* Auto-detect the scrubber by writing to the SBRWDATA0 CSR */ + regval = readl(priv->baseaddr + ECC_SBRWDATA0_OFST); + writel(~regval, priv->baseaddr + ECC_SBRWDATA0_OFST); + if (regval != readl(priv->baseaddr + ECC_SBRWDATA0_OFST)) { + priv->info.caps |= SNPS_CAP_ECC_SCRUBBER; + writel(regval, priv->baseaddr + ECC_SBRWDATA0_OFST); + } + /* Auto-detect the basic HIF/SDRAM bus parameters */ regval = readl(priv->baseaddr + DDR_MSTR_OFST); @@ -1633,6 +1919,12 @@ static struct mem_ctl_info *snps_mc_create(struct snps_edac_priv *priv) mci->scrub_cap = SCRUB_FLAG_SW_SRC; } + if (priv->info.caps & SNPS_CAP_ECC_SCRUBBER) { + mci->scrub_cap |= SCRUB_FLAG_HW_PROG | SCRUB_FLAG_HW_TUN; + mci->set_sdram_scrub_rate = snps_set_sdram_scrub_rate; + mci->get_sdram_scrub_rate = snps_get_sdram_scrub_rate; + } + mci->ctl_name = "snps_umctl2_ddrc"; mci->dev_name = SNPS_EDAC_MOD_STRING; mci->mod_name = SNPS_EDAC_MOD_VER; @@ -1707,6 +1999,16 @@ static int snps_request_ind_irq(struct mem_ctl_info *mci) } } + irq = platform_get_irq_byname_optional(priv->pdev, "ecc_sbr"); + if (irq > 0) { + rc = devm_request_irq(dev, irq, snps_sbr_irq_handler, 0, "ecc_sbr", mci); + if (rc) { + edac_printk(KERN_ERR, EDAC_MC, "Failed to request Sbr IRQ\n"); + return rc; + } + } + + return 0; } @@ -1813,6 +2115,8 @@ static int snps_ddrc_info_show(struct seq_file *s, void *data) if (priv->info.caps) { if (priv->info.caps & SNPS_CAP_ECC_SCRUB) seq_puts(s, " +Scrub"); + if (priv->info.caps & SNPS_CAP_ECC_SCRUBBER) + seq_puts(s, " +Scrubber"); if (priv->info.caps & SNPS_CAP_ZYNQMP) seq_puts(s, " +ZynqMP"); } else { From patchwork Mon Aug 22 19:19:56 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Serge Semin X-Patchwork-Id: 599294 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A0007C3F6B0 for ; Mon, 22 Aug 2022 19:22:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238583AbiHVTWF (ORCPT ); Mon, 22 Aug 2022 15:22:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47684 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238726AbiHVTVA (ORCPT ); Mon, 22 Aug 2022 15:21:00 -0400 Received: from mail.baikalelectronics.com (mail.baikalelectronics.com [87.245.175.230]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 21B7B1835F; Mon, 22 Aug 2022 12:20:41 -0700 (PDT) Received: from mail (mail.baikal.int [192.168.51.25]) by mail.baikalelectronics.com (Postfix) with ESMTP id A9BF0DA4; Mon, 22 Aug 2022 22:23:38 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.baikalelectronics.com A9BF0DA4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baikalelectronics.ru; s=mail; t=1661196218; bh=/tarMJ/Lpj3lIo7Fjjl2T4vGuOsDP7eZcPqYpQHmyVA=; h=From:To:CC:Subject:Date:In-Reply-To:References:From; b=AZgKD+ROYiobj4UbtS2PCkhDBf1p0+AAaqTuVPXRQZus1qjGmWJan6PRcgf3/MVMj NJ27n6XkwcZr/ORq5JeGVY66k27tMRLBREGblr/90OoW0bB22xu9XbR12x983VFHhw seyXp6YOdG3WNxxYeZoOJcRauBJ9jNlqM4tJP+Y4= Received: from localhost (192.168.168.10) by mail (192.168.51.25) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 22 Aug 2022 22:20:24 +0300 From: Serge Semin To: Michal Simek , Borislav Petkov , Mauro Carvalho Chehab , Tony Luck , James Morse , Robert Richter CC: Serge Semin , Serge Semin , Alexey Malahov , Michail Ivanov , Pavel Parkhomenko , Punnaiah Choudary Kalluri , Manish Narani , Dinh Nguyen , Rob Herring , Krzysztof Kozlowski , , , , Subject: [PATCH 13/13] EDAC/synopsys: Add Baikal-T1 DDRC support Date: Mon, 22 Aug 2022 22:19:56 +0300 Message-ID: <20220822191957.28546-14-Sergey.Semin@baikalelectronics.ru> In-Reply-To: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> References: <20220822191957.28546-1-Sergey.Semin@baikalelectronics.ru> MIME-Version: 1.0 X-ClientProxiedBy: MAIL.baikal.int (192.168.51.25) To mail (192.168.51.25) Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Baikal-T1 SoC is equipped with the DW uMCTl2 DDRC of v2.61a with 32-bit DQ-bus accepting DDR2/DDR3 SDRAMs of up to 2 ranks, 1:2 HIF/SDRAM clocks rate ratio, HIF interface burst length of 8 Full DQ-bus words, 40-bit System/Application address width and 128-bits data width, 3 System address regions with block size 256MB. There is SEC/DED ECC capability with Scrub (RMW) and Scrubber features. Since the Baikal-T1 DDR controller is capable of the ECC let's add it to the DW uMCTL2 DDRC EDAC driver. The most of the parameters above will be autodetected except HIF burst length and SAR block size, which will be set by means of the Baikal-T1-specific initialization method. The controller compatible string "baikal,bt1-ddrc" will be used to attach the driver to the kernel device. It's chosen in accordance with the just updated DT-bindings. Signed-off-by: Serge Semin --- drivers/edac/synopsys_edac.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index f86d1be2702a..9780f61ac84c 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -1342,6 +1342,20 @@ static int zynqmp_init_plat(struct snps_edac_priv *priv) return 0; } +/* + * bt1_init_plat - Baikal-T1-specific platform initialization. + * @priv: DDR memory controller private data. + * + * Return: always zero. + */ +static int bt1_init_plat(struct snps_edac_priv *priv) +{ + priv->info.hif_burst_len = SNPS_DDR_BL8; + priv->sys_app_map.minsize = DDR_MIN_SARSIZE; + + return 0; +} + /** * snps_get_dtype - Return the controller memory width. * @mstr: Master CSR value. @@ -2470,6 +2484,7 @@ static int snps_mc_remove(struct platform_device *pdev) static const struct of_device_id snps_edac_match[] = { { .compatible = "xlnx,zynqmp-ddrc-2.40a", .data = zynqmp_init_plat }, + { .compatible = "baikal,bt1-ddrc", .data = bt1_init_plat }, { .compatible = "snps,ddrc-3.80a" }, { } };