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(a)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