linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / mtd / devices / doc2000.c
index 603a795..e4345cf 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/bitops.h>
-#include <linux/mutex.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
@@ -55,10 +54,17 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
                    size_t *retlen, u_char *buf);
 static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
                     size_t *retlen, const u_char *buf);
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
-                       struct mtd_oob_ops *ops);
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
-                        struct mtd_oob_ops *ops);
+static int doc_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 doc_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 doc_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);
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+                       size_t *retlen, u_char *buf);
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+                        size_t *retlen, const u_char *buf);
 static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len,
                         size_t *retlen, const u_char *buf);
 static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
@@ -510,9 +516,16 @@ static int DoC2k_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
        return retval;
 }
 
-/* This routine is found from the docprobe code by symbol_get(),
- * which will bump the use count of this module. */
-void DoC2k_init(struct mtd_info *mtd)
+static const char im_name[] = "DoC2k_init";
+
+/* This routine is made available to other mtd code via
+ * inter_module_register.  It must only be accessed through
+ * inter_module_get which will bump the use count of this module.  The
+ * addresses passed back in mtd are valid as long as the use count of
+ * this module is non-zero, i.e. between inter_module_get and
+ * inter_module_put.  Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
+ */
+static void DoC2k_init(struct mtd_info *mtd)
 {
        struct DiskOnChip *this = mtd->priv;
        struct DiskOnChip *old = NULL;
@@ -572,7 +585,7 @@ void DoC2k_init(struct mtd_info *mtd)
        mtd->ecctype = MTD_ECC_RS_DiskOnChip;
        mtd->size = 0;
        mtd->erasesize = 0;
-       mtd->writesize = 512;
+       mtd->oobblock = 512;
        mtd->oobsize = 16;
        mtd->owner = THIS_MODULE;
        mtd->erase = doc_erase;
@@ -580,6 +593,9 @@ void DoC2k_init(struct mtd_info *mtd)
        mtd->unpoint = NULL;
        mtd->read = doc_read;
        mtd->write = doc_write;
+       mtd->read_ecc = doc_read_ecc;
+       mtd->write_ecc = doc_write_ecc;
+       mtd->writev_ecc = doc_writev_ecc;
        mtd->read_oob = doc_read_oob;
        mtd->write_oob = doc_write_oob;
        mtd->sync = NULL;
@@ -589,7 +605,7 @@ void DoC2k_init(struct mtd_info *mtd)
 
        this->curfloor = -1;
        this->curchip = -1;
-       mutex_init(&this->lock);
+       init_MUTEX(&this->lock);
 
        /* Ident all the chips present. */
        DoC_ScanChips(this, maxchips);
@@ -606,15 +622,21 @@ void DoC2k_init(struct mtd_info *mtd)
                return;
        }
 }
-EXPORT_SYMBOL_GPL(DoC2k_init);
 
 static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
                    size_t * retlen, u_char * buf)
+{
+       /* Just a special case of doc_read_ecc */
+       return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL);
+}
+
+static int doc_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)
 {
        struct DiskOnChip *this = mtd->priv;
        void __iomem *docptr = this->virtadr;
        struct Nand *mychip;
-       unsigned char syndrome[6], eccbuf[6];
+       unsigned char syndrome[6];
        volatile char dummy;
        int i, len256 = 0, ret=0;
        size_t left = len;
@@ -623,7 +645,7 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
        if (from >= this->totlen)
                return -EINVAL;
 
-       mutex_lock(&this->lock);
+       down(&this->lock);
 
        *retlen = 0;
        while (left) {
@@ -662,9 +684,15 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
                DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP,
                            CDSN_CTRL_ECC_IO);
 
-               /* Prime the ECC engine */
-               WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
-               WriteDOC(DOC_ECC_EN, docptr, ECCConf);
+               if (eccbuf) {
+                       /* Prime the ECC engine */
+                       WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+                       WriteDOC(DOC_ECC_EN, docptr, ECCConf);
+               } else {
+                       /* disable the ECC engine */
+                       WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+                       WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+               }
 
                /* treat crossing 256-byte sector for 2M x 8bits devices */
                if (this->page256 && from + len > (from | 0xff) + 1) {
@@ -681,59 +709,58 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
                /* Let the caller know we completed it */
                *retlen += len;
 
-               /* Read the ECC data through the DiskOnChip ECC logic */
-               /* Note: this will work even with 2M x 8bit devices as   */
-               /*       they have 8 bytes of OOB per 256 page. mf.      */
-               DoC_ReadBuf(this, eccbuf, 6);
-
-               /* Flush the pipeline */
-               if (DoC_is_Millennium(this)) {
-                       dummy = ReadDOC(docptr, ECCConf);
-                       dummy = ReadDOC(docptr, ECCConf);
-                       i = ReadDOC(docptr, ECCConf);
-               } else {
-                       dummy = ReadDOC(docptr, 2k_ECCStatus);
-                       dummy = ReadDOC(docptr, 2k_ECCStatus);
-                       i = ReadDOC(docptr, 2k_ECCStatus);
-               }
+               if (eccbuf) {
+                       /* Read the ECC data through the DiskOnChip ECC logic */
+                       /* Note: this will work even with 2M x 8bit devices as   */
+                       /*       they have 8 bytes of OOB per 256 page. mf.      */
+                       DoC_ReadBuf(this, eccbuf, 6);
+
+                       /* Flush the pipeline */
+                       if (DoC_is_Millennium(this)) {
+                               dummy = ReadDOC(docptr, ECCConf);
+                               dummy = ReadDOC(docptr, ECCConf);
+                               i = ReadDOC(docptr, ECCConf);
+                       } else {
+                               dummy = ReadDOC(docptr, 2k_ECCStatus);
+                               dummy = ReadDOC(docptr, 2k_ECCStatus);
+                               i = ReadDOC(docptr, 2k_ECCStatus);
+                       }
 
-               /* Check the ECC Status */
-               if (i & 0x80) {
-                       int nb_errors;
-                       /* There was an ECC error */
+                       /* Check the ECC Status */
+                       if (i & 0x80) {
+                               int nb_errors;
+                               /* There was an ECC error */
 #ifdef ECC_DEBUG
-                       printk(KERN_ERR "DiskOnChip ECC Error: Read at %lx\n", (long)from);
+                               printk(KERN_ERR "DiskOnChip ECC Error: Read at %lx\n", (long)from);
 #endif
-                       /* Read the ECC syndrom through the DiskOnChip ECC
-                          logic.  These syndrome will be all ZERO when there
-                          is no error */
-                       for (i = 0; i < 6; i++) {
-                               syndrome[i] =
-                                       ReadDOC(docptr, ECCSyndrome0 + i);
-                       }
-                       nb_errors = doc_decode_ecc(buf, syndrome);
+                               /* Read the ECC syndrom through the DiskOnChip ECC logic.
+                                  These syndrome will be all ZERO when there is no error */
+                               for (i = 0; i < 6; i++) {
+                                       syndrome[i] =
+                                           ReadDOC(docptr, ECCSyndrome0 + i);
+                               }
+                               nb_errors = doc_decode_ecc(buf, syndrome);
 
 #ifdef ECC_DEBUG
-                       printk(KERN_ERR "Errors corrected: %x\n", nb_errors);
+                               printk(KERN_ERR "Errors corrected: %x\n", nb_errors);
 #endif
-                       if (nb_errors < 0) {
-                               /* We return error, but have actually done the
-                                  read. Not that this can be told to
-                                  user-space, via sys_read(), but at least
-                                  MTD-aware stuff can know about it by
-                                  checking *retlen */
-                               ret = -EIO;
+                               if (nb_errors < 0) {
+                                       /* We return error, but have actually done the read. Not that
+                                          this can be told to user-space, via sys_read(), but at least
+                                          MTD-aware stuff can know about it by checking *retlen */
+                                       ret = -EIO;
+                               }
                        }
-               }
 
 #ifdef PSYCHO_DEBUG
-               printk(KERN_DEBUG "ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
-                      (long)from, eccbuf[0], eccbuf[1], eccbuf[2],
-                      eccbuf[3], eccbuf[4], eccbuf[5]);
+                       printk(KERN_DEBUG "ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+                                    (long)from, eccbuf[0], eccbuf[1], eccbuf[2],
+                                    eccbuf[3], eccbuf[4], eccbuf[5]);
 #endif
 
-               /* disable the ECC engine */
-               WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
+                       /* disable the ECC engine */
+                       WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
+               }
 
                /* according to 11.4.1, we need to wait for the busy line
                 * drop if we read to the end of the page.  */
@@ -747,18 +774,25 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
                buf += len;
        }
 
-       mutex_unlock(&this->lock);
+       up(&this->lock);
 
        return ret;
 }
 
 static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
                     size_t * retlen, const u_char * buf)
+{
+       char eccbuf[6];
+       return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL);
+}
+
+static int doc_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)
 {
        struct DiskOnChip *this = mtd->priv;
        int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */
        void __iomem *docptr = this->virtadr;
-       unsigned char eccbuf[6];
        volatile char dummy;
        int len256 = 0;
        struct Nand *mychip;
@@ -769,7 +803,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
        if (to >= this->totlen)
                return -EINVAL;
 
-       mutex_lock(&this->lock);
+       down(&this->lock);
 
        *retlen = 0;
        while (left) {
@@ -812,9 +846,15 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
                DoC_Command(this, NAND_CMD_SEQIN, 0);
                DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO);
 
-               /* Prime the ECC engine */
-               WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
-               WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+               if (eccbuf) {
+                       /* Prime the ECC engine */
+                       WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+                       WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+               } else {
+                       /* disable the ECC engine */
+                       WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+                       WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+               }
 
                /* treat crossing 256-byte sector for 2M x 8bits devices */
                if (this->page256 && to + len > (to | 0xff) + 1) {
@@ -833,7 +873,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
                                printk(KERN_ERR "Error programming flash\n");
                                /* Error in programming */
                                *retlen = 0;
-                               mutex_unlock(&this->lock);
+                               up(&this->lock);
                                return -EIO;
                        }
 
@@ -844,35 +884,39 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
 
                DoC_WriteBuf(this, &buf[len256], len - len256);
 
-               WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr, CDSNControl);
-
-               if (DoC_is_Millennium(this)) {
-                       WriteDOC(0, docptr, NOP);
-                       WriteDOC(0, docptr, NOP);
-                       WriteDOC(0, docptr, NOP);
-               } else {
-                       WriteDOC_(0, docptr, this->ioreg);
-                       WriteDOC_(0, docptr, this->ioreg);
-                       WriteDOC_(0, docptr, this->ioreg);
-               }
+               if (eccbuf) {
+                       WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr,
+                                CDSNControl);
+
+                       if (DoC_is_Millennium(this)) {
+                               WriteDOC(0, docptr, NOP);
+                               WriteDOC(0, docptr, NOP);
+                               WriteDOC(0, docptr, NOP);
+                       } else {
+                               WriteDOC_(0, docptr, this->ioreg);
+                               WriteDOC_(0, docptr, this->ioreg);
+                               WriteDOC_(0, docptr, this->ioreg);
+                       }
 
-               WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_FLASH_IO | CDSN_CTRL_CE, docptr,
-                        CDSNControl);
+                       WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_FLASH_IO | CDSN_CTRL_CE, docptr,
+                                CDSNControl);
 
-               /* Read the ECC data through the DiskOnChip ECC logic */
-               for (di = 0; di < 6; di++) {
-                       eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di);
-               }
+                       /* Read the ECC data through the DiskOnChip ECC logic */
+                       for (di = 0; di < 6; di++) {
+                               eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di);
+                       }
 
-               /* Reset the ECC engine */
-               WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+                       /* Reset the ECC engine */
+                       WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
 
 #ifdef PSYCHO_DEBUG
-               printk
-                       ("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
-                        (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
-                        eccbuf[4], eccbuf[5]);
+                       printk
+                           ("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+                            (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
+                            eccbuf[4], eccbuf[5]);
 #endif
+               }
+
                DoC_Command(this, NAND_CMD_PAGEPROG, 0);
 
                DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
@@ -891,7 +935,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
                        printk(KERN_ERR "Error programming flash\n");
                        /* Error in programming */
                        *retlen = 0;
-                       mutex_unlock(&this->lock);
+                       up(&this->lock);
                        return -EIO;
                }
 
@@ -912,7 +956,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
 
                        ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x);
                        if (ret) {
-                               mutex_unlock(&this->lock);
+                               up(&this->lock);
                                return ret;
                        }
                }
@@ -922,24 +966,78 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
                buf += len;
        }
 
-       mutex_unlock(&this->lock);
+       up(&this->lock);
        return 0;
 }
 
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
-                       struct mtd_oob_ops *ops)
+static int doc_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)
+{
+       static char static_buf[512];
+       static DECLARE_MUTEX(writev_buf_sem);
+
+       size_t totretlen = 0;
+       size_t thisvecofs = 0;
+       int ret= 0;
+
+       down(&writev_buf_sem);
+
+       while(count) {
+               size_t thislen, thisretlen;
+               unsigned char *buf;
+
+               buf = vecs->iov_base + thisvecofs;
+               thislen = vecs->iov_len - thisvecofs;
+
+
+               if (thislen >= 512) {
+                       thislen = thislen & ~(512-1);
+                       thisvecofs += thislen;
+               } else {
+                       /* Not enough to fill a page. Copy into buf */
+                       memcpy(static_buf, buf, thislen);
+                       buf = &static_buf[thislen];
+
+                       while(count && thislen < 512) {
+                               vecs++;
+                               count--;
+                               thisvecofs = min((512-thislen), vecs->iov_len);
+                               memcpy(buf, vecs->iov_base, thisvecofs);
+                               thislen += thisvecofs;
+                               buf += thisvecofs;
+                       }
+                       buf = static_buf;
+               }
+               if (count && thisvecofs == vecs->iov_len) {
+                       thisvecofs = 0;
+                       vecs++;
+                       count--;
+               }
+               ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel);
+
+               totretlen += thisretlen;
+
+               if (ret || thisretlen != thislen)
+                       break;
+
+               to += thislen;
+       }
+
+       up(&writev_buf_sem);
+       *retlen = totretlen;
+       return ret;
+}
+
+
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+                       size_t * retlen, u_char * buf)
 {
        struct DiskOnChip *this = mtd->priv;
        int len256 = 0, ret;
        struct Nand *mychip;
-       uint8_t *buf = ops->oobbuf;
-       size_t len = ops->len;
-
-       BUG_ON(ops->mode != MTD_OOB_PLACE);
-
-       ofs += ops->ooboffs;
 
-       mutex_lock(&this->lock);
+       down(&this->lock);
 
        mychip = &this->chips[ofs >> this->chipshift];
 
@@ -978,14 +1076,14 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
 
        DoC_ReadBuf(this, &buf[len256], len - len256);
 
-       ops->retlen = len;
+       *retlen = len;
        /* Reading the full OOB data drops us off of the end of the page,
          * causing the flash device to go into busy mode, so we need
          * to wait until ready 11.4.1 and Toshiba TC58256FT docs */
 
        ret = DoC_WaitReady(this);
 
-       mutex_unlock(&this->lock);
+       up(&this->lock);
        return ret;
 
 }
@@ -1093,20 +1191,17 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len,
 
 }
 
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
-                        struct mtd_oob_ops *ops)
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+                        size_t * retlen, const u_char * buf)
 {
-       struct DiskOnChip *this = mtd->priv;
-       int ret;
+       struct DiskOnChip *this = mtd->priv;
+       int ret;
 
-       BUG_ON(ops->mode != MTD_OOB_PLACE);
+       down(&this->lock);
+       ret = doc_write_oob_nolock(mtd, ofs, len, retlen, buf);
 
-       mutex_lock(&this->lock);
-       ret = doc_write_oob_nolock(mtd, ofs + ops->ooboffs, ops->len,
-                                  &ops->retlen, ops->oobbuf);
-
-       mutex_unlock(&this->lock);
-       return ret;
+       up(&this->lock);
+       return ret;
 }
 
 static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
@@ -1119,10 +1214,10 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
        struct Nand *mychip;
        int status;
 
-       mutex_lock(&this->lock);
+       down(&this->lock);
 
        if (ofs & (mtd->erasesize-1) || len & (mtd->erasesize-1)) {
-               mutex_unlock(&this->lock);
+               up(&this->lock);
                return -EINVAL;
        }
 
@@ -1170,7 +1265,7 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
  callback:
        mtd_erase_callback(instr);
 
-       mutex_unlock(&this->lock);
+       up(&this->lock);
        return 0;
 }
 
@@ -1181,6 +1276,12 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
  *
  ****************************************************************************/
 
+static int __init init_doc2000(void)
+{
+       inter_module_register(im_name, THIS_MODULE, &DoC2k_init);
+       return 0;
+}
+
 static void __exit cleanup_doc2000(void)
 {
        struct mtd_info *mtd;
@@ -1196,9 +1297,11 @@ static void __exit cleanup_doc2000(void)
                kfree(this->chips);
                kfree(mtd);
        }
+       inter_module_unregister(im_name);
 }
 
 module_exit(cleanup_doc2000);
+module_init(init_doc2000);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");