
Wolfgang,
this patch implements functionaly I found useful to have and hopefuly others can benefit from it too. Please drop all my previous NAND related, not yet applied patches. Once (if) applied I will rediff the rest.
Now you can do:
# mtdparts [snip] device nand0 <omapnand.0>, # parts = 5 #: name size offset mask_flags 0: kernel0 0x00400000 0x00000000 0 1: rootfs0 0x02800000 0x00400000 0 2: kernel1 0x00400000 0x02c00000 0 3: rootfs1 0x02800000 0x03000000 0 4: data 0x02800000 0x05800000 0
# nand write 10400000 kernel1 100000 NAND write: device 0 offset 0x2c00000, size 0x100000 1048576 bytes written: OK
# nboot kernel0 Loading from NAND 128MiB 3,3V 8-bit, offset 0x0 ...
# nand era kernel1 NAND erase: device 0 offset 0x2c00000, size 0x400000 OK
or
# nand era nand0,2 NAND erase: device 0 offset 0x2c00000, size 0x400000 OK
# chpart rootfs1 partition changed to nand0,3 # fsload /boot/uImage ### JFFS2 loading '/boot/uImage' to 0x10400000 ...
etc...
Best regards, ladis
Signed-off-by: Ladislav Michl ladis@linux-mips.org
CHANGELOG * Patch by Ladislav Michl, 18 Aug 2006: chpart, nboot and NAND subsystem related commands now accept also partition name to specify offset.
diff --git a/common/cmd_jffs2.c b/common/cmd_jffs2.c index 201c3c1..90b2930 100644 --- a/common/cmd_jffs2.c +++ b/common/cmd_jffs2.c @@ -1300,7 +1303,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 +1312,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; diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 21adb1b..2c9268c 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -36,6 +36,15 @@ #endif #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,66 @@ static int nand_dump(nand_info_t *nand,
/* ------------------------------------------------------------------------- */
-static void -arg_off_size(int argc, char *argv[], ulong *off, ulong *size, ulong totsize) +static int str2long(char *p, ulong *num) { - *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; + char *endptr;
- 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;
+out: + printf("device %d offset 0x%x, size 0x%x ", idx, *off, *size); + return 0; }
int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) @@ -176,7 +201,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, return 0; }
- if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && + if (strcmp(cmd, "bad") != 0 && strncmp(cmd, "erase", 3) != 0 && strncmp(cmd, "dump", 4) != 0 && strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0) goto usage; @@ -197,13 +222,23 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, return 0; }
- if (strcmp(cmd, "erase") == 0) { - arg_off_size(argc - 2, argv + 2, &off, &size, nand->size); - if (off == 0 && size == 0) + /* + * Syntax is: + * 0 1 2 3 4 + * nand erase [clean] [off size] + */ + if (strncmp(cmd, "erase", 3) == 0) { + /* "clean" at index 2 means request to erase OOB */ + int clean = (argc > 2 && strcmp(argv[2], "clean") == 0) ? 1 : 0; + int o = clean ? 3 : 2; + puts("\nNAND 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; - - printf("\nNAND erase: device %d offset 0x%x, size 0x%x ", - nand_curr_device, off, size); + if (clean) { /* TODO */ + puts("'clean' not implemented yet\n"); + return 1; + } ret = nand_erase(nand, off, size); printf("%s\n", ret ? "ERROR" : "OK");
@@ -241,13 +276,10 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, */ 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); + printf("\nNAND %s: ", i ? "read" : "write"); + if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) + return 1;
if (i) ret = nand_read(nand, off, &size, (u_char *)addr); @@ -268,8 +300,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" @@ -279,62 +311,20 @@ U_BOOT_CMD(nand, 5, 1, do_nand, "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[]) +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; } @@ -353,7 +343,7 @@ int do_nandboot(cmd_tbl_t * cmdtp, int f
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; } @@ -367,7 +357,7 @@ int do_nandboot(cmd_tbl_t * cmdtp, int f 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); @@ -378,9 +368,81 @@ int do_nandboot(cmd_tbl_t * cmdtp, int f 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: +usage: + 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) */
diff --git a/doc/README.nand b/doc/README.nand index f2d6a5b..a2d10db 100644 --- a/doc/README.nand +++ b/doc/README.nand @@ -36,14 +36,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. @@ -53,28 +58,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 @@ -82,7 +87,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