412 lines
9.3 KiB
C
412 lines
9.3 KiB
C
/*
|
|
* Rick Bronson and Pantelis Antoniou
|
|
*/
|
|
|
|
#include <common.h>
|
|
|
|
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
|
|
|
|
#include <command.h>
|
|
#include <watchdog.h>
|
|
#include <malloc.h>
|
|
#include <asm/byteorder.h>
|
|
|
|
#ifdef CONFIG_SHOW_BOOT_PROGRESS
|
|
# include <status_led.h>
|
|
# define SHOW_BOOT_PROGRESS(arg) show_boot_progress(arg)
|
|
#else
|
|
# define SHOW_BOOT_PROGRESS(arg)
|
|
#endif
|
|
|
|
#include <jffs2/jffs2.h>
|
|
#include <nand.h>
|
|
|
|
extern nand_info_t nand_info[]; /* info for NAND chips */
|
|
|
|
static int nand_dump_oob(nand_info_t *nand, ulong off)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int nand_dump(nand_info_t *nand, ulong off)
|
|
{
|
|
int i;
|
|
u_char *buf, *p;
|
|
|
|
buf = malloc(nand->oobblock + nand->oobsize);
|
|
if (!buf) {
|
|
puts("No memory for page buffer\n");
|
|
return 1;
|
|
}
|
|
off &= ~(nand->oobblock - 1);
|
|
i = nand_read_raw(nand, buf, off, nand->oobblock, nand->oobsize);
|
|
if (i < 0) {
|
|
printf("Error (%d) reading page %08x\n", i, off);
|
|
free(buf);
|
|
return 1;
|
|
}
|
|
printf("Page %08x dump:\n", off);
|
|
i = nand->oobblock >> 4; p = buf;
|
|
while (i--) {
|
|
printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x"
|
|
" %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
|
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
|
|
p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
|
|
p += 16;
|
|
}
|
|
puts("OOB:\n");
|
|
i = nand->oobsize >> 3;
|
|
while (i--) {
|
|
printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
|
|
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
|
|
p += 8;
|
|
}
|
|
free(buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static void
|
|
arg_off_size(int argc, char *argv[], ulong *off, ulong *size, ulong totsize)
|
|
{
|
|
*off = 0;
|
|
*size = 0;
|
|
|
|
#if defined(CONFIG_JFFS2_NAND) && defined(CFG_JFFS_CUSTOM_PART)
|
|
if (argc >= 1 && strcmp(argv[0], "partition") == 0) {
|
|
int part_num;
|
|
struct part_info *part;
|
|
const char *partstr;
|
|
|
|
if (argc >= 2)
|
|
partstr = argv[1];
|
|
else
|
|
partstr = getenv("partition");
|
|
|
|
if (partstr)
|
|
part_num = (int)simple_strtoul(partstr, NULL, 10);
|
|
else
|
|
part_num = 0;
|
|
|
|
part = jffs2_part_info(part_num);
|
|
if (part == NULL) {
|
|
printf("\nInvalid partition %d\n", part_num);
|
|
return;
|
|
}
|
|
*size = part->size;
|
|
*off = (ulong)part->offset;
|
|
} else
|
|
#endif
|
|
{
|
|
if (argc >= 1)
|
|
*off = (ulong)simple_strtoul(argv[0], NULL, 16);
|
|
else
|
|
*off = 0;
|
|
|
|
if (argc >= 2)
|
|
*size = (ulong)simple_strtoul(argv[1], NULL, 16);
|
|
else
|
|
*size = totsize - *off;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
|
|
{
|
|
int i, dev, ret;
|
|
ulong addr, off, size;
|
|
char *cmd, *s;
|
|
nand_info_t *nand;
|
|
|
|
/* at least two arguments please */
|
|
if (argc < 2)
|
|
goto usage;
|
|
|
|
cmd = argv[1];
|
|
|
|
if (strcmp(cmd, "info") == 0) {
|
|
|
|
putc('\n');
|
|
for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) {
|
|
if (nand_info[i].name)
|
|
printf("Device %d: %s\n", i, nand_info[i].name);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp(cmd, "device") == 0) {
|
|
|
|
if (argc < 3) {
|
|
if ((nand_curr_device < 0) ||
|
|
(nand_curr_device >= CFG_MAX_NAND_DEVICE))
|
|
puts("\nno devices available\n");
|
|
else
|
|
printf("\nDevice %d: %s\n", nand_curr_device,
|
|
nand_info[nand_curr_device].name);
|
|
return 0;
|
|
}
|
|
dev = (int)simple_strtoul(argv[2], NULL, 10);
|
|
if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) {
|
|
puts("No such device\n");
|
|
return 1;
|
|
}
|
|
printf("Device %d: %s", dev, nand_info[dev].name);
|
|
puts("... is now current device\n");
|
|
nand_curr_device = dev;
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 &&
|
|
strncmp(cmd, "dump", 4) != 0 &&
|
|
strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0
|
|
#ifdef CONFIG_MTD_NAND_UNSAFE
|
|
&& strcmp(cmd, "scrub") != 0 && strcmp(cmd, "biterr") != 0
|
|
&& strcmp(cmd, "markbad") != 0
|
|
#endif
|
|
)
|
|
goto usage;
|
|
|
|
/* the following commands operate on the current device */
|
|
if (nand_curr_device < 0 || nand_curr_device >= CFG_MAX_NAND_DEVICE ||
|
|
!nand_info[nand_curr_device].name) {
|
|
puts("\nno devices available\n");
|
|
return 1;
|
|
}
|
|
nand = &nand_info[nand_curr_device];
|
|
|
|
if (strcmp(cmd, "bad") == 0) {
|
|
printf("\nDevice %d bad blocks:\n", nand_curr_device);
|
|
for (off = 0; off < nand->size; off += nand->erasesize)
|
|
if (nand_block_isbad(nand, off))
|
|
printf(" %08x\n", off);
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp(cmd, "erase") == 0
|
|
#ifdef CONFIG_MTD_NAND_UNSAFE
|
|
|| strcmp(cmd, "scrub") == 0
|
|
#endif
|
|
) {
|
|
|
|
#ifdef CONFIG_MTD_NAND_UNSAFE
|
|
i = strcmp(cmd, "scrub") == 0; /* 1 scrub, 0 = erase */
|
|
#endif
|
|
|
|
arg_off_size(argc - 2, argv + 2, &off, &size, nand->size);
|
|
if (off == 0 && size == 0)
|
|
return 1;
|
|
|
|
printf("\nNAND %s: device %d offset 0x%x, size 0x%x ",
|
|
#ifdef CONFIG_MTD_NAND_UNSAFE
|
|
i ? "scrub" :
|
|
#endif
|
|
"erase",
|
|
nand_curr_device, off, size);
|
|
|
|
#ifdef CONFIG_MTD_NAND_UNSAFE
|
|
if (i)
|
|
ret = nand_scrub(nand, off, size);
|
|
else
|
|
#endif
|
|
ret = nand_erase(nand, off, size);
|
|
|
|
printf("%s\n", ret ? "ERROR" : "OK");
|
|
|
|
return ret == 0 ? 0 : 1;
|
|
}
|
|
|
|
if (strncmp(cmd, "dump", 4) == 0) {
|
|
if (argc < 3)
|
|
goto usage;
|
|
|
|
s = strchr(cmd, '.');
|
|
off = (int)simple_strtoul(argv[2], NULL, 16);
|
|
|
|
if (s != NULL && strcmp(s, ".oob") == 0)
|
|
ret = nand_dump_oob(nand, off);
|
|
else
|
|
ret = nand_dump(nand, off);
|
|
|
|
return ret == 0 ? 1 : 0;
|
|
|
|
}
|
|
|
|
/* read write */
|
|
if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
|
|
if (argc < 4)
|
|
goto usage;
|
|
/*
|
|
s = strchr(cmd, '.');
|
|
clean = CLEAN_NONE;
|
|
if (s != NULL) {
|
|
if (strcmp(s, ".jffs2") == 0 || strcmp(s, ".e") == 0
|
|
|| strcmp(s, ".i"))
|
|
clean = CLEAN_JFFS2;
|
|
}
|
|
*/
|
|
addr = (ulong)simple_strtoul(argv[2], NULL, 16);
|
|
|
|
arg_off_size(argc - 3, argv + 3, &off, &size, nand->size);
|
|
if (off == 0 && size == 0)
|
|
return 1;
|
|
|
|
i = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
|
|
printf("\nNAND %s: device %d offset %u, size %u ... ",
|
|
i ? "read" : "write", nand_curr_device, off, size);
|
|
|
|
if (i)
|
|
ret = nand_read(nand, off, &size, (u_char *)addr);
|
|
else
|
|
ret = nand_write(nand, off, &size, (u_char *)addr);
|
|
|
|
printf(" %d bytes %s: %s\n", size,
|
|
i ? "read" : "written", ret ? "ERROR" : "OK");
|
|
|
|
return ret == 0 ? 0 : 1;
|
|
}
|
|
#ifdef CONFIG_MTD_NAND_UNSAFE
|
|
if (strcmp(cmd, "markbad") == 0 || strcmp(cmd, "biterr") == 0) {
|
|
if (argc < 3)
|
|
goto usage;
|
|
|
|
i = strcmp(cmd, "biterr") == 0;
|
|
|
|
off = (int)simple_strtoul(argv[2], NULL, 16);
|
|
|
|
if (i)
|
|
ret = nand_make_bit_error(nand, off);
|
|
else
|
|
ret = nand_mark_bad(nand, off);
|
|
|
|
return ret == 0 ? 0 : 1;
|
|
}
|
|
#endif
|
|
|
|
usage:
|
|
printf("Usage:\n%s\n", cmdtp->usage);
|
|
return 1;
|
|
}
|
|
|
|
U_BOOT_CMD(nand, 5, 1, do_nand,
|
|
"nand - NAND sub-system\n",
|
|
"info - show available NAND devices\n"
|
|
"nand device [dev] - show or set current device\n"
|
|
"nand read[.jffs2] - addr off size\n"
|
|
"nand write[.jffs2] - addr off size - read/write `size' bytes starting\n"
|
|
" at offset `off' to/from memory address `addr'\n"
|
|
"nand erase [clean] [off size] - erase `size' bytes from\n"
|
|
" offset `off' (entire device if not specified)\n"
|
|
"nand bad - show bad blocks\n"
|
|
"nand dump[.oob] off - dump page\n"
|
|
"nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n"
|
|
"nand markbad off - mark bad block at offset (UNSAFE)\n"
|
|
"nand biterr off - make a bit error at offset (UNSAFE)\n");
|
|
|
|
int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
|
|
{
|
|
char *boot_device = NULL;
|
|
char *ep;
|
|
int dev;
|
|
int r;
|
|
ulong addr, cnt, offset = 0;
|
|
image_header_t *hdr;
|
|
nand_info_t *nand;
|
|
|
|
switch (argc) {
|
|
case 1:
|
|
addr = CFG_LOAD_ADDR;
|
|
boot_device = getenv("bootdevice");
|
|
break;
|
|
case 2:
|
|
addr = simple_strtoul(argv[1], NULL, 16);
|
|
boot_device = getenv("bootdevice");
|
|
break;
|
|
case 3:
|
|
addr = simple_strtoul(argv[1], NULL, 16);
|
|
boot_device = argv[2];
|
|
break;
|
|
case 4:
|
|
addr = simple_strtoul(argv[1], NULL, 16);
|
|
boot_device = argv[2];
|
|
offset = simple_strtoul(argv[3], NULL, 16);
|
|
break;
|
|
default:
|
|
printf("Usage:\n%s\n", cmdtp->usage);
|
|
SHOW_BOOT_PROGRESS(-1);
|
|
return 1;
|
|
}
|
|
|
|
if (!boot_device) {
|
|
puts("\n** No boot device **\n");
|
|
SHOW_BOOT_PROGRESS(-1);
|
|
return 1;
|
|
}
|
|
|
|
dev = simple_strtoul(boot_device, &ep, 16);
|
|
|
|
if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) {
|
|
printf("\n** Device %d not available\n", dev);
|
|
SHOW_BOOT_PROGRESS(-1);
|
|
return 1;
|
|
}
|
|
|
|
nand = &nand_info[dev];
|
|
printf("\nLoading from device %d: %s (offset 0x%lx)\n",
|
|
dev, nand->name, offset);
|
|
|
|
cnt = nand->oobblock;
|
|
r = nand_read(nand, offset, &cnt, (u_char *) addr);
|
|
if (r) {
|
|
printf("** Read error on %d\n", dev);
|
|
SHOW_BOOT_PROGRESS(-1);
|
|
return 1;
|
|
}
|
|
|
|
hdr = (image_header_t *) addr;
|
|
|
|
if (ntohl(hdr->ih_magic) != IH_MAGIC) {
|
|
printf("\n** Bad Magic Number 0x%x **\n", hdr->ih_magic);
|
|
SHOW_BOOT_PROGRESS(-1);
|
|
return 1;
|
|
}
|
|
|
|
print_image_hdr(hdr);
|
|
|
|
cnt = (ntohl(hdr->ih_size) + sizeof (image_header_t));
|
|
|
|
r = nand_read(nand, offset, &cnt, (u_char *) addr);
|
|
if (r) {
|
|
printf("** Read error on %d\n", dev);
|
|
SHOW_BOOT_PROGRESS(-1);
|
|
return 1;
|
|
}
|
|
|
|
/* Loading ok, update default load address */
|
|
|
|
load_addr = addr;
|
|
|
|
/* Check if we should attempt an auto-start */
|
|
if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) {
|
|
char *local_args[2];
|
|
extern int do_bootm(cmd_tbl_t *, int, int, char *[]);
|
|
|
|
local_args[0] = argv[0];
|
|
local_args[1] = NULL;
|
|
|
|
printf("Automatic boot of image at addr 0x%08lx ...\n", addr);
|
|
|
|
do_bootm(cmdtp, 0, 1, local_args);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_CMD(nboot, 4, 1, do_nandboot,
|
|
"nboot - boot from NAND device\n", "loadAddr dev\n");
|
|
|
|
|
|
#endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */
|
|
|