Commit 837a6ba4 authored by Gupta, Pekon's avatar Gupta, Pekon Committed by David Woodhouse

mtd: nand: subpage write support for hardware based ECC schemes

This patch adds support for subpage (partial-page) writes when using
hardware based ECC schemes.
Advantages:
(1) reduces storage overhead when using file-systems like UBIFS, which
store LEB header at page-size granularity.
(2) allows independent subpage writes, thereby increasing NAND storage
efficiency for non-page aligned data.
+ updated cafe_nand and lpc32xx_mlc NAND drivers for change in
chip->write_page interface.
Signed-off-by: default avatarGupta, Pekon <pekon@ti.com>
Signed-off-by: default avatarArtem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 4ff6772b
...@@ -530,8 +530,8 @@ static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd, ...@@ -530,8 +530,8 @@ static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
} }
static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required, int page, uint32_t offset, int data_len, const uint8_t *buf,
int cached, int raw) int oob_required, int page, int cached, int raw)
{ {
int status; int status;
......
...@@ -540,8 +540,8 @@ static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd, ...@@ -540,8 +540,8 @@ static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd,
} }
static int lpc32xx_write_page(struct mtd_info *mtd, struct nand_chip *chip, static int lpc32xx_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required, int page, uint32_t offset, int data_len, const uint8_t *buf,
int cached, int raw) int oob_required, int page, int cached, int raw)
{ {
int res; int res;
......
...@@ -1110,7 +1110,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -1110,7 +1110,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
} }
/** /**
* nand_read_subpage - [REPLACEABLE] software ECC based sub-page read function * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function
* @mtd: mtd info structure * @mtd: mtd info structure
* @chip: nand chip info structure * @chip: nand chip info structure
* @data_offs: offset of requested data within the page * @data_offs: offset of requested data within the page
...@@ -1978,6 +1978,67 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -1978,6 +1978,67 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
return 0; return 0;
} }
/**
* nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write
* @mtd: mtd info structure
* @chip: nand chip info structure
* @column: column address of subpage within the page
* @data_len: data length
* @oob_required: must write chip->oob_poi to OOB
*/
static int nand_write_subpage_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint32_t offset,
uint32_t data_len, const uint8_t *data_buf,
int oob_required)
{
uint8_t *oob_buf = chip->oob_poi;
uint8_t *ecc_calc = chip->buffers->ecccalc;
int ecc_size = chip->ecc.size;
int ecc_bytes = chip->ecc.bytes;
int ecc_steps = chip->ecc.steps;
uint32_t *eccpos = chip->ecc.layout->eccpos;
uint32_t start_step = offset / ecc_size;
uint32_t end_step = (offset + data_len - 1) / ecc_size;
int oob_bytes = mtd->oobsize / ecc_steps;
int step, i;
for (step = 0; step < ecc_steps; step++) {
/* configure controller for WRITE access */
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
/* write data (untouched subpages already masked by 0xFF) */
chip->write_buf(mtd, data_buf, ecc_size);
/* mask ECC of un-touched subpages by padding 0xFF */
if ((step < start_step) || (step > end_step))
memset(ecc_calc, 0xff, ecc_bytes);
else
chip->ecc.calculate(mtd, data_buf, ecc_calc);
/* mask OOB of un-touched subpages by padding 0xFF */
/* if oob_required, preserve OOB metadata of written subpage */
if (!oob_required || (step < start_step) || (step > end_step))
memset(oob_buf, 0xff, oob_bytes);
data_buf += ecc_size;
ecc_calc += ecc_bytes;
oob_buf += oob_bytes;
}
/* copy calculated ECC for whole page to chip->buffer->oob */
/* this include masked-value(0xFF) for unwritten subpages */
ecc_calc = chip->buffers->ecccalc;
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
/* write OOB buffer to NAND device */
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
}
/** /**
* nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write
* @mtd: mtd info structure * @mtd: mtd info structure
...@@ -2030,6 +2091,8 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, ...@@ -2030,6 +2091,8 @@ static int nand_write_page_syndrome(struct mtd_info *mtd,
* nand_write_page - [REPLACEABLE] write one page * nand_write_page - [REPLACEABLE] write one page
* @mtd: MTD device structure * @mtd: MTD device structure
* @chip: NAND chip descriptor * @chip: NAND chip descriptor
* @offset: address offset within the page
* @data_len: length of actual data to be written
* @buf: the data to write * @buf: the data to write
* @oob_required: must write chip->oob_poi to OOB * @oob_required: must write chip->oob_poi to OOB
* @page: page number to write * @page: page number to write
...@@ -2037,15 +2100,25 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, ...@@ -2037,15 +2100,25 @@ static int nand_write_page_syndrome(struct mtd_info *mtd,
* @raw: use _raw version of write_page * @raw: use _raw version of write_page
*/ */
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required, int page, uint32_t offset, int data_len, const uint8_t *buf,
int cached, int raw) int oob_required, int page, int cached, int raw)
{ {
int status; int status, subpage;
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
chip->ecc.write_subpage)
subpage = offset || (data_len < mtd->writesize);
else
subpage = 0;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
if (unlikely(raw)) if (unlikely(raw))
status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required); status = chip->ecc.write_page_raw(mtd, chip, buf,
oob_required);
else if (subpage)
status = chip->ecc.write_subpage(mtd, chip, offset, data_len,
buf, oob_required);
else else
status = chip->ecc.write_page(mtd, chip, buf, oob_required); status = chip->ecc.write_page(mtd, chip, buf, oob_required);
...@@ -2159,7 +2232,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, ...@@ -2159,7 +2232,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
uint8_t *oob = ops->oobbuf; uint8_t *oob = ops->oobbuf;
uint8_t *buf = ops->datbuf; uint8_t *buf = ops->datbuf;
int ret, subpage; int ret;
int oob_required = oob ? 1 : 0; int oob_required = oob ? 1 : 0;
ops->retlen = 0; ops->retlen = 0;
...@@ -2174,10 +2247,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, ...@@ -2174,10 +2247,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
} }
column = to & (mtd->writesize - 1); column = to & (mtd->writesize - 1);
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);
...@@ -2226,9 +2295,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, ...@@ -2226,9 +2295,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
/* We still need to erase leftover OOB data */ /* We still need to erase leftover OOB data */
memset(chip->oob_poi, 0xff, mtd->oobsize); memset(chip->oob_poi, 0xff, mtd->oobsize);
} }
ret = chip->write_page(mtd, chip, column, bytes, wbuf,
ret = chip->write_page(mtd, chip, wbuf, oob_required, page, oob_required, page, cached,
cached, (ops->mode == MTD_OPS_RAW)); (ops->mode == MTD_OPS_RAW));
if (ret) if (ret)
break; break;
...@@ -3414,6 +3483,10 @@ int nand_scan_tail(struct mtd_info *mtd) ...@@ -3414,6 +3483,10 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.read_oob = nand_read_oob_std; chip->ecc.read_oob = nand_read_oob_std;
if (!chip->ecc.write_oob) if (!chip->ecc.write_oob)
chip->ecc.write_oob = nand_write_oob_std; chip->ecc.write_oob = nand_write_oob_std;
if (!chip->ecc.read_subpage)
chip->ecc.read_subpage = nand_read_subpage;
if (!chip->ecc.write_subpage)
chip->ecc.write_subpage = nand_write_subpage_hwecc;
case NAND_ECC_HW_SYNDROME: case NAND_ECC_HW_SYNDROME:
if ((!chip->ecc.calculate || !chip->ecc.correct || if ((!chip->ecc.calculate || !chip->ecc.correct ||
......
...@@ -318,6 +318,7 @@ struct nand_hw_control { ...@@ -318,6 +318,7 @@ struct nand_hw_control {
* any single ECC step, 0 if bitflips uncorrectable, -EIO hw error * any single ECC step, 0 if bitflips uncorrectable, -EIO hw error
* @read_subpage: function to read parts of the page covered by ECC; * @read_subpage: function to read parts of the page covered by ECC;
* returns same as read_page() * returns same as read_page()
* @write_subpage: function to write parts of the page covered by ECC.
* @write_page: function to write a page according to the ECC generator * @write_page: function to write a page according to the ECC generator
* requirements. * requirements.
* @write_oob_raw: function to write chip OOB data without ECC * @write_oob_raw: function to write chip OOB data without ECC
...@@ -349,6 +350,9 @@ struct nand_ecc_ctrl { ...@@ -349,6 +350,9 @@ struct nand_ecc_ctrl {
uint8_t *buf, int oob_required, int page); uint8_t *buf, int oob_required, int page);
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip, int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offs, uint32_t len, uint8_t *buf); uint32_t offs, uint32_t len, uint8_t *buf);
int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offset, uint32_t data_len,
const uint8_t *data_buf, int oob_required);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required); const uint8_t *buf, int oob_required);
int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
...@@ -484,8 +488,8 @@ struct nand_chip { ...@@ -484,8 +488,8 @@ struct nand_chip {
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
int status, int page); int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required, int page, uint32_t offset, int data_len, const uint8_t *buf,
int cached, int raw); int oob_required, int page, int cached, int raw);
int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip, int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
int feature_addr, uint8_t *subfeature_para); int feature_addr, uint8_t *subfeature_para);
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip, int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
......
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