]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
26459488 | 2 | /* |
5cd9661d | 3 | * EMIF: DDR3 test commands |
26459488 | 4 | * |
5cd9661d | 5 | * Copyright (C) 2012-2017 Texas Instruments Incorporated, <www.ti.com> |
26459488 HZ |
6 | */ |
7 | ||
9edefc27 | 8 | #include <cpu_func.h> |
09140113 | 9 | #include <env.h> |
9b4a205f | 10 | #include <init.h> |
26459488 | 11 | #include <asm/arch/hardware.h> |
8a8af8a2 LV |
12 | #include <asm/cache.h> |
13 | #include <asm/emif.h> | |
26459488 HZ |
14 | #include <common.h> |
15 | #include <command.h> | |
16 | ||
17 | DECLARE_GLOBAL_DATA_PTR; | |
18 | ||
8a8af8a2 LV |
19 | #ifdef CONFIG_ARCH_KEYSTONE |
20 | #include <asm/arch/ddr3.h> | |
26459488 | 21 | #define DDR_MIN_ADDR CONFIG_SYS_SDRAM_BASE |
01abae4d | 22 | #define STACKSIZE (512 << 10) /* 512 KiB */ |
26459488 HZ |
23 | |
24 | #define DDR_REMAP_ADDR 0x80000000 | |
25 | #define ECC_START_ADDR1 ((DDR_MIN_ADDR - DDR_REMAP_ADDR) >> 17) | |
26 | ||
27 | #define ECC_END_ADDR1 (((gd->start_addr_sp - DDR_REMAP_ADDR - \ | |
01abae4d | 28 | STACKSIZE) >> 17) - 2) |
8a8af8a2 | 29 | #endif |
26459488 HZ |
30 | |
31 | #define DDR_TEST_BURST_SIZE 1024 | |
32 | ||
33 | static int ddr_memory_test(u32 start_address, u32 end_address, int quick) | |
34 | { | |
35 | u32 index_start, value, index; | |
36 | ||
37 | index_start = start_address; | |
38 | ||
39 | while (1) { | |
40 | /* Write a pattern */ | |
41 | for (index = index_start; | |
42 | index < index_start + DDR_TEST_BURST_SIZE; | |
43 | index += 4) | |
44 | __raw_writel(index, index); | |
45 | ||
46 | /* Read and check the pattern */ | |
47 | for (index = index_start; | |
48 | index < index_start + DDR_TEST_BURST_SIZE; | |
49 | index += 4) { | |
50 | value = __raw_readl(index); | |
51 | if (value != index) { | |
52 | printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n", | |
53 | index, value, __raw_readl(index)); | |
54 | ||
55 | return -1; | |
56 | } | |
57 | } | |
58 | ||
59 | index_start += DDR_TEST_BURST_SIZE; | |
60 | if (index_start >= end_address) | |
61 | break; | |
62 | ||
63 | if (quick) | |
64 | continue; | |
65 | ||
66 | /* Write a pattern for complementary values */ | |
67 | for (index = index_start; | |
68 | index < index_start + DDR_TEST_BURST_SIZE; | |
69 | index += 4) | |
70 | __raw_writel((u32)~index, index); | |
71 | ||
72 | /* Read and check the pattern */ | |
73 | for (index = index_start; | |
74 | index < index_start + DDR_TEST_BURST_SIZE; | |
75 | index += 4) { | |
76 | value = __raw_readl(index); | |
77 | if (value != ~index) { | |
78 | printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n", | |
79 | index, value, __raw_readl(index)); | |
80 | ||
81 | return -1; | |
82 | } | |
83 | } | |
84 | ||
85 | index_start += DDR_TEST_BURST_SIZE; | |
86 | if (index_start >= end_address) | |
87 | break; | |
88 | ||
89 | /* Write a pattern */ | |
90 | for (index = index_start; | |
91 | index < index_start + DDR_TEST_BURST_SIZE; | |
92 | index += 2) | |
93 | __raw_writew((u16)index, index); | |
94 | ||
95 | /* Read and check the pattern */ | |
96 | for (index = index_start; | |
97 | index < index_start + DDR_TEST_BURST_SIZE; | |
98 | index += 2) { | |
99 | value = __raw_readw(index); | |
100 | if (value != (u16)index) { | |
101 | printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n", | |
102 | index, value, __raw_readw(index)); | |
103 | ||
104 | return -1; | |
105 | } | |
106 | } | |
107 | ||
108 | index_start += DDR_TEST_BURST_SIZE; | |
109 | if (index_start >= end_address) | |
110 | break; | |
111 | ||
112 | /* Write a pattern */ | |
113 | for (index = index_start; | |
114 | index < index_start + DDR_TEST_BURST_SIZE; | |
115 | index += 1) | |
116 | __raw_writeb((u8)index, index); | |
117 | ||
118 | /* Read and check the pattern */ | |
119 | for (index = index_start; | |
120 | index < index_start + DDR_TEST_BURST_SIZE; | |
121 | index += 1) { | |
122 | value = __raw_readb(index); | |
123 | if (value != (u8)index) { | |
124 | printf("ddr_memory_test: Failed at address index = 0x%x value = 0x%x *(index) = 0x%x\n", | |
125 | index, value, __raw_readb(index)); | |
126 | ||
127 | return -1; | |
128 | } | |
129 | } | |
130 | ||
131 | index_start += DDR_TEST_BURST_SIZE; | |
132 | if (index_start >= end_address) | |
133 | break; | |
134 | } | |
135 | ||
136 | puts("ddr memory test PASSED!\n"); | |
137 | return 0; | |
138 | } | |
139 | ||
140 | static int ddr_memory_compare(u32 address1, u32 address2, u32 size) | |
141 | { | |
142 | u32 index, value, index2, value2; | |
143 | ||
144 | for (index = address1, index2 = address2; | |
145 | index < address1 + size; | |
146 | index += 4, index2 += 4) { | |
147 | value = __raw_readl(index); | |
148 | value2 = __raw_readl(index2); | |
149 | ||
150 | if (value != value2) { | |
151 | printf("ddr_memory_test: Compare failed at address = 0x%x value = 0x%x, address2 = 0x%x value2 = 0x%x\n", | |
152 | index, value, index2, value2); | |
153 | ||
154 | return -1; | |
155 | } | |
156 | } | |
157 | ||
158 | puts("ddr memory compare PASSED!\n"); | |
159 | return 0; | |
160 | } | |
161 | ||
8a8af8a2 LV |
162 | static void ddr_check_ecc_status(void) |
163 | { | |
164 | struct emif_reg_struct *emif = (struct emif_reg_struct *)EMIF1_BASE; | |
165 | u32 err_1b = readl(&emif->emif_1b_ecc_err_cnt); | |
166 | u32 int_status = readl(&emif->emif_irqstatus_raw_sys); | |
167 | int ecc_test = 0; | |
168 | char *env; | |
169 | ||
170 | env = env_get("ecc_test"); | |
171 | if (env) | |
172 | ecc_test = simple_strtol(env, NULL, 0); | |
173 | ||
174 | puts("ECC test Status:\n"); | |
175 | if (int_status & EMIF_INT_WR_ECC_ERR_SYS_MASK) | |
176 | puts("\tECC test: DDR ECC write error interrupted\n"); | |
177 | ||
178 | if (int_status & EMIF_INT_TWOBIT_ECC_ERR_SYS_MASK) | |
179 | if (!ecc_test) | |
180 | panic("\tECC test: DDR ECC 2-bit error interrupted"); | |
181 | ||
182 | if (int_status & EMIF_INT_ONEBIT_ECC_ERR_SYS_MASK) | |
183 | puts("\tECC test: DDR ECC 1-bit error interrupted\n"); | |
184 | ||
185 | if (err_1b) | |
186 | printf("\tECC test: 1-bit ECC err count: 0x%x\n", err_1b); | |
187 | } | |
188 | ||
189 | static int ddr_memory_ecc_err(u32 addr, u32 ecc_err) | |
26459488 | 190 | { |
8a8af8a2 LV |
191 | struct emif_reg_struct *emif = (struct emif_reg_struct *)EMIF1_BASE; |
192 | u32 ecc_ctrl = readl(&emif->emif_ecc_ctrl_reg); | |
193 | u32 val1, val2, val3; | |
194 | ||
195 | debug("Disabling D-Cache before ECC test\n"); | |
196 | dcache_disable(); | |
197 | invalidate_dcache_all(); | |
26459488 | 198 | |
8a8af8a2 LV |
199 | puts("Testing DDR ECC:\n"); |
200 | puts("\tECC test: Disabling DDR ECC ...\n"); | |
201 | writel(0, &emif->emif_ecc_ctrl_reg); | |
26459488 | 202 | |
8a8af8a2 LV |
203 | val1 = readl(addr); |
204 | val2 = val1 ^ ecc_err; | |
205 | writel(val2, addr); | |
26459488 | 206 | |
8a8af8a2 | 207 | val3 = readl(addr); |
8a8af8a2 LV |
208 | #ifdef CONFIG_ARCH_KEYSTONE |
209 | ecc_ctrl = ECC_START_ADDR1 | (ECC_END_ADDR1 << 16); | |
210 | writel(ecc_ctrl, EMIF1_BASE + KS2_DDR3_ECC_ADDR_RANGE1_OFFSET); | |
211 | ddr3_enable_ecc(EMIF1_BASE, 1); | |
212 | #else | |
213 | writel(ecc_ctrl, &emif->emif_ecc_ctrl_reg); | |
214 | #endif | |
26459488 | 215 | |
92ffd0d9 KB |
216 | printf("\tECC test: addr 0x%x, read data 0x%x, written data 0x%x, err pattern: 0x%x, read after write data 0x%x\n", |
217 | addr, val1, val2, ecc_err, val3); | |
218 | ||
219 | puts("\tECC test: Enabled DDR ECC ...\n"); | |
220 | ||
8a8af8a2 LV |
221 | val1 = readl(addr); |
222 | printf("\tECC test: addr 0x%x, read data 0x%x\n", addr, val1); | |
26459488 | 223 | |
8a8af8a2 LV |
224 | ddr_check_ecc_status(); |
225 | ||
226 | debug("Enabling D-cache back after ECC test\n"); | |
227 | enable_caches(); | |
26459488 | 228 | |
26459488 HZ |
229 | return 0; |
230 | } | |
231 | ||
8a8af8a2 LV |
232 | static int is_addr_valid(u32 addr) |
233 | { | |
234 | struct emif_reg_struct *emif = (struct emif_reg_struct *)EMIF1_BASE; | |
235 | u32 start_addr, end_addr, range, ecc_ctrl; | |
236 | ||
237 | #ifdef CONFIG_ARCH_KEYSTONE | |
238 | ecc_ctrl = EMIF_ECC_REG_ECC_ADDR_RGN_1_EN_MASK; | |
239 | range = ECC_START_ADDR1 | (ECC_END_ADDR1 << 16); | |
240 | #else | |
241 | ecc_ctrl = readl(&emif->emif_ecc_ctrl_reg); | |
242 | range = readl(&emif->emif_ecc_address_range_1); | |
243 | #endif | |
244 | ||
245 | /* Check in ecc address range 1 */ | |
246 | if (ecc_ctrl & EMIF_ECC_REG_ECC_ADDR_RGN_1_EN_MASK) { | |
247 | start_addr = ((range & EMIF_ECC_REG_ECC_START_ADDR_MASK) << 16) | |
248 | + CONFIG_SYS_SDRAM_BASE; | |
5ebe6c0c LV |
249 | end_addr = (range & EMIF_ECC_REG_ECC_END_ADDR_MASK) + 0xFFFF + |
250 | CONFIG_SYS_SDRAM_BASE; | |
8a8af8a2 LV |
251 | if ((addr >= start_addr) && (addr <= end_addr)) |
252 | /* addr within ecc address range 1 */ | |
253 | return 1; | |
254 | } | |
255 | ||
256 | /* Check in ecc address range 2 */ | |
257 | if (ecc_ctrl & EMIF_ECC_REG_ECC_ADDR_RGN_2_EN_MASK) { | |
258 | range = readl(&emif->emif_ecc_address_range_2); | |
259 | start_addr = ((range & EMIF_ECC_REG_ECC_START_ADDR_MASK) << 16) | |
260 | + CONFIG_SYS_SDRAM_BASE; | |
5ebe6c0c LV |
261 | end_addr = (range & EMIF_ECC_REG_ECC_END_ADDR_MASK) + 0xFFFF + |
262 | CONFIG_SYS_SDRAM_BASE; | |
8a8af8a2 LV |
263 | if ((addr >= start_addr) && (addr <= end_addr)) |
264 | /* addr within ecc address range 2 */ | |
265 | return 1; | |
266 | } | |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
271 | static int is_ecc_enabled(void) | |
272 | { | |
273 | struct emif_reg_struct *emif = (struct emif_reg_struct *)EMIF1_BASE; | |
274 | u32 ecc_ctrl = readl(&emif->emif_ecc_ctrl_reg); | |
275 | ||
276 | return (ecc_ctrl & EMIF_ECC_CTRL_REG_ECC_EN_MASK) && | |
277 | (ecc_ctrl & EMIF_ECC_REG_RMW_EN_MASK); | |
278 | } | |
279 | ||
09140113 SG |
280 | static int do_ddr_test(struct cmd_tbl *cmdtp, |
281 | int flag, int argc, char *const argv[]) | |
26459488 HZ |
282 | { |
283 | u32 start_addr, end_addr, size, ecc_err; | |
284 | ||
285 | if ((argc == 4) && (strncmp(argv[1], "ecc_err", 8) == 0)) { | |
8a8af8a2 LV |
286 | if (!is_ecc_enabled()) { |
287 | puts("ECC not enabled. Please Enable ECC any try again\n"); | |
288 | return CMD_RET_FAILURE; | |
26459488 HZ |
289 | } |
290 | ||
291 | start_addr = simple_strtoul(argv[2], NULL, 16); | |
292 | ecc_err = simple_strtoul(argv[3], NULL, 16); | |
293 | ||
8a8af8a2 LV |
294 | if (!is_addr_valid(start_addr)) { |
295 | puts("Invalid address. Please enter ECC supported address!\n"); | |
296 | return CMD_RET_FAILURE; | |
26459488 HZ |
297 | } |
298 | ||
8a8af8a2 | 299 | ddr_memory_ecc_err(start_addr, ecc_err); |
26459488 HZ |
300 | return 0; |
301 | } | |
302 | ||
303 | if (!(((argc == 4) && (strncmp(argv[1], "test", 5) == 0)) || | |
304 | ((argc == 5) && (strncmp(argv[1], "compare", 8) == 0)))) | |
305 | return cmd_usage(cmdtp); | |
306 | ||
307 | start_addr = simple_strtoul(argv[2], NULL, 16); | |
308 | end_addr = simple_strtoul(argv[3], NULL, 16); | |
309 | ||
310 | if ((start_addr < CONFIG_SYS_SDRAM_BASE) || | |
311 | (start_addr > (CONFIG_SYS_SDRAM_BASE + | |
8a8af8a2 | 312 | get_effective_memsize() - 1)) || |
26459488 HZ |
313 | (end_addr < CONFIG_SYS_SDRAM_BASE) || |
314 | (end_addr > (CONFIG_SYS_SDRAM_BASE + | |
8a8af8a2 | 315 | get_effective_memsize() - 1)) || (start_addr >= end_addr)) { |
26459488 HZ |
316 | puts("Invalid start or end address!\n"); |
317 | return cmd_usage(cmdtp); | |
318 | } | |
319 | ||
320 | puts("Please wait ...\n"); | |
321 | if (argc == 5) { | |
322 | size = simple_strtoul(argv[4], NULL, 16); | |
323 | ddr_memory_compare(start_addr, end_addr, size); | |
324 | } else { | |
325 | ddr_memory_test(start_addr, end_addr, 0); | |
326 | } | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
331 | U_BOOT_CMD(ddr, 5, 1, do_ddr_test, | |
332 | "DDR3 test", | |
333 | "test <start_addr in hex> <end_addr in hex> - test DDR from start\n" | |
334 | " address to end address\n" | |
335 | "ddr compare <start_addr in hex> <end_addr in hex> <size in hex> -\n" | |
336 | " compare DDR data of (size) bytes from start address to end\n" | |
337 | " address\n" | |
338 | "ddr ecc_err <addr in hex> <bit_err in hex> - generate bit errors\n" | |
339 | " in DDR data at <addr>, the command will read a 32-bit data\n" | |
340 | " from <addr>, and write (data ^ bit_err) back to <addr>\n" | |
341 | ); |