[MTD] NAND: add subpage write support

Linux-MTD Mailing List linux-mtd at lists.infradead.org
Wed Nov 29 11:59:02 EST 2006


Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=29072b96078ffde36f03d51e6b5d0cff1ba8c7df
Commit:     29072b96078ffde36f03d51e6b5d0cff1ba8c7df
Parent:     f6a7ecb18dabd88bd9f28e7bece564cabe8ffe82
Author:     Thomas Gleixner <tglx at linutronix.de>
AuthorDate: Thu Sep 28 15:38:36 2006 +0200
Committer:  Artem Bityutskiy <dedekind at infradead.org>
CommitDate: Wed Nov 29 17:03:52 2006 +0200

    [MTD] NAND: add subpage write support
    
    Many SLC NANDs support up to 4 writes at one NAND page. Add support
    of this feature.
    
    Signed-off-by: Artem Bityutskiy <dedekind at infradead.org>
---
 drivers/mtd/mtdconcat.c      |    1 +
 drivers/mtd/mtdpart.c        |    1 +
 drivers/mtd/nand/nand_base.c |   53 +++++++++++++++++++++++++++++++++++-------
 include/linux/mtd/mtd.h      |    2 ++
 include/linux/mtd/nand.h     |    9 +++++++
 5 files changed, 57 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index ec51483..0690268 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -772,6 +772,7 @@ struct mtd_info *mtd_concat_create(struc
 		concat->mtd.ecc_stats.badblocks +=
 			subdev[i]->ecc_stats.badblocks;
 		if (concat->mtd.writesize   !=  subdev[i]->writesize ||
+		    concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
 		    concat->mtd.oobsize    !=  subdev[i]->oobsize ||
 		    concat->mtd.ecctype    !=  subdev[i]->ecctype ||
 		    concat->mtd.eccsize    !=  subdev[i]->eccsize ||
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 89692f8..bafd2fb 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -340,6 +340,7 @@ int add_mtd_partitions(struct mtd_info *
 		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;
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 5dcb2e0..eed3271 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1590,7 +1590,7 @@ static uint8_t *nand_fill_oob(struct nan
 	return NULL;
 }
 
-#define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0
+#define NOTALIGNED(x)	(x & (chip->subpagesize - 1)) != 0
 
 /**
  * nand_do_write_ops - [Internal] NAND write with ECC
@@ -1603,15 +1603,16 @@ #define NOTALIGNED(x) (x & (mtd->writesi
 static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 			     struct mtd_oob_ops *ops)
 {
-	int chipnr, realpage, page, blockmask;
+	int chipnr, realpage, page, blockmask, column;
 	struct nand_chip *chip = mtd->priv;
 	uint32_t writelen = ops->len;
 	uint8_t *oob = ops->oobbuf;
 	uint8_t *buf = ops->datbuf;
-	int bytes = mtd->writesize;
-	int ret;
+	int ret, subpage;
 
 	ops->retlen = 0;
+	if (!writelen)
+		return 0;
 
 	/* reject writes, which are not page aligned */
 	if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
@@ -1620,8 +1621,11 @@ static int nand_do_write_ops(struct mtd_
 		return -EINVAL;
 	}
 
-	if (!writelen)
-		return 0;
+	column = to & (mtd->writesize - 1);
+	subpage = column || (writelen & (mtd->writesize - 1));
+
+	if (subpage && oob)
+		return -EINVAL;
 
 	chipnr = (int)(to >> chip->chip_shift);
 	chip->select_chip(mtd, chipnr);
@@ -1644,12 +1648,24 @@ static int nand_do_write_ops(struct mtd_
 		memset(chip->oob_poi, 0xff, mtd->oobsize);
 
 	while(1) {
+		int bytes = mtd->writesize;
 		int cached = writelen > bytes && page != blockmask;
+		uint8_t *wbuf = buf;
+
+		/* Partial page write ? */
+		if (unlikely(column || writelen < (mtd->writesize - 1))) {
+			cached = 0;
+			bytes = min_t(int, bytes - column, (int) writelen);
+			chip->pagebuf = -1;
+			memset(chip->buffers->databuf, 0xff, mtd->writesize);
+			memcpy(&chip->buffers->databuf[column], buf, bytes);
+			wbuf = chip->buffers->databuf;
+		}
 
 		if (unlikely(oob))
 			oob = nand_fill_oob(chip, oob, ops);
 
-		ret = chip->write_page(mtd, chip, buf, page, cached,
+		ret = chip->write_page(mtd, chip, wbuf, page, cached,
 				       (ops->mode == MTD_OOB_RAW));
 		if (ret)
 			break;
@@ -1658,6 +1674,7 @@ static int nand_do_write_ops(struct mtd_
 		if (!writelen)
 			break;
 
+		column = 0;
 		buf += bytes;
 		realpage++;
 
@@ -2201,8 +2218,8 @@ static struct nand_flash_dev *nand_get_f
 	/* Newer devices have all the information in additional id bytes */
 	if (!type->pagesize) {
 		int extid;
-		/* The 3rd id byte contains non relevant data ATM */
-		extid = chip->read_byte(mtd);
+		/* The 3rd id byte holds MLC / multichip data */
+		chip->cellinfo = chip->read_byte(mtd);
 		/* The 4th id byte is the important one */
 		extid = chip->read_byte(mtd);
 		/* Calc pagesize */
@@ -2482,6 +2499,24 @@ int nand_scan_tail(struct mtd_info *mtd)
 	}
 	chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
 
+	/*
+	 * Allow subpage writes up to ecc.steps. Not possible for MLC
+	 * FLASH.
+	 */
+	if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
+	    !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
+		switch(chip->ecc.steps) {
+		case 2:
+			mtd->subpage_sft = 1;
+			break;
+		case 4:
+		case 8:
+			mtd->subpage_sft = 2;
+			break;
+		}
+	}
+	chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
 	/* Initialize state */
 	chip->state = FL_READY;
 
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index e34bbc9..18acb6d 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -200,6 +200,8 @@ #define MTD_PROGREGION_CTRLMODE_INVALID(
 
 	/* ECC status information */
 	struct mtd_ecc_stats ecc_stats;
+	/* Subpage shift (NAND) */
+	int subpage_sft;
 
 	void *priv;
 
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 6fc3e07..1aeedf2 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -166,6 +166,9 @@ #define BBT_AUTO_REFRESH	0x00000080
  * for all large page devices, as they do not support
  * autoincrement.*/
 #define NAND_NO_READRDY		0x00000100
+/* Chip does not allow subpage writes */
+#define NAND_NO_SUBPAGE_WRITE	0x00000200
+
 
 /* Options valid for Samsung large page devices */
 #define NAND_SAMSUNG_LP_OPTIONS \
@@ -193,6 +196,9 @@ #define NAND_OWN_BUFFERS	0x00040000
 /* Nand scan has allocated controller struct */
 #define NAND_CONTROLLER_ALLOC	0x80000000
 
+/* Cell info constants */
+#define NAND_CI_CHIPNR_MSK	0x03
+#define NAND_CI_CELLTYPE_MSK	0x0C
 
 /*
  * nand_state_t - chip states
@@ -341,6 +347,7 @@ struct nand_buffers {
  * @chipsize:		[INTERN] the size of one chip for multichip arrays
  * @pagemask:		[INTERN] page number mask = number of (pages / chip) - 1
  * @pagebuf:		[INTERN] holds the pagenumber which is currently in data_buf
+ * @subpagesize:	[INTERN] holds the subpagesize
  * @ecclayout:		[REPLACEABLE] the default ecc placement scheme
  * @bbt:		[INTERN] bad block table pointer
  * @bbt_td:		[REPLACEABLE] bad block table descriptor for flash lookup
@@ -388,6 +395,8 @@ struct nand_chip {
 	unsigned long	chipsize;
 	int		pagemask;
 	int		pagebuf;
+	int		subpagesize;
+	uint8_t		cellinfo;
 	int		badblockpos;
 
 	nand_state_t	state;



More information about the linux-mtd-cvs mailing list