fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / mtd / mtdpart.c
index 460d56f..bafd2fb 100644 (file)
@@ -5,18 +5,17 @@
  *
  * This code is GPL
  *
- * $Id: mtdpart.c,v 1.41 2003/06/18 14:53:02 dwmw2 Exp $
+ * $Id: mtdpart.c,v 1.55 2005/11/07 11:14:20 gleixner Exp $
  *
  *     02-21-2002      Thomas Gleixner <gleixner@autronix.de>
  *                     added support for read_oob, write_oob
- */    
+ */
 
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/list.h>
-#include <linux/config.h>
 #include <linux/kmod.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
@@ -41,29 +40,34 @@ struct mtd_part {
  */
 #define PART(x)  ((struct mtd_part *)(x))
 
-       
-/* 
+
+/*
  * MTD methods which simply translate the effective address and pass through
  * to the _real_ device.
  */
 
-static int part_read (struct mtd_info *mtd, loff_t from, size_t len, 
+static int part_read (struct mtd_info *mtd, loff_t from, size_t len,
                        size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
+       int res;
+
        if (from >= mtd->size)
                len = 0;
        else if (from + len > mtd->size)
                len = mtd->size - from;
-       if (part->master->read_ecc == NULL)     
-               return part->master->read (part->master, from + part->offset, 
-                                       len, retlen, buf);
-       else
-               return part->master->read_ecc (part->master, from + part->offset, 
-                                       len, retlen, buf, NULL, &mtd->oobinfo);
+       res = part->master->read (part->master, from + part->offset,
+                                  len, retlen, buf);
+       if (unlikely(res)) {
+               if (res == -EUCLEAN)
+                       mtd->ecc_stats.corrected++;
+               if (res == -EBADMSG)
+                       mtd->ecc_stats.failed++;
+       }
+       return res;
 }
 
-static int part_point (struct mtd_info *mtd, loff_t from, size_t len, 
+static int part_point (struct mtd_info *mtd, loff_t from, size_t len,
                        size_t *retlen, u_char **buf)
 {
        struct mtd_part *part = PART(mtd);
@@ -71,9 +75,10 @@ static int part_point (struct mtd_info *mtd, loff_t from, size_t len,
                len = 0;
        else if (from + len > mtd->size)
                len = mtd->size - from;
-       return part->master->point (part->master, from + part->offset, 
+       return part->master->point (part->master, from + part->offset,
                                    len, retlen, buf);
 }
+
 static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
 {
        struct mtd_part *part = PART(mtd);
@@ -81,176 +86,141 @@ 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_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 part_read_oob(struct mtd_info *mtd, loff_t from,
+                        struct mtd_oob_ops *ops)
 {
        struct mtd_part *part = PART(mtd);
-       if (oobsel == NULL)
-               oobsel = &mtd->oobinfo;
+       int res;
+
        if (from >= mtd->size)
-               len = 0;
-       else if (from + len > mtd->size)
-               len = mtd->size - from;
-       return part->master->read_ecc (part->master, from + part->offset, 
-                                       len, retlen, buf, eccbuf, oobsel);
+               return -EINVAL;
+       if (ops->datbuf && from + ops->len > mtd->size)
+               return -EINVAL;
+       res = part->master->read_oob(part->master, from + part->offset, ops);
+
+       if (unlikely(res)) {
+               if (res == -EUCLEAN)
+                       mtd->ecc_stats.corrected++;
+               if (res == -EBADMSG)
+                       mtd->ecc_stats.failed++;
+       }
+       return res;
 }
 
-static int part_read_oob (struct mtd_info *mtd, loff_t from, size_t len, 
+static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
                        size_t *retlen, u_char *buf)
 {
        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, 
+       return part->master->read_user_prot_reg (part->master, from,
                                        len, retlen, buf);
 }
 
-static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, 
-                       size_t *retlen, u_char *buf)
+static int part_get_user_prot_info (struct mtd_info *mtd,
+                                   struct otp_info *buf, size_t len)
 {
        struct mtd_part *part = PART(mtd);
-       return part->master->read_user_prot_reg (part->master, from, 
-                                       len, retlen, buf);
+       return part->master->get_user_prot_info (part->master, buf, len);
 }
 
-static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, 
+static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
                        size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
-       return part->master->read_fact_prot_reg (part->master, from, 
+       return part->master->read_fact_prot_reg (part->master, from,
                                        len, retlen, buf);
 }
 
-static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
-                       size_t *retlen, const u_char *buf)
+static int part_get_fact_prot_info (struct mtd_info *mtd,
+                                   struct otp_info *buf, size_t len)
 {
        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;
-       if (part->master->write_ecc == NULL)    
-               return part->master->write (part->master, to + part->offset, 
-                                       len, retlen, buf);
-       else
-               return part->master->write_ecc (part->master, to + part->offset, 
-                                       len, retlen, buf, NULL, &mtd->oobinfo);
-                                                       
+       return part->master->get_fact_prot_info (part->master, buf, len);
 }
 
-static int part_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 part_write (struct mtd_info *mtd, loff_t to, size_t len,
+                       size_t *retlen, const u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
        if (!(mtd->flags & MTD_WRITEABLE))
                return -EROFS;
-       if (oobsel == NULL)
-               oobsel = &mtd->oobinfo;
        if (to >= mtd->size)
                len = 0;
        else if (to + len > mtd->size)
                len = mtd->size - to;
-       return part->master->write_ecc (part->master, to + part->offset, 
-                                       len, retlen, buf, eccbuf, oobsel);
+       return part->master->write (part->master, to + part->offset,
+                                   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 (ops->datbuf && 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, 
+static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
                        size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
-       return part->master->write_user_prot_reg (part->master, from, 
+       return part->master->write_user_prot_reg (part->master, from,
                                        len, retlen, buf);
 }
 
-static int part_writev (struct mtd_info *mtd,  const struct iovec *vecs,
-                        unsigned long count, loff_t to, size_t *retlen)
+static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len)
 {
        struct mtd_part *part = PART(mtd);
-       if (!(mtd->flags & MTD_WRITEABLE))
-               return -EROFS;
-       if (part->master->writev_ecc == NULL)   
-               return part->master->writev (part->master, vecs, count,
-                                       to + part->offset, retlen);
-       else
-               return part->master->writev_ecc (part->master, vecs, count,
-                                       to + part->offset, retlen,
-                                       NULL, &mtd->oobinfo);
+       return part->master->lock_user_prot_reg (part->master, from, len);
 }
 
-static int part_readv (struct mtd_info *mtd,  struct iovec *vecs,
-                        unsigned long count, loff_t from, size_t *retlen)
-{
-       struct mtd_part *part = PART(mtd);
-       if (part->master->readv_ecc == NULL)    
-               return part->master->readv (part->master, vecs, count,
-                                       from + part->offset, retlen);
-       else
-               return part->master->readv_ecc (part->master, vecs, count,
-                                       from + part->offset, retlen, 
-                                       NULL, &mtd->oobinfo);
-}
-
-static int part_writev_ecc (struct mtd_info *mtd,  const struct iovec *vecs,
-                        unsigned long count, loff_t to, size_t *retlen,
-                        u_char *eccbuf,  struct nand_oobinfo *oobsel)
+static int part_writev (struct mtd_info *mtd,  const struct kvec *vecs,
+                        unsigned long count, loff_t to, size_t *retlen)
 {
        struct mtd_part *part = PART(mtd);
        if (!(mtd->flags & MTD_WRITEABLE))
                return -EROFS;
-       if (oobsel == NULL)
-               oobsel = &mtd->oobinfo;
-       return part->master->writev_ecc (part->master, vecs, count,
-                                       to + part->offset, retlen,
-                                       eccbuf, oobsel);
-}
-
-static int part_readv_ecc (struct mtd_info *mtd,  struct iovec *vecs,
-                        unsigned long count, loff_t from, size_t *retlen,
-                        u_char *eccbuf,  struct nand_oobinfo *oobsel)
-{
-       struct mtd_part *part = PART(mtd);
-       if (oobsel == NULL)
-               oobsel = &mtd->oobinfo;
-       return part->master->readv_ecc (part->master, vecs, count,
-                                       from + part->offset, retlen, 
-                                       eccbuf, oobsel);
+       return part->master->writev (part->master, vecs, count,
+                                       to + part->offset, retlen);
 }
 
 static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
 {
        struct mtd_part *part = PART(mtd);
+       int ret;
        if (!(mtd->flags & MTD_WRITEABLE))
                return -EROFS;
        if (instr->addr >= mtd->size)
                return -EINVAL;
        instr->addr += part->offset;
-       return part->master->erase(part->master, instr);
+       ret = part->master->erase(part->master, instr);
+       return ret;
 }
 
+void mtd_erase_callback(struct erase_info *instr)
+{
+       if (instr->mtd->erase == part_erase) {
+               struct mtd_part *part = PART(instr->mtd);
+
+               if (instr->fail_addr != 0xffffffff)
+                       instr->fail_addr -= part->offset;
+               instr->addr -= part->offset;
+       }
+       if (instr->callback)
+               instr->callback(instr);
+}
+EXPORT_SYMBOL_GPL(mtd_erase_callback);
+
 static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
 {
        struct mtd_part *part = PART(mtd);
-       if ((len + ofs) > mtd->size) 
+       if ((len + ofs) > mtd->size)
                return -EINVAL;
        return part->master->lock(part->master, ofs + part->offset, len);
 }
@@ -258,7 +228,7 @@ static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
 static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len)
 {
        struct mtd_part *part = PART(mtd);
-       if ((len + ofs) > mtd->size) 
+       if ((len + ofs) > mtd->size)
                return -EINVAL;
        return part->master->unlock(part->master, ofs + part->offset, len);
 }
@@ -281,8 +251,33 @@ static void part_resume(struct mtd_info *mtd)
        part->master->resume(part->master);
 }
 
-/* 
- * This function unregisters and destroy all slave MTD objects which are 
+static int part_block_isbad (struct mtd_info *mtd, loff_t ofs)
+{
+       struct mtd_part *part = PART(mtd);
+       if (ofs >= mtd->size)
+               return -EINVAL;
+       ofs += part->offset;
+       return part->master->block_isbad(part->master, ofs);
+}
+
+static int part_block_markbad (struct mtd_info *mtd, loff_t ofs)
+{
+       struct mtd_part *part = PART(mtd);
+       int res;
+
+       if (!(mtd->flags & MTD_WRITEABLE))
+               return -EROFS;
+       if (ofs >= mtd->size)
+               return -EINVAL;
+       ofs += part->offset;
+       res = part->master->block_markbad(part->master, ofs);
+       if (!res)
+               mtd->ecc_stats.badblocks++;
+       return res;
+}
+
+/*
+ * This function unregisters and destroy all slave MTD objects which are
  * attached to the given master MTD object.
  */
 
@@ -315,8 +310,8 @@ int del_mtd_partitions(struct mtd_info *master)
  * (Q: should we register the master MTD object as well?)
  */
 
-int add_mtd_partitions(struct mtd_info *master, 
-                      struct mtd_partition *parts,
+int add_mtd_partitions(struct mtd_info *master,
+                      const struct mtd_partition *parts,
                       int nbparts)
 {
        struct mtd_part *slave;
@@ -328,24 +323,24 @@ int add_mtd_partitions(struct mtd_info *master,
        for (i = 0; i < nbparts; i++) {
 
                /* allocate the partition structure */
-               slave = kmalloc (sizeof(*slave), GFP_KERNEL);
+               slave = kzalloc (sizeof(*slave), GFP_KERNEL);
                if (!slave) {
                        printk ("memory allocation error while creating partitions for \"%s\"\n",
                                master->name);
                        del_mtd_partitions(master);
                        return -ENOMEM;
                }
-               memset(slave, 0, sizeof(*slave));
                list_add(&slave->list, &mtd_partitions);
 
                /* set up the MTD object for this partition */
                slave->mtd.type = master->type;
                slave->mtd.flags = master->flags & ~parts[i].mask_flags;
                slave->mtd.size = parts[i].size;
-               slave->mtd.oobblock = master->oobblock;
+               slave->mtd.writesize = master->writesize;
                slave->mtd.oobsize = master->oobsize;
                slave->mtd.ecctype = master->ecctype;
                slave->mtd.eccsize = master->eccsize;
+               slave->mtd.subpage_sft = master->subpage_sft;
 
                slave->mtd.name = parts[i].name;
                slave->mtd.bank_size = master->bank_size;
@@ -358,11 +353,7 @@ int add_mtd_partitions(struct mtd_info *master,
                        slave->mtd.point = part_point;
                        slave->mtd.unpoint = part_unpoint;
                }
-               
-               if (master->read_ecc)
-                       slave->mtd.read_ecc = part_read_ecc;
-               if (master->write_ecc)
-                       slave->mtd.write_ecc = part_write_ecc;
+
                if (master->read_oob)
                        slave->mtd.read_oob = part_read_oob;
                if (master->write_oob)
@@ -373,6 +364,12 @@ int add_mtd_partitions(struct mtd_info *master,
                        slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
                if(master->write_user_prot_reg)
                        slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
+               if(master->lock_user_prot_reg)
+                       slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
+               if(master->get_user_prot_info)
+                       slave->mtd.get_user_prot_info = part_get_user_prot_info;
+               if(master->get_fact_prot_info)
+                       slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
                if (master->sync)
                        slave->mtd.sync = part_sync;
                if (!i && master->suspend && master->resume) {
@@ -381,16 +378,14 @@ int add_mtd_partitions(struct mtd_info *master,
                }
                if (master->writev)
                        slave->mtd.writev = part_writev;
-               if (master->readv)
-                       slave->mtd.readv = part_readv;
-               if (master->writev_ecc)
-                       slave->mtd.writev_ecc = part_writev_ecc;
-               if (master->readv_ecc)
-                       slave->mtd.readv_ecc = part_readv_ecc;
                if (master->lock)
                        slave->mtd.lock = part_lock;
                if (master->unlock)
                        slave->mtd.unlock = part_unlock;
+               if (master->block_isbad)
+                       slave->mtd.block_isbad = part_block_isbad;
+               if (master->block_markbad)
+                       slave->mtd.block_markbad = part_block_markbad;
                slave->mtd.erase = part_erase;
                slave->master = master;
                slave->offset = parts[i].offset;
@@ -399,9 +394,10 @@ int add_mtd_partitions(struct mtd_info *master,
                if (slave->offset == MTDPART_OFS_APPEND)
                        slave->offset = cur_offset;
                if (slave->offset == MTDPART_OFS_NXTBLK) {
-                       u_int32_t emask = master->erasesize-1;
-                       slave->offset = (cur_offset + emask) & ~emask;
-                       if (slave->offset != cur_offset) {
+                       slave->offset = cur_offset;
+                       if ((cur_offset % master->erasesize) != 0) {
+                               /* Round up to next erasesize */
+                               slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize;
                                printk(KERN_NOTICE "Moving partition %d: "
                                       "0x%08x -> 0x%08x\n", i,
                                       cur_offset, slave->offset);
@@ -410,8 +406,8 @@ int add_mtd_partitions(struct mtd_info *master,
                if (slave->mtd.size == MTDPART_SIZ_FULL)
                        slave->mtd.size = master->size - slave->offset;
                cur_offset = slave->offset + slave->mtd.size;
-       
-               printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, 
+
+               printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset,
                        slave->offset + slave->mtd.size, slave->mtd.name);
 
                /* let's do some sanity checks */
@@ -431,7 +427,7 @@ int add_mtd_partitions(struct mtd_info *master,
                        /* Deal with variable erase size stuff */
                        int i;
                        struct mtd_erase_region_info *regions = master->eraseregions;
-                       
+
                        /* Find the first erase regions which is part of this partition. */
                        for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++)
                                ;
@@ -446,7 +442,7 @@ int add_mtd_partitions(struct mtd_info *master,
                        slave->mtd.erasesize = master->erasesize;
                }
 
-               if ((slave->mtd.flags & MTD_WRITEABLE) && 
+               if ((slave->mtd.flags & MTD_WRITEABLE) &&
                    (slave->offset % slave->mtd.erasesize)) {
                        /* Doesn't start on a boundary of major erase size */
                        /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */
@@ -454,13 +450,25 @@ int add_mtd_partitions(struct mtd_info *master,
                        printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
                                parts[i].name);
                }
-               if ((slave->mtd.flags & MTD_WRITEABLE) && 
+               if ((slave->mtd.flags & MTD_WRITEABLE) &&
                    (slave->mtd.size % slave->mtd.erasesize)) {
                        slave->mtd.flags &= ~MTD_WRITEABLE;
                        printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
                                parts[i].name);
                }
 
+               slave->mtd.ecclayout = master->ecclayout;
+               if (master->block_isbad) {
+                       uint32_t offs = 0;
+
+                       while(offs < slave->mtd.size) {
+                               if (master->block_isbad(master,
+                                                       offs + slave->offset))
+                                       slave->mtd.ecc_stats.badblocks++;
+                               offs += slave->mtd.erasesize;
+                       }
+               }
+
                if(parts[i].mtdp)
                {       /* store the object pointer (caller may or may not register it */
                        *parts[i].mtdp = &slave->mtd;
@@ -480,10 +488,10 @@ int add_mtd_partitions(struct mtd_info *master,
 EXPORT_SYMBOL(add_mtd_partitions);
 EXPORT_SYMBOL(del_mtd_partitions);
 
-static spinlock_t part_parser_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(part_parser_lock);
 static LIST_HEAD(part_parsers);
 
-struct mtd_part_parser *get_partition_parser(const char *name)
+static struct mtd_part_parser *get_partition_parser(const char *name)
 {
        struct list_head *this;
        void *ret = NULL;
@@ -519,12 +527,12 @@ int deregister_mtd_parser(struct mtd_part_parser *p)
        return 0;
 }
 
-int parse_mtd_partitions(struct mtd_info *master, const char **types, 
+int parse_mtd_partitions(struct mtd_info *master, const char **types,
                         struct mtd_partition **pparts, unsigned long origin)
 {
        struct mtd_part_parser *parser;
        int ret = 0;
-               
+
        for ( ; ret <= 0 && *types; types++) {
                parser = get_partition_parser(*types);
 #ifdef CONFIG_KMOD
@@ -538,7 +546,7 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types,
                }
                ret = (*parser->parse_fn)(master, pparts, origin);
                if (ret > 0) {
-                       printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", 
+                       printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
                               ret, parser->name, master->name);
                }
                put_partition_parser(parser);