From: Stephen Boyd <sboyd@codeaurora.org> To: Borislav Petkov <bp@alien8.de> Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-edac@vger.kernel.org, Stepan Moskovchenko <stepanm@codeaurora.org> Subject: [PATCH v6 4/5] edac: Add support for Krait CPU cache error detection Date: Fri, 4 Apr 2014 12:57:29 -0700 [thread overview] Message-ID: <1396641450-12854-5-git-send-email-sboyd@codeaurora.org> (raw) In-Reply-To: <1396641450-12854-1-git-send-email-sboyd@codeaurora.org> Add support for the Krait CPU cache error detection. This is a simplified version of the code originally written by Stepan Moskovchenko[1] ported to the EDAC device framework. [1] https://www.codeaurora.org/cgit/quic/la/kernel/msm/tree/arch/arm/mach-msm/cache_erp.c?h=msm-3.4 Cc: Stepan Moskovchenko <stepanm@codeaurora.org> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> --- drivers/edac/Kconfig | 8 + drivers/edac/Makefile | 2 + drivers/edac/krait_edac.c | 370 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 380 insertions(+) create mode 100644 drivers/edac/krait_edac.c diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 878f09005fad..4dae3d353ea9 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -368,4 +368,12 @@ config EDAC_OCTEON_PCI Support for error detection and correction on the Cavium Octeon family of SOCs. +config EDAC_KRAIT_CACHE + tristate "Krait L1/L2 Cache" + depends on EDAC_MM_EDAC && ARM && OF + select KRAIT_L2_ACCESSORS + help + Support for error detection and correction on the + Krait L1/L2 cache controller. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 4154ed6a02c6..b6ea50564223 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -64,3 +64,5 @@ obj-$(CONFIG_EDAC_OCTEON_PC) += octeon_edac-pc.o obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o + +obj-$(CONFIG_EDAC_KRAIT_CACHE) += krait_edac.o diff --git a/drivers/edac/krait_edac.c b/drivers/edac/krait_edac.c new file mode 100644 index 000000000000..90ec0e982927 --- /dev/null +++ b/drivers/edac/krait_edac.c @@ -0,0 +1,370 @@ +/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/cpu.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> + +#include <asm/krait-l2-accessors.h> + +#include "edac_core.h" + +#define CESR_DCTPE BIT(0) +#define CESR_DCDPE BIT(1) +#define CESR_ICTPE BIT(2) +#define CESR_ICDPE BIT(3) +#define CESR_DCTE (BIT(4) | BIT(5)) +#define CESR_ICTE (BIT(6) | BIT(7)) +#define CESR_TLBMH BIT(16) +#define CESR_I_MASK 0x000000cc +/* Print a message for everything but TLB MH events */ +#define CESR_PRINT_MASK 0x000000ff + +#define L2ESR 0x204 +#define L2ESR_MPDCD BIT(0) +#define L2ESR_MPSLV BIT(1) +#define L2ESR_TSESB BIT(2) +#define L2ESR_TSEDB BIT(3) +#define L2ESR_DSESB BIT(4) +#define L2ESR_DSEDB BIT(5) +#define L2ESR_MSE BIT(6) +#define L2ESR_MPLDREXNOK BIT(8) +#define L2ESR_CPU_MASK 0xf +#define L2ESR_CPU_SHIFT 16 +#define L2ESR_SP BIT(20) + +#define L2ESYNR0 0x208 +#define L2ESYNR1 0x209 +#define L2EAR0 0x20c +#define L2EAR1 0x20d + +struct krait_edac { + int l1_irq; + struct edac_device_ctl_info * __percpu *edev; + struct notifier_block notifier; +}; + +struct krait_edac_error { + const char * const msg; + void (*func)(struct edac_device_ctl_info *edac_dev, + int inst_nr, int block_nr, const char *msg); +}; + +static unsigned int read_cesr(void) +{ + unsigned int cesr; + + asm volatile ("mrc p15, 7, %0, c15, c0, 1 @ cesr" : "=r" (cesr)); + return cesr; +} + +static void write_cesr(unsigned int cesr) +{ + asm volatile ("mcr p15, 7, %0, c15, c0, 1 @ cesr" : : "r" (cesr)); +} + +static unsigned int read_cesynr(void) +{ + unsigned int cesynr; + + asm volatile ("mrc p15, 7, %0, c15, c0, 3 @ cesynr" : "=r" (cesynr)); + return cesynr; +} + +static irqreturn_t krait_l1_irq(int irq, void *dev_id) +{ + struct edac_device_ctl_info **edac_p = dev_id; + struct edac_device_ctl_info *edac = *edac_p; + unsigned int cesr = read_cesr(); + unsigned int i_cesynr, d_cesynr; + unsigned int cpu = smp_processor_id(); + int print_regs = cesr & CESR_PRINT_MASK; + int i; + static const struct krait_edac_error errors[] = { + { "D-cache tag parity error", edac_device_handle_ue }, + { "D-cache data parity error", edac_device_handle_ue }, + { "I-cache tag parity error", edac_device_handle_ce }, + { "I-cache data parity error", edac_device_handle_ce }, + { "D-cache tag timing error", edac_device_handle_ue }, + { "D-cache data timing error", edac_device_handle_ue }, + { "I-cache tag timing error", edac_device_handle_ce }, + { "I-cache data timing error", edac_device_handle_ce } + }; + + if (print_regs) { + pr_alert("L1 / TLB Error detected on CPU %d!\n", cpu); + pr_alert("CESR = 0x%08x\n", cesr); + } + + for (i = 0; i < ARRAY_SIZE(errors); i++) + if (BIT(i) & cesr) + errors[i].func(edac, cpu, 0, errors[i].msg); + + if (cesr & CESR_TLBMH) { + asm ("mcr p15, 0, r0, c8, c7, 0"); + edac_device_handle_ce(edac, cpu, 0, "TLB Multi-Hit error"); + } + + if (cesr & (CESR_ICTPE | CESR_ICDPE | CESR_ICTE)) { + i_cesynr = read_cesynr(); + pr_alert("I-side CESYNR = 0x%08x\n", i_cesynr); + write_cesr(CESR_I_MASK); + + /* + * Clear the I-side bits from the captured CESR value so that we + * don't accidentally clear any new I-side errors when we do + * the CESR write-clear operation. + */ + cesr &= ~CESR_I_MASK; + } + + if (cesr & (CESR_DCTPE | CESR_DCDPE | CESR_DCTE)) { + d_cesynr = read_cesynr(); + pr_alert("D-side CESYNR = 0x%08x\n", d_cesynr); + } + + /* Clear the interrupt bits we processed */ + write_cesr(cesr); + + return IRQ_HANDLED; +} + +static irqreturn_t krait_l2_irq(int irq, void *dev_id) +{ + struct edac_device_ctl_info *edac = dev_id; + unsigned int l2esr; + unsigned int l2esynr0; + unsigned int l2esynr1; + unsigned int l2ear0; + unsigned int l2ear1; + unsigned long cpu; + int i; + static const struct krait_edac_error errors[] = { + { "master port decode error", edac_device_handle_ce }, + { "master port slave error", edac_device_handle_ce }, + { "tag soft error, single-bit", edac_device_handle_ce }, + { "tag soft error, double-bit", edac_device_handle_ue }, + { "data soft error, single-bit", edac_device_handle_ce }, + { "data soft error, double-bit", edac_device_handle_ue }, + { "modified soft error", edac_device_handle_ce }, + { "slave port exclusive monitor not available", + edac_device_handle_ue}, + { "master port LDREX received Normal OK response", + edac_device_handle_ce }, + }; + + l2esr = krait_get_l2_indirect_reg(L2ESR); + pr_alert("Error detected!\n"); + pr_alert("L2ESR = 0x%08x\n", l2esr); + + if (l2esr & (L2ESR_TSESB | L2ESR_TSEDB | L2ESR_MSE | L2ESR_SP)) { + l2esynr0 = krait_get_l2_indirect_reg(L2ESYNR0); + l2esynr1 = krait_get_l2_indirect_reg(L2ESYNR1); + l2ear0 = krait_get_l2_indirect_reg(L2EAR0); + l2ear1 = krait_get_l2_indirect_reg(L2EAR1); + + pr_alert("L2ESYNR0 = 0x%08x\n", l2esynr0); + pr_alert("L2ESYNR1 = 0x%08x\n", l2esynr1); + pr_alert("L2EAR0 = 0x%08x\n", l2ear0); + pr_alert("L2EAR1 = 0x%08x\n", l2ear1); + } + + cpu = (l2esr >> L2ESR_CPU_SHIFT) & L2ESR_CPU_MASK; + cpu = __ffs(cpu); + if (cpu) + cpu--; + for (i = 0; i < ARRAY_SIZE(errors); i++) + if (BIT(i) & l2esr) + errors[i].func(edac, cpu, 1, errors[i].msg); + + krait_set_l2_indirect_reg(L2ESR, l2esr); + + return IRQ_HANDLED; +} + +static void enable_l1_irq(void *info) +{ + const struct krait_edac *k = info; + + enable_percpu_irq(k->l1_irq, IRQ_TYPE_LEVEL_HIGH); +} + +static void disable_l1_irq(void *info) +{ + const struct krait_edac *k = info; + + disable_percpu_irq(k->l1_irq); +} + +static int +krait_edac_notify(struct notifier_block *nfb, unsigned long action, void *hcpu) +{ + struct krait_edac *p = container_of(nfb, struct krait_edac, notifier); + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_STARTING: + enable_l1_irq(p); + break; + + case CPU_DYING: + disable_l1_irq(p); + break; + } + return NOTIFY_OK; +} + +static int krait_edac_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct edac_device_ctl_info *edev; + struct krait_edac *p; + int l1_irq, l2_irq; + int ret, cpu; + struct device_node *cpunode, *l1node, *l2node; + + cpunode = of_get_cpu_node(0, NULL); + if (!cpunode) + return -ENODEV; + + l1node = of_parse_phandle(cpunode, "next-level-cache", 0); + of_node_put(cpunode); + if (!l1node) + return -ENODEV; + + l1_irq = irq_of_parse_and_map(l1node, 0); + l2node = of_parse_phandle(l1node, "next-level-cache", 0); + of_node_put(l1node); + if (!l2node) + return -ENODEV; + + l2_irq = irq_of_parse_and_map(l2node, 0); + of_node_put(l2node); + + if (l1_irq < 0) + return l1_irq; + if (l2_irq < 0) + return l2_irq; + + p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + platform_set_drvdata(pdev, p); + p->l1_irq = l1_irq; + + p->edev = alloc_percpu(struct edac_device_ctl_info *); + if (!p->edev) + return -ENOMEM; + + edev = edac_device_alloc_ctl_info(0, "cpu", num_possible_cpus(), + "L", 2, 1, NULL, 0, + edac_device_alloc_index()); + if (!edev) { + ret = -ENOMEM; + goto err_alloc; + } + + edev->dev = dev; + edev->mod_name = dev_name(dev); + edev->dev_name = dev_name(dev); + edev->ctl_name = "cache"; + + for_each_possible_cpu(cpu) + *per_cpu_ptr(p->edev, cpu) = edev; + + ret = edac_device_add_device(edev); + if (ret) + goto err_add; + + ret = request_percpu_irq(l1_irq, krait_l1_irq, "L1 err", + p->edev); + if (ret) + goto err_l1_irq; + + ret = devm_request_irq(dev, l2_irq, krait_l2_irq, 0, "L2 err", + edev); + if (ret) + goto err_l2_irq; + + p->notifier.notifier_call = krait_edac_notify; + register_hotcpu_notifier(&p->notifier); + on_each_cpu(enable_l1_irq, p, true); + + return 0; +err_l2_irq: + free_percpu_irq(p->l1_irq, p->edev); +err_l1_irq: + edac_device_del_device(dev); +err_add: + edac_device_free_ctl_info(edev); +err_alloc: + free_percpu(p->edev); + return ret; +} + +static int krait_edac_remove(struct platform_device *pdev) +{ + struct krait_edac *p = platform_get_drvdata(pdev); + + unregister_hotcpu_notifier(&p->notifier); + on_each_cpu(disable_l1_irq, p, true); + free_percpu_irq(p->l1_irq, p->edev); + edac_device_del_device(&pdev->dev); + edac_device_free_ctl_info(*__this_cpu_ptr(p->edev)); + free_percpu(p->edev); + + return 0; +} + +static struct platform_driver krait_edac_driver = { + .probe = krait_edac_probe, + .remove = krait_edac_remove, + .driver = { + .name = "krait_edac", + .owner = THIS_MODULE, + }, +}; + +static struct platform_device *krait_edacp; + +static int __init krait_edac_driver_init(void) +{ + struct device_node *np; + + np = of_get_cpu_node(0, NULL); + if (!np) + return 0; + + if (!krait_edacp && of_device_is_compatible(np, "qcom,krait")) + krait_edacp = of_platform_device_create(np, "krait_edac", NULL); + of_node_put(np); + + return platform_driver_register(&krait_edac_driver); +} +module_init(krait_edac_driver_init); + +static void __exit krait_edac_driver_exit(void) +{ + platform_driver_unregister(&krait_edac_driver); + platform_device_unregister(krait_edacp); +} +module_exit(krait_edac_driver_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Krait CPU cache error reporting driver"); +MODULE_ALIAS("platform:krait_edac"); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by The Linux Foundation
WARNING: multiple messages have this Message-ID (diff)
From: sboyd@codeaurora.org (Stephen Boyd) To: linux-arm-kernel@lists.infradead.org Subject: [PATCH v6 4/5] edac: Add support for Krait CPU cache error detection Date: Fri, 4 Apr 2014 12:57:29 -0700 [thread overview] Message-ID: <1396641450-12854-5-git-send-email-sboyd@codeaurora.org> (raw) In-Reply-To: <1396641450-12854-1-git-send-email-sboyd@codeaurora.org> Add support for the Krait CPU cache error detection. This is a simplified version of the code originally written by Stepan Moskovchenko[1] ported to the EDAC device framework. [1] https://www.codeaurora.org/cgit/quic/la/kernel/msm/tree/arch/arm/mach-msm/cache_erp.c?h=msm-3.4 Cc: Stepan Moskovchenko <stepanm@codeaurora.org> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> --- drivers/edac/Kconfig | 8 + drivers/edac/Makefile | 2 + drivers/edac/krait_edac.c | 370 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 380 insertions(+) create mode 100644 drivers/edac/krait_edac.c diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 878f09005fad..4dae3d353ea9 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -368,4 +368,12 @@ config EDAC_OCTEON_PCI Support for error detection and correction on the Cavium Octeon family of SOCs. +config EDAC_KRAIT_CACHE + tristate "Krait L1/L2 Cache" + depends on EDAC_MM_EDAC && ARM && OF + select KRAIT_L2_ACCESSORS + help + Support for error detection and correction on the + Krait L1/L2 cache controller. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 4154ed6a02c6..b6ea50564223 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -64,3 +64,5 @@ obj-$(CONFIG_EDAC_OCTEON_PC) += octeon_edac-pc.o obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o + +obj-$(CONFIG_EDAC_KRAIT_CACHE) += krait_edac.o diff --git a/drivers/edac/krait_edac.c b/drivers/edac/krait_edac.c new file mode 100644 index 000000000000..90ec0e982927 --- /dev/null +++ b/drivers/edac/krait_edac.c @@ -0,0 +1,370 @@ +/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/cpu.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> + +#include <asm/krait-l2-accessors.h> + +#include "edac_core.h" + +#define CESR_DCTPE BIT(0) +#define CESR_DCDPE BIT(1) +#define CESR_ICTPE BIT(2) +#define CESR_ICDPE BIT(3) +#define CESR_DCTE (BIT(4) | BIT(5)) +#define CESR_ICTE (BIT(6) | BIT(7)) +#define CESR_TLBMH BIT(16) +#define CESR_I_MASK 0x000000cc +/* Print a message for everything but TLB MH events */ +#define CESR_PRINT_MASK 0x000000ff + +#define L2ESR 0x204 +#define L2ESR_MPDCD BIT(0) +#define L2ESR_MPSLV BIT(1) +#define L2ESR_TSESB BIT(2) +#define L2ESR_TSEDB BIT(3) +#define L2ESR_DSESB BIT(4) +#define L2ESR_DSEDB BIT(5) +#define L2ESR_MSE BIT(6) +#define L2ESR_MPLDREXNOK BIT(8) +#define L2ESR_CPU_MASK 0xf +#define L2ESR_CPU_SHIFT 16 +#define L2ESR_SP BIT(20) + +#define L2ESYNR0 0x208 +#define L2ESYNR1 0x209 +#define L2EAR0 0x20c +#define L2EAR1 0x20d + +struct krait_edac { + int l1_irq; + struct edac_device_ctl_info * __percpu *edev; + struct notifier_block notifier; +}; + +struct krait_edac_error { + const char * const msg; + void (*func)(struct edac_device_ctl_info *edac_dev, + int inst_nr, int block_nr, const char *msg); +}; + +static unsigned int read_cesr(void) +{ + unsigned int cesr; + + asm volatile ("mrc p15, 7, %0, c15, c0, 1 @ cesr" : "=r" (cesr)); + return cesr; +} + +static void write_cesr(unsigned int cesr) +{ + asm volatile ("mcr p15, 7, %0, c15, c0, 1 @ cesr" : : "r" (cesr)); +} + +static unsigned int read_cesynr(void) +{ + unsigned int cesynr; + + asm volatile ("mrc p15, 7, %0, c15, c0, 3 @ cesynr" : "=r" (cesynr)); + return cesynr; +} + +static irqreturn_t krait_l1_irq(int irq, void *dev_id) +{ + struct edac_device_ctl_info **edac_p = dev_id; + struct edac_device_ctl_info *edac = *edac_p; + unsigned int cesr = read_cesr(); + unsigned int i_cesynr, d_cesynr; + unsigned int cpu = smp_processor_id(); + int print_regs = cesr & CESR_PRINT_MASK; + int i; + static const struct krait_edac_error errors[] = { + { "D-cache tag parity error", edac_device_handle_ue }, + { "D-cache data parity error", edac_device_handle_ue }, + { "I-cache tag parity error", edac_device_handle_ce }, + { "I-cache data parity error", edac_device_handle_ce }, + { "D-cache tag timing error", edac_device_handle_ue }, + { "D-cache data timing error", edac_device_handle_ue }, + { "I-cache tag timing error", edac_device_handle_ce }, + { "I-cache data timing error", edac_device_handle_ce } + }; + + if (print_regs) { + pr_alert("L1 / TLB Error detected on CPU %d!\n", cpu); + pr_alert("CESR = 0x%08x\n", cesr); + } + + for (i = 0; i < ARRAY_SIZE(errors); i++) + if (BIT(i) & cesr) + errors[i].func(edac, cpu, 0, errors[i].msg); + + if (cesr & CESR_TLBMH) { + asm ("mcr p15, 0, r0, c8, c7, 0"); + edac_device_handle_ce(edac, cpu, 0, "TLB Multi-Hit error"); + } + + if (cesr & (CESR_ICTPE | CESR_ICDPE | CESR_ICTE)) { + i_cesynr = read_cesynr(); + pr_alert("I-side CESYNR = 0x%08x\n", i_cesynr); + write_cesr(CESR_I_MASK); + + /* + * Clear the I-side bits from the captured CESR value so that we + * don't accidentally clear any new I-side errors when we do + * the CESR write-clear operation. + */ + cesr &= ~CESR_I_MASK; + } + + if (cesr & (CESR_DCTPE | CESR_DCDPE | CESR_DCTE)) { + d_cesynr = read_cesynr(); + pr_alert("D-side CESYNR = 0x%08x\n", d_cesynr); + } + + /* Clear the interrupt bits we processed */ + write_cesr(cesr); + + return IRQ_HANDLED; +} + +static irqreturn_t krait_l2_irq(int irq, void *dev_id) +{ + struct edac_device_ctl_info *edac = dev_id; + unsigned int l2esr; + unsigned int l2esynr0; + unsigned int l2esynr1; + unsigned int l2ear0; + unsigned int l2ear1; + unsigned long cpu; + int i; + static const struct krait_edac_error errors[] = { + { "master port decode error", edac_device_handle_ce }, + { "master port slave error", edac_device_handle_ce }, + { "tag soft error, single-bit", edac_device_handle_ce }, + { "tag soft error, double-bit", edac_device_handle_ue }, + { "data soft error, single-bit", edac_device_handle_ce }, + { "data soft error, double-bit", edac_device_handle_ue }, + { "modified soft error", edac_device_handle_ce }, + { "slave port exclusive monitor not available", + edac_device_handle_ue}, + { "master port LDREX received Normal OK response", + edac_device_handle_ce }, + }; + + l2esr = krait_get_l2_indirect_reg(L2ESR); + pr_alert("Error detected!\n"); + pr_alert("L2ESR = 0x%08x\n", l2esr); + + if (l2esr & (L2ESR_TSESB | L2ESR_TSEDB | L2ESR_MSE | L2ESR_SP)) { + l2esynr0 = krait_get_l2_indirect_reg(L2ESYNR0); + l2esynr1 = krait_get_l2_indirect_reg(L2ESYNR1); + l2ear0 = krait_get_l2_indirect_reg(L2EAR0); + l2ear1 = krait_get_l2_indirect_reg(L2EAR1); + + pr_alert("L2ESYNR0 = 0x%08x\n", l2esynr0); + pr_alert("L2ESYNR1 = 0x%08x\n", l2esynr1); + pr_alert("L2EAR0 = 0x%08x\n", l2ear0); + pr_alert("L2EAR1 = 0x%08x\n", l2ear1); + } + + cpu = (l2esr >> L2ESR_CPU_SHIFT) & L2ESR_CPU_MASK; + cpu = __ffs(cpu); + if (cpu) + cpu--; + for (i = 0; i < ARRAY_SIZE(errors); i++) + if (BIT(i) & l2esr) + errors[i].func(edac, cpu, 1, errors[i].msg); + + krait_set_l2_indirect_reg(L2ESR, l2esr); + + return IRQ_HANDLED; +} + +static void enable_l1_irq(void *info) +{ + const struct krait_edac *k = info; + + enable_percpu_irq(k->l1_irq, IRQ_TYPE_LEVEL_HIGH); +} + +static void disable_l1_irq(void *info) +{ + const struct krait_edac *k = info; + + disable_percpu_irq(k->l1_irq); +} + +static int +krait_edac_notify(struct notifier_block *nfb, unsigned long action, void *hcpu) +{ + struct krait_edac *p = container_of(nfb, struct krait_edac, notifier); + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_STARTING: + enable_l1_irq(p); + break; + + case CPU_DYING: + disable_l1_irq(p); + break; + } + return NOTIFY_OK; +} + +static int krait_edac_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct edac_device_ctl_info *edev; + struct krait_edac *p; + int l1_irq, l2_irq; + int ret, cpu; + struct device_node *cpunode, *l1node, *l2node; + + cpunode = of_get_cpu_node(0, NULL); + if (!cpunode) + return -ENODEV; + + l1node = of_parse_phandle(cpunode, "next-level-cache", 0); + of_node_put(cpunode); + if (!l1node) + return -ENODEV; + + l1_irq = irq_of_parse_and_map(l1node, 0); + l2node = of_parse_phandle(l1node, "next-level-cache", 0); + of_node_put(l1node); + if (!l2node) + return -ENODEV; + + l2_irq = irq_of_parse_and_map(l2node, 0); + of_node_put(l2node); + + if (l1_irq < 0) + return l1_irq; + if (l2_irq < 0) + return l2_irq; + + p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + platform_set_drvdata(pdev, p); + p->l1_irq = l1_irq; + + p->edev = alloc_percpu(struct edac_device_ctl_info *); + if (!p->edev) + return -ENOMEM; + + edev = edac_device_alloc_ctl_info(0, "cpu", num_possible_cpus(), + "L", 2, 1, NULL, 0, + edac_device_alloc_index()); + if (!edev) { + ret = -ENOMEM; + goto err_alloc; + } + + edev->dev = dev; + edev->mod_name = dev_name(dev); + edev->dev_name = dev_name(dev); + edev->ctl_name = "cache"; + + for_each_possible_cpu(cpu) + *per_cpu_ptr(p->edev, cpu) = edev; + + ret = edac_device_add_device(edev); + if (ret) + goto err_add; + + ret = request_percpu_irq(l1_irq, krait_l1_irq, "L1 err", + p->edev); + if (ret) + goto err_l1_irq; + + ret = devm_request_irq(dev, l2_irq, krait_l2_irq, 0, "L2 err", + edev); + if (ret) + goto err_l2_irq; + + p->notifier.notifier_call = krait_edac_notify; + register_hotcpu_notifier(&p->notifier); + on_each_cpu(enable_l1_irq, p, true); + + return 0; +err_l2_irq: + free_percpu_irq(p->l1_irq, p->edev); +err_l1_irq: + edac_device_del_device(dev); +err_add: + edac_device_free_ctl_info(edev); +err_alloc: + free_percpu(p->edev); + return ret; +} + +static int krait_edac_remove(struct platform_device *pdev) +{ + struct krait_edac *p = platform_get_drvdata(pdev); + + unregister_hotcpu_notifier(&p->notifier); + on_each_cpu(disable_l1_irq, p, true); + free_percpu_irq(p->l1_irq, p->edev); + edac_device_del_device(&pdev->dev); + edac_device_free_ctl_info(*__this_cpu_ptr(p->edev)); + free_percpu(p->edev); + + return 0; +} + +static struct platform_driver krait_edac_driver = { + .probe = krait_edac_probe, + .remove = krait_edac_remove, + .driver = { + .name = "krait_edac", + .owner = THIS_MODULE, + }, +}; + +static struct platform_device *krait_edacp; + +static int __init krait_edac_driver_init(void) +{ + struct device_node *np; + + np = of_get_cpu_node(0, NULL); + if (!np) + return 0; + + if (!krait_edacp && of_device_is_compatible(np, "qcom,krait")) + krait_edacp = of_platform_device_create(np, "krait_edac", NULL); + of_node_put(np); + + return platform_driver_register(&krait_edac_driver); +} +module_init(krait_edac_driver_init); + +static void __exit krait_edac_driver_exit(void) +{ + platform_driver_unregister(&krait_edac_driver); + platform_device_unregister(krait_edacp); +} +module_exit(krait_edac_driver_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Krait CPU cache error reporting driver"); +MODULE_ALIAS("platform:krait_edac"); -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by The Linux Foundation
next prev parent reply other threads:[~2014-04-04 19:57 UTC|newest] Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top 2014-04-04 19:57 [PATCH v6 0/5] Krait L1/L2 EDAC driver Stephen Boyd 2014-04-04 19:57 ` Stephen Boyd 2014-04-04 19:57 ` [PATCH v6 1/5] genirq: export percpu irq functions for module usage Stephen Boyd 2014-04-04 19:57 ` Stephen Boyd 2014-04-04 19:57 ` Stephen Boyd 2014-04-04 19:57 ` [PATCH v6 2/5] ARM: Add Krait L2 register accessor functions Stephen Boyd 2014-04-04 19:57 ` Stephen Boyd 2014-04-07 20:18 ` Borislav Petkov 2014-04-07 20:18 ` Borislav Petkov 2014-04-07 21:56 ` Stephen Boyd 2014-04-07 21:56 ` Stephen Boyd 2014-04-08 6:43 ` Borislav Petkov 2014-04-08 6:43 ` Borislav Petkov 2014-04-08 14:25 ` Christopher Covington 2014-04-08 14:25 ` Christopher Covington 2014-04-08 15:10 ` Borislav Petkov 2014-04-08 15:10 ` Borislav Petkov 2014-04-08 16:19 ` One Thousand Gnomes 2014-04-08 16:19 ` One Thousand Gnomes 2014-04-08 16:42 ` Borislav Petkov 2014-04-08 16:42 ` Borislav Petkov 2014-04-04 19:57 ` [PATCH v6 3/5] devicetree: bindings: Document Krait cache error interrupts Stephen Boyd 2014-04-04 19:57 ` Stephen Boyd 2014-04-04 19:57 ` Stephen Boyd 2014-04-08 15:39 ` Borislav Petkov 2014-04-08 15:39 ` Borislav Petkov 2014-04-08 19:55 ` Stephen Boyd 2014-04-08 19:55 ` Stephen Boyd [not found] ` <20140408153925.GJ30077-fF5Pk5pvG8Y@public.gmane.org> 2014-04-29 10:34 ` Lorenzo Pieralisi 2014-04-29 10:34 ` Lorenzo Pieralisi 2014-04-29 10:34 ` Lorenzo Pieralisi 2014-04-29 19:02 ` Borislav Petkov 2014-04-29 19:02 ` Borislav Petkov 2014-04-29 19:02 ` Borislav Petkov 2014-04-04 19:57 ` Stephen Boyd [this message] 2014-04-04 19:57 ` [PATCH v6 4/5] edac: Add support for Krait CPU cache error detection Stephen Boyd 2014-04-08 17:35 ` Borislav Petkov 2014-04-08 17:35 ` Borislav Petkov 2014-04-08 19:54 ` Stephen Boyd 2014-04-08 19:54 ` Stephen Boyd 2014-04-09 15:24 ` Borislav Petkov 2014-04-09 15:24 ` Borislav Petkov 2014-04-04 19:57 ` [PATCH v6 5/5] ARM: dts: msm: Fix Krait CPU/L2 nodes Stephen Boyd 2014-04-04 19:57 ` Stephen Boyd
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=1396641450-12854-5-git-send-email-sboyd@codeaurora.org \ --to=sboyd@codeaurora.org \ --cc=bp@alien8.de \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-arm-msm@vger.kernel.org \ --cc=linux-edac@vger.kernel.org \ --cc=linux-kernel@vger.kernel.org \ --cc=stepanm@codeaurora.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.