From nobody Thu May 7 21:11:39 2026 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 D012FC433F5 for ; Thu, 19 May 2022 07:26:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234957AbiESH0J (ORCPT ); Thu, 19 May 2022 03:26:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42818 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234777AbiESHZM (ORCPT ); Thu, 19 May 2022 03:25:12 -0400 Received: from mail-pj1-x1034.google.com (mail-pj1-x1034.google.com [IPv6:2607:f8b0:4864:20::1034]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D765F8720F; Thu, 19 May 2022 00:25:09 -0700 (PDT) Received: by mail-pj1-x1034.google.com with SMTP id nk9-20020a17090b194900b001df2fcdc165so8047767pjb.0; Thu, 19 May 2022 00:25:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=izhcXDphxsSYMgLi7WOIxwg7ve9I4BtGghSY1eoBQEs=; b=jZzhFSYQ6xCZWQ48bdsBqBDao2zARJZkZyDZsUfdzMeDDlGumxtck1+YKCQVqhOiwn zzWbwYkxaOrsQSp52OnMaLSG80Kz+x8GL3kyJJ0cVGc7kyQU7uCwQfPJF4Avk0KuCMYd vDGTVqQUNFa83s4j8Pm0YxbfhwEDkje/lPcSgsyK6vkjd+u2dQuyckvBZnFBAHrHLT9s Eh/40tmJitMVYZkEJBUC+q8QLiZVn+Go8EtS5x/iCKhb3Tt0EpSQhib0cLLtuzuj7wHH jx2826dhlVarBTt6wtB8eDqqsBZHqrT+Mbaki/HCwiHKTJfdhqWO0aXDHqi8BCeAepP4 gMyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=izhcXDphxsSYMgLi7WOIxwg7ve9I4BtGghSY1eoBQEs=; b=uMzI7muTf8g2wXdabfbD08l8e5c1gOPZG+6XXxle/kdw5HK8aBXlSuMoCeVIHwaDR+ GNXAvA44Q9sum+tWdSzEOVuxbBQHjnYMyfJCQLFk1Pzi90Cs4u9VnTkV002Yl1I8b1Tg QiiLJ4xurzdc87boLrCwoBXaabR5X0f06v9YXdL2zK7zRY7pobbfn9tPyzkpb6IuiVed 3q4kJav2X5pERgCZ23valw3xz+SjUA0S/bT46BTxD5eFh08DEqCnr6W7OTlaueRXVmlO 4v6pt/yf/rifLakd2Mmrv+fNORnBynrO0sivzxEaM/p/J1Ad9AYhEBHO/U9qMozI1r2E npJw== X-Gm-Message-State: AOAM530u1rLSwAZqv9Q+uFwnl8m0j52AXLuKoXiokMVLG8WNDtbQBK2Z 0GT0bx1WTJkEwJuLihSe2I0= X-Google-Smtp-Source: ABdhPJxRWMLiPZC1hEmCnRDpsE1fl+ZNEr/v7kfplga+jDdVtU/2eIrwAPw+PI8xO0UejtD6e40BnQ== X-Received: by 2002:a17:903:4044:b0:161:823e:6f93 with SMTP id n4-20020a170903404400b00161823e6f93mr3501687pla.172.1652945109335; Thu, 19 May 2022 00:25:09 -0700 (PDT) Received: from localhost.localdomain ([116.89.141.50]) by smtp.gmail.com with ESMTPSA id b12-20020a170902650c00b00161ac982b9esm2974159plk.185.2022.05.19.00.25.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 19 May 2022 00:25:08 -0700 (PDT) From: Medad CChien X-Google-Original-From: Medad CChien To: rric@kernel.org, james.morse@arm.com, tony.luck@intel.com, mchehab@kernel.org, bp@alien8.de, robh+dt@kernel.org, benjaminfair@google.com, yuenn@google.com, venture@google.com, KWLIU@nuvoton.com, YSCHU@nuvoton.com, JJLIU0@nuvoton.com, KFTING@nuvoton.com, avifishman70@gmail.com, tmaimon77@gmail.com, tali.perry1@gmail.com, ctcchien@nuvoton.com Cc: linux-edac@vger.kernel.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, openbmc@lists.ozlabs.org Subject: [PATCH v10 1/3] dt-bindings: edac: nuvoton: add NPCM memory controller Date: Thu, 19 May 2022 15:24:52 +0800 Message-Id: <20220519072454.24063-2-ctcchien@nuvoton.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220519072454.24063-1-ctcchien@nuvoton.com> References: <20220519072454.24063-1-ctcchien@nuvoton.com> Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Document devicetree bindings for the Nuvoton BMC NPCM memory controller. Signed-off-by: Medad CChien Reviewed-by: Krzysztof Kozlowski --- .../edac/nuvoton,npcm-memory-controller.yaml | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Documentation/devicetree/bindings/edac/nuvoton,npcm-mem= ory-controller.yaml diff --git a/Documentation/devicetree/bindings/edac/nuvoton,npcm-memory-con= troller.yaml b/Documentation/devicetree/bindings/edac/nuvoton,npcm-memory-c= ontroller.yaml new file mode 100644 index 000000000000..a5c8d332d1c1 --- /dev/null +++ b/Documentation/devicetree/bindings/edac/nuvoton,npcm-memory-controller= .yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/edac/nuvoton,npcm-memory-controller.yam= l# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Nuvoton NPCM Memory Controller + +maintainers: + - Medad CChien + - Stanley Chu + +description: | + The Nuvoton BMC SoC supports DDR4 memory with and without ECC (error + correction check). + + The memory controller supports single bit error correction, double bit + error detection (in-line ECC in which a section (1/8th) of the memory + device used to store data is used for ECC storage). + + Note, the bootloader must configure ECC mode for the memory controller. + +properties: + compatible: + enum: + - nuvoton,npcm750-memory-controller + - nuvoton,npcm845-memory-controller + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + items: + - description: uncorrectable error interrupt + - description: correctable error interrupt + + interrupt-names: + minItems: 1 + items: + - const: ue + - const: ce + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + ahb { + #address-cells =3D <2>; + #size-cells =3D <2>; + mc: memory-controller@f0824000 { + compatible =3D "nuvoton,npcm750-memory-controller"; + reg =3D <0x0 0xf0824000 0x0 0x1000>; + interrupts =3D ; + }; + }; --=20 2.17.1 From nobody Thu May 7 21:11:39 2026 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 E56F4C433FE for ; Thu, 19 May 2022 07:26:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235005AbiESH0U (ORCPT ); Thu, 19 May 2022 03:26:20 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42964 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234791AbiESHZR (ORCPT ); Thu, 19 May 2022 03:25:17 -0400 Received: from mail-pl1-x634.google.com (mail-pl1-x634.google.com [IPv6:2607:f8b0:4864:20::634]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0EC7484A25; Thu, 19 May 2022 00:25:15 -0700 (PDT) Received: by mail-pl1-x634.google.com with SMTP id q4so4019619plr.11; Thu, 19 May 2022 00:25:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=gmirLNjLGfJzW6YYZUOR8avyTXMQpr4Eec+Omnxr9sI=; b=oUoSk7CnlRMCqq9hO54muVaLUmgCKKOAwoVTP2rtdSuWxwgURKiQUolQjUJBW7b8j5 mT9CWiPpZ2qoBfpSZN8MhHSPhRycuj8A+2bBTqjuVaSQe4yFlZHmxz3PcmYpOVEirtKq VXuauTL45uJHKfrWF153GF4SkjsQj/mz3lUynt0uKi0FQKO+TLNGRjuM/qFFAzQlUX4V eoruy5T+nX4NDK7TDZ3oRFukBxNjgoKqm7p8kFn8Q+Plc9+5Sh243x+FiqLvE1o9cJMZ T2nUNAUXG5EbzX0V273RxUrvHhOFYXfDbbF7tfBenG0PcHsHggoUFhAfWejEqxKz7s9s Bcig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=gmirLNjLGfJzW6YYZUOR8avyTXMQpr4Eec+Omnxr9sI=; b=pGpPHwO7eRVm91oT5SoZ+TR4TJ8as6Ie3ZUfNBWrJ8h7ZyAbcxOiAuvkfuZAJiCeTx NFzqXJ7uuR0xveIl56XbwToulu2HRtG1j0j3zP2nMBssFOlzxGplQyxidzEZXcQAH7yI uAu6Xia+lqFEOocq3kB7ksIyHNg7b0CVBvERGlGuOoR/Gz7xDrftL3EnLhBa260G2dKf NKJxvz9iF6hwO5IZfXhnLwHXhaN3+BCmi2bUR1VjnGdiDjtYN/dVsL66qZ1FRQaEpu74 hGtnT07hA6twejxHIkC9GEYgpY6apitPmVN7Tnk7QtgPrneFfvVDUY+OGCalmQ7jweml uYNQ== X-Gm-Message-State: AOAM532KXVcVuA/03ndMaewFgsQg1QYjumF6wm9fuHzU6Q400SEAsz8r VhnKPmsYao3lv3j9AVHyqWM= X-Google-Smtp-Source: ABdhPJyaTGnm7nnQEQih6sRYmOiv45VMCDxaIAPaajssWkrJpJTlAmxvNDLzAAVtX/cFc8r0f3AwXQ== X-Received: by 2002:a17:90a:f08c:b0:1df:6f35:361d with SMTP id cn12-20020a17090af08c00b001df6f35361dmr4331336pjb.161.1652945114466; Thu, 19 May 2022 00:25:14 -0700 (PDT) Received: from localhost.localdomain ([116.89.141.50]) by smtp.gmail.com with ESMTPSA id b12-20020a170902650c00b00161ac982b9esm2974159plk.185.2022.05.19.00.25.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 19 May 2022 00:25:13 -0700 (PDT) From: Medad CChien X-Google-Original-From: Medad CChien To: rric@kernel.org, james.morse@arm.com, tony.luck@intel.com, mchehab@kernel.org, bp@alien8.de, robh+dt@kernel.org, benjaminfair@google.com, yuenn@google.com, venture@google.com, KWLIU@nuvoton.com, YSCHU@nuvoton.com, JJLIU0@nuvoton.com, KFTING@nuvoton.com, avifishman70@gmail.com, tmaimon77@gmail.com, tali.perry1@gmail.com, ctcchien@nuvoton.com Cc: linux-edac@vger.kernel.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, openbmc@lists.ozlabs.org Subject: [PATCH v10 2/3] ARM: dts: nuvoton: Add memory controller node Date: Thu, 19 May 2022 15:24:53 +0800 Message-Id: <20220519072454.24063-3-ctcchien@nuvoton.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220519072454.24063-1-ctcchien@nuvoton.com> References: <20220519072454.24063-1-ctcchien@nuvoton.com> Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" ECC must be configured in the BootBlock header. Then, you can read error counts via the EDAC kernel framework. Signed-off-by: Medad CChien --- arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi b/arch/arm/boot/= dts/nuvoton-common-npcm7xx.dtsi index 3696980a3da1..ba542b26941e 100644 --- a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi +++ b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi @@ -106,6 +106,13 @@ interrupt-parent =3D <&gic>; ranges; =20 + mc: memory-controller@f0824000 { + compatible =3D "nuvoton,npcm750-memory-controller"; + reg =3D <0x0 0xf0824000 0x0 0x1000>; + interrupts =3D ; + status =3D "disabled"; + }; + rstc: rstc@f0801000 { compatible =3D "nuvoton,npcm750-reset"; reg =3D <0xf0801000 0x70>; --=20 2.17.1 From nobody Thu May 7 21:11:39 2026 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 07ACFC4332F for ; Thu, 19 May 2022 07:26:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235116AbiESH0X (ORCPT ); Thu, 19 May 2022 03:26:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43140 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234811AbiESHZX (ORCPT ); Thu, 19 May 2022 03:25:23 -0400 Received: from mail-pj1-x1032.google.com (mail-pj1-x1032.google.com [IPv6:2607:f8b0:4864:20::1032]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 44D4C84A04; Thu, 19 May 2022 00:25:20 -0700 (PDT) Received: by mail-pj1-x1032.google.com with SMTP id nr2-20020a17090b240200b001df2b1bfc40so7978361pjb.5; Thu, 19 May 2022 00:25:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=STqY4wRePjbhrf51yAr7fxvjxuRPwXTkbnjKMBK4/ZY=; b=ccrAUUL2SxKfVL5oOZmbXgmXU4TTDUwirODcwpZDbe+Vny0HZ07t9FqpNLSo1BAw6C OTZd5c8Lz97iK/UxhtFjcGEdlSXe4ocx5vRGHjwv6Diku7QWzEKlEEy5d6lThZpuE7Kd eBjuTqvuWOr9h0s/E+USWqaAR6QmFKQYYsin5/Ngqq27RmwNXiYyzFoNYBYSwx56nIsA v+7yAl2s2YB8FZhhHoH/wD/ebcfnBsJllF54SimxLNfcnsd614W+ptih9UVstiRkoXdU +/lg+62fGk+LxhXFxXIA7PJ2dVKznyt2UKUdaQRlYK8jgx4CcbCRHEZXoEpKDbY/2jsw IaGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=STqY4wRePjbhrf51yAr7fxvjxuRPwXTkbnjKMBK4/ZY=; b=h4QU2lOQK5VevDZ8JuZPHP0i1uQANwLgHsLmN0o8ObmEZo6ETDgW3lQ3Go/OEHs2j+ XjfRxulTSJj6U4EEfH+x0zm8Dvz9CCdsrmvjB14EpYGKgc1951NUK5tslyNl0fRU8IqS 2byfiK+2bSbVBWwo8/M2gIRYdD46cHr7NSoTxI2rTbpm0fia4upyh9dSONQY21dz1hKF ITQ1s08TaBM8ReKSsAJ1Wv8hPcSznSpV/79nv5WMt4zAwIPLJO0atyFH/XyHxnoKn8UE EnNev4VMYbcokE2yIu+8qFARW7Ynt4Ay9MX7bYIhWU7AZ83pFLBQsn3CaEY0pmUOayZu PpiA== X-Gm-Message-State: AOAM533VFFT9T5Nz4IeN08buF4MRA2XGEMuxSPgry60kOBjjwc5YsD9B 6fIoUwji7Ntb4tUKeQuIbgU= X-Google-Smtp-Source: ABdhPJwFHVwK+s0J0LbG1NyqVOnbD2k0Sf1yF+k53ukyXlE3JARhkCunOXOT68H8PrxrVJdxkG5z8w== X-Received: by 2002:a17:90a:5886:b0:1dc:6b58:91e with SMTP id j6-20020a17090a588600b001dc6b58091emr3768263pji.176.1652945119570; Thu, 19 May 2022 00:25:19 -0700 (PDT) Received: from localhost.localdomain ([116.89.141.50]) by smtp.gmail.com with ESMTPSA id b12-20020a170902650c00b00161ac982b9esm2974159plk.185.2022.05.19.00.25.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 19 May 2022 00:25:18 -0700 (PDT) From: Medad CChien X-Google-Original-From: Medad CChien To: rric@kernel.org, james.morse@arm.com, tony.luck@intel.com, mchehab@kernel.org, bp@alien8.de, robh+dt@kernel.org, benjaminfair@google.com, yuenn@google.com, venture@google.com, KWLIU@nuvoton.com, YSCHU@nuvoton.com, JJLIU0@nuvoton.com, KFTING@nuvoton.com, avifishman70@gmail.com, tmaimon77@gmail.com, tali.perry1@gmail.com, ctcchien@nuvoton.com Cc: linux-edac@vger.kernel.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, openbmc@lists.ozlabs.org Subject: [PATCH v10 3/3] EDAC: nuvoton: Add NPCM memory controller driver Date: Thu, 19 May 2022 15:24:54 +0800 Message-Id: <20220519072454.24063-4-ctcchien@nuvoton.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220519072454.24063-1-ctcchien@nuvoton.com> References: <20220519072454.24063-1-ctcchien@nuvoton.com> Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Add memory controller support for Nuvoton NPCM SoC. Note: you can force an ecc event by writing a string to edac sysfs node and remember to define CONFIG_EDAC_DEBUG to enable this feature example: force a correctable event on checkcode bit 0 echo "CE checkcode 0" > /sys/devices/system/edac/mc/mc0/forced_ecc_error Datasheet: Cadence DDR Controller Register Reference Manual For DDR4 Memories Chapter 2: Detailed Register Map Signed-off-by: Medad CChien --- drivers/edac/Kconfig | 17 + drivers/edac/Makefile | 1 + drivers/edac/npcm_edac.c | 680 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 698 insertions(+) create mode 100644 drivers/edac/npcm_edac.c diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 58ab63642e72..aab7ba6c0d15 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -539,4 +539,21 @@ config EDAC_DMC520 Support for error detection and correction on the SoCs with ARM DMC-520 DRAM controller. =20 +config EDAC_NPCM + tristate "Nuvoton NPCM DDR Memory Controller" + depends on (ARCH_NPCM || COMPILE_TEST) + help + Support for error detection and correction on the Nuvoton NPCM DDR + memory controller. + + The Nuvoton BMC SoC supports DDR4 memory with and without ECC (error + correction check). + + The memory controller supports single bit error correction, double bit + error detection (in-line ECC in which a section (1/8th) of the memory + device used to store data is used for ECC storage). + + First, ECC must be configured in the BootBlock header. Then, this driver + will expose error counters via the EDAC kernel framework. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 2d1641a27a28..db3c59d3ad84 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -84,3 +84,4 @@ obj-$(CONFIG_EDAC_QCOM) +=3D qcom_edac.o obj-$(CONFIG_EDAC_ASPEED) +=3D aspeed_edac.o obj-$(CONFIG_EDAC_BLUEFIELD) +=3D bluefield_edac.o obj-$(CONFIG_EDAC_DMC520) +=3D dmc520_edac.o +obj-$(CONFIG_EDAC_NPCM) +=3D npcm_edac.o diff --git a/drivers/edac/npcm_edac.c b/drivers/edac/npcm_edac.c new file mode 100644 index 000000000000..e7cb9bbb9d81 --- /dev/null +++ b/drivers/edac/npcm_edac.c @@ -0,0 +1,680 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 Nuvoton Technology Corporation + +#include +#include + +#include "edac_module.h" + +#define NPCM_EDAC_MOD_NAME "npcm-edac" +#define FORCED_ECC_ERR_EVENT_SUPPORT BIT(1) +#define EDAC_MSG_SIZE 256 +/* Granularity of reported error in bytes */ +#define NPCM_EDAC_ERR_GRAIN 1 + +#define MEM_TYPE_DDR4 0xA + +#define NPCM7XX_CHIP 0x700 +#define NPCM8XX_CHIP 0x800 + +/* Control register width definitions */ +#define WDTH_16 (2) +#define WDTH_32 (1) +#define WDTH_64 (0) +#define CTL_MEM_MAX_WIDTH_MASK GENMASK(4, 0) +#define CTL_REG_WIDTH_SHIFT (32) +#define XOR_CHECK_BIT_SPLIT_WIDTH (16) +#define CTL_CONTROLLER_BUSY_FLAG BIT(0) +#define NPCM_ECC_CTL_FORCE_WC BIT(8) +#define NPCM_ECC_CTL_AUTO_WRITEBACK_EN BIT(24) +#define NPCM_ECC_CTL_XOR_BITS_MASK GENMASK(23, 16) +#define NPCM_ECC_CTL_MTYPE_MASK GENMASK(11, 8) +#define NPCM_ECC_CTL_GLOBAL_INT_DISABLE BIT(31) + +/* Syndrome values */ +#define ECC_DOUBLE_MULTI_ERR_SYND 0x03 + +static char data_synd[] =3D { + 0xf4, 0xf1, 0xec, 0xea, 0xe9, 0xe6, 0xe5, 0xe3, + 0xdc, 0xda, 0xd9, 0xd6, 0xd5, 0xd3, 0xce, 0xcb, + 0xb5, 0xb0, 0xad, 0xab, 0xa8, 0xa7, 0xa4, 0xa2, + 0x9d, 0x9b, 0x98, 0x97, 0x94, 0x92, 0x8f, 0x8a, + 0x75, 0x70, 0x6d, 0x6b, 0x68, 0x67, 0x64, 0x62, + 0x5e, 0x5b, 0x58, 0x57, 0x54, 0x52, 0x4f, 0x4a, + 0x34, 0x31, 0x2c, 0x2a, 0x29, 0x26, 0x25, 0x23, + 0x1c, 0x1a, 0x19, 0x16, 0x15, 0x13, 0x0e, 0x0b +}; + +static char check_synd[] =3D { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0= x80 }; + +struct npcm_edac_platform_data { + /* force ECC event */ + u32 ip_features; + u32 ddr_ctl_controller_busy_reg; + u32 ecc_ctl_xor_check_bits_reg; + + u32 chip; + + /* DDR4 Controller Registers */ + u32 ddr_ctl_mem_type_reg; + u32 ddr_ctl_mem_width_reg; + + u32 ecc_ctl_en_reg; + u32 ecc_ctl_int_mask; + u32 ecc_ctl_int_status; + u32 ecc_ctl_int_ack; + u32 ecc_ctl_int_mask_master; + u32 ecc_ctl_int_mask_ecc; + + u32 ecc_sig_c_addr_l; + u32 ecc_sig_c_addr_h; + u32 ecc_sig_c_data_l; + u32 ecc_sig_c_data_h; + u32 ecc_sig_c_id; + u32 ecc_sig_c_synd; + + u32 ecc_sig_u_addr_l; + u32 ecc_sig_u_addr_h; + u32 ecc_sig_u_data_l; + u32 ecc_sig_u_data_h; + u32 ecc_sig_u_id; + u32 ecc_sig_u_synd; + + /* MASK */ + u32 ecc_ctl_ecc_enable_mask; + u32 ecc_ctl_en_int_master_mask; + u32 ecc_ctl_en_int_ecc_mask; + + /* ECC IRQ Macros */ + u32 ecc_int_ce_event; + u32 ecc_int_second_ce_event; + u32 ecc_int_ue_event; + u32 ecc_int_second_ue_event; + u32 ecc_int_ce_ue_mask; + u32 ecc_ce_intr_mask; + u32 ecc_ue_intr_mask; + + /* ECC Signature Macros */ + u32 ecc_sig_c_id_shift; + u32 ecc_sig_c_synd_shift; + u32 ecc_sig_c_addr_h_mask; + u32 ecc_sig_c_id_mask; + u32 ecc_sig_c_synd_mask; + + u32 ecc_sig_u_id_shift; + u32 ecc_sig_u_synd_shift; + u32 ecc_sig_u_addr_h_mask; + u32 ecc_sig_u_id_mask; + u32 ecc_sig_u_synd_mask; +}; + +struct priv_data { + void __iomem *reg; + u32 ce_cnt; + u32 ue_cnt; + char message[EDAC_MSG_SIZE]; + const struct npcm_edac_platform_data *npcm_chip; +}; + + +static void init_mem_layout(struct mem_ctl_info *mci) +{ + struct priv_data *priv =3D mci->pvt_info; + const struct npcm_edac_platform_data *npcm_chip =3D priv->npcm_chip; + struct csrow_info *csi; + struct dimm_info *dimm; + struct sysinfo info; + enum mem_type mtype; + u32 val, width; + u32 size, row; + int j; + + dimm =3D edac_get_dimm(mci, 0, 0, 0); + + if (dimm) + return; + + si_meminfo(&info); + size =3D info.totalram * info.mem_unit; + for (row =3D 0; row < mci->nr_csrows; row++) { + csi =3D mci->csrows[row]; + + for (j =3D 0; j < csi->nr_channels; j++) { + dimm =3D csi->channels[j]->dimm; + dimm->edac_mode =3D EDAC_FLAG_SECDED; + /* Get memory type by reading hw registers */ + val =3D readl(priv->reg + npcm_chip->ddr_ctl_mem_type_reg); + + mtype =3D val & NPCM_ECC_CTL_MTYPE_MASK; + if (mtype =3D=3D MEM_TYPE_DDR4) + dimm->mtype =3D MEM_DDR4; + else + dimm->mtype =3D MEM_EMPTY; + + /* Get EDAC devtype width for the current mc */ + width =3D readl(priv->reg + npcm_chip->ddr_ctl_mem_width_reg) + & CTL_MEM_MAX_WIDTH_MASK; + switch (width) { + case WDTH_16: + dimm->dtype =3D DEV_X2; + break; + case WDTH_32: + dimm->dtype =3D DEV_X4; + break; + case WDTH_64: + dimm->dtype =3D DEV_X8; + break; + default: + dimm->dtype =3D DEV_UNKNOWN; + } + + dimm->nr_pages =3D (size >> PAGE_SHIFT) / + csi->nr_channels; + dimm->grain =3D NPCM_EDAC_ERR_GRAIN; + } + } +} + + +static void handle_correctable_error(struct mem_ctl_info *mci) +{ + struct priv_data *priv =3D mci->pvt_info; + const struct npcm_edac_platform_data *npcm_chip =3D priv->npcm_chip; + u64 err_c_addr =3D 0x0; + u64 err_c_data =3D 0x0; + u32 err_c_synd, err_c_id; + u32 sig_val_l, sig_val_h =3D 0x0; + + sig_val_l =3D readl(priv->reg + npcm_chip->ecc_sig_c_addr_l); + + if (npcm_chip->chip =3D=3D NPCM8XX_CHIP) + sig_val_h =3D (readl(priv->reg + npcm_chip->ecc_sig_c_addr_h) & + npcm_chip->ecc_sig_c_addr_h_mask); + + err_c_addr =3D (((err_c_addr | sig_val_h) << + CTL_REG_WIDTH_SHIFT) | sig_val_l); + + sig_val_l =3D readl(priv->reg + npcm_chip->ecc_sig_c_data_l); + + if (npcm_chip->chip =3D=3D NPCM8XX_CHIP) + sig_val_h =3D readl(priv->reg + npcm_chip->ecc_sig_c_data_h); + + err_c_data =3D (((err_c_data | sig_val_h) << + CTL_REG_WIDTH_SHIFT) | sig_val_l); + + err_c_id =3D ((readl(priv->reg + npcm_chip->ecc_sig_c_id) & + npcm_chip->ecc_sig_c_id_mask) >> + npcm_chip->ecc_sig_c_id_shift); + + err_c_synd =3D ((readl(priv->reg + npcm_chip->ecc_sig_c_synd) & + npcm_chip->ecc_sig_c_synd_mask) >> + npcm_chip->ecc_sig_c_synd_shift); + + priv->ce_cnt +=3D 1; + + snprintf(priv->message, + EDAC_MSG_SIZE, "DDR ECC %s: data=3D0x%llx source_id=3D%#08x", + mci->ctl_name, err_c_data, err_c_id); + + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + 1, + err_c_addr >> PAGE_SHIFT, + err_c_addr & ~PAGE_MASK, + err_c_synd, 0, 0, -1, + priv->message, ""); +} + +static void handle_ue(struct mem_ctl_info *mci) +{ + struct priv_data *priv =3D mci->pvt_info; + const struct npcm_edac_platform_data *npcm_chip =3D priv->npcm_chip; + u64 err_u_addr =3D 0x0; + u64 err_u_data =3D 0x0; + u32 err_u_synd, err_u_id; + u32 sig_val_l, sig_val_h =3D 0x0; + + sig_val_l =3D readl(priv->reg + npcm_chip->ecc_sig_u_addr_l); + + if (npcm_chip->chip =3D=3D NPCM8XX_CHIP) + sig_val_h =3D (readl(priv->reg + npcm_chip->ecc_sig_u_addr_h) & + npcm_chip->ecc_sig_u_addr_h_mask); + + err_u_addr =3D (((err_u_addr | sig_val_h) << + CTL_REG_WIDTH_SHIFT) | sig_val_l); + + sig_val_l =3D readl(priv->reg + npcm_chip->ecc_sig_u_data_l); + + if (npcm_chip->chip =3D=3D NPCM8XX_CHIP) + sig_val_h =3D readl(priv->reg + npcm_chip->ecc_sig_u_data_h); + + err_u_data =3D (((err_u_data | sig_val_h) << + CTL_REG_WIDTH_SHIFT) | sig_val_l); + + err_u_id =3D ((readl(priv->reg + npcm_chip->ecc_sig_u_id) & + npcm_chip->ecc_sig_u_id_mask) >> + npcm_chip->ecc_sig_u_id_shift); + + err_u_synd =3D ((readl(priv->reg + npcm_chip->ecc_sig_u_synd) & + npcm_chip->ecc_sig_u_synd_mask) >> + npcm_chip->ecc_sig_u_synd_shift); + priv->ue_cnt +=3D 1; + + snprintf(priv->message, EDAC_MSG_SIZE, + "DDR ECC %s: addr=3D0x%llx data=3D0x%llx source_id=3D%#08x", + mci->ctl_name, err_u_addr, err_u_data, err_u_id); + + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + 1, + err_u_addr >> PAGE_SHIFT, + err_u_addr & ~PAGE_MASK, + err_u_synd, 0, 0, -1, + priv->message, ""); +} + +static irqreturn_t edac_ecc_isr(int irq, void *dev_id) +{ + struct mem_ctl_info *mci =3D dev_id; + struct priv_data *priv =3D mci->pvt_info; + const struct npcm_edac_platform_data *npcm_chip =3D priv->npcm_chip; + u32 intr_status; + u32 val; + + /* Check the intr status and confirm ECC error intr */ + intr_status =3D readl(priv->reg + npcm_chip->ecc_ctl_int_status); + + edac_dbg(3, "dev: %s, id: %s: IRQ: %d, interrupt status: 0x%x\n", + mci->dev_name, mci->ctl_name, irq, intr_status); + + val =3D intr_status & npcm_chip->ecc_int_ce_ue_mask; + if (!((val & npcm_chip->ecc_ce_intr_mask) || (val & npcm_chip->ecc_ue_int= r_mask))) + return IRQ_NONE; + + if (val & npcm_chip->ecc_ce_intr_mask) { + handle_correctable_error(mci); + + /* Clear the interrupt source */ + if (val & npcm_chip->ecc_int_ce_event) + writel(npcm_chip->ecc_int_ce_event, priv->reg + npcm_chip->ecc_ctl_int_= ack); + else if (val & npcm_chip->ecc_int_second_ce_event) + writel(npcm_chip->ecc_int_second_ce_event, + priv->reg + npcm_chip->ecc_ctl_int_ack); + else + edac_printk(KERN_ERR, NPCM_EDAC_MOD_NAME, "Failed to clear CE IRQ\n"); + } + + if (val & npcm_chip->ecc_ue_intr_mask) { + handle_ue(mci); + + /* Clear the interrupt source */ + if (val & npcm_chip->ecc_int_ue_event) + writel(npcm_chip->ecc_int_ue_event, priv->reg + npcm_chip->ecc_ctl_int_= ack); + else if (val & npcm_chip->ecc_int_second_ue_event) + writel(npcm_chip->ecc_int_second_ue_event, + priv->reg + npcm_chip->ecc_ctl_int_ack); + else + edac_printk(KERN_ERR, NPCM_EDAC_MOD_NAME, "Failed to clear UE IRQ\n"); + } + + edac_dbg(3, "Total error count CE %d UE %d\n", + priv->ce_cnt, priv->ue_cnt); + + return IRQ_HANDLED; +} + +static ssize_t forced_ecc_error_show(struct device *dev, + struct device_attribute *mattr, + char *data) +{ + return sprintf(data, "CDNS-DDR4 Force Injection Help:\n" + "Example:\n" + "echo \"CE checkcode 0\" > /sys/devices/system/edac/mc/mc0/forced= _ecc_error\n" + "CE: Corrected\n" + "UE: Uncorrected\n" + "checkcode/data:source\n" + "bit [0-63] for data [0-7] for checkcode:bit number\n"); +} + +static ssize_t forced_ecc_error_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) +{ + struct mem_ctl_info *mci =3D to_mci(dev); + struct priv_data *priv =3D mci->pvt_info; + const struct npcm_edac_platform_data *npcm_chip =3D priv->npcm_chip; + int args_cnt; + int ret; + char **args; + u32 regval; + u8 bit_no; + + /* Check ecc enabled */ + if (!(readl(priv->reg + npcm_chip->ecc_ctl_en_reg) & npcm_chip->ecc_ctl_e= cc_enable_mask)) + return count; + + /* Check no write operation pending to controller */ + while (readl(priv->reg + npcm_chip->ddr_ctl_controller_busy_reg) & + CTL_CONTROLLER_BUSY_FLAG) { + usleep_range(1000, 10000); + } + + /* Split string buffer into separate parameters */ + args =3D argv_split(GFP_KERNEL, data, &args_cnt); + + /* Write appropriate syndrome to xor_check_bit */ + if (!strcmp(args[0], "CE") && args_cnt =3D=3D 3) { + ret =3D kstrtou8(args[2], 0, &bit_no); + if (ret) + return ret; + if (!strcmp(args[1], "checkcode")) { + if (bit_no > 7) { + edac_printk(KERN_INFO, NPCM_EDAC_MOD_NAME, "bit_no for checkcode must = be 0~7\n"); + return count; + } + regval =3D readl(priv->reg + npcm_chip->ecc_ctl_xor_check_bits_reg); + regval =3D (regval & ~(NPCM_ECC_CTL_XOR_BITS_MASK)) | + (check_synd[bit_no] << XOR_CHECK_BIT_SPLIT_WIDTH); + writel(regval, priv->reg + npcm_chip->ecc_ctl_xor_check_bits_reg); + } else if (!strcmp(args[1], "data")) { + if (bit_no > 63) { + edac_printk(KERN_INFO, NPCM_EDAC_MOD_NAME, "bit_no for data must be 0~= 63\n"); + return count; + } + regval =3D readl(priv->reg + npcm_chip->ecc_ctl_xor_check_bits_reg); + regval =3D (regval & ~(NPCM_ECC_CTL_XOR_BITS_MASK)) | + (data_synd[bit_no] << XOR_CHECK_BIT_SPLIT_WIDTH); + writel(regval, priv->reg + npcm_chip->ecc_ctl_xor_check_bits_reg); + } + /* Enable the ECC writeback_en for corrected error */ + regval =3D readl(priv->reg + npcm_chip->ecc_ctl_xor_check_bits_reg); + writel((regval | NPCM_ECC_CTL_AUTO_WRITEBACK_EN), + priv->reg + npcm_chip->ecc_ctl_xor_check_bits_reg); + } else if (!strcmp(args[0], "UE")) { + regval =3D readl(priv->reg + npcm_chip->ecc_ctl_xor_check_bits_reg); + regval =3D (regval & ~(NPCM_ECC_CTL_XOR_BITS_MASK)) | + (ECC_DOUBLE_MULTI_ERR_SYND << XOR_CHECK_BIT_SPLIT_WIDTH); + writel(regval, priv->reg + npcm_chip->ecc_ctl_xor_check_bits_reg); + } + + /* Assert fwc */ + writel((NPCM_ECC_CTL_FORCE_WC | readl(priv->reg + npcm_chip->ecc_ctl_xor_= check_bits_reg)), + priv->reg + npcm_chip->ecc_ctl_xor_check_bits_reg); + + return count; +} + +static DEVICE_ATTR_RW(forced_ecc_error); +static int create_sysfs_attributes(struct mem_ctl_info *mci) +{ + int rc; + + rc =3D device_create_file(&mci->dev, &dev_attr_forced_ecc_error); + if (rc < 0) + return rc; + return 0; +} + +static void remove_sysfs_attributes(struct mem_ctl_info *mci) +{ + device_remove_file(&mci->dev, &dev_attr_forced_ecc_error); +} + +static const struct npcm_edac_platform_data npcm7xx_edac =3D { + .chip =3D NPCM7XX_CHIP, + + /* CDNS DDR4 Controller Registers */ + .ecc_ctl_en_reg =3D 0x174, + .ecc_ctl_int_status =3D 0x1D0, + .ecc_ctl_int_ack =3D 0x1D4, + .ecc_ctl_int_mask_master =3D 0x1D8, + + .ecc_sig_c_addr_l =3D 0x188, + .ecc_sig_c_data_l =3D 0x190, + .ecc_sig_c_id =3D 0x194, + .ecc_sig_c_synd =3D 0x18C, + + .ecc_sig_u_addr_l =3D 0x17C, + .ecc_sig_u_data_l =3D 0x184, + .ecc_sig_u_id =3D 0x194, + .ecc_sig_u_synd =3D 0x180, + + /* MASK */ + .ecc_ctl_ecc_enable_mask =3D BIT(24), + .ecc_ctl_en_int_master_mask =3D GENMASK(30, 7) | GENMASK(2, 0), + + /* ECC IRQ Macros */ + .ecc_int_ce_event =3D BIT(3), + .ecc_int_second_ce_event =3D BIT(4), + .ecc_int_ue_event =3D BIT(5), + .ecc_int_second_ue_event =3D BIT(6), + .ecc_int_ce_ue_mask =3D GENMASK(6, 3), + .ecc_ce_intr_mask =3D GENMASK(4, 3), + .ecc_ue_intr_mask =3D GENMASK(6, 5), + + /* ECC Signature Macros */ + .ecc_sig_c_id_shift =3D 16, + .ecc_sig_c_synd_shift =3D 0, + + .ecc_sig_c_id_mask =3D GENMASK(29, 16), + .ecc_sig_c_synd_mask =3D GENMASK(6, 0), + + .ecc_sig_u_id_shift =3D 0, + .ecc_sig_u_synd_shift =3D 0, + + .ecc_sig_u_id_mask =3D GENMASK(13, 0), + .ecc_sig_u_synd_mask =3D GENMASK(6, 0), +}; + +static const struct npcm_edac_platform_data npcm8xx_edac =3D { + .ip_features =3D FORCED_ECC_ERR_EVENT_SUPPORT, + .ddr_ctl_controller_busy_reg =3D 0x20C, + .ecc_ctl_xor_check_bits_reg =3D 0x174, + + .chip =3D NPCM8XX_CHIP, + + /* CDNS DDR4 Controller Registers */ + .ddr_ctl_mem_type_reg =3D 0x000, + .ddr_ctl_mem_width_reg =3D 0x00c, + + .ecc_ctl_en_reg =3D 0x16C, + .ecc_ctl_int_status =3D 0x228, + .ecc_ctl_int_ack =3D 0x244, + .ecc_ctl_int_mask_master =3D 0x220, + .ecc_ctl_int_mask_ecc =3D 0x260, + + .ecc_sig_c_addr_l =3D 0x18C, + .ecc_sig_c_addr_h =3D 0x190, + .ecc_sig_c_data_l =3D 0x194, + .ecc_sig_c_data_h =3D 0x198, + .ecc_sig_c_id =3D 0x19C, + .ecc_sig_c_synd =3D 0x190, + + .ecc_sig_u_addr_l =3D 0x17C, + .ecc_sig_u_addr_h =3D 0x180, + .ecc_sig_u_data_l =3D 0x184, + .ecc_sig_u_data_h =3D 0x188, + .ecc_sig_u_id =3D 0x19C, + .ecc_sig_u_synd =3D 0x180, + + /* MASK */ + .ecc_ctl_ecc_enable_mask =3D GENMASK(17, 16), + .ecc_ctl_en_int_master_mask =3D GENMASK(30, 3) | GENMASK(1, 0), + .ecc_ctl_en_int_ecc_mask =3D GENMASK(8, 4), + + /* ECC IRQ Macros */ + .ecc_int_ce_event =3D BIT(0), + .ecc_int_second_ce_event =3D BIT(1), + .ecc_int_ue_event =3D BIT(2), + .ecc_int_second_ue_event =3D BIT(3), + .ecc_int_ce_ue_mask =3D GENMASK(3, 0), + .ecc_ce_intr_mask =3D GENMASK(1, 0), + .ecc_ue_intr_mask =3D GENMASK(3, 2), + + /* ECC Signature Macros */ + .ecc_sig_c_id_shift =3D 8, + .ecc_sig_c_synd_shift =3D 8, + .ecc_sig_c_addr_h_mask =3D GENMASK(1, 0), + .ecc_sig_c_id_mask =3D GENMASK(29, 16), + .ecc_sig_c_synd_mask =3D GENMASK(15, 8), + + .ecc_sig_u_id_shift =3D 0, + .ecc_sig_u_synd_shift =3D 8, + .ecc_sig_u_addr_h_mask =3D GENMASK(1, 0), + .ecc_sig_u_id_mask =3D GENMASK(13, 0), + .ecc_sig_u_synd_mask =3D GENMASK(15, 8), +}; + +static const struct of_device_id npcm_edac_of_match[] =3D { + { .compatible =3D "nuvoton,npcm750-memory-controller", .data =3D &npcm7xx= _edac }, + { .compatible =3D "nuvoton,npcm845-memory-controller", .data =3D &npcm8xx= _edac }, + {}, +}; + +MODULE_DEVICE_TABLE(of, npcm_edac_of_match); + +static int npcm_edac_mc_probe(struct platform_device *pdev) +{ + const struct npcm_edac_platform_data *npcm_chip; + struct device *dev =3D &pdev->dev; + struct edac_mc_layer layers[1]; + const struct of_device_id *id; + struct priv_data *priv_data; + struct mem_ctl_info *mci; + struct resource *res; + void __iomem *reg; + int ret =3D -ENODEV; + int irq; + u32 ecc_en; + + id =3D of_match_device(npcm_edac_of_match, &pdev->dev); + + npcm_chip =3D of_device_get_match_data(&pdev->dev); + + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg =3D devm_ioremap_resource(dev, res); + if (IS_ERR(reg)) { + edac_printk(KERN_ERR, NPCM_EDAC_MOD_NAME, "cdns DDR4 mc regs are not def= ined\n"); + return PTR_ERR(reg); + } + + ecc_en =3D readl(reg + npcm_chip->ecc_ctl_en_reg); + + if ((ecc_en & npcm_chip->ecc_ctl_ecc_enable_mask) =3D=3D npcm_chip->ecc_c= tl_ecc_enable_mask) { + edac_printk(KERN_INFO, NPCM_EDAC_MOD_NAME, "ECC reporting and correcting= on. "); + } else { + edac_printk(KERN_INFO, NPCM_EDAC_MOD_NAME, "ECC disabled\n"); + return -ENXIO; + } + + edac_printk(KERN_INFO, NPCM_EDAC_MOD_NAME, "IO mapped reg addr: %p\n", re= g); + + layers[0].type =3D EDAC_MC_LAYER_ALL_MEM; + layers[0].size =3D 1; + + mci =3D edac_mc_alloc(0, ARRAY_SIZE(layers), layers, + sizeof(struct priv_data)); + if (!mci) { + edac_printk(KERN_ERR, NPCM_EDAC_MOD_NAME, "Failed memory allocation for = mc instance\n"); + return -ENOMEM; + } + mci->pdev =3D &pdev->dev; + priv_data =3D mci->pvt_info; + priv_data->reg =3D reg; + priv_data->npcm_chip =3D npcm_chip; + priv_data->ce_cnt =3D 0; + priv_data->ue_cnt =3D 0; + platform_set_drvdata(pdev, mci); + + /* Initialize controller capabilities */ + mci->mtype_cap =3D MEM_FLAG_DDR4; + mci->edac_ctl_cap =3D EDAC_FLAG_SECDED; + mci->scrub_cap =3D SCRUB_FLAG_HW_SRC; + mci->scrub_mode =3D SCRUB_HW_SRC; + mci->edac_cap =3D EDAC_FLAG_SECDED; + mci->ctl_name =3D id->compatible; + mci->dev_name =3D dev_name(&pdev->dev); + mci->mod_name =3D NPCM_EDAC_MOD_NAME; + mci->ctl_page_to_phys =3D NULL; + + /* Interrupt feature is supported by cadence mc */ + edac_op_state =3D EDAC_OPSTATE_INT; + if (IS_ENABLED(CONFIG_EDAC_DEBUG)) + init_mem_layout(mci); + + /* Set up Interrupt handler for ECC */ + irq =3D platform_get_irq(pdev, 0); + if (irq < 0) { + edac_printk(KERN_ERR, NPCM_EDAC_MOD_NAME, "irq number not defined for EC= C.\n"); + goto err; + } + ret =3D devm_request_irq(dev, irq, edac_ecc_isr, 0, "cdns-edac-mc-ecc-irq= ", mci); + if (ret) { + edac_printk(KERN_ERR, NPCM_EDAC_MOD_NAME, "request_irq fail for NPCM_EDA= C irq\n"); + goto err; + } + ret =3D edac_mc_add_mc(mci); + if (ret) { + edac_printk(KERN_ERR, NPCM_EDAC_MOD_NAME, "Failed to register with EDAC = core\n"); + goto err; + } + + if (IS_ENABLED(CONFIG_EDAC_DEBUG) && + (npcm_chip->ip_features & FORCED_ECC_ERR_EVENT_SUPPORT) && + npcm_chip->chip =3D=3D NPCM8XX_CHIP) { + if (create_sysfs_attributes(mci)) + edac_printk(KERN_ERR, NPCM_EDAC_MOD_NAME, "Failed to create sysfs entri= es\n"); + } + + /* Only enable MC interrupts with ECC - clear global int mask bit and ecc= bit */ + writel(npcm_chip->ecc_ctl_en_int_master_mask, + priv_data->reg + npcm_chip->ecc_ctl_int_mask_master); + + if (npcm_chip->chip =3D=3D NPCM8XX_CHIP) { + /* clear single and multi for ce and ue */ + writel(npcm_chip->ecc_ctl_en_int_ecc_mask, + priv_data->reg + npcm_chip->ecc_ctl_int_mask_ecc); + } + + return 0; + + edac_mc_del_mc(&pdev->dev); + +err: + edac_mc_free(mci); + return ret; +} + +static int npcm_edac_mc_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci =3D platform_get_drvdata(pdev); + struct priv_data *priv =3D mci->pvt_info; + const struct npcm_edac_platform_data *npcm_chip =3D priv->npcm_chip; + + writel(NPCM_ECC_CTL_GLOBAL_INT_DISABLE, priv->reg + npcm_chip->ecc_ctl_in= t_mask_master); + + /* Disable ecc feature before removing driver by writing 0 */ + writel((unsigned int)(~(npcm_chip->ecc_ctl_ecc_enable_mask)), + priv->reg + npcm_chip->ecc_ctl_en_reg); + + if (IS_ENABLED(CONFIG_EDAC_DEBUG)) + remove_sysfs_attributes(mci); + + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); + + return 0; +} + +static struct platform_driver npcm_edac_mc_driver =3D { + .driver =3D { + .name =3D "npcm-edac", + .of_match_table =3D npcm_edac_of_match, + }, + .probe =3D npcm_edac_mc_probe, + .remove =3D npcm_edac_mc_remove, +}; + +module_platform_driver(npcm_edac_mc_driver); + +MODULE_AUTHOR("Medad CChien "); +MODULE_DESCRIPTION("Nuvoton NPCM EDAC Driver"); +MODULE_LICENSE("GPL v2"); --=20 2.17.1