]>
Commit | Line | Data |
---|---|---|
72f58822 L |
1 | /* |
2 | * (C) Copyright 2000 | |
3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | |
4 | * | |
5 | * SPDX-License-Identifier: GPL-2.0+ | |
6 | */ | |
7 | ||
8 | /* | |
9 | * Memory Functions | |
10 | * | |
11 | * Copied from FADS ROM, Dan Malek (dmalek@jlc.net) | |
12 | */ | |
13 | ||
14 | #include "common.h" | |
15 | #include <stdlib.h> | |
16 | #include <ctype.h> | |
17 | ||
18 | #include "config.h" | |
19 | #include "debug.h" | |
20 | #include "command.h" | |
21 | #include "cli_readline.h" | |
22 | #include "con-utils.h" | |
23 | #include "z80-if.h" | |
24 | ||
72f58822 L |
25 | |
26 | #ifndef CONFIG_SYS_MEMTEST_SCRATCH | |
27 | #define CONFIG_SYS_MEMTEST_SCRATCH 0 | |
28 | #endif | |
29 | ||
72f58822 L |
30 | /* Display values from last command. |
31 | * Memory modify remembered values are different from display memory. | |
32 | */ | |
33 | static uint32_t dp_last_addr; | |
34 | static uint32_t dp_last_length = 0x100; | |
35 | static uint32_t mm_last_addr; | |
36 | ||
37 | static uint32_t base_address = 0; | |
38 | ||
39 | /*--------------------------------------------------------------------------*/ | |
40 | ||
41 | static void print_blanks(uint_fast8_t count) | |
42 | { | |
43 | while(count--) | |
44 | putchar(' '); | |
45 | } | |
46 | ||
47 | int z180_dump_mem(uint32_t startaddr, uint32_t len, const char *title) | |
48 | { | |
49 | uint8_t buf[16]; | |
50 | uint8_t llen = 16; | |
51 | uint8_t pre = startaddr % 16; | |
52 | uint32_t addr = startaddr & ~0x0f; | |
53 | len += pre; | |
54 | uint8_t i; | |
6035a17b | 55 | |
72f58822 L |
56 | if (title && *title) |
57 | printf_P(PSTR("%s\n"),title); | |
6035a17b | 58 | |
72f58822 L |
59 | while (len) { |
60 | if (len < 16) | |
61 | llen = len; | |
62 | ||
62f624d3 | 63 | z80_bus_cmd(Request); |
72f58822 L |
64 | for (i = pre; i < llen; i++) |
65 | buf[i] = z80_read(addr + i); | |
62f624d3 | 66 | z80_bus_cmd(Release); |
72f58822 L |
67 | |
68 | printf_P(PSTR("%.5lx:"), addr); | |
69 | #if 0 | |
70 | print_blanks(3 * pre); | |
71 | ||
72 | /* Print hex values */ | |
73 | for (i = pre; i < llen; i++) | |
74 | printf_P(PSTR(" %.2x"), buf[i]); | |
75 | #else | |
76 | for (i = 0; i < llen; i++) { | |
77 | if ((i % 8) == 0) | |
78 | putchar(' '); | |
79 | if (i < pre) | |
80 | printf_P(PSTR(".. ")); | |
81 | else | |
82 | printf_P(PSTR("%.2x "), buf[i]); | |
83 | } | |
84 | #endif | |
85 | /* fill line with whitespace for nice ASCII print */ | |
86 | #if 1 | |
87 | print_blanks(3 * (16u - i) + (16u-i)/8 + 1 + pre); | |
88 | #else | |
89 | ||
90 | #endif | |
91 | /* Print data in ASCII characters */ | |
92 | for (i = pre; i < llen; i++) | |
93 | printf_P(PSTR("%c"), isprint(buf[i]) ? buf[i] : '.'); | |
94 | putchar('\n'); | |
95 | ||
96 | pre = 0; | |
97 | addr += 16; | |
98 | len -= llen; | |
99 | ||
100 | if (ctrlc()) | |
101 | return -1; | |
102 | } | |
103 | return 0; | |
104 | } | |
105 | ||
106 | ||
107 | /*--------------------------------------------------------------------------*/ | |
108 | ||
109 | /* Memory Display | |
110 | * | |
111 | * Syntax: | |
112 | * md {addr} {len} | |
113 | */ | |
d0581f88 | 114 | command_ret_t do_mem_md(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
72f58822 L |
115 | { |
116 | uint32_t addr, length; | |
6035a17b | 117 | |
72f58822 L |
118 | (void) cmdtp; |
119 | ||
6035a17b | 120 | #if 0 |
72f58822 L |
121 | printf_P(PSTR("flag: %d, argc: %d"), flag, argc); |
122 | for (int i = 0; i < argc; i++) { | |
123 | printf_P(PSTR(", argv[%d]: %s"), i, argv[i] ? argv[i] : "<NULL>"); | |
124 | } | |
125 | putchar('\n'); | |
126 | #endif | |
127 | ||
128 | /* We use the last specified parameters, unless new ones are | |
129 | * entered. | |
130 | */ | |
131 | addr = dp_last_addr; | |
132 | length = dp_last_length; | |
133 | ||
134 | if (argc < 2) | |
135 | return CMD_RET_USAGE; | |
136 | ||
137 | if ((flag & CMD_FLAG_REPEAT) == 0) { | |
138 | /* Address is specified since argc > 1 */ | |
139 | addr = strtoul(argv[1], NULL, 16); | |
140 | addr += base_address; | |
141 | ||
142 | /* If another parameter, it is the length to display. */ | |
143 | if (argc > 2) | |
144 | length = strtoul(argv[2], NULL, 16); | |
145 | } | |
146 | ||
147 | /* Print the lines. */ | |
148 | z180_dump_mem(addr, length, NULL); | |
149 | ||
150 | dp_last_addr = addr + length; | |
151 | dp_last_length = length; | |
d0581f88 | 152 | return CMD_RET_SUCCESS; |
72f58822 L |
153 | } |
154 | ||
d0581f88 L |
155 | /* Modify memory. |
156 | * | |
157 | * Syntax: | |
158 | * mm {addr} | |
159 | * nm {addr} | |
160 | */ | |
161 | static command_ret_t | |
162 | mod_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const argv[]) | |
163 | { | |
164 | uint32_t addr; | |
165 | uint8_t data; | |
166 | int nbytes; | |
167 | ||
168 | (void) cmdtp; | |
169 | ||
170 | if (argc != 2) | |
171 | return CMD_RET_USAGE; | |
172 | ||
173 | /* We use the last specified parameters, unless new ones are | |
174 | * entered. | |
175 | */ | |
176 | addr = mm_last_addr; | |
177 | ||
178 | if ((flag & CMD_FLAG_REPEAT) == 0) { | |
179 | /* New command specified. | |
180 | */ | |
181 | ||
182 | /* Address is specified since argc > 1 | |
183 | */ | |
184 | addr = strtoul(argv[1], NULL, 16); | |
185 | addr += base_address; | |
186 | } | |
187 | ||
188 | /* Print the address, followed by value. Then accept input for | |
189 | * the next value. A non-converted value exits. | |
190 | */ | |
191 | do { | |
192 | z80_bus_cmd(Request); | |
193 | data = z80_read(addr); | |
69988dc1 | 194 | printf_P(PSTR("%05lx: %02x"), addr, data); |
d0581f88 L |
195 | z80_bus_cmd(Release); |
196 | ||
197 | nbytes = cli_readline(PSTR(" ? ")); | |
198 | if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) { | |
199 | /* <CR> pressed as only input, don't modify current | |
200 | * location and move to next. "-" pressed will go back. | |
201 | */ | |
202 | if (incrflag) | |
203 | addr += nbytes ? -1 : 1; | |
204 | nbytes = 1; | |
205 | } | |
206 | else { | |
207 | char *endp; | |
208 | data = strtoul(console_buffer, &endp, 16); | |
209 | nbytes = endp - console_buffer; | |
210 | if (nbytes) { | |
211 | z80_bus_cmd(Request); | |
212 | z80_write(addr, data); | |
213 | z80_bus_cmd(Release); | |
214 | if (incrflag) | |
215 | addr++; | |
216 | } | |
217 | } | |
218 | } while (nbytes); | |
219 | ||
220 | mm_last_addr = addr; | |
221 | return CMD_RET_SUCCESS; | |
222 | } | |
223 | ||
224 | ||
225 | command_ret_t do_mem_mm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
72f58822 L |
226 | { |
227 | return mod_mem (cmdtp, 1, flag, argc, argv); | |
228 | } | |
d0581f88 | 229 | command_ret_t do_mem_nm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
72f58822 L |
230 | { |
231 | return mod_mem (cmdtp, 0, flag, argc, argv); | |
232 | } | |
233 | ||
d0581f88 | 234 | command_ret_t do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
72f58822 L |
235 | { |
236 | uint8_t writeval; | |
237 | uint32_t addr, count; | |
238 | ||
239 | (void) cmdtp; | |
240 | (void) flag; | |
241 | ||
242 | if ((argc < 3) || (argc > 4)) | |
243 | return CMD_RET_USAGE; | |
244 | ||
245 | /* Address is specified since argc > 1 | |
246 | */ | |
247 | addr = strtoul(argv[1], NULL, 16); | |
248 | addr += base_address; | |
249 | ||
250 | /* Get the value to write. | |
251 | */ | |
252 | writeval = (uint8_t) strtoul(argv[2], NULL, 16); | |
253 | ||
254 | /* Count ? */ | |
255 | if (argc == 4) { | |
256 | count = strtoul(argv[3], NULL, 16); | |
257 | } else { | |
258 | count = 1; | |
259 | } | |
260 | ||
62f624d3 | 261 | z80_bus_cmd(Request); |
72f58822 L |
262 | while (count-- > 0) { |
263 | z80_write(addr, writeval); | |
6035a17b | 264 | ++addr; |
72f58822 | 265 | } |
62f624d3 | 266 | z80_bus_cmd(Release); |
72f58822 | 267 | |
d0581f88 | 268 | return CMD_RET_SUCCESS; |
72f58822 L |
269 | } |
270 | ||
271 | #ifdef CONFIG_MX_CYCLIC | |
d0581f88 | 272 | command_ret_t do_mem_mdc ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
72f58822 L |
273 | { |
274 | int i; | |
275 | uint32_t count; | |
276 | ||
277 | if (argc < 4) | |
278 | return CMD_RET_USAGE; | |
279 | ||
280 | count = strtoul(argv[3], NULL, 10); | |
281 | ||
282 | for (;;) { | |
283 | do_mem_md (NULL, 0, 3, argv); | |
284 | ||
285 | /* delay for <count> ms... */ | |
286 | /* TODO: use timer */ | |
287 | for (i=0; i<count; i++) | |
288 | udelay (1000); | |
289 | ||
290 | /* check for ctrl-c to abort... */ | |
291 | if (ctrlc()) { | |
69988dc1 | 292 | my_puts_P(PSTR("Abort\n")); |
d0581f88 | 293 | return CMD_RET_SUCCESS; |
72f58822 L |
294 | } |
295 | } | |
296 | ||
d0581f88 | 297 | return CMD_RET_SUCCESS; |
72f58822 L |
298 | } |
299 | ||
d0581f88 | 300 | command_ret_t do_mem_mwc ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
72f58822 L |
301 | { |
302 | int i; | |
303 | uint32_t count; | |
304 | ||
305 | if (argc < 4) | |
306 | return CMD_RET_USAGE; | |
307 | ||
308 | count = strtoul(argv[3], NULL, 10); | |
309 | ||
310 | for (;;) { | |
311 | do_mem_mw (NULL, 0, 3, argv); | |
312 | ||
313 | /* delay for <count> ms... */ | |
314 | /* TODO: use timer */ | |
315 | for (i=0; i<count; i++) | |
316 | udelay (1000); | |
317 | ||
318 | /* check for ctrl-c to abort... */ | |
319 | if (ctrlc()) { | |
69988dc1 | 320 | my_puts_P(PSTR("Abort\n")); |
d0581f88 | 321 | return CMD_RET_SUCCESS; |
72f58822 L |
322 | } |
323 | } | |
324 | ||
d0581f88 | 325 | return CMD_RET_SUCCESS; |
72f58822 L |
326 | } |
327 | #endif /* CONFIG_MX_CYCLIC */ | |
328 | ||
d0581f88 | 329 | command_ret_t do_mem_cmp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
72f58822 L |
330 | { |
331 | uint32_t addr1, addr2, count, ngood; | |
d0581f88 | 332 | command_ret_t rcode = CMD_RET_SUCCESS; |
72f58822 L |
333 | uint8_t byte1, byte2; |
334 | ||
335 | (void) cmdtp; | |
336 | (void) flag; | |
337 | ||
338 | if (argc != 4) | |
339 | return CMD_RET_USAGE; | |
340 | ||
341 | ||
342 | addr1 = strtoul(argv[1], NULL, 16); | |
343 | addr1 += base_address; | |
344 | addr2 = strtoul(argv[2], NULL, 16); | |
345 | addr2 += base_address; | |
346 | count = strtoul(argv[3], NULL, 16); | |
347 | ||
348 | for (ngood = 0; ngood < count; ++ngood) { | |
62f624d3 | 349 | z80_bus_cmd(Request); |
72f58822 L |
350 | byte1 = z80_read(addr1); |
351 | byte2 = z80_read(addr2); | |
62f624d3 | 352 | z80_bus_cmd(Release); |
72f58822 | 353 | if (byte1 != byte2) { |
6035a17b L |
354 | printf_P(PSTR("byte at 0x%05lx (%#02x) != " |
355 | "byte at 0x%05lx (%#02x)\n"), | |
72f58822 | 356 | addr1, byte1, addr2, byte2); |
d0581f88 | 357 | rcode = CMD_RET_FAILURE; |
72f58822 L |
358 | break; |
359 | } | |
360 | addr1++; | |
361 | addr2++; | |
362 | ||
363 | /* check for ctrl-c to abort... */ | |
364 | if (ctrlc()) { | |
69988dc1 | 365 | my_puts_P(PSTR("Abort\n")); |
d0581f88 | 366 | return CMD_RET_SUCCESS; |
72f58822 L |
367 | } |
368 | } | |
369 | ||
69988dc1 | 370 | printf_P(PSTR("Total of %ld byte(s) (0x%lx) were the same\n"), ngood, ngood); |
72f58822 L |
371 | return rcode; |
372 | } | |
373 | ||
d0581f88 | 374 | command_ret_t do_mem_cp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
72f58822 L |
375 | { |
376 | uint32_t src, dest, count; | |
377 | int_fast8_t step; | |
378 | ||
379 | (void) cmdtp; | |
380 | (void) flag; | |
381 | ||
382 | if (argc != 4) | |
383 | return CMD_RET_USAGE; | |
384 | ||
385 | src = strtoul(argv[1], NULL, 16); | |
386 | src += base_address; | |
387 | dest = strtoul(argv[2], NULL, 16); | |
388 | dest += base_address; | |
389 | count = strtoul(argv[3], NULL, 16); | |
390 | ||
391 | if (count == 0) { | |
69988dc1 | 392 | my_puts_P(PSTR("Zero length?\n")); |
d0581f88 | 393 | return CMD_RET_FAILURE; |
72f58822 | 394 | } |
6035a17b | 395 | |
72f58822 L |
396 | if (dest > src) { |
397 | src += count - 1; | |
398 | dest += count - 1; | |
399 | step = -1; | |
400 | } else | |
401 | step = 1; | |
402 | ||
403 | while (count-- > 0) { | |
404 | uint8_t data; | |
62f624d3 | 405 | z80_bus_cmd(Request); |
72f58822 L |
406 | data = z80_read(src); |
407 | z80_write(dest, data); | |
62f624d3 | 408 | z80_bus_cmd(Release); |
72f58822 L |
409 | src += step; |
410 | dest += step; | |
411 | ||
412 | /* check for ctrl-c to abort... */ | |
413 | if (ctrlc()) { | |
69988dc1 | 414 | my_puts_P(PSTR("Abort\n")); |
d0581f88 | 415 | return CMD_RET_SUCCESS; |
72f58822 L |
416 | } |
417 | } | |
d0581f88 | 418 | return CMD_RET_SUCCESS; |
72f58822 L |
419 | } |
420 | ||
d0581f88 | 421 | command_ret_t do_mem_base(cmd_tbl_t *cmdtp, int flag, int argc, |
72f58822 L |
422 | char * const argv[]) |
423 | { | |
424 | (void) cmdtp; | |
425 | (void) flag; | |
426 | ||
427 | if (argc > 1) { | |
428 | /* Set new base address. */ | |
429 | base_address = strtoul(argv[1], NULL, 16); | |
430 | } | |
431 | /* Print the current base address. */ | |
69988dc1 | 432 | printf_P(PSTR("Base Address: 0x%05lx\n"), base_address); |
d0581f88 | 433 | return CMD_RET_SUCCESS; |
72f58822 L |
434 | } |
435 | ||
d0581f88 | 436 | command_ret_t do_mem_loop(cmd_tbl_t *cmdtp, int flag, int argc, |
72f58822 L |
437 | char * const argv[]) |
438 | { | |
439 | uint32_t addr, length; | |
440 | ||
441 | (void) cmdtp; | |
442 | (void) flag; | |
443 | ||
444 | if (argc < 3) | |
445 | return CMD_RET_USAGE; | |
446 | ||
447 | /* Address is always specified. */ | |
448 | addr = strtoul(argv[1], NULL, 16); | |
449 | ||
450 | /* Length is the number of bytes. */ | |
451 | length = strtoul(argv[2], NULL, 16); | |
452 | ||
453 | ||
454 | /* We want to optimize the loops to run as fast as possible. | |
455 | * If we have only one object, just run infinite loops. | |
456 | */ | |
457 | if (length == 1) { | |
62f624d3 | 458 | z80_bus_cmd(Request); |
72f58822 L |
459 | for (;;) |
460 | z80_read(addr); | |
62f624d3 | 461 | z80_bus_cmd(Release); |
72f58822 L |
462 | } |
463 | ||
62f624d3 | 464 | z80_bus_cmd(Request); |
72f58822 L |
465 | for (;;) { |
466 | uint32_t i = length; | |
467 | uint32_t p = addr; | |
468 | while (i-- > 0) | |
469 | z80_read(p++); | |
470 | } | |
62f624d3 | 471 | z80_bus_cmd(Release); |
72f58822 | 472 | |
d0581f88 | 473 | return CMD_RET_SUCCESS; |
72f58822 L |
474 | } |
475 | ||
476 | #ifdef CONFIG_LOOPW | |
d0581f88 | 477 | command_ret_t do_mem_loopw (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
72f58822 L |
478 | { |
479 | uint32_t addr, length; | |
480 | uint8_t data; | |
481 | ||
482 | (void) cmdtp; | |
483 | (void) flag; | |
484 | ||
485 | if (argc < 4) | |
486 | return CMD_RET_USAGE; | |
487 | ||
488 | /* Address is always specified. */ | |
489 | addr = strtoul(argv[1], NULL, 16); | |
490 | ||
491 | /* Length is the number of bytes. */ | |
492 | length = strtoul(argv[2], NULL, 16); | |
493 | ||
494 | data = strtoul(argv[3], NULL, 16); | |
495 | ||
496 | /* We want to optimize the loops to run as fast as possible. | |
497 | * If we have only one object, just run infinite loops. | |
498 | */ | |
499 | if (length == 1) { | |
62f624d3 | 500 | z80_bus_cmd(Request); |
72f58822 L |
501 | for (;;) |
502 | z80_write(addr, data); | |
503 | } | |
504 | ||
505 | for (;;) { | |
506 | uint32_t i = length; | |
507 | uint32_t p = addr; | |
508 | while (i-- > 0) | |
509 | z80_write(p++, data); | |
510 | } | |
511 | } | |
512 | #endif /* CONFIG_LOOPW */ | |
513 | ||
514 | #ifdef CONFIG_CMD_MEMTEST | |
515 | static uint32_t mem_test_alt(vu_long *buf, uint32_t start_addr, uint32_t end_addr, | |
516 | vu_long *dummy) | |
517 | { | |
518 | vu_long *addr; | |
519 | uint32_t errs = 0; | |
520 | uint32_t val, readback; | |
521 | int j; | |
522 | vu_long offset; | |
523 | vu_long test_offset; | |
524 | vu_long pattern; | |
525 | vu_long temp; | |
526 | vu_long anti_pattern; | |
527 | vu_long num_words; | |
528 | static const FLASH uint32_t bitpattern[] = { | |
529 | 0x00000001, /* single bit */ | |
530 | 0x00000003, /* two adjacent bits */ | |
531 | 0x00000007, /* three adjacent bits */ | |
532 | 0x0000000F, /* four adjacent bits */ | |
533 | 0x00000005, /* two non-adjacent bits */ | |
534 | 0x00000015, /* three non-adjacent bits */ | |
535 | 0x00000055, /* four non-adjacent bits */ | |
536 | 0xaaaaaaaa, /* alternating 1/0 */ | |
537 | }; | |
538 | ||
539 | num_words = (end_addr - start_addr) / sizeof(vu_long); | |
540 | ||
541 | /* | |
542 | * Data line test: write a pattern to the first | |
543 | * location, write the 1's complement to a 'parking' | |
544 | * address (changes the state of the data bus so a | |
545 | * floating bus doesn't give a false OK), and then | |
546 | * read the value back. Note that we read it back | |
547 | * into a variable because the next time we read it, | |
548 | * it might be right (been there, tough to explain to | |
549 | * the quality guys why it prints a failure when the | |
550 | * "is" and "should be" are obviously the same in the | |
551 | * error message). | |
552 | * | |
553 | * Rather than exhaustively testing, we test some | |
554 | * patterns by shifting '1' bits through a field of | |
555 | * '0's and '0' bits through a field of '1's (i.e. | |
556 | * pattern and ~pattern). | |
557 | */ | |
558 | addr = buf; | |
559 | for (j = 0; j < sizeof(bitpattern) / sizeof(bitpattern[0]); j++) { | |
560 | val = bitpattern[j]; | |
561 | for (; val != 0; val <<= 1) { | |
562 | *addr = val; | |
563 | *dummy = ~val; /* clear the test data off the bus */ | |
564 | readback = *addr; | |
565 | if (readback != val) { | |
69988dc1 L |
566 | printf_P(PSTR("FAILURE (data line): " |
567 | "expected %05lx, actual %05lx\n"), | |
72f58822 L |
568 | val, readback); |
569 | errs++; | |
570 | if (ctrlc()) | |
571 | return -1; | |
572 | } | |
573 | *addr = ~val; | |
574 | *dummy = val; | |
575 | readback = *addr; | |
576 | if (readback != ~val) { | |
69988dc1 L |
577 | printf_P(PSTR("FAILURE (data line): " |
578 | "Is %05lx, should be %05lx\n"), | |
72f58822 L |
579 | readback, ~val); |
580 | errs++; | |
581 | if (ctrlc()) | |
582 | return -1; | |
583 | } | |
584 | } | |
585 | } | |
586 | ||
587 | /* | |
588 | * Based on code whose Original Author and Copyright | |
589 | * information follows: Copyright (c) 1998 by Michael | |
590 | * Barr. This software is placed into the public | |
591 | * domain and may be used for any purpose. However, | |
592 | * this notice must not be changed or removed and no | |
593 | * warranty is either expressed or implied by its | |
594 | * publication or distribution. | |
595 | */ | |
596 | ||
597 | /* | |
598 | * Address line test | |
599 | ||
600 | * Description: Test the address bus wiring in a | |
601 | * memory region by performing a walking | |
602 | * 1's test on the relevant bits of the | |
603 | * address and checking for aliasing. | |
604 | * This test will find single-bit | |
605 | * address failures such as stuck-high, | |
606 | * stuck-low, and shorted pins. The base | |
607 | * address and size of the region are | |
608 | * selected by the caller. | |
609 | ||
610 | * Notes: For best results, the selected base | |
611 | * address should have enough LSB 0's to | |
612 | * guarantee single address bit changes. | |
613 | * For example, to test a 64-Kbyte | |
614 | * region, select a base address on a | |
615 | * 64-Kbyte boundary. Also, select the | |
616 | * region size as a power-of-two if at | |
617 | * all possible. | |
618 | * | |
619 | * Returns: 0 if the test succeeds, 1 if the test fails. | |
620 | */ | |
621 | pattern = (vu_long) 0xaaaaaaaa; | |
622 | anti_pattern = (vu_long) 0x55555555; | |
623 | ||
624 | debug("%s:%d: length = 0x%.5lx\n", __func__, __LINE__, num_words); | |
625 | /* | |
626 | * Write the default pattern at each of the | |
627 | * power-of-two offsets. | |
628 | */ | |
629 | for (offset = 1; offset < num_words; offset <<= 1) | |
630 | addr[offset] = pattern; | |
631 | ||
632 | /* | |
633 | * Check for address bits stuck high. | |
634 | */ | |
635 | test_offset = 0; | |
636 | addr[test_offset] = anti_pattern; | |
637 | ||
638 | for (offset = 1; offset < num_words; offset <<= 1) { | |
639 | temp = addr[offset]; | |
640 | if (temp != pattern) { | |
69988dc1 L |
641 | printf_P(PSTR("\nFAILURE: Address bit stuck high @ 0x%.5lx:" |
642 | " expected 0x%.5lx, actual 0x%.5lx\n"), | |
72f58822 L |
643 | start_addr + offset*sizeof(vu_long), |
644 | pattern, temp); | |
645 | errs++; | |
646 | if (ctrlc()) | |
647 | return -1; | |
648 | } | |
649 | } | |
650 | addr[test_offset] = pattern; | |
651 | ||
652 | /* | |
653 | * Check for addr bits stuck low or shorted. | |
654 | */ | |
655 | for (test_offset = 1; test_offset < num_words; test_offset <<= 1) { | |
656 | addr[test_offset] = anti_pattern; | |
657 | ||
658 | for (offset = 1; offset < num_words; offset <<= 1) { | |
659 | temp = addr[offset]; | |
660 | if ((temp != pattern) && (offset != test_offset)) { | |
69988dc1 | 661 | printf_P(PSTR("\nFAILURE: Address bit stuck low or" |
72f58822 | 662 | " shorted @ 0x%.5lx: expected 0x%.5lx," |
69988dc1 | 663 | " actual 0x%.5lx\n"), |
72f58822 L |
664 | start_addr + offset*sizeof(vu_long), |
665 | pattern, temp); | |
666 | errs++; | |
667 | if (ctrlc()) | |
668 | return -1; | |
669 | } | |
670 | } | |
671 | addr[test_offset] = pattern; | |
672 | } | |
673 | ||
674 | /* | |
675 | * Description: Test the integrity of a physical | |
676 | * memory device by performing an | |
677 | * increment/decrement test over the | |
678 | * entire region. In the process every | |
679 | * storage bit in the device is tested | |
680 | * as a zero and a one. The base address | |
681 | * and the size of the region are | |
682 | * selected by the caller. | |
683 | * | |
684 | * Returns: 0 if the test succeeds, 1 if the test fails. | |
685 | */ | |
686 | num_words++; | |
687 | ||
688 | /* | |
689 | * Fill memory with a known pattern. | |
690 | */ | |
691 | for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { | |
692 | addr[offset] = pattern; | |
693 | } | |
694 | ||
695 | /* | |
696 | * Check each location and invert it for the second pass. | |
697 | */ | |
698 | for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { | |
699 | temp = addr[offset]; | |
700 | if (temp != pattern) { | |
69988dc1 L |
701 | printf_P(PSTR("\nFAILURE (read/write) @ 0x%.5lx:" |
702 | " expected 0x%.5lx, actual 0x%.5lx)\n"), | |
72f58822 L |
703 | start_addr + offset*sizeof(vu_long), |
704 | pattern, temp); | |
705 | errs++; | |
706 | if (ctrlc()) | |
707 | return -1; | |
708 | } | |
709 | ||
710 | anti_pattern = ~pattern; | |
711 | addr[offset] = anti_pattern; | |
712 | } | |
713 | ||
714 | /* | |
715 | * Check each location for the inverted pattern and zero it. | |
716 | */ | |
717 | for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) { | |
718 | WATCHDOG_RESET(); | |
719 | anti_pattern = ~pattern; | |
720 | temp = addr[offset]; | |
721 | if (temp != anti_pattern) { | |
69988dc1 L |
722 | printf_P(PSTR("\nFAILURE (read/write): @ 0x%.5lx:" |
723 | " expected 0x%.5lx, actual 0x%.5lx)\n"), | |
72f58822 L |
724 | start_addr + offset*sizeof(vu_long), |
725 | anti_pattern, temp); | |
726 | errs++; | |
727 | if (ctrlc()) | |
728 | return -1; | |
729 | } | |
730 | addr[offset] = 0; | |
731 | } | |
732 | ||
733 | return 0; | |
734 | } | |
735 | ||
736 | static uint32_t mem_test_quick(vu_long *buf, uint32_t start_addr, uint32_t end_addr, | |
737 | vu_long pattern, int iteration) | |
738 | { | |
739 | vu_long *end; | |
740 | vu_long *addr; | |
741 | uint32_t errs = 0; | |
742 | uint32_t incr, length; | |
743 | uint32_t val, readback; | |
744 | ||
745 | /* Alternate the pattern */ | |
746 | incr = 1; | |
747 | if (iteration & 1) { | |
748 | incr = -incr; | |
749 | /* | |
750 | * Flip the pattern each time to make lots of zeros and | |
751 | * then, the next time, lots of ones. We decrement | |
752 | * the "negative" patterns and increment the "positive" | |
753 | * patterns to preserve this feature. | |
754 | */ | |
755 | if (pattern & 0x80000000) | |
756 | pattern = -pattern; /* complement & increment */ | |
757 | else | |
758 | pattern = ~pattern; | |
759 | } | |
760 | length = (end_addr - start_addr) / sizeof(uint32_t); | |
761 | end = buf + length; | |
69988dc1 | 762 | printf_P(PSTR("\rPattern %08lX Writing..." |
72f58822 | 763 | "%12s" |
69988dc1 | 764 | "\b\b\b\b\b\b\b\b\b\b"), |
72f58822 L |
765 | pattern, ""); |
766 | ||
767 | for (addr = buf, val = pattern; addr < end; addr++) { | |
768 | *addr = val; | |
769 | val += incr; | |
770 | } | |
771 | ||
69988dc1 | 772 | my_puts_P(PSTR("Reading...")); |
72f58822 L |
773 | |
774 | for (addr = buf, val = pattern; addr < end; addr++) { | |
775 | readback = *addr; | |
776 | if (readback != val) { | |
777 | uint32_t offset = addr - buf; | |
778 | ||
69988dc1 L |
779 | printf_P(PSTR("\nMem error @ 0x%08X: " |
780 | "found %08lX, expected %08lX\n"), | |
72f58822 L |
781 | (unsigned int)(uintptr_t)(start_addr + offset*sizeof(vu_long)), |
782 | readback, val); | |
783 | errs++; | |
784 | if (ctrlc()) | |
785 | return -1; | |
786 | } | |
787 | val += incr; | |
788 | } | |
789 | ||
790 | return 0; | |
791 | } | |
792 | ||
793 | /* | |
794 | * Perform a memory test. A more complete alternative test can be | |
795 | * configured using CONFIG_SYS_ALT_MEMTEST. The complete test loops until | |
796 | * interrupted by ctrl-c or by a failure of one of the sub-tests. | |
797 | */ | |
d0581f88 | 798 | command_ret_t do_mem_mtest(cmd_tbl_t *cmdtp, int flag, int argc, |
72f58822 L |
799 | char * const argv[]) |
800 | { | |
801 | uint32_t start, end; | |
802 | vu_long *buf, *dummy; | |
803 | int iteration_limit; | |
d0581f88 | 804 | /* TODO: command_ret_t */ |
72f58822 L |
805 | int ret; |
806 | uint32_t errs = 0; /* number of errors, or -1 if interrupted */ | |
807 | uint32_t pattern; | |
808 | int iteration; | |
809 | #if defined(CONFIG_SYS_ALT_MEMTEST) | |
810 | const int alt_test = 1; | |
811 | #else | |
812 | const int alt_test = 0; | |
813 | #endif | |
814 | ||
815 | if (argc > 1) | |
816 | start = strtoul(argv[1], NULL, 16); | |
817 | else | |
818 | start = CONFIG_SYS_MEMTEST_START; | |
819 | ||
820 | if (argc > 2) | |
821 | end = strtoul(argv[2], NULL, 16); | |
822 | else | |
823 | end = CONFIG_SYS_MEMTEST_END; | |
824 | ||
825 | if (argc > 3) | |
826 | pattern = (uint32_t)strtoul(argv[3], NULL, 16); | |
827 | else | |
828 | pattern = 0; | |
829 | ||
830 | if (argc > 4) | |
831 | iteration_limit = (uint32_t)strtoul(argv[4], NULL, 16); | |
832 | else | |
833 | iteration_limit = 0; | |
834 | ||
69988dc1 | 835 | printf_P(PSTR("Testing %08x ... %08x:\n"), (unsigned int)start, (unsigned int)end); |
72f58822 L |
836 | debug("%s:%d: start %#05lx end %#05lx\n", __func__, __LINE__, |
837 | start, end); | |
838 | ||
839 | /* TODO: */ | |
840 | // buf = map_sysmem(start, end - start); | |
841 | // dummy = map_sysmem(CONFIG_SYS_MEMTEST_SCRATCH, sizeof(vu_long)); | |
842 | for (iteration = 0; | |
843 | !iteration_limit || iteration < iteration_limit; | |
844 | iteration++) { | |
845 | if (ctrlc()) { | |
846 | errs = -1UL; | |
847 | break; | |
848 | } | |
849 | ||
69988dc1 | 850 | printf_P(PSTR("Iteration: %6d\r"), iteration + 1); |
72f58822 L |
851 | debug("\n"); |
852 | if (alt_test) { | |
853 | errs = mem_test_alt(buf, start, end, dummy); | |
854 | } else { | |
855 | errs = mem_test_quick(buf, start, end, pattern, | |
856 | iteration); | |
857 | } | |
858 | if (errs == -1UL) | |
859 | break; | |
860 | } | |
861 | ||
862 | if (errs == -1UL) { | |
863 | /* Memory test was aborted - write a newline to finish off */ | |
864 | putc('\n'); | |
865 | ret = 1; | |
866 | } else { | |
69988dc1 | 867 | printf_P(PSTR("Tested %d iteration(s) with %lu errors.\n"), |
72f58822 L |
868 | iteration, errs); |
869 | ret = errs != 0; | |
870 | } | |
871 | ||
872 | return ret; /* not reached */ | |
873 | } | |
874 | #endif /* CONFIG_CMD_MEMTEST */ |