From: Marek Majtyka <marekx.majt...@intel.com>

This is really a basic implementation. There can be added
error handlers for other module (debug, perf, hni, xp).

Fixed backward compatibility issue between linux axxia
BSP 1.59 with older uboot releases.

Signed-off-by: Marek Majtyka <marekx.majt...@intel.com>
---
 .../devicetree/bindings/arm/axxia/edac_l3.txt      |  17 +
 arch/arm64/boot/dts/intel/axm5616-victoria.dts     |   5 +
 arch/arm64/boot/dts/intel/axm56xx.dtsi             |   9 +
 drivers/edac/Kconfig                               |  12 +-
 drivers/edac/Makefile                              |   3 +-
 drivers/edac/axxia_edac-l3_56xx.c                  | 499 +++++++++++++++++++++
 include/linux/arm-smccc.h                          |   2 +
 7 files changed, 544 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/axxia/edac_l3.txt
 create mode 100644 drivers/edac/axxia_edac-l3_56xx.c

diff --git a/Documentation/devicetree/bindings/arm/axxia/edac_l3.txt 
b/Documentation/devicetree/bindings/arm/axxia/edac_l3.txt
new file mode 100644
index 0000000..e37b5b8
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/axxia/edac_l3.txt
@@ -0,0 +1,17 @@
+Axxia Error Detection & Correction [EDAC]
+The EDAC accesses a range of registers in the dickens l3 controller
+
+Required properties:
+- compatible : should contain "intel,ccn504-l3-cache"
+- reg : should contain address of ccn first node; its range shall contain
+       all ccn nodes registers ( 0x1000000 ).
+- interrupts : if given driver uses interrupts, if not poll mechanism applies
+
+Example:
+       edac_l3: edac_l3 {
+               compatible = "intel,ccn504-l3-cache";
+               reg = <0x80 0x00000000 0 0x1000000>;
+               interrupts = <0 256 4>;
+               syscon = <&syscon>;
+               status = "disabled";
+       };
diff --git a/arch/arm64/boot/dts/intel/axm5616-victoria.dts 
b/arch/arm64/boot/dts/intel/axm5616-victoria.dts
index cd946c6..ce815f9 100644
--- a/arch/arm64/boot/dts/intel/axm5616-victoria.dts
+++ b/arch/arm64/boot/dts/intel/axm5616-victoria.dts
@@ -215,6 +215,11 @@
 &edac_l2 {
        status = "okay";
 };
+
+&edac_l3 {
+       status = "okay";
+};
+
 &mtc {
        status = "okay";
 };
diff --git a/arch/arm64/boot/dts/intel/axm56xx.dtsi 
b/arch/arm64/boot/dts/intel/axm56xx.dtsi
index 7757b37..203aac0 100644
--- a/arch/arm64/boot/dts/intel/axm56xx.dtsi
+++ b/arch/arm64/boot/dts/intel/axm56xx.dtsi
@@ -40,6 +40,7 @@
                gpdma1    = &gpdma1;
                edac_cpu  = &edac_cpu;
                edac_l2  = &edac_l2;
+               edac_l3  = &edac_l3;
                sm0       = &sm0;
                sm1       = &sm1;
                cm0       = &cm0;
@@ -111,6 +112,14 @@
                        status = "disabled";
                };
 
+               edac_l3: edac_l3 {
+                       compatible = "intel,ccn504-l3-cache";
+                       reg = <0x80 0x00000000 0 0x1000000>;
+                       interrupts = <0 256 4>;
+                       syscon = <&syscon>;
+                       status = "disabled";
+               };
+
                sm0: sm0@00220000 {
                        compatible = "intel,smmon";
                        reg = <0 0x00220000 0 0x1000>;
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 30d45b5..fb10774 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -409,8 +409,16 @@ config EDAC_AXXIA_CMEM_5600
          the Configuration Memory error detection. Configuration Memory error
          detection is interrupt driven.
 
-config EDAC_AXXIA_L3
-       tristate "AXXIA EDAC L3 Controller"
+config EDAC_AXXIA_L3_5500
+       tristate "AXXIA EDAC L3 Controller for 5500"
+       help
+         Support for the eight L3 caches error detection
+         on the AXXIA AXM55xx devices. This enables the
+         L3 cache error detection. L3 cache error detection
+         uses polling mechanism.
+
+config EDAC_AXXIA_L3_5600
+       tristate "AXXIA EDAC L3 Controller for 5600"
        help
          Support for the eight L3 caches error detection
          on the AXXIA AXM55xx devices. This enables the
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index f41deab..c545959 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -21,7 +21,8 @@ endif
 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_L3_5500)       += axxia_edac-l3.o
+obj-$(CONFIG_EDAC_AXXIA_L3_5600)       += axxia_edac-l3_56xx.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
diff --git a/drivers/edac/axxia_edac-l3_56xx.c 
b/drivers/edac/axxia_edac-l3_56xx.c
new file mode 100644
index 0000000..fa9548b
--- /dev/null
+++ b/drivers/edac/axxia_edac-l3_56xx.c
@@ -0,0 +1,499 @@
+/*
+ * drivers/edac/axxia_edac-l3_56xx.c
+ *
+ * EDAC Driver for Intel's Axxia 5600 L3 (DICKENS)
+ *
+ * Copyright (C) 2017 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/interrupt.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 <linux/arm-smccc.h>
+#include "edac_core.h"
+#include "edac_module.h"
+
+#define INTEL_EDAC_MOD_STR     "axxia56xx_edac"
+
+#define SYSCON_PERSIST_SCRATCH 0xdc
+#define L3_PERSIST_SCRATCH_BIT (0x1 << 4)
+
+#define CCN_DT_PMOVSR                  0x0198
+#define CCN_DT_PMOVSR_CLR              0x01a0
+
+#define CCN_MN_ERRINT_STATUS           0x0008
+#define CCN_MN_ERRINT_STATUS__INTREQ__DESSERT          0x11
+#define CCN_MN_ERRINT_STATUS__ALL_ERRORS__ENABLE       0x02
+#define CCN_MN_ERRINT_STATUS__ALL_ERRORS__DISABLED     0x20
+#define CCN_MN_ERRINT_STATUS__ALL_ERRORS__DISABLE      0x22
+#define CCN_MN_ERRINT_STATUS__CORRECTED_ERRORS_ENABLE  0x04
+#define CCN_MN_ERRINT_STATUS__CORRECTED_ERRORS_DISABLED        0x40
+#define CCN_MN_ERRINT_STATUS__CORRECTED_ERRORS_DISABLE 0x44
+#define CCN_MN_ERRINT_STATUS__PMU_EVENTS__ENABLE       0x08
+#define CCN_MN_ERRINT_STATUS__PMU_EVENTS__DISABLED     0x80
+#define CCN_MN_ERRINT_STATUS__PMU_EVENTS__DISABLE      0x88
+
+#define CCN_MN_ERR_SIG_VAL_63_0                0x0300
+#define CCN_MN_ERR_SIG_VAL_63_0__DT                    (1 << 1)
+#define CCN_MN_ERROR_TYPE_VALUE                0x0320
+
+#define CCN_REGION_SIZE        0x10000
+
+#define CCN_HNI_NODE_BIT               8
+#define CCN_HNF_NODES                  8
+#define CCN_XP_NODES                   11
+#define CCN_DT_NODE_BASE_ADDR          (1 * CCN_REGION_SIZE)
+#define CCN_HNI_NODE_BASE_ADDR(i)      (0x80000 + (i) * CCN_REGION_SIZE)
+#define CCN_HNF_NODE_BASE_ADDR(i)      (0x200000 + (i) * CCN_REGION_SIZE)
+#define CCN_XP_NODE_BASE_ADDR(i)       (0x400000 + (i) * CCN_REGION_SIZE)
+
+#define CCN_NODE_ERR_SYND_REG0         0x400
+#define CCN_NODE_ERR_SYND_REG1         0x408
+#define CCN_NODE_ERR_SYND_CLR          0x480
+
+union dickens_hnf_err_syndrome_reg0 {
+       struct __packed {
+       #ifdef CPU_BIG_ENDIAN
+               unsigned long long err_extnd                    : 1;
+               unsigned long long first_err_vld                : 1;
+               unsigned long long err_class                    : 2;
+               unsigned long long mult_err                     : 1;
+               unsigned long long err_count                    : 16;
+               unsigned long long reserved_err_syndrome_reg0_20: 23;
+               unsigned long long err_count_set                : 12;
+               unsigned long long err_count_ovrflw             : 1;
+               unsigned long long err_count_match              : 1;
+               unsigned long long err_count_type               : 2;
+               unsigned long long par_err_id                   : 1;
+               unsigned long long err_id                       : 3;
+       #else
+               unsigned long long err_id                       : 3;
+               unsigned long long par_err_id                   : 1;
+               unsigned long long err_count_type               : 2;
+               unsigned long long err_count_match              : 1;
+               unsigned long long err_count_ovrflw             : 1;
+               unsigned long long err_count_set                : 12;
+               unsigned long long reserved_err_syndrome_reg0_20: 23;
+               unsigned long long err_count                    : 16;
+               unsigned long long mult_err                     : 1;
+               unsigned long long err_class                    : 2;
+               unsigned long long first_err_vld                : 1;
+               unsigned long long err_extnd                    : 1;
+       #endif
+       } reg0;
+       u64 value;
+};
+
+union dickens_hnf_err_syndrome_reg1 {
+       struct __packed {
+       #ifdef CPU_BIG_ENDIAN
+               unsigned long long reserved_err_syndrome_reg1_55: 9;
+               unsigned long long err_srcid                    : 7;
+               unsigned long long reserved_err_syndrome_reg1_46: 2;
+               unsigned long long err_optype                   : 2;
+               unsigned long long err_addr                     : 44;
+       #else
+               unsigned long long err_addr                     : 44;
+               unsigned long long err_optype                   : 2;
+               unsigned long long reserved_err_syndrome_reg1_46: 2;
+               unsigned long long err_srcid                    : 7;
+               unsigned long long reserved_err_syndrome_reg1_55: 9;
+       #endif
+       } reg1;
+       u64 value;
+};
+
+union dickens_hnf_err_syndrome_clr {
+       struct __packed {
+       #ifdef CPU_BIG_ENDIAN
+               unsigned long long reserved_err_syndrome_clr_63 : 1;
+               unsigned long long first_err_vld_clr            : 1;
+               unsigned long long reserved_err_syndrome_clr_60 : 2;
+               unsigned long long mult_err_clr                 : 1;
+               unsigned long long reserved_err_syndrome_clr_0  : 59;
+       #else
+               unsigned long long reserved_err_syndrome_clr_0  : 59;
+               unsigned long long mult_err_clr                 : 1;
+               unsigned long long reserved_err_syndrome_clr_60 : 2;
+               unsigned long long first_err_vld_clr            : 1;
+               unsigned long long reserved_err_syndrome_clr_63 : 1;
+       #endif
+       } clr;
+       u64 value;
+};
+
+struct event_data {
+       u64 err_synd_reg0;
+       u64 err_synd_reg1;
+       int idx;
+};
+
+/* Private structure for common edac device */
+struct intel_edac_dev_info {
+       struct platform_device *pdev;
+       char *ctl_name;
+       char *blk_name;
+       int edac_idx;
+       int irq_used;
+       struct event_data data[CCN_HNF_NODES];
+       struct event_data data_xp[CCN_XP_NODES];
+       struct event_data data_hni;
+       struct regmap *syscon;
+       void __iomem *dickens_L3;
+       struct edac_device_ctl_info *edac_dev;
+       void (*check)(struct edac_device_ctl_info *edac_dev);
+};
+
+static void clear_node_error(void __iomem *addr)
+{
+       union dickens_hnf_err_syndrome_clr err_syndrome_clr;
+
+       err_syndrome_clr.value = 0x0;
+       err_syndrome_clr.clr.first_err_vld_clr = 0x1;
+       err_syndrome_clr.clr.mult_err_clr = 0x1;
+       writeq(err_syndrome_clr.value, addr);
+}
+
+static irqreturn_t
+ccn_pmu_overflow_handler(struct intel_edac_dev_info *dev_info)
+{
+       u64 pmovsr = 0;
+
+       pmovsr = readq(dev_info->dickens_L3 +
+                       CCN_DT_NODE_BASE_ADDR + CCN_DT_PMOVSR);
+       if (!pmovsr)
+               return IRQ_NONE;
+
+       /*
+        * TODO
+        * Add perf implementation for handling this
+        * skipped for now - low priority
+        */
+
+       writeq(pmovsr, dev_info->dickens_L3 +
+               CCN_DT_NODE_BASE_ADDR  + CCN_DT_PMOVSR_CLR);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t ccn_irq_thread(int irq, void *device)
+{
+       struct intel_edac_dev_info *dev_info = device;
+       struct edac_device_ctl_info *edac_dev = dev_info->edac_dev;
+       union dickens_hnf_err_syndrome_reg0 err_syndrome_reg0;
+       union dickens_hnf_err_syndrome_reg1 err_syndrome_reg1;
+       struct arm_smccc_res r;
+       unsigned int count = 0;
+       int i, j;
+
+       /* only HNF nodes are of our interest */
+       for (i = 0; i < CCN_HNF_NODES; ++i) {
+               err_syndrome_reg0.value = dev_info->data[i].err_synd_reg0;
+               err_syndrome_reg1.value = dev_info->data[i].err_synd_reg1;
+
+               dev_info->data[i].err_synd_reg0 = 0;
+               dev_info->data[i].err_synd_reg1 = 0;
+
+               if (err_syndrome_reg0.reg0.first_err_vld) {
+                       if (err_syndrome_reg0.reg0.err_class & 0x3) {
+                               regmap_update_bits(dev_info->syscon,
+                                                  SYSCON_PERSIST_SCRATCH,
+                                                  L3_PERSIST_SCRATCH_BIT,
+                                                  L3_PERSIST_SCRATCH_BIT);
+                               /* Fatal error */
+                               pr_emerg("L3 uncorrectable error\n");
+                               arm_smccc_smc(0xc4000027,
+                                       CCN_MN_ERRINT_STATUS__INTREQ__DESSERT,
+                                       0, 0, 0, 0, 0, 0, &r);
+                               machine_restart(NULL);
+                       }
+                       count = err_syndrome_reg0.reg0.err_count;
+                       for (j = 0; j < count; j++)
+                               edac_device_handle_ce(edac_dev, 0,
+                                       dev_info->data[i].idx,
+                                       edac_dev->ctl_name);
+               }
+       }
+
+       /* Interrupt deasserted */
+       arm_smccc_smc(0xc4000027, CCN_MN_ERRINT_STATUS__INTREQ__DESSERT,
+                       0, 0, 0, 0, 0, 0, &r);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t ccn_irq_handler(int irq, void *device)
+{
+       struct intel_edac_dev_info *dev_info = device;
+       void __iomem *ccn_base = dev_info->dickens_L3;
+
+       irqreturn_t res = IRQ_NONE;
+       u64 err_sig_val[3];
+       u64 err_type_value[4];
+       u64 err_or;
+       u64 err_synd_reg0 = 0, err_synd_reg1 = 0;
+       int i;
+
+       /* PMU overflow is a special case - for the future */
+       err_or = err_sig_val[0] = readq(ccn_base + CCN_MN_ERR_SIG_VAL_63_0);
+       if (err_or & CCN_MN_ERR_SIG_VAL_63_0__DT) {
+               err_or &= ~CCN_MN_ERR_SIG_VAL_63_0__DT;
+               res = ccn_pmu_overflow_handler(dev_info);
+       }
+
+       /* Have to read all err_sig_vals to clear them */
+       for (i = 1; i < ARRAY_SIZE(err_sig_val); i++) {
+               err_sig_val[i] = readq(ccn_base +
+                               CCN_MN_ERR_SIG_VAL_63_0 + i * 8);
+               err_or |= err_sig_val[i];
+       }
+
+       err_type_value[i] = readq(ccn_base + CCN_MN_ERROR_TYPE_VALUE);
+       err_type_value[i] = readq(ccn_base + CCN_MN_ERROR_TYPE_VALUE + 0x8);
+       err_type_value[i] = readq(ccn_base + CCN_MN_ERROR_TYPE_VALUE + 0x10);
+       err_type_value[i] = readq(ccn_base + CCN_MN_ERROR_TYPE_VALUE + 0x20);
+
+       /* check hni node */
+       if ((0x1 << CCN_HNI_NODE_BIT) & err_sig_val[0]) {
+               err_synd_reg0 = readq(ccn_base + CCN_HNI_NODE_BASE_ADDR(0) +
+                                       CCN_NODE_ERR_SYND_REG0);
+               err_synd_reg1 = readq(ccn_base + CCN_HNI_NODE_BASE_ADDR(0) +
+                                       CCN_NODE_ERR_SYND_REG1);
+
+               dev_info->data_hni.err_synd_reg0 = err_synd_reg0;
+               dev_info->data_hni.err_synd_reg1 = err_synd_reg1;
+               dev_info->data_hni.idx = 0;
+
+               clear_node_error(ccn_base + CCN_HNI_NODE_BASE_ADDR(0) +
+                                       CCN_NODE_ERR_SYND_CLR);
+       }
+
+       /* go through all hnf nodes */
+       for (i = 0; i < CCN_HNF_NODES; ++i) {
+               /* when enabled process */
+               if ((0x1 << (32 + i)) & err_sig_val[1]) {
+                       err_synd_reg0 = readq(ccn_base +
+                                               CCN_HNF_NODE_BASE_ADDR(i) +
+                                               CCN_NODE_ERR_SYND_REG0);
+                       err_synd_reg1 = readq(ccn_base +
+                                               CCN_HNF_NODE_BASE_ADDR(i) +
+                                               CCN_NODE_ERR_SYND_REG1);
+
+                       dev_info->data[i].err_synd_reg0 = err_synd_reg0;
+                       dev_info->data[i].err_synd_reg1 = err_synd_reg1;
+                       dev_info->data[i].idx = i;
+
+                       clear_node_error(ccn_base + CCN_HNF_NODE_BASE_ADDR(i) +
+                                               CCN_NODE_ERR_SYND_CLR);
+               }
+       }
+
+       /* process XP errors only and only if bit[0] in enabled */
+       if (0x1 & err_sig_val[2]) {
+               /* look into all XP nodes */
+               for (i = 0; i < CCN_XP_NODES; ++i) {
+                       err_synd_reg0 = readq(ccn_base +
+                                               CCN_XP_NODE_BASE_ADDR(i) +
+                                               CCN_NODE_ERR_SYND_REG0);
+
+                       dev_info->data_xp[i].err_synd_reg0 = err_synd_reg0;
+                       dev_info->data_xp[i].err_synd_reg1 = 0;
+
+                       clear_node_error(ccn_base + CCN_XP_NODE_BASE_ADDR(i) +
+                               CCN_NODE_ERR_SYND_CLR);
+               }
+       }
+
+       if (err_or)
+               dev_err(&dev_info->pdev->dev,
+                       "Error reported in %016llx %016llx %016llx.\n",
+                       err_sig_val[2], err_sig_val[1], err_sig_val[0]);
+
+       /* HERE all error data collected, but interrupt not deasserted */
+       return IRQ_WAKE_THREAD;
+}
+
+
+/* Check for L3 Errors */
+static void intel_l3_error_check(struct edac_device_ctl_info *edac_dev)
+{
+       void __iomem *addr;
+       union dickens_hnf_err_syndrome_reg0 err_syndrome_reg0;
+       union dickens_hnf_err_syndrome_clr err_syndrome_clr;
+       unsigned int count = 0;
+       int i, instance;
+       struct intel_edac_dev_info *dev_info;
+
+       err_syndrome_clr.value = 0x0;
+       err_syndrome_clr.clr.first_err_vld_clr = 0x1;
+       err_syndrome_clr.clr.mult_err_clr = 0x1;
+
+       dev_info = (struct intel_edac_dev_info *) edac_dev->pvt_info;
+       addr = dev_info->dickens_L3 + CCN_HNF_NODE_BASE_ADDR(0);
+
+       for (instance = 0;
+               instance < CCN_HNF_NODES;
+               instance++, addr += CCN_REGION_SIZE) {
+
+               err_syndrome_reg0.value =
+                       readq(addr + CCN_NODE_ERR_SYND_REG0);
+               /* First error valid */
+               if (err_syndrome_reg0.reg0.first_err_vld) {
+                       if (err_syndrome_reg0.reg0.err_class & 0x3) {
+                               regmap_update_bits(dev_info->syscon,
+                                                  SYSCON_PERSIST_SCRATCH,
+                                                  L3_PERSIST_SCRATCH_BIT,
+                                                  L3_PERSIST_SCRATCH_BIT);
+                               /* Fatal error */
+                               pr_emerg("L3 uncorrectable error\n");
+                               machine_restart(NULL);
+                       }
+                       count = err_syndrome_reg0.reg0.err_count;
+                       for (i = 0; i < count; i++)
+                               edac_device_handle_ce(edac_dev, 0,
+                                       instance, edac_dev->ctl_name);
+
+                       /* clear the valid bit */
+                       clear_node_error(addr + CCN_NODE_ERR_SYND_CLR);
+               }
+       }
+}
+
+static int intel_edac_l3_probe(struct platform_device *pdev)
+{
+       struct intel_edac_dev_info *dev_info = NULL;
+       struct device_node *np = pdev->dev.of_node;
+       struct resource *r;
+
+       struct arm_smccc_res ret;
+
+       dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
+       if (!dev_info)
+               return -ENOMEM;
+
+       dev_info->ctl_name = kstrdup(np->name, GFP_KERNEL);
+       dev_info->blk_name = "l3merrsr";
+       dev_info->pdev = pdev;
+       dev_info->edac_idx = edac_device_alloc_index();
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               pr_err("Unable to get mem resource\n");
+               goto err1;
+       }
+
+       dev_info->dickens_L3 = devm_ioremap(&pdev->dev, r->start,
+                                           resource_size(r));
+       if (!dev_info->dickens_L3) {
+               pr_err("INTEL_L3 devm_ioremap error\n");
+               goto err1;
+       }
+
+       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;
+       }
+       dev_info->edac_dev =
+               edac_device_alloc_ctl_info(0, dev_info->ctl_name,
+                                          1, dev_info->blk_name,
+                                          8, 0, NULL, 0,
+                                          dev_info->edac_idx);
+       if (!dev_info->edac_dev) {
+               pr_info("No memory for edac device\n");
+               goto err1;
+       }
+
+       r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!r)
+               return -EINVAL;
+
+       /*
+        * Check if we can use the interrupt here.
+        * We are not interested in PMU events, so let's try to disable it.
+        * Once -1 return, it means old uboot without ccn service.
+        * Then only polling mechanism is allowed, as it was before.
+        */
+       arm_smccc_smc(0xc4000027, CCN_MN_ERRINT_STATUS__PMU_EVENTS__DISABLE,
+                       0, 0, 0, 0, 0, 0, &ret);
+
+       if (ret.a0 != ARM_SMCCC_UNKNOWN)
+               dev_info->irq_used = 1;
+
+       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);
+
+       if (dev_info->irq_used) {
+               edac_op_state = EDAC_OPSTATE_INT;
+               dev_info->edac_dev->edac_check = NULL;
+       } else {
+               edac_op_state = EDAC_OPSTATE_POLL;
+               dev_info->edac_dev->edac_check = intel_l3_error_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;
+       }
+
+       if (dev_info->irq_used) {
+               if (devm_request_threaded_irq(&dev_info->pdev->dev, r->start,
+                       ccn_irq_handler, ccn_irq_thread, IRQF_ONESHOT,
+                       dev_name(&dev_info->pdev->dev), dev_info))
+                       goto err2;
+       }
+
+       return 0;
+err2:
+       edac_device_free_ctl_info(dev_info->edac_dev);
+err1:
+       platform_device_unregister(dev_info->pdev);
+       return 1;
+}
+
+static int intel_edac_l3_remove(struct platform_device *pdev)
+{
+       platform_device_unregister(pdev);
+       return 0;
+}
+
+static const struct of_device_id intel_edac_l3_match[] = {
+       {
+       .compatible = "intel,ccn504-l3-cache",
+       },
+       {},
+};
+
+static struct platform_driver intel_edac_l3_driver = {
+       .probe = intel_edac_l3_probe,
+       .remove = intel_edac_l3_remove,
+       .driver = {
+               .name = "intel_edac_l3",
+               .of_match_table = intel_edac_l3_match,
+       }
+};
+
+module_platform_driver(intel_edac_l3_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Marek Majtyka <marekx.majt...@intel.com>");
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index 4c5bca38..66799e9 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -60,6 +60,8 @@
 #define ARM_SMCCC_QUIRK_NONE           0
 #define ARM_SMCCC_QUIRK_QCOM_A6                1 /* Save/restore register a6 */
 
+#define ARM_SMCCC_UNKNOWN              0xffffffff
+
 #ifndef __ASSEMBLY__
 
 #include <linux/linkage.h>
-- 
2.7.4

-- 
_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to