summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-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
14 files changed, 859 insertions, 468 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;