From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965275AbbJ0RcH (ORCPT ); Tue, 27 Oct 2015 13:32:07 -0400 Received: from mail-bl2on0057.outbound.protection.outlook.com ([65.55.169.57]:55399 "EHLO na01-bl2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S965207AbbJ0Rbz (ORCPT ); Tue, 27 Oct 2015 13:31:55 -0400 Authentication-Results: spf=none (sender IP is 165.204.84.221) smtp.mailfrom=amd.com; alien8.de; dkim=none (message not signed) header.d=none;alien8.de; dmarc=permerror action=none header.from=amd.com; X-WSS-ID: 0NWW3CX-07-0GM-02 X-M-MSG: From: Brijesh Singh To: CC: Brijesh Singh , , , , , , , , , , , , , , , Subject: [PATCH v3] EDAC: Add ARM64 EDAC Date: Tue, 27 Oct 2015 12:29:45 -0500 Message-ID: <1445966985-5777-1-git-send-email-brijeshkumar.singh@amd.com> X-Mailer: git-send-email 1.9.1 MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.180.168.240] X-EOPAttributedMessage: 0 X-Forefront-Antispam-Report: CIP:165.204.84.221;CTRY:US;IPV:NLI;EFV:NLI;SFV:NSPM;SFS:(10009020)(6009001)(2980300002)(428002)(189002)(199003)(5004730100002)(42186005)(33646002)(97736004)(48376002)(87936001)(36756003)(50466002)(92566002)(5007970100001)(5003940100001)(105586002)(50986999)(229853001)(106466001)(19580405001)(2351001)(101416001)(5008740100001)(19580395003)(189998001)(86362001)(11100500001)(575784001)(110136002)(47776003)(50226001)(2004002);DIR:OUT;SFP:1101;SCL:1;SRVR:BLUPR12MB0707;H:atltwp01.amd.com;FPR:;SPF:None;PTR:InfoDomainNonexistent;MX:1;A:1;LANG:en; X-Microsoft-Exchange-Diagnostics: 1;BLUPR12MB0707;2:rOD6y9jr9rEr9z2WO7nwT+7mk+KxiTwcRIyJnF+keoxw+wFtEWyBBQmd06r161mNK57ZjZKRULD1Cu5yycFXKgg+c5Jq4OsB11JDotGmSqCmJXwQ5sm3CudaO/7fd1hHjWA5nOFKiZ3dfCPhE/t1Z2GFb4qH2qpnu0XcxDlUNf0=;3:LD1mRK0Zamdky14mS849vwQqtMwbCewiN5g3Aj0K8ZAJ49Q6Nl222prr8jArFBFy5FVEOMDNI3PjWuCketf9HSscDF8YrjHe/6etRTT/fAou0jD+ANoYT4Dz2oQopKOYbRsf2rBg1/NBVJ3he/wkfdL8LCVMeKX50GoE3Rv93Zs96+k1qC8wHLaXKVHltOeL1jbEwT2LIHbetV6jiuTfPI94Ub75VZNDy9MUxlBpNGkhbx2e+v2C1Mmn07dpRvD8;25:8Icy3KZqRDw23XabyQP8dH/3k5iLlBeWTXO5GR3d5H4JkYoPzGFav3+WILHYPmU7/KTfhNGNnWLqR08HgsWaoY6kGjVWUGm1trbilvlFzUvtyXY0OtxXh1gqoK5I4+BAvYQveF0KjyO/Yr7k3K0XdcZJC/0iDmeXoQsVX/GY7HozTmm3vKDnGWVqFZH2xoX4iUQs6/falU+MRFfS3WHEgTT6/hfrBGwBm0CzafmUU/7LQ9x1t53Y8I+cNm3aU4TcvcrZLdyJN3HQ1YPGzv+dOA== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BLUPR12MB0707; X-Microsoft-Exchange-Diagnostics: 1;BLUPR12MB0707;20:NW0Ev35cLcB/PpgavRbnmWi7Yb0cyyzVInhuh2iCQ44SfpT+NvKyOJtmF45RBoML0a5cGiL/eJdo6cEQr0cwOP3ZMziZTgiVK87VB3PX3R79iIKf1YTc/upue52oHE9J2Bre3niu/OS+riioW1Vq/mi6kv/3g2xXzVwIyubShqb47y1FGTeq+YmRWoyMYy6kbK7nSDVDPsJh1sDwQqpoT6ECnisWwwp8bMkSAMhTepum+MWLAuHV4iBh6oXPuyg/f4dKHMBa15Y6lAEWu2yjf2nIbk9MInzg0P3FXogeBHTesSLZog2JtIvJVaBCYtKfLpyZlikmWEsVr+kU2LS2HSpc5XaN6oFCm6yaa32jsVj2TlyiVUCf+tXKvDSQ3j6zu39y0+3Q7NGPSo8qYGX91D6DUN3uSktAJDzlJSmP21fKc22dDGmIIYaspxkMk8EXf8CMPphmqgCB6TgpawIMAqgLEFJI7Nzd2xO47CBuvBTAlxCN/8Q/UICvSgsW46KK;4:Ghi3nkH4mO/XMIDc6lQOw/OPJrH0QuOOKmG46oP6p8hBEFU5YxgMRaXpvAeMLTimhBynBNUvuEGKx5kM/zP8p3X/Ez6Vf+MRKZgRFdUMOM3U/gKp80wTuq4QaPNNFCEg5rXZpoARMjxHw1rv1hAcQLzfAdkDfEcjwR3XxK1yjwC1uiX2zgx/kSU007eW6kQMCNIDoxK7S8/R/F5RKOl9OVr5sDg3k69baOij0FqFVc2Ij+C1g+3Kk8p05RAP/8pPZ7R5roXnHCxLtElsh7NR94aq/pzOinIJz3yd46PMtdmGPcv7OB7lgU+9hn9hTieC0fG2gKaz1ml8+PmypC5lb4ZPQT99laq3UID/KCzNcfow8Fo9RvEzWQEzJfys1aiT X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(767451399110); X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(601004)(2401047)(520078)(5005006)(8121501046)(3002001)(102215026);SRVR:BLUPR12MB0707;BCL:0;PCL:0;RULEID:;SRVR:BLUPR12MB0707; X-Forefront-PRVS: 0742443479 X-Microsoft-Exchange-Diagnostics: 1;BLUPR12MB0707;23:XutU5Aljnw0gDs61fUKMWosPRpohzk5dI0rgZ+2crQbaF56SU2VId2UUkgnL4DizXQ15d0to0+/XmgfEmrNKlPTOhNZ/ILpAZE0safPIVPdWbBxfJEW21Y+h1TyzRmgOPbk9iwLtOdnkuxmuaco8gnbHtlEQlT83eDbXlb2km2mK7U4VBwpOKX8NHV0gFFoexic5/SKtF7nQfNJEfbJPgBeN0xjCourl3ANQudwI8XU05v8g7B4eqFJgsiyX3orZPSrnIw7vwSqR71cmaav1tVJGNkE0T06rtbB3KsRHyWW+sf8rWkKy4nV3OPQ+jbXP+fgQu5S1Yv9+nmh/xLHAdww9ZiQsvUJFQt/gOQUnMWamItTWplVsO9Tsn+khqDZYAywZvF3ez9EUctH4homNFXPeSF5K3V+ZWo17SE0RDKikK1EnWfUJObaAV6LlCDA/gK9SFLFAokdcoCjn5VGgS8bU/oz5FShSwpa30K2t1S0hWd4bXPOOht07FHyYhI0GoOwORgHjKVqsTmbJQ5Z9XbV9jkUy7Ibwul4SgRCdfmD39nBrDtjpWuX6cdJ3iP9B+Qqw6zyJgHmzHI87mB/ZCjtUT35i1vDqokBJUSkW+PFiLSWNAXMiJIPvQto+hq6DMiyNjenMp0nLPEq1qExouSGxvRn6t8iaHcLXnDDj3aQmCtG5ihzuYTi+cU7Pjw5gu78sdnrchz8SAtBdvdrUZm1icAhD9AxBp8dbbLb2S33tXzMtURdvV4GVIUEeyHai22PlCoWubu8ZWyafmwkzGZMJQJHdyUUK3v7/BLB2H2QTeC9/kkNbSDHq9EtGgaOKNrpolgYFiKW965kgN98lGkFjilVXMbKDJrKGtIpag4TEHIK3FvQVsCIynPli49Maj8Mm2iQHS09PMT2IkpWfWXyD+yR8XGgRItFcW7BJuUs= X-Microsoft-Exchange-Diagnostics: 1;BLUPR12MB0707;5:qcShfp9CJ0uSEPMs0KjdQM51yER/uBX8xKn5n96XTnk10a2MH2B7JaUdGmpdsspK+GLTtTGCnCa27sW7OE2msS/05+OGhXvw/FmOQVG05iQRkSydHPwBZFXwzryAOvkgck+ub2ByhBMdlnVVoC/AQQ==;24:Zn7gqobfRrjHcfdyA61SbyPavwW7jNQYasvGlQWFLktiPvOfNqLMRZw44i2WAxYsTl9bVjxUfbCsoBk8hY4bAvPTPQFJ5aBuPdQASl/MtSc=;20:k7GRq9aF4tS+DsKcbnfYK60sps8G8ituF7K0xOIh4uy1n0rZs8BxU0Lubhb4wP6cVCs6Kqrt990MHiK8bvzBiYWqu+NzNIvzI5SZSpGUBJihMTq+9AX3lIq07lVIXhf7yLo+im5jzRLFt6v1UrqKwyieVF/m/SHNeGTisltwNyzvFcPoXaJOFxZGowwuVG7eArTs9DA4ILNij7PbQfFRrfpWKpeigL8H1FIOx3OVH4TirnM6hzLzSWAUbVLbJZXJ SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 27 Oct 2015 17:31:47.0888 (UTC) X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=3dd8961f-e488-4e60-8e11-a82d994e183d;Ip=[165.204.84.221];Helo=[atltwp01.amd.com] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BLUPR12MB0707 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add support for Cortex A57 and A53 EDAC driver. Signed-off-by: Brijesh Singh CC: robh+dt@kernel.org CC: pawel.moll@arm.com CC: mark.rutland@arm.com CC: ijc+devicetree@hellion.org.uk CC: galak@codeaurora.org CC: dougthompson@xmission.com CC: bp@alien8.de CC: mchehab@osg.samsung.com CC: devicetree@vger.kernel.org CC: guohanjun@huawei.com CC: andre.przywara@arm.com CC: arnd@arndb.de CC: sboyd@codeaurora.org CC: linux-kernel@vger.kernel.org CC: linux-edac@vger.kernel.org --- v3: * change DT binding compatibilty string to 'cortex-a57-edac' * remove A57/A53 prefix from register bit definition * unify A57 and A53 L1/L2 error parsing functions * use mc trace event function to report the error * update Kconfig default to 'n' v2: * convert into generic arm64 edac driver * remove AMD specific references from dt binding * remove poll_msec property from dt binding * add poll_msec as a module param, default is 100ms * update copyright text * define macro mnemonics for L1 and L2 RAMID * check L2 error per-cluster instead of per core * update function names * use get_online_cpus() and put_online_cpus() to make L1 and L2 register read hotplug-safe * add error check in probe routine .../devicetree/bindings/edac/cortex-arm64-edac.txt | 15 + drivers/edac/Kconfig | 8 + drivers/edac/Makefile | 1 + drivers/edac/cortex_arm64_edac.c | 349 +++++++++++++++++++++ 4 files changed, 373 insertions(+) create mode 100644 Documentation/devicetree/bindings/edac/cortex-arm64-edac.txt create mode 100644 drivers/edac/cortex_arm64_edac.c diff --git a/Documentation/devicetree/bindings/edac/cortex-arm64-edac.txt b/Documentation/devicetree/bindings/edac/cortex-arm64-edac.txt new file mode 100644 index 0000000..552f0c7 --- /dev/null +++ b/Documentation/devicetree/bindings/edac/cortex-arm64-edac.txt @@ -0,0 +1,15 @@ +* ARM Cortex A57 and A53 L1/L2 cache error reporting + +CPU Memory Error Syndrome and L2 Memory Error Syndrome registers can be used +for checking L1 and L2 memory errors. + +The following section describes the Cortex A57/A53 EDAC DT node binding. + +Required properties: +- compatible: Should be "arm,cortex-a57-edac" or "arm,cortex-a53-edac" + +Example: + edac { + compatible = "arm,cortex-a57-edac"; + }; + diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index ef25000..84507b5 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -390,4 +390,12 @@ config EDAC_XGENE Support for error detection and correction on the APM X-Gene family of SOCs. +config EDAC_CORTEX_ARM64 + tristate "ARM Cortex A57/A53" + depends on EDAC_MM_EDAC && ARM64 + default n + help + Support for error detection and correction on the + ARM Cortex A57 and A53. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index ae3c5f3..ac01660 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -68,3 +68,4 @@ obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o obj-$(CONFIG_EDAC_ALTERA_MC) += altera_edac.o obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o +obj-$(CONFIG_EDAC_CORTEX_ARM64) += cortex_arm64_edac.o diff --git a/drivers/edac/cortex_arm64_edac.c b/drivers/edac/cortex_arm64_edac.c new file mode 100644 index 0000000..7c936b66 --- /dev/null +++ b/drivers/edac/cortex_arm64_edac.c @@ -0,0 +1,349 @@ +/* + * Cortex A57 and A53 EDAC + * + * Copyright (c) 2015, Advanced Micro Devices + * Author: Brijesh Singh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "edac_core.h" + +#define DRV_NAME "cortex_edac" + +#define CPUMERRSR_EL1_INDEX(x, y) ((x) & (y)) +#define CPUMERRSR_EL1_BANK_WAY(x, y) (((x) >> 18) & (y)) +#define CPUMERRSR_EL1_RAMID(x) (((x) >> 24) & 0x7f) +#define CPUMERRSR_EL1_VALID(x) ((x) & (1 << 31)) +#define CPUMERRSR_EL1_REPEAT(x) (((x) >> 32) & 0x7f) +#define CPUMERRSR_EL1_OTHER(x) (((x) >> 40) & 0xff) +#define CPUMERRSR_EL1_FATAL(x) ((x) & (1UL << 63)) +#define L1_I_TAG_RAM 0x00 +#define L1_I_DATA_RAM 0x01 +#define L1_D_TAG_RAM 0x08 +#define L1_D_DATA_RAM 0x09 +#define L1_D_DIRTY_RAM 0x14 +#define TLB_RAM 0x18 + +#define L2MERRSR_EL1_CPUID_WAY(x) (((x) >> 18) & 0xf) +#define L2MERRSR_EL1_RAMID(x) (((x) >> 24) & 0x7f) +#define L2MERRSR_EL1_VALID(x) ((x) & (1 << 31)) +#define L2MERRSR_EL1_REPEAT(x) (((x) >> 32) & 0xff) +#define L2MERRSR_EL1_OTHER(x) (((x) >> 40) & 0xff) +#define L2MERRSR_EL1_FATAL(x) ((x) & (1UL << 63)) +#define L2_TAG_RAM 0x10 +#define L2_DATA_RAM 0x11 +#define L2_SNOOP_RAM 0x12 +#define L2_DIRTY_RAM 0x14 +#define L2_INCLUSION_PF_RAM 0x18 + +#define L1_CACHE 0 +#define L2_CACHE 1 + +#define EDAC_MOD_STR DRV_NAME + +static int poll_msec = 100; + +struct cortex_arm64_edac { + struct edac_device_ctl_info *edac_ctl; +}; + +static inline u64 read_cpumerrsr_el1(void) +{ + u64 val; + + asm volatile("mrs %0, s3_1_c15_c2_2" : "=r" (val)); + return val; +} + +static inline void write_cpumerrsr_el1(u64 val) +{ + asm volatile("msr s3_1_c15_c2_2, %0" :: "r" (val)); +} + +static inline u64 read_l2merrsr_el1(void) +{ + u64 val; + + asm volatile("mrs %0, s3_1_c15_c2_3" : "=r" (val)); + return val; +} + +static inline void write_l2merrsr_el1(u64 val) +{ + asm volatile("msr s3_1_c15_c2_3, %0" :: "r" (val)); +} + +static void parse_cpumerrsr(void *arg) +{ + int cpu, partnum, way; + unsigned int index = 0; + u64 val = read_cpumerrsr_el1(); + int repeat_err, other_err; + + /* we do not support fatal error handling so far */ + if (CPUMERRSR_EL1_FATAL(val)) + return; + + /* check if we have valid error before continuing */ + if (!CPUMERRSR_EL1_VALID(val)) + return; + + cpu = smp_processor_id(); + partnum = read_cpuid_part_number(); + repeat_err = CPUMERRSR_EL1_REPEAT(val); + other_err = CPUMERRSR_EL1_OTHER(val); + + /* way/bank and index address bit ranges are different between + * A57 and A53 */ + if (partnum == ARM_CPU_PART_CORTEX_A57) { + index = CPUMERRSR_EL1_INDEX(val, 0x1ffff); + way = CPUMERRSR_EL1_BANK_WAY(val, 0x1f); + } else { + index = CPUMERRSR_EL1_INDEX(val, 0xfff); + way = CPUMERRSR_EL1_BANK_WAY(val, 0x7); + } + + edac_printk(KERN_CRIT, EDAC_MOD_STR, "CPU%d L1 error detected!\n", cpu); + edac_printk(KERN_CRIT, EDAC_MOD_STR, "index=%#x, RAMID=", index); + + switch (CPUMERRSR_EL1_RAMID(val)) { + case L1_I_TAG_RAM: + pr_cont("'L1-I Tag RAM' (way %d)", way); + break; + case L1_I_DATA_RAM: + pr_cont("'L1-I Data RAM' (bank %d)", way); + break; + case L1_D_TAG_RAM: + pr_cont("'L1-D Tag RAM' (way %d)", way); + break; + case L1_D_DATA_RAM: + pr_cont("'L1-D Data RAM' (bank %d)", way); + break; + case L1_D_DIRTY_RAM: + pr_cont("'L1 Dirty RAM'"); + break; + case TLB_RAM: + pr_cont("'TLB RAM'"); + break; + default: + pr_cont("'unknown'"); + break; + } + + pr_cont(", repeat=%d, other=%d (CPUMERRSR_EL1=%#llx)\n", repeat_err, + other_err, val); + + trace_mc_event(HW_EVENT_ERR_CORRECTED, "L1 non-fatal error", + "", repeat_err, 0, 0, 0, -1, index, 0, 0, DRV_NAME); + write_cpumerrsr_el1(0); +} + +static void a57_parse_l2merrsr_way(u8 ramid, u8 val) +{ + switch (ramid) { + case L2_TAG_RAM: + case L2_DATA_RAM: + case L2_DIRTY_RAM: + pr_cont("(cpu%d tag, way %d)", val / 2, val % 2); + break; + case L2_SNOOP_RAM: + pr_cont("(cpu%d tag, way %d)", (val & 0x6) >> 1, + (val & 0x1)); + break; + } +} + +static void a53_parse_l2merrsr_way(u8 ramid, u8 val) +{ + switch (ramid) { + case L2_TAG_RAM: + pr_cont("(way %d)", val); + case L2_DATA_RAM: + pr_cont("(bank %d)", val); + break; + case L2_SNOOP_RAM: + pr_cont("(cpu%d tag, way %d)", val / 2, val % 4); + break; + } +} + +static void parse_l2merrsr(void *arg) +{ + int cpu, partnum; + unsigned int index; + int repeat_err, other_err; + u64 val = read_l2merrsr_el1(); + + /* we do not support fatal error handling so far */ + if (L2MERRSR_EL1_FATAL(val)) + return; + + /* check if we have valid error before continuing */ + if (!L2MERRSR_EL1_VALID(val)) + return; + + cpu = smp_processor_id(); + partnum = read_cpuid_part_number(); + repeat_err = L2MERRSR_EL1_REPEAT(val); + other_err = L2MERRSR_EL1_OTHER(val); + + /* index address range is different between A57 and A53 */ + if (partnum == ARM_CPU_PART_CORTEX_A57) + index = val & 0x1ffff; + else + index = (val >> 3) & 0x3fff; + + edac_printk(KERN_CRIT, EDAC_MOD_STR, "CPU%d L2 error detected!\n", cpu); + edac_printk(KERN_CRIT, EDAC_MOD_STR, "index=%#x RAMID=", index); + + switch (L2MERRSR_EL1_RAMID(val)) { + case L2_TAG_RAM: + pr_cont("'L2 Tag RAM'"); + break; + case L2_DATA_RAM: + pr_cont("'L2 Data RAM'"); + break; + case L2_SNOOP_RAM: + pr_cont("'L2 Snoop tag RAM'"); + break; + case L2_DIRTY_RAM: + pr_cont("'L2 Dirty RAM'"); + break; + case L2_INCLUSION_PF_RAM: + pr_cont("'L2 inclusion PF RAM'"); + break; + default: + pr_cont("unknown"); + break; + } + + /* cpuid/way bit description is different between A57 and A53 */ + if (partnum == ARM_CPU_PART_CORTEX_A57) + a57_parse_l2merrsr_way(L2MERRSR_EL1_RAMID(val), + L2MERRSR_EL1_CPUID_WAY(val)); + else + a53_parse_l2merrsr_way(L2MERRSR_EL1_RAMID(val), + L2MERRSR_EL1_CPUID_WAY(val)); + + pr_cont(", repeat=%d, other=%d (L2MERRSR_EL1=%#llx)\n", repeat_err, + other_err, val); + trace_mc_event(HW_EVENT_ERR_CORRECTED, "L2 non-fatal error", + "", repeat_err, 0, 0, 0, -1, index, 0, 0, DRV_NAME); + write_l2merrsr_el1(0); +} + +static void cortex_arm64_edac_check(struct edac_device_ctl_info *edac_ctl) +{ + int cpu; + struct cpumask cluster_mask, old_mask; + + cpumask_clear(&cluster_mask); + cpumask_clear(&old_mask); + + get_online_cpus(); + for_each_online_cpu(cpu) { + /* Check CPU L1 error */ + smp_call_function_single(cpu, parse_cpumerrsr, NULL, 0); + cpumask_copy(&cluster_mask, topology_core_cpumask(cpu)); + if (cpumask_equal(&cluster_mask, &old_mask)) + continue; + cpumask_copy(&old_mask, &cluster_mask); + /* Check CPU L2 error */ + smp_call_function_any(&cluster_mask, parse_l2merrsr, NULL, 0); + } + put_online_cpus(); +} + +static int cortex_arm64_edac_probe(struct platform_device *pdev) +{ + int rc; + struct cortex_arm64_edac *drv; + struct device *dev = &pdev->dev; + + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + /* Only POLL mode is supported */ + edac_op_state = EDAC_OPSTATE_POLL; + + drv->edac_ctl = edac_device_alloc_ctl_info(0, "cpu", + num_possible_cpus(), "L", 2, + 1, NULL, 0, + edac_device_alloc_index()); + if (IS_ERR(drv->edac_ctl)) + return -ENOMEM; + + drv->edac_ctl->poll_msec = poll_msec; + drv->edac_ctl->edac_check = cortex_arm64_edac_check; + drv->edac_ctl->dev = dev; + drv->edac_ctl->mod_name = dev_name(dev); + drv->edac_ctl->dev_name = dev_name(dev); + drv->edac_ctl->ctl_name = "cache"; + platform_set_drvdata(pdev, drv); + + rc = edac_device_add_device(drv->edac_ctl); + if (rc) + edac_device_free_ctl_info(drv->edac_ctl); + + return rc; +} + +static int cortex_arm64_edac_remove(struct platform_device *pdev) +{ + struct cortex_arm64_edac *drv = dev_get_drvdata(&pdev->dev); + struct edac_device_ctl_info *edac_ctl = drv->edac_ctl; + + edac_device_del_device(edac_ctl->dev); + edac_device_free_ctl_info(edac_ctl); + + return 0; +} + +static const struct of_device_id cortex_arm64_edac_of_match[] = { + { .compatible = "arm,cortex-a57-edac" }, + { .compatible = "arm,cortex-a53-edac" }, + {} +}; +MODULE_DEVICE_TABLE(of, cortex_arm64_edac_of_match); + +static struct platform_driver cortex_arm64_edac_driver = { + .probe = cortex_arm64_edac_probe, + .remove = cortex_arm64_edac_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = cortex_arm64_edac_of_match, + }, +}; + +static int __init cortex_arm64_edac_init(void) +{ + return platform_driver_register(&cortex_arm64_edac_driver); +} +module_init(cortex_arm64_edac_init); + +static void __exit cortex_arm64_edac_exit(void) +{ + platform_driver_unregister(&cortex_arm64_edac_driver); +} +module_exit(cortex_arm64_edac_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Brijesh Singh "); +MODULE_DESCRIPTION("Cortex A57 and A53 EDAC driver"); +module_param(poll_msec, int, 0444); +MODULE_PARM_DESC(poll_msec, "EDAC monitor poll interval in msec"); -- 1.9.1