[PATCH] NAND: Partition name support added to NAND subsystem
authorStefan Roese <sr@denx.de>
Sat, 28 Oct 2006 13:55:52 +0000 (15:55 +0200)
committerStefan Roese <sr@denx.de>
Sat, 28 Oct 2006 15:11:10 +0000 (17:11 +0200)
chpart, nboot and NAND subsystem related commands now accept also partition
name to specify offset.

Signed-off-by: Ladislav Michl <ladis@linux-mips.org>
Signed-off-by: Stefan Roese <sr@denx.de>
common/cmd_jffs2.c
common/cmd_nand.c
doc/README.nand
drivers/nand/nand_util.c

index b5fd41724f6a4eb3c831e37d69765ebb1e3d02c7..7fd1fa33daf2fbccf21880e6d53f0904af8c30cd 100644 (file)
@@ -1300,7 +1300,7 @@ static void list_partitions(void)
  * Given partition identifier in form of <dev_type><dev_num>,<part_num> find
  * corresponding device and verify partition number.
  *
- * @param id string describing device and partition
+ * @param id string describing device and partition or partition name
  * @param dev pointer to the requested device (output)
  * @param part_num verified partition number (output)
  * @param part pointer to requested partition (output)
@@ -1309,11 +1309,23 @@ static void list_partitions(void)
 int find_dev_and_part(const char *id, struct mtd_device **dev,
                u8 *part_num, struct part_info **part)
 {
+       struct list_head *dentry, *pentry;
        u8 type, dnum, pnum;
        const char *p;
 
        DEBUGF("--- find_dev_and_part ---\nid = %s\n", id);
 
+       list_for_each(dentry, &devices) {
+               *part_num = 0;
+               *dev = list_entry(dentry, struct mtd_device, link);
+               list_for_each(pentry, &(*dev)->parts) {
+                       *part = list_entry(pentry, struct part_info, link);
+                       if (strcmp((*part)->name, id) == 0)
+                               return 0;
+                       (*part_num)++;
+               }
+       }
+
        p = id;
        *dev = NULL;
        *part = NULL;
index 4fb3b65960d924e184bf03e3900c55257b28b70b..7286726f1c15b45e49c9956d3829bbdba3ecdb1b 100644 (file)
 #include <jffs2/jffs2.h>
 #include <nand.h>
 
+#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
+
+/* parition handling routines */
+int mtdparts_init(void);
+int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
+int find_dev_and_part(const char *id, struct mtd_device **dev,
+               u8 *part_num, struct part_info **part);
+#endif
+
 extern nand_info_t nand_info[];       /* info for NAND chips */
 
 static int nand_dump_oob(nand_info_t *nand, ulong off)
@@ -83,50 +92,75 @@ static int nand_dump(nand_info_t *nand, ulong off)
 
 /* ------------------------------------------------------------------------- */
 
-static void
-arg_off_size(int argc, char *argv[], ulong *off, ulong *size, ulong totsize)
+static inline int str2long(char *p, ulong *num)
 {
-       *off = 0;
-       *size = 0;
+       char *endptr;
 
-#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;
+       *num = simple_strtoul(p, &endptr, 16);
+       return (*p != '\0' && *endptr == '\0') ? 1 : 0;
+}
 
-               part = jffs2_part_info(part_num);
-               if (part == NULL) {
-                       printf("\nInvalid partition %d\n", part_num);
-                       return;
+static int
+arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, ulong *size)
+{
+       int idx = nand_curr_device;
+#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
+       struct mtd_device *dev;
+       struct part_info *part;
+       u8 pnum;
+
+       if (argc >= 1 && !(str2long(argv[0], off))) {
+               if ((mtdparts_init() == 0) &&
+                   (find_dev_and_part(argv[0], &dev, &pnum, &part) == 0)) {
+                       if (dev->id->type != MTD_DEV_TYPE_NAND) {
+                               puts("not a NAND device\n");
+                               return -1;
+                       }
+                       *off = part->offset;
+                       if (argc >= 2) {
+                               if (!(str2long(argv[1], size))) {
+                                       printf("'%s' is not a number\n", argv[1]);
+                                       return -1;
+                               }
+                               if (*size > part->size)
+                                       *size = part->size;
+                       } else {
+                               *size = part->size;
+                       }
+                       idx = dev->id->num;
+                       *nand = nand_info[idx];
+                       goto out;
                }
-               *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;
+       if (argc >= 1) {
+               if (!(str2long(argv[0], off))) {
+                       printf("'%s' is not a number\n", argv[0]);
+                       return -1;
+               }
+       } else {
+               *off = 0;
+       }
 
+       if (argc >= 2) {
+               if (!(str2long(argv[1], size))) {
+                       printf("'%s' is not a number\n", argv[1]);
+                       return -1;
+               }
+       } else {
+               *size = nand->size - *off;
        }
 
+#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
+out:
+#endif
+       printf("device %d ", idx);
+       if (*size == nand->size)
+               puts("whole chip\n");
+       else
+               printf("offset 0x%x, size 0x%x\n", *off, *size);
+       return 0;
 }
 
 int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
@@ -213,35 +247,22 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
                return 0;
        }
 
+       /*
+        * Syntax is:
+        *   0    1     2       3    4
+        *   nand erase [clean] [off size]
+        */
        if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) {
                nand_erase_options_t opts;
-               int clean = argc >= 3 && !strcmp("clean", argv[2]);
-               int rest_argc = argc - 2;
-               char **rest_argv = argv + 2;
+               /* "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");
 
-               if (clean) {
-                       rest_argc--;
-                       rest_argv++;
-               }
-
-               if (rest_argc == 0) {
-
-                       printf("\nNAND %s: device %d whole chip\n",
-                              cmd,
-                              nand_curr_device);
-
-                       off = size = 0;
-               } else {
-                       arg_off_size(rest_argc, rest_argv, &off, &size,
-                                    nand->size);
-
-                       if (off == 0 && size == 0)
-                               return 1;
-
-                       printf("\nNAND %s: device %d offset 0x%x, size 0x%x\n",
-                              cmd, nand_curr_device, off, size);
-               }
+               printf("\nNAND %s: ", scrub ? "scrub" : "erase");
+               /* skip first two or three arguments, look for offset and size */
+               if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0)
+                       return 1;
 
                memset(&opts, 0, sizeof(opts));
                opts.offset = off;
@@ -250,23 +271,22 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
                opts.quiet  = quiet;
 
                if (scrub) {
-                       printf("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"
-                               );
+                       puts("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 (getc() == 'y' && getc() == '\r') {
                                opts.scrub = 1;
                        } else {
-                               printf("scrub aborted\n");
+                               puts("scrub aborted\n");
                                return -1;
                        }
                }
@@ -301,13 +321,10 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 
                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;
-
                read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
-               printf("\nNAND %s: device %d offset %u, size %u ... ",
-                      read ? "read" : "write", nand_curr_device, off, size);
+               printf("\nNAND %s: ", read ? "read" : "write");
+               if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0)
+                       return 1;
 
                s = strchr(cmd, '.');
                if (s != NULL &&
@@ -334,15 +351,13 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
                                opts.quiet      = quiet;
                                ret = nand_write_opts(nand, &opts);
                        }
-                       printf("%s\n", ret ? "ERROR" : "OK");
-                       return ret == 0 ? 0 : 1;
+               } else {
+                       if (read)
+                               ret = nand_read(nand, off, &size, (u_char *)addr);
+                       else
+                               ret = nand_write(nand, off, &size, (u_char *)addr);
                }
 
-               if (read)
-                       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,
                       read ? "read" : "written", ret ? "ERROR" : "OK");
 
@@ -412,9 +427,9 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
                       }
                } else {
                        if (!nand_lock(nand, tight)) {
-                               printf ("NAND flash successfully locked\n");
+                               puts("NAND flash successfully locked\n");
                        } else {
-                               printf ("Error locking NAND flash. \n");
+                               puts("Error locking NAND flash\n");
                                return 1;
                        }
                }
@@ -422,19 +437,14 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
        }
 
        if (strcmp(cmd, "unlock") == 0) {
-               if (argc == 2) {
-                       off = 0;
-                       size = nand->size;
-               } else {
-                       arg_off_size(argc - 2, argv + 2, &off, &size,
-                                    nand->size);
-               }
+               if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0)
+                       return 1;
 
                if (!nand_unlock(nand, off, size)) {
-                       printf("NAND flash successfully unlocked\n");
+                       puts("NAND flash successfully unlocked\n");
                } else {
-                       printf("Error unlocking NAND flash. "
-                              "Write and erase will probably fail\n");
+                       puts("Error unlocking NAND flash, "
+                            "write and erase will probably fail\n");
                        return 1;
                }
                return 0;
@@ -449,8 +459,8 @@ 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"
+       "nand read[.jffs2]     - addr off|partition size\n"
+       "nand write[.jffs2]    - addr off|partiton 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"
@@ -462,62 +472,20 @@ U_BOOT_CMD(nand, 5, 1, do_nand,
        "nand lock [tight] [status] - bring nand to lock state or display locked pages\n"
        "nand unlock [offset] [size] - unlock section\n");
 
-int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
+static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
+                          ulong offset, ulong addr, char *cmd)
 {
-       char *boot_device = NULL;
-       char *ep;
-       int dev;
        int r;
-       ulong addr, cnt, offset = 0;
+       char *ep;
+       ulong cnt;
        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);
+       printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset);
 
        cnt = nand->oobblock;
        r = nand_read(nand, offset, &cnt, (u_char *) addr);
        if (r) {
-               printf("** Read error on %d\n", dev);
+               puts("** Read error\n");
                SHOW_BOOT_PROGRESS(-1);
                return 1;
        }
@@ -536,7 +504,7 @@ int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 
        r = nand_read(nand, offset, &cnt, (u_char *) addr);
        if (r) {
-               printf("** Read error on %d\n", dev);
+               puts("** Read error\n");
                SHOW_BOOT_PROGRESS(-1);
                return 1;
        }
@@ -550,7 +518,7 @@ int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
                char *local_args[2];
                extern int do_bootm(cmd_tbl_t *, int, int, char *[]);
 
-               local_args[0] = argv[0];
+               local_args[0] = cmd;
                local_args[1] = NULL;
 
                printf("Automatic boot of image at addr 0x%08lx ...\n", addr);
@@ -561,9 +529,83 @@ int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
        return 0;
 }
 
-U_BOOT_CMD(nboot, 4, 1, do_nandboot,
-       "nboot   - boot from NAND device\n", "loadAddr dev\n");
+int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
+{
+       char *boot_device = NULL;
+       int idx;
+       ulong addr, offset = 0;
+#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
+       struct mtd_device *dev;
+       struct part_info *part;
+       u8 pnum;
+
+       if (argc >= 2) {
+               char *p = (argc == 2) ? argv[1] : argv[2];
+               if (!(str2long(p, &addr)) && (mtdparts_init() == 0) &&
+                   (find_dev_and_part(p, &dev, &pnum, &part) == 0)) {
+                       if (dev->id->type != MTD_DEV_TYPE_NAND) {
+                               puts("Not a NAND device\n");
+                               return 1;
+                       }
+                       if (argc > 3)
+                               goto usage;
+                       if (argc == 3)
+                               addr = simple_strtoul(argv[2], NULL, 16);
+                       else
+                               addr = CFG_LOAD_ADDR;
+                       return nand_load_image(cmdtp, &nand_info[dev->id->num],
+                                              part->offset, addr, argv[0]);
+               }
+       }
+#endif
 
+       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:
+#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
+usage:
+#endif
+               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;
+       }
+
+       idx = simple_strtoul(boot_device, NULL, 16);
+
+       if (idx < 0 || idx >= CFG_MAX_NAND_DEVICE || !nand_info[idx].name) {
+               printf("\n** Device %d not available\n", idx);
+               SHOW_BOOT_PROGRESS(-1);
+               return 1;
+       }
+
+       return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]);
+}
+
+U_BOOT_CMD(nboot, 4, 1, do_nandboot,
+       "nboot   - boot from NAND device\n",
+       "[partition] | [[[loadAddr] dev] offset]\n");
 
 #endif                         /* (CONFIG_COMMANDS & CFG_CMD_NAND) */
 
index 5279a4f2bc5c345d4fd083bc7aeaa5f74bade3e9..b5171f4d407c6d602100bb9b15993754d6d0c921 100644 (file)
@@ -34,14 +34,19 @@ Commands:
    nand device num
       Make device `num' the current device and print information about it.
 
-   nand erase off size
-   nand erase clean [off size]
-      Erase `size' bytes starting at offset `off'.  Only complete erase
-      blocks can be erased.
+   nand erase off|partition size
+   nand erase clean [off|partition size]
+      Erase `size' bytes starting at offset `off'. Alternatively partition
+      name can be specified, in this case size will be eventually limited
+      to not exceed partition size (this behaviour applies also to read
+      and write commands). Only complete erase blocks can be erased.
+
+      If `erase' is specified without an offset or size, the entire flash
+      is erased. If `erase' is specified with partition but without an
+      size, the entire partition is erased.
 
       If `clean' is specified, a JFFS2-style clean marker is written to
-      each block after it is erased. If `clean' is specified without an
-      offset or size, the entire flash is erased.
+      each block after it is erased.
 
       This command will not erase blocks that are marked bad. There is
       a debug option in cmd_nand.c to allow bad blocks to be erased.
@@ -51,28 +56,28 @@ Commands:
    nand info
       Print information about all of the NAND devices found.
 
-   nand read addr ofs size
+   nand read addr ofs|partition size
       Read `size' bytes from `ofs' in NAND flash to `addr'. If a page
       cannot be read because it is marked bad or an uncorrectable data
       error is found the command stops with an error.
 
-   nand read.jffs2 addr ofs size
+   nand read.jffs2 addr ofs|partition size
       Like `read', but the data for blocks that are marked bad is read as
       0xff. This gives a readable JFFS2 image that can be processed by
       the JFFS2 commands such as ls and fsload.
 
-   nand read.oob addr ofs size
+   nand read.oob addr ofs|partition size
       Read `size' bytes from the out-of-band data area corresponding to
       `ofs' in NAND flash to `addr'. This is limited to the 16 bytes of
       data for one 512-byte page or 2 256-byte pages. There is no check
       for bad blocks or ECC errors.
 
-   nand write addr ofs size
+   nand write addr ofs|partition size
       Write `size' bytes from `addr' to `ofs' in NAND flash. If a page
       cannot be written because it is marked bad or the write fails the
       command stops with an error.
 
-   nand write.jffs2 addr ofs size
+   nand write.jffs2 addr ofs|partition size
       Like `write', but blocks that are marked bad are skipped and the
       is written to the next block instead. This allows writing writing
       a JFFS2 image, as long as the image is short enough to fit even
@@ -80,7 +85,7 @@ Commands:
       produced by mkfs.jffs2 should work well, but loading an image copied
       from another flash is going to be trouble if there are any bad blocks.
 
-   nand write.oob addr ofs size
+   nand write.oob addr ofs|partition size
       Write `size' bytes from `addr' to the out-of-band data area
       corresponding to `ofs' in NAND flash. This is limited to the 16 bytes
       of data for one 512-byte page or 2 256-byte pages. There is no check
index 7570210d5caee17593494d04ccf417b2cbbfa31d..10bf03659ede94c2827b0bd28967f52764587a91 100644 (file)
@@ -83,15 +83,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
 
        erase.mtd = meminfo;
        erase.len  = meminfo->erasesize;
-       if (opts->offset == 0 && opts->length == 0) {
-               /* erase complete chip */
-               erase.addr = 0;
-               erase_length = meminfo->size;
-       } else {
-               /* erase specified region */
-               erase.addr = opts->offset;
-               erase_length = opts->length;
-       }
+       erase.addr = opts->offset;
+       erase_length = opts->length;
 
        isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0;