diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 41aaf7f..dcccc19 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -449,14 +449,40 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) * 0 1 2 3 4 * nand erase [clean] [off size] */ - if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) { + if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) { nand_erase_options_t opts; /* "clean" at index 2 means request to write cleanmarker */ int clean = argc > 2 && !strcmp("clean", argv[2]); int o = clean ? 3 : 2; - int scrub = !strcmp(cmd, "scrub"); + int scrub = !strncmp(cmd, "scrub", 5); + int part = 0; + int chip = 0; + int spread = 0; + int args = 2; + + if (cmd[5] != 0) { + if (!strcmp(&cmd[5], ".spread")) { + spread = 1; + } else if (!strcmp(&cmd[5], ".part")) { + part = 1; + args = 1; + } else if (!strcmp(&cmd[5], ".chip")) { + chip = 1; + args = 0; + } else { + goto usage; + } + } + + /* + * Don't allow missing arguments to cause full chip/partition + * erases -- easy to do accidentally, e.g. with a misspelled + * variable name. + */ + if (argc != o + args) + goto usage; - printf("\nNAND %s: ", scrub ? "scrub" : "erase"); + printf("\nNAND %s: ", cmd); /* skip first two or three arguments, look for offset and size */ if (arg_off_size(argc - o, argv + o, &dev, &off, &size) != 0) return 1; @@ -468,6 +494,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) opts.length = size; opts.jffs2 = clean; opts.quiet = quiet; + opts.spread = spread; if (scrub) { puts("Warning: " @@ -648,11 +675,16 @@ U_BOOT_CMD( "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" - "nand erase [clean] [off size] - erase 'size' bytes from\n" - " offset 'off' (entire device if not specified)\n" + "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" + "nand erase.part [clean] partition - erase entire mtd partition'\n" + "nand erase.chip [clean] - erase entire chip'\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 scrub off size | scrub.part partition | scrub.chip\n" + " really clean NAND erasing bad blocks (UNSAFE)\n" "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n" "nand biterr off - make a bit error at offset (UNSAFE)" #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 0f67790..6a5dd37 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -75,7 +75,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) { struct jffs2_unknown_node cleanmarker; erase_info_t erase; - ulong erase_length; + unsigned long erase_length, erased_length; /* in blocks */ int bbtest = 1; int result; int percent_complete = -1; @@ -84,13 +84,19 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) struct mtd_oob_ops oob_opts; struct nand_chip *chip = meminfo->priv; + if ((opts->offset & (meminfo->writesize - 1)) != 0) { + printf("Attempt to erase non page aligned data\n"); + return -1; + } + memset(&erase, 0, sizeof(erase)); memset(&oob_opts, 0, sizeof(oob_opts)); erase.mtd = meminfo; erase.len = meminfo->erasesize; erase.addr = opts->offset; - erase_length = opts->length; + erase_length = lldiv(opts->length + meminfo->erasesize - 1, + meminfo->erasesize); cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); @@ -114,15 +120,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) priv_nand->bbt = NULL; } - if (erase_length < meminfo->erasesize) { - printf("Warning: Erase size 0x%08lx smaller than one " \ - "erase block 0x%08x\n",erase_length, meminfo->erasesize); - printf(" Erasing 0x%08x instead\n", meminfo->erasesize); - erase_length = meminfo->erasesize; - } - - for (; - erase.addr < opts->offset + erase_length; + for (erased_length = 0; + erased_length < erase_length; erase.addr += meminfo->erasesize) { WATCHDOG_RESET (); @@ -135,6 +134,10 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) "0x%08llx " " \n", erase.addr); + + if (!opts->spread) + erased_length++; + continue; } else if (ret < 0) { @@ -145,6 +148,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) } } + erased_length++; + result = meminfo->erase(meminfo, &erase); if (result != 0) { printf("\n%s: MTD Erase failure: %d\n", @@ -171,9 +176,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) } if (!opts->quiet) { - unsigned long long n =(unsigned long long) - (erase.addr + meminfo->erasesize - opts->offset) - * 100; + unsigned long long n = erased_length * 100ULL; int percent; do_div(n, erase_length); diff --git a/include/nand.h b/include/nand.h index 8bdf419..a452411 100644 --- a/include/nand.h +++ b/include/nand.h @@ -98,13 +98,16 @@ struct nand_read_options { typedef struct nand_read_options nand_read_options_t; struct nand_erase_options { - ulong length; /* number of bytes to erase */ - ulong offset; /* first address in NAND to erase */ + loff_t length; /* number of bytes to erase */ + loff_t offset; /* first address in NAND to erase */ int quiet; /* don't display progress messages */ int jffs2; /* if true: format for jffs2 usage * (write appropriate cleanmarker blocks) */ int scrub; /* if true, really clean NAND by erasing * bad blocks (UNSAFE) */ + + /* Don't include skipped bad blocks in size to be erased */ + int spread; }; typedef struct nand_erase_options nand_erase_options_t;