arm: emif-common: Add suppport for enabling ECC
authorLokesh Vutla <lokeshvutla@ti.com>
Fri, 29 Dec 2017 06:17:48 +0000 (11:47 +0530)
committerTom Rini <trini@konsulko.com>
Fri, 19 Jan 2018 20:49:25 +0000 (15:49 -0500)
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>
arch/arm/include/asm/emif.h
arch/arm/mach-omap2/emif-common.c

index a661ba9032b0d2ed2b48ce7d2d6378685dd087fa..1924f041d292e99d5d0e90ec70cf02f29d234c29 100644 (file)
 
 #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 @@ struct emif_regs {
        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 {
index 2b03dbecf4283ae377483f476aa576a4369054c6..e3ef37b077762400d8ed8c9cb3011a83f56786d7 100644 (file)
@@ -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 @@ static void dra7_ddr3_leveling(u32 base, const struct emif_regs *regs)
        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 @@ static void dra7_ddr3_init(u32 base, const struct emif_regs *regs)
 
        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)