aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/sunxi/patches-4.1/111-mtd-add-support-for-nand-partitions.patch
diff options
context:
space:
mode:
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.patch1295
1 files changed, 1295 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..d4eaeec
--- /dev/null
+++ b/target/linux/sunxi/patches-4.1/111-mtd-add-support-for-nand-partitions.patch
@@ -0,0 +1,1295 @@
+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(-)
+
+--- 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
+ */