arm: emif-common: Add suppport for enabling ECC

For data integrity, the EMIF1 supports ECC on the data
written or read from the SDRAM. Add support for enabling
ECC support in EMIF1.

Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
Signed-off-by: Krunal Bhargav <k-bhargav@ti.com>
diff --git a/arch/arm/include/asm/emif.h b/arch/arm/include/asm/emif.h
index a661ba9..1924f04 100644
--- a/arch/arm/include/asm/emif.h
+++ b/arch/arm/include/asm/emif.h
@@ -604,6 +604,34 @@
 
 #define EMIF_EXT_PHY_CTRL_TIMING_REG	0x5
 
+/* EMIF ECC CTRL reg */
+#define EMIF_ECC_CTRL_REG_ECC_EN_SHIFT			31
+#define EMIF_ECC_CTRL_REG_ECC_EN_MASK			(1 << 31)
+#define EMIF_ECC_CTRL_REG_ECC_ADDR_RGN_PROT_SHIFT	30
+#define EMIF_ECC_CTRL_REG_ECC_ADDR_RGN_PROT_MASK	(1 << 30)
+#define EMIF_ECC_CTRL_REG_ECC_VERIFY_DIS_SHIFT		29
+#define EMIF_ECC_CTRL_REG_ECC_VERIFY_DIS_MASK		(1 << 29)
+#define EMIF_ECC_REG_RMW_EN_SHIFT			28
+#define EMIF_ECC_REG_RMW_EN_MASK			(1 << 28)
+#define EMIF_ECC_REG_ECC_ADDR_RGN_2_EN_SHIFT		1
+#define EMIF_ECC_REG_ECC_ADDR_RGN_2_EN_MASK		(1 << 1)
+#define EMIF_ECC_REG_ECC_ADDR_RGN_1_EN_SHIFT		0
+#define EMIF_ECC_REG_ECC_ADDR_RGN_1_EN_MASK		(1 << 0)
+
+/* EMIF ECC ADDRESS RANGE */
+#define EMIF_ECC_REG_ECC_END_ADDR_SHIFT			16
+#define EMIF_ECC_REG_ECC_END_ADDR_MASK			(0xffff << 16)
+#define EMIF_ECC_REG_ECC_START_ADDR_SHIFT		0
+#define EMIF_ECC_REG_ECC_START_ADDR_MASK		(0xffff << 0)
+
+/* EMIF_SYSTEM_OCP_INTERRUPT_RAW_STATUS */
+#define EMIF_INT_ONEBIT_ECC_ERR_SYS_SHIFT		5
+#define EMIF_INT_ONEBIT_ECC_ERR_SYS_MASK		(1 << 5)
+#define EMIF_INT_TWOBIT_ECC_ERR_SYS_SHIFT		4
+#define EMIF_INT_TWOBIT_ECC_ERR_SYS_MASK		(1 << 4)
+#define EMIF_INT_WR_ECC_ERR_SYS_SHIFT			3
+#define EMIF_INT_WR_ECC_ERR_SYS_MASK			(1 << 3)
+
 /* Reg mapping structure */
 struct emif_reg_struct {
 	u32 emif_mod_id_rev;
@@ -1205,6 +1233,9 @@
 	u32 emif_connect_id_serv_1_map;
 	u32 emif_connect_id_serv_2_map;
 	u32 emif_cos_config;
+	u32 emif_ecc_ctrl_reg;
+	u32 emif_ecc_address_range_1;
+	u32 emif_ecc_address_range_2;
 };
 
 struct lpddr2_mr_regs {
diff --git a/arch/arm/mach-omap2/emif-common.c b/arch/arm/mach-omap2/emif-common.c
index 2b03dbe..e3ef37b 100644
--- a/arch/arm/mach-omap2/emif-common.c
+++ b/arch/arm/mach-omap2/emif-common.c
@@ -17,6 +17,7 @@
 #include <asm/omap_sec_common.h>
 #include <asm/utils.h>
 #include <linux/compiler.h>
+#include <asm/ti-common/ti-edma3.h>
 
 static int emif1_enabled = -1, emif2_enabled = -1;
 
@@ -332,6 +333,71 @@
 	update_hwleveling_output(base, regs);
 }
 
+static void dra7_reset_ddr_data(u32 base, u32 size)
+{
+#if defined(CONFIG_TI_EDMA3) && !defined(CONFIG_DMA)
+	enable_edma3_clocks();
+
+	edma3_fill(EDMA3_BASE, 1, (void *)base, 0, size);
+
+	disable_edma3_clocks();
+#else
+	memset((void *)base, 0, size);
+#endif
+}
+
+static void dra7_enable_ecc(u32 base, const struct emif_regs *regs)
+{
+	struct emif_reg_struct *emif = (struct emif_reg_struct *)base;
+	u32 rgn, size;
+
+	/* ECC available only on dra76x EMIF1 */
+	if ((base != EMIF1_BASE) || !is_dra76x())
+		return;
+
+	if (regs->emif_ecc_ctrl_reg & EMIF_ECC_CTRL_REG_ECC_EN_MASK) {
+		writel(regs->emif_ecc_address_range_1,
+		       &emif->emif_ecc_address_range_1);
+		writel(regs->emif_ecc_address_range_2,
+		       &emif->emif_ecc_address_range_2);
+		writel(regs->emif_ecc_ctrl_reg, &emif->emif_ecc_ctrl_reg);
+
+		/* Set region1 memory with 0 */
+		rgn = ((regs->emif_ecc_address_range_1 &
+			EMIF_ECC_REG_ECC_START_ADDR_MASK) << 16) +
+		       CONFIG_SYS_SDRAM_BASE;
+		size = (regs->emif_ecc_address_range_1 &
+			EMIF_ECC_REG_ECC_END_ADDR_MASK) + 0x10000;
+
+		if (regs->emif_ecc_ctrl_reg &
+		    EMIF_ECC_REG_ECC_ADDR_RGN_1_EN_MASK)
+			dra7_reset_ddr_data(rgn, size);
+
+		/* Set region2 memory with 0 */
+		rgn = ((regs->emif_ecc_address_range_2 &
+			EMIF_ECC_REG_ECC_START_ADDR_MASK) << 16) +
+		       CONFIG_SYS_SDRAM_BASE;
+		size = (regs->emif_ecc_address_range_2 &
+			EMIF_ECC_REG_ECC_END_ADDR_MASK) + 0x10000;
+
+		if (regs->emif_ecc_ctrl_reg &
+		    EMIF_ECC_REG_ECC_ADDR_RGN_2_EN_MASK)
+			dra7_reset_ddr_data(rgn, size);
+
+#ifdef CONFIG_DRA7XX
+		/* Clear the status flags and other history */
+		writel(readl(&emif->emif_1b_ecc_err_cnt),
+		       &emif->emif_1b_ecc_err_cnt);
+		writel(0xffffffff, &emif->emif_1b_ecc_err_dist_1);
+		writel(0x1, &emif->emif_2b_ecc_err_addr_log);
+		writel(EMIF_INT_WR_ECC_ERR_SYS_MASK |
+		       EMIF_INT_TWOBIT_ECC_ERR_SYS_MASK |
+		       EMIF_INT_ONEBIT_ECC_ERR_SYS_MASK,
+		       &emif->emif_irqstatus_sys);
+#endif
+	}
+}
+
 static void dra7_ddr3_init(u32 base, const struct emif_regs *regs)
 {
 	struct emif_reg_struct *emif = (struct emif_reg_struct *)base;
@@ -368,8 +434,29 @@
 
 	writel(regs->ref_ctrl_final, &emif->emif_sdram_ref_ctrl);
 
-	if (regs->emif_rd_wr_lvl_rmp_ctl & EMIF_REG_RDWRLVL_EN_MASK)
+	if (regs->emif_rd_wr_lvl_rmp_ctl & EMIF_REG_RDWRLVL_EN_MASK) {
+		/*
+		 * Perform Dummy ECC setup just to allow hardware
+		 * leveling of ECC memories
+		 */
+		if (is_dra76x() && (base == EMIF1_BASE) &&
+		    (regs->emif_ecc_ctrl_reg & EMIF_ECC_CTRL_REG_ECC_EN_MASK)) {
+			writel(0, &emif->emif_ecc_address_range_1);
+			writel(0, &emif->emif_ecc_address_range_2);
+			writel(EMIF_ECC_CTRL_REG_ECC_EN_MASK |
+			       EMIF_ECC_CTRL_REG_ECC_ADDR_RGN_PROT_MASK,
+			       &emif->emif_ecc_ctrl_reg);
+		}
+
 		dra7_ddr3_leveling(base, regs);
+
+		/* Disable ECC */
+		if (is_dra76x())
+			writel(0, &emif->emif_ecc_ctrl_reg);
+	}
+
+	/* Enable ECC as necessary */
+	dra7_enable_ecc(base, regs);
 }
 
 static void omap5_ddr3_init(u32 base, const struct emif_regs *regs)