diff options
author | Zoltan Herpai <wigyori@uid0.hu> | 2015-08-09 12:25:18 +0000 |
---|---|---|
committer | Zoltan Herpai <wigyori@uid0.hu> | 2015-08-09 12:25:18 +0000 |
commit | 50018a752771efce3ae222457d4d231d1f80a1a5 (patch) | |
tree | ced2b9c1e216ce0e3d0bb204e359a7a9d6d2a3ef /target/linux/sunxi/patches-4.1/111-mtd-add-support-for-nand-partitions.patch | |
parent | 0b8643af4fa7eebd55b63d6d547f85da1409f83d (diff) | |
download | mtk-20170518-50018a752771efce3ae222457d4d231d1f80a1a5.zip mtk-20170518-50018a752771efce3ae222457d4d231d1f80a1a5.tar.gz mtk-20170518-50018a752771efce3ae222457d4d231d1f80a1a5.tar.bz2 |
sunxi: add support for 4.1
Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
SVN-Revision: 46571
Diffstat (limited to 'target/linux/sunxi/patches-4.1/111-mtd-add-support-for-nand-partitions.patch')
-rw-r--r-- | target/linux/sunxi/patches-4.1/111-mtd-add-support-for-nand-partitions.patch | 1307 |
1 files changed, 1307 insertions, 0 deletions
diff --git a/target/linux/sunxi/patches-4.1/111-mtd-add-support-for-nand-partitions.patch b/target/linux/sunxi/patches-4.1/111-mtd-add-support-for-nand-partitions.patch new file mode 100644 index 0000000..88e313d --- /dev/null +++ b/target/linux/sunxi/patches-4.1/111-mtd-add-support-for-nand-partitions.patch @@ -0,0 +1,1307 @@ +From a95cc309cf74eed3fc457dec3dcc44d9bf79e0e6 Mon Sep 17 00:00:00 2001 +From: Boris BREZILLON <boris.brezillon@free-electrons.com> +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 <boris.brezillon@free-electrons.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + 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(-) + +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index 5897d8d..8242470 100644 +--- 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 +diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile +index 582bbd05..fcbe032 100644 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -53,4 +53,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += 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 +diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c +index f580ed1..a30b67f 100644 +--- 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(struct mtd_info *mtd, + 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(struct mtd_info *mtd, + 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_info *mtd, struct nand_chip *chip, + 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_info *mtd, struct nand_chip *chip, + 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_info *mtd, struct nand_chip *chip, + 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_info *mtd, struct nand_chip *chip, + 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_info *mtd, struct nand_chip *chip, + 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_info *mtd, struct nand_chip *chip, + 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 mtd_info *mtd, struct nand_chip *chip, + 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_first(struct mtd_info *mtd, + 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_first(struct mtd_info *mtd, + 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(struct mtd_info *mtd, struct nand_chip *chip, + 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(struct mtd_info *mtd, struct nand_chip *chip, + + 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 nand_chip *chip, uint8_t *oob, + 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 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + * 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 *mtd, loff_t from, size_t len, + } + + /** ++ * 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 mtd_info *mtd, struct nand_chip *chip, + 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_info *mtd, struct nand_chip *chip, + 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(struct mtd_info *mtd, + * 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_info *mtd, loff_t from, + 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_info *mtd, loff_t from, + + 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 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, + 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 mtd_info *mtd, + 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(struct mtd_info *mtd, + 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 mtd_info *mtd, struct nand_chip *chip, + 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(struct mtd_info *mtd, + { + 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(struct mtd_info *mtd, + + 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(struct mtd_info *mtd, + 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(struct mtd_info *mtd, + /* 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(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; + 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_info *mtd, struct nand_chip *chip, + 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_info *mtd, struct nand_chip *chip, + 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_info *mtd, uint8_t *oob, size_t len, + 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_info *mtd, loff_t to, size_t len, + } + + /** ++ * 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 *mtd, loff_t to, size_t len, + } + + /** ++ * 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_info *mtd, loff_t to, + __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_info *mtd, loff_t to, + 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 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, + } + + /** ++ * 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 *mtd, struct erase_info *instr) + } + + /** ++ * 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_info *mtd, loff_t offs) + } + + /** ++ * 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_info *mtd, loff_t ofs) + } + + /** ++ * 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_info *mtd, struct nand_ecc_ctrl *ecc) + } + + /** ++ * 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) { +diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c +index 3803e0b..b82b976 100644 +--- 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_info *mtd, const unsigned char *buf, + 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_info *mtd, unsigned char *buf, + 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 */ +diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c +index 97c4c02..f35c418 100644 +--- 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 *mtd, const unsigned char *buf, + 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 *mtd, unsigned char *buf, + 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); + +diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h +index 3d4ea7e..510e09b 100644 +--- 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 + */ |