[U-Boot] [U-boot] [Patch 3/4] keystone2: ecc: add ddr3 error detection and correction support

Ivan Khoronzhuk ivan.khoronzhuk at ti.com
Fri Oct 10 17:17:58 CEST 2014


From: Vitaly Andrianov <vitalya at ti.com>

This patch adds the DDR3 ECC support to enable ECC in the DDR3
EMIF controller for Keystone II devices.

By default, ECC will only be enabled if RMW is supported in the
DDR EMIF controller. The entire DDR memory will be scrubbed to
zero using an EDMA channel after ECC is enabled and before
u-boot is re-located to DDR memory.

An ecc_test environment variable is added for ECC testing.
If ecc_test is set to 0, a detection of 2-bit error will reset
the device, if ecc_test is set to 1, 2-bit error detection
will not reset the device, user can still boot the kernel to
check the ECC error handling in kernel.

Signed-off-by: Hao Zhang <hzhang at ti.com>
Signed-off-by: Vitaly Andrianov <vitalya at ti.com>
Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk at ti.com>
---
 arch/arm/cpu/armv7/keystone/ddr3.c                 | 244 +++++++++++++++++++++
 arch/arm/include/asm/arch-keystone/ddr3.h          |   6 +
 arch/arm/include/asm/arch-keystone/hardware-k2hk.h |   4 +
 arch/arm/include/asm/arch-keystone/hardware.h      |  46 ++++
 board/ti/ks2_evm/board.c                           |   3 +
 board/ti/ks2_evm/ddr3_k2hk.c                       |  16 ++
 6 files changed, 319 insertions(+)

diff --git a/arch/arm/cpu/armv7/keystone/ddr3.c b/arch/arm/cpu/armv7/keystone/ddr3.c
index 2eabec1..923906a 100644
--- a/arch/arm/cpu/armv7/keystone/ddr3.c
+++ b/arch/arm/cpu/armv7/keystone/ddr3.c
@@ -9,9 +9,19 @@
 
 #include <asm/io.h>
 #include <common.h>
+#include <asm/arch/msmc.h>
 #include <asm/arch/ddr3.h>
 #include <asm/arch/psc_defs.h>
 
+#include <asm/ti-common/ti-edma3.h>
+
+#define DDR3_EDMA_BLK_SIZE_SHIFT	10
+#define DDR3_EDMA_BLK_SIZE		(1 << DDR3_EDMA_BLK_SIZE_SHIFT)
+#define DDR3_EDMA_BCNT			0x8000
+#define DDR3_EDMA_CCNT			1
+#define DDR3_EDMA_XF_SIZE		(DDR3_EDMA_BLK_SIZE * DDR3_EDMA_BCNT)
+#define DDR3_EDMA_SLOT_NUM		1
+
 void ddr3_init_ddrphy(u32 base, struct ddr3_phy_config *phy_cfg)
 {
 	unsigned int tmp;
@@ -70,6 +80,240 @@ void ddr3_init_ddremif(u32 base, struct ddr3_emif_config *emif_cfg)
 	__raw_writel(emif_cfg->sdrfc,  base + KS2_DDR3_SDRFC_OFFSET);
 }
 
+int ddr3_ecc_support_rmw(u32 base)
+{
+	u32 value = __raw_readl(base + KS2_DDR3_MIDR_OFFSET);
+
+	/* Check the DDR3 controller ID reg if the controllers
+	   supports ECC RMW or not */
+	if (value == 0x40461C02)
+		return 1;
+
+	return 0;
+}
+
+static void ddr3_ecc_config(u32 base, u32 value)
+{
+	u32 data;
+
+	__raw_writel(value,  base + KS2_DDR3_ECC_CTRL_OFFSET);
+	udelay(100000); /* delay required to synchronize across clock domains */
+
+	if (value & KS2_DDR3_ECC_EN) {
+		/* Clear the 1-bit error count */
+		data = __raw_readl(base + KS2_DDR3_ONE_BIT_ECC_ERR_CNT_OFFSET);
+		__raw_writel(data, base + KS2_DDR3_ONE_BIT_ECC_ERR_CNT_OFFSET);
+
+		/* enable the ECC interrupt */
+		__raw_writel(KS2_DDR3_1B_ECC_ERR_SYS | KS2_DDR3_2B_ECC_ERR_SYS |
+			     KS2_DDR3_WR_ECC_ERR_SYS,
+			     base + KS2_DDR3_ECC_INT_ENABLE_SET_SYS_OFFSET);
+
+		/* Clear the ECC error interrupt status */
+		__raw_writel(KS2_DDR3_1B_ECC_ERR_SYS | KS2_DDR3_2B_ECC_ERR_SYS |
+			     KS2_DDR3_WR_ECC_ERR_SYS,
+			     base + KS2_DDR3_ECC_INT_STATUS_OFFSET);
+	}
+}
+
+static void ddr3_reset_data(u32 base, u32 ddr3_size)
+{
+	u32 mpax[2];
+	u32 seg_num;
+	u32 seg, blks, dst, edma_blks;
+	struct edma3_slot_config slot;
+	struct edma3_channel_config edma_channel;
+	u32 edma_src[DDR3_EDMA_BLK_SIZE/4] __aligned(16) = {0, };
+
+	/* Setup an edma to copy the 1k block to the entire DDR */
+	puts("\nClear entire DDR3 memory to enable ECC\n");
+
+	/* save the SES MPAX regs */
+	msmc_get_ses_mpax(8, 0, mpax);
+
+	/* setup edma slot 1 configuration */
+	slot.opt = EDMA3_SLOPT_TRANS_COMP_INT_ENB |
+		   EDMA3_SLOPT_COMP_CODE(0) |
+		   EDMA3_SLOPT_STATIC | EDMA3_SLOPT_AB_SYNC;
+	slot.bcnt = DDR3_EDMA_BCNT;
+	slot.acnt = DDR3_EDMA_BLK_SIZE;
+	slot.ccnt = DDR3_EDMA_CCNT;
+	slot.src_bidx = 0;
+	slot.dst_bidx = DDR3_EDMA_BLK_SIZE;
+	slot.src_cidx = 0;
+	slot.dst_cidx = 0;
+	slot.link = EDMA3_PARSET_NULL_LINK;
+	slot.bcntrld = 0;
+	edma3_slot_configure(KS2_EDMA0_BASE, DDR3_EDMA_SLOT_NUM, &slot);
+
+	/* configure quik edma channel */
+	edma_channel.slot = DDR3_EDMA_SLOT_NUM;
+	edma_channel.chnum = 0;
+	edma_channel.complete_code = 0;
+	/* event trigger after dst update */
+	edma_channel.trigger_slot_word = EDMA3_TWORD(dst);
+	qedma3_start(KS2_EDMA0_BASE, &edma_channel);
+
+	/* DDR3 size in segments (4KB seg size) */
+	seg_num = ddr3_size << (30 - KS2_MSMC_SEG_SIZE_SHIFT);
+
+	for (seg = 0; seg < seg_num; seg += KS2_MSMC_MAP_SEG_NUM) {
+		/* map 2GB 36-bit DDR address to 32-bit DDR address in EMIF
+		   access slave interface so that edma driver can access */
+		msmc_map_ses_segment(8, 0, base >> KS2_MSMC_SEG_SIZE_SHIFT,
+				     KS2_MSMC_DST_SEG_BASE + seg, MPAX_SEG_2G);
+
+		if ((seg_num - seg) > KS2_MSMC_MAP_SEG_NUM)
+			edma_blks = KS2_MSMC_MAP_SEG_NUM <<
+					(KS2_MSMC_SEG_SIZE_SHIFT
+					- DDR3_EDMA_BLK_SIZE_SHIFT);
+		else
+			edma_blks = (seg_num - seg) << (KS2_MSMC_SEG_SIZE_SHIFT
+					- DDR3_EDMA_BLK_SIZE_SHIFT);
+
+		/* Use edma driver to scrub 2GB DDR memory */
+		for (dst = base, blks = 0; blks < edma_blks;
+		     blks += DDR3_EDMA_BCNT, dst += DDR3_EDMA_XF_SIZE) {
+			edma3_set_src_addr(KS2_EDMA0_BASE,
+					   edma_channel.slot, (u32)edma_src);
+			edma3_set_dest_addr(KS2_EDMA0_BASE,
+					    edma_channel.slot, (u32)dst);
+
+			while (edma3_check_for_transfer(KS2_EDMA0_BASE,
+							&edma_channel))
+				udelay(10);
+		}
+	}
+
+	qedma3_stop(KS2_EDMA0_BASE, &edma_channel);
+
+	/* restore the SES MPAX regs */
+	msmc_set_ses_mpax(8, 0, mpax);
+}
+
+static void ddr3_ecc_init_range(u32 base)
+{
+	u32 ecc_val = KS2_DDR3_ECC_EN;
+	u32 rmw = ddr3_ecc_support_rmw(base);
+
+	if (rmw)
+		ecc_val |= KS2_DDR3_ECC_RMW_EN;
+
+	__raw_writel(0, base + KS2_DDR3_ECC_ADDR_RANGE1_OFFSET);
+
+	ddr3_ecc_config(base, ecc_val);
+}
+
+void ddr3_enable_ecc(u32 base, int test)
+{
+	u32 ecc_val = KS2_DDR3_ECC_ENABLE;
+	u32 rmw = ddr3_ecc_support_rmw(base);
+
+	if (test)
+		ecc_val |= KS2_DDR3_ECC_ADDR_RNG_1_EN;
+
+	if (!rmw) {
+		if (!test)
+			/* by default, disable ecc when rmw = 0 and no
+			   ecc test */
+			ecc_val = 0;
+	} else {
+		ecc_val |= KS2_DDR3_ECC_RMW_EN;
+	}
+
+	ddr3_ecc_config(base, ecc_val);
+}
+
+void ddr3_disable_ecc(u32 base)
+{
+	ddr3_ecc_config(base, 0);
+}
+
+#if defined(CONFIG_SOC_K2HK) || defined(CONFIG_SOC_K2L)
+static void cic_init(u32 base)
+{
+	/* Disable CIC global interrupts */
+	__raw_writel(0, base + KS2_CIC_GLOBAL_ENABLE);
+
+	/* Set to normal mode, no nesting, no priority hold */
+	__raw_writel(0, base + KS2_CIC_CTRL);
+	__raw_writel(0, base + KS2_CIC_HOST_CTRL);
+
+	/* Enable CIC global interrupts */
+	__raw_writel(1, base + KS2_CIC_GLOBAL_ENABLE);
+}
+
+static void cic_map_cic_to_gic(u32 base, u32 chan_num, u32 irq_num)
+{
+	/* Map the system interrupt to a CIC channel */
+	__raw_writeb(chan_num, base + KS2_CIC_CHAN_MAP(0) + irq_num);
+
+	/* Enable CIC system interrupt */
+	__raw_writel(irq_num, base + KS2_CIC_SYS_ENABLE_IDX_SET);
+
+	/* Enable CIC Host interrupt */
+	__raw_writel(chan_num, base + KS2_CIC_HOST_ENABLE_IDX_SET);
+}
+
+static void ddr3_map_ecc_cic2_irq(u32 base)
+{
+	cic_init(base);
+	cic_map_cic_to_gic(base, KS2_CIC2_DDR3_ECC_CHAN_NUM,
+			   KS2_CIC2_DDR3_ECC_IRQ_NUM);
+}
+#endif
+
+void ddr3_init_ecc(u32 base)
+{
+	u32 ddr3_size;
+
+	if (!ddr3_ecc_support_rmw(base)) {
+		ddr3_disable_ecc(base);
+		return;
+	}
+
+	ddr3_ecc_init_range(base);
+	ddr3_size = ddr3_get_size();
+	ddr3_reset_data(CONFIG_SYS_SDRAM_BASE, ddr3_size);
+
+	/* mapping DDR3 ECC system interrupt from CIC2 to GIC */
+#if defined(CONFIG_SOC_K2HK) || defined(CONFIG_SOC_K2L)
+	ddr3_map_ecc_cic2_irq(KS2_CIC2_BASE);
+#endif
+	ddr3_enable_ecc(base, 0);
+}
+
+void ddr3_check_ecc_int(u32 base)
+{
+	char *env;
+	int ecc_test = 0;
+	u32 value = __raw_readl(base + KS2_DDR3_ECC_INT_STATUS_OFFSET);
+
+	env = getenv("ecc_test");
+	if (env)
+		ecc_test = simple_strtol(env, NULL, 0);
+
+	if (value & KS2_DDR3_WR_ECC_ERR_SYS)
+		puts("DDR3 ECC write error interrupted\n");
+
+	if (value & KS2_DDR3_2B_ECC_ERR_SYS) {
+		puts("DDR3 ECC 2-bit error interrupted\n");
+
+		if (!ecc_test) {
+			puts("Reseting the device ...\n");
+			reset_cpu(0);
+		}
+	}
+
+	value = __raw_readl(base + KS2_DDR3_ONE_BIT_ECC_ERR_CNT_OFFSET);
+	if (value) {
+		printf("1-bit ECC err count: 0x%x\n", value);
+		value = __raw_readl(base +
+				    KS2_DDR3_ONE_BIT_ECC_ERR_ADDR_LOG_OFFSET);
+		printf("1-bit ECC err address log: 0x%x\n", value);
+	}
+}
+
 void ddr3_reset_ddrphy(void)
 {
 	u32 tmp;
diff --git a/arch/arm/include/asm/arch-keystone/ddr3.h b/arch/arm/include/asm/arch-keystone/ddr3.h
index 6bf35d3..b044d6f 100644
--- a/arch/arm/include/asm/arch-keystone/ddr3.h
+++ b/arch/arm/include/asm/arch-keystone/ddr3.h
@@ -49,8 +49,14 @@ struct ddr3_emif_config {
 };
 
 void ddr3_init(void);
+int ddr3_get_size(void);
 void ddr3_reset_ddrphy(void);
+void ddr3_init_ecc(u32 base);
+void ddr3_disable_ecc(u32 base);
+void ddr3_check_ecc_int(u32 base);
+int ddr3_ecc_support_rmw(u32 base);
 void ddr3_err_reset_workaround(void);
+void ddr3_enable_ecc(u32 base, int test);
 void ddr3_init_ddrphy(u32 base, struct ddr3_phy_config *phy_cfg);
 void ddr3_init_ddremif(u32 base, struct ddr3_emif_config *emif_cfg);
 
diff --git a/arch/arm/include/asm/arch-keystone/hardware-k2hk.h b/arch/arm/include/asm/arch-keystone/hardware-k2hk.h
index 28de3f5..5a9ea4f 100644
--- a/arch/arm/include/asm/arch-keystone/hardware-k2hk.h
+++ b/arch/arm/include/asm/arch-keystone/hardware-k2hk.h
@@ -79,6 +79,10 @@
 #define KS2_DDR3B_EMIF_DATA_BASE	0x60000000
 #define KS2_DDR3B_DDRPHYC		0x02328000
 
+#define KS2_CIC2_DDR3_ECC_IRQ_NUM	0x0D3 /* DDR3 ECC system irq number */
+#define KS2_CIC2_DDR3_ECC_CHAN_NUM	0x01D /* DDR3 ECC int mapped to CIC2
+						 channel 29 */
+
 /* SGMII SerDes */
 #define KS2_LANES_PER_SGMII_SERDES	4
 
diff --git a/arch/arm/include/asm/arch-keystone/hardware.h b/arch/arm/include/asm/arch-keystone/hardware.h
index d2bd6cb..3e54998 100644
--- a/arch/arm/include/asm/arch-keystone/hardware.h
+++ b/arch/arm/include/asm/arch-keystone/hardware.h
@@ -87,6 +87,52 @@ typedef volatile unsigned int   *dv_reg_p;
 
 #define KS2_DDR3_PLLCTRL_PHY_RESET	0x80000000
 
+/* DDR3 ECC */
+#define KS2_DDR3_ECC_INT_STATUS_OFFSET			0x0AC
+#define KS2_DDR3_ECC_INT_ENABLE_SET_SYS_OFFSET		0x0B4
+#define KS2_DDR3_ECC_CTRL_OFFSET			0x110
+#define KS2_DDR3_ECC_ADDR_RANGE1_OFFSET			0x114
+#define KS2_DDR3_ONE_BIT_ECC_ERR_CNT_OFFSET		0x130
+#define KS2_DDR3_ONE_BIT_ECC_ERR_ADDR_LOG_OFFSET	0x13C
+
+/* DDR3 ECC Interrupt Status register */
+#define KS2_DDR3_1B_ECC_ERR_SYS		BIT(5)
+#define KS2_DDR3_2B_ECC_ERR_SYS		BIT(4)
+#define KS2_DDR3_WR_ECC_ERR_SYS		BIT(3)
+
+/* DDR3 ECC Control register */
+#define KS2_DDR3_ECC_EN			BIT(31)
+#define KS2_DDR3_ECC_ADDR_RNG_PROT	BIT(30)
+#define KS2_DDR3_ECC_VERIFY_EN		BIT(29)
+#define KS2_DDR3_ECC_RMW_EN		BIT(28)
+#define KS2_DDR3_ECC_ADDR_RNG_1_EN	BIT(0)
+
+#define KS2_DDR3_ECC_ENABLE		(KS2_DDR3_ECC_EN | \
+					KS2_DDR3_ECC_ADDR_RNG_PROT | \
+					KS2_DDR3_ECC_VERIFY_EN)
+
+/* EDMA */
+#define KS2_EDMA0_BASE			0x02700000
+
+/* EDMA3 register offsets */
+#define KS2_EDMA_QCHMAP0		0x0200
+#define KS2_EDMA_IPR			0x1068
+#define KS2_EDMA_ICR			0x1070
+#define KS2_EDMA_QEECR			0x1088
+#define KS2_EDMA_QEESR			0x108c
+#define KS2_EDMA_PARAM_1(x)		(0x4020 + (4 * x))
+
+/* Chip Interrupt Controller */
+#define KS2_CIC2_BASE			0x02608000
+
+/* Chip Interrupt Controller register offsets */
+#define KS2_CIC_CTRL			0x04
+#define KS2_CIC_HOST_CTRL		0x0C
+#define KS2_CIC_GLOBAL_ENABLE		0x10
+#define KS2_CIC_SYS_ENABLE_IDX_SET	0x28
+#define KS2_CIC_HOST_ENABLE_IDX_SET	0x34
+#define KS2_CIC_CHAN_MAP(n)		(0x0400 + (n << 2))
+
 #define KS2_UART0_BASE                	0x02530c00
 #define KS2_UART1_BASE                	0x02531000
 
diff --git a/board/ti/ks2_evm/board.c b/board/ti/ks2_evm/board.c
index cdfca23..ffb0c7e 100644
--- a/board/ti/ks2_evm/board.c
+++ b/board/ti/ks2_evm/board.c
@@ -39,6 +39,7 @@ int dram_init(void)
 	gd->ram_size = get_ram_size((long *)CONFIG_SYS_SDRAM_BASE,
 				    CONFIG_MAX_RAM_BANK_SIZE);
 	aemif_init(ARRAY_SIZE(aemif_configs), aemif_configs);
+	ddr3_init_ecc(KS2_DDR3A_EMIF_CTRL_BASE);
 	return 0;
 }
 
@@ -234,5 +235,7 @@ void ft_board_setup_ex(void *blob, bd_t *bd)
 			reserve_start += 2;
 		}
 	}
+
+	ddr3_check_ecc_int(KS2_DDR3A_EMIF_CTRL_BASE);
 }
 #endif
diff --git a/board/ti/ks2_evm/ddr3_k2hk.c b/board/ti/ks2_evm/ddr3_k2hk.c
index 6070a99..a1c3d05 100644
--- a/board/ti/ks2_evm/ddr3_k2hk.c
+++ b/board/ti/ks2_evm/ddr3_k2hk.c
@@ -12,6 +12,8 @@
 #include <asm/arch/ddr3.h>
 #include <asm/arch/hardware.h>
 
+static int ddr3_size;
+
 struct pll_init_data ddr3a_333 = DDR3_PLL_333(A);
 struct pll_init_data ddr3a_400 = DDR3_PLL_400(A);
 
@@ -44,12 +46,14 @@ void ddr3_init(void)
 			ddr3_init_ddremif(KS2_DDR3A_EMIF_CTRL_BASE,
 					  &ddr3_1600_8g);
 			printf("DRAM:  Capacity 8 GiB (includes reported below)\n");
+			ddr3_size = 8;
 		} else {
 			ddr3_init_ddrphy(KS2_DDR3A_DDRPHYC, &ddr3phy_1600_8g);
 			ddr3_1600_8g.sdcfg |= 0x1000;
 			ddr3_init_ddremif(KS2_DDR3A_EMIF_CTRL_BASE,
 					  &ddr3_1600_8g);
 			printf("DRAM:  Capacity 4 GiB (includes reported below)\n");
+			ddr3_size = 4;
 		}
 	} else if (!strcmp(dimm_name, "SQR-SD3T-2G1333SED")) {
 		init_pll(&ddr3a_333);
@@ -70,11 +74,15 @@ void ddr3_init(void)
 			}
 			ddr3_init_ddremif(KS2_DDR3A_EMIF_CTRL_BASE,
 					  &ddr3_1333_2g);
+			ddr3_size = 2;
+			printf("DRAM:  2 GiB");
 		} else {
 			ddr3_init_ddrphy(KS2_DDR3A_DDRPHYC, &ddr3phy_1333_2g);
 			ddr3_1333_2g.sdcfg |= 0x1000;
 			ddr3_init_ddremif(KS2_DDR3A_EMIF_CTRL_BASE,
 					  &ddr3_1333_2g);
+			ddr3_size = 1;
+			printf("DRAM:  1 GiB");
 		}
 	} else {
 		printf("Unknown SO-DIMM. Cannot configure DDR3\n");
@@ -86,3 +94,11 @@ void ddr3_init(void)
 	if (cpu_revision() <= 1)
 		ddr3_err_reset_workaround();
 }
+
+/**
+ * ddr3_get_size - return ddr3 size in GiB
+ */
+int ddr3_get_size(void)
+{
+	return ddr3_size;
+}
-- 
1.8.3.2



More information about the U-Boot mailing list