]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/mtd/mtdconcat.c
MTD: mtdconcat NAND/Sibley support (rev.2)
[linux-2.6-omap-h63xx.git] / drivers / mtd / mtdconcat.c
index f3e65af33a9c86d6338847ddb2b4c16ed5229774..da4c9c190bb3e4abc1b2e3d8d46ad553da5de145 100644 (file)
@@ -7,14 +7,15 @@
  *
  * This code is GPL
  *
- * $Id: mtdconcat.c,v 1.9 2004/06/30 15:17:41 dbrown Exp $
+ * $Id: mtdconcat.c,v 1.11 2005/11/07 11:14:20 gleixner Exp $
  */
 
-#include <linux/module.h>
-#include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/sched.h>       /* TASK_* */
+#include <linux/sched.h>
+#include <linux/types.h>
+
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/concat.h>
 
@@ -43,7 +44,7 @@ struct mtd_concat {
  */
 #define CONCAT(x)  ((struct mtd_concat *)(x))
 
-/* 
+/*
  * MTD methods which look up the relevant subdevice, translate the
  * effective address and pass through to the subdevice.
  */
@@ -249,6 +250,106 @@ concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
        return err;
 }
 
+static int
+concat_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
+               unsigned long count, loff_t to, size_t * retlen,
+               u_char *eccbuf, struct nand_oobinfo *oobsel)
+{
+       struct mtd_concat *concat = CONCAT(mtd);
+       struct kvec *vecs_copy;
+       unsigned long entry_low, entry_high;
+       size_t total_len = 0;
+       int i;
+       int err = -EINVAL;
+
+       if (!(mtd->flags & MTD_WRITEABLE))
+               return -EROFS;
+
+       *retlen = 0;
+
+       /* Calculate total length of data */
+       for (i = 0; i < count; i++)
+               total_len += vecs[i].iov_len;
+
+       /* Do not allow write past end of device */
+       if ((to + total_len) > mtd->size)
+               return -EINVAL;
+
+       /* Check alignment */
+       if (mtd->writesize > 1)
+               if ((to % mtd->writesize) || (total_len % mtd->writesize))
+                       return -EINVAL;
+
+       /* make a copy of vecs */
+       vecs_copy = kmalloc(sizeof(struct kvec) * count, GFP_KERNEL);
+       if (!vecs_copy)
+               return -ENOMEM;
+       memcpy(vecs_copy, vecs, sizeof(struct kvec) * count);
+
+       entry_low = 0;
+       for (i = 0; i < concat->num_subdev; i++) {
+               struct mtd_info *subdev = concat->subdev[i];
+               size_t size, wsize, retsize, old_iov_len;
+
+               if (to >= subdev->size) {
+                       to -= subdev->size;
+                       continue;
+               }
+
+               size = min(total_len, (size_t)(subdev->size - to));
+               wsize = size; /* store for future use */
+
+               entry_high = entry_low;
+               while (entry_high < count) {
+                       if (size <= vecs_copy[entry_high].iov_len)
+                               break;
+                       size -= vecs_copy[entry_high++].iov_len;
+               }
+
+               old_iov_len = vecs_copy[entry_high].iov_len;
+               vecs_copy[entry_high].iov_len = size;
+
+               if (!(subdev->flags & MTD_WRITEABLE))
+                       err = -EROFS;
+               else if (eccbuf)
+                       err = subdev->writev_ecc(subdev, &vecs_copy[entry_low],
+                               entry_high - entry_low + 1, to, &retsize,
+                               eccbuf, oobsel);
+               else
+                       err = subdev->writev(subdev, &vecs_copy[entry_low],
+                               entry_high - entry_low + 1, to, &retsize);
+
+               vecs_copy[entry_high].iov_len = old_iov_len - size;
+               vecs_copy[entry_high].iov_base += size;
+
+               entry_low = entry_high;
+
+               if (err)
+                       break;
+
+               *retlen += retsize;
+               total_len -= wsize;
+               if (concat->mtd.type == MTD_NANDFLASH && eccbuf)
+                       eccbuf += mtd->oobavail * (wsize / mtd->oobblock);
+
+               if (total_len == 0)
+                       break;
+
+               err = -EINVAL;
+               to = 0;
+       }
+
+       kfree(vecs_copy);
+       return err;
+}
+
+static int
+concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
+               unsigned long count, loff_t to, size_t * retlen)
+{
+       return concat_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL);
+}
+
 static int
 concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
                size_t * retlen, u_char * buf)
@@ -476,8 +577,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
        }
 
        /* must never happen since size limit has been verified above */
-       if (i >= concat->num_subdev)
-               BUG();
+       BUG_ON(i >= concat->num_subdev);
 
        /* now do the erase: */
        err = 0;
@@ -499,8 +599,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
                if ((err = concat_dev_erase(subdev, erase))) {
                        /* sanity check: should never happen since
                         * block alignment has been checked above */
-                       if (err == -EINVAL)
-                               BUG();
+                       BUG_ON(err == -EINVAL);
                        if (erase->fail_addr != 0xffffffff)
                                instr->fail_addr = erase->fail_addr + offset;
                        break;
@@ -637,6 +736,58 @@ static void concat_resume(struct mtd_info *mtd)
        }
 }
 
+static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+       struct mtd_concat *concat = CONCAT(mtd);
+       int i, res = 0;
+
+       if (!concat->subdev[0]->block_isbad)
+               return res;
+
+       if (ofs > mtd->size)
+               return -EINVAL;
+
+       for (i = 0; i < concat->num_subdev; i++) {
+               struct mtd_info *subdev = concat->subdev[i];
+
+               if (ofs >= subdev->size) {
+                       ofs -= subdev->size;
+                       continue;
+               }
+
+               res = subdev->block_isbad(subdev, ofs);
+               break;
+       }
+
+       return res;
+}
+
+static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+       struct mtd_concat *concat = CONCAT(mtd);
+       int i, err = -EINVAL;
+
+       if (!concat->subdev[0]->block_markbad)
+               return 0;
+
+       if (ofs > mtd->size)
+               return -EINVAL;
+
+       for (i = 0; i < concat->num_subdev; i++) {
+               struct mtd_info *subdev = concat->subdev[i];
+
+               if (ofs >= subdev->size) {
+                       ofs -= subdev->size;
+                       continue;
+               }
+
+               err = subdev->block_markbad(subdev, ofs);
+               break;
+       }
+
+       return err;
+}
+
 /*
  * This function constructs a virtual MTD device by concatenating
  * num_devs MTD devices. A pointer to the new device object is
@@ -686,10 +837,18 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],     /* subdevices to c
                concat->mtd.read_ecc = concat_read_ecc;
        if (subdev[0]->write_ecc)
                concat->mtd.write_ecc = concat_write_ecc;
+       if (subdev[0]->writev)
+               concat->mtd.writev = concat_writev;
+       if (subdev[0]->writev_ecc)
+               concat->mtd.writev_ecc = concat_writev_ecc;
        if (subdev[0]->read_oob)
                concat->mtd.read_oob = concat_read_oob;
        if (subdev[0]->write_oob)
                concat->mtd.write_oob = concat_write_oob;
+       if (subdev[0]->block_isbad)
+               concat->mtd.block_isbad = concat_block_isbad;
+       if (subdev[0]->block_markbad)
+               concat->mtd.block_markbad = concat_block_markbad;
 
        concat->subdev[0] = subdev[0];
 
@@ -735,14 +894,13 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],     /* subdevices to c
 
        }
 
+       if(concat->mtd.type == MTD_NANDFLASH)
+               memcpy(&concat->mtd.oobinfo, &subdev[0]->oobinfo,
+                       sizeof(struct nand_oobinfo));
+
        concat->num_subdev = num_devs;
        concat->mtd.name = name;
 
-       /*
-        * NOTE: for now, we do not provide any readv()/writev() methods
-        *       because they are messy to implement and they are not
-        *       used to a great extent anyway.
-        */
        concat->mtd.erase = concat_erase;
        concat->mtd.read = concat_read;
        concat->mtd.write = concat_write;
@@ -877,7 +1035,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],      /* subdevices to c
        return &concat->mtd;
 }
 
-/* 
+/*
  * This function destroys an MTD object obtained from concat_mtd_devs()
  */