nand erase: .spread, .part, .chip subcommands
authorScott Wood <scottwood@freescale.com>
Wed, 25 Aug 2010 19:43:29 +0000 (14:43 -0500)
committerScott Wood <scottwood@freescale.com>
Mon, 11 Oct 2010 20:10:17 +0000 (15:10 -0500)
A while back, in http://lists.denx.de/pipermail/u-boot/2009-June/054428.html,
Michele De Candia posted a patch to not count bad blocks toward the
requested size to be erased.  This is desireable when you're passing in
something like $filesize, but not when you're trying to erase a partition.

Thus, a .spread subcommand (named for consistency with
http://lists.denx.de/pipermail/u-boot/2010-August/075163.html) is introduced
to make explicit the user's desire to erase for a given amount of data,
rather than to erase a specific region of the chip.

While passing $filesize to "nand erase" is useful, accidentally passing
something like $fliesize currently produces quite unpleasant results, as the
variable evaluates to nothing and U-Boot assumes that you want to erase
the entire rest of the chip/partition.  To improve the safety of the
erase command, require the user to make explicit their intentions by
using a .part or .chip subcommand.  This is an incompatible user interface
change, but keeping compatibility would eliminate the safety gain, and IMHO
it's worth it.

While touching nand_erase_opts(), make it accept 64-bit offsets and sizes,
fix the percentage display when erase length is rounded up, eliminate
an inconsistent warning about rounding up the erase length which only
happened when the length was less than one block (rounding up for $filesize
is normal operation), and add a diagnostic if there's an attempt to erase
beginning at a non-block boundary.

Signed-off-by: Scott Wood <scottwood@freescale.com>
Tested-by: Ben Gardiner <bengardiner@nanometrics.ca>
common/cmd_nand.c
drivers/mtd/nand/nand_util.c
include/nand.h

index 41aaf7f..dcccc19 100644 (file)
@@ -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
index 0f67790..6a5dd37 100644 (file)
@@ -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);
index 8bdf419..a452411 100644 (file)
@@ -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;