[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@infradead.org>
This commit is contained in:
parent
f6a7ecb18d
commit
29072b9607
@ -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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
/* 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)
|
|||||||
}
|
}
|
||||||
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 {
|
|||||||
|
|
||||||
/* 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 {
|
|||||||
* 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 {
|
|||||||
/* 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 {
|
|||||||
* @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 {
|
|||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user