All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v10 0/4] edac: Add APM X-Gene SoC EDAC driver
@ 2015-05-18 23:24 ` Loc Ho
  0 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-18 23:24 UTC (permalink / raw)
  To: dougthompson-aS9lmoZGLiVWk0Htik3J/w, bp-Gina5bIWoIWzQB+pC5nmwQ,
	mchehab-JPH+aEBZ4P+UEJcrhfAQsw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg
  Cc: linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	jcm-H+wXaHxf7aLQT0dZR+AlfA, patches-qTEPVZfXA3Y, Loc Ho

This patch adds support for the APM X-Gene SoC EDAC driver for DT.

v10:
* Move regmap for CSW, MCBA, MCBB, and efuse to top driver node and add
  accessor support functions
* Change retrieval OF address proerty for subnode to use of_address_to_resource
* Add COMPILE_TEST to Kconfig for EDAC_XGENE
* Update DT binding documentation and DT node accordingly
* Remove DT node naming and number from nodes

v9:
* Update maintainer section
* Update binding documentation to reflect update DT binding
* Restructure the EDAC driver such that its includes subnode for MC, PMD's,
  and future L3's and SoC.
* Update DT node to reflect update DT binding
* NOTE: Temporary removed L3 and SoC. They will be added back later on.

v8:
* Change ASM_EDAC_H to __ASM_EDAC_H in file edac.h
* Add WARN_ONCE in stub function atomic_scrub
* Update DTS binding documentation (with only memory controller node)
* Temporary remove L1/L2, L3, and SoC driver code and update memory driver
  code accordingly

v7:
* Update binding documentation for memory controller, PMD, L3, and SoC EDAC
* Change resource for PCP, CSW, MCBA, MCBB, and efuse to syscon type nodes
  and updae driver accordingly
* Fix clearing L2 RTO register properly
* Fix the MODULE_DEVICE_TABLE for OF
* Update DT accordingly to binding documentation

v6:
* Rebase to 4.0.0-rc3
* Add memory scrub stub function and enable ARM64 EDAC support patch
* Add bit definition defines for L2RTOS registers
* Remove un-necessary clearing of all L1/L2 software generated registers
* Remove wrong notification of LSU un-correctable error
* Change L2 reporting of un-correcable error to correctable error
* Change clearing of the L2C L2RTO registers
* Add support for L2 HW version 1 and version 2 or above
* Add support for L3 HW version 1 and version 2 or above

v5:
* Rebase to 3.17.rc1 (next)
* Update binding documentation for additional SoC node binding resource
* Enable MCU correctable and uncorrectable interrupts if not enabled by
  firmware
* Enable top level interrupt only after all MCU registered. Otherwise,
  error interrupt will never get cleared by the corresponding MCU.
* Remove clearing of L1 and L2 errors during initialization time. Otherwise,
  they will not be captured between firmware booting and error configuration.
* Add capture and clearing SoC register bus errors
* Add register bus resource to SoC DT node

v4:
* Fix PMD l1/l2 error reading address due to wrong variable type
* Fix clearing of software generated and HW errors for l1/l2

v3:
* Update binding documentation for PMD DT node and exampples
* Add binding documentation for SoC DT node
* Change MC, PMD, and L3C driver error injection to use debugfs
* Add missing IRQ for MC correctable error (code and DT)
* Use true/false where appropriate instead 1/0
* Add bit definition for L1 MMUESR register and fully decode this error
* Remove the un-necessary dev variable from xgene_edac_pmd_ctx structure
* Add check for disabled PMD (code and DT)
* Switch to edac_printk instead pr_err
* Some minor comments update

v2:
* Add EDAC entry in MAINTAINERS for APM EDAC driver
* Remove the MC scrub patch
* Remove the word 'Caches' from Kconfig
* Change all MASK defines to use BIT(x)
* Update comment or remove them
* Wrap error injection code around CONFIG_EDAC_DEBUG
* Change function name xgene_edac_mc_hw_init to xgene_edac_mc_irq_ctl
* Change all function XXX_hw_init to XXX_hw_ctl
* Fix typo 'activie'
* Move calling function edac_mc_alloc after resource retrieval
* Check for NULL on platform_get_resource return if reference directly
* Add documentation for struct xgene_edac_pmd_ctx
* Move L1 and L2 check out of function xgene_edac_pmd_check to its own
  functions
* Use for loop for configure each CPU of an PMD
* Replace /2 by >> 1
* Remove unnecessary comment on edac_device_add_device failure
* Make mem_err_ip static const
* Unwind EDAC register correctly if failed
---
Loc Ho (5):
  arm64: Enable EDAC on ARM64
  MAINTAINERS: Add entry for APM X-Gene SoC EDAC driver
  Documentation: Add documentation for the APM X-Gene SoC EDAC DTS
    binding
  edac: Add APM X-Gene SoC EDAC driver
  arm64: Add APM X-Gene SoC EDAC DTS entries

 .../devicetree/bindings/edac/apm-xgene-edac.txt    |   74 ++
 MAINTAINERS                                        |    7 +
 arch/arm64/Kconfig                                 |    1 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |   75 ++
 arch/arm64/include/asm/edac.h                      |   28 +
 drivers/edac/Kconfig                               |    7 +
 drivers/edac/Makefile                              |    1 +
 drivers/edac/xgene_edac.c                          | 1313 ++++++++++++++++++++
 8 files changed, 1506 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/edac/apm-xgene-edac.txt
 create mode 100644 arch/arm64/include/asm/edac.h
 create mode 100644 drivers/edac/xgene_edac.c

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 0/4] edac: Add APM X-Gene SoC EDAC driver
@ 2015-05-18 23:24 ` Loc Ho
  0 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-18 23:24 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds support for the APM X-Gene SoC EDAC driver for DT.

v10:
* Move regmap for CSW, MCBA, MCBB, and efuse to top driver node and add
  accessor support functions
* Change retrieval OF address proerty for subnode to use of_address_to_resource
* Add COMPILE_TEST to Kconfig for EDAC_XGENE
* Update DT binding documentation and DT node accordingly
* Remove DT node naming and number from nodes

v9:
* Update maintainer section
* Update binding documentation to reflect update DT binding
* Restructure the EDAC driver such that its includes subnode for MC, PMD's,
  and future L3's and SoC.
* Update DT node to reflect update DT binding
* NOTE: Temporary removed L3 and SoC. They will be added back later on.

v8:
* Change ASM_EDAC_H to __ASM_EDAC_H in file edac.h
* Add WARN_ONCE in stub function atomic_scrub
* Update DTS binding documentation (with only memory controller node)
* Temporary remove L1/L2, L3, and SoC driver code and update memory driver
  code accordingly

v7:
* Update binding documentation for memory controller, PMD, L3, and SoC EDAC
* Change resource for PCP, CSW, MCBA, MCBB, and efuse to syscon type nodes
  and updae driver accordingly
* Fix clearing L2 RTO register properly
* Fix the MODULE_DEVICE_TABLE for OF
* Update DT accordingly to binding documentation

v6:
* Rebase to 4.0.0-rc3
* Add memory scrub stub function and enable ARM64 EDAC support patch
* Add bit definition defines for L2RTOS registers
* Remove un-necessary clearing of all L1/L2 software generated registers
* Remove wrong notification of LSU un-correctable error
* Change L2 reporting of un-correcable error to correctable error
* Change clearing of the L2C L2RTO registers
* Add support for L2 HW version 1 and version 2 or above
* Add support for L3 HW version 1 and version 2 or above

v5:
* Rebase to 3.17.rc1 (next)
* Update binding documentation for additional SoC node binding resource
* Enable MCU correctable and uncorrectable interrupts if not enabled by
  firmware
* Enable top level interrupt only after all MCU registered. Otherwise,
  error interrupt will never get cleared by the corresponding MCU.
* Remove clearing of L1 and L2 errors during initialization time. Otherwise,
  they will not be captured between firmware booting and error configuration.
* Add capture and clearing SoC register bus errors
* Add register bus resource to SoC DT node

v4:
* Fix PMD l1/l2 error reading address due to wrong variable type
* Fix clearing of software generated and HW errors for l1/l2

v3:
* Update binding documentation for PMD DT node and exampples
* Add binding documentation for SoC DT node
* Change MC, PMD, and L3C driver error injection to use debugfs
* Add missing IRQ for MC correctable error (code and DT)
* Use true/false where appropriate instead 1/0
* Add bit definition for L1 MMUESR register and fully decode this error
* Remove the un-necessary dev variable from xgene_edac_pmd_ctx structure
* Add check for disabled PMD (code and DT)
* Switch to edac_printk instead pr_err
* Some minor comments update

v2:
* Add EDAC entry in MAINTAINERS for APM EDAC driver
* Remove the MC scrub patch
* Remove the word 'Caches' from Kconfig
* Change all MASK defines to use BIT(x)
* Update comment or remove them
* Wrap error injection code around CONFIG_EDAC_DEBUG
* Change function name xgene_edac_mc_hw_init to xgene_edac_mc_irq_ctl
* Change all function XXX_hw_init to XXX_hw_ctl
* Fix typo 'activie'
* Move calling function edac_mc_alloc after resource retrieval
* Check for NULL on platform_get_resource return if reference directly
* Add documentation for struct xgene_edac_pmd_ctx
* Move L1 and L2 check out of function xgene_edac_pmd_check to its own
  functions
* Use for loop for configure each CPU of an PMD
* Replace /2 by >> 1
* Remove unnecessary comment on edac_device_add_device failure
* Make mem_err_ip static const
* Unwind EDAC register correctly if failed
---
Loc Ho (5):
  arm64: Enable EDAC on ARM64
  MAINTAINERS: Add entry for APM X-Gene SoC EDAC driver
  Documentation: Add documentation for the APM X-Gene SoC EDAC DTS
    binding
  edac: Add APM X-Gene SoC EDAC driver
  arm64: Add APM X-Gene SoC EDAC DTS entries

 .../devicetree/bindings/edac/apm-xgene-edac.txt    |   74 ++
 MAINTAINERS                                        |    7 +
 arch/arm64/Kconfig                                 |    1 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |   75 ++
 arch/arm64/include/asm/edac.h                      |   28 +
 drivers/edac/Kconfig                               |    7 +
 drivers/edac/Makefile                              |    1 +
 drivers/edac/xgene_edac.c                          | 1313 ++++++++++++++++++++
 8 files changed, 1506 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/edac/apm-xgene-edac.txt
 create mode 100644 arch/arm64/include/asm/edac.h
 create mode 100644 drivers/edac/xgene_edac.c

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 1/5] arm64: Enable EDAC on ARM64
  2015-05-18 23:24 ` Loc Ho
@ 2015-05-18 23:24     ` Loc Ho
  -1 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-18 23:24 UTC (permalink / raw)
  To: dougthompson-aS9lmoZGLiVWk0Htik3J/w, bp-Gina5bIWoIWzQB+pC5nmwQ,
	mchehab-JPH+aEBZ4P+UEJcrhfAQsw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg
  Cc: linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	jcm-H+wXaHxf7aLQT0dZR+AlfA, patches-qTEPVZfXA3Y, Loc Ho

Add an stub atomic_scrub function and enable EDAC for arm64.

Signed-off-by: Loc Ho <lho-qTEPVZfXA3Y@public.gmane.org>
---
 arch/arm64/Kconfig            |    1 +
 arch/arm64/include/asm/edac.h |   28 ++++++++++++++++++++++++++++
 2 files changed, 29 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm64/include/asm/edac.h

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 4269dba..577078f 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -22,6 +22,7 @@ config ARM64
 	select BUILDTIME_EXTABLE_SORT
 	select CLONE_BACKWARDS
 	select COMMON_CLK
+	select EDAC_SUPPORT
 	select CPU_PM if (SUSPEND || CPU_IDLE)
 	select DCACHE_WORD_ACCESS
 	select GENERIC_ALLOCATOR
diff --git a/arch/arm64/include/asm/edac.h b/arch/arm64/include/asm/edac.h
new file mode 100644
index 0000000..683495b
--- /dev/null
+++ b/arch/arm64/include/asm/edac.h
@@ -0,0 +1,28 @@
+/*
+ * ARM64 EDAC Header File
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_EDAC_H
+#define __ASM_EDAC_H
+
+/*
+ * ECC atomic, DMA, SMP and interrupt safe scrub function.
+ */
+static inline void atomic_scrub(void *va, u32 size)
+{
+	/* Stub function for now until an ARM64 HW has a way to test it. */
+	WARN_ONCE(1, "not implemented");
+}
+
+#endif
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v10 1/5] arm64: Enable EDAC on ARM64
@ 2015-05-18 23:24     ` Loc Ho
  0 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-18 23:24 UTC (permalink / raw)
  To: linux-arm-kernel

Add an stub atomic_scrub function and enable EDAC for arm64.

Signed-off-by: Loc Ho <lho@apm.com>
---
 arch/arm64/Kconfig            |    1 +
 arch/arm64/include/asm/edac.h |   28 ++++++++++++++++++++++++++++
 2 files changed, 29 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm64/include/asm/edac.h

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 4269dba..577078f 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -22,6 +22,7 @@ config ARM64
 	select BUILDTIME_EXTABLE_SORT
 	select CLONE_BACKWARDS
 	select COMMON_CLK
+	select EDAC_SUPPORT
 	select CPU_PM if (SUSPEND || CPU_IDLE)
 	select DCACHE_WORD_ACCESS
 	select GENERIC_ALLOCATOR
diff --git a/arch/arm64/include/asm/edac.h b/arch/arm64/include/asm/edac.h
new file mode 100644
index 0000000..683495b
--- /dev/null
+++ b/arch/arm64/include/asm/edac.h
@@ -0,0 +1,28 @@
+/*
+ * ARM64 EDAC Header File
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_EDAC_H
+#define __ASM_EDAC_H
+
+/*
+ * ECC atomic, DMA, SMP and interrupt safe scrub function.
+ */
+static inline void atomic_scrub(void *va, u32 size)
+{
+	/* Stub function for now until an ARM64 HW has a way to test it. */
+	WARN_ONCE(1, "not implemented");
+}
+
+#endif
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v10 2/5] MAINTAINERS: Add entry for APM X-Gene SoC EDAC driver
  2015-05-18 23:24     ` Loc Ho
@ 2015-05-18 23:24         ` Loc Ho
  -1 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-18 23:24 UTC (permalink / raw)
  To: dougthompson-aS9lmoZGLiVWk0Htik3J/w, bp-Gina5bIWoIWzQB+pC5nmwQ,
	mchehab-JPH+aEBZ4P+UEJcrhfAQsw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg
  Cc: linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	jcm-H+wXaHxf7aLQT0dZR+AlfA, patches-qTEPVZfXA3Y, Loc Ho

This patch adds a MAINTAINERS entry for APM X-Gene SoC EDAC driver.

Signed-off-by: Loc Ho <lho-qTEPVZfXA3Y@public.gmane.org>
---
 MAINTAINERS |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 00a586b..0134fee 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3735,6 +3735,13 @@ W:	bluesmoke.sourceforge.net
 S:	Maintained
 F:	drivers/edac/sb_edac.c
 
+EDAC-XGENE
+APPLIED MICRO (APM) X-GENE SOC EDAC
+M:     Loc Ho <lho-qTEPVZfXA3Y@public.gmane.org>
+S:     Supported
+F:     drivers/edac/xgene_edac.c
+F:     Documentation/devicetree/bindings/edac/apm-xgene-edac.txt
+
 EDIROL UA-101/UA-1000 DRIVER
 M:	Clemens Ladisch <clemens-P6GI/4k7KOmELgA04lAiVw@public.gmane.org>
 L:	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw@public.gmane.org (moderated for non-subscribers)
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v10 2/5] MAINTAINERS: Add entry for APM X-Gene SoC EDAC driver
@ 2015-05-18 23:24         ` Loc Ho
  0 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-18 23:24 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds a MAINTAINERS entry for APM X-Gene SoC EDAC driver.

Signed-off-by: Loc Ho <lho@apm.com>
---
 MAINTAINERS |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 00a586b..0134fee 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3735,6 +3735,13 @@ W:	bluesmoke.sourceforge.net
 S:	Maintained
 F:	drivers/edac/sb_edac.c
 
+EDAC-XGENE
+APPLIED MICRO (APM) X-GENE SOC EDAC
+M:     Loc Ho <lho@apm.com>
+S:     Supported
+F:     drivers/edac/xgene_edac.c
+F:     Documentation/devicetree/bindings/edac/apm-xgene-edac.txt
+
 EDIROL UA-101/UA-1000 DRIVER
 M:	Clemens Ladisch <clemens@ladisch.de>
 L:	alsa-devel at alsa-project.org (moderated for non-subscribers)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v10 3/5] Documentation: Add documentation for the APM X-Gene SoC EDAC DTS binding
  2015-05-18 23:24         ` Loc Ho
@ 2015-05-18 23:24             ` Loc Ho
  -1 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-18 23:24 UTC (permalink / raw)
  To: dougthompson-aS9lmoZGLiVWk0Htik3J/w, bp-Gina5bIWoIWzQB+pC5nmwQ,
	mchehab-JPH+aEBZ4P+UEJcrhfAQsw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg
  Cc: linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	jcm-H+wXaHxf7aLQT0dZR+AlfA, patches-qTEPVZfXA3Y, Loc Ho

This patch adds documentation for the APM X-Gene SoC EDAC DTS binding.

Signed-off-by: Loc Ho <lho-qTEPVZfXA3Y@public.gmane.org>
---
 .../devicetree/bindings/edac/apm-xgene-edac.txt    |   74 ++++++++++++++++++++
 1 files changed, 74 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/edac/apm-xgene-edac.txt

diff --git a/Documentation/devicetree/bindings/edac/apm-xgene-edac.txt b/Documentation/devicetree/bindings/edac/apm-xgene-edac.txt
new file mode 100644
index 0000000..d8f2782
--- /dev/null
+++ b/Documentation/devicetree/bindings/edac/apm-xgene-edac.txt
@@ -0,0 +1,74 @@
+* APM X-Gene SoC EDAC node
+
+EDAC node is defined to describe on-chip error detection and correction.
+The follow error types are supported:
+
+  memory controller	- Memory controller
+  PMD (L1/L2)		- Processor module unit (PMD) L1/L2 cache
+
+The following section describes the EDAC DT node binding.
+
+Required properties:
+- compatible		: Shall be "apm,xgene-edac".
+- regmap-csw		: Regmap of the CPU switch fabric (CSW) resource.
+- regmap-mcba		: Regmap of the MCB-A (memory bridge) resource.
+- regmap-mcbb		: Regmap of the MCB-B (memory bridge) resource.
+- regmap-efuse		: Regmap of the PMD efuse resource.
+- reg			: First resource shall be the CPU bus (PCP) resource.
+- interrupts            : Interrupt-specifier for MCU, PMD, L3, or SoC error
+			  IRQ(s).
+
+Required properties for memory controller subnode:
+- compatible		: Shall be "apm,xgene-edac-mc".
+- reg			: First resource shall be the memory controller unit
+                          (MCU) resource.
+
+Required properties for PMD subnode:
+- compatible		: Shall be "apm,xgene-edac-pmd".
+- reg			: First resource shall be the PMD resource.
+
+Example:
+	csw: csw@7e200000 {
+		compatible = "syscon";
+		reg = <0x0 0x7e200000 0x0 0x1000>;
+	};
+
+	mcba: mcba@7e700000 {
+		compatible = "syscon";
+		reg = <0x0 0x7e700000 0x0 0x1000>;
+	};
+
+	mcbb: mcbb@7e720000 {
+		compatible = "syscon";
+		reg = <0x0 0x7e720000 0x0 0x1000>;
+	};
+
+	efuse: efuse@1054a000 {
+		compatible = "syscon";
+		reg = <0x0 0x1054a000 0x0 0x20>;
+	};
+
+	edac@78800000 {
+		compatible = "apm,xgene-edac";
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		regmap-csw = <&csw>;
+		regmap-mcba = <&mcba>;
+		regmap-mcbb = <&mcbb>;
+		regmap-efuse = <&efuse>;
+		reg = <0x0 0x78800000 0x0 0x100>;
+		interrupts = <0x0 0x20 0x4>,
+			     <0x0 0x21 0x4>,
+			     <0x0 0x27 0x4>;
+
+		edacmc@7e800000 {
+			compatible = "apm,xgene-edac-mc";
+			reg = <0x0 0x7e800000 0x0 0x1000>;
+		};
+
+		edacpmd@7c000000 {
+			compatible = "apm,xgene-edac-pmd";
+			reg = <0x0 0x7c000000 0x0 0x200000>;
+		};
+	};
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v10 3/5] Documentation: Add documentation for the APM X-Gene SoC EDAC DTS binding
@ 2015-05-18 23:24             ` Loc Ho
  0 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-18 23:24 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds documentation for the APM X-Gene SoC EDAC DTS binding.

Signed-off-by: Loc Ho <lho@apm.com>
---
 .../devicetree/bindings/edac/apm-xgene-edac.txt    |   74 ++++++++++++++++++++
 1 files changed, 74 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/edac/apm-xgene-edac.txt

diff --git a/Documentation/devicetree/bindings/edac/apm-xgene-edac.txt b/Documentation/devicetree/bindings/edac/apm-xgene-edac.txt
new file mode 100644
index 0000000..d8f2782
--- /dev/null
+++ b/Documentation/devicetree/bindings/edac/apm-xgene-edac.txt
@@ -0,0 +1,74 @@
+* APM X-Gene SoC EDAC node
+
+EDAC node is defined to describe on-chip error detection and correction.
+The follow error types are supported:
+
+  memory controller	- Memory controller
+  PMD (L1/L2)		- Processor module unit (PMD) L1/L2 cache
+
+The following section describes the EDAC DT node binding.
+
+Required properties:
+- compatible		: Shall be "apm,xgene-edac".
+- regmap-csw		: Regmap of the CPU switch fabric (CSW) resource.
+- regmap-mcba		: Regmap of the MCB-A (memory bridge) resource.
+- regmap-mcbb		: Regmap of the MCB-B (memory bridge) resource.
+- regmap-efuse		: Regmap of the PMD efuse resource.
+- reg			: First resource shall be the CPU bus (PCP) resource.
+- interrupts            : Interrupt-specifier for MCU, PMD, L3, or SoC error
+			  IRQ(s).
+
+Required properties for memory controller subnode:
+- compatible		: Shall be "apm,xgene-edac-mc".
+- reg			: First resource shall be the memory controller unit
+                          (MCU) resource.
+
+Required properties for PMD subnode:
+- compatible		: Shall be "apm,xgene-edac-pmd".
+- reg			: First resource shall be the PMD resource.
+
+Example:
+	csw: csw at 7e200000 {
+		compatible = "syscon";
+		reg = <0x0 0x7e200000 0x0 0x1000>;
+	};
+
+	mcba: mcba at 7e700000 {
+		compatible = "syscon";
+		reg = <0x0 0x7e700000 0x0 0x1000>;
+	};
+
+	mcbb: mcbb at 7e720000 {
+		compatible = "syscon";
+		reg = <0x0 0x7e720000 0x0 0x1000>;
+	};
+
+	efuse: efuse at 1054a000 {
+		compatible = "syscon";
+		reg = <0x0 0x1054a000 0x0 0x20>;
+	};
+
+	edac at 78800000 {
+		compatible = "apm,xgene-edac";
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		regmap-csw = <&csw>;
+		regmap-mcba = <&mcba>;
+		regmap-mcbb = <&mcbb>;
+		regmap-efuse = <&efuse>;
+		reg = <0x0 0x78800000 0x0 0x100>;
+		interrupts = <0x0 0x20 0x4>,
+			     <0x0 0x21 0x4>,
+			     <0x0 0x27 0x4>;
+
+		edacmc at 7e800000 {
+			compatible = "apm,xgene-edac-mc";
+			reg = <0x0 0x7e800000 0x0 0x1000>;
+		};
+
+		edacpmd at 7c000000 {
+			compatible = "apm,xgene-edac-pmd";
+			reg = <0x0 0x7c000000 0x0 0x200000>;
+		};
+	};
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
  2015-05-18 23:24             ` Loc Ho
@ 2015-05-18 23:24                 ` Loc Ho
  -1 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-18 23:24 UTC (permalink / raw)
  To: dougthompson-aS9lmoZGLiVWk0Htik3J/w, bp-Gina5bIWoIWzQB+pC5nmwQ,
	mchehab-JPH+aEBZ4P+UEJcrhfAQsw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg
  Cc: linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	jcm-H+wXaHxf7aLQT0dZR+AlfA, patches-qTEPVZfXA3Y, Loc Ho

This patch adds support for the APM X-Gene SoC EDAC driver.

Signed-off-by: Loc Ho <lho-qTEPVZfXA3Y@public.gmane.org>
---
 drivers/edac/Kconfig      |    7 +
 drivers/edac/Makefile     |    1 +
 drivers/edac/xgene_edac.c | 1313 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1321 insertions(+), 0 deletions(-)
 create mode 100644 drivers/edac/xgene_edac.c

diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index cb59619..ffa7cac 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -392,4 +392,11 @@ config EDAC_SYNOPSYS
 	  Support for error detection and correction on the Synopsys DDR
 	  memory controller.
 
+config EDAC_XGENE
+	tristate "APM X-Gene SoC"
+	depends on EDAC_MM_EDAC && (ARM64 || COMPILE_TEST)
+	help
+	  Support for error detection and correction on the
+	  APM X-Gene family of SOCs.
+
 endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index b255f36..28ef2a5 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -68,3 +68,4 @@ obj-$(CONFIG_EDAC_OCTEON_PCI)		+= octeon_edac-pci.o
 
 obj-$(CONFIG_EDAC_ALTERA_MC)		+= altera_edac.o
 obj-$(CONFIG_EDAC_SYNOPSYS)		+= synopsys_edac.o
+obj-$(CONFIG_EDAC_XGENE)		+= xgene_edac.o
diff --git a/drivers/edac/xgene_edac.c b/drivers/edac/xgene_edac.c
new file mode 100644
index 0000000..8155b86
--- /dev/null
+++ b/drivers/edac/xgene_edac.c
@@ -0,0 +1,1313 @@
+/*
+ * APM X-Gene SoC EDAC (error detection and correction)
+ *
+ * Copyright (c) 2015, Applied Micro Circuits Corporation
+ * Author: Feng Kan <fkan-qTEPVZfXA3Y@public.gmane.org>
+ *         Loc Ho <lho-qTEPVZfXA3Y@public.gmane.org>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/ctype.h>
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+
+#include "edac_core.h"
+
+#define EDAC_MOD_STR			"xgene_edac"
+
+/* Global error configuration status registers (CSR) */
+#define PCPHPERRINTSTS			0x0000
+#define PCPHPERRINTMSK			0x0004
+#define  MCU_CTL_ERR_MASK		BIT(12)
+#define  IOB_PA_ERR_MASK		BIT(11)
+#define  IOB_BA_ERR_MASK		BIT(10)
+#define  IOB_XGIC_ERR_MASK		BIT(9)
+#define  IOB_RB_ERR_MASK		BIT(8)
+#define  L3C_UNCORR_ERR_MASK		BIT(5)
+#define  MCU_UNCORR_ERR_MASK		BIT(4)
+#define  PMD3_MERR_MASK			BIT(3)
+#define  PMD2_MERR_MASK			BIT(2)
+#define  PMD1_MERR_MASK			BIT(1)
+#define  PMD0_MERR_MASK			BIT(0)
+#define PCPLPERRINTSTS			0x0008
+#define PCPLPERRINTMSK			0x000C
+#define  CSW_SWITCH_TRACE_ERR_MASK	BIT(2)
+#define  L3C_CORR_ERR_MASK		BIT(1)
+#define  MCU_CORR_ERR_MASK		BIT(0)
+#define MEMERRINTSTS			0x0010
+#define MEMERRINTMSK			0x0014
+
+struct xgene_edac {
+	struct device		*dev;
+	struct regmap		*csw_map;
+	struct regmap		*mcba_map;
+	struct regmap		*mcbb_map;
+	struct regmap		*efuse_map;
+	void __iomem		*pcp_csr;
+	spinlock_t		lock;
+	struct dentry		*dfs;
+
+	struct list_head	mcus;
+	struct list_head	pmds;
+};
+
+static int xgene_edac_pcp_rd(struct xgene_edac *edac, u32 reg, u32 *val);
+static int xgene_edac_pcp_clrbits(struct xgene_edac *edac, u32 reg,
+				  u32 bits_mask);
+static int xgene_edac_pcp_setbits(struct xgene_edac *edac, u32 reg,
+				  u32 bits_mask);
+static int xgene_edac_csw_rd(struct xgene_edac *edac, u32 reg, u32 *val);
+static int xgene_edac_mcba_rd(struct xgene_edac *edac, u32 reg, u32 *val);
+static int xgene_edac_mcbb_rd(struct xgene_edac *edac, u32 reg, u32 *val);
+static int xgene_edac_efuse_rd(struct xgene_edac *edac, u32 reg, u32 *val);
+
+static int edac_mc_idx;
+static int edac_mc_active_mask;
+static int edac_mc_registered_mask;
+static DEFINE_MUTEX(xgene_edac_mc_lock);
+
+/* Memory controller error CSR */
+#define MCU_MAX_RANK			8
+#define MCU_RANK_STRIDE			0x40
+
+#define MCUGECR				0x0110
+#define  MCU_GECR_DEMANDUCINTREN_MASK	BIT(0)
+#define  MCU_GECR_BACKUCINTREN_MASK	BIT(1)
+#define  MCU_GECR_CINTREN_MASK		BIT(2)
+#define  MUC_GECR_MCUADDRERREN_MASK	BIT(9)
+#define MCUGESR				0x0114
+#define  MCU_GESR_ADDRNOMATCH_ERR_MASK	BIT(7)
+#define  MCU_GESR_ADDRMULTIMATCH_ERR_MASK	BIT(6)
+#define  MCU_GESR_PHYP_ERR_MASK		BIT(3)
+#define MCUESRR0			0x0314
+#define  MCU_ESRR_MULTUCERR_MASK	BIT(3)
+#define  MCU_ESRR_BACKUCERR_MASK	BIT(2)
+#define  MCU_ESRR_DEMANDUCERR_MASK	BIT(1)
+#define  MCU_ESRR_CERR_MASK		BIT(0)
+#define MCUESRRA0			0x0318
+#define MCUEBLRR0			0x031c
+#define  MCU_EBLRR_ERRBANK_RD(src)	(((src) & 0x00000007) >> 0)
+#define MCUERCRR0			0x0320
+#define  MCU_ERCRR_ERRROW_RD(src)	(((src) & 0xFFFF0000) >> 16)
+#define  MCU_ERCRR_ERRCOL_RD(src)	((src) & 0x00000FFF)
+#define MCUSBECNT0			0x0324
+#define MCU_SBECNT_COUNT(src)		((src) & 0xFFFF)
+
+#define CSW_CSWCR			0x0000
+#define  CSW_CSWCR_DUALMCB_MASK		BIT(0)
+
+#define MCBADDRMR			0x0000
+#define  MCBADDRMR_MCU_INTLV_MODE_MASK	BIT(3)
+#define  MCBADDRMR_DUALMCU_MODE_MASK	BIT(2)
+#define  MCBADDRMR_MCB_INTLV_MODE_MASK	BIT(1)
+#define  MCBADDRMR_ADDRESS_MODE_MASK	BIT(0)
+
+struct xgene_edac_mc_ctx {
+	struct list_head	next;
+	char			*name;
+	struct mem_ctl_info	*mci;
+	struct xgene_edac	*edac;
+	void __iomem		*mcu_csr;
+	int			mcu_id;
+};
+
+#ifdef CONFIG_EDAC_DEBUG
+static ssize_t xgene_edac_mc_err_inject_write(struct file *file,
+					      const char __user *data,
+					      size_t count, loff_t *ppos)
+{
+	struct mem_ctl_info *mci = file->private_data;
+	struct xgene_edac_mc_ctx *ctx = mci->pvt_info;
+	int i;
+
+	for (i = 0; i < MCU_MAX_RANK; i++) {
+		writel(MCU_ESRR_MULTUCERR_MASK | MCU_ESRR_BACKUCERR_MASK |
+		       MCU_ESRR_DEMANDUCERR_MASK | MCU_ESRR_CERR_MASK,
+		       ctx->mcu_csr + MCUESRRA0 + i * MCU_RANK_STRIDE);
+	}
+	return count;
+}
+
+static const struct file_operations xgene_edac_mc_debug_inject_fops = {
+	.open = simple_open,
+	.write = xgene_edac_mc_err_inject_write,
+	.llseek = generic_file_llseek,
+};
+
+static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
+{
+	if (!mci->debugfs)
+		return;
+
+	debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
+			    &xgene_edac_mc_debug_inject_fops);
+}
+#else
+static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
+{
+}
+#endif
+
+static void xgene_edac_mc_check(struct mem_ctl_info *mci)
+{
+	struct xgene_edac_mc_ctx *ctx = mci->pvt_info;
+	unsigned int pcp_hp_stat;
+	unsigned int pcp_lp_stat;
+	u32 reg;
+	u32 rank;
+	u32 bank;
+	u32 count;
+	u32 col_row;
+
+	if (xgene_edac_pcp_rd(ctx->edac, PCPHPERRINTSTS, &pcp_hp_stat))
+		return;
+	if (xgene_edac_pcp_rd(ctx->edac, PCPLPERRINTSTS, &pcp_lp_stat))
+		return;
+	if (!((MCU_UNCORR_ERR_MASK & pcp_hp_stat) ||
+	      (MCU_CTL_ERR_MASK & pcp_hp_stat) ||
+	      (MCU_CORR_ERR_MASK & pcp_lp_stat)))
+		return;
+
+	for (rank = 0; rank < MCU_MAX_RANK; rank++) {
+		reg = readl(ctx->mcu_csr + MCUESRR0 + rank * MCU_RANK_STRIDE);
+
+		/* Detect uncorrectable memory error */
+		if (reg & (MCU_ESRR_DEMANDUCERR_MASK |
+			   MCU_ESRR_BACKUCERR_MASK)) {
+			/* Detected uncorrectable memory error */
+			edac_mc_chipset_printk(mci, KERN_ERR, "X-Gene",
+				"MCU uncorrectable error at rank %d\n", rank);
+
+			edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+				1, 0, 0, 0, 0, 0, -1, mci->ctl_name, "");
+		}
+
+		/* Detect correctable memory error */
+		if (reg & MCU_ESRR_CERR_MASK) {
+			bank = readl(ctx->mcu_csr + MCUEBLRR0 +
+				     rank * MCU_RANK_STRIDE);
+			col_row = readl(ctx->mcu_csr + MCUERCRR0 +
+					rank * MCU_RANK_STRIDE);
+			count = readl(ctx->mcu_csr + MCUSBECNT0 +
+				      rank * MCU_RANK_STRIDE);
+			edac_mc_chipset_printk(mci, KERN_WARNING, "X-Gene",
+				"MCU correctable error at rank %d bank %d column %d row %d count %d\n",
+				rank, MCU_EBLRR_ERRBANK_RD(bank),
+				MCU_ERCRR_ERRCOL_RD(col_row),
+				MCU_ERCRR_ERRROW_RD(col_row),
+				MCU_SBECNT_COUNT(count));
+
+			edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+				1, 0, 0, 0, 0, 0, -1, mci->ctl_name, "");
+		}
+
+		/* Clear all error registers */
+		writel(0x0, ctx->mcu_csr + MCUEBLRR0 + rank * MCU_RANK_STRIDE);
+		writel(0x0, ctx->mcu_csr + MCUERCRR0 + rank * MCU_RANK_STRIDE);
+		writel(0x0, ctx->mcu_csr + MCUSBECNT0 +
+		       rank * MCU_RANK_STRIDE);
+		writel(reg, ctx->mcu_csr + MCUESRR0 + rank * MCU_RANK_STRIDE);
+	}
+
+	/* Detect memory controller error */
+	reg = readl(ctx->mcu_csr + MCUGESR);
+	if (reg) {
+		if (reg & MCU_GESR_ADDRNOMATCH_ERR_MASK)
+			edac_mc_chipset_printk(mci, KERN_WARNING, "X-Gene",
+				"MCU address miss-match error\n");
+		if (reg & MCU_GESR_ADDRMULTIMATCH_ERR_MASK)
+			edac_mc_chipset_printk(mci, KERN_WARNING, "X-Gene",
+				"MCU address multi-match error\n");
+
+		writel(reg, ctx->mcu_csr + MCUGESR);
+	}
+}
+
+static void xgene_edac_mc_irq_ctl(struct mem_ctl_info *mci, bool enable)
+{
+	struct xgene_edac_mc_ctx *ctx = mci->pvt_info;
+	unsigned int val;
+
+	if (edac_op_state != EDAC_OPSTATE_INT)
+		return;
+
+	mutex_lock(&xgene_edac_mc_lock);
+
+	/*
+	 * As there is only single bit for enable error and interrupt mask,
+	 * we must only enable top level interrupt after all MCUs are
+	 * registered. Otherwise, if there is an error and the corresponding
+	 * MCU has not registered, the interrupt will never get cleared. To
+	 * determine all MCU have registered, we will keep track of active
+	 * MCUs and registered MCUs.
+	 */
+	if (enable) {
+		/* Set registered MCU bit */
+		edac_mc_registered_mask |= 1 << ctx->mcu_id;
+
+		/* Enable interrupt after all active MCU registered */
+		if (edac_mc_registered_mask == edac_mc_active_mask) {
+			/* Enable memory controller top level interrupt */
+			xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK,
+					       MCU_UNCORR_ERR_MASK |
+					       MCU_CTL_ERR_MASK);
+			xgene_edac_pcp_clrbits(ctx->edac, PCPLPERRINTMSK,
+					       MCU_CORR_ERR_MASK);
+		}
+
+		/* Enable MCU interrupt and error reporting */
+		val = readl(ctx->mcu_csr + MCUGECR);
+		val |= MCU_GECR_DEMANDUCINTREN_MASK |
+		       MCU_GECR_BACKUCINTREN_MASK |
+		       MCU_GECR_CINTREN_MASK |
+		       MUC_GECR_MCUADDRERREN_MASK;
+		writel(val, ctx->mcu_csr + MCUGECR);
+	} else {
+		/* Disable MCU interrupt */
+		val = readl(ctx->mcu_csr + MCUGECR);
+		val &= ~(MCU_GECR_DEMANDUCINTREN_MASK |
+			 MCU_GECR_BACKUCINTREN_MASK |
+			 MCU_GECR_CINTREN_MASK |
+			 MUC_GECR_MCUADDRERREN_MASK);
+		writel(val, ctx->mcu_csr + MCUGECR);
+
+		/* Disable memory controller top level interrupt */
+		xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK,
+				       MCU_UNCORR_ERR_MASK | MCU_CTL_ERR_MASK);
+		xgene_edac_pcp_setbits(ctx->edac, PCPLPERRINTMSK,
+				       MCU_CORR_ERR_MASK);
+
+		/* Clear registered MCU bit */
+		edac_mc_registered_mask &= ~(1 << ctx->mcu_id);
+	}
+
+	mutex_unlock(&xgene_edac_mc_lock);
+}
+
+static int xgene_edac_mc_is_active(struct xgene_edac_mc_ctx *ctx, int mc_idx)
+{
+	unsigned int reg;
+	u32 mcu_mask;
+
+	if (xgene_edac_csw_rd(ctx->edac, CSW_CSWCR, &reg))
+		return 0;
+
+	if (reg & CSW_CSWCR_DUALMCB_MASK) {
+		/*
+		 * Dual MCB active - Determine if all 4 active or just MCU0
+		 * and MCU2 active
+		 */
+		if (xgene_edac_mcbb_rd(ctx->edac, MCBADDRMR, &reg))
+			return 0;
+		mcu_mask = (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0xF : 0x5;
+	} else {
+		/*
+		 * Single MCB active - Determine if MCU0/MCU1 or just MCU0
+		 * active
+		 */
+		if (xgene_edac_mcba_rd(ctx->edac, MCBADDRMR, &reg))
+			return 0;
+		mcu_mask = (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0x3 : 0x1;
+	}
+
+	/* Save active MC mask if hasn't set already */
+	if (!edac_mc_active_mask)
+		edac_mc_active_mask = mcu_mask;
+
+	return (mcu_mask & (1 << mc_idx)) ? 1 : 0;
+}
+
+static int xgene_edac_mc_add(struct xgene_edac *edac, struct device_node *np)
+{
+	struct mem_ctl_info *mci;
+	struct edac_mc_layer layers[2];
+	struct xgene_edac_mc_ctx tmp_ctx;
+	struct xgene_edac_mc_ctx *ctx;
+	struct resource res;
+	int rc;
+
+	memset(&tmp_ctx, 0, sizeof(tmp_ctx));
+	tmp_ctx.edac = edac;
+
+	if (!devres_open_group(edac->dev, xgene_edac_mc_add, GFP_KERNEL))
+		return -ENOMEM;
+
+	rc = of_address_to_resource(np, 0, &res);
+	if (rc < 0) {
+		dev_err(edac->dev, "no MCU resource address\n");
+		goto err_group;
+	}
+	tmp_ctx.mcu_csr = devm_ioremap_resource(edac->dev, &res);
+	if (IS_ERR(tmp_ctx.mcu_csr)) {
+		dev_err(edac->dev, "unable to map MCU resource\n");
+		rc = PTR_ERR(tmp_ctx.mcu_csr);
+		goto err_group;
+	}
+
+	/* Ignore non-active MCU */
+	tmp_ctx.mcu_id = ((res.start >> 16) & 0xF) / 4;
+	if (!xgene_edac_mc_is_active(&tmp_ctx, tmp_ctx.mcu_id)) {
+		rc = -ENODEV;
+		goto err_group;
+	}
+
+	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+	layers[0].size = 4;
+	layers[0].is_virt_csrow = true;
+	layers[1].type = EDAC_MC_LAYER_CHANNEL;
+	layers[1].size = 2;
+	layers[1].is_virt_csrow = false;
+	mci = edac_mc_alloc(edac_mc_idx++, ARRAY_SIZE(layers), layers,
+			    sizeof(*ctx));
+	if (!mci) {
+		rc = -ENOMEM;
+		goto err_group;
+	}
+
+	ctx = mci->pvt_info;
+	*ctx = tmp_ctx;		/* Copy over resource value */
+	ctx->name = "xgene_edac_mc_err";
+	ctx->mci = mci;
+	mci->pdev = &mci->dev;
+	mci->ctl_name = ctx->name;
+	mci->dev_name = ctx->name;
+
+	mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_RDDR2 | MEM_FLAG_RDDR3 |
+			 MEM_FLAG_DDR | MEM_FLAG_DDR2 | MEM_FLAG_DDR3;
+	mci->edac_ctl_cap = EDAC_FLAG_SECDED;
+	mci->edac_cap = EDAC_FLAG_SECDED;
+	mci->mod_name = EDAC_MOD_STR;
+	mci->mod_ver = "0.1";
+	mci->ctl_page_to_phys = NULL;
+	mci->scrub_cap = SCRUB_FLAG_HW_SRC;
+	mci->scrub_mode = SCRUB_HW_SRC;
+
+	if (edac_op_state == EDAC_OPSTATE_POLL)
+		mci->edac_check = xgene_edac_mc_check;
+
+	if (edac_mc_add_mc(mci)) {
+		dev_err(edac->dev, "edac_mc_add_mc failed\n");
+		rc = -EINVAL;
+		goto err_free;
+	}
+
+	xgene_edac_mc_create_debugfs_node(mci);
+
+	list_add(&ctx->next, &edac->mcus);
+
+	xgene_edac_mc_irq_ctl(mci, true);
+
+	devres_remove_group(edac->dev, xgene_edac_mc_add);
+
+	dev_info(edac->dev, "X-Gene EDAC MC registered\n");
+	return 0;
+
+err_free:
+	edac_mc_free(mci);
+err_group:
+	devres_release_group(edac->dev, xgene_edac_mc_add);
+	return rc;
+}
+
+static int xgene_edac_mc_remove(struct xgene_edac_mc_ctx *mcu)
+{
+	xgene_edac_mc_irq_ctl(mcu->mci, false);
+	edac_mc_del_mc(&mcu->mci->dev);
+	edac_mc_free(mcu->mci);
+	return 0;
+}
+
+/* CPU L1/L2 error CSR */
+#define MAX_CPU_PER_PMD				2
+#define CPU_CSR_STRIDE				0x00100000
+#define CPU_L2C_PAGE				0x000D0000
+#define CPU_MEMERR_L2C_PAGE			0x000E0000
+#define CPU_MEMERR_CPU_PAGE			0x000F0000
+
+#define MEMERR_CPU_ICFECR_PAGE_OFFSET		0x0000
+#define MEMERR_CPU_ICFESR_PAGE_OFFSET		0x0004
+#define  MEMERR_CPU_ICFESR_ERRWAY_RD(src)	(((src) & 0xFF000000) >> 24)
+#define  MEMERR_CPU_ICFESR_ERRINDEX_RD(src)	(((src) & 0x003F0000) >> 16)
+#define  MEMERR_CPU_ICFESR_ERRINFO_RD(src)	(((src) & 0x0000FF00) >> 8)
+#define  MEMERR_CPU_ICFESR_ERRTYPE_RD(src)	(((src) & 0x00000070) >> 4)
+#define  MEMERR_CPU_ICFESR_MULTCERR_MASK	BIT(2)
+#define  MEMERR_CPU_ICFESR_CERR_MASK		BIT(0)
+#define MEMERR_CPU_LSUESR_PAGE_OFFSET		0x000c
+#define  MEMERR_CPU_LSUESR_ERRWAY_RD(src)	(((src) & 0xFF000000) >> 24)
+#define  MEMERR_CPU_LSUESR_ERRINDEX_RD(src)	(((src) & 0x003F0000) >> 16)
+#define  MEMERR_CPU_LSUESR_ERRINFO_RD(src)	(((src) & 0x0000FF00) >> 8)
+#define  MEMERR_CPU_LSUESR_ERRTYPE_RD(src)	(((src) & 0x00000070) >> 4)
+#define  MEMERR_CPU_LSUESR_MULTCERR_MASK	BIT(2)
+#define  MEMERR_CPU_LSUESR_CERR_MASK		BIT(0)
+#define MEMERR_CPU_LSUECR_PAGE_OFFSET		0x0008
+#define MEMERR_CPU_MMUECR_PAGE_OFFSET		0x0010
+#define MEMERR_CPU_MMUESR_PAGE_OFFSET		0x0014
+#define  MEMERR_CPU_MMUESR_ERRWAY_RD(src)	(((src) & 0xFF000000) >> 24)
+#define  MEMERR_CPU_MMUESR_ERRINDEX_RD(src)	(((src) & 0x007F0000) >> 16)
+#define  MEMERR_CPU_MMUESR_ERRINFO_RD(src)	(((src) & 0x0000FF00) >> 8)
+#define  MEMERR_CPU_MMUESR_ERRREQSTR_LSU_MASK	BIT(7)
+#define  MEMERR_CPU_MMUESR_ERRTYPE_RD(src)	(((src) & 0x00000070) >> 4)
+#define  MEMERR_CPU_MMUESR_MULTCERR_MASK	BIT(2)
+#define  MEMERR_CPU_MMUESR_CERR_MASK		BIT(0)
+#define MEMERR_CPU_ICFESRA_PAGE_OFFSET		0x0804
+#define MEMERR_CPU_LSUESRA_PAGE_OFFSET		0x080c
+#define MEMERR_CPU_MMUESRA_PAGE_OFFSET		0x0814
+
+#define MEMERR_L2C_L2ECR_PAGE_OFFSET		0x0000
+#define MEMERR_L2C_L2ESR_PAGE_OFFSET		0x0004
+#define  MEMERR_L2C_L2ESR_ERRSYN_RD(src)	(((src) & 0xFF000000) >> 24)
+#define  MEMERR_L2C_L2ESR_ERRWAY_RD(src)	(((src) & 0x00FC0000) >> 18)
+#define  MEMERR_L2C_L2ESR_ERRCPU_RD(src)	(((src) & 0x00020000) >> 17)
+#define  MEMERR_L2C_L2ESR_ERRGROUP_RD(src)	(((src) & 0x0000E000) >> 13)
+#define  MEMERR_L2C_L2ESR_ERRACTION_RD(src)	(((src) & 0x00001C00) >> 10)
+#define  MEMERR_L2C_L2ESR_ERRTYPE_RD(src)	(((src) & 0x00000300) >> 8)
+#define  MEMERR_L2C_L2ESR_MULTUCERR_MASK	BIT(3)
+#define  MEMERR_L2C_L2ESR_MULTICERR_MASK	BIT(2)
+#define  MEMERR_L2C_L2ESR_UCERR_MASK		BIT(1)
+#define  MEMERR_L2C_L2ESR_ERR_MASK		BIT(0)
+#define MEMERR_L2C_L2EALR_PAGE_OFFSET		0x0008
+#define CPUX_L2C_L2RTOCR_PAGE_OFFSET		0x0010
+#define MEMERR_L2C_L2EAHR_PAGE_OFFSET		0x000c
+#define CPUX_L2C_L2RTOSR_PAGE_OFFSET		0x0014
+#define  MEMERR_L2C_L2RTOSR_MULTERR_MASK	BIT(1)
+#define  MEMERR_L2C_L2RTOSR_ERR_MASK		BIT(0)
+#define CPUX_L2C_L2RTOALR_PAGE_OFFSET		0x0018
+#define CPUX_L2C_L2RTOAHR_PAGE_OFFSET		0x001c
+#define MEMERR_L2C_L2ESRA_PAGE_OFFSET		0x0804
+
+/*
+ * Processor Module Domain (PMD) context - Context for a pair of processsors.
+ * Each PMD consists of 2 CPUs and a shared L2 cache. Each CPU consists of
+ * its own L1 cache.
+ */
+struct xgene_edac_pmd_ctx {
+	struct list_head	next;
+	struct device		ddev;
+	char			*name;
+	struct xgene_edac	*edac;
+	struct edac_device_ctl_info *edac_dev;
+	void __iomem		*pmd_csr;
+	int			pmd;
+};
+
+static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev,
+				    int cpu_idx)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	void __iomem *pg_f;
+	u32 val;
+
+	pg_f = ctx->pmd_csr + cpu_idx * CPU_CSR_STRIDE + CPU_MEMERR_CPU_PAGE;
+
+	val = readl(pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET);
+	if (val) {
+		dev_err(edac_dev->dev,
+			"CPU%d L1 memory error ICF 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n",
+			ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
+			MEMERR_CPU_ICFESR_ERRWAY_RD(val),
+			MEMERR_CPU_ICFESR_ERRINDEX_RD(val),
+			MEMERR_CPU_ICFESR_ERRINFO_RD(val));
+		if (val & MEMERR_CPU_ICFESR_CERR_MASK)
+			dev_err(edac_dev->dev,
+				"One or more correctable error\n");
+		if (val & MEMERR_CPU_ICFESR_MULTCERR_MASK)
+			dev_err(edac_dev->dev, "Multiple correctable error\n");
+		switch (MEMERR_CPU_ICFESR_ERRTYPE_RD(val)) {
+		case 1:
+			dev_err(edac_dev->dev, "L1 TLB multiple hit\n");
+			break;
+		case 2:
+			dev_err(edac_dev->dev, "Way select multiple hit\n");
+			break;
+		case 3:
+			dev_err(edac_dev->dev, "Physical tag parity error\n");
+			break;
+		case 4:
+		case 5:
+			dev_err(edac_dev->dev, "L1 data parity error\n");
+			break;
+		case 6:
+			dev_err(edac_dev->dev, "L1 pre-decode parity error\n");
+			break;
+		}
+
+		/* Clear any HW errors */
+		writel(val, pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET);
+
+		if (val & (MEMERR_CPU_ICFESR_CERR_MASK |
+			   MEMERR_CPU_ICFESR_MULTCERR_MASK))
+			edac_device_handle_ce(edac_dev, 0, 0,
+					      edac_dev->ctl_name);
+	}
+
+	val = readl(pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET);
+	if (val) {
+		dev_err(edac_dev->dev,
+			"CPU%d memory error LSU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n",
+			ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
+			MEMERR_CPU_LSUESR_ERRWAY_RD(val),
+			MEMERR_CPU_LSUESR_ERRINDEX_RD(val),
+			MEMERR_CPU_LSUESR_ERRINFO_RD(val));
+		if (val & MEMERR_CPU_LSUESR_CERR_MASK)
+			dev_err(edac_dev->dev,
+				"One or more correctable error\n");
+		if (val & MEMERR_CPU_LSUESR_MULTCERR_MASK)
+			dev_err(edac_dev->dev, "Multiple correctable error\n");
+		switch (MEMERR_CPU_LSUESR_ERRTYPE_RD(val)) {
+		case 0:
+			dev_err(edac_dev->dev, "Load tag error\n");
+			break;
+		case 1:
+			dev_err(edac_dev->dev, "Load data error\n");
+			break;
+		case 2:
+			dev_err(edac_dev->dev, "WSL multihit error\n");
+			break;
+		case 3:
+			dev_err(edac_dev->dev, "Store tag error\n");
+			break;
+		case 4:
+			dev_err(edac_dev->dev,
+				"DTB multihit from load pipeline error\n");
+			break;
+		case 5:
+			dev_err(edac_dev->dev,
+				"DTB multihit from store pipeline error\n");
+			break;
+		}
+
+		/* Clear any HW errors */
+		writel(val, pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET);
+
+		if (val & (MEMERR_CPU_LSUESR_CERR_MASK |
+			   MEMERR_CPU_LSUESR_MULTCERR_MASK))
+			edac_device_handle_ce(edac_dev, 0, 0,
+					      edac_dev->ctl_name);
+	}
+
+	val = readl(pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET);
+	if (val) {
+		dev_err(edac_dev->dev,
+			"CPU%d memory error MMU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X %s\n",
+			ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
+			MEMERR_CPU_MMUESR_ERRWAY_RD(val),
+			MEMERR_CPU_MMUESR_ERRINDEX_RD(val),
+			MEMERR_CPU_MMUESR_ERRINFO_RD(val),
+			val & MEMERR_CPU_MMUESR_ERRREQSTR_LSU_MASK ? "LSU" :
+								     "ICF");
+		if (val & MEMERR_CPU_MMUESR_CERR_MASK)
+			dev_err(edac_dev->dev,
+				"One or more correctable error\n");
+		if (val & MEMERR_CPU_MMUESR_MULTCERR_MASK)
+			dev_err(edac_dev->dev, "Multiple correctable error\n");
+		switch (MEMERR_CPU_MMUESR_ERRTYPE_RD(val)) {
+		case 0:
+			dev_err(edac_dev->dev, "Stage 1 UTB hit error\n");
+			break;
+		case 1:
+			dev_err(edac_dev->dev, "Stage 1 UTB miss error\n");
+			break;
+		case 2:
+			dev_err(edac_dev->dev, "Stage 1 UTB allocate error\n");
+			break;
+		case 3:
+			dev_err(edac_dev->dev,
+				"TMO operation single bank error\n");
+			break;
+		case 4:
+			dev_err(edac_dev->dev, "Stage 2 UTB error\n");
+			break;
+		case 5:
+			dev_err(edac_dev->dev, "Stage 2 UTB miss error\n");
+			break;
+		case 6:
+			dev_err(edac_dev->dev, "Stage 2 UTB allocate error\n");
+			break;
+		case 7:
+			dev_err(edac_dev->dev,
+				"TMO operation multiple bank error\n");
+			break;
+		}
+
+		/* Clear any HW errors */
+		writel(val, pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET);
+
+		edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
+	}
+}
+
+static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	void __iomem *pg_d;
+	void __iomem *pg_e;
+	u32 val_hi;
+	u32 val_lo;
+	u32 val;
+
+	/* Check L2 */
+	pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE;
+	val = readl(pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET);
+	if (val) {
+		val_lo = readl(pg_e + MEMERR_L2C_L2EALR_PAGE_OFFSET);
+		val_hi = readl(pg_e + MEMERR_L2C_L2EAHR_PAGE_OFFSET);
+		dev_err(edac_dev->dev,
+			"PMD%d memory error L2C L2ESR 0x%08X @ 0x%08X.%08X\n",
+			ctx->pmd, val, val_hi, val_lo);
+		dev_err(edac_dev->dev,
+			"ErrSyndrome 0x%02X ErrWay 0x%02X ErrCpu %d ErrGroup 0x%02X ErrAction 0x%02X\n",
+			MEMERR_L2C_L2ESR_ERRSYN_RD(val),
+			MEMERR_L2C_L2ESR_ERRWAY_RD(val),
+			MEMERR_L2C_L2ESR_ERRCPU_RD(val),
+			MEMERR_L2C_L2ESR_ERRGROUP_RD(val),
+			MEMERR_L2C_L2ESR_ERRACTION_RD(val));
+
+		if (val & MEMERR_L2C_L2ESR_ERR_MASK)
+			dev_err(edac_dev->dev,
+				"One or more correctable error\n");
+		if (val & MEMERR_L2C_L2ESR_MULTICERR_MASK)
+			dev_err(edac_dev->dev, "Multiple correctable error\n");
+		if (val & MEMERR_L2C_L2ESR_UCERR_MASK)
+			dev_err(edac_dev->dev,
+				"One or more uncorrectable error\n");
+		if (val & MEMERR_L2C_L2ESR_MULTUCERR_MASK)
+			dev_err(edac_dev->dev,
+				"Multiple uncorrectable error\n");
+
+		switch (MEMERR_L2C_L2ESR_ERRTYPE_RD(val)) {
+		case 0:
+			dev_err(edac_dev->dev, "Outbound SDB parity error\n");
+			break;
+		case 1:
+			dev_err(edac_dev->dev, "Inbound SDB parity error\n");
+			break;
+		case 2:
+			dev_err(edac_dev->dev, "Tag ECC error\n");
+			break;
+		case 3:
+			dev_err(edac_dev->dev, "Data ECC error\n");
+			break;
+		}
+
+		/* Clear any HW errors */
+		writel(val, pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET);
+
+		if (val & (MEMERR_L2C_L2ESR_ERR_MASK |
+			   MEMERR_L2C_L2ESR_MULTICERR_MASK))
+			edac_device_handle_ce(edac_dev, 0, 0,
+					      edac_dev->ctl_name);
+		if (val & (MEMERR_L2C_L2ESR_UCERR_MASK |
+			   MEMERR_L2C_L2ESR_MULTUCERR_MASK))
+			edac_device_handle_ue(edac_dev, 0, 0,
+					      edac_dev->ctl_name);
+	}
+
+	/* Check if any memory request timed out on L2 cache */
+	pg_d = ctx->pmd_csr + CPU_L2C_PAGE;
+	val = readl(pg_d + CPUX_L2C_L2RTOSR_PAGE_OFFSET);
+	if (val) {
+		val_lo = readl(pg_d + CPUX_L2C_L2RTOALR_PAGE_OFFSET);
+		val_hi = readl(pg_d + CPUX_L2C_L2RTOAHR_PAGE_OFFSET);
+		dev_err(edac_dev->dev,
+			"PMD%d L2C error L2C RTOSR 0x%08X @ 0x%08X.%08X\n",
+			ctx->pmd, val, val_hi, val_lo);
+		writel(val, pg_d + CPUX_L2C_L2RTOSR_PAGE_OFFSET);
+	}
+}
+
+static void xgene_edac_pmd_check(struct edac_device_ctl_info *edac_dev)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	unsigned int pcp_hp_stat;
+	int i;
+
+	if (xgene_edac_pcp_rd(ctx->edac, PCPHPERRINTSTS, &pcp_hp_stat))
+		return;
+	if (!((PMD0_MERR_MASK << ctx->pmd) & pcp_hp_stat))
+		return;
+
+	/* Check CPU L1 error */
+	for (i = 0; i < MAX_CPU_PER_PMD; i++)
+		xgene_edac_pmd_l1_check(edac_dev, i);
+
+	/* Check CPU L2 error */
+	xgene_edac_pmd_l2_check(edac_dev);
+}
+
+static void xgene_edac_pmd_cpu_hw_cfg(struct edac_device_ctl_info *edac_dev,
+				      int cpu)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	void __iomem *pg_f = ctx->pmd_csr + cpu * CPU_CSR_STRIDE +
+			     CPU_MEMERR_CPU_PAGE;
+
+	/*
+	 * Enable CPU memory error:
+	 *  MEMERR_CPU_ICFESRA, MEMERR_CPU_LSUESRA, and MEMERR_CPU_MMUESRA
+	 */
+	writel(0x00000301, pg_f + MEMERR_CPU_ICFECR_PAGE_OFFSET);
+	writel(0x00000301, pg_f + MEMERR_CPU_LSUECR_PAGE_OFFSET);
+	writel(0x00000101, pg_f + MEMERR_CPU_MMUECR_PAGE_OFFSET);
+}
+
+static bool xgene_edac_pmd_l2c_version1(void)
+{
+	/* Check all chips with PMD L2C version 1 HW */
+	#define REVIDR_MINOR_REV(revidr)	((revidr) & 0x00000007)
+
+	switch (MIDR_VARIANT(read_cpuid_id())) {
+	case 0:
+		switch (MIDR_REVISION(read_cpuid_id())) {
+		case 0:
+
+			switch (REVIDR_MINOR_REV(read_cpuid(REVIDR_EL1))) {
+			case 1:
+			case 2:
+				return true;
+			};
+			break;
+		case 1:
+			if (REVIDR_MINOR_REV(read_cpuid(REVIDR_EL1)) == 1)
+				return true;
+			break;
+		}
+		break;
+	case 1:
+		switch (MIDR_REVISION(read_cpuid_id())) {
+		case 0:
+			switch (REVIDR_MINOR_REV(read_cpuid(REVIDR_EL1))) {
+			case 1:
+				return true;
+			};
+			break;
+		case 1:
+			switch (REVIDR_MINOR_REV(read_cpuid(REVIDR_EL1))) {
+			case 1:
+			case 0:
+				return true;
+			};
+			break;
+		}
+		break;
+	}
+
+	return false;
+}
+
+static void xgene_edac_pmd_hw_cfg(struct edac_device_ctl_info *edac_dev)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	void __iomem *pg_d = ctx->pmd_csr + CPU_L2C_PAGE;
+	void __iomem *pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE;
+
+	/* Enable PMD memory error - MEMERR_L2C_L2ECR and L2C_L2RTOCR */
+	writel(0x00000703, pg_e + MEMERR_L2C_L2ECR_PAGE_OFFSET);
+	/* Configure L2C HW request time out feature if supported */
+	if (!xgene_edac_pmd_l2c_version1())
+		writel(0x00000119, pg_d + CPUX_L2C_L2RTOCR_PAGE_OFFSET);
+}
+
+static void xgene_edac_pmd_hw_ctl(struct edac_device_ctl_info *edac_dev,
+				  bool enable)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	int i;
+
+	/* Enable PMD error interrupt */
+	if (edac_dev->op_state == OP_RUNNING_INTERRUPT) {
+		if (enable)
+			xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK,
+					       PMD0_MERR_MASK << ctx->pmd);
+		else
+			xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK,
+					       PMD0_MERR_MASK << ctx->pmd);
+	}
+
+	if (enable) {
+		xgene_edac_pmd_hw_cfg(edac_dev);
+
+		/* Two CPUs per a PMD */
+		for (i = 0; i < MAX_CPU_PER_PMD; i++)
+			xgene_edac_pmd_cpu_hw_cfg(edac_dev, i);
+	}
+}
+
+#ifdef CONFIG_EDAC_DEBUG
+static ssize_t xgene_edac_pmd_l1_inject_ctrl_write(struct file *file,
+						   const char __user *data,
+						   size_t count, loff_t *ppos)
+{
+	struct edac_device_ctl_info *edac_dev = file->private_data;
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	void __iomem *cpux_pg_f;
+	int i;
+
+	for (i = 0; i < MAX_CPU_PER_PMD; i++) {
+		cpux_pg_f = ctx->pmd_csr + i * CPU_CSR_STRIDE +
+			    CPU_MEMERR_CPU_PAGE;
+
+		writel(MEMERR_CPU_ICFESR_MULTCERR_MASK |
+		       MEMERR_CPU_ICFESR_CERR_MASK,
+		       cpux_pg_f + MEMERR_CPU_ICFESRA_PAGE_OFFSET);
+		writel(MEMERR_CPU_LSUESR_MULTCERR_MASK |
+		       MEMERR_CPU_LSUESR_CERR_MASK,
+		       cpux_pg_f + MEMERR_CPU_LSUESRA_PAGE_OFFSET);
+		writel(MEMERR_CPU_MMUESR_MULTCERR_MASK |
+		       MEMERR_CPU_MMUESR_CERR_MASK,
+		       cpux_pg_f + MEMERR_CPU_MMUESRA_PAGE_OFFSET);
+	}
+	return count;
+}
+
+static ssize_t xgene_edac_pmd_l2_inject_ctrl_write(struct file *file,
+						   const char __user *data,
+						   size_t count, loff_t *ppos)
+{
+	struct edac_device_ctl_info *edac_dev = file->private_data;
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	void __iomem *pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE;
+
+	writel(MEMERR_L2C_L2ESR_MULTUCERR_MASK |
+	       MEMERR_L2C_L2ESR_MULTICERR_MASK |
+	       MEMERR_L2C_L2ESR_UCERR_MASK |
+	       MEMERR_L2C_L2ESR_ERR_MASK,
+	       pg_e + MEMERR_L2C_L2ESRA_PAGE_OFFSET);
+	return count;
+}
+
+static const struct file_operations xgene_edac_pmd_debug_inject_fops[] = {
+	{
+	.open = simple_open,
+	.write = xgene_edac_pmd_l1_inject_ctrl_write,
+	.llseek = generic_file_llseek, },
+	{
+	.open = simple_open,
+	.write = xgene_edac_pmd_l2_inject_ctrl_write,
+	.llseek = generic_file_llseek, },
+	{ }
+};
+
+static void xgene_edac_pmd_create_debugfs_nodes(
+	struct edac_device_ctl_info *edac_dev)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	struct dentry *edac_debugfs;
+	char name[30];
+
+	/*
+	 * Todo: Switch to common EDAC debug file system for edac device
+	 *       when available.
+	 */
+	if (!ctx->edac->dfs) {
+		ctx->edac->dfs = debugfs_create_dir(edac_dev->dev->kobj.name,
+						    NULL);
+		if (!ctx->edac->dfs)
+			return;
+	}
+	sprintf(name, "PMD%d", ctx->pmd);
+	edac_debugfs = debugfs_create_dir(name, ctx->edac->dfs);
+	if (!edac_debugfs)
+		return;
+
+	debugfs_create_file("l1_inject_ctrl", S_IWUSR, edac_debugfs, edac_dev,
+			    &xgene_edac_pmd_debug_inject_fops[0]);
+	debugfs_create_file("l2_inject_ctrl", S_IWUSR, edac_debugfs, edac_dev,
+			    &xgene_edac_pmd_debug_inject_fops[1]);
+}
+#else
+static void xgene_edac_pmd_create_debugfs_nodes(
+	struct edac_device_ctl_info *edac_dev)
+{
+}
+#endif
+
+static int xgene_edac_pmd_available(u32 efuse, int pmd)
+{
+	return (efuse & (1 << pmd)) ? 0 : 1;
+}
+
+static int xgene_edac_pmd_add(struct xgene_edac *edac, struct device_node *np)
+{
+	struct edac_device_ctl_info *edac_dev;
+	struct xgene_edac_pmd_ctx *ctx;
+	struct resource res;
+	char edac_name[10];
+	int pmd;
+	int rc;
+	u32 val;
+
+	if (!devres_open_group(edac->dev, xgene_edac_pmd_add, GFP_KERNEL))
+		return -ENOMEM;
+
+	/* Find the PMD number from its address */
+	rc = of_address_to_resource(np, 0, &res);
+	if (rc < 0) {
+		dev_err(edac->dev, "no PMD resource address\n");
+		goto err_group;
+	}
+	pmd = ((res.start >> 20) & 0x1E) >> 1;
+
+	/* Determine if this PMD is disabled */
+	rc = xgene_edac_efuse_rd(edac, 0, &val);
+	if (rc)
+		goto err_group;
+	if (!xgene_edac_pmd_available(val, pmd)) {
+		rc = -ENODEV;
+		goto err_group;
+	}
+
+	sprintf(edac_name, "l2c%d", pmd);
+	edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx),
+					      edac_name, 1, "l2c", 1, 2, NULL,
+					      0, edac_device_alloc_index());
+	if (!edac_dev) {
+		rc = -ENOMEM;
+		goto err_group;
+	}
+
+	ctx = edac_dev->pvt_info;
+	ctx->name = "xgene_pmd_err";
+	ctx->pmd = pmd;
+	ctx->edac = edac;
+	ctx->edac_dev = edac_dev;
+	ctx->ddev = *edac->dev;
+	edac_dev->dev = &ctx->ddev;
+	edac_dev->ctl_name = ctx->name;
+	edac_dev->dev_name = ctx->name;
+	edac_dev->mod_name = EDAC_MOD_STR;
+
+	ctx->pmd_csr = devm_ioremap_resource(edac->dev, &res);
+	if (IS_ERR(ctx->pmd_csr)) {
+		dev_err(edac->dev,
+			"devm_ioremap_resource failed for PMD resource address\n");
+		rc = PTR_ERR(ctx->pmd_csr);
+		goto err_free;
+	}
+
+	if (edac_op_state == EDAC_OPSTATE_POLL)
+		edac_dev->edac_check = xgene_edac_pmd_check;
+
+	xgene_edac_pmd_create_debugfs_nodes(edac_dev);
+
+	rc = edac_device_add_device(edac_dev);
+	if (rc > 0) {
+		dev_err(edac->dev, "edac_device_add_device failed\n");
+		rc = -ENOMEM;
+		goto err_free;
+	}
+
+	if (edac_op_state == EDAC_OPSTATE_INT)
+		edac_dev->op_state = OP_RUNNING_INTERRUPT;
+
+	list_add(&ctx->next, &edac->pmds);
+
+	xgene_edac_pmd_hw_ctl(edac_dev, 1);
+
+	devres_remove_group(edac->dev, xgene_edac_pmd_add);
+
+	dev_info(edac->dev, "X-Gene EDAC PMD%d registered\n", ctx->pmd);
+	return 0;
+
+err_free:
+	edac_device_free_ctl_info(edac_dev);
+err_group:
+	devres_release_group(edac->dev, xgene_edac_pmd_add);
+	return rc;
+}
+
+static int xgene_edac_pmd_remove(struct xgene_edac_pmd_ctx *pmd)
+{
+	struct edac_device_ctl_info *edac_dev = pmd->edac_dev;
+
+	xgene_edac_pmd_hw_ctl(edac_dev, 0);
+	edac_device_del_device(edac_dev->dev);
+	edac_device_free_ctl_info(edac_dev);
+	return 0;
+}
+
+static int xgene_edac_pcp_rd(struct xgene_edac *edac, u32 reg, u32 *val)
+{
+	if (edac == NULL)
+		return -EINVAL;
+
+	*val = readl(edac->pcp_csr + reg);
+	return 0;
+}
+
+static int xgene_edac_pcp_clrbits(struct xgene_edac *edac, u32 reg,
+				  u32 bits_mask)
+{
+	u32 val;
+
+	if (edac == NULL)
+		return -EINVAL;
+
+	spin_lock(&edac->lock);
+	val = readl(edac->pcp_csr + reg);
+	val &= ~bits_mask;
+	writel(val, edac->pcp_csr + reg);
+	spin_unlock(&edac->lock);
+	return 0;
+}
+
+static int xgene_edac_csw_rd(struct xgene_edac *edac, u32 reg, u32 *val)
+{
+	if (edac == NULL)
+		return -EINVAL;
+
+	return regmap_read(edac->csw_map, reg, val);
+}
+
+static int xgene_edac_mcba_rd(struct xgene_edac *edac, u32 reg, u32 *val)
+{
+	if (edac == NULL)
+		return -EINVAL;
+
+	return regmap_read(edac->mcba_map, reg, val);
+}
+
+static int xgene_edac_mcbb_rd(struct xgene_edac *edac, u32 reg, u32 *val)
+{
+	if (edac == NULL)
+		return -EINVAL;
+
+	return regmap_read(edac->mcbb_map, reg, val);
+}
+
+static int xgene_edac_efuse_rd(struct xgene_edac *edac, u32 reg, u32 *val)
+{
+	if (edac == NULL)
+		return -EINVAL;
+
+	return regmap_read(edac->efuse_map, reg, val);
+}
+
+static int xgene_edac_pcp_setbits(struct xgene_edac *edac, u32 reg,
+				  u32 bits_mask)
+{
+	u32 val;
+
+	if (edac == NULL)
+		return -EINVAL;
+
+	spin_lock(&edac->lock);
+	val = readl(edac->pcp_csr + reg);
+	val |= bits_mask;
+	writel(val, edac->pcp_csr + reg);
+	spin_unlock(&edac->lock);
+	return 0;
+}
+
+static irqreturn_t xgene_edac_isr(int irq, void *dev_id)
+{
+	struct xgene_edac *ctx = dev_id;
+	struct xgene_edac_pmd_ctx *pmd;
+	unsigned int pcp_hp_stat;
+	unsigned int pcp_lp_stat;
+
+	if (xgene_edac_pcp_rd(ctx, PCPHPERRINTSTS, &pcp_hp_stat))
+		return IRQ_NONE;
+	if (xgene_edac_pcp_rd(ctx, PCPLPERRINTSTS, &pcp_lp_stat))
+		return IRQ_NONE;
+	if ((MCU_UNCORR_ERR_MASK & pcp_hp_stat) ||
+	    (MCU_CTL_ERR_MASK & pcp_hp_stat) ||
+	    (MCU_CORR_ERR_MASK & pcp_lp_stat)) {
+		struct xgene_edac_mc_ctx *mcu;
+
+		list_for_each_entry(mcu, &ctx->mcus, next) {
+			xgene_edac_mc_check(mcu->mci);
+		}
+	}
+
+	list_for_each_entry(pmd, &ctx->pmds, next) {
+		if ((PMD0_MERR_MASK << pmd->pmd) & pcp_hp_stat)
+			xgene_edac_pmd_check(pmd->edac_dev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int xgene_edac_probe(struct platform_device *pdev)
+{
+	struct xgene_edac *edac;
+	struct device_node *child;
+	struct resource *res;
+	int rc;
+
+	edac = devm_kzalloc(&pdev->dev, sizeof(*edac), GFP_KERNEL);
+	if (!edac)
+		return -ENOMEM;
+
+	edac->dev = &pdev->dev;
+	platform_set_drvdata(pdev, edac);
+	INIT_LIST_HEAD(&edac->mcus);
+	INIT_LIST_HEAD(&edac->pmds);
+	spin_lock_init(&edac->lock);
+
+	edac->csw_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+							"regmap-csw");
+	if (IS_ERR(edac->csw_map)) {
+		dev_err(edac->dev, "unable to get syscon regmap csw\n");
+		rc = PTR_ERR(edac->csw_map);
+		goto out_err;
+	}
+
+	edac->mcba_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+							 "regmap-mcba");
+	if (IS_ERR(edac->mcba_map)) {
+		dev_err(edac->dev, "unable to get syscon regmap mcba\n");
+		rc = PTR_ERR(edac->mcba_map);
+		goto out_err;
+	}
+
+	edac->mcbb_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+							 "regmap-mcbb");
+	if (IS_ERR(edac->mcbb_map)) {
+		dev_err(edac->dev, "unable to get syscon regmap mcbb\n");
+		rc = PTR_ERR(edac->mcbb_map);
+		goto out_err;
+	}
+	edac->efuse_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+							  "regmap-efuse");
+	if (IS_ERR(edac->efuse_map)) {
+		dev_err(edac->dev, "unable to get syscon regmap efuse\n");
+		rc = PTR_ERR(edac->efuse_map);
+		goto out_err;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	edac->pcp_csr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(edac->pcp_csr)) {
+		dev_err(&pdev->dev, "no PCP resource address\n");
+		rc = PTR_ERR(edac->pcp_csr);
+		goto out_err;
+	}
+
+	if (edac_op_state == EDAC_OPSTATE_INT) {
+		int irq;
+		int i;
+
+		for (i = 0; i < 3; i++) {
+			irq = platform_get_irq(pdev, i);
+			if (irq < 0) {
+				dev_err(&pdev->dev, "No IRQ resource\n");
+				rc = -EINVAL;
+				goto out_err;
+			}
+			rc = devm_request_irq(&pdev->dev, irq,
+					      xgene_edac_isr, IRQF_SHARED,
+					      dev_name(&pdev->dev), edac);
+			if (rc) {
+				dev_err(&pdev->dev,
+					"Could not request IRQ %d\n", irq);
+				goto out_err;
+			}
+		}
+	}
+
+	for_each_child_of_node(pdev->dev.of_node, child) {
+		if (!of_device_is_available(child))
+			continue;
+		if (of_device_is_compatible(child, "apm,xgene-edac-mc"))
+			xgene_edac_mc_add(edac, child);
+		if (of_device_is_compatible(child, "apm,xgene-edac-pmd"))
+			xgene_edac_pmd_add(edac, child);
+	}
+
+	return 0;
+
+out_err:
+	return rc;
+}
+
+static int xgene_edac_remove(struct platform_device *pdev)
+{
+	struct xgene_edac *edac = dev_get_drvdata(&pdev->dev);
+	struct xgene_edac_mc_ctx *mcu;
+	struct xgene_edac_mc_ctx *temp_mcu;
+	struct xgene_edac_pmd_ctx *pmd;
+	struct xgene_edac_pmd_ctx *temp_pmd;
+
+	list_for_each_entry_safe(mcu, temp_mcu, &edac->mcus, next) {
+		xgene_edac_mc_remove(mcu);
+	}
+
+	list_for_each_entry_safe(pmd, temp_pmd, &edac->pmds, next) {
+		xgene_edac_pmd_remove(pmd);
+	}
+	return 0;
+}
+
+static const struct of_device_id xgene_edac_of_match[] = {
+	{ .compatible = "apm,xgene-edac" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, xgene_edac_of_match);
+
+static struct platform_driver xgene_edac_driver = {
+	.probe = xgene_edac_probe,
+	.remove = xgene_edac_remove,
+	.driver = {
+		.name = "xgene-edac",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_edac_of_match,
+	},
+};
+
+static int __init xgene_edac_init(void)
+{
+	int rc;
+
+	/* Make sure error reporting method is sane */
+	switch (edac_op_state) {
+	case EDAC_OPSTATE_POLL:
+	case EDAC_OPSTATE_INT:
+		break;
+	default:
+		edac_op_state = EDAC_OPSTATE_INT;
+		break;
+	}
+
+	rc = platform_driver_register(&xgene_edac_driver);
+	if (rc) {
+		edac_printk(KERN_ERR, EDAC_MOD_STR,
+			    "EDAC fails to register\n");
+		goto reg_failed;
+	}
+
+	return 0;
+
+reg_failed:
+	return rc;
+}
+module_init(xgene_edac_init);
+
+static void __exit xgene_edac_exit(void)
+{
+	platform_driver_unregister(&xgene_edac_driver);
+}
+module_exit(xgene_edac_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Feng Kan <fkan-qTEPVZfXA3Y@public.gmane.org>");
+MODULE_DESCRIPTION("APM X-Gene EDAC driver");
+module_param(edac_op_state, int, 0444);
+MODULE_PARM_DESC(edac_op_state,
+		 "EDAC error reporting state: 0=Poll, 2=Interrupt");
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
@ 2015-05-18 23:24                 ` Loc Ho
  0 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-18 23:24 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds support for the APM X-Gene SoC EDAC driver.

Signed-off-by: Loc Ho <lho@apm.com>
---
 drivers/edac/Kconfig      |    7 +
 drivers/edac/Makefile     |    1 +
 drivers/edac/xgene_edac.c | 1313 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1321 insertions(+), 0 deletions(-)
 create mode 100644 drivers/edac/xgene_edac.c

diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index cb59619..ffa7cac 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -392,4 +392,11 @@ config EDAC_SYNOPSYS
 	  Support for error detection and correction on the Synopsys DDR
 	  memory controller.
 
+config EDAC_XGENE
+	tristate "APM X-Gene SoC"
+	depends on EDAC_MM_EDAC && (ARM64 || COMPILE_TEST)
+	help
+	  Support for error detection and correction on the
+	  APM X-Gene family of SOCs.
+
 endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index b255f36..28ef2a5 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -68,3 +68,4 @@ obj-$(CONFIG_EDAC_OCTEON_PCI)		+= octeon_edac-pci.o
 
 obj-$(CONFIG_EDAC_ALTERA_MC)		+= altera_edac.o
 obj-$(CONFIG_EDAC_SYNOPSYS)		+= synopsys_edac.o
+obj-$(CONFIG_EDAC_XGENE)		+= xgene_edac.o
diff --git a/drivers/edac/xgene_edac.c b/drivers/edac/xgene_edac.c
new file mode 100644
index 0000000..8155b86
--- /dev/null
+++ b/drivers/edac/xgene_edac.c
@@ -0,0 +1,1313 @@
+/*
+ * APM X-Gene SoC EDAC (error detection and correction)
+ *
+ * Copyright (c) 2015, Applied Micro Circuits Corporation
+ * Author: Feng Kan <fkan@apm.com>
+ *         Loc Ho <lho@apm.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/ctype.h>
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+
+#include "edac_core.h"
+
+#define EDAC_MOD_STR			"xgene_edac"
+
+/* Global error configuration status registers (CSR) */
+#define PCPHPERRINTSTS			0x0000
+#define PCPHPERRINTMSK			0x0004
+#define  MCU_CTL_ERR_MASK		BIT(12)
+#define  IOB_PA_ERR_MASK		BIT(11)
+#define  IOB_BA_ERR_MASK		BIT(10)
+#define  IOB_XGIC_ERR_MASK		BIT(9)
+#define  IOB_RB_ERR_MASK		BIT(8)
+#define  L3C_UNCORR_ERR_MASK		BIT(5)
+#define  MCU_UNCORR_ERR_MASK		BIT(4)
+#define  PMD3_MERR_MASK			BIT(3)
+#define  PMD2_MERR_MASK			BIT(2)
+#define  PMD1_MERR_MASK			BIT(1)
+#define  PMD0_MERR_MASK			BIT(0)
+#define PCPLPERRINTSTS			0x0008
+#define PCPLPERRINTMSK			0x000C
+#define  CSW_SWITCH_TRACE_ERR_MASK	BIT(2)
+#define  L3C_CORR_ERR_MASK		BIT(1)
+#define  MCU_CORR_ERR_MASK		BIT(0)
+#define MEMERRINTSTS			0x0010
+#define MEMERRINTMSK			0x0014
+
+struct xgene_edac {
+	struct device		*dev;
+	struct regmap		*csw_map;
+	struct regmap		*mcba_map;
+	struct regmap		*mcbb_map;
+	struct regmap		*efuse_map;
+	void __iomem		*pcp_csr;
+	spinlock_t		lock;
+	struct dentry		*dfs;
+
+	struct list_head	mcus;
+	struct list_head	pmds;
+};
+
+static int xgene_edac_pcp_rd(struct xgene_edac *edac, u32 reg, u32 *val);
+static int xgene_edac_pcp_clrbits(struct xgene_edac *edac, u32 reg,
+				  u32 bits_mask);
+static int xgene_edac_pcp_setbits(struct xgene_edac *edac, u32 reg,
+				  u32 bits_mask);
+static int xgene_edac_csw_rd(struct xgene_edac *edac, u32 reg, u32 *val);
+static int xgene_edac_mcba_rd(struct xgene_edac *edac, u32 reg, u32 *val);
+static int xgene_edac_mcbb_rd(struct xgene_edac *edac, u32 reg, u32 *val);
+static int xgene_edac_efuse_rd(struct xgene_edac *edac, u32 reg, u32 *val);
+
+static int edac_mc_idx;
+static int edac_mc_active_mask;
+static int edac_mc_registered_mask;
+static DEFINE_MUTEX(xgene_edac_mc_lock);
+
+/* Memory controller error CSR */
+#define MCU_MAX_RANK			8
+#define MCU_RANK_STRIDE			0x40
+
+#define MCUGECR				0x0110
+#define  MCU_GECR_DEMANDUCINTREN_MASK	BIT(0)
+#define  MCU_GECR_BACKUCINTREN_MASK	BIT(1)
+#define  MCU_GECR_CINTREN_MASK		BIT(2)
+#define  MUC_GECR_MCUADDRERREN_MASK	BIT(9)
+#define MCUGESR				0x0114
+#define  MCU_GESR_ADDRNOMATCH_ERR_MASK	BIT(7)
+#define  MCU_GESR_ADDRMULTIMATCH_ERR_MASK	BIT(6)
+#define  MCU_GESR_PHYP_ERR_MASK		BIT(3)
+#define MCUESRR0			0x0314
+#define  MCU_ESRR_MULTUCERR_MASK	BIT(3)
+#define  MCU_ESRR_BACKUCERR_MASK	BIT(2)
+#define  MCU_ESRR_DEMANDUCERR_MASK	BIT(1)
+#define  MCU_ESRR_CERR_MASK		BIT(0)
+#define MCUESRRA0			0x0318
+#define MCUEBLRR0			0x031c
+#define  MCU_EBLRR_ERRBANK_RD(src)	(((src) & 0x00000007) >> 0)
+#define MCUERCRR0			0x0320
+#define  MCU_ERCRR_ERRROW_RD(src)	(((src) & 0xFFFF0000) >> 16)
+#define  MCU_ERCRR_ERRCOL_RD(src)	((src) & 0x00000FFF)
+#define MCUSBECNT0			0x0324
+#define MCU_SBECNT_COUNT(src)		((src) & 0xFFFF)
+
+#define CSW_CSWCR			0x0000
+#define  CSW_CSWCR_DUALMCB_MASK		BIT(0)
+
+#define MCBADDRMR			0x0000
+#define  MCBADDRMR_MCU_INTLV_MODE_MASK	BIT(3)
+#define  MCBADDRMR_DUALMCU_MODE_MASK	BIT(2)
+#define  MCBADDRMR_MCB_INTLV_MODE_MASK	BIT(1)
+#define  MCBADDRMR_ADDRESS_MODE_MASK	BIT(0)
+
+struct xgene_edac_mc_ctx {
+	struct list_head	next;
+	char			*name;
+	struct mem_ctl_info	*mci;
+	struct xgene_edac	*edac;
+	void __iomem		*mcu_csr;
+	int			mcu_id;
+};
+
+#ifdef CONFIG_EDAC_DEBUG
+static ssize_t xgene_edac_mc_err_inject_write(struct file *file,
+					      const char __user *data,
+					      size_t count, loff_t *ppos)
+{
+	struct mem_ctl_info *mci = file->private_data;
+	struct xgene_edac_mc_ctx *ctx = mci->pvt_info;
+	int i;
+
+	for (i = 0; i < MCU_MAX_RANK; i++) {
+		writel(MCU_ESRR_MULTUCERR_MASK | MCU_ESRR_BACKUCERR_MASK |
+		       MCU_ESRR_DEMANDUCERR_MASK | MCU_ESRR_CERR_MASK,
+		       ctx->mcu_csr + MCUESRRA0 + i * MCU_RANK_STRIDE);
+	}
+	return count;
+}
+
+static const struct file_operations xgene_edac_mc_debug_inject_fops = {
+	.open = simple_open,
+	.write = xgene_edac_mc_err_inject_write,
+	.llseek = generic_file_llseek,
+};
+
+static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
+{
+	if (!mci->debugfs)
+		return;
+
+	debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
+			    &xgene_edac_mc_debug_inject_fops);
+}
+#else
+static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
+{
+}
+#endif
+
+static void xgene_edac_mc_check(struct mem_ctl_info *mci)
+{
+	struct xgene_edac_mc_ctx *ctx = mci->pvt_info;
+	unsigned int pcp_hp_stat;
+	unsigned int pcp_lp_stat;
+	u32 reg;
+	u32 rank;
+	u32 bank;
+	u32 count;
+	u32 col_row;
+
+	if (xgene_edac_pcp_rd(ctx->edac, PCPHPERRINTSTS, &pcp_hp_stat))
+		return;
+	if (xgene_edac_pcp_rd(ctx->edac, PCPLPERRINTSTS, &pcp_lp_stat))
+		return;
+	if (!((MCU_UNCORR_ERR_MASK & pcp_hp_stat) ||
+	      (MCU_CTL_ERR_MASK & pcp_hp_stat) ||
+	      (MCU_CORR_ERR_MASK & pcp_lp_stat)))
+		return;
+
+	for (rank = 0; rank < MCU_MAX_RANK; rank++) {
+		reg = readl(ctx->mcu_csr + MCUESRR0 + rank * MCU_RANK_STRIDE);
+
+		/* Detect uncorrectable memory error */
+		if (reg & (MCU_ESRR_DEMANDUCERR_MASK |
+			   MCU_ESRR_BACKUCERR_MASK)) {
+			/* Detected uncorrectable memory error */
+			edac_mc_chipset_printk(mci, KERN_ERR, "X-Gene",
+				"MCU uncorrectable error at rank %d\n", rank);
+
+			edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+				1, 0, 0, 0, 0, 0, -1, mci->ctl_name, "");
+		}
+
+		/* Detect correctable memory error */
+		if (reg & MCU_ESRR_CERR_MASK) {
+			bank = readl(ctx->mcu_csr + MCUEBLRR0 +
+				     rank * MCU_RANK_STRIDE);
+			col_row = readl(ctx->mcu_csr + MCUERCRR0 +
+					rank * MCU_RANK_STRIDE);
+			count = readl(ctx->mcu_csr + MCUSBECNT0 +
+				      rank * MCU_RANK_STRIDE);
+			edac_mc_chipset_printk(mci, KERN_WARNING, "X-Gene",
+				"MCU correctable error at rank %d bank %d column %d row %d count %d\n",
+				rank, MCU_EBLRR_ERRBANK_RD(bank),
+				MCU_ERCRR_ERRCOL_RD(col_row),
+				MCU_ERCRR_ERRROW_RD(col_row),
+				MCU_SBECNT_COUNT(count));
+
+			edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+				1, 0, 0, 0, 0, 0, -1, mci->ctl_name, "");
+		}
+
+		/* Clear all error registers */
+		writel(0x0, ctx->mcu_csr + MCUEBLRR0 + rank * MCU_RANK_STRIDE);
+		writel(0x0, ctx->mcu_csr + MCUERCRR0 + rank * MCU_RANK_STRIDE);
+		writel(0x0, ctx->mcu_csr + MCUSBECNT0 +
+		       rank * MCU_RANK_STRIDE);
+		writel(reg, ctx->mcu_csr + MCUESRR0 + rank * MCU_RANK_STRIDE);
+	}
+
+	/* Detect memory controller error */
+	reg = readl(ctx->mcu_csr + MCUGESR);
+	if (reg) {
+		if (reg & MCU_GESR_ADDRNOMATCH_ERR_MASK)
+			edac_mc_chipset_printk(mci, KERN_WARNING, "X-Gene",
+				"MCU address miss-match error\n");
+		if (reg & MCU_GESR_ADDRMULTIMATCH_ERR_MASK)
+			edac_mc_chipset_printk(mci, KERN_WARNING, "X-Gene",
+				"MCU address multi-match error\n");
+
+		writel(reg, ctx->mcu_csr + MCUGESR);
+	}
+}
+
+static void xgene_edac_mc_irq_ctl(struct mem_ctl_info *mci, bool enable)
+{
+	struct xgene_edac_mc_ctx *ctx = mci->pvt_info;
+	unsigned int val;
+
+	if (edac_op_state != EDAC_OPSTATE_INT)
+		return;
+
+	mutex_lock(&xgene_edac_mc_lock);
+
+	/*
+	 * As there is only single bit for enable error and interrupt mask,
+	 * we must only enable top level interrupt after all MCUs are
+	 * registered. Otherwise, if there is an error and the corresponding
+	 * MCU has not registered, the interrupt will never get cleared. To
+	 * determine all MCU have registered, we will keep track of active
+	 * MCUs and registered MCUs.
+	 */
+	if (enable) {
+		/* Set registered MCU bit */
+		edac_mc_registered_mask |= 1 << ctx->mcu_id;
+
+		/* Enable interrupt after all active MCU registered */
+		if (edac_mc_registered_mask == edac_mc_active_mask) {
+			/* Enable memory controller top level interrupt */
+			xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK,
+					       MCU_UNCORR_ERR_MASK |
+					       MCU_CTL_ERR_MASK);
+			xgene_edac_pcp_clrbits(ctx->edac, PCPLPERRINTMSK,
+					       MCU_CORR_ERR_MASK);
+		}
+
+		/* Enable MCU interrupt and error reporting */
+		val = readl(ctx->mcu_csr + MCUGECR);
+		val |= MCU_GECR_DEMANDUCINTREN_MASK |
+		       MCU_GECR_BACKUCINTREN_MASK |
+		       MCU_GECR_CINTREN_MASK |
+		       MUC_GECR_MCUADDRERREN_MASK;
+		writel(val, ctx->mcu_csr + MCUGECR);
+	} else {
+		/* Disable MCU interrupt */
+		val = readl(ctx->mcu_csr + MCUGECR);
+		val &= ~(MCU_GECR_DEMANDUCINTREN_MASK |
+			 MCU_GECR_BACKUCINTREN_MASK |
+			 MCU_GECR_CINTREN_MASK |
+			 MUC_GECR_MCUADDRERREN_MASK);
+		writel(val, ctx->mcu_csr + MCUGECR);
+
+		/* Disable memory controller top level interrupt */
+		xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK,
+				       MCU_UNCORR_ERR_MASK | MCU_CTL_ERR_MASK);
+		xgene_edac_pcp_setbits(ctx->edac, PCPLPERRINTMSK,
+				       MCU_CORR_ERR_MASK);
+
+		/* Clear registered MCU bit */
+		edac_mc_registered_mask &= ~(1 << ctx->mcu_id);
+	}
+
+	mutex_unlock(&xgene_edac_mc_lock);
+}
+
+static int xgene_edac_mc_is_active(struct xgene_edac_mc_ctx *ctx, int mc_idx)
+{
+	unsigned int reg;
+	u32 mcu_mask;
+
+	if (xgene_edac_csw_rd(ctx->edac, CSW_CSWCR, &reg))
+		return 0;
+
+	if (reg & CSW_CSWCR_DUALMCB_MASK) {
+		/*
+		 * Dual MCB active - Determine if all 4 active or just MCU0
+		 * and MCU2 active
+		 */
+		if (xgene_edac_mcbb_rd(ctx->edac, MCBADDRMR, &reg))
+			return 0;
+		mcu_mask = (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0xF : 0x5;
+	} else {
+		/*
+		 * Single MCB active - Determine if MCU0/MCU1 or just MCU0
+		 * active
+		 */
+		if (xgene_edac_mcba_rd(ctx->edac, MCBADDRMR, &reg))
+			return 0;
+		mcu_mask = (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0x3 : 0x1;
+	}
+
+	/* Save active MC mask if hasn't set already */
+	if (!edac_mc_active_mask)
+		edac_mc_active_mask = mcu_mask;
+
+	return (mcu_mask & (1 << mc_idx)) ? 1 : 0;
+}
+
+static int xgene_edac_mc_add(struct xgene_edac *edac, struct device_node *np)
+{
+	struct mem_ctl_info *mci;
+	struct edac_mc_layer layers[2];
+	struct xgene_edac_mc_ctx tmp_ctx;
+	struct xgene_edac_mc_ctx *ctx;
+	struct resource res;
+	int rc;
+
+	memset(&tmp_ctx, 0, sizeof(tmp_ctx));
+	tmp_ctx.edac = edac;
+
+	if (!devres_open_group(edac->dev, xgene_edac_mc_add, GFP_KERNEL))
+		return -ENOMEM;
+
+	rc = of_address_to_resource(np, 0, &res);
+	if (rc < 0) {
+		dev_err(edac->dev, "no MCU resource address\n");
+		goto err_group;
+	}
+	tmp_ctx.mcu_csr = devm_ioremap_resource(edac->dev, &res);
+	if (IS_ERR(tmp_ctx.mcu_csr)) {
+		dev_err(edac->dev, "unable to map MCU resource\n");
+		rc = PTR_ERR(tmp_ctx.mcu_csr);
+		goto err_group;
+	}
+
+	/* Ignore non-active MCU */
+	tmp_ctx.mcu_id = ((res.start >> 16) & 0xF) / 4;
+	if (!xgene_edac_mc_is_active(&tmp_ctx, tmp_ctx.mcu_id)) {
+		rc = -ENODEV;
+		goto err_group;
+	}
+
+	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+	layers[0].size = 4;
+	layers[0].is_virt_csrow = true;
+	layers[1].type = EDAC_MC_LAYER_CHANNEL;
+	layers[1].size = 2;
+	layers[1].is_virt_csrow = false;
+	mci = edac_mc_alloc(edac_mc_idx++, ARRAY_SIZE(layers), layers,
+			    sizeof(*ctx));
+	if (!mci) {
+		rc = -ENOMEM;
+		goto err_group;
+	}
+
+	ctx = mci->pvt_info;
+	*ctx = tmp_ctx;		/* Copy over resource value */
+	ctx->name = "xgene_edac_mc_err";
+	ctx->mci = mci;
+	mci->pdev = &mci->dev;
+	mci->ctl_name = ctx->name;
+	mci->dev_name = ctx->name;
+
+	mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_RDDR2 | MEM_FLAG_RDDR3 |
+			 MEM_FLAG_DDR | MEM_FLAG_DDR2 | MEM_FLAG_DDR3;
+	mci->edac_ctl_cap = EDAC_FLAG_SECDED;
+	mci->edac_cap = EDAC_FLAG_SECDED;
+	mci->mod_name = EDAC_MOD_STR;
+	mci->mod_ver = "0.1";
+	mci->ctl_page_to_phys = NULL;
+	mci->scrub_cap = SCRUB_FLAG_HW_SRC;
+	mci->scrub_mode = SCRUB_HW_SRC;
+
+	if (edac_op_state == EDAC_OPSTATE_POLL)
+		mci->edac_check = xgene_edac_mc_check;
+
+	if (edac_mc_add_mc(mci)) {
+		dev_err(edac->dev, "edac_mc_add_mc failed\n");
+		rc = -EINVAL;
+		goto err_free;
+	}
+
+	xgene_edac_mc_create_debugfs_node(mci);
+
+	list_add(&ctx->next, &edac->mcus);
+
+	xgene_edac_mc_irq_ctl(mci, true);
+
+	devres_remove_group(edac->dev, xgene_edac_mc_add);
+
+	dev_info(edac->dev, "X-Gene EDAC MC registered\n");
+	return 0;
+
+err_free:
+	edac_mc_free(mci);
+err_group:
+	devres_release_group(edac->dev, xgene_edac_mc_add);
+	return rc;
+}
+
+static int xgene_edac_mc_remove(struct xgene_edac_mc_ctx *mcu)
+{
+	xgene_edac_mc_irq_ctl(mcu->mci, false);
+	edac_mc_del_mc(&mcu->mci->dev);
+	edac_mc_free(mcu->mci);
+	return 0;
+}
+
+/* CPU L1/L2 error CSR */
+#define MAX_CPU_PER_PMD				2
+#define CPU_CSR_STRIDE				0x00100000
+#define CPU_L2C_PAGE				0x000D0000
+#define CPU_MEMERR_L2C_PAGE			0x000E0000
+#define CPU_MEMERR_CPU_PAGE			0x000F0000
+
+#define MEMERR_CPU_ICFECR_PAGE_OFFSET		0x0000
+#define MEMERR_CPU_ICFESR_PAGE_OFFSET		0x0004
+#define  MEMERR_CPU_ICFESR_ERRWAY_RD(src)	(((src) & 0xFF000000) >> 24)
+#define  MEMERR_CPU_ICFESR_ERRINDEX_RD(src)	(((src) & 0x003F0000) >> 16)
+#define  MEMERR_CPU_ICFESR_ERRINFO_RD(src)	(((src) & 0x0000FF00) >> 8)
+#define  MEMERR_CPU_ICFESR_ERRTYPE_RD(src)	(((src) & 0x00000070) >> 4)
+#define  MEMERR_CPU_ICFESR_MULTCERR_MASK	BIT(2)
+#define  MEMERR_CPU_ICFESR_CERR_MASK		BIT(0)
+#define MEMERR_CPU_LSUESR_PAGE_OFFSET		0x000c
+#define  MEMERR_CPU_LSUESR_ERRWAY_RD(src)	(((src) & 0xFF000000) >> 24)
+#define  MEMERR_CPU_LSUESR_ERRINDEX_RD(src)	(((src) & 0x003F0000) >> 16)
+#define  MEMERR_CPU_LSUESR_ERRINFO_RD(src)	(((src) & 0x0000FF00) >> 8)
+#define  MEMERR_CPU_LSUESR_ERRTYPE_RD(src)	(((src) & 0x00000070) >> 4)
+#define  MEMERR_CPU_LSUESR_MULTCERR_MASK	BIT(2)
+#define  MEMERR_CPU_LSUESR_CERR_MASK		BIT(0)
+#define MEMERR_CPU_LSUECR_PAGE_OFFSET		0x0008
+#define MEMERR_CPU_MMUECR_PAGE_OFFSET		0x0010
+#define MEMERR_CPU_MMUESR_PAGE_OFFSET		0x0014
+#define  MEMERR_CPU_MMUESR_ERRWAY_RD(src)	(((src) & 0xFF000000) >> 24)
+#define  MEMERR_CPU_MMUESR_ERRINDEX_RD(src)	(((src) & 0x007F0000) >> 16)
+#define  MEMERR_CPU_MMUESR_ERRINFO_RD(src)	(((src) & 0x0000FF00) >> 8)
+#define  MEMERR_CPU_MMUESR_ERRREQSTR_LSU_MASK	BIT(7)
+#define  MEMERR_CPU_MMUESR_ERRTYPE_RD(src)	(((src) & 0x00000070) >> 4)
+#define  MEMERR_CPU_MMUESR_MULTCERR_MASK	BIT(2)
+#define  MEMERR_CPU_MMUESR_CERR_MASK		BIT(0)
+#define MEMERR_CPU_ICFESRA_PAGE_OFFSET		0x0804
+#define MEMERR_CPU_LSUESRA_PAGE_OFFSET		0x080c
+#define MEMERR_CPU_MMUESRA_PAGE_OFFSET		0x0814
+
+#define MEMERR_L2C_L2ECR_PAGE_OFFSET		0x0000
+#define MEMERR_L2C_L2ESR_PAGE_OFFSET		0x0004
+#define  MEMERR_L2C_L2ESR_ERRSYN_RD(src)	(((src) & 0xFF000000) >> 24)
+#define  MEMERR_L2C_L2ESR_ERRWAY_RD(src)	(((src) & 0x00FC0000) >> 18)
+#define  MEMERR_L2C_L2ESR_ERRCPU_RD(src)	(((src) & 0x00020000) >> 17)
+#define  MEMERR_L2C_L2ESR_ERRGROUP_RD(src)	(((src) & 0x0000E000) >> 13)
+#define  MEMERR_L2C_L2ESR_ERRACTION_RD(src)	(((src) & 0x00001C00) >> 10)
+#define  MEMERR_L2C_L2ESR_ERRTYPE_RD(src)	(((src) & 0x00000300) >> 8)
+#define  MEMERR_L2C_L2ESR_MULTUCERR_MASK	BIT(3)
+#define  MEMERR_L2C_L2ESR_MULTICERR_MASK	BIT(2)
+#define  MEMERR_L2C_L2ESR_UCERR_MASK		BIT(1)
+#define  MEMERR_L2C_L2ESR_ERR_MASK		BIT(0)
+#define MEMERR_L2C_L2EALR_PAGE_OFFSET		0x0008
+#define CPUX_L2C_L2RTOCR_PAGE_OFFSET		0x0010
+#define MEMERR_L2C_L2EAHR_PAGE_OFFSET		0x000c
+#define CPUX_L2C_L2RTOSR_PAGE_OFFSET		0x0014
+#define  MEMERR_L2C_L2RTOSR_MULTERR_MASK	BIT(1)
+#define  MEMERR_L2C_L2RTOSR_ERR_MASK		BIT(0)
+#define CPUX_L2C_L2RTOALR_PAGE_OFFSET		0x0018
+#define CPUX_L2C_L2RTOAHR_PAGE_OFFSET		0x001c
+#define MEMERR_L2C_L2ESRA_PAGE_OFFSET		0x0804
+
+/*
+ * Processor Module Domain (PMD) context - Context for a pair of processsors.
+ * Each PMD consists of 2 CPUs and a shared L2 cache. Each CPU consists of
+ * its own L1 cache.
+ */
+struct xgene_edac_pmd_ctx {
+	struct list_head	next;
+	struct device		ddev;
+	char			*name;
+	struct xgene_edac	*edac;
+	struct edac_device_ctl_info *edac_dev;
+	void __iomem		*pmd_csr;
+	int			pmd;
+};
+
+static void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev,
+				    int cpu_idx)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	void __iomem *pg_f;
+	u32 val;
+
+	pg_f = ctx->pmd_csr + cpu_idx * CPU_CSR_STRIDE + CPU_MEMERR_CPU_PAGE;
+
+	val = readl(pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET);
+	if (val) {
+		dev_err(edac_dev->dev,
+			"CPU%d L1 memory error ICF 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n",
+			ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
+			MEMERR_CPU_ICFESR_ERRWAY_RD(val),
+			MEMERR_CPU_ICFESR_ERRINDEX_RD(val),
+			MEMERR_CPU_ICFESR_ERRINFO_RD(val));
+		if (val & MEMERR_CPU_ICFESR_CERR_MASK)
+			dev_err(edac_dev->dev,
+				"One or more correctable error\n");
+		if (val & MEMERR_CPU_ICFESR_MULTCERR_MASK)
+			dev_err(edac_dev->dev, "Multiple correctable error\n");
+		switch (MEMERR_CPU_ICFESR_ERRTYPE_RD(val)) {
+		case 1:
+			dev_err(edac_dev->dev, "L1 TLB multiple hit\n");
+			break;
+		case 2:
+			dev_err(edac_dev->dev, "Way select multiple hit\n");
+			break;
+		case 3:
+			dev_err(edac_dev->dev, "Physical tag parity error\n");
+			break;
+		case 4:
+		case 5:
+			dev_err(edac_dev->dev, "L1 data parity error\n");
+			break;
+		case 6:
+			dev_err(edac_dev->dev, "L1 pre-decode parity error\n");
+			break;
+		}
+
+		/* Clear any HW errors */
+		writel(val, pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET);
+
+		if (val & (MEMERR_CPU_ICFESR_CERR_MASK |
+			   MEMERR_CPU_ICFESR_MULTCERR_MASK))
+			edac_device_handle_ce(edac_dev, 0, 0,
+					      edac_dev->ctl_name);
+	}
+
+	val = readl(pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET);
+	if (val) {
+		dev_err(edac_dev->dev,
+			"CPU%d memory error LSU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n",
+			ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
+			MEMERR_CPU_LSUESR_ERRWAY_RD(val),
+			MEMERR_CPU_LSUESR_ERRINDEX_RD(val),
+			MEMERR_CPU_LSUESR_ERRINFO_RD(val));
+		if (val & MEMERR_CPU_LSUESR_CERR_MASK)
+			dev_err(edac_dev->dev,
+				"One or more correctable error\n");
+		if (val & MEMERR_CPU_LSUESR_MULTCERR_MASK)
+			dev_err(edac_dev->dev, "Multiple correctable error\n");
+		switch (MEMERR_CPU_LSUESR_ERRTYPE_RD(val)) {
+		case 0:
+			dev_err(edac_dev->dev, "Load tag error\n");
+			break;
+		case 1:
+			dev_err(edac_dev->dev, "Load data error\n");
+			break;
+		case 2:
+			dev_err(edac_dev->dev, "WSL multihit error\n");
+			break;
+		case 3:
+			dev_err(edac_dev->dev, "Store tag error\n");
+			break;
+		case 4:
+			dev_err(edac_dev->dev,
+				"DTB multihit from load pipeline error\n");
+			break;
+		case 5:
+			dev_err(edac_dev->dev,
+				"DTB multihit from store pipeline error\n");
+			break;
+		}
+
+		/* Clear any HW errors */
+		writel(val, pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET);
+
+		if (val & (MEMERR_CPU_LSUESR_CERR_MASK |
+			   MEMERR_CPU_LSUESR_MULTCERR_MASK))
+			edac_device_handle_ce(edac_dev, 0, 0,
+					      edac_dev->ctl_name);
+	}
+
+	val = readl(pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET);
+	if (val) {
+		dev_err(edac_dev->dev,
+			"CPU%d memory error MMU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X %s\n",
+			ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
+			MEMERR_CPU_MMUESR_ERRWAY_RD(val),
+			MEMERR_CPU_MMUESR_ERRINDEX_RD(val),
+			MEMERR_CPU_MMUESR_ERRINFO_RD(val),
+			val & MEMERR_CPU_MMUESR_ERRREQSTR_LSU_MASK ? "LSU" :
+								     "ICF");
+		if (val & MEMERR_CPU_MMUESR_CERR_MASK)
+			dev_err(edac_dev->dev,
+				"One or more correctable error\n");
+		if (val & MEMERR_CPU_MMUESR_MULTCERR_MASK)
+			dev_err(edac_dev->dev, "Multiple correctable error\n");
+		switch (MEMERR_CPU_MMUESR_ERRTYPE_RD(val)) {
+		case 0:
+			dev_err(edac_dev->dev, "Stage 1 UTB hit error\n");
+			break;
+		case 1:
+			dev_err(edac_dev->dev, "Stage 1 UTB miss error\n");
+			break;
+		case 2:
+			dev_err(edac_dev->dev, "Stage 1 UTB allocate error\n");
+			break;
+		case 3:
+			dev_err(edac_dev->dev,
+				"TMO operation single bank error\n");
+			break;
+		case 4:
+			dev_err(edac_dev->dev, "Stage 2 UTB error\n");
+			break;
+		case 5:
+			dev_err(edac_dev->dev, "Stage 2 UTB miss error\n");
+			break;
+		case 6:
+			dev_err(edac_dev->dev, "Stage 2 UTB allocate error\n");
+			break;
+		case 7:
+			dev_err(edac_dev->dev,
+				"TMO operation multiple bank error\n");
+			break;
+		}
+
+		/* Clear any HW errors */
+		writel(val, pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET);
+
+		edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
+	}
+}
+
+static void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	void __iomem *pg_d;
+	void __iomem *pg_e;
+	u32 val_hi;
+	u32 val_lo;
+	u32 val;
+
+	/* Check L2 */
+	pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE;
+	val = readl(pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET);
+	if (val) {
+		val_lo = readl(pg_e + MEMERR_L2C_L2EALR_PAGE_OFFSET);
+		val_hi = readl(pg_e + MEMERR_L2C_L2EAHR_PAGE_OFFSET);
+		dev_err(edac_dev->dev,
+			"PMD%d memory error L2C L2ESR 0x%08X @ 0x%08X.%08X\n",
+			ctx->pmd, val, val_hi, val_lo);
+		dev_err(edac_dev->dev,
+			"ErrSyndrome 0x%02X ErrWay 0x%02X ErrCpu %d ErrGroup 0x%02X ErrAction 0x%02X\n",
+			MEMERR_L2C_L2ESR_ERRSYN_RD(val),
+			MEMERR_L2C_L2ESR_ERRWAY_RD(val),
+			MEMERR_L2C_L2ESR_ERRCPU_RD(val),
+			MEMERR_L2C_L2ESR_ERRGROUP_RD(val),
+			MEMERR_L2C_L2ESR_ERRACTION_RD(val));
+
+		if (val & MEMERR_L2C_L2ESR_ERR_MASK)
+			dev_err(edac_dev->dev,
+				"One or more correctable error\n");
+		if (val & MEMERR_L2C_L2ESR_MULTICERR_MASK)
+			dev_err(edac_dev->dev, "Multiple correctable error\n");
+		if (val & MEMERR_L2C_L2ESR_UCERR_MASK)
+			dev_err(edac_dev->dev,
+				"One or more uncorrectable error\n");
+		if (val & MEMERR_L2C_L2ESR_MULTUCERR_MASK)
+			dev_err(edac_dev->dev,
+				"Multiple uncorrectable error\n");
+
+		switch (MEMERR_L2C_L2ESR_ERRTYPE_RD(val)) {
+		case 0:
+			dev_err(edac_dev->dev, "Outbound SDB parity error\n");
+			break;
+		case 1:
+			dev_err(edac_dev->dev, "Inbound SDB parity error\n");
+			break;
+		case 2:
+			dev_err(edac_dev->dev, "Tag ECC error\n");
+			break;
+		case 3:
+			dev_err(edac_dev->dev, "Data ECC error\n");
+			break;
+		}
+
+		/* Clear any HW errors */
+		writel(val, pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET);
+
+		if (val & (MEMERR_L2C_L2ESR_ERR_MASK |
+			   MEMERR_L2C_L2ESR_MULTICERR_MASK))
+			edac_device_handle_ce(edac_dev, 0, 0,
+					      edac_dev->ctl_name);
+		if (val & (MEMERR_L2C_L2ESR_UCERR_MASK |
+			   MEMERR_L2C_L2ESR_MULTUCERR_MASK))
+			edac_device_handle_ue(edac_dev, 0, 0,
+					      edac_dev->ctl_name);
+	}
+
+	/* Check if any memory request timed out on L2 cache */
+	pg_d = ctx->pmd_csr + CPU_L2C_PAGE;
+	val = readl(pg_d + CPUX_L2C_L2RTOSR_PAGE_OFFSET);
+	if (val) {
+		val_lo = readl(pg_d + CPUX_L2C_L2RTOALR_PAGE_OFFSET);
+		val_hi = readl(pg_d + CPUX_L2C_L2RTOAHR_PAGE_OFFSET);
+		dev_err(edac_dev->dev,
+			"PMD%d L2C error L2C RTOSR 0x%08X @ 0x%08X.%08X\n",
+			ctx->pmd, val, val_hi, val_lo);
+		writel(val, pg_d + CPUX_L2C_L2RTOSR_PAGE_OFFSET);
+	}
+}
+
+static void xgene_edac_pmd_check(struct edac_device_ctl_info *edac_dev)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	unsigned int pcp_hp_stat;
+	int i;
+
+	if (xgene_edac_pcp_rd(ctx->edac, PCPHPERRINTSTS, &pcp_hp_stat))
+		return;
+	if (!((PMD0_MERR_MASK << ctx->pmd) & pcp_hp_stat))
+		return;
+
+	/* Check CPU L1 error */
+	for (i = 0; i < MAX_CPU_PER_PMD; i++)
+		xgene_edac_pmd_l1_check(edac_dev, i);
+
+	/* Check CPU L2 error */
+	xgene_edac_pmd_l2_check(edac_dev);
+}
+
+static void xgene_edac_pmd_cpu_hw_cfg(struct edac_device_ctl_info *edac_dev,
+				      int cpu)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	void __iomem *pg_f = ctx->pmd_csr + cpu * CPU_CSR_STRIDE +
+			     CPU_MEMERR_CPU_PAGE;
+
+	/*
+	 * Enable CPU memory error:
+	 *  MEMERR_CPU_ICFESRA, MEMERR_CPU_LSUESRA, and MEMERR_CPU_MMUESRA
+	 */
+	writel(0x00000301, pg_f + MEMERR_CPU_ICFECR_PAGE_OFFSET);
+	writel(0x00000301, pg_f + MEMERR_CPU_LSUECR_PAGE_OFFSET);
+	writel(0x00000101, pg_f + MEMERR_CPU_MMUECR_PAGE_OFFSET);
+}
+
+static bool xgene_edac_pmd_l2c_version1(void)
+{
+	/* Check all chips with PMD L2C version 1 HW */
+	#define REVIDR_MINOR_REV(revidr)	((revidr) & 0x00000007)
+
+	switch (MIDR_VARIANT(read_cpuid_id())) {
+	case 0:
+		switch (MIDR_REVISION(read_cpuid_id())) {
+		case 0:
+
+			switch (REVIDR_MINOR_REV(read_cpuid(REVIDR_EL1))) {
+			case 1:
+			case 2:
+				return true;
+			};
+			break;
+		case 1:
+			if (REVIDR_MINOR_REV(read_cpuid(REVIDR_EL1)) == 1)
+				return true;
+			break;
+		}
+		break;
+	case 1:
+		switch (MIDR_REVISION(read_cpuid_id())) {
+		case 0:
+			switch (REVIDR_MINOR_REV(read_cpuid(REVIDR_EL1))) {
+			case 1:
+				return true;
+			};
+			break;
+		case 1:
+			switch (REVIDR_MINOR_REV(read_cpuid(REVIDR_EL1))) {
+			case 1:
+			case 0:
+				return true;
+			};
+			break;
+		}
+		break;
+	}
+
+	return false;
+}
+
+static void xgene_edac_pmd_hw_cfg(struct edac_device_ctl_info *edac_dev)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	void __iomem *pg_d = ctx->pmd_csr + CPU_L2C_PAGE;
+	void __iomem *pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE;
+
+	/* Enable PMD memory error - MEMERR_L2C_L2ECR and L2C_L2RTOCR */
+	writel(0x00000703, pg_e + MEMERR_L2C_L2ECR_PAGE_OFFSET);
+	/* Configure L2C HW request time out feature if supported */
+	if (!xgene_edac_pmd_l2c_version1())
+		writel(0x00000119, pg_d + CPUX_L2C_L2RTOCR_PAGE_OFFSET);
+}
+
+static void xgene_edac_pmd_hw_ctl(struct edac_device_ctl_info *edac_dev,
+				  bool enable)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	int i;
+
+	/* Enable PMD error interrupt */
+	if (edac_dev->op_state == OP_RUNNING_INTERRUPT) {
+		if (enable)
+			xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK,
+					       PMD0_MERR_MASK << ctx->pmd);
+		else
+			xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK,
+					       PMD0_MERR_MASK << ctx->pmd);
+	}
+
+	if (enable) {
+		xgene_edac_pmd_hw_cfg(edac_dev);
+
+		/* Two CPUs per a PMD */
+		for (i = 0; i < MAX_CPU_PER_PMD; i++)
+			xgene_edac_pmd_cpu_hw_cfg(edac_dev, i);
+	}
+}
+
+#ifdef CONFIG_EDAC_DEBUG
+static ssize_t xgene_edac_pmd_l1_inject_ctrl_write(struct file *file,
+						   const char __user *data,
+						   size_t count, loff_t *ppos)
+{
+	struct edac_device_ctl_info *edac_dev = file->private_data;
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	void __iomem *cpux_pg_f;
+	int i;
+
+	for (i = 0; i < MAX_CPU_PER_PMD; i++) {
+		cpux_pg_f = ctx->pmd_csr + i * CPU_CSR_STRIDE +
+			    CPU_MEMERR_CPU_PAGE;
+
+		writel(MEMERR_CPU_ICFESR_MULTCERR_MASK |
+		       MEMERR_CPU_ICFESR_CERR_MASK,
+		       cpux_pg_f + MEMERR_CPU_ICFESRA_PAGE_OFFSET);
+		writel(MEMERR_CPU_LSUESR_MULTCERR_MASK |
+		       MEMERR_CPU_LSUESR_CERR_MASK,
+		       cpux_pg_f + MEMERR_CPU_LSUESRA_PAGE_OFFSET);
+		writel(MEMERR_CPU_MMUESR_MULTCERR_MASK |
+		       MEMERR_CPU_MMUESR_CERR_MASK,
+		       cpux_pg_f + MEMERR_CPU_MMUESRA_PAGE_OFFSET);
+	}
+	return count;
+}
+
+static ssize_t xgene_edac_pmd_l2_inject_ctrl_write(struct file *file,
+						   const char __user *data,
+						   size_t count, loff_t *ppos)
+{
+	struct edac_device_ctl_info *edac_dev = file->private_data;
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	void __iomem *pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE;
+
+	writel(MEMERR_L2C_L2ESR_MULTUCERR_MASK |
+	       MEMERR_L2C_L2ESR_MULTICERR_MASK |
+	       MEMERR_L2C_L2ESR_UCERR_MASK |
+	       MEMERR_L2C_L2ESR_ERR_MASK,
+	       pg_e + MEMERR_L2C_L2ESRA_PAGE_OFFSET);
+	return count;
+}
+
+static const struct file_operations xgene_edac_pmd_debug_inject_fops[] = {
+	{
+	.open = simple_open,
+	.write = xgene_edac_pmd_l1_inject_ctrl_write,
+	.llseek = generic_file_llseek, },
+	{
+	.open = simple_open,
+	.write = xgene_edac_pmd_l2_inject_ctrl_write,
+	.llseek = generic_file_llseek, },
+	{ }
+};
+
+static void xgene_edac_pmd_create_debugfs_nodes(
+	struct edac_device_ctl_info *edac_dev)
+{
+	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
+	struct dentry *edac_debugfs;
+	char name[30];
+
+	/*
+	 * Todo: Switch to common EDAC debug file system for edac device
+	 *       when available.
+	 */
+	if (!ctx->edac->dfs) {
+		ctx->edac->dfs = debugfs_create_dir(edac_dev->dev->kobj.name,
+						    NULL);
+		if (!ctx->edac->dfs)
+			return;
+	}
+	sprintf(name, "PMD%d", ctx->pmd);
+	edac_debugfs = debugfs_create_dir(name, ctx->edac->dfs);
+	if (!edac_debugfs)
+		return;
+
+	debugfs_create_file("l1_inject_ctrl", S_IWUSR, edac_debugfs, edac_dev,
+			    &xgene_edac_pmd_debug_inject_fops[0]);
+	debugfs_create_file("l2_inject_ctrl", S_IWUSR, edac_debugfs, edac_dev,
+			    &xgene_edac_pmd_debug_inject_fops[1]);
+}
+#else
+static void xgene_edac_pmd_create_debugfs_nodes(
+	struct edac_device_ctl_info *edac_dev)
+{
+}
+#endif
+
+static int xgene_edac_pmd_available(u32 efuse, int pmd)
+{
+	return (efuse & (1 << pmd)) ? 0 : 1;
+}
+
+static int xgene_edac_pmd_add(struct xgene_edac *edac, struct device_node *np)
+{
+	struct edac_device_ctl_info *edac_dev;
+	struct xgene_edac_pmd_ctx *ctx;
+	struct resource res;
+	char edac_name[10];
+	int pmd;
+	int rc;
+	u32 val;
+
+	if (!devres_open_group(edac->dev, xgene_edac_pmd_add, GFP_KERNEL))
+		return -ENOMEM;
+
+	/* Find the PMD number from its address */
+	rc = of_address_to_resource(np, 0, &res);
+	if (rc < 0) {
+		dev_err(edac->dev, "no PMD resource address\n");
+		goto err_group;
+	}
+	pmd = ((res.start >> 20) & 0x1E) >> 1;
+
+	/* Determine if this PMD is disabled */
+	rc = xgene_edac_efuse_rd(edac, 0, &val);
+	if (rc)
+		goto err_group;
+	if (!xgene_edac_pmd_available(val, pmd)) {
+		rc = -ENODEV;
+		goto err_group;
+	}
+
+	sprintf(edac_name, "l2c%d", pmd);
+	edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx),
+					      edac_name, 1, "l2c", 1, 2, NULL,
+					      0, edac_device_alloc_index());
+	if (!edac_dev) {
+		rc = -ENOMEM;
+		goto err_group;
+	}
+
+	ctx = edac_dev->pvt_info;
+	ctx->name = "xgene_pmd_err";
+	ctx->pmd = pmd;
+	ctx->edac = edac;
+	ctx->edac_dev = edac_dev;
+	ctx->ddev = *edac->dev;
+	edac_dev->dev = &ctx->ddev;
+	edac_dev->ctl_name = ctx->name;
+	edac_dev->dev_name = ctx->name;
+	edac_dev->mod_name = EDAC_MOD_STR;
+
+	ctx->pmd_csr = devm_ioremap_resource(edac->dev, &res);
+	if (IS_ERR(ctx->pmd_csr)) {
+		dev_err(edac->dev,
+			"devm_ioremap_resource failed for PMD resource address\n");
+		rc = PTR_ERR(ctx->pmd_csr);
+		goto err_free;
+	}
+
+	if (edac_op_state == EDAC_OPSTATE_POLL)
+		edac_dev->edac_check = xgene_edac_pmd_check;
+
+	xgene_edac_pmd_create_debugfs_nodes(edac_dev);
+
+	rc = edac_device_add_device(edac_dev);
+	if (rc > 0) {
+		dev_err(edac->dev, "edac_device_add_device failed\n");
+		rc = -ENOMEM;
+		goto err_free;
+	}
+
+	if (edac_op_state == EDAC_OPSTATE_INT)
+		edac_dev->op_state = OP_RUNNING_INTERRUPT;
+
+	list_add(&ctx->next, &edac->pmds);
+
+	xgene_edac_pmd_hw_ctl(edac_dev, 1);
+
+	devres_remove_group(edac->dev, xgene_edac_pmd_add);
+
+	dev_info(edac->dev, "X-Gene EDAC PMD%d registered\n", ctx->pmd);
+	return 0;
+
+err_free:
+	edac_device_free_ctl_info(edac_dev);
+err_group:
+	devres_release_group(edac->dev, xgene_edac_pmd_add);
+	return rc;
+}
+
+static int xgene_edac_pmd_remove(struct xgene_edac_pmd_ctx *pmd)
+{
+	struct edac_device_ctl_info *edac_dev = pmd->edac_dev;
+
+	xgene_edac_pmd_hw_ctl(edac_dev, 0);
+	edac_device_del_device(edac_dev->dev);
+	edac_device_free_ctl_info(edac_dev);
+	return 0;
+}
+
+static int xgene_edac_pcp_rd(struct xgene_edac *edac, u32 reg, u32 *val)
+{
+	if (edac == NULL)
+		return -EINVAL;
+
+	*val = readl(edac->pcp_csr + reg);
+	return 0;
+}
+
+static int xgene_edac_pcp_clrbits(struct xgene_edac *edac, u32 reg,
+				  u32 bits_mask)
+{
+	u32 val;
+
+	if (edac == NULL)
+		return -EINVAL;
+
+	spin_lock(&edac->lock);
+	val = readl(edac->pcp_csr + reg);
+	val &= ~bits_mask;
+	writel(val, edac->pcp_csr + reg);
+	spin_unlock(&edac->lock);
+	return 0;
+}
+
+static int xgene_edac_csw_rd(struct xgene_edac *edac, u32 reg, u32 *val)
+{
+	if (edac == NULL)
+		return -EINVAL;
+
+	return regmap_read(edac->csw_map, reg, val);
+}
+
+static int xgene_edac_mcba_rd(struct xgene_edac *edac, u32 reg, u32 *val)
+{
+	if (edac == NULL)
+		return -EINVAL;
+
+	return regmap_read(edac->mcba_map, reg, val);
+}
+
+static int xgene_edac_mcbb_rd(struct xgene_edac *edac, u32 reg, u32 *val)
+{
+	if (edac == NULL)
+		return -EINVAL;
+
+	return regmap_read(edac->mcbb_map, reg, val);
+}
+
+static int xgene_edac_efuse_rd(struct xgene_edac *edac, u32 reg, u32 *val)
+{
+	if (edac == NULL)
+		return -EINVAL;
+
+	return regmap_read(edac->efuse_map, reg, val);
+}
+
+static int xgene_edac_pcp_setbits(struct xgene_edac *edac, u32 reg,
+				  u32 bits_mask)
+{
+	u32 val;
+
+	if (edac == NULL)
+		return -EINVAL;
+
+	spin_lock(&edac->lock);
+	val = readl(edac->pcp_csr + reg);
+	val |= bits_mask;
+	writel(val, edac->pcp_csr + reg);
+	spin_unlock(&edac->lock);
+	return 0;
+}
+
+static irqreturn_t xgene_edac_isr(int irq, void *dev_id)
+{
+	struct xgene_edac *ctx = dev_id;
+	struct xgene_edac_pmd_ctx *pmd;
+	unsigned int pcp_hp_stat;
+	unsigned int pcp_lp_stat;
+
+	if (xgene_edac_pcp_rd(ctx, PCPHPERRINTSTS, &pcp_hp_stat))
+		return IRQ_NONE;
+	if (xgene_edac_pcp_rd(ctx, PCPLPERRINTSTS, &pcp_lp_stat))
+		return IRQ_NONE;
+	if ((MCU_UNCORR_ERR_MASK & pcp_hp_stat) ||
+	    (MCU_CTL_ERR_MASK & pcp_hp_stat) ||
+	    (MCU_CORR_ERR_MASK & pcp_lp_stat)) {
+		struct xgene_edac_mc_ctx *mcu;
+
+		list_for_each_entry(mcu, &ctx->mcus, next) {
+			xgene_edac_mc_check(mcu->mci);
+		}
+	}
+
+	list_for_each_entry(pmd, &ctx->pmds, next) {
+		if ((PMD0_MERR_MASK << pmd->pmd) & pcp_hp_stat)
+			xgene_edac_pmd_check(pmd->edac_dev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int xgene_edac_probe(struct platform_device *pdev)
+{
+	struct xgene_edac *edac;
+	struct device_node *child;
+	struct resource *res;
+	int rc;
+
+	edac = devm_kzalloc(&pdev->dev, sizeof(*edac), GFP_KERNEL);
+	if (!edac)
+		return -ENOMEM;
+
+	edac->dev = &pdev->dev;
+	platform_set_drvdata(pdev, edac);
+	INIT_LIST_HEAD(&edac->mcus);
+	INIT_LIST_HEAD(&edac->pmds);
+	spin_lock_init(&edac->lock);
+
+	edac->csw_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+							"regmap-csw");
+	if (IS_ERR(edac->csw_map)) {
+		dev_err(edac->dev, "unable to get syscon regmap csw\n");
+		rc = PTR_ERR(edac->csw_map);
+		goto out_err;
+	}
+
+	edac->mcba_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+							 "regmap-mcba");
+	if (IS_ERR(edac->mcba_map)) {
+		dev_err(edac->dev, "unable to get syscon regmap mcba\n");
+		rc = PTR_ERR(edac->mcba_map);
+		goto out_err;
+	}
+
+	edac->mcbb_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+							 "regmap-mcbb");
+	if (IS_ERR(edac->mcbb_map)) {
+		dev_err(edac->dev, "unable to get syscon regmap mcbb\n");
+		rc = PTR_ERR(edac->mcbb_map);
+		goto out_err;
+	}
+	edac->efuse_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+							  "regmap-efuse");
+	if (IS_ERR(edac->efuse_map)) {
+		dev_err(edac->dev, "unable to get syscon regmap efuse\n");
+		rc = PTR_ERR(edac->efuse_map);
+		goto out_err;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	edac->pcp_csr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(edac->pcp_csr)) {
+		dev_err(&pdev->dev, "no PCP resource address\n");
+		rc = PTR_ERR(edac->pcp_csr);
+		goto out_err;
+	}
+
+	if (edac_op_state == EDAC_OPSTATE_INT) {
+		int irq;
+		int i;
+
+		for (i = 0; i < 3; i++) {
+			irq = platform_get_irq(pdev, i);
+			if (irq < 0) {
+				dev_err(&pdev->dev, "No IRQ resource\n");
+				rc = -EINVAL;
+				goto out_err;
+			}
+			rc = devm_request_irq(&pdev->dev, irq,
+					      xgene_edac_isr, IRQF_SHARED,
+					      dev_name(&pdev->dev), edac);
+			if (rc) {
+				dev_err(&pdev->dev,
+					"Could not request IRQ %d\n", irq);
+				goto out_err;
+			}
+		}
+	}
+
+	for_each_child_of_node(pdev->dev.of_node, child) {
+		if (!of_device_is_available(child))
+			continue;
+		if (of_device_is_compatible(child, "apm,xgene-edac-mc"))
+			xgene_edac_mc_add(edac, child);
+		if (of_device_is_compatible(child, "apm,xgene-edac-pmd"))
+			xgene_edac_pmd_add(edac, child);
+	}
+
+	return 0;
+
+out_err:
+	return rc;
+}
+
+static int xgene_edac_remove(struct platform_device *pdev)
+{
+	struct xgene_edac *edac = dev_get_drvdata(&pdev->dev);
+	struct xgene_edac_mc_ctx *mcu;
+	struct xgene_edac_mc_ctx *temp_mcu;
+	struct xgene_edac_pmd_ctx *pmd;
+	struct xgene_edac_pmd_ctx *temp_pmd;
+
+	list_for_each_entry_safe(mcu, temp_mcu, &edac->mcus, next) {
+		xgene_edac_mc_remove(mcu);
+	}
+
+	list_for_each_entry_safe(pmd, temp_pmd, &edac->pmds, next) {
+		xgene_edac_pmd_remove(pmd);
+	}
+	return 0;
+}
+
+static const struct of_device_id xgene_edac_of_match[] = {
+	{ .compatible = "apm,xgene-edac" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, xgene_edac_of_match);
+
+static struct platform_driver xgene_edac_driver = {
+	.probe = xgene_edac_probe,
+	.remove = xgene_edac_remove,
+	.driver = {
+		.name = "xgene-edac",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_edac_of_match,
+	},
+};
+
+static int __init xgene_edac_init(void)
+{
+	int rc;
+
+	/* Make sure error reporting method is sane */
+	switch (edac_op_state) {
+	case EDAC_OPSTATE_POLL:
+	case EDAC_OPSTATE_INT:
+		break;
+	default:
+		edac_op_state = EDAC_OPSTATE_INT;
+		break;
+	}
+
+	rc = platform_driver_register(&xgene_edac_driver);
+	if (rc) {
+		edac_printk(KERN_ERR, EDAC_MOD_STR,
+			    "EDAC fails to register\n");
+		goto reg_failed;
+	}
+
+	return 0;
+
+reg_failed:
+	return rc;
+}
+module_init(xgene_edac_init);
+
+static void __exit xgene_edac_exit(void)
+{
+	platform_driver_unregister(&xgene_edac_driver);
+}
+module_exit(xgene_edac_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Feng Kan <fkan@apm.com>");
+MODULE_DESCRIPTION("APM X-Gene EDAC driver");
+module_param(edac_op_state, int, 0444);
+MODULE_PARM_DESC(edac_op_state,
+		 "EDAC error reporting state: 0=Poll, 2=Interrupt");
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v10 5/5] arm64: Add APM X-Gene SoC EDAC DTS entries
  2015-05-18 23:24                 ` Loc Ho
@ 2015-05-18 23:24                     ` Loc Ho
  -1 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-18 23:24 UTC (permalink / raw)
  To: dougthompson-aS9lmoZGLiVWk0Htik3J/w, bp-Gina5bIWoIWzQB+pC5nmwQ,
	mchehab-JPH+aEBZ4P+UEJcrhfAQsw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg
  Cc: linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	jcm-H+wXaHxf7aLQT0dZR+AlfA, patches-qTEPVZfXA3Y, Loc Ho

This patch adds APM X-Gene SoC EDAC DTS entries.

Signed-off-by: Loc Ho <lho-qTEPVZfXA3Y@public.gmane.org>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi |   75 ++++++++++++++++++++++++++++++++
 1 files changed, 75 insertions(+), 0 deletions(-)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index c8d3e0e..51aa174 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -374,6 +374,81 @@
 			};
 		};
 
+		csw: csw@7e200000 {
+			compatible = "syscon";
+			reg = <0x0 0x7e200000 0x0 0x1000>;
+		};
+
+		mcba: mcba@7e700000 {
+			compatible = "syscon";
+			reg = <0x0 0x7e700000 0x0 0x1000>;
+		};
+
+		mcbb: mcbb@7e720000 {
+			compatible = "syscon";
+			reg = <0x0 0x7e720000 0x0 0x1000>;
+		};
+
+		efuse: efuse@1054a000 {
+			compatible = "syscon";
+			reg = <0x0 0x1054a000 0x0 0x20>;
+		};
+
+		edac@78800000 {
+			compatible = "apm,xgene-edac";
+			#address-cells = <2>;
+			#size-cells = <2>;
+			ranges;
+			regmap-csw = <&csw>;
+			regmap-mcba = <&mcba>;
+			regmap-mcbb = <&mcbb>;
+			regmap-efuse = <&efuse>;
+			reg = <0x0 0x78800000 0x0 0x100>;
+			interrupts = <0x0 0x20 0x4>,
+				     <0x0 0x21 0x4>,
+				     <0x0 0x27 0x4>;
+
+			edacmc@7e800000 {
+				compatible = "apm,xgene-edac-mc";
+				reg = <0x0 0x7e800000 0x0 0x1000>;
+			};
+
+			edacmc@7e840000 {
+				compatible = "apm,xgene-edac-mc";
+				reg = <0x0 0x7e840000 0x0 0x1000>;
+			};
+
+			edacmc@7e880000 {
+				compatible = "apm,xgene-edac-mc";
+				reg = <0x0 0x7e880000 0x0 0x1000>;
+			};
+
+			edacmc@7e8c0000 {
+				compatible = "apm,xgene-edac-mc";
+				reg = <0x0 0x7e8c0000 0x0 0x1000>;
+			};
+
+			edacpmd@7c000000 {
+				compatible = "apm,xgene-edac-pmd";
+				reg = <0x0 0x7c000000 0x0 0x200000>;
+			};
+
+			edacpmd@7c200000 {
+				compatible = "apm,xgene-edac-pmd";
+				reg = <0x0 0x7c200000 0x0 0x200000>;
+			};
+
+			edacpmd@7c400000 {
+				compatible = "apm,xgene-edac-pmd";
+				reg = <0x0 0x7c400000 0x0 0x200000>;
+			};
+
+			edacpmd@7c600000 {
+				compatible = "apm,xgene-edac-pmd";
+				reg = <0x0 0x7c600000 0x0 0x200000>;
+			};
+		};
+
 		pcie0: pcie@1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v10 5/5] arm64: Add APM X-Gene SoC EDAC DTS entries
@ 2015-05-18 23:24                     ` Loc Ho
  0 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-18 23:24 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds APM X-Gene SoC EDAC DTS entries.

Signed-off-by: Loc Ho <lho@apm.com>
---
 arch/arm64/boot/dts/apm/apm-storm.dtsi |   75 ++++++++++++++++++++++++++++++++
 1 files changed, 75 insertions(+), 0 deletions(-)

diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi
index c8d3e0e..51aa174 100644
--- a/arch/arm64/boot/dts/apm/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi
@@ -374,6 +374,81 @@
 			};
 		};
 
+		csw: csw at 7e200000 {
+			compatible = "syscon";
+			reg = <0x0 0x7e200000 0x0 0x1000>;
+		};
+
+		mcba: mcba at 7e700000 {
+			compatible = "syscon";
+			reg = <0x0 0x7e700000 0x0 0x1000>;
+		};
+
+		mcbb: mcbb at 7e720000 {
+			compatible = "syscon";
+			reg = <0x0 0x7e720000 0x0 0x1000>;
+		};
+
+		efuse: efuse at 1054a000 {
+			compatible = "syscon";
+			reg = <0x0 0x1054a000 0x0 0x20>;
+		};
+
+		edac at 78800000 {
+			compatible = "apm,xgene-edac";
+			#address-cells = <2>;
+			#size-cells = <2>;
+			ranges;
+			regmap-csw = <&csw>;
+			regmap-mcba = <&mcba>;
+			regmap-mcbb = <&mcbb>;
+			regmap-efuse = <&efuse>;
+			reg = <0x0 0x78800000 0x0 0x100>;
+			interrupts = <0x0 0x20 0x4>,
+				     <0x0 0x21 0x4>,
+				     <0x0 0x27 0x4>;
+
+			edacmc at 7e800000 {
+				compatible = "apm,xgene-edac-mc";
+				reg = <0x0 0x7e800000 0x0 0x1000>;
+			};
+
+			edacmc at 7e840000 {
+				compatible = "apm,xgene-edac-mc";
+				reg = <0x0 0x7e840000 0x0 0x1000>;
+			};
+
+			edacmc at 7e880000 {
+				compatible = "apm,xgene-edac-mc";
+				reg = <0x0 0x7e880000 0x0 0x1000>;
+			};
+
+			edacmc at 7e8c0000 {
+				compatible = "apm,xgene-edac-mc";
+				reg = <0x0 0x7e8c0000 0x0 0x1000>;
+			};
+
+			edacpmd at 7c000000 {
+				compatible = "apm,xgene-edac-pmd";
+				reg = <0x0 0x7c000000 0x0 0x200000>;
+			};
+
+			edacpmd at 7c200000 {
+				compatible = "apm,xgene-edac-pmd";
+				reg = <0x0 0x7c200000 0x0 0x200000>;
+			};
+
+			edacpmd at 7c400000 {
+				compatible = "apm,xgene-edac-pmd";
+				reg = <0x0 0x7c400000 0x0 0x200000>;
+			};
+
+			edacpmd at 7c600000 {
+				compatible = "apm,xgene-edac-pmd";
+				reg = <0x0 0x7c600000 0x0 0x200000>;
+			};
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [PATCH v10 1/5] arm64: Enable EDAC on ARM64
  2015-05-18 23:24     ` Loc Ho
@ 2015-05-19 17:03         ` Borislav Petkov
  -1 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-19 17:03 UTC (permalink / raw)
  To: Loc Ho
  Cc: dougthompson-aS9lmoZGLiVWk0Htik3J/w,
	mchehab-JPH+aEBZ4P+UEJcrhfAQsw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	jcm-H+wXaHxf7aLQT0dZR+AlfA, patches-qTEPVZfXA3Y

On Mon, May 18, 2015 at 05:24:37PM -0600, Loc Ho wrote:
> Add an stub atomic_scrub function and enable EDAC for arm64.
> 
> Signed-off-by: Loc Ho <lho-qTEPVZfXA3Y@public.gmane.org>
> ---
>  arch/arm64/Kconfig            |    1 +
>  arch/arm64/include/asm/edac.h |   28 ++++++++++++++++++++++++++++
>  2 files changed, 29 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm64/include/asm/edac.h
> 
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 4269dba..577078f 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -22,6 +22,7 @@ config ARM64
>  	select BUILDTIME_EXTABLE_SORT
>  	select CLONE_BACKWARDS
>  	select COMMON_CLK
> +	select EDAC_SUPPORT
>  	select CPU_PM if (SUSPEND || CPU_IDLE)
>  	select DCACHE_WORD_ACCESS
>  	select GENERIC_ALLOCATOR
> diff --git a/arch/arm64/include/asm/edac.h b/arch/arm64/include/asm/edac.h
> new file mode 100644
> index 0000000..683495b
> --- /dev/null
> +++ b/arch/arm64/include/asm/edac.h
> @@ -0,0 +1,28 @@
> +/*
> + * ARM64 EDAC Header File
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +#ifndef __ASM_EDAC_H
> +#define __ASM_EDAC_H
> +
> +/*
> + * ECC atomic, DMA, SMP and interrupt safe scrub function.
> + */
> +static inline void atomic_scrub(void *va, u32 size)
> +{
> +	/* Stub function for now until an ARM64 HW has a way to test it. */
> +	WARN_ONCE(1, "not implemented");
> +}

So you're adding this empty function, it issues a warning for which the
user can't really do anything about and on top of that, it is not even
used in this patchset.

So please kill it from this submission.

You can add a full-fledged version later after you test it successfully
on real hw.

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 1/5] arm64: Enable EDAC on ARM64
@ 2015-05-19 17:03         ` Borislav Petkov
  0 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-19 17:03 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 18, 2015 at 05:24:37PM -0600, Loc Ho wrote:
> Add an stub atomic_scrub function and enable EDAC for arm64.
> 
> Signed-off-by: Loc Ho <lho@apm.com>
> ---
>  arch/arm64/Kconfig            |    1 +
>  arch/arm64/include/asm/edac.h |   28 ++++++++++++++++++++++++++++
>  2 files changed, 29 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm64/include/asm/edac.h
> 
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 4269dba..577078f 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -22,6 +22,7 @@ config ARM64
>  	select BUILDTIME_EXTABLE_SORT
>  	select CLONE_BACKWARDS
>  	select COMMON_CLK
> +	select EDAC_SUPPORT
>  	select CPU_PM if (SUSPEND || CPU_IDLE)
>  	select DCACHE_WORD_ACCESS
>  	select GENERIC_ALLOCATOR
> diff --git a/arch/arm64/include/asm/edac.h b/arch/arm64/include/asm/edac.h
> new file mode 100644
> index 0000000..683495b
> --- /dev/null
> +++ b/arch/arm64/include/asm/edac.h
> @@ -0,0 +1,28 @@
> +/*
> + * ARM64 EDAC Header File
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +#ifndef __ASM_EDAC_H
> +#define __ASM_EDAC_H
> +
> +/*
> + * ECC atomic, DMA, SMP and interrupt safe scrub function.
> + */
> +static inline void atomic_scrub(void *va, u32 size)
> +{
> +	/* Stub function for now until an ARM64 HW has a way to test it. */
> +	WARN_ONCE(1, "not implemented");
> +}

So you're adding this empty function, it issues a warning for which the
user can't really do anything about and on top of that, it is not even
used in this patchset.

So please kill it from this submission.

You can add a full-fledged version later after you test it successfully
on real hw.

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v10 1/5] arm64: Enable EDAC on ARM64
  2015-05-19 17:03         ` Borislav Petkov
@ 2015-05-19 19:57             ` Loc Ho
  -1 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-19 19:57 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Doug Thompson, Mauro Carvalho Chehab, Rob Herring, Mark Rutland,
	Ian Campbell, linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jon Masters,
	patches-qTEPVZfXA3Y

Hi,

On Tue, May 19, 2015 at 10:03 AM, Borislav Petkov <bp-Gina5bIWoIWzQB+pC5nmwQ@public.gmane.org> wrote:
> On Mon, May 18, 2015 at 05:24:37PM -0600, Loc Ho wrote:
>> Add an stub atomic_scrub function and enable EDAC for arm64.
>>
>> Signed-off-by: Loc Ho <lho-qTEPVZfXA3Y@public.gmane.org>
>> ---
>>  arch/arm64/Kconfig            |    1 +
>>  arch/arm64/include/asm/edac.h |   28 ++++++++++++++++++++++++++++
>>  2 files changed, 29 insertions(+), 0 deletions(-)
>>  create mode 100644 arch/arm64/include/asm/edac.h
>>
>> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
>> index 4269dba..577078f 100644
>> --- a/arch/arm64/Kconfig
>> +++ b/arch/arm64/Kconfig
>> @@ -22,6 +22,7 @@ config ARM64
>>       select BUILDTIME_EXTABLE_SORT
>>       select CLONE_BACKWARDS
>>       select COMMON_CLK
>> +     select EDAC_SUPPORT
>>       select CPU_PM if (SUSPEND || CPU_IDLE)
>>       select DCACHE_WORD_ACCESS
>>       select GENERIC_ALLOCATOR
>> diff --git a/arch/arm64/include/asm/edac.h b/arch/arm64/include/asm/edac.h
>> new file mode 100644
>> index 0000000..683495b
>> --- /dev/null
>> +++ b/arch/arm64/include/asm/edac.h
>> @@ -0,0 +1,28 @@
>> +/*
>> + * ARM64 EDAC Header File
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +#ifndef __ASM_EDAC_H
>> +#define __ASM_EDAC_H
>> +
>> +/*
>> + * ECC atomic, DMA, SMP and interrupt safe scrub function.
>> + */
>> +static inline void atomic_scrub(void *va, u32 size)
>> +{
>> +     /* Stub function for now until an ARM64 HW has a way to test it. */
>> +     WARN_ONCE(1, "not implemented");
>> +}
>
> So you're adding this empty function, it issues a warning for which the
> user can't really do anything about and on top of that, it is not even
> used in this patchset.
>
> So please kill it from this submission.

This was discussed here
http://lists.infradead.org/pipermail/linux-arm-kernel/2015-March/326986.html.
Without this, it will have compiler error as this function is called
by.function edac_mc_scrub_block in file drivers/edac/edac_mc.c.

>
> You can add a full-fledged version later after you test it successfully
> on real hw.

Our platform don't use it as it has HW scrub support.

-Loc
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 1/5] arm64: Enable EDAC on ARM64
@ 2015-05-19 19:57             ` Loc Ho
  0 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-19 19:57 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Tue, May 19, 2015 at 10:03 AM, Borislav Petkov <bp@alien8.de> wrote:
> On Mon, May 18, 2015 at 05:24:37PM -0600, Loc Ho wrote:
>> Add an stub atomic_scrub function and enable EDAC for arm64.
>>
>> Signed-off-by: Loc Ho <lho@apm.com>
>> ---
>>  arch/arm64/Kconfig            |    1 +
>>  arch/arm64/include/asm/edac.h |   28 ++++++++++++++++++++++++++++
>>  2 files changed, 29 insertions(+), 0 deletions(-)
>>  create mode 100644 arch/arm64/include/asm/edac.h
>>
>> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
>> index 4269dba..577078f 100644
>> --- a/arch/arm64/Kconfig
>> +++ b/arch/arm64/Kconfig
>> @@ -22,6 +22,7 @@ config ARM64
>>       select BUILDTIME_EXTABLE_SORT
>>       select CLONE_BACKWARDS
>>       select COMMON_CLK
>> +     select EDAC_SUPPORT
>>       select CPU_PM if (SUSPEND || CPU_IDLE)
>>       select DCACHE_WORD_ACCESS
>>       select GENERIC_ALLOCATOR
>> diff --git a/arch/arm64/include/asm/edac.h b/arch/arm64/include/asm/edac.h
>> new file mode 100644
>> index 0000000..683495b
>> --- /dev/null
>> +++ b/arch/arm64/include/asm/edac.h
>> @@ -0,0 +1,28 @@
>> +/*
>> + * ARM64 EDAC Header File
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +#ifndef __ASM_EDAC_H
>> +#define __ASM_EDAC_H
>> +
>> +/*
>> + * ECC atomic, DMA, SMP and interrupt safe scrub function.
>> + */
>> +static inline void atomic_scrub(void *va, u32 size)
>> +{
>> +     /* Stub function for now until an ARM64 HW has a way to test it. */
>> +     WARN_ONCE(1, "not implemented");
>> +}
>
> So you're adding this empty function, it issues a warning for which the
> user can't really do anything about and on top of that, it is not even
> used in this patchset.
>
> So please kill it from this submission.

This was discussed here
http://lists.infradead.org/pipermail/linux-arm-kernel/2015-March/326986.html.
Without this, it will have compiler error as this function is called
by.function edac_mc_scrub_block in file drivers/edac/edac_mc.c.

>
> You can add a full-fledged version later after you test it successfully
> on real hw.

Our platform don't use it as it has HW scrub support.

-Loc

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v10 1/5] arm64: Enable EDAC on ARM64
  2015-05-19 19:57             ` Loc Ho
@ 2015-05-19 20:33                 ` Borislav Petkov
  -1 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-19 20:33 UTC (permalink / raw)
  To: Loc Ho
  Cc: Doug Thompson, Mauro Carvalho Chehab, Rob Herring, Mark Rutland,
	Ian Campbell, linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jon Masters,
	patches-qTEPVZfXA3Y

On Tue, May 19, 2015 at 12:57:32PM -0700, Loc Ho wrote:
> This was discussed here
> http://lists.infradead.org/pipermail/linux-arm-kernel/2015-March/326986.html.
> Without this, it will have compiler error as this function is called
> by.function edac_mc_scrub_block in file drivers/edac/edac_mc.c.

Argh, that's just silly. We should have a __weak atomic_scrub() in the
edac core which arches can override but not have to supply a dummy just
because...

I'll try to hack up something later.

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 1/5] arm64: Enable EDAC on ARM64
@ 2015-05-19 20:33                 ` Borislav Petkov
  0 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-19 20:33 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, May 19, 2015 at 12:57:32PM -0700, Loc Ho wrote:
> This was discussed here
> http://lists.infradead.org/pipermail/linux-arm-kernel/2015-March/326986.html.
> Without this, it will have compiler error as this function is called
> by.function edac_mc_scrub_block in file drivers/edac/edac_mc.c.

Argh, that's just silly. We should have a __weak atomic_scrub() in the
edac core which arches can override but not have to supply a dummy just
because...

I'll try to hack up something later.

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v10 1/5] arm64: Enable EDAC on ARM64
  2015-05-19 20:33                 ` Borislav Petkov
@ 2015-05-21 18:07                     ` Borislav Petkov
  -1 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-21 18:07 UTC (permalink / raw)
  To: Loc Ho
  Cc: Doug Thompson, Mauro Carvalho Chehab, Rob Herring, Mark Rutland,
	Ian Campbell, linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jon Masters,
	patches-qTEPVZfXA3Y, Russell King, Ralf Baechle,
	Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Chris Metcalf, x86-DgEjT+Ai2ygdnm+yROfE0A, Markos Chandras,
	Steven J. Hill, . Maciej W. Rozycki

Adding more arch people.

On Tue, May 19, 2015 at 10:33:36PM +0200, Borislav Petkov wrote:
> On Tue, May 19, 2015 at 12:57:32PM -0700, Loc Ho wrote:
> > This was discussed here
> > http://lists.infradead.org/pipermail/linux-arm-kernel/2015-March/326986.html.
> > Without this, it will have compiler error as this function is called
> > by.function edac_mc_scrub_block in file drivers/edac/edac_mc.c.
> 
> Argh, that's just silly. We should have a __weak atomic_scrub() in the
> edac core which arches can override but not have to supply a dummy just
> because...
> 
> I'll try to hack up something later.

Phew, so this wasn't easy but I think I have it. And it is nicer now.
Patch as a reply to this message.

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 1/5] arm64: Enable EDAC on ARM64
@ 2015-05-21 18:07                     ` Borislav Petkov
  0 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-21 18:07 UTC (permalink / raw)
  To: linux-arm-kernel

Adding more arch people.

On Tue, May 19, 2015 at 10:33:36PM +0200, Borislav Petkov wrote:
> On Tue, May 19, 2015 at 12:57:32PM -0700, Loc Ho wrote:
> > This was discussed here
> > http://lists.infradead.org/pipermail/linux-arm-kernel/2015-March/326986.html.
> > Without this, it will have compiler error as this function is called
> > by.function edac_mc_scrub_block in file drivers/edac/edac_mc.c.
> 
> Argh, that's just silly. We should have a __weak atomic_scrub() in the
> edac core which arches can override but not have to supply a dummy just
> because...
> 
> I'll try to hack up something later.

Phew, so this wasn't easy but I think I have it. And it is nicer now.
Patch as a reply to this message.

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [RFC PATCH] EDAC: Cleanup atomic_scrub mess
  2015-05-21 18:07                     ` Borislav Petkov
@ 2015-05-21 18:11                         ` Borislav Petkov
  -1 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-21 18:11 UTC (permalink / raw)
  To: Loc Ho
  Cc: Doug Thompson, Mauro Carvalho Chehab, Rob Herring, Mark Rutland,
	Ian Campbell, linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jon Masters,
	patches-qTEPVZfXA3Y, Russell King, Ralf Baechle,
	Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Chris Metcalf, x86-DgEjT+Ai2ygdnm+yROfE0A, Markos Chandras,
	Steven J. Hill, . Maciej W. Rozycki

From: Borislav Petkov <bp-l3A5Bk7waGM@public.gmane.org>

So first of all, this atomic_scrub() function's naming is bad. It looks
like an atomic_t helper. Change it to edac_atomic_scrub().

The bigger problem is that this function is arch-specific and every new
arch which doesn't necessarily need that functionality still needs to
define it, otherwise EDAC doesn't compile.

So instead of doing that and including arch-specific headers, have each
arch define an EDAC_ATOMIC_SCRUB symbol which can be used in edac_mc.c
for ifdeffery. Much cleaner.

We already are doing this with another symbol - EDAC_SUPPORT. This is
also much cleaner than having CONFIG_EDAC explicitly depend on all the
arches which need/have EDAC support and drivers.

This way I can kill the useless edac.h header in tile too.

Signed-off-by: Borislav Petkov <bp-l3A5Bk7waGM@public.gmane.org>
Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
Cc: Ralf Baechle <ralf-6z/3iImG2C8G8FEW9MqTrA@public.gmane.org>
Cc: Benjamin Herrenschmidt <benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r@public.gmane.org>
Cc: Paul Mackerras <paulus-eUNUBHrolfbYtjvyW6yDsg@public.gmane.org>
Cc: Michael Ellerman <mpe-Gsx/Oe8HsFggBc27wqDAHg@public.gmane.org>
Cc: Chris Metcalf <cmetcalf-d5a29ZRxExrQT0dZR+AlfA@public.gmane.org>
Cc: x86-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org
Cc: Doug Thompson <dougthompson-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
Cc: Mauro Carvalho Chehab <mchehab-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
Cc: Markos Chandras <markos.chandras-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
Cc: "Steven J. Hill" <Steven.Hill-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
Cc: "Maciej W. Rozycki" <macro-qD8j1LwMmJjtCj0u4l0SBw@public.gmane.org>
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-mips-6z/3iImG2C8G8FEW9MqTrA@public.gmane.org
Cc: linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
Cc: linux-edac-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
---

Patch is still RFC as I need to compile in on those other arches first.
It should build though - it is simple enough ... famous last words :-P

 arch/arm/Kconfig                |  2 ++
 arch/arm/include/asm/edac.h     |  5 +++--
 arch/mips/Kconfig               |  1 +
 arch/mips/include/asm/edac.h    |  4 ++--
 arch/powerpc/Kconfig            |  2 ++
 arch/powerpc/include/asm/edac.h |  4 ++--
 arch/tile/Kconfig               |  1 +
 arch/tile/include/asm/edac.h    | 29 -----------------------------
 arch/x86/Kconfig                |  2 ++
 arch/x86/include/asm/edac.h     |  2 +-
 drivers/edac/Kconfig            |  7 ++++---
 drivers/edac/edac_mc.c          |  9 +++++++--
 12 files changed, 27 insertions(+), 41 deletions(-)
 delete mode 100644 arch/tile/include/asm/edac.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 45df48ba0b12..325d6f3a596a 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -15,6 +15,8 @@ config ARM
 	select CLONE_BACKWARDS
 	select CPU_PM if (SUSPEND || CPU_IDLE)
 	select DCACHE_WORD_ACCESS if HAVE_EFFICIENT_UNALIGNED_ACCESS
+	select EDAC_SUPPORT
+	select EDAC_ATOMIC_SCRUB
 	select GENERIC_ALLOCATOR
 	select GENERIC_ATOMIC64 if (CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI)
 	select GENERIC_CLOCKEVENTS_BROADCAST if SMP
diff --git a/arch/arm/include/asm/edac.h b/arch/arm/include/asm/edac.h
index 0df7a2c1fc3d..5189fa819b60 100644
--- a/arch/arm/include/asm/edac.h
+++ b/arch/arm/include/asm/edac.h
@@ -18,11 +18,12 @@
 #define ASM_EDAC_H
 /*
  * ECC atomic, DMA, SMP and interrupt safe scrub function.
- * Implements the per arch atomic_scrub() that EDAC use for software
+ * Implements the per arch edac_atomic_scrub() that EDAC use for software
  * ECC scrubbing.  It reads memory and then writes back the original
  * value, allowing the hardware to detect and correct memory errors.
  */
-static inline void atomic_scrub(void *va, u32 size)
+
+static inline void edac_atomic_scrub(void *va, u32 size)
 {
 #if __LINUX_ARM_ARCH__ >= 6
 	unsigned int *virt_addr = va;
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index f5016656494f..b65edf514b40 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -819,6 +819,7 @@ config CAVIUM_OCTEON_SOC
 	select SYS_SUPPORTS_64BIT_KERNEL
 	select SYS_SUPPORTS_BIG_ENDIAN
 	select EDAC_SUPPORT
+	select EDAC_ATOMIC_SCRUB
 	select SYS_SUPPORTS_LITTLE_ENDIAN
 	select SYS_SUPPORTS_HOTPLUG_CPU if CPU_BIG_ENDIAN
 	select SYS_HAS_EARLY_PRINTK
diff --git a/arch/mips/include/asm/edac.h b/arch/mips/include/asm/edac.h
index 94105d3f58f4..980b16527374 100644
--- a/arch/mips/include/asm/edac.h
+++ b/arch/mips/include/asm/edac.h
@@ -5,7 +5,7 @@
 
 /* ECC atomic, DMA, SMP and interrupt safe scrub function */
 
-static inline void atomic_scrub(void *va, u32 size)
+static inline void edac_atomic_scrub(void *va, u32 size)
 {
 	unsigned long *virt_addr = va;
 	unsigned long temp;
@@ -21,7 +21,7 @@ static inline void atomic_scrub(void *va, u32 size)
 
 		__asm__ __volatile__ (
 		"	.set	mips2					\n"
-		"1:	ll	%0, %1		# atomic_scrub		\n"
+		"1:	ll	%0, %1		# edac_atomic_scrub	\n"
 		"	addu	%0, $0					\n"
 		"	sc	%0, %1					\n"
 		"	beqz	%0, 1b					\n"
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 190cc48abc0c..5ef27113b898 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -153,6 +153,8 @@ config PPC
 	select NO_BOOTMEM
 	select HAVE_GENERIC_RCU_GUP
 	select HAVE_PERF_EVENTS_NMI if PPC64
+	select EDAC_SUPPORT
+	select EDAC_ATOMIC_SCRUB
 
 config GENERIC_CSUM
 	def_bool CPU_LITTLE_ENDIAN
diff --git a/arch/powerpc/include/asm/edac.h b/arch/powerpc/include/asm/edac.h
index 6ead88bbfbb8..5571e23d253e 100644
--- a/arch/powerpc/include/asm/edac.h
+++ b/arch/powerpc/include/asm/edac.h
@@ -12,11 +12,11 @@
 #define ASM_EDAC_H
 /*
  * ECC atomic, DMA, SMP and interrupt safe scrub function.
- * Implements the per arch atomic_scrub() that EDAC use for software
+ * Implements the per arch edac_atomic_scrub() that EDAC use for software
  * ECC scrubbing.  It reads memory and then writes back the original
  * value, allowing the hardware to detect and correct memory errors.
  */
-static __inline__ void atomic_scrub(void *va, u32 size)
+static __inline__ void edac_atomic_scrub(void *va, u32 size)
 {
 	unsigned int *virt_addr = va;
 	unsigned int temp;
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig
index a07e31b50d3f..59cf0b911898 100644
--- a/arch/tile/Kconfig
+++ b/arch/tile/Kconfig
@@ -28,6 +28,7 @@ config TILE
 	select HAVE_DEBUG_STACKOVERFLOW
 	select ARCH_WANT_FRAME_POINTERS
 	select HAVE_CONTEXT_TRACKING
+	select EDAC_SUPPORT
 
 # FIXME: investigate whether we need/want these options.
 #	select HAVE_IOREMAP_PROT
diff --git a/arch/tile/include/asm/edac.h b/arch/tile/include/asm/edac.h
deleted file mode 100644
index 87fc83eeaffd..000000000000
--- a/arch/tile/include/asm/edac.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2011 Tilera Corporation. 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
- *   as published by the Free Software Foundation, version 2.
- *
- *   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, GOOD TITLE or
- *   NON INFRINGEMENT.  See the GNU General Public License for
- *   more details.
- */
-
-#ifndef _ASM_TILE_EDAC_H
-#define _ASM_TILE_EDAC_H
-
-/* ECC atomic, DMA, SMP and interrupt safe scrub function */
-
-static inline void atomic_scrub(void *va, u32 size)
-{
-	/*
-	 * These is nothing to be done here because CE is
-	 * corrected by the mshim.
-	 */
-	return;
-}
-
-#endif /* _ASM_TILE_EDAC_H */
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 226d5696e1d1..482c160a9fe9 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -143,6 +143,8 @@ config X86
 	select ACPI_LEGACY_TABLES_LOOKUP if ACPI
 	select X86_FEATURE_NAMES if PROC_FS
 	select SRCU
+	select EDAC_SUPPORT
+	select EDAC_ATOMIC_SCRUB
 
 config INSTRUCTION_DECODER
 	def_bool y
diff --git a/arch/x86/include/asm/edac.h b/arch/x86/include/asm/edac.h
index e9b57ecc70c5..cf8fdf83b231 100644
--- a/arch/x86/include/asm/edac.h
+++ b/arch/x86/include/asm/edac.h
@@ -3,7 +3,7 @@
 
 /* ECC atomic, DMA, SMP and interrupt safe scrub function */
 
-static inline void atomic_scrub(void *va, u32 size)
+static inline void edac_atomic_scrub(void *va, u32 size)
 {
 	u32 i, *virt_addr = va;
 
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 2d2530cdf99d..a90e06ac1631 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -2,15 +2,16 @@
 #	EDAC Kconfig
 #	Copyright (c) 2008 Doug Thompson www.softwarebitmaker.com
 #	Licensed and distributed under the GPL
-#
+
+config EDAC_ATOMIC_SCRUB
+	bool
 
 config EDAC_SUPPORT
 	bool
 
 menuconfig EDAC
 	bool "EDAC (Error Detection And Correction) reporting"
-	depends on HAS_IOMEM
-	depends on X86 || PPC || TILE || ARM || EDAC_SUPPORT
+	depends on HAS_IOMEM && EDAC_SUPPORT
 	help
 	  EDAC is designed to report errors in the core system.
 	  These are low-level errors that are reported in the CPU or
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index af3be1914dbb..943ed8cf71b9 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -30,11 +30,16 @@
 #include <linux/bitops.h>
 #include <asm/uaccess.h>
 #include <asm/page.h>
-#include <asm/edac.h>
 #include "edac_core.h"
 #include "edac_module.h"
 #include <ras/ras_event.h>
 
+#ifdef CONFIG_EDAC_ATOMIC_SCRUB
+#include <asm/edac.h>
+#else
+#define edac_atomic_scrub(va, size) do { } while (0)
+#endif
+
 /* lock to memory controller's control array */
 static DEFINE_MUTEX(mem_ctls_mutex);
 static LIST_HEAD(mc_devices);
@@ -874,7 +879,7 @@ static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
 	virt_addr = kmap_atomic(pg);
 
 	/* Perform architecture specific atomic scrub operation */
-	atomic_scrub(virt_addr + offset, size);
+	edac_atomic_scrub(virt_addr + offset, size);
 
 	/* Unmap and complete */
 	kunmap_atomic(virt_addr);
-- 
2.3.5

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH] EDAC: Cleanup atomic_scrub mess
@ 2015-05-21 18:11                         ` Borislav Petkov
  0 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-21 18:11 UTC (permalink / raw)
  To: linux-arm-kernel

From: Borislav Petkov <bp@suse.de>

So first of all, this atomic_scrub() function's naming is bad. It looks
like an atomic_t helper. Change it to edac_atomic_scrub().

The bigger problem is that this function is arch-specific and every new
arch which doesn't necessarily need that functionality still needs to
define it, otherwise EDAC doesn't compile.

So instead of doing that and including arch-specific headers, have each
arch define an EDAC_ATOMIC_SCRUB symbol which can be used in edac_mc.c
for ifdeffery. Much cleaner.

We already are doing this with another symbol - EDAC_SUPPORT. This is
also much cleaner than having CONFIG_EDAC explicitly depend on all the
arches which need/have EDAC support and drivers.

This way I can kill the useless edac.h header in tile too.

Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Chris Metcalf <cmetcalf@ezchip.com>
Cc: x86 at kernel.org
Cc: Doug Thompson <dougthompson@xmission.com>
Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Cc: Markos Chandras <markos.chandras@imgtec.com>
Cc: "Steven J. Hill" <Steven.Hill@imgtec.com>
Cc: "Maciej W. Rozycki" <macro@codesourcery.com>
Cc: linux-arm-kernel at lists.infradead.org
Cc: linux-kernel at vger.kernel.org
Cc: linux-mips at linux-mips.org
Cc: linuxppc-dev at lists.ozlabs.org
Cc: linux-edac at vger.kernel.org
---

Patch is still RFC as I need to compile in on those other arches first.
It should build though - it is simple enough ... famous last words :-P

 arch/arm/Kconfig                |  2 ++
 arch/arm/include/asm/edac.h     |  5 +++--
 arch/mips/Kconfig               |  1 +
 arch/mips/include/asm/edac.h    |  4 ++--
 arch/powerpc/Kconfig            |  2 ++
 arch/powerpc/include/asm/edac.h |  4 ++--
 arch/tile/Kconfig               |  1 +
 arch/tile/include/asm/edac.h    | 29 -----------------------------
 arch/x86/Kconfig                |  2 ++
 arch/x86/include/asm/edac.h     |  2 +-
 drivers/edac/Kconfig            |  7 ++++---
 drivers/edac/edac_mc.c          |  9 +++++++--
 12 files changed, 27 insertions(+), 41 deletions(-)
 delete mode 100644 arch/tile/include/asm/edac.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 45df48ba0b12..325d6f3a596a 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -15,6 +15,8 @@ config ARM
 	select CLONE_BACKWARDS
 	select CPU_PM if (SUSPEND || CPU_IDLE)
 	select DCACHE_WORD_ACCESS if HAVE_EFFICIENT_UNALIGNED_ACCESS
+	select EDAC_SUPPORT
+	select EDAC_ATOMIC_SCRUB
 	select GENERIC_ALLOCATOR
 	select GENERIC_ATOMIC64 if (CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI)
 	select GENERIC_CLOCKEVENTS_BROADCAST if SMP
diff --git a/arch/arm/include/asm/edac.h b/arch/arm/include/asm/edac.h
index 0df7a2c1fc3d..5189fa819b60 100644
--- a/arch/arm/include/asm/edac.h
+++ b/arch/arm/include/asm/edac.h
@@ -18,11 +18,12 @@
 #define ASM_EDAC_H
 /*
  * ECC atomic, DMA, SMP and interrupt safe scrub function.
- * Implements the per arch atomic_scrub() that EDAC use for software
+ * Implements the per arch edac_atomic_scrub() that EDAC use for software
  * ECC scrubbing.  It reads memory and then writes back the original
  * value, allowing the hardware to detect and correct memory errors.
  */
-static inline void atomic_scrub(void *va, u32 size)
+
+static inline void edac_atomic_scrub(void *va, u32 size)
 {
 #if __LINUX_ARM_ARCH__ >= 6
 	unsigned int *virt_addr = va;
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index f5016656494f..b65edf514b40 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -819,6 +819,7 @@ config CAVIUM_OCTEON_SOC
 	select SYS_SUPPORTS_64BIT_KERNEL
 	select SYS_SUPPORTS_BIG_ENDIAN
 	select EDAC_SUPPORT
+	select EDAC_ATOMIC_SCRUB
 	select SYS_SUPPORTS_LITTLE_ENDIAN
 	select SYS_SUPPORTS_HOTPLUG_CPU if CPU_BIG_ENDIAN
 	select SYS_HAS_EARLY_PRINTK
diff --git a/arch/mips/include/asm/edac.h b/arch/mips/include/asm/edac.h
index 94105d3f58f4..980b16527374 100644
--- a/arch/mips/include/asm/edac.h
+++ b/arch/mips/include/asm/edac.h
@@ -5,7 +5,7 @@
 
 /* ECC atomic, DMA, SMP and interrupt safe scrub function */
 
-static inline void atomic_scrub(void *va, u32 size)
+static inline void edac_atomic_scrub(void *va, u32 size)
 {
 	unsigned long *virt_addr = va;
 	unsigned long temp;
@@ -21,7 +21,7 @@ static inline void atomic_scrub(void *va, u32 size)
 
 		__asm__ __volatile__ (
 		"	.set	mips2					\n"
-		"1:	ll	%0, %1		# atomic_scrub		\n"
+		"1:	ll	%0, %1		# edac_atomic_scrub	\n"
 		"	addu	%0, $0					\n"
 		"	sc	%0, %1					\n"
 		"	beqz	%0, 1b					\n"
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 190cc48abc0c..5ef27113b898 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -153,6 +153,8 @@ config PPC
 	select NO_BOOTMEM
 	select HAVE_GENERIC_RCU_GUP
 	select HAVE_PERF_EVENTS_NMI if PPC64
+	select EDAC_SUPPORT
+	select EDAC_ATOMIC_SCRUB
 
 config GENERIC_CSUM
 	def_bool CPU_LITTLE_ENDIAN
diff --git a/arch/powerpc/include/asm/edac.h b/arch/powerpc/include/asm/edac.h
index 6ead88bbfbb8..5571e23d253e 100644
--- a/arch/powerpc/include/asm/edac.h
+++ b/arch/powerpc/include/asm/edac.h
@@ -12,11 +12,11 @@
 #define ASM_EDAC_H
 /*
  * ECC atomic, DMA, SMP and interrupt safe scrub function.
- * Implements the per arch atomic_scrub() that EDAC use for software
+ * Implements the per arch edac_atomic_scrub() that EDAC use for software
  * ECC scrubbing.  It reads memory and then writes back the original
  * value, allowing the hardware to detect and correct memory errors.
  */
-static __inline__ void atomic_scrub(void *va, u32 size)
+static __inline__ void edac_atomic_scrub(void *va, u32 size)
 {
 	unsigned int *virt_addr = va;
 	unsigned int temp;
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig
index a07e31b50d3f..59cf0b911898 100644
--- a/arch/tile/Kconfig
+++ b/arch/tile/Kconfig
@@ -28,6 +28,7 @@ config TILE
 	select HAVE_DEBUG_STACKOVERFLOW
 	select ARCH_WANT_FRAME_POINTERS
 	select HAVE_CONTEXT_TRACKING
+	select EDAC_SUPPORT
 
 # FIXME: investigate whether we need/want these options.
 #	select HAVE_IOREMAP_PROT
diff --git a/arch/tile/include/asm/edac.h b/arch/tile/include/asm/edac.h
deleted file mode 100644
index 87fc83eeaffd..000000000000
--- a/arch/tile/include/asm/edac.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2011 Tilera Corporation. 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
- *   as published by the Free Software Foundation, version 2.
- *
- *   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, GOOD TITLE or
- *   NON INFRINGEMENT.  See the GNU General Public License for
- *   more details.
- */
-
-#ifndef _ASM_TILE_EDAC_H
-#define _ASM_TILE_EDAC_H
-
-/* ECC atomic, DMA, SMP and interrupt safe scrub function */
-
-static inline void atomic_scrub(void *va, u32 size)
-{
-	/*
-	 * These is nothing to be done here because CE is
-	 * corrected by the mshim.
-	 */
-	return;
-}
-
-#endif /* _ASM_TILE_EDAC_H */
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 226d5696e1d1..482c160a9fe9 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -143,6 +143,8 @@ config X86
 	select ACPI_LEGACY_TABLES_LOOKUP if ACPI
 	select X86_FEATURE_NAMES if PROC_FS
 	select SRCU
+	select EDAC_SUPPORT
+	select EDAC_ATOMIC_SCRUB
 
 config INSTRUCTION_DECODER
 	def_bool y
diff --git a/arch/x86/include/asm/edac.h b/arch/x86/include/asm/edac.h
index e9b57ecc70c5..cf8fdf83b231 100644
--- a/arch/x86/include/asm/edac.h
+++ b/arch/x86/include/asm/edac.h
@@ -3,7 +3,7 @@
 
 /* ECC atomic, DMA, SMP and interrupt safe scrub function */
 
-static inline void atomic_scrub(void *va, u32 size)
+static inline void edac_atomic_scrub(void *va, u32 size)
 {
 	u32 i, *virt_addr = va;
 
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 2d2530cdf99d..a90e06ac1631 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -2,15 +2,16 @@
 #	EDAC Kconfig
 #	Copyright (c) 2008 Doug Thompson www.softwarebitmaker.com
 #	Licensed and distributed under the GPL
-#
+
+config EDAC_ATOMIC_SCRUB
+	bool
 
 config EDAC_SUPPORT
 	bool
 
 menuconfig EDAC
 	bool "EDAC (Error Detection And Correction) reporting"
-	depends on HAS_IOMEM
-	depends on X86 || PPC || TILE || ARM || EDAC_SUPPORT
+	depends on HAS_IOMEM && EDAC_SUPPORT
 	help
 	  EDAC is designed to report errors in the core system.
 	  These are low-level errors that are reported in the CPU or
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index af3be1914dbb..943ed8cf71b9 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -30,11 +30,16 @@
 #include <linux/bitops.h>
 #include <asm/uaccess.h>
 #include <asm/page.h>
-#include <asm/edac.h>
 #include "edac_core.h"
 #include "edac_module.h"
 #include <ras/ras_event.h>
 
+#ifdef CONFIG_EDAC_ATOMIC_SCRUB
+#include <asm/edac.h>
+#else
+#define edac_atomic_scrub(va, size) do { } while (0)
+#endif
+
 /* lock to memory controller's control array */
 static DEFINE_MUTEX(mem_ctls_mutex);
 static LIST_HEAD(mc_devices);
@@ -874,7 +879,7 @@ static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
 	virt_addr = kmap_atomic(pg);
 
 	/* Perform architecture specific atomic scrub operation */
-	atomic_scrub(virt_addr + offset, size);
+	edac_atomic_scrub(virt_addr + offset, size);
 
 	/* Unmap and complete */
 	kunmap_atomic(virt_addr);
-- 
2.3.5

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [PATCH v10 3/5] Documentation: Add documentation for the APM X-Gene SoC EDAC DTS binding
  2015-05-18 23:24             ` Loc Ho
@ 2015-05-22  8:02                 ` Arnd Bergmann
  -1 siblings, 0 replies; 48+ messages in thread
From: Arnd Bergmann @ 2015-05-22  8:02 UTC (permalink / raw)
  To: Loc Ho
  Cc: dougthompson-aS9lmoZGLiVWk0Htik3J/w, bp-Gina5bIWoIWzQB+pC5nmwQ,
	mchehab-JPH+aEBZ4P+UEJcrhfAQsw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	jcm-H+wXaHxf7aLQT0dZR+AlfA, patches-qTEPVZfXA3Y

On Monday 18 May 2015 17:24:39 Loc Ho wrote:
> This patch adds documentation for the APM X-Gene SoC EDAC DTS binding.
> 
> Signed-off-by: Loc Ho <lho-qTEPVZfXA3Y@public.gmane.org>

This is starting to look pretty good. One final comment:

> +Example:
> +	csw: csw@7e200000 {
> +		compatible = "syscon";
> +		reg = <0x0 0x7e200000 0x0 0x1000>;
> +	};
> +
> +	mcba: mcba@7e700000 {
> +		compatible = "syscon";
> +		reg = <0x0 0x7e700000 0x0 0x1000>;
> +	};
> +
> +	mcbb: mcbb@7e720000 {
> +		compatible = "syscon";
> +		reg = <0x0 0x7e720000 0x0 0x1000>;
> +	};
> +
> +	efuse: efuse@1054a000 {
> +		compatible = "syscon";
> +		reg = <0x0 0x1054a000 0x0 0x20>;
> +	};

I think it would be helpful to assign a proper compatible string (in
addition o syscon, not replacing it) for each of these, just in case
we ever need to add a proper driver for one of them, or in case a
future chip uses an unmodified edac block with a slightly different
set of fuses or other registers that are referenced here.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 3/5] Documentation: Add documentation for the APM X-Gene SoC EDAC DTS binding
@ 2015-05-22  8:02                 ` Arnd Bergmann
  0 siblings, 0 replies; 48+ messages in thread
From: Arnd Bergmann @ 2015-05-22  8:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 18 May 2015 17:24:39 Loc Ho wrote:
> This patch adds documentation for the APM X-Gene SoC EDAC DTS binding.
> 
> Signed-off-by: Loc Ho <lho@apm.com>

This is starting to look pretty good. One final comment:

> +Example:
> +	csw: csw at 7e200000 {
> +		compatible = "syscon";
> +		reg = <0x0 0x7e200000 0x0 0x1000>;
> +	};
> +
> +	mcba: mcba at 7e700000 {
> +		compatible = "syscon";
> +		reg = <0x0 0x7e700000 0x0 0x1000>;
> +	};
> +
> +	mcbb: mcbb at 7e720000 {
> +		compatible = "syscon";
> +		reg = <0x0 0x7e720000 0x0 0x1000>;
> +	};
> +
> +	efuse: efuse at 1054a000 {
> +		compatible = "syscon";
> +		reg = <0x0 0x1054a000 0x0 0x20>;
> +	};

I think it would be helpful to assign a proper compatible string (in
addition o syscon, not replacing it) for each of these, just in case
we ever need to add a proper driver for one of them, or in case a
future chip uses an unmodified edac block with a slightly different
set of fuses or other registers that are referenced here.

	Arnd

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
  2015-05-18 23:24                 ` Loc Ho
@ 2015-05-22  8:23                     ` Arnd Bergmann
  -1 siblings, 0 replies; 48+ messages in thread
From: Arnd Bergmann @ 2015-05-22  8:23 UTC (permalink / raw)
  To: Loc Ho
  Cc: dougthompson-aS9lmoZGLiVWk0Htik3J/w, bp-Gina5bIWoIWzQB+pC5nmwQ,
	mchehab-JPH+aEBZ4P+UEJcrhfAQsw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	jcm-H+wXaHxf7aLQT0dZR+AlfA, patches-qTEPVZfXA3Y

On Monday 18 May 2015 17:24:40 Loc Ho wrote:
> This patch adds support for the APM X-Gene SoC EDAC driver.
> 
> Signed-off-by: Loc Ho <lho-qTEPVZfXA3Y@public.gmane.org>
> ---
>  drivers/edac/Kconfig      |    7 +
>  drivers/edac/Makefile     |    1 +
>  drivers/edac/xgene_edac.c | 1313 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1321 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/edac/xgene_edac.c

Looks reasonable overall, good job. Just a few details that are left:

> +
> +static int xgene_edac_pcp_rd(struct xgene_edac *edac, u32 reg, u32 *val);
> +static int xgene_edac_pcp_clrbits(struct xgene_edac *edac, u32 reg,
> +				  u32 bits_mask);
> +static int xgene_edac_pcp_setbits(struct xgene_edac *edac, u32 reg,
> +				  u32 bits_mask);
> +static int xgene_edac_csw_rd(struct xgene_edac *edac, u32 reg, u32 *val);
> +static int xgene_edac_mcba_rd(struct xgene_edac *edac, u32 reg, u32 *val);
> +static int xgene_edac_mcbb_rd(struct xgene_edac *edac, u32 reg, u32 *val);
> +static int xgene_edac_efuse_rd(struct xgene_edac *edac, u32 reg, u32 *val);

It's better to avoid any forward declaration for local functions, and instead
reorder the file to move the definition before the first use.

> +static int edac_mc_idx;
> +static int edac_mc_active_mask;
> +static int edac_mc_registered_mask;
> +static DEFINE_MUTEX(xgene_edac_mc_lock);

It would also be best to avoid global variables, but it seems that at
least the edac_mc_idx is needed to work with the EDAC subsystem.
Maybe Boris has an idea for how to avoid it.

> +#ifdef CONFIG_EDAC_DEBUG
> +static ssize_t xgene_edac_mc_err_inject_write(struct file *file,
> +					      const char __user *data,
> +					      size_t count, loff_t *ppos)
> +{
> +	struct mem_ctl_info *mci = file->private_data;
> +	struct xgene_edac_mc_ctx *ctx = mci->pvt_info;
> +	int i;
> +
> +	for (i = 0; i < MCU_MAX_RANK; i++) {
> +		writel(MCU_ESRR_MULTUCERR_MASK | MCU_ESRR_BACKUCERR_MASK |
> +		       MCU_ESRR_DEMANDUCERR_MASK | MCU_ESRR_CERR_MASK,
> +		       ctx->mcu_csr + MCUESRRA0 + i * MCU_RANK_STRIDE);
> +	}
> +	return count;
> +}
> +
> +static const struct file_operations xgene_edac_mc_debug_inject_fops = {
> +	.open = simple_open,
> +	.write = xgene_edac_mc_err_inject_write,
> +	.llseek = generic_file_llseek,
> +};
> +
> +static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
> +{
> +	if (!mci->debugfs)
> +		return;
> +
> +	debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
> +			    &xgene_edac_mc_debug_inject_fops);
> +}
> +#else
> +static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
> +{
> +}
> +#endif

Here, it would be better style to avoid the #ifdef. If you just use

static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
{
	if (! IS_ENABLED(CONFIG_EDAC_DEBUG) || !mci->debugfs)
		return;

the compiler will drop all the debug code when that option is not
set, but will still check the code for correctness, and emit
any warnings that one might want to see.


> +static int xgene_edac_csw_rd(struct xgene_edac *edac, u32 reg, u32 *val)
> +{
> +	if (edac == NULL)
> +		return -EINVAL;
> +
> +	return regmap_read(edac->csw_map, reg, val);
> +}
> +
> +static int xgene_edac_mcba_rd(struct xgene_edac *edac, u32 reg, u32 *val)
> +{
> +	if (edac == NULL)
> +		return -EINVAL;
> +
> +	return regmap_read(edac->mcba_map, reg, val);
> +}
> +
> +static int xgene_edac_mcbb_rd(struct xgene_edac *edac, u32 reg, u32 *val)
> +{
> +	if (edac == NULL)
> +		return -EINVAL;
> +
> +	return regmap_read(edac->mcbb_map, reg, val);
> +}
> +
> +static int xgene_edac_efuse_rd(struct xgene_edac *edac, u32 reg, u32 *val)
> +{
> +	if (edac == NULL)
> +		return -EINVAL;
> +
> +	return regmap_read(edac->efuse_map, reg, val);
> +}

I would drop the NULL check, and just refuse to probe the
device if any of the regmaps are unavailable. You list the
properties as 'required' in DT, so if any of the pointers
are NULL here, that is really a bug somewhere (kernel or DT),
not a user error, so -EINVAL is not appropriate.

Also, these all have very few callers, so just open-code the
regmap_read() instead of having wrappers.

> +
> +static int xgene_edac_pcp_setbits(struct xgene_edac *edac, u32 reg,
> +				  u32 bits_mask)
> +{
> +	u32 val;
> +
> +	if (edac == NULL)
> +		return -EINVAL;
> +
> +	spin_lock(&edac->lock);
> +	val = readl(edac->pcp_csr + reg);
> +	val |= bits_mask;
> +	writel(val, edac->pcp_csr + reg);
> +	spin_unlock(&edac->lock);
> +	return 0;
> +}

If you cache the interrupt masks in the xgene_edac structure, you
can avoid the read-modify-write functions for updating the
register in place.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
@ 2015-05-22  8:23                     ` Arnd Bergmann
  0 siblings, 0 replies; 48+ messages in thread
From: Arnd Bergmann @ 2015-05-22  8:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 18 May 2015 17:24:40 Loc Ho wrote:
> This patch adds support for the APM X-Gene SoC EDAC driver.
> 
> Signed-off-by: Loc Ho <lho@apm.com>
> ---
>  drivers/edac/Kconfig      |    7 +
>  drivers/edac/Makefile     |    1 +
>  drivers/edac/xgene_edac.c | 1313 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1321 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/edac/xgene_edac.c

Looks reasonable overall, good job. Just a few details that are left:

> +
> +static int xgene_edac_pcp_rd(struct xgene_edac *edac, u32 reg, u32 *val);
> +static int xgene_edac_pcp_clrbits(struct xgene_edac *edac, u32 reg,
> +				  u32 bits_mask);
> +static int xgene_edac_pcp_setbits(struct xgene_edac *edac, u32 reg,
> +				  u32 bits_mask);
> +static int xgene_edac_csw_rd(struct xgene_edac *edac, u32 reg, u32 *val);
> +static int xgene_edac_mcba_rd(struct xgene_edac *edac, u32 reg, u32 *val);
> +static int xgene_edac_mcbb_rd(struct xgene_edac *edac, u32 reg, u32 *val);
> +static int xgene_edac_efuse_rd(struct xgene_edac *edac, u32 reg, u32 *val);

It's better to avoid any forward declaration for local functions, and instead
reorder the file to move the definition before the first use.

> +static int edac_mc_idx;
> +static int edac_mc_active_mask;
> +static int edac_mc_registered_mask;
> +static DEFINE_MUTEX(xgene_edac_mc_lock);

It would also be best to avoid global variables, but it seems that at
least the edac_mc_idx is needed to work with the EDAC subsystem.
Maybe Boris has an idea for how to avoid it.

> +#ifdef CONFIG_EDAC_DEBUG
> +static ssize_t xgene_edac_mc_err_inject_write(struct file *file,
> +					      const char __user *data,
> +					      size_t count, loff_t *ppos)
> +{
> +	struct mem_ctl_info *mci = file->private_data;
> +	struct xgene_edac_mc_ctx *ctx = mci->pvt_info;
> +	int i;
> +
> +	for (i = 0; i < MCU_MAX_RANK; i++) {
> +		writel(MCU_ESRR_MULTUCERR_MASK | MCU_ESRR_BACKUCERR_MASK |
> +		       MCU_ESRR_DEMANDUCERR_MASK | MCU_ESRR_CERR_MASK,
> +		       ctx->mcu_csr + MCUESRRA0 + i * MCU_RANK_STRIDE);
> +	}
> +	return count;
> +}
> +
> +static const struct file_operations xgene_edac_mc_debug_inject_fops = {
> +	.open = simple_open,
> +	.write = xgene_edac_mc_err_inject_write,
> +	.llseek = generic_file_llseek,
> +};
> +
> +static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
> +{
> +	if (!mci->debugfs)
> +		return;
> +
> +	debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
> +			    &xgene_edac_mc_debug_inject_fops);
> +}
> +#else
> +static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
> +{
> +}
> +#endif

Here, it would be better style to avoid the #ifdef. If you just use

static void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
{
	if (! IS_ENABLED(CONFIG_EDAC_DEBUG) || !mci->debugfs)
		return;

the compiler will drop all the debug code when that option is not
set, but will still check the code for correctness, and emit
any warnings that one might want to see.


> +static int xgene_edac_csw_rd(struct xgene_edac *edac, u32 reg, u32 *val)
> +{
> +	if (edac == NULL)
> +		return -EINVAL;
> +
> +	return regmap_read(edac->csw_map, reg, val);
> +}
> +
> +static int xgene_edac_mcba_rd(struct xgene_edac *edac, u32 reg, u32 *val)
> +{
> +	if (edac == NULL)
> +		return -EINVAL;
> +
> +	return regmap_read(edac->mcba_map, reg, val);
> +}
> +
> +static int xgene_edac_mcbb_rd(struct xgene_edac *edac, u32 reg, u32 *val)
> +{
> +	if (edac == NULL)
> +		return -EINVAL;
> +
> +	return regmap_read(edac->mcbb_map, reg, val);
> +}
> +
> +static int xgene_edac_efuse_rd(struct xgene_edac *edac, u32 reg, u32 *val)
> +{
> +	if (edac == NULL)
> +		return -EINVAL;
> +
> +	return regmap_read(edac->efuse_map, reg, val);
> +}

I would drop the NULL check, and just refuse to probe the
device if any of the regmaps are unavailable. You list the
properties as 'required' in DT, so if any of the pointers
are NULL here, that is really a bug somewhere (kernel or DT),
not a user error, so -EINVAL is not appropriate.

Also, these all have very few callers, so just open-code the
regmap_read() instead of having wrappers.

> +
> +static int xgene_edac_pcp_setbits(struct xgene_edac *edac, u32 reg,
> +				  u32 bits_mask)
> +{
> +	u32 val;
> +
> +	if (edac == NULL)
> +		return -EINVAL;
> +
> +	spin_lock(&edac->lock);
> +	val = readl(edac->pcp_csr + reg);
> +	val |= bits_mask;
> +	writel(val, edac->pcp_csr + reg);
> +	spin_unlock(&edac->lock);
> +	return 0;
> +}

If you cache the interrupt masks in the xgene_edac structure, you
can avoid the read-modify-write functions for updating the
register in place.

	Arnd

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v10 0/4] edac: Add APM X-Gene SoC EDAC driver
  2015-05-18 23:24 ` Loc Ho
@ 2015-05-22  8:24   ` Arnd Bergmann
  -1 siblings, 0 replies; 48+ messages in thread
From: Arnd Bergmann @ 2015-05-22  8:24 UTC (permalink / raw)
  To: Loc Ho
  Cc: mark.rutland, devicetree, ijc+devicetree, jcm, mchehab, patches,
	robh+dt, bp, dougthompson, linux-arm-kernel, linux-edac

On Monday 18 May 2015 17:24:36 Loc Ho wrote:
> This patch adds support for the APM X-Gene SoC EDAC driver for DT.
> 
> v10:
> * Move regmap for CSW, MCBA, MCBB, and efuse to top driver node and add
>   accessor support functions
> * Change retrieval OF address proerty for subnode to use of_address_to_resource
> * Add COMPILE_TEST to Kconfig for EDAC_XGENE
> * Update DT binding documentation and DT node accordingly
> * Remove DT node naming and number from nodes
> 

Looks good to me. When you address my last comments, please add

Acked-by: Arnd Bergmann <arnd@arndb.de>

	Arnd

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 0/4] edac: Add APM X-Gene SoC EDAC driver
@ 2015-05-22  8:24   ` Arnd Bergmann
  0 siblings, 0 replies; 48+ messages in thread
From: Arnd Bergmann @ 2015-05-22  8:24 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 18 May 2015 17:24:36 Loc Ho wrote:
> This patch adds support for the APM X-Gene SoC EDAC driver for DT.
> 
> v10:
> * Move regmap for CSW, MCBA, MCBB, and efuse to top driver node and add
>   accessor support functions
> * Change retrieval OF address proerty for subnode to use of_address_to_resource
> * Add COMPILE_TEST to Kconfig for EDAC_XGENE
> * Update DT binding documentation and DT node accordingly
> * Remove DT node naming and number from nodes
> 

Looks good to me. When you address my last comments, please add

Acked-by: Arnd Bergmann <arnd@arndb.de>

	Arnd

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
  2015-05-22  8:23                     ` Arnd Bergmann
@ 2015-05-22  8:46                       ` Borislav Petkov
  -1 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-22  8:46 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Loc Ho, dougthompson-aS9lmoZGLiVWk0Htik3J/w,
	mchehab-JPH+aEBZ4P+UEJcrhfAQsw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	jcm-H+wXaHxf7aLQT0dZR+AlfA, patches-qTEPVZfXA3Y

On Fri, May 22, 2015 at 10:23:11AM +0200, Arnd Bergmann wrote:
> > +static int edac_mc_idx;
> > +static int edac_mc_active_mask;
> > +static int edac_mc_registered_mask;
> > +static DEFINE_MUTEX(xgene_edac_mc_lock);
> 
> It would also be best to avoid global variables, but it seems that at
> least the edac_mc_idx is needed to work with the EDAC subsystem.
> Maybe Boris has an idea for how to avoid it.

Right, so AFAICT this is used in

	edac_mc_alloc(edac_mc_idx++

and basically we're supplying the memory controller numbers in the order
we're calling xgene_edac_mc_add(). Perhaps we need to make the memory
controller numbering explicit with DT...

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
@ 2015-05-22  8:46                       ` Borislav Petkov
  0 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-22  8:46 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, May 22, 2015 at 10:23:11AM +0200, Arnd Bergmann wrote:
> > +static int edac_mc_idx;
> > +static int edac_mc_active_mask;
> > +static int edac_mc_registered_mask;
> > +static DEFINE_MUTEX(xgene_edac_mc_lock);
> 
> It would also be best to avoid global variables, but it seems that at
> least the edac_mc_idx is needed to work with the EDAC subsystem.
> Maybe Boris has an idea for how to avoid it.

Right, so AFAICT this is used in

	edac_mc_alloc(edac_mc_idx++

and basically we're supplying the memory controller numbers in the order
we're calling xgene_edac_mc_add(). Perhaps we need to make the memory
controller numbering explicit with DT...

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
  2015-05-22  8:46                       ` Borislav Petkov
@ 2015-05-22  8:55                           ` Arnd Bergmann
  -1 siblings, 0 replies; 48+ messages in thread
From: Arnd Bergmann @ 2015-05-22  8:55 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Loc Ho, dougthompson-aS9lmoZGLiVWk0Htik3J/w,
	mchehab-JPH+aEBZ4P+UEJcrhfAQsw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	jcm-H+wXaHxf7aLQT0dZR+AlfA, patches-qTEPVZfXA3Y

On Friday 22 May 2015 10:46:25 Borislav Petkov wrote:
> On Fri, May 22, 2015 at 10:23:11AM +0200, Arnd Bergmann wrote:
> > > +static int edac_mc_idx;
> > > +static int edac_mc_active_mask;
> > > +static int edac_mc_registered_mask;
> > > +static DEFINE_MUTEX(xgene_edac_mc_lock);
> > 
> > It would also be best to avoid global variables, but it seems that at
> > least the edac_mc_idx is needed to work with the EDAC subsystem.
> > Maybe Boris has an idea for how to avoid it.
> 
> Right, so AFAICT this is used in
> 
>         edac_mc_alloc(edac_mc_idx++
> 
> and basically we're supplying the memory controller numbers in the order
> we're calling xgene_edac_mc_add(). Perhaps we need to make the memory
> controller numbering explicit with DT...

I see now that the number is also used in xgene_edac_mc_is_active()
as an index for the MCBADDRMR register, which seems very fragile,
as it relies on the DT nodes getting seen in the same order by the
driver that the hardware blocks are used in.

Better add an explicit DT property for the index that is defined
to be the same as MCBADDRMR bits, e.g. 

                       edacmc@7e800000 {
                             compatible = "apm,xgene-edac-mc";
                             reg = <0x0 0x7e800000 0x0 0x1000>;
			      memory-controller = <0>;
                       };

or use a reference to the normal memory controller DT node
outside of the EDAC, and put a number in that one.

Is there a strict 1:1 relation between the DIMM slot numbering
on the board and the memory controller number within the EDAC
registers?

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
@ 2015-05-22  8:55                           ` Arnd Bergmann
  0 siblings, 0 replies; 48+ messages in thread
From: Arnd Bergmann @ 2015-05-22  8:55 UTC (permalink / raw)
  To: linux-arm-kernel

On Friday 22 May 2015 10:46:25 Borislav Petkov wrote:
> On Fri, May 22, 2015 at 10:23:11AM +0200, Arnd Bergmann wrote:
> > > +static int edac_mc_idx;
> > > +static int edac_mc_active_mask;
> > > +static int edac_mc_registered_mask;
> > > +static DEFINE_MUTEX(xgene_edac_mc_lock);
> > 
> > It would also be best to avoid global variables, but it seems that at
> > least the edac_mc_idx is needed to work with the EDAC subsystem.
> > Maybe Boris has an idea for how to avoid it.
> 
> Right, so AFAICT this is used in
> 
>         edac_mc_alloc(edac_mc_idx++
> 
> and basically we're supplying the memory controller numbers in the order
> we're calling xgene_edac_mc_add(). Perhaps we need to make the memory
> controller numbering explicit with DT...

I see now that the number is also used in xgene_edac_mc_is_active()
as an index for the MCBADDRMR register, which seems very fragile,
as it relies on the DT nodes getting seen in the same order by the
driver that the hardware blocks are used in.

Better add an explicit DT property for the index that is defined
to be the same as MCBADDRMR bits, e.g. 

                       edacmc at 7e800000 {
                             compatible = "apm,xgene-edac-mc";
                             reg = <0x0 0x7e800000 0x0 0x1000>;
			      memory-controller = <0>;
                       };

or use a reference to the normal memory controller DT node
outside of the EDAC, and put a number in that one.

Is there a strict 1:1 relation between the DIMM slot numbering
on the board and the memory controller number within the EDAC
registers?

	Arnd

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
  2015-05-18 23:24                 ` Loc Ho
@ 2015-05-22  8:59                   ` Arnd Bergmann
  -1 siblings, 0 replies; 48+ messages in thread
From: Arnd Bergmann @ 2015-05-22  8:59 UTC (permalink / raw)
  To: Loc Ho
  Cc: mark.rutland, devicetree, ijc+devicetree, jcm, mchehab, patches,
	robh+dt, bp, dougthompson, linux-arm-kernel, linux-edac

On Monday 18 May 2015 17:24:40 Loc Ho wrote:
> +       tmp_ctx.mcu_id = ((res.start >> 16) & 0xF) / 4;
> 

Just found this line: you are effectively hardcoding the physical
register layout of the chip in the driver here, to get the index
of the memory controller. This is a very bad idea as it prevents
you from reusing this driver for another chip that puts the
same registers in a different place, don't do that.

	Arnd

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
@ 2015-05-22  8:59                   ` Arnd Bergmann
  0 siblings, 0 replies; 48+ messages in thread
From: Arnd Bergmann @ 2015-05-22  8:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 18 May 2015 17:24:40 Loc Ho wrote:
> +       tmp_ctx.mcu_id = ((res.start >> 16) & 0xF) / 4;
> 

Just found this line: you are effectively hardcoding the physical
register layout of the chip in the driver here, to get the index
of the memory controller. This is a very bad idea as it prevents
you from reusing this driver for another chip that puts the
same registers in a different place, don't do that.

	Arnd

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
  2015-05-22  8:46                       ` Borislav Petkov
@ 2015-05-22 18:25                           ` Loc Ho
  -1 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-22 18:25 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Arnd Bergmann, Doug Thompson, Mauro Carvalho Chehab, Rob Herring,
	Mark Rutland, Ian Campbell, linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jon Masters,
	patches-qTEPVZfXA3Y

Hi,

>> > +static int edac_mc_idx;
>> > +static int edac_mc_active_mask;
>> > +static int edac_mc_registered_mask;
>> > +static DEFINE_MUTEX(xgene_edac_mc_lock);
>>
>> It would also be best to avoid global variables, but it seems that at
>> least the edac_mc_idx is needed to work with the EDAC subsystem.
>> Maybe Boris has an idea for how to avoid it.
>
> Right, so AFAICT this is used in
>
>         edac_mc_alloc(edac_mc_idx++
>
> and basically we're supplying the memory controller numbers in the order
> we're calling xgene_edac_mc_add(). Perhaps we need to make the memory
> controller numbering explicit with DT...
>

Let me move them all into the top level driver given that the MC is
now a subnode.

-Loc
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
@ 2015-05-22 18:25                           ` Loc Ho
  0 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-22 18:25 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

>> > +static int edac_mc_idx;
>> > +static int edac_mc_active_mask;
>> > +static int edac_mc_registered_mask;
>> > +static DEFINE_MUTEX(xgene_edac_mc_lock);
>>
>> It would also be best to avoid global variables, but it seems that at
>> least the edac_mc_idx is needed to work with the EDAC subsystem.
>> Maybe Boris has an idea for how to avoid it.
>
> Right, so AFAICT this is used in
>
>         edac_mc_alloc(edac_mc_idx++
>
> and basically we're supplying the memory controller numbers in the order
> we're calling xgene_edac_mc_add(). Perhaps we need to make the memory
> controller numbering explicit with DT...
>

Let me move them all into the top level driver given that the MC is
now a subnode.

-Loc

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
  2015-05-22  8:55                           ` Arnd Bergmann
@ 2015-05-22 18:28                             ` Loc Ho
  -1 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-22 18:28 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Borislav Petkov, Doug Thompson, Mauro Carvalho Chehab,
	Rob Herring, Mark Rutland, Ian Campbell,
	linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jon Masters,
	patches-qTEPVZfXA3Y

Hi,

>> > > +static int edac_mc_idx;
>> > > +static int edac_mc_active_mask;
>> > > +static int edac_mc_registered_mask;
>> > > +static DEFINE_MUTEX(xgene_edac_mc_lock);
>> >
>> > It would also be best to avoid global variables, but it seems that at
>> > least the edac_mc_idx is needed to work with the EDAC subsystem.
>> > Maybe Boris has an idea for how to avoid it.
>>
>> Right, so AFAICT this is used in
>>
>>         edac_mc_alloc(edac_mc_idx++
>>
>> and basically we're supplying the memory controller numbers in the order
>> we're calling xgene_edac_mc_add(). Perhaps we need to make the memory
>> controller numbering explicit with DT...
>
> I see now that the number is also used in xgene_edac_mc_is_active()
> as an index for the MCBADDRMR register, which seems very fragile,
> as it relies on the DT nodes getting seen in the same order by the
> driver that the hardware blocks are used in.
>
> Better add an explicit DT property for the index that is defined
> to be the same as MCBADDRMR bits, e.g.
>
>                        edacmc@7e800000 {
>                              compatible = "apm,xgene-edac-mc";
>                              reg = <0x0 0x7e800000 0x0 0x1000>;
>                               memory-controller = <0>;
>                        };
>
> or use a reference to the normal memory controller DT node
> outside of the EDAC, and put a number in that one.
>
> Is there a strict 1:1 relation between the DIMM slot numbering
> on the board and the memory controller number within the EDAC
> registers?

Yes... The status active bit position is strictly tie to an
controller. Like the suggestion with memory-controller property.

-Loc
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver
@ 2015-05-22 18:28                             ` Loc Ho
  0 siblings, 0 replies; 48+ messages in thread
From: Loc Ho @ 2015-05-22 18:28 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

>> > > +static int edac_mc_idx;
>> > > +static int edac_mc_active_mask;
>> > > +static int edac_mc_registered_mask;
>> > > +static DEFINE_MUTEX(xgene_edac_mc_lock);
>> >
>> > It would also be best to avoid global variables, but it seems that at
>> > least the edac_mc_idx is needed to work with the EDAC subsystem.
>> > Maybe Boris has an idea for how to avoid it.
>>
>> Right, so AFAICT this is used in
>>
>>         edac_mc_alloc(edac_mc_idx++
>>
>> and basically we're supplying the memory controller numbers in the order
>> we're calling xgene_edac_mc_add(). Perhaps we need to make the memory
>> controller numbering explicit with DT...
>
> I see now that the number is also used in xgene_edac_mc_is_active()
> as an index for the MCBADDRMR register, which seems very fragile,
> as it relies on the DT nodes getting seen in the same order by the
> driver that the hardware blocks are used in.
>
> Better add an explicit DT property for the index that is defined
> to be the same as MCBADDRMR bits, e.g.
>
>                        edacmc at 7e800000 {
>                              compatible = "apm,xgene-edac-mc";
>                              reg = <0x0 0x7e800000 0x0 0x1000>;
>                               memory-controller = <0>;
>                        };
>
> or use a reference to the normal memory controller DT node
> outside of the EDAC, and put a number in that one.
>
> Is there a strict 1:1 relation between the DIMM slot numbering
> on the board and the memory controller number within the EDAC
> registers?

Yes... The status active bit position is strictly tie to an
controller. Like the suggestion with memory-controller property.

-Loc

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH] EDAC: Cleanup atomic_scrub mess
  2015-05-21 18:11                         ` Borislav Petkov
@ 2015-05-22 20:13                           ` Chris Metcalf
  -1 siblings, 0 replies; 48+ messages in thread
From: Chris Metcalf @ 2015-05-22 20:13 UTC (permalink / raw)
  To: Borislav Petkov, Loc Ho
  Cc: Mark Rutland, devicetree, . Maciej W. Rozycki, x86, Russell King,
	Markos Chandras, Ian Campbell, Jon Masters,
	Mauro Carvalho Chehab, patches, Ralf Baechle, Rob Herring,
	Benjamin Herrenschmidt, Michael Ellerman, Doug Thompson,
	Steven J. Hill, Paul Mackerras, linux-arm-kernel, linux-edac

On 05/21/2015 02:11 PM, Borislav Petkov wrote:
> From: Borislav Petkov<bp@suse.de>
>
> So first of all, this atomic_scrub() function's naming is bad. It looks
> like an atomic_t helper. Change it to edac_atomic_scrub().
>
> The bigger problem is that this function is arch-specific and every new
> arch which doesn't necessarily need that functionality still needs to
> define it, otherwise EDAC doesn't compile.
>
> So instead of doing that and including arch-specific headers, have each
> arch define an EDAC_ATOMIC_SCRUB symbol which can be used in edac_mc.c
> for ifdeffery. Much cleaner.
>
> We already are doing this with another symbol - EDAC_SUPPORT. This is
> also much cleaner than having CONFIG_EDAC explicitly depend on all the
> arches which need/have EDAC support and drivers.
>
> This way I can kill the useless edac.h header in tile too.
>
> Signed-off-by: Borislav Petkov<bp@suse.de>

Acked-by: Chris Metcalf <cmetcalf@ezchip.com> [for tile]

-- 
Chris Metcalf, EZChip Semiconductor
http://www.ezchip.com

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [RFC PATCH] EDAC: Cleanup atomic_scrub mess
@ 2015-05-22 20:13                           ` Chris Metcalf
  0 siblings, 0 replies; 48+ messages in thread
From: Chris Metcalf @ 2015-05-22 20:13 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/21/2015 02:11 PM, Borislav Petkov wrote:
> From: Borislav Petkov<bp@suse.de>
>
> So first of all, this atomic_scrub() function's naming is bad. It looks
> like an atomic_t helper. Change it to edac_atomic_scrub().
>
> The bigger problem is that this function is arch-specific and every new
> arch which doesn't necessarily need that functionality still needs to
> define it, otherwise EDAC doesn't compile.
>
> So instead of doing that and including arch-specific headers, have each
> arch define an EDAC_ATOMIC_SCRUB symbol which can be used in edac_mc.c
> for ifdeffery. Much cleaner.
>
> We already are doing this with another symbol - EDAC_SUPPORT. This is
> also much cleaner than having CONFIG_EDAC explicitly depend on all the
> arches which need/have EDAC support and drivers.
>
> This way I can kill the useless edac.h header in tile too.
>
> Signed-off-by: Borislav Petkov<bp@suse.de>

Acked-by: Chris Metcalf <cmetcalf@ezchip.com> [for tile]

-- 
Chris Metcalf, EZChip Semiconductor
http://www.ezchip.com

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH] EDAC: Cleanup atomic_scrub mess
  2015-05-22 20:13                           ` Chris Metcalf
@ 2015-05-27 15:52                               ` Borislav Petkov
  -1 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-27 15:52 UTC (permalink / raw)
  To: Chris Metcalf
  Cc: Loc Ho, Doug Thompson, Mauro Carvalho Chehab, Rob Herring,
	Mark Rutland, Ian Campbell, linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jon Masters,
	patches-qTEPVZfXA3Y, Russell King, Ralf Baechle,
	Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	x86-DgEjT+Ai2ygdnm+yROfE0A, Markos Chandras, Steven J. Hill,
	. Maciej W. Rozycki

On Fri, May 22, 2015 at 04:13:22PM -0400, Chris Metcalf wrote:
> On 05/21/2015 02:11 PM, Borislav Petkov wrote:
> >From: Borislav Petkov<bp-l3A5Bk7waGM@public.gmane.org>
> >
> >So first of all, this atomic_scrub() function's naming is bad. It looks
> >like an atomic_t helper. Change it to edac_atomic_scrub().
> >
> >The bigger problem is that this function is arch-specific and every new
> >arch which doesn't necessarily need that functionality still needs to
> >define it, otherwise EDAC doesn't compile.
> >
> >So instead of doing that and including arch-specific headers, have each
> >arch define an EDAC_ATOMIC_SCRUB symbol which can be used in edac_mc.c
> >for ifdeffery. Much cleaner.
> >
> >We already are doing this with another symbol - EDAC_SUPPORT. This is
> >also much cleaner than having CONFIG_EDAC explicitly depend on all the
> >arches which need/have EDAC support and drivers.
> >
> >This way I can kill the useless edac.h header in tile too.
> >
> >Signed-off-by: Borislav Petkov<bp-l3A5Bk7waGM@public.gmane.org>
> 
> Acked-by: Chris Metcalf <cmetcalf-d5a29ZRxExrQT0dZR+AlfA@public.gmane.org> [for tile]

Thanks.

Just to clarify after today's discussion on IRC: this patch doesn't
change current DRAM scrubbing behavior on the relevant arches - it
simply makes the definition of that atomic_scrub thing non-mandatory on
new arches or on those which don't need it.

In the meantime, patch has been build-tested on arm and ppc - the two
I'm missing an ACK for.

:-)

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [RFC PATCH] EDAC: Cleanup atomic_scrub mess
@ 2015-05-27 15:52                               ` Borislav Petkov
  0 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-27 15:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, May 22, 2015 at 04:13:22PM -0400, Chris Metcalf wrote:
> On 05/21/2015 02:11 PM, Borislav Petkov wrote:
> >From: Borislav Petkov<bp@suse.de>
> >
> >So first of all, this atomic_scrub() function's naming is bad. It looks
> >like an atomic_t helper. Change it to edac_atomic_scrub().
> >
> >The bigger problem is that this function is arch-specific and every new
> >arch which doesn't necessarily need that functionality still needs to
> >define it, otherwise EDAC doesn't compile.
> >
> >So instead of doing that and including arch-specific headers, have each
> >arch define an EDAC_ATOMIC_SCRUB symbol which can be used in edac_mc.c
> >for ifdeffery. Much cleaner.
> >
> >We already are doing this with another symbol - EDAC_SUPPORT. This is
> >also much cleaner than having CONFIG_EDAC explicitly depend on all the
> >arches which need/have EDAC support and drivers.
> >
> >This way I can kill the useless edac.h header in tile too.
> >
> >Signed-off-by: Borislav Petkov<bp@suse.de>
> 
> Acked-by: Chris Metcalf <cmetcalf@ezchip.com> [for tile]

Thanks.

Just to clarify after today's discussion on IRC: this patch doesn't
change current DRAM scrubbing behavior on the relevant arches - it
simply makes the definition of that atomic_scrub thing non-mandatory on
new arches or on those which don't need it.

In the meantime, patch has been build-tested on arm and ppc - the two
I'm missing an ACK for.

:-)

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH] EDAC: Cleanup atomic_scrub mess
  2015-05-27 15:52                               ` Borislav Petkov
@ 2015-05-28  2:27                                   ` Michael Ellerman
  -1 siblings, 0 replies; 48+ messages in thread
From: Michael Ellerman @ 2015-05-28  2:27 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Chris Metcalf, Loc Ho, Doug Thompson, Mauro Carvalho Chehab,
	Rob Herring, Mark Rutland, Ian Campbell,
	linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jon Masters,
	patches-qTEPVZfXA3Y, Russell King, Ralf Baechle,
	Benjamin Herrenschmidt, Paul Mackerras,
	x86-DgEjT+Ai2ygdnm+yROfE0A, Markos Chandras, Steven J. Hill,
	. Maciej W. Rozycki

On Wed, 2015-05-27 at 17:52 +0200, Borislav Petkov wrote:
> On Fri, May 22, 2015 at 04:13:22PM -0400, Chris Metcalf wrote:
> > On 05/21/2015 02:11 PM, Borislav Petkov wrote:
> > >From: Borislav Petkov<bp-l3A5Bk7waGM@public.gmane.org>
> > >
> > >So first of all, this atomic_scrub() function's naming is bad. It looks
> > >like an atomic_t helper. Change it to edac_atomic_scrub().
> > >
> > >The bigger problem is that this function is arch-specific and every new
> > >arch which doesn't necessarily need that functionality still needs to
> > >define it, otherwise EDAC doesn't compile.
> > >
> > >So instead of doing that and including arch-specific headers, have each
> > >arch define an EDAC_ATOMIC_SCRUB symbol which can be used in edac_mc.c
> > >for ifdeffery. Much cleaner.
> > >
> > >We already are doing this with another symbol - EDAC_SUPPORT. This is
> > >also much cleaner than having CONFIG_EDAC explicitly depend on all the
> > >arches which need/have EDAC support and drivers.
> > >
> > >This way I can kill the useless edac.h header in tile too.
> > >
> > >Signed-off-by: Borislav Petkov<bp-l3A5Bk7waGM@public.gmane.org>
> > 
> > Acked-by: Chris Metcalf <cmetcalf-d5a29ZRxExrQT0dZR+AlfA@public.gmane.org> [for tile]
> 
> Thanks.
> 
> Just to clarify after today's discussion on IRC: this patch doesn't
> change current DRAM scrubbing behavior on the relevant arches - it
> simply makes the definition of that atomic_scrub thing non-mandatory on
> new arches or on those which don't need it.
> 
> In the meantime, patch has been build-tested on arm and ppc - the two
> I'm missing an ACK for.

I haven't tested it but it looks sane:

Acked-by: Michael Ellerman <mpe-Gsx/Oe8HsFggBc27wqDAHg@public.gmane.org>

cheers


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [RFC PATCH] EDAC: Cleanup atomic_scrub mess
@ 2015-05-28  2:27                                   ` Michael Ellerman
  0 siblings, 0 replies; 48+ messages in thread
From: Michael Ellerman @ 2015-05-28  2:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 2015-05-27 at 17:52 +0200, Borislav Petkov wrote:
> On Fri, May 22, 2015 at 04:13:22PM -0400, Chris Metcalf wrote:
> > On 05/21/2015 02:11 PM, Borislav Petkov wrote:
> > >From: Borislav Petkov<bp@suse.de>
> > >
> > >So first of all, this atomic_scrub() function's naming is bad. It looks
> > >like an atomic_t helper. Change it to edac_atomic_scrub().
> > >
> > >The bigger problem is that this function is arch-specific and every new
> > >arch which doesn't necessarily need that functionality still needs to
> > >define it, otherwise EDAC doesn't compile.
> > >
> > >So instead of doing that and including arch-specific headers, have each
> > >arch define an EDAC_ATOMIC_SCRUB symbol which can be used in edac_mc.c
> > >for ifdeffery. Much cleaner.
> > >
> > >We already are doing this with another symbol - EDAC_SUPPORT. This is
> > >also much cleaner than having CONFIG_EDAC explicitly depend on all the
> > >arches which need/have EDAC support and drivers.
> > >
> > >This way I can kill the useless edac.h header in tile too.
> > >
> > >Signed-off-by: Borislav Petkov<bp@suse.de>
> > 
> > Acked-by: Chris Metcalf <cmetcalf@ezchip.com> [for tile]
> 
> Thanks.
> 
> Just to clarify after today's discussion on IRC: this patch doesn't
> change current DRAM scrubbing behavior on the relevant arches - it
> simply makes the definition of that atomic_scrub thing non-mandatory on
> new arches or on those which don't need it.
> 
> In the meantime, patch has been build-tested on arm and ppc - the two
> I'm missing an ACK for.

I haven't tested it but it looks sane:

Acked-by: Michael Ellerman <mpe@ellerman.id.au>

cheers

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH] EDAC: Cleanup atomic_scrub mess
  2015-05-21 18:11                         ` Borislav Petkov
@ 2015-05-28 12:34                             ` Russell King - ARM Linux
  -1 siblings, 0 replies; 48+ messages in thread
From: Russell King - ARM Linux @ 2015-05-28 12:34 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Loc Ho, Doug Thompson, Mauro Carvalho Chehab, Rob Herring,
	Mark Rutland, Ian Campbell, linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jon Masters,
	patches-qTEPVZfXA3Y, Ralf Baechle, Benjamin Herrenschmidt,
	Paul Mackerras, Michael Ellerman, Chris Metcalf,
	x86-DgEjT+Ai2ygdnm+yROfE0A, Markos Chandras, Steven J. Hill,
	. Maciej W. Rozycki

On Thu, May 21, 2015 at 08:11:57PM +0200, Borislav Petkov wrote:
> From: Borislav Petkov <bp-l3A5Bk7waGM@public.gmane.org>
> 
> So first of all, this atomic_scrub() function's naming is bad. It looks
> like an atomic_t helper. Change it to edac_atomic_scrub().
> 
> The bigger problem is that this function is arch-specific and every new
> arch which doesn't necessarily need that functionality still needs to
> define it, otherwise EDAC doesn't compile.
> 
> So instead of doing that and including arch-specific headers, have each
> arch define an EDAC_ATOMIC_SCRUB symbol which can be used in edac_mc.c
> for ifdeffery. Much cleaner.
> 
> We already are doing this with another symbol - EDAC_SUPPORT. This is
> also much cleaner than having CONFIG_EDAC explicitly depend on all the
> arches which need/have EDAC support and drivers.
> 
> This way I can kill the useless edac.h header in tile too.
> 
> Signed-off-by: Borislav Petkov <bp-l3A5Bk7waGM@public.gmane.org>
> Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>

Acked-by: Russell King <rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>

> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 45df48ba0b12..325d6f3a596a 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -15,6 +15,8 @@ config ARM
>  	select CLONE_BACKWARDS
>  	select CPU_PM if (SUSPEND || CPU_IDLE)
>  	select DCACHE_WORD_ACCESS if HAVE_EFFICIENT_UNALIGNED_ACCESS
> +	select EDAC_SUPPORT
> +	select EDAC_ATOMIC_SCRUB

I wonder if it would make sense to conditionalise EDAC_SUPPORT on...
		if CPU_32v6 || CPU_32v7

since presumably its not useful for older architectures (certainly
edac_atomic_scrub() is a no-op for earlier arches.)

>  	select GENERIC_ALLOCATOR
>  	select GENERIC_ATOMIC64 if (CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI)
>  	select GENERIC_CLOCKEVENTS_BROADCAST if SMP
> diff --git a/arch/arm/include/asm/edac.h b/arch/arm/include/asm/edac.h
> index 0df7a2c1fc3d..5189fa819b60 100644
> --- a/arch/arm/include/asm/edac.h
> +++ b/arch/arm/include/asm/edac.h
> @@ -18,11 +18,12 @@
>  #define ASM_EDAC_H
>  /*
>   * ECC atomic, DMA, SMP and interrupt safe scrub function.
> - * Implements the per arch atomic_scrub() that EDAC use for software
> + * Implements the per arch edac_atomic_scrub() that EDAC use for software
>   * ECC scrubbing.  It reads memory and then writes back the original
>   * value, allowing the hardware to detect and correct memory errors.
>   */
> -static inline void atomic_scrub(void *va, u32 size)
> +
> +static inline void edac_atomic_scrub(void *va, u32 size)
>  {
>  #if __LINUX_ARM_ARCH__ >= 6
>  	unsigned int *virt_addr = va;

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [RFC PATCH] EDAC: Cleanup atomic_scrub mess
@ 2015-05-28 12:34                             ` Russell King - ARM Linux
  0 siblings, 0 replies; 48+ messages in thread
From: Russell King - ARM Linux @ 2015-05-28 12:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, May 21, 2015 at 08:11:57PM +0200, Borislav Petkov wrote:
> From: Borislav Petkov <bp@suse.de>
> 
> So first of all, this atomic_scrub() function's naming is bad. It looks
> like an atomic_t helper. Change it to edac_atomic_scrub().
> 
> The bigger problem is that this function is arch-specific and every new
> arch which doesn't necessarily need that functionality still needs to
> define it, otherwise EDAC doesn't compile.
> 
> So instead of doing that and including arch-specific headers, have each
> arch define an EDAC_ATOMIC_SCRUB symbol which can be used in edac_mc.c
> for ifdeffery. Much cleaner.
> 
> We already are doing this with another symbol - EDAC_SUPPORT. This is
> also much cleaner than having CONFIG_EDAC explicitly depend on all the
> arches which need/have EDAC support and drivers.
> 
> This way I can kill the useless edac.h header in tile too.
> 
> Signed-off-by: Borislav Petkov <bp@suse.de>
> Cc: Russell King <linux@arm.linux.org.uk>

Acked-by: Russell King <rmk+kernel@arm.linux.org.uk>

> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 45df48ba0b12..325d6f3a596a 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -15,6 +15,8 @@ config ARM
>  	select CLONE_BACKWARDS
>  	select CPU_PM if (SUSPEND || CPU_IDLE)
>  	select DCACHE_WORD_ACCESS if HAVE_EFFICIENT_UNALIGNED_ACCESS
> +	select EDAC_SUPPORT
> +	select EDAC_ATOMIC_SCRUB

I wonder if it would make sense to conditionalise EDAC_SUPPORT on...
		if CPU_32v6 || CPU_32v7

since presumably its not useful for older architectures (certainly
edac_atomic_scrub() is a no-op for earlier arches.)

>  	select GENERIC_ALLOCATOR
>  	select GENERIC_ATOMIC64 if (CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI)
>  	select GENERIC_CLOCKEVENTS_BROADCAST if SMP
> diff --git a/arch/arm/include/asm/edac.h b/arch/arm/include/asm/edac.h
> index 0df7a2c1fc3d..5189fa819b60 100644
> --- a/arch/arm/include/asm/edac.h
> +++ b/arch/arm/include/asm/edac.h
> @@ -18,11 +18,12 @@
>  #define ASM_EDAC_H
>  /*
>   * ECC atomic, DMA, SMP and interrupt safe scrub function.
> - * Implements the per arch atomic_scrub() that EDAC use for software
> + * Implements the per arch edac_atomic_scrub() that EDAC use for software
>   * ECC scrubbing.  It reads memory and then writes back the original
>   * value, allowing the hardware to detect and correct memory errors.
>   */
> -static inline void atomic_scrub(void *va, u32 size)
> +
> +static inline void edac_atomic_scrub(void *va, u32 size)
>  {
>  #if __LINUX_ARM_ARCH__ >= 6
>  	unsigned int *virt_addr = va;

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH] EDAC: Cleanup atomic_scrub mess
  2015-05-28 12:34                             ` Russell King - ARM Linux
@ 2015-05-28 13:37                                 ` Borislav Petkov
  -1 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-28 13:37 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Loc Ho, Doug Thompson, Mauro Carvalho Chehab, Rob Herring,
	Mark Rutland, Ian Campbell, linux-edac-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Jon Masters,
	patches-qTEPVZfXA3Y, Ralf Baechle, Benjamin Herrenschmidt,
	Paul Mackerras, Michael Ellerman, Chris Metcalf,
	x86-DgEjT+Ai2ygdnm+yROfE0A, Markos Chandras, Steven J. Hill,
	. Maciej W. Rozycki

On Thu, May 28, 2015 at 01:34:49PM +0100, Russell King - ARM Linux wrote:
> Acked-by: Russell King <rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>

Thanks!

I've got all the ACKs now :-)

> 
> > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> > index 45df48ba0b12..325d6f3a596a 100644
> > --- a/arch/arm/Kconfig
> > +++ b/arch/arm/Kconfig
> > @@ -15,6 +15,8 @@ config ARM
> >  	select CLONE_BACKWARDS
> >  	select CPU_PM if (SUSPEND || CPU_IDLE)
> >  	select DCACHE_WORD_ACCESS if HAVE_EFFICIENT_UNALIGNED_ACCESS
> > +	select EDAC_SUPPORT
> > +	select EDAC_ATOMIC_SCRUB
> 
> I wonder if it would make sense to conditionalise EDAC_SUPPORT on...
> 		if CPU_32v6 || CPU_32v7

I guess you can.

Especially if no newer 32-bit ARM would need the scrubbing anymore.
Disadvantage is, if turns out CPU_32v8 (would there even be v8, no
idea...) and newer would need it after all, you'd have to explicitly
enable it.

Thanks.

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [RFC PATCH] EDAC: Cleanup atomic_scrub mess
@ 2015-05-28 13:37                                 ` Borislav Petkov
  0 siblings, 0 replies; 48+ messages in thread
From: Borislav Petkov @ 2015-05-28 13:37 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, May 28, 2015 at 01:34:49PM +0100, Russell King - ARM Linux wrote:
> Acked-by: Russell King <rmk+kernel@arm.linux.org.uk>

Thanks!

I've got all the ACKs now :-)

> 
> > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> > index 45df48ba0b12..325d6f3a596a 100644
> > --- a/arch/arm/Kconfig
> > +++ b/arch/arm/Kconfig
> > @@ -15,6 +15,8 @@ config ARM
> >  	select CLONE_BACKWARDS
> >  	select CPU_PM if (SUSPEND || CPU_IDLE)
> >  	select DCACHE_WORD_ACCESS if HAVE_EFFICIENT_UNALIGNED_ACCESS
> > +	select EDAC_SUPPORT
> > +	select EDAC_ATOMIC_SCRUB
> 
> I wonder if it would make sense to conditionalise EDAC_SUPPORT on...
> 		if CPU_32v6 || CPU_32v7

I guess you can.

Especially if no newer 32-bit ARM would need the scrubbing anymore.
Disadvantage is, if turns out CPU_32v8 (would there even be v8, no
idea...) and newer would need it after all, you'd have to explicitly
enable it.

Thanks.

-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--

^ permalink raw reply	[flat|nested] 48+ messages in thread

end of thread, other threads:[~2015-05-28 13:37 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-05-18 23:24 [PATCH v10 0/4] edac: Add APM X-Gene SoC EDAC driver Loc Ho
2015-05-18 23:24 ` Loc Ho
     [not found] ` <1431991481-25684-1-git-send-email-lho-qTEPVZfXA3Y@public.gmane.org>
2015-05-18 23:24   ` [PATCH v10 1/5] arm64: Enable EDAC on ARM64 Loc Ho
2015-05-18 23:24     ` Loc Ho
     [not found]     ` <1431991481-25684-2-git-send-email-lho-qTEPVZfXA3Y@public.gmane.org>
2015-05-18 23:24       ` [PATCH v10 2/5] MAINTAINERS: Add entry for APM X-Gene SoC EDAC driver Loc Ho
2015-05-18 23:24         ` Loc Ho
     [not found]         ` <1431991481-25684-3-git-send-email-lho-qTEPVZfXA3Y@public.gmane.org>
2015-05-18 23:24           ` [PATCH v10 3/5] Documentation: Add documentation for the APM X-Gene SoC EDAC DTS binding Loc Ho
2015-05-18 23:24             ` Loc Ho
     [not found]             ` <1431991481-25684-4-git-send-email-lho-qTEPVZfXA3Y@public.gmane.org>
2015-05-18 23:24               ` [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver Loc Ho
2015-05-18 23:24                 ` Loc Ho
     [not found]                 ` <1431991481-25684-5-git-send-email-lho-qTEPVZfXA3Y@public.gmane.org>
2015-05-18 23:24                   ` [PATCH v10 5/5] arm64: Add APM X-Gene SoC EDAC DTS entries Loc Ho
2015-05-18 23:24                     ` Loc Ho
2015-05-22  8:23                   ` [PATCH v10 4/5] edac: Add APM X-Gene SoC EDAC driver Arnd Bergmann
2015-05-22  8:23                     ` Arnd Bergmann
2015-05-22  8:46                     ` Borislav Petkov
2015-05-22  8:46                       ` Borislav Petkov
     [not found]                       ` <20150522084625.GE23022-fF5Pk5pvG8Y@public.gmane.org>
2015-05-22  8:55                         ` Arnd Bergmann
2015-05-22  8:55                           ` Arnd Bergmann
2015-05-22 18:28                           ` Loc Ho
2015-05-22 18:28                             ` Loc Ho
2015-05-22 18:25                         ` Loc Ho
2015-05-22 18:25                           ` Loc Ho
2015-05-22  8:59                 ` Arnd Bergmann
2015-05-22  8:59                   ` Arnd Bergmann
2015-05-22  8:02               ` [PATCH v10 3/5] Documentation: Add documentation for the APM X-Gene SoC EDAC DTS binding Arnd Bergmann
2015-05-22  8:02                 ` Arnd Bergmann
2015-05-19 17:03       ` [PATCH v10 1/5] arm64: Enable EDAC on ARM64 Borislav Petkov
2015-05-19 17:03         ` Borislav Petkov
     [not found]         ` <20150519170308.GL4641-fF5Pk5pvG8Y@public.gmane.org>
2015-05-19 19:57           ` Loc Ho
2015-05-19 19:57             ` Loc Ho
     [not found]             ` <CAPw-ZT=mTpt_vz+e=rM5ywK8feNKkJQrpEWg=noRCziJ0840TQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-05-19 20:33               ` Borislav Petkov
2015-05-19 20:33                 ` Borislav Petkov
     [not found]                 ` <20150519203336.GP4641-fF5Pk5pvG8Y@public.gmane.org>
2015-05-21 18:07                   ` Borislav Petkov
2015-05-21 18:07                     ` Borislav Petkov
     [not found]                     ` <20150521180719.GE3689-fF5Pk5pvG8Y@public.gmane.org>
2015-05-21 18:11                       ` [RFC PATCH] EDAC: Cleanup atomic_scrub mess Borislav Petkov
2015-05-21 18:11                         ` Borislav Petkov
2015-05-22 20:13                         ` Chris Metcalf
2015-05-22 20:13                           ` Chris Metcalf
     [not found]                           ` <555F8DE2.4060402-d5a29ZRxExrQT0dZR+AlfA@public.gmane.org>
2015-05-27 15:52                             ` Borislav Petkov
2015-05-27 15:52                               ` Borislav Petkov
     [not found]                               ` <20150527155219.GB19407-fF5Pk5pvG8Y@public.gmane.org>
2015-05-28  2:27                                 ` Michael Ellerman
2015-05-28  2:27                                   ` Michael Ellerman
     [not found]                         ` <20150521181157.GF3689-fF5Pk5pvG8Y@public.gmane.org>
2015-05-28 12:34                           ` Russell King - ARM Linux
2015-05-28 12:34                             ` Russell King - ARM Linux
     [not found]                             ` <20150528123448.GA2067-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org>
2015-05-28 13:37                               ` Borislav Petkov
2015-05-28 13:37                                 ` Borislav Petkov
2015-05-22  8:24 ` [PATCH v10 0/4] edac: Add APM X-Gene SoC EDAC driver Arnd Bergmann
2015-05-22  8:24   ` Arnd Bergmann

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.