]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/mtd/onenand/onenand_base.c
Merge branch 'omap-pool'
[linux-2.6-omap-h63xx.git] / drivers / mtd / onenand / onenand_base.c
index 529af271db17486074ef44a6610bd365ace65d28..30d6999e5f9f5a75a22078ee56459798ea0fc865 100644 (file)
@@ -1455,7 +1455,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
                                struct mtd_oob_ops *ops)
 {
        struct onenand_chip *this = mtd->priv;
-       int written = 0, column, thislen, subpage;
+       int written = 0, column, thislen = 0, subpage = 0;
+       int prev = 0, prevlen = 0, prev_subpage = 0, first = 1;
        int oobwritten = 0, oobcolumn, thisooblen, oobsize;
        size_t len = ops->len;
        size_t ooblen = ops->ooblen;
@@ -1482,6 +1483,10 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
                 return -EINVAL;
         }
 
+       /* Check zero length */
+       if (!len)
+               return 0;
+
        if (ops->mode == MTD_OOB_AUTO)
                oobsize = this->ecclayout->oobavail;
        else
@@ -1492,79 +1497,121 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
        column = to & (mtd->writesize - 1);
 
        /* Loop until all data write */
-       while (written < len) {
-               u_char *wbuf = (u_char *) buf;
+       while (1) {
+               if (written < len) {
+                       u_char *wbuf = (u_char *) buf;
 
-               thislen = min_t(int, mtd->writesize - column, len - written);
-               thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
+                       thislen = min_t(int, mtd->writesize - column, len - written);
+                       thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
 
-               cond_resched();
+                       cond_resched();
 
-               this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
+                       this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
 
-               /* Partial page write */
-               subpage = thislen < mtd->writesize;
-               if (subpage) {
-                       memset(this->page_buf, 0xff, mtd->writesize);
-                       memcpy(this->page_buf + column, buf, thislen);
-                       wbuf = this->page_buf;
-               }
+                       /* Partial page write */
+                       subpage = thislen < mtd->writesize;
+                       if (subpage) {
+                               memset(this->page_buf, 0xff, mtd->writesize);
+                               memcpy(this->page_buf + column, buf, thislen);
+                               wbuf = this->page_buf;
+                       }
 
-               this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
+                       this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
 
-               if (oob) {
-                       oobbuf = this->oob_buf;
+                       if (oob) {
+                               oobbuf = this->oob_buf;
 
-                       /* We send data to spare ram with oobsize
-                        * to prevent byte access */
-                       memset(oobbuf, 0xff, mtd->oobsize);
-                       if (ops->mode == MTD_OOB_AUTO)
-                               onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
-                       else
-                               memcpy(oobbuf + oobcolumn, oob, thisooblen);
+                               /* We send data to spare ram with oobsize
+                                * to prevent byte access */
+                               memset(oobbuf, 0xff, mtd->oobsize);
+                               if (ops->mode == MTD_OOB_AUTO)
+                                       onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
+                               else
+                                       memcpy(oobbuf + oobcolumn, oob, thisooblen);
 
-                       oobwritten += thisooblen;
-                       oob += thisooblen;
-                       oobcolumn = 0;
+                               oobwritten += thisooblen;
+                               oob += thisooblen;
+                               oobcolumn = 0;
+                       } else
+                               oobbuf = (u_char *) ffchars;
+
+                       this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
                } else
-                       oobbuf = (u_char *) ffchars;
+                       ONENAND_SET_NEXT_BUFFERRAM(this);
 
-               this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
+               /*
+                * 2 PLANE, MLC, and Flex-OneNAND doesn't support
+                * write-while-programe feature.
+                */
+               if (!ONENAND_IS_2PLANE(this) && !first) {
+                       ONENAND_SET_PREV_BUFFERRAM(this);
 
-               this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
+                       ret = this->wait(mtd, FL_WRITING);
 
-               ret = this->wait(mtd, FL_WRITING);
+                       /* In partial page write we don't update bufferram */
+                       onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
+                       if (ret) {
+                               written -= prevlen;
+                               printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+                               break;
+                       }
 
-               /* In partial page write we don't update bufferram */
-               onenand_update_bufferram(mtd, to, !ret && !subpage);
-               if (ONENAND_IS_2PLANE(this)) {
-                       ONENAND_SET_BUFFERRAM1(this);
-                       onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
-               }
+                       if (written == len) {
+                               /* Only check verify write turn on */
+                               ret = onenand_verify(mtd, buf - len, to - len, len);
+                               if (ret)
+                                       printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
+                               break;
+                       }
 
-               if (ret) {
-                       printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
-                       break;
+                       ONENAND_SET_NEXT_BUFFERRAM(this);
                }
 
-               /* Only check verify write turn on */
-               ret = onenand_verify(mtd, buf, to, thislen);
-               if (ret) {
-                       printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
-                       break;
-               }
+               this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
 
-               written += thislen;
+               /*
+                * 2 PLANE, MLC, and Flex-OneNAND wait here
+                */
+               if (ONENAND_IS_2PLANE(this)) {
+                       ret = this->wait(mtd, FL_WRITING);
 
-               if (written == len)
-                       break;
+                       /* In partial page write we don't update bufferram */
+                       onenand_update_bufferram(mtd, to, !ret && !subpage);
+                       if (ret) {
+                               printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+                               break;
+                       }
+
+                       /* Only check verify write turn on */
+                       ret = onenand_verify(mtd, buf, to, thislen);
+                       if (ret) {
+                               printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
+                               break;
+                       }
+
+                       written += thislen;
+
+                       if (written == len)
+                               break;
+
+               } else
+                       written += thislen;
 
                column = 0;
+               prev_subpage = subpage;
+               prev = to;
+               prevlen = thislen;
                to += thislen;
                buf += thislen;
+               first = 0;
        }
 
+       /* In error case, clear all bufferrams */
+       if (written != len)
+               onenand_invalidate_bufferram(mtd, 0, -1);
+
        ops->retlen = written;
+       ops->oobretlen = oobwritten;
 
        return ret;
 }