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