[MTD] NAND: add subpage write support
authorThomas Gleixner <tglx@linutronix.de>
Thu, 28 Sep 2006 13:38:36 +0000 (15:38 +0200)
committerArtem Bityutskiy <dedekind@infradead.org>
Wed, 29 Nov 2006 15:03:52 +0000 (17:03 +0200)
Many SLC NANDs support up to 4 writes at one NAND page. Add support
of this feature.

Signed-off-by: Artem Bityutskiy <dedekind@infradead.org>
drivers/mtd/mtdconcat.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/nand_base.c
include/linux/mtd/mtd.h
include/linux/mtd/nand.h

index ec51483b1c51b27a7fc1e8a3edf1c15af7e4fbc2..06902683bc2a7c0a71541558b76aaad032e0c578 100644 (file)
@@ -772,6 +772,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],       /* subdevices to c
                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 ||
index 89692f898ef4ad78a77dced4b9d0a04cc44288a7..bafd2fba87bde0f2b6f82bed155e751cbe408c12 100644 (file)
@@ -340,6 +340,7 @@ int add_mtd_partitions(struct mtd_info *master,
                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;
index 5dcb2e066ce71f3a5b8bb2f2ad37f9e7422f2620..eed3271b99cc2e7ccfc81b091011194089b8fb00 100644 (file)
@@ -1590,7 +1590,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
        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 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
 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_info *mtd, loff_t to,
                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_info *mtd, loff_t to,
                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_info *mtd, loff_t to,
                if (!writelen)
                        break;
 
+               column = 0;
                buf += bytes;
                realpage++;
 
@@ -2201,8 +2218,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        /* 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;
 
index e34bbc98d4feaaa41ff04adf5471300c6c67aa9c..18acb6d0033b07ff2943bfb3e61dd918f4b1bc11 100644 (file)
@@ -200,6 +200,8 @@ struct mtd_info {
 
        /* ECC status information */
        struct mtd_ecc_stats ecc_stats;
+       /* Subpage shift (NAND) */
+       int subpage_sft;
 
        void *priv;
 
index 6fc3e07497e0175aa49f6ffe856c9131bbc30f7b..1aeedf27a1ff29571c74f7da50fdaa8dc5c3941a 100644 (file)
@@ -166,6 +166,9 @@ typedef enum {
  * 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 @@ typedef enum {
 /* 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;