diff options
author | wdenk <wdenk> | 2003-05-31 18:35:21 +0000 |
---|---|---|
committer | wdenk <wdenk> | 2003-05-31 18:35:21 +0000 |
commit | 7a8e9bed17d7924a9c5c4699b1f6a3a0359524ed (patch) | |
tree | 5c273df9c5efa7b1b6a4ca88904e48039ef591e8 /common/cmd_nand.c | |
parent | 3b57fe0a70b903f4db66c558bb9828bc58acf06b (diff) | |
download | u-boot-imx-7a8e9bed17d7924a9c5c4699b1f6a3a0359524ed.zip u-boot-imx-7a8e9bed17d7924a9c5c4699b1f6a3a0359524ed.tar.gz u-boot-imx-7a8e9bed17d7924a9c5c4699b1f6a3a0359524ed.tar.bz2 |
* Patch by Marc Singer, 29 May 2003:
Fixed rarp boot method for IA32 and other little-endian CPUs.
* Patch by Marc Singer, 28 May 2003:
Added port I/O commands.
* Patch by Matthew McClintock, 28 May 2003
- cpu/mpc824x/start.S: fix relocation code when booting from RAM
- minor patches for utx8245
* Patch by Daniel Engström, 28 May 2003:
x86 update
* Patch by Dave Ellis, 9 May 2003 + 27 May 2003:
add nand flash support to SXNI855T configuration
fix/extend nand flash support:
- fix 'nand erase' command so does not erase bad blocks
- fix 'nand write' command so does not write to bad blocks
- fix nand_probe() so handles no flash detected properly
- add doc/README.nand
- add .jffs2 and .oob options to nand read/write
- add 'nand bad' command to list bad blocks
- add 'clean' option to 'nand erase' to write JFFS2 clean markers
- make NAND read/write faster
* Patch by Rune Torgersen, 23 May 2003:
Update for MPC8266ADS board
Diffstat (limited to 'common/cmd_nand.c')
-rw-r--r-- | common/cmd_nand.c | 590 |
1 files changed, 298 insertions, 292 deletions
diff --git a/common/cmd_nand.c b/common/cmd_nand.c index edb717d..4b27c77 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -20,9 +20,9 @@ #if (CONFIG_COMMANDS & CFG_CMD_NAND) -#include <linux/mtd/nftl.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ids.h> +#include <jffs2/jffs2.h> /* * Definition of the out of band configuration structure @@ -33,14 +33,32 @@ 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 */ +/* bits for nand_rw() `cmd'; or together as needed */ +#define NANDRW_READ 0x01 +#define NANDRW_WRITE 0x00 +#define NANDRW_JFFS2 0x02 + /* * Function Prototypes */ @@ -48,17 +66,22 @@ static void nand_print(struct nand_chip *nand); static 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); +static 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); #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; @@ -96,6 +119,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 +154,86 @@ 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, &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 */ +#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 + 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); - printf ("%d bytes %s: %s\n", total, cmd ? "read" : "write", + printf ("%d bytes %s: %s\n", total, + (cmd & NANDRW_READ) ? "read" : "write", 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"); @@ -215,11 +296,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 +321,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 +340,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,23 +348,103 @@ int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) return rcode; } +/* 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; + 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 */ + + /* 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 + */ static 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; 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_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, + if (cmd & NANDRW_READ) + ret = nand_read_ecc(nand, start, + min(len, eblk + erasesize - start), &n, (u_char*)buf, noecc ? NULL : eccbuf); else - ret = nand_write_ecc(nand, start, len, + ret = nand_write_ecc(nand, start, + min(len, eblk + erasesize - start), &n, (u_char*)buf, noecc ? NULL : eccbuf); @@ -303,30 +464,18 @@ 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"); } } @@ -484,6 +633,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; @@ -579,41 +729,12 @@ 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) { + while (cntr >= 16) { *data_buf++ = READ_NAND(nandptr); *data_buf++ = READ_NAND(nandptr); *data_buf++ = READ_NAND(nandptr); @@ -639,14 +760,6 @@ static void nand_fast_read(unsigned char *data_buf, int cntr, unsigned long nand } } -/* 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 <kaos@ocs.com.au> 29 Oct 2000. - */ - /* * NAND read with ECC */ @@ -661,7 +774,6 @@ 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) { @@ -696,7 +808,7 @@ static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len, NanD_Command(nand, NAND_CMD_READ0); 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) @@ -719,7 +831,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 +858,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 */ @@ -763,12 +876,12 @@ readdata: 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 @@ -847,6 +960,7 @@ 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_READ0); NanD_Command(nand, NAND_CMD_SEQIN); NanD_Address(nand, ADDR_COLUMN_PAGE, (page << nand->page_shift) + col); @@ -982,180 +1096,17 @@ out: 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,6 +1117,7 @@ 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); @@ -1188,14 +1140,20 @@ static int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len, * to wait until ready 11.4.1 and Toshiba TC58256FT nands */ ret = NanD_WaitReady(nand); + 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,6 +1162,8 @@ 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); @@ -1229,7 +1189,8 @@ static int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, /* 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); @@ -1238,6 +1199,7 @@ static int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, 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,7 +1207,8 @@ 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); + for (i = len256; i < len; i++) + WRITE_NAND(buf[i], nandptr); NanD_Command(nand, NAND_CMD_PAGEPROG); NanD_Command(nand, NAND_CMD_STATUS); @@ -1254,18 +1217,27 @@ static int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, 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) +static 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; @@ -1289,9 +1261,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 +1274,49 @@ 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); - - NanD_Command(nand, NAND_CMD_STATUS); + /* 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); + + 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; + } - if (READ_NAND(nandptr) & 1) { - printf ("%s: Error erasing at 0x%lx\n", - __FUNCTION__, (long)ofs); - /* There was an error */ - ret = -1; - goto out; + 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; @@ -1346,7 +1346,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,9 +1354,9 @@ 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; i<CFG_MAX_NAND_DEVICE; i++) { if (nand_dev_desc[i].ChipID == NAND_ChipID_UNKNOWN) { @@ -1366,15 +1365,23 @@ void nand_probe(unsigned long physadr) } } - if (curr_device == -1) - curr_device = i; - memset((char *)nand, 0, sizeof(struct nand_chip)); - nand->cache_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; + } + + 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"); @@ -1451,11 +1458,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 +1470,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]; |