From: Thor Thayer <[email protected]>
This series of patches adds the Arria10 OCRAM EDAC support
Thor Thayer (7):
EDAC, altera: New file operations for Arria10 ECC modules
EDAC, altera: Add register offset for ECC Enable
EDAC, altera: Make OCRAM ECC dependency check generic
Documentation: dt: socfpga: Add Altera Arria10 OCRAM binding
EDAC, altera: Addition of Arria10 OCRAM ECC
ARM: socfpga: Enable Arria10 OCRAM ECC on startup
ARM: dts: Add Altera Arria10 OCRAM EDAC devicetree entry
.../bindings/arm/altera/socfpga-eccmgr.txt | 10 ++
arch/arm/boot/dts/socfpga_arria10.dtsi | 5 +
arch/arm/mach-socfpga/ocram.c | 128 ++++++++++++++++++++
drivers/edac/altera_edac.c | 127 +++++++++++++++----
drivers/edac/altera_edac.h | 37 ++++++
5 files changed, 284 insertions(+), 23 deletions(-)
--
1.7.9.5
From: Thor Thayer <[email protected]>
Addition of the Arria10 On-Chip RAM ECC handling. Addition
of private data structure for Arria10 OCRAM ECC.
Addition of a new file operations function and trigger
function for Arria10 OCRAM and other peripheral memories.
Signed-off-by: Thor Thayer <[email protected]>
---
v2: Remove the Arria10 ECC block init functions.
---
drivers/edac/altera_edac.c | 78 ++++++++++++++++++++++++++++++++++++++++++++
drivers/edac/altera_edac.h | 35 ++++++++++++++++++++
2 files changed, 113 insertions(+)
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index f7ffc77..11775dc 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -550,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_ocramecc_data;
const struct edac_device_prv_data a10_l2ecc_data;
static irqreturn_t altr_edac_device_handler(int irq, void *dev_id)
@@ -674,6 +675,16 @@ static const struct file_operations altr_edac_device_inject_fops = {
.llseek = generic_file_llseek,
};
+static ssize_t altr_edac_a10_device_trig(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos);
+
+static const struct file_operations altr_edac_a10_device_inject_fops = {
+ .open = simple_open,
+ .write = altr_edac_a10_device_trig,
+ .llseek = generic_file_llseek,
+};
+
static void altr_create_edacdev_dbgfs(struct edac_device_ctl_info *edac_dci,
const struct edac_device_prv_data *priv)
{
@@ -701,6 +712,8 @@ static const struct of_device_id altr_edac_device_of_match[] = {
#ifdef CONFIG_EDAC_ALTERA_OCRAM
{ .compatible = "altr,socfpga-ocram-ecc",
.data = (void *)&ocramecc_data },
+ { .compatible = "altr,socfpga-a10-ocram-ecc",
+ .data = (void *)&a10_ocramecc_data },
#endif
{},
};
@@ -889,6 +902,24 @@ const struct edac_device_prv_data ocramecc_data = {
.inject_fops = &altr_edac_device_inject_fops,
};
+static irqreturn_t altr_edac_a10_ecc_irq(struct altr_edac_device_dev *dci,
+ bool sberr);
+
+const struct edac_device_prv_data a10_ocramecc_data = {
+ .setup = altr_check_ecc_deps,
+ .ce_clear_mask = ALTR_A10_ECC_SERRPENA,
+ .ue_clear_mask = ALTR_A10_ECC_DERRPENA,
+ .irq_status_mask = A10_SYSMGR_ECC_INTSTAT_OCRAM,
+ .dbgfs_name = "altr_ocram_trigger",
+ .ecc_enable_mask = ALTR_A10_OCRAM_ECC_EN_CTL,
+ .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST,
+ .ce_set_mask = ALTR_A10_ECC_TSERRA,
+ .ue_set_mask = ALTR_A10_ECC_TDERRA,
+ .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST,
+ .ecc_irq_handler = altr_edac_a10_ecc_irq,
+ .inject_fops = &altr_edac_a10_device_inject_fops,
+};
+
#endif /* CONFIG_EDAC_ALTERA_OCRAM */
/********************* L2 Cache EDAC Device Functions ********************/
@@ -1007,6 +1038,50 @@ const struct edac_device_prv_data a10_l2ecc_data = {
* Based on xgene_edac.c peripheral code.
*/
+static ssize_t altr_edac_a10_device_trig(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct edac_device_ctl_info *edac_dci = file->private_data;
+ struct altr_edac_device_dev *drvdata = edac_dci->pvt_info;
+ const struct edac_device_prv_data *priv = drvdata->data;
+ void __iomem *set_addr = (drvdata->base + priv->set_err_ofst);
+ unsigned long flags;
+ u8 trig_type;
+
+ if (!user_buf || get_user(trig_type, user_buf))
+ return -EFAULT;
+
+ local_irq_save(flags);
+ if (trig_type == ALTR_UE_TRIGGER_CHAR)
+ writel(priv->ue_set_mask, set_addr);
+ else
+ writel(priv->ce_set_mask, set_addr);
+ /* Ensure the interrupt test bits are set */
+ wmb();
+ local_irq_restore(flags);
+
+ return count;
+}
+
+static irqreturn_t altr_edac_a10_ecc_irq(struct altr_edac_device_dev *dci,
+ bool sberr)
+{
+ void __iomem *base = dci->base;
+
+ if (sberr) {
+ writel(ALTR_A10_ECC_SERRPENA,
+ base + ALTR_A10_ECC_INTSTAT_OFST);
+ edac_device_handle_ce(dci->edac_dev, 0, 0, dci->edac_dev_name);
+ } else {
+ writel(ALTR_A10_ECC_DERRPENA,
+ base + ALTR_A10_ECC_INTSTAT_OFST);
+ edac_device_handle_ue(dci->edac_dev, 0, 0, dci->edac_dev_name);
+ panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n");
+ }
+ return IRQ_HANDLED;
+}
+
static irqreturn_t altr_edac_a10_irq_handler(int irq, void *dev_id)
{
irqreturn_t rc = IRQ_NONE;
@@ -1171,6 +1246,9 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
continue;
if (of_device_is_compatible(child, "altr,socfpga-a10-l2-ecc"))
altr_edac_a10_device_add(edac, child);
+ else if (of_device_is_compatible(child,
+ "altr,socfpga-a10-ocram-ecc"))
+ altr_edac_a10_device_add(edac, child);
}
return 0;
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h
index cb6b2b9..42090f3 100644
--- a/drivers/edac/altera_edac.h
+++ b/drivers/edac/altera_edac.h
@@ -220,9 +220,41 @@ struct altr_sdram_mc_data {
#define ALTR_L2_ECC_INJD BIT(2)
/* Arria10 General ECC Block Module Defines */
+#define ALTR_A10_ECC_CTRL_OFST 0x08
+#define ALTR_A10_ECC_EN BIT(0)
+#define ALTR_A10_ECC_INITA BIT(16)
+#define ALTR_A10_ECC_INITB BIT(24)
+
+#define ALTR_A10_ECC_INITSTAT_OFST 0x0C
+#define ALTR_A10_ECC_INITCOMPLETEA BIT(0)
+#define ALTR_A10_ECC_INITCOMPLETEB BIT(8)
+
+#define ALTR_A10_ECC_ERRINTEN_OFST 0x10
+#define ALTR_A10_ECC_SERRINTEN BIT(0)
+
+#define ALTR_A10_ECC_INTSTAT_OFST 0x20
+#define ALTR_A10_ECC_SERRPENA BIT(0)
+#define ALTR_A10_ECC_DERRPENA BIT(8)
+#define ALTR_A10_ECC_ERRPENA_MASK (ALTR_A10_ECC_SERRPENA | \
+ ALTR_A10_ECC_DERRPENA)
+#define ALTR_A10_ECC_SERRPENB BIT(16)
+#define ALTR_A10_ECC_DERRPENB BIT(24)
+#define ALTR_A10_ECC_ERRPENB_MASK (ALTR_A10_ECC_SERRPENB | \
+ ALTR_A10_ECC_DERRPENB)
+
+#define ALTR_A10_ECC_INTTEST_OFST 0x24
+#define ALTR_A10_ECC_TSERRA BIT(0)
+#define ALTR_A10_ECC_TDERRA BIT(8)
+
+/* ECC Manager Defines */
+#define A10_SYSMGR_ECC_INTMASK_SET_OFST 0x94
+#define A10_SYSMGR_ECC_INTMASK_CLR_OFST 0x98
+#define A10_SYSMGR_ECC_INTMASK_OCRAM BIT(1)
+
#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_SYSMGR_ECC_INTSTAT_OCRAM BIT(1)
#define A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST 0xA8
#define A10_SYSGMR_MPU_CLEAR_L2_ECC_SB BIT(15)
@@ -245,6 +277,9 @@ struct altr_sdram_mc_data {
#define ALTR_A10_L2_ECC_CE_INJ_MASK 0x00000101
#define ALTR_A10_L2_ECC_UE_INJ_MASK 0x00010101
+/* Arria 10 OCRAM ECC Management Group Defines */
+#define ALTR_A10_OCRAM_ECC_EN_CTL (BIT(1) | BIT(0))
+
struct altr_edac_device_dev;
struct edac_device_prv_data {
--
1.7.9.5
From: Thor Thayer <[email protected]>
In preparation for the Arria10 peripheral ECCs, the OCRAM
ECC dependency check was moved into the general ECC area
since this same function can be used by other memories.
Signed-off-by: Thor Thayer <[email protected]>
---
v2: No change
---
drivers/edac/altera_edac.c | 43 +++++++++++++++++++++----------------------
1 file changed, 21 insertions(+), 22 deletions(-)
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index f0a6de7..f7ffc77 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -648,6 +648,26 @@ static ssize_t altr_edac_device_trig(struct file *file,
return count;
}
+/*
+ * Test for memory's ECC dependencies upon entry because platform specific
+ * startup should have initialized the memory and enabled the ECC.
+ * Can't turn on ECC here because accessing un-initialized memory will
+ * cause CE/UE errors possibly causing an ABORT.
+ */
+static int altr_check_ecc_deps(struct altr_edac_device_dev *device)
+{
+ void __iomem *base = device->base;
+ const struct edac_device_prv_data *prv = device->data;
+
+ if (readl(base + prv->ecc_en_ofst) & prv->ecc_enable_mask)
+ return 0;
+
+ edac_printk(KERN_ERR, EDAC_DEVICE,
+ "%s: No ECC present or ECC disabled.\n",
+ device->edac_dev_name);
+ return -ENODEV;
+}
+
static const struct file_operations altr_edac_device_inject_fops = {
.open = simple_open,
.write = altr_edac_device_trig,
@@ -853,29 +873,8 @@ static void ocram_free_mem(void *p, size_t size, void *other)
gen_pool_free((struct gen_pool *)other, (u32)p, size);
}
-/*
- * altr_ocram_check_deps()
- * Test for OCRAM cache ECC dependencies upon entry because
- * platform specific startup should have initialized the
- * On-Chip RAM memory and enabled the ECC.
- * 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 altr_edac_device_dev *device)
-{
- void __iomem *base = device->base;
- const struct edac_device_prv_data *prv = device->data;
-
- if (readl(base + prv->ecc_en_ofst) & prv->ecc_enable_mask)
- return 0;
-
- edac_printk(KERN_ERR, EDAC_DEVICE,
- "OCRAM: No ECC present or ECC disabled.\n");
- return -ENODEV;
-}
-
const struct edac_device_prv_data ocramecc_data = {
- .setup = altr_ocram_check_deps,
+ .setup = altr_check_ecc_deps,
.ce_clear_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_SERR),
.ue_clear_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_DERR),
.dbgfs_name = "altr_ocram_trigger",
--
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 ECC enable register.
Signed-off-by: Thor Thayer <[email protected]>
---
v2: No change
---
drivers/edac/altera_edac.c | 3 ++-
drivers/edac/altera_edac.h | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index fb6fe56..f0a6de7 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -866,7 +866,7 @@ static int altr_ocram_check_deps(struct altr_edac_device_dev *device)
void __iomem *base = device->base;
const struct edac_device_prv_data *prv = device->data;
- if (readl(base) & prv->ecc_enable_mask)
+ if (readl(base + prv->ecc_en_ofst) & prv->ecc_enable_mask)
return 0;
edac_printk(KERN_ERR, EDAC_DEVICE,
@@ -882,6 +882,7 @@ const struct edac_device_prv_data ocramecc_data = {
.alloc_mem = ocram_alloc_mem,
.free_mem = ocram_free_mem,
.ecc_enable_mask = ALTR_OCR_ECC_EN,
+ .ecc_en_ofst = ALTR_OCR_ECC_REG_OFFSET,
.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,
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h
index c995388..cb6b2b9 100644
--- a/drivers/edac/altera_edac.h
+++ b/drivers/edac/altera_edac.h
@@ -256,6 +256,7 @@ struct edac_device_prv_data {
void * (*alloc_mem)(size_t size, void **other);
void (*free_mem)(void *p, size_t size, void *other);
int ecc_enable_mask;
+ int ecc_en_ofst;
int ce_set_mask;
int ue_set_mask;
int set_err_ofst;
--
1.7.9.5
From: Thor Thayer <[email protected]>
Add the device tree entries needed to support the Altera On-Chip
RAM EDAC on the Arria10 chip.
Signed-off-by: Thor Thayer <[email protected]>
---
v2: No change
---
arch/arm/boot/dts/socfpga_arria10.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/socfpga_arria10.dtsi b/arch/arm/boot/dts/socfpga_arria10.dtsi
index 519e9e4..27cc497 100644
--- a/arch/arm/boot/dts/socfpga_arria10.dtsi
+++ b/arch/arm/boot/dts/socfpga_arria10.dtsi
@@ -612,6 +612,11 @@
compatible = "altr,socfpga-a10-l2-ecc";
reg = <0xffd06010 0x4>;
};
+
+ ocram-ecc@ff8c3000 {
+ compatible = "altr,socfpga-a10-ocram-ecc";
+ reg = <0xff8c3000 0x400>;
+ };
};
rst: rstmgr@ffd05000 {
--
1.7.9.5
From: Thor Thayer <[email protected]>
Enable ECC for Arria10 On-Chip RAM on machine startup. The ECC has to
be enabled and memory initialized before data is stored in memory
otherwise the ECC will fail on reads.
Signed-off-by: Thor Thayer <[email protected]>
---
v2: Add Arria10 ECC block initialization locally.
---
arch/arm/mach-socfpga/ocram.c | 128 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 128 insertions(+)
diff --git a/arch/arm/mach-socfpga/ocram.c b/arch/arm/mach-socfpga/ocram.c
index 60ec643..d4a524c 100644
--- a/arch/arm/mach-socfpga/ocram.c
+++ b/arch/arm/mach-socfpga/ocram.c
@@ -13,12 +13,15 @@
* 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/delay.h>
#include <linux/io.h>
#include <linux/genalloc.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
+#include "core.h"
+
#define ALTR_OCRAM_CLEAR_ECC 0x00000018
#define ALTR_OCRAM_ECC_EN 0x00000019
@@ -47,3 +50,128 @@ void socfpga_init_ocram_ecc(void)
iounmap(mapped_ocr_edac_addr);
}
+
+/* Arria10 OCRAM Section */
+#define ALTR_A10_ECC_CTRL_OFST 0x08
+#define ALTR_A10_OCRAM_ECC_EN_CTL (BIT(1) | BIT(0))
+#define ALTR_A10_ECC_INITA BIT(16)
+
+#define ALTR_A10_ECC_INITSTAT_OFST 0x0C
+#define ALTR_A10_ECC_INITCOMPLETEA BIT(0)
+#define ALTR_A10_ECC_INITCOMPLETEB BIT(8)
+
+#define ALTR_A10_ECC_ERRINTEN_OFST 0x10
+#define ALTR_A10_ECC_SERRINTEN BIT(0)
+
+#define ALTR_A10_ECC_INTSTAT_OFST 0x20
+#define ALTR_A10_ECC_SERRPENA BIT(0)
+#define ALTR_A10_ECC_DERRPENA BIT(8)
+#define ALTR_A10_ECC_ERRPENA_MASK (ALTR_A10_ECC_SERRPENA | \
+ ALTR_A10_ECC_DERRPENA)
+/* ECC Manager Defines */
+#define A10_SYSMGR_ECC_INTMASK_SET_OFST 0x94
+#define A10_SYSMGR_ECC_INTMASK_CLR_OFST 0x98
+#define A10_SYSMGR_ECC_INTMASK_OCRAM BIT(1)
+
+#define ALTR_A10_ECC_INIT_WATCHDOG_10US 10000
+
+static void ecc_set_bits(u32 bit_mask, void __iomem *ioaddr)
+{
+ u32 value = readl(ioaddr);
+
+ value |= bit_mask;
+ writel(value, ioaddr);
+}
+
+static void ecc_clear_bits(u32 bit_mask, void __iomem *ioaddr)
+{
+ u32 value = readl(ioaddr);
+
+ value &= ~bit_mask;
+ writel(value, ioaddr);
+}
+
+static int ecc_test_bits(u32 bit_mask, void __iomem *ioaddr)
+{
+ u32 value = readl(ioaddr);
+
+ return (value & bit_mask) ? 1 : 0;
+}
+
+/*
+ * This function uses the memory initialization block in the Arria10 ECC
+ * controller to initialize/clear the entire memory data and ECC data.
+ */
+static int altr_init_memory_port(void __iomem *ioaddr)
+{
+ int limit = ALTR_A10_ECC_INIT_WATCHDOG_10US;
+
+ ecc_set_bits(ALTR_A10_ECC_INITA, (ioaddr + ALTR_A10_ECC_CTRL_OFST));
+ while (limit--) {
+ if (ecc_test_bits(ALTR_A10_ECC_INITCOMPLETEA,
+ (ioaddr + ALTR_A10_ECC_INITSTAT_OFST)))
+ break;
+ udelay(1);
+ }
+ if (limit < 0)
+ return -EBUSY;
+
+ /* Clear any pending ECC interrupts */
+ writel(ALTR_A10_ECC_ERRPENA_MASK,
+ (ioaddr + ALTR_A10_ECC_INTSTAT_OFST));
+
+ return 0;
+}
+
+void socfpga_init_arria10_ocram_ecc(void)
+{
+ struct device_node *np;
+ int ret = 0;
+ void __iomem *ecc_block_base;
+
+ if (!sys_manager_base_addr) {
+ pr_err("SOCFPGA: sys-mgr is not initialized\n");
+ return;
+ }
+
+ /* Find the OCRAM EDAC device tree node */
+ np = of_find_compatible_node(NULL, NULL, "altr,socfpga-a10-ocram-ecc");
+ if (!np) {
+ pr_err("Unable to find socfpga-a10-ocram-ecc\n");
+ return;
+ }
+
+ /* Map the ECC Block */
+ ecc_block_base = of_iomap(np, 0);
+ of_node_put(np);
+ if (!ecc_block_base) {
+ pr_err("Unable to map OCRAM ECC block\n");
+ return;
+ }
+
+ /* Disable ECC */
+ writel(ALTR_A10_OCRAM_ECC_EN_CTL,
+ sys_manager_base_addr + A10_SYSMGR_ECC_INTMASK_SET_OFST);
+ ecc_clear_bits(ALTR_A10_ECC_SERRINTEN,
+ (ecc_block_base + ALTR_A10_ECC_ERRINTEN_OFST));
+ ecc_clear_bits(ALTR_A10_OCRAM_ECC_EN_CTL,
+ (ecc_block_base + ALTR_A10_ECC_CTRL_OFST));
+
+ /* Use HW initialization block to initialize memory for ECC */
+ ret = altr_init_memory_port(ecc_block_base);
+ if (ret) {
+ pr_err("ECC: cannot init OCRAM PORTA memory\n");
+ return;
+ }
+
+ /* Enable ECC */
+ ecc_set_bits(ALTR_A10_OCRAM_ECC_EN_CTL,
+ (ecc_block_base + ALTR_A10_ECC_CTRL_OFST));
+ ecc_set_bits(ALTR_A10_ECC_SERRINTEN,
+ (ecc_block_base + ALTR_A10_ECC_ERRINTEN_OFST));
+ writel(ALTR_A10_OCRAM_ECC_EN_CTL,
+ sys_manager_base_addr + A10_SYSMGR_ECC_INTMASK_CLR_OFST);
+
+ /* Ensure all writes complete */
+ wmb();
+}
--
1.7.9.5
From: Thor Thayer <[email protected]>
In preparation for the Arria10 peripheral ECCs, new file
operations are used because the Arria10 IRQ trigger mechanism
is different than Cyclone5/Arria5 and Arria10 L2 cache.
Add new pointer for file operations function to ecc data
structure and point to current file operations function.
Signed-off-by: Thor Thayer <[email protected]>
---
v2: No change
---
drivers/edac/altera_edac.c | 5 ++++-
drivers/edac/altera_edac.h | 1 +
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index 0afdc58..fb6fe56 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -668,7 +668,7 @@ static void altr_create_edacdev_dbgfs(struct edac_device_ctl_info *edac_dci,
if (!edac_debugfs_create_file(priv->dbgfs_name, S_IWUSR,
drvdata->debugfs_dir, edac_dci,
- &altr_edac_device_inject_fops))
+ priv->inject_fops))
debugfs_remove_recursive(drvdata->debugfs_dir);
}
@@ -886,6 +886,7 @@ const struct edac_device_prv_data ocramecc_data = {
.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,
+ .inject_fops = &altr_edac_device_inject_fops,
};
#endif /* CONFIG_EDAC_ALTERA_OCRAM */
@@ -975,6 +976,7 @@ const struct edac_device_prv_data l2ecc_data = {
.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,
+ .inject_fops = &altr_edac_device_inject_fops,
};
const struct edac_device_prv_data a10_l2ecc_data = {
@@ -991,6 +993,7 @@ const struct edac_device_prv_data a10_l2ecc_data = {
.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,
+ .inject_fops = &altr_edac_device_inject_fops,
};
#endif /* CONFIG_EDAC_ALTERA_L2C */
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h
index b0a17d0..c995388 100644
--- a/drivers/edac/altera_edac.h
+++ b/drivers/edac/altera_edac.h
@@ -262,6 +262,7 @@ struct edac_device_prv_data {
irqreturn_t (*ecc_irq_handler)(struct altr_edac_device_dev *dci,
bool sb);
int trig_alloc_sz;
+ const struct file_operations *inject_fops;
};
struct altr_edac_device_dev {
--
1.7.9.5
From: Thor Thayer <[email protected]>
Add the device tree bindings needed to support the Altera On-Chip
RAM ECC on the Arria10 chip.
Signed-off-by: Thor Thayer <[email protected]>
---
v2: Align Required Properties descriptions
---
.../bindings/arm/altera/socfpga-eccmgr.txt | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-eccmgr.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-eccmgr.txt
index 37ff9bf..5a6b160 100644
--- a/Documentation/devicetree/bindings/arm/altera/socfpga-eccmgr.txt
+++ b/Documentation/devicetree/bindings/arm/altera/socfpga-eccmgr.txt
@@ -71,6 +71,11 @@ Required Properties:
- compatible : Should be "altr,socfpga-a10-l2-ecc"
- reg : Address and size for ECC error interrupt clear registers.
+On-Chip RAM ECC
+Required Properties:
+- compatible : Should be "altr,socfpga-a10-ocram-ecc"
+- reg : Address and size for ECC block registers.
+
Example:
eccmgr: eccmgr@ffd06000 {
@@ -86,4 +91,9 @@ Example:
compatible = "altr,socfpga-a10-l2-ecc";
reg = <0xffd06010 0x4>;
};
+
+ ocram-ecc@ff8c3000 {
+ compatible = "altr,socfpga-a10-ocram-ecc";
+ reg = <0xff8c3000 0x90>;
+ };
};
--
1.7.9.5
On Thu, Mar 31, 2016 at 01:48:04PM -0500, [email protected] wrote:
> From: Thor Thayer <[email protected]>
>
> Add the device tree bindings needed to support the Altera On-Chip
> RAM ECC on the Arria10 chip.
>
> Signed-off-by: Thor Thayer <[email protected]>
> ---
> v2: Align Required Properties descriptions
> ---
> .../bindings/arm/altera/socfpga-eccmgr.txt | 10 ++++++++++
> 1 file changed, 10 insertions(+)
Acked-by: Rob Herring <[email protected]>
Hi,
On 03/31/2016 01:48 PM, [email protected] wrote:
> From: Thor Thayer <[email protected]>
>
> Enable ECC for Arria10 On-Chip RAM on machine startup. The ECC has to
> be enabled and memory initialized before data is stored in memory
> otherwise the ECC will fail on reads.
>
> Signed-off-by: Thor Thayer <[email protected]>
> ---
> v2: Add Arria10 ECC block initialization locally.
> ---
> arch/arm/mach-socfpga/ocram.c | 128 +++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 128 insertions(+)
>
> diff --git a/arch/arm/mach-socfpga/ocram.c b/arch/arm/mach-socfpga/ocram.c
> index 60ec643..d4a524c 100644
> --- a/arch/arm/mach-socfpga/ocram.c
> +++ b/arch/arm/mach-socfpga/ocram.c
> @@ -13,12 +13,15 @@
> * 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/delay.h>
> #include <linux/io.h>
> #include <linux/genalloc.h>
> #include <linux/module.h>
> #include <linux/of_address.h>
> #include <linux/of_platform.h>
>
> +#include "core.h"
> +
> #define ALTR_OCRAM_CLEAR_ECC 0x00000018
> #define ALTR_OCRAM_ECC_EN 0x00000019
>
> @@ -47,3 +50,128 @@ void socfpga_init_ocram_ecc(void)
>
> iounmap(mapped_ocr_edac_addr);
> }
> +
> +/* Arria10 OCRAM Section */
> +#define ALTR_A10_ECC_CTRL_OFST 0x08
> +#define ALTR_A10_OCRAM_ECC_EN_CTL (BIT(1) | BIT(0))
> +#define ALTR_A10_ECC_INITA BIT(16)
> +
> +#define ALTR_A10_ECC_INITSTAT_OFST 0x0C
> +#define ALTR_A10_ECC_INITCOMPLETEA BIT(0)
> +#define ALTR_A10_ECC_INITCOMPLETEB BIT(8)
> +
> +#define ALTR_A10_ECC_ERRINTEN_OFST 0x10
> +#define ALTR_A10_ECC_SERRINTEN BIT(0)
> +
> +#define ALTR_A10_ECC_INTSTAT_OFST 0x20
> +#define ALTR_A10_ECC_SERRPENA BIT(0)
> +#define ALTR_A10_ECC_DERRPENA BIT(8)
> +#define ALTR_A10_ECC_ERRPENA_MASK (ALTR_A10_ECC_SERRPENA | \
> + ALTR_A10_ECC_DERRPENA)
> +/* ECC Manager Defines */
> +#define A10_SYSMGR_ECC_INTMASK_SET_OFST 0x94
> +#define A10_SYSMGR_ECC_INTMASK_CLR_OFST 0x98
> +#define A10_SYSMGR_ECC_INTMASK_OCRAM BIT(1)
> +
> +#define ALTR_A10_ECC_INIT_WATCHDOG_10US 10000
> +
> +static void ecc_set_bits(u32 bit_mask, void __iomem *ioaddr)
> +{
> + u32 value = readl(ioaddr);
> +
> + value |= bit_mask;
> + writel(value, ioaddr);
> +}
> +
> +static void ecc_clear_bits(u32 bit_mask, void __iomem *ioaddr)
> +{
> + u32 value = readl(ioaddr);
> +
> + value &= ~bit_mask;
> + writel(value, ioaddr);
> +}
> +
> +static int ecc_test_bits(u32 bit_mask, void __iomem *ioaddr)
> +{
> + u32 value = readl(ioaddr);
> +
> + return (value & bit_mask) ? 1 : 0;
> +}
> +
> +/*
> + * This function uses the memory initialization block in the Arria10 ECC
> + * controller to initialize/clear the entire memory data and ECC data.
> + */
> +static int altr_init_memory_port(void __iomem *ioaddr)
> +{
> + int limit = ALTR_A10_ECC_INIT_WATCHDOG_10US;
> +
> + ecc_set_bits(ALTR_A10_ECC_INITA, (ioaddr + ALTR_A10_ECC_CTRL_OFST));
> + while (limit--) {
> + if (ecc_test_bits(ALTR_A10_ECC_INITCOMPLETEA,
> + (ioaddr + ALTR_A10_ECC_INITSTAT_OFST)))
> + break;
> + udelay(1);
> + }
> + if (limit < 0)
> + return -EBUSY;
> +
> + /* Clear any pending ECC interrupts */
> + writel(ALTR_A10_ECC_ERRPENA_MASK,
> + (ioaddr + ALTR_A10_ECC_INTSTAT_OFST));
> +
> + return 0;
> +}
> +
> +void socfpga_init_arria10_ocram_ecc(void)
> +{
> + struct device_node *np;
> + int ret = 0;
> + void __iomem *ecc_block_base;
> +
> + if (!sys_manager_base_addr) {
> + pr_err("SOCFPGA: sys-mgr is not initialized\n");
> + return;
> + }
> +
> + /* Find the OCRAM EDAC device tree node */
> + np = of_find_compatible_node(NULL, NULL, "altr,socfpga-a10-ocram-ecc");
> + if (!np) {
> + pr_err("Unable to find socfpga-a10-ocram-ecc\n");
> + return;
> + }
> +
> + /* Map the ECC Block */
> + ecc_block_base = of_iomap(np, 0);
> + of_node_put(np);
> + if (!ecc_block_base) {
> + pr_err("Unable to map OCRAM ECC block\n");
> + return;
> + }
> +
> + /* Disable ECC */
> + writel(ALTR_A10_OCRAM_ECC_EN_CTL,
> + sys_manager_base_addr + A10_SYSMGR_ECC_INTMASK_SET_OFST);
> + ecc_clear_bits(ALTR_A10_ECC_SERRINTEN,
> + (ecc_block_base + ALTR_A10_ECC_ERRINTEN_OFST));
> + ecc_clear_bits(ALTR_A10_OCRAM_ECC_EN_CTL,
> + (ecc_block_base + ALTR_A10_ECC_CTRL_OFST));
> +
> + /* Use HW initialization block to initialize memory for ECC */
> + ret = altr_init_memory_port(ecc_block_base);
> + if (ret) {
> + pr_err("ECC: cannot init OCRAM PORTA memory\n");
> + return;
I realize that I'm not calling iounmap(ecc_block_base) and I'll fix that
in the next revision with a goto.
> + }
> +
> + /* Enable ECC */
> + ecc_set_bits(ALTR_A10_OCRAM_ECC_EN_CTL,
> + (ecc_block_base + ALTR_A10_ECC_CTRL_OFST));
> + ecc_set_bits(ALTR_A10_ECC_SERRINTEN,
> + (ecc_block_base + ALTR_A10_ECC_ERRINTEN_OFST));
> + writel(ALTR_A10_OCRAM_ECC_EN_CTL,
> + sys_manager_base_addr + A10_SYSMGR_ECC_INTMASK_CLR_OFST);
> +
The iounmap() will go here. I'd still like to hear review comments for
the rest.
Thanks for reviewing.
> + /* Ensure all writes complete */
> + wmb();
> +}
>
On Tue, Apr 05, 2016 at 12:25:33AM -0500, Thor Thayer wrote:
> I realize that I'm not calling iounmap(ecc_block_base) and I'll fix that in
> the next revision with a goto.
I'm assuming nothing else changes. Because I've applied 1-4 already.
Yes, no?
If no, then please send only an updated version of this patch as a reply
to this thread here.
Thanks.
--
Regards/Gruss,
Boris.
ECO tip #101: Trim your mails when you reply.
On Tue, 5 Apr 2016, Borislav Petkov wrote:
> On Tue, Apr 05, 2016 at 12:25:33AM -0500, Thor Thayer wrote:
> > I realize that I'm not calling iounmap(ecc_block_base) and I'll fix that in
> > the next revision with a goto.
>
> I'm assuming nothing else changes. Because I've applied 1-4 already.
>
> Yes, no?
>
> If no, then please send only an updated version of this patch as a reply
> to this thread here.
>
My only suggestion was to change the 3 helper functions(ecc_set_bits,
ecc_clear_bits, and ecc_test_bits) should be static inline.
So with that change:
Acked-by: Dinh Nguyen <[email protected]>
BR,
Dinh
On Thu, 31 Mar 2016, [email protected] wrote:
> From: Thor Thayer <[email protected]>
>
> Add the device tree entries needed to support the Altera On-Chip
> RAM EDAC on the Arria10 chip.
>
> Signed-off-by: Thor Thayer <[email protected]>
> ---
> v2: No change
> ---
> arch/arm/boot/dts/socfpga_arria10.dtsi | 5 +++++
> 1 file changed, 5 insertions(+)
I've applied this patch and will take through the arm-soc tree.
Thanks,
Dinh
On Tue, Apr 05, 2016 at 01:37:49PM -0500, Dinh Nguyen wrote:
> My only suggestion was to change the 3 helper functions(ecc_set_bits,
> ecc_clear_bits, and ecc_test_bits) should be static inline.
That doesn't take care of the iounmap error path Thor is talking about,
AFAICT.
So, I've pushed out what I have applied so far:
http://git.kernel.org/cgit/linux/kernel/git/bp/bp.git/log/?h=for-next
Thor, please send me what is outstanding ontop.
Thanks.
--
Regards/Gruss,
Boris.
ECO tip #101: Trim your mails when you reply.
Hi Boris,
On 04/05/2016 12:31 AM, Borislav Petkov wrote:
> On Tue, Apr 05, 2016 at 12:25:33AM -0500, Thor Thayer wrote:
>> I realize that I'm not calling iounmap(ecc_block_base) and I'll fix that in
>> the next revision with a goto.
>
> I'm assuming nothing else changes. Because I've applied 1-4 already.
>
> Yes, no?
>
> If no, then please send only an updated version of this patch as a reply
> to this thread here.
>
> Thanks.
>
Yes, nothing else changes. The rest was OK. Thanks!
On Wed, Apr 06, 2016 at 07:36:49PM -0500, Thor Thayer wrote:
> Yes, nothing else changes. The rest was OK. Thanks!
Cool. The two remaining applied and pushed out.
Thanks.
--
Regards/Gruss,
Boris.
ECO tip #101: Trim your mails when you reply.