int do_spi_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
{
int ret = 0;
ulong addr;
loff_t off, size;
char *cmd, *s;
nand_info_t *nand = (nand_info_t *)get_mtd_info();
#ifdef CONFIG_SYS_NAND_QUIET
int quiet = CONFIG_SYS_NAND_QUIET;
#else
int quiet = 0;
#endif
const char *quiet_str = getenv("quiet");
int dev = nand_curr_device;
int repeat = flag & CMD_FLAG_REPEAT;
if (argc<2)
goto usage;
if (quiet_str)
quiet = simple_strtoul(quiet_str, NULL, 0) != 0;
cmd = argv[1];
if (repeat && strcmp(cmd, "dump"))
return 0;
if (strcmp(cmd, "info") == 0) {
spi_nand_info();
return 0;
}
if (strcmp(cmd, "device") == 0) {
if (argc < 3) {
putc('\n');
if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE)
puts("no devices available\n");
else
nand_print_and_set_info(dev);
return 0;
}
dev = (int)simple_strtoul(argv[2], NULL, 10);
set_dev(dev);
return 0;
}
if (strcmp(cmd, "bad") == 0) {
printf("\nDevice %d bad blocks:\n", dev);
for (off = 0; off < nand->size; off += nand->erasesize){
if (nand_block_isbad(nand, off))
printf(" %08llx\n", (unsigned long long)off);
}
printf("NAND size is %08llx, Erase size is %08x\n",nand->size, nand->erasesize);
return 0;
}
if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) {
nand_erase_options_t opts;
int clean = argc > 2 && !strcmp("clean", argv[2]);
int scrub_yes = argc > 2 && !strcmp("-y", argv[2]);
int o = (clean || scrub_yes) ? 3 : 2;
int scrub = !strncmp(cmd, "scrub", 5);
int spread = 0;
int args = 2;
const char *scrub_warn =
"Warning: "
"scrub option will erase all factory set bad blocks!\n"
" "
"There is no reliable way to recover them.\n"
" "
"Use this command only for testing purposes if you\n"
" "
"are sure of what you are doing!\n"
"\nReally scrub this NAND flash? <y/N>\n";
if (cmd[5] != 0) {
if (!strcmp(&cmd[5], ".spread")) {
spread = 1;
} else if (!strcmp(&cmd[5], ".part")) {
args = 1;
} else if (!strcmp(&cmd[5], ".chip")) {
args = 0;
} else {
goto usage;
}
}
if (argc != o + args)
goto usage;
printf("\nNAND %s: ", cmd);
if (arg_off_size(argc - o, argv + o, &dev, &off, &size) != 0)
return 1;
memset(&opts, 0, sizeof(opts));
opts.offset = off;
opts.length = size;
opts.jffs2 = clean;
opts.quiet = quiet;
opts.spread = spread;
if (scrub)) {
if (!scrub_yes)
puts(scrub_warn);
if (scrub_yes)
opts.scrub = 1;
else if (getc() == 'y') {
puts("y");
if (getc() == '\r')
opts.scrub = 1;
else {
puts("scrub aborted\n");
return -1;
}
} else {
puts("scrub aborted\n");
return -1;
}
}
ret = nand_erase_opts(nand, &opts);
printf("%s\n", ret ? "ERROR" : "OK");
return ret == 0 ? 0 : 1;
}
if (strncmp(cmd, "dump", 4) == 0) {
if (argc < 3)
goto usage;
off = (int)simple_strtoul(argv[2], NULL, 16);
ret = nand_dump(nand, off, !strcmp(&cmd[4], ".oob"), repeat);
return ret == 0 ? 1 : 0;
}
if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
size_t rwsize;
ulong pagecount = 1;
int read;
int raw = 0;
int no_verify = 0;
if (argc < 4)
goto usage;
addr = (ulong)simple_strtoul(argv[2], NULL, 16);
read = strncmp(cmd, "read", 4) == 0;
printf("\nNAND %s: ", read ? "read" : "write");
s = strchr(cmd, '.');
if (s && !strncmp(s, ".raw", 4)) {
char *_s = s;
int raw_size = 0;
uint32_t raw_unit_size = nand->writesize + nand->oobsize;
raw = 1;
while(_s) {
_s = strchr(_s+1, '.');
if (_s && !strncmp(_s, ".noverify", 9)) {
no_verify = 1;
} else if (_s && !strncmp(_s, ".size", 5)) {
raw_size = 1;
}
}
if (arg_off(argv[3], &dev, &off, &size))
return 1;
if (set_dev(dev))
return 1;
if (argc > 4 && !str2long(argv[4], &pagecount)) {
printf("'%s' is not a number\n", argv[4]);
return 1;
}
if (raw_size) {
pagecount = (pagecount+(raw_unit_size-1))/raw_unit_size;
printf("transfer size to %ld page(s),", pagecount);
}
#是否超出flash最大存储空间大小
if (pagecount * nand->writesize > size) {
puts("Size exceeds partition or device limit\n");
return -1;
}
rwsize = pagecount * (raw_unit_size);
} else {
if (arg_off_size(argc - 3, argv + 3, &dev, &off,
&size) != 0)
return 1;
if (set_dev(dev))
return 1;
if (argc < 5)
adjust_size_for_badblocks(&size, off, dev);
rwsize = size;
}
if (!s || !strcmp(s, ".jffs2") ||
!strcmp(s, ".e") || !strcmp(s, ".i")) {
if (read)
ret = nand_read_skip_bad(nand, off, &rwsize,
(u_char *)addr);
else
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr, 0);
#ifdef CONFIG_CMD_NAND_TRIMFFS
} else if (!strcmp(s, ".trimffs")) {
if (read) {
printf("Unknown nand command suffix '%s'\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr,
WITH_DROP_FFS);
#endif
#ifdef CONFIG_CMD_NAND_YAFFS
} else if (!strcmp(s, ".yaffs")) {
if (read) {
printf("Unknown nand command suffix '%s'.\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr, WITH_YAFFS_OOB);
#endif
} else if (!strcmp(s, ".oob")) {
mtd_oob_ops_t ops = {
.oobbuf = (u8 *)addr,
.ooblen = rwsize,
.mode = MTD_OOB_RAW
};
if (read)
ret = nand->read_oob(nand, off, &ops);
else
ret = nand->write_oob(nand, off, &ops);
} else if (raw) {
ret = raw_access(nand, addr, off, pagecount, read,
no_verify);
} else {
printf("Unknown nand command suffix '%s'.\n", s);
return 1;
}
printf(" %zu bytes %s: %s\n", rwsize,
read ? "read" : "written", ret ? "ERROR" : "OK");
return ret == 0 ? 0 : 1;
}
if (strcmp(cmd, "markbad") == 0) {
argc -= 2;
argv += 2;
if (argc <= 0)
goto usage;
while (argc > 0) {
addr = simple_strtoul(*argv, NULL, 16);
if (nand->block_markbad(nand, addr)) {
printf("block 0x%08lx NOT marked "
"as bad! ERROR %d\n",
addr, ret);
ret = 1;
} else {
printf("block 0x%08lx successfully "
"marked as bad\n",
addr);
}
--argc;
++argv;
}
return ret;
}
if (strcmp(cmd, "biterr") == 0) {
return 1;
}
usage:
return cmd_usage(cmdtp);
}
U_BOOT_CMD(
spi_nand, CONFIG_SYS_MAXARGS, 1, do_spi_nand,
"SPI-NAND sub-system",
"info - show available NAND devices\n"
"spi_nand device [dev] - show or set current device\n"
"spi_nand read - addr off|partition size\n"
"spi_nand write - addr off|partition size\n"
" read/write 'size' bytes starting at offset 'off'\n"
" to/from memory address 'addr', skipping bad blocks.\n"
"spi_nand read.raw - addr off|partition [count]\n"
"spi_nand write.raw[.noverify] - addr off|partition [count]\n"
" Use read.raw/write.raw to avoid ECC and access the flash as-is.\n"
" 'count' is the page count\n"
#ifdef CONFIG_CMD_NAND_TRIMFFS
"spi_nand write.trimffs - addr off|partition size\n"
" write 'size' bytes starting at offset 'off' from memory address\n"
" 'addr', skipping bad blocks and dropping any pages at the end\n"
" of eraseblocks that contain only 0xFF\n"
#endif
#ifdef CONFIG_CMD_NAND_YAFFS
"spi_nand write.yaffs - addr off|partition size\n"
" write 'size' bytes starting at offset 'off' with yaffs format\n"
" from memory address 'addr', skipping bad blocks.\n"
#endif
"spi_nand erase[.spread] [clean] off size - erase 'size' bytes "
"from offset 'off'\n"
" With '.spread', erase enough for given file size, otherwise,\n"
" 'size' includes skipped bad blocks.\n"
"spi_nand erase.part [clean] partition - erase entire mtd partition'\n"
"spi_nand erase.chip [clean] - erase entire chip'\n"
"spi_nand bad - show bad blocks\n"
"spi_nand dump[.oob] off - dump page\n"
"spi_nand scrub [-y] off size | scrub.part partition | scrub.chip\n"
" really clean NAND erasing bad blocks (UNSAFE)\n"
"spi_nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n"
"spi_nand biterr off - make a bit error at offset (UNSAFE)"
);
U_BOOT_CMD(
nand, CONFIG_SYS_MAXARGS, 1, do_spi_nand,
"NAND sub-system",
"alias spi_nand"
);