[ARM] pxa: move pxa2xx chip selects definitions out of pxa-regs.h
[linux-2.6.git] / drivers / mtd / inftlcore.c
index 3396f0e..73f0522 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) {
@@ -151,6 +147,67 @@ 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;
+
+       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.
  */
@@ -227,9 +284,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;
@@ -292,7 +349,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,
@@ -304,9 +361,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);
        }
 
        /*
@@ -331,6 +388,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.
@@ -339,7 +400,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++;
                }
        }
@@ -437,8 +497,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 +585,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 +607,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 +618,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 +670,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 +771,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 +806,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 +850,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 +880,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
@@ -859,7 +919,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;
@@ -880,6 +943,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,
@@ -890,9 +954,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);
 }