[U-Boot] [PATCH] Add a + prefix form to nand commands.

Permit the use of a + prefix to sizes in nand commands to extend operations to multiples of the appropriate page or erase block size.
Signed-off-by: Josh Karabin gkarabin@vocollect.com --- common/cmd_nand.c | 122 +++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 99 insertions(+), 23 deletions(-)
diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 1992531..516363e 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -102,14 +102,23 @@ static inline int str2long(char *p, ulong *num) }
static int -arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, size_t *size) +arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, + size_t *size, size_t *headsize, size_t *tailsize) { int idx = nand_curr_device; + char *ps = argv[1]; + size_t max_tailsize; #if defined(CONFIG_CMD_MTDPARTS) struct mtd_device *dev; struct part_info *part; u8 pnum; +#endif + max_tailsize = *tailsize; + + if (argc >= 1 && *ps == '+') + ps++;
+#if defined(CONFIG_CMD_MTDPARTS) if (argc >= 1 && !(str2long(argv[0], off))) { if ((mtdparts_init() == 0) && (find_dev_and_part(argv[0], &dev, &pnum, &part) == 0)) { @@ -119,8 +128,8 @@ arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, size_t *size } *off = part->offset; if (argc >= 2) { - if (!(str2long(argv[1], (ulong *)size))) { - printf("'%s' is not a number\n", argv[1]); + if (!(str2long(ps, (ulong *)size))) { + printf("'%s' is not a number\n", ps); return -1; } if (*size > part->size) @@ -145,8 +154,8 @@ arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, size_t *size }
if (argc >= 2) { - if (!(str2long(argv[1], (ulong *)size))) { - printf("'%s' is not a number\n", argv[1]); + if (!(str2long(ps, (ulong *)size))) { + printf("'%s' is not a number\n", ps); return -1; } } else { @@ -156,6 +165,16 @@ arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, size_t *size #if defined(CONFIG_CMD_MTDPARTS) out: #endif + if (ps != argv[1]) { + *tailsize = *size & (max_tailsize - 1); + *headsize = *size - *tailsize; + if (*tailsize) + *size = *headsize + max_tailsize; + } else { + *tailsize = 0; + *headsize = *size; + } + printf("device %d ", idx); if (*size == nand->size) puts("whole chip\n"); @@ -229,6 +248,8 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) int quiet = 0; #endif const char *quiet_str = getenv("quiet"); + size_t tailsize = 0; + size_t headsize = 0;
/* at least two arguments please */ if (argc < 2) @@ -317,12 +338,14 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
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) + tailsize = nand->erasesize; + if (arg_off_size(argc - o, argv + o, nand, &off, &size, + &headsize, &tailsize) != 0) return 1;
memset(&opts, 0, sizeof(opts)); opts.offset = off; - opts.length = size; + opts.length = headsize; opts.jffs2 = clean; opts.quiet = quiet;
@@ -346,7 +369,18 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) return -1; } } - ret = nand_erase_opts(nand, &opts); + + if (headsize) + ret = nand_erase_opts(nand, &opts); + else + ret = 0; + + if (!ret && tailsize) { + opts.offset = off + headsize; + opts.length = nand->erasesize; + ret = nand_erase_opts(nand, &opts); + } + printf("%s\n", ret ? "ERROR" : "OK");
return ret == 0 ? 0 : 1; @@ -378,23 +412,52 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ printf("\nNAND %s: ", read ? "read" : "write"); - if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) + tailsize = nand->writesize; + if (arg_off_size(argc - 3, argv + 3, nand, &off, &size, + &headsize, &tailsize) != 0) return 1;
s = strchr(cmd, '.'); if (!s || !strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i")) { if (read) - ret = nand_read_skip_bad(nand, off, &size, - (u_char *)addr); - else - ret = nand_write_skip_bad(nand, off, &size, - (u_char *)addr); + ret = nand_read_skip_bad(nand, off, &headsize, + (u_char *)addr); + else { + if (headsize) + ret = nand_write_skip_bad(nand, off, + &headsize, + (u_char *)addr); + else + ret = 0; + + if (!ret && tailsize) { + size_t pagesize = nand->writesize; + u_char *tailpage; + + tailpage = malloc(pagesize); + if (tailpage) { + memcpy(tailpage, + (u_char *)addr + + headsize, tailsize); + memset(tailpage + tailsize, + 0xff, + pagesize - tailsize); + ret = nand_write_skip_bad( + nand, + off + headsize, + &pagesize, + tailpage); + } + else + ret = 1; + } + } } else if (!strcmp(s, ".oob")) { /* out-of-band data */ mtd_oob_ops_t ops = { .oobbuf = (u8 *)addr, - .ooblen = size, + .ooblen = size + tailsize, .mode = MTD_OOB_RAW };
@@ -457,10 +520,20 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) }
if (strcmp(cmd, "unlock") == 0) { - if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) + tailsize = nand->erasesize; + if (arg_off_size(argc - 2, argv + 2, nand, &off, &size, + &headsize, &tailsize) < 0) return 1;
- if (!nand_unlock(nand, off, size)) { + if (headsize) + ret = nand_unlock(nand, off, headsize); + else + ret = 0; + + if (!ret && tailsize) + ret = nand_unlock(nand, off+headsize, nand->erasesize); + + if (!ret) { puts("NAND flash successfully unlocked\n"); } else { puts("Error unlocking NAND flash, " @@ -480,12 +553,14 @@ U_BOOT_CMD(nand, 5, 1, do_nand, "NAND sub-system", "info - show available NAND devices\n" "nand device [dev] - show or set current device\n" - "nand read - addr off|partition size\n" - "nand write - addr off|partition size\n" + "nand read - addr off|partition [+]size\n" + "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" + " to/from memory address 'addr', skipping bad blocks,\n" + " extending to a page size if '+' is specified.\n" + "nand erase [clean] [off [+]size] - erase 'size' bytes from\n" + " extending to an erase block if '+' is specified,\n" + " from offset 'off' (entire device if not specified)\n" "nand bad - show bad blocks\n" "nand dump[.oob] off - dump page\n" "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" @@ -494,7 +569,8 @@ U_BOOT_CMD(nand, 5, 1, do_nand, #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK "nand lock [tight] [status]\n" " bring nand to lock state or display locked pages\n" - "nand unlock [offset] [size] - unlock section\n" + "nand unlock [offset] [[+]size] - unlock section\n" + " extending to an erase block if + is specified.\n" #endif );

On Mon, May 18, 2009 at 11:58:45PM -0400, Josh Karabin wrote:
static int -arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, size_t *size) +arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off,
- size_t *size, size_t *headsize, size_t *tailsize)
{ int idx = nand_curr_device;
- char *ps = argv[1];
- size_t max_tailsize;
#if defined(CONFIG_CMD_MTDPARTS) struct mtd_device *dev; struct part_info *part; u8 pnum; +#endif
- max_tailsize = *tailsize;
Should allow tailsize to be NULL if the caller doesn't want to support +.
- if (ps != argv[1]) {
*tailsize = *size & (max_tailsize - 1);
*headsize = *size - *tailsize;
if (*tailsize)
*size = *headsize + max_tailsize;
If max_tailsize is zero (doesn't happen currently, but...), this will assign everything to tailsize rather than headsize.
- size_t tailsize = 0;
- size_t headsize = 0;
Does GCC complain if you remove those initializations? They don't seem necessary, and suggest that something is actually using those default values.
@@ -346,7 +369,18 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) return -1; } }
ret = nand_erase_opts(nand, &opts);
if (headsize)
ret = nand_erase_opts(nand, &opts);
else
ret = 0;
if (!ret && tailsize) {
opts.offset = off + headsize;
opts.length = nand->erasesize;
ret = nand_erase_opts(nand, &opts);
}
Why not just set opts.length = size, rounded up if the plus is there? I'm not sure the headsize/tailsize split makes sense for erase.
I assume that having offset be unaligned is still an error.
if (!s || !strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i")) { if (read)
ret = nand_read_skip_bad(nand, off, &size,
(u_char *)addr);
else
ret = nand_write_skip_bad(nand, off, &size,
(u_char *)addr);
ret = nand_read_skip_bad(nand, off, &headsize,
(u_char *)addr);
else {
Looks like you don't handle the tail when reading -- only when writing.
if (headsize)
ret = nand_write_skip_bad(nand, off,
&headsize,
(u_char *)addr);
else
ret = 0;
if (!ret && tailsize) {
size_t pagesize = nand->writesize;
u_char *tailpage;
tailpage = malloc(pagesize);
if (tailpage) {
memcpy(tailpage,
(u_char *)addr +
headsize, tailsize);
memset(tailpage + tailsize,
0xff,
pagesize - tailsize);
ret = nand_write_skip_bad(
nand,
off + headsize,
&pagesize,
tailpage);
Please factor things into their own functions when indentation and if/else complexity reaches this level.
}
else
ret = 1;
If one half of the if/else has braces (which should be on the same line as the "else"), the other half should have them too.
If malloc fails, we should let the user know that rather than opaquely fail.
}
} else if (!strcmp(s, ".oob")) { /* out-of-band data */ mtd_oob_ops_t ops = { .oobbuf = (u8 *)addr,}
.ooblen = size,
.ooblen = size + tailsize,
Doesn't "size" include "tailsize"? Why would there be any rounding involved with OOB (other than perhaps 16-bit alignment for such chips)?
if (!nand_unlock(nand, off, size)) {
if (headsize)
ret = nand_unlock(nand, off, headsize);
else
ret = 0;
if (!ret && tailsize)
ret = nand_unlock(nand, off+headsize, nand->erasesize);
Same comment as erase.
-Scott
participants (2)
-
Josh Karabin
-
Scott Wood