Add function for printing command usage
[oweals/u-boot_mod.git] / u-boot / common / cmd_mem.c
1 /*
2  * (C) Copyright 2000
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 /*
25  * Memory Functions
26  *
27  * Copied from FADS ROM, Dan Malek (dmalek@jlc.net)
28  */
29
30 #include <common.h>
31 #include <command.h>
32 #if (CONFIG_COMMANDS & CFG_CMD_MMC)
33 #include <mmc.h>
34 #endif
35
36 #if (CONFIG_COMMANDS & (CFG_CMD_MEMORY  | \
37                                                 CFG_CMD_ITEST   | \
38                                                 CFG_CMD_PCI             | \
39                                                 CMD_CMD_PORTIO) )
40
41 DECLARE_GLOBAL_DATA_PTR;
42
43 /*
44  * Check for a size specification .b, .w or .l.
45  */
46 int cmd_get_data_size(char* arg, int default_size){
47         int len = strlen(arg);
48
49         if(len > 2 && arg[len - 2] == '.'){
50                 switch(arg[len - 1]){
51                         case 'b':
52                                 return(1);
53                         case 'w':
54                                 return(2);
55                         case 'l':
56                                 return(4);
57                         case 's':
58                                 return(-2);
59                         default:
60                                 return(-1);
61                 }
62         }
63
64         return(default_size);
65 }
66 #endif
67
68 #if (CONFIG_COMMANDS & CFG_CMD_MEMORY)
69
70 static int mod_mem(cmd_tbl_t *, int, int, int, char *[]);
71
72 /*
73  * Display values from last command.
74  * Memory modify remembered values are different from display memory.
75  */
76 uint dp_last_addr, dp_last_size;
77 uint dp_last_length = 0x40;
78 uint mm_last_addr, mm_last_size;
79
80 /*
81  * Memory Display
82  *
83  * Syntax:
84  *      md{.b, .w, .l} {addr} {len}
85  */
86 #define DISP_LINE_LEN   16
87 int do_mem_md(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){
88         ulong addr, length;
89         ulong i, nbytes, linebytes;
90         u_char *cp;
91         int size;
92         int rc = 0;
93
94         /*
95          * We use the last specified parameters, unless new ones are
96          * entered.
97          */
98         addr = dp_last_addr;
99         size = dp_last_size;
100         length = dp_last_length;
101
102         if(argc < 2){
103                 print_cmd_help(cmdtp);
104                 return(1);
105         }
106
107         if((flag & CMD_FLAG_REPEAT) == 0){
108                 /*
109                  * New command specified.  Check for a size specification.
110                  * Defaults to long if no or incorrect specification.
111                  */
112                 if((size = cmd_get_data_size(argv[0], 4)) < 0){
113                         return(1);
114                 }
115
116                 /*
117                  * Address is specified since argc > 1
118                  */
119                 addr = simple_strtoul(argv[1], NULL, 16);
120
121                 /*
122                  * If another parameter, it is the length to display.
123                  * Length is the number of objects, not number of bytes.
124                  */
125                 if(argc > 2){
126                         length = simple_strtoul(argv[2], NULL, 16);
127                 }
128         }
129
130         /*
131          * Print the lines.
132          *
133          * We buffer all read data, so we can make sure data is read only
134          * once, and all accesses are with the specified bus width.
135          */
136         nbytes = length * size;
137
138         do {
139                 char linebuf[DISP_LINE_LEN];
140                 uint *uip = (uint *)linebuf;
141                 ushort *usp = (ushort *)linebuf;
142                 u_char *ucp = (u_char *)linebuf;
143
144                 printf("%08lX:", addr);
145
146                 linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
147
148                 for(i = 0; i < linebytes; i += size){
149                         if(size == 4){
150                                 printf(" %08X", (*uip++ = *((uint *)addr)));
151                         } else if(size == 2){
152                                 printf(" %04X", (*usp++ = *((ushort *)addr)));
153                         } else {
154                                 printf(" %02X", (*ucp++ = *((u_char *)addr)));
155                         }
156                         addr += size;
157                 }
158
159                 puts("    ");
160                 cp = (u_char *)linebuf;
161
162                 for(i = 0; i < linebytes; i++){
163                         if((*cp < 0x20) || (*cp > 0x7e)){
164                                 putc('.');
165                         } else {
166                                 printf("%c", *cp);
167                         }
168                         cp++;
169                 }
170
171                 putc('\n');
172                 nbytes -= linebytes;
173
174                 if(ctrlc()){
175                         rc = 1;
176                         break;
177                 }
178
179         } while(nbytes > 0);
180
181         dp_last_addr = addr;
182         dp_last_length = length;
183         dp_last_size = size;
184
185         return(rc);
186 }
187
188 int do_mem_mm(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){
189         return(mod_mem(cmdtp, 1, flag, argc, argv));
190 }
191 int do_mem_nm(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){
192         return(mod_mem(cmdtp, 0, flag, argc, argv));
193 }
194
195 int do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){
196         ulong addr, writeval, count;
197         int size;
198
199         if((argc < 3) || (argc > 4)){
200                 print_cmd_help(cmdtp);
201                 return(1);
202         }
203
204         /*
205          * Check for size specification.
206          */
207         if((size = cmd_get_data_size(argv[0], 4)) < 1){
208                 return(1);
209         }
210
211         /*
212          * Address is specified since argc > 1
213          */
214         addr = simple_strtoul(argv[1], NULL, 16);
215
216         /*
217          * Get the value to write.
218          */
219         writeval = simple_strtoul(argv[2], NULL, 16);
220
221         /* Count ? */
222         if(argc == 4){
223                 count = simple_strtoul(argv[3], NULL, 16);
224         } else {
225                 count = 1;
226         }
227
228         while(count-- > 0){
229                 if(size == 4){
230                         *((ulong *)addr) = (ulong)writeval;
231                 } else if(size == 2){
232                         *((ushort *)addr) = (ushort)writeval;
233                 } else {
234                         *((u_char *)addr) = (u_char)writeval;
235                 }
236                 addr += size;
237         }
238
239         return(0);
240 }
241
242 int do_mem_cp(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){
243         ulong addr, dest, count;
244         int size;
245
246         if(argc != 4){
247                 print_cmd_help(cmdtp);
248                 return(1);
249         }
250
251         /*
252          * Check for size specification.
253          */
254         if((size = cmd_get_data_size(argv[0], 4)) < 0){
255                 return(1);
256         }
257
258         addr  = simple_strtoul(argv[1], NULL, 16);
259         dest  = simple_strtoul(argv[2], NULL, 16);
260         count = simple_strtoul(argv[3], NULL, 16);
261
262         if(count == 0){
263                 puts("## Error: zero length?\n");
264                 return(1);
265         }
266
267 #ifndef CFG_NO_FLASH
268         /* check if we are copying to Flash */
269         if(addr2info(dest) != NULL){
270                 int rc;
271
272                 puts("Copying to FLASH...\n");
273
274                 rc = flash_write((char *)addr, dest, count * size);
275
276                 if(rc != 0){
277                         flash_perror(rc);
278                         return(1);
279                 }
280
281                 puts("Done!\n\n");
282                 return(0);
283         }
284 #endif
285
286         while(count-- > 0){
287                 if(size == 4){
288                         *((ulong *)dest) = *((ulong *)addr);
289                 } else if(size == 2){
290                         *((ushort *)dest) = *((ushort *)addr);
291                 } else {
292                         *((u_char *)dest) = *((u_char *)addr);
293                 }
294                 addr += size;
295                 dest += size;
296         }
297
298         return(0);
299 }
300
301 /*
302  * Perform a memory test. A more complete alternative test can be
303  * configured using CFG_ALT_MEMTEST. The complete test loops until
304  * interrupted by ctrl-c or by a failure of one of the sub-tests.
305  */
306 int do_mem_mtest(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){
307         bd_t *bd = gd->bd;
308         vu_long *addr, *start, *end;
309         ulong val;
310         ulong readback;
311
312 #if defined(CFG_ALT_MEMTEST)
313         vu_long addr_mask;
314         vu_long offset;
315         vu_long test_offset;
316         vu_long pattern;
317         vu_long temp;
318         vu_long anti_pattern;
319         vu_long num_words;
320 #if defined(CFG_MEMTEST_SCRATCH)
321         vu_long *dummy = (vu_long*)CFG_MEMTEST_SCRATCH;
322 #else
323         vu_long *dummy = NULL;
324 #endif
325         int j;
326         int iterations = 1;
327
328         static const ulong bitpattern[] = {
329                 0x00000001, /* single bit */
330                 0x00000003, /* two adjacent bits */
331                 0x00000007, /* three adjacent bits */
332                 0x0000000F, /* four adjacent bits */
333                 0x00000005, /* two non-adjacent bits */
334                 0x00000015, /* three non-adjacent bits */
335                 0x00000055, /* four non-adjacent bits */
336                 0xaaaaaaaa, /* alternating 1/0 */
337         };
338 #else
339         ulong incr;
340         ulong pattern;
341         int rcode = 0;
342 #endif
343
344         if(argc > 1){
345                 start = (ulong *)simple_strtoul(argv[1], NULL, 16);
346         } else {
347                 start = (ulong *)CFG_MEMTEST_START;
348         }
349
350         if(argc > 2){
351                 end = (ulong *)simple_strtoul(argv[2], NULL, 16);
352         } else {
353                 end = (ulong *)CFG_MEMTEST_END;
354         }
355
356         if((uint)start >= (uint)end){
357                 puts("## Error: end address must be bigger than start address!\n");
358                 return(1);
359         }
360
361         if(((uint)start < (uint)CFG_MEMTEST_START) || ((uint)end > (uint)CFG_MEMTEST_END)){
362                 printf("## Error: start and end addresses should be in 0x%08X...0x%08X range!\n", (uint)CFG_MEMTEST_START, (uint)CFG_MEMTEST_END);
363                 return(1);
364         }
365
366 #if !defined(CFG_ALT_MEMTEST)
367         if(argc > 3){
368                 pattern = (ulong)simple_strtoul(argv[3], NULL, 16);
369         } else {
370                 pattern = 0;
371         }
372 #endif
373
374 #if defined(CFG_ALT_MEMTEST)
375         printf("Testing RAM 0x%08X...0x%08X:\n", (uint)start, (uint)end);
376
377         for(;;){
378                 if(ctrlc()){
379                         putc('\n');
380                         return(1);
381                 }
382
383                 printf("Iteration: %6d\r", iterations);
384
385                 iterations++;
386
387                 /*
388                  * Data line test: write a pattern to the first
389                  * location, write the 1's complement to a 'parking'
390                  * address (changes the state of the data bus so a
391                  * floating bus doen't give a false OK), and then
392                  * read the value back. Note that we read it back
393                  * into a variable because the next time we read it,
394                  * it might be right (been there, tough to explain to
395                  * the quality guys why it prints a failure when the
396                  * "is" and "should be" are obviously the same in the
397                  * error message).
398                  *
399                  * Rather than exhaustively testing, we test some
400                  * patterns by shifting '1' bits through a field of
401                  * '0's and '0' bits through a field of '1's (i.e.
402                  * pattern and ~pattern).
403                  */
404                 addr = start;
405
406                 for(j = 0; j < sizeof(bitpattern)/sizeof(bitpattern[0]); j++){
407                         val = bitpattern[j];
408
409                         for(; val != 0; val <<= 1){
410                                 *addr = val;
411                                 *dummy = ~val; /* clear the test data off of the bus */
412                                 readback = *addr;
413
414                                 if(readback != val){
415                                         printf("## Error (data line): expected 0x%08lX, found 0x%08lX\n", val, readback);
416                                 }
417
418                                 *addr = ~val;
419                                 *dummy = val;
420                                 readback = *addr;
421
422                                 if(readback != ~val){
423                                         printf("## Error (data line): expected %08lX, found %08lX\n", ~val, readback);
424                                 }
425                         }
426                 }
427
428                 /*
429                  * Based on code whose Original Author and Copyright
430                  * information follows: Copyright (c) 1998 by Michael
431                  * Barr. This software is placed into the public
432                  * domain and may be used for any purpose. However,
433                  * this notice must not be changed or removed and no
434                  * warranty is either expressed or implied by its
435                  * publication or distribution.
436                  */
437
438                 /*
439                  * Address line test
440                  *
441                  * Description: Test the address bus wiring in a
442                  *              memory region by performing a walking
443                  *              1's test on the relevant bits of the
444                  *              address and checking for aliasing.
445                  *              This test will find single-bit
446                  *              address failures such as stuck -high,
447                  *              stuck-low, and shorted pins. The base
448                  *              address and size of the region are
449                  *              selected by the caller.
450                  *
451                  * Notes:       For best results, the selected base
452                  *              address should have enough LSB 0's to
453                  *              guarantee single address bit changes.
454                  *              For example, to test a 64-Kbyte
455                  *              region, select a base address on a
456                  *              64-Kbyte boundary. Also, select the
457                  *              region size as a power-of-two if at
458                  *              all possible.
459                  *
460                  * Returns:     0 if the test succeeds, 1 if the test fails.
461                  *
462                  * ## NOTE ##   Be sure to specify start and end
463                  *              addresses such that addr_mask has
464                  *              lots of bits set. For example an
465                  *              address range of 01000000 02000000 is
466                  *              bad while a range of 01000000
467                  *              01ffffff is perfect.
468                  */
469                 addr_mask = ((ulong)end - (ulong)start)/sizeof(vu_long);
470                 pattern = (vu_long)0xaaaaaaaa;
471                 anti_pattern = (vu_long)0x55555555;
472
473                 /*
474                  * Write the default pattern at each of the
475                  * power-of-two offsets.
476                  */
477                 for(offset = 1; (offset & addr_mask) != 0; offset <<= 1){
478                         start[offset] = pattern;
479                 }
480
481                 /*
482                  * Check for address bits stuck high.
483                  */
484                 test_offset = 0;
485                 start[test_offset] = anti_pattern;
486
487                 for(offset = 1; (offset & addr_mask) != 0; offset <<= 1){
488                         temp = start[offset];
489
490                         if(temp != pattern){
491                                 printf("\n## Error: address bit stuck high @ 0x%.8lX, expected 0x%.8lX, found 0x%.8lX\n", (ulong)&start[offset], pattern, temp);
492                                 return(1);
493                         }
494                 }
495                 start[test_offset] = pattern;
496
497                 /*
498                  * Check for addr bits stuck low or shorted.
499                  */
500                 for(test_offset = 1; (test_offset & addr_mask) != 0; test_offset <<= 1){
501                         start[test_offset] = anti_pattern;
502
503                         for(offset = 1; (offset & addr_mask) != 0; offset <<= 1){
504                                 temp = start[offset];
505
506                                 if((temp != pattern) && (offset != test_offset)){
507                                         printf("\n## Error: address bit stuck low or shorted @ 0x%.8lX, expected 0x%.8lX, found 0x%.8lX\n", (ulong)&start[offset], pattern, temp);
508                                         return(1);
509                                 }
510                         }
511                         start[test_offset] = pattern;
512                 }
513
514                 /*
515                  * Description: Test the integrity of a physical
516                  *              memory device by performing an
517                  *              increment/decrement test over the
518                  *              entire region. In the process every
519                  *              storage bit in the device is tested
520                  *              as a zero and a one. The base address
521                  *              and the size of the region are
522                  *              selected by the caller.
523                  *
524                  * Returns:     0 if the test succeeds, 1 if the test fails.
525                  */
526                 num_words = ((ulong)end - (ulong)start)/sizeof(vu_long) + 1;
527
528                 /*
529                  * Fill memory with a known pattern.
530                  */
531                 for(pattern = 1, offset = 0; offset < num_words; pattern++, offset++){
532                         start[offset] = pattern;
533                 }
534
535                 /*
536                  * Check each location and invert it for the second pass.
537                  */
538                 for(pattern = 1, offset = 0; offset < num_words; pattern++, offset++){
539                         temp = start[offset];
540
541                         if(temp != pattern){
542                                 printf("\n## Error (read/write) @ 0x%.8lX: expected 0x%.8lX, found 0x%.8lX)\n", (ulong)&start[offset], pattern, temp);
543                                 return(1);
544                         }
545
546                         anti_pattern = ~pattern;
547                         start[offset] = anti_pattern;
548                 }
549
550                 /*
551                  * Check each location for the inverted pattern and zero it.
552                  */
553                 for(pattern = 1, offset = 0; offset < num_words; pattern++, offset++){
554                         anti_pattern = ~pattern;
555                         temp = start[offset];
556
557                         if(temp != anti_pattern){
558                                 printf("\n## Error (read/write): @ 0x%.8lX: expected 0x%.8lX, found 0x%.8lX)\n", (ulong)&start[offset], anti_pattern, temp);
559                                 return(1);
560                         }
561                         start[offset] = 0;
562                 }
563         }
564
565 #else /* The original, quickie test */
566         incr = 1;
567         for(;;){
568                 if(ctrlc()){
569                         putc('\n');
570                         return(1);
571                 }
572
573                 printf("\rPattern %08lX  Writing...%12s\b\b\b\b\b\b\b\b\b\b", pattern, "");
574
575                 for(addr = start, val = pattern; addr < end; addr++){
576                         *addr = val;
577                         val += incr;
578                 }
579
580                 puts("Reading...");
581
582                 for(addr = start, val = pattern; addr < end; addr++){
583                         readback = *addr;
584                         if(readback != val){
585                                 printf("\n## Error @ 0x%08X: found %08lX, expected %08lX\n", (uint)addr, readback, val);
586                                 rcode = 1;
587                         }
588                         val += incr;
589                 }
590
591                 /*
592                  * Flip the pattern each time to make lots of zeros and
593                  * then, the next time, lots of ones.  We decrement
594                  * the "negative" patterns and increment the "positive"
595                  * patterns to preserve this feature.
596                  */
597                 if(pattern & 0x80000000){
598                         pattern = -pattern; /* complement & increment */
599                 } else {
600                         pattern = ~pattern;
601                 }
602                 incr = -incr;
603         }
604         return(rcode);
605 #endif
606 }
607
608 /* Modify memory.
609  *
610  * Syntax:
611  *      mm{.b, .w, .l} {addr}
612  *      nm{.b, .w, .l} {addr}
613  */
614 static int mod_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char *argv[]){
615         ulong addr, i;
616         int nbytes, size;
617         extern char console_buffer[];
618
619         if(argc != 2){
620                 print_cmd_help(cmdtp);
621                 return(1);
622         }
623
624         /* We use the last specified parameters, unless new ones are
625          * entered.
626          */
627         addr = mm_last_addr;
628         size = mm_last_size;
629
630         if((flag & CMD_FLAG_REPEAT) == 0){
631                 /* New command specified.  Check for a size specification.
632                  * Defaults to long if no or incorrect specification.
633                  */
634                 if((size = cmd_get_data_size(argv[0], 4)) < 0){
635                         return(1);
636                 }
637
638                 /* Address is specified since argc > 1
639                  */
640                 addr = simple_strtoul(argv[1], NULL, 16);
641         }
642
643         /* Print the address, followed by value.  Then accept input for
644          * the next value.  A non-converted value exits.
645          */
646         do {
647                 printf("%08lX:", addr);
648
649                 if(size == 4){
650                         printf(" %08X", *((uint *)addr));
651                 } else if(size == 2){
652                         printf(" %04X", *((ushort *)addr));
653                 } else{
654                         printf(" %02X", *((u_char *)addr));
655                 }
656
657                 nbytes = readline(" ? ");
658                 if(nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')){
659                         /* <CR> pressed as only input, don't modify current
660                          * location and move to next. "-" pressed will go back.
661                          */
662                         if(incrflag){
663                                 addr += nbytes ? -size : size;
664                         }
665                         nbytes = 1;
666                 } else {
667                         char *endp;
668                         i = simple_strtoul(console_buffer, &endp, 16);
669                         nbytes = endp - console_buffer;
670                         if(nbytes){
671                                 if(size == 4){
672                                         *((uint *)addr) = i;
673                                 } else if(size == 2){
674                                         *((ushort *)addr) = i;
675                                 } else {
676                                         *((u_char *)addr) = i;
677                                 }
678                                 if(incrflag){
679                                         addr += size;
680                                 }
681                         }
682                 }
683         } while(nbytes);
684
685         mm_last_addr = addr;
686         mm_last_size = size;
687
688         return(0);
689 }
690
691 /**************************************************/
692 U_BOOT_CMD(md, 3, 1, do_mem_md, "memory display\n", "[.b, .w, .l] address [# of objects]\n\t- memory display\n");
693 U_BOOT_CMD(mm, 2, 1, do_mem_mm, "memory modify (auto-incrementing)\n", "[.b, .w, .l] address\n\t- memory modify, auto increment address\n");
694 U_BOOT_CMD(nm, 2, 1, do_mem_nm, "memory modify (constant address)\n", "[.b, .w, .l] address\n\t- memory modify, read and keep address\n");
695 U_BOOT_CMD(mw, 4, 1, do_mem_mw, "memory write (fill)\n", "[.b, .w, .l] address value [count]\n\t- write memory\n");
696 #if defined(CFG_ALT_MEMTEST)
697 U_BOOT_CMD(mtest, 3, 1, do_mem_mtest, "RAM test\n", "[start [end]]\n\t- complete, alternative RAM test\n");
698 #else
699 U_BOOT_CMD(mtest, 4, 1, do_mem_mtest, "simple RAM test\n", "[start [end [pattern]]]\n\t- simple RAM read/write test\n");
700 #endif
701 U_BOOT_CMD(cp, 4, 1, do_mem_cp, "memory copy\n", "[.b, .w, .l] source target count\n\t- copy memory\n");
702
703 #endif  /* CFG_CMD_MEMORY */