|
@@ -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;
|
|
|
}
|