This version refactors how the EDAC is configured for Arria10 since
the ECC hardware is significantly different than Cyclone5 and Arria5.
Since all the IRQs are shared, a new probe function based on the
xgene codebase was used.
[PATCHv3 1/9] EDAC: Altera L2 Kconfig change from select to depends
[PATCHv3 2/9] EDAC, altera: Move Device structs and defines to
[PATCHv3 3/9] EDAC, altera: Remove platform device from check_deps()
[PATCHv3 4/9] EDAC, altera: Abstract ECC Enable Mask in check_deps()
[PATCHv3 5/9] EDAC, altera: Add register offset for ECC Error Inject
[PATCHv3 6/9] Documentation: dt: socfpga: Add Altera Arria10 L2
[PATCHv3 7/9] EDAC, altera: Addition of Arria10 L2 Cache ECC
[PATCHv3 8/9] ARM: socfpga: Enable Arria10 L2 cache ECC on startup
[PATCHv3 9/9] ARM: dts: Add Altera Arria10 L2 Cache EDAC devicetree
From: Thor Thayer <[email protected]>
Force L2 cache dependency instead of forcing selection of
L2 cache.
Signed-off-by: Thor Thayer <[email protected]>
---
v2/3 No change
---
drivers/edac/Kconfig | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 37755e6..6ca7474 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -378,12 +378,11 @@ config EDAC_ALTERA
config EDAC_ALTERA_L2C
bool "Altera L2 Cache ECC"
- depends on EDAC_ALTERA=y
- select CACHE_L2X0
+ depends on EDAC_ALTERA=y && CACHE_L2X0
help
Support for error detection and correction on the
Altera L2 cache Memory for Altera SoCs. This option
- requires L2 cache so it will force that selection.
+ requires L2 cache.
config EDAC_ALTERA_OCRAM
bool "Altera On-Chip RAM ECC"
--
1.7.9.5
From: Thor Thayer <[email protected]>
Move the device structs and defines to altera_edac.h in preparation
for adding the Arria10 L2 cache ECC.
Signed-off-by: Thor Thayer <[email protected]>
---
v2: Split original patch into smaller patches. Move private data
and defines into header file.
v3: Commented description above defines.
---
drivers/edac/altera_edac.c | 43 -------------------------------------------
drivers/edac/altera_edac.h | 44 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 44 insertions(+), 43 deletions(-)
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index 63e4209..eee7a39 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -78,27 +78,6 @@ static const struct altr_sdram_prv_data a10_data = {
.ue_set_mask = A10_DIAGINT_TDERRA_MASK,
};
-/************************** EDAC Device Defines **************************/
-
-/* OCRAM ECC Management Group Defines */
-#define ALTR_MAN_GRP_OCRAM_ECC_OFFSET 0x04
-#define ALTR_OCR_ECC_EN BIT(0)
-#define ALTR_OCR_ECC_INJS BIT(1)
-#define ALTR_OCR_ECC_INJD BIT(2)
-#define ALTR_OCR_ECC_SERR BIT(3)
-#define ALTR_OCR_ECC_DERR BIT(4)
-
-/* L2 ECC Management Group Defines */
-#define ALTR_MAN_GRP_L2_ECC_OFFSET 0x00
-#define ALTR_L2_ECC_EN BIT(0)
-#define ALTR_L2_ECC_INJS BIT(1)
-#define ALTR_L2_ECC_INJD BIT(2)
-
-#define ALTR_UE_TRIGGER_CHAR 'U' /* Trigger for UE */
-#define ALTR_TRIGGER_READ_WRD_CNT 32 /* Line size x 4 */
-#define ALTR_TRIG_OCRAM_BYTE_SIZE 128 /* Line size x 4 */
-#define ALTR_TRIG_L2C_BYTE_SIZE 4096 /* Full Page */
-
/*********************** EDAC Memory Controller Functions ****************/
/* The SDRAM controller uses the EDAC Memory Controller framework. */
@@ -571,28 +550,6 @@ module_platform_driver(altr_edac_driver);
const struct edac_device_prv_data ocramecc_data;
const struct edac_device_prv_data l2ecc_data;
-struct edac_device_prv_data {
- int (*setup)(struct platform_device *pdev, void __iomem *base);
- int ce_clear_mask;
- int ue_clear_mask;
- char dbgfs_name[20];
- void * (*alloc_mem)(size_t size, void **other);
- void (*free_mem)(void *p, size_t size, void *other);
- int ecc_enable_mask;
- int ce_set_mask;
- int ue_set_mask;
- int trig_alloc_sz;
-};
-
-struct altr_edac_device_dev {
- void __iomem *base;
- int sb_irq;
- int db_irq;
- const struct edac_device_prv_data *data;
- struct dentry *debugfs_dir;
- char *edac_dev_name;
-};
-
static irqreturn_t altr_edac_device_handler(int irq, void *dev_id)
{
irqreturn_t ret_value = IRQ_NONE;
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h
index 953077d..993bb0a 100644
--- a/drivers/edac/altera_edac.h
+++ b/drivers/edac/altera_edac.h
@@ -195,4 +195,48 @@ struct altr_sdram_mc_data {
const struct altr_sdram_prv_data *data;
};
+/************************** EDAC Device Defines **************************/
+/***** General Device Trigger Defines *****/
+#define ALTR_UE_TRIGGER_CHAR 'U' /* Trigger for UE */
+#define ALTR_TRIGGER_READ_WRD_CNT 32 /* Line size x 4 */
+#define ALTR_TRIG_OCRAM_BYTE_SIZE 128 /* Line size x 4 */
+#define ALTR_TRIG_L2C_BYTE_SIZE 4096 /* Full Page */
+
+/******* Cyclone5 and Arria5 Defines *******/
+/* OCRAM ECC Management Group Defines */
+#define ALTR_MAN_GRP_OCRAM_ECC_OFFSET 0x04
+#define ALTR_OCR_ECC_EN BIT(0)
+#define ALTR_OCR_ECC_INJS BIT(1)
+#define ALTR_OCR_ECC_INJD BIT(2)
+#define ALTR_OCR_ECC_SERR BIT(3)
+#define ALTR_OCR_ECC_DERR BIT(4)
+
+/* L2 ECC Management Group Defines */
+#define ALTR_MAN_GRP_L2_ECC_OFFSET 0x00
+#define ALTR_L2_ECC_EN BIT(0)
+#define ALTR_L2_ECC_INJS BIT(1)
+#define ALTR_L2_ECC_INJD BIT(2)
+
+struct edac_device_prv_data {
+ int (*setup)(struct platform_device *pdev, void __iomem *base);
+ int ce_clear_mask;
+ int ue_clear_mask;
+ char dbgfs_name[20];
+ void * (*alloc_mem)(size_t size, void **other);
+ void (*free_mem)(void *p, size_t size, void *other);
+ int ecc_enable_mask;
+ int ce_set_mask;
+ int ue_set_mask;
+ int trig_alloc_sz;
+};
+
+struct altr_edac_device_dev {
+ void __iomem *base;
+ int sb_irq;
+ int db_irq;
+ const struct edac_device_prv_data *data;
+ struct dentry *debugfs_dir;
+ char *edac_dev_name;
+};
+
#endif /* #ifndef _ALTERA_EDAC_H */
--
1.7.9.5
From: Thor Thayer <[email protected]>
In preparation for the Arria10 peripheral ECCs, the
platform device parameter is removed from the check_deps()
functions because it is not needed and makes the Arria10
check_deps() cleaner.
Signed-off-by: Thor Thayer <[email protected]>
---
v3: This change added.
---
drivers/edac/altera_edac.c | 10 +++++-----
drivers/edac/altera_edac.h | 4 +++-
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index eee7a39..e20a045 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -746,7 +746,7 @@ static int altr_edac_device_probe(struct platform_device *pdev)
/* Check specific dependencies for the module */
if (drvdata->data->setup) {
- res = drvdata->data->setup(pdev, drvdata->base);
+ res = drvdata->data->setup(drvdata);
if (res)
goto fail1;
}
@@ -856,9 +856,9 @@ static void ocram_free_mem(void *p, size_t size, void *other)
* Can't turn on ECC here because accessing un-initialized
* memory will cause CE/UE errors possibly causing an ABORT.
*/
-static int altr_ocram_check_deps(struct platform_device *pdev,
- void __iomem *base)
+static int altr_ocram_check_deps(struct altr_edac_device_dev *device)
{
+ void __iomem *base = device->base;
if (readl(base) & ALTR_OCR_ECC_EN)
return 0;
@@ -923,9 +923,9 @@ static void l2_free_mem(void *p, size_t size, void *other)
* Bail if ECC is not enabled.
* Note that L2 Cache Enable is forced at build time.
*/
-static int altr_l2_check_deps(struct platform_device *pdev,
- void __iomem *base)
+static int altr_l2_check_deps(struct altr_edac_device_dev *device)
{
+ void __iomem *base = device->base;
if (readl(base) & ALTR_L2_ECC_EN)
return 0;
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h
index 993bb0a..32c798a 100644
--- a/drivers/edac/altera_edac.h
+++ b/drivers/edac/altera_edac.h
@@ -217,8 +217,10 @@ struct altr_sdram_mc_data {
#define ALTR_L2_ECC_INJS BIT(1)
#define ALTR_L2_ECC_INJD BIT(2)
+struct altr_edac_device_dev;
+
struct edac_device_prv_data {
- int (*setup)(struct platform_device *pdev, void __iomem *base);
+ int (*setup)(struct altr_edac_device_dev *device);
int ce_clear_mask;
int ue_clear_mask;
char dbgfs_name[20];
--
1.7.9.5
From: Thor Thayer <[email protected]>
Add the device tree bindings needed to support the Altera L2
cache on the Arria10 chip. Since all the peripherals share
IRQs, the IRQ fields are now in the ecc_manager.
Signed-off-by: Thor Thayer <[email protected]>
---
v2 Correct spelling of Arria10 in patch title.
v3 Major restructuring change for ecc_manager to include IRQs
---
.../bindings/arm/altera/socfpga-eccmgr.txt | 40 ++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-eccmgr.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-eccmgr.txt
index 885f93d..37ff9bf 100644
--- a/Documentation/devicetree/bindings/arm/altera/socfpga-eccmgr.txt
+++ b/Documentation/devicetree/bindings/arm/altera/socfpga-eccmgr.txt
@@ -3,6 +3,7 @@ This driver uses the EDAC framework to implement the SOCFPGA ECC Manager.
The ECC Manager counts and corrects single bit errors and counts/handles
double bit errors which are uncorrectable.
+Cyclone5 and Arria5 ECC Manager
Required Properties:
- compatible : Should be "altr,socfpga-ecc-manager"
- #address-cells: must be 1
@@ -47,3 +48,42 @@ Example:
interrupts = <0 178 1>, <0 179 1>;
};
};
+
+Arria10 SoCFPGA ECC Manager
+The Arria10 SoC ECC Manager handles the IRQs for each peripheral
+in a shared register instead of individual IRQs like the Cyclone5
+and Arria5. Therefore the device tree is different as well.
+
+Required Properties:
+- compatible : Should be "altr,socfpga-a10-ecc-manager"
+- altr,sysgr-syscon : phandle to Arria10 System Manager Block
+ containing the ECC manager registers.
+- #address-cells: must be 1
+- #size-cells: must be 1
+- interrupts : Should be single bit error interrupt, then double bit error
+ interrupt. Note the rising edge type.
+- ranges : standard definition, should translate from local addresses
+
+Subcomponents:
+
+L2 Cache ECC
+Required Properties:
+- compatible : Should be "altr,socfpga-a10-l2-ecc"
+- reg : Address and size for ECC error interrupt clear registers.
+
+Example:
+
+ eccmgr: eccmgr@ffd06000 {
+ compatible = "altr,socfpga-a10-ecc-manager";
+ altr,sysmgr-syscon = <&sysmgr>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupts = <0 2 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 IRQ_TYPE_LEVEL_HIGH>;
+ ranges;
+
+ l2-ecc@ffd06010 {
+ compatible = "altr,socfpga-a10-l2-ecc";
+ reg = <0xffd06010 0x4>;
+ };
+ };
--
1.7.9.5
From: Thor Thayer <[email protected]>
In preparation for the Arria10 peripheral ECCs, the
ECC Enable mask is used in place of hard coded masks
in the check dependency functions.
Signed-off-by: Thor Thayer <[email protected]>
---
v3: This change added.
---
drivers/edac/altera_edac.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index e20a045..0dbfa47 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -859,7 +859,9 @@ static void ocram_free_mem(void *p, size_t size, void *other)
static int altr_ocram_check_deps(struct altr_edac_device_dev *device)
{
void __iomem *base = device->base;
- if (readl(base) & ALTR_OCR_ECC_EN)
+ const struct edac_device_prv_data *prv = device->data;
+
+ if (readl(base) & prv->ecc_enable_mask)
return 0;
edac_printk(KERN_ERR, EDAC_DEVICE,
@@ -926,7 +928,10 @@ static void l2_free_mem(void *p, size_t size, void *other)
static int altr_l2_check_deps(struct altr_edac_device_dev *device)
{
void __iomem *base = device->base;
- if (readl(base) & ALTR_L2_ECC_EN)
+ const struct edac_device_prv_data *prv = device->data;
+
+ if ((readl(base) & prv->ecc_enable_mask) ==
+ prv->ecc_enable_mask)
return 0;
edac_printk(KERN_ERR, EDAC_DEVICE,
--
1.7.9.5
From: Thor Thayer <[email protected]>
Enable ECC for Arria10 L2 cache on machine startup. The ECC has to be
enabled before data is stored in memory otherwise the ECC will fail
on reads.
Use DT_MACHINE to select Arria10 L2 cache function.
Signed-off-by: Thor Thayer <[email protected]>
Acked-by: Dinh Nguyen <[email protected]>
---
v2: Split into 2 separate functions selected with DT_MACHINE.
v3: No change
---
arch/arm/mach-socfpga/core.h | 1 +
arch/arm/mach-socfpga/l2_cache.c | 49 ++++++++++++++++++++++++++++++++++++++
arch/arm/mach-socfpga/socfpga.c | 10 +++++++-
3 files changed, 59 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-socfpga/core.h b/arch/arm/mach-socfpga/core.h
index 575195b..bfbc78d 100644
--- a/arch/arm/mach-socfpga/core.h
+++ b/arch/arm/mach-socfpga/core.h
@@ -38,6 +38,7 @@ extern void socfpga_init_clocks(void);
extern void socfpga_sysmgr_init(void);
void socfpga_init_l2_ecc(void);
void socfpga_init_ocram_ecc(void);
+void socfpga_init_arria10_l2_ecc(void);
extern void __iomem *sys_manager_base_addr;
extern void __iomem *rst_manager_base_addr;
diff --git a/arch/arm/mach-socfpga/l2_cache.c b/arch/arm/mach-socfpga/l2_cache.c
index e3907ab..4267c95f 100644
--- a/arch/arm/mach-socfpga/l2_cache.c
+++ b/arch/arm/mach-socfpga/l2_cache.c
@@ -17,6 +17,20 @@
#include <linux/of_platform.h>
#include <linux/of_address.h>
+#include "core.h"
+
+/* A10 System Manager L2 ECC Control register */
+#define A10_MPU_CTRL_L2_ECC_OFST 0x0
+#define A10_MPU_CTRL_L2_ECC_EN BIT(0)
+
+/* A10 System Manager Global IRQ Mask register */
+#define A10_SYSMGR_ECC_INTMASK_CLR_OFST 0x98
+#define A10_SYSMGR_ECC_INTMASK_CLR_L2 BIT(0)
+
+/* A10 System Manager L2 ECC IRQ Clear register */
+#define A10_SYSMGR_MPU_CLEAR_L2_ECC_OFST 0xA8
+#define A10_SYSMGR_MPU_CLEAR_L2_ECC (BIT(31) | BIT(15))
+
void socfpga_init_l2_ecc(void)
{
struct device_node *np;
@@ -39,3 +53,38 @@ void socfpga_init_l2_ecc(void)
writel(0x01, mapped_l2_edac_addr);
iounmap(mapped_l2_edac_addr);
}
+
+void socfpga_init_arria10_l2_ecc(void)
+{
+ struct device_node *np;
+ void __iomem *mapped_l2_edac_addr;
+
+ /* Find the L2 EDAC device tree node */
+ np = of_find_compatible_node(NULL, NULL, "altr,socfpga-a10-l2-ecc");
+ if (!np) {
+ pr_err("Unable to find socfpga-a10-l2-ecc in dtb\n");
+ return;
+ }
+
+ mapped_l2_edac_addr = of_iomap(np, 0);
+ of_node_put(np);
+ if (!mapped_l2_edac_addr) {
+ pr_err("Unable to find L2 ECC mapping in dtb\n");
+ return;
+ }
+
+ if (!sys_manager_base_addr) {
+ pr_err("System Mananger not mapped for L2 ECC\n");
+ goto exit;
+ }
+ /* Clear any pending IRQs */
+ writel(A10_SYSMGR_MPU_CLEAR_L2_ECC, (sys_manager_base_addr +
+ A10_SYSMGR_MPU_CLEAR_L2_ECC_OFST));
+ /* Enable ECC */
+ writel(A10_SYSMGR_ECC_INTMASK_CLR_L2, sys_manager_base_addr +
+ A10_SYSMGR_ECC_INTMASK_CLR_OFST);
+ writel(A10_MPU_CTRL_L2_ECC_EN, mapped_l2_edac_addr +
+ A10_MPU_CTRL_L2_ECC_OFST);
+exit:
+ iounmap(mapped_l2_edac_addr);
+}
diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c
index 7e0aad2..e9b5b60 100644
--- a/arch/arm/mach-socfpga/socfpga.c
+++ b/arch/arm/mach-socfpga/socfpga.c
@@ -66,6 +66,14 @@ static void __init socfpga_init_irq(void)
socfpga_init_ocram_ecc();
}
+static void __init socfpga_arria10_init_irq(void)
+{
+ irqchip_init();
+ socfpga_sysmgr_init();
+ if (IS_ENABLED(CONFIG_EDAC_ALTERA_L2C))
+ socfpga_init_arria10_l2_ecc();
+}
+
static void socfpga_cyclone5_restart(enum reboot_mode mode, const char *cmd)
{
u32 temp;
@@ -113,7 +121,7 @@ static const char *altera_a10_dt_match[] = {
DT_MACHINE_START(SOCFPGA_A10, "Altera SOCFPGA Arria10")
.l2c_aux_val = 0,
.l2c_aux_mask = ~0,
- .init_irq = socfpga_init_irq,
+ .init_irq = socfpga_arria10_init_irq,
.restart = socfpga_arria10_restart,
.dt_compat = altera_a10_dt_match,
MACHINE_END
--
1.7.9.5
From: Thor Thayer <[email protected]>
Add the device tree entries needed to support the Altera L2
cache EDAC on the Arria10 chip.
Signed-off-by: Thor Thayer <[email protected]>
---
v2 Match register value (l2-ecc@ffd06010)
v3 Set ecc_manager to beginning of system_manager. Add sysman
phandle. Move IRQs into ecc_manager from children.
---
arch/arm/boot/dts/socfpga_arria10.dtsi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/arm/boot/dts/socfpga_arria10.dtsi b/arch/arm/boot/dts/socfpga_arria10.dtsi
index cce9e50..345ea97 100644
--- a/arch/arm/boot/dts/socfpga_arria10.dtsi
+++ b/arch/arm/boot/dts/socfpga_arria10.dtsi
@@ -599,6 +599,21 @@
reg = <0xffe00000 0x40000>;
};
+ eccmgr: eccmgr@ffd06000 {
+ compatible = "altr,socfpga-a10-ecc-manager";
+ altr,sysmgr-syscon = <&sysmgr>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupts = <0 2 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 IRQ_TYPE_LEVEL_HIGH>;
+ ranges;
+
+ l2-ecc@ffd06010 {
+ compatible = "altr,socfpga-a10-l2-ecc";
+ reg = <0xffd06010 0x4>;
+ };
+ };
+
rst: rstmgr@ffd05000 {
#reset-cells = <1>;
compatible = "altr,rst-mgr";
--
1.7.9.5
From: Thor Thayer <[email protected]>
Addition of the Arria10 L2 Cache ECC handling. Addition
of private data structure for Arria10 L2 cache ECC and
the probe function for it.
The Arria10 ECC device IRQs are in a shared register so
the ECC Manager parent/child relationship requires a
different probe function.
Signed-off-by: Thor Thayer <[email protected]>
---
v2: Split large patch into smaller patches. Addition of
Arria10 L2 cache dependency check and private data.
v3: Use new probe function to maintain parental/child
relationship for shared IRQ in ECC manager and
individual ECC module probes.
---
drivers/edac/altera_edac.c | 231 ++++++++++++++++++++++++++++++++++++++++++++
drivers/edac/altera_edac.h | 42 ++++++++
2 files changed, 273 insertions(+)
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index 502bf1f..0afdc58 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -24,6 +24,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
+#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -549,6 +550,7 @@ module_platform_driver(altr_edac_driver);
const struct edac_device_prv_data ocramecc_data;
const struct edac_device_prv_data l2ecc_data;
+const struct edac_device_prv_data a10_l2ecc_data;
static irqreturn_t altr_edac_device_handler(int irq, void *dev_id)
{
@@ -673,6 +675,8 @@ static void altr_create_edacdev_dbgfs(struct edac_device_ctl_info *edac_dci,
static const struct of_device_id altr_edac_device_of_match[] = {
#ifdef CONFIG_EDAC_ALTERA_L2C
{ .compatible = "altr,socfpga-l2-ecc", .data = (void *)&l2ecc_data },
+ { .compatible = "altr,socfpga-a10-l2-ecc",
+ .data = (void *)&a10_l2ecc_data },
#endif
#ifdef CONFIG_EDAC_ALTERA_OCRAM
{ .compatible = "altr,socfpga-ocram-ecc",
@@ -941,6 +945,24 @@ static int altr_l2_check_deps(struct altr_edac_device_dev *device)
return -ENODEV;
}
+static irqreturn_t altr_edac_a10_l2_irq(struct altr_edac_device_dev *dci,
+ bool sberr)
+{
+ if (sberr) {
+ regmap_write(dci->edac->ecc_mgr_map,
+ A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST,
+ A10_SYSGMR_MPU_CLEAR_L2_ECC_SB);
+ edac_device_handle_ce(dci->edac_dev, 0, 0, dci->edac_dev_name);
+ } else {
+ regmap_write(dci->edac->ecc_mgr_map,
+ A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST,
+ A10_SYSGMR_MPU_CLEAR_L2_ECC_MB);
+ edac_device_handle_ue(dci->edac_dev, 0, 0, dci->edac_dev_name);
+ panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n");
+ }
+ return IRQ_HANDLED;
+}
+
const struct edac_device_prv_data l2ecc_data = {
.setup = altr_l2_check_deps,
.ce_clear_mask = 0,
@@ -955,8 +977,217 @@ const struct edac_device_prv_data l2ecc_data = {
.trig_alloc_sz = ALTR_TRIG_L2C_BYTE_SIZE,
};
+const struct edac_device_prv_data a10_l2ecc_data = {
+ .setup = altr_l2_check_deps,
+ .ce_clear_mask = ALTR_A10_L2_ECC_SERR_CLR,
+ .ue_clear_mask = ALTR_A10_L2_ECC_MERR_CLR,
+ .irq_status_mask = A10_SYSMGR_ECC_INTSTAT_L2,
+ .dbgfs_name = "altr_l2_trigger",
+ .alloc_mem = l2_alloc_mem,
+ .free_mem = l2_free_mem,
+ .ecc_enable_mask = ALTR_A10_L2_ECC_EN_CTL,
+ .ce_set_mask = ALTR_A10_L2_ECC_CE_INJ_MASK,
+ .ue_set_mask = ALTR_A10_L2_ECC_UE_INJ_MASK,
+ .set_err_ofst = ALTR_A10_L2_ECC_INJ_OFST,
+ .ecc_irq_handler = altr_edac_a10_l2_irq,
+ .trig_alloc_sz = ALTR_TRIG_L2C_BYTE_SIZE,
+};
+
#endif /* CONFIG_EDAC_ALTERA_L2C */
+/********************* Arria10 EDAC Device Functions *************************/
+
+/*
+ * The Arria10 EDAC Device Functions differ from the Cyclone5/Arria5
+ * because 2 IRQs are shared among the all ECC peripherals. The ECC
+ * manager manages the IRQs and the children.
+ * Based on xgene_edac.c peripheral code.
+ */
+
+static irqreturn_t altr_edac_a10_irq_handler(int irq, void *dev_id)
+{
+ irqreturn_t rc = IRQ_NONE;
+ struct altr_arria10_edac *edac = dev_id;
+ struct altr_edac_device_dev *dci;
+ int irq_status;
+ bool sberr = (irq == edac->sb_irq) ? 1 : 0;
+ int sm_offset = sberr ? A10_SYSMGR_ECC_INTSTAT_SERR_OFST :
+ A10_SYSMGR_ECC_INTSTAT_DERR_OFST;
+
+ regmap_read(edac->ecc_mgr_map, sm_offset, &irq_status);
+
+ if ((irq != edac->sb_irq) && (irq != edac->db_irq)) {
+ WARN_ON(1);
+ } else {
+ list_for_each_entry(dci, &edac->a10_ecc_devices, next) {
+ if (irq_status & dci->data->irq_status_mask)
+ rc = dci->data->ecc_irq_handler(dci, sberr);
+ }
+ }
+
+ return rc;
+}
+
+static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
+ struct device_node *np)
+{
+ struct edac_device_ctl_info *dci;
+ struct altr_edac_device_dev *altdev;
+ char *ecc_name = (char *)np->name;
+ struct resource res;
+ int edac_idx;
+ int rc = 0;
+ const struct edac_device_prv_data *prv;
+ /* Get matching node and check for valid result */
+ const struct of_device_id *pdev_id =
+ of_match_node(altr_edac_device_of_match, np);
+ if (IS_ERR_OR_NULL(pdev_id))
+ return -ENODEV;
+
+ /* Get driver specific data for this EDAC device */
+ prv = pdev_id->data;
+ if (IS_ERR_OR_NULL(prv))
+ return -ENODEV;
+
+ if (!devres_open_group(edac->dev, altr_edac_a10_device_add, GFP_KERNEL))
+ return -ENOMEM;
+
+ rc = of_address_to_resource(np, 0, &res);
+ if (rc < 0) {
+ edac_printk(KERN_ERR, EDAC_DEVICE,
+ "%s: no resource address\n", ecc_name);
+ goto err_release_group;
+ }
+
+ edac_idx = edac_device_alloc_index();
+ dci = edac_device_alloc_ctl_info(sizeof(*altdev), ecc_name,
+ 1, ecc_name, 1, 0, NULL, 0,
+ edac_idx);
+
+ if (!dci) {
+ edac_printk(KERN_ERR, EDAC_DEVICE,
+ "%s: Unable to allocate EDAC device\n", ecc_name);
+ rc = -ENOMEM;
+ goto err_release_group;
+ }
+
+ altdev = dci->pvt_info;
+ dci->dev = edac->dev;
+ altdev->edac_dev_name = ecc_name;
+ altdev->edac_idx = edac_idx;
+ altdev->edac = edac;
+ altdev->edac_dev = dci;
+ altdev->data = prv;
+ altdev->ddev = *edac->dev;
+ dci->dev = &altdev->ddev;
+ dci->ctl_name = "Altera ECC Manager";
+ dci->mod_name = ecc_name;
+ dci->dev_name = ecc_name;
+
+ altdev->base = devm_ioremap_resource(edac->dev, &res);
+ if (IS_ERR(altdev->base)) {
+ rc = PTR_ERR(altdev->base);
+ goto err_release_group1;
+ }
+
+ /* Check specific dependencies for the module */
+ if (altdev->data->setup) {
+ rc = altdev->data->setup(altdev);
+ if (rc)
+ goto err_release_group1;
+ }
+
+ rc = edac_device_add_device(dci);
+ if (rc) {
+ dev_err(edac->dev, "edac_device_add_device failed\n");
+ rc = -ENOMEM;
+ goto err_release_group1;
+ }
+
+ altr_create_edacdev_dbgfs(dci, prv);
+
+ list_add(&altdev->next, &edac->a10_ecc_devices);
+
+ devres_remove_group(edac->dev, altr_edac_a10_device_add);
+
+ return 0;
+
+err_release_group1:
+ edac_device_free_ctl_info(dci);
+err_release_group:
+ edac_printk(KERN_ALERT, EDAC_DEVICE, "%s: %d\n", __func__, __LINE__);
+ devres_release_group(edac->dev, NULL);
+ edac_printk(KERN_ERR, EDAC_DEVICE,
+ "%s:Error setting up EDAC device: %d\n", ecc_name, rc);
+
+ return rc;
+}
+
+static int altr_edac_a10_probe(struct platform_device *pdev)
+{
+ struct altr_arria10_edac *edac;
+ struct device_node *child;
+ 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->a10_ecc_devices);
+
+ edac->ecc_mgr_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "altr,sysmgr-syscon");
+ if (IS_ERR(edac->ecc_mgr_map)) {
+ edac_printk(KERN_ERR, EDAC_DEVICE,
+ "Unable to get syscon altr,sysmgr-syscon\n");
+ return PTR_ERR(edac->ecc_mgr_map);
+ }
+
+ edac->sb_irq = platform_get_irq(pdev, 0);
+ rc = devm_request_irq(&pdev->dev, edac->sb_irq,
+ altr_edac_a10_irq_handler,
+ IRQF_SHARED, dev_name(&pdev->dev), edac);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_DEVICE, "No SBERR IRQ resource\n");
+ return rc;
+ }
+
+ edac->db_irq = platform_get_irq(pdev, 1);
+ rc = devm_request_irq(&pdev->dev, edac->db_irq,
+ altr_edac_a10_irq_handler,
+ IRQF_SHARED, dev_name(&pdev->dev), edac);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
+ return rc;
+ }
+
+ for_each_child_of_node(pdev->dev.of_node, child) {
+ if (!of_device_is_available(child))
+ continue;
+ if (of_device_is_compatible(child, "altr,socfpga-a10-l2-ecc"))
+ altr_edac_a10_device_add(edac, child);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id altr_edac_a10_of_match[] = {
+ { .compatible = "altr,socfpga-a10-ecc-manager" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, altr_edac_a10_of_match);
+
+static struct platform_driver altr_edac_a10_driver = {
+ .probe = altr_edac_a10_probe,
+ .driver = {
+ .name = "socfpga_a10_ecc_manager",
+ .of_match_table = altr_edac_a10_of_match,
+ },
+};
+module_platform_driver(altr_edac_a10_driver);
+
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Thor Thayer");
MODULE_DESCRIPTION("EDAC Driver for Altera Memories");
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h
index d7ef94c..b0a17d0 100644
--- a/drivers/edac/altera_edac.h
+++ b/drivers/edac/altera_edac.h
@@ -219,12 +219,39 @@ struct altr_sdram_mc_data {
#define ALTR_L2_ECC_INJS BIT(1)
#define ALTR_L2_ECC_INJD BIT(2)
+/* Arria10 General ECC Block Module Defines */
+#define A10_SYSMGR_ECC_INTSTAT_SERR_OFST 0x9C
+#define A10_SYSMGR_ECC_INTSTAT_DERR_OFST 0xA0
+#define A10_SYSMGR_ECC_INTSTAT_L2 BIT(0)
+
+#define A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST 0xA8
+#define A10_SYSGMR_MPU_CLEAR_L2_ECC_SB BIT(15)
+#define A10_SYSGMR_MPU_CLEAR_L2_ECC_MB BIT(31)
+
+/* Arria 10 L2 ECC Management Group Defines */
+#define ALTR_A10_L2_ECC_CTL_OFST 0x0
+#define ALTR_A10_L2_ECC_EN_CTL BIT(0)
+
+#define ALTR_A10_L2_ECC_STATUS 0xFFD060A4
+#define ALTR_A10_L2_ECC_STAT_OFST 0xA4
+#define ALTR_A10_L2_ECC_SERR_PEND BIT(0)
+#define ALTR_A10_L2_ECC_MERR_PEND BIT(0)
+
+#define ALTR_A10_L2_ECC_CLR_OFST 0x4
+#define ALTR_A10_L2_ECC_SERR_CLR BIT(15)
+#define ALTR_A10_L2_ECC_MERR_CLR BIT(31)
+
+#define ALTR_A10_L2_ECC_INJ_OFST ALTR_A10_L2_ECC_CTL_OFST
+#define ALTR_A10_L2_ECC_CE_INJ_MASK 0x00000101
+#define ALTR_A10_L2_ECC_UE_INJ_MASK 0x00010101
+
struct altr_edac_device_dev;
struct edac_device_prv_data {
int (*setup)(struct altr_edac_device_dev *device);
int ce_clear_mask;
int ue_clear_mask;
+ int irq_status_mask;
char dbgfs_name[20];
void * (*alloc_mem)(size_t size, void **other);
void (*free_mem)(void *p, size_t size, void *other);
@@ -232,16 +259,31 @@ struct edac_device_prv_data {
int ce_set_mask;
int ue_set_mask;
int set_err_ofst;
+ irqreturn_t (*ecc_irq_handler)(struct altr_edac_device_dev *dci,
+ bool sb);
int trig_alloc_sz;
};
struct altr_edac_device_dev {
+ struct list_head next;
void __iomem *base;
int sb_irq;
int db_irq;
const struct edac_device_prv_data *data;
struct dentry *debugfs_dir;
char *edac_dev_name;
+ struct altr_arria10_edac *edac;
+ struct edac_device_ctl_info *edac_dev;
+ struct device ddev;
+ int edac_idx;
+};
+
+struct altr_arria10_edac {
+ struct device *dev;
+ struct regmap *ecc_mgr_map;
+ int sb_irq;
+ int db_irq;
+ struct list_head a10_ecc_devices;
};
#endif /* #ifndef _ALTERA_EDAC_H */
--
1.7.9.5
From: Thor Thayer <[email protected]>
In preparation for the Arria10 peripheral ECCs, a register
offset from the ECC base was added to the private data
structure to index to the error injection register.
Signed-off-by: Thor Thayer <[email protected]>
---
v2: Split large patch into smaller patches. Add an ECC
error inject offset to support the different register
layout of Arria10 peripheral ECCs.
v3: Addition of offset defines (previously in ECC Enable
patch that was dropped).
---
drivers/edac/altera_edac.c | 7 +++++--
drivers/edac/altera_edac.h | 3 +++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index 0dbfa47..502bf1f 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -622,8 +622,9 @@ static ssize_t altr_edac_device_trig(struct file *file,
if (ACCESS_ONCE(ptemp[i]))
result = -1;
/* Toggle Error bit (it is latched), leave ECC enabled */
- writel(error_mask, drvdata->base);
- writel(priv->ecc_enable_mask, drvdata->base);
+ writel(error_mask, (drvdata->base + priv->set_err_ofst));
+ writel(priv->ecc_enable_mask, (drvdata->base +
+ priv->set_err_ofst));
ptemp[i] = i;
}
/* Ensure it has been written out */
@@ -879,6 +880,7 @@ const struct edac_device_prv_data ocramecc_data = {
.ecc_enable_mask = ALTR_OCR_ECC_EN,
.ce_set_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_INJS),
.ue_set_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_INJD),
+ .set_err_ofst = ALTR_OCR_ECC_REG_OFFSET,
.trig_alloc_sz = ALTR_TRIG_OCRAM_BYTE_SIZE,
};
@@ -949,6 +951,7 @@ const struct edac_device_prv_data l2ecc_data = {
.ecc_enable_mask = ALTR_L2_ECC_EN,
.ce_set_mask = (ALTR_L2_ECC_EN | ALTR_L2_ECC_INJS),
.ue_set_mask = (ALTR_L2_ECC_EN | ALTR_L2_ECC_INJD),
+ .set_err_ofst = ALTR_L2_ECC_REG_OFFSET,
.trig_alloc_sz = ALTR_TRIG_L2C_BYTE_SIZE,
};
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h
index 32c798a..d7ef94c 100644
--- a/drivers/edac/altera_edac.h
+++ b/drivers/edac/altera_edac.h
@@ -205,6 +205,7 @@ struct altr_sdram_mc_data {
/******* Cyclone5 and Arria5 Defines *******/
/* OCRAM ECC Management Group Defines */
#define ALTR_MAN_GRP_OCRAM_ECC_OFFSET 0x04
+#define ALTR_OCR_ECC_REG_OFFSET 0x00
#define ALTR_OCR_ECC_EN BIT(0)
#define ALTR_OCR_ECC_INJS BIT(1)
#define ALTR_OCR_ECC_INJD BIT(2)
@@ -213,6 +214,7 @@ struct altr_sdram_mc_data {
/* L2 ECC Management Group Defines */
#define ALTR_MAN_GRP_L2_ECC_OFFSET 0x00
+#define ALTR_L2_ECC_REG_OFFSET 0x00
#define ALTR_L2_ECC_EN BIT(0)
#define ALTR_L2_ECC_INJS BIT(1)
#define ALTR_L2_ECC_INJD BIT(2)
@@ -229,6 +231,7 @@ struct edac_device_prv_data {
int ecc_enable_mask;
int ce_set_mask;
int ue_set_mask;
+ int set_err_ofst;
int trig_alloc_sz;
};
--
1.7.9.5
On Mon, Mar 21, 2016 at 11:01:43AM -0500, [email protected] wrote:
> From: Thor Thayer <[email protected]>
>
> Add the device tree bindings needed to support the Altera L2
> cache on the Arria10 chip. Since all the peripherals share
> IRQs, the IRQ fields are now in the ecc_manager.
>
> Signed-off-by: Thor Thayer <[email protected]>
> ---
> v2 Correct spelling of Arria10 in patch title.
> v3 Major restructuring change for ecc_manager to include IRQs
> ---
> .../bindings/arm/altera/socfpga-eccmgr.txt | 40 ++++++++++++++++++++
> 1 file changed, 40 insertions(+)
Acked-by: Rob Herring <[email protected]>
On Mon, Mar 21, 2016 at 11:01:46AM -0500, [email protected] wrote:
> From: Thor Thayer <[email protected]>
>
> Add the device tree entries needed to support the Altera L2
> cache EDAC on the Arria10 chip.
>
> Signed-off-by: Thor Thayer <[email protected]>
> ---
> v2 Match register value (l2-ecc@ffd06010)
> v3 Set ecc_manager to beginning of system_manager. Add sysman
> phandle. Move IRQs into ecc_manager from children.
> ---
> arch/arm/boot/dts/socfpga_arria10.dtsi | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/arch/arm/boot/dts/socfpga_arria10.dtsi b/arch/arm/boot/dts/socfpga_arria10.dtsi
> index cce9e50..345ea97 100644
> --- a/arch/arm/boot/dts/socfpga_arria10.dtsi
> +++ b/arch/arm/boot/dts/socfpga_arria10.dtsi
> @@ -599,6 +599,21 @@
> reg = <0xffe00000 0x40000>;
> };
>
> + eccmgr: eccmgr@ffd06000 {
> + compatible = "altr,socfpga-a10-ecc-manager";
> + altr,sysmgr-syscon = <&sysmgr>;
> + #address-cells = <1>;
> + #size-cells = <1>;
> + interrupts = <0 2 IRQ_TYPE_LEVEL_HIGH>,
> + <0 0 IRQ_TYPE_LEVEL_HIGH>;
> + ranges;
> +
> + l2-ecc@ffd06010 {
> + compatible = "altr,socfpga-a10-l2-ecc";
> + reg = <0xffd06010 0x4>;
> + };
> + };
> +
> rst: rstmgr@ffd05000 {
> #reset-cells = <1>;
> compatible = "altr,rst-mgr";
> --
I've picked up all except this one: need an ACK for it too. Dinh, DT-people?
--
Regards/Gruss,
Boris.
ECO tip #101: Trim your mails when you reply.
Hi Boris,
On Tue, Mar 29, 2016 at 3:45 AM, Borislav Petkov <[email protected]> wrote:
> On Mon, Mar 21, 2016 at 11:01:46AM -0500, [email protected] wrote:
>> From: Thor Thayer <[email protected]>
>>
>> Add the device tree entries needed to support the Altera L2
>> cache EDAC on the Arria10 chip.
>>
>> Signed-off-by: Thor Thayer <[email protected]>
>> ---
>> v2 Match register value (l2-ecc@ffd06010)
>> v3 Set ecc_manager to beginning of system_manager. Add sysman
>> phandle. Move IRQs into ecc_manager from children.
>> ---
>> arch/arm/boot/dts/socfpga_arria10.dtsi | 15 +++++++++++++++
>> 1 file changed, 15 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/socfpga_arria10.dtsi b/arch/arm/boot/dts/socfpga_arria10.dtsi
>> index cce9e50..345ea97 100644
>> --- a/arch/arm/boot/dts/socfpga_arria10.dtsi
>> +++ b/arch/arm/boot/dts/socfpga_arria10.dtsi
>> @@ -599,6 +599,21 @@
>> reg = <0xffe00000 0x40000>;
>> };
>>
>> + eccmgr: eccmgr@ffd06000 {
>> + compatible = "altr,socfpga-a10-ecc-manager";
>> + altr,sysmgr-syscon = <&sysmgr>;
>> + #address-cells = <1>;
>> + #size-cells = <1>;
>> + interrupts = <0 2 IRQ_TYPE_LEVEL_HIGH>,
>> + <0 0 IRQ_TYPE_LEVEL_HIGH>;
>> + ranges;
>> +
>> + l2-ecc@ffd06010 {
>> + compatible = "altr,socfpga-a10-l2-ecc";
>> + reg = <0xffd06010 0x4>;
>> + };
>> + };
>> +
>> rst: rstmgr@ffd05000 {
>> #reset-cells = <1>;
>> compatible = "altr,rst-mgr";
>> --
>
> I've picked up all except this one: need an ACK for it too. Dinh, DT-people?
>
If you don't mind, I can take this patch. This will prevent merge
conflicts in the
DTS board files.
Thanks,
Dinh
On Tue, Mar 29, 2016 at 07:15:38AM -0500, Dinh Nguyen wrote:
> If you don't mind, I can take this patch. This will prevent merge
> conflicts in the
> DTS board files.
Fine with me as long as people don't start complaining if they start
testing my for-next branch and realize that the Arria10 support is not
complete.
Unless they test linux-next where your tree is too, I assume.
--
Regards/Gruss,
Boris.
ECO tip #101: Trim your mails when you reply.
On Tue, Mar 29, 2016 at 9:00 AM, Borislav Petkov <[email protected]> wrote:
> Fine with me as long as people don't start complaining if they start
> testing my for-next branch and realize that the Arria10 support is not
> complete.
>
> Unless they test linux-next where your tree is too, I assume.
>
Yes, I take the patch through arm-soc and will ultimately land in
linux-next.
Dinh