UBI: amend NOR flash pre-erase quirk
[safe/jmp/linux-2.6] / drivers / mtd / inftlcore.c
index efb1a95..d8cf29c 100644 (file)
@@ -7,8 +7,6 @@
  * (c) 1999 Machine Vision Holdings, Inc.
  * Author: David Woodhouse <dwmw2@infradead.org>
  *
- * $Id: inftlcore.c,v 1.19 2005/11/07 11:14:20 gleixner Exp $
- *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -24,7 +22,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/delay.h>
@@ -53,7 +50,7 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
        struct INFTLrecord *inftl;
        unsigned long temp;
 
-       if (mtd->type != MTD_NANDFLASH)
+       if (mtd->type != MTD_NANDFLASH || mtd->size > UINT_MAX)
                return;
        /* OK, this is moderately ugly.  But probably safe.  Alternatives? */
        if (memcmp(mtd->name, "DiskOnChip", 10))
@@ -68,17 +65,16 @@ 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;
 
        if (INFTL_mount(inftl) < 0) {
@@ -164,10 +160,9 @@ int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
        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;
+       *retlen = ops.oobretlen;
        return res;
 }
 
@@ -185,10 +180,9 @@ int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
        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;
+       *retlen = ops.oobretlen;
        return res;
 }
 
@@ -232,7 +226,7 @@ static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
        if (!desperate && inftl->numfreeEUNs < 2) {
                DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free "
                        "EUNs (%d)\n", inftl->numfreeEUNs);
-               return 0xffff;
+               return BLOCK_NIL;
        }
 
        /* Scan for a free block */
@@ -287,7 +281,8 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
        silly = MAX_LOOPS;
        while (thisEUN < inftl->nb_blocks) {
                for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
-                       if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
+                       if ((BlockMap[block] != BLOCK_NIL) ||
+                           BlockDeleted[block])
                                continue;
 
                        if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
@@ -355,7 +350,7 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
                ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) +
                                (block * SECTORSIZE), SECTORSIZE, &retlen,
                                movebuf);
-               if (ret < 0) {
+               if (ret < 0 && ret != -EUCLEAN) {
                        ret = mtd->read(mtd,
                                        (inftl->EraseSize * BlockMap[block]) +
                                        (block * SECTORSIZE), SECTORSIZE,
@@ -394,6 +389,10 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
                if (thisEUN == targetEUN)
                        break;
 
+               /* Unlink the last block from the chain. */
+               inftl->PUtable[prevEUN] = BLOCK_NIL;
+
+               /* Now try to erase it. */
                if (INFTL_formatblock(inftl, thisEUN) < 0) {
                        /*
                         * Could not erase : mark block as reserved.
@@ -402,7 +401,6 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
                } else {
                        /* Correctly erased : mark it as free */
                        inftl->PUtable[thisEUN] = BLOCK_FREE;
-                       inftl->PUtable[prevEUN] = BLOCK_NIL;
                        inftl->numfreeEUNs++;
                }
        }
@@ -528,7 +526,7 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
                        if (!silly--) {
                                printk(KERN_WARNING "INFTL: infinite loop in "
                                        "Virtual Unit Chain 0x%x\n", thisVUC);
-                               return 0xffff;
+                               return BLOCK_NIL;
                        }
 
                        /* Skip to next block in chain */
@@ -552,7 +550,7 @@ hitused:
                         * waiting to be picked up. We're going to have to fold
                         * a chain to make room.
                         */
-                       thisEUN = INFTL_makefreeblock(inftl, 0xffff);
+                       thisEUN = INFTL_makefreeblock(inftl, BLOCK_NIL);
 
                        /*
                         * Hopefully we free something, lets try again.
@@ -634,7 +632,7 @@ hitused:
 
        printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
                "Unit Chain 0x%x\n", thisVUC);
-       return 0xffff;
+       return BLOCK_NIL;
 }
 
 /*
@@ -922,7 +920,10 @@ foundit:
        } else {
                size_t retlen;
                loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
-               if (mtd->read(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;
@@ -943,6 +944,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,
@@ -953,9 +955,6 @@ static struct mtd_blktrans_ops inftl_tr = {
 
 static int __init init_inftl(void)
 {
-       printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.19 $, "
-               "inftlmount.c %s\n", inftlmountrev);
-
        return register_mtd_blktrans(&inftl_tr);
 }