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