Skip to content

Commit

Permalink
[MTD] NAND: add subpage write support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Thomas Gleixner authored and Artem Bityutskiy committed Nov 29, 2006
1 parent f6a7ecb commit 29072b9
Showing 5 changed files with 57 additions and 9 deletions.
1 change: 1 addition & 0 deletions drivers/mtd/mtdconcat.c
Original file line number Diff line number Diff line change
@@ -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 ||
1 change: 1 addition & 0 deletions drivers/mtd/mtdpart.c
Original file line number Diff line number Diff line change
@@ -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;
53 changes: 44 additions & 9 deletions drivers/mtd/nand/nand_base.c
Original file line number Diff line number Diff line change
@@ -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;

2 changes: 2 additions & 0 deletions include/linux/mtd/mtd.h
Original file line number Diff line number Diff line change
@@ -200,6 +200,8 @@ struct mtd_info {

/* ECC status information */
struct mtd_ecc_stats ecc_stats;
/* Subpage shift (NAND) */
int subpage_sft;

void *priv;

9 changes: 9 additions & 0 deletions include/linux/mtd/nand.h
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit 29072b9

Please sign in to comment.