From: John Jacques <john.jacq...@intel.com> Ported AXM55xx implementation: - separated code between AXM55xx and AXM56xx architecture - added new assembly functions for A57 to get CPU/L2 error syndrom registers - "code rebranding" from LSI to Intel - added device tree documentation - added device tree configuration for AXM56xx - separated axm56xx and axm55xx kernel configuration
Signed-off-by: Marek Majtyka <marekx.majt...@intel.com> --- .../devicetree/bindings/arm/axxia/edac_cpu.txt | 14 + .../devicetree/bindings/arm/axxia/edac_l2.txt | 14 + arch/arm/include/asm/axxia_l2_55xx.h | 32 +++ arch/arm64/boot/dts/intel/axm5616-victoria.dts | 7 + arch/arm64/boot/dts/intel/axm56xx.dtsi | 13 + arch/arm64/include/asm/axxia_l2_56xx.h | 35 +++ drivers/edac/Kconfig | 12 +- drivers/edac/Makefile | 6 +- drivers/edac/axxia_edac-l2_cpu.c | 27 +- drivers/edac/axxia_edac-l2_cpu_56xx.c | 299 +++++++++++++++++++++ 10 files changed, 430 insertions(+), 29 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/axxia/edac_cpu.txt create mode 100644 Documentation/devicetree/bindings/arm/axxia/edac_l2.txt create mode 100644 arch/arm/include/asm/axxia_l2_55xx.h create mode 100644 arch/arm64/include/asm/axxia_l2_56xx.h create mode 100644 drivers/edac/axxia_edac-l2_cpu_56xx.c diff --git a/Documentation/devicetree/bindings/arm/axxia/edac_cpu.txt b/Documentation/devicetree/bindings/arm/axxia/edac_cpu.txt new file mode 100644 index 0000000..da12bd5 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/axxia/edac_cpu.txt @@ -0,0 +1,14 @@ +Axxia Error Detection & Correction [EDAC] +The EDAC accesses ARM v7/v8 CPUMERRSR_EL1 register data. + +Required properties: +- compatible : should contain "intel,cortex-a57-cpu" for AXM56xx + : should contain "lsi,cortex-a15-cpu" for AXM55xx +- syscon : should referernce syscon node for both 55xx/56xx + +Example: + edac_cpu: edac_cpu { + compatible = "intel,cortex-a57-cpu"; + syscon = <&syscon>; + status = "disabled"; + }; diff --git a/Documentation/devicetree/bindings/arm/axxia/edac_l2.txt b/Documentation/devicetree/bindings/arm/axxia/edac_l2.txt new file mode 100644 index 0000000..d90629c --- /dev/null +++ b/Documentation/devicetree/bindings/arm/axxia/edac_l2.txt @@ -0,0 +1,14 @@ +Axxia Error Detection & Correction [EDAC] +The EDAC accesses ARM v7/v8 L2MERRSR_EL1 register data. + +Required properties: +- compatible : should contain "intel,cortex-a57-l2-cache" for AXM56xx + : should contain "lsi,cortex-a15-l2-cache" for AXM55xx +- syscon : should referernce syscon node for both 55xx/56xx + +Example: + edac_l2: edac_l2 { + compatible = "intel,cortex-a57-l2-cache"; + syscon = <&syscon>; + status = "disabled"; + }; diff --git a/arch/arm/include/asm/axxia_l2_55xx.h b/arch/arm/include/asm/axxia_l2_55xx.h new file mode 100644 index 0000000..ab07e60 --- /dev/null +++ b/arch/arm/include/asm/axxia_l2_55xx.h @@ -0,0 +1,32 @@ +#ifndef __AXXIA_L2_MODULE__ +#define __AXXIA_L2_MODULE__ + +#include <linux/types.h> + +static inline u64 read_cpumerrsr(void) +{ + u64 val; + + asm volatile("mrrc\tp15, 0, %Q0, %R0, c15" : "=r" (val)); + return val; +} + +static inline u64 read_l2merrsr(void) +{ + u64 val; + + asm volatile("mrrc\tp15, 1, %Q0, %R0, c15" : "=r"(val)); + return val; +} + +inline void write_cpumerrsr(u64 val) +{ + asm volatile("mcrr\tp15, 0, %Q0, %R0, c15" : : "r" (val)); +} + +inline void write_l2merrsr(u64 val) +{ + asm volatile("mcrr\tp15, 1, %Q0, %R0, c15" : : "r"(val)); +} + +#endif /*__AXXIA_L2_MODULE__*/ diff --git a/arch/arm64/boot/dts/intel/axm5616-victoria.dts b/arch/arm64/boot/dts/intel/axm5616-victoria.dts index fec5143..cd946c6 100644 --- a/arch/arm64/boot/dts/intel/axm5616-victoria.dts +++ b/arch/arm64/boot/dts/intel/axm5616-victoria.dts @@ -208,6 +208,13 @@ status = "okay"; }; +&edac_cpu { + status = "okay"; +}; + +&edac_l2 { + status = "okay"; +}; &mtc { status = "okay"; }; diff --git a/arch/arm64/boot/dts/intel/axm56xx.dtsi b/arch/arm64/boot/dts/intel/axm56xx.dtsi index 08ebbe9..fc82673 100644 --- a/arch/arm64/boot/dts/intel/axm56xx.dtsi +++ b/arch/arm64/boot/dts/intel/axm56xx.dtsi @@ -38,6 +38,8 @@ i2c3 = &i2c3; gpdma0 = &gpdma0; gpdma1 = &gpdma1; + edac_cpu = &edac_cpu; + edac_l2 = &edac_l2; sm0 = &sm0; sm1 = &sm1; cm0 = &cm0; @@ -97,6 +99,17 @@ compatible = "intel,axxia-syscon", "syscon"; reg = <0x80 0x02c00000 0 0x40000>; }; + edac_cpu: edac_cpu { + compatible = "intel,cortex-a57-cpu"; + syscon = <&syscon>; + status = "disabled"; + }; + + edac_l2: edac_l2 { + compatible = "intel,cortex-a57-l2-cache"; + syscon = <&syscon>; + status = "disabled"; + }; sm0: sm0@00220000 { compatible = "intel,smmon"; diff --git a/arch/arm64/include/asm/axxia_l2_56xx.h b/arch/arm64/include/asm/axxia_l2_56xx.h new file mode 100644 index 0000000..fe61ef2 --- /dev/null +++ b/arch/arm64/include/asm/axxia_l2_56xx.h @@ -0,0 +1,35 @@ +#ifndef __AXXIA_L2_MODULE__ +#define __AXXIA_L2_MODULE__ + +#include <linux/types.h> + +static inline u64 read_cpumerrsr(void) +{ + u64 val; + + asm volatile("mrs\t%x0, S3_1_c15_c2_2" : "=r" (val)); + return val; +} + +static inline u64 read_l2merrsr(void) +{ + u64 val; + + asm volatile("mrs\t%x0, S3_1_c15_c2_3" : "=r" (val)); + return val; +} + +inline void write_cpumerrsr(u64 val) +{ + + asm volatile("msr\tS3_1_c15_c2_2, %x0" : : "r" (val)); +} + +inline void write_l2merrsr(u64 val) +{ + + asm volatile("msr\tS3_1_c15_c2_3, %x0" : : "r" (val)); +} + + +#endif /*__AXXIA_L2_MODULE__*/ diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 1874410..30d45b5 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -417,14 +417,22 @@ config EDAC_AXXIA_L3 L3 cache error detection. L3 cache error detection uses polling mechanism. -config EDAC_AXXIA_L2_CPU - tristate "AXXIA EDAC L2/CPU Controller" +config EDAC_AXXIA_L2_CPU_5500 + tristate "AXXIA EDAC L2/CPU Controller for 5500" help Support for L2 cache and A15 CPU error detection on AXXIA AXM55xx devices. This enables the L2 cache and A15 CPU error detction. L2 cache and A15 CPU error detection uses polling mechanism. +config EDAC_AXXIA_L2_CPU_5600 + tristate "AXXIA EDAC L2/CPU Controller for 5600" + help + Support for L2 cache and A57 CPU error detection + on AXXIA AXM56xx devices. This enables the L2 + cache and A57 CPU error detction. L2 cache and A57 + CPU error detection uses polling mechanism. + config EDAC_ALTERA bool "Altera SOCFPGA ECC" depends on EDAC_MM_EDAC=y && ARCH_SOCFPGA diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index c5f2638..f41deab 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -22,7 +22,8 @@ obj-$(CONFIG_EDAC_AXXIA_SYSMEM_5500) += axxia_edac-mc.o obj-$(CONFIG_EDAC_AXXIA_SYSMEM_5600) += axxia_edac-mc_56xx.o obj-$(CONFIG_EDAC_AXXIA_CMEM_5600) += axxia_edac-cmc_56xx.o obj-$(CONFIG_EDAC_AXXIA_L3) += axxia_edac-l3.o -obj-$(CONFIG_EDAC_AXXIA_L2_CPU) += axxia_edac-l2_cpu.o +obj-$(CONFIG_EDAC_AXXIA_L2_CPU_5500) += axxia_edac-l2_cpu.o +obj-$(CONFIG_EDAC_AXXIA_L2_CPU_5600) += axxia_edac-l2_cpu_56xx.o obj-$(CONFIG_EDAC_GHES) += ghes_edac.o edac_mce_amd-y := mce_amd.o @@ -82,3 +83,6 @@ obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o obj-$(CONFIG_EDAC_ALTERA) += altera_edac.o obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o + +CFLAGS_axxia_edac-l2_cpu.o += -I$(srctree)/arch/$(ARCH)/include/asm/ -include $(srctree)/arch/$(ARCH)/include/asm/axxia_l2_55xx.h +CFLAGS_axxia_edac-l2_cpu_56xx.o += -I$(srctree)/arch/$(ARCH)/include/asm/ -include $(srctree)/arch/$(ARCH)/include/asm/axxia_l2_56xx.h diff --git a/drivers/edac/axxia_edac-l2_cpu.c b/drivers/edac/axxia_edac-l2_cpu.c index 3c87f4c..4768b2a 100644 --- a/drivers/edac/axxia_edac-l2_cpu.c +++ b/drivers/edac/axxia_edac-l2_cpu.c @@ -35,6 +35,7 @@ #include <linux/regmap.h> #include "edac_core.h" #include "edac_module.h" +#include "axxia_l2_55xx.h" #define LSI_EDAC_MOD_STR "lsi_edac" #define CORES_PER_CLUSTER 4 @@ -54,32 +55,6 @@ struct lsi_edac_dev_info { void (*check)(struct edac_device_ctl_info *edac_dev); }; -static inline u64 read_cpumerrsr(void) -{ - u64 val; - - asm volatile("mrrc\tp15, 0, %Q0, %R0, c15" : "=r" (val)); - return val; -} - -static inline u64 read_l2merrsr(void) -{ - u64 val; - - asm volatile("mrrc\tp15, 1, %Q0, %R0, c15" : "=r"(val)); - return val; -} - -inline void write_cpumerrsr(u64 val) -{ - asm volatile("mcrr\tp15, 0, %Q0, %R0, c15" : : "r" (val)); -} - -inline void write_l2merrsr(u64 val) -{ - asm volatile("mcrr\tp15, 1, %Q0, %R0, c15" : : "r"(val)); -} - void log_cpumerrsr(void *edac) { struct edac_device_ctl_info *edac_dev = edac; diff --git a/drivers/edac/axxia_edac-l2_cpu_56xx.c b/drivers/edac/axxia_edac-l2_cpu_56xx.c new file mode 100644 index 0000000..4b5f6bf --- /dev/null +++ b/drivers/edac/axxia_edac-l2_cpu_56xx.c @@ -0,0 +1,299 @@ +/* + * drivers/edac/axxia_edac-l2_cpu_56xx.c + * + * EDAC Driver for Intel's Axxia 5600 System Memory Controller + * + * Copyright (C) 2016 Intel Inc. + * + * This file may be distributed under the terms of the + * GNU General Public License. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/lsi-ncr.h> +#include <linux/edac.h> +#include <linux/of_platform.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include "edac_core.h" +#include "edac_module.h" +#include "axxia_l2_56xx.h" + + +#define INTEL_EDAC_MOD_STR "axxia56xx_edac" +#define CORES_PER_CLUSTER 4 + +#define SYSCON_PERSIST_SCRATCH 0xdc +#define L2_PERSIST_SCRATCH_BIT (0x1 << 5) +#define CPU_PERSIST_SCRATCH_BIT (0x1 << 6) + +/* Private structure for common edac device */ +struct intel_edac_dev_info { + struct platform_device *pdev; + char *ctl_name; + char *blk_name; + int edac_idx; + struct regmap *syscon; + struct edac_device_ctl_info *edac_dev; + void (*check)(struct edac_device_ctl_info *edac_dev); +}; + +void log_cpumerrsr(void *edac) +{ + struct edac_device_ctl_info *edac_dev = edac; + u64 val, clear_val; + u32 count0, count1; + int i; + struct intel_edac_dev_info *dev_info; + + dev_info = edac_dev->pvt_info; + + /* Read S3_1_c15_c2_2 for CPUMERRSR_EL1 counts */ + val = read_cpumerrsr(); + if (val & 0x80000000) { + int cpu = get_cpu(); + + count0 = ((val) & 0x000000ff00000000) >> 31; + count1 = ((val) & 0x0000ff0000000000) >> 39; + + /* increment correctable error counts */ + for (i = 0; i < count0+count1; i++) { + edac_device_handle_ce(edac_dev, 0, + cpu, edac_dev->ctl_name); + } + + /* Clear the valid bit */ + clear_val = 0x80000000; + write_cpumerrsr(clear_val); + put_cpu(); + } + if (val & 0x8000000000000000) { + regmap_update_bits(dev_info->syscon, + SYSCON_PERSIST_SCRATCH, + CPU_PERSIST_SCRATCH_BIT, + CPU_PERSIST_SCRATCH_BIT); + pr_emerg("CPU uncorrectable error\n"); + machine_restart(NULL); + } +} + + +/* Check for CPU Errors */ +static void intel_cpu_error_check(struct edac_device_ctl_info *edac_dev) +{ + /* execute on current cpu */ + log_cpumerrsr(edac_dev); + + /* send ipis to execute on other cpus */ + smp_call_function(log_cpumerrsr, edac_dev, 1); + +} + +void log_l2merrsr(void *edac) +{ + struct edac_device_ctl_info *edac_dev = edac; + u64 val, clear_val; + u32 count0, count1; + int i; + struct intel_edac_dev_info *dev_info; + + dev_info = edac_dev->pvt_info; + + val = read_l2merrsr(); + if (val & 0x80000000) { + int cpu = get_cpu(); + + count0 = ((val) & 0x000000ff00000000) >> 31; + count1 = ((val) & 0x0000ff0000000000) >> 39; + + /* increment correctable error counts */ + for (i = 0; i < count0+count1; i++) { + edac_device_handle_ce(edac_dev, 0, + cpu/CORES_PER_CLUSTER, + edac_dev->ctl_name); + } + + /* Clear the valid bit */ + clear_val = 0x80000000; + write_l2merrsr(clear_val); + put_cpu(); + } + if (val & 0x8000000000000000) { + regmap_update_bits(dev_info->syscon, + SYSCON_PERSIST_SCRATCH, + L2_PERSIST_SCRATCH_BIT, + L2_PERSIST_SCRATCH_BIT); + pr_emerg("L2 uncorrectable error\n"); + machine_restart(NULL); + } +} + +/* Check for L2 Errors */ +static void intel_l2_error_check(struct edac_device_ctl_info *edac_dev) +{ + /* 4 cores per cluster */ + int nr_cluster_ids = ((nr_cpu_ids - 1) / CORES_PER_CLUSTER) + 1; + int i, j, cpu; + + /* execute on current cpu */ + log_l2merrsr(edac_dev); + + for (i = 0; i < nr_cluster_ids; i++) { + /* No need to run on local cluster. */ + if (i == (get_cpu() / CORES_PER_CLUSTER)) { + put_cpu(); + continue; + } + /* + * Have some core in each cluster execute this, + * Start with the first core on that cluster. + */ + cpu = i * CORES_PER_CLUSTER; + for (j = cpu; j < cpu + CORES_PER_CLUSTER; j++) { + if (cpu_online(j)) { + smp_call_function_single(j, log_l2merrsr, + edac_dev, 1); + break; + } + } + put_cpu(); + } +} + +static void intel_add_edac_devices(struct platform_device *pdev, + int num) +{ + struct intel_edac_dev_info *dev_info = NULL; + /* 4 cores per cluster */ + int nr_cluster_ids = ((nr_cpu_ids - 1) / CORES_PER_CLUSTER) + 1; + struct device_node *np = pdev->dev.of_node; + + dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL); + if (!dev_info) + return; + + dev_info->ctl_name = kstrdup(np->name, GFP_KERNEL); + if (num == 0) { + dev_info->blk_name = "cpumerrsr"; + dev_info->check = intel_cpu_error_check; + } else { + dev_info->blk_name = "l2merrsr"; + dev_info->check = intel_l2_error_check; + } + dev_info->pdev = pdev; + dev_info->edac_idx = edac_device_alloc_index(); + dev_info->syscon = + syscon_regmap_lookup_by_phandle(np, "syscon"); + if (IS_ERR(dev_info->syscon)) { + pr_info("%s: syscon lookup failed\n", np->name); + goto err1; + } + + if (num == 0) { + /* cpu L1 */ + dev_info->edac_dev = + edac_device_alloc_ctl_info(0, dev_info->ctl_name, + 1, dev_info->blk_name, num_possible_cpus(), 0, NULL, + 0, dev_info->edac_idx); + } else { + /* cluster L2 */ + dev_info->edac_dev = + edac_device_alloc_ctl_info(0, dev_info->ctl_name, + 1, dev_info->blk_name, nr_cluster_ids, 0, NULL, + 0, dev_info->edac_idx); + } + if (!dev_info->edac_dev) { + pr_info("No memory for edac device\n"); + goto err1; + } + + dev_info->edac_dev->pvt_info = dev_info; + dev_info->edac_dev->dev = &dev_info->pdev->dev; + dev_info->edac_dev->ctl_name = dev_info->ctl_name; + dev_info->edac_dev->mod_name = INTEL_EDAC_MOD_STR; + dev_info->edac_dev->dev_name = dev_name(&dev_info->pdev->dev); + + dev_info->edac_dev->edac_check = dev_info->check; + + if (edac_device_add_device(dev_info->edac_dev) != 0) { + pr_info("Unable to add edac device for %s\n", + dev_info->ctl_name); + goto err2; + } + + return; +err2: + edac_device_free_ctl_info(dev_info->edac_dev); +err1: + platform_device_unregister(dev_info->pdev); +} + +static int intel_edac_cpu_probe(struct platform_device *pdev) +{ + edac_op_state = EDAC_OPSTATE_POLL; + intel_add_edac_devices(pdev, 0); + return 0; +} + +static int intel_edac_cpu_remove(struct platform_device *pdev) +{ + platform_device_unregister(pdev); + return 0; +} + +static int intel_edac_l2_probe(struct platform_device *pdev) +{ + edac_op_state = EDAC_OPSTATE_POLL; + intel_add_edac_devices(pdev, 1); + return 0; +} + +static int intel_edac_l2_remove(struct platform_device *pdev) +{ + platform_device_unregister(pdev); + return 0; +} + +static const struct of_device_id intel_edac_l2_match[] = { + { + .compatible = "intel,cortex-a57-l2-cache", + }, + {}, +}; + +static struct platform_driver intel_edac_l2_driver = { + .probe = intel_edac_l2_probe, + .remove = intel_edac_l2_remove, + .driver = { + .name = "intel_edac_l2", + .of_match_table = intel_edac_l2_match, + } +}; +static const struct of_device_id intel_edac_cpu_match[] = { + { + .compatible = "intel,cortex-a57-cpu", + }, + {}, +}; + +static struct platform_driver intel_edac_cpu_driver = { + .probe = intel_edac_cpu_probe, + .remove = intel_edac_cpu_remove, + .driver = { + .name = "intel_edac_cpu", + .of_match_table = intel_edac_cpu_match, + } +}; + +module_platform_driver(intel_edac_cpu_driver); +module_platform_driver(intel_edac_l2_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marek Majtyka <marekx.majt...@intel.com>"); -- 2.7.4 -- _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto