From 932394ac43e2e778e664eeb6e456fecd0fae6e59 Mon Sep 17 00:00:00 2001 From: Wolfgang Denk Date: Wed, 17 Aug 2005 12:55:25 +0200 Subject: Rewrite of NAND code based on what is in 2.6.12 Linux kernel Patch by Ladislav Michl, 29 Jun 2005 --- CHANGELOG | 7 + common/cmd_nand.c | 2039 +++++--------------------------- drivers/nand/Makefile | 17 + drivers/nand/diskonchip.c | 1782 ++++++++++++++++++++++++++++ drivers/nand/nand.c | 71 ++ drivers/nand/nand_base.c | 2630 +++++++++++++++++++++++++++++++++++++++++ drivers/nand/nand_bbt.c | 1056 +++++++++++++++++ drivers/nand/nand_ecc.c | 244 ++++ drivers/nand/nand_ids.c | 127 ++ fs/jffs2/jffs2_nand_1pass.c | 1045 ++++++++++++++++ fs/jffs2/jffs2_nand_private.h | 133 +++ include/linux/mtd/compat.h | 44 + include/linux/mtd/mtd-abi.h | 99 ++ include/linux/mtd/mtd.h | 214 ++++ include/linux/mtd/nand.h | 503 ++++++-- include/linux/mtd/nand_ecc.h | 30 + include/linux/mtd/nand_ids.h | 1 + include/nand.h | 56 + 18 files changed, 8220 insertions(+), 1878 deletions(-) create mode 100644 drivers/nand/Makefile create mode 100644 drivers/nand/diskonchip.c create mode 100644 drivers/nand/nand.c create mode 100644 drivers/nand/nand_base.c create mode 100644 drivers/nand/nand_bbt.c create mode 100644 drivers/nand/nand_ecc.c create mode 100644 drivers/nand/nand_ids.c create mode 100644 fs/jffs2/jffs2_nand_1pass.c create mode 100644 fs/jffs2/jffs2_nand_private.h create mode 100644 include/linux/mtd/compat.h create mode 100644 include/linux/mtd/mtd-abi.h create mode 100644 include/linux/mtd/mtd.h create mode 100644 include/linux/mtd/nand_ecc.h create mode 100644 include/nand.h diff --git a/CHANGELOG b/CHANGELOG index cd2697abb7..5e7016137a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +====================================================================== +Changes for U-Boot 1.1.4: +====================================================================== + +* Rewrite of NAND code based on what is in 2.6.12 Linux kernel + Patch by Ladislav Michl, 29 Jun 2005 + ====================================================================== Changes for U-Boot 1.1.3: ====================================================================== diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 449991785c..ae4c68ac1f 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -1,18 +1,15 @@ /* - * Driver for NAND support, Rick Bronson - * borrowed heavily from: - * (c) 1999 Machine Vision Holdings, Inc. - * (c) 1999, 2000 David Woodhouse - * - * Added 16-bit nand support - * (C) 2004 Texas Instruments + * Rick Bronson and Pantelis Antoniou */ #include + +#if (CONFIG_COMMANDS & CFG_CMD_NAND) + #include -#include -#include #include +#include +#include #ifdef CONFIG_SHOW_BOOT_PROGRESS # include @@ -21,289 +18,311 @@ # define SHOW_BOOT_PROGRESS(arg) #endif -#if (CONFIG_COMMANDS & CFG_CMD_NAND) - -#include -#include #include +#include -#ifdef CONFIG_OMAP1510 -void archflashwp(void *archdata, int wp); -#endif +extern nand_info_t nand_info[]; /* info for NAND chips */ -#define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) +static int nand_dump_oob(nand_info_t *nand, ulong off) +{ + return 0; +} -/* - * 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}; - -#undef NAND_DEBUG -#undef PSYCHO_DEBUG - -/* ****************** 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 +static int nand_dump(nand_info_t *nand, ulong off) +{ + int i; + u_char *buf, *p; + + 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 void +arg_off_size(int argc, char *argv[], ulong *off, ulong *size, ulong totsize) +{ + *off = 0; + *size = 0; + +#if defined(CONFIG_JFFS2_NAND) && defined(CFG_JFFS_CUSTOM_PART) + if (argc >= 1 && strcmp(argv[0], "partition") == 0) { + int part_num; + struct part_info *part; + const char *partstr; + + if (argc >= 2) + partstr = argv[1]; + else + partstr = getenv("partition"); + + if (partstr) + part_num = (int)simple_strtoul(partstr, NULL, 10); + else + part_num = 0; + + part = jffs2_part_info(part_num); + if (part == NULL) { + printf("\nInvalid partition %d\n", part_num); + return; + } + *size = part->size; + *off = (ulong)part->offset; + } else #endif + { + if (argc >= 1) + *off = (ulong)simple_strtoul(argv[0], NULL, 16); + else + *off = 0; -struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE] = {{0}}; + if (argc >= 2) + *size = (ulong)simple_strtoul(argv[1], NULL, 16); + else + *size = totsize - *off; -/* Current NAND Device */ -static int curr_device = -1; + } -/* ------------------------------------------------------------------------- */ +} -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; - 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'); + cmd = argv[1]; - for (i=0; i= CFG_MAX_NAND_DEVICE)) { - puts ("\nno devices available\n"); - return 1; + putc('\n'); + for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) { + if (nand_info[i].name) + printf("Device %d: %s\n", i, nand_info[i].name); } - printf ("\nDevice %d: ", curr_device); - nand_print(&nand_dev_desc[curr_device]); return 0; + } - } else if (strcmp(argv[1],"bad") == 0) { - if ((curr_device < 0) || (curr_device >= CFG_MAX_NAND_DEVICE)) { - puts ("\nno devices available\n"); + if (strcmp(cmd, "device") == 0) { + + 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; 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 +#ifdef CONFIG_MTD_NAND_UNSAFE + && strcmp(cmd, "scrub") != 0 && strcmp(cmd, "biterr") != 0 + && strcmp(cmd, "markbad") != 0 +#endif + ) + 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 (strcmp(cmd, "erase") == 0 +#ifdef CONFIG_MTD_NAND_UNSAFE + || strcmp(cmd, "scrub") == 0 +#endif + ) { - if (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN) { +#ifdef CONFIG_MTD_NAND_UNSAFE + i = strcmp(cmd, "scrub") == 0; /* 1 scrub, 0 = erase */ +#endif + + arg_off_size(argc - 2, argv + 2, &off, &size, nand->size); + if (off == 0 && size == 0) return 1; - } - curr_device = dev; + printf("\nNAND %s: device %d offset 0x%x, size 0x%x ", +#ifdef CONFIG_MTD_NAND_UNSAFE + i ? "scrub" : +#endif + "erase", + nand_curr_device, off, size); - puts ("... is now current device\n"); +#ifdef CONFIG_MTD_NAND_UNSAFE + if (i) + ret = nand_scrub(nand, off, size); + else +#endif + ret = nand_erase(nand, off, size); - return 0; + printf("%s\n", ret ? "ERROR" : "OK"); + + 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], '.'); - - 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); - } - 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) */ -#endif /* CFG_NAND_SKIP_BAD_DOT_I */ - else if (cmdtail) { - printf ("Usage:\n%s\n", cmdtp->usage); + /* read write */ + if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { + if (argc < 4) + goto usage; +/* + s = strchr(cmd, '.'); + clean = CLEAN_NONE; + if (s != NULL) { + if (strcmp(s, ".jffs2") == 0 || strcmp(s, ".e") == 0 + || strcmp(s, ".i")) + clean = CLEAN_JFFS2; + } +*/ + addr = (ulong)simple_strtoul(argv[2], NULL, 16); + + arg_off_size(argc - 3, argv + 3, &off, &size, nand->size); + if (off == 0 && size == 0) return 1; - } - printf ("\nNAND %s: device %d offset %ld, size %ld ... ", - (cmd & NANDRW_READ) ? "read" : "write", - curr_device, off, size); + i = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ + printf("\nNAND %s: device %d offset %u, size %u ... ", + i ? "read" : "write", nand_curr_device, off, size); - ret = nand_rw(nand_dev_desc + curr_device, cmd, off, size, - &total, (u_char*)addr); + if (i) + ret = nand_read(nand, off, &size, (u_char *)addr); + else + ret = nand_write(nand, off, &size, (u_char *)addr); - printf (" %d bytes %s: %s\n", total, - (cmd & NANDRW_READ) ? "read" : "written", - ret ? "ERROR" : "OK"); + printf(" %d bytes %s: %s\n", size, + i ? "read" : "written", ret ? "ERROR" : "OK"); - 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; + return ret == 0 ? 0 : 1; + } +#ifdef CONFIG_MTD_NAND_UNSAFE + if (strcmp(cmd, "markbad") == 0 || strcmp(cmd, "biterr") == 0) { + if (argc < 3) + goto usage; - printf ("\nNAND erase: device %d offset %ld, size %ld ... ", - curr_device, off, size); + i = strcmp(cmd, "biterr") == 0; - ret = nand_erase (nand_dev_desc + curr_device, off, size, clean); + off = (int)simple_strtoul(argv[2], NULL, 16); - printf("%s\n", ret ? "ERROR" : "OK"); + if (i) + ret = nand_make_bit_error(nand, off); + else + ret = nand_mark_bad(nand, off); - return ret; - } else { - printf ("Usage:\n%s\n", cmdtp->usage); - rcode = 1; + return ret == 0 ? 0 : 1; } +#endif - 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 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" -); + "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"); -int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) { char *boot_device = NULL; char *ep; int dev; - ulong cnt; - ulong addr; - ulong offset = 0; + int r; + ulong addr, cnt, offset = 0; image_header_t *hdr; - int rcode = 0; + nand_info_t *nand; + 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); @@ -315,55 +334,53 @@ 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); + printf("Usage:\n%s\n", cmdtp->usage); + SHOW_BOOT_PROGRESS(-1); return 1; } if (!boot_device) { - puts ("\n** No boot device **\n"); - SHOW_BOOT_PROGRESS (-1); + puts("\n** No boot device **\n"); + SHOW_BOOT_PROGRESS(-1); return 1; } dev = simple_strtoul(boot_device, &ep, 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 (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) { + printf("\n** Device %d not available\n", dev); + SHOW_BOOT_PROGRESS(-1); return 1; } - 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); + nand = &nand_info[dev]; + printf("\nLoading from device %d: %s (offset 0x%lx)\n", + dev, nand->name, offset); - 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); + cnt = nand->oobblock; + r = nand_read(nand, offset, &cnt, (u_char *) addr); + if (r) { + printf("** Read error on %d\n", dev); + SHOW_BOOT_PROGRESS(-1); return 1; } - hdr = (image_header_t *)addr; - - if (ntohl(hdr->ih_magic) == IH_MAGIC) { + hdr = (image_header_t *) addr; - 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); + if (ntohl(hdr->ih_magic) != IH_MAGIC) { + 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); + print_image_hdr(hdr); + + cnt = (ntohl(hdr->ih_size) + sizeof (image_header_t)); + + r = nand_read(nand, offset, &cnt, (u_char *) addr); + if (r) { + printf("** Read error on %d\n", dev); + SHOW_BOOT_PROGRESS(-1); return 1; } @@ -372,1526 +389,24 @@ int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) load_addr = addr; /* Check if we should attempt an auto-start */ - if (((ep = getenv("autostart")) != NULL) && (strcmp(ep,"yes") == 0)) { + if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) { char *local_args[2]; - extern int do_bootm (cmd_tbl_t *, int, int, char *[]); + 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" -); + printf("Automatic boot of image at addr 0x%08lx ...\n", addr); -/* 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) + do_bootm(cmdtp, 0, 1, local_args); 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(); -#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); - - NanD_Command(nand, NAND_CMD_READOOB); - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs & (~0x1ff)); - } - - NanD_ReadBuf(nand, &buf[len256], len - len256); - - *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 */ - - ret = NanD_WaitReady(nand, 1); - NAND_DISABLE_CE(nand); /* set pin high */ - - return ret; - -} - -/* write to the 16 bytes of oob data that correspond to a 512 byte - * page or 2 256-byte pages. - */ -static int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, - size_t * retlen, const u_char * buf) -{ - 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 */ - - /* Reset the chip */ - NanD_Command(nand, NAND_CMD_RESET); - - /* 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); - } - - /* 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; - } - - /* 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); - } - - /* 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)); - } - - 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; - -} - -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; - } - - nandptr = nand->IO_ADDR; - - /* 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; - } - - /* 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; - } - - /* 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]; - - /* 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 */ - - NanD_Command(nand, NAND_CMD_ERASE1); - NanD_Address(nand, ADDR_PAGE, ofs); - NanD_Command(nand, NAND_CMD_ERASE2); - - 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) { - 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; - - /* 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; - } else { - p = NAND_JFFS2_OOB16_FSDAPOS; - l = NAND_JFFS2_OOB16_FSDALEN; - } - - ret = nand_write_oob(nand, ofs + p, l, &n, - (u_char *)&clean_marker); - /* quit here if write failed */ - if (ret) - goto out; - } - } - 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(); -#endif - - return ret; -} - -static inline int nandcheck(unsigned long potential, unsigned long physadr) -{ - return 0; -} - -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; - - for (i=0; iIO_ADDR = physadr; - nand->cache_page = -1; /* init the cache page */ - NanD_ScanChips(nand); - - 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); - } - - nand->ChipID = ChipID; - if (curr_device == -1) - curr_device = i; - - nand->data_buf = malloc (nand->oobblock + nand->oobsize); - if (!nand->data_buf) { - puts ("Cannot allocate memory for data structures.\n"); - return (0); - } - - 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 -}; - - -/* - * Creates non-inverted ECC code from line parity - */ -static void nand_trans_result(u_char reg2, u_char reg3, - u_char *ecc_code) -{ - 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; - } - - /* 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; - } - - /* 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; - - /* Build up column parity */ - for(j = 0; j < 256; j++) { - - /* Get CP0 - CP5 from table */ - idx = nand_ecc_precalc_table[dat[j]]; - reg1 ^= idx; - - /* All bit XOR = 1 ? */ - if (idx & 0x40) { - reg3 ^= (u_char) j; - } - } - - /* 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; -} - -/* - * 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; - - /* Do error detection */ - d1 = calc_ecc[0] ^ read_ecc[0]; - d2 = calc_ecc[1] ^ read_ecc[1]; - d3 = calc_ecc[2] ^ read_ecc[2]; - - if ((d1 | d2 | d3) == 0) { - /* No errors */ - return 0; - } 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; - } - } - } - - /* Should never happen */ - return -1; -} - -#endif - -#ifdef CONFIG_JFFS2_NAND - -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); -} +U_BOOT_CMD(nboot, 4, 1, do_nandboot, + "nboot - boot from NAND device\n", "loadAddr dev\n"); -#endif /* CONFIG_JFFS2_NAND */ +#endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */ -#endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */ diff --git a/drivers/nand/Makefile b/drivers/nand/Makefile new file mode 100644 index 0000000000..3906bf951d --- /dev/null +++ b/drivers/nand/Makefile @@ -0,0 +1,17 @@ +include $(TOPDIR)/config.mk + +LIB := libnand.a + +OBJS := nand.o nand_base.o nand_ids.o nand_ecc.o nand_bbt.o +all: $(LIB) + +$(LIB): $(OBJS) + $(AR) crv $@ $(OBJS) + +######################################################################### + +.depend: Makefile $(OBJS:.o=.c) + $(CC) -M $(CFLAGS) $(OBJS:.o=.c) > $@ + +sinclude .depend + diff --git a/drivers/nand/diskonchip.c b/drivers/nand/diskonchip.c new file mode 100644 index 0000000000..02135c3ac2 --- /dev/null +++ b/drivers/nand/diskonchip.c @@ -0,0 +1,1782 @@ +/* + * drivers/mtd/nand/diskonchip.c + * + * (C) 2003 Red Hat, Inc. + * (C) 2004 Dan Brown + * (C) 2004 Kalev Lember + * + * Author: David Woodhouse + * Additional Diskonchip 2000 and Millennium support by Dan Brown + * Diskonchip Millennium Plus support by Kalev Lember + * + * Error correction code lifted from the old docecc code + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * converted to the generic Reed-Solomon library by Thomas Gleixner + * + * Interface to generic NAND code for M-Systems DiskOnChip devices + * + * $Id: diskonchip.c,v 1.45 2005/01/05 18:05:14 dwmw2 Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* Where to look for the devices? */ +#ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS +#define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0 +#endif + +static unsigned long __initdata doc_locations[] = { +#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) +#ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH + 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, + 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, + 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, + 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, + 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000, +#else /* CONFIG_MTD_DOCPROBE_HIGH */ + 0xc8000, 0xca000, 0xcc000, 0xce000, + 0xd0000, 0xd2000, 0xd4000, 0xd6000, + 0xd8000, 0xda000, 0xdc000, 0xde000, + 0xe0000, 0xe2000, 0xe4000, 0xe6000, + 0xe8000, 0xea000, 0xec000, 0xee000, +#endif /* CONFIG_MTD_DOCPROBE_HIGH */ +#elif defined(__PPC__) + 0xe4000000, +#elif defined(CONFIG_MOMENCO_OCELOT) + 0x2f000000, + 0xff000000, +#elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C) + 0xff000000, +##else +#warning Unknown architecture for DiskOnChip. No default probe locations defined +#endif + 0xffffffff }; + +static struct mtd_info *doclist = NULL; + +struct doc_priv { + void __iomem *virtadr; + unsigned long physadr; + u_char ChipID; + u_char CDSNControl; + int chips_per_floor; /* The number of chips detected on each floor */ + int curfloor; + int curchip; + int mh0_page; + int mh1_page; + struct mtd_info *nextdoc; +}; + +/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL + MediaHeader. The spec says to just keep going, I think, but that's just + silly. */ +#define MAX_MEDIAHEADER_SCAN 8 + +/* This is the syndrome computed by the HW ecc generator upon reading an empty + page, one with all 0xff for data and stored ecc code. */ +static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a }; +/* This is the ecc value computed by the HW ecc generator upon writing an empty + page, one with all 0xff for data. */ +static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; + +#define INFTL_BBT_RESERVED_BLOCKS 4 + +#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32) +#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) +#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) + +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd); +static void doc200x_select_chip(struct mtd_info *mtd, int chip); + +static int debug=0; +module_param(debug, int, 0); + +static int try_dword=1; +module_param(try_dword, int, 0); + +static int no_ecc_failures=0; +module_param(no_ecc_failures, int, 0); + +#ifdef CONFIG_MTD_PARTITIONS +static int no_autopart=0; +module_param(no_autopart, int, 0); +#endif + +#ifdef MTD_NAND_DISKONCHIP_BBTWRITE +static int inftl_bbt_write=1; +#else +static int inftl_bbt_write=0; +#endif +module_param(inftl_bbt_write, int, 0); + +static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS; +module_param(doc_config_location, ulong, 0); +MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); + + +/* Sector size for HW ECC */ +#define SECTOR_SIZE 512 +/* The sector bytes are packed into NB_DATA 10 bit words */ +#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10) +/* Number of roots */ +#define NROOTS 4 +/* First consective root */ +#define FCR 510 +/* Number of symbols */ +#define NN 1023 + +/* the Reed Solomon control structure */ +static struct rs_control *rs_decoder; + +/* + * The HW decoder in the DoC ASIC's provides us a error syndrome, + * which we must convert to a standard syndrom usable by the generic + * Reed-Solomon library code. + * + * Fabrice Bellard figured this out in the old docecc code. I added + * some comments, improved a minor bit and converted it to make use + * of the generic Reed-Solomon libary. tglx + */ +static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc) +{ + int i, j, nerr, errpos[8]; + uint8_t parity; + uint16_t ds[4], s[5], tmp, errval[8], syn[4]; + + /* Convert the ecc bytes into words */ + ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8); + ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6); + ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4); + ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2); + parity = ecc[1]; + + /* Initialize the syndrom buffer */ + for (i = 0; i < NROOTS; i++) + s[i] = ds[0]; + /* + * Evaluate + * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0] + * where x = alpha^(FCR + i) + */ + for(j = 1; j < NROOTS; j++) { + if(ds[j] == 0) + continue; + tmp = rs->index_of[ds[j]]; + for(i = 0; i < NROOTS; i++) + s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)]; + } + + /* Calc s[i] = s[i] / alpha^(v + i) */ + for (i = 0; i < NROOTS; i++) { + if (syn[i]) + syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i)); + } + /* Call the decoder library */ + nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval); + + /* Incorrectable errors ? */ + if (nerr < 0) + return nerr; + + /* + * Correct the errors. The bitpositions are a bit of magic, + * but they are given by the design of the de/encoder circuit + * in the DoC ASIC's. + */ + for(i = 0;i < nerr; i++) { + int index, bitpos, pos = 1015 - errpos[i]; + uint8_t val; + if (pos >= NB_DATA && pos < 1019) + continue; + if (pos < NB_DATA) { + /* extract bit position (MSB first) */ + pos = 10 * (NB_DATA - 1 - pos) - 6; + /* now correct the following 10 bits. At most two bytes + can be modified since pos is even */ + index = (pos >> 3) ^ 1; + bitpos = pos & 7; + if ((index >= 0 && index < SECTOR_SIZE) || + index == (SECTOR_SIZE + 1)) { + val = (uint8_t) (errval[i] >> (2 + bitpos)); + parity ^= val; + if (index < SECTOR_SIZE) + data[index] ^= val; + } + index = ((pos >> 3) + 1) ^ 1; + bitpos = (bitpos + 10) & 7; + if (bitpos == 0) + bitpos = 8; + if ((index >= 0 && index < SECTOR_SIZE) || + index == (SECTOR_SIZE + 1)) { + val = (uint8_t)(errval[i] << (8 - bitpos)); + parity ^= val; + if (index < SECTOR_SIZE) + data[index] ^= val; + } + } + } + /* If the parity is wrong, no rescue possible */ + return parity ? -1 : nerr; +} + +static void DoC_Delay(struct doc_priv *doc, unsigned short cycles) +{ + volatile char dummy; + int i; + + for (i = 0; i < cycles; i++) { + if (DoC_is_Millennium(doc)) + dummy = ReadDOC(doc->virtadr, NOP); + else if (DoC_is_MillenniumPlus(doc)) + dummy = ReadDOC(doc->virtadr, Mplus_NOP); + else + dummy = ReadDOC(doc->virtadr, DOCStatus); + } + +} + +#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) + +/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ +static int _DoC_WaitReady(struct doc_priv *doc) +{ + void __iomem *docptr = doc->virtadr; + unsigned long timeo = jiffies + (HZ * 10); + + if(debug) printk("_DoC_WaitReady...\n"); + /* Out-of-line routine to wait for chip response */ + if (DoC_is_MillenniumPlus(doc)) { + while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { + if (time_after(jiffies, timeo)) { + printk("_DoC_WaitReady timed out.\n"); + return -EIO; + } + udelay(1); + cond_resched(); + } + } else { + while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { + if (time_after(jiffies, timeo)) { + printk("_DoC_WaitReady timed out.\n"); + return -EIO; + } + udelay(1); + cond_resched(); + } + } + + return 0; +} + +static inline int DoC_WaitReady(struct doc_priv *doc) +{ + void __iomem *docptr = doc->virtadr; + int ret = 0; + + if (DoC_is_MillenniumPlus(doc)) { + DoC_Delay(doc, 4); + + if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) + /* Call the out-of-line routine to wait */ + ret = _DoC_WaitReady(doc); + } else { + DoC_Delay(doc, 4); + + if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) + /* Call the out-of-line routine to wait */ + ret = _DoC_WaitReady(doc); + DoC_Delay(doc, 2); + } + + if(debug) printk("DoC_WaitReady OK\n"); + return ret; +} + +static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + if(debug)printk("write_byte %02x\n", datum); + WriteDOC(datum, docptr, CDSNSlowIO); + WriteDOC(datum, docptr, 2k_CDSN_IO); +} + +static u_char doc2000_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + u_char ret; + + ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(doc, 2); + ret = ReadDOC(docptr, 2k_CDSN_IO); + if (debug) printk("read_byte returns %02x\n", ret); + return ret; +} + +static void doc2000_writebuf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + if (debug)printk("writebuf of %d bytes: ", len); + for (i=0; i < len; i++) { + WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); + if (debug && i < 16) + printk("%02x ", buf[i]); + } + if (debug) printk("\n"); +} + +static void doc2000_readbuf(struct mtd_info *mtd, + u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug)printk("readbuf of %d bytes: ", len); + + for (i=0; i < len; i++) { + buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); + } +} + +static void doc2000_readbuf_dword(struct mtd_info *mtd, + u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug) printk("readbuf_dword of %d bytes: ", len); + + if (unlikely((((unsigned long)buf)|len) & 3)) { + for (i=0; i < len; i++) { + *(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); + } + } else { + for (i=0; i < len; i+=4) { + *(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); + } + } +} + +static int doc2000_verifybuf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + for (i=0; i < len; i++) + if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO)) + return -EFAULT; + return 0; +} + +static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + uint16_t ret; + + doc200x_select_chip(mtd, nr); + doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); + this->write_byte(mtd, NAND_CMD_READID); + doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); + doc200x_hwcontrol(mtd, NAND_CTL_SETALE); + this->write_byte(mtd, 0); + doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); + + ret = this->read_byte(mtd) << 8; + ret |= this->read_byte(mtd); + + if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) { + /* First chip probe. See if we get same results by 32-bit access */ + union { + uint32_t dword; + uint8_t byte[4]; + } ident; + void __iomem *docptr = doc->virtadr; + + doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); + doc2000_write_byte(mtd, NAND_CMD_READID); + doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); + doc200x_hwcontrol(mtd, NAND_CTL_SETALE); + doc2000_write_byte(mtd, 0); + doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); + + ident.dword = readl(docptr + DoC_2k_CDSN_IO); + if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { + printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); + this->read_buf = &doc2000_readbuf_dword; + } + } + + return ret; +} + +static void __init doc2000_count_chips(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + uint16_t mfrid; + int i; + + /* Max 4 chips per floor on DiskOnChip 2000 */ + doc->chips_per_floor = 4; + + /* Find out what the first chip is */ + mfrid = doc200x_ident_chip(mtd, 0); + + /* Find how many chips in each floor. */ + for (i = 1; i < 4; i++) { + if (doc200x_ident_chip(mtd, i) != mfrid) + break; + } + doc->chips_per_floor = i; + printk(KERN_DEBUG "Detected %d chips per floor.\n", i); +} + +static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +{ + struct doc_priv *doc = this->priv; + + int status; + + DoC_WaitReady(doc); + this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + DoC_WaitReady(doc); + status = (int)this->read_byte(mtd); + + return status; +} + +static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + WriteDOC(datum, docptr, CDSNSlowIO); + WriteDOC(datum, docptr, Mil_CDSN_IO); + WriteDOC(datum, docptr, WritePipeTerm); +} + +static u_char doc2001_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + //ReadDOC(docptr, CDSNSlowIO); + /* 11.4.5 -- delay twice to allow extended length cycle */ + DoC_Delay(doc, 2); + ReadDOC(docptr, ReadPipeInit); + //return ReadDOC(docptr, Mil_CDSN_IO); + return ReadDOC(docptr, LastDataRead); +} + +static void doc2001_writebuf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + for (i=0; i < len; i++) + WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); + /* Terminate write pipeline */ + WriteDOC(0x00, docptr, WritePipeTerm); +} + +static void doc2001_readbuf(struct mtd_info *mtd, + u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + /* Start read pipeline */ + ReadDOC(docptr, ReadPipeInit); + + for (i=0; i < len-1; i++) + buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff)); + + /* Terminate read pipeline */ + buf[i] = ReadDOC(docptr, LastDataRead); +} + +static int doc2001_verifybuf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + /* Start read pipeline */ + ReadDOC(docptr, ReadPipeInit); + + for (i=0; i < len-1; i++) + if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { + ReadDOC(docptr, LastDataRead); + return i; + } + if (buf[i] != ReadDOC(docptr, LastDataRead)) + return i; + return 0; +} + +static u_char doc2001plus_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + u_char ret; + + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + ret = ReadDOC(docptr, Mplus_LastDataRead); + if (debug) printk("read_byte returns %02x\n", ret); + return ret; +} + +static void doc2001plus_writebuf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug)printk("writebuf of %d bytes: ", len); + for (i=0; i < len; i++) { + WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); + if (debug && i < 16) + printk("%02x ", buf[i]); + } + if (debug) printk("\n"); +} + +static void doc2001plus_readbuf(struct mtd_info *mtd, + u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug)printk("readbuf of %d bytes: ", len); + + /* Start read pipeline */ + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + + for (i=0; i < len-2; i++) { + buf[i] = ReadDOC(docptr, Mil_CDSN_IO); + if (debug && i < 16) + printk("%02x ", buf[i]); + } + + /* Terminate read pipeline */ + buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead); + if (debug && i < 16) + printk("%02x ", buf[len-2]); + buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead); + if (debug && i < 16) + printk("%02x ", buf[len-1]); + if (debug) printk("\n"); +} + +static int doc2001plus_verifybuf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug)printk("verifybuf of %d bytes: ", len); + + /* Start read pipeline */ + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + + for (i=0; i < len-2; i++) + if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { + ReadDOC(docptr, Mplus_LastDataRead); + ReadDOC(docptr, Mplus_LastDataRead); + return i; + } + if (buf[len-2] != ReadDOC(docptr, Mplus_LastDataRead)) + return len-2; + if (buf[len-1] != ReadDOC(docptr, Mplus_LastDataRead)) + return len-1; + return 0; +} + +static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int floor = 0; + + if(debug)printk("select chip (%d)\n", chip); + + if (chip == -1) { + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + return; + } + + floor = chip / doc->chips_per_floor; + chip -= (floor * doc->chips_per_floor); + + /* Assert ChipEnable and deassert WriteProtect */ + WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect); + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + doc->curchip = chip; + doc->curfloor = floor; +} + +static void doc200x_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int floor = 0; + + if(debug)printk("select chip (%d)\n", chip); + + if (chip == -1) + return; + + floor = chip / doc->chips_per_floor; + chip -= (floor * doc->chips_per_floor); + + /* 11.4.4 -- deassert CE before changing chip */ + doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE); + + WriteDOC(floor, docptr, FloorSelect); + WriteDOC(chip, docptr, CDSNDeviceSelect); + + doc200x_hwcontrol(mtd, NAND_CTL_SETNCE); + + doc->curchip = chip; + doc->curfloor = floor; +} + +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + switch(cmd) { + case NAND_CTL_SETNCE: + doc->CDSNControl |= CDSN_CTRL_CE; + break; + case NAND_CTL_CLRNCE: + doc->CDSNControl &= ~CDSN_CTRL_CE; + break; + case NAND_CTL_SETCLE: + doc->CDSNControl |= CDSN_CTRL_CLE; + break; + case NAND_CTL_CLRCLE: + doc->CDSNControl &= ~CDSN_CTRL_CLE; + break; + case NAND_CTL_SETALE: + doc->CDSNControl |= CDSN_CTRL_ALE; + break; + case NAND_CTL_CLRALE: + doc->CDSNControl &= ~CDSN_CTRL_ALE; + break; + case NAND_CTL_SETWP: + doc->CDSNControl |= CDSN_CTRL_WP; + break; + case NAND_CTL_CLRWP: + doc->CDSNControl &= ~CDSN_CTRL_WP; + break; + } + if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); + WriteDOC(doc->CDSNControl, docptr, CDSNControl); + /* 11.4.3 -- 4 NOPs after CSDNControl write */ + DoC_Delay(doc, 4); +} + +static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + /* + * Must terminate write pipeline before sending any commands + * to the device. + */ + if (command == NAND_CMD_PAGEPROG) { + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + } + + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->oobblock) { + /* OOB area */ + column -= mtd->oobblock; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + WriteDOC(readcmd, docptr, Mplus_FlashCmd); + } + WriteDOC(command, docptr, Mplus_FlashCmd); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + + if (column != -1 || page_addr != -1) { + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16) + column >>= 1; + WriteDOC(column, docptr, Mplus_FlashAddress); + } + if (page_addr != -1) { + WriteDOC((unsigned char) (page_addr & 0xff), docptr, Mplus_FlashAddress); + WriteDOC((unsigned char) ((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress); + /* One more address cycle for higher density devices */ + if (this->chipsize & 0x0c000000) { + WriteDOC((unsigned char) ((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress); + printk("high density\n"); + } + } + WriteDOC(0, docptr, Mplus_WritePipeTerm); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + /* deassert ALE */ + if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || command == NAND_CMD_READOOB || command == NAND_CMD_READID) + WriteDOC(0, docptr, Mplus_FlashControl); + } + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + udelay(this->chip_delay); + WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + while ( !(this->read_byte(mtd) & 0x40)); + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay (this->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay (100); + /* wait until command is processed */ + while (!this->dev_ready(mtd)); +} + +static int doc200x_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + if (DoC_is_MillenniumPlus(doc)) { + /* 11.4.2 -- must NOP four times before checking FR/B# */ + DoC_Delay(doc, 4); + if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { + if(debug) + printk("not ready\n"); + return 0; + } + if (debug)printk("was ready\n"); + return 1; + } else { + /* 11.4.2 -- must NOP four times before checking FR/B# */ + DoC_Delay(doc, 4); + if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { + if(debug) + printk("not ready\n"); + return 0; + } + /* 11.4.2 -- Must NOP twice if it's ready */ + DoC_Delay(doc, 2); + if (debug)printk("was ready\n"); + return 1; + } +} + +static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + /* This is our last resort if we couldn't find or create a BBT. Just + pretend all blocks are good. */ + return 0; +} + +static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + /* Prime the ECC engine */ + switch(mode) { + case NAND_ECC_READ: + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN, docptr, ECCConf); + break; + case NAND_ECC_WRITE: + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); + break; + } +} + +static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + /* Prime the ECC engine */ + switch(mode) { + case NAND_ECC_READ: + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); + break; + case NAND_ECC_WRITE: + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); + break; + } +} + +/* This code is only called on write */ +static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + unsigned char *ecc_code) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + int emptymatch = 1; + + /* flush the pipeline */ + if (DoC_is_2000(doc)) { + WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl); + WriteDOC(0, docptr, 2k_CDSN_IO); + WriteDOC(0, docptr, 2k_CDSN_IO); + WriteDOC(0, docptr, 2k_CDSN_IO); + WriteDOC(doc->CDSNControl, docptr, CDSNControl); + } else if (DoC_is_MillenniumPlus(doc)) { + WriteDOC(0, docptr, Mplus_NOP); + WriteDOC(0, docptr, Mplus_NOP); + WriteDOC(0, docptr, Mplus_NOP); + } else { + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + } + + for (i = 0; i < 6; i++) { + if (DoC_is_MillenniumPlus(doc)) + ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); + else + ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); + if (ecc_code[i] != empty_write_ecc[i]) + emptymatch = 0; + } + if (DoC_is_MillenniumPlus(doc)) + WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); + else + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); +#if 0 + /* If emptymatch=1, we might have an all-0xff data buffer. Check. */ + if (emptymatch) { + /* Note: this somewhat expensive test should not be triggered + often. It could be optimized away by examining the data in + the writebuf routine, and remembering the result. */ + for (i = 0; i < 512; i++) { + if (dat[i] == 0xff) continue; + emptymatch = 0; + break; + } + } + /* If emptymatch still =1, we do have an all-0xff data buffer. + Return all-0xff ecc value instead of the computed one, so + it'll look just like a freshly-erased page. */ + if (emptymatch) memset(ecc_code, 0xff, 6); +#endif + return 0; +} + +static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) +{ + int i, ret = 0; + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + volatile u_char dummy; + int emptymatch = 1; + + /* flush the pipeline */ + if (DoC_is_2000(doc)) { + dummy = ReadDOC(docptr, 2k_ECCStatus); + dummy = ReadDOC(docptr, 2k_ECCStatus); + dummy = ReadDOC(docptr, 2k_ECCStatus); + } else if (DoC_is_MillenniumPlus(doc)) { + dummy = ReadDOC(docptr, Mplus_ECCConf); + dummy = ReadDOC(docptr, Mplus_ECCConf); + dummy = ReadDOC(docptr, Mplus_ECCConf); + } else { + dummy = ReadDOC(docptr, ECCConf); + dummy = ReadDOC(docptr, ECCConf); + dummy = ReadDOC(docptr, ECCConf); + } + + /* Error occured ? */ + if (dummy & 0x80) { + for (i = 0; i < 6; i++) { + if (DoC_is_MillenniumPlus(doc)) + calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); + else + calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); + if (calc_ecc[i] != empty_read_syndrome[i]) + emptymatch = 0; + } + /* If emptymatch=1, the read syndrome is consistent with an + all-0xff data and stored ecc block. Check the stored ecc. */ + if (emptymatch) { + for (i = 0; i < 6; i++) { + if (read_ecc[i] == 0xff) continue; + emptymatch = 0; + break; + } + } + /* If emptymatch still =1, check the data block. */ + if (emptymatch) { + /* Note: this somewhat expensive test should not be triggered + often. It could be optimized away by examining the data in + the readbuf routine, and remembering the result. */ + for (i = 0; i < 512; i++) { + if (dat[i] == 0xff) continue; + emptymatch = 0; + break; + } + } + /* If emptymatch still =1, this is almost certainly a freshly- + erased block, in which case the ECC will not come out right. + We'll suppress the error and tell the caller everything's + OK. Because it is. */ + if (!emptymatch) ret = doc_ecc_decode (rs_decoder, dat, calc_ecc); + if (ret > 0) + printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret); + } + if (DoC_is_MillenniumPlus(doc)) + WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); + else + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); + if (no_ecc_failures && (ret == -1)) { + printk(KERN_ERR "suppressing ECC failure\n"); + ret = 0; + } + return ret; +} + +//u_char mydatabuf[528]; + +static struct nand_oobinfo doc200x_oobinfo = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 6, + .eccpos = {0, 1, 2, 3, 4, 5}, + .oobfree = { {8, 8} } +}; + +/* Find the (I)NFTL Media Header, and optionally also the mirror media header. + On sucessful return, buf will contain a copy of the media header for + further processing. id is the string to scan for, and will presumably be + either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media + header. The page #s of the found media headers are placed in mh0_page and + mh1_page in the DOC private structure. */ +static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, + const char *id, int findmirror) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift); + int ret; + size_t retlen; + + end = min(end, mtd->size); // paranoia + for (offs = 0; offs < end; offs += mtd->erasesize) { + ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); + if (retlen != mtd->oobblock) continue; + if (ret) { + printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", + offs); + } + if (memcmp(buf, id, 6)) continue; + printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs); + if (doc->mh0_page == -1) { + doc->mh0_page = offs >> this->page_shift; + if (!findmirror) return 1; + continue; + } + doc->mh1_page = offs >> this->page_shift; + return 2; + } + if (doc->mh0_page == -1) { + printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id); + return 0; + } + /* Only one mediaheader was found. We want buf to contain a + mediaheader on return, so we'll have to re-read the one we found. */ + offs = doc->mh0_page << this->page_shift; + ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); + if (retlen != mtd->oobblock) { + /* Insanity. Give up. */ + printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); + return 0; + } + return 1; +} + +static inline int __init nftl_partscan(struct mtd_info *mtd, + struct mtd_partition *parts) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + int ret = 0; + u_char *buf; + struct NFTLMediaHeader *mh; + const unsigned psize = 1 << this->page_shift; + unsigned blocks, maxblocks; + int offs, numheaders; + + buf = kmalloc(mtd->oobblock, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); + return 0; + } + if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out; + mh = (struct NFTLMediaHeader *) buf; + +//#ifdef CONFIG_MTD_DEBUG_VERBOSE +// if (CONFIG_MTD_DEBUG_VERBOSE >= 2) + printk(KERN_INFO " DataOrgID = %s\n" + " NumEraseUnits = %d\n" + " FirstPhysicalEUN = %d\n" + " FormattedSize = %d\n" + " UnitSizeFactor = %d\n", + mh->DataOrgID, mh->NumEraseUnits, + mh->FirstPhysicalEUN, mh->FormattedSize, + mh->UnitSizeFactor); +//#endif + + blocks = mtd->size >> this->phys_erase_shift; + maxblocks = min(32768U, mtd->erasesize - psize); + + if (mh->UnitSizeFactor == 0x00) { + /* Auto-determine UnitSizeFactor. The constraints are: + - There can be at most 32768 virtual blocks. + - There can be at most (virtual block size - page size) + virtual blocks (because MediaHeader+BBT must fit in 1). + */ + mh->UnitSizeFactor = 0xff; + while (blocks > maxblocks) { + blocks >>= 1; + maxblocks = min(32768U, (maxblocks << 1) + psize); + mh->UnitSizeFactor--; + } + printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor); + } + + /* NOTE: The lines below modify internal variables of the NAND and MTD + layers; variables with have already been configured by nand_scan. + Unfortunately, we didn't know before this point what these values + should be. Thus, this code is somewhat dependant on the exact + implementation of the NAND layer. */ + if (mh->UnitSizeFactor != 0xff) { + this->bbt_erase_shift += (0xff - mh->UnitSizeFactor); + mtd->erasesize <<= (0xff - mh->UnitSizeFactor); + printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize); + blocks = mtd->size >> this->bbt_erase_shift; + maxblocks = min(32768U, mtd->erasesize - psize); + } + + if (blocks > maxblocks) { + printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor); + goto out; + } + + /* Skip past the media headers. */ + offs = max(doc->mh0_page, doc->mh1_page); + offs <<= this->page_shift; + offs += mtd->erasesize; + + //parts[0].name = " DiskOnChip Boot / Media Header partition"; + //parts[0].offset = 0; + //parts[0].size = offs; + + parts[0].name = " DiskOnChip BDTL partition"; + parts[0].offset = offs; + parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; + + offs += parts[0].size; + if (offs < mtd->size) { + parts[1].name = " DiskOnChip Remainder partition"; + parts[1].offset = offs; + parts[1].size = mtd->size - offs; + ret = 2; + goto out; + } + ret = 1; +out: + kfree(buf); + return ret; +} + +/* This is a stripped-down copy of the code in inftlmount.c */ +static inline int __init inftl_partscan(struct mtd_info *mtd, + struct mtd_partition *parts) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + int ret = 0; + u_char *buf; + struct INFTLMediaHeader *mh; + struct INFTLPartition *ip; + int numparts = 0; + int blocks; + int vshift, lastvunit = 0; + int i; + int end = mtd->size; + + if (inftl_bbt_write) + end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); + + buf = kmalloc(mtd->oobblock, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); + return 0; + } + + if (!find_media_headers(mtd, buf, "BNAND", 0)) goto out; + doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift); + mh = (struct INFTLMediaHeader *) buf; + + mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks); + mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); + mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions); + mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits); + mh->FormatFlags = le32_to_cpu(mh->FormatFlags); + mh->PercentUsed = le32_to_cpu(mh->PercentUsed); + +//#ifdef CONFIG_MTD_DEBUG_VERBOSE +// if (CONFIG_MTD_DEBUG_VERBOSE >= 2) + printk(KERN_INFO " bootRecordID = %s\n" + " NoOfBootImageBlocks = %d\n" + " NoOfBinaryPartitions = %d\n" + " NoOfBDTLPartitions = %d\n" + " BlockMultiplerBits = %d\n" + " FormatFlgs = %d\n" + " OsakVersion = %d.%d.%d.%d\n" + " PercentUsed = %d\n", + mh->bootRecordID, mh->NoOfBootImageBlocks, + mh->NoOfBinaryPartitions, + mh->NoOfBDTLPartitions, + mh->BlockMultiplierBits, mh->FormatFlags, + ((unsigned char *) &mh->OsakVersion)[0] & 0xf, + ((unsigned char *) &mh->OsakVersion)[1] & 0xf, + ((unsigned char *) &mh->OsakVersion)[2] & 0xf, + ((unsigned char *) &mh->OsakVersion)[3] & 0xf, + mh->PercentUsed); +//#endif + + vshift = this->phys_erase_shift + mh->BlockMultiplierBits; + + blocks = mtd->size >> vshift; + if (blocks > 32768) { + printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits); + goto out; + } + + blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift); + if (inftl_bbt_write && (blocks > mtd->erasesize)) { + printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n"); + goto out; + } + + /* Scan the partitions */ + for (i = 0; (i < 4); i++) { + ip = &(mh->Partitions[i]); + ip->virtualUnits = le32_to_cpu(ip->virtualUnits); + ip->firstUnit = le32_to_cpu(ip->firstUnit); + ip->lastUnit = le32_to_cpu(ip->lastUnit); + ip->flags = le32_to_cpu(ip->flags); + ip->spareUnits = le32_to_cpu(ip->spareUnits); + ip->Reserved0 = le32_to_cpu(ip->Reserved0); + +//#ifdef CONFIG_MTD_DEBUG_VERBOSE +// if (CONFIG_MTD_DEBUG_VERBOSE >= 2) + printk(KERN_INFO " PARTITION[%d] ->\n" + " virtualUnits = %d\n" + " firstUnit = %d\n" + " lastUnit = %d\n" + " flags = 0x%x\n" + " spareUnits = %d\n", + i, ip->virtualUnits, ip->firstUnit, + ip->lastUnit, ip->flags, + ip->spareUnits); +//#endif + +/* + if ((i == 0) && (ip->firstUnit > 0)) { + parts[0].name = " DiskOnChip IPL / Media Header partition"; + parts[0].offset = 0; + parts[0].size = mtd->erasesize * ip->firstUnit; + numparts = 1; + } +*/ + + if (ip->flags & INFTL_BINARY) + parts[numparts].name = " DiskOnChip BDK partition"; + else + parts[numparts].name = " DiskOnChip BDTL partition"; + parts[numparts].offset = ip->firstUnit << vshift; + parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift; + numparts++; + if (ip->lastUnit > lastvunit) lastvunit = ip->lastUnit; + if (ip->flags & INFTL_LAST) break; + } + lastvunit++; + if ((lastvunit << vshift) < end) { + parts[numparts].name = " DiskOnChip Remainder partition"; + parts[numparts].offset = lastvunit << vshift; + parts[numparts].size = end - parts[numparts].offset; + numparts++; + } + ret = numparts; +out: + kfree(buf); + return ret; +} + +static int __init nftl_scan_bbt(struct mtd_info *mtd) +{ + int ret, numparts; + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + struct mtd_partition parts[2]; + + memset((char *) parts, 0, sizeof(parts)); + /* On NFTL, we have to find the media headers before we can read the + BBTs, since they're stored in the media header eraseblocks. */ + numparts = nftl_partscan(mtd, parts); + if (!numparts) return -EIO; + this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | + NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | + NAND_BBT_VERSION; + this->bbt_td->veroffs = 7; + this->bbt_td->pages[0] = doc->mh0_page + 1; + if (doc->mh1_page != -1) { + this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | + NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | + NAND_BBT_VERSION; + this->bbt_md->veroffs = 7; + this->bbt_md->pages[0] = doc->mh1_page + 1; + } else { + this->bbt_md = NULL; + } + + /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set. + At least as nand_bbt.c is currently written. */ + if ((ret = nand_scan_bbt(mtd, NULL))) + return ret; + add_mtd_device(mtd); +#ifdef CONFIG_MTD_PARTITIONS + if (!no_autopart) + add_mtd_partitions(mtd, parts, numparts); +#endif + return 0; +} + +static int __init inftl_scan_bbt(struct mtd_info *mtd) +{ + int ret, numparts; + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + struct mtd_partition parts[5]; + + if (this->numchips > doc->chips_per_floor) { + printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n"); + return -EIO; + } + + if (DoC_is_MillenniumPlus(doc)) { + this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE; + if (inftl_bbt_write) + this->bbt_td->options |= NAND_BBT_WRITE; + this->bbt_td->pages[0] = 2; + this->bbt_md = NULL; + } else { + this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | + NAND_BBT_VERSION; + if (inftl_bbt_write) + this->bbt_td->options |= NAND_BBT_WRITE; + this->bbt_td->offs = 8; + this->bbt_td->len = 8; + this->bbt_td->veroffs = 7; + this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS; + this->bbt_td->reserved_block_code = 0x01; + this->bbt_td->pattern = "MSYS_BBT"; + + this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | + NAND_BBT_VERSION; + if (inftl_bbt_write) + this->bbt_md->options |= NAND_BBT_WRITE; + this->bbt_md->offs = 8; + this->bbt_md->len = 8; + this->bbt_md->veroffs = 7; + this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS; + this->bbt_md->reserved_block_code = 0x01; + this->bbt_md->pattern = "TBB_SYSM"; + } + + /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set. + At least as nand_bbt.c is currently written. */ + if ((ret = nand_scan_bbt(mtd, NULL))) + return ret; + memset((char *) parts, 0, sizeof(parts)); + numparts = inftl_partscan(mtd, parts); + /* At least for now, require the INFTL Media Header. We could probably + do without it for non-INFTL use, since all it gives us is + autopartitioning, but I want to give it more thought. */ + if (!numparts) return -EIO; + add_mtd_device(mtd); +#ifdef CONFIG_MTD_PARTITIONS + if (!no_autopart) + add_mtd_partitions(mtd, parts, numparts); +#endif + return 0; +} + +static inline int __init doc2000_init(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + + this->write_byte = doc2000_write_byte; + this->read_byte = doc2000_read_byte; + this->write_buf = doc2000_writebuf; + this->read_buf = doc2000_readbuf; + this->verify_buf = doc2000_verifybuf; + this->scan_bbt = nftl_scan_bbt; + + doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO; + doc2000_count_chips(mtd); + mtd->name = "DiskOnChip 2000 (NFTL Model)"; + return (4 * doc->chips_per_floor); +} + +static inline int __init doc2001_init(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + + this->write_byte = doc2001_write_byte; + this->read_byte = doc2001_read_byte; + this->write_buf = doc2001_writebuf; + this->read_buf = doc2001_readbuf; + this->verify_buf = doc2001_verifybuf; + + ReadDOC(doc->virtadr, ChipID); + ReadDOC(doc->virtadr, ChipID); + ReadDOC(doc->virtadr, ChipID); + if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) { + /* It's not a Millennium; it's one of the newer + DiskOnChip 2000 units with a similar ASIC. + Treat it like a Millennium, except that it + can have multiple chips. */ + doc2000_count_chips(mtd); + mtd->name = "DiskOnChip 2000 (INFTL Model)"; + this->scan_bbt = inftl_scan_bbt; + return (4 * doc->chips_per_floor); + } else { + /* Bog-standard Millennium */ + doc->chips_per_floor = 1; + mtd->name = "DiskOnChip Millennium"; + this->scan_bbt = nftl_scan_bbt; + return 1; + } +} + +static inline int __init doc2001plus_init(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + + this->write_byte = NULL; + this->read_byte = doc2001plus_read_byte; + this->write_buf = doc2001plus_writebuf; + this->read_buf = doc2001plus_readbuf; + this->verify_buf = doc2001plus_verifybuf; + this->scan_bbt = inftl_scan_bbt; + this->hwcontrol = NULL; + this->select_chip = doc2001plus_select_chip; + this->cmdfunc = doc2001plus_command; + this->enable_hwecc = doc2001plus_enable_hwecc; + + doc->chips_per_floor = 1; + mtd->name = "DiskOnChip Millennium Plus"; + + return 1; +} + +static inline int __init doc_probe(unsigned long physadr) +{ + unsigned char ChipID; + struct mtd_info *mtd; + struct nand_chip *nand; + struct doc_priv *doc; + void __iomem *virtadr; + unsigned char save_control; + unsigned char tmp, tmpb, tmpc; + int reg, len, numchips; + int ret = 0; + + virtadr = ioremap(physadr, DOC_IOREMAP_LEN); + if (!virtadr) { + printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr); + return -EIO; + } + + /* It's not possible to cleanly detect the DiskOnChip - the + * bootup procedure will put the device into reset mode, and + * it's not possible to talk to it without actually writing + * to the DOCControl register. So we store the current contents + * of the DOCControl register's location, in case we later decide + * that it's not a DiskOnChip, and want to put it back how we + * found it. + */ + save_control = ReadDOC(virtadr, DOCControl); + + /* Reset the DiskOnChip ASIC */ + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, + virtadr, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, + virtadr, DOCControl); + + /* Enable the DiskOnChip ASIC */ + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, + virtadr, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, + virtadr, DOCControl); + + ChipID = ReadDOC(virtadr, ChipID); + + switch(ChipID) { + case DOC_ChipID_Doc2k: + reg = DoC_2k_ECCStatus; + break; + case DOC_ChipID_DocMil: + reg = DoC_ECCConf; + break; + case DOC_ChipID_DocMilPlus16: + case DOC_ChipID_DocMilPlus32: + case 0: + /* Possible Millennium Plus, need to do more checks */ + /* Possibly release from power down mode */ + for (tmp = 0; (tmp < 4); tmp++) + ReadDOC(virtadr, Mplus_Power); + + /* Reset the Millennium Plus ASIC */ + tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | + DOC_MODE_BDECT; + WriteDOC(tmp, virtadr, Mplus_DOCControl); + WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); + + mdelay(1); + /* Enable the Millennium Plus ASIC */ + tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | + DOC_MODE_BDECT; + WriteDOC(tmp, virtadr, Mplus_DOCControl); + WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); + mdelay(1); + + ChipID = ReadDOC(virtadr, ChipID); + + switch (ChipID) { + case DOC_ChipID_DocMilPlus16: + reg = DoC_Mplus_Toggle; + break; + case DOC_ChipID_DocMilPlus32: + printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n"); + default: + ret = -ENODEV; + goto notfound; + } + break; + + default: + ret = -ENODEV; + goto notfound; + } + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + if ((tmp == tmpb) || (tmp != tmpc)) { + printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr); + ret = -ENODEV; + goto notfound; + } + + for (mtd = doclist; mtd; mtd = doc->nextdoc) { + unsigned char oldval; + unsigned char newval; + nand = mtd->priv; + doc = nand->priv; + /* Use the alias resolution register to determine if this is + in fact the same DOC aliased to a new address. If writes + to one chip's alias resolution register change the value on + the other chip, they're the same chip. */ + if (ChipID == DOC_ChipID_DocMilPlus16) { + oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); + newval = ReadDOC(virtadr, Mplus_AliasResolution); + } else { + oldval = ReadDOC(doc->virtadr, AliasResolution); + newval = ReadDOC(virtadr, AliasResolution); + } + if (oldval != newval) + continue; + if (ChipID == DOC_ChipID_DocMilPlus16) { + WriteDOC(~newval, virtadr, Mplus_AliasResolution); + oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); + WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it + } else { + WriteDOC(~newval, virtadr, AliasResolution); + oldval = ReadDOC(doc->virtadr, AliasResolution); + WriteDOC(newval, virtadr, AliasResolution); // restore it + } + newval = ~newval; + if (oldval == newval) { + printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr); + goto notfound; + } + } + + printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr); + + len = sizeof(struct mtd_info) + + sizeof(struct nand_chip) + + sizeof(struct doc_priv) + + (2 * sizeof(struct nand_bbt_descr)); + mtd = kmalloc(len, GFP_KERNEL); + if (!mtd) { + printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len); + ret = -ENOMEM; + goto fail; + } + memset(mtd, 0, len); + + nand = (struct nand_chip *) (mtd + 1); + doc = (struct doc_priv *) (nand + 1); + nand->bbt_td = (struct nand_bbt_descr *) (doc + 1); + nand->bbt_md = nand->bbt_td + 1; + + mtd->priv = nand; + mtd->owner = THIS_MODULE; + + nand->priv = doc; + nand->select_chip = doc200x_select_chip; + nand->hwcontrol = doc200x_hwcontrol; + nand->dev_ready = doc200x_dev_ready; + nand->waitfunc = doc200x_wait; + nand->block_bad = doc200x_block_bad; + nand->enable_hwecc = doc200x_enable_hwecc; + nand->calculate_ecc = doc200x_calculate_ecc; + nand->correct_data = doc200x_correct_data; + + nand->autooob = &doc200x_oobinfo; + nand->eccmode = NAND_ECC_HW6_512; + nand->options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME; + + doc->physadr = physadr; + doc->virtadr = virtadr; + doc->ChipID = ChipID; + doc->curfloor = -1; + doc->curchip = -1; + doc->mh0_page = -1; + doc->mh1_page = -1; + doc->nextdoc = doclist; + + if (ChipID == DOC_ChipID_Doc2k) + numchips = doc2000_init(mtd); + else if (ChipID == DOC_ChipID_DocMilPlus16) + numchips = doc2001plus_init(mtd); + else + numchips = doc2001_init(mtd); + + if ((ret = nand_scan(mtd, numchips))) { + /* DBB note: i believe nand_release is necessary here, as + buffers may have been allocated in nand_base. Check with + Thomas. FIX ME! */ + /* nand_release will call del_mtd_device, but we haven't yet + added it. This is handled without incident by + del_mtd_device, as far as I can tell. */ + nand_release(mtd); + kfree(mtd); + goto fail; + } + + /* Success! */ + doclist = mtd; + return 0; + +notfound: + /* Put back the contents of the DOCControl register, in case it's not + actually a DiskOnChip. */ + WriteDOC(save_control, virtadr, DOCControl); +fail: + iounmap(virtadr); + return ret; +} + +static void release_nanddoc(void) +{ + struct mtd_info *mtd, *nextmtd; + struct nand_chip *nand; + struct doc_priv *doc; + + for (mtd = doclist; mtd; mtd = nextmtd) { + nand = mtd->priv; + doc = nand->priv; + + nextmtd = doc->nextdoc; + nand_release(mtd); + iounmap(doc->virtadr); + kfree(mtd); + } +} + +static int __init init_nanddoc(void) +{ + int i, ret = 0; + + /* We could create the decoder on demand, if memory is a concern. + * This way we have it handy, if an error happens + * + * Symbolsize is 10 (bits) + * Primitve polynomial is x^10+x^3+1 + * first consecutive root is 510 + * primitve element to generate roots = 1 + * generator polinomial degree = 4 + */ + rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); + if (!rs_decoder) { + printk (KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); + return -ENOMEM; + } + + if (doc_config_location) { + printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); + ret = doc_probe(doc_config_location); + if (ret < 0) + goto outerr; + } else { + for (i=0; (doc_locations[i] != 0xffffffff); i++) { + doc_probe(doc_locations[i]); + } + } + /* No banner message any more. Print a message if no DiskOnChip + found, so the user knows we at least tried. */ + if (!doclist) { + printk(KERN_INFO "No valid DiskOnChip devices found\n"); + ret = -ENODEV; + goto outerr; + } + return 0; +outerr: + free_rs(rs_decoder); + return ret; +} + +static void __exit cleanup_nanddoc(void) +{ + /* Cleanup the nand/DoC resources */ + release_nanddoc(); + + /* Free the reed solomon resources */ + if (rs_decoder) { + free_rs(rs_decoder); + } +} + +module_init(init_nanddoc); +module_exit(cleanup_nanddoc); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver\n"); diff --git a/drivers/nand/nand.c b/drivers/nand/nand.c new file mode 100644 index 0000000000..d187c89ea1 --- /dev/null +++ b/drivers/nand/nand.c @@ -0,0 +1,71 @@ +/* + * (C) Copyright 2005 + * 2N Telekomunikace, a.s. + * Ladislav Michl + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include + +#if (CONFIG_COMMANDS & CFG_CMD_NAND) + +#include + +#ifndef CFG_NAND_BASE_LIST +#define CFG_NAND_BASE_LIST { CFG_NAND_BASE } +#endif + +int nand_curr_device = -1; +nand_info_t nand_info[CFG_MAX_NAND_DEVICE]; + +static struct nand_chip nand_chip[CFG_MAX_NAND_DEVICE]; +static ulong base_address[CFG_MAX_NAND_DEVICE] = CFG_NAND_BASE_LIST; + +static const char default_nand_name[] = "nand"; + +extern void board_nand_init(struct nand_chip *nand); + +static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand, + ulong base_addr) +{ + mtd->priv = nand; + + nand->IO_ADDR_R = nand->IO_ADDR_W = base_addr; + board_nand_init(nand); + + if (nand_scan(mtd, 1) == 0) { + if (!mtd->name) + mtd->name = default_nand_name; + } else + mtd->name = NULL; + +} + +void nand_init(void) +{ + int i; + + for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) { + nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]); + if (nand_curr_device == -1) + nand_curr_device = i; + } +} + +#endif diff --git a/drivers/nand/nand_base.c b/drivers/nand/nand_base.c new file mode 100644 index 0000000000..c423512f18 --- /dev/null +++ b/drivers/nand/nand_base.c @@ -0,0 +1,2630 @@ +/* + * drivers/mtd/nand.c + * + * Overview: + * This is the generic MTD driver for NAND flash devices. It should be + * capable of working with almost all NAND chips currently available. + * Basic support for AG-AND chips is provided. + * + * Additional technical information is available on + * http://www.linux-mtd.infradead.org/tech/nand.html + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2002 Thomas Gleixner (tglx@linutronix.de) + * + * 02-08-2004 tglx: support for strange chips, which cannot auto increment + * pages on read / read_oob + * + * 03-17-2004 tglx: Check ready before auto increment check. Simon Bayes + * pointed this out, as he marked an auto increment capable chip + * as NOAUTOINCR in the board driver. + * Make reads over block boundaries work too + * + * 04-14-2004 tglx: first working version for 2k page size chips + * + * 05-19-2004 tglx: Basic support for Renesas AG-AND chips + * + * 09-24-2004 tglx: add support for hardware controllers (e.g. ECC) shared + * among multiple independend devices. Suggestions and initial patch + * from Ben Dooks + * + * Credits: + * David Woodhouse for adding multichip support + * + * Aleph One Ltd. and Toby Churchill Ltd. for supporting the + * rework for 2K page size chips + * + * TODO: + * Enable cached programming for 2k page size chips + * Check, if mtd->ecctype should be set to MTD_ECC_HW + * if we have HW ecc support. + * The AG-AND chips have nice features for speed improvement, + * which are not supported yet. Read / program 4 pages in one go. + * + * $Id: nand_base.c,v 1.126 2004/12/13 11:22:25 lavinen Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +/* XXX U-BOOT XXX */ +#if 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTD_PARTITIONS +#include +#endif + +#else + +#include + +#if (CONFIG_COMMANDS & CFG_CMD_NAND) + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_JFFS2_NAND +#include +#endif + +#endif + +/* Define default oob placement schemes for large and small page devices */ +static struct nand_oobinfo nand_oob_8 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 3, + .eccpos = {0, 1, 2}, + .oobfree = { {3, 2}, {6, 2} } +}; + +static struct nand_oobinfo nand_oob_16 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 6, + .eccpos = {0, 1, 2, 3, 6, 7}, + .oobfree = { {8, 8} } +}; + +static struct nand_oobinfo nand_oob_64 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 24, + .eccpos = { + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { {2, 38} } +}; + +/* This is used for padding purposes in nand_write_oob */ +static u_char ffchars[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +/* + * NAND low-level MTD interface functions + */ +static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len); +static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len); +static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len); + +static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); +static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); +static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); +static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf); +static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); +static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf); +/* XXX U-BOOT XXX */ +#if 0 +static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t * retlen); +static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); +#endif +static int nand_erase (struct mtd_info *mtd, struct erase_info *instr); +static void nand_sync (struct mtd_info *mtd); + +/* Some internal functions */ +static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, + struct nand_oobinfo *oobsel, int mode); +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE +static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, + u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode); +#else +#define nand_verify_pages(...) (0) +#endif + +static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state); + +/** + * nand_release_device - [GENERIC] release chip + * @mtd: MTD device structure + * + * Deselect, release chip lock and wake up anyone waiting on the device + */ +/* XXX U-BOOT XXX */ +#if 0 +static void nand_release_device (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + /* De-select the NAND device */ + this->select_chip(mtd, -1); + /* Do we have a hardware controller ? */ + if (this->controller) { + spin_lock(&this->controller->lock); + this->controller->active = NULL; + spin_unlock(&this->controller->lock); + } + /* Release the chip */ + spin_lock (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock (&this->chip_lock); +} +#else +#define nand_release_device(mtd) do {} while(0) +#endif + +/** + * nand_read_byte - [DEFAULT] read one byte from the chip + * @mtd: MTD device structure + * + * Default read function for 8bit buswith + */ +static u_char nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return readb(this->IO_ADDR_R); +} + +/** + * nand_write_byte - [DEFAULT] write one byte to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * Default write function for 8it buswith + */ +static void nand_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd->priv; + writeb(byte, this->IO_ADDR_W); +} + +/** + * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswith with + * endianess conversion + */ +static u_char nand_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); +} + +/** + * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * Default write function for 16bit buswith with + * endianess conversion + */ +static void nand_write_byte16(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd->priv; + writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); +} + +/** + * nand_read_word - [DEFAULT] read one word from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswith without + * endianess conversion + */ +static u16 nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return readw(this->IO_ADDR_R); +} + +/** + * nand_write_word - [DEFAULT] write one word to the chip + * @mtd: MTD device structure + * @word: data word to write + * + * Default write function for 16bit buswith without + * endianess conversion + */ +static void nand_write_word(struct mtd_info *mtd, u16 word) +{ + struct nand_chip *this = mtd->priv; + writew(word, this->IO_ADDR_W); +} + +/** + * nand_select_chip - [DEFAULT] control CE line + * @mtd: MTD device structure + * @chip: chipnumber to select, -1 for deselect + * + * Default select function for 1 chip devices. + */ +static void nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + switch(chip) { + case -1: + this->hwcontrol(mtd, NAND_CTL_CLRNCE); + break; + case 0: + this->hwcontrol(mtd, NAND_CTL_SETNCE); + break; + + default: + BUG(); + } +} + +/** + * nand_write_buf - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 8bit buswith + */ +static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_W); +} + +/** + * nand_read_buf - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 8bit buswith + */ +static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R); +} + +/** + * nand_verify_buf - [DEFAULT] Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * Default verify function for 8bit buswith + */ +static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R)) + return -EFAULT; + + return 0; +} + +/** + * nand_write_buf16 - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 16bit buswith + */ +static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_W); + +} + +/** + * nand_read_buf16 - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 16bit buswith + */ +static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_R); +} + +/** + * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * Default verify function for 16bit buswith + */ +static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_R)) + return -EFAULT; + + return 0; +} + +/** + * nand_block_bad - [DEFAULT] Read bad block marker from the chip + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * + * Check, if the block is bad. + */ +static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + int page, chipnr, res = 0; + struct nand_chip *this = mtd->priv; + u16 bad; + + if (getchip) { + page = (int)(ofs >> this->page_shift); + chipnr = (int)(ofs >> this->chip_shift); + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_READING); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + } else + page = (int) ofs; + + if (this->options & NAND_BUSWIDTH_16) { + this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask); + bad = cpu_to_le16(this->read_word(mtd)); + if (this->badblockpos & 0x1) + bad >>= 1; + if ((bad & 0xFF) != 0xff) + res = 1; + } else { + this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask); + if (this->read_byte(mtd) != 0xff) + res = 1; + } + + if (getchip) { + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + } + + return res; +} + +/** + * nand_default_block_markbad - [DEFAULT] mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This is the default implementation, which can be overridden by + * a hardware specific driver. +*/ +static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *this = mtd->priv; + u_char buf[2] = {0, 0}; + size_t retlen; + int block; + + /* Get block number */ + block = ((int) ofs) >> this->bbt_erase_shift; + this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + + /* Do we have a flash based bad block table ? */ + if (this->options & NAND_USE_FLASH_BBT) + return nand_update_bbt (mtd, ofs); + + /* We write two bytes, so we dont have to mess with 16 bit access */ + ofs += mtd->oobsize + (this->badblockpos & ~0x01); + return nand_write_oob (mtd, ofs , 2, &retlen, buf); +} + +/** + * nand_check_wp - [GENERIC] check if the chip is write protected + * @mtd: MTD device structure + * Check, if the device is write protected + * + * The function expects, that the device is already selected + */ +static int nand_check_wp (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + /* Check the WP bit */ + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); + return (this->read_byte(mtd) & 0x80) ? 0 : 1; +} + +/** + * nand_block_checkbad - [GENERIC] Check if a block is marked bad + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * @allowbbt: 1, if its allowed to access the bbt area + * + * Check, if the block is bad. Either by reading the bad block table or + * calling of the scan function. + */ +static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) +{ + struct nand_chip *this = mtd->priv; + + if (!this->bbt) + return this->block_bad(mtd, ofs, getchip); + + /* Return info from the table */ + return nand_isbad_bbt (mtd, ofs, allowbbt); +} + +/** + * nand_command - [DEFAULT] Send command to NAND device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This function is used for small page + * devices (256/512 Bytes per page) + */ +static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + register struct nand_chip *this = mtd->priv; + + /* Begin command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->oobblock) { + /* OOB area */ + column -= mtd->oobblock; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + this->write_byte(mtd, readcmd); + } + this->write_byte(mtd, command); + + /* Set ALE and clear CLE to start address cycle */ + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { + this->hwcontrol(mtd, NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16) + column >>= 1; + this->write_byte(mtd, column); + } + if (page_addr != -1) { + this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); + this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); + /* One more address cycle for devices > 32MiB */ + if (this->chipsize > (32 << 20)) + this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); + } + /* Latch in address */ + this->hwcontrol(mtd, NAND_CTL_CLRALE); + } + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + udelay(this->chip_delay); + this->hwcontrol(mtd, NAND_CTL_SETCLE); + this->write_byte(mtd, NAND_CMD_STATUS); + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + while ( !(this->read_byte(mtd) & 0x40)); + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay (this->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay (100); + /* wait until command is processed */ + while (!this->dev_ready(mtd)); +} + +/** + * nand_command_lp - [DEFAULT] Send command to NAND large page device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This is the version for the new large page devices + * We dont have the seperate regions as we have in the small page devices. + * We must emulate NAND_CMD_READOOB to keep the code compatible. + * + */ +static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + register struct nand_chip *this = mtd->priv; + + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + column += mtd->oobblock; + command = NAND_CMD_READ0; + } + + + /* Begin command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* Write out the command to the device. */ + this->write_byte(mtd, command); + /* End command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { + this->hwcontrol(mtd, NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16) + column >>= 1; + this->write_byte(mtd, column & 0xff); + this->write_byte(mtd, column >> 8); + } + if (page_addr != -1) { + this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); + this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); + /* One more address cycle for devices > 128MiB */ + if (this->chipsize > (128 << 20)) + this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff)); + } + /* Latch in address */ + this->hwcontrol(mtd, NAND_CTL_CLRALE); + } + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + udelay(this->chip_delay); + this->hwcontrol(mtd, NAND_CTL_SETCLE); + this->write_byte(mtd, NAND_CMD_STATUS); + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + while ( !(this->read_byte(mtd) & 0x40)); + return; + + case NAND_CMD_READ0: + /* Begin command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* Write out the start read command */ + this->write_byte(mtd, NAND_CMD_READSTART); + /* End command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + /* Fall through into ready check */ + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay (this->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay (100); + /* wait until command is processed */ + while (!this->dev_ready(mtd)); +} + +/** + * nand_get_device - [GENERIC] Get chip for selected access + * @this: the nand chip descriptor + * @mtd: MTD device structure + * @new_state: the state which is requested + * + * Get the device and lock it for exclusive access + */ +/* XXX U-BOOT XXX */ +#if 0 +static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) +{ + struct nand_chip *active = this; + + DECLARE_WAITQUEUE (wait, current); + + /* + * Grab the lock and see if the device is available + */ +retry: + /* Hardware controller shared among independend devices */ + if (this->controller) { + spin_lock (&this->controller->lock); + if (this->controller->active) + active = this->controller->active; + else + this->controller->active = this; + spin_unlock (&this->controller->lock); + } + + if (active == this) { + spin_lock (&this->chip_lock); + if (this->state == FL_READY) { + this->state = new_state; + spin_unlock (&this->chip_lock); + return; + } + } + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&active->wq, &wait); + spin_unlock (&active->chip_lock); + schedule (); + remove_wait_queue (&active->wq, &wait); + goto retry; +} +#else +static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) {} +#endif + +/** + * nand_wait - [DEFAULT] wait until the command is done + * @mtd: MTD device structure + * @this: NAND chip structure + * @state: state to select the max. timeout value + * + * Wait for command done. This applies to erase and program only + * Erase can take up to 400ms and program up to 20ms according to + * general NAND and SmartMedia specs + * +*/ +/* XXX U-BOOT XXX */ +#if 0 +static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +{ + unsigned long timeo = jiffies; + int status; + + if (state == FL_ERASING) + timeo += (HZ * 400) / 1000; + else + timeo += (HZ * 20) / 1000; + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay (100); + + if ((state == FL_ERASING) && (this->options & NAND_IS_AND)) + this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1); + else + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); + + while (time_before(jiffies, timeo)) { + /* Check, if we were interrupted */ + if (this->state != state) + return 0; + + if (this->dev_ready) { + if (this->dev_ready(mtd)) + break; + } else { + if (this->read_byte(mtd) & NAND_STATUS_READY) + break; + } + yield (); + } + status = (int) this->read_byte(mtd); + return status; + + return 0; +} +#else +static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +{ + /* TODO */ + return 0; +} +#endif + +/** + * nand_write_page - [GENERIC] write one page + * @mtd: MTD device structure + * @this: NAND chip structure + * @page: startpage inside the chip, must be called with (page & this->pagemask) + * @oob_buf: out of band data buffer + * @oobsel: out of band selecttion structre + * @cached: 1 = enable cached programming if supported by chip + * + * Nand_page_program function is used for write and writev ! + * This function will always program a full page of data + * If you call it with a non page aligned buffer, you're lost :) + * + * Cached programming is not supported yet. + */ +static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, + u_char *oob_buf, struct nand_oobinfo *oobsel, int cached) +{ + int i, status; + u_char ecc_code[32]; + int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; + int *oob_config = oobsel->eccpos; + int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; + int eccbytes = 0; + + /* FIXME: Enable cached programming */ + cached = 0; + + /* Send command to begin auto page programming */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page); + + /* Write out complete page of data, take care of eccmode */ + switch (eccmode) { + /* No ecc, write all */ + case NAND_ECC_NONE: + printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); + this->write_buf(mtd, this->data_poi, mtd->oobblock); + break; + + /* Software ecc 3/256, write all */ + case NAND_ECC_SOFT: + for (; eccsteps; eccsteps--) { + this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); + for (i = 0; i < 3; i++, eccidx++) + oob_buf[oob_config[eccidx]] = ecc_code[i]; + datidx += this->eccsize; + } + this->write_buf(mtd, this->data_poi, mtd->oobblock); + break; + default: + eccbytes = this->eccbytes; + for (; eccsteps; eccsteps--) { + /* enable hardware ecc logic for write */ + this->enable_hwecc(mtd, NAND_ECC_WRITE); + this->write_buf(mtd, &this->data_poi[datidx], this->eccsize); + this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); + for (i = 0; i < eccbytes; i++, eccidx++) + oob_buf[oob_config[eccidx]] = ecc_code[i]; + /* If the hardware ecc provides syndromes then + * the ecc code must be written immidiately after + * the data bytes (words) */ + if (this->options & NAND_HWECC_SYNDROME) + this->write_buf(mtd, ecc_code, eccbytes); + datidx += this->eccsize; + } + break; + } + + /* Write out OOB data */ + if (this->options & NAND_HWECC_SYNDROME) + this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes); + else + this->write_buf(mtd, oob_buf, mtd->oobsize); + + /* Send command to actually program the data */ + this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1); + + if (!cached) { + /* call wait ready function */ + status = this->waitfunc (mtd, this, FL_WRITING); + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); + return -EIO; + } + } else { + /* FIXME: Implement cached programming ! */ + /* wait until cache is ready*/ + // status = this->waitfunc (mtd, this, FL_CACHEDRPG); + } + return 0; +} + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE +/** + * nand_verify_pages - [GENERIC] verify the chip contents after a write + * @mtd: MTD device structure + * @this: NAND chip structure + * @page: startpage inside the chip, must be called with (page & this->pagemask) + * @numpages: number of pages to verify + * @oob_buf: out of band data buffer + * @oobsel: out of band selecttion structre + * @chipnr: number of the current chip + * @oobmode: 1 = full buffer verify, 0 = ecc only + * + * 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. + */ +static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, + u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode) +{ + int i, j, datidx = 0, oobofs = 0, res = -EIO; + int eccsteps = this->eccsteps; + int hweccbytes; + u_char oobdata[64]; + + hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0; + + /* Send command to read back the first page */ + this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); + + for(;;) { + for (j = 0; j < eccsteps; j++) { + /* Loop through and verify the data */ + if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + goto out; + } + datidx += mtd->eccsize; + /* Have we a hw generator layout ? */ + if (!hweccbytes) + continue; + if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + goto out; + } + oobofs += hweccbytes; + } + + /* check, if we must compare all data or if we just have to + * compare the ecc bytes + */ + if (oobmode) { + if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + goto out; + } + } else { + /* Read always, else autoincrement fails */ + this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps); + + if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) { + int ecccnt = oobsel->eccbytes; + + for (i = 0; i < ecccnt; i++) { + int idx = oobsel->eccpos[i]; + if (oobdata[idx] != oob_buf[oobofs + idx] ) { + DEBUG (MTD_DEBUG_LEVEL0, + "%s: Failed ECC write " + "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i); + goto out; + } + } + } + } + oobofs += mtd->oobsize - hweccbytes * eccsteps; + page++; + numpages--; + + /* Apply delay or wait for ready/busy pin + * Do this before the AUTOINCR check, so no problems + * arise if a chip which does auto increment + * is marked as NOAUTOINCR by the board driver. + * Do this also before returning, so the chip is + * ready for the next command. + */ + if (!this->dev_ready) + udelay (this->chip_delay); + else + while (!this->dev_ready(mtd)); + + /* All done, return happy */ + if (!numpages) + return 0; + + + /* Check, if the chip supports auto page increment */ + if (!NAND_CANAUTOINCR(this)) + this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); + } + /* + * Terminate the read command. We come here in case of an error + * So we must issue a reset command. + */ +out: + this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1); + return res; +} +#endif + +/** + * nand_read - [MTD Interface] MTD compability function for nand_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL +*/ +static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) +{ + return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL); +} + + +/** + * nand_read_ecc - [MTD Interface] Read data with ECC + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * @oob_buf: filesystem supplied oob data buffer + * @oobsel: oob selection structure + * + * NAND read with ECC + */ +static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) +{ + int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; + int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; + struct nand_chip *this = mtd->priv; + u_char *data_poi, *oob_data = oob_buf; + u_char ecc_calc[32]; + u_char ecc_code[32]; + int eccmode, eccsteps; + int *oob_config, datidx; + int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; + int eccbytes; + int compareecc = 1; + int oobreadlen; + + + DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n"); + *retlen = 0; + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd ,FL_READING); + + /* use userspace supplied oobinfo, if zero */ + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + + /* Autoplace of oob data ? Use the default placement scheme */ + if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) + oobsel = this->autooob; + + eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; + oob_config = oobsel->eccpos; + + /* Select the NAND device */ + chipnr = (int)(from >> this->chip_shift); + this->select_chip(mtd, chipnr); + + /* First we calculate the starting page */ + realpage = (int) (from >> this->page_shift); + page = realpage & this->pagemask; + + /* Get raw starting column */ + col = from & (mtd->oobblock - 1); + + end = mtd->oobblock; + ecc = this->eccsize; + eccbytes = this->eccbytes; + + if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) + compareecc = 0; + + oobreadlen = mtd->oobsize; + if (this->options & NAND_HWECC_SYNDROME) + oobreadlen -= oobsel->eccbytes; + + /* Loop until all data read */ + while (read < len) { + + int aligned = (!col && (len - read) >= end); + /* + * If the read is not page aligned, we have to read into data buffer + * due to ecc, else we read into return buffer direct + */ + if (aligned) + data_poi = &buf[read]; + else + data_poi = this->data_buf; + + /* Check, if we have this page in the buffer + * + * FIXME: Make it work when we must provide oob data too, + * check the usage of data_buf oob field + */ + if (realpage == this->pagebuf && !oob_buf) { + /* aligned read ? */ + if (aligned) + memcpy (data_poi, this->data_buf, end); + goto readdata; + } + + /* Check, if we must send the read command */ + if (sndcmd) { + this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); + sndcmd = 0; + } + + /* get oob area, if we have no oob buffer from fs-driver */ + if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE || + oobsel->useecc == MTD_NANDECC_AUTOPL_USR) + oob_data = &this->data_buf[end]; + + eccsteps = this->eccsteps; + + switch (eccmode) { + case NAND_ECC_NONE: { /* No ECC, Read in a page */ +/* XXX U-BOOT XXX */ +#if 0 + static unsigned long lastwhinge = 0; + if ((lastwhinge / HZ) != (jiffies / HZ)) { + printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n"); + lastwhinge = jiffies; + } +#else + puts("Reading data from NAND FLASH without ECC is not recommended\n"); +#endif + this->read_buf(mtd, data_poi, end); + break; + } + + case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ + this->read_buf(mtd, data_poi, end); + for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) + this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); + break; + + default: + for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) { + this->enable_hwecc(mtd, NAND_ECC_READ); + this->read_buf(mtd, &data_poi[datidx], ecc); + + /* HW ecc with syndrome calculation must read the + * syndrome from flash immidiately after the data */ + if (!compareecc) { + /* Some hw ecc generators need to know when the + * syndrome is read from flash */ + this->enable_hwecc(mtd, NAND_ECC_READSYN); + this->read_buf(mtd, &oob_data[i], eccbytes); + /* We calc error correction directly, it checks the hw + * generator for an error, reads back the syndrome and + * does the error correction on the fly */ + if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " + "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); + ecc_failed++; + } + } else { + this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); + } + } + break; + } + + /* read oobdata */ + this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); + + /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */ + if (!compareecc) + goto readoob; + + /* Pick the ECC bytes out of the oob data */ + for (j = 0; j < oobsel->eccbytes; j++) + ecc_code[j] = oob_data[oob_config[j]]; + + /* correct data, if neccecary */ + for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) { + ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]); + + /* Get next chunk of ecc bytes */ + j += eccbytes; + + /* Check, if we have a fs supplied oob-buffer, + * This is the legacy mode. Used by YAFFS1 + * Should go away some day + */ + if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) { + int *p = (int *)(&oob_data[mtd->oobsize]); + p[i] = ecc_status; + } + + if (ecc_status == -1) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); + ecc_failed++; + } + } + + readoob: + /* check, if we have a fs supplied oob-buffer */ + if (oob_buf) { + /* without autoplace. Legacy mode used by YAFFS1 */ + switch(oobsel->useecc) { + case MTD_NANDECC_AUTOPLACE: + case MTD_NANDECC_AUTOPL_USR: + /* Walk through the autoplace chunks */ + for (i = 0, j = 0; j < mtd->oobavail; i++) { + int from = oobsel->oobfree[i][0]; + int num = oobsel->oobfree[i][1]; + memcpy(&oob_buf[oob], &oob_data[from], num); + j+= num; + } + oob += mtd->oobavail; + break; + case MTD_NANDECC_PLACE: + /* YAFFS1 legacy mode */ + oob_data += this->eccsteps * sizeof (int); + default: + oob_data += mtd->oobsize; + } + } + readdata: + /* Partial page read, transfer data into fs buffer */ + if (!aligned) { + for (j = col; j < end && read < len; j++) + buf[read++] = data_poi[j]; + this->pagebuf = realpage; + } else + read += mtd->oobblock; + + /* Apply delay or wait for ready/busy pin + * Do this before the AUTOINCR check, so no problems + * arise if a chip which does auto increment + * is marked as NOAUTOINCR by the board driver. + */ + if (!this->dev_ready) + udelay (this->chip_delay); + else + while (!this->dev_ready(mtd)); + + if (read == len) + break; + + /* For subsequent reads align to page boundary. */ + col = 0; + /* Increment page address */ + realpage++; + + page = realpage & this->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + /* Check, if the chip supports auto page increment + * or if we have hit a block boundary. + */ + if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) + sndcmd = 1; + } + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + /* + * Return success, if no ECC failures, else -EBADMSG + * fs driver will take care of that, because + * retlen == desired len and result == -EBADMSG + */ + *retlen = read; + return ecc_failed ? -EBADMSG : 0; +} + +/** + * nand_read_oob - [MTD Interface] NAND read out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * NAND read out-of-band data from the spare area + */ +static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) +{ + int i, col, page, chipnr; + struct nand_chip *this = mtd->priv; + int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + + /* Shift to get page */ + page = (int)(from >> this->page_shift); + chipnr = (int)(from >> this->chip_shift); + + /* Mask to get column */ + col = from & (mtd->oobsize - 1); + + /* Initialize return length value */ + *retlen = 0; + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n"); + *retlen = 0; + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd , FL_READING); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Send the read command */ + this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask); + /* + * Read the data, if we read more than one page + * oob data, let the device transfer the data ! + */ + i = 0; + while (i < len) { + int thislen = mtd->oobsize - col; + thislen = min_t(int, thislen, len); + this->read_buf(mtd, &buf[i], thislen); + i += thislen; + + /* Apply delay or wait for ready/busy pin + * Do this before the AUTOINCR check, so no problems + * arise if a chip which does auto increment + * is marked as NOAUTOINCR by the board driver. + */ + if (!this->dev_ready) + udelay (this->chip_delay); + else + while (!this->dev_ready(mtd)); + + /* Read more ? */ + if (i < len) { + page++; + col = 0; + + /* Check, if we cross a chip boundary */ + if (!(page & this->pagemask)) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + + /* Check, if the chip supports auto page increment + * or if we have hit a block boundary. + */ + if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) { + /* For subsequent page reads set offset to 0 */ + this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask); + } + } + } + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + /* Return happy */ + *retlen = len; + return 0; +} + +/** + * nand_read_raw - [GENERIC] Read raw data including oob into buffer + * @mtd: MTD device structure + * @buf: temporary buffer + * @from: offset to read from + * @len: number of bytes to read + * @ooblen: number of oob data bytes to read + * + * Read raw data including oob into buffer + */ +int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen) +{ + struct nand_chip *this = mtd->priv; + int page = (int) (from >> this->page_shift); + int chip = (int) (from >> this->chip_shift); + int sndcmd = 1; + int cnt = 0; + int pagesize = mtd->oobblock + mtd->oobsize; + int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd , FL_READING); + + this->select_chip (mtd, chip); + + /* Add requested oob length */ + len += ooblen; + + while (len) { + if (sndcmd) + this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask); + sndcmd = 0; + + this->read_buf (mtd, &buf[cnt], pagesize); + + len -= pagesize; + cnt += pagesize; + page++; + + if (!this->dev_ready) + udelay (this->chip_delay); + else + while (!this->dev_ready(mtd)); + + /* Check, if the chip supports auto page increment */ + if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) + sndcmd = 1; + } + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + return 0; +} + + +/** + * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer + * @mtd: MTD device structure + * @fsbuf: buffer given by fs driver + * @oobsel: out of band selection structre + * @autoplace: 1 = place given buffer into the oob bytes + * @numpages: number of pages to prepare + * + * Return: + * 1. Filesystem buffer available and autoplacement is off, + * return filesystem buffer + * 2. No filesystem buffer or autoplace is off, return internal + * buffer + * 3. Filesystem buffer is given and autoplace selected + * put data from fs buffer into internal buffer and + * retrun internal buffer + * + * Note: The internal buffer is filled with 0xff. This must + * be done only once, when no autoplacement happens + * Autoplacement sets the buffer dirty flag, which + * forces the 0xff fill before using the buffer again. + * +*/ +static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel, + int autoplace, int numpages) +{ + struct nand_chip *this = mtd->priv; + int i, len, ofs; + + /* Zero copy fs supplied buffer */ + if (fsbuf && !autoplace) + return fsbuf; + + /* Check, if the buffer must be filled with ff again */ + if (this->oobdirty) { + memset (this->oob_buf, 0xff, + mtd->oobsize << (this->phys_erase_shift - this->page_shift)); + this->oobdirty = 0; + } + + /* If we have no autoplacement or no fs buffer use the internal one */ + if (!autoplace || !fsbuf) + return this->oob_buf; + + /* Walk through the pages and place the data */ + this->oobdirty = 1; + ofs = 0; + while (numpages--) { + for (i = 0, len = 0; len < mtd->oobavail; i++) { + int to = ofs + oobsel->oobfree[i][0]; + int num = oobsel->oobfree[i][1]; + memcpy (&this->oob_buf[to], fsbuf, num); + len += num; + fsbuf += num; + } + ofs += mtd->oobavail; + } + return this->oob_buf; +} + +#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0 + +/** + * nand_write - [MTD Interface] compability function for nand_write_ecc + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL + * +*/ +static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) +{ + return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL)); +} + +/** + * nand_write_ecc - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * @eccbuf: filesystem supplied oob data buffer + * @oobsel: oob selection structure + * + * NAND write with ECC + */ +static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) +{ + int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr; + int autoplace = 0, numpages, totalpages; + struct nand_chip *this = mtd->priv; + u_char *oobbuf, *bufstart; + int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); + + DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + + /* Initialize retlen, in case of early exit */ + *retlen = 0; + + /* Do not allow write past end of device */ + if ((to + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n"); + return -EINVAL; + } + + /* reject writes, which are not page aligned */ + if (NOTALIGNED (to) || NOTALIGNED(len)) { + printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_WRITING); + + /* Calculate chipnr */ + chipnr = (int)(to >> this->chip_shift); + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + goto out; + + /* if oobsel is NULL, use chip defaults */ + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + + /* Autoplace of oob data ? Use the default placement scheme */ + if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { + oobsel = this->autooob; + autoplace = 1; + } + if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) + autoplace = 1; + + /* Setup variables and oob buffer */ + totalpages = len >> this->page_shift; + page = (int) (to >> this->page_shift); + /* Invalidate the page cache, if we write to the cached page */ + if (page <= this->pagebuf && this->pagebuf < (page + totalpages)) + this->pagebuf = -1; + + /* Set it relative to chip */ + page &= this->pagemask; + startpage = page; + /* Calc number of pages we can write in one go */ + numpages = min (ppblock - (startpage & (ppblock - 1)), totalpages); + oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages); + bufstart = (u_char *)buf; + + /* Loop until all data is written */ + while (written < len) { + + this->data_poi = (u_char*) &buf[written]; + /* Write one page. If this is the last page to write + * or the last page in this block, then use the + * real pageprogram command, else select cached programming + * if supported by the chip. + */ + ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0)); + if (ret) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret); + goto out; + } + /* Next oob page */ + oob += mtd->oobsize; + /* Update written bytes count */ + written += mtd->oobblock; + if (written == len) + goto cmp; + + /* Increment page address */ + page++; + + /* Have we hit a block boundary ? Then we have to verify and + * if verify is ok, we have to setup the oob buffer for + * the next pages. + */ + if (!(page & (ppblock - 1))){ + int ofs; + this->data_poi = bufstart; + ret = nand_verify_pages (mtd, this, startpage, + page - startpage, + oobbuf, oobsel, chipnr, (eccbuf != NULL)); + if (ret) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); + goto out; + } + *retlen = written; + + ofs = autoplace ? mtd->oobavail : mtd->oobsize; + if (eccbuf) + eccbuf += (page - startpage) * ofs; + totalpages -= page - startpage; + numpages = min (totalpages, ppblock); + page &= this->pagemask; + startpage = page; + oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, + autoplace, numpages); + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + } + } + /* Verify the remaining pages */ +cmp: + this->data_poi = bufstart; + ret = nand_verify_pages (mtd, this, startpage, totalpages, + oobbuf, oobsel, chipnr, (eccbuf != NULL)); + if (!ret) + *retlen = written; + else + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); + +out: + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + return ret; +} + + +/** + * nand_write_oob - [MTD Interface] NAND write out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write out-of-band + */ +static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) +{ + int column, page, status, ret = -EIO, chipnr; + struct nand_chip *this = mtd->priv; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + + /* Shift to get page */ + page = (int) (to >> this->page_shift); + chipnr = (int) (to >> this->chip_shift); + + /* Mask to get column */ + column = to & (mtd->oobsize - 1); + + /* Initialize return length value */ + *retlen = 0; + + /* Do not allow write past end of page */ + if ((column + len) > mtd->oobsize) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_WRITING); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Reset the chip. Some chips (like the Toshiba TC5832DC found + in one of my DiskOnChip 2000 test units) will clear the whole + data page too if we don't do this. I have no clue why, but + I seem to have 'fixed' it in the doc2000 driver in + August 1999. dwmw2. */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + goto out; + + /* Invalidate the page cache, if we write to the cached page */ + if (page == this->pagebuf) + this->pagebuf = -1; + + if (NAND_MUST_PAD(this)) { + /* Write out desired data */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask); + /* prepad 0xff for partial programming */ + this->write_buf(mtd, ffchars, column); + /* write data */ + this->write_buf(mtd, buf, len); + /* postpad 0xff for partial programming */ + this->write_buf(mtd, ffchars, mtd->oobsize - (len+column)); + } else { + /* Write out desired data */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask); + /* write data */ + this->write_buf(mtd, buf, len); + } + /* Send command to program the OOB data */ + this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = this->waitfunc (mtd, this, FL_WRITING); + + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); + ret = -EIO; + goto out; + } + /* Return happy */ + *retlen = len; + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask); + + if (this->verify_buf(mtd, buf, len)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page); + ret = -EIO; + goto out; + } +#endif + ret = 0; +out: + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + return ret; +} + +/* XXX U-BOOT XXX */ +#if 0 +/** + * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc + * @mtd: MTD device structure + * @vecs: the iovectors to write + * @count: number of vectors + * @to: offset to write to + * @retlen: pointer to variable to store the number of written bytes + * + * NAND write with kvec. This just calls the ecc function + */ +static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, + loff_t to, size_t * retlen) +{ + return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL)); +} + +/** + * nand_writev_ecc - [MTD Interface] write with iovec with ecc + * @mtd: MTD device structure + * @vecs: the iovectors to write + * @count: number of vectors + * @to: offset to write to + * @retlen: pointer to variable to store the number of written bytes + * @eccbuf: filesystem supplied oob data buffer + * @oobsel: oob selection structure + * + * NAND write with iovec with ecc + */ +static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, + loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel) +{ + int i, page, len, total_len, ret = -EIO, written = 0, chipnr; + int oob, numpages, autoplace = 0, startpage; + struct nand_chip *this = mtd->priv; + int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); + u_char *oobbuf, *bufstart; + + /* Preset written len for early exit */ + *retlen = 0; + + /* Calculate total length of data */ + total_len = 0; + for (i = 0; i < count; i++) + total_len += (int) vecs[i].iov_len; + + DEBUG (MTD_DEBUG_LEVEL3, + "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count); + + /* Do not allow write past end of page */ + if ((to + total_len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n"); + return -EINVAL; + } + + /* reject writes, which are not page aligned */ + if (NOTALIGNED (to) || NOTALIGNED(total_len)) { + printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_WRITING); + + /* Get the current chip-nr */ + chipnr = (int) (to >> this->chip_shift); + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + goto out; + + /* if oobsel is NULL, use chip defaults */ + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + + /* Autoplace of oob data ? Use the default placement scheme */ + if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { + oobsel = this->autooob; + autoplace = 1; + } + if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) + autoplace = 1; + + /* Setup start page */ + page = (int) (to >> this->page_shift); + /* Invalidate the page cache, if we write to the cached page */ + if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift)) + this->pagebuf = -1; + + startpage = page & this->pagemask; + + /* Loop until all kvec' data has been written */ + len = 0; + while (count) { + /* If the given tuple is >= pagesize then + * write it out from the iov + */ + if ((vecs->iov_len - len) >= mtd->oobblock) { + /* Calc number of pages we can write + * out of this iov in one go */ + numpages = (vecs->iov_len - len) >> this->page_shift; + /* Do not cross block boundaries */ + numpages = min (ppblock - (startpage & (ppblock - 1)), numpages); + oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); + bufstart = (u_char *)vecs->iov_base; + bufstart += len; + this->data_poi = bufstart; + oob = 0; + for (i = 1; i <= numpages; i++) { + /* Write one page. If this is the last page to write + * then use the real pageprogram command, else select + * cached programming if supported by the chip. + */ + ret = nand_write_page (mtd, this, page & this->pagemask, + &oobbuf[oob], oobsel, i != numpages); + if (ret) + goto out; + this->data_poi += mtd->oobblock; + len += mtd->oobblock; + oob += mtd->oobsize; + page++; + } + /* Check, if we have to switch to the next tuple */ + if (len >= (int) vecs->iov_len) { + vecs++; + len = 0; + count--; + } + } else { + /* We must use the internal buffer, read data out of each + * tuple until we have a full page to write + */ + int cnt = 0; + while (cnt < mtd->oobblock) { + if (vecs->iov_base != NULL && vecs->iov_len) + this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++]; + /* Check, if we have to switch to the next tuple */ + if (len >= (int) vecs->iov_len) { + vecs++; + len = 0; + count--; + } + } + this->pagebuf = page; + this->data_poi = this->data_buf; + bufstart = this->data_poi; + numpages = 1; + oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); + ret = nand_write_page (mtd, this, page & this->pagemask, + oobbuf, oobsel, 0); + if (ret) + goto out; + page++; + } + + this->data_poi = bufstart; + ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0); + if (ret) + goto out; + + written += mtd->oobblock * numpages; + /* All done ? */ + if (!count) + break; + + startpage = page & this->pagemask; + /* Check, if we cross a chip boundary */ + if (!startpage) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + } + ret = 0; +out: + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + *retlen = written; + return ret; +} +#endif + +/** + * single_erease_cmd - [GENERIC] NAND standard block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * Standard erase command for NAND chips + */ +static void single_erase_cmd (struct mtd_info *mtd, int page) +{ + struct nand_chip *this = mtd->priv; + /* Send commands to erase a block */ + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); + this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); +} + +/** + * multi_erease_cmd - [GENERIC] AND specific block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * AND multi block erase command function + * Erase 4 consecutive blocks + */ +static void multi_erase_cmd (struct mtd_info *mtd, int page) +{ + struct nand_chip *this = mtd->priv; + /* Send commands to erase a block */ + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); + this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); +} + +/** + * nand_erase - [MTD Interface] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * + * Erase one ore more blocks + */ +static int nand_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + return nand_erase_nand (mtd, instr, 0); +} + +/** + * nand_erase_intern - [NAND Interface] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * @allowbbt: allow erasing the bbt area + * + * Erase one ore more blocks + */ +int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt) +{ + int page, len, status, pages_per_block, ret, chipnr; + struct nand_chip *this = mtd->priv; + + DEBUG (MTD_DEBUG_LEVEL3, + "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); + + /* Start address must align on block boundary */ + if (instr->addr & ((1 << this->phys_erase_shift) - 1)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); + return -EINVAL; + } + + /* Length must align on block boundary */ + if (instr->len & ((1 << this->phys_erase_shift) - 1)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n"); + return -EINVAL; + } + + /* Do not allow erase past end of device */ + if ((instr->len + instr->addr) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n"); + return -EINVAL; + } + + instr->fail_addr = 0xffffffff; + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_ERASING); + + /* Shift to get first page */ + page = (int) (instr->addr >> this->page_shift); + chipnr = (int) (instr->addr >> this->chip_shift); + + /* Calculate pages in each block */ + pages_per_block = 1 << (this->phys_erase_shift - this->page_shift); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Check the WP bit */ + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* Loop through the pages */ + len = instr->len; + + instr->state = MTD_ERASING; + + while (len) { + /* Check if we have a bad block, we do not erase bad blocks ! */ + if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) { + printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* Invalidate the page cache, if we erase the block which contains + the current cached page */ + if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block)) + this->pagebuf = -1; + + this->erase_cmd (mtd, page & this->pagemask); + + status = this->waitfunc (mtd, this, FL_ERASING); + + /* See if block erase succeeded */ + if (status & 0x01) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = (page << this->page_shift); + goto erase_exit; + } + + /* Increment page address and decrement length */ + len -= (1 << this->phys_erase_shift); + page += pages_per_block; + + /* Check, if we cross a chip boundary */ + if (len && !(page & this->pagemask)) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + } + instr->state = MTD_ERASE_DONE; + +erase_exit: + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; + /* Do call back function */ + if (!ret) + mtd_erase_callback(instr); + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + /* Return more or less happy */ + return ret; +} + +/** + * nand_sync - [MTD Interface] sync + * @mtd: MTD device structure + * + * Sync is actually a wait for chip ready function + */ +static void nand_sync (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_SYNCING); + /* Release it and go back */ + nand_release_device (mtd); +} + + +/** + * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs) +{ + /* Check for invalid offset */ + if (ofs > mtd->size) + return -EINVAL; + + return nand_block_checkbad (mtd, ofs, 1, 0); +} + +/** + * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *this = mtd->priv; + int ret; + + if ((ret = nand_block_isbad(mtd, ofs))) { + /* If it was bad already, return success and do nothing. */ + if (ret > 0) + return 0; + return ret; + } + + return this->block_markbad(mtd, ofs); +} + +/** + * nand_scan - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: Number of chips to scan for + * + * This fills out all the not initialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. Buffers are allocated if + * they are not provided by the board driver + * + */ +int nand_scan (struct mtd_info *mtd, int maxchips) +{ + int i, j, nand_maf_id, nand_dev_id, busw; + struct nand_chip *this = mtd->priv; + + /* Get buswidth to select the correct functions*/ + busw = this->options & NAND_BUSWIDTH_16; + + /* check for proper chip_delay setup, set 20us if not */ + if (!this->chip_delay) + this->chip_delay = 20; + + /* check, if a user supplied command function given */ + if (this->cmdfunc == NULL) + this->cmdfunc = nand_command; + + /* check, if a user supplied wait function given */ + if (this->waitfunc == NULL) + this->waitfunc = nand_wait; + + if (!this->select_chip) + this->select_chip = nand_select_chip; + if (!this->write_byte) + this->write_byte = busw ? nand_write_byte16 : nand_write_byte; + if (!this->read_byte) + this->read_byte = busw ? nand_read_byte16 : nand_read_byte; + if (!this->write_word) + this->write_word = nand_write_word; + if (!this->read_word) + this->read_word = nand_read_word; + if (!this->block_bad) + this->block_bad = nand_block_bad; + if (!this->block_markbad) + this->block_markbad = nand_default_block_markbad; + if (!this->write_buf) + this->write_buf = busw ? nand_write_buf16 : nand_write_buf; + if (!this->read_buf) + this->read_buf = busw ? nand_read_buf16 : nand_read_buf; + if (!this->verify_buf) + this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; + if (!this->scan_bbt) + this->scan_bbt = nand_default_bbt; + + /* Select the device */ + this->select_chip(mtd, 0); + + /* Send the command for reading device ID */ + this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + nand_maf_id = this->read_byte(mtd); + nand_dev_id = this->read_byte(mtd); + + /* Print and store flash device information */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + + if (nand_dev_id != nand_flash_ids[i].id) + continue; + + if (!mtd->name) mtd->name = nand_flash_ids[i].name; + this->chipsize = nand_flash_ids[i].chipsize << 20; + + /* New devices have all the information in additional id bytes */ + if (!nand_flash_ids[i].pagesize) { + int extid; + /* The 3rd id byte contains non relevant data ATM */ + extid = this->read_byte(mtd); + /* The 4th id byte is the important one */ + extid = this->read_byte(mtd); + /* Calc pagesize */ + mtd->oobblock = 1024 << (extid & 0x3); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + + } else { + /* Old devices have this data hardcoded in the + * device id table */ + mtd->erasesize = nand_flash_ids[i].erasesize; + mtd->oobblock = nand_flash_ids[i].pagesize; + mtd->oobsize = mtd->oobblock / 32; + busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; + } + + /* Check, if buswidth is correct. Hardware drivers should set + * this correct ! */ + if (busw != (this->options & NAND_BUSWIDTH_16)) { + printk (KERN_INFO "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, + nand_manuf_ids[i].name , mtd->name); + printk (KERN_WARNING + "NAND bus width %d instead %d bit\n", + (this->options & NAND_BUSWIDTH_16) ? 16 : 8, + busw ? 16 : 8); + this->select_chip(mtd, -1); + return 1; + } + + /* Calculate the address shift from the page size */ + this->page_shift = ffs(mtd->oobblock) - 1; + this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1; + this->chip_shift = ffs(this->chipsize) - 1; + + /* Set the bad block position */ + this->badblockpos = mtd->oobblock > 512 ? + NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; + + /* Get chip options, preserve non chip based options */ + this->options &= ~NAND_CHIPOPTIONS_MSK; + this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK; + /* Set this as a default. Board drivers can override it, if neccecary */ + this->options |= NAND_NO_AUTOINCR; + /* Check if this is a not a samsung device. Do not clear the options + * for chips which are not having an extended id. + */ + if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize) + this->options &= ~NAND_SAMSUNG_LP_OPTIONS; + + /* Check for AND chips with 4 page planes */ + if (this->options & NAND_4PAGE_ARRAY) + this->erase_cmd = multi_erase_cmd; + else + this->erase_cmd = single_erase_cmd; + + /* Do not replace user supplied command function ! */ + if (mtd->oobblock > 512 && this->cmdfunc == nand_command) + this->cmdfunc = nand_command_lp; + + /* Try to identify manufacturer */ + for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { + if (nand_manuf_ids[j].id == nand_maf_id) + break; + } + printk (KERN_INFO "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, + nand_manuf_ids[j].name , nand_flash_ids[i].name); + break; + } + + if (!nand_flash_ids[i].name) { + printk (KERN_WARNING "No NAND device found!!!\n"); + this->select_chip(mtd, -1); + return 1; + } + + for (i=1; i < maxchips; i++) { + this->select_chip(mtd, i); + + /* Send the command for reading device ID */ + this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + if (nand_maf_id != this->read_byte(mtd) || + nand_dev_id != this->read_byte(mtd)) + break; + } + if (i > 1) + printk(KERN_INFO "%d NAND chips detected\n", i); + + /* Allocate buffers, if neccecary */ + if (!this->oob_buf) { + size_t len; + len = mtd->oobsize << (this->phys_erase_shift - this->page_shift); + this->oob_buf = kmalloc (len, GFP_KERNEL); + if (!this->oob_buf) { + printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n"); + return -ENOMEM; + } + this->options |= NAND_OOBBUF_ALLOC; + } + + if (!this->data_buf) { + size_t len; + len = mtd->oobblock + mtd->oobsize; + this->data_buf = kmalloc (len, GFP_KERNEL); + if (!this->data_buf) { + if (this->options & NAND_OOBBUF_ALLOC) + kfree (this->oob_buf); + printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n"); + return -ENOMEM; + } + this->options |= NAND_DATABUF_ALLOC; + } + + /* Store the number of chips and calc total size for mtd */ + this->numchips = i; + mtd->size = i * this->chipsize; + /* Convert chipsize to number of pages per chip -1. */ + this->pagemask = (this->chipsize >> this->page_shift) - 1; + /* Preset the internal oob buffer */ + memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift)); + + /* If no default placement scheme is given, select an + * appropriate one */ + if (!this->autooob) { + /* Select the appropriate default oob placement scheme for + * placement agnostic filesystems */ + switch (mtd->oobsize) { + case 8: + this->autooob = &nand_oob_8; + break; + case 16: + this->autooob = &nand_oob_16; + break; + case 64: + this->autooob = &nand_oob_64; + break; + default: + printk (KERN_WARNING "No oob scheme defined for oobsize %d\n", + mtd->oobsize); +/* BUG(); */ + } + } + + /* The number of bytes available for the filesystem to place fs dependend + * oob data */ + if (this->options & NAND_BUSWIDTH_16) { + mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2); + if (this->autooob->eccbytes & 0x01) + mtd->oobavail--; + } else + mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1); + + /* + * check ECC mode, default to software + * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize + * fallback to software ECC + */ + this->eccsize = 256; /* set default eccsize */ + this->eccbytes = 3; + + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + if (mtd->oobblock < 2048) { + printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", + mtd->oobblock); + this->eccmode = NAND_ECC_SOFT; + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + } else + this->eccsize = 2048; + break; + + case NAND_ECC_HW3_512: + case NAND_ECC_HW6_512: + case NAND_ECC_HW8_512: + if (mtd->oobblock == 256) { + printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); + this->eccmode = NAND_ECC_SOFT; + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + } else + this->eccsize = 512; /* set eccsize to 512 */ + break; + + case NAND_ECC_HW3_256: + break; + + case NAND_ECC_NONE: + printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); + this->eccmode = NAND_ECC_NONE; + break; + + case NAND_ECC_SOFT: + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + break; + + default: + printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); +/* BUG(); */ + } + + /* Check hardware ecc function availability and adjust number of ecc bytes per + * calculation step + */ + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccbytes += 4; + case NAND_ECC_HW8_512: + this->eccbytes += 2; + case NAND_ECC_HW6_512: + this->eccbytes += 3; + case NAND_ECC_HW3_512: + case NAND_ECC_HW3_256: + if (this->calculate_ecc && this->correct_data && this->enable_hwecc) + break; + printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); +/* BUG(); */ + } + + mtd->eccsize = this->eccsize; + + /* Set the number of read / write steps for one page to ensure ECC generation */ + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccsteps = mtd->oobblock / 2048; + break; + case NAND_ECC_HW3_512: + case NAND_ECC_HW6_512: + case NAND_ECC_HW8_512: + this->eccsteps = mtd->oobblock / 512; + break; + case NAND_ECC_HW3_256: + case NAND_ECC_SOFT: + this->eccsteps = mtd->oobblock / 256; + break; + + case NAND_ECC_NONE: + this->eccsteps = 1; + break; + } + +/* XXX U-BOOT XXX */ +#if 0 + /* Initialize state, waitqueue and spinlock */ + this->state = FL_READY; + init_waitqueue_head (&this->wq); + spin_lock_init (&this->chip_lock); +#endif + + /* De-select the device */ + this->select_chip(mtd, -1); + + /* Invalidate the pagebuffer reference */ + this->pagebuf = -1; + + /* Fill in remaining MTD driver data */ + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; + mtd->ecctype = MTD_ECC_SW; + mtd->erase = nand_erase; + mtd->point = NULL; + mtd->unpoint = NULL; + mtd->read = nand_read; + mtd->write = nand_write; + mtd->read_ecc = nand_read_ecc; + mtd->write_ecc = nand_write_ecc; + mtd->read_oob = nand_read_oob; + mtd->write_oob = nand_write_oob; +/* XXX U-BOOT XXX */ +#if 0 + mtd->readv = NULL; + mtd->writev = nand_writev; + mtd->writev_ecc = nand_writev_ecc; +#endif + mtd->sync = nand_sync; +/* XXX U-BOOT XXX */ +#if 0 + mtd->lock = NULL; + mtd->unlock = NULL; + mtd->suspend = NULL; + mtd->resume = NULL; +#endif + mtd->block_isbad = nand_block_isbad; + mtd->block_markbad = nand_block_markbad; + + /* and make the autooob the default one */ + memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); +/* XXX U-BOOT XXX */ +#if 0 + mtd->owner = THIS_MODULE; +#endif + /* Build bad block table */ + return this->scan_bbt (mtd); +} + +/** + * nand_release - [NAND Interface] Free resources held by the NAND device + * @mtd: MTD device structure +*/ +void nand_release (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + +#ifdef CONFIG_MTD_PARTITIONS + /* Deregister partitions */ + del_mtd_partitions (mtd); +#endif + /* Deregister the device */ +/* XXX U-BOOT XXX */ +#if 0 + del_mtd_device (mtd); +#endif + /* Free bad block table memory, if allocated */ + if (this->bbt) + kfree (this->bbt); + /* Buffer allocated by nand_scan ? */ + if (this->options & NAND_OOBBUF_ALLOC) + kfree (this->oob_buf); + /* Buffer allocated by nand_scan ? */ + if (this->options & NAND_DATABUF_ALLOC) + kfree (this->data_buf); +} + +#endif diff --git a/drivers/nand/nand_bbt.c b/drivers/nand/nand_bbt.c new file mode 100644 index 0000000000..6f7e05b7eb --- /dev/null +++ b/drivers/nand/nand_bbt.c @@ -0,0 +1,1056 @@ +/* + * drivers/mtd/nand_bbt.c + * + * Overview: + * Bad block table support for the NAND driver + * + * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) + * + * $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Description: + * + * When nand_scan_bbt is called, then it tries to find the bad block table + * depending on the options in the bbt descriptor(s). If a bbt is found + * then the contents are read and the memory based bbt is created. If a + * mirrored bbt is selected then the mirror is searched too and the + * versions are compared. If the mirror has a greater version number + * than the mirror bbt is used to build the memory based bbt. + * If the tables are not versioned, then we "or" the bad block information. + * If one of the bbt's is out of date or does not exist it is (re)created. + * If no bbt exists at all then the device is scanned for factory marked + * good / bad blocks and the bad block tables are created. + * + * For manufacturer created bbts like the one found on M-SYS DOC devices + * the bbt is searched and read but never created + * + * The autogenerated bad block table is located in the last good blocks + * of the device. The table is mirrored, so it can be updated eventually. + * The table is marked in the oob area with an ident pattern and a version + * number which indicates which of both tables is more up to date. + * + * The table uses 2 bits per block + * 11b: block is good + * 00b: block is factory marked bad + * 01b, 10b: block is marked bad due to wear + * + * The memory bad block table uses the following scheme: + * 00b: block is good + * 01b: block is marked bad due to wear + * 10b: block is reserved (to protect the bbt area) + * 11b: block is factory marked bad + * + * Multichip devices like DOC store the bad block info per floor. + * + * Following assumptions are made: + * - bbts start at a page boundary, if autolocated on a block boundary + * - the space neccecary for a bbt in FLASH does not exceed a block boundary + * + */ + +#include + +#if (CONFIG_COMMANDS & CFG_CMD_NAND) + +#include +#include +#include +#include + +#include + +/** + * check_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @len: the length of buffer to search + * @paglen: the pagelength + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. + * If the SCAN_EMPTY option is set then check, if all bytes except the + * pattern area contain 0xff + * +*/ +static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +{ + int i, end; + uint8_t *p = buf; + + end = paglen + td->offs; + if (td->options & NAND_BBT_SCANEMPTY) { + for (i = 0; i < end; i++) { + if (p[i] != 0xff) + return -1; + } + } + p += end; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[i] != td->pattern[i]) + return -1; + } + + p += td->len; + end += td->len; + if (td->options & NAND_BBT_SCANEMPTY) { + for (i = end; i < len; i++) { + if (*p++ != 0xff) + return -1; + } + } + return 0; +} + +/** + * read_bbt - [GENERIC] Read the bad block table starting from page + * @mtd: MTD device structure + * @buf: temporary buffer + * @page: the starting page + * @num: the number of bbt descriptors to read + * @bits: number of bits per block + * @offs: offset in the memory table + * @reserved_block_code: Pattern to identify reserved blocks + * + * Read the bad block table starting from page. + * + */ +static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num, + int bits, int offs, int reserved_block_code) +{ + int res, i, j, act = 0; + struct nand_chip *this = mtd->priv; + size_t retlen, len, totlen; + loff_t from; + uint8_t msk = (uint8_t) ((1 << bits) - 1); + + totlen = (num * bits) >> 3; + from = ((loff_t)page) << this->page_shift; + + while (totlen) { + len = min (totlen, (size_t) (1 << this->bbt_erase_shift)); + res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob); + if (res < 0) { + if (retlen != len) { + printk (KERN_INFO "nand_bbt: Error reading bad block table\n"); + return res; + } + printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n"); + } + + /* Analyse data */ + for (i = 0; i < len; i++) { + uint8_t dat = buf[i]; + for (j = 0; j < 8; j += bits, act += 2) { + uint8_t tmp = (dat >> j) & msk; + if (tmp == msk) + continue; + if (reserved_block_code && + (tmp == reserved_block_code)) { + printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", + ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); + continue; + } + /* Leave it for now, if its matured we can move this + * message to MTD_DEBUG_LEVEL0 */ + printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", + ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + /* Factory marked bad or worn out ? */ + if (tmp == 0) + this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); + else + this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); + } + } + totlen -= len; + from += len; + } + return 0; +} + +/** + * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @chip: read the table for a specific chip, -1 read all chips. + * Applies only if NAND_BBT_PERCHIP option is set + * + * Read the bad block table for all chips starting at a given page + * We assume that the bbt bits are in consecutive order. +*/ +static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) +{ + struct nand_chip *this = mtd->priv; + int res = 0, i; + int bits; + + bits = td->options & NAND_BBT_NRBITS_MSK; + if (td->options & NAND_BBT_PERCHIP) { + int offs = 0; + for (i = 0; i < this->numchips; i++) { + if (chip == -1 || chip == i) + res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code); + if (res) + return res; + offs += this->chipsize >> (this->bbt_erase_shift + 2); + } + } else { + res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code); + if (res) + return res; + } + return 0; +} + +/** + * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Read the bad block table(s) for all chips starting at a given page + * We assume that the bbt bits are in consecutive order. + * +*/ +static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, + struct nand_bbt_descr *md) +{ + struct nand_chip *this = mtd->priv; + + /* Read the primary version, if available */ + if (td->options & NAND_BBT_VERSION) { + nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); + td->version[0] = buf[mtd->oobblock + td->veroffs]; + printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); + } + + /* Read the mirror version, if available */ + if (md && (md->options & NAND_BBT_VERSION)) { + nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); + md->version[0] = buf[mtd->oobblock + md->veroffs]; + printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); + } + + return 1; +} + +/** + * create_bbt - [GENERIC] Create a bad block table by scanning the device + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * @chip: create the table for a specific chip, -1 read all chips. + * Applies only if NAND_BBT_PERCHIP option is set + * + * Create a bad block table by scanning the device + * for the given good/bad block identify pattern + */ +static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) +{ + struct nand_chip *this = mtd->priv; + int i, j, numblocks, len, scanlen; + int startblock; + loff_t from; + size_t readlen, ooblen; + + printk (KERN_INFO "Scanning device for bad blocks\n"); + + if (bd->options & NAND_BBT_SCANALLPAGES) + len = 1 << (this->bbt_erase_shift - this->page_shift); + else { + if (bd->options & NAND_BBT_SCAN2NDPAGE) + len = 2; + else + len = 1; + } + scanlen = mtd->oobblock + mtd->oobsize; + readlen = len * mtd->oobblock; + ooblen = len * mtd->oobsize; + + if (chip == -1) { + /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it + * makes shifting and masking less painful */ + numblocks = mtd->size >> (this->bbt_erase_shift - 1); + startblock = 0; + from = 0; + } else { + if (chip >= this->numchips) { + printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", + chip + 1, this->numchips); + return; + } + numblocks = this->chipsize >> (this->bbt_erase_shift - 1); + startblock = chip * numblocks; + numblocks += startblock; + from = startblock << (this->bbt_erase_shift - 1); + } + + for (i = startblock; i < numblocks;) { + nand_read_raw (mtd, buf, from, readlen, ooblen); + for (j = 0; j < len; j++) { + if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", + i >> 1, (unsigned int) from); + break; + } + } + i += 2; + from += (1 << this->bbt_erase_shift); + } +} + +/** + * search_bbt - [GENERIC] scan the device for a specific bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * + * Read the bad block table by searching for a given ident pattern. + * Search is preformed either from the beginning up or from the end of + * the device downwards. The search starts always at the start of a + * block. + * If the option NAND_BBT_PERCHIP is given, each chip is searched + * for a bbt, which contains the bad block information of this chip. + * This is neccecary to provide support for certain DOC devices. + * + * The bbt ident pattern resides in the oob area of the first page + * in a block. + */ +static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd->priv; + int i, chips; + int bits, startblock, block, dir; + int scanlen = mtd->oobblock + mtd->oobsize; + int bbtblocks; + + /* Search direction top -> down ? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = (mtd->size >> this->bbt_erase_shift) -1; + dir = -1; + } else { + startblock = 0; + dir = 1; + } + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + bbtblocks = this->chipsize >> this->bbt_erase_shift; + startblock &= bbtblocks - 1; + } else { + chips = 1; + bbtblocks = mtd->size >> this->bbt_erase_shift; + } + + /* Number of bits for each erase block in the bbt */ + bits = td->options & NAND_BBT_NRBITS_MSK; + + for (i = 0; i < chips; i++) { + /* Reset version information */ + td->version[i] = 0; + td->pages[i] = -1; + /* Scan the maximum number of blocks */ + for (block = 0; block < td->maxblocks; block++) { + int actblock = startblock + dir * block; + /* Read first page */ + nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize); + if (!check_pattern(buf, scanlen, mtd->oobblock, td)) { + td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); + if (td->options & NAND_BBT_VERSION) { + td->version[i] = buf[mtd->oobblock + td->veroffs]; + } + break; + } + } + startblock += this->chipsize >> this->bbt_erase_shift; + } + /* Check, if we found a bbt for each requested chip */ + for (i = 0; i < chips; i++) { + if (td->pages[i] == -1) + printk (KERN_WARNING "Bad block table not found for chip %d\n", i); + else + printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]); + } + return 0; +} + +/** + * search_read_bbts - [GENERIC] scan the device for bad block table(s) + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Search and read the bad block table(s) +*/ +static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md) +{ + /* Search the primary table */ + search_bbt (mtd, buf, td); + + /* Search the mirror table */ + if (md) + search_bbt (mtd, buf, md); + + /* Force result check */ + return 1; +} + + +/** + * write_bbt - [GENERIC] (Re)write the bad block table + * + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * @chipsel: selector for a specific chip, -1 for all + * + * (Re)write the bad block table + * +*/ +static int write_bbt (struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) +{ + struct nand_chip *this = mtd->priv; + struct nand_oobinfo oobinfo; + struct erase_info einfo; + int i, j, res, chip = 0; + int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; + int nrchips, bbtoffs, pageoffs; + uint8_t msk[4]; + uint8_t rcode = td->reserved_block_code; + size_t retlen, len = 0; + loff_t to; + + if (!rcode) + rcode = 0xff; + /* Write bad block table per chip rather than per device ? */ + if (td->options & NAND_BBT_PERCHIP) { + numblocks = (int) (this->chipsize >> this->bbt_erase_shift); + /* Full device write or specific chip ? */ + if (chipsel == -1) { + nrchips = this->numchips; + } else { + nrchips = chipsel + 1; + chip = chipsel; + } + } else { + numblocks = (int) (mtd->size >> this->bbt_erase_shift); + nrchips = 1; + } + + /* Loop through the chips */ + for (; chip < nrchips; chip++) { + + /* There was already a version of the table, reuse the page + * This applies for absolute placement too, as we have the + * page nr. in td->pages. + */ + if (td->pages[chip] != -1) { + page = td->pages[chip]; + goto write; + } + + /* Automatic placement of the bad block table */ + /* Search direction top -> down ? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = numblocks * (chip + 1) - 1; + dir = -1; + } else { + startblock = chip * numblocks; + dir = 1; + } + + for (i = 0; i < td->maxblocks; i++) { + int block = startblock + dir * i; + /* Check, if the block is bad */ + switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) { + case 0x01: + case 0x03: + continue; + } + page = block << (this->bbt_erase_shift - this->page_shift); + /* Check, if the block is used by the mirror table */ + if (!md || md->pages[chip] != page) + goto write; + } + printk (KERN_ERR "No space left to write bad block table\n"); + return -ENOSPC; +write: + + /* Set up shift count and masks for the flash table */ + bits = td->options & NAND_BBT_NRBITS_MSK; + switch (bits) { + case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break; + case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break; + case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break; + case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break; + default: return -EINVAL; + } + + bbtoffs = chip * (numblocks >> 2); + + to = ((loff_t) page) << this->page_shift; + + memcpy (&oobinfo, this->autooob, sizeof(oobinfo)); + oobinfo.useecc = MTD_NANDECC_PLACEONLY; + + /* Must we save the block contents ? */ + if (td->options & NAND_BBT_SAVECONTENT) { + /* Make it block aligned */ + to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); + len = 1 << this->bbt_erase_shift; + res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); + if (res < 0) { + if (retlen != len) { + printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n"); + return res; + } + printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n"); + } + /* Calc the byte offset in the buffer */ + pageoffs = page - (int)(to >> this->page_shift); + offs = pageoffs << this->page_shift; + /* Preset the bbt area with 0xff */ + memset (&buf[offs], 0xff, (size_t)(numblocks >> sft)); + /* Preset the bbt's oob area with 0xff */ + memset (&buf[len + pageoffs * mtd->oobsize], 0xff, + ((len >> this->page_shift) - pageoffs) * mtd->oobsize); + if (td->options & NAND_BBT_VERSION) { + buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip]; + } + } else { + /* Calc length */ + len = (size_t) (numblocks >> sft); + /* Make it page aligned ! */ + len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1); + /* Preset the buffer with 0xff */ + memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize); + offs = 0; + /* Pattern is located in oob area of first page */ + memcpy (&buf[len + td->offs], td->pattern, td->len); + if (td->options & NAND_BBT_VERSION) { + buf[len + td->veroffs] = td->version[chip]; + } + } + + /* walk through the memory table */ + for (i = 0; i < numblocks; ) { + uint8_t dat; + dat = this->bbt[bbtoffs + (i >> 2)]; + for (j = 0; j < 4; j++ , i++) { + int sftcnt = (i << (3 - sft)) & sftmsk; + /* Do not store the reserved bbt blocks ! */ + buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt); + dat >>= 2; + } + } + + memset (&einfo, 0, sizeof (einfo)); + einfo.mtd = mtd; + einfo.addr = (unsigned long) to; + einfo.len = 1 << this->bbt_erase_shift; + res = nand_erase_nand (mtd, &einfo, 1); + if (res < 0) { + printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res); + return res; + } + + res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); + if (res < 0) { + printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res); + return res; + } + printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n", + (unsigned int) to, td->version[chip]); + + /* Mark it as used */ + td->pages[chip] = page; + } + return 0; +} + +/** + * nand_memory_bbt - [GENERIC] create a memory based bad block table + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function creates a memory based bbt by scanning the device + * for manufacturer / software marked good / bad blocks +*/ +static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd->priv; + + /* Ensure that we only scan for the pattern and nothing else */ + bd->options = 0; + create_bbt (mtd, this->data_buf, bd, -1); + return 0; +} + +/** + * check_create - [GENERIC] create and write bbt(s) if neccecary + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * + * The function checks the results of the previous call to read_bbt + * and creates / updates the bbt(s) if neccecary + * Creation is neccecary if no bbt was found for the chip/device + * Update is neccecary if one of the tables is missing or the + * version nr. of one table is less than the other +*/ +static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) +{ + int i, chips, writeops, chipsel, res; + struct nand_chip *this = mtd->priv; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + struct nand_bbt_descr *rd, *rd2; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) + chips = this->numchips; + else + chips = 1; + + for (i = 0; i < chips; i++) { + writeops = 0; + rd = NULL; + rd2 = NULL; + /* Per chip or per device ? */ + chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; + /* Mirrored table avilable ? */ + if (md) { + if (td->pages[i] == -1 && md->pages[i] == -1) { + writeops = 0x03; + goto create; + } + + if (td->pages[i] == -1) { + rd = md; + td->version[i] = md->version[i]; + writeops = 1; + goto writecheck; + } + + if (md->pages[i] == -1) { + rd = td; + md->version[i] = td->version[i]; + writeops = 2; + goto writecheck; + } + + if (td->version[i] == md->version[i]) { + rd = td; + if (!(td->options & NAND_BBT_VERSION)) + rd2 = md; + goto writecheck; + } + + if (((int8_t) (td->version[i] - md->version[i])) > 0) { + rd = td; + md->version[i] = td->version[i]; + writeops = 2; + } else { + rd = md; + td->version[i] = md->version[i]; + writeops = 1; + } + + goto writecheck; + + } else { + if (td->pages[i] == -1) { + writeops = 0x01; + goto create; + } + rd = td; + goto writecheck; + } +create: + /* Create the bad block table by scanning the device ? */ + if (!(td->options & NAND_BBT_CREATE)) + continue; + + /* Create the table in memory by scanning the chip(s) */ + create_bbt (mtd, buf, bd, chipsel); + + td->version[i] = 1; + if (md) + md->version[i] = 1; +writecheck: + /* read back first ? */ + if (rd) + read_abs_bbt (mtd, buf, rd, chipsel); + /* If they weren't versioned, read both. */ + if (rd2) + read_abs_bbt (mtd, buf, rd2, chipsel); + + /* Write the bad block table to the device ? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = write_bbt (mtd, buf, td, md, chipsel); + if (res < 0) + return res; + } + + /* Write the mirror bad block table to the device ? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt (mtd, buf, md, td, chipsel); + if (res < 0) + return res; + } + } + return 0; +} + +/** + * mark_bbt_regions - [GENERIC] mark the bad block table regions + * @mtd: MTD device structure + * @td: bad block table descriptor + * + * The bad block table regions are marked as "bad" to prevent + * accidental erasures / writes. The regions are identified by + * the mark 0x02. +*/ +static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd->priv; + int i, j, chips, block, nrblocks, update; + uint8_t oldval, newval; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); + } else { + chips = 1; + nrblocks = (int)(mtd->size >> this->bbt_erase_shift); + } + + for (i = 0; i < chips; i++) { + if ((td->options & NAND_BBT_ABSPAGE) || + !(td->options & NAND_BBT_WRITE)) { + if (td->pages[i] == -1) continue; + block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); + block <<= 1; + oldval = this->bbt[(block >> 3)]; + newval = oldval | (0x2 << (block & 0x06)); + this->bbt[(block >> 3)] = newval; + if ((oldval != newval) && td->reserved_block_code) + nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1)); + continue; + } + update = 0; + if (td->options & NAND_BBT_LASTBLOCK) + block = ((i + 1) * nrblocks) - td->maxblocks; + else + block = i * nrblocks; + block <<= 1; + for (j = 0; j < td->maxblocks; j++) { + oldval = this->bbt[(block >> 3)]; + newval = oldval | (0x2 << (block & 0x06)); + this->bbt[(block >> 3)] = newval; + if (oldval != newval) update = 1; + block += 2; + } + /* If we want reserved blocks to be recorded to flash, and some + new ones have been marked, then we need to update the stored + bbts. This should only happen once. */ + if (update && td->reserved_block_code) + nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1)); + } +} + +/** + * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function checks, if a bad block table(s) is/are already + * available. If not it scans the device for manufacturer + * marked good / bad blocks and writes the bad block table(s) to + * the selected place. + * + * The bad block table memory is allocated here. It must be freed + * by calling the nand_free_bbt function. + * +*/ +int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd->priv; + int len, res = 0; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + len = mtd->size >> (this->bbt_erase_shift + 2); + /* Allocate memory (2bit per block) */ + this->bbt = kmalloc (len, GFP_KERNEL); + if (!this->bbt) { + printk (KERN_ERR "nand_scan_bbt: Out of memory\n"); + return -ENOMEM; + } + /* Clear the memory bad block table */ + memset (this->bbt, 0x00, len); + + /* If no primary table decriptor is given, scan the device + * to build a memory based bad block table + */ + if (!td) + return nand_memory_bbt(mtd, bd); + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = kmalloc (len, GFP_KERNEL); + if (!buf) { + printk (KERN_ERR "nand_bbt: Out of memory\n"); + kfree (this->bbt); + this->bbt = NULL; + return -ENOMEM; + } + + /* Is the bbt at a given page ? */ + if (td->options & NAND_BBT_ABSPAGE) { + res = read_abs_bbts (mtd, buf, td, md); + } else { + /* Search the bad block table using a pattern in oob */ + res = search_read_bbts (mtd, buf, td, md); + } + + if (res) + res = check_create (mtd, buf, bd); + + /* Prevent the bbt regions from erasing / writing */ + mark_bbt_region (mtd, td); + if (md) + mark_bbt_region (mtd, md); + + kfree (buf); + return res; +} + + +/** + * nand_update_bbt - [NAND Interface] update bad block table(s) + * @mtd: MTD device structure + * @offs: the offset of the newly marked block + * + * The function updates the bad block table(s) +*/ +int nand_update_bbt (struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd->priv; + int len, res = 0, writeops = 0; + int chip, chipsel; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + if (!this->bbt || !td) + return -EINVAL; + + len = mtd->size >> (this->bbt_erase_shift + 2); + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = kmalloc (len, GFP_KERNEL); + if (!buf) { + printk (KERN_ERR "nand_update_bbt: Out of memory\n"); + return -ENOMEM; + } + + writeops = md != NULL ? 0x03 : 0x01; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chip = (int) (offs >> this->chip_shift); + chipsel = chip; + } else { + chip = 0; + chipsel = -1; + } + + td->version[chip]++; + if (md) + md->version[chip]++; + + /* Write the bad block table to the device ? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = write_bbt (mtd, buf, td, md, chipsel); + if (res < 0) + goto out; + } + /* Write the mirror bad block table to the device ? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt (mtd, buf, md, td, chipsel); + } + +out: + kfree (buf); + return res; +} + +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks + * + * The memory based patterns just + */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { + .options = 0, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr smallpage_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; + +static struct nand_bbt_descr agand_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 0x20, + .len = 6, + .pattern = scan_agand_pattern +}; + +/* Generic flash bbt decriptors +*/ +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +/** + * nand_default_bbt - [NAND Interface] Select a default bad block table for the device + * @mtd: MTD device structure + * + * This function selects the default bad block table + * support for the device and calls the nand_scan_bbt function + * +*/ +int nand_default_bbt (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + /* Default for AG-AND. We must use a flash based + * bad block table as the devices have factory marked + * _good_ blocks. Erasing those blocks leads to loss + * of the good / bad information, so we _must_ store + * this information in a good / bad table during + * startup + */ + if (this->options & NAND_IS_AND) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + this->options |= NAND_USE_FLASH_BBT; + return nand_scan_bbt (mtd, &agand_flashbased); + } + + + /* Is a flash based bad block table requested ? */ + if (this->options & NAND_USE_FLASH_BBT) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_flashbased : &smallpage_flashbased; + } + } else { + this->bbt_td = NULL; + this->bbt_md = NULL; + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + } + return nand_scan_bbt (mtd, this->badblock_pattern); +} + +/** + * nand_isbad_bbt - [NAND Interface] Check if a block is bad + * @mtd: MTD device structure + * @offs: offset in the device + * @allowbbt: allow access to bad block table region + * +*/ +int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt) +{ + struct nand_chip *this = mtd->priv; + int block; + uint8_t res; + + /* Get block number * 2 */ + block = (int) (offs >> (this->bbt_erase_shift - 1)); + res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; + + DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int)offs, res, block >> 1); + + switch ((int)res) { + case 0x00: return 0; + case 0x01: return 1; + case 0x02: return allowbbt ? 0 : 1; + } + return 1; +} + +#endif diff --git a/drivers/nand/nand_ecc.c b/drivers/nand/nand_ecc.c new file mode 100644 index 0000000000..dc9db4b480 --- /dev/null +++ b/drivers/nand/nand_ecc.c @@ -0,0 +1,244 @@ +/* + * This file contains an ECC algorithm from Toshiba that detects and + * corrects 1 bit errors in a 256 byte block of data. + * + * drivers/mtd/nand/nand_ecc.c + * + * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) + * Toshiba America Electronics Components, Inc. + * + * $Id: nand_ecc.c,v 1.14 2004/06/16 15:34:37 gleixner Exp $ + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 or (at your option) any + * later version. + * + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * As a special exception, if other files instantiate templates or use + * macros or inline functions from these files, or you compile these + * files and link them with other works to produce a work based on these + * files, these files do not by themselves cause the resulting work to be + * covered by the GNU General Public License. However the source code for + * these files must still be made available in accordance with section (3) + * of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + */ + +#include + +#if (CONFIG_COMMANDS & CFG_CMD_NAND) + +/* + * 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 +}; + + +/** + * nand_trans_result - [GENERIC] create non-inverted ECC + * @reg2: line parity reg 2 + * @reg3: line parity reg 3 + * @ecc_code: ecc + * + * Creates non-inverted ECC code from line parity + */ +static void nand_trans_result(u_char reg2, u_char reg3, + u_char *ecc_code) +{ + 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; + } + + /* 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; + } + + /* Store two of the ECC bytes */ + ecc_code[0] = tmp1; + ecc_code[1] = tmp2; +} + +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block + * @mtd: MTD block structure + * @dat: raw data + * @ecc_code: buffer for ECC + */ +int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ + u_char idx, reg1, reg2, reg3; + int j; + + /* Initialize variables */ + reg1 = reg2 = reg3 = 0; + ecc_code[0] = ecc_code[1] = ecc_code[2] = 0; + + /* Build up column parity */ + for(j = 0; j < 256; j++) { + + /* Get CP0 - CP5 from table */ + idx = nand_ecc_precalc_table[dat[j]]; + reg1 ^= (idx & 0x3f); + + /* All bit XOR = 1 ? */ + if (idx & 0x40) { + reg3 ^= (u_char) j; + reg2 ^= ~((u_char) j); + } + } + + /* Create non-inverted ECC code from line parity */ + nand_trans_result(reg2, 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; + return 0; +} + +/** + * nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @dat: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct a 1 bit error for 256 byte block + */ +int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) +{ + u_char a, b, c, d1, d2, d3, add, bit, i; + + /* Do error detection */ + d1 = calc_ecc[0] ^ read_ecc[0]; + d2 = calc_ecc[1] ^ read_ecc[1]; + d3 = calc_ecc[2] ^ read_ecc[2]; + + if ((d1 | d2 | d3) == 0) { + /* No errors */ + return 0; + } + 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; + } + } + } + + /* Should never happen */ + return -1; +} + +#endif /* CONFIG_COMMANDS & CFG_CMD_NAND */ diff --git a/drivers/nand/nand_ids.c b/drivers/nand/nand_ids.c new file mode 100644 index 0000000000..5df1e8971d --- /dev/null +++ b/drivers/nand/nand_ids.c @@ -0,0 +1,127 @@ +/* + * drivers/mtd/nandids.c + * + * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) + * + * $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 gleixner Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include + +#if (CONFIG_COMMANDS & CFG_CMD_NAND) + +#include + +/* +* Chip ID list +* +* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, +* options +* +* Pagesize; 0, 256, 512 +* 0 get this information from the extended chip ID ++ 256 256 Byte page size +* 512 512 Byte page size +*/ +struct nand_flash_dev nand_flash_ids[] = { + {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, + {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, + {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, + {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, + + {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, + {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + + {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, + {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, + {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, + {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, + {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, + {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, + {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, + {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, + + {"NAND 512MiB 3,3V 8-bit", 0xDC, 512, 512, 0x4000, 0}, + + /* These are the new chips with large page size. The pagesize + * and the erasesize is determined from the extended id bytes + */ + /* 1 Gigabit */ + {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 2 Gigabit */ + {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 4 Gigabit */ + {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 8 Gigabit */ + {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 16 Gigabit */ + {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout ! + * The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes + * 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 + * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go + * There are more speed improvements for reads and writes possible, but not implemented now + */ + {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY}, + + {NULL,} +}; + +/* +* Manufacturer ID list +*/ +struct nand_manufacturers nand_manuf_ids[] = { + {NAND_MFR_TOSHIBA, "Toshiba"}, + {NAND_MFR_SAMSUNG, "Samsung"}, + {NAND_MFR_FUJITSU, "Fujitsu"}, + {NAND_MFR_NATIONAL, "National"}, + {NAND_MFR_RENESAS, "Renesas"}, + {NAND_MFR_STMICRO, "ST Micro"}, + {0x0, "Unknown"} +}; +#endif diff --git a/fs/jffs2/jffs2_nand_1pass.c b/fs/jffs2/jffs2_nand_1pass.c new file mode 100644 index 0000000000..ce80a43fa1 --- /dev/null +++ b/fs/jffs2/jffs2_nand_1pass.c @@ -0,0 +1,1045 @@ +#include + +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) + +#include +#include +#include + +#include +#include +#include + +#include "jffs2_nand_private.h" + +#define NODE_CHUNK 1024 /* size of memory allocation chunk in b_nodes */ + +/* Debugging switches */ +#undef DEBUG_DIRENTS /* print directory entry list after scan */ +#undef DEBUG_FRAGMENTS /* print fragment list after scan */ +#undef DEBUG /* enable debugging messages */ + +#ifdef DEBUG +# define DEBUGF(fmt,args...) printf(fmt ,##args) +#else +# define DEBUGF(fmt,args...) +#endif + +static int nanddev = -1; /* nand device of current partition */ +static nand_info_t *nand; + +/* Compression names */ +static char *compr_names[] = { + "NONE", + "ZERO", + "RTIME", + "RUBINMIPS", + "COPY", + "DYNRUBIN", + "ZLIB", +#if defined(CONFIG_JFFS2_LZO_LZARI) + "LZO", + "LZARI", +#endif +}; + +/* Spinning wheel */ +static char spinner[] = { '|', '/', '-', '\\' }; + +/* Memory management */ +struct mem_block { + unsigned index; + struct mem_block *next; + char nodes[0]; +}; + +static void +free_nodes(struct b_list *list) +{ + while (list->listMemBase != NULL) { + struct mem_block *next = list->listMemBase->next; + free(list->listMemBase); + list->listMemBase = next; + } +} + +static struct b_node * +add_node(struct b_list *list, int size) +{ + u32 index = 0; + struct mem_block *memBase; + struct b_node *b; + + memBase = list->listMemBase; + if (memBase != NULL) + index = memBase->index; + + if (memBase == NULL || index >= NODE_CHUNK) { + /* we need more space before we continue */ + memBase = mmalloc(sizeof(struct mem_block) + NODE_CHUNK * size); + if (memBase == NULL) { + putstr("add_node: malloc failed\n"); + return NULL; + } + memBase->next = list->listMemBase; + index = 0; + } + /* now we have room to add it. */ + b = (struct b_node *)&memBase->nodes[size * index]; + index ++; + + memBase->index = index; + list->listMemBase = memBase; + list->listCount++; + return b; +} + +static struct b_node * +insert_node(struct b_list *list, struct b_node *new) +{ +#ifdef CFG_JFFS2_SORT_FRAGMENTS + struct b_node *b, *prev; + + if (list->listTail != NULL && list->listCompare(new, list->listTail)) + prev = list->listTail; + else if (list->listLast != NULL && list->listCompare(new, list->listLast)) + prev = list->listLast; + else + prev = NULL; + + for (b = (prev ? prev->next : list->listHead); + b != NULL && list->listCompare(new, b); + prev = b, b = b->next) { + list->listLoops++; + } + if (b != NULL) + list->listLast = prev; + + if (b != NULL) { + new->next = b; + if (prev != NULL) + prev->next = new; + else + list->listHead = new; + } else +#endif + { + new->next = (struct b_node *) NULL; + if (list->listTail != NULL) { + list->listTail->next = new; + list->listTail = new; + } else { + list->listTail = list->listHead = new; + } + } + + return new; +} + +static struct b_node * +insert_inode(struct b_list *list, struct jffs2_raw_inode *node, u32 offset) +{ + struct b_inode *new; + + if (!(new = (struct b_inode *)add_node(list, sizeof(struct b_inode)))) { + putstr("add_node failed!\r\n"); + return NULL; + } + new->offset = offset; + new->version = node->version; + new->ino = node->ino; + new->isize = node->isize; + new->csize = node->csize; + + return insert_node(list, (struct b_node *)new); +} + +static struct b_node * +insert_dirent(struct b_list *list, struct jffs2_raw_dirent *node, u32 offset) +{ + struct b_dirent *new; + + if (!(new = (struct b_dirent *)add_node(list, sizeof(struct b_dirent)))) { + putstr("add_node failed!\r\n"); + return NULL; + } + new->offset = offset; + new->version = node->version; + new->pino = node->pino; + new->ino = node->ino; + new->nhash = full_name_hash(node->name, node->nsize); + new->nsize = node->nsize; + new->type = node->type; + + return insert_node(list, (struct b_node *)new); +} + +#ifdef CFG_JFFS2_SORT_FRAGMENTS +/* Sort data entries with the latest version last, so that if there + * is overlapping data the latest version will be used. + */ +static int compare_inodes(struct b_node *new, struct b_node *old) +{ + struct jffs2_raw_inode ojNew; + struct jffs2_raw_inode ojOld; + struct jffs2_raw_inode *jNew = + (struct jffs2_raw_inode *)get_fl_mem(new->offset, sizeof(ojNew), &ojNew); + struct jffs2_raw_inode *jOld = + (struct jffs2_raw_inode *)get_fl_mem(old->offset, sizeof(ojOld), &ojOld); + + return jNew->version > jOld->version; +} + +/* Sort directory entries so all entries in the same directory + * with the same name are grouped together, with the latest version + * last. This makes it easy to eliminate all but the latest version + * by marking the previous version dead by setting the inode to 0. + */ +static int compare_dirents(struct b_node *new, struct b_node *old) +{ + struct jffs2_raw_dirent ojNew; + struct jffs2_raw_dirent ojOld; + struct jffs2_raw_dirent *jNew = + (struct jffs2_raw_dirent *)get_fl_mem(new->offset, sizeof(ojNew), &ojNew); + struct jffs2_raw_dirent *jOld = + (struct jffs2_raw_dirent *)get_fl_mem(old->offset, sizeof(ojOld), &ojOld); + int cmp; + + /* ascending sort by pino */ + if (jNew->pino != jOld->pino) + return jNew->pino > jOld->pino; + + /* pino is the same, so use ascending sort by nsize, so + * we don't do strncmp unless we really must. + */ + if (jNew->nsize != jOld->nsize) + return jNew->nsize > jOld->nsize; + + /* length is also the same, so use ascending sort by name + */ + cmp = strncmp(jNew->name, jOld->name, jNew->nsize); + if (cmp != 0) + return cmp > 0; + + /* we have duplicate names in this directory, so use ascending + * sort by version + */ + if (jNew->version > jOld->version) { + /* since jNew is newer, we know jOld is not valid, so + * mark it with inode 0 and it will not be used + */ + jOld->ino = 0; + return 1; + } + + return 0; +} +#endif + +static u32 +jffs_init_1pass_list(struct part_info *part) +{ + struct b_lists *pL; + + if (part->jffs2_priv != NULL) { + pL = (struct b_lists *)part->jffs2_priv; + free_nodes(&pL->frag); + free_nodes(&pL->dir); + free(pL); + } + if (NULL != (part->jffs2_priv = malloc(sizeof(struct b_lists)))) { + pL = (struct b_lists *)part->jffs2_priv; + + memset(pL, 0, sizeof(*pL)); +#ifdef CFG_JFFS2_SORT_FRAGMENTS + pL->dir.listCompare = compare_dirents; + pL->frag.listCompare = compare_inodes; +#endif + } + return 0; +} + +/* find the inode from the slashless name given a parent */ +static long +jffs2_1pass_read_inode(struct b_lists *pL, u32 ino, char *dest, + struct stat *stat) +{ + struct b_inode *jNode; + u32 totalSize = 0; + u32 latestVersion = 0; + long ret; + +#ifdef CFG_JFFS2_SORT_FRAGMENTS + /* Find file size before loading any data, so fragments that + * start past the end of file can be ignored. A fragment + * that is partially in the file is loaded, so extra data may + * be loaded up to the next 4K boundary above the file size. + * This shouldn't cause trouble when loading kernel images, so + * we will live with it. + */ + for (jNode = (struct b_inode *)pL->frag.listHead; jNode; jNode = jNode->next) { + if ((ino == jNode->ino)) { + /* get actual file length from the newest node */ + if (jNode->version >= latestVersion) { + totalSize = jNode->isize; + latestVersion = jNode->version; + } + } + } +#endif + + for (jNode = (struct b_inode *)pL->frag.listHead; jNode; jNode = jNode->next) { + if ((ino != jNode->ino)) + continue; +#ifndef CFG_JFFS2_SORT_FRAGMENTS + /* get actual file length from the newest node */ + if (jNode->version >= latestVersion) { + totalSize = jNode->isize; + latestVersion = jNode->version; + } +#endif + if (dest || stat) { + char *src, *dst; + char data[4096 + sizeof(struct jffs2_raw_inode)]; + struct jffs2_raw_inode *inode; + size_t len; + + inode = (struct jffs2_raw_inode *)&data; + len = sizeof(struct jffs2_raw_inode); + if (dest) + len += jNode->csize; + nand_read(nand, jNode->offset, &len, inode); + /* ignore data behind latest known EOF */ + if (inode->offset > totalSize) + continue; + + if (stat) { + stat->st_mtime = inode->mtime; + stat->st_mode = inode->mode; + stat->st_ino = inode->ino; + stat->st_size = totalSize; + } + + if (!dest) + continue; + + src = ((char *) inode) + sizeof(struct jffs2_raw_inode); + dst = (char *) (dest + inode->offset); + + switch (inode->compr) { + case JFFS2_COMPR_NONE: + ret = 0; + memcpy(dst, src, inode->dsize); + break; + case JFFS2_COMPR_ZERO: + ret = 0; + memset(dst, 0, inode->dsize); + break; + case JFFS2_COMPR_RTIME: + ret = 0; + rtime_decompress(src, dst, inode->csize, inode->dsize); + break; + case JFFS2_COMPR_DYNRUBIN: + /* this is slow but it works */ + ret = 0; + dynrubin_decompress(src, dst, inode->csize, inode->dsize); + break; + case JFFS2_COMPR_ZLIB: + ret = zlib_decompress(src, dst, inode->csize, inode->dsize); + break; +#if defined(CONFIG_JFFS2_LZO_LZARI) + case JFFS2_COMPR_LZO: + ret = lzo_decompress(src, dst, inode->csize, inode->dsize); + break; + case JFFS2_COMPR_LZARI: + ret = lzari_decompress(src, dst, inode->csize, inode->dsize); + break; +#endif + default: + /* unknown */ + putLabeledWord("UNKOWN COMPRESSION METHOD = ", inode->compr); + return -1; + } + } + } + + return totalSize; +} + +/* find the inode from the slashless name given a parent */ +static u32 +jffs2_1pass_find_inode(struct b_lists * pL, const char *name, u32 pino) +{ + struct b_dirent *jDir; + int len = strlen(name); /* name is assumed slash free */ + unsigned int nhash = full_name_hash(name, len); + u32 version = 0; + u32 inode = 0; + + /* we need to search all and return the inode with the highest version */ + for (jDir = (struct b_dirent *)pL->dir.listHead; jDir; jDir = jDir->next) { + if ((pino == jDir->pino) && (jDir->ino) && /* 0 for unlink */ + (len == jDir->nsize) && (nhash == jDir->nhash)) { + /* TODO: compare name */ + if (jDir->version < version) + continue; + + if (jDir->version == version && inode != 0) { + /* I'm pretty sure this isn't legal */ + putstr(" ** ERROR ** "); +// putnstr(jDir->name, jDir->nsize); +// putLabeledWord(" has dup version =", version); + } + inode = jDir->ino; + version = jDir->version; + } + } + return inode; +} + +char *mkmodestr(unsigned long mode, char *str) +{ + static const char *l = "xwr"; + int mask = 1, i; + char c; + + switch (mode & S_IFMT) { + case S_IFDIR: str[0] = 'd'; break; + case S_IFBLK: str[0] = 'b'; break; + case S_IFCHR: str[0] = 'c'; break; + case S_IFIFO: str[0] = 'f'; break; + case S_IFLNK: str[0] = 'l'; break; + case S_IFSOCK: str[0] = 's'; break; + case S_IFREG: str[0] = '-'; break; + default: str[0] = '?'; + } + + for(i = 0; i < 9; i++) { + c = l[i%3]; + str[9-i] = (mode & mask)?c:'-'; + mask = mask<<1; + } + + if(mode & S_ISUID) str[3] = (mode & S_IXUSR)?'s':'S'; + if(mode & S_ISGID) str[6] = (mode & S_IXGRP)?'s':'S'; + if(mode & S_ISVTX) str[9] = (mode & S_IXOTH)?'t':'T'; + str[10] = '\0'; + return str; +} + +static inline void dump_stat(struct stat *st, const char *name) +{ + char str[20]; + char s[64], *p; + + if (st->st_mtime == (time_t)(-1)) /* some ctimes really hate -1 */ + st->st_mtime = 1; + + ctime_r(&st->st_mtime, s/*,64*/); /* newlib ctime doesn't have buflen */ + + if ((p = strchr(s,'\n')) != NULL) *p = '\0'; + if ((p = strchr(s,'\r')) != NULL) *p = '\0'; + +/* + printf("%6lo %s %8ld %s %s\n", st->st_mode, mkmodestr(st->st_mode, str), + st->st_size, s, name); +*/ + + printf(" %s %8ld %s %s", mkmodestr(st->st_mode,str), st->st_size, s, name); +} + +static inline int +dump_inode(struct b_lists *pL, struct b_dirent *d, struct b_inode *i) +{ + char fname[JFFS2_MAX_NAME_LEN + 1]; + struct stat st; + size_t len; + + if(!d || !i) return -1; + len = d->nsize; + nand_read(nand, d->offset + sizeof(struct jffs2_raw_dirent), + &len, &fname); + fname[d->nsize] = '\0'; + + memset(&st, 0, sizeof(st)); + + jffs2_1pass_read_inode(pL, i->ino, NULL, &st); + + dump_stat(&st, fname); +/* FIXME + if (d->type == DT_LNK) { + unsigned char *src = (unsigned char *) (&i[1]); + putstr(" -> "); + putnstr(src, (int)i->dsize); + } +*/ + putstr("\r\n"); + + return 0; +} + +/* list inodes with the given pino */ +static u32 +jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino) +{ + struct b_dirent *jDir; + u32 i_version = 0; + + for (jDir = (struct b_dirent *)pL->dir.listHead; jDir; jDir = jDir->next) { + if ((pino == jDir->pino) && (jDir->ino)) { /* ino=0 -> unlink */ + struct b_inode *jNode = (struct b_inode *)pL->frag.listHead; + struct b_inode *i = NULL; + + while (jNode) { + if (jNode->ino == jDir->ino && jNode->version >= i_version) { + i_version = jNode->version; + i = jNode; + } + jNode = jNode->next; + } + dump_inode(pL, jDir, i); + } + } + return pino; +} + +static u32 +jffs2_1pass_search_inode(struct b_lists * pL, const char *fname, u32 pino) +{ + int i; + char tmp[256]; + char working_tmp[256]; + char *c; + + /* discard any leading slash */ + i = 0; + while (fname[i] == '/') + i++; + strcpy(tmp, &fname[i]); + + while ((c = (char *) strchr(tmp, '/'))) /* we are still dired searching */ + { + strncpy(working_tmp, tmp, c - tmp); + working_tmp[c - tmp] = '\0'; +#if 0 + putstr("search_inode: tmp = "); + putstr(tmp); + putstr("\r\n"); + putstr("search_inode: wtmp = "); + putstr(working_tmp); + putstr("\r\n"); + putstr("search_inode: c = "); + putstr(c); + putstr("\r\n"); +#endif + for (i = 0; i < strlen(c) - 1; i++) + tmp[i] = c[i + 1]; + tmp[i] = '\0'; +#if 0 + putstr("search_inode: post tmp = "); + putstr(tmp); + putstr("\r\n"); +#endif + + if (!(pino = jffs2_1pass_find_inode(pL, working_tmp, pino))) { + putstr("find_inode failed for name="); + putstr(working_tmp); + putstr("\r\n"); + return 0; + } + } + /* this is for the bare filename, directories have already been mapped */ + if (!(pino = jffs2_1pass_find_inode(pL, tmp, pino))) { + putstr("find_inode failed for name="); + putstr(tmp); + putstr("\r\n"); + return 0; + } + return pino; + +} + +static u32 +jffs2_1pass_resolve_inode(struct b_lists * pL, u32 ino) +{ + struct b_dirent *jDir; + struct b_inode *jNode; + u8 jDirFoundType = 0; + u32 jDirFoundIno = 0; + u32 jDirFoundPino = 0; + char tmp[JFFS2_MAX_NAME_LEN + 1]; + u32 version = 0; + u32 pino; + + /* we need to search all and return the inode with the highest version */ + for (jDir = (struct b_dirent *)pL->dir.listHead; jDir; jDir = jDir->next) { + if (ino == jDir->ino) { + if (jDir->version < version) + continue; + + if (jDir->version == version && jDirFoundType) { + /* I'm pretty sure this isn't legal */ + putstr(" ** ERROR ** "); +// putnstr(jDir->name, jDir->nsize); +// putLabeledWord(" has dup version (resolve) = ", +// version); + } + + jDirFoundType = jDir->type; + jDirFoundIno = jDir->ino; + jDirFoundPino = jDir->pino; + version = jDir->version; + } + } + /* now we found the right entry again. (shoulda returned inode*) */ + if (jDirFoundType != DT_LNK) + return jDirFoundIno; + + /* it's a soft link so we follow it again. */ + for (jNode = (struct b_inode *)pL->frag.listHead; jNode; jNode = jNode->next) { + if (jNode->ino == jDirFoundIno) { + size_t len = jNode->csize; + nand_read(nand, jNode->offset + sizeof(struct jffs2_raw_inode), &len, &tmp); + tmp[jNode->csize] = '\0'; + break; + } + } + /* ok so the name of the new file to find is in tmp */ + /* if it starts with a slash it is root based else shared dirs */ + if (tmp[0] == '/') + pino = 1; + else + pino = jDirFoundPino; + + return jffs2_1pass_search_inode(pL, tmp, pino); +} + +static u32 +jffs2_1pass_search_list_inodes(struct b_lists * pL, const char *fname, u32 pino) +{ + int i; + char tmp[256]; + char working_tmp[256]; + char *c; + + /* discard any leading slash */ + i = 0; + while (fname[i] == '/') + i++; + strcpy(tmp, &fname[i]); + working_tmp[0] = '\0'; + while ((c = (char *) strchr(tmp, '/'))) /* we are still dired searching */ + { + strncpy(working_tmp, tmp, c - tmp); + working_tmp[c - tmp] = '\0'; + for (i = 0; i < strlen(c) - 1; i++) + tmp[i] = c[i + 1]; + tmp[i] = '\0'; + /* only a failure if we arent looking at top level */ + if (!(pino = jffs2_1pass_find_inode(pL, working_tmp, pino)) && + (working_tmp[0])) { + putstr("find_inode failed for name="); + putstr(working_tmp); + putstr("\r\n"); + return 0; + } + } + + if (tmp[0] && !(pino = jffs2_1pass_find_inode(pL, tmp, pino))) { + putstr("find_inode failed for name="); + putstr(tmp); + putstr("\r\n"); + return 0; + } + /* this is for the bare filename, directories have already been mapped */ + if (!(pino = jffs2_1pass_list_inodes(pL, pino))) { + putstr("find_inode failed for name="); + putstr(tmp); + putstr("\r\n"); + return 0; + } + return pino; + +} + +unsigned char +jffs2_1pass_rescan_needed(struct part_info *part) +{ + struct b_node *b; + struct jffs2_unknown_node onode; + struct jffs2_unknown_node *node; + struct b_lists *pL = (struct b_lists *)part->jffs2_priv; + + if (part->jffs2_priv == 0){ + DEBUGF ("rescan: First time in use\n"); + return 1; + } + /* if we have no list, we need to rescan */ + if (pL->frag.listCount == 0) { + DEBUGF ("rescan: fraglist zero\n"); + return 1; + } + + /* or if we are scanning a new partition */ + if (pL->partOffset != part->offset) { + DEBUGF ("rescan: different partition\n"); + return 1; + } + +#if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) + if (nanddev != (int)part->usr_priv - 1) { + DEBUGF ("rescan: nand device changed\n"); + return -1; + } +#endif /* defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) */ + /* FIXME */ +#if 0 + /* but suppose someone reflashed a partition at the same offset... */ + b = pL->dir.listHead; + while (b) { + node = (struct jffs2_unknown_node *) get_fl_mem(b->offset, + sizeof(onode), &onode); + if (node->nodetype != JFFS2_NODETYPE_DIRENT) { + DEBUGF ("rescan: fs changed beneath me? (%lx)\n", + (unsigned long) b->offset); + return 1; + } + b = b->next; + } +#endif + return 0; +} + +#ifdef DEBUG_FRAGMENTS +static void +dump_fragments(struct b_lists *pL) +{ + struct b_node *b; + struct jffs2_raw_inode ojNode; + struct jffs2_raw_inode *jNode; + + putstr("\r\n\r\n******The fragment Entries******\r\n"); + b = pL->frag.listHead; + while (b) { + jNode = (struct jffs2_raw_inode *) get_fl_mem(b->offset, + sizeof(ojNode), &ojNode); + putLabeledWord("\r\n\tbuild_list: FLASH_OFFSET = ", b->offset); + putLabeledWord("\tbuild_list: totlen = ", jNode->totlen); + putLabeledWord("\tbuild_list: inode = ", jNode->ino); + putLabeledWord("\tbuild_list: version = ", jNode->version); + putLabeledWord("\tbuild_list: isize = ", jNode->isize); + putLabeledWord("\tbuild_list: atime = ", jNode->atime); + putLabeledWord("\tbuild_list: offset = ", jNode->offset); + putLabeledWord("\tbuild_list: csize = ", jNode->csize); + putLabeledWord("\tbuild_list: dsize = ", jNode->dsize); + putLabeledWord("\tbuild_list: compr = ", jNode->compr); + putLabeledWord("\tbuild_list: usercompr = ", jNode->usercompr); + putLabeledWord("\tbuild_list: flags = ", jNode->flags); + putLabeledWord("\tbuild_list: offset = ", b->offset); /* FIXME: ? [RS] */ + b = b->next; + } +} +#endif + +#ifdef DEBUG_DIRENTS +static void +dump_dirents(struct b_lists *pL) +{ + struct b_node *b; + struct jffs2_raw_dirent *jDir; + + putstr("\r\n\r\n******The directory Entries******\r\n"); + b = pL->dir.listHead; + while (b) { + jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset); + putstr("\r\n"); + putnstr(jDir->name, jDir->nsize); + putLabeledWord("\r\n\tbuild_list: magic = ", jDir->magic); + putLabeledWord("\tbuild_list: nodetype = ", jDir->nodetype); + putLabeledWord("\tbuild_list: hdr_crc = ", jDir->hdr_crc); + putLabeledWord("\tbuild_list: pino = ", jDir->pino); + putLabeledWord("\tbuild_list: version = ", jDir->version); + putLabeledWord("\tbuild_list: ino = ", jDir->ino); + putLabeledWord("\tbuild_list: mctime = ", jDir->mctime); + putLabeledWord("\tbuild_list: nsize = ", jDir->nsize); + putLabeledWord("\tbuild_list: type = ", jDir->type); + putLabeledWord("\tbuild_list: node_crc = ", jDir->node_crc); + putLabeledWord("\tbuild_list: name_crc = ", jDir->name_crc); + putLabeledWord("\tbuild_list: offset = ", b->offset); /* FIXME: ? [RS] */ + b = b->next; + put_fl_mem(jDir); + } +} +#endif + +static int +jffs2_fill_scan_buf(nand_info_t *nand, unsigned char *buf, + unsigned ofs, unsigned len) +{ + int ret; + unsigned olen; + + olen = len; + ret = nand_read(nand, ofs, &olen, buf); + if (ret) { + printf("nand_read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret); + return ret; + } + if (olen < len) { + printf("Read at 0x%x gave only 0x%x bytes\n", ofs, olen); + return -1; + } + return 0; +} + +#define EMPTY_SCAN_SIZE 1024 +static u32 +jffs2_1pass_build_lists(struct part_info * part) +{ + struct b_lists *pL; + struct jffs2_unknown_node *node; + unsigned nr_blocks, sectorsize, ofs, offset; + char *buf; + int i; + u32 counter = 0; + u32 counter4 = 0; + u32 counterF = 0; + u32 counterN = 0; + +#if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) + nanddev = (int)part->usr_priv - 1; + nand = &nand_info[nanddev]; +#endif /* defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND) */ + + /* if we are building a list we need to refresh the cache. */ + jffs_init_1pass_list(part); + pL = (struct b_lists *)part->jffs2_priv; + pL->partOffset = part->offset; + puts ("Scanning JFFS2 FS: "); + + sectorsize = nand->erasesize; + nr_blocks = part->size / sectorsize; + buf = malloc(sectorsize); + if (!buf) + return 0; + + for (i = 0; i < nr_blocks; i++) { + printf("\b\b%c ", spinner[counter++ % sizeof(spinner)]); + + offset = part->offset + i * sectorsize; + + if (nand_block_isbad(nand, offset)) + continue; + + if (jffs2_fill_scan_buf(nand, buf, offset, EMPTY_SCAN_SIZE)) + return 0; + + ofs = 0; + /* Scan only 4KiB of 0xFF before declaring it's empty */ + while (ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) + ofs += 4; + if (ofs == EMPTY_SCAN_SIZE) + continue; + + if (jffs2_fill_scan_buf(nand, buf + EMPTY_SCAN_SIZE, offset + EMPTY_SCAN_SIZE, sectorsize - EMPTY_SCAN_SIZE)) + return 0; + offset += ofs; + + while (ofs < sectorsize - sizeof(struct jffs2_unknown_node)) { + node = (struct jffs2_unknown_node *)&buf[ofs]; + if (node->magic != JFFS2_MAGIC_BITMASK || !hdr_crc(node)) { + offset += 4; + ofs += 4; + counter4++; + continue; + } + /* if its a fragment add it */ + if (node->nodetype == JFFS2_NODETYPE_INODE && + inode_crc((struct jffs2_raw_inode *) node)) { + if (insert_inode(&pL->frag, (struct jffs2_raw_inode *) node, + offset) == NULL) { + return 0; + } + } else if (node->nodetype == JFFS2_NODETYPE_DIRENT && + dirent_crc((struct jffs2_raw_dirent *) node) && + dirent_name_crc((struct jffs2_raw_dirent *) node)) { + if (! (counterN%100)) + puts ("\b\b. "); + if (insert_dirent(&pL->dir, (struct jffs2_raw_dirent *) node, + offset) == NULL) { + return 0; + } + counterN++; + } else if (node->nodetype == JFFS2_NODETYPE_CLEANMARKER) { + if (node->totlen != sizeof(struct jffs2_unknown_node)) + printf("OOPS Cleanmarker has bad size " + "%d != %d\n", node->totlen, + sizeof(struct jffs2_unknown_node)); + } else if (node->nodetype == JFFS2_NODETYPE_PADDING) { + if (node->totlen < sizeof(struct jffs2_unknown_node)) + printf("OOPS Padding has bad size " + "%d < %d\n", node->totlen, + sizeof(struct jffs2_unknown_node)); + } else { + printf("Unknown node type: %x len %d " + "offset 0x%x\n", node->nodetype, + node->totlen, offset); + } + offset += ((node->totlen + 3) & ~3); + ofs += ((node->totlen + 3) & ~3); + counterF++; + } + } + + putstr("\b\b done.\r\n"); /* close off the dots */ + +#if 0 + putLabeledWord("dir entries = ", pL->dir.listCount); + putLabeledWord("frag entries = ", pL->frag.listCount); + putLabeledWord("+4 increments = ", counter4); + putLabeledWord("+file_offset increments = ", counterF); +#endif + +#ifdef DEBUG_DIRENTS + dump_dirents(pL); +#endif + +#ifdef DEBUG_FRAGMENTS + dump_fragments(pL); +#endif + + /* give visual feedback that we are done scanning the flash */ + led_blink(0x0, 0x0, 0x1, 0x1); /* off, forever, on 100ms, off 100ms */ + free(buf); + + return 1; +} + + +static u32 +jffs2_1pass_fill_info(struct b_lists * pL, struct b_jffs2_info * piL) +{ + struct b_node *b; + struct jffs2_raw_inode ojNode; + struct jffs2_raw_inode *jNode; + int i; + + for (i = 0; i < JFFS2_NUM_COMPR; i++) { + piL->compr_info[i].num_frags = 0; + piL->compr_info[i].compr_sum = 0; + piL->compr_info[i].decompr_sum = 0; + } +/* FIXME + b = pL->frag.listHead; + while (b) { + jNode = (struct jffs2_raw_inode *) get_fl_mem(b->offset, + sizeof(ojNode), &ojNode); + if (jNode->compr < JFFS2_NUM_COMPR) { + piL->compr_info[jNode->compr].num_frags++; + piL->compr_info[jNode->compr].compr_sum += jNode->csize; + piL->compr_info[jNode->compr].decompr_sum += jNode->dsize; + } + b = b->next; + } +*/ + return 0; +} + + +static struct b_lists * +jffs2_get_list(struct part_info * part, const char *who) +{ + if (jffs2_1pass_rescan_needed(part)) { + if (!jffs2_1pass_build_lists(part)) { + printf("%s: Failed to scan JFFSv2 file structure\n", who); + return NULL; + } + } + return (struct b_lists *)part->jffs2_priv; +} + + +/* Print directory / file contents */ +u32 +jffs2_1pass_ls(struct part_info * part, const char *fname) +{ + struct b_lists *pl; + long ret = 0; + u32 inode; + + if (! (pl = jffs2_get_list(part, "ls"))) + return 0; + + if (! (inode = jffs2_1pass_search_list_inodes(pl, fname, 1))) { + putstr("ls: Failed to scan jffs2 file structure\r\n"); + return 0; + } + +#if 0 + putLabeledWord("found file at inode = ", inode); + putLabeledWord("read_inode returns = ", ret); +#endif + + return ret; +} + + +/* Load a file from flash into memory. fname can be a full path */ +u32 +jffs2_1pass_load(char *dest, struct part_info * part, const char *fname) +{ + + struct b_lists *pl; + long ret = 0; + u32 inode; + + if (! (pl = jffs2_get_list(part, "load"))) + return 0; + + if (! (inode = jffs2_1pass_search_inode(pl, fname, 1))) { + putstr("load: Failed to find inode\r\n"); + return 0; + } + + /* Resolve symlinks */ + if (! (inode = jffs2_1pass_resolve_inode(pl, inode))) { + putstr("load: Failed to resolve inode structure\r\n"); + return 0; + } + + if ((ret = jffs2_1pass_read_inode(pl, inode, dest, NULL)) < 0) { + putstr("load: Failed to read inode\r\n"); + return 0; + } + + DEBUGF ("load: loaded '%s' to 0x%lx (%ld bytes)\n", fname, + (unsigned long) dest, ret); + return ret; +} + +/* Return information about the fs on this partition */ +u32 +jffs2_1pass_info(struct part_info * part) +{ + struct b_jffs2_info info; + struct b_lists *pl; + int i; + + if (! (pl = jffs2_get_list(part, "info"))) + return 0; + + jffs2_1pass_fill_info(pl, &info); + for (i = 0; i < JFFS2_NUM_COMPR; i++) { + printf ("Compression: %s\n" + "\tfrag count: %d\n" + "\tcompressed sum: %d\n" + "\tuncompressed sum: %d\n", + compr_names[i], + info.compr_info[i].num_frags, + info.compr_info[i].compr_sum, + info.compr_info[i].decompr_sum); + } + return 1; +} + +#endif /* CFG_CMD_JFFS2 */ diff --git a/fs/jffs2/jffs2_nand_private.h b/fs/jffs2/jffs2_nand_private.h new file mode 100644 index 0000000000..18cca8d076 --- /dev/null +++ b/fs/jffs2/jffs2_nand_private.h @@ -0,0 +1,133 @@ +#ifndef jffs2_private_h +#define jffs2_private_h + +#include + +struct b_node { + struct b_node *next; +}; + +struct b_inode { + struct b_inode *next; + u32 offset; /* physical offset to beginning of real inode */ + u32 version; + u32 ino; + u32 isize; + u32 csize; +}; + +struct b_dirent { + struct b_dirent *next; + u32 offset; /* physical offset to beginning of real dirent */ + u32 version; + u32 pino; + u32 ino; + unsigned int nhash; + unsigned char nsize; + unsigned char type; +}; + +struct b_list { + struct b_node *listTail; + struct b_node *listHead; + unsigned int listCount; + struct mem_block *listMemBase; +}; + +struct b_lists { + char *partOffset; + struct b_list dir; + struct b_list frag; +}; + +struct b_compr_info { + u32 num_frags; + u32 compr_sum; + u32 decompr_sum; +}; + +struct b_jffs2_info { + struct b_compr_info compr_info[JFFS2_NUM_COMPR]; +}; + +static inline int +hdr_crc(struct jffs2_unknown_node *node) +{ +#if 1 + u32 crc = crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_unknown_node) - 4); +#else + /* what's the semantics of this? why is this here? */ + u32 crc = crc32_no_comp(~0, (unsigned char *)node, sizeof(struct jffs2_unknown_node) - 4); + + crc ^= ~0; +#endif + if (node->hdr_crc != crc) { + return 0; + } else { + return 1; + } +} + +static inline int +dirent_crc(struct jffs2_raw_dirent *node) +{ + if (node->node_crc != crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_raw_dirent) - 8)) { + return 0; + } else { + return 1; + } +} + +static inline int +dirent_name_crc(struct jffs2_raw_dirent *node) +{ + if (node->name_crc != crc32_no_comp(0, (unsigned char *)&(node->name), node->nsize)) { + return 0; + } else { + return 1; + } +} + +static inline int +inode_crc(struct jffs2_raw_inode *node) +{ + if (node->node_crc != crc32_no_comp(0, (unsigned char *)node, sizeof(struct jffs2_raw_inode) - 8)) { + return 0; + } else { + return 1; + } +} + +/* Borrowed from include/linux/dcache.h */ + +/* Name hashing routines. Initial hash value */ +/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */ +#define init_name_hash() 0 + +/* partial hash update function. Assume roughly 4 bits per character */ +static inline unsigned long +partial_name_hash(unsigned long c, unsigned long prevhash) +{ + return (prevhash + (c << 4) + (c >> 4)) * 11; +} + +/* + * Finally: cut down the number of bits to a int value (and try to avoid + * losing bits) + */ +static inline unsigned long end_name_hash(unsigned long hash) +{ + return (unsigned int) hash; +} + +/* Compute the hash for a name string. */ +static inline unsigned int +full_name_hash(const unsigned char *name, unsigned int len) +{ + unsigned long hash = init_name_hash(); + while (len--) + hash = partial_name_hash(*name++, hash); + return end_name_hash(hash); +} + +#endif /* jffs2_private.h */ diff --git a/include/linux/mtd/compat.h b/include/linux/mtd/compat.h new file mode 100644 index 0000000000..460cd45c80 --- /dev/null +++ b/include/linux/mtd/compat.h @@ -0,0 +1,44 @@ +#ifndef _LINUX_COMPAT_H_ +#define _LINUX_COMPAT_H_ + +#define __user +#define __iomem + +#define ndelay(x) udelay(1) + +#define printk printf + +#define KERN_EMERG +#define KERN_ALERT +#define KERN_CRIT +#define KERN_ERR +#define KERN_WARNING +#define KERN_NOTICE +#define KERN_INFO +#define KERN_DEBUG + +#define kmalloc(size, flags) malloc(size) +#define kfree(ptr) free(ptr) + +/* + * ..and if you can't take the strict + * types, you can specify one yourself. + * + * Or not use min/max at all, of course. + */ +#define min_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) +#define max_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) + +#define BUG() do { \ + printf("U-Boot BUG at %s:%d!\n", __FILE__, __LINE__); \ +} while (0) + +#define BUG_ON(condition) do { if (condition) BUG(); } while(0) + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define PAGE_SIZE 4096 +#endif diff --git a/include/linux/mtd/mtd-abi.h b/include/linux/mtd/mtd-abi.h new file mode 100644 index 0000000000..afe96b59cb --- /dev/null +++ b/include/linux/mtd/mtd-abi.h @@ -0,0 +1,99 @@ +/* + * $Id: mtd-abi.h,v 1.7 2004/11/23 15:37:32 gleixner Exp $ + * + * Portions of MTD ABI definition which are shared by kernel and user space + */ + +#ifndef __MTD_ABI_H__ +#define __MTD_ABI_H__ + +struct erase_info_user { + uint32_t start; + uint32_t length; +}; + +struct mtd_oob_buf { + uint32_t start; + uint32_t length; + unsigned char *ptr; +}; + +#define MTD_ABSENT 0 +#define MTD_RAM 1 +#define MTD_ROM 2 +#define MTD_NORFLASH 3 +#define MTD_NANDFLASH 4 +#define MTD_PEROM 5 +#define MTD_OTHER 14 +#define MTD_UNKNOWN 15 + +#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash) +#define MTD_SET_BITS 2 // Bits can be set +#define MTD_ERASEABLE 4 // Has an erase function +#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible +#define MTD_VOLATILE 16 // Set for RAMs +#define MTD_XIP 32 // eXecute-In-Place possible +#define MTD_OOB 64 // Out-of-band data (NAND flash) +#define MTD_ECC 128 // Device capable of automatic ECC +#define MTD_NO_VIRTBLOCKS 256 // Virtual blocks not allowed + +// Some common devices / combinations of capabilities +#define MTD_CAP_ROM 0 +#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE) +#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE) +#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB) +#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS) + + +// Types of automatic ECC/Checksum available +#define MTD_ECC_NONE 0 // No automatic ECC available +#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip +#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices + +/* ECC byte placement */ +#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) +#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode) +#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme +#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read) +#define MTD_NANDECC_AUTOPL_USR 4 // Use the given autoplacement scheme rather than using the default + +struct mtd_info_user { + uint8_t type; + uint32_t flags; + uint32_t size; // Total size of the MTD + uint32_t erasesize; + uint32_t oobblock; // Size of OOB blocks (e.g. 512) + uint32_t oobsize; // Amount of OOB data per block (e.g. 16) + uint32_t ecctype; + uint32_t eccsize; +}; + +struct region_info_user { + uint32_t offset; /* At which this region starts, + * from the beginning of the MTD */ + uint32_t erasesize; /* For this region */ + uint32_t numblocks; /* Number of blocks in this region */ + uint32_t regionindex; +}; + +#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) +#define MEMERASE _IOW('M', 2, struct erase_info_user) +#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) +#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) +#define MEMLOCK _IOW('M', 5, struct erase_info_user) +#define MEMUNLOCK _IOW('M', 6, struct erase_info_user) +#define MEMGETREGIONCOUNT _IOR('M', 7, int) +#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) +#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) +#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo) +#define MEMGETBADBLOCK _IOW('M', 11, loff_t) +#define MEMSETBADBLOCK _IOW('M', 12, loff_t) + +struct nand_oobinfo { + uint32_t useecc; + uint32_t eccbytes; + uint32_t oobfree[8][2]; + uint32_t eccpos[32]; +}; + +#endif /* __MTD_ABI_H__ */ diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h new file mode 100644 index 0000000000..9ef4cced5e --- /dev/null +++ b/include/linux/mtd/mtd.h @@ -0,0 +1,214 @@ +/* + * $Id: mtd.h,v 1.56 2004/08/09 18:46:04 dmarlin Exp $ + * + * Copyright (C) 1999-2003 David Woodhouse et al. + * + * Released under GPL + */ + +#ifndef __MTD_MTD_H__ +#define __MTD_MTD_H__ +#include +#include + +#define MAX_MTD_DEVICES 16 + +#define MTD_ERASE_PENDING 0x01 +#define MTD_ERASING 0x02 +#define MTD_ERASE_SUSPEND 0x04 +#define MTD_ERASE_DONE 0x08 +#define MTD_ERASE_FAILED 0x10 + +/* If the erase fails, fail_addr might indicate exactly which block failed. If + fail_addr = 0xffffffff, the failure was not at the device level or was not + specific to any particular block. */ +struct erase_info { + struct mtd_info *mtd; + u_int32_t addr; + u_int32_t len; + u_int32_t fail_addr; + u_long time; + u_long retries; + u_int dev; + u_int cell; + void (*callback) (struct erase_info *self); + u_long priv; + u_char state; + struct erase_info *next; +}; + +struct mtd_erase_region_info { + u_int32_t offset; /* At which this region starts, from the beginning of the MTD */ + u_int32_t erasesize; /* For this region */ + u_int32_t numblocks; /* Number of blocks of erasesize in this region */ +}; + +struct mtd_info { + u_char type; + u_int32_t flags; + u_int32_t size; // Total size of the MTD + + /* "Major" erase size for the device. Naïve users may take this + * to be the only erase size available, or may use the more detailed + * information below if they desire + */ + u_int32_t erasesize; + + u_int32_t oobblock; // Size of OOB blocks (e.g. 512) + u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) + u_int32_t oobavail; // Number of bytes in OOB area available for fs + u_int32_t ecctype; + u_int32_t eccsize; + + + // Kernel-only stuff starts here. + char *name; + int index; + + // oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO) + struct nand_oobinfo oobinfo; + + /* Data for variable erase regions. If numeraseregions is zero, + * it means that the whole device has erasesize as given above. + */ + int numeraseregions; + struct mtd_erase_region_info *eraseregions; + + /* This really shouldn't be here. It can go away in 2.5 */ + u_int32_t bank_size; + + int (*erase) (struct mtd_info *mtd, struct erase_info *instr); + + /* This stuff for eXecute-In-Place */ + int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf); + + /* We probably shouldn't allow XIP if the unpoint isn't a NULL */ + void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len); + + + int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + + int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); + int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); + + int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + + /* + * Methods to access the protection register area, present in some + * flash devices. The user data is one time programmable but the + * factory data is read only. + */ + int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + + int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + + /* This function is not yet implemented */ + int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); +#if 0 + /* kvec-based read/write methods. We need these especially for NAND flash, + with its limited number of write cycles per erase. + NB: The 'count' parameter is the number of _vectors_, each of + which contains an (ofs, len) tuple. + */ + int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen); + int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, + size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); + int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); + int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, + size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); +#endif + /* Sync */ + void (*sync) (struct mtd_info *mtd); +#if 0 + /* Chip-supported device locking */ + int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len); + int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len); + + /* Power Management functions */ + int (*suspend) (struct mtd_info *mtd); + void (*resume) (struct mtd_info *mtd); +#endif + /* Bad block management functions */ + int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); + int (*block_markbad) (struct mtd_info *mtd, loff_t ofs); + + void *priv; + + struct module *owner; + int usecount; +}; + + + /* Kernel-side ioctl definitions */ + +extern int add_mtd_device(struct mtd_info *mtd); +extern int del_mtd_device (struct mtd_info *mtd); + +extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); + +extern void put_mtd_device(struct mtd_info *mtd); + +#if 0 +struct mtd_notifier { + void (*add)(struct mtd_info *mtd); + void (*remove)(struct mtd_info *mtd); + struct list_head list; +}; + + +extern void register_mtd_user (struct mtd_notifier *new); +extern int unregister_mtd_user (struct mtd_notifier *old); + +int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen); + +int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs, + unsigned long count, loff_t from, size_t *retlen); +#endif + +#define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args) +#define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d)) +#define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg) +#define MTD_READ(mtd, args...) (*(mtd->read))(mtd, args) +#define MTD_WRITE(mtd, args...) (*(mtd->write))(mtd, args) +#define MTD_READV(mtd, args...) (*(mtd->readv))(mtd, args) +#define MTD_WRITEV(mtd, args...) (*(mtd->writev))(mtd, args) +#define MTD_READECC(mtd, args...) (*(mtd->read_ecc))(mtd, args) +#define MTD_WRITEECC(mtd, args...) (*(mtd->write_ecc))(mtd, args) +#define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args) +#define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args) +#define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd); } while (0) + + +#ifdef CONFIG_MTD_PARTITIONS +void mtd_erase_callback(struct erase_info *instr); +#else +static inline void mtd_erase_callback(struct erase_info *instr) +{ + if (instr->callback) + instr->callback(instr); +} +#endif + +/* + * Debugging macro and defines + */ +#define MTD_DEBUG_LEVEL0 (0) /* Quiet */ +#define MTD_DEBUG_LEVEL1 (1) /* Audible */ +#define MTD_DEBUG_LEVEL2 (2) /* Loud */ +#define MTD_DEBUG_LEVEL3 (3) /* Noisy */ + +#ifdef CONFIG_MTD_DEBUG +#define DEBUG(n, args...) \ + do { \ + if (n <= CONFIG_MTD_DEBUG_VERBOSE) \ + printk(KERN_INFO args); \ + } while(0) +#else /* CONFIG_MTD_DEBUG */ +#define DEBUG(n, args...) do { } while(0) + +#endif /* CONFIG_MTD_DEBUG */ + +#endif /* __MTD_MTD_H__ */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 5236904959..9a0108f78c 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -2,10 +2,10 @@ * linux/include/linux/mtd/nand.h * * Copyright (c) 2000 David Woodhouse - * Steven J. Hill - * Thomas Gleixner + * Steven J. Hill + * Thomas Gleixner * - * $Id: nand.h,v 1.7 2003/07/24 23:30:46 a0384864 Exp $ + * $Id: nand.h,v 1.68 2004/11/12 10:40:37 gleixner Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -24,7 +24,7 @@ * bat later if I did something naughty. * 10-11-2000 SJH Added private NAND flash structure for driver * 10-24-2000 SJH Added prototype for 'nand_scan' function - * 10-29-2001 TG changed nand_chip structure to support + * 10-29-2001 TG changed nand_chip structure to support * hardwarespecific function for accessing control lines * 02-21-2002 TG added support for different read/write adress and * ready/busy line access function @@ -32,10 +32,68 @@ * command delay times for different chips * 04-28-2002 TG OOB config defines moved from nand.c to avoid duplicate * defines in jffs2/wbuf.c + * 08-07-2002 TG forced bad block location to byte 5 of OOB, even if + * CONFIG_MTD_NAND_ECC_JFFS2 is not set + * 08-10-2002 TG extensions to nand_chip structure to support HW-ECC + * + * 08-29-2002 tglx nand_chip structure: data_poi for selecting + * internal / fs-driver buffer + * support for 6byte/512byte hardware ECC + * read_ecc, write_ecc extended for different oob-layout + * oob layout selections: NAND_NONE_OOB, NAND_JFFS2_OOB, + * NAND_YAFFS_OOB + * 11-25-2002 tglx Added Manufacturer code FUJITSU, NATIONAL + * Split manufacturer and device ID structures + * + * 02-08-2004 tglx added option field to nand structure for chip anomalities + * 05-25-2004 tglx added bad block table support, ST-MICRO manufacturer id + * update of nand_chip structure description */ #ifndef __LINUX_MTD_NAND_H #define __LINUX_MTD_NAND_H +#include +#include + +struct mtd_info; +/* Scan and identify a NAND device */ +extern int nand_scan (struct mtd_info *mtd, int max_chips); +/* Free resources held by the NAND device */ +extern void nand_release (struct mtd_info *mtd); + +/* Read raw data from the device without ECC */ +extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen); + + +/* The maximum number of NAND chips in an array */ +#define NAND_MAX_CHIPS 8 + +/* This constant declares the max. oobsize / page, which + * is supported now. If you add a chip with bigger oobsize/page + * adjust this accordingly. + */ +#define NAND_MAX_OOBSIZE 64 + +/* + * Constants for hardware specific CLE/ALE/NCE function +*/ +/* Select the chip by setting nCE to low */ +#define NAND_CTL_SETNCE 1 +/* Deselect the chip by setting nCE to high */ +#define NAND_CTL_CLRNCE 2 +/* Select the command latch by setting CLE to high */ +#define NAND_CTL_SETCLE 3 +/* Deselect the command latch by setting CLE to low */ +#define NAND_CTL_CLRCLE 4 +/* Select the address latch by setting ALE to high */ +#define NAND_CTL_SETALE 5 +/* Deselect the address latch by setting ALE to low */ +#define NAND_CTL_CLRALE 6 +/* Set write protection by setting WP to high. Not used! */ +#define NAND_CTL_SETWP 7 +/* Clear write protection by setting WP to low. Not used! */ +#define NAND_CTL_CLRWP 8 + /* * Standard NAND flash commands */ @@ -45,12 +103,104 @@ #define NAND_CMD_READOOB 0x50 #define NAND_CMD_ERASE1 0x60 #define NAND_CMD_STATUS 0x70 +#define NAND_CMD_STATUS_MULTI 0x71 #define NAND_CMD_SEQIN 0x80 #define NAND_CMD_READID 0x90 #define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_RESET 0xff +/* Extended commands for large page devices */ +#define NAND_CMD_READSTART 0x30 +#define NAND_CMD_CACHEDPROG 0x15 + +/* Status bits */ +#define NAND_STATUS_FAIL 0x01 +#define NAND_STATUS_FAIL_N1 0x02 +#define NAND_STATUS_TRUE_READY 0x20 +#define NAND_STATUS_READY 0x40 +#define NAND_STATUS_WP 0x80 + +/* + * Constants for ECC_MODES + */ + +/* No ECC. Usage is not recommended ! */ +#define NAND_ECC_NONE 0 +/* Software ECC 3 byte ECC per 256 Byte data */ +#define NAND_ECC_SOFT 1 +/* Hardware ECC 3 byte ECC per 256 Byte data */ +#define NAND_ECC_HW3_256 2 +/* Hardware ECC 3 byte ECC per 512 Byte data */ +#define NAND_ECC_HW3_512 3 +/* Hardware ECC 3 byte ECC per 512 Byte data */ +#define NAND_ECC_HW6_512 4 +/* Hardware ECC 8 byte ECC per 512 Byte data */ +#define NAND_ECC_HW8_512 6 +/* Hardware ECC 12 byte ECC per 2048 Byte data */ +#define NAND_ECC_HW12_2048 7 + /* + * Constants for Hardware ECC +*/ +/* Reset Hardware ECC for read */ +#define NAND_ECC_READ 0 +/* Reset Hardware ECC for write */ +#define NAND_ECC_WRITE 1 +/* Enable Hardware ECC before syndrom is read back from flash */ +#define NAND_ECC_READSYN 2 + +/* Option constants for bizarre disfunctionality and real +* features +*/ +/* Chip can not auto increment pages */ +#define NAND_NO_AUTOINCR 0x00000001 +/* Buswitdh is 16 bit */ +#define NAND_BUSWIDTH_16 0x00000002 +/* Device supports partial programming without padding */ +#define NAND_NO_PADDING 0x00000004 +/* Chip has cache program function */ +#define NAND_CACHEPRG 0x00000008 +/* Chip has copy back function */ +#define NAND_COPYBACK 0x00000010 +/* AND Chip which has 4 banks and a confusing page / block + * assignment. See Renesas datasheet for further information */ +#define NAND_IS_AND 0x00000020 +/* Chip has a array of 4 pages which can be read without + * additional ready /busy waits */ +#define NAND_4PAGE_ARRAY 0x00000040 + +/* Options valid for Samsung large page devices */ +#define NAND_SAMSUNG_LP_OPTIONS \ + (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK) + +/* Macros to identify the above */ +#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR)) +#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING)) +#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) +#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK)) + +/* Mask to zero out the chip options, which come from the id table */ +#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) + +/* Non chip related options */ +/* Use a flash based bad block table. This option is passed to the + * default bad block table function. */ +#define NAND_USE_FLASH_BBT 0x00010000 +/* The hw ecc generator provides a syndrome instead a ecc value on read + * This can only work if we have the ecc bytes directly behind the + * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */ +#define NAND_HWECC_SYNDROME 0x00020000 + + +/* Options set by nand scan */ +/* Nand scan has allocated oob_buf */ +#define NAND_OOBBUF_ALLOC 0x40000000 +/* Nand scan has allocated data_buf */ +#define NAND_DATABUF_ALLOC 0x80000000 + + +/* + * nand_state_t - chip states * Enumeration for NAND flash chip state */ typedef enum { @@ -58,71 +208,138 @@ typedef enum { FL_READING, FL_WRITING, FL_ERASING, - FL_SYNCING + FL_SYNCING, + FL_CACHEDPRG, } nand_state_t; +/* Keep gcc happy */ +struct nand_chip; -/* - * NAND Private Flash Chip Data - * - * Structure overview: - * - * IO_ADDR - address to access the 8 I/O lines of the flash device - * - * hwcontrol - hardwarespecific function for accesing control-lines - * - * dev_ready - hardwarespecific function for accesing device ready/busy line - * - * chip_lock - spinlock used to protect access to this structure - * - * wq - wait queue to sleep on if a NAND operation is in progress - * - * state - give the current state of the NAND device - * - * page_shift - number of address bits in a page (column address bits) - * - * data_buf - data buffer passed to/from MTD user modules - * - * data_cache - data cache for redundant page access and shadow for - * ECC failure - * - * ecc_code_buf - used only for holding calculated or read ECCs for - * a page read or written when ECC is in use - * - * reserved - padding to make structure fall on word boundary if - * when ECC is in use +#if 0 +/** + * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices + * @lock: protection lock + * @active: the mtd device which holds the controller currently */ -struct Nand { - char floor, chip; - unsigned long curadr; - unsigned char curmode; - /* Also some erase/write/pipeline info when we get that far */ +struct nand_hw_control { + spinlock_t lock; + struct nand_chip *active; }; +#endif +/** + * struct nand_chip - NAND Private Flash Chip Data + * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device + * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device + * @read_byte: [REPLACEABLE] read one byte from the chip + * @write_byte: [REPLACEABLE] write one byte to the chip + * @read_word: [REPLACEABLE] read one word from the chip + * @write_word: [REPLACEABLE] write one word to the chip + * @write_buf: [REPLACEABLE] write data from the buffer to the chip + * @read_buf: [REPLACEABLE] read data from the chip into the buffer + * @verify_buf: [REPLACEABLE] verify buffer contents against the chip data + * @select_chip: [REPLACEABLE] select chip nr + * @block_bad: [REPLACEABLE] check, if the block is bad + * @block_markbad: [REPLACEABLE] mark the block bad + * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines + * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line + * If set to NULL no access to ready/busy is available and the ready/busy information + * is read from the chip status register + * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip + * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready + * @calculate_ecc: [REPLACEABLE] function for ecc calculation or readback from ecc hardware + * @correct_data: [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw) + * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only + * be provided if a hardware ECC is available + * @erase_cmd: [INTERN] erase command write function, selectable due to AND support + * @scan_bbt: [REPLACEABLE] function to scan bad block table + * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines + * @eccsize: [INTERN] databytes used per ecc-calculation + * @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step + * @eccsteps: [INTERN] number of ecc calculation steps per page + * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) + * @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip + * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress + * @state: [INTERN] the current state of the NAND device + * @page_shift: [INTERN] number of address bits in a page (column address bits) + * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock + * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry + * @chip_shift: [INTERN] number of address bits in one chip + * @data_buf: [INTERN] internal buffer for one page + oob + * @oob_buf: [INTERN] oob buffer for one eraseblock + * @oobdirty: [INTERN] indicates that oob_buf must be reinitialized + * @data_poi: [INTERN] pointer to a data buffer + * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about + * special functionality. See the defines for further explanation + * @badblockpos: [INTERN] position of the bad block marker in the oob area + * @numchips: [INTERN] number of physical chips + * @chipsize: [INTERN] the size of one chip for multichip arrays + * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 + * @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf + * @autooob: [REPLACEABLE] the default (auto)placement scheme + * @bbt: [INTERN] bad block table pointer + * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup + * @bbt_md: [REPLACEABLE] bad block table mirror descriptor + * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan + * @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices + * @priv: [OPTIONAL] pointer to private chip date + */ + struct nand_chip { + void __iomem *IO_ADDR_R; + void __iomem *IO_ADDR_W; + + u_char (*read_byte)(struct mtd_info *mtd); + void (*write_byte)(struct mtd_info *mtd, u_char byte); + u16 (*read_word)(struct mtd_info *mtd); + void (*write_word)(struct mtd_info *mtd, u16 word); + + void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len); + void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len); + int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len); + void (*select_chip)(struct mtd_info *mtd, int chip); + int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); + int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); + void (*hwcontrol)(struct mtd_info *mtd, int cmd); + int (*dev_ready)(struct mtd_info *mtd); + void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); + int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); + int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); + int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); + void (*enable_hwecc)(struct mtd_info *mtd, int mode); + void (*erase_cmd)(struct mtd_info *mtd, int page); + int (*scan_bbt)(struct mtd_info *mtd); + int eccmode; + int eccsize; + int eccbytes; + int eccsteps; + int chip_delay; +#if 0 + spinlock_t chip_lock; + wait_queue_head_t wq; + nand_state_t state; +#endif int page_shift; + int phys_erase_shift; + int bbt_erase_shift; + int chip_shift; u_char *data_buf; - u_char *data_cache; - int cache_page; - u_char ecc_code_buf[6]; - u_char reserved[2]; - char ChipID; /* Type of DiskOnChip */ - struct Nand *chips; - int chipshift; - char* chips_name; - unsigned long erasesize; - unsigned long mfr; /* Flash IDs - only one type of flash per device */ - unsigned long id; - char* name; - int numchips; - char page256; - char pageadrlen; - unsigned long IO_ADDR; /* address to access the 8 I/O lines to the flash device */ - unsigned long totlen; - uint oobblock; /* Size of OOB blocks (e.g. 512) */ - uint oobsize; /* Amount of OOB data per block (e.g. 16) */ - uint eccsize; - int bus16; + u_char *oob_buf; + int oobdirty; + u_char *data_poi; + unsigned int options; + int badblockpos; + int numchips; + unsigned long chipsize; + int pagemask; + int pagebuf; + struct nand_oobinfo *autooob; + uint8_t *bbt; + struct nand_bbt_descr *bbt_td; + struct nand_bbt_descr *bbt_md; + struct nand_bbt_descr *badblock_pattern; + struct nand_hw_control *controller; + void *priv; }; /* @@ -130,71 +347,125 @@ struct nand_chip { */ #define NAND_MFR_TOSHIBA 0x98 #define NAND_MFR_SAMSUNG 0xec +#define NAND_MFR_FUJITSU 0x04 +#define NAND_MFR_NATIONAL 0x8f +#define NAND_MFR_RENESAS 0x07 +#define NAND_MFR_STMICRO 0x20 -/* - * NAND Flash Device ID Structure - * - * Structure overview: - * - * name - Complete name of device - * - * manufacture_id - manufacturer ID code of device. - * - * model_id - model ID code of device. - * - * chipshift - total number of address bits for the device which - * is used to calculate address offsets and the total - * number of bytes the device is capable of. +/** + * struct nand_flash_dev - NAND Flash Device ID Structure * - * page256 - denotes if flash device has 256 byte pages or not. - * - * pageadrlen - number of bytes minus one needed to hold the - * complete address into the flash array. Keep in - * mind that when a read or write is done to a - * specific address, the address is input serially - * 8 bits at a time. This structure member is used - * by the read/write routines as a loop index for - * shifting the address out 8 bits at a time. - * - * erasesize - size of an erase block in the flash device. + * @name: Identify the device type + * @id: device ID code + * @pagesize: Pagesize in bytes. Either 256 or 512 or 0 + * If the pagesize is 0, then the real pagesize + * and the eraseize are determined from the + * extended id bytes in the chip + * @erasesize: Size of an erase block in the flash device. + * @chipsize: Total chipsize in Mega Bytes + * @options: Bitfield to store chip relevant options */ struct nand_flash_dev { - char * name; - int manufacture_id; - int model_id; - int chipshift; - char page256; - char pageadrlen; + char *name; + int id; + unsigned long pagesize; + unsigned long chipsize; unsigned long erasesize; - int bus16; + unsigned long options; +}; + +/** + * struct nand_manufacturers - NAND Flash Manufacturer ID Structure + * @name: Manufacturer name + * @id: manufacturer ID code of device. +*/ +struct nand_manufacturers { + int id; + char * name; +}; + +extern struct nand_flash_dev nand_flash_ids[]; +extern struct nand_manufacturers nand_manuf_ids[]; + +/** + * struct nand_bbt_descr - bad block table descriptor + * @options: options for this descriptor + * @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE + * when bbt is searched, then we store the found bbts pages here. + * Its an array and supports up to 8 chips now + * @offs: offset of the pattern in the oob area of the page + * @veroffs: offset of the bbt version counter in the oob are of the page + * @version: version read from the bbt page during scan + * @len: length of the pattern, if 0 no pattern check is performed + * @maxblocks: maximum number of blocks to search for a bbt. This number of + * blocks is reserved at the end of the device where the tables are + * written. + * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than + * bad) block in the stored bbt + * @pattern: pattern to identify bad block table or factory marked good / + * bad blocks, can be NULL, if len = 0 + * + * Descriptor for the bad block table marker and the descriptor for the + * pattern which identifies good and bad blocks. The assumption is made + * that the pattern and the version count are always located in the oob area + * of the first block. + */ +struct nand_bbt_descr { + int options; + int pages[NAND_MAX_CHIPS]; + int offs; + int veroffs; + uint8_t version[NAND_MAX_CHIPS]; + int len; + int maxblocks; + int reserved_block_code; + uint8_t *pattern; }; +/* Options for the bad block table descriptors */ + +/* The number of bits used per block in the bbt on the device */ +#define NAND_BBT_NRBITS_MSK 0x0000000F +#define NAND_BBT_1BIT 0x00000001 +#define NAND_BBT_2BIT 0x00000002 +#define NAND_BBT_4BIT 0x00000004 +#define NAND_BBT_8BIT 0x00000008 +/* The bad block table is in the last good block of the device */ +#define NAND_BBT_LASTBLOCK 0x00000010 +/* The bbt is at the given page, else we must scan for the bbt */ +#define NAND_BBT_ABSPAGE 0x00000020 +/* The bbt is at the given page, else we must scan for the bbt */ +#define NAND_BBT_SEARCH 0x00000040 +/* bbt is stored per chip on multichip devices */ +#define NAND_BBT_PERCHIP 0x00000080 +/* bbt has a version counter at offset veroffs */ +#define NAND_BBT_VERSION 0x00000100 +/* Create a bbt if none axists */ +#define NAND_BBT_CREATE 0x00000200 +/* Search good / bad pattern through all pages of a block */ +#define NAND_BBT_SCANALLPAGES 0x00000400 +/* Scan block empty during good / bad block scan */ +#define NAND_BBT_SCANEMPTY 0x00000800 +/* Write bbt if neccecary */ +#define NAND_BBT_WRITE 0x00001000 +/* Read and write back block contents when writing bbt */ +#define NAND_BBT_SAVECONTENT 0x00002000 +/* Search good / bad pattern on the first and the second page */ +#define NAND_BBT_SCAN2NDPAGE 0x00004000 + +/* The maximum number of blocks to scan for a bbt */ +#define NAND_BBT_SCAN_MAXBLOCKS 4 + +extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd); +extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs); +extern int nand_default_bbt (struct mtd_info *mtd); +extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt); +extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); + /* * Constants for oob configuration */ -#define NAND_NOOB_ECCPOS0 0 -#define NAND_NOOB_ECCPOS1 1 -#define NAND_NOOB_ECCPOS2 2 -#define NAND_NOOB_ECCPOS3 3 -#define NAND_NOOB_ECCPOS4 6 -#define NAND_NOOB_ECCPOS5 7 -#define NAND_NOOB_BADBPOS -1 -#define NAND_NOOB_ECCVPOS -1 - -#define NAND_JFFS2_OOB_ECCPOS0 0 -#define NAND_JFFS2_OOB_ECCPOS1 1 -#define NAND_JFFS2_OOB_ECCPOS2 2 -#define NAND_JFFS2_OOB_ECCPOS3 3 -#define NAND_JFFS2_OOB_ECCPOS4 6 -#define NAND_JFFS2_OOB_ECCPOS5 7 -#define NAND_JFFS2_OOB_BADBPOS 5 -#define NAND_JFFS2_OOB_ECCVPOS 4 - -#define NAND_JFFS2_OOB8_FSDAPOS 6 -#define NAND_JFFS2_OOB16_FSDAPOS 8 -#define NAND_JFFS2_OOB8_FSDALEN 2 -#define NAND_JFFS2_OOB16_FSDALEN 8 - -unsigned long nand_probe(unsigned long physadr); +#define NAND_SMALL_BADBLOCK_POS 5 +#define NAND_LARGE_BADBLOCK_POS 0 #endif /* __LINUX_MTD_NAND_H */ diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h new file mode 100644 index 0000000000..12c5bc342e --- /dev/null +++ b/include/linux/mtd/nand_ecc.h @@ -0,0 +1,30 @@ +/* + * drivers/mtd/nand_ecc.h + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * $Id: nand_ecc.h,v 1.4 2004/06/17 02:35:02 dbrown Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file is the header for the ECC algorithm. + */ + +#ifndef __MTD_NAND_ECC_H__ +#define __MTD_NAND_ECC_H__ + +struct mtd_info; + +/* + * Calculate 3 byte ECC code for 256 byte block + */ +int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); + +/* + * Detect and correct a 1 bit error for 256 byte block + */ +int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); + +#endif /* __MTD_NAND_ECC_H__ */ diff --git a/include/linux/mtd/nand_ids.h b/include/linux/mtd/nand_ids.h index a3d0363a2a..75c305b569 100644 --- a/include/linux/mtd/nand_ids.h +++ b/include/linux/mtd/nand_ids.h @@ -49,6 +49,7 @@ static struct nand_flash_dev nand_flash_ids[] = { {"Samsung KM29W16000", NAND_MFR_SAMSUNG, 0xea, 21, 1, 2, 0x1000, 0}, {"Samsung K9F5616Q0C", NAND_MFR_SAMSUNG, 0x45, 25, 0, 2, 0x4000, 1}, {"Samsung K9K1216Q0C", NAND_MFR_SAMSUNG, 0x46, 26, 0, 3, 0x4000, 1}, + {"Samsung K9F1G08U0M", NAND_MFR_SAMSUNG, 0xf1, 27, 0, 2, 0, 0}, {NULL,} }; diff --git a/include/nand.h b/include/nand.h new file mode 100644 index 0000000000..29580da4fe --- /dev/null +++ b/include/nand.h @@ -0,0 +1,56 @@ +/* + * (C) Copyright 2005 + * 2N Telekomunikace, a.s. + * Ladislav Michl + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _NAND_H_ +#define _NAND_H_ + +#include +#include +#include + +typedef struct mtd_info nand_info_t; + +extern int nand_curr_device; +extern nand_info_t nand_info[]; + +static inline int nand_read(nand_info_t *info, ulong ofs, ulong *len, u_char *buf) +{ + return info->read(info, ofs, *len, len, buf); +} + +static inline int nand_write(nand_info_t *info, ulong ofs, ulong *len, u_char *buf) +{ + return info->write(info, ofs, *len, len, buf); +} + +static inline int nand_block_isbad(nand_info_t *info, ulong ofs) +{ + return info->block_isbad(info, ofs); +} + +static inline int nand_erase(nand_info_t *info, ulong off, ulong size) +{ + return 0; /* FIXME */ +} + +#endif -- cgit v1.2.1