
converted to new style subcmd handling. partly tested on sheevaplug read and information commands tested on sheevaplug (sorry did not want to nuke my device so I did not want to test things like nand scrub)
Signed-off-by: Frans Meulenbroeks fransmeulenbroeks@gmail.com
---
note that nand biterr has no implementation --- common/cmd_nand.c | 595 ++++++++++++++++++++++++++++++++++------------------- 1 files changed, 386 insertions(+), 209 deletions(-)
diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 9b0c930..d537b4b 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -204,281 +204,458 @@ static void nand_print_info(int idx) nand->name, nand->erasesize >> 10); }
-int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +static int nand_quiet(void) { - int i, dev, ret = 0; - ulong addr, off; - size_t size; - char *cmd, *s; - nand_info_t *nand; + const char *quiet_str = getenv("quiet"); #ifdef CONFIG_SYS_NAND_QUIET int quiet = CONFIG_SYS_NAND_QUIET; #else int quiet = 0; #endif - const char *quiet_str = getenv("quiet"); - - /* at least two arguments please */ - if (argc < 2) - goto usage; - if (quiet_str) quiet = simple_strtoul(quiet_str, NULL, 0) != 0; + return quiet; +}
- cmd = argv[1]; +static nand_info_t *get_nand(void) +{ + /* the following commands operate on the current device */ + if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) { + puts("\nno devices available\n"); + return NULL; + } + return &nand_info[nand_curr_device]; +}
- if (strcmp(cmd, "info") == 0) { +static int do_nand_info(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int i;
- putc('\n'); - for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { - if (nand_info[i].name) - nand_print_info(i); - } - return 0; + putc('\n'); + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { + if (nand_info[i].name) + nand_print_info(i); } + return 0; +}
- if (strcmp(cmd, "device") == 0) { +static int do_nand_device(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int dev;
- if (argc < 3) { - putc('\n'); - if ((nand_curr_device < 0) || - (nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE)) - puts("no devices available\n"); - else - nand_print_info(nand_curr_device); - return 0; - } - dev = (int)simple_strtoul(argv[2], NULL, 10); - if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) { - puts("No such device\n"); - return 1; - } - printf("Device %d: %s", dev, nand_info[dev].name); - puts("... is now current device\n"); - nand_curr_device = dev; + if (argc < 2) { + putc('\n'); + if ((nand_curr_device < 0) || + (nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE)) + puts("no devices available\n"); + else + nand_print_info(nand_curr_device); + return 0; + } + dev = (int)simple_strtoul(argv[1], NULL, 10); + if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) { + puts("No such device\n"); + return 1; + } + printf("Device %d: %s", dev, nand_info[dev].name); + puts("... is now current device\n"); + nand_curr_device = dev;
#ifdef CONFIG_SYS_NAND_SELECT_DEVICE - /* - * Select the chip in the board/cpu specific driver - */ - board_nand_select_device(nand_info[dev].priv, dev); + /* + * Select the chip in the board/cpu specific driver + */ + board_nand_select_device(nand_info[dev].priv, dev); #endif
- return 0; + return 0; +} + +static int do_nand_read(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int ret = 0; + size_t size; + ulong off; + ulong addr; + char *s; + nand_info_t *nand; + + nand = get_nand(); + if (!nand) + return 1; + + if (argc < 4) + { + cmd_usage(cmdtp); + return 1; }
- if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && - strncmp(cmd, "dump", 4) != 0 && - strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && - strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && - strcmp(cmd, "biterr") != 0 && - strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) - goto usage; + addr = (ulong)simple_strtoul(argv[1], NULL, 16);
- /* the following commands operate on the current device */ - if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || - !nand_info[nand_curr_device].name) { - puts("\nno devices available\n"); + printf("\nNAND read: "); + if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) != 0) + return 1; + + s = strchr(argv[0], '.'); + if (!s || !strcmp(s, ".jffs2") || + !strcmp(s, ".e") || !strcmp(s, ".i")) { + ret = nand_read_skip_bad(nand, off, &size, (u_char *)addr); + } else if (!strcmp(s, ".oob")) { + /* out-of-band data */ + mtd_oob_ops_t ops = { + .oobbuf = (u8 *)addr, + .ooblen = size, + .mode = MTD_OOB_RAW + }; + + ret = nand->read_oob(nand, off, &ops); + } else { + printf("Unknown nand command suffix '%s'.\n", s); return 1; } - nand = &nand_info[nand_curr_device];
- if (strcmp(cmd, "bad") == 0) { - printf("\nDevice %d bad blocks:\n", nand_curr_device); - for (off = 0; off < nand->size; off += nand->erasesize) - if (nand_block_isbad(nand, off)) - printf(" %08lx\n", off); - return 0; + printf(" %zu bytes read: %s\n", size, ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +static int do_nand_write(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int ret = 0; + size_t size; + ulong off; + ulong addr; + char *s; + nand_info_t *nand; + + nand = get_nand(); + if (!nand) + return 1; + + if (argc < 4) + { + cmd_usage(cmdtp); + return 1; + } + + addr = (ulong)simple_strtoul(argv[1], NULL, 16); + + printf("\nNAND write: "); + if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) != 0) + return 1; + + s = strchr(argv[0], '.'); + if (!s || !strcmp(s, ".jffs2") || + !strcmp(s, ".e") || !strcmp(s, ".i")) { + ret = nand_write_skip_bad(nand, off, &size, (u_char *)addr); + } else if (!strcmp(s, ".oob")) { + /* out-of-band data */ + mtd_oob_ops_t ops = { + .oobbuf = (u8 *)addr, + .ooblen = size, + .mode = MTD_OOB_RAW + }; + + ret = nand->write_oob(nand, off, &ops); + } else { + printf("Unknown nand command suffix '%s'.\n", s); + return 1; }
+ printf(" %zu bytes written: %s\n", size, ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +static int do_nand_erase(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ /* * 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; - /* "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"); - - 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; - opts.length = size; - opts.jffs2 = clean; - opts.quiet = quiet; - - if (scrub) { - 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') { - puts("y"); - if (getc() == '\r') - opts.scrub = 1; - else { - puts("scrub aborted\n"); - return -1; - } - } else { - puts("scrub aborted\n"); - return -1; - } - } - ret = nand_erase_opts(nand, &opts); - printf("%s\n", ret ? "ERROR" : "OK"); + int ret = 0; + size_t size; + ulong off; + nand_erase_options_t opts; + /* "clean" at index 1 means request to write cleanmarker */ + int clean = argc > 1 && !strcmp("clean", argv[1]); + int o = clean ? 2 : 1; + nand_info_t *nand; + int quiet = nand_quiet();
- return ret == 0 ? 0 : 1; - } + nand = get_nand(); + if (!nand) + return 1;
- if (strncmp(cmd, "dump", 4) == 0) { - if (argc < 3) - goto usage; + printf("\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;
- s = strchr(cmd, '.'); - off = (int)simple_strtoul(argv[2], NULL, 16); + memset(&opts, 0, sizeof(opts)); + opts.offset = off; + opts.length = size; + opts.jffs2 = clean; + opts.quiet = quiet;
- if (s != NULL && strcmp(s, ".oob") == 0) - ret = nand_dump(nand, off, 1); - else - ret = nand_dump(nand, off, 0); + ret = nand_erase_opts(nand, &opts); + printf("%s\n", ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +}
- return ret == 0 ? 1 : 0; +static int do_nand_scrub(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + /* + * Syntax is: + * 0 1 2 3 4 + * nand scrub [clean] [off size] + */
+ int ret = 0; + size_t size; + ulong off; + nand_erase_options_t opts; + /* "clean" at index 1 means request to write cleanmarker */ + int clean = argc > 1 && !strcmp("clean", argv[1]); + int o = clean ? 2 : 1; + nand_info_t *nand; + int quiet = nand_quiet(); + + nand = get_nand(); + if (!nand) + return 1; + + printf("\nNAND scrub: "); + /* 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; + opts.length = size; + opts.jffs2 = clean; + opts.quiet = quiet; + + 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') { + puts("y"); + if (getc() == '\r') + opts.scrub = 1; + else { + puts("scrub aborted\n"); + return -1; + } + } else { + puts("scrub aborted\n"); + return -1; } + ret = nand_erase_opts(nand, &opts); + printf("%s\n", ret ? "ERROR" : "OK");
- if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { - int read; + return ret == 0 ? 0 : 1; +}
- if (argc < 4) - goto usage; +static int do_nand_bad(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + ulong off; + nand_info_t *nand;
- addr = (ulong)simple_strtoul(argv[2], NULL, 16); + nand = get_nand(); + if (!nand) + return 1;
- 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) - return 1; + printf("\nDevice %d bad blocks:\n", nand_curr_device); + for (off = 0; off < nand->size; off += nand->erasesize) + if (nand_block_isbad(nand, off)) + printf(" %08lx\n", off); + return 0; +}
- 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); - } else if (!strcmp(s, ".oob")) { - /* out-of-band data */ - mtd_oob_ops_t ops = { - .oobbuf = (u8 *)addr, - .ooblen = size, - .mode = MTD_OOB_RAW - }; - - if (read) - ret = nand->read_oob(nand, off, &ops); - else - ret = nand->write_oob(nand, off, &ops); - } else { - printf("Unknown nand command suffix '%s'.\n", s); - return 1; - } +static int do_nand_dump(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int ret = 0; + ulong off; + char *s; + nand_info_t *nand;
- printf(" %zu bytes %s: %s\n", size, - read ? "read" : "written", ret ? "ERROR" : "OK"); + nand = get_nand(); + if (!nand) + return 1;
- return ret == 0 ? 0 : 1; + if (argc < 2) + { + cmd_usage(cmdtp); + return 1; }
- if (strcmp(cmd, "markbad") == 0) { - argc -= 2; - argv += 2; + s = strchr(argv[0], '.'); + off = (int)simple_strtoul(argv[1], NULL, 16);
- if (argc <= 0) - goto usage; + if (s != NULL && strcmp(s, ".oob") == 0) + ret = nand_dump(nand, off, 1); + else + ret = nand_dump(nand, off, 0);
- while (argc > 0) { - addr = simple_strtoul(*argv, NULL, 16); + return ret == 0 ? 1 : 0; +}
- if (nand->block_markbad(nand, addr)) { - printf("block 0x%08lx NOT marked " - "as bad! ERROR %d\n", - addr, ret); - ret = 1; - } else { - printf("block 0x%08lx successfully " - "marked as bad\n", - addr); - } - --argc; - ++argv; - } - return ret; - } +static int do_nand_markbad(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int ret = 0; + ulong addr; + nand_info_t *nand; + + nand = get_nand(); + if (!nand) + return 1;
- if (strcmp(cmd, "biterr") == 0) { - /* todo */ + argc--; + argv++; + + if (argc <= 0) + { + cmd_usage(cmdtp); return 1; }
-#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK - if (strcmp(cmd, "lock") == 0) { - int tight = 0; - int status = 0; - if (argc == 3) { - if (!strcmp("tight", argv[2])) - tight = 1; - if (!strcmp("status", argv[2])) - status = 1; - } - if (status) { - do_nand_status(nand); + while (argc > 0) { + addr = simple_strtoul(*argv, NULL, 16); + + if (nand->block_markbad(nand, addr)) { + printf("block 0x%08lx NOT marked " + "as bad! ERROR %d\n", + addr, ret); + ret = 1; } else { - if (!nand_lock(nand, tight)) { - puts("NAND flash successfully locked\n"); - } else { - puts("Error locking NAND flash\n"); - return 1; - } + printf("block 0x%08lx successfully " + "marked as bad\n", + addr); } - return 0; + argc--; + argv++; } + return ret; +}
- if (strcmp(cmd, "unlock") == 0) { - if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) - return 1; +static int do_nand_biterr(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + nand_info_t *nand;
- if (!nand_unlock(nand, off, size)) { - puts("NAND flash successfully unlocked\n"); + nand = get_nand(); + if (!nand) + return 1; + + /* todo */ + return 1; +} + +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK +static int do_nand_lock(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + ulong addr; + size_t size; + nand_info_t *nand; + int tight = 0; + int status = 0; + + nand = get_nand(); + if (!nand) + return 1; + + if (argc == 2) { + if (!strcmp("tight", argv[1])) + tight = 1; + if (!strcmp("status", argv[1])) + status = 1; + } + if (status) { + do_nand_status(nand); + } else { + if (!nand_lock(nand, tight)) { + puts("NAND flash successfully locked\n"); } else { - puts("Error unlocking NAND flash, " - "write and erase will probably fail\n"); + puts("Error locking NAND flash\n"); return 1; } - return 0; } + return 0; +} + +static int do_nand_unlock(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + ulong addr; + size_t size; + nand_info_t *nand; + + argc--; + argv++; + + nand = get_nand(); + if (!nand) + return 1; + + if (arg_off_size(argc, argv, nand, &off, &size) < 0) + return 1; + + if (!nand_unlock(nand, off, size)) { + puts("NAND flash successfully unlocked\n"); + } else { + puts("Error unlocking NAND flash, " + "write and erase will probably fail\n"); + return 1; + } + return 0; +} #endif
-usage: - cmd_usage(cmdtp); - return 1; +static cmd_tbl_t cmd_nand_sub[] = { + U_BOOT_CMD_MKENT(info, 1, 1, do_nand_info, "", ""), + U_BOOT_CMD_MKENT(device, 2, 1, do_nand_device, "", ""), + U_BOOT_CMD_MKENT(read, 4, 1, do_nand_read, "", ""), + U_BOOT_CMD_MKENT(write, 4, 1, do_nand_write, "", ""), + U_BOOT_CMD_MKENT(erase, 4, 1, do_nand_erase, "", ""), + U_BOOT_CMD_MKENT(bad, 1, 1, do_nand_bad, "", ""), + U_BOOT_CMD_MKENT(dump, 2, 1, do_nand_dump, "", ""), + U_BOOT_CMD_MKENT(scrub, 1, 1, do_nand_scrub, "", ""), + U_BOOT_CMD_MKENT(markbad, CONFIG_SYS_MAXARGS, 1, do_nand_markbad, "", ""), + U_BOOT_CMD_MKENT(biterr, 2, 1, do_nand_biterr, "", ""), +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK + U_BOOT_CMD_MKENT(lock, 1, 1, do_nand_lock, "", ""), + U_BOOT_CMD_MKENT(unlock, 1, 1, do_nand_unlock, "", ""), +#endif +}; + +static int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + cmd_tbl_t *c; + + /* Strip off leading 'nand' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_nand_sub[0], ARRAY_SIZE(cmd_nand_sub)); + + if (c) { + return c->cmd(cmdtp, flag, argc, argv); + } else { + cmd_usage(cmdtp); + return 1; + } }
U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand,