From 29072b96078ffde36f03d51e6b5d0cff1ba8c7df Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 28 Sep 2006 15:38:36 +0200 Subject: [PATCH] [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 --- 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 ec51483b1c51..06902683bc2a 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -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 || diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 89692f898ef4..bafd2fba87bd 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -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; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 5dcb2e066ce7..eed3271b99cc 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 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; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index e34bbc98d4fe..18acb6d0033b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -200,6 +200,8 @@ struct mtd_info { /* 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 6fc3e07497e0..1aeedf27a1ff 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -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;