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

Reply via email to