[linux-yocto] [PATCH 25/48] drivers/edac: Added ccn504 Interrupt Driven Driver

Daniel Dragomir daniel.dragomir at windriver.com
Mon Dec 11 05:13:55 PST 2017


From: Marek Majtyka <marekx.majtyka at 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.majtyka at 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 at 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.majtyka at 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



More information about the linux-yocto mailing list