Commit 29072b96 authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Artem Bityutskiy

[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: default avatarArtem Bityutskiy <dedekind@infradead.org>
parent f6a7ecb1
...@@ -772,6 +772,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c ...@@ -772,6 +772,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->mtd.ecc_stats.badblocks += concat->mtd.ecc_stats.badblocks +=
subdev[i]->ecc_stats.badblocks; subdev[i]->ecc_stats.badblocks;
if (concat->mtd.writesize != subdev[i]->writesize || if (concat->mtd.writesize != subdev[i]->writesize ||
concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
concat->mtd.oobsize != subdev[i]->oobsize || concat->mtd.oobsize != subdev[i]->oobsize ||
concat->mtd.ecctype != subdev[i]->ecctype || concat->mtd.ecctype != subdev[i]->ecctype ||
concat->mtd.eccsize != subdev[i]->eccsize || concat->mtd.eccsize != subdev[i]->eccsize ||
......
...@@ -340,6 +340,7 @@ int add_mtd_partitions(struct mtd_info *master, ...@@ -340,6 +340,7 @@ int add_mtd_partitions(struct mtd_info *master,
slave->mtd.oobsize = master->oobsize; slave->mtd.oobsize = master->oobsize;
slave->mtd.ecctype = master->ecctype; slave->mtd.ecctype = master->ecctype;
slave->mtd.eccsize = master->eccsize; slave->mtd.eccsize = master->eccsize;
slave->mtd.subpage_sft = master->subpage_sft;
slave->mtd.name = parts[i].name; slave->mtd.name = parts[i].name;
slave->mtd.bank_size = master->bank_size; slave->mtd.bank_size = master->bank_size;
......
...@@ -1590,7 +1590,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, ...@@ -1590,7 +1590,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
return NULL; 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 * 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, ...@@ -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, static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops) struct mtd_oob_ops *ops)
{ {
int chipnr, realpage, page, blockmask; int chipnr, realpage, page, blockmask, column;
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
uint32_t writelen = ops->len; uint32_t writelen = ops->len;
uint8_t *oob = ops->oobbuf; uint8_t *oob = ops->oobbuf;
uint8_t *buf = ops->datbuf; uint8_t *buf = ops->datbuf;
int bytes = mtd->writesize; int ret, subpage;
int ret;
ops->retlen = 0; ops->retlen = 0;
if (!writelen)
return 0;
/* reject writes, which are not page aligned */ /* reject writes, which are not page aligned */
if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
...@@ -1620,8 +1621,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, ...@@ -1620,8 +1621,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
return -EINVAL; return -EINVAL;
} }
if (!writelen) column = to & (mtd->writesize - 1);
return 0; subpage = column || (writelen & (mtd->writesize - 1));
if (subpage && oob)
return -EINVAL;
chipnr = (int)(to >> chip->chip_shift); chipnr = (int)(to >> chip->chip_shift);
chip->select_chip(mtd, chipnr); chip->select_chip(mtd, chipnr);
...@@ -1644,12 +1648,24 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, ...@@ -1644,12 +1648,24 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
memset(chip->oob_poi, 0xff, mtd->oobsize); memset(chip->oob_poi, 0xff, mtd->oobsize);
while(1) { while(1) {
int bytes = mtd->writesize;
int cached = writelen > bytes && page != blockmask; 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)) if (unlikely(oob))
oob = nand_fill_oob(chip, oob, ops); 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)); (ops->mode == MTD_OOB_RAW));
if (ret) if (ret)
break; break;
...@@ -1658,6 +1674,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, ...@@ -1658,6 +1674,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
if (!writelen) if (!writelen)
break; break;
column = 0;
buf += bytes; buf += bytes;
realpage++; realpage++;
...@@ -2201,8 +2218,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, ...@@ -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 */ /* Newer devices have all the information in additional id bytes */
if (!type->pagesize) { if (!type->pagesize) {
int extid; int extid;
/* The 3rd id byte contains non relevant data ATM */ /* The 3rd id byte holds MLC / multichip data */
extid = chip->read_byte(mtd); chip->cellinfo = chip->read_byte(mtd);
/* The 4th id byte is the important one */ /* The 4th id byte is the important one */
extid = chip->read_byte(mtd); extid = chip->read_byte(mtd);
/* Calc pagesize */ /* Calc pagesize */
...@@ -2482,6 +2499,24 @@ int nand_scan_tail(struct mtd_info *mtd) ...@@ -2482,6 +2499,24 @@ int nand_scan_tail(struct mtd_info *mtd)
} }
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; 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 */ /* Initialize state */
chip->state = FL_READY; chip->state = FL_READY;
......
...@@ -200,6 +200,8 @@ struct mtd_info { ...@@ -200,6 +200,8 @@ struct mtd_info {
/* ECC status information */ /* ECC status information */
struct mtd_ecc_stats ecc_stats; struct mtd_ecc_stats ecc_stats;
/* Subpage shift (NAND) */
int subpage_sft;
void *priv; void *priv;
......
...@@ -166,6 +166,9 @@ typedef enum { ...@@ -166,6 +166,9 @@ typedef enum {
* for all large page devices, as they do not support * for all large page devices, as they do not support
* autoincrement.*/ * autoincrement.*/
#define NAND_NO_READRDY 0x00000100 #define NAND_NO_READRDY 0x00000100
/* Chip does not allow subpage writes */
#define NAND_NO_SUBPAGE_WRITE 0x00000200
/* Options valid for Samsung large page devices */ /* Options valid for Samsung large page devices */
#define NAND_SAMSUNG_LP_OPTIONS \ #define NAND_SAMSUNG_LP_OPTIONS \
...@@ -193,6 +196,9 @@ typedef enum { ...@@ -193,6 +196,9 @@ typedef enum {
/* Nand scan has allocated controller struct */ /* Nand scan has allocated controller struct */
#define NAND_CONTROLLER_ALLOC 0x80000000 #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 * nand_state_t - chip states
...@@ -341,6 +347,7 @@ struct nand_buffers { ...@@ -341,6 +347,7 @@ struct nand_buffers {
* @chipsize: [INTERN] the size of one chip for multichip arrays * @chipsize: [INTERN] the size of one chip for multichip arrays
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
* @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf * @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf
* @subpagesize: [INTERN] holds the subpagesize
* @ecclayout: [REPLACEABLE] the default ecc placement scheme * @ecclayout: [REPLACEABLE] the default ecc placement scheme
* @bbt: [INTERN] bad block table pointer * @bbt: [INTERN] bad block table pointer
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup
...@@ -388,6 +395,8 @@ struct nand_chip { ...@@ -388,6 +395,8 @@ struct nand_chip {
unsigned long chipsize; unsigned long chipsize;
int pagemask; int pagemask;
int pagebuf; int pagebuf;
int subpagesize;
uint8_t cellinfo;
int badblockpos; int badblockpos;
nand_state_t state; nand_state_t state;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment