From a95cc309cf74eed3fc457dec3dcc44d9bf79e0e6 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 28 Jul 2014 15:01:15 +0200 Subject: [PATCH] mtd: nand: Add support for NAND partitions Add support for NAND partitions, and indirectly for per partition ECC config, and also per partiton random seed support for the upcoming randomizer support. This is necessary to be able to use different ECC / randomizer settings for the parts of the NAND which are read directly by a bootrom (which has a fixed ECC / random seed setting) and the generic data part of the NAND for which we often want a stronger ECC and / or random seed. Provide helper functions to add/delete/allocate nand partitions. NAND core code now make use of the partition specific nand_ecc_ctrl struct (if available) when doing read/write operations. Signed-off-by: Boris BREZILLON Signed-off-by: Hans de Goede --- drivers/mtd/nand/Kconfig | 4 + drivers/mtd/nand/Makefile | 2 + drivers/mtd/nand/nand_base.c | 712 +++++++++++++++++++++++++++++++++++-------- drivers/mtd/nand/nand_bch.c | 16 +- drivers/mtd/nand/nand_ecc.c | 4 +- include/linux/mtd/nand.h | 38 +++ 6 files changed, 635 insertions(+), 141 deletions(-) --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -22,6 +22,10 @@ menuconfig MTD_NAND if MTD_NAND +config MTD_OF_NAND_PARTS + tristate + default n + config MTD_NAND_BCH tristate select BCH --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -53,4 +53,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o +obj-$(CONFIG_MTD_OF_NAND_PARTS) += ofnandpart.o + nand-objs := nand_base.o nand_bbt.o nand_timings.o --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1134,26 +1134,26 @@ static int nand_read_page_raw_syndrome(s struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; + int eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; uint8_t *oob = chip->oob_poi; int steps, size; - for (steps = chip->ecc.steps; steps > 0; steps--) { + for (steps = chip->cur_ecc->steps; steps > 0; steps--) { chip->read_buf(mtd, buf, eccsize); buf += eccsize; - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; + if (chip->cur_ecc->prepad) { + chip->read_buf(mtd, oob, chip->cur_ecc->prepad); + oob += chip->cur_ecc->prepad; } chip->read_buf(mtd, oob, eccbytes); oob += eccbytes; - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; + if (chip->cur_ecc->postpad) { + chip->read_buf(mtd, oob, chip->cur_ecc->postpad); + oob += chip->cur_ecc->postpad; } } @@ -1175,30 +1175,31 @@ static int nand_read_page_raw_syndrome(s static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *p = buf; uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; unsigned int max_bitflips = 0; - chip->ecc.read_page_raw(mtd, chip, buf, 1, page); + chip->cur_ecc->read_page_raw(mtd, chip, buf, 1, page); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; - eccsteps = chip->ecc.steps; + eccsteps = chip->cur_ecc->steps; p = buf; for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], + &ecc_calc[i]); if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1223,7 +1224,7 @@ static int nand_read_subpage(struct mtd_ int page) { int start_step, end_step, num_steps; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; uint8_t *p; int data_col_addr, i, gaps = 0; int datafrag_len, eccfrag_len, aligned_len, aligned_pos; @@ -1232,16 +1233,16 @@ static int nand_read_subpage(struct mtd_ unsigned int max_bitflips = 0; /* Column address within the page aligned to ECC size (256bytes) */ - start_step = data_offs / chip->ecc.size; - end_step = (data_offs + readlen - 1) / chip->ecc.size; + start_step = data_offs / chip->cur_ecc->size; + end_step = (data_offs + readlen - 1) / chip->cur_ecc->size; num_steps = end_step - start_step + 1; - index = start_step * chip->ecc.bytes; + index = start_step * chip->cur_ecc->bytes; /* Data size aligned to ECC ecc.size */ - datafrag_len = num_steps * chip->ecc.size; - eccfrag_len = num_steps * chip->ecc.bytes; + datafrag_len = num_steps * chip->cur_ecc->size; + eccfrag_len = num_steps * chip->cur_ecc->bytes; - data_col_addr = start_step * chip->ecc.size; + data_col_addr = start_step * chip->cur_ecc->size; /* If we read not a page aligned data */ if (data_col_addr != 0) chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); @@ -1250,8 +1251,9 @@ static int nand_read_subpage(struct mtd_ chip->read_buf(mtd, p, datafrag_len); /* Calculate ECC */ - for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) - chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); + for (i = 0; i < eccfrag_len; + i += chip->cur_ecc->bytes, p += chip->cur_ecc->size) + chip->cur_ecc->calculate(mtd, p, &chip->buffers->ecccalc[i]); /* * The performance is faster if we position offsets according to @@ -1275,7 +1277,8 @@ static int nand_read_subpage(struct mtd_ aligned_len = eccfrag_len; if (eccpos[index] & (busw - 1)) aligned_len++; - if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1)) + if (eccpos[index + (num_steps * chip->cur_ecc->bytes)] & + (busw - 1)) aligned_len++; chip->cmdfunc(mtd, NAND_CMD_RNDOUT, @@ -1287,11 +1290,13 @@ static int nand_read_subpage(struct mtd_ chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]]; p = bufpoi + data_col_addr; - for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { + for (i = 0; i < eccfrag_len; + i += chip->cur_ecc->bytes, p += chip->cur_ecc->size) { int stat; - stat = chip->ecc.correct(mtd, p, - &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + stat = chip->cur_ecc->correct(mtd, p, + &chip->buffers->ecccode[i], + &chip->buffers->ecccalc[i]); if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1315,32 +1320,33 @@ static int nand_read_subpage(struct mtd_ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *p = buf; uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->cur_ecc->hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); } chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; - eccsteps = chip->ecc.steps; + eccsteps = chip->cur_ecc->steps; p = buf; for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], + &ecc_calc[i]); if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1368,12 +1374,12 @@ static int nand_read_page_hwecc(struct m static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *p = buf; uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; uint8_t *ecc_calc = chip->buffers->ecccalc; unsigned int max_bitflips = 0; @@ -1382,17 +1388,17 @@ static int nand_read_page_hwecc_oob_firs chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; - chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->cur_ecc->hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); - stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); + stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], NULL); if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1417,9 +1423,9 @@ static int nand_read_page_hwecc_oob_firs static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *p = buf; uint8_t *oob = chip->oob_poi; unsigned int max_bitflips = 0; @@ -1427,17 +1433,17 @@ static int nand_read_page_syndrome(struc for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; - chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->cur_ecc->hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; + if (chip->cur_ecc->prepad) { + chip->read_buf(mtd, oob, chip->cur_ecc->prepad); + oob += chip->cur_ecc->prepad; } - chip->ecc.hwctl(mtd, NAND_ECC_READSYN); + chip->cur_ecc->hwctl(mtd, NAND_ECC_READSYN); chip->read_buf(mtd, oob, eccbytes); - stat = chip->ecc.correct(mtd, p, oob, NULL); + stat = chip->cur_ecc->correct(mtd, p, oob, NULL); if (stat < 0) { mtd->ecc_stats.failed++; @@ -1448,9 +1454,9 @@ static int nand_read_page_syndrome(struc oob += eccbytes; - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; + if (chip->cur_ecc->postpad) { + chip->read_buf(mtd, oob, chip->cur_ecc->postpad); + oob += chip->cur_ecc->postpad; } } @@ -1480,7 +1486,7 @@ static uint8_t *nand_transfer_oob(struct return oob + len; case MTD_OPS_AUTO_OOB: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; + struct nand_oobfree *free = chip->cur_ecc->layout->oobfree; uint32_t boffs = 0, roffs = ops->ooboffs; size_t bytes = 0; @@ -1600,17 +1606,21 @@ read_retry: * the read methods return max bitflips per ecc step. */ if (unlikely(ops->mode == MTD_OPS_RAW)) - ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, - oob_required, - page); + ret = chip->cur_ecc->read_page_raw(mtd, chip, + bufpoi, + oob_required, + page); else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && !oob) - ret = chip->ecc.read_subpage(mtd, chip, - col, bytes, bufpoi, - page); + ret = chip->cur_ecc->read_subpage(mtd, chip, + col, bytes, + bufpoi, + page); else - ret = chip->ecc.read_page(mtd, chip, bufpoi, - oob_required, page); + ret = chip->cur_ecc->read_page(mtd, chip, + bufpoi, + oob_required, + page); if (ret < 0) { if (use_bufpoi) /* Invalidate page cache */ @@ -1746,6 +1756,39 @@ static int nand_read(struct mtd_info *mt } /** + * nand_part_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * Get hold of the chip and call nand_do_read. + */ +static int nand_part_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + struct mtd_oob_ops ops; + int ret; + + from += part->offset; + nand_get_device(part->master, FL_READING); + if (part->ecc) + chip->cur_ecc = part->ecc; + ops.len = len; + ops.datbuf = buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + ret = nand_do_read_ops(part->master, from, &ops); + *retlen = ops.retlen; + chip->cur_ecc = &chip->ecc; + nand_release_device(part->master); + return ret; +} + +/** * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function * @mtd: mtd info structure * @chip: nand chip info structure @@ -1770,13 +1813,14 @@ static int nand_read_oob_syndrome(struct int page) { int length = mtd->oobsize; - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size; + int chunk = chip->cur_ecc->bytes + chip->cur_ecc->prepad + + chip->cur_ecc->postpad; + int eccsize = chip->cur_ecc->size; uint8_t *bufpoi = chip->oob_poi; int i, toread, sndrnd = 0, pos; - chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); - for (i = 0; i < chip->ecc.steps; i++) { + chip->cmdfunc(mtd, NAND_CMD_READ0, chip->cur_ecc->size, page); + for (i = 0; i < chip->cur_ecc->steps; i++) { if (sndrnd) { pos = eccsize + i * (eccsize + chunk); if (mtd->writesize > 512) @@ -1829,9 +1873,10 @@ static int nand_write_oob_std(struct mtd static int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page) { - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size, length = mtd->oobsize; - int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; + int chunk = chip->cur_ecc->bytes + chip->cur_ecc->prepad + + chip->cur_ecc->postpad; + int eccsize = chip->cur_ecc->size, length = mtd->oobsize; + int i, len, pos, status = 0, sndcmd = 0, steps = chip->cur_ecc->steps; const uint8_t *bufpoi = chip->oob_poi; /* @@ -1839,7 +1884,7 @@ static int nand_write_oob_syndrome(struc * or * data-pad-ecc-pad-data-pad .... ecc-pad-oob */ - if (!chip->ecc.prepad && !chip->ecc.postpad) { + if (!chip->cur_ecc->prepad && !chip->cur_ecc->postpad) { pos = steps * (eccsize + chunk); steps = 0; } else @@ -1903,7 +1948,7 @@ static int nand_do_read_oob(struct mtd_i stats = mtd->ecc_stats; if (ops->mode == MTD_OPS_AUTO_OOB) - len = chip->ecc.layout->oobavail; + len = chip->cur_ecc->layout->oobavail; else len = mtd->oobsize; @@ -1931,9 +1976,9 @@ static int nand_do_read_oob(struct mtd_i while (1) { if (ops->mode == MTD_OPS_RAW) - ret = chip->ecc.read_oob_raw(mtd, chip, page); + ret = chip->cur_ecc->read_oob_raw(mtd, chip, page); else - ret = chip->ecc.read_oob(mtd, chip, page); + ret = chip->cur_ecc->read_oob(mtd, chip, page); if (ret < 0) break; @@ -2021,6 +2066,56 @@ out: return ret; } +/** + * nand_part_read_oob - [MTD Interface] NAND read data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + * + * NAND read data and/or out-of-band data. + */ +static int nand_part_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow reads past end of device */ + if (ops->datbuf && (from + ops->len) > mtd->size) { + pr_debug("%s: attempt to read beyond end of device\n", + __func__); + return -EINVAL; + } + + from += part->offset; + nand_get_device(part->master, FL_READING); + if (part->ecc) + chip->cur_ecc = part->ecc; + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_read_oob(part->master, from, ops); + else + ret = nand_do_read_ops(part->master, from, ops); + +out: + chip->cur_ecc = &chip->ecc; + nand_release_device(part->master); + return ret; +} + /** * nand_write_page_raw - [INTERN] raw page write function @@ -2054,26 +2149,26 @@ static int nand_write_page_raw_syndrome( struct nand_chip *chip, const uint8_t *buf, int oob_required) { - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; + int eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; uint8_t *oob = chip->oob_poi; int steps, size; - for (steps = chip->ecc.steps; steps > 0; steps--) { + for (steps = chip->cur_ecc->steps; steps > 0; steps--) { chip->write_buf(mtd, buf, eccsize); buf += eccsize; - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; + if (chip->cur_ecc->prepad) { + chip->write_buf(mtd, oob, chip->cur_ecc->prepad); + oob += chip->cur_ecc->prepad; } chip->write_buf(mtd, oob, eccbytes); oob += eccbytes; - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; + if (chip->cur_ecc->postpad) { + chip->write_buf(mtd, oob, chip->cur_ecc->postpad); + oob += chip->cur_ecc->postpad; } } @@ -2093,21 +2188,21 @@ static int nand_write_page_raw_syndrome( static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; /* Software ECC calculation */ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - return chip->ecc.write_page_raw(mtd, chip, buf, 1); + return chip->cur_ecc->write_page_raw(mtd, chip, buf, 1); } /** @@ -2120,20 +2215,20 @@ static int nand_write_page_swecc(struct static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t *eccpos = chip->cur_ecc->layout->eccpos; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE); chip->write_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); + chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]); } - for (i = 0; i < chip->ecc.total; i++) + for (i = 0; i < chip->cur_ecc->total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -2158,10 +2253,10 @@ static int nand_write_subpage_hwecc(stru { 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; + int ecc_size = chip->cur_ecc->size; + int ecc_bytes = chip->cur_ecc->bytes; + int ecc_steps = chip->cur_ecc->steps; + uint32_t *eccpos = chip->cur_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; @@ -2169,7 +2264,7 @@ static int nand_write_subpage_hwecc(stru for (step = 0; step < ecc_steps; step++) { /* configure controller for WRITE access */ - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE); /* write data (untouched subpages already masked by 0xFF) */ chip->write_buf(mtd, buf, ecc_size); @@ -2178,7 +2273,7 @@ static int nand_write_subpage_hwecc(stru if ((step < start_step) || (step > end_step)) memset(ecc_calc, 0xff, ecc_bytes); else - chip->ecc.calculate(mtd, buf, ecc_calc); + chip->cur_ecc->calculate(mtd, buf, ecc_calc); /* mask OOB of un-touched subpages by padding 0xFF */ /* if oob_required, preserve OOB metadata of written subpage */ @@ -2193,7 +2288,7 @@ static int nand_write_subpage_hwecc(stru /* 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++) + for (i = 0; i < chip->cur_ecc->total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; /* write OOB buffer to NAND device */ @@ -2217,29 +2312,29 @@ static int nand_write_page_syndrome(stru struct nand_chip *chip, const uint8_t *buf, int oob_required) { - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; + int i, eccsize = chip->cur_ecc->size; + int eccbytes = chip->cur_ecc->bytes; + int eccsteps = chip->cur_ecc->steps; const uint8_t *p = buf; uint8_t *oob = chip->oob_poi; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE); chip->write_buf(mtd, p, eccsize); - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; + if (chip->cur_ecc->prepad) { + chip->write_buf(mtd, oob, chip->cur_ecc->prepad); + oob += chip->cur_ecc->prepad; } - chip->ecc.calculate(mtd, p, oob); + chip->cur_ecc->calculate(mtd, p, oob); chip->write_buf(mtd, oob, eccbytes); oob += eccbytes; - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; + if (chip->cur_ecc->postpad) { + chip->write_buf(mtd, oob, chip->cur_ecc->postpad); + oob += chip->cur_ecc->postpad; } } @@ -2270,7 +2365,7 @@ static int nand_write_page(struct mtd_in int status, subpage; if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && - chip->ecc.write_subpage) + chip->cur_ecc->write_subpage) subpage = offset || (data_len < mtd->writesize); else subpage = 0; @@ -2278,13 +2373,15 @@ static int nand_write_page(struct mtd_in chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) - status = chip->ecc.write_page_raw(mtd, chip, buf, - oob_required); + status = chip->cur_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); + status = chip->cur_ecc->write_subpage(mtd, chip, offset, + data_len, buf, + oob_required); else - status = chip->ecc.write_page(mtd, chip, buf, oob_required); + status = chip->cur_ecc->write_page(mtd, chip, buf, + oob_required); if (status < 0) return status; @@ -2343,7 +2440,7 @@ static uint8_t *nand_fill_oob(struct mtd return oob + len; case MTD_OPS_AUTO_OOB: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; + struct nand_oobfree *free = chip->cur_ecc->layout->oobfree; uint32_t boffs = 0, woffs = ops->ooboffs; size_t bytes = 0; @@ -2539,6 +2636,46 @@ static int panic_nand_write(struct mtd_i } /** + * panic_nand_part_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC. Used when performing writes in interrupt context, this + * may for example be called by mtdoops when writing an oops while in panic. + */ +static int panic_nand_part_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + struct mtd_oob_ops ops; + int ret; + + to += part->offset; + /* Wait for the device to get ready */ + panic_nand_wait(part->master, chip, 400); + + /* Grab the device */ + panic_nand_get_device(chip, part->master, FL_WRITING); + if (part->ecc) + chip->cur_ecc = part->ecc; + + ops.len = len; + ops.datbuf = (uint8_t *)buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + ret = nand_do_write_ops(part->master, to, &ops); + + chip->cur_ecc = &chip->ecc; + *retlen = ops.retlen; + return ret; +} + +/** * nand_write - [MTD Interface] NAND write with ECC * @mtd: MTD device structure * @to: offset to write to @@ -2566,6 +2703,39 @@ static int nand_write(struct mtd_info *m } /** + * nand_part_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC. + */ +static int nand_part_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + struct mtd_oob_ops ops; + int ret; + + to += part->offset; + nand_get_device(part->master, FL_WRITING); + if (part->ecc) + chip->cur_ecc = part->ecc; + ops.len = len; + ops.datbuf = (uint8_t *)buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + ret = nand_do_write_ops(part->master, to, &ops); + *retlen = ops.retlen; + chip->cur_ecc = &chip->ecc; + nand_release_device(part->master); + return ret; +} + +/** * nand_do_write_oob - [MTD Interface] NAND write out-of-band * @mtd: MTD device structure * @to: offset to write to @@ -2583,7 +2753,7 @@ static int nand_do_write_oob(struct mtd_ __func__, (unsigned int)to, (int)ops->ooblen); if (ops->mode == MTD_OPS_AUTO_OOB) - len = chip->ecc.layout->oobavail; + len = chip->cur_ecc->layout->oobavail; else len = mtd->oobsize; @@ -2637,9 +2807,11 @@ static int nand_do_write_oob(struct mtd_ nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); if (ops->mode == MTD_OPS_RAW) - status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask); + status = chip->cur_ecc->write_oob_raw(mtd, chip, + page & chip->pagemask); else - status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + status = chip->cur_ecc->write_oob(mtd, chip, + page & chip->pagemask); chip->select_chip(mtd, -1); @@ -2694,6 +2866,54 @@ out: } /** + * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + */ +static int nand_part_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + struct nand_part *part = to_nand_part(mtd); + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow writes past end of device */ + if (ops->datbuf && (to + ops->len) > mtd->size) { + pr_debug("%s: attempt to write beyond end of device\n", + __func__); + return -EINVAL; + } + + to += part->offset; + nand_get_device(part->master, FL_WRITING); + if (part->ecc) + chip->cur_ecc = part->ecc; + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_write_oob(part->master, to, ops); + else + ret = nand_do_write_ops(part->master, to, ops); + +out: + chip->cur_ecc = &chip->ecc; + nand_release_device(part->master); + return ret; +} + +/** * single_erase - [GENERIC] NAND standard block erase command function * @mtd: MTD device structure * @page: the page address of the block which will be erased @@ -2723,6 +2943,29 @@ static int nand_erase(struct mtd_info *m } /** + * nand_part_erase - [MTD Interface] erase partition block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * + * Erase one ore more blocks. + */ +static int nand_part_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct nand_part *part = to_nand_part(mtd); + int ret; + + instr->addr += part->offset; + ret = nand_erase_nand(part->master, instr, 0); + if (ret) { + if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) + instr->fail_addr -= part->offset; + instr->addr -= part->offset; + } + + return ret; +} + +/** * nand_erase_nand - [INTERN] erase block(s) * @mtd: MTD device structure * @instr: erase instruction @@ -2864,6 +3107,18 @@ static int nand_block_isbad(struct mtd_i } /** + * nand_part_block_isbad - [MTD Interface] Check if block at offset is bad + * @mtd: MTD device structure + * @offs: offset relative to mtd start + */ +static int nand_part_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + struct nand_part *part = to_nand_part(mtd); + + return nand_block_checkbad(part->master, part->offset + offs, 1, 0); +} + +/** * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad * @mtd: MTD device structure * @ofs: offset relative to mtd start @@ -2884,6 +3139,33 @@ static int nand_block_markbad(struct mtd } /** + * nand_part_block_markbad - [MTD Interface] Mark block at the given offset as + * bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int nand_part_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_part *part = to_nand_part(mtd); + int ret; + + ofs += part->offset; + ret = nand_block_isbad(part->master, ofs); + if (ret) { + /* If it was bad already, return success and do nothing */ + if (ret > 0) + return 0; + return ret; + } + + ret = nand_block_markbad_lowlevel(part->master, ofs); + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} + +/** * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand * @mtd: MTD device structure * @chip: nand chip info structure @@ -4099,6 +4381,169 @@ static int nand_ecc_ctrl_init(struct mtd } /** + * nand_add_partition - [NAND Interface] Add a NAND partition to a NAND device + * @master: MTD device structure representing the NAND device + * @part: NAND partition to add to the NAND device + * + * Adds a NAND partition to a NAND device. + * The NAND partition cannot overlap with another existing partition. + * + * Returns zero in case of success and a negative error code in case of failure. + */ +int nand_add_partition(struct mtd_info *master, struct nand_part *part) +{ + struct nand_chip *chip = master->priv; + struct mtd_info *mtd = &part->mtd; + struct nand_ecc_ctrl *ecc = part->ecc; + struct nand_part *pos; + bool inserted = false; + int ret; + + /* set up the MTD object for this partition */ + mtd->type = master->type; + mtd->flags = master->flags & ~mtd->flags; + mtd->writesize = master->writesize; + mtd->writebufsize = master->writebufsize; + mtd->oobsize = master->oobsize; + mtd->oobavail = master->oobavail; + mtd->subpage_sft = master->subpage_sft; + mtd->erasesize = master->erasesize; + + mtd->priv = chip; + mtd->owner = master->owner; + mtd->backing_dev_info = master->backing_dev_info; + + mtd->dev.parent = master->dev.parent; + + if (ecc) { + ret = nand_ecc_ctrl_init(mtd, ecc); + if (ret) + return ret; + } else { + ecc = &chip->ecc; + } + + mtd->_erase = nand_part_erase; + mtd->_point = NULL; + mtd->_unpoint = NULL; + mtd->_read = nand_part_read; + mtd->_write = nand_part_write; + mtd->_panic_write = panic_nand_part_write; + mtd->_read_oob = nand_part_read_oob; + mtd->_write_oob = nand_part_write_oob; + mtd->_sync = nand_sync; + mtd->_lock = NULL; + mtd->_unlock = NULL; + mtd->_suspend = nand_suspend; + mtd->_resume = nand_resume; + mtd->_block_isbad = nand_part_block_isbad; + mtd->_block_markbad = nand_part_block_markbad; + + /* propagate ecc info to mtd_info */ + mtd->ecclayout = ecc->layout; + mtd->ecc_strength = ecc->strength; + mtd->ecc_step_size = ecc->size; + /* + * Initialize bitflip_threshold to its default prior scan_bbt() call. + * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be + * properly set. + */ + if (!mtd->bitflip_threshold) + mtd->bitflip_threshold = mtd->ecc_strength; + + part->master = master; + + mutex_lock(&chip->part_lock); + list_for_each_entry(pos, &chip->partitions, node) { + if (part->offset >= pos->offset + pos->mtd.size) { + continue; + } else if (part->offset + mtd->size > pos->offset) { + ret = -EINVAL; + goto out; + } + + list_add(&part->node, pos->node.prev); + inserted = true; + break; + } + + if (!inserted) + list_add_tail(&part->node, &chip->partitions); + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + list_del(&part->node); + goto out; + } + + if (master->_block_isbad) { + uint64_t offs = 0; + + while (offs < mtd->size) { + if (mtd_block_isreserved(master, offs + part->offset)) + mtd->ecc_stats.bbtblocks++; + else if (mtd_block_isbad(master, offs + part->offset)) + mtd->ecc_stats.badblocks++; + offs += mtd->erasesize; + } + } + +out: + mutex_unlock(&chip->part_lock); + return ret; +} +EXPORT_SYMBOL(nand_add_partition); + +/** + * nand_del_partition - [NAND Interface] Delete a NAND part from a NAND dev + * @part: NAND partition to delete + * + * Deletes a NAND partition from a NAND device. + */ +void nand_del_partition(struct nand_part *part) +{ + struct nand_chip *chip = part->mtd.priv; + + mutex_lock(&chip->part_lock); + mtd_device_unregister(&part->mtd); + list_del(&part->node); + mutex_unlock(&chip->part_lock); + + if (part->ecc && part->ecc->mode == NAND_ECC_SOFT_BCH) + nand_bch_free((struct nand_bch_control *)part->ecc->priv); + + if (part->release) + part->release(part); +} +EXPORT_SYMBOL(nand_del_partition); + +/* + * NAND part release function. Used by nandpart_alloc as its release function. + */ +static void nandpart_release(struct nand_part *part) +{ + kfree(part); +} + +/** + * nandpart_alloc - [NAND Interface] Allocate a NAND part struct + * + * Allocate a NAND partition and assign the nandpart release function. + * This nand_part struct must be filled before passing it to the + * nand_add_partition function. + */ +struct nand_part *nandpart_alloc(void) +{ + struct nand_part *part = kzalloc(sizeof(*part), GFP_KERNEL); + if (!part) + return ERR_PTR(-ENOMEM); + part->release = nandpart_release; + + return part; +} +EXPORT_SYMBOL(nandpart_alloc); + +/** * nand_scan_tail - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure * @@ -4146,6 +4591,11 @@ int nand_scan_tail(struct mtd_info *mtd) return ret; } + INIT_LIST_HEAD(&chip->partitions); + mutex_init(&chip->part_lock); + + chip->cur_ecc = &chip->ecc; + /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { switch (ecc->steps) { --- a/drivers/mtd/nand/nand_bch.c +++ b/drivers/mtd/nand/nand_bch.c @@ -53,14 +53,14 @@ int nand_bch_calculate_ecc(struct mtd_in unsigned char *code) { const struct nand_chip *chip = mtd->priv; - struct nand_bch_control *nbc = chip->ecc.priv; + struct nand_bch_control *nbc = chip->cur_ecc->priv; unsigned int i; - memset(code, 0, chip->ecc.bytes); - encode_bch(nbc->bch, buf, chip->ecc.size, code); + memset(code, 0, chip->cur_ecc->bytes); + encode_bch(nbc->bch, buf, chip->cur_ecc->size, code); /* apply mask so that an erased page is a valid codeword */ - for (i = 0; i < chip->ecc.bytes; i++) + for (i = 0; i < chip->cur_ecc->bytes; i++) code[i] ^= nbc->eccmask[i]; return 0; @@ -80,15 +80,15 @@ int nand_bch_correct_data(struct mtd_inf unsigned char *read_ecc, unsigned char *calc_ecc) { const struct nand_chip *chip = mtd->priv; - struct nand_bch_control *nbc = chip->ecc.priv; + struct nand_bch_control *nbc = chip->cur_ecc->priv; unsigned int *errloc = nbc->errloc; int i, count; - count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, - NULL, errloc); + count = decode_bch(nbc->bch, NULL, chip->cur_ecc->size, read_ecc, + calc_ecc, NULL, errloc); if (count > 0) { for (i = 0; i < count; i++) { - if (errloc[i] < (chip->ecc.size*8)) + if (errloc[i] < (chip->cur_ecc->size*8)) /* error is located in data, correct it */ buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); /* else error in ecc, no action needed */ --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -424,7 +424,7 @@ int nand_calculate_ecc(struct mtd_info * unsigned char *code) { __nand_calculate_ecc(buf, - ((struct nand_chip *)mtd->priv)->ecc.size, code); + ((struct nand_chip *)mtd->priv)->cur_ecc->size, code); return 0; } @@ -524,7 +524,7 @@ int nand_correct_data(struct mtd_info *m unsigned char *read_ecc, unsigned char *calc_ecc) { return __nand_correct_data(buf, read_ecc, calc_ecc, - ((struct nand_chip *)mtd->priv)->ecc.size); + ((struct nand_chip *)mtd->priv)->cur_ecc->size); } EXPORT_SYMBOL(nand_correct_data); --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -708,6 +708,7 @@ struct nand_chip { struct nand_hw_control *controller; struct nand_ecc_ctrl ecc; + struct nand_ecc_ctrl *cur_ecc; struct nand_buffers *buffers; struct nand_hw_control hwcontrol; @@ -717,9 +718,46 @@ struct nand_chip { struct nand_bbt_descr *badblock_pattern; + struct list_head partitions; + struct mutex part_lock; + void *priv; }; +/** + * struct nand_part - NAND partition structure + * @node: list node used to attach the partition to its NAND dev + * @mtd: MTD partiton info + * @master: MTD device representing the NAND chip + * @offset: partition offset + * @ecc: partition specific ECC struct + * @release: function used to release this nand_part struct + * + * NAND partitions work as standard MTD partitions except it can override + * NAND chip ECC handling. + * This is particularly useful for SoCs that need specific ECC configs to boot + * from NAND while these ECC configs do not fit the NAND chip ECC requirements. + */ +struct nand_part { + struct list_head node; + struct mtd_info mtd; + struct mtd_info *master; + uint64_t offset; + struct nand_ecc_ctrl *ecc; + void (*release)(struct nand_part *part); +}; + +static inline struct nand_part *to_nand_part(struct mtd_info *mtd) +{ + return container_of(mtd, struct nand_part, mtd); +} + +int nand_add_partition(struct mtd_info *master, struct nand_part *part); + +void nand_del_partition(struct nand_part *part); + +struct nand_part *nandpart_alloc(void); + /* * NAND Flash Manufacturer ID Codes */