--- linux-2.6.22.14/drivers/mtd/nand/nand_base.c 2008-01-22 14:01:05.000000000 +0200 +++ linux/drivers/mtd/nand/nand_base.c 2008-01-15 11:01:54.000000000 +0200 @@ -65,10 +65,12 @@ static struct nand_ecclayout nand_oob_16 = { .eccbytes = 6, - .eccpos = {0, 1, 2, 3, 6, 7}, + .eccpos = {8, 9, 10, 13, 14, 15}, .oobfree = { - {.offset = 8, - . length = 8}} + {.offset = 0, + .length = 8}, + {.offset = 11, + .length = 2}} }; static struct nand_ecclayout nand_oob_64 = { @@ -448,6 +450,60 @@ } EXPORT_SYMBOL_GPL(nand_wait_ready); +static int get_backup_page(struct mtd_info *mtd, int page, int allow_bad) { +#ifdef MIPSEL + struct nand_chip *chip = mtd->priv; + int page_offset = chip->backup_offset >> chip->page_shift; + + if (page < page_offset) { + int page_backup = page + page_offset; + loff_t ofs_backup = ((loff_t) page_backup) << chip->page_shift; + if (allow_bad || !nand_block_checkbad(mtd, ofs_backup, 0, 1)) { + return page_backup; + } + } +#endif + return -1; +} + +static void nand_backup_merge(struct mtd_info *mtd, uint8_t *orig, int len) { + for ( ; len > 0; --len, ++orig) { + uint8_t byte = nand_read_byte(mtd); + *orig &= byte; + } +} + +static int check_backup_oob(struct mtd_info *mtd, struct nand_chip *chip, + int bpage) { + int differ = 0; + uint8_t ff = 0xff; + uint8_t *optr; + uint8_t *oend; + + /* check that OOB areas are equal or backup is empty */ + *(uint32_t*)(&chip->oob_poi[BACKUP_4xFF_OFFSET]) = 0xffffffff; + nand_wait_ready(mtd); + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, bpage); + + optr = chip->oob_poi; + oend = optr + mtd->oobsize; + for ( ; optr < oend; ++optr) { + uint8_t byte = nand_read_byte(mtd); + ff &= byte; + if (byte != *optr) { + *optr &= byte; + differ = 1; + } + } + return differ && ff != 0xff; +} + +static int read_page_raw_dummy(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + return 0; +} + /** * nand_command - [DEFAULT] Send command to NAND device * @mtd: MTD device structure @@ -1122,6 +1178,7 @@ /* Is the current page in the buffer ? */ if (realpage != chip->pagebuf || oob) { + int bpage = get_backup_page(mtd, page, 0); bufpoi = aligned ? buf : chip->buffers->databuf; if (likely(sndcmd)) { @@ -1136,6 +1193,31 @@ ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi); + + if (bpage >= 0) { + int use_backup = (mtd->ecc_stats.failed + - stats.failed + + mtd->ecc_stats.corrected + - stats.corrected); + if (!use_backup) { + use_backup = check_backup_oob(mtd, chip, + bpage); + } + sndcmd = 1; + if (use_backup) { + void *rptr; + chip->cmdfunc(mtd, NAND_CMD_READ0, + 0x00, bpage); + nand_backup_merge(mtd, bufpoi, + mtd->writesize); + + rptr = chip->ecc.read_page_raw; + chip->ecc.read_page_raw = read_page_raw_dummy; + ret = chip->ecc.read_page(mtd, chip, bufpoi); + chip->ecc.read_page_raw = rptr; + } + } + if (ret < 0) break; @@ -1152,7 +1234,8 @@ /* Raw mode does data:oob:data:oob */ if (ops->mode != MTD_OOB_RAW) { int toread = min(oobreadlen, - chip->ecc.layout->oobavail); + ops->mode == MTD_OOB_PLACE ? + mtd->oobsize : chip->ecc.layout->oobavail); if (toread) { oob = nand_transfer_oob(chip, oob, ops, toread); @@ -1266,11 +1349,20 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) { + int bpage = get_backup_page(mtd, page, 0); + if (sndcmd) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); sndcmd = 0; } chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (bpage >= 0) { + nand_wait_ready(mtd); + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, bpage); + nand_backup_merge(mtd, chip->oob_poi, mtd->oobsize); + sndcmd = 1; + } return sndcmd; } @@ -1698,7 +1790,22 @@ const uint8_t *buf, int page, int cached, int raw) { int status; + int bpage = get_backup_page(mtd, page, 1); + if (bpage >= 0) { + loff_t bofs = ((loff_t) bpage) << chip->page_shift; + if (nand_block_checkbad(mtd, bofs, 0, 1)) { +#if 0 + printk(KERN_INFO "write to page 0x%08x" + ", for which backup is bad\n", + page); +#endif + return -EIO; + } + *(uint32_t*)(&chip->oob_poi[BACKUP_4xFF_OFFSET]) = 0; + *(uint32_t*)(&chip->oob_poi[ALWAYS_4x00_OFFSET]) = 0; + } +write_again: chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) @@ -1738,6 +1845,16 @@ if (chip->verify_buf(mtd, buf, mtd->writesize)) return -EIO; #endif + if (bpage >= 0) { + page = bpage; + bpage = -2; + *(uint32_t*)(&chip->oob_poi[BACKUP_4xFF_OFFSET]) = 0xffffffff; + if (chip->ecc.mode == NAND_ECC_SOFT) { + // ECC is already calculated, do not recalculate it + raw = 1; + } + goto write_again; + } return 0; } @@ -2112,6 +2229,7 @@ int allowbbt) { int page, status, pages_per_block, ret, chipnr; + int erase_bad = 0; struct nand_chip *chip = mtd->priv; loff_t rewrite_bbt[NAND_MAX_CHIPS]={0}; unsigned int bbt_masked_page = 0xffffffff; @@ -2126,6 +2244,11 @@ return -EINVAL; } + if (instr->len == 0xDeadBeef) { + instr->len = (1 << chip->phys_erase_shift); + erase_bad = 1; + } + /* Length must align on block boundary */ if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " @@ -2178,6 +2301,18 @@ instr->state = MTD_ERASING; while (len) { + int opage = page; + int bpage = get_backup_page(mtd, page, 1); + erase_again: + /* + * try to erase bad block + */ + if (erase_bad && chip->bbt) { + loff_t ofs = ((loff_t) page) << chip->page_shift; + int block = ofs >> chip->bbt_erase_shift; + chip->bbt[block / 4] &= ~(0x03 << ((block & 0x03) * 2)); + } + /* * heck if we have a bad block, we do not erase bad blocks ! */ @@ -2228,6 +2363,15 @@ rewrite_bbt[chipnr] = ((loff_t)page << chip->page_shift); + /* erase backup page as well */ + if (bpage >= 0) { + if (page != bpage) { + page = bpage; + goto erase_again; + } + page = opage; + } + /* Increment page address and decrement length */ len -= (1 << chip->phys_erase_shift); page += pages_per_block; @@ -2564,6 +2708,32 @@ return type; } +#ifdef MIPSEL +void nand_read_id(struct mtd_info *mtd, unsigned char *ids) +{ + struct nand_chip *chip = mtd->priv; + if (chip == NULL) { + /* this is partition - get a master */ + mtd = *(struct mtd_info **)(mtd + 1); + chip = mtd->priv; + } + + /* Grab the lock and see if the device is available */ + nand_get_device(chip, mtd , FL_READING); + + chip->select_chip(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + ids[0] = chip->read_byte(mtd); + ids[1] = chip->read_byte(mtd); + ids[2] = chip->read_byte(mtd); + ids[3] = chip->read_byte(mtd); + chip->select_chip(mtd, -1); + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); +} +#endif + /** * nand_scan_ident - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure