mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-18 10:48:51 +00:00
8e434cb705
This additional bitflip memory test is inspired by the bitflip test in memtester v4.3.0. It show some errors on some problematic GARDENA MT7688 based boards. The other memory tests usually don't show any errors here. Signed-off-by: Stefan Roese <sr@denx.de>
1352 lines
29 KiB
C
1352 lines
29 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2000
|
|
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
|
*/
|
|
|
|
/*
|
|
* Memory Functions
|
|
*
|
|
* Copied from FADS ROM, Dan Malek (dmalek@jlc.net)
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <console.h>
|
|
#include <bootretry.h>
|
|
#include <cli.h>
|
|
#include <command.h>
|
|
#include <console.h>
|
|
#include <flash.h>
|
|
#include <hash.h>
|
|
#include <mapmem.h>
|
|
#include <watchdog.h>
|
|
#include <asm/io.h>
|
|
#include <linux/compiler.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#ifndef CONFIG_SYS_MEMTEST_SCRATCH
|
|
#define CONFIG_SYS_MEMTEST_SCRATCH 0
|
|
#endif
|
|
|
|
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 ulong dp_last_addr, dp_last_size;
|
|
static ulong dp_last_length = 0x40;
|
|
static ulong mm_last_addr, mm_last_size;
|
|
|
|
static ulong base_address = 0;
|
|
|
|
/* Memory Display
|
|
*
|
|
* Syntax:
|
|
* md{.b, .w, .l, .q} {addr} {len}
|
|
*/
|
|
#define DISP_LINE_LEN 16
|
|
static int do_mem_md(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
ulong addr, length, bytes;
|
|
const void *buf;
|
|
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;
|
|
|
|
if (argc < 2)
|
|
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);
|
|
}
|
|
|
|
bytes = size * length;
|
|
buf = map_sysmem(addr, bytes);
|
|
|
|
/* Print the lines. */
|
|
print_buffer(addr, buf, size, length, DISP_LINE_LEN / size);
|
|
addr += bytes;
|
|
unmap_sysmem(buf);
|
|
|
|
dp_last_addr = addr;
|
|
dp_last_length = length;
|
|
dp_last_size = size;
|
|
return (rc);
|
|
}
|
|
|
|
static int do_mem_mm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
return mod_mem (cmdtp, 1, flag, argc, argv);
|
|
}
|
|
static int do_mem_nm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
return mod_mem (cmdtp, 0, flag, argc, argv);
|
|
}
|
|
|
|
static int do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
u64 writeval;
|
|
#else
|
|
ulong writeval;
|
|
#endif
|
|
ulong addr, count;
|
|
int size;
|
|
void *buf, *start;
|
|
ulong bytes;
|
|
|
|
if ((argc < 3) || (argc > 4))
|
|
return CMD_RET_USAGE;
|
|
|
|
/* Check for size specification.
|
|
*/
|
|
if ((size = cmd_get_data_size(argv[0], 4)) < 1)
|
|
return 1;
|
|
|
|
/* Address is specified since argc > 1
|
|
*/
|
|
addr = simple_strtoul(argv[1], NULL, 16);
|
|
addr += base_address;
|
|
|
|
/* Get the value to write.
|
|
*/
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
writeval = simple_strtoull(argv[2], NULL, 16);
|
|
#else
|
|
writeval = simple_strtoul(argv[2], NULL, 16);
|
|
#endif
|
|
|
|
/* Count ? */
|
|
if (argc == 4) {
|
|
count = simple_strtoul(argv[3], NULL, 16);
|
|
} else {
|
|
count = 1;
|
|
}
|
|
|
|
bytes = size * count;
|
|
start = map_sysmem(addr, bytes);
|
|
buf = start;
|
|
while (count-- > 0) {
|
|
if (size == 4)
|
|
*((u32 *)buf) = (u32)writeval;
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
else if (size == 8)
|
|
*((u64 *)buf) = (u64)writeval;
|
|
#endif
|
|
else if (size == 2)
|
|
*((u16 *)buf) = (u16)writeval;
|
|
else
|
|
*((u8 *)buf) = (u8)writeval;
|
|
buf += size;
|
|
}
|
|
unmap_sysmem(start);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_CMD_MX_CYCLIC
|
|
static int do_mem_mdc(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
int i;
|
|
ulong count;
|
|
|
|
if (argc < 4)
|
|
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;
|
|
}
|
|
|
|
static int do_mem_mwc(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
int i;
|
|
ulong count;
|
|
|
|
if (argc < 4)
|
|
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_CMD_MX_CYCLIC */
|
|
|
|
static int do_mem_cmp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
ulong addr1, addr2, count, ngood, bytes;
|
|
int size;
|
|
int rcode = 0;
|
|
const char *type;
|
|
const void *buf1, *buf2, *base;
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
u64 word1, word2;
|
|
#else
|
|
ulong word1, word2;
|
|
#endif
|
|
|
|
if (argc != 4)
|
|
return CMD_RET_USAGE;
|
|
|
|
/* Check for size specification.
|
|
*/
|
|
if ((size = cmd_get_data_size(argv[0], 4)) < 0)
|
|
return 1;
|
|
type = size == 8 ? "double word" :
|
|
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);
|
|
|
|
bytes = size * count;
|
|
base = buf1 = map_sysmem(addr1, bytes);
|
|
buf2 = map_sysmem(addr2, bytes);
|
|
for (ngood = 0; ngood < count; ++ngood) {
|
|
if (size == 4) {
|
|
word1 = *(u32 *)buf1;
|
|
word2 = *(u32 *)buf2;
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
} else if (size == 8) {
|
|
word1 = *(u64 *)buf1;
|
|
word2 = *(u64 *)buf2;
|
|
#endif
|
|
} else if (size == 2) {
|
|
word1 = *(u16 *)buf1;
|
|
word2 = *(u16 *)buf2;
|
|
} else {
|
|
word1 = *(u8 *)buf1;
|
|
word2 = *(u8 *)buf2;
|
|
}
|
|
if (word1 != word2) {
|
|
ulong offset = buf1 - base;
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
printf("%s at 0x%p (%#0*llx) != %s at 0x%p (%#0*llx)\n",
|
|
type, (void *)(addr1 + offset), size, word1,
|
|
type, (void *)(addr2 + offset), size, word2);
|
|
#else
|
|
printf("%s at 0x%08lx (%#0*lx) != %s at 0x%08lx (%#0*lx)\n",
|
|
type, (ulong)(addr1 + offset), size, word1,
|
|
type, (ulong)(addr2 + offset), size, word2);
|
|
#endif
|
|
rcode = 1;
|
|
break;
|
|
}
|
|
|
|
buf1 += size;
|
|
buf2 += size;
|
|
|
|
/* reset watchdog from time to time */
|
|
if ((ngood % (64 << 10)) == 0)
|
|
WATCHDOG_RESET();
|
|
}
|
|
unmap_sysmem(buf1);
|
|
unmap_sysmem(buf2);
|
|
|
|
printf("Total of %ld %s(s) were the same\n", ngood, type);
|
|
return rcode;
|
|
}
|
|
|
|
static int do_mem_cp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
ulong addr, dest, count;
|
|
void *src, *dst;
|
|
int size;
|
|
|
|
if (argc != 4)
|
|
return CMD_RET_USAGE;
|
|
|
|
/* Check for size specification.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
src = map_sysmem(addr, count * size);
|
|
dst = map_sysmem(dest, count * size);
|
|
|
|
#ifdef CONFIG_MTD_NOR_FLASH
|
|
/* check if we are copying to Flash */
|
|
if (addr2info((ulong)dst)) {
|
|
int rc;
|
|
|
|
puts ("Copy to Flash... ");
|
|
|
|
rc = flash_write((char *)src, (ulong)dst, count * size);
|
|
if (rc != 0) {
|
|
flash_perror(rc);
|
|
unmap_sysmem(src);
|
|
unmap_sysmem(dst);
|
|
return (1);
|
|
}
|
|
puts ("done\n");
|
|
unmap_sysmem(src);
|
|
unmap_sysmem(dst);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
memcpy(dst, src, count * size);
|
|
|
|
unmap_sysmem(src);
|
|
unmap_sysmem(dst);
|
|
return 0;
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
static int do_mem_loop(cmd_tbl_t *cmdtp, int flag, int argc,
|
|
char * const argv[])
|
|
{
|
|
ulong addr, length, i, bytes;
|
|
int size;
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
volatile u64 *llp;
|
|
#endif
|
|
volatile u32 *longp;
|
|
volatile u16 *shortp;
|
|
volatile u8 *cp;
|
|
const void *buf;
|
|
|
|
if (argc < 3)
|
|
return CMD_RET_USAGE;
|
|
|
|
/*
|
|
* 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 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);
|
|
|
|
bytes = size * length;
|
|
buf = map_sysmem(addr, bytes);
|
|
|
|
/* 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) {
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
if (size == 8) {
|
|
llp = (u64 *)buf;
|
|
for (;;)
|
|
i = *llp;
|
|
}
|
|
#endif
|
|
if (size == 4) {
|
|
longp = (u32 *)buf;
|
|
for (;;)
|
|
i = *longp;
|
|
}
|
|
if (size == 2) {
|
|
shortp = (u16 *)buf;
|
|
for (;;)
|
|
i = *shortp;
|
|
}
|
|
cp = (u8 *)buf;
|
|
for (;;)
|
|
i = *cp;
|
|
}
|
|
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
if (size == 8) {
|
|
for (;;) {
|
|
llp = (u64 *)buf;
|
|
i = length;
|
|
while (i-- > 0)
|
|
*llp++;
|
|
}
|
|
}
|
|
#endif
|
|
if (size == 4) {
|
|
for (;;) {
|
|
longp = (u32 *)buf;
|
|
i = length;
|
|
while (i-- > 0)
|
|
*longp++;
|
|
}
|
|
}
|
|
if (size == 2) {
|
|
for (;;) {
|
|
shortp = (u16 *)buf;
|
|
i = length;
|
|
while (i-- > 0)
|
|
*shortp++;
|
|
}
|
|
}
|
|
for (;;) {
|
|
cp = (u8 *)buf;
|
|
i = length;
|
|
while (i-- > 0)
|
|
*cp++;
|
|
}
|
|
unmap_sysmem(buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_LOOPW
|
|
static int do_mem_loopw(cmd_tbl_t *cmdtp, int flag, int argc,
|
|
char * const argv[])
|
|
{
|
|
ulong addr, length, i, bytes;
|
|
int size;
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
volatile u64 *llp;
|
|
u64 data;
|
|
#else
|
|
ulong data;
|
|
#endif
|
|
volatile u32 *longp;
|
|
volatile u16 *shortp;
|
|
volatile u8 *cp;
|
|
void *buf;
|
|
|
|
if (argc < 4)
|
|
return CMD_RET_USAGE;
|
|
|
|
/*
|
|
* 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 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 */
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
data = simple_strtoull(argv[3], NULL, 16);
|
|
#else
|
|
data = simple_strtoul(argv[3], NULL, 16);
|
|
#endif
|
|
|
|
bytes = size * length;
|
|
buf = map_sysmem(addr, bytes);
|
|
|
|
/* 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) {
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
if (size == 8) {
|
|
llp = (u64 *)buf;
|
|
for (;;)
|
|
*llp = data;
|
|
}
|
|
#endif
|
|
if (size == 4) {
|
|
longp = (u32 *)buf;
|
|
for (;;)
|
|
*longp = data;
|
|
}
|
|
if (size == 2) {
|
|
shortp = (u16 *)buf;
|
|
for (;;)
|
|
*shortp = data;
|
|
}
|
|
cp = (u8 *)buf;
|
|
for (;;)
|
|
*cp = data;
|
|
}
|
|
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
if (size == 8) {
|
|
for (;;) {
|
|
llp = (u64 *)buf;
|
|
i = length;
|
|
while (i-- > 0)
|
|
*llp++ = data;
|
|
}
|
|
}
|
|
#endif
|
|
if (size == 4) {
|
|
for (;;) {
|
|
longp = (u32 *)buf;
|
|
i = length;
|
|
while (i-- > 0)
|
|
*longp++ = data;
|
|
}
|
|
}
|
|
if (size == 2) {
|
|
for (;;) {
|
|
shortp = (u16 *)buf;
|
|
i = length;
|
|
while (i-- > 0)
|
|
*shortp++ = data;
|
|
}
|
|
}
|
|
for (;;) {
|
|
cp = (u8 *)buf;
|
|
i = length;
|
|
while (i-- > 0)
|
|
*cp++ = data;
|
|
}
|
|
}
|
|
#endif /* CONFIG_LOOPW */
|
|
|
|
#ifdef CONFIG_CMD_MEMTEST
|
|
static ulong mem_test_alt(vu_long *buf, ulong start_addr, ulong end_addr,
|
|
vu_long *dummy)
|
|
{
|
|
vu_long *addr;
|
|
ulong errs = 0;
|
|
ulong val, readback;
|
|
int j;
|
|
vu_long offset;
|
|
vu_long test_offset;
|
|
vu_long pattern;
|
|
vu_long temp;
|
|
vu_long anti_pattern;
|
|
vu_long num_words;
|
|
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 */
|
|
};
|
|
|
|
num_words = (end_addr - start_addr) / sizeof(vu_long);
|
|
|
|
/*
|
|
* 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 doesn'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 = buf;
|
|
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 the bus */
|
|
readback = *addr;
|
|
if (readback != val) {
|
|
printf("FAILURE (data line): "
|
|
"expected %08lx, actual %08lx\n",
|
|
val, readback);
|
|
errs++;
|
|
if (ctrlc())
|
|
return -1;
|
|
}
|
|
*addr = ~val;
|
|
*dummy = val;
|
|
readback = *addr;
|
|
if (readback != ~val) {
|
|
printf("FAILURE (data line): "
|
|
"Is %08lx, should be %08lx\n",
|
|
readback, ~val);
|
|
errs++;
|
|
if (ctrlc())
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
pattern = (vu_long) 0xaaaaaaaa;
|
|
anti_pattern = (vu_long) 0x55555555;
|
|
|
|
debug("%s:%d: length = 0x%.8lx\n", __func__, __LINE__, num_words);
|
|
/*
|
|
* Write the default pattern at each of the
|
|
* power-of-two offsets.
|
|
*/
|
|
for (offset = 1; offset < num_words; offset <<= 1)
|
|
addr[offset] = pattern;
|
|
|
|
/*
|
|
* Check for address bits stuck high.
|
|
*/
|
|
test_offset = 0;
|
|
addr[test_offset] = anti_pattern;
|
|
|
|
for (offset = 1; offset < num_words; offset <<= 1) {
|
|
temp = addr[offset];
|
|
if (temp != pattern) {
|
|
printf("\nFAILURE: Address bit stuck high @ 0x%.8lx:"
|
|
" expected 0x%.8lx, actual 0x%.8lx\n",
|
|
start_addr + offset*sizeof(vu_long),
|
|
pattern, temp);
|
|
errs++;
|
|
if (ctrlc())
|
|
return -1;
|
|
}
|
|
}
|
|
addr[test_offset] = pattern;
|
|
WATCHDOG_RESET();
|
|
|
|
/*
|
|
* Check for addr bits stuck low or shorted.
|
|
*/
|
|
for (test_offset = 1; test_offset < num_words; test_offset <<= 1) {
|
|
addr[test_offset] = anti_pattern;
|
|
|
|
for (offset = 1; offset < num_words; offset <<= 1) {
|
|
temp = addr[offset];
|
|
if ((temp != pattern) && (offset != test_offset)) {
|
|
printf("\nFAILURE: Address bit stuck low or"
|
|
" shorted @ 0x%.8lx: expected 0x%.8lx,"
|
|
" actual 0x%.8lx\n",
|
|
start_addr + offset*sizeof(vu_long),
|
|
pattern, temp);
|
|
errs++;
|
|
if (ctrlc())
|
|
return -1;
|
|
}
|
|
}
|
|
addr[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++;
|
|
|
|
/*
|
|
* Fill memory with a known pattern.
|
|
*/
|
|
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
|
|
WATCHDOG_RESET();
|
|
addr[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 = addr[offset];
|
|
if (temp != pattern) {
|
|
printf("\nFAILURE (read/write) @ 0x%.8lx:"
|
|
" expected 0x%.8lx, actual 0x%.8lx)\n",
|
|
start_addr + offset*sizeof(vu_long),
|
|
pattern, temp);
|
|
errs++;
|
|
if (ctrlc())
|
|
return -1;
|
|
}
|
|
|
|
anti_pattern = ~pattern;
|
|
addr[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 = addr[offset];
|
|
if (temp != anti_pattern) {
|
|
printf("\nFAILURE (read/write): @ 0x%.8lx:"
|
|
" expected 0x%.8lx, actual 0x%.8lx)\n",
|
|
start_addr + offset*sizeof(vu_long),
|
|
anti_pattern, temp);
|
|
errs++;
|
|
if (ctrlc())
|
|
return -1;
|
|
}
|
|
addr[offset] = 0;
|
|
}
|
|
|
|
return errs;
|
|
}
|
|
|
|
static int compare_regions(volatile unsigned long *bufa,
|
|
volatile unsigned long *bufb, size_t count)
|
|
{
|
|
volatile unsigned long *p1 = bufa;
|
|
volatile unsigned long *p2 = bufb;
|
|
int errs = 0;
|
|
size_t i;
|
|
|
|
for (i = 0; i < count; i++, p1++, p2++) {
|
|
if (*p1 != *p2) {
|
|
printf("FAILURE: 0x%08lx != 0x%08lx (delta=0x%08lx -> bit %ld) at offset 0x%08lx\n",
|
|
(unsigned long)*p1, (unsigned long)*p2,
|
|
*p1 ^ *p2, __ffs(*p1 ^ *p2),
|
|
(unsigned long)(i * sizeof(unsigned long)));
|
|
errs++;
|
|
}
|
|
}
|
|
|
|
return errs;
|
|
}
|
|
|
|
static ulong test_bitflip_comparison(volatile unsigned long *bufa,
|
|
volatile unsigned long *bufb, size_t count)
|
|
{
|
|
volatile unsigned long *p1 = bufa;
|
|
volatile unsigned long *p2 = bufb;
|
|
unsigned int j, k;
|
|
unsigned long q;
|
|
size_t i;
|
|
int max;
|
|
int errs = 0;
|
|
|
|
max = sizeof(unsigned long) * 8;
|
|
for (k = 0; k < max; k++) {
|
|
q = 0x00000001L << k;
|
|
for (j = 0; j < 8; j++) {
|
|
WATCHDOG_RESET();
|
|
q = ~q;
|
|
p1 = (volatile unsigned long *)bufa;
|
|
p2 = (volatile unsigned long *)bufb;
|
|
for (i = 0; i < count; i++)
|
|
*p1++ = *p2++ = (i % 2) == 0 ? q : ~q;
|
|
|
|
errs += compare_regions(bufa, bufb, count);
|
|
}
|
|
|
|
if (ctrlc())
|
|
return -1UL;
|
|
}
|
|
|
|
return errs;
|
|
}
|
|
|
|
static ulong mem_test_quick(vu_long *buf, ulong start_addr, ulong end_addr,
|
|
vu_long pattern, int iteration)
|
|
{
|
|
vu_long *end;
|
|
vu_long *addr;
|
|
ulong errs = 0;
|
|
ulong incr, length;
|
|
ulong val, readback;
|
|
|
|
/* Alternate the pattern */
|
|
incr = 1;
|
|
if (iteration & 1) {
|
|
incr = -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;
|
|
}
|
|
length = (end_addr - start_addr) / sizeof(ulong);
|
|
end = buf + length;
|
|
printf("\rPattern %08lX Writing..."
|
|
"%12s"
|
|
"\b\b\b\b\b\b\b\b\b\b",
|
|
pattern, "");
|
|
|
|
for (addr = buf, val = pattern; addr < end; addr++) {
|
|
WATCHDOG_RESET();
|
|
*addr = val;
|
|
val += incr;
|
|
}
|
|
|
|
puts("Reading...");
|
|
|
|
for (addr = buf, val = pattern; addr < end; addr++) {
|
|
WATCHDOG_RESET();
|
|
readback = *addr;
|
|
if (readback != val) {
|
|
ulong offset = addr - buf;
|
|
|
|
printf("\nMem error @ 0x%08X: "
|
|
"found %08lX, expected %08lX\n",
|
|
(uint)(uintptr_t)(start_addr + offset*sizeof(vu_long)),
|
|
readback, val);
|
|
errs++;
|
|
if (ctrlc())
|
|
return -1;
|
|
}
|
|
val += incr;
|
|
}
|
|
|
|
return errs;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static int do_mem_mtest(cmd_tbl_t *cmdtp, int flag, int argc,
|
|
char * const argv[])
|
|
{
|
|
ulong start, end;
|
|
vu_long *buf, *dummy;
|
|
ulong iteration_limit = 0;
|
|
ulong count = 0;
|
|
ulong errs = 0; /* number of errors, or -1 if interrupted */
|
|
ulong pattern = 0;
|
|
int iteration;
|
|
|
|
start = CONFIG_SYS_MEMTEST_START;
|
|
end = CONFIG_SYS_MEMTEST_END;
|
|
|
|
if (argc > 1)
|
|
if (strict_strtoul(argv[1], 16, &start) < 0)
|
|
return CMD_RET_USAGE;
|
|
|
|
if (argc > 2)
|
|
if (strict_strtoul(argv[2], 16, &end) < 0)
|
|
return CMD_RET_USAGE;
|
|
|
|
if (argc > 3)
|
|
if (strict_strtoul(argv[3], 16, &pattern) < 0)
|
|
return CMD_RET_USAGE;
|
|
|
|
if (argc > 4)
|
|
if (strict_strtoul(argv[4], 16, &iteration_limit) < 0)
|
|
return CMD_RET_USAGE;
|
|
|
|
if (end < start) {
|
|
printf("Refusing to do empty test\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("Testing %08lx ... %08lx:\n", start, end);
|
|
debug("%s:%d: start %#08lx end %#08lx\n", __func__, __LINE__,
|
|
start, end);
|
|
|
|
buf = map_sysmem(start, end - start);
|
|
dummy = map_sysmem(CONFIG_SYS_MEMTEST_SCRATCH, sizeof(vu_long));
|
|
for (iteration = 0;
|
|
!iteration_limit || iteration < iteration_limit;
|
|
iteration++) {
|
|
if (ctrlc()) {
|
|
errs = -1UL;
|
|
break;
|
|
}
|
|
|
|
printf("Iteration: %6d\r", iteration + 1);
|
|
debug("\n");
|
|
if (IS_ENABLED(CONFIG_SYS_ALT_MEMTEST)) {
|
|
errs = mem_test_alt(buf, start, end, dummy);
|
|
if (errs == -1UL)
|
|
break;
|
|
count += errs;
|
|
errs = test_bitflip_comparison(buf,
|
|
buf + (end - start) / 2,
|
|
(end - start) /
|
|
sizeof(unsigned long));
|
|
} else {
|
|
errs = mem_test_quick(buf, start, end, pattern,
|
|
iteration);
|
|
}
|
|
if (errs == -1UL)
|
|
break;
|
|
count += errs;
|
|
}
|
|
|
|
unmap_sysmem((void *)buf);
|
|
unmap_sysmem((void *)dummy);
|
|
|
|
if (errs == -1UL) {
|
|
/* Memory test was aborted - write a newline to finish off */
|
|
putc('\n');
|
|
}
|
|
printf("Tested %d iteration(s) with %lu errors.\n", iteration, count);
|
|
|
|
return errs != 0;
|
|
}
|
|
#endif /* CONFIG_CMD_MEMTEST */
|
|
|
|
/* Modify memory.
|
|
*
|
|
* Syntax:
|
|
* mm{.b, .w, .l, .q} {addr}
|
|
* nm{.b, .w, .l, .q} {addr}
|
|
*/
|
|
static int
|
|
mod_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const argv[])
|
|
{
|
|
ulong addr;
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
u64 i;
|
|
#else
|
|
ulong i;
|
|
#endif
|
|
int nbytes, size;
|
|
void *ptr = NULL;
|
|
|
|
if (argc != 2)
|
|
return CMD_RET_USAGE;
|
|
|
|
bootretry_reset_cmd_timeout(); /* got a good command to get here */
|
|
/* 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;
|
|
}
|
|
|
|
/* Print the address, followed by value. Then accept input for
|
|
* the next value. A non-converted value exits.
|
|
*/
|
|
do {
|
|
ptr = map_sysmem(addr, size);
|
|
printf("%08lx:", addr);
|
|
if (size == 4)
|
|
printf(" %08x", *((u32 *)ptr));
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
else if (size == 8)
|
|
printf(" %016llx", *((u64 *)ptr));
|
|
#endif
|
|
else if (size == 2)
|
|
printf(" %04x", *((u16 *)ptr));
|
|
else
|
|
printf(" %02x", *((u8 *)ptr));
|
|
|
|
nbytes = cli_readline(" ? ");
|
|
if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) {
|
|
/* <CR> pressed as only input, don't modify current
|
|
* location and move to next. "-" pressed will go back.
|
|
*/
|
|
if (incrflag)
|
|
addr += nbytes ? -size : size;
|
|
nbytes = 1;
|
|
/* good enough to not time out */
|
|
bootretry_reset_cmd_timeout();
|
|
}
|
|
#ifdef CONFIG_BOOT_RETRY_TIME
|
|
else if (nbytes == -2) {
|
|
break; /* timed out, exit the command */
|
|
}
|
|
#endif
|
|
else {
|
|
char *endp;
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
i = simple_strtoull(console_buffer, &endp, 16);
|
|
#else
|
|
i = simple_strtoul(console_buffer, &endp, 16);
|
|
#endif
|
|
nbytes = endp - console_buffer;
|
|
if (nbytes) {
|
|
/* good enough to not time out
|
|
*/
|
|
bootretry_reset_cmd_timeout();
|
|
if (size == 4)
|
|
*((u32 *)ptr) = i;
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
else if (size == 8)
|
|
*((u64 *)ptr) = i;
|
|
#endif
|
|
else if (size == 2)
|
|
*((u16 *)ptr) = i;
|
|
else
|
|
*((u8 *)ptr) = i;
|
|
if (incrflag)
|
|
addr += size;
|
|
}
|
|
}
|
|
} while (nbytes);
|
|
if (ptr)
|
|
unmap_sysmem(ptr);
|
|
|
|
mm_last_addr = addr;
|
|
mm_last_size = size;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_CMD_CRC32
|
|
|
|
static int do_mem_crc(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
int flags = 0;
|
|
int ac;
|
|
char * const *av;
|
|
|
|
if (argc < 3)
|
|
return CMD_RET_USAGE;
|
|
|
|
av = argv + 1;
|
|
ac = argc - 1;
|
|
#ifdef CONFIG_CRC32_VERIFY
|
|
if (strcmp(*av, "-v") == 0) {
|
|
flags |= HASH_FLAG_VERIFY | HASH_FLAG_ENV;
|
|
av++;
|
|
ac--;
|
|
}
|
|
#endif
|
|
|
|
return hash_command("crc32", flags, cmdtp, flag, ac, av);
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_CMD_RANDOM
|
|
static int do_random(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
unsigned long addr, len;
|
|
unsigned long seed; // NOT INITIALIZED ON PURPOSE
|
|
unsigned int *buf, *start;
|
|
unsigned char *buf8;
|
|
unsigned int i;
|
|
|
|
if (argc < 3 || argc > 4) {
|
|
printf("usage: %s <addr> <len> [<seed>]\n", argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
len = simple_strtoul(argv[2], NULL, 16);
|
|
addr = simple_strtoul(argv[1], NULL, 16);
|
|
|
|
if (argc == 4) {
|
|
seed = simple_strtoul(argv[3], NULL, 16);
|
|
if (seed == 0) {
|
|
printf("The seed cannot be 0. Using 0xDEADBEEF.\n");
|
|
seed = 0xDEADBEEF;
|
|
}
|
|
} else {
|
|
seed = get_timer(0) ^ rand();
|
|
}
|
|
|
|
srand(seed);
|
|
start = map_sysmem(addr, len);
|
|
buf = start;
|
|
for (i = 0; i < (len / 4); i++)
|
|
*buf++ = rand();
|
|
|
|
buf8 = (unsigned char *)buf;
|
|
for (i = 0; i < (len % 4); i++)
|
|
*buf8++ = rand() & 0xFF;
|
|
|
|
unmap_sysmem(start);
|
|
printf("%lu bytes filled with random data\n", len);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
/**************************************************/
|
|
U_BOOT_CMD(
|
|
md, 3, 1, do_mem_md,
|
|
"memory display",
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
"[.b, .w, .l, .q] address [# of objects]"
|
|
#else
|
|
"[.b, .w, .l] address [# of objects]"
|
|
#endif
|
|
);
|
|
|
|
|
|
U_BOOT_CMD(
|
|
mm, 2, 1, do_mem_mm,
|
|
"memory modify (auto-incrementing address)",
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
"[.b, .w, .l, .q] address"
|
|
#else
|
|
"[.b, .w, .l] address"
|
|
#endif
|
|
);
|
|
|
|
|
|
U_BOOT_CMD(
|
|
nm, 2, 1, do_mem_nm,
|
|
"memory modify (constant address)",
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
"[.b, .w, .l, .q] address"
|
|
#else
|
|
"[.b, .w, .l] address"
|
|
#endif
|
|
);
|
|
|
|
U_BOOT_CMD(
|
|
mw, 4, 1, do_mem_mw,
|
|
"memory write (fill)",
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
"[.b, .w, .l, .q] address value [count]"
|
|
#else
|
|
"[.b, .w, .l] address value [count]"
|
|
#endif
|
|
);
|
|
|
|
U_BOOT_CMD(
|
|
cp, 4, 1, do_mem_cp,
|
|
"memory copy",
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
"[.b, .w, .l, .q] source target count"
|
|
#else
|
|
"[.b, .w, .l] source target count"
|
|
#endif
|
|
);
|
|
|
|
U_BOOT_CMD(
|
|
cmp, 4, 1, do_mem_cmp,
|
|
"memory compare",
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
"[.b, .w, .l, .q] addr1 addr2 count"
|
|
#else
|
|
"[.b, .w, .l] addr1 addr2 count"
|
|
#endif
|
|
);
|
|
|
|
#ifdef CONFIG_CMD_CRC32
|
|
|
|
#ifndef CONFIG_CRC32_VERIFY
|
|
|
|
U_BOOT_CMD(
|
|
crc32, 4, 1, do_mem_crc,
|
|
"checksum calculation",
|
|
"address count [addr]\n - compute CRC32 checksum [save at addr]"
|
|
);
|
|
|
|
#else /* CONFIG_CRC32_VERIFY */
|
|
|
|
U_BOOT_CMD(
|
|
crc32, 5, 1, do_mem_crc,
|
|
"checksum calculation",
|
|
"address count [addr]\n - compute CRC32 checksum [save at addr]\n"
|
|
"-v address count crc\n - verify crc of memory area"
|
|
);
|
|
|
|
#endif /* CONFIG_CRC32_VERIFY */
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_CMD_MEMINFO
|
|
static int do_mem_info(cmd_tbl_t *cmdtp, int flag, int argc,
|
|
char * const argv[])
|
|
{
|
|
puts("DRAM: ");
|
|
print_size(gd->ram_size, "\n");
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
U_BOOT_CMD(
|
|
base, 2, 1, do_mem_base,
|
|
"print or set address offset",
|
|
"\n - print address offset for memory commands\n"
|
|
"base off\n - set address offset for memory commands to 'off'"
|
|
);
|
|
|
|
U_BOOT_CMD(
|
|
loop, 3, 1, do_mem_loop,
|
|
"infinite loop on address range",
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
"[.b, .w, .l, .q] address number_of_objects"
|
|
#else
|
|
"[.b, .w, .l] address number_of_objects"
|
|
#endif
|
|
);
|
|
|
|
#ifdef CONFIG_LOOPW
|
|
U_BOOT_CMD(
|
|
loopw, 4, 1, do_mem_loopw,
|
|
"infinite write loop on address range",
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
"[.b, .w, .l, .q] address number_of_objects data_to_write"
|
|
#else
|
|
"[.b, .w, .l] address number_of_objects data_to_write"
|
|
#endif
|
|
);
|
|
#endif /* CONFIG_LOOPW */
|
|
|
|
#ifdef CONFIG_CMD_MEMTEST
|
|
U_BOOT_CMD(
|
|
mtest, 5, 1, do_mem_mtest,
|
|
"simple RAM read/write test",
|
|
"[start [end [pattern [iterations]]]]"
|
|
);
|
|
#endif /* CONFIG_CMD_MEMTEST */
|
|
|
|
#ifdef CONFIG_CMD_MX_CYCLIC
|
|
U_BOOT_CMD(
|
|
mdc, 4, 1, do_mem_mdc,
|
|
"memory display cyclic",
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
"[.b, .w, .l, .q] address count delay(ms)"
|
|
#else
|
|
"[.b, .w, .l] address count delay(ms)"
|
|
#endif
|
|
);
|
|
|
|
U_BOOT_CMD(
|
|
mwc, 4, 1, do_mem_mwc,
|
|
"memory write cyclic",
|
|
#ifdef MEM_SUPPORT_64BIT_DATA
|
|
"[.b, .w, .l, .q] address value delay(ms)"
|
|
#else
|
|
"[.b, .w, .l] address value delay(ms)"
|
|
#endif
|
|
);
|
|
#endif /* CONFIG_CMD_MX_CYCLIC */
|
|
|
|
#ifdef CONFIG_CMD_MEMINFO
|
|
U_BOOT_CMD(
|
|
meminfo, 3, 1, do_mem_info,
|
|
"display memory information",
|
|
""
|
|
);
|
|
#endif
|
|
|
|
#ifdef CONFIG_CMD_RANDOM
|
|
U_BOOT_CMD(
|
|
random, 4, 0, do_random,
|
|
"fill memory with random pattern",
|
|
"<addr> <len> [<seed>]\n"
|
|
" - Fill 'len' bytes of memory starting at 'addr' with random data\n"
|
|
);
|
|
#endif
|