Newer
Older
/*
* (C) Copyright 2000
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/*
* Memory Functions
*
* Copied from FADS ROM, Dan Malek (dmalek@jlc.net)
*/
#include <common.h>
#include <command.h>
#ifdef CONFIG_HAS_DATAFLASH
#include <dataflash.h>
#endif
#include <watchdog.h>
static int mod_mem(cmd_tbl_t *, int, int, int, char * const []);
/* Display values from last command.
* Memory modify remembered values are different from display memory.
*/
static uint dp_last_addr, dp_last_size;
static uint dp_last_length = 0x40;
static uint mm_last_addr, mm_last_size;
static ulong base_address = 0;
/* Memory Display
*
* Syntax:
* md{.b, .w, .l} {addr} {len}
*/
#define DISP_LINE_LEN 16
int do_mem_md ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
ulong addr, length;
#if defined(CONFIG_HAS_DATAFLASH)
ulong nbytes, linebytes;
#endif
int size;
int rc = 0;
/* We use the last specified parameters, unless new ones are
* entered.
*/
addr = dp_last_addr;
size = dp_last_size;
length = dp_last_length;
return CMD_RET_USAGE;
if ((flag & CMD_FLAG_REPEAT) == 0) {
/* New command specified. Check for a size specification.
* Defaults to long if no or incorrect specification.
*/
if ((size = cmd_get_data_size(argv[0], 4)) < 0)
return 1;
/* Address is specified since argc > 1
*/
addr = simple_strtoul(argv[1], NULL, 16);
addr += base_address;
/* If another parameter, it is the length to display.
* Length is the number of objects, not number of bytes.
*/
if (argc > 2)
length = simple_strtoul(argv[2], NULL, 16);
}
#if defined(CONFIG_HAS_DATAFLASH)
/* Print the lines.
*
* We buffer all read data, so we can make sure data is read only
* once, and all accesses are with the specified bus width.
*/
nbytes = length * size;
do {
char linebuf[DISP_LINE_LEN];
void* p;
linebytes = (nbytes>DISP_LINE_LEN)?DISP_LINE_LEN:nbytes;
rc = read_dataflash(addr, (linebytes/size)*size, linebuf);
p = (rc == DATAFLASH_OK) ? linebuf : (void*)addr;
print_buffer(addr, p, size, linebytes/size, DISP_LINE_LEN/size);
addr += linebytes;
if (ctrlc()) {
rc = 1;
break;
}
} while (nbytes > 0);
#else
# if defined(CONFIG_BLACKFIN)
/* See if we're trying to display L1 inst */
if (addr_bfin_on_chip_mem(addr)) {
char linebuf[DISP_LINE_LEN];
ulong linebytes, nbytes = length * size;
do {
linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
memcpy(linebuf, (void *)addr, linebytes);
print_buffer(addr, linebuf, size, linebytes/size, DISP_LINE_LEN/size);
nbytes -= linebytes;
addr += linebytes;
if (ctrlc()) {
rc = 1;
break;
}
} while (nbytes > 0);
} else
# endif
{
/* Print the lines. */
print_buffer(addr, (void*)addr, size, length, DISP_LINE_LEN/size);
addr += size*length;
#endif
dp_last_addr = addr;
dp_last_length = length;
dp_last_size = size;
return (rc);
}
int do_mem_mm ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
return mod_mem (cmdtp, 1, flag, argc, argv);
}
int do_mem_nm ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
return mod_mem (cmdtp, 0, flag, argc, argv);
}
int do_mem_mw ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
ulong addr, writeval, count;
int size;
if ((argc < 3) || (argc > 4))
return CMD_RET_USAGE;
if ((size = cmd_get_data_size(argv[0], 4)) < 1)
return 1;
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/* Address is specified since argc > 1
*/
addr = simple_strtoul(argv[1], NULL, 16);
addr += base_address;
/* Get the value to write.
*/
writeval = simple_strtoul(argv[2], NULL, 16);
/* Count ? */
if (argc == 4) {
count = simple_strtoul(argv[3], NULL, 16);
} else {
count = 1;
}
while (count-- > 0) {
if (size == 4)
*((ulong *)addr) = (ulong )writeval;
else if (size == 2)
*((ushort *)addr) = (ushort)writeval;
else
*((u_char *)addr) = (u_char)writeval;
addr += size;
}
return 0;
}
#ifdef CONFIG_MX_CYCLIC
int do_mem_mdc ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int i;
ulong count;
return CMD_RET_USAGE;
count = simple_strtoul(argv[3], NULL, 10);
for (;;) {
do_mem_md (NULL, 0, 3, argv);
/* delay for <count> ms... */
for (i=0; i<count; i++)
udelay (1000);
/* check for ctrl-c to abort... */
if (ctrlc()) {
puts("Abort\n");
return 0;
}
}
return 0;
}
int do_mem_mwc ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int i;
ulong count;
return CMD_RET_USAGE;
count = simple_strtoul(argv[3], NULL, 10);
for (;;) {
do_mem_mw (NULL, 0, 3, argv);
/* delay for <count> ms... */
for (i=0; i<count; i++)
udelay (1000);
/* check for ctrl-c to abort... */
if (ctrlc()) {
puts("Abort\n");
return 0;
}
}
return 0;
}
#endif /* CONFIG_MX_CYCLIC */
int do_mem_cmp (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
ulong addr1, addr2, count, ngood;
int size;
return CMD_RET_USAGE;
if ((size = cmd_get_data_size(argv[0], 4)) < 0)
return 1;
type = size == 4 ? "word" : size == 2 ? "halfword" : "byte";
addr1 = simple_strtoul(argv[1], NULL, 16);
addr1 += base_address;
addr2 = simple_strtoul(argv[2], NULL, 16);
addr2 += base_address;
count = simple_strtoul(argv[3], NULL, 16);
#ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash(addr1) | addr_dataflash(addr2)){
puts ("Comparison with DataFlash space not supported.\n\r");
return 0;
}
#endif
#ifdef CONFIG_BLACKFIN
if (addr_bfin_on_chip_mem(addr1) || addr_bfin_on_chip_mem(addr2)) {
puts ("Comparison with L1 instruction memory not supported.\n\r");
return 0;
}
#endif
for (ngood = 0; ngood < count; ++ngood) {
word1 = *(ulong *)addr1;
word2 = *(ulong *)addr2;
} else if (size == 2) {
word1 = *(ushort *)addr1;
word2 = *(ushort *)addr2;
} else {
word1 = *(u_char *)addr1;
word2 = *(u_char *)addr2;
if (word1 != word2) {
printf("%s at 0x%08lx (%#0*lx) != %s at 0x%08lx (%#0*lx)\n",
type, addr1, size, word1,
type, addr2, size, word2);
rcode = 1;
break;
/* reset watchdog from time to time */
if ((ngood % (64 << 10)) == 0)
printf("Total of %ld %s(s) were the same\n", ngood, type);
int do_mem_cp ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
ulong addr, dest, count;
int size;
return CMD_RET_USAGE;
if ((size = cmd_get_data_size(argv[0], 4)) < 0)
return 1;
addr = simple_strtoul(argv[1], NULL, 16);
addr += base_address;
dest = simple_strtoul(argv[2], NULL, 16);
dest += base_address;
count = simple_strtoul(argv[3], NULL, 16);
if (count == 0) {
puts ("Zero length ???\n");
return 1;
}
#ifndef CONFIG_SYS_NO_FLASH
if ( (addr2info(dest) != NULL)
#ifdef CONFIG_HAS_DATAFLASH
if (rc != 0) {
flash_perror (rc);
return (1);
}
puts ("done\n");
return 0;
}
#endif
#ifdef CONFIG_HAS_DATAFLASH
/* Check if we are copying from RAM or Flash to DataFlash */
if (addr_dataflash(dest) && !addr_dataflash(addr)){
int rc;
puts ("Copy to DataFlash... ");
rc = write_dataflash (dest, addr, count*size);
if (rc != 1) {
dataflash_perror (rc);
return (1);
}
puts ("done\n");
return 0;
}
/* Check if we are copying from DataFlash to RAM */
if (addr_dataflash(addr) && !addr_dataflash(dest)
#ifndef CONFIG_SYS_NO_FLASH
&& (addr2info(dest) == NULL)
#endif
){
int rc;
rc = read_dataflash(addr, count * size, (char *) dest);
if (rc != 1) {
dataflash_perror (rc);
return (1);
}
return 0;
}
if (addr_dataflash(addr) && addr_dataflash(dest)){
puts ("Unsupported combination of source/destination.\n\r");
return 1;
}
#endif
#ifdef CONFIG_BLACKFIN
/* See if we're copying to/from L1 inst */
if (addr_bfin_on_chip_mem(dest) || addr_bfin_on_chip_mem(addr)) {
memcpy((void *)dest, (void *)addr, count * size);
return 0;
}
#endif
while (count-- > 0) {
if (size == 4)
*((ulong *)dest) = *((ulong *)addr);
else if (size == 2)
*((ushort *)dest) = *((ushort *)addr);
else
*((u_char *)dest) = *((u_char *)addr);
addr += size;
dest += size;
/* reset watchdog from time to time */
if ((count % (64 << 10)) == 0)
WATCHDOG_RESET();
int do_mem_base (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
if (argc > 1) {
/* Set new base address.
*/
base_address = simple_strtoul(argv[1], NULL, 16);
}
/* Print the current base address.
*/
printf("Base Address: 0x%08lx\n", base_address);
return 0;
}
int do_mem_loop (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
int size;
volatile uint *longp;
volatile ushort *shortp;
volatile u_char *cp;
return CMD_RET_USAGE;
/* Check for a size spefication.
* Defaults to long if no or incorrect specification.
*/
if ((size = cmd_get_data_size(argv[0], 4)) < 0)
return 1;
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
/* Address is always specified.
*/
addr = simple_strtoul(argv[1], NULL, 16);
/* Length is the number of objects, not number of bytes.
*/
length = simple_strtoul(argv[2], NULL, 16);
/* We want to optimize the loops to run as fast as possible.
* If we have only one object, just run infinite loops.
*/
if (length == 1) {
if (size == 4) {
longp = (uint *)addr;
for (;;)
i = *longp;
}
if (size == 2) {
shortp = (ushort *)addr;
for (;;)
i = *shortp;
}
cp = (u_char *)addr;
for (;;)
i = *cp;
}
if (size == 4) {
for (;;) {
longp = (uint *)addr;
i = length;
while (i-- > 0)
}
}
if (size == 2) {
for (;;) {
shortp = (ushort *)addr;
i = length;
while (i-- > 0)
}
}
for (;;) {
cp = (u_char *)addr;
i = length;
while (i-- > 0)
int do_mem_loopw (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
ulong addr, length, i, data;
int size;
volatile uint *longp;
volatile ushort *shortp;
volatile u_char *cp;
return CMD_RET_USAGE;
/* Check for a size spefication.
* Defaults to long if no or incorrect specification.
*/
if ((size = cmd_get_data_size(argv[0], 4)) < 0)
return 1;
/* Address is always specified.
*/
addr = simple_strtoul(argv[1], NULL, 16);
/* Length is the number of objects, not number of bytes.
*/
length = simple_strtoul(argv[2], NULL, 16);
/* data to write */
data = simple_strtoul(argv[3], NULL, 16);
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
/* We want to optimize the loops to run as fast as possible.
* If we have only one object, just run infinite loops.
*/
if (length == 1) {
if (size == 4) {
longp = (uint *)addr;
for (;;)
*longp = data;
}
if (size == 2) {
shortp = (ushort *)addr;
for (;;)
*shortp = data;
}
cp = (u_char *)addr;
for (;;)
*cp = data;
}
if (size == 4) {
for (;;) {
longp = (uint *)addr;
i = length;
while (i-- > 0)
*longp++ = data;
}
}
if (size == 2) {
for (;;) {
shortp = (ushort *)addr;
i = length;
while (i-- > 0)
*shortp++ = data;
}
}
for (;;) {
cp = (u_char *)addr;
i = length;
while (i-- > 0)
*cp++ = data;
}
}
#endif /* CONFIG_LOOPW */
/*
* Perform a memory test. A more complete alternative test can be
* configured using CONFIG_SYS_ALT_MEMTEST. The complete test loops until
* interrupted by ctrl-c or by a failure of one of the sub-tests.
*/
int do_mem_mtest (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
vu_long *addr, *start, *end;
ulong val;
ulong readback;
int iterations = 1;
int iteration_limit;
#if defined(CONFIG_SYS_ALT_MEMTEST)
vu_long offset;
vu_long test_offset;
vu_long pattern;
vu_long temp;
vu_long anti_pattern;
vu_long num_words;
#if defined(CONFIG_SYS_MEMTEST_SCRATCH)
vu_long *dummy = (vu_long*)CONFIG_SYS_MEMTEST_SCRATCH;
vu_long *dummy = 0; /* yes, this is address 0x0, not NULL */
int j;
static const ulong bitpattern[] = {
0x00000001, /* single bit */
0x00000003, /* two adjacent bits */
0x00000007, /* three adjacent bits */
0x0000000F, /* four adjacent bits */
0x00000005, /* two non-adjacent bits */
0x00000015, /* three non-adjacent bits */
0x00000055, /* four non-adjacent bits */
0xaaaaaaaa, /* alternating 1/0 */
};
#else
ulong incr;
ulong pattern;
#endif
start = (ulong *)CONFIG_SYS_MEMTEST_START;
end = (ulong *)(CONFIG_SYS_MEMTEST_END);
if (argc > 4)
iteration_limit = (ulong)simple_strtoul(argv[4], NULL, 16);
else
iteration_limit = 0;
#if defined(CONFIG_SYS_ALT_MEMTEST)
printf ("Testing %08x ... %08x:\n", (uint)start, (uint)end);
debug("%s:%d: start 0x%p end 0x%p\n",
__FUNCTION__, __LINE__, start, end);
for (;;) {
if (ctrlc()) {
putc ('\n');
return 1;
}
if (iteration_limit && iterations > iteration_limit) {
printf("Tested %d iteration(s) with %lu errors.\n",
iterations-1, errs);
return errs != 0;
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
iterations++;
/*
* Data line test: write a pattern to the first
* location, write the 1's complement to a 'parking'
* address (changes the state of the data bus so a
* floating bus doen't give a false OK), and then
* read the value back. Note that we read it back
* into a variable because the next time we read it,
* it might be right (been there, tough to explain to
* the quality guys why it prints a failure when the
* "is" and "should be" are obviously the same in the
* error message).
*
* Rather than exhaustively testing, we test some
* patterns by shifting '1' bits through a field of
* '0's and '0' bits through a field of '1's (i.e.
* pattern and ~pattern).
*/
addr = start;
for (j = 0; j < sizeof(bitpattern)/sizeof(bitpattern[0]); j++) {
val = bitpattern[j];
for(; val != 0; val <<= 1) {
*addr = val;
*dummy = ~val; /* clear the test data off of the bus */
readback = *addr;
if(readback != val) {
printf ("FAILURE (data line): "
errs++;
if (ctrlc()) {
putc ('\n');
return 1;
}
}
*addr = ~val;
*dummy = val;
readback = *addr;
if(readback != ~val) {
printf ("FAILURE (data line): "
"Is %08lx, should be %08lx\n",
errs++;
if (ctrlc()) {
putc ('\n');
return 1;
}
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
}
}
}
/*
* Based on code whose Original Author and Copyright
* information follows: Copyright (c) 1998 by Michael
* Barr. This software is placed into the public
* domain and may be used for any purpose. However,
* this notice must not be changed or removed and no
* warranty is either expressed or implied by its
* publication or distribution.
*/
/*
* Address line test
*
* Description: Test the address bus wiring in a
* memory region by performing a walking
* 1's test on the relevant bits of the
* address and checking for aliasing.
* This test will find single-bit
* address failures such as stuck -high,
* stuck-low, and shorted pins. The base
* address and size of the region are
* selected by the caller.
*
* Notes: For best results, the selected base
* address should have enough LSB 0's to
* guarantee single address bit changes.
* For example, to test a 64-Kbyte
* region, select a base address on a
* 64-Kbyte boundary. Also, select the
* region size as a power-of-two if at
* all possible.
*
* Returns: 0 if the test succeeds, 1 if the test fails.
*/
len = ((ulong)end - (ulong)start)/sizeof(vu_long);
pattern = (vu_long) 0xaaaaaaaa;
anti_pattern = (vu_long) 0x55555555;
debug("%s:%d: length = 0x%.8lx\n",
/*
* Write the default pattern at each of the
* power-of-two offsets.
*/
for (offset = 1; offset < len; offset <<= 1) {
start[offset] = pattern;
}
/*
* Check for address bits stuck high.
*/
test_offset = 0;
start[test_offset] = anti_pattern;
for (offset = 1; offset < len; offset <<= 1) {
temp = start[offset];
if (temp != pattern) {
printf ("\nFAILURE: Address bit stuck high @ 0x%.8lx:"
" expected 0x%.8lx, actual 0x%.8lx\n",
(ulong)&start[offset], pattern, temp);
errs++;
if (ctrlc()) {
putc ('\n');
return 1;
}
WATCHDOG_RESET();
/*
* Check for addr bits stuck low or shorted.
*/
for (test_offset = 1; test_offset < len; test_offset <<= 1) {
for (offset = 1; offset < len; offset <<= 1) {
temp = start[offset];
if ((temp != pattern) && (offset != test_offset)) {
printf ("\nFAILURE: Address bit stuck low or shorted @"
" 0x%.8lx: expected 0x%.8lx, actual 0x%.8lx\n",
(ulong)&start[offset], pattern, temp);
errs++;
if (ctrlc()) {
putc ('\n');
return 1;
}
}
}
start[test_offset] = pattern;
}
/*
* Description: Test the integrity of a physical
* memory device by performing an
* increment/decrement test over the
* entire region. In the process every
* storage bit in the device is tested
* as a zero and a one. The base address
* and the size of the region are
* selected by the caller.
*
* Returns: 0 if the test succeeds, 1 if the test fails.
*/
num_words = ((ulong)end - (ulong)start)/sizeof(vu_long) + 1;
/*
* Fill memory with a known pattern.
*/
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
WATCHDOG_RESET();
start[offset] = pattern;
}
/*
* Check each location and invert it for the second pass.
*/
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
WATCHDOG_RESET();
temp = start[offset];
if (temp != pattern) {
printf ("\nFAILURE (read/write) @ 0x%.8lx:"
" expected 0x%.8lx, actual 0x%.8lx)\n",
(ulong)&start[offset], pattern, temp);
errs++;
if (ctrlc()) {
putc ('\n');
return 1;
}
}
anti_pattern = ~pattern;
start[offset] = anti_pattern;
}
/*
* Check each location for the inverted pattern and zero it.
*/
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
WATCHDOG_RESET();
anti_pattern = ~pattern;
temp = start[offset];
if (temp != anti_pattern) {
printf ("\nFAILURE (read/write): @ 0x%.8lx:"
" expected 0x%.8lx, actual 0x%.8lx)\n",
(ulong)&start[offset], anti_pattern, temp);
errs++;
if (ctrlc()) {
putc ('\n');
return 1;
}
}
start[offset] = 0;
}
}
#else /* The original, quickie test */
incr = 1;
for (;;) {
if (ctrlc()) {
putc ('\n');
return 1;
}
if (iteration_limit && iterations > iteration_limit) {
printf("Tested %d iteration(s) with %lu errors.\n",
iterations-1, errs);
return errs != 0;
printf ("\rPattern %08lX Writing..."
"%12s"
"\b\b\b\b\b\b\b\b\b\b",
pattern, "");
for (addr=start,val=pattern; addr<end; addr++) {
WATCHDOG_RESET();
WATCHDOG_RESET();
readback = *addr;
if (readback != val) {
printf ("\nMem error @ 0x%08X: "
"found %08lX, expected %08lX\n",
(uint)(uintptr_t)addr, readback, val);
errs++;
if (ctrlc()) {
putc ('\n');
return 1;
}
}
val += incr;
}
/*
* Flip the pattern each time to make lots of zeros and
* then, the next time, lots of ones. We decrement
* the "negative" patterns and increment the "positive"
* patterns to preserve this feature.
*/
if(pattern & 0x80000000) {
pattern = -pattern; /* complement & increment */
}
else {
pattern = ~pattern;
}
incr = -incr;
}
#endif
return 0; /* not reached */
}
/* Modify memory.
*
* Syntax:
* mm{.b, .w, .l} {addr}
* nm{.b, .w, .l} {addr}
*/
static int
mod_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const argv[])
ulong addr, i;
int nbytes, size;
return CMD_RET_USAGE;
#ifdef CONFIG_BOOT_RETRY_TIME
reset_cmd_timeout(); /* got a good command to get here */
#endif
/* We use the last specified parameters, unless new ones are
* entered.
*/
addr = mm_last_addr;
size = mm_last_size;
if ((flag & CMD_FLAG_REPEAT) == 0) {
/* New command specified. Check for a size specification.
* Defaults to long if no or incorrect specification.
*/
if ((size = cmd_get_data_size(argv[0], 4)) < 0)
return 1;
/* Address is specified since argc > 1
*/
addr = simple_strtoul(argv[1], NULL, 16);
addr += base_address;
}
#ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash(addr)){
puts ("Can't modify DataFlash in place. Use cp instead.\n\r");
return 0;
}
#endif
#ifdef CONFIG_BLACKFIN
if (addr_bfin_on_chip_mem(addr)) {
puts ("Can't modify L1 instruction in place. Use cp instead.\n\r");
return 0;
}
#endif
/* Print the address, followed by value. Then accept input for
* the next value. A non-converted value exits.
*/
do {