2014-02-11 21:47:07

by Boris BREZILLON

[permalink] [raw]
Subject: [RFC PATCH v2 0/4] mtd: nand: add per partition ECC config

Hello,

This patch series is a proposal to add per partition ECC config.

It defines a new partition type called nand_part which stores a pointer to
a nand_ecc_ctrl struct.
This specific nand_ecc_ctrl struct is used in place of the base NAND chip
nand_ecc_ctrl struct when accessing NAND chip from nand_part MTD device.

If the partition does not define any ECC config, the NAND chip ECC config
is used instead.

This patch series also provides an helper function to parse DT defined NAND
partitions (ofnandpart_parse).

If you want to test it you'll have to replace calls to
mtd_device_parse_register with ofnandpart_parse within your NAND controller
driver and implement a driver specific parser function that will provide
the ECC config (see ofnandpart_data struct).

The 4th patch of this series is here as an example on how to move from MTD
partitions to NAND partitions.

Best Regards,

Boris

Changes since v1:
- almost everything :-)

Boris BREZILLON (4):
mtd: nand: take nand_ecc_ctrl initialization out of nand_scan_tail
mtd: nand: add support for NAND partitions
mtd: nand: add DT NAND partition parser
mtd: nand: add NAND partition support to the sunxi driver

drivers/mtd/nand/Kconfig | 4 +
drivers/mtd/nand/Makefile | 2 +
drivers/mtd/nand/nand_base.c | 763 ++++++++++++++++++++++++++++++++---------
drivers/mtd/nand/ofnandpart.c | 104 ++++++
drivers/mtd/nand/sunxi_nand.c | 69 +++-
include/linux/mtd/nand.h | 54 +++
6 files changed, 835 insertions(+), 161 deletions(-)
create mode 100644 drivers/mtd/nand/ofnandpart.c

--
1.7.9.5


2014-02-11 21:47:18

by Boris BREZILLON

[permalink] [raw]
Subject: [RFC PATCH v2 1/4] mtd: nand: take nand_ecc_ctrl initialization out of nand_scan_tail

Take ECC initialization code portion out of nand_scan_tail so that we can
re-use this implementation.

Signed-off-by: Boris BREZILLON <[email protected]>
---
drivers/mtd/nand/nand_base.c | 72 +++++++++++++++++++++++++++---------------
1 file changed, 46 insertions(+), 26 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index f59a465..f2e9312 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3582,32 +3582,14 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
}
EXPORT_SYMBOL(nand_scan_ident);

-
-/**
- * nand_scan_tail - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- *
- * This is the second phase of the normal nand_scan() function. It fills out
- * all the uninitialized function pointers with the defaults and scans for a
- * bad block table if appropriate.
+/*
+ * Initialize ECC struct:
+ * - fill ECC struct with default function/values when these ones are undefined
+ * - fill ECC infos based on MTD device
*/
-int nand_scan_tail(struct mtd_info *mtd)
+static int nand_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc)
{
int i;
- struct nand_chip *chip = mtd->priv;
- struct nand_ecc_ctrl *ecc = &chip->ecc;
-
- /* New bad blocks should be marked in OOB, flash-based BBT, or both */
- BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
- !(chip->bbt_options & NAND_BBT_USE_FLASH));
-
- if (!(chip->options & NAND_OWN_BUFFERS))
- chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
- if (!chip->buffers)
- return -ENOMEM;
-
- /* Set the internal oob buffer location, just after the page data */
- chip->oob_poi = chip->buffers->databuf + mtd->writesize;

/*
* If no default placement scheme is given, select an appropriate one.
@@ -3633,9 +3615,6 @@ int nand_scan_tail(struct mtd_info *mtd)
}
}

- if (!chip->write_page)
- chip->write_page = nand_write_page;
-
/*
* Check ECC mode, default to software if 3byte/512byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC
@@ -3802,6 +3781,47 @@ int nand_scan_tail(struct mtd_info *mtd)
}
ecc->total = ecc->steps * ecc->bytes;

+ return 0;
+}
+
+/**
+ * nand_scan_tail - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ *
+ * This is the second phase of the normal nand_scan() function. It fills out
+ * all the uninitialized function pointers with the defaults and scans for a
+ * bad block table if appropriate.
+ */
+int nand_scan_tail(struct mtd_info *mtd)
+{
+ int ret;
+ struct nand_chip *chip = mtd->priv;
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ /* New bad blocks should be marked in OOB, flash-based BBT, or both */
+ BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
+ !(chip->bbt_options & NAND_BBT_USE_FLASH));
+
+ if (!(chip->options & NAND_OWN_BUFFERS))
+ chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
+ if (!chip->buffers)
+ return -ENOMEM;
+
+ /* Set the internal oob buffer location, just after the page data */
+ chip->oob_poi = chip->buffers->databuf + mtd->writesize;
+
+ if (!chip->write_page)
+ chip->write_page = nand_write_page;
+
+ /* Initialize ECC struct */
+ ret = nand_ecc_ctrl_init(mtd, ecc);
+ if (ret) {
+ if (!(chip->options & NAND_OWN_BUFFERS))
+ kfree(chip->buffers);
+
+ return ret;
+ }
+
/* 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) {
--
1.7.9.5

2014-02-11 21:47:31

by Boris BREZILLON

[permalink] [raw]
Subject: [RFC PATCH v2 2/4] mtd: nand: add support for NAND partitions

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 <[email protected]>
---
drivers/mtd/nand/Kconfig | 4 +
drivers/mtd/nand/Makefile | 2 +
drivers/mtd/nand/nand_base.c | 691 ++++++++++++++++++++++++++++++++++--------
include/linux/mtd/nand.h | 37 +++
4 files changed, 602 insertions(+), 132 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..b4f8268 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,153 @@ 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->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;
+
+ 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)
+ return -EINVAL;
+
+ 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 +4247,8 @@ int nand_scan_tail(struct mtd_info *mtd)
return ret;
}

+ 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/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

2014-02-11 21:47:36

by Boris BREZILLON

[permalink] [raw]
Subject: [RFC PATCH v2 4/4] mtd: nand: add NAND partition support to the sunxi driver

Signed-off-by: Boris BREZILLON <[email protected]>
---
drivers/mtd/nand/sunxi_nand.c | 69 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 66 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index f24ad8b..4693d9f 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -168,6 +168,17 @@ struct sunxi_nand_hw_ecc {
struct nand_ecclayout layout;
};

+struct sunxi_nand_part {
+ struct nand_part part;
+ struct nand_ecc_ctrl ecc;
+};
+
+static inline struct sunxi_nand_part *
+to_sunxi_nand_part(struct nand_part *part)
+{
+ return container_of(part, struct sunxi_nand_part, part);
+}
+
struct sunxi_nand_chip {
struct list_head node;
struct nand_chip nand;
@@ -780,12 +791,58 @@ static int sunxi_nand_chip_ecc_init(struct device *dev,
return 0;
}

+static void sunxi_nand_part_release(struct nand_part *part)
+{
+ kfree(to_sunxi_nand_part(part));
+}
+
+struct nand_part *sunxi_ofnandpart_parse(void *priv, struct mtd_info *master,
+ struct device_node *pp)
+{
+ int mode = of_get_nand_ecc_mode(pp);
+ struct sunxi_nand_part *part;
+ struct nand_chip *chip = master->priv;
+ u32 strength;
+ u32 blk_size;
+
+ if (mode < 0)
+ return nandpart_alloc();
+
+ part = kzalloc(sizeof(*part), GFP_KERNEL);
+ part->part.release = sunxi_nand_part_release;
+
+ part->ecc.size = chip->ecc_step_ds;
+ part->ecc.strength = chip->ecc_strength_ds;
+ if (!of_get_nand_ecc_level(pp, &strength, &blk_size)) {
+ part->ecc.size = blk_size;
+ part->ecc.strength = strength;
+ }
+
+ part->ecc.mode = mode;
+ switch (mode) {
+ case NAND_ECC_SOFT_BCH:
+ part->ecc.bytes = ((part->ecc.strength *
+ fls(8 * part->ecc.size)) + 7) / 8;
+ break;
+ case NAND_ECC_HW:
+ break;
+ case NAND_ECC_NONE:
+ break;
+ default:
+ break;
+ }
+
+ part->part.ecc = &part->ecc;
+
+ return &part->part;
+}
+
static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
struct device_node *np)
{
const struct nand_sdr_timings *timings;
struct sunxi_nand_chip *chip;
- struct mtd_part_parser_data ppdata;
+ struct ofnandpart_data ppdata;
struct mtd_info *mtd;
struct nand_chip *nand;
int nsels;
@@ -889,9 +946,15 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
mtd->name = chip->default_name;
}

- ppdata.of_node = np;
- ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ ppdata.node = np;
+ ppdata.parse = sunxi_ofnandpart_parse;
+ ret = ofnandpart_parse(mtd, &ppdata);
if (!ret)
+ ret = mtd_device_register(mtd, NULL, 0);
+ else if (ret > 0)
+ ret = 0;
+
+ if (ret)
return ret;

list_add_tail(&chip->node, &nfc->chips);
--
1.7.9.5

2014-02-11 21:48:05

by Boris BREZILLON

[permalink] [raw]
Subject: [RFC PATCH v2 3/4] mtd: nand: add DT NAND partition parser

Add ofnandpart_parse function to help parsing NAND partitions from DT.
This function should be called from NAND controller drivers just after the
nand_scan_tail in place of mtd_device_parse_register.
The caller can specify a parser function to retrieve HW specific
informations from the DT.

Signed-off-by: Boris BREZILLON <[email protected]>
---
drivers/mtd/nand/ofnandpart.c | 104 +++++++++++++++++++++++++++++++++++++++++
include/linux/mtd/nand.h | 17 +++++++
2 files changed, 121 insertions(+)
create mode 100644 drivers/mtd/nand/ofnandpart.c

diff --git a/drivers/mtd/nand/ofnandpart.c b/drivers/mtd/nand/ofnandpart.c
new file mode 100644
index 0000000..293daee
--- /dev/null
+++ b/drivers/mtd/nand/ofnandpart.c
@@ -0,0 +1,104 @@
+/*
+ * NAND Flash partitions described by the OF (or flattened) device tree
+ *
+ * Copyright © 2014 Boris BREZILLON <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+#include <linux/mtd/nand.h>
+
+static inline bool node_has_compatible(struct device_node *pp)
+{
+ return of_get_property(pp, "compatible", NULL);
+}
+
+int ofnandpart_parse(struct mtd_info *master,
+ const struct ofnandpart_data *data)
+{
+ struct device_node *node;
+ const char *partname;
+ struct device_node *pp;
+ int i;
+
+ if (!data)
+ return 0;
+
+ node = data->node;
+ if (!node)
+ return 0;
+
+ i = 0;
+ for_each_child_of_node(node, pp) {
+ const __be32 *reg;
+ int len;
+ int a_cells, s_cells;
+ uint64_t offset, size;
+ uint32_t mask_flags = 0;
+ struct nand_part *part;
+
+ if (node_has_compatible(pp))
+ continue;
+
+ reg = of_get_property(pp, "reg", &len);
+ if (!reg)
+ continue;
+
+ a_cells = of_n_addr_cells(pp);
+ s_cells = of_n_size_cells(pp);
+ offset = of_read_number(reg, a_cells);
+ size = of_read_number(reg + a_cells, s_cells);
+
+ partname = of_get_property(pp, "label", &len);
+ if (!partname)
+ partname = of_get_property(pp, "name", &len);
+
+ if (of_get_property(pp, "read-only", &len))
+ mask_flags |= MTD_WRITEABLE;
+
+ if (of_get_property(pp, "lock", &len))
+ mask_flags |= MTD_POWERUP_LOCK;
+
+ if (data->parse)
+ part = data->parse(data->priv, master, pp);
+ else
+ part = nandpart_alloc();
+
+ if (IS_ERR(part))
+ continue;
+
+ part->offset = offset;
+ part->master = master;
+ part->mtd.name = partname;
+ part->mtd.size = size;
+ part->mtd.flags = mask_flags;
+
+ if (nand_add_partition(master, part)) {
+ if (part->release)
+ part->release(part);
+ continue;
+ }
+
+ i++;
+ }
+
+ if (!i) {
+ of_node_put(pp);
+ pr_err("No valid partition found on %s\n", node->full_name);
+ }
+
+ return i;
+}
+EXPORT_SYMBOL(ofnandpart_parse);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parser for NAND flash partitioning information in device tree");
+MODULE_AUTHOR("Boris BREZILLON");
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index c4d271a..0c5890b 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -845,6 +845,23 @@ void nand_del_partition(struct nand_part *part);
struct nand_part *nandpart_alloc(void);

/**
+ * struct ofnandpart_data - struct used to retrieve NAND partitions from a DT
+ * node
+ * @parse: driver specific parser function
+ * @priv: driver private data
+ * @node: OF node containing NAND partitions
+ */
+struct ofnandpart_data {
+ struct nand_part *(*parse)(void *priv, struct mtd_info *master,
+ struct device_node *pp);
+ void *priv;
+ struct device_node *node;
+};
+
+int ofnandpart_parse(struct mtd_info *master,
+ const struct ofnandpart_data *data);
+
+/**
* struct nand_sdr_timings - SDR NAND chip timings
*
* This struct defines the timing requirements of a SDR NAND chip.
--
1.7.9.5

2014-02-12 14:38:42

by Boris BREZILLON

[permalink] [raw]
Subject: [RFC PATCH pre-v3 2/4] mtd: nand: add support for NAND partitions

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 <[email protected]>
---

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

2014-02-12 19:50:37

by Florian Fainelli

[permalink] [raw]
Subject: Re: [RFC PATCH v2 0/4] mtd: nand: add per partition ECC config

Hi Boris,

2014-02-11 13:46 GMT-08:00 Boris BREZILLON <[email protected]>:
> Hello,
>
> This patch series is a proposal to add per partition ECC config.
>
> It defines a new partition type called nand_part which stores a pointer to
> a nand_ecc_ctrl struct.
> This specific nand_ecc_ctrl struct is used in place of the base NAND chip
> nand_ecc_ctrl struct when accessing NAND chip from nand_part MTD device.
>
> If the partition does not define any ECC config, the NAND chip ECC config
> is used instead.

Although the idea does look nice on the paper, I wonder if it is
really useful to have that much complexity in the kernel. Most likely
what is happening is that your bootloader partition has a specific ECC
scheme due to the CPU bootrom or whatever early bootagent is running
on the system. When that is the case, this can be solved in user-space
by preparing full pages (main+spare) along with their specific ECC
requirements and use the MTD_FILE_MODE_RAW option on your specific MTD
partition.

You do not describe what is the end goal of this patchset, which might
help figuring out the potential other platforms requirements etc...

>
> This patch series also provides an helper function to parse DT defined NAND
> partitions (ofnandpart_parse).
>
> If you want to test it you'll have to replace calls to
> mtd_device_parse_register with ofnandpart_parse within your NAND controller
> driver and implement a driver specific parser function that will provide
> the ECC config (see ofnandpart_data struct).
>
> The 4th patch of this series is here as an example on how to move from MTD
> partitions to NAND partitions.
>
> Best Regards,
>
> Boris
>
> Changes since v1:
> - almost everything :-)
>
> Boris BREZILLON (4):
> mtd: nand: take nand_ecc_ctrl initialization out of nand_scan_tail
> mtd: nand: add support for NAND partitions
> mtd: nand: add DT NAND partition parser
> mtd: nand: add NAND partition support to the sunxi driver
>
> drivers/mtd/nand/Kconfig | 4 +
> drivers/mtd/nand/Makefile | 2 +
> drivers/mtd/nand/nand_base.c | 763 ++++++++++++++++++++++++++++++++---------
> drivers/mtd/nand/ofnandpart.c | 104 ++++++
> drivers/mtd/nand/sunxi_nand.c | 69 +++-
> include/linux/mtd/nand.h | 54 +++
> 6 files changed, 835 insertions(+), 161 deletions(-)
> create mode 100644 drivers/mtd/nand/ofnandpart.c
>
> --
> 1.7.9.5
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/



--
Florian

2014-02-12 21:20:59

by Boris BREZILLON

[permalink] [raw]
Subject: Re: [RFC PATCH v2 0/4] mtd: nand: add per partition ECC config

Hi Florian,

On 12/02/2014 20:49, Florian Fainelli wrote:
> Hi Boris,
>
> 2014-02-11 13:46 GMT-08:00 Boris BREZILLON <[email protected]>:
>> Hello,
>>
>> This patch series is a proposal to add per partition ECC config.
>>
>> It defines a new partition type called nand_part which stores a pointer to
>> a nand_ecc_ctrl struct.
>> This specific nand_ecc_ctrl struct is used in place of the base NAND chip
>> nand_ecc_ctrl struct when accessing NAND chip from nand_part MTD device.
>>
>> If the partition does not define any ECC config, the NAND chip ECC config
>> is used instead.
> Although the idea does look nice on the paper, I wonder if it is
> really useful to have that much complexity in the kernel. Most likely
> what is happening is that your bootloader partition has a specific ECC
> scheme due to the CPU bootrom or whatever early bootagent is running
> on the system. When that is the case, this can be solved in user-space
> by preparing full pages (main+spare) along with their specific ECC
> requirements and use the MTD_FILE_MODE_RAW option on your specific MTD
> partition.

Okay, that should be possible, althought in the sunxi specific case I
haven't
figured out how to generate these ECC data yet (even if it's using BCH
algorithms).

> You do not describe what is the end goal of this patchset, which might
> help figuring out the potential other platforms requirements etc...

My goal is exactly the one you described above, I just thought this would be
much easier to handle this within the kernel, without having to develop
extra
user space tools.
Moreover the user would be able to directly manipulate the data on the MTD
partitions.

This implementation is not that complex and should not impact performances
(it's just using cur_ecc pointer instead of intern ecc struct).

Most of the extra code introduced by this series come from the new
partition type
handling, which is quite straightforward BTW.

Anyway, I understand your concern. Could MTD maintainers (and NAND driver
developpers) give there opinion too ?

BTW, I'm also working on a data randomization layer (needed by some
MLC/TLC chips)
within the NAND core framework and I'm using the same per partition approach
(for the exact same reason: boot0 partition uses a randomizer seed that
might not fit
the NAND chip requirements).

Should I reconsider doing this too ?

Thanks for you comments.

Best Regards,

Boris
>
>> This patch series also provides an helper function to parse DT defined NAND
>> partitions (ofnandpart_parse).
>>
>> If you want to test it you'll have to replace calls to
>> mtd_device_parse_register with ofnandpart_parse within your NAND controller
>> driver and implement a driver specific parser function that will provide
>> the ECC config (see ofnandpart_data struct).
>>
>> The 4th patch of this series is here as an example on how to move from MTD
>> partitions to NAND partitions.
>>
>> Best Regards,
>>
>> Boris
>>
>> Changes since v1:
>> - almost everything :-)
>>
>> Boris BREZILLON (4):
>> mtd: nand: take nand_ecc_ctrl initialization out of nand_scan_tail
>> mtd: nand: add support for NAND partitions
>> mtd: nand: add DT NAND partition parser
>> mtd: nand: add NAND partition support to the sunxi driver
>>
>> drivers/mtd/nand/Kconfig | 4 +
>> drivers/mtd/nand/Makefile | 2 +
>> drivers/mtd/nand/nand_base.c | 763 ++++++++++++++++++++++++++++++++---------
>> drivers/mtd/nand/ofnandpart.c | 104 ++++++
>> drivers/mtd/nand/sunxi_nand.c | 69 +++-
>> include/linux/mtd/nand.h | 54 +++
>> 6 files changed, 835 insertions(+), 161 deletions(-)
>> create mode 100644 drivers/mtd/nand/ofnandpart.c
>>
>> --
>> 1.7.9.5
>>
>>
>> ______________________________________________________
>> Linux MTD discussion mailing list
>> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>
>

2014-02-12 21:40:15

by Boris BREZILLON

[permalink] [raw]
Subject: Re: [RFC PATCH v2 0/4] mtd: nand: add per partition ECC config

On 12/02/2014 22:20, Boris BREZILLON wrote:
> Hi Florian,
>
> On 12/02/2014 20:49, Florian Fainelli wrote:
>> Hi Boris,
>>
>> 2014-02-11 13:46 GMT-08:00 Boris BREZILLON <[email protected]>:
>>> Hello,
>>>
>>> This patch series is a proposal to add per partition ECC config.
>>>
>>> It defines a new partition type called nand_part which stores a
>>> pointer to
>>> a nand_ecc_ctrl struct.
>>> This specific nand_ecc_ctrl struct is used in place of the base NAND
>>> chip
>>> nand_ecc_ctrl struct when accessing NAND chip from nand_part MTD
>>> device.
>>>
>>> If the partition does not define any ECC config, the NAND chip ECC
>>> config
>>> is used instead.
>> Although the idea does look nice on the paper, I wonder if it is
>> really useful to have that much complexity in the kernel. Most likely
>> what is happening is that your bootloader partition has a specific ECC
>> scheme due to the CPU bootrom or whatever early bootagent is running
>> on the system. When that is the case, this can be solved in user-space
>> by preparing full pages (main+spare) along with their specific ECC
>> requirements and use the MTD_FILE_MODE_RAW option on your specific MTD
>> partition.
>
> Okay, that should be possible, althought in the sunxi specific case I
> haven't
> figured out how to generate these ECC data yet (even if it's using BCH
> algorithms).
>
>> You do not describe what is the end goal of this patchset, which might
>> help figuring out the potential other platforms requirements etc...
>

BTW, here is another case where per partition ECC would have been useful
:-) :
http://comments.gmane.org/gmane.linux.ports.arm.omap/108083

> My goal is exactly the one you described above, I just thought this
> would be
> much easier to handle this within the kernel, without having to
> develop extra
> user space tools.
> Moreover the user would be able to directly manipulate the data on the
> MTD
> partitions.
>
> This implementation is not that complex and should not impact
> performances
> (it's just using cur_ecc pointer instead of intern ecc struct).
>
> Most of the extra code introduced by this series come from the new
> partition type
> handling, which is quite straightforward BTW.
>
> Anyway, I understand your concern. Could MTD maintainers (and NAND driver
> developpers) give there opinion too ?
>
> BTW, I'm also working on a data randomization layer (needed by some
> MLC/TLC chips)
> within the NAND core framework and I'm using the same per partition
> approach
> (for the exact same reason: boot0 partition uses a randomizer seed
> that might not fit
> the NAND chip requirements).
>
> Should I reconsider doing this too ?
>
> Thanks for you comments.
>
> Best Regards,
>
> Boris
>>
>>> This patch series also provides an helper function to parse DT
>>> defined NAND
>>> partitions (ofnandpart_parse).
>>>
>>> If you want to test it you'll have to replace calls to
>>> mtd_device_parse_register with ofnandpart_parse within your NAND
>>> controller
>>> driver and implement a driver specific parser function that will
>>> provide
>>> the ECC config (see ofnandpart_data struct).
>>>
>>> The 4th patch of this series is here as an example on how to move
>>> from MTD
>>> partitions to NAND partitions.
>>>
>>> Best Regards,
>>>
>>> Boris
>>>
>>> Changes since v1:
>>> - almost everything :-)
>>>
>>> Boris BREZILLON (4):
>>> mtd: nand: take nand_ecc_ctrl initialization out of nand_scan_tail
>>> mtd: nand: add support for NAND partitions
>>> mtd: nand: add DT NAND partition parser
>>> mtd: nand: add NAND partition support to the sunxi driver
>>>
>>> drivers/mtd/nand/Kconfig | 4 +
>>> drivers/mtd/nand/Makefile | 2 +
>>> drivers/mtd/nand/nand_base.c | 763
>>> ++++++++++++++++++++++++++++++++---------
>>> drivers/mtd/nand/ofnandpart.c | 104 ++++++
>>> drivers/mtd/nand/sunxi_nand.c | 69 +++-
>>> include/linux/mtd/nand.h | 54 +++
>>> 6 files changed, 835 insertions(+), 161 deletions(-)
>>> create mode 100644 drivers/mtd/nand/ofnandpart.c
>>>
>>> --
>>> 1.7.9.5
>>>
>>>
>>> ______________________________________________________
>>> Linux MTD discussion mailing list
>>> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>>
>>
>

2014-02-12 22:44:28

by Florian Fainelli

[permalink] [raw]
Subject: Re: [RFC PATCH v2 0/4] mtd: nand: add per partition ECC config

2014-02-12 13:20 GMT-08:00 Boris BREZILLON <[email protected]>:
> Hi Florian,
>
>
> On 12/02/2014 20:49, Florian Fainelli wrote:
>>
>> Hi Boris,
>>
>> 2014-02-11 13:46 GMT-08:00 Boris BREZILLON <[email protected]>:
>>>
>>> Hello,
>>>
>>> This patch series is a proposal to add per partition ECC config.
>>>
>>> It defines a new partition type called nand_part which stores a pointer
>>> to
>>> a nand_ecc_ctrl struct.
>>> This specific nand_ecc_ctrl struct is used in place of the base NAND chip
>>> nand_ecc_ctrl struct when accessing NAND chip from nand_part MTD device.
>>>
>>> If the partition does not define any ECC config, the NAND chip ECC config
>>> is used instead.
>>
>> Although the idea does look nice on the paper, I wonder if it is
>> really useful to have that much complexity in the kernel. Most likely
>> what is happening is that your bootloader partition has a specific ECC
>> scheme due to the CPU bootrom or whatever early bootagent is running
>> on the system. When that is the case, this can be solved in user-space
>> by preparing full pages (main+spare) along with their specific ECC
>> requirements and use the MTD_FILE_MODE_RAW option on your specific MTD
>> partition.
>
>
> Okay, that should be possible, althought in the sunxi specific case I
> haven't
> figured out how to generate these ECC data yet (even if it's using BCH
> algorithms).
>
>
>> You do not describe what is the end goal of this patchset, which might
>> help figuring out the potential other platforms requirements etc...
>
>
> My goal is exactly the one you described above, I just thought this would be
> much easier to handle this within the kernel, without having to develop
> extra
> user space tools.

Each platform will have different requirements which are imho better
solved at the user-space level (bad block marker placement, ECC
scheme, number of times the bootloader must be replicated,
cryptography, signing, proprietary OOBs and whatnot). The situation is
probably made even worse with platforms implementing a trusted boot
scheme where you may have to modify your boot program for whatever
reason before writing it down again.

My point is that, an user-space solution already exists, and the
bootloaders are significantly smaller and not read/written often
enough to justify leveraging HW ECC for instance, which could be one
advantage of the per-partition ECC scheme.

>
> This implementation is not that complex and should not impact performances
> (it's just using cur_ecc pointer instead of intern ecc struct).

How about reading/erasing/writing concurrently (as in having two
user-space applications), is not that going to incur some sort of "ECC
context switching" cost? I am also not sure if all NAND controllers
will behave (they should) correctly when you switch from one ECC
scheme to another on the fly like this could be happening.

>
> Most of the extra code introduced by this series come from the new partition
> type
> handling, which is quite straightforward BTW.
>
> Anyway, I understand your concern. Could MTD maintainers (and NAND driver
> developpers) give there opinion too ?
>
> BTW, I'm also working on a data randomization layer (needed by some MLC/TLC
> chips)
> within the NAND core framework and I'm using the same per partition approach
> (for the exact same reason: boot0 partition uses a randomizer seed that
> might not fit
> the NAND chip requirements).
>
> Should I reconsider doing this too ?
>
> Thanks for you comments.
>
> Best Regards,
>
> Boris
>
>>
>>> This patch series also provides an helper function to parse DT defined
>>> NAND
>>> partitions (ofnandpart_parse).
>>>
>>> If you want to test it you'll have to replace calls to
>>> mtd_device_parse_register with ofnandpart_parse within your NAND
>>> controller
>>> driver and implement a driver specific parser function that will provide
>>> the ECC config (see ofnandpart_data struct).
>>>
>>> The 4th patch of this series is here as an example on how to move from
>>> MTD
>>> partitions to NAND partitions.
>>>
>>> Best Regards,
>>>
>>> Boris
>>>
>>> Changes since v1:
>>> - almost everything :-)
>>>
>>> Boris BREZILLON (4):
>>> mtd: nand: take nand_ecc_ctrl initialization out of nand_scan_tail
>>> mtd: nand: add support for NAND partitions
>>> mtd: nand: add DT NAND partition parser
>>> mtd: nand: add NAND partition support to the sunxi driver
>>>
>>> drivers/mtd/nand/Kconfig | 4 +
>>> drivers/mtd/nand/Makefile | 2 +
>>> drivers/mtd/nand/nand_base.c | 763
>>> ++++++++++++++++++++++++++++++++---------
>>> drivers/mtd/nand/ofnandpart.c | 104 ++++++
>>> drivers/mtd/nand/sunxi_nand.c | 69 +++-
>>> include/linux/mtd/nand.h | 54 +++
>>> 6 files changed, 835 insertions(+), 161 deletions(-)
>>> create mode 100644 drivers/mtd/nand/ofnandpart.c
>>>
>>> --
>>> 1.7.9.5
>>>
>>>
>>> ______________________________________________________
>>> Linux MTD discussion mailing list
>>> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>>
>>
>>
>



--
Florian

2014-02-13 09:06:13

by Boris BREZILLON

[permalink] [raw]
Subject: Re: [RFC PATCH v2 0/4] mtd: nand: add per partition ECC config

On 12/02/2014 23:43, Florian Fainelli wrote:
> 2014-02-12 13:20 GMT-08:00 Boris BREZILLON <[email protected]>:
>> Hi Florian,
>>
>>
>> On 12/02/2014 20:49, Florian Fainelli wrote:
>>> Hi Boris,
>>>
>>> 2014-02-11 13:46 GMT-08:00 Boris BREZILLON <[email protected]>:
>>>> Hello,
>>>>
>>>> This patch series is a proposal to add per partition ECC config.
>>>>
>>>> It defines a new partition type called nand_part which stores a pointer
>>>> to
>>>> a nand_ecc_ctrl struct.
>>>> This specific nand_ecc_ctrl struct is used in place of the base NAND chip
>>>> nand_ecc_ctrl struct when accessing NAND chip from nand_part MTD device.
>>>>
>>>> If the partition does not define any ECC config, the NAND chip ECC config
>>>> is used instead.
>>> Although the idea does look nice on the paper, I wonder if it is
>>> really useful to have that much complexity in the kernel. Most likely
>>> what is happening is that your bootloader partition has a specific ECC
>>> scheme due to the CPU bootrom or whatever early bootagent is running
>>> on the system. When that is the case, this can be solved in user-space
>>> by preparing full pages (main+spare) along with their specific ECC
>>> requirements and use the MTD_FILE_MODE_RAW option on your specific MTD
>>> partition.
>>
>> Okay, that should be possible, althought in the sunxi specific case I
>> haven't
>> figured out how to generate these ECC data yet (even if it's using BCH
>> algorithms).
>>
>>
>>> You do not describe what is the end goal of this patchset, which might
>>> help figuring out the potential other platforms requirements etc...
>>
>> My goal is exactly the one you described above, I just thought this would be
>> much easier to handle this within the kernel, without having to develop
>> extra
>> user space tools.
> Each platform will have different requirements which are imho better
> solved at the user-space level (bad block marker placement, ECC
> scheme, number of times the bootloader must be replicated,
> cryptography, signing, proprietary OOBs and whatnot). The situation is
> probably made even worse with platforms implementing a trusted boot
> scheme where you may have to modify your boot program for whatever
> reason before writing it down again.

On the other hand, most of the time the NAND controller drivers
already support all these specific ECC algorithms and OOB layouts
(I'm not talking about crypto, signing or trusted boot), and would be
able to expose these "weird" partitions to user-space with almost no
extra development cost.

>
> My point is that, an user-space solution already exists, and the
> bootloaders are significantly smaller and not read/written often
> enough to justify leveraging HW ECC for instance, which could be one
> advantage of the per-partition ECC scheme.

AFAICT, there's no such tools for the sunxi platform (but I might be
wrong).


>
>> This implementation is not that complex and should not impact performances
>> (it's just using cur_ecc pointer instead of intern ecc struct).
> How about reading/erasing/writing concurrently (as in having two
> user-space applications), is not that going to incur some sort of "ECC
> context switching" cost?

I can only talk for the sunxi NAND driver, and AFAIK, for this driver,
switching from one ECC config to another shouldn't imply any performance
cost.

I'll do some measurements to validate what I'm saying.

> I am also not sure if all NAND controllers
> will behave (they should) correctly when you switch from one ECC
> scheme to another on the fly like this could be happening.

The decision to use per-partition ECC config is left to the NAND controller
driver, thus, if a driver does not support ECC config switching (or if
this implies
too much performance cost) it can just use the standard
mtd_device_parse_register or assign part's ecc pointer to NULL.

I can even go further and take the nand_part code out of nand_base.c to make
it optional (I would just need to export some NAND core functions).
The same goes for the NAND partition list and list lock stored in the
nand_chip
struct: we can make it optional depending on a NAND_PART Kconfig option.

As you can see, I'm not giving up on this feature :-), because this
would help
me (and hopefully other users too) a lot to have access to these
specific partition
without any user space converters.

Best Regards,

Boris
>> Most of the extra code introduced by this series come from the new partition
>> type
>> handling, which is quite straightforward BTW.
>>
>> Anyway, I understand your concern. Could MTD maintainers (and NAND driver
>> developpers) give there opinion too ?
>>
>> BTW, I'm also working on a data randomization layer (needed by some MLC/TLC
>> chips)
>> within the NAND core framework and I'm using the same per partition approach
>> (for the exact same reason: boot0 partition uses a randomizer seed that
>> might not fit
>> the NAND chip requirements).
>>
>> Should I reconsider doing this too ?
>>
>> Thanks for you comments.
>>
>> Best Regards,
>>
>> Boris
>>
>>>> This patch series also provides an helper function to parse DT defined
>>>> NAND
>>>> partitions (ofnandpart_parse).
>>>>
>>>> If you want to test it you'll have to replace calls to
>>>> mtd_device_parse_register with ofnandpart_parse within your NAND
>>>> controller
>>>> driver and implement a driver specific parser function that will provide
>>>> the ECC config (see ofnandpart_data struct).
>>>>
>>>> The 4th patch of this series is here as an example on how to move from
>>>> MTD
>>>> partitions to NAND partitions.
>>>>
>>>> Best Regards,
>>>>
>>>> Boris
>>>>
>>>> Changes since v1:
>>>> - almost everything :-)
>>>>
>>>> Boris BREZILLON (4):
>>>> mtd: nand: take nand_ecc_ctrl initialization out of nand_scan_tail
>>>> mtd: nand: add support for NAND partitions
>>>> mtd: nand: add DT NAND partition parser
>>>> mtd: nand: add NAND partition support to the sunxi driver
>>>>
>>>> drivers/mtd/nand/Kconfig | 4 +
>>>> drivers/mtd/nand/Makefile | 2 +
>>>> drivers/mtd/nand/nand_base.c | 763
>>>> ++++++++++++++++++++++++++++++++---------
>>>> drivers/mtd/nand/ofnandpart.c | 104 ++++++
>>>> drivers/mtd/nand/sunxi_nand.c | 69 +++-
>>>> include/linux/mtd/nand.h | 54 +++
>>>> 6 files changed, 835 insertions(+), 161 deletions(-)
>>>> create mode 100644 drivers/mtd/nand/ofnandpart.c
>>>>
>>>> --
>>>> 1.7.9.5
>>>>
>>>>
>>>> ______________________________________________________
>>>> Linux MTD discussion mailing list
>>>> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>>>
>>>
>
>