You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1295 lines
39 KiB
1295 lines
39 KiB
From a95cc309cf74eed3fc457dec3dcc44d9bf79e0e6 Mon Sep 17 00:00:00 2001
|
|
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
|
|
Date: Mon, 28 Jul 2014 15:01:15 +0200
|
|
Subject: [PATCH] mtd: nand: Add support for NAND partitions
|
|
|
|
Add support for NAND partitions, and indirectly for per partition ECC
|
|
config, and also per partiton random seed support for the upcoming
|
|
randomizer support.
|
|
|
|
This is necessary to be able to use different ECC / randomizer settings for
|
|
the parts of the NAND which are read directly by a bootrom (which has a
|
|
fixed ECC / random seed setting) and the generic data part of the NAND for
|
|
which we often want a stronger ECC and / or random seed.
|
|
|
|
Provide helper functions to add/delete/allocate nand partitions.
|
|
NAND core code now make use of the partition specific nand_ecc_ctrl struct
|
|
(if available) when doing read/write operations.
|
|
|
|
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
---
|
|
drivers/mtd/nand/Kconfig | 4 +
|
|
drivers/mtd/nand/Makefile | 2 +
|
|
drivers/mtd/nand/nand_base.c | 712 +++++++++++++++++++++++++++++++++++--------
|
|
drivers/mtd/nand/nand_bch.c | 16 +-
|
|
drivers/mtd/nand/nand_ecc.c | 4 +-
|
|
include/linux/mtd/nand.h | 38 +++
|
|
6 files changed, 635 insertions(+), 141 deletions(-)
|
|
|
|
--- a/drivers/mtd/nand/Kconfig
|
|
+++ b/drivers/mtd/nand/Kconfig
|
|
@@ -22,6 +22,10 @@ menuconfig MTD_NAND
|
|
|
|
if MTD_NAND
|
|
|
|
+config MTD_OF_NAND_PARTS
|
|
+ tristate
|
|
+ default n
|
|
+
|
|
config MTD_NAND_BCH
|
|
tristate
|
|
select BCH
|
|
--- a/drivers/mtd/nand/Makefile
|
|
+++ b/drivers/mtd/nand/Makefile
|
|
@@ -53,4 +53,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) +=
|
|
obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
|
|
obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
|
|
|
|
+obj-$(CONFIG_MTD_OF_NAND_PARTS) += ofnandpart.o
|
|
+
|
|
nand-objs := nand_base.o nand_bbt.o nand_timings.o
|
|
--- a/drivers/mtd/nand/nand_base.c
|
|
+++ b/drivers/mtd/nand/nand_base.c
|
|
@@ -1134,26 +1134,26 @@ static int nand_read_page_raw_syndrome(s
|
|
struct nand_chip *chip, uint8_t *buf,
|
|
int oob_required, int page)
|
|
{
|
|
- int eccsize = chip->ecc.size;
|
|
- int eccbytes = chip->ecc.bytes;
|
|
+ int eccsize = chip->cur_ecc->size;
|
|
+ int eccbytes = chip->cur_ecc->bytes;
|
|
uint8_t *oob = chip->oob_poi;
|
|
int steps, size;
|
|
|
|
- for (steps = chip->ecc.steps; steps > 0; steps--) {
|
|
+ for (steps = chip->cur_ecc->steps; steps > 0; steps--) {
|
|
chip->read_buf(mtd, buf, eccsize);
|
|
buf += eccsize;
|
|
|
|
- if (chip->ecc.prepad) {
|
|
- chip->read_buf(mtd, oob, chip->ecc.prepad);
|
|
- oob += chip->ecc.prepad;
|
|
+ if (chip->cur_ecc->prepad) {
|
|
+ chip->read_buf(mtd, oob, chip->cur_ecc->prepad);
|
|
+ oob += chip->cur_ecc->prepad;
|
|
}
|
|
|
|
chip->read_buf(mtd, oob, eccbytes);
|
|
oob += eccbytes;
|
|
|
|
- if (chip->ecc.postpad) {
|
|
- chip->read_buf(mtd, oob, chip->ecc.postpad);
|
|
- oob += chip->ecc.postpad;
|
|
+ if (chip->cur_ecc->postpad) {
|
|
+ chip->read_buf(mtd, oob, chip->cur_ecc->postpad);
|
|
+ oob += chip->cur_ecc->postpad;
|
|
}
|
|
}
|
|
|
|
@@ -1175,30 +1175,31 @@ static int nand_read_page_raw_syndrome(s
|
|
static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
uint8_t *buf, int oob_required, int page)
|
|
{
|
|
- int i, eccsize = chip->ecc.size;
|
|
- int eccbytes = chip->ecc.bytes;
|
|
- int eccsteps = chip->ecc.steps;
|
|
+ int i, eccsize = chip->cur_ecc->size;
|
|
+ int eccbytes = chip->cur_ecc->bytes;
|
|
+ int eccsteps = chip->cur_ecc->steps;
|
|
uint8_t *p = buf;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
+ uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
|
|
unsigned int max_bitflips = 0;
|
|
|
|
- chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
|
|
+ chip->cur_ecc->read_page_raw(mtd, chip, buf, 1, page);
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
|
|
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
+ chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]);
|
|
|
|
- for (i = 0; i < chip->ecc.total; i++)
|
|
+ for (i = 0; i < chip->cur_ecc->total; i++)
|
|
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
|
|
|
- eccsteps = chip->ecc.steps;
|
|
+ eccsteps = chip->cur_ecc->steps;
|
|
p = buf;
|
|
|
|
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
int stat;
|
|
|
|
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
|
+ stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i],
|
|
+ &ecc_calc[i]);
|
|
if (stat < 0) {
|
|
mtd->ecc_stats.failed++;
|
|
} else {
|
|
@@ -1223,7 +1224,7 @@ static int nand_read_subpage(struct mtd_
|
|
int page)
|
|
{
|
|
int start_step, end_step, num_steps;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
+ uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
|
|
uint8_t *p;
|
|
int data_col_addr, i, gaps = 0;
|
|
int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
|
|
@@ -1232,16 +1233,16 @@ static int nand_read_subpage(struct mtd_
|
|
unsigned int max_bitflips = 0;
|
|
|
|
/* Column address within the page aligned to ECC size (256bytes) */
|
|
- start_step = data_offs / chip->ecc.size;
|
|
- end_step = (data_offs + readlen - 1) / chip->ecc.size;
|
|
+ start_step = data_offs / chip->cur_ecc->size;
|
|
+ end_step = (data_offs + readlen - 1) / chip->cur_ecc->size;
|
|
num_steps = end_step - start_step + 1;
|
|
- index = start_step * chip->ecc.bytes;
|
|
+ index = start_step * chip->cur_ecc->bytes;
|
|
|
|
/* Data size aligned to ECC ecc.size */
|
|
- datafrag_len = num_steps * chip->ecc.size;
|
|
- eccfrag_len = num_steps * chip->ecc.bytes;
|
|
+ datafrag_len = num_steps * chip->cur_ecc->size;
|
|
+ eccfrag_len = num_steps * chip->cur_ecc->bytes;
|
|
|
|
- data_col_addr = start_step * chip->ecc.size;
|
|
+ data_col_addr = start_step * chip->cur_ecc->size;
|
|
/* If we read not a page aligned data */
|
|
if (data_col_addr != 0)
|
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
|
|
@@ -1250,8 +1251,9 @@ static int nand_read_subpage(struct mtd_
|
|
chip->read_buf(mtd, p, datafrag_len);
|
|
|
|
/* Calculate ECC */
|
|
- for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
|
|
- chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);
|
|
+ for (i = 0; i < eccfrag_len;
|
|
+ i += chip->cur_ecc->bytes, p += chip->cur_ecc->size)
|
|
+ chip->cur_ecc->calculate(mtd, p, &chip->buffers->ecccalc[i]);
|
|
|
|
/*
|
|
* The performance is faster if we position offsets according to
|
|
@@ -1275,7 +1277,8 @@ static int nand_read_subpage(struct mtd_
|
|
aligned_len = eccfrag_len;
|
|
if (eccpos[index] & (busw - 1))
|
|
aligned_len++;
|
|
- if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
|
|
+ if (eccpos[index + (num_steps * chip->cur_ecc->bytes)] &
|
|
+ (busw - 1))
|
|
aligned_len++;
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
|
|
@@ -1287,11 +1290,13 @@ static int nand_read_subpage(struct mtd_
|
|
chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]];
|
|
|
|
p = bufpoi + data_col_addr;
|
|
- for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
|
|
+ for (i = 0; i < eccfrag_len;
|
|
+ i += chip->cur_ecc->bytes, p += chip->cur_ecc->size) {
|
|
int stat;
|
|
|
|
- stat = chip->ecc.correct(mtd, p,
|
|
- &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
|
|
+ stat = chip->cur_ecc->correct(mtd, p,
|
|
+ &chip->buffers->ecccode[i],
|
|
+ &chip->buffers->ecccalc[i]);
|
|
if (stat < 0) {
|
|
mtd->ecc_stats.failed++;
|
|
} else {
|
|
@@ -1315,32 +1320,33 @@ static int nand_read_subpage(struct mtd_
|
|
static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
uint8_t *buf, int oob_required, int page)
|
|
{
|
|
- int i, eccsize = chip->ecc.size;
|
|
- int eccbytes = chip->ecc.bytes;
|
|
- int eccsteps = chip->ecc.steps;
|
|
+ int i, eccsize = chip->cur_ecc->size;
|
|
+ int eccbytes = chip->cur_ecc->bytes;
|
|
+ int eccsteps = chip->cur_ecc->steps;
|
|
uint8_t *p = buf;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
+ uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
|
|
unsigned int max_bitflips = 0;
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
|
+ chip->cur_ecc->hwctl(mtd, NAND_ECC_READ);
|
|
chip->read_buf(mtd, p, eccsize);
|
|
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
+ chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]);
|
|
}
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
|
|
- for (i = 0; i < chip->ecc.total; i++)
|
|
+ for (i = 0; i < chip->cur_ecc->total; i++)
|
|
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
|
|
|
- eccsteps = chip->ecc.steps;
|
|
+ eccsteps = chip->cur_ecc->steps;
|
|
p = buf;
|
|
|
|
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
int stat;
|
|
|
|
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
|
+ stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i],
|
|
+ &ecc_calc[i]);
|
|
if (stat < 0) {
|
|
mtd->ecc_stats.failed++;
|
|
} else {
|
|
@@ -1368,12 +1374,12 @@ static int nand_read_page_hwecc(struct m
|
|
static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
|
|
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
|
|
{
|
|
- int i, eccsize = chip->ecc.size;
|
|
- int eccbytes = chip->ecc.bytes;
|
|
- int eccsteps = chip->ecc.steps;
|
|
+ int i, eccsize = chip->cur_ecc->size;
|
|
+ int eccbytes = chip->cur_ecc->bytes;
|
|
+ int eccsteps = chip->cur_ecc->steps;
|
|
uint8_t *p = buf;
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
+ uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
unsigned int max_bitflips = 0;
|
|
|
|
@@ -1382,17 +1388,17 @@ static int nand_read_page_hwecc_oob_firs
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
|
|
|
|
- for (i = 0; i < chip->ecc.total; i++)
|
|
+ for (i = 0; i < chip->cur_ecc->total; i++)
|
|
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
int stat;
|
|
|
|
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
|
+ chip->cur_ecc->hwctl(mtd, NAND_ECC_READ);
|
|
chip->read_buf(mtd, p, eccsize);
|
|
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
+ chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]);
|
|
|
|
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
|
|
+ stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], NULL);
|
|
if (stat < 0) {
|
|
mtd->ecc_stats.failed++;
|
|
} else {
|
|
@@ -1417,9 +1423,9 @@ static int nand_read_page_hwecc_oob_firs
|
|
static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
|
uint8_t *buf, int oob_required, int page)
|
|
{
|
|
- int i, eccsize = chip->ecc.size;
|
|
- int eccbytes = chip->ecc.bytes;
|
|
- int eccsteps = chip->ecc.steps;
|
|
+ int i, eccsize = chip->cur_ecc->size;
|
|
+ int eccbytes = chip->cur_ecc->bytes;
|
|
+ int eccsteps = chip->cur_ecc->steps;
|
|
uint8_t *p = buf;
|
|
uint8_t *oob = chip->oob_poi;
|
|
unsigned int max_bitflips = 0;
|
|
@@ -1427,17 +1433,17 @@ static int nand_read_page_syndrome(struc
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
int stat;
|
|
|
|
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
|
+ chip->cur_ecc->hwctl(mtd, NAND_ECC_READ);
|
|
chip->read_buf(mtd, p, eccsize);
|
|
|
|
- if (chip->ecc.prepad) {
|
|
- chip->read_buf(mtd, oob, chip->ecc.prepad);
|
|
- oob += chip->ecc.prepad;
|
|
+ if (chip->cur_ecc->prepad) {
|
|
+ chip->read_buf(mtd, oob, chip->cur_ecc->prepad);
|
|
+ oob += chip->cur_ecc->prepad;
|
|
}
|
|
|
|
- chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
|
|
+ chip->cur_ecc->hwctl(mtd, NAND_ECC_READSYN);
|
|
chip->read_buf(mtd, oob, eccbytes);
|
|
- stat = chip->ecc.correct(mtd, p, oob, NULL);
|
|
+ stat = chip->cur_ecc->correct(mtd, p, oob, NULL);
|
|
|
|
if (stat < 0) {
|
|
mtd->ecc_stats.failed++;
|
|
@@ -1448,9 +1454,9 @@ static int nand_read_page_syndrome(struc
|
|
|
|
oob += eccbytes;
|
|
|
|
- if (chip->ecc.postpad) {
|
|
- chip->read_buf(mtd, oob, chip->ecc.postpad);
|
|
- oob += chip->ecc.postpad;
|
|
+ if (chip->cur_ecc->postpad) {
|
|
+ chip->read_buf(mtd, oob, chip->cur_ecc->postpad);
|
|
+ oob += chip->cur_ecc->postpad;
|
|
}
|
|
}
|
|
|
|
@@ -1480,7 +1486,7 @@ static uint8_t *nand_transfer_oob(struct
|
|
return oob + len;
|
|
|
|
case MTD_OPS_AUTO_OOB: {
|
|
- struct nand_oobfree *free = chip->ecc.layout->oobfree;
|
|
+ struct nand_oobfree *free = chip->cur_ecc->layout->oobfree;
|
|
uint32_t boffs = 0, roffs = ops->ooboffs;
|
|
size_t bytes = 0;
|
|
|
|
@@ -1600,17 +1606,21 @@ read_retry:
|
|
* the read methods return max bitflips per ecc step.
|
|
*/
|
|
if (unlikely(ops->mode == MTD_OPS_RAW))
|
|
- ret = chip->ecc.read_page_raw(mtd, chip, bufpoi,
|
|
- oob_required,
|
|
- page);
|
|
+ ret = chip->cur_ecc->read_page_raw(mtd, chip,
|
|
+ bufpoi,
|
|
+ oob_required,
|
|
+ page);
|
|
else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
|
|
!oob)
|
|
- ret = chip->ecc.read_subpage(mtd, chip,
|
|
- col, bytes, bufpoi,
|
|
- page);
|
|
+ ret = chip->cur_ecc->read_subpage(mtd, chip,
|
|
+ col, bytes,
|
|
+ bufpoi,
|
|
+ page);
|
|
else
|
|
- ret = chip->ecc.read_page(mtd, chip, bufpoi,
|
|
- oob_required, page);
|
|
+ ret = chip->cur_ecc->read_page(mtd, chip,
|
|
+ bufpoi,
|
|
+ oob_required,
|
|
+ page);
|
|
if (ret < 0) {
|
|
if (use_bufpoi)
|
|
/* Invalidate page cache */
|
|
@@ -1746,6 +1756,39 @@ static int nand_read(struct mtd_info *mt
|
|
}
|
|
|
|
/**
|
|
+ * nand_part_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc
|
|
+ * @mtd: MTD device structure
|
|
+ * @from: offset to read from
|
|
+ * @len: number of bytes to read
|
|
+ * @retlen: pointer to variable to store the number of read bytes
|
|
+ * @buf: the databuffer to put data
|
|
+ *
|
|
+ * Get hold of the chip and call nand_do_read.
|
|
+ */
|
|
+static int nand_part_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|
+ size_t *retlen, uint8_t *buf)
|
|
+{
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
+ struct nand_part *part = to_nand_part(mtd);
|
|
+ struct mtd_oob_ops ops;
|
|
+ int ret;
|
|
+
|
|
+ from += part->offset;
|
|
+ nand_get_device(part->master, FL_READING);
|
|
+ if (part->ecc)
|
|
+ chip->cur_ecc = part->ecc;
|
|
+ ops.len = len;
|
|
+ ops.datbuf = buf;
|
|
+ ops.oobbuf = NULL;
|
|
+ ops.mode = MTD_OPS_PLACE_OOB;
|
|
+ ret = nand_do_read_ops(part->master, from, &ops);
|
|
+ *retlen = ops.retlen;
|
|
+ chip->cur_ecc = &chip->ecc;
|
|
+ nand_release_device(part->master);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
* nand_read_oob_std - [REPLACEABLE] the most common OOB data read function
|
|
* @mtd: mtd info structure
|
|
* @chip: nand chip info structure
|
|
@@ -1770,13 +1813,14 @@ static int nand_read_oob_syndrome(struct
|
|
int page)
|
|
{
|
|
int length = mtd->oobsize;
|
|
- int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
|
|
- int eccsize = chip->ecc.size;
|
|
+ int chunk = chip->cur_ecc->bytes + chip->cur_ecc->prepad +
|
|
+ chip->cur_ecc->postpad;
|
|
+ int eccsize = chip->cur_ecc->size;
|
|
uint8_t *bufpoi = chip->oob_poi;
|
|
int i, toread, sndrnd = 0, pos;
|
|
|
|
- chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page);
|
|
- for (i = 0; i < chip->ecc.steps; i++) {
|
|
+ chip->cmdfunc(mtd, NAND_CMD_READ0, chip->cur_ecc->size, page);
|
|
+ for (i = 0; i < chip->cur_ecc->steps; i++) {
|
|
if (sndrnd) {
|
|
pos = eccsize + i * (eccsize + chunk);
|
|
if (mtd->writesize > 512)
|
|
@@ -1829,9 +1873,10 @@ static int nand_write_oob_std(struct mtd
|
|
static int nand_write_oob_syndrome(struct mtd_info *mtd,
|
|
struct nand_chip *chip, int page)
|
|
{
|
|
- int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
|
|
- int eccsize = chip->ecc.size, length = mtd->oobsize;
|
|
- int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps;
|
|
+ int chunk = chip->cur_ecc->bytes + chip->cur_ecc->prepad +
|
|
+ chip->cur_ecc->postpad;
|
|
+ int eccsize = chip->cur_ecc->size, length = mtd->oobsize;
|
|
+ int i, len, pos, status = 0, sndcmd = 0, steps = chip->cur_ecc->steps;
|
|
const uint8_t *bufpoi = chip->oob_poi;
|
|
|
|
/*
|
|
@@ -1839,7 +1884,7 @@ static int nand_write_oob_syndrome(struc
|
|
* or
|
|
* data-pad-ecc-pad-data-pad .... ecc-pad-oob
|
|
*/
|
|
- if (!chip->ecc.prepad && !chip->ecc.postpad) {
|
|
+ if (!chip->cur_ecc->prepad && !chip->cur_ecc->postpad) {
|
|
pos = steps * (eccsize + chunk);
|
|
steps = 0;
|
|
} else
|
|
@@ -1903,7 +1948,7 @@ static int nand_do_read_oob(struct mtd_i
|
|
stats = mtd->ecc_stats;
|
|
|
|
if (ops->mode == MTD_OPS_AUTO_OOB)
|
|
- len = chip->ecc.layout->oobavail;
|
|
+ len = chip->cur_ecc->layout->oobavail;
|
|
else
|
|
len = mtd->oobsize;
|
|
|
|
@@ -1931,9 +1976,9 @@ static int nand_do_read_oob(struct mtd_i
|
|
|
|
while (1) {
|
|
if (ops->mode == MTD_OPS_RAW)
|
|
- ret = chip->ecc.read_oob_raw(mtd, chip, page);
|
|
+ ret = chip->cur_ecc->read_oob_raw(mtd, chip, page);
|
|
else
|
|
- ret = chip->ecc.read_oob(mtd, chip, page);
|
|
+ ret = chip->cur_ecc->read_oob(mtd, chip, page);
|
|
|
|
if (ret < 0)
|
|
break;
|
|
@@ -2021,6 +2066,56 @@ out:
|
|
return ret;
|
|
}
|
|
|
|
+/**
|
|
+ * nand_part_read_oob - [MTD Interface] NAND read data and/or out-of-band
|
|
+ * @mtd: MTD device structure
|
|
+ * @from: offset to read from
|
|
+ * @ops: oob operation description structure
|
|
+ *
|
|
+ * NAND read data and/or out-of-band data.
|
|
+ */
|
|
+static int nand_part_read_oob(struct mtd_info *mtd, loff_t from,
|
|
+ struct mtd_oob_ops *ops)
|
|
+{
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
+ struct nand_part *part = to_nand_part(mtd);
|
|
+ int ret = -ENOTSUPP;
|
|
+
|
|
+ ops->retlen = 0;
|
|
+
|
|
+ /* Do not allow reads past end of device */
|
|
+ if (ops->datbuf && (from + ops->len) > mtd->size) {
|
|
+ pr_debug("%s: attempt to read beyond end of device\n",
|
|
+ __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ from += part->offset;
|
|
+ nand_get_device(part->master, FL_READING);
|
|
+ if (part->ecc)
|
|
+ chip->cur_ecc = part->ecc;
|
|
+
|
|
+ switch (ops->mode) {
|
|
+ case MTD_OPS_PLACE_OOB:
|
|
+ case MTD_OPS_AUTO_OOB:
|
|
+ case MTD_OPS_RAW:
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (!ops->datbuf)
|
|
+ ret = nand_do_read_oob(part->master, from, ops);
|
|
+ else
|
|
+ ret = nand_do_read_ops(part->master, from, ops);
|
|
+
|
|
+out:
|
|
+ chip->cur_ecc = &chip->ecc;
|
|
+ nand_release_device(part->master);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
|
|
/**
|
|
* nand_write_page_raw - [INTERN] raw page write function
|
|
@@ -2054,26 +2149,26 @@ static int nand_write_page_raw_syndrome(
|
|
struct nand_chip *chip,
|
|
const uint8_t *buf, int oob_required)
|
|
{
|
|
- int eccsize = chip->ecc.size;
|
|
- int eccbytes = chip->ecc.bytes;
|
|
+ int eccsize = chip->cur_ecc->size;
|
|
+ int eccbytes = chip->cur_ecc->bytes;
|
|
uint8_t *oob = chip->oob_poi;
|
|
int steps, size;
|
|
|
|
- for (steps = chip->ecc.steps; steps > 0; steps--) {
|
|
+ for (steps = chip->cur_ecc->steps; steps > 0; steps--) {
|
|
chip->write_buf(mtd, buf, eccsize);
|
|
buf += eccsize;
|
|
|
|
- if (chip->ecc.prepad) {
|
|
- chip->write_buf(mtd, oob, chip->ecc.prepad);
|
|
- oob += chip->ecc.prepad;
|
|
+ if (chip->cur_ecc->prepad) {
|
|
+ chip->write_buf(mtd, oob, chip->cur_ecc->prepad);
|
|
+ oob += chip->cur_ecc->prepad;
|
|
}
|
|
|
|
chip->write_buf(mtd, oob, eccbytes);
|
|
oob += eccbytes;
|
|
|
|
- if (chip->ecc.postpad) {
|
|
- chip->write_buf(mtd, oob, chip->ecc.postpad);
|
|
- oob += chip->ecc.postpad;
|
|
+ if (chip->cur_ecc->postpad) {
|
|
+ chip->write_buf(mtd, oob, chip->cur_ecc->postpad);
|
|
+ oob += chip->cur_ecc->postpad;
|
|
}
|
|
}
|
|
|
|
@@ -2093,21 +2188,21 @@ static int nand_write_page_raw_syndrome(
|
|
static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
const uint8_t *buf, int oob_required)
|
|
{
|
|
- int i, eccsize = chip->ecc.size;
|
|
- int eccbytes = chip->ecc.bytes;
|
|
- int eccsteps = chip->ecc.steps;
|
|
+ int i, eccsize = chip->cur_ecc->size;
|
|
+ int eccbytes = chip->cur_ecc->bytes;
|
|
+ int eccsteps = chip->cur_ecc->steps;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
const uint8_t *p = buf;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
+ uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
|
|
|
|
/* Software ECC calculation */
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
|
|
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
+ chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]);
|
|
|
|
- for (i = 0; i < chip->ecc.total; i++)
|
|
+ for (i = 0; i < chip->cur_ecc->total; i++)
|
|
chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
|
|
|
- return chip->ecc.write_page_raw(mtd, chip, buf, 1);
|
|
+ return chip->cur_ecc->write_page_raw(mtd, chip, buf, 1);
|
|
}
|
|
|
|
/**
|
|
@@ -2120,20 +2215,20 @@ static int nand_write_page_swecc(struct
|
|
static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
const uint8_t *buf, int oob_required)
|
|
{
|
|
- int i, eccsize = chip->ecc.size;
|
|
- int eccbytes = chip->ecc.bytes;
|
|
- int eccsteps = chip->ecc.steps;
|
|
+ int i, eccsize = chip->cur_ecc->size;
|
|
+ int eccbytes = chip->cur_ecc->bytes;
|
|
+ int eccsteps = chip->cur_ecc->steps;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
const uint8_t *p = buf;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
+ uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
|
|
+ chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE);
|
|
chip->write_buf(mtd, p, eccsize);
|
|
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
+ chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]);
|
|
}
|
|
|
|
- for (i = 0; i < chip->ecc.total; i++)
|
|
+ for (i = 0; i < chip->cur_ecc->total; i++)
|
|
chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
|
|
|
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
@@ -2158,10 +2253,10 @@ static int nand_write_subpage_hwecc(stru
|
|
{
|
|
uint8_t *oob_buf = chip->oob_poi;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
- int ecc_size = chip->ecc.size;
|
|
- int ecc_bytes = chip->ecc.bytes;
|
|
- int ecc_steps = chip->ecc.steps;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
+ int ecc_size = chip->cur_ecc->size;
|
|
+ int ecc_bytes = chip->cur_ecc->bytes;
|
|
+ int ecc_steps = chip->cur_ecc->steps;
|
|
+ uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
|
|
uint32_t start_step = offset / ecc_size;
|
|
uint32_t end_step = (offset + data_len - 1) / ecc_size;
|
|
int oob_bytes = mtd->oobsize / ecc_steps;
|
|
@@ -2169,7 +2264,7 @@ static int nand_write_subpage_hwecc(stru
|
|
|
|
for (step = 0; step < ecc_steps; step++) {
|
|
/* configure controller for WRITE access */
|
|
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
|
|
+ chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE);
|
|
|
|
/* write data (untouched subpages already masked by 0xFF) */
|
|
chip->write_buf(mtd, buf, ecc_size);
|
|
@@ -2178,7 +2273,7 @@ static int nand_write_subpage_hwecc(stru
|
|
if ((step < start_step) || (step > end_step))
|
|
memset(ecc_calc, 0xff, ecc_bytes);
|
|
else
|
|
- chip->ecc.calculate(mtd, buf, ecc_calc);
|
|
+ chip->cur_ecc->calculate(mtd, buf, ecc_calc);
|
|
|
|
/* mask OOB of un-touched subpages by padding 0xFF */
|
|
/* if oob_required, preserve OOB metadata of written subpage */
|
|
@@ -2193,7 +2288,7 @@ static int nand_write_subpage_hwecc(stru
|
|
/* copy calculated ECC for whole page to chip->buffer->oob */
|
|
/* this include masked-value(0xFF) for unwritten subpages */
|
|
ecc_calc = chip->buffers->ecccalc;
|
|
- for (i = 0; i < chip->ecc.total; i++)
|
|
+ for (i = 0; i < chip->cur_ecc->total; i++)
|
|
chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
|
|
|
/* write OOB buffer to NAND device */
|
|
@@ -2217,29 +2312,29 @@ static int nand_write_page_syndrome(stru
|
|
struct nand_chip *chip,
|
|
const uint8_t *buf, int oob_required)
|
|
{
|
|
- int i, eccsize = chip->ecc.size;
|
|
- int eccbytes = chip->ecc.bytes;
|
|
- int eccsteps = chip->ecc.steps;
|
|
+ int i, eccsize = chip->cur_ecc->size;
|
|
+ int eccbytes = chip->cur_ecc->bytes;
|
|
+ int eccsteps = chip->cur_ecc->steps;
|
|
const uint8_t *p = buf;
|
|
uint8_t *oob = chip->oob_poi;
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
|
|
- chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
|
|
+ chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE);
|
|
chip->write_buf(mtd, p, eccsize);
|
|
|
|
- if (chip->ecc.prepad) {
|
|
- chip->write_buf(mtd, oob, chip->ecc.prepad);
|
|
- oob += chip->ecc.prepad;
|
|
+ if (chip->cur_ecc->prepad) {
|
|
+ chip->write_buf(mtd, oob, chip->cur_ecc->prepad);
|
|
+ oob += chip->cur_ecc->prepad;
|
|
}
|
|
|
|
- chip->ecc.calculate(mtd, p, oob);
|
|
+ chip->cur_ecc->calculate(mtd, p, oob);
|
|
chip->write_buf(mtd, oob, eccbytes);
|
|
oob += eccbytes;
|
|
|
|
- if (chip->ecc.postpad) {
|
|
- chip->write_buf(mtd, oob, chip->ecc.postpad);
|
|
- oob += chip->ecc.postpad;
|
|
+ if (chip->cur_ecc->postpad) {
|
|
+ chip->write_buf(mtd, oob, chip->cur_ecc->postpad);
|
|
+ oob += chip->cur_ecc->postpad;
|
|
}
|
|
}
|
|
|
|
@@ -2270,7 +2365,7 @@ static int nand_write_page(struct mtd_in
|
|
int status, subpage;
|
|
|
|
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
|
|
- chip->ecc.write_subpage)
|
|
+ chip->cur_ecc->write_subpage)
|
|
subpage = offset || (data_len < mtd->writesize);
|
|
else
|
|
subpage = 0;
|
|
@@ -2278,13 +2373,15 @@ static int nand_write_page(struct mtd_in
|
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
|
|
|
|
if (unlikely(raw))
|
|
- status = chip->ecc.write_page_raw(mtd, chip, buf,
|
|
- oob_required);
|
|
+ status = chip->cur_ecc->write_page_raw(mtd, chip, buf,
|
|
+ oob_required);
|
|
else if (subpage)
|
|
- status = chip->ecc.write_subpage(mtd, chip, offset, data_len,
|
|
- buf, oob_required);
|
|
+ status = chip->cur_ecc->write_subpage(mtd, chip, offset,
|
|
+ data_len, buf,
|
|
+ oob_required);
|
|
else
|
|
- status = chip->ecc.write_page(mtd, chip, buf, oob_required);
|
|
+ status = chip->cur_ecc->write_page(mtd, chip, buf,
|
|
+ oob_required);
|
|
|
|
if (status < 0)
|
|
return status;
|
|
@@ -2343,7 +2440,7 @@ static uint8_t *nand_fill_oob(struct mtd
|
|
return oob + len;
|
|
|
|
case MTD_OPS_AUTO_OOB: {
|
|
- struct nand_oobfree *free = chip->ecc.layout->oobfree;
|
|
+ struct nand_oobfree *free = chip->cur_ecc->layout->oobfree;
|
|
uint32_t boffs = 0, woffs = ops->ooboffs;
|
|
size_t bytes = 0;
|
|
|
|
@@ -2539,6 +2636,46 @@ static int panic_nand_write(struct mtd_i
|
|
}
|
|
|
|
/**
|
|
+ * panic_nand_part_write - [MTD Interface] NAND write with ECC
|
|
+ * @mtd: MTD device structure
|
|
+ * @to: offset to write to
|
|
+ * @len: number of bytes to write
|
|
+ * @retlen: pointer to variable to store the number of written bytes
|
|
+ * @buf: the data to write
|
|
+ *
|
|
+ * NAND write with ECC. Used when performing writes in interrupt context, this
|
|
+ * may for example be called by mtdoops when writing an oops while in panic.
|
|
+ */
|
|
+static int panic_nand_part_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|
+ size_t *retlen, const uint8_t *buf)
|
|
+{
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
+ struct nand_part *part = to_nand_part(mtd);
|
|
+ struct mtd_oob_ops ops;
|
|
+ int ret;
|
|
+
|
|
+ to += part->offset;
|
|
+ /* Wait for the device to get ready */
|
|
+ panic_nand_wait(part->master, chip, 400);
|
|
+
|
|
+ /* Grab the device */
|
|
+ panic_nand_get_device(chip, part->master, FL_WRITING);
|
|
+ if (part->ecc)
|
|
+ chip->cur_ecc = part->ecc;
|
|
+
|
|
+ ops.len = len;
|
|
+ ops.datbuf = (uint8_t *)buf;
|
|
+ ops.oobbuf = NULL;
|
|
+ ops.mode = MTD_OPS_PLACE_OOB;
|
|
+
|
|
+ ret = nand_do_write_ops(part->master, to, &ops);
|
|
+
|
|
+ chip->cur_ecc = &chip->ecc;
|
|
+ *retlen = ops.retlen;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
* nand_write - [MTD Interface] NAND write with ECC
|
|
* @mtd: MTD device structure
|
|
* @to: offset to write to
|
|
@@ -2566,6 +2703,39 @@ static int nand_write(struct mtd_info *m
|
|
}
|
|
|
|
/**
|
|
+ * nand_part_write - [MTD Interface] NAND write with ECC
|
|
+ * @mtd: MTD device structure
|
|
+ * @to: offset to write to
|
|
+ * @len: number of bytes to write
|
|
+ * @retlen: pointer to variable to store the number of written bytes
|
|
+ * @buf: the data to write
|
|
+ *
|
|
+ * NAND write with ECC.
|
|
+ */
|
|
+static int nand_part_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|
+ size_t *retlen, const uint8_t *buf)
|
|
+{
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
+ struct nand_part *part = to_nand_part(mtd);
|
|
+ struct mtd_oob_ops ops;
|
|
+ int ret;
|
|
+
|
|
+ to += part->offset;
|
|
+ nand_get_device(part->master, FL_WRITING);
|
|
+ if (part->ecc)
|
|
+ chip->cur_ecc = part->ecc;
|
|
+ ops.len = len;
|
|
+ ops.datbuf = (uint8_t *)buf;
|
|
+ ops.oobbuf = NULL;
|
|
+ ops.mode = MTD_OPS_PLACE_OOB;
|
|
+ ret = nand_do_write_ops(part->master, to, &ops);
|
|
+ *retlen = ops.retlen;
|
|
+ chip->cur_ecc = &chip->ecc;
|
|
+ nand_release_device(part->master);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
* nand_do_write_oob - [MTD Interface] NAND write out-of-band
|
|
* @mtd: MTD device structure
|
|
* @to: offset to write to
|
|
@@ -2583,7 +2753,7 @@ static int nand_do_write_oob(struct mtd_
|
|
__func__, (unsigned int)to, (int)ops->ooblen);
|
|
|
|
if (ops->mode == MTD_OPS_AUTO_OOB)
|
|
- len = chip->ecc.layout->oobavail;
|
|
+ len = chip->cur_ecc->layout->oobavail;
|
|
else
|
|
len = mtd->oobsize;
|
|
|
|
@@ -2637,9 +2807,11 @@ static int nand_do_write_oob(struct mtd_
|
|
nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops);
|
|
|
|
if (ops->mode == MTD_OPS_RAW)
|
|
- status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask);
|
|
+ status = chip->cur_ecc->write_oob_raw(mtd, chip,
|
|
+ page & chip->pagemask);
|
|
else
|
|
- status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
|
|
+ status = chip->cur_ecc->write_oob(mtd, chip,
|
|
+ page & chip->pagemask);
|
|
|
|
chip->select_chip(mtd, -1);
|
|
|
|
@@ -2694,6 +2866,54 @@ out:
|
|
}
|
|
|
|
/**
|
|
+ * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
|
|
+ * @mtd: MTD device structure
|
|
+ * @to: offset to write to
|
|
+ * @ops: oob operation description structure
|
|
+ */
|
|
+static int nand_part_write_oob(struct mtd_info *mtd, loff_t to,
|
|
+ struct mtd_oob_ops *ops)
|
|
+{
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
+ struct nand_part *part = to_nand_part(mtd);
|
|
+ int ret = -ENOTSUPP;
|
|
+
|
|
+ ops->retlen = 0;
|
|
+
|
|
+ /* Do not allow writes past end of device */
|
|
+ if (ops->datbuf && (to + ops->len) > mtd->size) {
|
|
+ pr_debug("%s: attempt to write beyond end of device\n",
|
|
+ __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ to += part->offset;
|
|
+ nand_get_device(part->master, FL_WRITING);
|
|
+ if (part->ecc)
|
|
+ chip->cur_ecc = part->ecc;
|
|
+
|
|
+ switch (ops->mode) {
|
|
+ case MTD_OPS_PLACE_OOB:
|
|
+ case MTD_OPS_AUTO_OOB:
|
|
+ case MTD_OPS_RAW:
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (!ops->datbuf)
|
|
+ ret = nand_do_write_oob(part->master, to, ops);
|
|
+ else
|
|
+ ret = nand_do_write_ops(part->master, to, ops);
|
|
+
|
|
+out:
|
|
+ chip->cur_ecc = &chip->ecc;
|
|
+ nand_release_device(part->master);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
* single_erase - [GENERIC] NAND standard block erase command function
|
|
* @mtd: MTD device structure
|
|
* @page: the page address of the block which will be erased
|
|
@@ -2723,6 +2943,29 @@ static int nand_erase(struct mtd_info *m
|
|
}
|
|
|
|
/**
|
|
+ * nand_part_erase - [MTD Interface] erase partition block(s)
|
|
+ * @mtd: MTD device structure
|
|
+ * @instr: erase instruction
|
|
+ *
|
|
+ * Erase one ore more blocks.
|
|
+ */
|
|
+static int nand_part_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|
+{
|
|
+ struct nand_part *part = to_nand_part(mtd);
|
|
+ int ret;
|
|
+
|
|
+ instr->addr += part->offset;
|
|
+ ret = nand_erase_nand(part->master, instr, 0);
|
|
+ if (ret) {
|
|
+ if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
|
|
+ instr->fail_addr -= part->offset;
|
|
+ instr->addr -= part->offset;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
* nand_erase_nand - [INTERN] erase block(s)
|
|
* @mtd: MTD device structure
|
|
* @instr: erase instruction
|
|
@@ -2864,6 +3107,18 @@ static int nand_block_isbad(struct mtd_i
|
|
}
|
|
|
|
/**
|
|
+ * nand_part_block_isbad - [MTD Interface] Check if block at offset is bad
|
|
+ * @mtd: MTD device structure
|
|
+ * @offs: offset relative to mtd start
|
|
+ */
|
|
+static int nand_part_block_isbad(struct mtd_info *mtd, loff_t offs)
|
|
+{
|
|
+ struct nand_part *part = to_nand_part(mtd);
|
|
+
|
|
+ return nand_block_checkbad(part->master, part->offset + offs, 1, 0);
|
|
+}
|
|
+
|
|
+/**
|
|
* nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
|
|
* @mtd: MTD device structure
|
|
* @ofs: offset relative to mtd start
|
|
@@ -2884,6 +3139,33 @@ static int nand_block_markbad(struct mtd
|
|
}
|
|
|
|
/**
|
|
+ * nand_part_block_markbad - [MTD Interface] Mark block at the given offset as
|
|
+ * bad
|
|
+ * @mtd: MTD device structure
|
|
+ * @ofs: offset relative to mtd start
|
|
+ */
|
|
+static int nand_part_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|
+{
|
|
+ struct nand_part *part = to_nand_part(mtd);
|
|
+ int ret;
|
|
+
|
|
+ ofs += part->offset;
|
|
+ ret = nand_block_isbad(part->master, ofs);
|
|
+ if (ret) {
|
|
+ /* If it was bad already, return success and do nothing */
|
|
+ if (ret > 0)
|
|
+ return 0;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = nand_block_markbad_lowlevel(part->master, ofs);
|
|
+ if (!ret)
|
|
+ mtd->ecc_stats.badblocks++;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
* nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
|
|
* @mtd: MTD device structure
|
|
* @chip: nand chip info structure
|
|
@@ -4099,6 +4381,169 @@ static int nand_ecc_ctrl_init(struct mtd
|
|
}
|
|
|
|
/**
|
|
+ * nand_add_partition - [NAND Interface] Add a NAND partition to a NAND device
|
|
+ * @master: MTD device structure representing the NAND device
|
|
+ * @part: NAND partition to add to the NAND device
|
|
+ *
|
|
+ * Adds a NAND partition to a NAND device.
|
|
+ * The NAND partition cannot overlap with another existing partition.
|
|
+ *
|
|
+ * Returns zero in case of success and a negative error code in case of failure.
|
|
+ */
|
|
+int nand_add_partition(struct mtd_info *master, struct nand_part *part)
|
|
+{
|
|
+ struct nand_chip *chip = master->priv;
|
|
+ struct mtd_info *mtd = &part->mtd;
|
|
+ struct nand_ecc_ctrl *ecc = part->ecc;
|
|
+ struct nand_part *pos;
|
|
+ bool inserted = false;
|
|
+ int ret;
|
|
+
|
|
+ /* set up the MTD object for this partition */
|
|
+ mtd->type = master->type;
|
|
+ mtd->flags = master->flags & ~mtd->flags;
|
|
+ mtd->writesize = master->writesize;
|
|
+ mtd->writebufsize = master->writebufsize;
|
|
+ mtd->oobsize = master->oobsize;
|
|
+ mtd->oobavail = master->oobavail;
|
|
+ mtd->subpage_sft = master->subpage_sft;
|
|
+ mtd->erasesize = master->erasesize;
|
|
+
|
|
+ mtd->priv = chip;
|
|
+ mtd->owner = master->owner;
|
|
+ mtd->backing_dev_info = master->backing_dev_info;
|
|
+
|
|
+ mtd->dev.parent = master->dev.parent;
|
|
+
|
|
+ if (ecc) {
|
|
+ ret = nand_ecc_ctrl_init(mtd, ecc);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ } else {
|
|
+ ecc = &chip->ecc;
|
|
+ }
|
|
+
|
|
+ mtd->_erase = nand_part_erase;
|
|
+ mtd->_point = NULL;
|
|
+ mtd->_unpoint = NULL;
|
|
+ mtd->_read = nand_part_read;
|
|
+ mtd->_write = nand_part_write;
|
|
+ mtd->_panic_write = panic_nand_part_write;
|
|
+ mtd->_read_oob = nand_part_read_oob;
|
|
+ mtd->_write_oob = nand_part_write_oob;
|
|
+ mtd->_sync = nand_sync;
|
|
+ mtd->_lock = NULL;
|
|
+ mtd->_unlock = NULL;
|
|
+ mtd->_suspend = nand_suspend;
|
|
+ mtd->_resume = nand_resume;
|
|
+ mtd->_block_isbad = nand_part_block_isbad;
|
|
+ mtd->_block_markbad = nand_part_block_markbad;
|
|
+
|
|
+ /* propagate ecc info to mtd_info */
|
|
+ mtd->ecclayout = ecc->layout;
|
|
+ mtd->ecc_strength = ecc->strength;
|
|
+ mtd->ecc_step_size = ecc->size;
|
|
+ /*
|
|
+ * Initialize bitflip_threshold to its default prior scan_bbt() call.
|
|
+ * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
|
|
+ * properly set.
|
|
+ */
|
|
+ if (!mtd->bitflip_threshold)
|
|
+ mtd->bitflip_threshold = mtd->ecc_strength;
|
|
+
|
|
+ part->master = master;
|
|
+
|
|
+ mutex_lock(&chip->part_lock);
|
|
+ list_for_each_entry(pos, &chip->partitions, node) {
|
|
+ if (part->offset >= pos->offset + pos->mtd.size) {
|
|
+ continue;
|
|
+ } else if (part->offset + mtd->size > pos->offset) {
|
|
+ ret = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ list_add(&part->node, pos->node.prev);
|
|
+ inserted = true;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (!inserted)
|
|
+ list_add_tail(&part->node, &chip->partitions);
|
|
+
|
|
+ ret = mtd_device_register(mtd, NULL, 0);
|
|
+ if (ret) {
|
|
+ list_del(&part->node);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (master->_block_isbad) {
|
|
+ uint64_t offs = 0;
|
|
+
|
|
+ while (offs < mtd->size) {
|
|
+ if (mtd_block_isreserved(master, offs + part->offset))
|
|
+ mtd->ecc_stats.bbtblocks++;
|
|
+ else if (mtd_block_isbad(master, offs + part->offset))
|
|
+ mtd->ecc_stats.badblocks++;
|
|
+ offs += mtd->erasesize;
|
|
+ }
|
|
+ }
|
|
+
|
|
+out:
|
|
+ mutex_unlock(&chip->part_lock);
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL(nand_add_partition);
|
|
+
|
|
+/**
|
|
+ * nand_del_partition - [NAND Interface] Delete a NAND part from a NAND dev
|
|
+ * @part: NAND partition to delete
|
|
+ *
|
|
+ * Deletes a NAND partition from a NAND device.
|
|
+ */
|
|
+void nand_del_partition(struct nand_part *part)
|
|
+{
|
|
+ struct nand_chip *chip = part->mtd.priv;
|
|
+
|
|
+ mutex_lock(&chip->part_lock);
|
|
+ mtd_device_unregister(&part->mtd);
|
|
+ list_del(&part->node);
|
|
+ mutex_unlock(&chip->part_lock);
|
|
+
|
|
+ if (part->ecc && part->ecc->mode == NAND_ECC_SOFT_BCH)
|
|
+ nand_bch_free((struct nand_bch_control *)part->ecc->priv);
|
|
+
|
|
+ if (part->release)
|
|
+ part->release(part);
|
|
+}
|
|
+EXPORT_SYMBOL(nand_del_partition);
|
|
+
|
|
+/*
|
|
+ * NAND part release function. Used by nandpart_alloc as its release function.
|
|
+ */
|
|
+static void nandpart_release(struct nand_part *part)
|
|
+{
|
|
+ kfree(part);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * nandpart_alloc - [NAND Interface] Allocate a NAND part struct
|
|
+ *
|
|
+ * Allocate a NAND partition and assign the nandpart release function.
|
|
+ * This nand_part struct must be filled before passing it to the
|
|
+ * nand_add_partition function.
|
|
+ */
|
|
+struct nand_part *nandpart_alloc(void)
|
|
+{
|
|
+ struct nand_part *part = kzalloc(sizeof(*part), GFP_KERNEL);
|
|
+ if (!part)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+ part->release = nandpart_release;
|
|
+
|
|
+ return part;
|
|
+}
|
|
+EXPORT_SYMBOL(nandpart_alloc);
|
|
+
|
|
+/**
|
|
* nand_scan_tail - [NAND Interface] Scan for the NAND device
|
|
* @mtd: MTD device structure
|
|
*
|
|
@@ -4146,6 +4591,11 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
return ret;
|
|
}
|
|
|
|
+ INIT_LIST_HEAD(&chip->partitions);
|
|
+ mutex_init(&chip->part_lock);
|
|
+
|
|
+ chip->cur_ecc = &chip->ecc;
|
|
+
|
|
/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
|
|
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
|
|
switch (ecc->steps) {
|
|
--- a/drivers/mtd/nand/nand_bch.c
|
|
+++ b/drivers/mtd/nand/nand_bch.c
|
|
@@ -53,14 +53,14 @@ int nand_bch_calculate_ecc(struct mtd_in
|
|
unsigned char *code)
|
|
{
|
|
const struct nand_chip *chip = mtd->priv;
|
|
- struct nand_bch_control *nbc = chip->ecc.priv;
|
|
+ struct nand_bch_control *nbc = chip->cur_ecc->priv;
|
|
unsigned int i;
|
|
|
|
- memset(code, 0, chip->ecc.bytes);
|
|
- encode_bch(nbc->bch, buf, chip->ecc.size, code);
|
|
+ memset(code, 0, chip->cur_ecc->bytes);
|
|
+ encode_bch(nbc->bch, buf, chip->cur_ecc->size, code);
|
|
|
|
/* apply mask so that an erased page is a valid codeword */
|
|
- for (i = 0; i < chip->ecc.bytes; i++)
|
|
+ for (i = 0; i < chip->cur_ecc->bytes; i++)
|
|
code[i] ^= nbc->eccmask[i];
|
|
|
|
return 0;
|
|
@@ -80,15 +80,15 @@ int nand_bch_correct_data(struct mtd_inf
|
|
unsigned char *read_ecc, unsigned char *calc_ecc)
|
|
{
|
|
const struct nand_chip *chip = mtd->priv;
|
|
- struct nand_bch_control *nbc = chip->ecc.priv;
|
|
+ struct nand_bch_control *nbc = chip->cur_ecc->priv;
|
|
unsigned int *errloc = nbc->errloc;
|
|
int i, count;
|
|
|
|
- count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
|
|
- NULL, errloc);
|
|
+ count = decode_bch(nbc->bch, NULL, chip->cur_ecc->size, read_ecc,
|
|
+ calc_ecc, NULL, errloc);
|
|
if (count > 0) {
|
|
for (i = 0; i < count; i++) {
|
|
- if (errloc[i] < (chip->ecc.size*8))
|
|
+ if (errloc[i] < (chip->cur_ecc->size*8))
|
|
/* error is located in data, correct it */
|
|
buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
|
|
/* else error in ecc, no action needed */
|
|
--- a/drivers/mtd/nand/nand_ecc.c
|
|
+++ b/drivers/mtd/nand/nand_ecc.c
|
|
@@ -424,7 +424,7 @@ int nand_calculate_ecc(struct mtd_info *
|
|
unsigned char *code)
|
|
{
|
|
__nand_calculate_ecc(buf,
|
|
- ((struct nand_chip *)mtd->priv)->ecc.size, code);
|
|
+ ((struct nand_chip *)mtd->priv)->cur_ecc->size, code);
|
|
|
|
return 0;
|
|
}
|
|
@@ -524,7 +524,7 @@ int nand_correct_data(struct mtd_info *m
|
|
unsigned char *read_ecc, unsigned char *calc_ecc)
|
|
{
|
|
return __nand_correct_data(buf, read_ecc, calc_ecc,
|
|
- ((struct nand_chip *)mtd->priv)->ecc.size);
|
|
+ ((struct nand_chip *)mtd->priv)->cur_ecc->size);
|
|
}
|
|
EXPORT_SYMBOL(nand_correct_data);
|
|
|
|
--- a/include/linux/mtd/nand.h
|
|
+++ b/include/linux/mtd/nand.h
|
|
@@ -708,6 +708,7 @@ struct nand_chip {
|
|
struct nand_hw_control *controller;
|
|
|
|
struct nand_ecc_ctrl ecc;
|
|
+ struct nand_ecc_ctrl *cur_ecc;
|
|
struct nand_buffers *buffers;
|
|
struct nand_hw_control hwcontrol;
|
|
|
|
@@ -717,9 +718,46 @@ struct nand_chip {
|
|
|
|
struct nand_bbt_descr *badblock_pattern;
|
|
|
|
+ struct list_head partitions;
|
|
+ struct mutex part_lock;
|
|
+
|
|
void *priv;
|
|
};
|
|
|
|
+/**
|
|
+ * struct nand_part - NAND partition structure
|
|
+ * @node: list node used to attach the partition to its NAND dev
|
|
+ * @mtd: MTD partiton info
|
|
+ * @master: MTD device representing the NAND chip
|
|
+ * @offset: partition offset
|
|
+ * @ecc: partition specific ECC struct
|
|
+ * @release: function used to release this nand_part struct
|
|
+ *
|
|
+ * NAND partitions work as standard MTD partitions except it can override
|
|
+ * NAND chip ECC handling.
|
|
+ * This is particularly useful for SoCs that need specific ECC configs to boot
|
|
+ * from NAND while these ECC configs do not fit the NAND chip ECC requirements.
|
|
+ */
|
|
+struct nand_part {
|
|
+ struct list_head node;
|
|
+ struct mtd_info mtd;
|
|
+ struct mtd_info *master;
|
|
+ uint64_t offset;
|
|
+ struct nand_ecc_ctrl *ecc;
|
|
+ void (*release)(struct nand_part *part);
|
|
+};
|
|
+
|
|
+static inline struct nand_part *to_nand_part(struct mtd_info *mtd)
|
|
+{
|
|
+ return container_of(mtd, struct nand_part, mtd);
|
|
+}
|
|
+
|
|
+int nand_add_partition(struct mtd_info *master, struct nand_part *part);
|
|
+
|
|
+void nand_del_partition(struct nand_part *part);
|
|
+
|
|
+struct nand_part *nandpart_alloc(void);
|
|
+
|
|
/*
|
|
* NAND Flash Manufacturer ID Codes
|
|
*/
|
|
|