diff options
-rw-r--r-- | drivers/mtd/devices/doc2000.c | 39 | ||||
-rw-r--r-- | drivers/mtd/devices/doc2001.c | 34 | ||||
-rw-r--r-- | drivers/mtd/devices/doc2001plus.c | 34 | ||||
-rw-r--r-- | drivers/mtd/inftlcore.c | 111 | ||||
-rw-r--r-- | drivers/mtd/inftlmount.c | 27 | ||||
-rw-r--r-- | drivers/mtd/mtdchar.c | 59 | ||||
-rw-r--r-- | drivers/mtd/mtdconcat.c | 90 | ||||
-rw-r--r-- | drivers/mtd/mtdpart.c | 29 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 542 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 188 | ||||
-rw-r--r-- | drivers/mtd/nftlcore.c | 92 | ||||
-rw-r--r-- | drivers/mtd/nftlmount.c | 29 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 46 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_bbt.c | 7 | ||||
-rw-r--r-- | fs/jffs2/jffs2_fs_sb.h | 1 | ||||
-rw-r--r-- | fs/jffs2/wbuf.c | 230 | ||||
-rw-r--r-- | include/linux/mtd/mtd.h | 50 | ||||
-rw-r--r-- | include/linux/mtd/nand.h | 10 |
18 files changed, 1028 insertions, 590 deletions
diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index d9ba1ee658f6..c54e40464d82 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -59,10 +59,10 @@ static int doc_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 doc_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 doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, u_char *buf); -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, const u_char *buf); +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops); +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops); static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); @@ -959,12 +959,18 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, return 0; } -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t * retlen, u_char * buf) +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) { struct DiskOnChip *this = mtd->priv; int len256 = 0, ret; struct Nand *mychip; + uint8_t *buf = ops->oobbuf; + size_t len = ops->len; + + BUG_ON(ops->mode != MTD_OOB_PLACE); + + ofs += ops->ooboffs; mutex_lock(&this->lock); @@ -1005,7 +1011,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, DoC_ReadBuf(this, &buf[len256], len - len256); - *retlen = len; + ops->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 docs */ @@ -1120,17 +1126,20 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, } -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t * retlen, const u_char * buf) +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) { - struct DiskOnChip *this = mtd->priv; - int ret; + struct DiskOnChip *this = mtd->priv; + int ret; - mutex_lock(&this->lock); - ret = doc_write_oob_nolock(mtd, ofs, len, retlen, buf); + BUG_ON(ops->mode != MTD_OOB_PLACE); + + mutex_lock(&this->lock); + ret = doc_write_oob_nolock(mtd, ofs + ops->ooboffs, ops->len, + &ops->retlen, ops->oobbuf); - mutex_unlock(&this->lock); - return ret; + mutex_unlock(&this->lock); + return ret; } static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index 579c0b570ae5..0cf022a69e65 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -43,10 +43,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, static int doc_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 doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, u_char *buf); -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, const u_char *buf); +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops); +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops); static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); static struct mtd_info *docmillist = NULL; @@ -662,8 +662,8 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, return ret; } -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, u_char *buf) +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) { #ifndef USE_MEMCPY int i; @@ -672,6 +672,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + uint8_t *buf = ops->oobbuf; + size_t len = ops->len; + + BUG_ON(ops->mode != MTD_OOB_PLACE); + + ofs += ops->ooboffs; /* Find the chip which is to be used and select it */ if (this->curfloor != mychip->floor) { @@ -708,13 +714,13 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, #endif buf[len - 1] = ReadDOC(docptr, LastDataRead); - *retlen = len; + ops->retlen = len; return 0; } -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, const u_char *buf) +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) { #ifndef USE_MEMCPY int i; @@ -724,6 +730,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + uint8_t *buf = ops->oobbuf; + size_t len = ops->len; + + BUG_ON(ops->mode != MTD_OOB_PLACE); + + ofs += ops->ooboffs; /* Find the chip which is to be used and select it */ if (this->curfloor != mychip->floor) { @@ -775,12 +787,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { printk("Error programming oob data\n"); /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ - *retlen = 0; + ops->retlen = 0; ret = -EIO; } dummy = ReadDOC(docptr, LastDataRead); - *retlen = len; + ops->retlen = len; return ret; } diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 1ee0c0dcb53b..66cb1e50469a 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -47,10 +47,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, static int doc_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 doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, u_char *buf); -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, const u_char *buf); +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops); +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops); static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); static struct mtd_info *docmilpluslist = NULL; @@ -868,14 +868,20 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, return ret; } -static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, u_char *buf) +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) { loff_t fofs, base; struct DiskOnChip *this = mtd->priv; void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; size_t i, size, got, want; + uint8_t *buf = ops->oobbuf; + size_t len = ops->len; + + BUG_ON(ops->mode != MTD_OOB_PLACE); + + ofs += ops->ooboffs; DoC_CheckASIC(docptr); @@ -941,12 +947,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, /* Disable flash internally */ WriteDOC(0, docptr, Mplus_FlashSelect); - *retlen = len; + ops->retlen = len; return 0; } -static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, - size_t *retlen, const u_char *buf) +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, + struct mtd_oob_ops *ops) { volatile char dummy; loff_t fofs, base; @@ -955,6 +961,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, struct Nand *mychip = &this->chips[ofs >> this->chipshift]; size_t i, size, got, want; int ret = 0; + uint8_t *buf = ops->oobbuf; + size_t len = ops->len; + + BUG_ON(ops->mode != MTD_OOB_PLACE); + + ofs += ops->ooboffs; DoC_CheckASIC(docptr); @@ -1030,7 +1042,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, printk("MTD: Error 0x%x programming oob at 0x%x\n", dummy, (int)ofs); /* FIXME: implement Bad Block Replacement */ - *retlen = 0; + ops->retlen = 0; ret = -EIO; } dummy = ReadDOC(docptr, Mplus_LastDataRead); @@ -1043,7 +1055,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, /* Disable flash internally */ WriteDOC(0, docptr, Mplus_FlashSelect); - *retlen = len; + ops->retlen = len; return ret; } diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 3396f0e1ac5f..efb1a95aa0a0 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -151,6 +151,69 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev) */ /* + * Read oob data from flash + */ +int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct mtd_oob_ops ops; + int res; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = offs & (mtd->writesize - 1); + ops.ooblen = len; + ops.oobbuf = buf; + ops.datbuf = NULL; + ops.len = len; + + res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.retlen; + return res; +} + +/* + * Write oob data to flash + */ +int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct mtd_oob_ops ops; + int res; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = offs & (mtd->writesize - 1); + ops.ooblen = len; + ops.oobbuf = buf; + ops.datbuf = NULL; + ops.len = len; + + res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.retlen; + return res; +} + +/* + * Write data and oob to flash + */ +static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + int res; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = offs; + ops.ooblen = mtd->oobsize; + ops.oobbuf = oob; + ops.datbuf = buf; + ops.len = len; + + res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.retlen; + return res; +} + +/* * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition. * This function is used when the give Virtual Unit Chain. */ @@ -227,9 +290,9 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned if ((BlockMap[block] != 0xffff) || BlockDeleted[block]) continue; - if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) - + (block * SECTORSIZE), 16 , &retlen, - (char *)&oob) < 0) + if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + + (block * SECTORSIZE), 16, &retlen, + (char *)&oob) < 0) status = SECTOR_IGNORE; else status = oob.b.Status | oob.b.Status1; @@ -304,9 +367,9 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned memset(&oob, 0xff, sizeof(struct inftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; - nand_write_raw(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + - (block * SECTORSIZE), SECTORSIZE, &retlen, - movebuf, (char *)&oob); + inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + + (block * SECTORSIZE), SECTORSIZE, &retlen, + movebuf, (char *)&oob); } /* @@ -437,8 +500,8 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) silly = MAX_LOOPS; while (thisEUN <= inftl->lastEUN) { - mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + - blockofs, 8, &retlen, (char *)&bci); + inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + + blockofs, 8, &retlen, (char *)&bci); status = bci.Status | bci.Status1; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in " @@ -525,8 +588,8 @@ hitused: nacs = 0; thisEUN = inftl->VUtable[thisVUC]; if (thisEUN != BLOCK_NIL) { - mtd->read_oob(mtd, thisEUN * inftl->EraseSize - + 8, 8, &retlen, (char *)&oob.u); + inftl_read_oob(mtd, thisEUN * inftl->EraseSize + + 8, 8, &retlen, (char *)&oob.u); anac = oob.u.a.ANAC + 1; nacs = oob.u.a.NACs + 1; } @@ -547,8 +610,8 @@ hitused: oob.u.a.parityPerField = parity; oob.u.a.discarded = 0xaa; - mtd->write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8, - &retlen, (char *)&oob.u); + inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); /* Also back up header... */ oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC); @@ -558,8 +621,8 @@ hitused: oob.u.b.parityPerField = parity; oob.u.b.discarded = 0xaa; - mtd->write_oob(mtd, writeEUN * inftl->EraseSize + - SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); + inftl_write_oob(mtd, writeEUN * inftl->EraseSize + + SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC]; inftl->VUtable[thisVUC] = writeEUN; @@ -610,8 +673,8 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) if (BlockUsed[block] || BlockDeleted[block]) continue; - if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) - + (block * SECTORSIZE), 8 , &retlen, + if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + + (block * SECTORSIZE), 8 , &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else @@ -711,8 +774,8 @@ static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block) "block=%d)\n", inftl, block); while (thisEUN < inftl->nb_blocks) { - if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + - blockofs, 8, &retlen, (char *)&bci) < 0) + if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + + blockofs, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else status = bci.Status | bci.Status1; @@ -746,10 +809,10 @@ foundit: if (thisEUN != BLOCK_NIL) { loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; - if (mtd->read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) + if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) return -EIO; bci.Status = bci.Status1 = SECTOR_DELETED; - if (mtd->write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) + if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) return -EIO; INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE)); } @@ -790,9 +853,9 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, memset(&oob, 0xff, sizeof(struct inftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; - nand_write_raw(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + - blockofs, SECTORSIZE, &retlen, (char *)buffer, - (char *)&oob); + inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + + blockofs, SECTORSIZE, &retlen, (char *)buffer, + (char *)&oob); /* * need to write SECTOR_USED flags since they are not written * in mtd_writeecc @@ -820,7 +883,7 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, "buffer=%p)\n", inftl, block, buffer); while (thisEUN < inftl->nb_blocks) { - if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + + if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index b4cda7d0a52d..8f6006f1a519 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -43,6 +43,11 @@ char inftlmountrev[]="$Revision: 1.18 $"; +extern int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); +extern int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); + /* * find_boot_record: Find the INFTL Media Header and its Spare copy which * contains the various device information of the INFTL partition and @@ -107,9 +112,9 @@ static int find_boot_record(struct INFTLrecord *inftl) } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = mtd->read_oob(mtd, block * inftl->EraseSize + - SECTORSIZE + 8, 8, &retlen, - (char *)&h1) < 0)) { + if ((ret = inftl_read_oob(mtd, block * inftl->EraseSize + + SECTORSIZE + 8, 8, &retlen, + (char *)&h1) < 0)) { printk(KERN_WARNING "INFTL: ANAND header found at " "0x%x in mtd%d, but OOB data read failed " "(err %d)\n", block * inftl->EraseSize, @@ -363,8 +368,8 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, return -1; if (check_oob) { - if(mtd->read_oob(mtd, address, mtd->oobsize, - &retlen, &buf[SECTORSIZE]) < 0) + if(inftl_read_oob(mtd, address, mtd->oobsize, + &retlen, &buf[SECTORSIZE]) < 0) return -1; if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) return -1; @@ -433,7 +438,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block) uci.Reserved[2] = 0; uci.Reserved[3] = 0; instr->addr = block * inftl->EraseSize + SECTORSIZE * 2; - if (mtd->write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0) + if (inftl_write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0) goto fail; return 0; fail: @@ -611,11 +616,11 @@ int INFTL_mount(struct INFTLrecord *s) break; } - if (mtd->read_oob(mtd, block * s->EraseSize + 8, - 8, &retlen, (char *)&h0) < 0 || - mtd->read_oob(mtd, block * s->EraseSize + - 2 * SECTORSIZE + 8, 8, &retlen, - (char *)&h1) < 0) { + if (inftl_read_oob(mtd, block * s->EraseSize + 8, + 8, &retlen, (char *)&h0) < 0 || + inftl_read_oob(mtd, block * s->EraseSize + + 2 * SECTORSIZE + 8, 8, &retlen, + (char *)&h1) < 0) { /* Should never happen? */ do_format_chain++; break; diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index b45e7747daa3..7522fc3a2827 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -408,8 +408,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, case MEMWRITEOOB: { struct mtd_oob_buf buf; - void *databuf; - ssize_t retlen; + struct mtd_oob_ops ops; if(!(file->f_mode & 2)) return -EPERM; @@ -417,7 +416,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) return -EFAULT; - if (buf.length > 0x4096) + if (buf.length > 4096) return -EINVAL; if (!mtd->write_oob) @@ -429,21 +428,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (ret) return ret; - databuf = kmalloc(buf.length, GFP_KERNEL); - if (!databuf) + ops.len = buf.length; + ops.ooblen = mtd->oobsize; + ops.ooboffs = buf.start & (mtd->oobsize - 1); + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + if (ops.ooboffs && ops.len > (ops.ooblen - ops.ooboffs)) + return -EINVAL; + + ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); + if (!ops.oobbuf) return -ENOMEM; - if (copy_from_user(databuf, buf.ptr, buf.length)) { - kfree(databuf); + if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) { + kfree(ops.oobbuf); return -EFAULT; } - ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); + buf.start &= ~(mtd->oobsize - 1); + ret = mtd->write_oob(mtd, buf.start, &ops); - if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t))) + if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen, + sizeof(uint32_t))) ret = -EFAULT; - kfree(databuf); + kfree(ops.oobbuf); break; } @@ -451,13 +461,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file, case MEMREADOOB: { struct mtd_oob_buf buf; - void *databuf; - ssize_t retlen; + struct mtd_oob_ops ops; if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) return -EFAULT; - if (buf.length > 0x4096) + if (buf.length > 4096) return -EINVAL; if (!mtd->read_oob) @@ -465,22 +474,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file, else ret = access_ok(VERIFY_WRITE, buf.ptr, buf.length) ? 0 : -EFAULT; - if (ret) return ret; - databuf = kmalloc(buf.length, GFP_KERNEL); - if (!databuf) + ops.len = buf.length; + ops.ooblen = mtd->oobsize; + ops.ooboffs = buf.start & (mtd->oobsize - 1); + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + if (ops.ooboffs && ops.len > (ops.ooblen - ops.ooboffs)) + return -EINVAL; + + ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); + if (!ops.oobbuf) return -ENOMEM; - ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); + buf.start &= ~(mtd->oobsize - 1); + ret = mtd->read_oob(mtd, buf.start, &ops); - if (put_user(retlen, (uint32_t __user *)argp)) + if (put_user(ops.retlen, (uint32_t __user *)argp)) ret = -EFAULT; - else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) + else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf, + ops.retlen)) ret = -EFAULT; - kfree(databuf); + kfree(ops.oobbuf); break; } diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index ec15abcdbdfa..38151b8e6631 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -231,101 +231,85 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs, } static int -concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t * retlen, u_char * buf) +concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { struct mtd_concat *concat = CONCAT(mtd); - int err = -EINVAL; - int i; + struct mtd_oob_ops devops = *ops; + int i, err; - *retlen = 0; + ops->retlen = 0; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; - size_t size, retsize; if (from >= subdev->size) { - /* Not destined for this subdev */ - size = 0; from -= subdev->size; continue; } - if (from + len > subdev->size) - /* First part goes into this subdev */ - size = subdev->size - from; - else - /* Entire transaction goes into this subdev */ - size = len; - if (subdev->read_oob) - err = subdev->read_oob(subdev, from, size, - &retsize, buf); - else - err = -EINVAL; + /* partial read ? */ + if (from + devops.len > subdev->size) + devops.len = subdev->size - from; + err = subdev->read_oob(subdev, from, &devops); + ops->retlen += devops.retlen; if (err) - break; + return err; - *retlen += retsize; - len -= size; - if (len == 0) - break; + devops.len = ops->len - ops->retlen; + if (!devops.len) + return 0; + + if (devops.datbuf) + devops.datbuf += devops.retlen; + if (devops.oobbuf) + devops.oobbuf += devops.ooblen; - err = -EINVAL; - buf += size; from = 0; } - return err; + return -EINVAL; } static int -concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t * retlen, const u_char * buf) +concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { struct mtd_concat *concat = CONCAT(mtd); - int err = -EINVAL; - int i; + struct mtd_oob_ops devops = *ops; + int i, err; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - *retlen = 0; + ops->retlen = 0; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; - size_t size, retsize; if (to >= subdev->size) { - size = 0; to -= subdev->size; continue; } - if (to + len > subdev->size) - size = subdev->size - to; - else - size = len; - if (!(subdev->flags & MTD_WRITEABLE)) - err = -EROFS; - else if (subdev->write_oob) - err = subdev->write_oob(subdev, to, size, &retsize, - buf); - else - err = -EINVAL; + /* partial write ? */ + if (to + devops.len > subdev->size) + devops.len = subdev->size - to; + err = subdev->write_oob(subdev, to, &devops); + ops->retlen += devops.retlen; if (err) - break; + return err; - *retlen += retsize; - len -= size; - if (len == 0) - break; + devops.len = ops->len - ops->retlen; + if (!devops.len) + return 0; - err = -EINVAL; - buf += size; + if (devops.datbuf) + devops.datbuf += devops.retlen; + if (devops.oobbuf) + devops.oobbuf += devops.ooblen; to = 0; } - return err; + return -EINVAL; } static void concat_erase_callback(struct erase_info *instr) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 6d7639b98eab..f22aeccf01e7 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -78,16 +78,16 @@ static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_ part->master->unpoint (part->master, addr, from + part->offset, len); } -static int part_read_oob (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); + if (from >= mtd->size) - len = 0; - else if (from + len > mtd->size) - len = mtd->size - from; - return part->master->read_oob (part->master, from + part->offset, - len, retlen, buf); + return -EINVAL; + if (from + ops->len > mtd->size) + return -EINVAL; + return part->master->read_oob(part->master, from + part->offset, ops); } static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, @@ -134,18 +134,19 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len, len, retlen, buf); } -static int part_write_oob (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int part_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; + if (to >= mtd->size) - len = 0; - else if (to + len > mtd->size) - len = mtd->size - to; - return part->master->write_oob (part->master, to + part->offset, - len, retlen, buf); + return -EINVAL; + if (to + ops->len > mtd->size) + return -EINVAL; + return part->master->write_oob(part->master, to + part->offset, ops); } static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e922b829c4be..b8e6e1579cf1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -81,23 +81,12 @@ static struct nand_ecclayout nand_oob_64 = { .length = 38}} }; -/* This is used for padding purposes in nand_write_oob */ -static uint8_t 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, -}; - -static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf); static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state); +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + /* * For devices which display every fart in the system on a seperate LED. Is * compiled away when LED support is disabled. @@ -358,7 +347,6 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; uint8_t buf[2] = { 0, 0 }; - size_t retlen; int block; /* Get block number */ @@ -371,8 +359,13 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) return nand_update_bbt(mtd, ofs); /* We write two bytes, so we dont have to mess with 16 bit access */ - ofs += mtd->oobsize + (chip->badblockpos & ~0x01); - return nand_write_oob(mtd, ofs, 2, &retlen, buf); + ofs += mtd->oobsize; + chip->ops.len = 2; + chip->ops.datbuf = NULL; + chip->ops.oobbuf = buf; + chip->ops.ooboffs = chip->badblockpos & ~0x01; + + return nand_do_write_oob(mtd, ofs, &chip->ops); } /** @@ -740,6 +733,20 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip, int state) } /** + * nand_read_page_raw - [Intern] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + */ +static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + +/** * nand_read_page_swecc - {REPLACABLE] software ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure @@ -756,11 +763,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_code = chip->buffers.ecccode; int *eccpos = chip->ecc.layout->eccpos; - chip->read_buf(mtd, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - if (chip->ecc.mode == NAND_ECC_NONE) - return 0; + nand_read_page_raw(mtd, chip, buf); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); @@ -882,18 +885,50 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, } /** - * nand_do_read - [Internal] Read data with ECC + * nand_transfer_oob - [Internal] Transfer oob to client buffer + * @chip: nand chip structure + * @ops: oob ops structure + */ +static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops) +{ + size_t len = ops->ooblen; + + switch(ops->mode) { + + case MTD_OOB_PLACE: + case MTD_OOB_RAW: + memcpy(oob, chip->oob_poi + ops->ooboffs, len); + return oob + len; + + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + size_t bytes; + + for(; free->length && len; free++, len -= bytes) { + bytes = min(len, free->length); + + memcpy(oob, chip->oob_poi + free->offset, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + +/** + * nand_do_read_ops - [Internal] 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 * * Internal function. Called with chip held. */ -int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, uint8_t *buf) +static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { int chipnr, page, realpage, col, bytes, aligned; struct nand_chip *chip = mtd->priv; @@ -901,8 +936,8 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; int sndcmd = 1; int ret = 0; - uint32_t readlen = len; - uint8_t *bufpoi; + uint32_t readlen = ops->len; + uint8_t *bufpoi, *oob, *buf; stats = mtd->ecc_stats; @@ -915,12 +950,15 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, col = (int)(from & (mtd->writesize - 1)); chip->oob_poi = chip->buffers.oobrbuf; + buf = ops->datbuf; + oob = ops->oobbuf; + while(1) { bytes = min(mtd->writesize - col, readlen); aligned = (bytes == mtd->writesize); /* Is the current page in the buffer ? */ - if (realpage != chip->pagebuf) { + if (realpage != chip->pagebuf || oob) { bufpoi = aligned ? buf : chip->buffers.databuf; if (likely(sndcmd)) { @@ -939,6 +977,16 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, memcpy(buf, chip->buffers.databuf + col, bytes); } + buf += bytes; + + if (unlikely(oob)) { + /* Raw mode does data:oob:data:oob */ + if (ops->mode != MTD_OOB_RAW) + oob = nand_transfer_oob(chip, oob, ops); + else + buf = nand_transfer_oob(chip, buf, ops); + } + if (!(chip->options & NAND_NO_READRDY)) { /* * Apply delay or wait for ready/busy pin. Do @@ -952,10 +1000,11 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, else nand_wait_ready(mtd); } - } else + } else { memcpy(buf, chip->buffers.databuf + col, bytes); + buf += bytes; + } - buf += bytes; readlen -= bytes; if (!readlen) @@ -981,7 +1030,7 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, sndcmd = 1; } - *retlen = len - (size_t) readlen; + ops->retlen = ops->len - (size_t) readlen; if (ret) return ret; @@ -1002,57 +1051,49 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf) { + struct nand_chip *chip = mtd->priv; int ret; - *retlen = 0; /* Do not allow reads past end of device */ if ((from + len) > mtd->size) return -EINVAL; if (!len) return 0; - nand_get_device(mtd->priv, mtd, FL_READING); + nand_get_device(chip, mtd, FL_READING); - ret = nand_do_read(mtd, from, len, retlen, buf); + chip->ops.len = len; + chip->ops.datbuf = buf; + chip->ops.oobbuf = NULL; + + ret = nand_do_read_ops(mtd, from, &chip->ops); nand_release_device(mtd); + *retlen = chip->ops.retlen; return ret; } /** - * nand_read_oob - [MTD Interface] NAND read out-of-band + * nand_do_read_oob - [Intern] 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 + * @ops: oob operations description structure * * 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, uint8_t *buf) +static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { int col, page, realpage, chipnr, sndcmd = 1; struct nand_chip *chip = mtd->priv; int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; - int readlen = len; + int direct, bytes, readlen = ops->len; + uint8_t *bufpoi, *buf = ops->oobbuf; DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int)from, (int)len); - /* 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"); - return -EINVAL; - } - - nand_get_device(chip, mtd, FL_READING); - chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1060,20 +1101,31 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; - /* Mask to get column */ - col = from & (mtd->oobsize - 1); + if (ops->mode != MTD_OOB_AUTO) { + col = ops->ooboffs; + direct = 1; + } else { + col = 0; + direct = 0; + } while(1) { - int bytes = min((int)(mtd->oobsize - col), readlen); + bytes = direct ? ops->ooblen : mtd->oobsize; + bufpoi = direct ? buf : chip->buffers.oobrbuf; if (likely(sndcmd)) { chip->cmdfunc(mtd, NAND_CMD_READOOB, col, page); sndcmd = 0; } - chip->read_buf(mtd, buf, bytes); + chip->read_buf(mtd, bufpoi, bytes); - readlen -= bytes; + if (unlikely(!direct)) + buf = nand_transfer_oob(chip, buf, ops); + else + buf += ops->ooblen; + + readlen -= ops->ooblen; if (!readlen) break; @@ -1090,10 +1142,6 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, nand_wait_ready(mtd); } - buf += bytes; - bytes = mtd->oobsize; - col = 0; - /* Increment page address */ realpage++; @@ -1112,81 +1160,76 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, sndcmd = 1; } - /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); - - *retlen = len; + ops->retlen = ops->len; return 0; } /** - * nand_read_raw - [GENERIC] Read raw data including oob into buffer + * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band * @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 + * @ops: oob operation description structure * - * Read raw data including oob into buffer + * NAND read data and/or out-of-band data */ -int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, - size_t ooblen) +static int nand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) { + int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) = NULL; struct nand_chip *chip = mtd->priv; - int page = (int)(from >> chip->page_shift); - int chipnr = (int)(from >> chip->chip_shift); - int sndcmd = 1; - int cnt = 0; - int pagesize = mtd->writesize + mtd->oobsize; - int blockcheck; + int ret = -ENOTSUPP; + + ops->retlen = 0; /* Do not allow reads past end of device */ - if ((from + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: " + if ((from + ops->len) > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " "Attempt read beyond end of device\n"); return -EINVAL; } - /* Grab the lock and see if the device is available */ nand_get_device(chip, mtd, FL_READING); - chip->select_chip(mtd, chipnr); - - /* Add requested oob length */ - len += ooblen; - blockcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + switch(ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + break; - while (len) { - if (likely(sndcmd)) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, - page & chip->pagemask); - sndcmd = 0; - } + case MTD_OOB_RAW: + /* Replace the read_page algorithm temporary */ + read_page = chip->ecc.read_page; + chip->ecc.read_page = nand_read_page_raw; + break; - chip->read_buf(mtd, &buf[cnt], pagesize); + default: + goto out; + } - len -= pagesize; - cnt += pagesize; - page++; + if (!ops->datbuf) + ret = nand_do_read_oob(mtd, from, ops); + else + ret = nand_do_read_ops(mtd, from, ops); - if (!(chip->options & NAND_NO_READRDY)) { - if (!chip->dev_ready) - udelay(chip->chip_delay); - else - nand_wait_ready(mtd); - } + if (unlikely(ops->mode == MTD_OOB_RAW)) + chip->ecc.read_page = read_page; + out: + nand_release_device(mtd); + return ret; +} - /* - * Check, if the chip supports auto page increment or if we - * cross a block boundary. - */ - if (!NAND_CANAUTOINCR(chip) || !(page & blockcheck)) - sndcmd = 1; - } - /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); - return 0; +/** + * nand_write_page_raw - [Intern] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } /** @@ -1205,17 +1248,14 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *p = buf; int *eccpos = chip->ecc.layout->eccpos; - if (chip->ecc.mode != NAND_ECC_NONE) { - /* Software ecc calculation */ - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + /* Software ecc calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; - } + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; - chip->write_buf(mtd, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_write_page_raw(mtd, chip, buf); } /** @@ -1342,51 +1382,77 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, return 0; } +/** + * nand_fill_oob - [Internal] Transfer client buffer to oob + * @chip: nand chip structure + * @oob: oob data buffer + * @ops: oob ops structure + */ +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops) +{ + size_t len = ops->ooblen; + + switch(ops->mode) { + + case MTD_OOB_PLACE: + case MTD_OOB_RAW: + memcpy(chip->oob_poi + ops->ooboffs, oob, len); + return oob + len; + + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + size_t bytes; + + for(; free->length && len; free++, len -= bytes) { + bytes = min(len, free->length); + memcpy(chip->oob_poi + free->offset, oob, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + #define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0 /** - * nand_write - [MTD Interface] NAND write with ECC + * nand_do_write_ops - [Internal] 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 + * @ops: oob operations description structure * * NAND write with ECC */ -static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf) +static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) { int chipnr, realpage, page, blockmask; struct nand_chip *chip = mtd->priv; - uint32_t writelen = len; + uint32_t writelen = ops->len; + uint8_t *oob = ops->oobbuf; + uint8_t *buf = ops->datbuf; int bytes = mtd->writesize; - int ret = -EIO; + int ret; - *retlen = 0; - - /* Do not allow write past end of device */ - if ((to + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write: " - "Attempt to write past end of page\n"); - return -EINVAL; - } + ops->retlen = 0; /* reject writes, which are not page aligned */ - if (NOTALIGNED(to) || NOTALIGNED(len)) { + if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { printk(KERN_NOTICE "nand_write: " "Attempt to write not page aligned data\n"); return -EINVAL; } - if (!len) + if (!writelen) return 0; - nand_get_device(chip, mtd, FL_WRITING); - /* Check, if it is write protected */ if (nand_check_wp(mtd)) - goto out; + return -EIO; chipnr = (int)(to >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1397,7 +1463,7 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, /* Invalidate the page cache, when we write to the cached page */ if (to <= (chip->pagebuf << chip->page_shift) && - (chip->pagebuf << chip->page_shift) < (to + len)) + (chip->pagebuf << chip->page_shift) < (to + ops->len)) chip->pagebuf = -1; chip->oob_poi = chip->buffers.oobwbuf; @@ -1405,6 +1471,9 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, while(1) { int cached = writelen > bytes && page != blockmask; + if (unlikely(oob)) + oob = nand_fill_oob(chip, oob, ops); + ret = nand_write_page(mtd, chip, buf, page, cached); if (ret) break; @@ -1424,94 +1493,74 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, chip->select_chip(mtd, chipnr); } } - out: - *retlen = len - writelen; - nand_release_device(mtd); + + if (unlikely(oob)) + memset(chip->oob_poi, 0xff, mtd->oobsize); + + ops->retlen = ops->len - writelen; return ret; } /** - * nand_write_raw - [GENERIC] Write raw data including oob + * nand_write - [MTD Interface] NAND write with ECC * @mtd: MTD device structure - * @buf: source buffer * @to: offset to write to * @len: number of bytes to write - * @buf: source buffer - * @oob: oob buffer + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write * - * Write raw data including oob + * NAND write with ECC */ -int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, - const uint8_t *buf, uint8_t *oob) +static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) { struct nand_chip *chip = mtd->priv; - int page = (int)(to >> chip->page_shift); - int chipnr = (int)(to >> chip->chip_shift); int ret; - *retlen = 0; - - /* Do not allow writes past end of device */ - if ((to + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt write " - "beyond end of device\n"); + /* Do not allow reads past end of device */ + if ((to + len) > mtd->size) return -EINVAL; - } + if (!len) + return 0; - /* Grab the lock and see if the device is available */ - nand_get_device(chip, mtd, FL_WRITING); + nand_get_device(chip, mtd, FL_READING); - chip->select_chip(mtd, chipnr); - chip->oob_poi = oob; + chip->ops.len = len; + chip->ops.datbuf = (uint8_t *)buf; + chip->ops.oobbuf = NULL; - while (len != *retlen) { - ret = nand_write_page(mtd, chip, buf, page, 0); - if (ret) - return ret; - page++; - *retlen += mtd->writesize; - buf += mtd->writesize; - chip->oob_poi += mtd->oobsize; - } + ret = nand_do_write_ops(mtd, to, &chip->ops); - /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); - return 0; + + *retlen = chip->ops.retlen; + return ret; } -EXPORT_SYMBOL_GPL(nand_write_raw); /** - * nand_write_oob - [MTD Interface] NAND write out-of-band + * nand_do_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 + * @ops: oob operation description structure * * NAND write out-of-band */ -static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf) +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) { - int column, page, status, ret = -EIO, chipnr; + int chipnr, page, status; struct nand_chip *chip = mtd->priv; DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", - (unsigned int)to, (int)len); - - /* Initialize return length value */ - *retlen = 0; + (unsigned int)to, (int)ops->len); /* Do not allow write past end of page */ - column = to & (mtd->oobsize - 1); - if ((column + len) > mtd->oobsize) { + if ((ops->ooboffs + ops->len) > mtd->oobsize) { DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " "Attempt to write past end of page\n"); return -EINVAL; } - nand_get_device(chip, mtd, FL_WRITING); - chipnr = (int)(to >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1528,26 +1577,27 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, /* Check, if it is write protected */ if (nand_check_wp(mtd)) - goto out; + return -EROFS; /* Invalidate the page cache, if we write to the cached page */ if (page == chip->pagebuf) chip->pagebuf = -1; - if (NAND_MUST_PAD(chip)) { + if (ops->mode == MTD_OOB_AUTO || NAND_MUST_PAD(chip)) { + chip->oob_poi = chip->buffers.oobwbuf; + memset(chip->oob_poi, 0xff, mtd->oobsize); + nand_fill_oob(chip, ops->oobbuf, ops); chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page & chip->pagemask); - /* prepad 0xff for partial programming */ - chip->write_buf(mtd, ffchars, column); - /* write data */ - chip->write_buf(mtd, buf, len); - /* postpad 0xff for partial programming */ - chip->write_buf(mtd, ffchars, mtd->oobsize - (len + column)); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + memset(chip->oob_poi, 0xff, mtd->oobsize); } else { - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + column, + chip->cmdfunc(mtd, NAND_CMD_SEQIN, + mtd->writesize + ops->ooboffs, page & chip->pagemask); - chip->write_buf(mtd, buf, len); + chip->write_buf(mtd, ops->oobbuf, ops->len); } + /* Send command to program the OOB data */ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); @@ -1557,27 +1607,75 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, if (status & NAND_STATUS_FAIL) { DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); - ret = -EIO; - goto out; + return -EIO; } - *retlen = len; + ops->retlen = ops->len; #ifdef CONFIG_MTD_NAND_VERIFY_WRITE - /* Send command to read back the data */ - chip->cmdfunc(mtd, NAND_CMD_READOOB, column, page & chip->pagemask); + if (ops->mode != MTD_OOB_AUTO) { + /* Send command to read back the data */ + chip->cmdfunc(mtd, NAND_CMD_READOOB, ops->ooboffs, + page & chip->pagemask); - if (chip->verify_buf(mtd, buf, len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Failed write verify, page 0x%08x\n", page); - ret = -EIO; - goto out; + if (chip->verify_buf(mtd, ops->oobbuf, ops->len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " + "Failed write verify, page 0x%08x\n", page); + return -EIO; + } } #endif - ret = 0; + return 0; +} + +/** + * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + */ +static int nand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) = NULL; + struct nand_chip *chip = mtd->priv; + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow writes past end of device */ + if ((to + ops->len) > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); + return -EINVAL; + } + + nand_get_device(chip, mtd, FL_READING); + + switch(ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + break; + + case MTD_OOB_RAW: + /* Replace the write_page algorithm temporary */ + write_page = chip->ecc.write_page; + chip->ecc.write_page = nand_write_page_raw; + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_write_oob(mtd, to, ops); + else + ret = nand_do_write_ops(mtd, to, ops); + + if (unlikely(ops->mode == MTD_OOB_RAW)) + chip->ecc.write_page = write_page; out: - /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); - return ret; } @@ -2191,8 +2289,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) case NAND_ECC_NONE: printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " "This is not recommended !!\n"); - chip->ecc.read_page = nand_read_page_swecc; - chip->ecc.write_page = nand_write_page_swecc; + chip->ecc.read_page = nand_read_page_raw; + chip->ecc.write_page = nand_write_page_raw; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; break; diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 40f99304df76..480c3cbf9bf9 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -230,6 +230,42 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc return 0; } +/* + * Scan read raw data from flash + */ +static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + size_t len) +{ + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_RAW; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.datbuf = buf; + ops.len = len; + + return mtd->read_oob(mtd, offs, &ops); +} + +/* + * Scan write data with oob to flash + */ +static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, + uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.datbuf = buf; + ops.oobbuf = oob; + ops.len = len; + + return mtd->write_oob(mtd, offs, &ops); +} + /** * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page * @mtd: MTD device structure @@ -241,27 +277,85 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc * 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) +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->writesize, mtd->oobsize); + scan_read_raw(mtd, buf, td->pages[0] << this->page_shift, + mtd->writesize); td->version[0] = buf[mtd->writesize + td->veroffs]; - printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); + 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->writesize, mtd->oobsize); + scan_read_raw(mtd, buf, md->pages[0] << this->page_shift, + mtd->writesize); md->version[0] = buf[mtd->writesize + md->veroffs]; - printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); + printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", + md->pages[0], md->version[0]); } - return 1; } +/* + * Scan a given block full + */ +static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, size_t readlen, + int scanlen, int len) +{ + int ret, j; + + ret = scan_read_raw(mtd, buf, offs, readlen); + if (ret) + return ret; + + for (j = 0; j < len; j++, buf += scanlen) { + if (check_pattern(buf, scanlen, mtd->writesize, bd)) + return 1; + } + return 0; +} + +/* + * Scan a given block partially + */ +static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, int len) +{ + struct mtd_oob_ops ops; + int j, ret; + + ops.len = mtd->oobsize; + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + for (j = 0; j < len; j++) { + /* + * Read the full oob until read_oob is fixed to + * handle single byte reads for 16 bit + * buswidth + */ + ret = mtd->read_oob(mtd, offs, &ops); + if (ret) + return ret; + + if (check_short_pattern(buf, bd)) + return 1; + + offs += mtd->writesize; + } + return 0; +} + /** * create_bbt - [GENERIC] Create a bad block table by scanning the device * @mtd: MTD device structure @@ -273,13 +367,14 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des * Create a bad block table by scanning the device * for the given good/bad block identify pattern */ -static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) +static int 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 i, numblocks, len, scanlen; int startblock; loff_t from; - size_t readlen, ooblen; + size_t readlen; printk(KERN_INFO "Scanning device for bad blocks\n"); @@ -294,18 +389,17 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr if (!(bd->options & NAND_BBT_SCANEMPTY)) { /* We need only read few bytes from the OOB area */ - scanlen = ooblen = 0; + scanlen = 0; readlen = bd->len; } else { /* Full page content should be read */ scanlen = mtd->writesize + mtd->oobsize; readlen = len * mtd->writesize; - 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 */ + /* 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; @@ -324,35 +418,21 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr for (i = startblock; i < numblocks;) { int ret; - if (bd->options & NAND_BBT_SCANEMPTY) - if ((ret = nand_read_raw(mtd, buf, from, readlen, ooblen))) - return ret; - - for (j = 0; j < len; j++) { - if (!(bd->options & NAND_BBT_SCANEMPTY)) { - size_t retlen; - - /* Read the full oob until read_oob is fixed to - * handle single byte reads for 16 bit buswidth */ - ret = mtd->read_oob(mtd, from + j * mtd->writesize, mtd->oobsize, &retlen, buf); - if (ret) - return ret; - - if (check_short_pattern(buf, bd)) { - this->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", - i >> 1, (unsigned int)from); - break; - } - } else { - if (check_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { - this->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", - i >> 1, (unsigned int)from); - break; - } - } + if (bd->options & NAND_BBT_SCANALLPAGES) + ret = scan_block_full(mtd, bd, from, buf, readlen, + scanlen, len); + else + ret = scan_block_fast(mtd, bd, from, buf, len); + + if (ret < 0) + return ret; + + if (ret) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", + i >> 1, (unsigned int)from); } + i += 2; from += (1 << this->bbt_erase_shift); } @@ -383,6 +463,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr int bits, startblock, block, dir; int scanlen = mtd->writesize + mtd->oobsize; int bbtblocks; + int blocktopage = this->bbt_erase_shift - this->page_shift; /* Search direction top -> down ? */ if (td->options & NAND_BBT_LASTBLOCK) { @@ -412,11 +493,14 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr td->pages[i] = -1; /* Scan the maximum number of blocks */ for (block = 0; block < td->maxblocks; block++) { + int actblock = startblock + dir * block; + loff_t offs = actblock << this->bbt_erase_shift; + /* Read first page */ - nand_read_raw(mtd, buf, actblock << this->bbt_erase_shift, mtd->writesize, mtd->oobsize); + scan_read_raw(mtd, buf, offs, mtd->writesize); if (!check_pattern(buf, scanlen, mtd->writesize, td)) { - td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); + td->pages[i] = actblock << blocktopage; if (td->options & NAND_BBT_VERSION) { td->version[i] = buf[mtd->writesize + td->veroffs]; } @@ -481,8 +565,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, int nrchips, bbtoffs, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; - size_t retlen, len = 0, ooblen; + size_t retlen, len = 0; loff_t to; + struct mtd_oob_ops ops; + + ops.ooblen = mtd->oobsize; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; if (!rcode) rcode = 0xff; @@ -583,10 +673,10 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, "bad block table\n"); } /* Read oob data */ - ooblen = (len >> this->page_shift) * mtd->oobsize; - res = mtd->read_oob(mtd, to + mtd->writesize, ooblen, - &retlen, &buf[len]); - if (res < 0 || retlen != ooblen) + ops.len = (len >> this->page_shift) * mtd->oobsize; + ops.oobbuf = &buf[len]; + res = mtd->read_oob(mtd, to + mtd->writesize, &ops); + if (res < 0 || ops.retlen != ops.len) goto outerr; /* Calc the byte offset in the buffer */ @@ -635,7 +725,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, if (res < 0) goto outerr; - res = nand_write_raw(mtd, to, len, &retlen, buf, &buf[len]); + res = scan_write_bbt(mtd, to, len, buf, &buf[len]); if (res < 0) goto outerr; diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 359533b33d9b..f6ffe7949b26 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -134,6 +134,69 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev) kfree(nftl); } +/* + * Read oob data from flash + */ +int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct mtd_oob_ops ops; + int res; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = offs & (mtd->writesize - 1); + ops.ooblen = len; + ops.oobbuf = buf; + ops.datbuf = NULL; + ops.len = len; + + res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.retlen; + return res; +} + +/* + * Write oob data to flash + */ +int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct mtd_oob_ops ops; + int res; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = offs & (mtd->writesize - 1); + ops.ooblen = len; + ops.oobbuf = buf; + ops.datbuf = NULL; + ops.len = len; + + res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.retlen; + return res; +} + +/* + * Write data and oob to flash + */ +static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + int res; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = offs; + ops.ooblen = mtd->oobsize; + ops.oobbuf = oob; + ops.datbuf = buf; + ops.len = len; + + res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.retlen; + return res; +} + #ifdef CONFIG_NFTL_RW /* Actual NFTL access routines */ @@ -216,7 +279,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p targetEUN = thisEUN; for (block = 0; block < nftl->EraseSize / 512; block ++) { - mtd->read_oob(mtd, (thisEUN * nftl->EraseSize) + + nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) + (block * 512), 16 , &retlen, (char *)&oob); if (block == 2) { @@ -333,7 +396,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p longer one */ oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); oob.u.c.unused = 0xffffffff; - mtd->write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, + nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, 8, &retlen, (char *)&oob.u); } @@ -369,17 +432,15 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p memset(&oob, 0xff, sizeof(struct nftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; - nand_write_raw(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + - (block * 512), 512, &retlen, movebuf, - (char *)&oob); - + nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + + (block * 512), 512, &retlen, movebuf, (char *)&oob); } /* add the header so that it is now a valid chain */ oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; - mtd->write_oob(mtd, (nftl->EraseSize * targetEUN) + 8, + nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8, 8, &retlen, (char *)&oob.u); /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ @@ -499,7 +560,7 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) lastEUN = writeEUN; - mtd->read_oob(mtd, + nftl_read_oob(mtd, (writeEUN * nftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci); @@ -588,12 +649,12 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; /* ... and on the flash itself */ - mtd->read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, + nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, &retlen, (char *)&oob.u); oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); - mtd->write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, + nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, &retlen, (char *)&oob.u); /* we link the new block to the chain only after the @@ -603,13 +664,13 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) /* Both in our cache... */ nftl->ReplUnitTable[lastEUN] = writeEUN; /* ... and on the flash itself */ - mtd->read_oob(mtd, (lastEUN * nftl->EraseSize) + 8, + nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8, 8, &retlen, (char *)&oob.u); oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = cpu_to_le16(writeEUN); - mtd->write_oob(mtd, (lastEUN * nftl->EraseSize) + 8, + nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8, 8, &retlen, (char *)&oob.u); } @@ -643,9 +704,8 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, memset(&oob, 0xff, sizeof(struct nftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; - nand_write_raw(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + - blockofs, 512, &retlen, (char *)buffer, - (char *)&oob); + nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, + 512, &retlen, (char *)buffer, (char *)&oob); return 0; } #endif /* CONFIG_NFTL_RW */ @@ -667,7 +727,7 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, if (thisEUN != BLOCK_NIL) { while (thisEUN < nftl->nb_blocks) { - if (mtd->read_oob(mtd, (thisEUN * nftl->EraseSize) + + if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 521b07cd2326..067262ee8df0 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -33,6 +33,11 @@ char nftlmountrev[]="$Revision: 1.41 $"; +extern int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); +extern int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); + /* 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[] @@ -92,7 +97,7 @@ static int find_boot_record(struct NFTLrecord *nftl) } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = mtd->read_oob(mtd, block * nftl->EraseSize + + if ((ret = nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) { printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n", @@ -283,7 +288,7 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int return -1; if (check_oob) { - if(mtd->read_oob(mtd, address, mtd->oobsize, + if(nftl_read_oob(mtd, address, mtd->oobsize, &retlen, &buf[SECTORSIZE]) < 0) return -1; if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) @@ -311,7 +316,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) struct mtd_info *mtd = nftl->mbd.mtd; /* Read the Unit Control Information #1 for Wear-Leveling */ - if (mtd->read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, + if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&uci) < 0) goto default_uci1; @@ -351,7 +356,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) goto fail; uci.WearInfo = le32_to_cpu(nb_erases); - if (mtd->write_oob(mtd, block * nftl->EraseSize + SECTORSIZE + + if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&uci) < 0) goto fail; return 0; @@ -383,7 +388,7 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b block = first_block; for (;;) { for (i = 0; i < sectors_per_block; i++) { - if (mtd->read_oob(mtd, + if (nftl_read_oob(mtd, block * nftl->EraseSize + i * SECTORSIZE, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; @@ -404,7 +409,7 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b /* sector not free actually : mark it as SECTOR_IGNORE */ bci.Status = SECTOR_IGNORE; bci.Status1 = SECTOR_IGNORE; - mtd->write_oob(mtd, block * + nftl_write_oob(mtd, block * nftl->EraseSize + i * SECTORSIZE, 8, &retlen, (char *)&bci); @@ -498,7 +503,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) size_t retlen; /* check erase mark. */ - if (mtd->read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) return -1; @@ -513,7 +518,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) h1.EraseMark = cpu_to_le16(ERASE_MARK); h1.EraseMark1 = cpu_to_le16(ERASE_MARK); h1.WearInfo = cpu_to_le32(0); - if (mtd->write_oob(mtd, + if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) return -1; @@ -526,7 +531,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) SECTORSIZE, 0) != 0) return -1; - if (mtd->read_oob(mtd, block * nftl->EraseSize + i, + if (nftl_read_oob(mtd, block * nftl->EraseSize + i, 16, &retlen, buf) < 0) return -1; if (i == SECTORSIZE) { @@ -557,7 +562,7 @@ static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block) struct nftl_uci2 uci; size_t retlen; - if (mtd->read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, + if (nftl_read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, 8, &retlen, (char *)&uci) < 0) return 0; @@ -597,10 +602,10 @@ int NFTL_mount(struct NFTLrecord *s) for (;;) { /* read the block header. If error, we format the chain */ - if (mtd->read_oob(mtd, + if (nftl_read_oob(mtd, block * s->EraseSize + 8, 8, &retlen, (char *)&h0) < 0 || - mtd->read_oob(mtd, + nftl_read_oob(mtd, block * s->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) { diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index a0d3f011c0f2..84ec40d25438 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -671,7 +671,7 @@ out: } /** - * onenand_read_oob - [MTD Interface] OneNAND read out-of-band + * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure * @param from offset to read from * @param len number of bytes to read @@ -680,8 +680,8 @@ out: * * OneNAND read out-of-band data from the spare area */ -static int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) { struct onenand_chip *this = mtd->priv; int read = 0, thislen, column; @@ -744,6 +744,21 @@ out: return ret; } +/** + * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + */ +static int onenand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + BUG_ON(ops->mode != MTD_OOB_PLACE); + + return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->len, + &ops->retlen, ops->oobbuf); +} + #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE /** * onenand_verify_oob - [GENERIC] verify the oob contents after a write @@ -894,7 +909,7 @@ out: } /** - * onenand_write_oob - [MTD Interface] OneNAND write out-of-band + * onenand_do_write_oob - [Internal] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write @@ -903,8 +918,8 @@ out: * * OneNAND write out-of-band */ -static int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { struct onenand_chip *this = mtd->priv; int column, ret = 0; @@ -973,6 +988,21 @@ out: } /** + * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + */ +static int onenand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + BUG_ON(ops->mode != MTD_OOB_PLACE); + + return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->len, + &ops->retlen, ops->oobbuf); +} + +/** * onenand_block_checkbad - [GENERIC] Check if a block is marked bad * @param mtd MTD device structure * @param ofs offset from device start @@ -1138,7 +1168,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /* We write two bytes, so we dont have to mess with 16 bit access */ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); - return mtd->write_oob(mtd, ofs , 2, &retlen, buf); + return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf); } /** @@ -1328,7 +1358,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = mtd->write_oob(mtd, from, len, retlen, buf); + ret = onenand_do_write_oob(mtd, from, len, retlen, buf); /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index aafd7c2f7802..1b00dac3d7d6 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -17,6 +17,9 @@ #include <linux/mtd/onenand.h> #include <linux/mtd/compatmac.h> +extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); + /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer * @param buf the buffer to search @@ -87,8 +90,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr /* No need to read pages fully, * just read required OOB bytes */ - ret = mtd->read_oob(mtd, from + j * mtd->writesize + bd->offs, - readlen, &retlen, &buf[0]); + ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs, + readlen, &retlen, &buf[0]); if (ret) return ret; diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 506690cc9a78..935fec1b1201 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -100,6 +100,7 @@ struct jffs2_sb_info { #ifdef CONFIG_JFFS2_FS_WRITEBUFFER /* Write-behind buffer for NAND flash */ unsigned char *wbuf; + unsigned char *oobbuf; uint32_t wbuf_ofs; uint32_t wbuf_len; struct jffs2_inodirty *wbuf_inodes; diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index c6a62e162963..1195d06d4373 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -955,158 +955,159 @@ exit: return ret; } +#define NR_OOB_SCAN_PAGES 4 + /* - * Check, if the out of band area is empty + * Check, if the out of band area is empty */ -int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) +int jffs2_check_oob_empty(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, int mode) { - unsigned char *buf; - int ret = 0; - int i,len,page; - size_t retlen; - int oob_size; - - /* allocate a buffer for all oob data in this sector */ - oob_size = c->mtd->oobsize; - len = 4 * oob_size; - buf = kmalloc(len, GFP_KERNEL); - if (!buf) { - printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n"); - return -ENOMEM; - } - /* - * if mode = 0, we scan for a total empty oob area, else we have - * to take care of the cleanmarker in the first page of the block - */ - ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf); + int i, page, ret; + int oobsize = c->mtd->oobsize; + struct mtd_oob_ops ops; + + ops.len = NR_OOB_SCAN_PAGES * oobsize; + ops.ooblen = oobsize; + ops.oobbuf = c->oobbuf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); if (ret) { - D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); - goto out; + D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " + "failed %d for block at %08x\n", ret, jeb->offset)); + return ret; } - if (retlen < len) { - D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read " - "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset)); - ret = -EIO; - goto out; + if (ops.retlen < ops.len) { + D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " + "returned short read (%zd bytes not %d) for block " + "at %08x\n", ops.retlen, ops.len, jeb->offset)); + return -EIO; } /* Special check for first page */ - for(i = 0; i < oob_size ; i++) { + for(i = 0; i < oobsize ; i++) { /* Yeah, we know about the cleanmarker. */ if (mode && i >= c->fsdata_pos && i < c->fsdata_pos + c->fsdata_len) continue; - if (buf[i] != 0xFF) { - D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", - buf[i], i, jeb->offset)); - ret = 1; - goto out; + if (ops.oobbuf[i] != 0xFF) { + D2(printk(KERN_DEBUG "Found %02x at %x in OOB for " + "%08x\n", ops.oobbuf[i], i, jeb->offset)); + return 1; } } /* we know, we are aligned :) */ - for (page = oob_size; page < len; page += sizeof(long)) { - unsigned long dat = *(unsigned long *)(&buf[page]); - if(dat != -1) { - ret = 1; - goto out; - } + for (page = oobsize; page < ops.len; page += sizeof(long)) { + long dat = *(long *)(&ops.oobbuf[page]); + if(dat != -1) + return 1; } - -out: - kfree(buf); - - return ret; + return 0; } /* -* Scan for a valid cleanmarker and for bad blocks -* For virtual blocks (concatenated physical blocks) check the cleanmarker -* only in the first page of the first physical block, but scan for bad blocks in all -* physical blocks -*/ -int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) + * Scan for a valid cleanmarker and for bad blocks + */ +int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) { struct jffs2_unknown_node n; - unsigned char buf[2 * NAND_MAX_OOBSIZE]; - unsigned char *p; - int ret, i, cnt, retval = 0; - size_t retlen, offset; - int oob_size; - - offset = jeb->offset; - oob_size = c->mtd->oobsize; - - /* Loop through the physical blocks */ - for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) { - /* Check first if the block is bad. */ - if (c->mtd->block_isbad (c->mtd, offset)) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset)); - return 2; - } - /* - * We read oob data from page 0 and 1 of the block. - * page 0 contains cleanmarker and badblock info - * page 1 contains failure count of this block - */ - ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf); + struct mtd_oob_ops ops; + int oobsize = c->mtd->oobsize; + unsigned char *p,*b; + int i, ret; + size_t offset = jeb->offset; + + /* Check first if the block is bad. */ + if (c->mtd->block_isbad(c->mtd, offset)) { + D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()" + ": Bad block at %08x\n", jeb->offset)); + return 2; + } - if (ret) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); - return ret; - } - if (retlen < (oob_size << 1)) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset)); - return -EIO; - } + ops.len = oobsize; + ops.ooblen = oobsize; + ops.oobbuf = c->oobbuf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; - /* Check cleanmarker only on the first physical block */ - if (!cnt) { - n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); - n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); - n.totlen = cpu_to_je32 (8); - p = (unsigned char *) &n; + ret = c->mtd->read_oob(c->mtd, offset, &ops); + if (ret) { + D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " + "Read OOB failed %d for block at %08x\n", + ret, jeb->offset)); + return ret; + } - for (i = 0; i < c->fsdata_len; i++) { - if (buf[c->fsdata_pos + i] != p[i]) { - retval = 1; - } - } - D1(if (retval == 1) { - printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset); - printk(KERN_WARNING "OOB at %08zx was ", offset); - for (i=0; i < oob_size; i++) { - printk("%02x ", buf[i]); - } - printk("\n"); - }) - } - offset += c->mtd->erasesize; + if (ops.retlen < ops.len) { + D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): " + "Read OOB return short read (%zd bytes not %d) " + "for block at %08x\n", ops.retlen, ops.len, + jeb->offset)); + return -EIO; } - return retval; + + n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); + n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); + n.totlen = cpu_to_je32 (8); + p = (unsigned char *) &n; + b = c->oobbuf + c->fsdata_pos; + + for (i = c->fsdata_len; i; i--) { + if (*b++ != *p++) + ret = 1; + } + + D1(if (ret == 1) { + printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " + "Cleanmarker node not detected in block at %08x\n", + offset); + printk(KERN_WARNING "OOB at %08zx was ", offset); + for (i=0; i < oobsize; i++) + printk("%02x ", c->oobbuf[i]); + printk("\n"); + }); + return ret; } -int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) { - struct jffs2_unknown_node n; - int ret; - size_t retlen; + struct jffs2_unknown_node n; + int ret; + struct mtd_oob_ops ops; n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); n.totlen = cpu_to_je32(8); - ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n); + ops.len = c->fsdata_len; + ops.ooblen = c->fsdata_len;; + ops.oobbuf = (uint8_t *)&n; + ops.ooboffs = c->fsdata_pos; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops); if (ret) { - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); + D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " + "Write failed for block at %08x: error %d\n", + jeb->offset, ret)); return ret; } - if (retlen != c->fsdata_len) { - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len)); - return ret; + if (ops.retlen != ops.len) { + D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " + "Short write for block at %08x: %zd not %d\n", + jeb->offset, ops.retlen, ops.len)); + return -EIO; } return 0; } @@ -1185,6 +1186,10 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) if (!c->wbuf) return -ENOMEM; + c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->mtd->oobsize, GFP_KERNEL); + if (!c->oobbuf) + return -ENOMEM; + res = jffs2_nand_set_oobinfo(c); #ifdef BREAKME @@ -1202,6 +1207,7 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); + kfree(c->oobbuf); } int jffs2_dataflash_setup(struct jffs2_sb_info *c) { diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 4970c2e96fbf..e75bb584e80b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -67,6 +67,50 @@ struct mtd_ecc_stats { unsigned long failed; }; +/* + * oob operation modes + * + * MTD_OOB_PLACE: oob data are placed at the given offset + * MTD_OOB_AUTO: oob data are automatically placed at the free areas + * which are defined by the ecclayout + * MTD_OOB_RAW: mode to read raw data+oob in one chunk. The oob data + * is inserted into the data. Thats a raw image of the + * flash contents. + */ +typedef enum { + MTD_OOB_PLACE, + MTD_OOB_AUTO, + MTD_OOB_RAW, +} mtd_oob_mode_t; + +/** + * struct mtd_oob_ops - oob operation operands + * @mode: operation mode + * + * @len: number of bytes to write/read. When a data buffer is given + * (datbuf != NULL) this is the number of data bytes. When + + no data buffer is available this is the number of oob bytes. + * + * @retlen: number of bytes written/read. When a data buffer is given + * (datbuf != NULL) this is the number of data bytes. When + + no data buffer is available this is the number of oob bytes. + * + * @ooblen: number of oob bytes per page + * @ooboffs: offset of oob data in the oob area (only relevant when + * mode = MTD_OOB_PLACE) + * @datbuf: data buffer - if NULL only oob data are read/written + * @oobbuf: oob data buffer + */ +struct mtd_oob_ops { + mtd_oob_mode_t mode; + size_t len; + size_t retlen; + size_t ooblen; + uint32_t ooboffs; + uint8_t *datbuf; + uint8_t *oobbuf; +}; + struct mtd_info { u_char type; u_int32_t flags; @@ -125,8 +169,10 @@ struct mtd_info { 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_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); + int (*read_oob) (struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops); + int (*write_oob) (struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); /* * Methods to access the protection register area, present in some diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index dc2bf1bcf42b..bf2ce68901f5 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -31,14 +31,6 @@ 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); - - -extern int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf, uint8_t *oob); - /* The maximum number of NAND chips in an array */ #define NAND_MAX_CHIPS 8 @@ -375,6 +367,8 @@ struct nand_chip { struct nand_buffers buffers; struct nand_hw_control hwcontrol; + struct mtd_oob_ops ops; + uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; |