All of lore.kernel.org
 help / color / mirror / Atom feed
From: Boris BREZILLON <b.brezillon.dev@gmail.com>
To: David Woodhouse <dwmw2@infradead.org>,
	Brian Norris <computersforpeace@gmail.com>
Cc: Boris BREZILLON <b.brezillon.dev@gmail.com>,
	linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org,
	Ezequiel Garcia <ezequiel.garcia@free-electrons.com>,
	Gupta Pekon <pekon@ti.com>
Subject: [RFC PATCH pre-v3 2/4] mtd: nand: add support for NAND partitions
Date: Wed, 12 Feb 2014 15:38:34 +0100	[thread overview]
Message-ID: <1392215914-23347-1-git-send-email-b.brezillon.dev@gmail.com> (raw)
In-Reply-To: <1392155209-14495-1-git-send-email-b.brezillon.dev@gmail.com>

Add support for NAND partitions, and inderectly for per partition ECC
config.
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 <b.brezillon.dev@gmail.com>
---

Hello,

Sorry for the noise: this version adds mutex_lock and partition initialization
code which somehow were missing from my commit but were present in my source
tree :-).

It also fixes a bug when using software ECC on a given partition.

Best Regards,

Boris

Changes since v2:
- add missing mutex_lock in nand_add_partition
- add missing partition list and lock initilization
- make use of cur_ecc pointer instead of ecc in nand_ecc and nand_bch code

 drivers/mtd/nand/Kconfig     |    4 +
 drivers/mtd/nand/Makefile    |    2 +
 drivers/mtd/nand/nand_base.c |  698 ++++++++++++++++++++++++++++++++++--------
 drivers/mtd/nand/nand_bch.c  |   16 +-
 drivers/mtd/nand/nand_ecc.c  |    4 +-
 include/linux/mtd/nand.h     |   37 +++
 6 files changed, 619 insertions(+), 142 deletions(-)

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 784dd42..aa62ca3 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 e3b4a34..992de88 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -51,4 +51,6 @@ obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
 obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_nand.o
 
+obj-$(CONFIG_MTD_OF_NAND_PARTS)		+= ofnandpart.o
+
 nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index f2e9312..9622b20 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1031,26 +1031,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;
 		}
 	}
 
@@ -1072,30 +1072,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 {
@@ -1118,7 +1119,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
 			uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
 {
 	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;
@@ -1127,15 +1128,15 @@ 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;
 
 	/* 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);
@@ -1144,16 +1145,17 @@ 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
 	 * ecc.pos. Let's make sure that there are no gaps in ECC positions.
 	 */
 	for (i = 0; i < eccfrag_len - 1; i++) {
-		if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
-			eccpos[i + start_step * chip->ecc.bytes + 1]) {
+		if (eccpos[i + start_step * chip->cur_ecc->bytes] + 1 !=
+			eccpos[i + start_step * chip->cur_ecc->bytes + 1]) {
 			gaps = 1;
 			break;
 		}
@@ -1166,13 +1168,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
 		 * Send the command to read the particular ECC bytes take care
 		 * about buswidth alignment in read_buf.
 		 */
-		index = start_step * chip->ecc.bytes;
+		index = start_step * chip->cur_ecc->bytes;
 
 		aligned_pos = eccpos[index] & ~(busw - 1);
 		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,
@@ -1184,11 +1187,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 {
@@ -1212,32 +1217,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 {
@@ -1265,12 +1271,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;
 
@@ -1279,17 +1285,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 {
@@ -1314,9 +1320,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;
@@ -1324,17 +1330,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++;
@@ -1345,9 +1351,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;
 		}
 	}
 
@@ -1377,7 +1383,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;
 
@@ -1459,16 +1465,20 @@ 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);
+				ret = chip->cur_ecc->read_subpage(mtd, chip,
+								  col, bytes,
+								  bufpoi);
 			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 (!aligned)
 					/* Invalidate page cache */
@@ -1579,6 +1589,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(mtd, 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(mtd, from, &ops);
+	*retlen = ops.retlen;
+	chip->cur_ecc = &chip->ecc;
+	nand_release_device(mtd);
+	return ret;
+}
+
+/**
  * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function
  * @mtd: mtd info structure
  * @chip: nand chip info structure
@@ -1604,13 +1647,14 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
 {
 	uint8_t *buf = chip->oob_poi;
 	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 = buf;
 	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)
@@ -1663,9 +1707,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;
 
 	/*
@@ -1673,7 +1718,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
@@ -1737,7 +1782,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;
 
@@ -1765,9 +1810,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;
@@ -1855,6 +1900,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(mtd, 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(mtd, from, ops);
+	else
+		ret = nand_do_read_ops(mtd, from, ops);
+
+out:
+	chip->cur_ecc = &chip->ecc;
+	nand_release_device(mtd);
+	return ret;
+}
+
 
 /**
  * nand_write_page_raw - [INTERN] raw page write function
@@ -1888,26 +1983,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;
 		}
 	}
 
@@ -1927,21 +2022,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);
 }
 
 /**
@@ -1954,20 +2049,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);
@@ -1992,10 +2087,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;
@@ -2003,7 +2098,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);
@@ -2012,7 +2107,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 */
@@ -2027,7 +2122,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 */
@@ -2051,29 +2146,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;
 		}
 	}
 
@@ -2104,7 +2199,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;
@@ -2112,13 +2207,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;
@@ -2177,7 +2274,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;
 
@@ -2361,6 +2458,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(mtd, chip, 400);
+
+	/* Grab the device */
+	panic_nand_get_device(chip, mtd, 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(mtd, 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
@@ -2388,6 +2525,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(mtd, 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(mtd, to, &ops);
+	*retlen = ops.retlen;
+	chip->cur_ecc = &chip->ecc;
+	nand_release_device(mtd);
+	return ret;
+}
+
+/**
  * nand_do_write_oob - [MTD Interface] NAND write out-of-band
  * @mtd: MTD device structure
  * @to: offset to write to
@@ -2405,7 +2575,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;
 
@@ -2459,9 +2629,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);
 
@@ -2516,6 +2688,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(mtd, 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(mtd, to, ops);
+	else
+		ret = nand_do_write_ops(mtd, to, ops);
+
+out:
+	chip->cur_ecc = &chip->ecc;
+	nand_release_device(mtd);
+	return ret;
+}
+
+/**
  * single_erase_cmd - [GENERIC] NAND standard block erase command function
  * @mtd: MTD device structure
  * @page: the page address of the block which will be erased
@@ -2543,6 +2763,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(mtd, 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
@@ -2686,6 +2929,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(mtd, 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
@@ -2706,6 +2961,29 @@ 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(mtd, ofs);
+	if (ret) {
+		/* If it was bad already, return success and do nothing */
+		if (ret > 0)
+			return 0;
+		return ret;
+	}
+
+	return nand_block_markbad_lowlevel(mtd, ofs);
+}
+
+/**
  * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
  * @mtd: MTD device structure
  * @chip: nand chip info structure
@@ -3785,6 +4063,157 @@ 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;
+	}
+
+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
  *
@@ -3822,6 +4251,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 053c9a2..520bef5 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 c70e0a3..c4d271a 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -574,6 +574,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;
 
@@ -583,9 +584,40 @@ 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);
+}
+
 /*
  * NAND Flash Manufacturer ID Codes
  */
@@ -806,6 +838,11 @@ static inline bool nand_is_slc(struct nand_chip *chip)
 	return chip->bits_per_cell == 1;
 }
 
+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);
 
 /**
  * struct nand_sdr_timings - SDR NAND chip timings
-- 
1.7.9.5

WARNING: multiple messages have this Message-ID (diff)
From: Boris BREZILLON <b.brezillon.dev@gmail.com>
To: David Woodhouse <dwmw2@infradead.org>,
	Brian Norris <computersforpeace@gmail.com>
Cc: linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org,
	Gupta Pekon <pekon@ti.com>,
	Ezequiel Garcia <ezequiel.garcia@free-electrons.com>,
	Boris BREZILLON <b.brezillon.dev@gmail.com>
Subject: [RFC PATCH pre-v3 2/4] mtd: nand: add support for NAND partitions
Date: Wed, 12 Feb 2014 15:38:34 +0100	[thread overview]
Message-ID: <1392215914-23347-1-git-send-email-b.brezillon.dev@gmail.com> (raw)
In-Reply-To: <1392155209-14495-1-git-send-email-b.brezillon.dev@gmail.com>

Add support for NAND partitions, and inderectly for per partition ECC
config.
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 <b.brezillon.dev@gmail.com>
---

Hello,

Sorry for the noise: this version adds mutex_lock and partition initialization
code which somehow were missing from my commit but were present in my source
tree :-).

It also fixes a bug when using software ECC on a given partition.

Best Regards,

Boris

Changes since v2:
- add missing mutex_lock in nand_add_partition
- add missing partition list and lock initilization
- make use of cur_ecc pointer instead of ecc in nand_ecc and nand_bch code

 drivers/mtd/nand/Kconfig     |    4 +
 drivers/mtd/nand/Makefile    |    2 +
 drivers/mtd/nand/nand_base.c |  698 ++++++++++++++++++++++++++++++++++--------
 drivers/mtd/nand/nand_bch.c  |   16 +-
 drivers/mtd/nand/nand_ecc.c  |    4 +-
 include/linux/mtd/nand.h     |   37 +++
 6 files changed, 619 insertions(+), 142 deletions(-)

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 784dd42..aa62ca3 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 e3b4a34..992de88 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -51,4 +51,6 @@ obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
 obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_nand.o
 
+obj-$(CONFIG_MTD_OF_NAND_PARTS)		+= ofnandpart.o
+
 nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index f2e9312..9622b20 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1031,26 +1031,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;
 		}
 	}
 
@@ -1072,30 +1072,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 {
@@ -1118,7 +1119,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
 			uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
 {
 	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;
@@ -1127,15 +1128,15 @@ 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;
 
 	/* 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);
@@ -1144,16 +1145,17 @@ 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
 	 * ecc.pos. Let's make sure that there are no gaps in ECC positions.
 	 */
 	for (i = 0; i < eccfrag_len - 1; i++) {
-		if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
-			eccpos[i + start_step * chip->ecc.bytes + 1]) {
+		if (eccpos[i + start_step * chip->cur_ecc->bytes] + 1 !=
+			eccpos[i + start_step * chip->cur_ecc->bytes + 1]) {
 			gaps = 1;
 			break;
 		}
@@ -1166,13 +1168,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
 		 * Send the command to read the particular ECC bytes take care
 		 * about buswidth alignment in read_buf.
 		 */
-		index = start_step * chip->ecc.bytes;
+		index = start_step * chip->cur_ecc->bytes;
 
 		aligned_pos = eccpos[index] & ~(busw - 1);
 		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,
@@ -1184,11 +1187,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 {
@@ -1212,32 +1217,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 {
@@ -1265,12 +1271,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;
 
@@ -1279,17 +1285,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 {
@@ -1314,9 +1320,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;
@@ -1324,17 +1330,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++;
@@ -1345,9 +1351,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;
 		}
 	}
 
@@ -1377,7 +1383,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;
 
@@ -1459,16 +1465,20 @@ 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);
+				ret = chip->cur_ecc->read_subpage(mtd, chip,
+								  col, bytes,
+								  bufpoi);
 			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 (!aligned)
 					/* Invalidate page cache */
@@ -1579,6 +1589,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(mtd, 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(mtd, from, &ops);
+	*retlen = ops.retlen;
+	chip->cur_ecc = &chip->ecc;
+	nand_release_device(mtd);
+	return ret;
+}
+
+/**
  * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function
  * @mtd: mtd info structure
  * @chip: nand chip info structure
@@ -1604,13 +1647,14 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
 {
 	uint8_t *buf = chip->oob_poi;
 	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 = buf;
 	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)
@@ -1663,9 +1707,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;
 
 	/*
@@ -1673,7 +1718,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
@@ -1737,7 +1782,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;
 
@@ -1765,9 +1810,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;
@@ -1855,6 +1900,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(mtd, 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(mtd, from, ops);
+	else
+		ret = nand_do_read_ops(mtd, from, ops);
+
+out:
+	chip->cur_ecc = &chip->ecc;
+	nand_release_device(mtd);
+	return ret;
+}
+
 
 /**
  * nand_write_page_raw - [INTERN] raw page write function
@@ -1888,26 +1983,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;
 		}
 	}
 
@@ -1927,21 +2022,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);
 }
 
 /**
@@ -1954,20 +2049,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);
@@ -1992,10 +2087,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;
@@ -2003,7 +2098,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);
@@ -2012,7 +2107,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 */
@@ -2027,7 +2122,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 */
@@ -2051,29 +2146,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;
 		}
 	}
 
@@ -2104,7 +2199,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;
@@ -2112,13 +2207,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;
@@ -2177,7 +2274,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;
 
@@ -2361,6 +2458,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(mtd, chip, 400);
+
+	/* Grab the device */
+	panic_nand_get_device(chip, mtd, 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(mtd, 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
@@ -2388,6 +2525,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(mtd, 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(mtd, to, &ops);
+	*retlen = ops.retlen;
+	chip->cur_ecc = &chip->ecc;
+	nand_release_device(mtd);
+	return ret;
+}
+
+/**
  * nand_do_write_oob - [MTD Interface] NAND write out-of-band
  * @mtd: MTD device structure
  * @to: offset to write to
@@ -2405,7 +2575,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;
 
@@ -2459,9 +2629,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);
 
@@ -2516,6 +2688,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(mtd, 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(mtd, to, ops);
+	else
+		ret = nand_do_write_ops(mtd, to, ops);
+
+out:
+	chip->cur_ecc = &chip->ecc;
+	nand_release_device(mtd);
+	return ret;
+}
+
+/**
  * single_erase_cmd - [GENERIC] NAND standard block erase command function
  * @mtd: MTD device structure
  * @page: the page address of the block which will be erased
@@ -2543,6 +2763,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(mtd, 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
@@ -2686,6 +2929,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(mtd, 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
@@ -2706,6 +2961,29 @@ 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(mtd, ofs);
+	if (ret) {
+		/* If it was bad already, return success and do nothing */
+		if (ret > 0)
+			return 0;
+		return ret;
+	}
+
+	return nand_block_markbad_lowlevel(mtd, ofs);
+}
+
+/**
  * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
  * @mtd: MTD device structure
  * @chip: nand chip info structure
@@ -3785,6 +4063,157 @@ 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;
+	}
+
+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
  *
@@ -3822,6 +4251,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 053c9a2..520bef5 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 c70e0a3..c4d271a 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -574,6 +574,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;
 
@@ -583,9 +584,40 @@ 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);
+}
+
 /*
  * NAND Flash Manufacturer ID Codes
  */
@@ -806,6 +838,11 @@ static inline bool nand_is_slc(struct nand_chip *chip)
 	return chip->bits_per_cell == 1;
 }
 
+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);
 
 /**
  * struct nand_sdr_timings - SDR NAND chip timings
-- 
1.7.9.5


  parent reply	other threads:[~2014-02-12 14:39 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-02-11 21:46 [RFC PATCH v2 0/4] mtd: nand: add per partition ECC config Boris BREZILLON
2014-02-11 21:46 ` Boris BREZILLON
2014-02-11 21:46 ` [RFC PATCH v2 1/4] mtd: nand: take nand_ecc_ctrl initialization out of nand_scan_tail Boris BREZILLON
2014-02-11 21:46   ` Boris BREZILLON
2014-02-11 21:46 ` [RFC PATCH v2 2/4] mtd: nand: add support for NAND partitions Boris BREZILLON
2014-02-11 21:46   ` Boris BREZILLON
2014-02-11 21:46 ` [RFC PATCH v2 3/4] mtd: nand: add DT NAND partition parser Boris BREZILLON
2014-02-11 21:46   ` Boris BREZILLON
2014-02-11 21:46 ` [RFC PATCH v2 4/4] mtd: nand: add NAND partition support to the sunxi driver Boris BREZILLON
2014-02-11 21:46   ` Boris BREZILLON
2014-02-12 14:38 ` Boris BREZILLON [this message]
2014-02-12 14:38   ` [RFC PATCH pre-v3 2/4] mtd: nand: add support for NAND partitions Boris BREZILLON
2014-02-12 19:49 ` [RFC PATCH v2 0/4] mtd: nand: add per partition ECC config Florian Fainelli
2014-02-12 19:49   ` Florian Fainelli
2014-02-12 21:20   ` Boris BREZILLON
2014-02-12 21:20     ` Boris BREZILLON
2014-02-12 21:40     ` Boris BREZILLON
2014-02-12 21:40       ` Boris BREZILLON
2014-02-12 22:43     ` Florian Fainelli
2014-02-12 22:43       ` Florian Fainelli
2014-02-13  9:06       ` Boris BREZILLON
2014-02-13  9:06         ` Boris BREZILLON

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1392215914-23347-1-git-send-email-b.brezillon.dev@gmail.com \
    --to=b.brezillon.dev@gmail.com \
    --cc=computersforpeace@gmail.com \
    --cc=dwmw2@infradead.org \
    --cc=ezequiel.garcia@free-electrons.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mtd@lists.infradead.org \
    --cc=pekon@ti.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.