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