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