848 lines
26 KiB
Diff
848 lines
26 KiB
Diff
From 293984c7f167a08285596ef2166d8ab9cb571778 Mon Sep 17 00:00:00 2001
|
|
From: Boris BREZILLON <boris.brezillon@free-electrons.com>
|
|
Date: Mon, 28 Jul 2014 14:46:26 +0200
|
|
Subject: [PATCH] mtd: nand: Introduce a randomizer layer in the NAND framework
|
|
|
|
This patch introduce a new layer in the NAND framework to support both HW
|
|
and SW randomizers.
|
|
|
|
This randomization is required on some MLC/TLC NAND chips which do not
|
|
support large islands of same patterns.
|
|
|
|
The randomizer layer defines a nand_rnd_ctrl struct which is intended to
|
|
be used by NAND core functions or NAND drivers to randomize/derandomize
|
|
data stored on NAND chips.
|
|
|
|
The implementation can implement any of these functions:
|
|
- config: prepare a random transfer to/from the NAND chip
|
|
- write_buf: randomize and write data to the NAND chip
|
|
- read_buf: read and derandomize data from the NAND chip
|
|
|
|
read/write_buf functions are always called after a config call.
|
|
The config call specify the page, the column within the page and the action
|
|
that will take place after the config (either read or write).
|
|
If column is set to -1, the randomizer is disabled.
|
|
If page is set to -1, we keep working on the same page.
|
|
|
|
The randomizer layer provides helper functions that choose wether the
|
|
randomizer or the chip read/write_buf should be used.
|
|
|
|
Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
|
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
---
|
|
drivers/mtd/nand/nand_base.c | 278 ++++++++++++++++++++++++++++++++++---------
|
|
include/linux/mtd/nand.h | 98 +++++++++++++++
|
|
2 files changed, 321 insertions(+), 55 deletions(-)
|
|
|
|
--- a/drivers/mtd/nand/nand_base.c
|
|
+++ b/drivers/mtd/nand/nand_base.c
|
|
@@ -1102,6 +1102,62 @@ out:
|
|
EXPORT_SYMBOL(nand_lock);
|
|
|
|
/**
|
|
+ * nand_rnd_is_activ - check wether a region of a NAND page requires NAND
|
|
+ * randomizer to be disabled
|
|
+ * @mtd: mtd info
|
|
+ * @page: NAND page
|
|
+ * @column: offset within the page
|
|
+ * @len: len of the region
|
|
+ *
|
|
+ * Returns 1 if the randomizer should be enabled, 0 if not, or -ERR in case of
|
|
+ * error.
|
|
+ *
|
|
+ * In case of success len will contain the size of the region:
|
|
+ * - if the requested region fits in a NAND random region len will not change
|
|
+ * - else len will be replaced by the available length within the NAND random
|
|
+ * region
|
|
+ */
|
|
+int nand_rnd_is_activ(struct mtd_info *mtd, int page, int column, int *len)
|
|
+{
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
+ struct nand_rnd_layout *layout = chip->cur_rnd->layout;
|
|
+ struct nand_rndfree *range;
|
|
+ int ret = 1;
|
|
+ int tmp;
|
|
+ int i;
|
|
+
|
|
+ if (!len || *len < 0 || column < 0 ||
|
|
+ column + *len > mtd->writesize + mtd->oobsize)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (layout) {
|
|
+ for (i = 0; i < layout->nranges; i++) {
|
|
+ range = &layout->ranges[i];
|
|
+ if (column + *len <= range->offset) {
|
|
+ break;
|
|
+ } else if (column >= range->offset + range->length) {
|
|
+ continue;
|
|
+ } else if (column < range->offset) {
|
|
+ tmp = range->offset - column;
|
|
+ if (*len > tmp)
|
|
+ *len = tmp;
|
|
+ break;
|
|
+ } else {
|
|
+ tmp = range->offset + range->length - column;
|
|
+ if (*len > tmp)
|
|
+ *len = tmp;
|
|
+ ret = 0;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL(nand_rnd_is_activ);
|
|
+
|
|
+/**
|
|
* nand_page_is_empty - check wether a NAND page contains only FFs
|
|
* @mtd: mtd info
|
|
* @data: data buffer
|
|
@@ -1246,9 +1302,14 @@ EXPORT_SYMBOL(nand_pst_create);
|
|
static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|
uint8_t *buf, int oob_required, int page)
|
|
{
|
|
- chip->read_buf(mtd, buf, mtd->writesize);
|
|
- if (oob_required)
|
|
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
+ nand_rnd_config(mtd, page, 0, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, buf, mtd->writesize);
|
|
+ if (oob_required) {
|
|
+ nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
+ }
|
|
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -1270,28 +1331,40 @@ static int nand_read_page_raw_syndrome(s
|
|
int eccbytes = chip->cur_ecc->bytes;
|
|
uint8_t *oob = chip->oob_poi;
|
|
int steps, size;
|
|
+ int column = 0;
|
|
|
|
for (steps = chip->cur_ecc->steps; steps > 0; steps--) {
|
|
- chip->read_buf(mtd, buf, eccsize);
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, buf, eccsize);
|
|
buf += eccsize;
|
|
+ column += eccsize;
|
|
|
|
if (chip->cur_ecc->prepad) {
|
|
- chip->read_buf(mtd, oob, chip->cur_ecc->prepad);
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, oob, chip->cur_ecc->prepad);
|
|
oob += chip->cur_ecc->prepad;
|
|
+ column += chip->cur_ecc->prepad;
|
|
}
|
|
|
|
- chip->read_buf(mtd, oob, eccbytes);
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, oob, eccbytes);
|
|
oob += eccbytes;
|
|
+ column += eccbytes;
|
|
|
|
if (chip->cur_ecc->postpad) {
|
|
- chip->read_buf(mtd, oob, chip->cur_ecc->postpad);
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, oob, chip->cur_ecc->postpad);
|
|
oob += chip->cur_ecc->postpad;
|
|
+ column += chip->cur_ecc->postpad;
|
|
}
|
|
}
|
|
|
|
size = mtd->oobsize - (oob - chip->oob_poi);
|
|
- if (size)
|
|
- chip->read_buf(mtd, oob, size);
|
|
+ if (size) {
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, oob, size);
|
|
+ }
|
|
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
|
|
|
|
return 0;
|
|
}
|
|
@@ -1380,7 +1453,8 @@ static int nand_read_subpage(struct mtd_
|
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
|
|
|
|
p = bufpoi + data_col_addr;
|
|
- chip->read_buf(mtd, p, datafrag_len);
|
|
+ nand_rnd_config(mtd, -1, data_col_addr, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, p, datafrag_len);
|
|
|
|
/* Calculate ECC */
|
|
for (i = 0; i < eccfrag_len;
|
|
@@ -1399,7 +1473,8 @@ static int nand_read_subpage(struct mtd_
|
|
}
|
|
if (gaps) {
|
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
|
|
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
+ nand_rnd_config(mtd, -1, mtd->writesize, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
} else {
|
|
/*
|
|
* Send the command to read the particular ECC bytes take care
|
|
@@ -1415,7 +1490,8 @@ static int nand_read_subpage(struct mtd_
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
|
|
mtd->writesize + aligned_pos, -1);
|
|
- chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
|
|
+ nand_rnd_config(mtd, -1, mtd->writesize + aligned_pos, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
|
|
}
|
|
|
|
for (i = 0; i < eccfrag_len; i++)
|
|
@@ -1436,6 +1512,7 @@ static int nand_read_subpage(struct mtd_
|
|
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
|
}
|
|
}
|
|
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
|
|
return max_bitflips;
|
|
}
|
|
|
|
@@ -1460,13 +1537,17 @@ static int nand_read_page_hwecc(struct m
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
|
uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
|
|
unsigned int max_bitflips = 0;
|
|
+ int column = 0;
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
chip->cur_ecc->hwctl(mtd, NAND_ECC_READ);
|
|
- chip->read_buf(mtd, p, eccsize);
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, p, eccsize);
|
|
chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]);
|
|
+ column += eccsize;
|
|
}
|
|
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
|
|
for (i = 0; i < chip->cur_ecc->total; i++)
|
|
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
|
@@ -1486,6 +1567,7 @@ static int nand_read_page_hwecc(struct m
|
|
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
|
}
|
|
}
|
|
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
|
|
return max_bitflips;
|
|
}
|
|
|
|
@@ -1514,11 +1596,14 @@ static int nand_read_page_hwecc_oob_firs
|
|
uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
unsigned int max_bitflips = 0;
|
|
+ int column = 0;
|
|
|
|
/* Read the OOB area first */
|
|
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
|
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
+ nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
|
|
+ column = 0;
|
|
|
|
for (i = 0; i < chip->cur_ecc->total; i++)
|
|
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
|
@@ -1527,7 +1612,8 @@ static int nand_read_page_hwecc_oob_firs
|
|
int stat;
|
|
|
|
chip->cur_ecc->hwctl(mtd, NAND_ECC_READ);
|
|
- chip->read_buf(mtd, p, eccsize);
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, p, eccsize);
|
|
chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]);
|
|
|
|
stat = chip->cur_ecc->correct(mtd, p, &ecc_code[i], NULL);
|
|
@@ -1538,6 +1624,7 @@ static int nand_read_page_hwecc_oob_firs
|
|
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
|
}
|
|
}
|
|
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
|
|
return max_bitflips;
|
|
}
|
|
|
|
@@ -1561,20 +1648,27 @@ static int nand_read_page_syndrome(struc
|
|
uint8_t *p = buf;
|
|
uint8_t *oob = chip->oob_poi;
|
|
unsigned int max_bitflips = 0;
|
|
+ int column = 0;
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
int stat;
|
|
|
|
chip->cur_ecc->hwctl(mtd, NAND_ECC_READ);
|
|
- chip->read_buf(mtd, p, eccsize);
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, p, eccsize);
|
|
+ column += eccsize;
|
|
|
|
if (chip->cur_ecc->prepad) {
|
|
- chip->read_buf(mtd, oob, chip->cur_ecc->prepad);
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, oob, chip->cur_ecc->prepad);
|
|
oob += chip->cur_ecc->prepad;
|
|
}
|
|
|
|
chip->cur_ecc->hwctl(mtd, NAND_ECC_READSYN);
|
|
- chip->read_buf(mtd, oob, eccbytes);
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, oob, eccbytes);
|
|
+ column += eccbytes;
|
|
+
|
|
stat = chip->cur_ecc->correct(mtd, p, oob, NULL);
|
|
|
|
if (stat < 0) {
|
|
@@ -1587,29 +1681,36 @@ static int nand_read_page_syndrome(struc
|
|
oob += eccbytes;
|
|
|
|
if (chip->cur_ecc->postpad) {
|
|
- chip->read_buf(mtd, oob, chip->cur_ecc->postpad);
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, oob, chip->cur_ecc->postpad);
|
|
+ column += chip->cur_ecc->postpad;
|
|
oob += chip->cur_ecc->postpad;
|
|
}
|
|
}
|
|
|
|
/* Calculate remaining oob bytes */
|
|
i = mtd->oobsize - (oob - chip->oob_poi);
|
|
- if (i)
|
|
- chip->read_buf(mtd, oob, i);
|
|
+ if (i) {
|
|
+ nand_rnd_config(mtd, page, column, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, oob, i);
|
|
+ }
|
|
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
|
|
|
|
return max_bitflips;
|
|
}
|
|
|
|
/**
|
|
* 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:
|
|
@@ -1737,6 +1838,7 @@ read_retry:
|
|
* Now read the page into the buffer. Absent an error,
|
|
* the read methods return max bitflips per ecc step.
|
|
*/
|
|
+ nand_rnd_config(mtd, page, -1, NAND_RND_READ);
|
|
if (unlikely(ops->mode == MTD_OPS_RAW))
|
|
ret = chip->cur_ecc->read_page_raw(mtd, chip,
|
|
bufpoi,
|
|
@@ -1753,6 +1855,8 @@ read_retry:
|
|
bufpoi,
|
|
oob_required,
|
|
page);
|
|
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
|
|
+
|
|
if (ret < 0) {
|
|
if (use_bufpoi)
|
|
/* Invalidate page cache */
|
|
@@ -1780,8 +1884,8 @@ read_retry:
|
|
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;
|
|
}
|
|
}
|
|
@@ -1909,12 +2013,15 @@ static int nand_part_read(struct mtd_inf
|
|
nand_get_device(part->master, FL_READING);
|
|
if (part->ecc)
|
|
chip->cur_ecc = part->ecc;
|
|
+ if (part->rnd)
|
|
+ chip->cur_rnd = part->rnd;
|
|
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_rnd = &chip->rnd;
|
|
chip->cur_ecc = &chip->ecc;
|
|
nand_release_device(part->master);
|
|
return ret;
|
|
@@ -1930,7 +2037,9 @@ static int nand_read_oob_std(struct mtd_
|
|
int page)
|
|
{
|
|
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
|
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
+ nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
|
|
return 0;
|
|
}
|
|
|
|
@@ -1949,7 +2058,7 @@ static int nand_read_oob_syndrome(struct
|
|
chip->cur_ecc->postpad;
|
|
int eccsize = chip->cur_ecc->size;
|
|
uint8_t *bufpoi = chip->oob_poi;
|
|
- int i, toread, sndrnd = 0, pos;
|
|
+ int i, toread, sndrnd = 0, pos = eccsize;
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_READ0, chip->cur_ecc->size, page);
|
|
for (i = 0; i < chip->cur_ecc->steps; i++) {
|
|
@@ -1962,12 +2071,17 @@ static int nand_read_oob_syndrome(struct
|
|
} else
|
|
sndrnd = 1;
|
|
toread = min_t(int, length, chunk);
|
|
- chip->read_buf(mtd, bufpoi, toread);
|
|
+ nand_rnd_config(mtd, page, pos, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, bufpoi, toread);
|
|
bufpoi += toread;
|
|
length -= toread;
|
|
}
|
|
- if (length > 0)
|
|
- chip->read_buf(mtd, bufpoi, length);
|
|
+ if (length > 0) {
|
|
+ pos = mtd->writesize + mtd->oobsize - length;
|
|
+ nand_rnd_config(mtd, page, pos, NAND_RND_READ);
|
|
+ nand_rnd_read_buf(mtd, bufpoi, length);
|
|
+ }
|
|
+ nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
|
|
|
|
return 0;
|
|
}
|
|
@@ -1986,7 +2100,9 @@ static int nand_write_oob_std(struct mtd
|
|
int length = mtd->oobsize;
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
|
|
- chip->write_buf(mtd, buf, length);
|
|
+ nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, buf, length);
|
|
+ nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
|
|
/* Send command to program the OOB data */
|
|
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
|
|
|
@@ -2042,12 +2158,18 @@ static int nand_write_oob_syndrome(struc
|
|
} else
|
|
sndcmd = 1;
|
|
len = min_t(int, length, chunk);
|
|
- chip->write_buf(mtd, bufpoi, len);
|
|
+ nand_rnd_config(mtd, page, pos, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, bufpoi, len);
|
|
bufpoi += len;
|
|
length -= len;
|
|
}
|
|
- if (length > 0)
|
|
- chip->write_buf(mtd, bufpoi, length);
|
|
+ if (length > 0) {
|
|
+ pos = mtd->writesize + mtd->oobsize - length;
|
|
+ nand_rnd_config(mtd, page, pos, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, bufpoi, length);
|
|
+ }
|
|
+
|
|
+ nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
|
status = chip->waitfunc(mtd, chip);
|
|
@@ -2116,7 +2238,7 @@ static int nand_do_read_oob(struct mtd_i
|
|
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 */
|
|
@@ -2226,6 +2348,8 @@ static int nand_part_read_oob(struct mtd
|
|
nand_get_device(part->master, FL_READING);
|
|
if (part->ecc)
|
|
chip->cur_ecc = part->ecc;
|
|
+ if (part->rnd)
|
|
+ chip->cur_rnd = part->rnd;
|
|
|
|
switch (ops->mode) {
|
|
case MTD_OPS_PLACE_OOB:
|
|
@@ -2243,6 +2367,7 @@ static int nand_part_read_oob(struct mtd
|
|
ret = nand_do_read_ops(part->master, from, ops);
|
|
|
|
out:
|
|
+ chip->cur_rnd = &chip->rnd;
|
|
chip->cur_ecc = &chip->ecc;
|
|
nand_release_device(part->master);
|
|
return ret;
|
|
@@ -2261,9 +2386,11 @@ out:
|
|
static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|
const uint8_t *buf, int oob_required)
|
|
{
|
|
- chip->write_buf(mtd, buf, mtd->writesize);
|
|
- if (oob_required)
|
|
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
+ nand_rnd_write_buf(mtd, buf, mtd->writesize);
|
|
+ if (oob_required) {
|
|
+ nand_rnd_config(mtd, -1, mtd->writesize, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
+ }
|
|
|
|
return 0;
|
|
}
|
|
@@ -2285,28 +2412,39 @@ static int nand_write_page_raw_syndrome(
|
|
int eccbytes = chip->cur_ecc->bytes;
|
|
uint8_t *oob = chip->oob_poi;
|
|
int steps, size;
|
|
+ int column = 0;
|
|
|
|
for (steps = chip->cur_ecc->steps; steps > 0; steps--) {
|
|
- chip->write_buf(mtd, buf, eccsize);
|
|
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, buf, eccsize);
|
|
buf += eccsize;
|
|
+ column += eccsize;
|
|
|
|
if (chip->cur_ecc->prepad) {
|
|
- chip->write_buf(mtd, oob, chip->cur_ecc->prepad);
|
|
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, oob, chip->cur_ecc->prepad);
|
|
oob += chip->cur_ecc->prepad;
|
|
+ column += chip->cur_ecc->prepad;
|
|
}
|
|
|
|
- chip->write_buf(mtd, oob, eccbytes);
|
|
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, oob, eccbytes);
|
|
oob += eccbytes;
|
|
+ column += eccbytes;
|
|
|
|
if (chip->cur_ecc->postpad) {
|
|
- chip->write_buf(mtd, oob, chip->cur_ecc->postpad);
|
|
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, oob, chip->cur_ecc->postpad);
|
|
oob += chip->cur_ecc->postpad;
|
|
+ column += chip->cur_ecc->postpad;
|
|
}
|
|
}
|
|
|
|
size = mtd->oobsize - (oob - chip->oob_poi);
|
|
- if (size)
|
|
- chip->write_buf(mtd, oob, size);
|
|
+ if (size) {
|
|
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, oob, size);
|
|
+ }
|
|
|
|
return 0;
|
|
}
|
|
@@ -2353,17 +2491,21 @@ static int nand_write_page_hwecc(struct
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
const uint8_t *p = buf;
|
|
uint32_t *eccpos = chip->cur_ecc->layout->eccpos;
|
|
+ int column = 0;
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE);
|
|
- chip->write_buf(mtd, p, eccsize);
|
|
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, p, eccsize);
|
|
chip->cur_ecc->calculate(mtd, p, &ecc_calc[i]);
|
|
+ column += eccsize;
|
|
}
|
|
|
|
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);
|
|
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
|
|
return 0;
|
|
}
|
|
@@ -2399,7 +2541,9 @@ static int nand_write_subpage_hwecc(stru
|
|
chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE);
|
|
|
|
/* write data (untouched subpages already masked by 0xFF) */
|
|
- chip->write_buf(mtd, buf, ecc_size);
|
|
+ nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, buf, ecc_size);
|
|
+ offset += ecc_size;
|
|
|
|
/* mask ECC of un-touched subpages by padding 0xFF */
|
|
if ((step < start_step) || (step > end_step))
|
|
@@ -2424,7 +2568,8 @@ static int nand_write_subpage_hwecc(stru
|
|
chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
|
|
|
/* write OOB buffer to NAND device */
|
|
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
+ nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
|
|
return 0;
|
|
}
|
|
@@ -2449,31 +2594,42 @@ static int nand_write_page_syndrome(stru
|
|
int eccsteps = chip->cur_ecc->steps;
|
|
const uint8_t *p = buf;
|
|
uint8_t *oob = chip->oob_poi;
|
|
+ int column = 0;
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
|
|
chip->cur_ecc->hwctl(mtd, NAND_ECC_WRITE);
|
|
- chip->write_buf(mtd, p, eccsize);
|
|
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, p, eccsize);
|
|
+ column += eccsize;
|
|
|
|
if (chip->cur_ecc->prepad) {
|
|
- chip->write_buf(mtd, oob, chip->cur_ecc->prepad);
|
|
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, oob, chip->cur_ecc->prepad);
|
|
oob += chip->cur_ecc->prepad;
|
|
+ column += chip->cur_ecc->prepad;
|
|
}
|
|
|
|
chip->cur_ecc->calculate(mtd, p, oob);
|
|
- chip->write_buf(mtd, oob, eccbytes);
|
|
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, oob, eccbytes);
|
|
oob += eccbytes;
|
|
+ column += eccbytes;
|
|
|
|
if (chip->cur_ecc->postpad) {
|
|
- chip->write_buf(mtd, oob, chip->cur_ecc->postpad);
|
|
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, oob, chip->cur_ecc->postpad);
|
|
oob += chip->cur_ecc->postpad;
|
|
+ column += chip->cur_ecc->postpad;
|
|
}
|
|
}
|
|
|
|
/* Calculate remaining oob bytes */
|
|
i = mtd->oobsize - (oob - chip->oob_poi);
|
|
- if (i)
|
|
- chip->write_buf(mtd, oob, i);
|
|
+ if (i) {
|
|
+ nand_rnd_config(mtd, -1, column, NAND_RND_WRITE);
|
|
+ nand_rnd_write_buf(mtd, oob, i);
|
|
+ }
|
|
|
|
return 0;
|
|
}
|
|
@@ -2504,6 +2660,7 @@ static int nand_write_page(struct mtd_in
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
|
|
|
|
+ nand_rnd_config(mtd, page, 0, NAND_RND_WRITE);
|
|
if (unlikely(raw))
|
|
status = chip->cur_ecc->write_page_raw(mtd, chip, buf,
|
|
oob_required);
|
|
@@ -2514,6 +2671,7 @@ static int nand_write_page(struct mtd_in
|
|
else
|
|
status = chip->cur_ecc->write_page(mtd, chip, buf,
|
|
oob_required);
|
|
+ nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
|
|
|
|
if (status < 0)
|
|
return status;
|
|
@@ -2803,6 +2961,8 @@ static int panic_nand_part_write(struct
|
|
panic_nand_get_device(chip, part->master, FL_WRITING);
|
|
if (part->ecc)
|
|
chip->cur_ecc = part->ecc;
|
|
+ if (part->rnd)
|
|
+ chip->cur_rnd = part->rnd;
|
|
|
|
ops.len = len;
|
|
ops.datbuf = (uint8_t *)buf;
|
|
@@ -2811,6 +2971,7 @@ static int panic_nand_part_write(struct
|
|
|
|
ret = nand_do_write_ops(part->master, to, &ops);
|
|
|
|
+ chip->cur_rnd = &chip->rnd;
|
|
chip->cur_ecc = &chip->ecc;
|
|
*retlen = ops.retlen;
|
|
return ret;
|
|
@@ -2865,12 +3026,15 @@ static int nand_part_write(struct mtd_in
|
|
nand_get_device(part->master, FL_WRITING);
|
|
if (part->ecc)
|
|
chip->cur_ecc = part->ecc;
|
|
+ if (part->rnd)
|
|
+ chip->cur_rnd = part->rnd;
|
|
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_rnd = &chip->rnd;
|
|
chip->cur_ecc = &chip->ecc;
|
|
nand_release_device(part->master);
|
|
return ret;
|
|
@@ -3032,6 +3196,8 @@ static int nand_part_write_oob(struct mt
|
|
nand_get_device(part->master, FL_WRITING);
|
|
if (part->ecc)
|
|
chip->cur_ecc = part->ecc;
|
|
+ if (part->rnd)
|
|
+ chip->cur_rnd = part->rnd;
|
|
|
|
switch (ops->mode) {
|
|
case MTD_OPS_PLACE_OOB:
|
|
@@ -3049,6 +3215,7 @@ static int nand_part_write_oob(struct mt
|
|
ret = nand_do_write_ops(part->master, to, ops);
|
|
|
|
out:
|
|
+ chip->cur_rnd = &chip->rnd;
|
|
chip->cur_ecc = &chip->ecc;
|
|
nand_release_device(part->master);
|
|
return ret;
|
|
@@ -4749,6 +4916,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
mutex_init(&chip->part_lock);
|
|
|
|
chip->cur_ecc = &chip->ecc;
|
|
+ chip->cur_rnd = &chip->rnd;
|
|
|
|
/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
|
|
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
|
|
--- a/include/linux/mtd/nand.h
|
|
+++ b/include/linux/mtd/nand.h
|
|
@@ -539,6 +539,64 @@ void nand_page_set_status(struct mtd_inf
|
|
|
|
int nand_pst_create(struct mtd_info *mtd);
|
|
|
|
+/*
|
|
+ * Constants for randomizer modes
|
|
+ */
|
|
+typedef enum {
|
|
+ NAND_RND_NONE,
|
|
+ NAND_RND_SOFT,
|
|
+ NAND_RND_HW,
|
|
+} nand_rnd_modes_t;
|
|
+
|
|
+/*
|
|
+ * Constants for randomizer actions
|
|
+ */
|
|
+enum nand_rnd_action {
|
|
+ NAND_RND_NO_ACTION,
|
|
+ NAND_RND_READ,
|
|
+ NAND_RND_WRITE,
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct nand_rndfree - Structure defining a NAND page region where the
|
|
+ * randomizer should be disabled
|
|
+ * @offset: range offset
|
|
+ * @length: range length
|
|
+ */
|
|
+struct nand_rndfree {
|
|
+ u32 offset;
|
|
+ u32 length;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct nand_rnd_layout - Structure defining rndfree regions
|
|
+ * @nranges: number of ranges
|
|
+ * @ranges: array defining the rndfree regions
|
|
+ */
|
|
+struct nand_rnd_layout {
|
|
+ int nranges;
|
|
+ struct nand_rndfree ranges[0];
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct nand_rnd_ctrl - Randomizer Control structure
|
|
+ * @mode: Randomizer mode
|
|
+ * @config: function to prepare the randomizer (i.e.: set the appropriate
|
|
+ * seed/init value).
|
|
+ * @read_buf: function that read from the NAND and descramble the retrieved
|
|
+ * data.
|
|
+ * @write_buf: function that scramble data before writing it to the NAND.
|
|
+ */
|
|
+struct nand_rnd_ctrl {
|
|
+ nand_rnd_modes_t mode;
|
|
+ struct nand_rnd_layout *layout;
|
|
+ void *priv;
|
|
+ int (*config)(struct mtd_info *mtd, int page, int column,
|
|
+ enum nand_rnd_action action);
|
|
+ void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
|
+ void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
|
|
+};
|
|
+
|
|
/**
|
|
* struct nand_buffers - buffer structure for read/write
|
|
* @ecccalc: buffer pointer for calculated ECC, size is oobsize.
|
|
@@ -731,6 +789,9 @@ struct nand_chip {
|
|
struct nand_buffers *buffers;
|
|
struct nand_hw_control hwcontrol;
|
|
|
|
+ struct nand_rnd_ctrl rnd;
|
|
+ struct nand_rnd_ctrl *cur_rnd;
|
|
+
|
|
uint8_t *bbt;
|
|
struct nand_bbt_descr *bbt_td;
|
|
struct nand_bbt_descr *bbt_md;
|
|
@@ -752,6 +813,7 @@ struct nand_chip {
|
|
* @master: MTD device representing the NAND chip
|
|
* @offset: partition offset
|
|
* @ecc: partition specific ECC struct
|
|
+ * @rnd: partition specific randomizer struct
|
|
* @release: function used to release this nand_part struct
|
|
*
|
|
* NAND partitions work as standard MTD partitions except it can override
|
|
@@ -765,6 +827,7 @@ struct nand_part {
|
|
struct mtd_info *master;
|
|
uint64_t offset;
|
|
struct nand_ecc_ctrl *ecc;
|
|
+ struct nand_rnd_ctrl *rnd;
|
|
void (*release)(struct nand_part *part);
|
|
};
|
|
|
|
@@ -902,6 +965,41 @@ extern int nand_erase_nand(struct mtd_in
|
|
extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|
size_t *retlen, uint8_t *buf);
|
|
|
|
+static inline int nand_rnd_config(struct mtd_info *mtd, int page, int column,
|
|
+ enum nand_rnd_action action)
|
|
+{
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
+
|
|
+ if (chip->cur_rnd && chip->cur_rnd->config)
|
|
+ return chip->cur_rnd->config(mtd, page, column, action);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline void nand_rnd_write_buf(struct mtd_info *mtd, const uint8_t *buf,
|
|
+ int len)
|
|
+{
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
+
|
|
+ if (chip->cur_rnd && chip->cur_rnd->read_buf)
|
|
+ chip->cur_rnd->write_buf(mtd, buf, len);
|
|
+ else
|
|
+ chip->write_buf(mtd, buf, len);
|
|
+}
|
|
+
|
|
+static inline void nand_rnd_read_buf(struct mtd_info *mtd, uint8_t *buf,
|
|
+ int len)
|
|
+{
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
+
|
|
+ if (chip->cur_rnd && chip->cur_rnd->read_buf)
|
|
+ chip->cur_rnd->read_buf(mtd, buf, len);
|
|
+ else
|
|
+ chip->read_buf(mtd, buf, len);
|
|
+}
|
|
+
|
|
+int nand_rnd_is_activ(struct mtd_info *mtd, int page, int column, int *len);
|
|
+
|
|
/**
|
|
* struct platform_nand_chip - chip level device structure
|
|
* @nr_chips: max. number of chips to scan for
|