Re: [RFC PATCH] mtd: add per NAND partition ECC config

From: Boris BREZILLON
Date: Mon Feb 10 2014 - 13:12:01 EST


On 08/02/2014 11:26, Boris BREZILLON wrote:
This patch aims to add per partition ECC config for NAND devices.
It defines a new field in the mtd struct to store the mtd ECC config and
thus each mtd partition device can store its config instead of using the
default NAND chip config.

This feature is needed to support the sunxi boot0 paritition case:
Allwinner boot code (BROM) requires a specific HW ECC for its boot code
that may not fit the HW NAND requirements for the entire NAND chip.

Signed-off-by: Boris BREZILLON <b.brezillon.dev@xxxxxxxxx>
---
Hello,

This patch is just a draft that implement per partition ECC config.
It's currently not properly splitted (it should be separated in several
patches) and not documented either.

There's at least one point that bother me in the current implementation:
I introduced DT notions in the nand core code by the mean of the get_ecc_ctrl
callback, and so far this was kept out of mtd/nand core code (I guess it was
on purpose).

Please let me know if you see other drawbacks.

If you think per partition ECC should not be implemented, could you help me
find a way to handle sunxi specific case decribed above ?

My bad, this does not work properly: the eccctrl defined in mtd slaves are never used
because the mtd part_xx functions pass the master mtd struct to the NAND framework.

Best Regards,

Boris




drivers/mtd/mtdpart.c | 23 ++-
drivers/mtd/nand/nand_base.c | 428 ++++++++++++++++++++++++----------------
drivers/mtd/ofpart.c | 35 ++++
include/linux/mtd/mtd.h | 3 +
include/linux/mtd/nand.h | 12 ++
include/linux/mtd/partitions.h | 1 +
6 files changed, 332 insertions(+), 170 deletions(-)

diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 6e732c3..a5e262a 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -28,6 +28,7 @@
#include <linux/list.h>
#include <linux/kmod.h>
#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/err.h>
@@ -310,6 +311,8 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
static inline void free_partition(struct mtd_part *p)
{
kfree(p->mtd.name);
+ if (p->mtd.eccctrl && p->mtd.eccctrl->release)
+ p->mtd.eccctrl->release(p->mtd.eccctrl);
kfree(p);
}
@@ -364,7 +367,13 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
slave->mtd.writesize = master->writesize;
slave->mtd.writebufsize = master->writebufsize;
slave->mtd.oobsize = master->oobsize;
- slave->mtd.oobavail = master->oobavail;
+ if (part->eccctrl) {
+ slave->mtd.eccctrl = part->eccctrl;
+ slave->mtd.oobavail = part->eccctrl->layout->oobavail;
+ } else {
+ slave->mtd.eccctrl = master->eccctrl;
+ slave->mtd.oobavail = master->oobavail;
+ }
slave->mtd.subpage_sft = master->subpage_sft;
slave->mtd.name = name;
@@ -515,9 +524,15 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
part->name);
}
- slave->mtd.ecclayout = master->ecclayout;
- slave->mtd.ecc_step_size = master->ecc_step_size;
- slave->mtd.ecc_strength = master->ecc_strength;
+ if (part->eccctrl) {
+ slave->mtd.ecclayout = part->eccctrl->layout;
+ slave->mtd.ecc_step_size = part->eccctrl->size;
+ slave->mtd.ecc_strength = part->eccctrl->strength;
+ } else {
+ slave->mtd.ecclayout = master->ecclayout;
+ slave->mtd.ecc_step_size = master->ecc_step_size;
+ slave->mtd.ecc_strength = master->ecc_strength;
+ }
slave->mtd.bitflip_threshold = master->bitflip_threshold;
if (master->_block_isbad) {
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index f59a465..24c1571 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -45,6 +45,7 @@
#include <linux/leds.h>
#include <linux/io.h>
#include <linux/mtd/partitions.h>
+#include <linux/of_mtd.h>
/* Define default oob placement schemes for large and small page devices */
static struct nand_ecclayout nand_oob_8 = {
@@ -1031,26 +1032,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 = mtd->eccctrl->size;
+ int eccbytes = mtd->eccctrl->bytes;
uint8_t *oob = chip->oob_poi;
int steps, size;
- for (steps = chip->ecc.steps; steps > 0; steps--) {
+ for (steps = mtd->eccctrl->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 (mtd->eccctrl->prepad) {
+ chip->read_buf(mtd, oob, mtd->eccctrl->prepad);
+ oob += mtd->eccctrl->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 (mtd->eccctrl->postpad) {
+ chip->read_buf(mtd, oob, mtd->eccctrl->postpad);
+ oob += mtd->eccctrl->postpad;
}
}
@@ -1072,30 +1073,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 = mtd->eccctrl->size;
+ int eccbytes = mtd->eccctrl->bytes;
+ int eccsteps = mtd->eccctrl->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 = mtd->eccctrl->layout->eccpos;
unsigned int max_bitflips = 0;
- chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
+ mtd->eccctrl->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]);
+ mtd->eccctrl->calculate(mtd, p, &ecc_calc[i]);
- for (i = 0; i < chip->ecc.total; i++)
+ for (i = 0; i < mtd->eccctrl->total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
- eccsteps = chip->ecc.steps;
+ eccsteps = mtd->eccctrl->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 = mtd->eccctrl->correct(mtd, p, &ecc_code[i],
+ &ecc_calc[i]);
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
@@ -1118,7 +1120,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 = mtd->eccctrl->layout->eccpos;
uint8_t *p;
int data_col_addr, i, gaps = 0;
int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
@@ -1127,15 +1129,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 / mtd->eccctrl->size;
+ end_step = (data_offs + readlen - 1) / mtd->eccctrl->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 * mtd->eccctrl->size;
+ eccfrag_len = num_steps * mtd->eccctrl->bytes;
- data_col_addr = start_step * chip->ecc.size;
+ data_col_addr = start_step * mtd->eccctrl->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 +1146,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 += mtd->eccctrl->bytes, p += mtd->eccctrl->size)
+ mtd->eccctrl->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 * mtd->eccctrl->bytes] + 1 !=
+ eccpos[i + start_step * mtd->eccctrl->bytes + 1]) {
gaps = 1;
break;
}
@@ -1166,13 +1169,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 * mtd->eccctrl->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 * mtd->eccctrl->bytes)] &
+ (busw - 1))
aligned_len++;
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
@@ -1184,11 +1188,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 += mtd->eccctrl->bytes, p += mtd->eccctrl->size) {
int stat;
- stat = chip->ecc.correct(mtd, p,
- &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
+ stat = mtd->eccctrl->correct(mtd, p,
+ &chip->buffers->ecccode[i],
+ &chip->buffers->ecccalc[i]);
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
@@ -1212,32 +1218,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 = mtd->eccctrl->size;
+ int eccbytes = mtd->eccctrl->bytes;
+ int eccsteps = mtd->eccctrl->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 = mtd->eccctrl->layout->eccpos;
unsigned int max_bitflips = 0;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ mtd->eccctrl->hwctl(mtd, NAND_ECC_READ);
chip->read_buf(mtd, p, eccsize);
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ mtd->eccctrl->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 < mtd->eccctrl->total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
- eccsteps = chip->ecc.steps;
+ eccsteps = mtd->eccctrl->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 = mtd->eccctrl->correct(mtd, p, &ecc_code[i],
+ &ecc_calc[i]);
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
@@ -1265,12 +1272,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 = mtd->eccctrl->size;
+ int eccbytes = mtd->eccctrl->bytes;
+ int eccsteps = mtd->eccctrl->steps;
uint8_t *p = buf;
uint8_t *ecc_code = chip->buffers->ecccode;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint32_t *eccpos = mtd->eccctrl->layout->eccpos;
uint8_t *ecc_calc = chip->buffers->ecccalc;
unsigned int max_bitflips = 0;
@@ -1279,17 +1286,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 < mtd->eccctrl->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);
+ mtd->eccctrl->hwctl(mtd, NAND_ECC_READ);
chip->read_buf(mtd, p, eccsize);
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ mtd->eccctrl->calculate(mtd, p, &ecc_calc[i]);
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
+ stat = mtd->eccctrl->correct(mtd, p, &ecc_code[i], NULL);
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
@@ -1314,9 +1321,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 = mtd->eccctrl->size;
+ int eccbytes = mtd->eccctrl->bytes;
+ int eccsteps = mtd->eccctrl->steps;
uint8_t *p = buf;
uint8_t *oob = chip->oob_poi;
unsigned int max_bitflips = 0;
@@ -1324,17 +1331,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);
+ mtd->eccctrl->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 (mtd->eccctrl->prepad) {
+ chip->read_buf(mtd, oob, mtd->eccctrl->prepad);
+ oob += mtd->eccctrl->prepad;
}
- chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
+ mtd->eccctrl->hwctl(mtd, NAND_ECC_READSYN);
chip->read_buf(mtd, oob, eccbytes);
- stat = chip->ecc.correct(mtd, p, oob, NULL);
+ stat = mtd->eccctrl->correct(mtd, p, oob, NULL);
if (stat < 0) {
mtd->ecc_stats.failed++;
@@ -1345,9 +1352,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 (mtd->eccctrl->postpad) {
+ chip->read_buf(mtd, oob, mtd->eccctrl->postpad);
+ oob += mtd->eccctrl->postpad;
}
}
@@ -1361,14 +1368,16 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
/**
* nand_transfer_oob - [INTERN] Transfer oob to client buffer
- * @chip: nand chip structure
+ * @mtd: mtd structure
* @oob: oob destination address
* @ops: oob ops structure
* @len: size of oob to transfer
*/
-static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
+static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob,
struct mtd_oob_ops *ops, size_t len)
{
+ struct nand_chip *chip = mtd->priv;
+
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
@@ -1377,7 +1386,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 = mtd->eccctrl->layout->oobfree;
uint32_t boffs = 0, roffs = ops->ooboffs;
size_t bytes = 0;
@@ -1459,16 +1468,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 = mtd->eccctrl->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 = mtd->eccctrl->read_subpage(mtd, chip,
+ col, bytes,
+ bufpoi);
else
- ret = chip->ecc.read_page(mtd, chip, bufpoi,
- oob_required, page);
+ ret = mtd->eccctrl->read_page(mtd, chip,
+ bufpoi,
+ oob_required,
+ page);
if (ret < 0) {
if (!aligned)
/* Invalidate page cache */
@@ -1498,8 +1511,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
int toread = min(oobreadlen, max_oobsize);
if (toread) {
- oob = nand_transfer_oob(chip,
- oob, ops, toread);
+ oob = nand_transfer_oob(mtd, oob, ops,
+ toread);
oobreadlen -= toread;
}
}
@@ -1604,13 +1617,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 = mtd->eccctrl->bytes + mtd->eccctrl->prepad +
+ mtd->eccctrl->postpad;
+ int eccsize = mtd->eccctrl->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, mtd->eccctrl->size, page);
+ for (i = 0; i < mtd->eccctrl->steps; i++) {
if (sndrnd) {
pos = eccsize + i * (eccsize + chunk);
if (mtd->writesize > 512)
@@ -1663,9 +1677,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 = mtd->eccctrl->bytes + mtd->eccctrl->prepad +
+ mtd->eccctrl->postpad;
+ int eccsize = mtd->eccctrl->size, length = mtd->oobsize;
+ int i, len, pos, status = 0, sndcmd = 0, steps = mtd->eccctrl->steps;
const uint8_t *bufpoi = chip->oob_poi;
/*
@@ -1673,7 +1688,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 (!mtd->eccctrl->prepad && !mtd->eccctrl->postpad) {
pos = steps * (eccsize + chunk);
steps = 0;
} else
@@ -1737,7 +1752,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 = mtd->eccctrl->layout->oobavail;
else
len = mtd->oobsize;
@@ -1765,15 +1780,15 @@ 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 = mtd->eccctrl->read_oob_raw(mtd, chip, page);
else
- ret = chip->ecc.read_oob(mtd, chip, page);
+ ret = mtd->eccctrl->read_oob(mtd, chip, page);
if (ret < 0)
break;
len = min(len, readlen);
- buf = nand_transfer_oob(chip, buf, ops, len);
+ buf = nand_transfer_oob(mtd, buf, ops, len);
if (chip->options & NAND_NEED_READRDY) {
/* Apply delay or wait for ready/busy pin */
@@ -1888,26 +1903,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 = mtd->eccctrl->size;
+ int eccbytes = mtd->eccctrl->bytes;
uint8_t *oob = chip->oob_poi;
int steps, size;
- for (steps = chip->ecc.steps; steps > 0; steps--) {
+ for (steps = mtd->eccctrl->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 (mtd->eccctrl->prepad) {
+ chip->write_buf(mtd, oob, mtd->eccctrl->prepad);
+ oob += mtd->eccctrl->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 (mtd->eccctrl->postpad) {
+ chip->write_buf(mtd, oob, mtd->eccctrl->postpad);
+ oob += mtd->eccctrl->postpad;
}
}
@@ -1927,21 +1942,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 = mtd->eccctrl->size;
+ int eccbytes = mtd->eccctrl->bytes;
+ int eccsteps = mtd->eccctrl->steps;
uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint32_t *eccpos = mtd->eccctrl->layout->eccpos;
/* Software ECC calculation */
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ mtd->eccctrl->calculate(mtd, p, &ecc_calc[i]);
- for (i = 0; i < chip->ecc.total; i++)
+ for (i = 0; i < mtd->eccctrl->total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
- return chip->ecc.write_page_raw(mtd, chip, buf, 1);
+ return mtd->eccctrl->write_page_raw(mtd, chip, buf, 1);
}
/**
@@ -1954,20 +1969,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 = mtd->eccctrl->size;
+ int eccbytes = mtd->eccctrl->bytes;
+ int eccsteps = mtd->eccctrl->steps;
uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint32_t *eccpos = mtd->eccctrl->layout->eccpos;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+ mtd->eccctrl->hwctl(mtd, NAND_ECC_WRITE);
chip->write_buf(mtd, p, eccsize);
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ mtd->eccctrl->calculate(mtd, p, &ecc_calc[i]);
}
- for (i = 0; i < chip->ecc.total; i++)
+ for (i = 0; i < mtd->eccctrl->total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -1992,10 +2007,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 = mtd->eccctrl->size;
+ int ecc_bytes = mtd->eccctrl->bytes;
+ int ecc_steps = mtd->eccctrl->steps;
+ uint32_t *eccpos = mtd->eccctrl->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 +2018,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);
+ mtd->eccctrl->hwctl(mtd, NAND_ECC_WRITE);
/* write data (untouched subpages already masked by 0xFF) */
chip->write_buf(mtd, buf, ecc_size);
@@ -2012,7 +2027,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);
+ mtd->eccctrl->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 +2042,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 < mtd->eccctrl->total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
/* write OOB buffer to NAND device */
@@ -2051,29 +2066,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 = mtd->eccctrl->size;
+ int eccbytes = mtd->eccctrl->bytes;
+ int eccsteps = mtd->eccctrl->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);
+ mtd->eccctrl->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 (mtd->eccctrl->prepad) {
+ chip->write_buf(mtd, oob, mtd->eccctrl->prepad);
+ oob += mtd->eccctrl->prepad;
}
- chip->ecc.calculate(mtd, p, oob);
+ mtd->eccctrl->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 (mtd->eccctrl->postpad) {
+ chip->write_buf(mtd, oob, mtd->eccctrl->postpad);
+ oob += mtd->eccctrl->postpad;
}
}
@@ -2104,7 +2119,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)
+ mtd->eccctrl->write_subpage)
subpage = offset || (data_len < mtd->writesize);
else
subpage = 0;
@@ -2112,13 +2127,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 = mtd->eccctrl->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 = mtd->eccctrl->write_subpage(mtd, chip, offset,
+ data_len, buf,
+ oob_required);
else
- status = chip->ecc.write_page(mtd, chip, buf, oob_required);
+ status = mtd->eccctrl->write_page(mtd, chip, buf,
+ oob_required);
if (status < 0)
return status;
@@ -2177,7 +2194,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 = mtd->eccctrl->layout->oobfree;
uint32_t boffs = 0, woffs = ops->ooboffs;
size_t bytes = 0;
@@ -2405,7 +2422,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 = mtd->eccctrl->layout->oobavail;
else
len = mtd->oobsize;
@@ -2459,9 +2476,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 = mtd->eccctrl->write_oob_raw(mtd, chip,
+ page & chip->pagemask);
else
- status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
+ status = mtd->eccctrl->write_oob(mtd, chip,
+ page & chip->pagemask);
chip->select_chip(mtd, -1);
@@ -3582,32 +3601,9 @@ 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.
- */
-int nand_scan_tail(struct mtd_info *mtd)
+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,14 +3629,10 @@ 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
*/
-
switch (ecc->mode) {
case NAND_ECC_HW_OOB_FIRST:
/* Similar to NAND_ECC_HW, but a separate read_page handle */
@@ -3789,7 +3781,6 @@ int nand_scan_tail(struct mtd_info *mtd)
for (i = 0; ecc->layout->oobfree[i].length
&& i < ARRAY_SIZE(ecc->layout->oobfree); i++)
ecc->layout->oobavail += ecc->layout->oobfree[i].length;
- mtd->oobavail = ecc->layout->oobavail;
/*
* Set the number of read / write steps for one page depending on ECC
@@ -3802,6 +3793,111 @@ int nand_scan_tail(struct mtd_info *mtd)
}
ecc->total = ecc->steps * ecc->bytes;
+ return 0;
+}
+EXPORT_SYMBOL(nand_ecc_ctrl_init);
+
+
+static void nand_release_ecc_ctrl(const struct nand_ecc_ctrl *ecc)
+{
+ if (ecc->mode == NAND_ECC_SOFT_BCH)
+ nand_bch_free((struct nand_bch_control *)ecc->priv);
+
+ kfree(ecc);
+}
+
+const struct nand_ecc_ctrl *nand_get_ecc_ctrl(struct mtd_info *mtd,
+ nand_ecc_modes_t mode,
+ struct device_node *np)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_ecc_ctrl *ecc;
+ u32 ecc_step, ecc_strength;
+ int ret;
+
+ if (mode != NAND_ECC_NONE && mode != NAND_ECC_SOFT &&
+ mode != NAND_ECC_SOFT_BCH)
+ return ERR_PTR(-EINVAL);
+
+ ecc = kzalloc(sizeof(*ecc), GFP_KERNEL);
+ if (!ecc)
+ return ERR_PTR(-ENOMEM);
+
+ ecc->size = chip->ecc_step_ds;
+ ecc->strength = chip->ecc_strength_ds;
+ if (!of_get_nand_ecc_level(np, &ecc_strength, &ecc_step)) {
+ ecc->size = ecc_step;
+ ecc->strength = ecc_strength;
+ }
+
+ switch (mode) {
+ case NAND_ECC_NONE:
+ break;
+ case NAND_ECC_SOFT:
+ break;
+ case NAND_ECC_SOFT_BCH:
+ ecc->bytes = ((ecc->strength * fls(8 * ecc->size)) + 7) / 8;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ecc->mode = mode;
+ ret = nand_ecc_ctrl_init(mtd, ecc);
+ if (ret)
+ goto err;
+
+ ecc->release = nand_release_ecc_ctrl;
+
+ return ecc;
+
+err:
+ kfree(ecc);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(nand_get_ecc_ctrl);
+
+/**
+ * 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)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ret;
+ /*struct nand_rnd_ctrl *rnd = &chip->rnd;*/
+
+ /* 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;
+
+ if (!chip->get_ecc_ctrl)
+ chip->get_ecc_ctrl = nand_get_ecc_ctrl;
+
+ ret = nand_ecc_ctrl_init(mtd, ecc);
+ if (ret)
+ return ret;
+
+ mtd->eccctrl = &chip->ecc;
+ mtd->oobavail = ecc->layout->oobavail;
+
/* 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/ofpart.c b/drivers/mtd/ofpart.c
index d64f8c3..0365c1e 100644
--- a/drivers/mtd/ofpart.c
+++ b/drivers/mtd/ofpart.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h>
+#include <linux/of_mtd.h>
#include <linux/mtd/mtd.h>
#include <linux/slab.h>
#include <linux/mtd/partitions.h>
@@ -25,6 +26,25 @@ static bool node_has_compatible(struct device_node *pp)
return of_get_property(pp, "compatible", NULL);
}
+static int parse_ofnandpart(struct mtd_info *master,
+ struct mtd_partition *part,
+ struct device_node *pp)
+{
+ struct nand_chip *chip = master->priv;
+ int mode = of_get_nand_ecc_mode(pp);
+ const struct nand_ecc_ctrl *ret;
+
+ if (mode < 0)
+ return 0;
+
+ ret = chip->get_ecc_ctrl(master, mode, pp);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+
+ part->eccctrl = ret;
+ return 0;
+}
+
static int parse_ofpart_partitions(struct mtd_info *master,
struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
@@ -63,6 +83,7 @@ static int parse_ofpart_partitions(struct mtd_info *master,
const __be32 *reg;
int len;
int a_cells, s_cells;
+ int ret;
if (node_has_compatible(pp))
continue;
@@ -89,6 +110,20 @@ static int parse_ofpart_partitions(struct mtd_info *master,
if (of_get_property(pp, "lock", &len))
(*pparts)[i].mask_flags |= MTD_POWERUP_LOCK;
+ switch (master->type) {
+ case MTD_NANDFLASH:
+ case MTD_MLCNANDFLASH:
+ ret = parse_ofnandpart(master, &(*pparts)[i], pp);
+ if (ret) {
+ nr_parts--;
+ continue;
+ }
+
+ break;
+ default:
+ break;
+ }
+
i++;
}
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 8cc0e2f..7b08d50 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -109,6 +109,8 @@ struct nand_ecclayout {
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE];
};
+struct nand_ecc_ctrl;
+
struct module; /* only needed for owner field in mtd_info */
struct mtd_info {
@@ -169,6 +171,7 @@ struct mtd_info {
/* ECC layout structure pointer - read only! */
struct nand_ecclayout *ecclayout;
+ const struct nand_ecc_ctrl *eccctrl;
/* the ecc step size. */
unsigned int ecc_step_size;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index c70e0a3..d3f0cfd 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -407,8 +407,11 @@ struct nand_ecc_ctrl {
int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page);
int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip,
int page);
+ void (*release)(const struct nand_ecc_ctrl *ctrl);
};
+
+
/**
* struct nand_buffers - buffer structure for read/write
* @ecccalc: buffer for calculated ECC
@@ -544,6 +547,9 @@ struct nand_chip {
int feature_addr, uint8_t *subfeature_para);
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
int feature_addr, uint8_t *subfeature_para);
+ const struct nand_ecc_ctrl *(*get_ecc_ctrl)(struct mtd_info *mtd,
+ nand_ecc_modes_t mode,
+ struct device_node *np);
int chip_delay;
unsigned int options;
@@ -699,6 +705,12 @@ extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, uint8_t *buf);
+int nand_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc);
+
+const struct nand_ecc_ctrl *nand_get_ecc_ctrl(struct mtd_info *mtd,
+ nand_ecc_modes_t mode,
+ struct device_node *np);
+
/**
* struct platform_nand_chip - chip level device structure
* @nr_chips: max. number of chips to scan for
diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h
index 1f8d24b..9e39fb1 100644
--- a/include/linux/mtd/partitions.h
+++ b/include/linux/mtd/partitions.h
@@ -42,6 +42,7 @@ struct mtd_partition {
uint64_t offset; /* offset within the master MTD space */
uint32_t mask_flags; /* master MTD flags to mask out for this partition */
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only) */
+ const struct nand_ecc_ctrl *eccctrl; /* NAND ECC config for this partition (NAND only) */
};
#define MTDPART_OFS_RETAIN (-3)

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/