X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=common%2Fcmd_nand.c;h=8d6c959584f64de348c3fc0d5276f186c7c08ebc;hb=449c703374a8868453425e15da7e2f76221b72e4;hp=5648ab21785dee96053a6b85f3620924d4d8f610;hpb=55d150d95dbd4612f229e1f503c3a6aa8ed08a96;p=u-boot diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 5648ab2178..8d6c959584 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -9,302 +9,599 @@ */ #include -#include -#include -#include -#include -#ifdef CONFIG_SHOW_BOOT_PROGRESS -# include -# define SHOW_BOOT_PROGRESS(arg) show_boot_progress(arg) -#else -# define SHOW_BOOT_PROGRESS(arg) -#endif -#if (CONFIG_COMMANDS & CFG_CMD_NAND) +#ifndef CFG_NAND_LEGACY +/* + * + * New NAND support + * + */ +#include + +#if defined(CONFIG_CMD_NAND) -#include -#include +#include +#include +#include +#include #include +#include -#ifdef CONFIG_OMAP1510 -void archflashwp(void *archdata, int wp); +#if defined(CONFIG_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 -#define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) +extern nand_info_t nand_info[]; /* info for NAND chips */ -/* - * Definition of the out of band configuration structure - */ -struct nand_oob_config { - int ecc_pos[6]; /* position of ECC bytes inside oob */ - int badblock_pos; /* position of bad block flag inside oob -1 = inactive */ - int eccvalid_pos; /* position of ECC valid flag inside oob -1 = inactive */ -} oob_config = { {0}, 0, 0}; +static int nand_dump_oob(nand_info_t *nand, ulong off) +{ + return 0; +} -#undef NAND_DEBUG -#undef PSYCHO_DEBUG +static int nand_dump(nand_info_t *nand, ulong off) +{ + int i; + u_char *buf, *p; -/* ****************** WARNING ********************* - * When ALLOW_ERASE_BAD_DEBUG is non-zero the erase command will - * erase (or at least attempt to erase) blocks that are marked - * bad. This can be very handy if you are _sure_ that the block - * is OK, say because you marked a good block bad to test bad - * block handling and you are done testing, or if you have - * accidentally marked blocks bad. - * - * Erasing factory marked bad blocks is a _bad_ idea. If the - * erase succeeds there is no reliable way to find them again, - * and attempting to program or erase bad blocks can affect - * the data in _other_ (good) blocks. - */ -#define ALLOW_ERASE_BAD_DEBUG 0 + buf = malloc(nand->oobblock + nand->oobsize); + if (!buf) { + puts("No memory for page buffer\n"); + return 1; + } + off &= ~(nand->oobblock - 1); + i = nand_read_raw(nand, buf, off, nand->oobblock, nand->oobsize); + if (i < 0) { + printf("Error (%d) reading page %08x\n", i, off); + free(buf); + return 1; + } + printf("Page %08x dump:\n", off); + i = nand->oobblock >> 4; p = buf; + while (i--) { + printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x" + " %02x %02x %02x %02x %02x %02x %02x %02x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], + p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); + p += 16; + } + puts("OOB:\n"); + i = nand->oobsize >> 3; + while (i--) { + printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + p += 8; + } + free(buf); -#define CONFIG_MTD_NAND_ECC /* enable ECC */ -#define CONFIG_MTD_NAND_ECC_JFFS2 + return 0; +} -/* bits for nand_rw() `cmd'; or together as needed */ -#define NANDRW_READ 0x01 -#define NANDRW_WRITE 0x00 -#define NANDRW_JFFS2 0x02 -#define NANDRW_JFFS2_SKIP 0x04 +/* ------------------------------------------------------------------------- */ -/* - * Function Prototypes - */ -static void nand_print(struct nand_chip *nand); -int nand_rw (struct nand_chip* nand, int cmd, - size_t start, size_t len, - size_t * retlen, u_char * buf); -int nand_erase(struct nand_chip* nand, size_t ofs, size_t len, int clean); -static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len, - size_t * retlen, u_char *buf, u_char *ecc_code); -static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t len, - size_t * retlen, const u_char * buf, u_char * ecc_code); -static void nand_print_bad(struct nand_chip *nand); -static int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len, - size_t * retlen, u_char * buf); -static int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, - size_t * retlen, const u_char * buf); -static int NanD_WaitReady(struct nand_chip *nand, int ale_wait); -#ifdef CONFIG_MTD_NAND_ECC -static int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc); -static void nand_calculate_ecc (const u_char *dat, u_char *ecc_code); +static inline int str2long(char *p, ulong *num) +{ + char *endptr; + + *num = simple_strtoul(p, &endptr, 16); + return (*p != '\0' && *endptr == '\0') ? 1 : 0; +} + +static int +arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, ulong *size) +{ + int idx = nand_curr_device; +#if defined(CONFIG_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; + } + } #endif -struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE] = {{0}}; + if (argc >= 1) { + if (!(str2long(argv[0], off))) { + printf("'%s' is not a number\n", argv[0]); + return -1; + } + } else { + *off = 0; + } -/* Current NAND Device */ -static int curr_device = -1; + 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 defined(CONFIG_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[]) +int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) { - int rcode = 0; + int i, dev, ret; + ulong addr, off, size; + char *cmd, *s; + nand_info_t *nand; +#ifdef CFG_NAND_QUIET + int quiet = CFG_NAND_QUIET; +#else + int quiet = 0; +#endif + const char *quiet_str = getenv("quiet"); - switch (argc) { - case 0: - case 1: - printf ("Usage:\n%s\n", cmdtp->usage); - return 1; - case 2: - if (strcmp(argv[1],"info") == 0) { - int i; + /* at least two arguments please */ + if (argc < 2) + goto usage; - putc ('\n'); + if (quiet_str) + quiet = simple_strtoul(quiet_str, NULL, 0) != 0; - for (i=0; i= CFG_MAX_NAND_DEVICE)) { - puts ("\nno devices available\n"); - return 1; + if (strcmp(cmd, "info") == 0) { + + putc('\n'); + for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) { + if (nand_info[i].name) + printf("Device %d: %s, sector size %lu KiB\n", + i, nand_info[i].name, + nand_info[i].erasesize >> 10); } - printf ("\nDevice %d: ", curr_device); - nand_print(&nand_dev_desc[curr_device]); return 0; + } + + if (strcmp(cmd, "device") == 0) { - } else if (strcmp(argv[1],"bad") == 0) { - if ((curr_device < 0) || (curr_device >= CFG_MAX_NAND_DEVICE)) { - puts ("\nno devices available\n"); + if (argc < 3) { + if ((nand_curr_device < 0) || + (nand_curr_device >= CFG_MAX_NAND_DEVICE)) + puts("\nno devices available\n"); + else + printf("\nDevice %d: %s\n", nand_curr_device, + nand_info[nand_curr_device].name); + return 0; + } + dev = (int)simple_strtoul(argv[2], NULL, 10); + if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) { + puts("No such device\n"); return 1; } - printf ("\nDevice %d bad blocks:\n", curr_device); - nand_print_bad(&nand_dev_desc[curr_device]); + printf("Device %d: %s", dev, nand_info[dev].name); + puts("... is now current device\n"); + nand_curr_device = dev; + +#ifdef CFG_NAND_SELECT_DEVICE + /* + * Select the chip in the board/cpu specific driver + */ + board_nand_select_device(nand_info[dev].priv, dev); +#endif + return 0; + } + + 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; + /* the following commands operate on the current device */ + if (nand_curr_device < 0 || nand_curr_device >= CFG_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) { + puts("\nno devices available\n"); + return 1; } - printf ("Usage:\n%s\n", cmdtp->usage); - return 1; - case 3: - if (strcmp(argv[1],"device") == 0) { - int dev = (int)simple_strtoul(argv[2], NULL, 10); + nand = &nand_info[nand_curr_device]; - printf ("\nDevice %d: ", dev); - if (dev >= CFG_MAX_NAND_DEVICE) { - puts ("unknown device\n"); - return 1; - } - nand_print(&nand_dev_desc[dev]); - /*nand_print (dev);*/ + 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(" %08x\n", off); + return 0; + } - if (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN) { + /* + * 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; - } - - curr_device = dev; - puts ("... is now current device\n"); + 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? \n"); + + if (getc() == 'y' && getc() == '\r') { + opts.scrub = 1; + } else { + puts("scrub aborted\n"); + return -1; + } + } + ret = nand_erase_opts(nand, &opts); + printf("%s\n", ret ? "ERROR" : "OK"); - return 0; + return ret == 0 ? 0 : 1; } - else if (strcmp(argv[1],"erase") == 0 && strcmp(argv[2], "clean") == 0) { - struct nand_chip* nand = &nand_dev_desc[curr_device]; - ulong off = 0; - ulong size = nand->totlen; - int ret; - printf ("\nNAND erase: device %d offset %ld, size %ld ... ", - curr_device, off, size); + if (strncmp(cmd, "dump", 4) == 0) { + if (argc < 3) + goto usage; - ret = nand_erase (nand, off, size, 1); + s = strchr(cmd, '.'); + off = (int)simple_strtoul(argv[2], NULL, 16); - printf("%s\n", ret ? "ERROR" : "OK"); + if (s != NULL && strcmp(s, ".oob") == 0) + ret = nand_dump_oob(nand, off); + else + ret = nand_dump(nand, off); + + return ret == 0 ? 1 : 0; - return ret; } - printf ("Usage:\n%s\n", cmdtp->usage); - return 1; - default: - /* at least 4 args */ - - if (strncmp(argv[1], "read", 4) == 0 || - strncmp(argv[1], "write", 5) == 0) { - ulong addr = simple_strtoul(argv[2], NULL, 16); - ulong off = simple_strtoul(argv[3], NULL, 16); - ulong size = simple_strtoul(argv[4], NULL, 16); - int cmd = (strncmp(argv[1], "read", 4) == 0) ? - NANDRW_READ : NANDRW_WRITE; - int ret, total; - char* cmdtail = strchr(argv[1], '.'); + /* read write */ + if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { + int read; - if (cmdtail && !strncmp(cmdtail, ".oob", 2)) { - /* read out-of-band data */ - if (cmd & NANDRW_READ) { - ret = nand_read_oob(nand_dev_desc + curr_device, - off, size, &total, - (u_char*)addr); - } - else { - ret = nand_write_oob(nand_dev_desc + curr_device, - off, size, &total, - (u_char*)addr); + if (argc < 4) + goto usage; + + addr = (ulong)simple_strtoul(argv[2], NULL, 16); + + 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; + + s = strchr(cmd, '.'); + if (s != NULL && + (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) { + if (read) { + /* read */ + nand_read_options_t opts; + memset(&opts, 0, sizeof(opts)); + opts.buffer = (u_char*) addr; + opts.length = size; + opts.offset = off; + opts.quiet = quiet; + ret = nand_read_opts(nand, &opts); + } else { + /* write */ + nand_write_options_t opts; + memset(&opts, 0, sizeof(opts)); + opts.buffer = (u_char*) addr; + opts.length = size; + opts.offset = off; + /* opts.forcejffs2 = 1; */ + opts.pad = 1; + opts.blockalign = 1; + opts.quiet = quiet; + ret = nand_write_opts(nand, &opts); } - return ret; - } - else if (cmdtail && !strncmp(cmdtail, ".jffs2", 2)) - cmd |= NANDRW_JFFS2; /* skip bad blocks */ - else if (cmdtail && !strncmp(cmdtail, ".jffs2s", 2)) { - cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ - if (cmd & NANDRW_READ) - cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ - } -#ifdef SXNI855T - /* need ".e" same as ".j" for compatibility with older units */ - else if (cmdtail && !strcmp(cmdtail, ".e")) - cmd |= NANDRW_JFFS2; /* skip bad blocks */ -#endif -#ifdef CFG_NAND_SKIP_BAD_DOT_I - /* need ".i" same as ".jffs2s" for compatibility with older units (esd) */ - /* ".i" for image -> read skips bad block (no 0xff) */ - else if (cmdtail && !strcmp(cmdtail, ".i")) { - cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ - if (cmd & NANDRW_READ) - cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ + } else if (s != NULL && !strcmp(s, ".oob")) { + /* read out-of-band data */ + if (read) + ret = nand->read_oob(nand, off, size, (size_t *) &size, + (u_char *) addr); + else + ret = nand->write_oob(nand, off, size, (size_t *) &size, + (u_char *) addr); + } else { + if (read) + ret = nand_read(nand, off, &size, (u_char *)addr); + else + ret = nand_write(nand, off, &size, (u_char *)addr); } -#endif /* CFG_NAND_SKIP_BAD_DOT_I */ - else if (cmdtail) { - printf ("Usage:\n%s\n", cmdtp->usage); - return 1; + + printf(" %d bytes %s: %s\n", size, + read ? "read" : "written", ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; + } + + if (strcmp(cmd, "markbad") == 0) { + addr = (ulong)simple_strtoul(argv[2], NULL, 16); + + int ret = nand->block_markbad(nand, addr); + if (ret == 0) { + printf("block 0x%08lx successfully marked as bad\n", + (ulong) addr); + return 0; + } else { + printf("block 0x%08lx NOT marked as bad! ERROR %d\n", + (ulong) addr, ret); } + return 1; + } + if (strcmp(cmd, "biterr") == 0) { + /* todo */ + return 1; + } - printf ("\nNAND %s: device %d offset %ld, size %ld ... ", - (cmd & NANDRW_READ) ? "read" : "write", - curr_device, off, size); + 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; + } - ret = nand_rw(nand_dev_desc + curr_device, cmd, off, size, - &total, (u_char*)addr); + if (status) { + ulong block_start = 0; + ulong off; + int last_status = -1; - printf (" %d bytes %s: %s\n", total, - (cmd & NANDRW_READ) ? "read" : "written", - ret ? "ERROR" : "OK"); + struct nand_chip *nand_chip = nand->priv; + /* check the WP bit */ + nand_chip->cmdfunc (nand, NAND_CMD_STATUS, -1, -1); + printf("device is %swrite protected\n", + (nand_chip->read_byte(nand) & 0x80 ? + "NOT " : "" ) ); - return ret; - } else if (strcmp(argv[1],"erase") == 0 && - (argc == 4 || strcmp("clean", argv[2]) == 0)) { - int clean = argc == 5; - ulong off = simple_strtoul(argv[2 + clean], NULL, 16); - ulong size = simple_strtoul(argv[3 + clean], NULL, 16); - int ret; + for (off = 0; off < nand->size; off += nand->oobblock) { + int s = nand_get_lock_status(nand, off); - printf ("\nNAND erase: device %d offset %ld, size %ld ... ", - curr_device, off, size); + /* print message only if status has changed + * or at end of chip + */ + if (off == nand->size - nand->oobblock + || (s != last_status && off != 0)) { + + printf("%08x - %08x: %8d pages %s%s%s\n", + block_start, + off-1, + (off-block_start)/nand->oobblock, + ((last_status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), + ((last_status & NAND_LOCK_STATUS_LOCK) ? "LOCK " : ""), + ((last_status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); + } - ret = nand_erase (nand_dev_desc + curr_device, off, size, clean); + last_status = s; + } + } else { + if (!nand_lock(nand, tight)) { + puts("NAND flash successfully locked\n"); + } else { + puts("Error locking NAND flash\n"); + return 1; + } + } + return 0; + } - printf("%s\n", ret ? "ERROR" : "OK"); + if (strcmp(cmd, "unlock") == 0) { + if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) + return 1; - return ret; - } else { - printf ("Usage:\n%s\n", cmdtp->usage); - rcode = 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; } - return rcode; - } +usage: + printf("Usage:\n%s\n", cmdtp->usage); + return 1; } -U_BOOT_CMD( - nand, 5, 1, do_nand, +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[s]] addr off size\n" - "nand write[.jffs2] addr off size - read/write `size' bytes starting\n" + "info - show available NAND devices\n" + "nand device [dev] - show or set current device\n" + "nand read[.jffs2] - addr off|partition size\n" + "nand write[.jffs2] - addr off|partition 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" "nand bad - show bad blocks\n" - "nand read.oob addr off size - read out-of-band data\n" - "nand write.oob addr off size - read out-of-band data\n" -); - -int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) + "nand dump[.oob] off - dump page\n" + "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" + "nand markbad off - mark bad block at offset (UNSAFE)\n" + "nand biterr off - make a bit error at offset (UNSAFE)\n" + "nand lock [tight] [status] - bring nand to lock state or display locked pages\n" + "nand unlock [offset] [size] - unlock section\n"); + +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; + char *ep, *s; ulong cnt; - ulong addr; - ulong offset = 0; image_header_t *hdr; - int rcode = 0; + int jffs2 = 0; + + s = strchr(cmd, '.'); + if (s != NULL && + (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) + jffs2 = 1; + + printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset); + + cnt = nand->oobblock; + if (jffs2) { + nand_read_options_t opts; + memset(&opts, 0, sizeof(opts)); + opts.buffer = (u_char*) addr; + opts.length = cnt; + opts.offset = offset; + opts.quiet = 1; + r = nand_read_opts(nand, &opts); + } else { + r = nand_read(nand, offset, &cnt, (u_char *) addr); + } + + if (r) { + puts("** Read error\n"); + show_boot_progress (-56); + return 1; + } + show_boot_progress (56); + + hdr = (image_header_t *) addr; + + if (ntohl(hdr->ih_magic) != IH_MAGIC) { + printf("\n** Bad Magic Number 0x%x **\n", hdr->ih_magic); + show_boot_progress (-57); + return 1; + } + show_boot_progress (57); + + print_image_hdr(hdr); + + cnt = (ntohl(hdr->ih_size) + sizeof (image_header_t)); + if (jffs2) { + nand_read_options_t opts; + memset(&opts, 0, sizeof(opts)); + opts.buffer = (u_char*) addr; + opts.length = cnt; + opts.offset = offset; + opts.quiet = 1; + r = nand_read_opts(nand, &opts); + } else { + r = nand_read(nand, offset, &cnt, (u_char *) addr); + } + + if (r) { + puts("** Read error\n"); + show_boot_progress (-58); + return 1; + } + show_boot_progress (58); + + /* Loading ok, update default load address */ + + load_addr = addr; + + /* Check if we should attempt an auto-start */ + if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) { + char *local_args[2]; + extern int do_bootm(cmd_tbl_t *, int, int, char *[]); + + local_args[0] = cmd; + local_args[1] = NULL; + + printf("Automatic boot of image at addr 0x%08lx ...\n", addr); + + do_bootm(cmdtp, 0, 1, local_args); + return 1; + } + return 0; +} + +int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + char *boot_device = NULL; + int idx; + ulong addr, offset = 0; +#if defined(CONFIG_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[1], NULL, 16); + else + addr = CFG_LOAD_ADDR; + return nand_load_image(cmdtp, &nand_info[dev->id->num], + part->offset, addr, argv[0]); + } + } +#endif + + show_boot_progress(52); switch (argc) { case 1: addr = CFG_LOAD_ADDR; - boot_device = getenv ("bootdevice"); + boot_device = getenv("bootdevice"); break; case 2: addr = simple_strtoul(argv[1], NULL, 16); - boot_device = getenv ("bootdevice"); + boot_device = getenv("bootdevice"); break; case 3: addr = simple_strtoul(argv[1], NULL, 16); @@ -316,1583 +613,423 @@ int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) offset = simple_strtoul(argv[3], NULL, 16); break; default: - printf ("Usage:\n%s\n", cmdtp->usage); - SHOW_BOOT_PROGRESS (-1); +#if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) +usage: +#endif + printf("Usage:\n%s\n", cmdtp->usage); + show_boot_progress(-53); return 1; } + show_boot_progress(53); if (!boot_device) { - puts ("\n** No boot device **\n"); - SHOW_BOOT_PROGRESS (-1); + puts("\n** No boot device **\n"); + show_boot_progress(-54); return 1; } + show_boot_progress(54); - dev = simple_strtoul(boot_device, &ep, 16); + idx = simple_strtoul(boot_device, NULL, 16); - if ((dev >= CFG_MAX_NAND_DEVICE) || - (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN)) { - printf ("\n** Device %d not available\n", dev); - SHOW_BOOT_PROGRESS (-1); + if (idx < 0 || idx >= CFG_MAX_NAND_DEVICE || !nand_info[idx].name) { + printf("\n** Device %d not available\n", idx); + show_boot_progress(-55); return 1; } + show_boot_progress(55); - printf ("\nLoading from device %d: %s at 0x%lx (offset 0x%lx)\n", - dev, nand_dev_desc[dev].name, nand_dev_desc[dev].IO_ADDR, - offset); + return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]); +} - if (nand_rw (nand_dev_desc + dev, NANDRW_READ, offset, - SECTORSIZE, NULL, (u_char *)addr)) { - printf ("** Read error on %d\n", dev); - SHOW_BOOT_PROGRESS (-1); - return 1; - } +U_BOOT_CMD(nboot, 4, 1, do_nandboot, + "nboot - boot from NAND device\n", + "[.jffs2] [partition] | [[[loadAddr] dev] offset]\n"); - hdr = (image_header_t *)addr; +#endif - if (ntohl(hdr->ih_magic) == IH_MAGIC) { +#else /* CFG_NAND_LEGACY */ +/* + * + * Legacy NAND support - to be phased out + * + */ +#include +#include +#include +#include - print_image_hdr (hdr); - - cnt = (ntohl(hdr->ih_size) + sizeof(image_header_t)); - cnt -= SECTORSIZE; - } else { - printf ("\n** Bad Magic Number 0x%x **\n", hdr->ih_magic); - SHOW_BOOT_PROGRESS (-1); - return 1; - } - - if (nand_rw (nand_dev_desc + dev, NANDRW_READ, offset + SECTORSIZE, cnt, - NULL, (u_char *)(addr+SECTORSIZE))) { - printf ("** Read error on %d\n", dev); - SHOW_BOOT_PROGRESS (-1); - return 1; - } - - /* Loading ok, update default load address */ - - load_addr = addr; - - /* Check if we should attempt an auto-start */ - if (((ep = getenv("autostart")) != NULL) && (strcmp(ep,"yes") == 0)) { - char *local_args[2]; - extern int do_bootm (cmd_tbl_t *, int, int, char *[]); - - local_args[0] = argv[0]; - local_args[1] = NULL; - - printf ("Automatic boot of image at addr 0x%08lx ...\n", addr); - - do_bootm (cmdtp, 0, 1, local_args); - rcode = 1; - } - return rcode; -} - -U_BOOT_CMD( - nboot, 4, 1, do_nandboot, - "nboot - boot from NAND device\n", - "loadAddr dev\n" -); - -/* returns 0 if block containing pos is OK: - * valid erase block and - * not marked bad, or no bad mark position is specified - * returns 1 if marked bad or otherwise invalid - */ -int check_block (struct nand_chip *nand, unsigned long pos) -{ - int retlen; - uint8_t oob_data; - uint16_t oob_data16[6]; - int page0 = pos & (-nand->erasesize); - int page1 = page0 + nand->oobblock; - int badpos = oob_config.badblock_pos; - - if (pos >= nand->totlen) - return 1; - - if (badpos < 0) - return 0; /* no way to check, assume OK */ - - if (nand->bus16) { - if (nand_read_oob(nand, (page0 + 0), 12, &retlen, (uint8_t *)oob_data16) - || (oob_data16[2] & 0xff00) != 0xff00) - return 1; - if (nand_read_oob(nand, (page1 + 0), 12, &retlen, (uint8_t *)oob_data16) - || (oob_data16[2] & 0xff00) != 0xff00) - return 1; - } else { - /* Note - bad block marker can be on first or second page */ - if (nand_read_oob(nand, page0 + badpos, 1, &retlen, &oob_data) - || oob_data != 0xff - || nand_read_oob (nand, page1 + badpos, 1, &retlen, &oob_data) - || oob_data != 0xff) - return 1; - } - - return 0; -} - -/* print bad blocks in NAND flash */ -static void nand_print_bad(struct nand_chip* nand) -{ - unsigned long pos; - - for (pos = 0; pos < nand->totlen; pos += nand->erasesize) { - if (check_block(nand, pos)) - printf(" 0x%8.8lx\n", pos); - } - puts("\n"); -} - -/* cmd: 0: NANDRW_WRITE write, fail on bad block - * 1: NANDRW_READ read, fail on bad block - * 2: NANDRW_WRITE | NANDRW_JFFS2 write, skip bad blocks - * 3: NANDRW_READ | NANDRW_JFFS2 read, data all 0xff for bad blocks - * 7: NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP read, skip bad blocks - */ -int nand_rw (struct nand_chip* nand, int cmd, - size_t start, size_t len, - size_t * retlen, u_char * buf) -{ - int ret = 0, n, total = 0; - char eccbuf[6]; - /* eblk (once set) is the start of the erase block containing the - * data being processed. - */ - unsigned long eblk = ~0; /* force mismatch on first pass */ - unsigned long erasesize = nand->erasesize; - - while (len) { - if ((start & (-erasesize)) != eblk) { - /* have crossed into new erase block, deal with - * it if it is sure marked bad. - */ - eblk = start & (-erasesize); /* start of block */ - if (check_block(nand, eblk)) { - if (cmd == (NANDRW_READ | NANDRW_JFFS2)) { - while (len > 0 && - start - eblk < erasesize) { - *(buf++) = 0xff; - ++start; - ++total; - --len; - } - continue; - } else if (cmd == (NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP)) { - start += erasesize; - continue; - } else if (cmd == (NANDRW_WRITE | NANDRW_JFFS2)) { - /* skip bad block */ - start += erasesize; - continue; - } else { - ret = 1; - break; - } - } - } - /* The ECC will not be calculated correctly if - less than 512 is written or read */ - /* Is request at least 512 bytes AND it starts on a proper boundry */ - if((start != ROUND_DOWN(start, 0x200)) || (len < 0x200)) - printf("Warning block writes should be at least 512 bytes and start on a 512 byte boundry\n"); - - if (cmd & NANDRW_READ) { - ret = nand_read_ecc(nand, start, - min(len, eblk + erasesize - start), - &n, (u_char*)buf, eccbuf); - } else { - ret = nand_write_ecc(nand, start, - min(len, eblk + erasesize - start), - &n, (u_char*)buf, eccbuf); - } - - if (ret) - break; - - start += n; - buf += n; - total += n; - len -= n; - } - if (retlen) - *retlen = total; - - return ret; -} - -static void nand_print(struct nand_chip *nand) -{ - if (nand->numchips > 1) { - printf("%s at 0x%lx,\n" - "\t %d chips %s, size %d MB, \n" - "\t total size %ld MB, sector size %ld kB\n", - nand->name, nand->IO_ADDR, nand->numchips, - nand->chips_name, 1 << (nand->chipshift - 20), - nand->totlen >> 20, nand->erasesize >> 10); - } - else { - printf("%s at 0x%lx (", nand->chips_name, nand->IO_ADDR); - print_size(nand->totlen, ", "); - print_size(nand->erasesize, " sector)\n"); - } -} - -/* ------------------------------------------------------------------------- */ - -static int NanD_WaitReady(struct nand_chip *nand, int ale_wait) -{ - /* This is inline, to optimise the common case, where it's ready instantly */ - int ret = 0; - -#ifdef NAND_NO_RB /* in config file, shorter delays currently wrap accesses */ - if(ale_wait) - NAND_WAIT_READY(nand); /* do the worst case 25us wait */ - else - udelay(10); -#else /* has functional r/b signal */ - NAND_WAIT_READY(nand); -#endif - return ret; -} - -/* NanD_Command: Send a flash command to the flash chip */ - -static inline int NanD_Command(struct nand_chip *nand, unsigned char command) -{ - unsigned long nandptr = nand->IO_ADDR; - - /* Assert the CLE (Command Latch Enable) line to the flash chip */ - NAND_CTL_SETCLE(nandptr); - - /* Send the command */ - WRITE_NAND_COMMAND(command, nandptr); - - /* Lower the CLE line */ - NAND_CTL_CLRCLE(nandptr); - -#ifdef NAND_NO_RB - if(command == NAND_CMD_RESET){ - u_char ret_val; - NanD_Command(nand, NAND_CMD_STATUS); - do { - ret_val = READ_NAND(nandptr);/* wait till ready */ - } while((ret_val & 0x40) != 0x40); - } -#endif - return NanD_WaitReady(nand, 0); -} - -/* NanD_Address: Set the current address for the flash chip */ - -static int NanD_Address(struct nand_chip *nand, int numbytes, unsigned long ofs) -{ - unsigned long nandptr; - int i; - - nandptr = nand->IO_ADDR; - - /* Assert the ALE (Address Latch Enable) line to the flash chip */ - NAND_CTL_SETALE(nandptr); - - /* Send the address */ - /* Devices with 256-byte page are addressed as: - * Column (bits 0-7), Page (bits 8-15, 16-23, 24-31) - * there is no device on the market with page256 - * and more than 24 bits. - * Devices with 512-byte page are addressed as: - * Column (bits 0-7), Page (bits 9-16, 17-24, 25-31) - * 25-31 is sent only if the chip support it. - * bit 8 changes the read command to be sent - * (NAND_CMD_READ0 or NAND_CMD_READ1). - */ - - if (numbytes == ADDR_COLUMN || numbytes == ADDR_COLUMN_PAGE) - WRITE_NAND_ADDRESS(ofs, nandptr); - - ofs = ofs >> nand->page_shift; - - if (numbytes == ADDR_PAGE || numbytes == ADDR_COLUMN_PAGE) { - for (i = 0; i < nand->pageadrlen; i++, ofs = ofs >> 8) { - WRITE_NAND_ADDRESS(ofs, nandptr); - } - } - - /* Lower the ALE line */ - NAND_CTL_CLRALE(nandptr); - - /* Wait for the chip to respond */ - return NanD_WaitReady(nand, 1); -} - -/* NanD_SelectChip: Select a given flash chip within the current floor */ - -static inline int NanD_SelectChip(struct nand_chip *nand, int chip) -{ - /* Wait for it to be ready */ - return NanD_WaitReady(nand, 0); -} - -/* NanD_IdentChip: Identify a given NAND chip given {floor,chip} */ - -static int NanD_IdentChip(struct nand_chip *nand, int floor, int chip) -{ - int mfr, id, i; - - NAND_ENABLE_CE(nand); /* set pin low */ - /* Reset the chip */ - if (NanD_Command(nand, NAND_CMD_RESET)) { -#ifdef NAND_DEBUG - printf("NanD_Command (reset) for %d,%d returned true\n", - floor, chip); -#endif - NAND_DISABLE_CE(nand); /* set pin high */ - return 0; - } - - /* Read the NAND chip ID: 1. Send ReadID command */ - if (NanD_Command(nand, NAND_CMD_READID)) { -#ifdef NAND_DEBUG - printf("NanD_Command (ReadID) for %d,%d returned true\n", - floor, chip); -#endif - NAND_DISABLE_CE(nand); /* set pin high */ - return 0; - } - - /* Read the NAND chip ID: 2. Send address byte zero */ - NanD_Address(nand, ADDR_COLUMN, 0); - - /* Read the manufacturer and device id codes from the device */ - - mfr = READ_NAND(nand->IO_ADDR); - - id = READ_NAND(nand->IO_ADDR); - - NAND_DISABLE_CE(nand); /* set pin high */ - -#ifdef NAND_DEBUG - printf("NanD_Command (ReadID) got %x %x\n", mfr, id); -#endif - if (mfr == 0xff || mfr == 0) { - /* No response - return failure */ - return 0; - } - - /* Check it's the same as the first chip we identified. - * M-Systems say that any given nand_chip device should only - * contain _one_ type of flash part, although that's not a - * hardware restriction. */ - if (nand->mfr) { - if (nand->mfr == mfr && nand->id == id) { - return 1; /* This is another the same the first */ - } else { - printf("Flash chip at floor %d, chip %d is different:\n", - floor, chip); - } - } - - /* Print and store the manufacturer and ID codes. */ - for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (mfr == nand_flash_ids[i].manufacture_id && - id == nand_flash_ids[i].model_id) { -#ifdef NAND_DEBUG - printf("Flash chip found:\n\t Manufacturer ID: 0x%2.2X, " - "Chip ID: 0x%2.2X (%s)\n", mfr, id, - nand_flash_ids[i].name); -#endif - if (!nand->mfr) { - nand->mfr = mfr; - nand->id = id; - nand->chipshift = - nand_flash_ids[i].chipshift; - nand->page256 = nand_flash_ids[i].page256; - nand->eccsize = 256; - if (nand->page256) { - nand->oobblock = 256; - nand->oobsize = 8; - nand->page_shift = 8; - } else { - nand->oobblock = 512; - nand->oobsize = 16; - nand->page_shift = 9; - } - nand->pageadrlen = nand_flash_ids[i].pageadrlen; - nand->erasesize = nand_flash_ids[i].erasesize; - nand->chips_name = nand_flash_ids[i].name; - nand->bus16 = nand_flash_ids[i].bus16; - return 1; - } - return 0; - } - } - - -#ifdef NAND_DEBUG - /* We haven't fully identified the chip. Print as much as we know. */ - printf("Unknown flash chip found: %2.2X %2.2X\n", - id, mfr); -#endif - - return 0; -} - -/* NanD_ScanChips: Find all NAND chips present in a nand_chip, and identify them */ - -static void NanD_ScanChips(struct nand_chip *nand) -{ - int floor, chip; - int numchips[NAND_MAX_FLOORS]; - int maxchips = NAND_MAX_CHIPS; - int ret = 1; - - nand->numchips = 0; - nand->mfr = 0; - nand->id = 0; - - - /* For each floor, find the number of valid chips it contains */ - for (floor = 0; floor < NAND_MAX_FLOORS; floor++) { - ret = 1; - numchips[floor] = 0; - for (chip = 0; chip < maxchips && ret != 0; chip++) { - - ret = NanD_IdentChip(nand, floor, chip); - if (ret) { - numchips[floor]++; - nand->numchips++; - } - } - } - - /* If there are none at all that we recognise, bail */ - if (!nand->numchips) { -#ifdef NAND_DEBUG - puts ("No NAND flash chips recognised.\n"); -#endif - return; - } - - /* Allocate an array to hold the information for each chip */ - nand->chips = malloc(sizeof(struct Nand) * nand->numchips); - if (!nand->chips) { - puts ("No memory for allocating chip info structures\n"); - return; - } - - ret = 0; - - /* Fill out the chip array with {floor, chipno} for each - * detected chip in the device. */ - for (floor = 0; floor < NAND_MAX_FLOORS; floor++) { - for (chip = 0; chip < numchips[floor]; chip++) { - nand->chips[ret].floor = floor; - nand->chips[ret].chip = chip; - nand->chips[ret].curadr = 0; - nand->chips[ret].curmode = 0x50; - ret++; - } - } - - /* Calculate and print the total size of the device */ - nand->totlen = nand->numchips * (1 << nand->chipshift); - -#ifdef NAND_DEBUG - printf("%d flash chips found. Total nand_chip size: %ld MB\n", - nand->numchips, nand->totlen >> 20); -#endif -} - -/* we need to be fast here, 1 us per read translates to 1 second per meg */ -static void NanD_ReadBuf (struct nand_chip *nand, u_char * data_buf, int cntr) -{ - unsigned long nandptr = nand->IO_ADDR; - - NanD_Command (nand, NAND_CMD_READ0); - - if (nand->bus16) { - u16 val; - - while (cntr >= 16) { - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - cntr -= 16; - } - - while (cntr > 0) { - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - cntr -= 2; - } - } else { - while (cntr >= 16) { - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - cntr -= 16; - } - - while (cntr > 0) { - *data_buf++ = READ_NAND (nandptr); - cntr--; - } - } -} - -/* - * NAND read with ECC - */ -static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len, - size_t * retlen, u_char *buf, u_char *ecc_code) -{ - int col, page; - int ecc_status = 0; -#ifdef CONFIG_MTD_NAND_ECC - int j; - int ecc_failed = 0; - u_char *data_poi; - u_char ecc_calc[6]; -#endif - - /* Do not allow reads past end of device */ - if ((start + len) > nand->totlen) { - printf ("%s: Attempt read beyond end of device %x %x %x\n", - __FUNCTION__, (uint) start, (uint) len, (uint) nand->totlen); - *retlen = 0; - return -1; - } - - /* First we calculate the starting page */ - /*page = shr(start, nand->page_shift);*/ - page = start >> nand->page_shift; - - /* Get raw starting column */ - col = start & (nand->oobblock - 1); - - /* Initialize return value */ - *retlen = 0; - - /* Select the NAND device */ - NAND_ENABLE_CE(nand); /* set pin low */ - - /* Loop until all data read */ - while (*retlen < len) { - -#ifdef CONFIG_MTD_NAND_ECC - /* Do we have this page in cache ? */ - if (nand->cache_page == page) - goto readdata; - /* Send the read command */ - NanD_Command(nand, NAND_CMD_READ0); - if (nand->bus16) { - NanD_Address(nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + (col >> 1)); - } else { - NanD_Address(nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + col); - } - - /* Read in a page + oob data */ - NanD_ReadBuf(nand, nand->data_buf, nand->oobblock + nand->oobsize); - - /* copy data into cache, for read out of cache and if ecc fails */ - if (nand->data_cache) { - memcpy (nand->data_cache, nand->data_buf, - nand->oobblock + nand->oobsize); - } - - /* Pick the ECC bytes out of the oob data */ - for (j = 0; j < 6; j++) { - ecc_code[j] = nand->data_buf[(nand->oobblock + oob_config.ecc_pos[j])]; - } - - /* Calculate the ECC and verify it */ - /* If block was not written with ECC, skip ECC */ - if (oob_config.eccvalid_pos != -1 && - (nand->data_buf[nand->oobblock + oob_config.eccvalid_pos] & 0x0f) != 0x0f) { - - nand_calculate_ecc (&nand->data_buf[0], &ecc_calc[0]); - switch (nand_correct_data (&nand->data_buf[0], &ecc_code[0], &ecc_calc[0])) { - case -1: - printf ("%s: Failed ECC read, page 0x%08x\n", __FUNCTION__, page); - ecc_failed++; - break; - case 1: - case 2: /* transfer ECC corrected data to cache */ - if (nand->data_cache) - memcpy (nand->data_cache, nand->data_buf, 256); - break; - } - } - - if (oob_config.eccvalid_pos != -1 && - nand->oobblock == 512 && (nand->data_buf[nand->oobblock + oob_config.eccvalid_pos] & 0xf0) != 0xf0) { - - nand_calculate_ecc (&nand->data_buf[256], &ecc_calc[3]); - switch (nand_correct_data (&nand->data_buf[256], &ecc_code[3], &ecc_calc[3])) { - case -1: - printf ("%s: Failed ECC read, page 0x%08x\n", __FUNCTION__, page); - ecc_failed++; - break; - case 1: - case 2: /* transfer ECC corrected data to cache */ - if (nand->data_cache) - memcpy (&nand->data_cache[256], &nand->data_buf[256], 256); - break; - } - } -readdata: - /* Read the data from ECC data buffer into return buffer */ - data_poi = (nand->data_cache) ? nand->data_cache : nand->data_buf; - data_poi += col; - if ((*retlen + (nand->oobblock - col)) >= len) { - memcpy (buf + *retlen, data_poi, len - *retlen); - *retlen = len; - } else { - memcpy (buf + *retlen, data_poi, nand->oobblock - col); - *retlen += nand->oobblock - col; - } - /* Set cache page address, invalidate, if ecc_failed */ - nand->cache_page = (nand->data_cache && !ecc_failed) ? page : -1; - - ecc_status += ecc_failed; - ecc_failed = 0; - -#else - /* Send the read command */ - NanD_Command(nand, NAND_CMD_READ0); - if (nand->bus16) { - NanD_Address(nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + (col >> 1)); - } else { - NanD_Address(nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + col); - } - - /* Read the data directly into the return buffer */ - if ((*retlen + (nand->oobblock - col)) >= len) { - NanD_ReadBuf(nand, buf + *retlen, len - *retlen); - *retlen = len; - /* We're done */ - continue; - } else { - NanD_ReadBuf(nand, buf + *retlen, nand->oobblock - col); - *retlen += nand->oobblock - col; - } -#endif - /* For subsequent reads align to page boundary. */ - col = 0; - /* Increment page address */ - page++; - } - - /* De-select the NAND device */ - NAND_DISABLE_CE(nand); /* set pin high */ - - /* - * Return success, if no ECC failures, else -EIO - * fs driver will take care of that, because - * retlen == desired len and result == -EIO - */ - return ecc_status ? -1 : 0; -} - -/* - * Nand_page_program function is used for write and writev ! - */ -static int nand_write_page (struct nand_chip *nand, - int page, int col, int last, u_char * ecc_code) -{ - - int i; - unsigned long nandptr = nand->IO_ADDR; - -#ifdef CONFIG_MTD_NAND_ECC -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - int ecc_bytes = (nand->oobblock == 512) ? 6 : 3; -#endif -#endif - /* pad oob area */ - for (i = nand->oobblock; i < nand->oobblock + nand->oobsize; i++) - nand->data_buf[i] = 0xff; - -#ifdef CONFIG_MTD_NAND_ECC - /* Zero out the ECC array */ - for (i = 0; i < 6; i++) - ecc_code[i] = 0x00; - - /* Read back previous written data, if col > 0 */ - if (col) { - NanD_Command (nand, NAND_CMD_READ0); - if (nand->bus16) { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + (col >> 1)); - } else { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + col); - } - - if (nand->bus16) { - u16 val; - - for (i = 0; i < col; i += 2) { - val = READ_NAND (nandptr); - nand->data_buf[i] = val & 0xff; - nand->data_buf[i + 1] = val >> 8; - } - } else { - for (i = 0; i < col; i++) - nand->data_buf[i] = READ_NAND (nandptr); - } - } - - /* Calculate and write the ECC if we have enough data */ - if ((col < nand->eccsize) && (last >= nand->eccsize)) { - nand_calculate_ecc (&nand->data_buf[0], &(ecc_code[0])); - for (i = 0; i < 3; i++) { - nand->data_buf[(nand->oobblock + - oob_config.ecc_pos[i])] = ecc_code[i]; - } - if (oob_config.eccvalid_pos != -1) { - nand->data_buf[nand->oobblock + - oob_config.eccvalid_pos] = 0xf0; - } - } - - /* Calculate and write the second ECC if we have enough data */ - if ((nand->oobblock == 512) && (last == nand->oobblock)) { - nand_calculate_ecc (&nand->data_buf[256], &(ecc_code[3])); - for (i = 3; i < 6; i++) { - nand->data_buf[(nand->oobblock + - oob_config.ecc_pos[i])] = ecc_code[i]; - } - if (oob_config.eccvalid_pos != -1) { - nand->data_buf[nand->oobblock + - oob_config.eccvalid_pos] &= 0x0f; - } - } -#endif - /* Prepad for partial page programming !!! */ - for (i = 0; i < col; i++) - nand->data_buf[i] = 0xff; - - /* Postpad for partial page programming !!! oob is already padded */ - for (i = last; i < nand->oobblock; i++) - nand->data_buf[i] = 0xff; - - /* Send command to begin auto page programming */ - NanD_Command (nand, NAND_CMD_READ0); - NanD_Command (nand, NAND_CMD_SEQIN); - if (nand->bus16) { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + (col >> 1)); - } else { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + col); - } - - /* Write out complete page of data */ - if (nand->bus16) { - for (i = 0; i < (nand->oobblock + nand->oobsize); i += 2) { - WRITE_NAND (nand->data_buf[i] + - (nand->data_buf[i + 1] << 8), - nand->IO_ADDR); - } - } else { - for (i = 0; i < (nand->oobblock + nand->oobsize); i++) - WRITE_NAND (nand->data_buf[i], nand->IO_ADDR); - } - - /* Send command to actually program the data */ - NanD_Command (nand, NAND_CMD_PAGEPROG); - NanD_Command (nand, NAND_CMD_STATUS); -#ifdef NAND_NO_RB - { - u_char ret_val; - - do { - ret_val = READ_NAND (nandptr); /* wait till ready */ - } while ((ret_val & 0x40) != 0x40); - } -#endif - /* See if device thinks it succeeded */ - if (READ_NAND (nand->IO_ADDR) & 0x01) { - printf ("%s: Failed write, page 0x%08x, ", __FUNCTION__, - page); - return -1; - } -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - /* - * The NAND device assumes that it is always writing to - * a cleanly erased page. Hence, it performs its internal - * write verification only on bits that transitioned from - * 1 to 0. The device does NOT verify the whole page on a - * byte by byte basis. It is possible that the page was - * not completely erased or the page is becoming unusable - * due to wear. The read with ECC would catch the error - * later when the ECC page check fails, but we would rather - * catch it early in the page write stage. Better to write - * no data than invalid data. - */ - - /* Send command to read back the page */ - if (col < nand->eccsize) - NanD_Command (nand, NAND_CMD_READ0); - else - NanD_Command (nand, NAND_CMD_READ1); - if (nand->bus16) { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + (col >> 1)); - } else { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + col); - } - - /* Loop through and verify the data */ - if (nand->bus16) { - for (i = col; i < last; i = +2) { - if ((nand->data_buf[i] + - (nand->data_buf[i + 1] << 8)) != READ_NAND (nand->IO_ADDR)) { - printf ("%s: Failed write verify, page 0x%08x ", - __FUNCTION__, page); - return -1; - } - } - } else { - for (i = col; i < last; i++) { - if (nand->data_buf[i] != READ_NAND (nand->IO_ADDR)) { - printf ("%s: Failed write verify, page 0x%08x ", - __FUNCTION__, page); - return -1; - } - } - } - -#ifdef CONFIG_MTD_NAND_ECC - /* - * We also want to check that the ECC bytes wrote - * correctly for the same reasons stated above. - */ - NanD_Command (nand, NAND_CMD_READOOB); - if (nand->bus16) { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + (col >> 1)); - } else { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + col); - } - if (nand->bus16) { - for (i = 0; i < nand->oobsize; i += 2) { - u16 val; - - val = READ_NAND (nand->IO_ADDR); - nand->data_buf[i] = val & 0xff; - nand->data_buf[i + 1] = val >> 8; - } - } else { - for (i = 0; i < nand->oobsize; i++) { - nand->data_buf[i] = READ_NAND (nand->IO_ADDR); - } - } - for (i = 0; i < ecc_bytes; i++) { - if ((nand->data_buf[(oob_config.ecc_pos[i])] != ecc_code[i]) && ecc_code[i]) { - printf ("%s: Failed ECC write " - "verify, page 0x%08x, " - "%6i bytes were succesful\n", - __FUNCTION__, page, i); - return -1; - } - } -#endif /* CONFIG_MTD_NAND_ECC */ -#endif /* CONFIG_MTD_NAND_VERIFY_WRITE */ - return 0; -} - -static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t len, - size_t * retlen, const u_char * buf, u_char * ecc_code) -{ - int i, page, col, cnt, ret = 0; - - /* Do not allow write past end of device */ - if ((to + len) > nand->totlen) { - printf ("%s: Attempt to write past end of page\n", __FUNCTION__); - return -1; - } - - /* Shift to get page */ - page = ((int) to) >> nand->page_shift; - - /* Get the starting column */ - col = to & (nand->oobblock - 1); - - /* Initialize return length value */ - *retlen = 0; - - /* Select the NAND device */ -#ifdef CONFIG_OMAP1510 - archflashwp(0,0); -#endif -#ifdef CFG_NAND_WP - NAND_WP_OFF(); -#endif - - NAND_ENABLE_CE(nand); /* set pin low */ - - /* Check the WP bit */ - NanD_Command(nand, NAND_CMD_STATUS); - if (!(READ_NAND(nand->IO_ADDR) & 0x80)) { - printf ("%s: Device is write protected!!!\n", __FUNCTION__); - ret = -1; - goto out; - } - - /* Loop until all data is written */ - while (*retlen < len) { - /* Invalidate cache, if we write to this page */ - if (nand->cache_page == page) - nand->cache_page = -1; - - /* Write data into buffer */ - if ((col + len) >= nand->oobblock) { - for (i = col, cnt = 0; i < nand->oobblock; i++, cnt++) { - nand->data_buf[i] = buf[(*retlen + cnt)]; - } - } else { - for (i = col, cnt = 0; cnt < (len - *retlen); i++, cnt++) { - nand->data_buf[i] = buf[(*retlen + cnt)]; - } - } - /* We use the same function for write and writev !) */ - ret = nand_write_page (nand, page, col, i, ecc_code); - if (ret) - goto out; - - /* Next data start at page boundary */ - col = 0; - - /* Update written bytes count */ - *retlen += cnt; - - /* Increment page address */ - page++; - } - - /* Return happy */ - *retlen = len; - -out: - /* De-select the NAND device */ - NAND_DISABLE_CE(nand); /* set pin high */ -#ifdef CONFIG_OMAP1510 - archflashwp(0,1); -#endif -#ifdef CFG_NAND_WP - NAND_WP_ON(); +#ifdef CONFIG_show_boot_progress +# include +# define show_boot_progress(arg) show_boot_progress(arg) +#else +# define show_boot_progress(arg) #endif - return ret; -} - -/* read from the 16 bytes of oob data that correspond to a 512 byte - * page or 2 256-byte pages. - */ -static int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len, - size_t * retlen, u_char * buf) -{ - int len256 = 0; - struct Nand *mychip; - int ret = 0; - - mychip = &nand->chips[ofs >> nand->chipshift]; - - /* update address for 2M x 8bit devices. OOB starts on the second */ - /* page to maintain compatibility with nand_read_ecc. */ - if (nand->page256) { - if (!(ofs & 0x8)) - ofs += 0x100; - else - ofs -= 0x8; - } - - NAND_ENABLE_CE(nand); /* set pin low */ - NanD_Command(nand, NAND_CMD_READOOB); - if (nand->bus16) { - NanD_Address(nand, ADDR_COLUMN_PAGE, - ((ofs >> nand->page_shift) << nand->page_shift) + - ((ofs & (nand->oobblock - 1)) >> 1)); - } else { - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs); - } - - /* treat crossing 8-byte OOB data for 2M x 8bit devices */ - /* Note: datasheet says it should automaticaly wrap to the */ - /* next OOB block, but it didn't work here. mf. */ - if (nand->page256 && ofs + len > (ofs | 0x7) + 1) { - len256 = (ofs | 0x7) + 1 - ofs; - NanD_ReadBuf(nand, buf, len256); +#if defined(CONFIG_CMD_NAND) +#include +#if 0 +#include +#include +#endif - NanD_Command(nand, NAND_CMD_READOOB); - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs & (~0x1ff)); - } +#ifdef CONFIG_OMAP1510 +void archflashwp(void *archdata, int wp); +#endif - NanD_ReadBuf(nand, &buf[len256], len - len256); +#define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) - *retlen = len; - /* Reading the full OOB data drops us off of the end of the page, - * causing the flash device to go into busy mode, so we need - * to wait until ready 11.4.1 and Toshiba TC58256FT nands */ +#undef NAND_DEBUG +#undef PSYCHO_DEBUG - ret = NanD_WaitReady(nand, 1); - NAND_DISABLE_CE(nand); /* set pin high */ +/* ****************** WARNING ********************* + * When ALLOW_ERASE_BAD_DEBUG is non-zero the erase command will + * erase (or at least attempt to erase) blocks that are marked + * bad. This can be very handy if you are _sure_ that the block + * is OK, say because you marked a good block bad to test bad + * block handling and you are done testing, or if you have + * accidentally marked blocks bad. + * + * Erasing factory marked bad blocks is a _bad_ idea. If the + * erase succeeds there is no reliable way to find them again, + * and attempting to program or erase bad blocks can affect + * the data in _other_ (good) blocks. + */ +#define ALLOW_ERASE_BAD_DEBUG 0 - return ret; +#define CONFIG_MTD_NAND_ECC /* enable ECC */ +#define CONFIG_MTD_NAND_ECC_JFFS2 -} +/* bits for nand_legacy_rw() `cmd'; or together as needed */ +#define NANDRW_READ 0x01 +#define NANDRW_WRITE 0x00 +#define NANDRW_JFFS2 0x02 +#define NANDRW_JFFS2_SKIP 0x04 -/* write to the 16 bytes of oob data that correspond to a 512 byte - * page or 2 256-byte pages. +/* + * Imports from nand_legacy.c */ -static int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, - size_t * retlen, const u_char * buf) +extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE]; +extern int curr_device; +extern int nand_legacy_erase(struct nand_chip *nand, size_t ofs, + size_t len, int clean); +extern int nand_legacy_rw(struct nand_chip *nand, int cmd, size_t start, + size_t len, size_t *retlen, u_char *buf); +extern void nand_print(struct nand_chip *nand); +extern void nand_print_bad(struct nand_chip *nand); +extern int nand_read_oob(struct nand_chip *nand, size_t ofs, + size_t len, size_t *retlen, u_char *buf); +extern int nand_write_oob(struct nand_chip *nand, size_t ofs, + size_t len, size_t *retlen, const u_char *buf); + + +int do_nand (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) { - int len256 = 0; - int i; - unsigned long nandptr = nand->IO_ADDR; - -#ifdef PSYCHO_DEBUG - printf("nand_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n", - (long)ofs, len, buf[0], buf[1], buf[2], buf[3], - buf[8], buf[9], buf[14],buf[15]); -#endif - - NAND_ENABLE_CE(nand); /* set pin low to enable chip */ + int rcode = 0; - /* Reset the chip */ - NanD_Command(nand, NAND_CMD_RESET); + switch (argc) { + case 0: + case 1: + printf ("Usage:\n%s\n", cmdtp->usage); + return 1; + case 2: + if (strcmp (argv[1], "info") == 0) { + int i; - /* issue the Read2 command to set the pointer to the Spare Data Area. */ - NanD_Command(nand, NAND_CMD_READOOB); - if (nand->bus16) { - NanD_Address(nand, ADDR_COLUMN_PAGE, - ((ofs >> nand->page_shift) << nand->page_shift) + - ((ofs & (nand->oobblock - 1)) >> 1)); - } else { - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs); - } + putc ('\n'); - /* update address for 2M x 8bit devices. OOB starts on the second */ - /* page to maintain compatibility with nand_read_ecc. */ - if (nand->page256) { - if (!(ofs & 0x8)) - ofs += 0x100; - else - ofs -= 0x8; - } + for (i = 0; i < CFG_MAX_NAND_DEVICE; ++i) { + if (nand_dev_desc[i].ChipID == + NAND_ChipID_UNKNOWN) + continue; /* list only known devices */ + printf ("Device %d: ", i); + nand_print (&nand_dev_desc[i]); + } + return 0; - /* issue the Serial Data In command to initial the Page Program process */ - NanD_Command(nand, NAND_CMD_SEQIN); - if (nand->bus16) { - NanD_Address(nand, ADDR_COLUMN_PAGE, - ((ofs >> nand->page_shift) << nand->page_shift) + - ((ofs & (nand->oobblock - 1)) >> 1)); - } else { - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs); - } + } else if (strcmp (argv[1], "device") == 0) { + if ((curr_device < 0) + || (curr_device >= CFG_MAX_NAND_DEVICE)) { + puts ("\nno devices available\n"); + return 1; + } + printf ("\nDevice %d: ", curr_device); + nand_print (&nand_dev_desc[curr_device]); + return 0; - /* treat crossing 8-byte OOB data for 2M x 8bit devices */ - /* Note: datasheet says it should automaticaly wrap to the */ - /* next OOB block, but it didn't work here. mf. */ - if (nand->page256 && ofs + len > (ofs | 0x7) + 1) { - len256 = (ofs | 0x7) + 1 - ofs; - for (i = 0; i < len256; i++) - WRITE_NAND(buf[i], nandptr); - - NanD_Command(nand, NAND_CMD_PAGEPROG); - NanD_Command(nand, NAND_CMD_STATUS); -#ifdef NAND_NO_RB - { u_char ret_val; - do { - ret_val = READ_NAND(nandptr); /* wait till ready */ - } while ((ret_val & 0x40) != 0x40); - } -#endif - if (READ_NAND(nandptr) & 1) { - puts ("Error programming oob data\n"); - /* There was an error */ - NAND_DISABLE_CE(nand); /* set pin high */ - *retlen = 0; - return -1; - } - NanD_Command(nand, NAND_CMD_SEQIN); - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs & (~0x1ff)); - } + } else if (strcmp (argv[1], "bad") == 0) { + if ((curr_device < 0) + || (curr_device >= CFG_MAX_NAND_DEVICE)) { + puts ("\nno devices available\n"); + return 1; + } + printf ("\nDevice %d bad blocks:\n", curr_device); + nand_print_bad (&nand_dev_desc[curr_device]); + return 0; - if (nand->bus16) { - for (i = len256; i < len; i += 2) { - WRITE_NAND(buf[i] + (buf[i+1] << 8), nandptr); } - } else { - for (i = len256; i < len; i++) - WRITE_NAND(buf[i], nandptr); - } - - NanD_Command(nand, NAND_CMD_PAGEPROG); - NanD_Command(nand, NAND_CMD_STATUS); -#ifdef NAND_NO_RB - { u_char ret_val; - do { - ret_val = READ_NAND(nandptr); /* wait till ready */ - } while ((ret_val & 0x40) != 0x40); - } -#endif - if (READ_NAND(nandptr) & 1) { - puts ("Error programming oob data\n"); - /* There was an error */ - NAND_DISABLE_CE(nand); /* set pin high */ - *retlen = 0; - return -1; - } - - NAND_DISABLE_CE(nand); /* set pin high */ - *retlen = len; - return 0; - -} + printf ("Usage:\n%s\n", cmdtp->usage); + return 1; + case 3: + if (strcmp (argv[1], "device") == 0) { + int dev = (int) simple_strtoul (argv[2], NULL, 10); -int nand_erase(struct nand_chip* nand, size_t ofs, size_t len, int clean) -{ - /* This is defined as a structure so it will work on any system - * using native endian jffs2 (the default). - */ - static struct jffs2_unknown_node clean_marker = { - JFFS2_MAGIC_BITMASK, - JFFS2_NODETYPE_CLEANMARKER, - 8 /* 8 bytes in this node */ - }; - unsigned long nandptr; - struct Nand *mychip; - int ret = 0; - - if (ofs & (nand->erasesize-1) || len & (nand->erasesize-1)) { - printf ("Offset and size must be sector aligned, erasesize = %d\n", - (int) nand->erasesize); - return -1; - } + printf ("\nDevice %d: ", dev); + if (dev >= CFG_MAX_NAND_DEVICE) { + puts ("unknown device\n"); + return 1; + } + nand_print (&nand_dev_desc[dev]); + /*nand_print (dev); */ - nandptr = nand->IO_ADDR; + if (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN) { + return 1; + } - /* Select the NAND device */ -#ifdef CONFIG_OMAP1510 - archflashwp(0,0); -#endif -#ifdef CFG_NAND_WP - NAND_WP_OFF(); -#endif - NAND_ENABLE_CE(nand); /* set pin low */ - - /* Check the WP bit */ - NanD_Command(nand, NAND_CMD_STATUS); - if (!(READ_NAND(nand->IO_ADDR) & 0x80)) { - printf ("nand_write_ecc: Device is write protected!!!\n"); - ret = -1; - goto out; - } + curr_device = dev; - /* Check the WP bit */ - NanD_Command(nand, NAND_CMD_STATUS); - if (!(READ_NAND(nand->IO_ADDR) & 0x80)) { - printf ("%s: Device is write protected!!!\n", __FUNCTION__); - ret = -1; - goto out; - } + puts ("... is now current device\n"); - /* FIXME: Do nand in the background. Use timers or schedule_task() */ - while(len) { - /*mychip = &nand->chips[shr(ofs, nand->chipshift)];*/ - mychip = &nand->chips[ofs >> nand->chipshift]; + return 0; + } else if (strcmp (argv[1], "erase") == 0 + && strcmp (argv[2], "clean") == 0) { + struct nand_chip *nand = &nand_dev_desc[curr_device]; + ulong off = 0; + ulong size = nand->totlen; + int ret; - /* always check for bad block first, genuine bad blocks - * should _never_ be erased. - */ - if (ALLOW_ERASE_BAD_DEBUG || !check_block(nand, ofs)) { - /* Select the NAND device */ - NAND_ENABLE_CE(nand); /* set pin low */ + printf ("\nNAND erase: device %d offset %ld, size %ld ... ", curr_device, off, size); - NanD_Command(nand, NAND_CMD_ERASE1); - NanD_Address(nand, ADDR_PAGE, ofs); - NanD_Command(nand, NAND_CMD_ERASE2); + ret = nand_legacy_erase (nand, off, size, 1); - NanD_Command(nand, NAND_CMD_STATUS); + printf ("%s\n", ret ? "ERROR" : "OK"); -#ifdef NAND_NO_RB - { u_char ret_val; - do { - ret_val = READ_NAND(nandptr); /* wait till ready */ - } while ((ret_val & 0x40) != 0x40); - } -#endif - if (READ_NAND(nandptr) & 1) { - printf ("%s: Error erasing at 0x%lx\n", - __FUNCTION__, (long)ofs); - /* There was an error */ - ret = -1; - goto out; - } - if (clean) { - int n; /* return value not used */ - int p, l; + return ret; + } - /* clean marker position and size depend - * on the page size, since 256 byte pages - * only have 8 bytes of oob data - */ - if (nand->page256) { - p = NAND_JFFS2_OOB8_FSDAPOS; - l = NAND_JFFS2_OOB8_FSDALEN; + printf ("Usage:\n%s\n", cmdtp->usage); + return 1; + default: + /* at least 4 args */ + + if (strncmp (argv[1], "read", 4) == 0 || + strncmp (argv[1], "write", 5) == 0) { + ulong addr = simple_strtoul (argv[2], NULL, 16); + ulong off = simple_strtoul (argv[3], NULL, 16); + ulong size = simple_strtoul (argv[4], NULL, 16); + int cmd = (strncmp (argv[1], "read", 4) == 0) ? + NANDRW_READ : NANDRW_WRITE; + int ret, total; + char *cmdtail = strchr (argv[1], '.'); + + if (cmdtail && !strncmp (cmdtail, ".oob", 2)) { + /* read out-of-band data */ + if (cmd & NANDRW_READ) { + ret = nand_read_oob (nand_dev_desc + curr_device, + off, size, (size_t *) & total, + (u_char *) addr); } else { - p = NAND_JFFS2_OOB16_FSDAPOS; - l = NAND_JFFS2_OOB16_FSDALEN; + ret = nand_write_oob (nand_dev_desc + curr_device, + off, size, (size_t *) & total, + (u_char *) addr); } - - ret = nand_write_oob(nand, ofs + p, l, &n, - (u_char *)&clean_marker); - /* quit here if write failed */ - if (ret) - goto out; + return ret; + } else if (cmdtail && !strncmp (cmdtail, ".jffs2", 2)) + cmd |= NANDRW_JFFS2; /* skip bad blocks */ + else if (cmdtail && !strncmp (cmdtail, ".jffs2s", 2)) { + cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ + if (cmd & NANDRW_READ) + cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ } - } - ofs += nand->erasesize; - len -= nand->erasesize; - } - -out: - /* De-select the NAND device */ - NAND_DISABLE_CE(nand); /* set pin high */ -#ifdef CONFIG_OMAP1510 - archflashwp(0,1); -#endif -#ifdef CFG_NAND_WP - NAND_WP_ON(); +#ifdef SXNI855T + /* need ".e" same as ".j" for compatibility with older units */ + else if (cmdtail && !strcmp (cmdtail, ".e")) + cmd |= NANDRW_JFFS2; /* skip bad blocks */ #endif +#ifdef CFG_NAND_SKIP_BAD_DOT_I + /* need ".i" same as ".jffs2s" for compatibility with older units (esd) */ + /* ".i" for image -> read skips bad block (no 0xff) */ + else if (cmdtail && !strcmp (cmdtail, ".i")) { + cmd |= NANDRW_JFFS2; /* skip bad blocks (on read too) */ + if (cmd & NANDRW_READ) + cmd |= NANDRW_JFFS2_SKIP; /* skip bad blocks (on read too) */ + } +#endif /* CFG_NAND_SKIP_BAD_DOT_I */ + else if (cmdtail) { + printf ("Usage:\n%s\n", cmdtp->usage); + return 1; + } - return ret; -} + printf ("\nNAND %s: device %d offset %ld, size %ld ...\n", + (cmd & NANDRW_READ) ? "read" : "write", + curr_device, off, size); -static inline int nandcheck(unsigned long potential, unsigned long physadr) -{ - return 0; -} + ret = nand_legacy_rw (nand_dev_desc + curr_device, + cmd, off, size, + (size_t *) & total, + (u_char *) addr); -unsigned long nand_probe(unsigned long physadr) -{ - struct nand_chip *nand = NULL; - int i = 0, ChipID = 1; - -#ifdef CONFIG_MTD_NAND_ECC_JFFS2 - oob_config.ecc_pos[0] = NAND_JFFS2_OOB_ECCPOS0; - oob_config.ecc_pos[1] = NAND_JFFS2_OOB_ECCPOS1; - oob_config.ecc_pos[2] = NAND_JFFS2_OOB_ECCPOS2; - oob_config.ecc_pos[3] = NAND_JFFS2_OOB_ECCPOS3; - oob_config.ecc_pos[4] = NAND_JFFS2_OOB_ECCPOS4; - oob_config.ecc_pos[5] = NAND_JFFS2_OOB_ECCPOS5; - oob_config.eccvalid_pos = 4; -#else - oob_config.ecc_pos[0] = NAND_NOOB_ECCPOS0; - oob_config.ecc_pos[1] = NAND_NOOB_ECCPOS1; - oob_config.ecc_pos[2] = NAND_NOOB_ECCPOS2; - oob_config.ecc_pos[3] = NAND_NOOB_ECCPOS3; - oob_config.ecc_pos[4] = NAND_NOOB_ECCPOS4; - oob_config.ecc_pos[5] = NAND_NOOB_ECCPOS5; - oob_config.eccvalid_pos = NAND_NOOB_ECCVPOS; -#endif - oob_config.badblock_pos = 5; + printf (" %d bytes %s: %s\n", total, + (cmd & NANDRW_READ) ? "read" : "written", + ret ? "ERROR" : "OK"); - for (i=0; iIO_ADDR = physadr; - nand->cache_page = -1; /* init the cache page */ - NanD_ScanChips(nand); + ret = nand_legacy_erase (nand_dev_desc + curr_device, + off, size, clean); - if (nand->totlen == 0) { - /* no chips found, clean up and quit */ - memset((char *)nand, 0, sizeof(struct nand_chip)); - nand->ChipID = NAND_ChipID_UNKNOWN; - return (0); - } + printf ("%s\n", ret ? "ERROR" : "OK"); - nand->ChipID = ChipID; - if (curr_device == -1) - curr_device = i; + return ret; + } else { + printf ("Usage:\n%s\n", cmdtp->usage); + rcode = 1; + } - nand->data_buf = malloc (nand->oobblock + nand->oobsize); - if (!nand->data_buf) { - puts ("Cannot allocate memory for data structures.\n"); - return (0); + return rcode; } - - return (nand->totlen); } -#ifdef CONFIG_MTD_NAND_ECC -/* - * Pre-calculated 256-way 1 byte column parity - */ -static const u_char nand_ecc_precalc_table[] = { - 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, - 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, - 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, - 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, - 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, - 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, - 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, - 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, - 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, - 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, - 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, - 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, - 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, - 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, - 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, - 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, - 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, - 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, - 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, - 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, - 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, - 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, - 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, - 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, - 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, - 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, - 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, - 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, - 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, - 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, - 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, - 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 -}; - +U_BOOT_CMD( + nand, 5, 1, do_nand, + "nand - legacy NAND sub-system\n", + "info - show available NAND devices\n" + "nand device [dev] - show or set current device\n" + "nand read[.jffs2[s]] addr off size\n" + "nand write[.jffs2] addr off 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" + "nand bad - show bad blocks\n" + "nand read.oob addr off size - read out-of-band data\n" + "nand write.oob addr off size - read out-of-band data\n" +); -/* - * Creates non-inverted ECC code from line parity - */ -static void nand_trans_result(u_char reg2, u_char reg3, - u_char *ecc_code) +int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { - u_char a, b, i, tmp1, tmp2; - - /* Initialize variables */ - a = b = 0x80; - tmp1 = tmp2 = 0; - - /* Calculate first ECC byte */ - for (i = 0; i < 4; i++) { - if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */ - tmp1 |= b; - b >>= 1; - if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */ - tmp1 |= b; - b >>= 1; - a >>= 1; + char *boot_device = NULL; + char *ep; + int dev; + ulong cnt; + ulong addr; + ulong offset = 0; + image_header_t *hdr; + int rcode = 0; + show_boot_progress (52); + 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 (-53); + return 1; } - /* Calculate second ECC byte */ - b = 0x80; - for (i = 0; i < 4; i++) { - if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */ - tmp2 |= b; - b >>= 1; - if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */ - tmp2 |= b; - b >>= 1; - a >>= 1; + show_boot_progress (53); + if (!boot_device) { + puts ("\n** No boot device **\n"); + show_boot_progress (-54); + return 1; } + show_boot_progress (54); - /* Store two of the ECC bytes */ - ecc_code[0] = tmp1; - ecc_code[1] = tmp2; -} - -/* - * Calculate 3 byte ECC code for 256 byte block - */ -static void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) -{ - u_char idx, reg1, reg3; - int j; - - /* Initialize variables */ - reg1 = reg3 = 0; - ecc_code[0] = ecc_code[1] = ecc_code[2] = 0; + dev = simple_strtoul(boot_device, &ep, 16); - /* Build up column parity */ - for(j = 0; j < 256; j++) { + if ((dev >= CFG_MAX_NAND_DEVICE) || + (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN)) { + printf ("\n** Device %d not available\n", dev); + show_boot_progress (-55); + return 1; + } + show_boot_progress (55); - /* Get CP0 - CP5 from table */ - idx = nand_ecc_precalc_table[dat[j]]; - reg1 ^= idx; + printf ("\nLoading from device %d: %s at 0x%lx (offset 0x%lx)\n", + dev, nand_dev_desc[dev].name, nand_dev_desc[dev].IO_ADDR, + offset); - /* All bit XOR = 1 ? */ - if (idx & 0x40) { - reg3 ^= (u_char) j; - } + if (nand_legacy_rw (nand_dev_desc + dev, NANDRW_READ, offset, + SECTORSIZE, NULL, (u_char *)addr)) { + printf ("** Read error on %d\n", dev); + show_boot_progress (-56); + return 1; } + show_boot_progress (56); - /* Create non-inverted ECC code from line parity */ - nand_trans_result((reg1 & 0x40) ? ~reg3 : reg3, reg3, ecc_code); - - /* Calculate final ECC code */ - ecc_code[0] = ~ecc_code[0]; - ecc_code[1] = ~ecc_code[1]; - ecc_code[2] = ((~reg1) << 2) | 0x03; -} + hdr = (image_header_t *)addr; -/* - * Detect and correct a 1 bit error for 256 byte block - */ -static int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc) -{ - u_char a, b, c, d1, d2, d3, add, bit, i; + if (ntohl(hdr->ih_magic) == IH_MAGIC) { - /* Do error detection */ - d1 = calc_ecc[0] ^ read_ecc[0]; - d2 = calc_ecc[1] ^ read_ecc[1]; - d3 = calc_ecc[2] ^ read_ecc[2]; + print_image_hdr (hdr); - if ((d1 | d2 | d3) == 0) { - /* No errors */ - return 0; + cnt = (ntohl(hdr->ih_size) + sizeof(image_header_t)); + cnt -= SECTORSIZE; } else { - a = (d1 ^ (d1 >> 1)) & 0x55; - b = (d2 ^ (d2 >> 1)) & 0x55; - c = (d3 ^ (d3 >> 1)) & 0x54; - - /* Found and will correct single bit error in the data */ - if ((a == 0x55) && (b == 0x55) && (c == 0x54)) { - c = 0x80; - add = 0; - a = 0x80; - for (i=0; i<4; i++) { - if (d1 & c) - add |= a; - c >>= 2; - a >>= 1; - } - c = 0x80; - for (i=0; i<4; i++) { - if (d2 & c) - add |= a; - c >>= 2; - a >>= 1; - } - bit = 0; - b = 0x04; - c = 0x80; - for (i=0; i<3; i++) { - if (d3 & c) - bit |= b; - c >>= 2; - b >>= 1; - } - b = 0x01; - a = dat[add]; - a ^= (b << bit); - dat[add] = a; - return 1; - } - else { - i = 0; - while (d1) { - if (d1 & 0x01) - ++i; - d1 >>= 1; - } - while (d2) { - if (d2 & 0x01) - ++i; - d2 >>= 1; - } - while (d3) { - if (d3 & 0x01) - ++i; - d3 >>= 1; - } - if (i == 1) { - /* ECC Code Error Correction */ - read_ecc[0] = calc_ecc[0]; - read_ecc[1] = calc_ecc[1]; - read_ecc[2] = calc_ecc[2]; - return 2; - } - else { - /* Uncorrectable Error */ - return -1; - } - } + printf ("\n** Bad Magic Number 0x%x **\n", ntohl(hdr->ih_magic)); + show_boot_progress (-57); + return 1; } + show_boot_progress (57); - /* Should never happen */ - return -1; -} + if (nand_legacy_rw (nand_dev_desc + dev, NANDRW_READ, + offset + SECTORSIZE, cnt, NULL, + (u_char *)(addr+SECTORSIZE))) { + printf ("** Read error on %d\n", dev); + show_boot_progress (-58); + return 1; + } + show_boot_progress (58); -#endif + /* Loading ok, update default load address */ -#ifdef CONFIG_JFFS2_NAND + load_addr = addr; -int read_jffs2_nand(size_t start, size_t len, - size_t * retlen, u_char * buf, int nanddev) -{ - return nand_rw(nand_dev_desc + nanddev, NANDRW_READ | NANDRW_JFFS2, - start, len, retlen, buf); + /* Check if we should attempt an auto-start */ + if (((ep = getenv("autostart")) != NULL) && (strcmp(ep,"yes") == 0)) { + char *local_args[2]; + extern int do_bootm (cmd_tbl_t *, int, int, char *[]); + + local_args[0] = argv[0]; + local_args[1] = NULL; + + printf ("Automatic boot of image at addr 0x%08lx ...\n", addr); + + do_bootm (cmdtp, 0, 1, local_args); + rcode = 1; + } + return rcode; } -#endif /* CONFIG_JFFS2_NAND */ +U_BOOT_CMD( + nboot, 4, 1, do_nandboot, + "nboot - boot from NAND device\n", + "loadAddr dev\n" +); +#endif -#endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */ +#endif /* CFG_NAND_LEGACY */