summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--drivers/mtd/devices/doc2000.c39
-rw-r--r--drivers/mtd/devices/doc2001.c34
-rw-r--r--drivers/mtd/devices/doc2001plus.c34
-rw-r--r--drivers/mtd/inftlcore.c111
-rw-r--r--drivers/mtd/inftlmount.c27
-rw-r--r--drivers/mtd/mtdchar.c59
-rw-r--r--drivers/mtd/mtdconcat.c90
-rw-r--r--drivers/mtd/mtdpart.c29
-rw-r--r--drivers/mtd/nand/nand_base.c542
-rw-r--r--drivers/mtd/nand/nand_bbt.c188
-rw-r--r--drivers/mtd/nftlcore.c92
-rw-r--r--drivers/mtd/nftlmount.c29
-rw-r--r--drivers/mtd/onenand/onenand_base.c46
-rw-r--r--drivers/mtd/onenand/onenand_bbt.c7
-rw-r--r--fs/jffs2/jffs2_fs_sb.h1
-rw-r--r--fs/jffs2/wbuf.c230
-rw-r--r--include/linux/mtd/mtd.h50
-rw-r--r--include/linux/mtd/nand.h10
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;