1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2010
4  * Eastman Kodak Company, <www.kodak.com>
5  * Michael Zaidman, <michael.zaidman@kodak.com>
6  *
7  * The code is based on the cpu/mpc83xx/ecc.c written by
8  * Dave Liu <daveliu@freescale.com>
9  */
10 
11 #include <common.h>
12 #include <cpu_func.h>
13 #include <irq_func.h>
14 #include <log.h>
15 #include <mpc83xx.h>
16 #include <watchdog.h>
17 #include <asm/io.h>
18 #include <post.h>
19 
20 #if CONFIG_POST & CONFIG_SYS_POST_ECC
21 /*
22  * We use the RAW I/O accessors where possible in order to
23  * achieve performance goal, since the test's execution time
24  * affects the board start up time.
25  */
ecc_clear(ddr83xx_t * ddr)26 static inline void ecc_clear(ddr83xx_t *ddr)
27 {
28 	/* Clear capture registers */
29 	__raw_writel(0, &ddr->capture_address);
30 	__raw_writel(0, &ddr->capture_data_hi);
31 	__raw_writel(0, &ddr->capture_data_lo);
32 	__raw_writel(0, &ddr->capture_ecc);
33 	__raw_writel(0, &ddr->capture_attributes);
34 
35 	/* Clear SBEC and set SBET to 1 */
36 	out_be32(&ddr->err_sbe, 1 << ECC_ERROR_MAN_SBET_SHIFT);
37 
38 	/* Clear Error Detect register */
39 	out_be32(&ddr->err_detect, ECC_ERROR_DETECT_MME |\
40 			ECC_ERROR_DETECT_MBE |\
41 			ECC_ERROR_DETECT_SBE |\
42 			ECC_ERROR_DETECT_MSE);
43 
44 	isync();
45 }
46 
ecc_post_test(int flags)47 int ecc_post_test(int flags)
48 {
49 	int ret = 0;
50 	int int_state;
51 	int errbit;
52 	u32 pattern[2], writeback[2], retval[2];
53 	ddr83xx_t *ddr = &((immap_t *)CONFIG_SYS_IMMR)->ddr;
54 	volatile u64 *addr = (u64 *)CONFIG_SYS_POST_ECC_START_ADDR;
55 
56 	/* The pattern is written into memory to generate error */
57 	pattern[0] = 0xfedcba98UL;
58 	pattern[1] = 0x76543210UL;
59 
60 	/* After injecting error, re-initialize the memory with the value */
61 	writeback[0] = ~pattern[0];
62 	writeback[1] = ~pattern[1];
63 
64 	/* Check if ECC is enabled */
65 	if (__raw_readl(&ddr->err_disable) & ECC_ERROR_ENABLE) {
66 		debug("DDR's ECC is not enabled, skipping the ECC POST.\n");
67 		return 0;
68 	}
69 
70 	int_state = disable_interrupts();
71 	icache_enable();
72 
73 #ifdef CONFIG_DDR_32BIT
74 	/* It seems like no one really uses the CONFIG_DDR_32BIT mode */
75 #error "Add ECC POST support for CONFIG_DDR_32BIT here!"
76 #else
77 	for (addr = (u64*)CONFIG_SYS_POST_ECC_START_ADDR, errbit=0;
78 	     addr < (u64*)CONFIG_SYS_POST_ECC_STOP_ADDR; addr++, errbit++ ) {
79 
80 		WATCHDOG_RESET();
81 
82 		ecc_clear(ddr);
83 
84 		/* Enable error injection */
85 		setbits_be32(&ddr->ecc_err_inject, ECC_ERR_INJECT_EIEN);
86 		sync();
87 		isync();
88 
89 		/* Set bit to be injected */
90 		if (errbit < 32) {
91 			__raw_writel(1 << errbit, &ddr->data_err_inject_lo);
92 			__raw_writel(0, &ddr->data_err_inject_hi);
93 		} else {
94 			__raw_writel(0, &ddr->data_err_inject_lo);
95 			__raw_writel(1<<(errbit-32), &ddr->data_err_inject_hi);
96 		}
97 		sync();
98 		isync();
99 
100 		/* Write memory location injecting SBE */
101 		ppcDWstore((u32*)addr, pattern);
102 		sync();
103 
104 		/* Disable error injection */
105 		clrbits_be32(&ddr->ecc_err_inject, ECC_ERR_INJECT_EIEN);
106 		sync();
107 		isync();
108 
109 		/* Data read should generate SBE */
110 		ppcDWload((u32*)addr, retval);
111 		sync();
112 
113 		if (!(__raw_readl(&ddr->err_detect) & ECC_ERROR_DETECT_SBE) ||
114 			(__raw_readl(&ddr->data_err_inject_hi) !=
115 			(__raw_readl(&ddr->capture_data_hi) ^ pattern[0])) ||
116 			(__raw_readl(&ddr->data_err_inject_lo) !=
117 			(__raw_readl(&ddr->capture_data_lo) ^ pattern[1]))) {
118 
119 			post_log("ECC failed to detect SBE error at %08x, "
120 				"SBE injection mask %08x-%08x, wrote "
121 				"%08x-%08x, read %08x-%08x\n", addr,
122 				ddr->data_err_inject_hi,
123 				ddr->data_err_inject_lo,
124 				pattern[0], pattern[1],
125 				retval[0], retval[1]);
126 
127 			printf("ERR_DETECT Reg: %08x\n", ddr->err_detect);
128 			printf("ECC CAPTURE_DATA Reg: %08x-%08x\n",
129 				ddr->capture_data_hi, ddr->capture_data_lo);
130 			ret = 1;
131 			break;
132 		}
133 
134 		/* Re-initialize the ECC memory */
135 		ppcDWstore((u32*)addr, writeback);
136 		sync();
137 		isync();
138 
139 		errbit %= 63;
140 	}
141 #endif /* !CONFIG_DDR_32BIT */
142 
143 	ecc_clear(ddr);
144 
145 	icache_disable();
146 
147 	if (int_state)
148 		enable_interrupts();
149 
150 	return ret;
151 }
152 #endif
153