[RFC/PATCH] OneNAND: add subpage write support

Kyungmin Park kyungmin.park at samsung.com
Mon Dec 18 05:39:57 EST 2006


[MTD] OneNAND: add subpage write support
OneNAND supports up to 4 writes at one NAND page. Add support of this feature.

Any comments are welcome.

Thank you,
Kyungmin Park

--

diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 63ca61b..56ff20f 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -919,6 +919,10 @@ static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr)
 	void __iomem *dataram0, *dataram1;
 	int ret = 0;
 
+	/* In partial page write, just skip it */
+	if ((addr & (mtd->writesize - 1)) != 0)
+		return 0;
+
 	this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize);
 
 	ret = this->wait(mtd, FL_READING);
@@ -941,7 +945,7 @@ static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr)
 #define onenand_verify_oob(...)		(0)
 #endif
 
-#define NOTALIGNED(x)	((x & (mtd->writesize - 1)) != 0)
+#define NOTALIGNED(x)	((x & (this->subpagesize - 1)) != 0)
 
 /**
  * onenand_write - [MTD Interface] write buffer to FLASH
@@ -959,6 +963,7 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
 	struct onenand_chip *this = mtd->priv;
 	int written = 0;
 	int ret = 0;
+	int column, subpage;
 
 	DEBUG(MTD_DEBUG_LEVEL3, "onenand_write: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
 
@@ -977,21 +982,37 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
                 return -EINVAL;
         }
 
+	column = to & (mtd->writesize - 1);
+	subpage = column || (len & (mtd->writesize - 1));
+
 	/* Grab the lock and see if the device is available */
 	onenand_get_device(mtd, FL_WRITING);
 
 	/* Loop until all data write */
 	while (written < len) {
-		int thislen = min_t(int, mtd->writesize, len - written);
-
-		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->writesize);
+		int bytes = mtd->writesize;
+		int thislen = min_t(int, bytes, len - written);
+		u_char *wbuf = (u_char *) buf;
+
+		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, bytes);
+
+		/* Partial page write */
+		if (column || len < (mtd->writesize - 1)) {
+			bytes = min_t(int, bytes - column, (int) len);
+			memset(this->page_buf, 0xff, mtd->writesize);
+			memcpy(this->page_buf + column, buf, bytes);
+			wbuf = this->page_buf;
+			/* Even though partial write, we need pagesize */
+			thislen = mtd->writesize;
+		}
 
-		this->write_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen);
+		this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, thislen);
 		this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);
 
 		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
 
-		onenand_update_bufferram(mtd, to, 1);
+		/* In partial page write we don't update bufferram */
+		onenand_update_bufferram(mtd, to, !subpage);
 
 		ret = this->wait(mtd, FL_WRITING);
 		if (ret) {
@@ -1002,7 +1023,7 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
 		written += thislen;
 
 		/* Only check verify write turn on */
-		ret = onenand_verify_page(mtd, (u_char *) buf, to);
+		ret = onenand_verify_page(mtd, (u_char *) wbuf, to);
 		if (ret) {
 			DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret);
 			goto out;
@@ -1011,6 +1032,7 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
 		if (written == len)
 			break;
 
+		column = 0;
 		to += thislen;
 		buf += thislen;
 	}
@@ -2029,23 +2051,30 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
 	init_waitqueue_head(&this->wq);
 	spin_lock_init(&this->chip_lock);
 
+	/*
+	 * Allow subpage writes up to oobsize.
+	 */
 	switch (mtd->oobsize) {
 	case 64:
 		this->ecclayout = &onenand_oob_64;
+		mtd->subpage_sft = 2;
 		break;
 
 	case 32:
 		this->ecclayout = &onenand_oob_32;
+		mtd->subpage_sft = 1;
 		break;
 
 	default:
 		printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n",
 			mtd->oobsize);
+		mtd->subpage_sft = 0;
 		/* To prevent kernel oops */
 		this->ecclayout = &onenand_oob_32;
 		break;
 	}
 
+	this->subpagesize = mtd->writesize >> mtd->subpage_sft;
 	mtd->ecclayout = this->ecclayout;
 
 	/* Fill in remaining MTD driver data */
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index 62ca0f4..fe3500d 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -88,6 +88,7 @@ struct onenand_bufferram {
  *			operation is in progress
  * @state:		[INTERN] the current state of the OneNAND device
  * @page_buf:		data buffer
+ * @subpagesize:	[INTERN] holds the subpagesize
  * @ecclayout:		[REPLACEABLE] the default ecc placement scheme
  * @bbm:		[REPLACEABLE] pointer to Bad Block Management
  * @priv:		[OPTIONAL] pointer to private chip date
@@ -128,6 +129,7 @@ struct onenand_chip {
 	onenand_state_t		state;
 	unsigned char		*page_buf;
 
+	int			subpagesize;
 	struct nand_ecclayout	*ecclayout;
 
 	void			*bbm;


More information about the linux-mtd mailing list