X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmtd%2Finftlcore.c;h=b0e396504e67cf98d2469afc881594706c706bd9;hb=refs%2Fheads%2Fvserver;hp=8a544890173d3c857c953eb149c68b14b145ba90;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 8a5448901..b0e396504 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -24,7 +24,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include @@ -36,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -47,9 +47,6 @@ */ #define MAX_LOOPS 10000 -extern void INFTL_dumptables(struct INFTLrecord *inftl); -extern void INFTL_dumpVUchains(struct INFTLrecord *inftl); - static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { struct INFTLrecord *inftl; @@ -70,26 +67,23 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name); - inftl = kmalloc(sizeof(*inftl), GFP_KERNEL); + inftl = kzalloc(sizeof(*inftl), GFP_KERNEL); if (!inftl) { printk(KERN_WARNING "INFTL: Out of memory for data structures\n"); return; } - memset(inftl, 0, sizeof(*inftl)); inftl->mbd.mtd = mtd; inftl->mbd.devnum = -1; - inftl->mbd.blksize = 512; + inftl->mbd.tr = tr; - memcpy(&inftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo)); - inftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY; - if (INFTL_mount(inftl) < 0) { + if (INFTL_mount(inftl) < 0) { printk(KERN_WARNING "INFTL: could not mount device\n"); kfree(inftl); return; - } + } /* OK, it's a new one. Set up all the data structures. */ @@ -132,7 +126,7 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) return; } #ifdef PSYCHO_DEBUG - printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); + printk(KERN_INFO "INFTL: Found new inftl%c\n", inftl->mbd.devnum + 'a'); #endif return; } @@ -154,6 +148,67 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev) * Actual INFTL access routines. */ +/* + * 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; + + res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.oobretlen; + 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; + + res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); + *retlen = ops.oobretlen; + 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. @@ -201,10 +256,11 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned u16 BlockMap[MAX_SECTORS_PER_UNIT]; unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; unsigned int thisEUN, prevEUN, status; + struct mtd_info *mtd = inftl->mbd.mtd; int block, silly; unsigned int targetEUN; struct inftl_oob oob; - size_t retlen; + size_t retlen; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d," "pending=%d)\n", inftl, thisVUC, pendingblock); @@ -224,18 +280,18 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned * Scan to find the Erase Unit which holds the actual data for each * 512-byte block within the Chain. */ - silly = MAX_LOOPS; + silly = MAX_LOOPS; while (thisEUN < inftl->nb_blocks) { for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) { if ((BlockMap[block] != 0xffff) || BlockDeleted[block]) continue; - if (MTD_READOOB(inftl->mbd.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; + status = oob.b.Status | oob.b.Status1; switch(status) { case SECTOR_FREE: @@ -285,29 +341,31 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned continue; } - /* + /* * Copy only in non free block (free blocks can only * happen in case of media errors or deleted blocks). */ - if (BlockMap[block] == BLOCK_NIL) - continue; - - ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize * - BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, - &retlen, movebuf); - if (ret < 0) { - ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize * - BlockMap[block]) + (block * SECTORSIZE), - SECTORSIZE, &retlen, movebuf); + if (BlockMap[block] == BLOCK_NIL) + continue; + + ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) + + (block * SECTORSIZE), SECTORSIZE, &retlen, + movebuf); + if (ret < 0 && ret != -EUCLEAN) { + ret = mtd->read(mtd, + (inftl->EraseSize * BlockMap[block]) + + (block * SECTORSIZE), SECTORSIZE, + &retlen, movebuf); if (ret != -EIO) - DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went " - "away on retry?\n"); - } - memset(&oob, 0xff, sizeof(struct inftl_oob)); - oob.b.Status = oob.b.Status1 = SECTOR_USED; - MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + - (block * SECTORSIZE), SECTORSIZE, &retlen, - movebuf, (char *)&oob, &inftl->oobinfo); + DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went " + "away on retry?\n"); + } + memset(&oob, 0xff, sizeof(struct inftl_oob)); + oob.b.Status = oob.b.Status1 = SECTOR_USED; + + inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + + (block * SECTORSIZE), SECTORSIZE, &retlen, + movebuf, (char *)&oob); } /* @@ -332,17 +390,17 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned if (thisEUN == targetEUN) break; - if (INFTL_formatblock(inftl, thisEUN) < 0) { + if (INFTL_formatblock(inftl, thisEUN) < 0) { /* * Could not erase : mark block as reserved. */ inftl->PUtable[thisEUN] = BLOCK_RESERVED; - } else { + } else { /* Correctly erased : mark it as free */ inftl->PUtable[thisEUN] = BLOCK_FREE; inftl->PUtable[prevEUN] = BLOCK_NIL; inftl->numfreeEUNs++; - } + } } return targetEUN; @@ -418,6 +476,7 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE); unsigned int thisEUN, writeEUN, prev_block, status; unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1); + struct mtd_info *mtd = inftl->mbd.mtd; struct inftl_oob oob; struct inftl_bci bci; unsigned char anac, nacs, parity; @@ -437,10 +496,10 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) silly = MAX_LOOPS; while (thisEUN <= inftl->lastEUN) { - MTD_READOOB(inftl->mbd.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; + status = bci.Status | bci.Status1; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in " "EUN %d is %x\n", block , writeEUN, status); @@ -525,8 +584,8 @@ hitused: nacs = 0; thisEUN = inftl->VUtable[thisVUC]; if (thisEUN != BLOCK_NIL) { - MTD_READOOB(inftl->mbd.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 +606,8 @@ hitused: oob.u.a.parityPerField = parity; oob.u.a.discarded = 0xaa; - MTD_WRITEOOB(inftl->mbd.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 +617,8 @@ hitused: oob.u.b.parityPerField = parity; oob.u.b.discarded = 0xaa; - MTD_WRITEOOB(inftl->mbd.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; @@ -579,6 +638,7 @@ hitused: */ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) { + struct mtd_info *mtd = inftl->mbd.mtd; unsigned char BlockUsed[MAX_SECTORS_PER_UNIT]; unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; unsigned int thisEUN, status; @@ -609,9 +669,9 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) if (BlockUsed[block] || BlockDeleted[block]) continue; - if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) - + (block * SECTORSIZE), 8 , &retlen, - (char *)&bci) < 0) + if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + + (block * SECTORSIZE), 8 , &retlen, + (char *)&bci) < 0) status = SECTOR_IGNORE; else status = bci.Status | bci.Status1; @@ -673,12 +733,12 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n", thisEUN, thisVUC); - if (INFTL_formatblock(inftl, thisEUN) < 0) { + if (INFTL_formatblock(inftl, thisEUN) < 0) { /* * Could not erase : mark block as reserved. */ inftl->PUtable[thisEUN] = BLOCK_RESERVED; - } else { + } else { /* Correctly erased : mark it as free */ inftl->PUtable[thisEUN] = BLOCK_FREE; inftl->numfreeEUNs++; @@ -700,6 +760,7 @@ static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block) { unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); + struct mtd_info *mtd = inftl->mbd.mtd; unsigned int status; int silly = MAX_LOOPS; size_t retlen; @@ -709,8 +770,8 @@ static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block) "block=%d)\n", inftl, block); while (thisEUN < inftl->nb_blocks) { - if (MTD_READOOB(inftl->mbd.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; @@ -744,10 +805,10 @@ foundit: if (thisEUN != BLOCK_NIL) { loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; - if (MTD_READOOB(inftl->mbd.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_WRITEOOB(inftl->mbd.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)); } @@ -787,9 +848,10 @@ 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; - MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + - blockofs, SECTORSIZE, &retlen, (char *)buffer, - (char *)&oob, &inftl->oobinfo); + + 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 @@ -807,17 +869,18 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, struct INFTLrecord *inftl = (void *)mbd; unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); - unsigned int status; + struct mtd_info *mtd = inftl->mbd.mtd; + unsigned int status; int silly = MAX_LOOPS; - struct inftl_bci bci; + struct inftl_bci bci; size_t retlen; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld," "buffer=%p)\n", inftl, block, buffer); while (thisEUN < inftl->nb_blocks) { - if (MTD_READOOB(inftl->mbd.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; @@ -853,10 +916,12 @@ foundit: /* The requested block is not on the media, return all 0x00 */ memset(buffer, 0, SECTORSIZE); } else { - size_t retlen; + size_t retlen; loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; - if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen, - buffer)) + int ret = mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer); + + /* Handle corrected bit flips gracefully */ + if (ret < 0 && ret != -EUCLEAN) return -EIO; } return 0; @@ -877,6 +942,7 @@ static struct mtd_blktrans_ops inftl_tr = { .name = "inftl", .major = INFTL_MAJOR, .part_bits = INFTL_PARTN_BITS, + .blksize = 512, .getgeo = inftl_getgeo, .readsect = inftl_readblock, .writesect = inftl_writeblock, @@ -885,8 +951,6 @@ static struct mtd_blktrans_ops inftl_tr = { .owner = THIS_MODULE, }; -extern char inftlmountrev[]; - static int __init init_inftl(void) { printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.19 $, "