X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=common%2Fcmd_nand.c;h=b0c01d1205aa50e45ea0e78964a5a3a0f3fd9b1f;hb=ea1fba136a4778b8f55e6b94a76a548cfad9f522;hp=edb717d8af064720f54e7275f1c833f0117628d4;hpb=0db5bca8076998a7516102988ac976a2da28d531;p=oweals%2Fu-boot.git diff --git a/common/cmd_nand.c b/common/cmd_nand.c index edb717d8af..b0c01d1205 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -4,12 +4,15 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * + * Added 16-bit nand support + * (C) 2004 Texas Instruments */ #include #include #include #include +#include #ifdef CONFIG_SHOW_BOOT_PROGRESS # include @@ -20,9 +23,15 @@ #if (CONFIG_COMMANDS & CFG_CMD_NAND) -#include #include #include +#include + +#ifdef CONFIG_OMAP1510 +void archflashwp(void *archdata, int wp); +#endif + +#define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) /* * Definition of the out of band configuration structure @@ -33,32 +42,57 @@ struct nand_oob_config { int eccvalid_pos; /* position of ECC valid flag inside oob -1 = inactive */ } oob_config = { {0}, 0, 0}; -#define NAND_DEBUG -#undef ECC_DEBUG +#undef NAND_DEBUG #undef PSYCHO_DEBUG -#undef NFTL_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 #define CONFIG_MTD_NAND_ECC /* enable ECC */ -/* #define CONFIG_MTD_NAND_ECC_JFFS2 */ +#define CONFIG_MTD_NAND_ECC_JFFS2 + +/* 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); -static int nand_rw (struct nand_chip* nand, int cmd, +int nand_rw (struct nand_chip* nand, int cmd, size_t start, size_t len, size_t * retlen, u_char * buf); -static int nand_erase(struct nand_chip* nand, size_t ofs, size_t len); +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); #endif -static struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE] = {{0}}; +struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE] = {{0}}; /* Current NAND Device */ static int curr_device = -1; @@ -75,7 +109,7 @@ int do_nand (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) printf ("Usage:\n%s\n", cmdtp->usage); return 1; case 2: - if (strcmp(argv[1],"info") == 0) { + if (strcmp(argv[1],"info") == 0) { int i; putc ('\n'); @@ -96,6 +130,16 @@ int do_nand (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) 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"); + return 1; + } + printf ("\nDevice %d bad blocks:\n", curr_device); + nand_print_bad(&nand_dev_desc[curr_device]); + return 0; + } printf ("Usage:\n%s\n", cmdtp->usage); return 1; @@ -121,38 +165,100 @@ int do_nand (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) return 0; } + else if (strcmp(argv[1],"erase") == 0 && strcmp(argv[2], "clean") == 0) { + struct nand_chip* nand = &nand_dev_desc[curr_device]; + ulong off = 0; + ulong size = nand->totlen; + int ret; + + printf ("\nNAND erase: device %d offset %ld, size %ld ... ", + curr_device, off, size); + + ret = nand_erase (nand, off, size, 1); + + printf("%s\n", ret ? "ERROR" : "OK"); + + return ret; + } printf ("Usage:\n%s\n", cmdtp->usage); return 1; default: /* at least 4 args */ - if (strcmp(argv[1],"read") == 0 || strcmp(argv[1],"write") == 0) { + 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 = (strcmp(argv[1],"read") == 0); + int cmd = (strncmp(argv[1], "read", 4) == 0) ? + NANDRW_READ : NANDRW_WRITE; int ret, total; + char* cmdtail = strchr(argv[1], '.'); + + if (cmdtail && !strncmp(cmdtail, ".oob", 2)) { + /* read out-of-band data */ + if (cmd & NANDRW_READ) { + ret = nand_read_oob(nand_dev_desc + curr_device, + off, size, (size_t *)&total, + (u_char*)addr); + } + else { + ret = nand_write_oob(nand_dev_desc + curr_device, + off, size, (size_t *)&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); + return 1; + } printf ("\nNAND %s: device %d offset %ld, size %ld ... ", - cmd ? "read" : "write", curr_device, off, size); + (cmd & NANDRW_READ) ? "read" : "write", + curr_device, off, size); ret = nand_rw(nand_dev_desc + curr_device, cmd, off, size, - &total, (u_char*)addr); + (size_t *)&total, (u_char*)addr); - printf ("%d bytes %s: %s\n", total, cmd ? "read" : "write", + printf (" %d bytes %s: %s\n", total, + (cmd & NANDRW_READ) ? "read" : "written", ret ? "ERROR" : "OK"); return ret; - } else if (strcmp(argv[1],"erase") == 0) { - ulong off = simple_strtoul(argv[2], NULL, 16); - ulong size = simple_strtoul(argv[3], NULL, 16); + } 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; printf ("\nNAND erase: device %d offset %ld, size %ld ... ", curr_device, off, size); - ret = nand_erase (nand_dev_desc + curr_device, off, size); + ret = nand_erase (nand_dev_desc + curr_device, off, size, clean); printf("%s\n", ret ? "ERROR" : "OK"); @@ -166,6 +272,21 @@ int do_nand (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) } } +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" + " at offset `off' to/from memory address `addr'\n" + "nand erase [clean] [off size] - erase `size' bytes from\n" + " offset `off' (entire device if not specified)\n" + "nand bad - show bad blocks\n" + "nand read.oob addr off size - read out-of-band data\n" + "nand write.oob addr off size - read out-of-band data\n" +); + int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { char *boot_device = NULL; @@ -215,11 +336,11 @@ int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) return 1; } - printf ("\nLoading from device %d: %s at 0x%lX (offset 0x%lX)\n", + 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); - if (nand_rw (nand_dev_desc + dev, 1, 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); @@ -240,7 +361,7 @@ int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) return 1; } - if (nand_rw (nand_dev_desc + dev, 1, offset + SECTORSIZE, cnt, + 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); @@ -259,7 +380,7 @@ int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) local_args[0] = argv[0]; local_args[1] = NULL; - printf ("Automatic boot of image at addr 0x%08lX ...\n", addr); + printf ("Automatic boot of image at addr 0x%08lx ...\n", addr); do_bootm (cmdtp, 0, 1, local_args); rcode = 1; @@ -267,25 +388,125 @@ int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) return rcode; } -static int nand_rw (struct nand_chip* nand, int cmd, +U_BOOT_CMD( + nboot, 4, 1, do_nandboot, + "nboot - boot from NAND device\n", + "loadAddr dev\n" +); + +/* returns 0 if block containing pos is OK: + * valid erase block and + * not marked bad, or no bad mark position is specified + * returns 1 if marked bad or otherwise invalid + */ +int check_block (struct nand_chip *nand, unsigned long pos) +{ + size_t retlen; + uint8_t oob_data; + uint16_t oob_data16[6]; + int page0 = pos & (-nand->erasesize); + int page1 = page0 + nand->oobblock; + int badpos = oob_config.badblock_pos; + + if (pos >= nand->totlen) + return 1; + + if (badpos < 0) + return 0; /* no way to check, assume OK */ + + if (nand->bus16) { + if (nand_read_oob(nand, (page0 + 0), 12, &retlen, (uint8_t *)oob_data16) + || (oob_data16[2] & 0xff00) != 0xff00) + return 1; + if (nand_read_oob(nand, (page1 + 0), 12, &retlen, (uint8_t *)oob_data16) + || (oob_data16[2] & 0xff00) != 0xff00) + return 1; + } else { + /* Note - bad block marker can be on first or second page */ + if (nand_read_oob(nand, page0 + badpos, 1, &retlen, (unsigned char *)&oob_data) + || oob_data != 0xff + || nand_read_oob (nand, page1 + badpos, 1, &retlen, (unsigned char *)&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 noecc, ret = 0, n, total = 0; + int ret = 0, n, total = 0; char eccbuf[6]; - - while(len) { + /* 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 */ - noecc = (start != (start | 0x1ff) + 1) || (len < 0x200); - if (cmd) - ret = nand_read_ecc(nand, start, len, - &n, (u_char*)buf, - noecc ? NULL : eccbuf); - else - ret = nand_write_ecc(nand, start, len, - &n, (u_char*)buf, - noecc ? NULL : eccbuf); + /* 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), + (size_t *)&n, (u_char*)buf, (u_char *)eccbuf); + } else { + ret = nand_write_ecc(nand, start, + min(len, eblk + erasesize - start), + (size_t *)&n, (u_char*)buf, (u_char *)eccbuf); + } if (ret) break; @@ -303,48 +524,36 @@ static int nand_rw (struct nand_chip* nand, int cmd, static void nand_print(struct nand_chip *nand) { - printf("%s at 0x%lX,\n" - "\t %d chip%s %s, size %d MB, \n" - "\t total size %ld MB, sector size %ld kB\n", - nand->name, nand->IO_ADDR, nand->numchips, - nand->numchips>1 ? "s" : "", nand->chips_name, - 1 << (nand->chipshift - 20), - nand->totlen >> 20, nand->erasesize >> 10); - - if (nand->nftl_found) { - struct NFTLrecord *nftl = &nand->nftl; - unsigned long bin_size, flash_size; - - bin_size = nftl->nb_boot_blocks * nand->erasesize; - flash_size = (nftl->nb_blocks - nftl->nb_boot_blocks) * nand->erasesize; - - printf("\t NFTL boot record:\n" - "\t Binary partition: size %ld%s\n" - "\t Flash disk partition: size %ld%s, offset 0x%lx\n", - bin_size > (1 << 20) ? bin_size >> 20 : bin_size >> 10, - bin_size > (1 << 20) ? "MB" : "kB", - flash_size > (1 << 20) ? flash_size >> 20 : flash_size >> 10, - flash_size > (1 << 20) ? "MB" : "kB", bin_size); - } else { - puts ("\t No NFTL boot record found.\n"); + 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"); } } /* ------------------------------------------------------------------------- */ -/* This function is needed to avoid calls of the __ashrdi3 function. */ -#if 0 -static int shr(int val, int shift) -{ - return val >> shift; -} -#endif -static int NanD_WaitReady(struct nand_chip *nand) +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; - NAND_WAIT_READY(nand); +#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; } @@ -363,7 +572,16 @@ static inline int NanD_Command(struct nand_chip *nand, unsigned char command) /* Lower the CLE line */ NAND_CTL_CLRCLE(nandptr); - return NanD_WaitReady(nand); +#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 */ @@ -395,15 +613,17 @@ static int NanD_Address(struct nand_chip *nand, int numbytes, unsigned long ofs) ofs = ofs >> nand->page_shift; - if (numbytes == ADDR_PAGE || numbytes == ADDR_COLUMN_PAGE) - for (i = 0; i < nand->pageadrlen; i++, ofs = ofs >> 8) + 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); + return NanD_WaitReady(nand, 1); } /* NanD_SelectChip: Select a given flash chip within the current floor */ @@ -411,7 +631,7 @@ static int NanD_Address(struct nand_chip *nand, int numbytes, unsigned long ofs) static inline int NanD_SelectChip(struct nand_chip *nand, int chip) { /* Wait for it to be ready */ - return NanD_WaitReady(nand); + return NanD_WaitReady(nand, 0); } /* NanD_IdentChip: Identify a given NAND chip given {floor,chip} */ @@ -450,10 +670,13 @@ static int NanD_IdentChip(struct nand_chip *nand, int floor, int chip) id = READ_NAND(nand->IO_ADDR); - NAND_DISABLE_CE(nand); /* set pin high */ - /* No response - return failure */ + 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) { - printf("NanD_Command (ReadID) got %d %d\n", mfr, id); + /* No response - return failure */ return 0; } @@ -462,11 +685,12 @@ static int NanD_IdentChip(struct nand_chip *nand, int floor, int chip) * contain _one_ type of flash part, although that's not a * hardware restriction. */ if (nand->mfr) { - if (nand->mfr == mfr && nand->id == id) + if (nand->mfr == mfr && nand->id == id) { return 1; /* This is another the same the first */ - else + } else { printf("Flash chip at floor %d, chip %d is different:\n", floor, chip); + } } /* Print and store the manufacturer and ID codes. */ @@ -484,6 +708,7 @@ static int NanD_IdentChip(struct nand_chip *nand, int floor, int chip) 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; @@ -493,13 +718,11 @@ static int NanD_IdentChip(struct nand_chip *nand, int floor, int chip) 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; - return 1; + 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; } @@ -545,7 +768,9 @@ static void NanD_ScanChips(struct nand_chip *nand) /* If there are none at all that we recognise, bail */ if (!nand->numchips) { - puts ("No flash chips recognised.\n"); +#ifdef NAND_DEBUG + puts ("No NAND flash chips recognised.\n"); +#endif return; } @@ -579,74 +804,78 @@ static void NanD_ScanChips(struct nand_chip *nand) #endif } -#ifdef CONFIG_MTD_NAND_ECC /* we need to be fast here, 1 us per read translates to 1 second per meg */ -static void nand_fast_copy (unsigned char *source, unsigned char *dest, long cntr) +static void NanD_ReadBuf (struct nand_chip *nand, u_char * data_buf, int cntr) { - while (cntr > 16) { - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - *dest++ = *source++; - cntr -= 16; - } - - while (cntr > 0) { - *dest++ = *source++; - cntr--; - } -} -#endif + unsigned long nandptr = nand->IO_ADDR; -/* we need to be fast here, 1 us per read translates to 1 second per meg */ -static void nand_fast_read(unsigned char *data_buf, int cntr, unsigned long nandptr) -{ - 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_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--; + } } } -/* This routine is made available to other mtd code via - * inter_module_register. It must only be accessed through - * inter_module_get which will bump the use count of this module. The - * addresses passed back in mtd are valid as long as the use count of - * this module is non-zero, i.e. between inter_module_get and - * inter_module_put. Keith Owens 29 Oct 2000. - */ - /* * NAND read with ECC */ @@ -661,11 +890,11 @@ static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len, u_char *data_poi; u_char ecc_calc[6]; #endif - unsigned long nandptr = nand->IO_ADDR; /* 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); + printf ("%s: Attempt read beyond end of device %x %x %x\n", + __FUNCTION__, (uint) start, (uint) len, (uint) nand->totlen); *retlen = 0; return -1; } @@ -686,25 +915,33 @@ static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len, /* 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); - NanD_Address(nand, ADDR_COLUMN_PAGE, (page << nand->page_shift) + col); + 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_fast_read(nand->data_buf, nand->oobblock + nand->oobsize, nandptr); + 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); + 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++) + 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 */ @@ -719,7 +956,8 @@ static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len, break; case 1: case 2: /* transfer ECC corrected data to cache */ - memcpy (nand->data_cache, nand->data_buf, 256); + if (nand->data_cache) + memcpy (nand->data_cache, nand->data_buf, 256); break; } } @@ -745,10 +983,10 @@ readdata: data_poi = (nand->data_cache) ? nand->data_cache : nand->data_buf; data_poi += col; if ((*retlen + (nand->oobblock - col)) >= len) { - nand_fast_copy (data_poi, buf + *retlen, len - *retlen); + memcpy (buf + *retlen, data_poi, len - *retlen); *retlen = len; } else { - nand_fast_copy (data_poi, buf + *retlen, nand->oobblock - col); + memcpy (buf + *retlen, data_poi, nand->oobblock - col); *retlen += nand->oobblock - col; } /* Set cache page address, invalidate, if ecc_failed */ @@ -760,15 +998,22 @@ readdata: #else /* Send the read command */ NanD_Command(nand, NAND_CMD_READ0); - NanD_Address(nand, ADDR_COLUMN_PAGE, (page << nand->page_shift) + col); + 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_fast_read(buf + *retlen, len - *retlen, nandptr); + NanD_ReadBuf(nand, buf + *retlen, len - *retlen); *retlen = len; /* We're done */ continue; } else { - nand_fast_read(buf + *retlen, nand->oobblock - col, nandptr); + NanD_ReadBuf(nand, buf + *retlen, nand->oobblock - col); *retlen += nand->oobblock - col; } #endif @@ -797,8 +1042,9 @@ static int nand_write_page (struct nand_chip *nand, { int i; -#ifdef CONFIG_MTD_NAND_ECC 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 @@ -814,28 +1060,53 @@ static int nand_write_page (struct nand_chip *nand, /* Read back previous written data, if col > 0 */ if (col) { - NanD_Command(nand, NAND_CMD_READ0); - NanD_Address(nand, ADDR_COLUMN_PAGE, (page << nand->page_shift) + col); - for (i = 0; i < col; i++) - nand->data_buf[i] = READ_NAND (nandptr); + 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; + 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; + 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 !!! */ @@ -847,20 +1118,44 @@ static int nand_write_page (struct nand_chip *nand, nand->data_buf[i] = 0xff; /* Send command to begin auto page programming */ - NanD_Command(nand, NAND_CMD_SEQIN); - NanD_Address(nand, ADDR_COLUMN_PAGE, (page << nand->page_shift) + col); + 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 */ - for (i = 0; i < (nand->oobblock + nand->oobsize); i++) - WRITE_NAND(nand->data_buf[i], nand->IO_ADDR); + 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); - + 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); + if (READ_NAND (nand->IO_ADDR) & 0x01) { + printf ("%s: Failed write, page 0x%08x, ", __FUNCTION__, + page); return -1; } #ifdef CONFIG_MTD_NAND_VERIFY_WRITE @@ -879,16 +1174,34 @@ static int nand_write_page (struct nand_chip *nand, /* Send command to read back the page */ if (col < nand->eccsize) - NanD_Command(nand, NAND_CMD_READ0); + NanD_Command (nand, NAND_CMD_READ0); else - NanD_Command(nand, NAND_CMD_READ1); - NanD_Address(nand, ADDR_COLUMN_PAGE, (page << nand->page_shift) + col); + 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 */ - for (i = col; i < last; i++) { - if (nand->data_buf[i] != readb (nand->IO_ADDR)) { - printf ("%s: Failed write verify, page 0x%08x ", __FUNCTION__, page); - return -1; + 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; + } } } @@ -897,19 +1210,38 @@ static int nand_write_page (struct nand_chip *nand, * We also want to check that the ECC bytes wrote * correctly for the same reasons stated above. */ - NanD_Command(nand, NAND_CMD_READOOB); - NanD_Address(nand, ADDR_COLUMN_PAGE, (page << nand->page_shift) + col); - for (i = 0; i < nand->oobsize; i++) - nand->data_buf[i] = readb (nand->IO_ADDR); + 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); + "verify, page 0x%08x, " + "%6i bytes were succesful\n", + __FUNCTION__, page, i); return -1; } } -#endif -#endif +#endif /* CONFIG_MTD_NAND_ECC */ +#endif /* CONFIG_MTD_NAND_VERIFY_WRITE */ return 0; } @@ -934,7 +1266,14 @@ static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t len, *retlen = 0; /* Select the NAND device */ - NAND_ENABLE_CE(nand); /* set pin low */ +#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); @@ -951,12 +1290,15 @@ static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t len, nand->cache_page = -1; /* Write data into buffer */ - if ((col + len) >= nand->oobblock) - for (i = col, cnt = 0; i < nand->oobblock; i++, cnt++) + 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++) + } + } 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) @@ -978,184 +1320,27 @@ static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t 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; } -#if 0 /* not used */ -/* Read a buffer from NanD */ -static void NanD_ReadBuf(struct nand_chip *nand, u_char * buf, int len) -{ - unsigned long nandptr; - - nandptr = nand->IO_ADDR; - - for (; len > 0; len--) - *buf++ = READ_NAND(nandptr); - -} -/* Write a buffer to NanD */ -static void NanD_WriteBuf(struct nand_chip *nand, const u_char * buf, int len) -{ - unsigned long nandptr; - int i; - - nandptr = nand->IO_ADDR; - - if (len <= 0) - return; - - for (i = 0; i < len; i++) - WRITE_NAND(buf[i], nandptr); - -} - -/* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the - * various device information of the NFTL partition and Bad Unit Table. Update - * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[] - * is used for management of Erase Unit in other routines in nftl.c and nftlmount.c +/* read from the 16 bytes of oob data that correspond to a 512 byte + * page or 2 256-byte pages. */ -static int find_boot_record(struct NFTLrecord *nftl) -{ - struct nftl_uci1 h1; - struct nftl_oob oob; - unsigned int block, boot_record_count = 0; - int retlen; - u8 buf[SECTORSIZE]; - struct NFTLMediaHeader *mh = &nftl->MediaHdr; - unsigned int i; - - nftl->MediaUnit = BLOCK_NIL; - nftl->SpareMediaUnit = BLOCK_NIL; - - /* search for a valid boot record */ - for (block = 0; block < nftl->nb_blocks; block++) { - int ret; - - /* Check for ANAND header first. Then can whinge if it's found but later - checks fail */ - if ((ret = nand_read_ecc(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, - &retlen, buf, NULL))) { - static int warncount = 5; - - if (warncount) { - printf("Block read at 0x%x failed\n", block * nftl->EraseSize); - if (!--warncount) - puts ("Further failures for this block will not be printed\n"); - } - continue; - } - - if (retlen < 6 || memcmp(buf, "ANAND", 6)) { - /* ANAND\0 not found. Continue */ -#ifdef PSYCHO_DEBUG - printf("ANAND header not found at 0x%x\n", block * nftl->EraseSize); -#endif - continue; - } - -#ifdef NFTL_DEBUG - printf("ANAND header found at 0x%x\n", block * nftl->EraseSize); -#endif - - /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = nand_read_oob(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, - 8, &retlen, (char *)&h1) < 0)) { -#ifdef NFTL_DEBUG - printf("ANAND header found at 0x%x, but OOB data read failed\n", - block * nftl->EraseSize); -#endif - continue; - } - - /* OK, we like it. */ - - if (boot_record_count) { - /* We've already processed one. So we just check if - this one is the same as the first one we found */ - if (memcmp(mh, buf, sizeof(struct NFTLMediaHeader))) { -#ifdef NFTL_DEBUG - printf("NFTL Media Headers at 0x%x and 0x%x disagree.\n", - nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize); -#endif - /* if (debug) Print both side by side */ - return -1; - } - if (boot_record_count == 1) - nftl->SpareMediaUnit = block; - - boot_record_count++; - continue; - } - - /* This is the first we've seen. Copy the media header structure into place */ - memcpy(mh, buf, sizeof(struct NFTLMediaHeader)); - - /* Do some sanity checks on it */ - if (mh->UnitSizeFactor != 0xff) { - puts ("Sorry, we don't support UnitSizeFactor " - "of != 1 yet.\n"); - return -1; - } - - nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); - if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) { - printf ("NFTL Media Header sanity check failed:\n" - "nb_boot_blocks (%d) + 2 > nb_blocks (%d)\n", - nftl->nb_boot_blocks, nftl->nb_blocks); - return -1; - } - - nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize; - if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) { - printf ("NFTL Media Header sanity check failed:\n" - "numvunits (%d) > nb_blocks (%d) - nb_boot_blocks(%d) - 2\n", - nftl->numvunits, - nftl->nb_blocks, - nftl->nb_boot_blocks); - return -1; - } - - nftl->nr_sects = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); - - /* If we're not using the last sectors in the device for some reason, - reduce nb_blocks accordingly so we forget they're there */ - nftl->nb_blocks = le16_to_cpu(mh->NumEraseUnits) + le16_to_cpu(mh->FirstPhysicalEUN); - - /* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */ - for (i = 0; i < nftl->nb_blocks; i++) { - if ((i & (SECTORSIZE - 1)) == 0) { - /* read one sector for every SECTORSIZE of blocks */ - if ((ret = nand_read_ecc(nftl->mtd, block * nftl->EraseSize + - i + SECTORSIZE, SECTORSIZE, - &retlen, buf, (char *)&oob)) < 0) { - puts ("Read of bad sector table failed\n"); - return -1; - } - } - /* mark the Bad Erase Unit as RESERVED in ReplUnitTable */ - if (buf[i & (SECTORSIZE - 1)] != 0xff) - nftl->ReplUnitTable[i] = BLOCK_RESERVED; - } - - nftl->MediaUnit = block; - boot_record_count++; - - } /* foreach (block) */ - - return boot_record_count?0:-1; -} static int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len, - size_t * retlen, u_char * buf) + size_t * retlen, u_char * buf) { - int len256 = 0, ret; - unsigned long nandptr; + int len256 = 0; struct Nand *mychip; int ret = 0; - nandptr = nand->IO_ADDR; - - mychip = &nand->chips[shr(ofs, nand->chipshift)]; + 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. */ @@ -1166,8 +1351,15 @@ static int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len, ofs -= 0x8; } + NAND_ENABLE_CE(nand); /* set pin low */ NanD_Command(nand, NAND_CMD_READOOB); - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs); + 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 */ @@ -1184,18 +1376,24 @@ static int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len, *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 */ + * 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); + 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 @@ -1204,12 +1402,20 @@ static int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, 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); - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs); + 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. */ @@ -1222,22 +1428,35 @@ static int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, /* issue the Serial Data In command to initial the Page Program process */ NanD_Command(nand, NAND_CMD_SEQIN); - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs); + 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_WriteBuf(nand, buf, len256); + for (i = 0; i < len256; i++) + WRITE_NAND(buf[i], nandptr); NanD_Command(nand, NAND_CMD_PAGEPROG); NanD_Command(nand, NAND_CMD_STATUS); - /* NanD_WaitReady() is implicit in NanD_Command */ - +#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; } @@ -1245,41 +1464,68 @@ static int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, NanD_Address(nand, ADDR_COLUMN_PAGE, ofs & (~0x1ff)); } - NanD_WriteBuf(nand, &buf[len256], len - len256); + 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); - /* NanD_WaitReady() is implicit in NanD_Command */ - +#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; } -#endif -static int nand_erase(struct nand_chip* nand, size_t ofs, size_t len) +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); + (int) nand->erasesize); return -1; } nandptr = nand->IO_ADDR; /* Select the NAND device */ - NAND_ENABLE_CE(nand); /* set pin low */ +#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); @@ -1289,9 +1535,6 @@ static int nand_erase(struct nand_chip* nand, size_t ofs, size_t len) goto out; } - /* Select the NAND device */ - NAND_ENABLE_CE(nand); /* set pin low */ - /* Check the WP bit */ NanD_Command(nand, NAND_CMD_STATUS); if (!(READ_NAND(nand->IO_ADDR) & 0x80)) { @@ -1305,18 +1548,55 @@ static int nand_erase(struct nand_chip* nand, size_t ofs, size_t len) /*mychip = &nand->chips[shr(ofs, nand->chipshift)];*/ mychip = &nand->chips[ofs >> nand->chipshift]; - NanD_Command(nand, NAND_CMD_ERASE1); - NanD_Address(nand, ADDR_PAGE, ofs); - NanD_Command(nand, NAND_CMD_ERASE2); + /* 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_STATUS); + NanD_Command(nand, NAND_CMD_ERASE1); + NanD_Address(nand, ADDR_PAGE, ofs); + NanD_Command(nand, NAND_CMD_ERASE2); - if (READ_NAND(nandptr) & 1) { - printf ("%s: Error erasing at 0x%lx\n", - __FUNCTION__, (long)ofs); - /* There was an error */ - ret = -1; - goto out; + 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, (size_t *)&n, + (u_char *)&clean_marker); + /* quit here if write failed */ + if (ret) + goto out; + } } ofs += nand->erasesize; len -= nand->erasesize; @@ -1325,6 +1605,12 @@ static int nand_erase(struct nand_chip* nand, size_t ofs, size_t 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; } @@ -1334,7 +1620,7 @@ static inline int nandcheck(unsigned long potential, unsigned long physadr) return 0; } -void nand_probe(unsigned long physadr) +unsigned long nand_probe(unsigned long physadr) { struct nand_chip *nand = NULL; int i = 0, ChipID = 1; @@ -1346,7 +1632,6 @@ void nand_probe(unsigned long physadr) 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.badblock_pos = 5; oob_config.eccvalid_pos = 4; #else oob_config.ecc_pos[0] = NAND_NOOB_ECCPOS0; @@ -1355,31 +1640,43 @@ void nand_probe(unsigned long physadr) 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.badblock_pos = NAND_NOOB_BADBPOS; oob_config.eccvalid_pos = NAND_NOOB_ECCVPOS; #endif + oob_config.badblock_pos = 5; for (i=0; icache_page = -1; /* init the cache page */ nand->IO_ADDR = physadr; - nand->ChipID = ChipID; + 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; + return (0); } + + return (nand->totlen); } #ifdef CONFIG_MTD_NAND_ECC @@ -1387,22 +1684,38 @@ void nand_probe(unsigned long physadr) * 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 + 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 }; @@ -1451,11 +1764,11 @@ static void nand_trans_result(u_char reg2, u_char reg3, */ static void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) { - u_char idx, reg1, reg2, reg3; + u_char idx, reg1, reg3; int j; /* Initialize variables */ - reg1 = reg2 = reg3 = 0; + reg1 = reg3 = 0; ecc_code[0] = ecc_code[1] = ecc_code[2] = 0; /* Build up column parity */ @@ -1463,17 +1776,16 @@ static void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) /* Get CP0 - CP5 from table */ idx = nand_ecc_precalc_table[dat[j]]; - reg1 ^= (idx & 0x3f); + reg1 ^= idx; /* 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); + nand_trans_result((reg1 & 0x40) ? ~reg3 : reg3, reg3, ecc_code); /* Calculate final ECC code */ ecc_code[0] = ~ecc_code[0]; @@ -1496,8 +1808,7 @@ static int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc) if ((d1 | d2 | d3) == 0) { /* No errors */ return 0; - } - else { + } else { a = (d1 ^ (d1 >> 1)) & 0x55; b = (d2 ^ (d2 >> 1)) & 0x55; c = (d3 ^ (d3 >> 1)) & 0x54; @@ -1569,5 +1880,19 @@ static int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc) /* 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); +} + +#endif /* CONFIG_JFFS2_NAND */ + + #endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */