|
@@ -45,56 +45,98 @@
|
|
#include <linux/bitops.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/io.h>
|
|
#include <linux/io.h>
|
|
#include <linux/mtd/partitions.h>
|
|
#include <linux/mtd/partitions.h>
|
|
-#include <linux/of_mtd.h>
|
|
|
|
|
|
+#include <linux/of.h>
|
|
|
|
+
|
|
|
|
+static int nand_get_device(struct mtd_info *mtd, int new_state);
|
|
|
|
+
|
|
|
|
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
|
|
|
|
+ struct mtd_oob_ops *ops);
|
|
|
|
|
|
/* Define default oob placement schemes for large and small page devices */
|
|
/* Define default oob placement schemes for large and small page devices */
|
|
-static struct nand_ecclayout nand_oob_8 = {
|
|
|
|
- .eccbytes = 3,
|
|
|
|
- .eccpos = {0, 1, 2},
|
|
|
|
- .oobfree = {
|
|
|
|
- {.offset = 3,
|
|
|
|
- .length = 2},
|
|
|
|
- {.offset = 6,
|
|
|
|
- .length = 2} }
|
|
|
|
-};
|
|
|
|
|
|
+static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
|
|
|
|
+ struct mtd_oob_region *oobregion)
|
|
|
|
+{
|
|
|
|
+ struct nand_chip *chip = mtd_to_nand(mtd);
|
|
|
|
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
|
|
|
|
|
|
-static struct nand_ecclayout nand_oob_16 = {
|
|
|
|
- .eccbytes = 6,
|
|
|
|
- .eccpos = {0, 1, 2, 3, 6, 7},
|
|
|
|
- .oobfree = {
|
|
|
|
- {.offset = 8,
|
|
|
|
- . length = 8} }
|
|
|
|
-};
|
|
|
|
|
|
+ if (section > 1)
|
|
|
|
+ return -ERANGE;
|
|
|
|
|
|
-static struct nand_ecclayout nand_oob_64 = {
|
|
|
|
- .eccbytes = 24,
|
|
|
|
- .eccpos = {
|
|
|
|
- 40, 41, 42, 43, 44, 45, 46, 47,
|
|
|
|
- 48, 49, 50, 51, 52, 53, 54, 55,
|
|
|
|
- 56, 57, 58, 59, 60, 61, 62, 63},
|
|
|
|
- .oobfree = {
|
|
|
|
- {.offset = 2,
|
|
|
|
- .length = 38} }
|
|
|
|
-};
|
|
|
|
|
|
+ if (!section) {
|
|
|
|
+ oobregion->offset = 0;
|
|
|
|
+ oobregion->length = 4;
|
|
|
|
+ } else {
|
|
|
|
+ oobregion->offset = 6;
|
|
|
|
+ oobregion->length = ecc->total - 4;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
|
|
|
|
+ struct mtd_oob_region *oobregion)
|
|
|
|
+{
|
|
|
|
+ if (section > 1)
|
|
|
|
+ return -ERANGE;
|
|
|
|
+
|
|
|
|
+ if (mtd->oobsize == 16) {
|
|
|
|
+ if (section)
|
|
|
|
+ return -ERANGE;
|
|
|
|
+
|
|
|
|
+ oobregion->length = 8;
|
|
|
|
+ oobregion->offset = 8;
|
|
|
|
+ } else {
|
|
|
|
+ oobregion->length = 2;
|
|
|
|
+ if (!section)
|
|
|
|
+ oobregion->offset = 3;
|
|
|
|
+ else
|
|
|
|
+ oobregion->offset = 6;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
|
|
-static struct nand_ecclayout nand_oob_128 = {
|
|
|
|
- .eccbytes = 48,
|
|
|
|
- .eccpos = {
|
|
|
|
- 80, 81, 82, 83, 84, 85, 86, 87,
|
|
|
|
- 88, 89, 90, 91, 92, 93, 94, 95,
|
|
|
|
- 96, 97, 98, 99, 100, 101, 102, 103,
|
|
|
|
- 104, 105, 106, 107, 108, 109, 110, 111,
|
|
|
|
- 112, 113, 114, 115, 116, 117, 118, 119,
|
|
|
|
- 120, 121, 122, 123, 124, 125, 126, 127},
|
|
|
|
- .oobfree = {
|
|
|
|
- {.offset = 2,
|
|
|
|
- .length = 78} }
|
|
|
|
|
|
+const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
|
|
|
|
+ .ecc = nand_ooblayout_ecc_sp,
|
|
|
|
+ .free = nand_ooblayout_free_sp,
|
|
};
|
|
};
|
|
|
|
+EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops);
|
|
|
|
|
|
-static int nand_get_device(struct mtd_info *mtd, int new_state);
|
|
|
|
|
|
+static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
|
|
|
|
+ struct mtd_oob_region *oobregion)
|
|
|
|
+{
|
|
|
|
+ struct nand_chip *chip = mtd_to_nand(mtd);
|
|
|
|
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
|
|
|
|
|
|
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
|
|
|
|
- struct mtd_oob_ops *ops);
|
|
|
|
|
|
+ if (section)
|
|
|
|
+ return -ERANGE;
|
|
|
|
+
|
|
|
|
+ oobregion->length = ecc->total;
|
|
|
|
+ oobregion->offset = mtd->oobsize - oobregion->length;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
|
|
|
|
+ struct mtd_oob_region *oobregion)
|
|
|
|
+{
|
|
|
|
+ struct nand_chip *chip = mtd_to_nand(mtd);
|
|
|
|
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
|
|
|
|
+
|
|
|
|
+ if (section)
|
|
|
|
+ return -ERANGE;
|
|
|
|
+
|
|
|
|
+ oobregion->length = mtd->oobsize - ecc->total - 2;
|
|
|
|
+ oobregion->offset = 2;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
|
|
|
|
+ .ecc = nand_ooblayout_ecc_lp,
|
|
|
|
+ .free = nand_ooblayout_free_lp,
|
|
|
|
+};
|
|
|
|
+EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops);
|
|
|
|
|
|
static int check_offs_len(struct mtd_info *mtd,
|
|
static int check_offs_len(struct mtd_info *mtd,
|
|
loff_t ofs, uint64_t len)
|
|
loff_t ofs, uint64_t len)
|
|
@@ -1279,13 +1321,12 @@ 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,
|
|
static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
uint8_t *buf, int oob_required, int page)
|
|
uint8_t *buf, int oob_required, int page)
|
|
{
|
|
{
|
|
- int i, eccsize = chip->ecc.size;
|
|
|
|
|
|
+ int i, eccsize = chip->ecc.size, ret;
|
|
int eccbytes = chip->ecc.bytes;
|
|
int eccbytes = chip->ecc.bytes;
|
|
int eccsteps = chip->ecc.steps;
|
|
int eccsteps = chip->ecc.steps;
|
|
uint8_t *p = buf;
|
|
uint8_t *p = buf;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
|
|
unsigned int max_bitflips = 0;
|
|
unsigned int max_bitflips = 0;
|
|
|
|
|
|
chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
|
|
chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
|
|
@@ -1293,8 +1334,10 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
|
|
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
|
|
|
|
- for (i = 0; i < chip->ecc.total; i++)
|
|
|
|
- ecc_code[i] = chip->oob_poi[eccpos[i]];
|
|
|
|
|
|
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
|
|
|
|
+ chip->ecc.total);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
|
|
eccsteps = chip->ecc.steps;
|
|
eccsteps = chip->ecc.steps;
|
|
p = buf;
|
|
p = buf;
|
|
@@ -1326,14 +1369,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
|
uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
|
|
uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
|
|
int page)
|
|
int page)
|
|
{
|
|
{
|
|
- int start_step, end_step, num_steps;
|
|
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
|
|
|
|
+ int start_step, end_step, num_steps, ret;
|
|
uint8_t *p;
|
|
uint8_t *p;
|
|
int data_col_addr, i, gaps = 0;
|
|
int data_col_addr, i, gaps = 0;
|
|
int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
|
|
int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
|
|
int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
|
|
int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
|
|
- int index;
|
|
|
|
|
|
+ int index, section = 0;
|
|
unsigned int max_bitflips = 0;
|
|
unsigned int max_bitflips = 0;
|
|
|
|
+ struct mtd_oob_region oobregion = { };
|
|
|
|
|
|
/* Column address within the page aligned to ECC size (256bytes) */
|
|
/* Column address within the page aligned to ECC size (256bytes) */
|
|
start_step = data_offs / chip->ecc.size;
|
|
start_step = data_offs / chip->ecc.size;
|
|
@@ -1361,12 +1404,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
|
* The performance is faster if we position offsets according to
|
|
* The performance is faster if we position offsets according to
|
|
* ecc.pos. Let's make sure that there are no gaps in ECC positions.
|
|
* 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 + index] + 1 != eccpos[i + index + 1]) {
|
|
|
|
- gaps = 1;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ ret = mtd_ooblayout_find_eccregion(mtd, index, §ion, &oobregion);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ if (oobregion.length < eccfrag_len)
|
|
|
|
+ gaps = 1;
|
|
|
|
+
|
|
if (gaps) {
|
|
if (gaps) {
|
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
|
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
@@ -1375,20 +1419,23 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
|
* Send the command to read the particular ECC bytes take care
|
|
* Send the command to read the particular ECC bytes take care
|
|
* about buswidth alignment in read_buf.
|
|
* about buswidth alignment in read_buf.
|
|
*/
|
|
*/
|
|
- aligned_pos = eccpos[index] & ~(busw - 1);
|
|
|
|
|
|
+ aligned_pos = oobregion.offset & ~(busw - 1);
|
|
aligned_len = eccfrag_len;
|
|
aligned_len = eccfrag_len;
|
|
- if (eccpos[index] & (busw - 1))
|
|
|
|
|
|
+ if (oobregion.offset & (busw - 1))
|
|
aligned_len++;
|
|
aligned_len++;
|
|
- if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
|
|
|
|
|
|
+ if ((oobregion.offset + (num_steps * chip->ecc.bytes)) &
|
|
|
|
+ (busw - 1))
|
|
aligned_len++;
|
|
aligned_len++;
|
|
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
|
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
|
|
- mtd->writesize + aligned_pos, -1);
|
|
|
|
|
|
+ mtd->writesize + aligned_pos, -1);
|
|
chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
|
|
chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
|
|
}
|
|
}
|
|
|
|
|
|
- for (i = 0; i < eccfrag_len; i++)
|
|
|
|
- chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]];
|
|
|
|
|
|
+ ret = mtd_ooblayout_get_eccbytes(mtd, chip->buffers->ecccode,
|
|
|
|
+ chip->oob_poi, index, eccfrag_len);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
|
|
p = bufpoi + data_col_addr;
|
|
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->ecc.bytes, p += chip->ecc.size) {
|
|
@@ -1429,13 +1476,12 @@ 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,
|
|
static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
uint8_t *buf, int oob_required, int page)
|
|
uint8_t *buf, int oob_required, int page)
|
|
{
|
|
{
|
|
- int i, eccsize = chip->ecc.size;
|
|
|
|
|
|
+ int i, eccsize = chip->ecc.size, ret;
|
|
int eccbytes = chip->ecc.bytes;
|
|
int eccbytes = chip->ecc.bytes;
|
|
int eccsteps = chip->ecc.steps;
|
|
int eccsteps = chip->ecc.steps;
|
|
uint8_t *p = buf;
|
|
uint8_t *p = buf;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
|
|
unsigned int max_bitflips = 0;
|
|
unsigned int max_bitflips = 0;
|
|
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
@@ -1445,8 +1491,10 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
}
|
|
}
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
|
|
|
|
- for (i = 0; i < chip->ecc.total; i++)
|
|
|
|
- ecc_code[i] = chip->oob_poi[eccpos[i]];
|
|
|
|
|
|
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
|
|
|
|
+ chip->ecc.total);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
|
|
eccsteps = chip->ecc.steps;
|
|
eccsteps = chip->ecc.steps;
|
|
p = buf;
|
|
p = buf;
|
|
@@ -1491,12 +1539,11 @@ 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,
|
|
static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
|
|
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
|
|
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
|
|
{
|
|
{
|
|
- int i, eccsize = chip->ecc.size;
|
|
|
|
|
|
+ int i, eccsize = chip->ecc.size, ret;
|
|
int eccbytes = chip->ecc.bytes;
|
|
int eccbytes = chip->ecc.bytes;
|
|
int eccsteps = chip->ecc.steps;
|
|
int eccsteps = chip->ecc.steps;
|
|
uint8_t *p = buf;
|
|
uint8_t *p = buf;
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
unsigned int max_bitflips = 0;
|
|
unsigned int max_bitflips = 0;
|
|
|
|
|
|
@@ -1505,8 +1552,10 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
|
|
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
|
|
|
|
|
|
- for (i = 0; i < chip->ecc.total; i++)
|
|
|
|
- ecc_code[i] = chip->oob_poi[eccpos[i]];
|
|
|
|
|
|
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
|
|
|
|
+ chip->ecc.total);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
int stat;
|
|
int stat;
|
|
@@ -1607,14 +1656,17 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
|
|
/**
|
|
/**
|
|
* nand_transfer_oob - [INTERN] Transfer oob to client buffer
|
|
* nand_transfer_oob - [INTERN] Transfer oob to client buffer
|
|
- * @chip: nand chip structure
|
|
|
|
|
|
+ * @mtd: mtd info structure
|
|
* @oob: oob destination address
|
|
* @oob: oob destination address
|
|
* @ops: oob ops structure
|
|
* @ops: oob ops structure
|
|
* @len: size of oob to transfer
|
|
* @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 mtd_oob_ops *ops, size_t len)
|
|
{
|
|
{
|
|
|
|
+ struct nand_chip *chip = mtd_to_nand(mtd);
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
switch (ops->mode) {
|
|
switch (ops->mode) {
|
|
|
|
|
|
case MTD_OPS_PLACE_OOB:
|
|
case MTD_OPS_PLACE_OOB:
|
|
@@ -1622,31 +1674,12 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
|
|
memcpy(oob, chip->oob_poi + ops->ooboffs, len);
|
|
memcpy(oob, chip->oob_poi + ops->ooboffs, len);
|
|
return oob + len;
|
|
return oob + len;
|
|
|
|
|
|
- case MTD_OPS_AUTO_OOB: {
|
|
|
|
- struct nand_oobfree *free = chip->ecc.layout->oobfree;
|
|
|
|
- uint32_t boffs = 0, roffs = ops->ooboffs;
|
|
|
|
- size_t bytes = 0;
|
|
|
|
-
|
|
|
|
- for (; free->length && len; free++, len -= bytes) {
|
|
|
|
- /* Read request not from offset 0? */
|
|
|
|
- if (unlikely(roffs)) {
|
|
|
|
- if (roffs >= free->length) {
|
|
|
|
- roffs -= free->length;
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- boffs = free->offset + roffs;
|
|
|
|
- bytes = min_t(size_t, len,
|
|
|
|
- (free->length - roffs));
|
|
|
|
- roffs = 0;
|
|
|
|
- } else {
|
|
|
|
- bytes = min_t(size_t, len, free->length);
|
|
|
|
- boffs = free->offset;
|
|
|
|
- }
|
|
|
|
- memcpy(oob, chip->oob_poi + boffs, bytes);
|
|
|
|
- oob += bytes;
|
|
|
|
- }
|
|
|
|
- return oob;
|
|
|
|
- }
|
|
|
|
|
|
+ case MTD_OPS_AUTO_OOB:
|
|
|
|
+ ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oob_poi,
|
|
|
|
+ ops->ooboffs, len);
|
|
|
|
+ BUG_ON(ret);
|
|
|
|
+ return oob + len;
|
|
|
|
+
|
|
default:
|
|
default:
|
|
BUG();
|
|
BUG();
|
|
}
|
|
}
|
|
@@ -1780,7 +1813,7 @@ read_retry:
|
|
int toread = min(oobreadlen, max_oobsize);
|
|
int toread = min(oobreadlen, max_oobsize);
|
|
|
|
|
|
if (toread) {
|
|
if (toread) {
|
|
- oob = nand_transfer_oob(chip,
|
|
|
|
|
|
+ oob = nand_transfer_oob(mtd,
|
|
oob, ops, toread);
|
|
oob, ops, toread);
|
|
oobreadlen -= toread;
|
|
oobreadlen -= toread;
|
|
}
|
|
}
|
|
@@ -1893,13 +1926,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|
* @chip: nand chip info structure
|
|
* @chip: nand chip info structure
|
|
* @page: page number to read
|
|
* @page: page number to read
|
|
*/
|
|
*/
|
|
-static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
- int page)
|
|
|
|
|
|
+int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page)
|
|
{
|
|
{
|
|
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
|
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(nand_read_oob_std);
|
|
|
|
|
|
/**
|
|
/**
|
|
* nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
|
|
* nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
|
|
@@ -1908,8 +1941,8 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
|
|
* @chip: nand chip info structure
|
|
* @chip: nand chip info structure
|
|
* @page: page number to read
|
|
* @page: page number to read
|
|
*/
|
|
*/
|
|
-static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
- int page)
|
|
|
|
|
|
+int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
+ int page)
|
|
{
|
|
{
|
|
int length = mtd->oobsize;
|
|
int length = mtd->oobsize;
|
|
int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
|
|
int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
|
|
@@ -1937,6 +1970,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(nand_read_oob_syndrome);
|
|
|
|
|
|
/**
|
|
/**
|
|
* nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
|
|
* nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
|
|
@@ -1944,8 +1978,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
|
* @chip: nand chip info structure
|
|
* @chip: nand chip info structure
|
|
* @page: page number to write
|
|
* @page: page number to write
|
|
*/
|
|
*/
|
|
-static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
- int page)
|
|
|
|
|
|
+int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page)
|
|
{
|
|
{
|
|
int status = 0;
|
|
int status = 0;
|
|
const uint8_t *buf = chip->oob_poi;
|
|
const uint8_t *buf = chip->oob_poi;
|
|
@@ -1960,6 +1993,7 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
|
|
return status & NAND_STATUS_FAIL ? -EIO : 0;
|
|
return status & NAND_STATUS_FAIL ? -EIO : 0;
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(nand_write_oob_std);
|
|
|
|
|
|
/**
|
|
/**
|
|
* nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
|
|
* nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
|
|
@@ -1968,8 +2002,8 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
|
|
* @chip: nand chip info structure
|
|
* @chip: nand chip info structure
|
|
* @page: page number to write
|
|
* @page: page number to write
|
|
*/
|
|
*/
|
|
-static int nand_write_oob_syndrome(struct mtd_info *mtd,
|
|
|
|
- struct nand_chip *chip, int page)
|
|
|
|
|
|
+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 chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
|
|
int eccsize = chip->ecc.size, length = mtd->oobsize;
|
|
int eccsize = chip->ecc.size, length = mtd->oobsize;
|
|
@@ -2019,6 +2053,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
|
|
|
|
|
|
return status & NAND_STATUS_FAIL ? -EIO : 0;
|
|
return status & NAND_STATUS_FAIL ? -EIO : 0;
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(nand_write_oob_syndrome);
|
|
|
|
|
|
/**
|
|
/**
|
|
* nand_do_read_oob - [INTERN] NAND read out-of-band
|
|
* nand_do_read_oob - [INTERN] NAND read out-of-band
|
|
@@ -2078,7 +2113,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
|
|
break;
|
|
break;
|
|
|
|
|
|
len = min(len, readlen);
|
|
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) {
|
|
if (chip->options & NAND_NEED_READRDY) {
|
|
/* Apply delay or wait for ready/busy pin */
|
|
/* Apply delay or wait for ready/busy pin */
|
|
@@ -2237,19 +2272,20 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
const uint8_t *buf, int oob_required,
|
|
const uint8_t *buf, int oob_required,
|
|
int page)
|
|
int page)
|
|
{
|
|
{
|
|
- int i, eccsize = chip->ecc.size;
|
|
|
|
|
|
+ int i, eccsize = chip->ecc.size, ret;
|
|
int eccbytes = chip->ecc.bytes;
|
|
int eccbytes = chip->ecc.bytes;
|
|
int eccsteps = chip->ecc.steps;
|
|
int eccsteps = chip->ecc.steps;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
const uint8_t *p = buf;
|
|
const uint8_t *p = buf;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
|
|
|
|
|
|
/* Software ECC calculation */
|
|
/* Software ECC calculation */
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
|
|
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
|
|
|
|
- for (i = 0; i < chip->ecc.total; i++)
|
|
|
|
- chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
|
|
|
|
|
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
|
|
|
|
+ chip->ecc.total);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
|
|
return chip->ecc.write_page_raw(mtd, chip, buf, 1, page);
|
|
return chip->ecc.write_page_raw(mtd, chip, buf, 1, page);
|
|
}
|
|
}
|
|
@@ -2266,12 +2302,11 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
const uint8_t *buf, int oob_required,
|
|
const uint8_t *buf, int oob_required,
|
|
int page)
|
|
int page)
|
|
{
|
|
{
|
|
- int i, eccsize = chip->ecc.size;
|
|
|
|
|
|
+ int i, eccsize = chip->ecc.size, ret;
|
|
int eccbytes = chip->ecc.bytes;
|
|
int eccbytes = chip->ecc.bytes;
|
|
int eccsteps = chip->ecc.steps;
|
|
int eccsteps = chip->ecc.steps;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
|
const uint8_t *p = buf;
|
|
const uint8_t *p = buf;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
|
|
|
|
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
|
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
|
|
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
|
|
@@ -2279,8 +2314,10 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
}
|
|
}
|
|
|
|
|
|
- for (i = 0; i < chip->ecc.total; i++)
|
|
|
|
- chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
|
|
|
|
|
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
|
|
|
|
+ chip->ecc.total);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
|
|
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
|
|
|
|
@@ -2308,11 +2345,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
|
|
int ecc_size = chip->ecc.size;
|
|
int ecc_size = chip->ecc.size;
|
|
int ecc_bytes = chip->ecc.bytes;
|
|
int ecc_bytes = chip->ecc.bytes;
|
|
int ecc_steps = chip->ecc.steps;
|
|
int ecc_steps = chip->ecc.steps;
|
|
- uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
|
|
uint32_t start_step = offset / ecc_size;
|
|
uint32_t start_step = offset / ecc_size;
|
|
uint32_t end_step = (offset + data_len - 1) / ecc_size;
|
|
uint32_t end_step = (offset + data_len - 1) / ecc_size;
|
|
int oob_bytes = mtd->oobsize / ecc_steps;
|
|
int oob_bytes = mtd->oobsize / ecc_steps;
|
|
- int step, i;
|
|
|
|
|
|
+ int step, ret;
|
|
|
|
|
|
for (step = 0; step < ecc_steps; step++) {
|
|
for (step = 0; step < ecc_steps; step++) {
|
|
/* configure controller for WRITE access */
|
|
/* configure controller for WRITE access */
|
|
@@ -2340,8 +2376,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
|
|
/* copy calculated ECC for whole page to chip->buffer->oob */
|
|
/* copy calculated ECC for whole page to chip->buffer->oob */
|
|
/* this include masked-value(0xFF) for unwritten subpages */
|
|
/* this include masked-value(0xFF) for unwritten subpages */
|
|
ecc_calc = chip->buffers->ecccalc;
|
|
ecc_calc = chip->buffers->ecccalc;
|
|
- for (i = 0; i < chip->ecc.total; i++)
|
|
|
|
- chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
|
|
|
|
|
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
|
|
|
|
+ chip->ecc.total);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
|
|
/* write OOB buffer to NAND device */
|
|
/* write OOB buffer to NAND device */
|
|
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
@@ -2478,6 +2516,7 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
|
|
struct mtd_oob_ops *ops)
|
|
struct mtd_oob_ops *ops)
|
|
{
|
|
{
|
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
|
|
+ int ret;
|
|
|
|
|
|
/*
|
|
/*
|
|
* Initialise to all 0xFF, to avoid the possibility of left over OOB
|
|
* Initialise to all 0xFF, to avoid the possibility of left over OOB
|
|
@@ -2492,31 +2531,12 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
|
|
memcpy(chip->oob_poi + ops->ooboffs, oob, len);
|
|
memcpy(chip->oob_poi + ops->ooboffs, oob, len);
|
|
return oob + len;
|
|
return oob + len;
|
|
|
|
|
|
- case MTD_OPS_AUTO_OOB: {
|
|
|
|
- struct nand_oobfree *free = chip->ecc.layout->oobfree;
|
|
|
|
- uint32_t boffs = 0, woffs = ops->ooboffs;
|
|
|
|
- size_t bytes = 0;
|
|
|
|
-
|
|
|
|
- for (; free->length && len; free++, len -= bytes) {
|
|
|
|
- /* Write request not from offset 0? */
|
|
|
|
- if (unlikely(woffs)) {
|
|
|
|
- if (woffs >= free->length) {
|
|
|
|
- woffs -= free->length;
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- boffs = free->offset + woffs;
|
|
|
|
- bytes = min_t(size_t, len,
|
|
|
|
- (free->length - woffs));
|
|
|
|
- woffs = 0;
|
|
|
|
- } else {
|
|
|
|
- bytes = min_t(size_t, len, free->length);
|
|
|
|
- boffs = free->offset;
|
|
|
|
- }
|
|
|
|
- memcpy(chip->oob_poi + boffs, oob, bytes);
|
|
|
|
- oob += bytes;
|
|
|
|
- }
|
|
|
|
- return oob;
|
|
|
|
- }
|
|
|
|
|
|
+ case MTD_OPS_AUTO_OOB:
|
|
|
|
+ ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi,
|
|
|
|
+ ops->ooboffs, len);
|
|
|
|
+ BUG_ON(ret);
|
|
|
|
+ return oob + len;
|
|
|
|
+
|
|
default:
|
|
default:
|
|
BUG();
|
|
BUG();
|
|
}
|
|
}
|
|
@@ -3951,10 +3971,115 @@ ident_done:
|
|
return type;
|
|
return type;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static const char * const nand_ecc_modes[] = {
|
|
|
|
+ [NAND_ECC_NONE] = "none",
|
|
|
|
+ [NAND_ECC_SOFT] = "soft",
|
|
|
|
+ [NAND_ECC_HW] = "hw",
|
|
|
|
+ [NAND_ECC_HW_SYNDROME] = "hw_syndrome",
|
|
|
|
+ [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int of_get_nand_ecc_mode(struct device_node *np)
|
|
|
|
+{
|
|
|
|
+ const char *pm;
|
|
|
|
+ int err, i;
|
|
|
|
+
|
|
|
|
+ err = of_property_read_string(np, "nand-ecc-mode", &pm);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++)
|
|
|
|
+ if (!strcasecmp(pm, nand_ecc_modes[i]))
|
|
|
|
+ return i;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * For backward compatibility we support few obsoleted values that don't
|
|
|
|
+ * have their mappings into nand_ecc_modes_t anymore (they were merged
|
|
|
|
+ * with other enums).
|
|
|
|
+ */
|
|
|
|
+ if (!strcasecmp(pm, "soft_bch"))
|
|
|
|
+ return NAND_ECC_SOFT;
|
|
|
|
+
|
|
|
|
+ return -ENODEV;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const char * const nand_ecc_algos[] = {
|
|
|
|
+ [NAND_ECC_HAMMING] = "hamming",
|
|
|
|
+ [NAND_ECC_BCH] = "bch",
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int of_get_nand_ecc_algo(struct device_node *np)
|
|
|
|
+{
|
|
|
|
+ const char *pm;
|
|
|
|
+ int err, i;
|
|
|
|
+
|
|
|
|
+ err = of_property_read_string(np, "nand-ecc-algo", &pm);
|
|
|
|
+ if (!err) {
|
|
|
|
+ for (i = NAND_ECC_HAMMING; i < ARRAY_SIZE(nand_ecc_algos); i++)
|
|
|
|
+ if (!strcasecmp(pm, nand_ecc_algos[i]))
|
|
|
|
+ return i;
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * For backward compatibility we also read "nand-ecc-mode" checking
|
|
|
|
+ * for some obsoleted values that were specifying ECC algorithm.
|
|
|
|
+ */
|
|
|
|
+ err = of_property_read_string(np, "nand-ecc-mode", &pm);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ if (!strcasecmp(pm, "soft"))
|
|
|
|
+ return NAND_ECC_HAMMING;
|
|
|
|
+ else if (!strcasecmp(pm, "soft_bch"))
|
|
|
|
+ return NAND_ECC_BCH;
|
|
|
|
+
|
|
|
|
+ return -ENODEV;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int of_get_nand_ecc_step_size(struct device_node *np)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ u32 val;
|
|
|
|
+
|
|
|
|
+ ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
|
|
|
|
+ return ret ? ret : val;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int of_get_nand_ecc_strength(struct device_node *np)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ u32 val;
|
|
|
|
+
|
|
|
|
+ ret = of_property_read_u32(np, "nand-ecc-strength", &val);
|
|
|
|
+ return ret ? ret : val;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int of_get_nand_bus_width(struct device_node *np)
|
|
|
|
+{
|
|
|
|
+ u32 val;
|
|
|
|
+
|
|
|
|
+ if (of_property_read_u32(np, "nand-bus-width", &val))
|
|
|
|
+ return 8;
|
|
|
|
+
|
|
|
|
+ switch (val) {
|
|
|
|
+ case 8:
|
|
|
|
+ case 16:
|
|
|
|
+ return val;
|
|
|
|
+ default:
|
|
|
|
+ return -EIO;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool of_get_nand_on_flash_bbt(struct device_node *np)
|
|
|
|
+{
|
|
|
|
+ return of_property_read_bool(np, "nand-on-flash-bbt");
|
|
|
|
+}
|
|
|
|
+
|
|
static int nand_dt_init(struct nand_chip *chip)
|
|
static int nand_dt_init(struct nand_chip *chip)
|
|
{
|
|
{
|
|
struct device_node *dn = nand_get_flash_node(chip);
|
|
struct device_node *dn = nand_get_flash_node(chip);
|
|
- int ecc_mode, ecc_strength, ecc_step;
|
|
|
|
|
|
+ int ecc_mode, ecc_algo, ecc_strength, ecc_step;
|
|
|
|
|
|
if (!dn)
|
|
if (!dn)
|
|
return 0;
|
|
return 0;
|
|
@@ -3966,6 +4091,7 @@ static int nand_dt_init(struct nand_chip *chip)
|
|
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
|
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
|
|
|
|
|
ecc_mode = of_get_nand_ecc_mode(dn);
|
|
ecc_mode = of_get_nand_ecc_mode(dn);
|
|
|
|
+ ecc_algo = of_get_nand_ecc_algo(dn);
|
|
ecc_strength = of_get_nand_ecc_strength(dn);
|
|
ecc_strength = of_get_nand_ecc_strength(dn);
|
|
ecc_step = of_get_nand_ecc_step_size(dn);
|
|
ecc_step = of_get_nand_ecc_step_size(dn);
|
|
|
|
|
|
@@ -3978,6 +4104,9 @@ static int nand_dt_init(struct nand_chip *chip)
|
|
if (ecc_mode >= 0)
|
|
if (ecc_mode >= 0)
|
|
chip->ecc.mode = ecc_mode;
|
|
chip->ecc.mode = ecc_mode;
|
|
|
|
|
|
|
|
+ if (ecc_algo >= 0)
|
|
|
|
+ chip->ecc.algo = ecc_algo;
|
|
|
|
+
|
|
if (ecc_strength >= 0)
|
|
if (ecc_strength >= 0)
|
|
chip->ecc.strength = ecc_strength;
|
|
chip->ecc.strength = ecc_strength;
|
|
|
|
|
|
@@ -4054,6 +4183,82 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(nand_scan_ident);
|
|
EXPORT_SYMBOL(nand_scan_ident);
|
|
|
|
|
|
|
|
+static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
|
|
|
|
+{
|
|
|
|
+ struct nand_chip *chip = mtd_to_nand(mtd);
|
|
|
|
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
|
|
|
|
+
|
|
|
|
+ if (WARN_ON(ecc->mode != NAND_ECC_SOFT))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ switch (ecc->algo) {
|
|
|
|
+ case NAND_ECC_HAMMING:
|
|
|
|
+ ecc->calculate = nand_calculate_ecc;
|
|
|
|
+ ecc->correct = nand_correct_data;
|
|
|
|
+ ecc->read_page = nand_read_page_swecc;
|
|
|
|
+ ecc->read_subpage = nand_read_subpage;
|
|
|
|
+ ecc->write_page = nand_write_page_swecc;
|
|
|
|
+ ecc->read_page_raw = nand_read_page_raw;
|
|
|
|
+ ecc->write_page_raw = nand_write_page_raw;
|
|
|
|
+ ecc->read_oob = nand_read_oob_std;
|
|
|
|
+ ecc->write_oob = nand_write_oob_std;
|
|
|
|
+ if (!ecc->size)
|
|
|
|
+ ecc->size = 256;
|
|
|
|
+ ecc->bytes = 3;
|
|
|
|
+ ecc->strength = 1;
|
|
|
|
+ return 0;
|
|
|
|
+ case NAND_ECC_BCH:
|
|
|
|
+ if (!mtd_nand_has_bch()) {
|
|
|
|
+ WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ ecc->calculate = nand_bch_calculate_ecc;
|
|
|
|
+ ecc->correct = nand_bch_correct_data;
|
|
|
|
+ ecc->read_page = nand_read_page_swecc;
|
|
|
|
+ ecc->read_subpage = nand_read_subpage;
|
|
|
|
+ ecc->write_page = nand_write_page_swecc;
|
|
|
|
+ ecc->read_page_raw = nand_read_page_raw;
|
|
|
|
+ ecc->write_page_raw = nand_write_page_raw;
|
|
|
|
+ ecc->read_oob = nand_read_oob_std;
|
|
|
|
+ ecc->write_oob = nand_write_oob_std;
|
|
|
|
+ /*
|
|
|
|
+ * Board driver should supply ecc.size and ecc.strength
|
|
|
|
+ * values to select how many bits are correctable.
|
|
|
|
+ * Otherwise, default to 4 bits for large page devices.
|
|
|
|
+ */
|
|
|
|
+ if (!ecc->size && (mtd->oobsize >= 64)) {
|
|
|
|
+ ecc->size = 512;
|
|
|
|
+ ecc->strength = 4;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * if no ecc placement scheme was provided pickup the default
|
|
|
|
+ * large page one.
|
|
|
|
+ */
|
|
|
|
+ if (!mtd->ooblayout) {
|
|
|
|
+ /* handle large page devices only */
|
|
|
|
+ if (mtd->oobsize < 64) {
|
|
|
|
+ WARN(1, "OOB layout is required when using software BCH on small pages\n");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* See nand_bch_init() for details. */
|
|
|
|
+ ecc->bytes = 0;
|
|
|
|
+ ecc->priv = nand_bch_init(mtd);
|
|
|
|
+ if (!ecc->priv) {
|
|
|
|
+ WARN(1, "BCH ECC initialization failed!\n");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+ default:
|
|
|
|
+ WARN(1, "Unsupported ECC algorithm!\n");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Check if the chip configuration meet the datasheet requirements.
|
|
* Check if the chip configuration meet the datasheet requirements.
|
|
|
|
|
|
@@ -4098,14 +4303,15 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd)
|
|
*/
|
|
*/
|
|
int nand_scan_tail(struct mtd_info *mtd)
|
|
int nand_scan_tail(struct mtd_info *mtd)
|
|
{
|
|
{
|
|
- int i;
|
|
|
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
|
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
|
struct nand_buffers *nbuf;
|
|
struct nand_buffers *nbuf;
|
|
|
|
+ int ret;
|
|
|
|
|
|
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
|
|
/* 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 (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
|
|
|
|
+ !(chip->bbt_options & NAND_BBT_USE_FLASH)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
if (!(chip->options & NAND_OWN_BUFFERS)) {
|
|
if (!(chip->options & NAND_OWN_BUFFERS)) {
|
|
nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
|
|
nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
|
|
@@ -4128,24 +4334,22 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
/*
|
|
/*
|
|
* If no default placement scheme is given, select an appropriate one.
|
|
* If no default placement scheme is given, select an appropriate one.
|
|
*/
|
|
*/
|
|
- if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) {
|
|
|
|
|
|
+ if (!mtd->ooblayout &&
|
|
|
|
+ !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) {
|
|
switch (mtd->oobsize) {
|
|
switch (mtd->oobsize) {
|
|
case 8:
|
|
case 8:
|
|
- ecc->layout = &nand_oob_8;
|
|
|
|
- break;
|
|
|
|
case 16:
|
|
case 16:
|
|
- ecc->layout = &nand_oob_16;
|
|
|
|
|
|
+ mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops);
|
|
break;
|
|
break;
|
|
case 64:
|
|
case 64:
|
|
- ecc->layout = &nand_oob_64;
|
|
|
|
- break;
|
|
|
|
case 128:
|
|
case 128:
|
|
- ecc->layout = &nand_oob_128;
|
|
|
|
|
|
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
- pr_warn("No oob scheme defined for oobsize %d\n",
|
|
|
|
- mtd->oobsize);
|
|
|
|
- BUG();
|
|
|
|
|
|
+ WARN(1, "No oob scheme defined for oobsize %d\n",
|
|
|
|
+ mtd->oobsize);
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto err_free;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4161,8 +4365,9 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
case NAND_ECC_HW_OOB_FIRST:
|
|
case NAND_ECC_HW_OOB_FIRST:
|
|
/* Similar to NAND_ECC_HW, but a separate read_page handle */
|
|
/* Similar to NAND_ECC_HW, but a separate read_page handle */
|
|
if (!ecc->calculate || !ecc->correct || !ecc->hwctl) {
|
|
if (!ecc->calculate || !ecc->correct || !ecc->hwctl) {
|
|
- pr_warn("No ECC functions supplied; hardware ECC not possible\n");
|
|
|
|
- BUG();
|
|
|
|
|
|
+ WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto err_free;
|
|
}
|
|
}
|
|
if (!ecc->read_page)
|
|
if (!ecc->read_page)
|
|
ecc->read_page = nand_read_page_hwecc_oob_first;
|
|
ecc->read_page = nand_read_page_hwecc_oob_first;
|
|
@@ -4192,8 +4397,9 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
ecc->read_page == nand_read_page_hwecc ||
|
|
ecc->read_page == nand_read_page_hwecc ||
|
|
!ecc->write_page ||
|
|
!ecc->write_page ||
|
|
ecc->write_page == nand_write_page_hwecc)) {
|
|
ecc->write_page == nand_write_page_hwecc)) {
|
|
- pr_warn("No ECC functions supplied; hardware ECC not possible\n");
|
|
|
|
- BUG();
|
|
|
|
|
|
+ WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto err_free;
|
|
}
|
|
}
|
|
/* Use standard syndrome read/write page function? */
|
|
/* Use standard syndrome read/write page function? */
|
|
if (!ecc->read_page)
|
|
if (!ecc->read_page)
|
|
@@ -4211,61 +4417,22 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
|
|
|
|
if (mtd->writesize >= ecc->size) {
|
|
if (mtd->writesize >= ecc->size) {
|
|
if (!ecc->strength) {
|
|
if (!ecc->strength) {
|
|
- pr_warn("Driver must set ecc.strength when using hardware ECC\n");
|
|
|
|
- BUG();
|
|
|
|
|
|
+ WARN(1, "Driver must set ecc.strength when using hardware ECC\n");
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto err_free;
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
|
|
pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
|
|
ecc->size, mtd->writesize);
|
|
ecc->size, mtd->writesize);
|
|
ecc->mode = NAND_ECC_SOFT;
|
|
ecc->mode = NAND_ECC_SOFT;
|
|
|
|
+ ecc->algo = NAND_ECC_HAMMING;
|
|
|
|
|
|
case NAND_ECC_SOFT:
|
|
case NAND_ECC_SOFT:
|
|
- ecc->calculate = nand_calculate_ecc;
|
|
|
|
- ecc->correct = nand_correct_data;
|
|
|
|
- ecc->read_page = nand_read_page_swecc;
|
|
|
|
- ecc->read_subpage = nand_read_subpage;
|
|
|
|
- ecc->write_page = nand_write_page_swecc;
|
|
|
|
- ecc->read_page_raw = nand_read_page_raw;
|
|
|
|
- ecc->write_page_raw = nand_write_page_raw;
|
|
|
|
- ecc->read_oob = nand_read_oob_std;
|
|
|
|
- ecc->write_oob = nand_write_oob_std;
|
|
|
|
- if (!ecc->size)
|
|
|
|
- ecc->size = 256;
|
|
|
|
- ecc->bytes = 3;
|
|
|
|
- ecc->strength = 1;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case NAND_ECC_SOFT_BCH:
|
|
|
|
- if (!mtd_nand_has_bch()) {
|
|
|
|
- pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n");
|
|
|
|
- BUG();
|
|
|
|
- }
|
|
|
|
- ecc->calculate = nand_bch_calculate_ecc;
|
|
|
|
- ecc->correct = nand_bch_correct_data;
|
|
|
|
- ecc->read_page = nand_read_page_swecc;
|
|
|
|
- ecc->read_subpage = nand_read_subpage;
|
|
|
|
- ecc->write_page = nand_write_page_swecc;
|
|
|
|
- ecc->read_page_raw = nand_read_page_raw;
|
|
|
|
- ecc->write_page_raw = nand_write_page_raw;
|
|
|
|
- ecc->read_oob = nand_read_oob_std;
|
|
|
|
- ecc->write_oob = nand_write_oob_std;
|
|
|
|
- /*
|
|
|
|
- * Board driver should supply ecc.size and ecc.strength values
|
|
|
|
- * to select how many bits are correctable. Otherwise, default
|
|
|
|
- * to 4 bits for large page devices.
|
|
|
|
- */
|
|
|
|
- if (!ecc->size && (mtd->oobsize >= 64)) {
|
|
|
|
- ecc->size = 512;
|
|
|
|
- ecc->strength = 4;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* See nand_bch_init() for details. */
|
|
|
|
- ecc->bytes = 0;
|
|
|
|
- ecc->priv = nand_bch_init(mtd);
|
|
|
|
- if (!ecc->priv) {
|
|
|
|
- pr_warn("BCH ECC initialization failed!\n");
|
|
|
|
- BUG();
|
|
|
|
|
|
+ ret = nand_set_ecc_soft_ops(mtd);
|
|
|
|
+ if (ret) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto err_free;
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
|
|
|
|
@@ -4283,8 +4450,9 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
break;
|
|
break;
|
|
|
|
|
|
default:
|
|
default:
|
|
- pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode);
|
|
|
|
- BUG();
|
|
|
|
|
|
+ WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode);
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto err_free;
|
|
}
|
|
}
|
|
|
|
|
|
/* For many systems, the standard OOB write also works for raw */
|
|
/* For many systems, the standard OOB write also works for raw */
|
|
@@ -4293,20 +4461,9 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
if (!ecc->write_oob_raw)
|
|
if (!ecc->write_oob_raw)
|
|
ecc->write_oob_raw = ecc->write_oob;
|
|
ecc->write_oob_raw = ecc->write_oob;
|
|
|
|
|
|
- /*
|
|
|
|
- * The number of bytes available for a client to place data into
|
|
|
|
- * the out of band area.
|
|
|
|
- */
|
|
|
|
- mtd->oobavail = 0;
|
|
|
|
- if (ecc->layout) {
|
|
|
|
- for (i = 0; ecc->layout->oobfree[i].length; i++)
|
|
|
|
- mtd->oobavail += ecc->layout->oobfree[i].length;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* ECC sanity check: warn if it's too weak */
|
|
|
|
- if (!nand_ecc_strength_good(mtd))
|
|
|
|
- pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
|
|
|
|
- mtd->name);
|
|
|
|
|
|
+ /* propagate ecc info to mtd_info */
|
|
|
|
+ mtd->ecc_strength = ecc->strength;
|
|
|
|
+ mtd->ecc_step_size = ecc->size;
|
|
|
|
|
|
/*
|
|
/*
|
|
* Set the number of read / write steps for one page depending on ECC
|
|
* Set the number of read / write steps for one page depending on ECC
|
|
@@ -4314,11 +4471,27 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
*/
|
|
*/
|
|
ecc->steps = mtd->writesize / ecc->size;
|
|
ecc->steps = mtd->writesize / ecc->size;
|
|
if (ecc->steps * ecc->size != mtd->writesize) {
|
|
if (ecc->steps * ecc->size != mtd->writesize) {
|
|
- pr_warn("Invalid ECC parameters\n");
|
|
|
|
- BUG();
|
|
|
|
|
|
+ WARN(1, "Invalid ECC parameters\n");
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto err_free;
|
|
}
|
|
}
|
|
ecc->total = ecc->steps * ecc->bytes;
|
|
ecc->total = ecc->steps * ecc->bytes;
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * The number of bytes available for a client to place data into
|
|
|
|
+ * the out of band area.
|
|
|
|
+ */
|
|
|
|
+ ret = mtd_ooblayout_count_freebytes(mtd);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ ret = 0;
|
|
|
|
+
|
|
|
|
+ mtd->oobavail = ret;
|
|
|
|
+
|
|
|
|
+ /* ECC sanity check: warn if it's too weak */
|
|
|
|
+ if (!nand_ecc_strength_good(mtd))
|
|
|
|
+ pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
|
|
|
|
+ mtd->name);
|
|
|
|
+
|
|
/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
|
|
/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
|
|
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
|
|
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
|
|
switch (ecc->steps) {
|
|
switch (ecc->steps) {
|
|
@@ -4343,7 +4516,6 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
/* Large page NAND with SOFT_ECC should support subpage reads */
|
|
/* Large page NAND with SOFT_ECC should support subpage reads */
|
|
switch (ecc->mode) {
|
|
switch (ecc->mode) {
|
|
case NAND_ECC_SOFT:
|
|
case NAND_ECC_SOFT:
|
|
- case NAND_ECC_SOFT_BCH:
|
|
|
|
if (chip->page_shift > 9)
|
|
if (chip->page_shift > 9)
|
|
chip->options |= NAND_SUBPAGE_READ;
|
|
chip->options |= NAND_SUBPAGE_READ;
|
|
break;
|
|
break;
|
|
@@ -4375,10 +4547,6 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
mtd->_block_markbad = nand_block_markbad;
|
|
mtd->_block_markbad = nand_block_markbad;
|
|
mtd->writebufsize = mtd->writesize;
|
|
mtd->writebufsize = mtd->writesize;
|
|
|
|
|
|
- /* 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.
|
|
* Initialize bitflip_threshold to its default prior scan_bbt() call.
|
|
* scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
|
|
* scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
|
|
@@ -4393,6 +4561,10 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
|
|
|
|
/* Build bad block table */
|
|
/* Build bad block table */
|
|
return chip->scan_bbt(mtd);
|
|
return chip->scan_bbt(mtd);
|
|
|
|
+err_free:
|
|
|
|
+ if (!(chip->options & NAND_OWN_BUFFERS))
|
|
|
|
+ kfree(chip->buffers);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(nand_scan_tail);
|
|
EXPORT_SYMBOL(nand_scan_tail);
|
|
|
|
|
|
@@ -4436,7 +4608,8 @@ void nand_release(struct mtd_info *mtd)
|
|
{
|
|
{
|
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
|
|
|
|
- if (chip->ecc.mode == NAND_ECC_SOFT_BCH)
|
|
|
|
|
|
+ if (chip->ecc.mode == NAND_ECC_SOFT &&
|
|
|
|
+ chip->ecc.algo == NAND_ECC_BCH)
|
|
nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
|
|
nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
|
|
|
|
|
|
mtd_device_unregister(mtd);
|
|
mtd_device_unregister(mtd);
|