mtd: nand: ecc-bch: Stop using raw NAND structures

This code is meant to be reused by the SPI-NAND core. Now that the
driver has been cleaned and reorganized, use a generic ECC engine
object to store the driver's data instead of accessing members of the
nand_chip structure.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-9-miquel.raynal@bootlin.com
This commit is contained in:
Miquel Raynal 2020-09-30 01:01:12 +02:00
parent ea146d7fbf
commit 80fe603160
3 changed files with 97 additions and 78 deletions

View file

@ -11,23 +11,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
#include <linux/mtd/nand-ecc-sw-bch.h> #include <linux/mtd/nand-ecc-sw-bch.h>
#include <linux/bch.h>
/**
* struct nand_bch_control - private NAND BCH control structure
* @bch: BCH control structure
* @errloc: error location array
* @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
*/
struct nand_bch_control {
struct bch_control *bch;
unsigned int *errloc;
unsigned char *eccmask;
};
/** /**
* nand_ecc_sw_bch_calculate - Calculate the ECC corresponding to a data block * nand_ecc_sw_bch_calculate - Calculate the ECC corresponding to a data block
@ -38,16 +23,15 @@ struct nand_bch_control {
int nand_ecc_sw_bch_calculate(struct nand_device *nand, int nand_ecc_sw_bch_calculate(struct nand_device *nand,
const unsigned char *buf, unsigned char *code) const unsigned char *buf, unsigned char *code)
{ {
struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
struct nand_bch_control *nbc = chip->ecc.priv;
unsigned int i; unsigned int i;
memset(code, 0, chip->ecc.bytes); memset(code, 0, engine_conf->code_size);
bch_encode(nbc->bch, buf, chip->ecc.size, code); bch_encode(engine_conf->bch, buf, nand->ecc.ctx.conf.step_size, code);
/* apply mask so that an erased page is a valid codeword */ /* apply mask so that an erased page is a valid codeword */
for (i = 0; i < chip->ecc.bytes; i++) for (i = 0; i < engine_conf->code_size; i++)
code[i] ^= nbc->eccmask[i]; code[i] ^= engine_conf->eccmask[i];
return 0; return 0;
} }
@ -65,16 +49,16 @@ EXPORT_SYMBOL(nand_ecc_sw_bch_calculate);
int nand_ecc_sw_bch_correct(struct nand_device *nand, unsigned char *buf, int nand_ecc_sw_bch_correct(struct nand_device *nand, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc) unsigned char *read_ecc, unsigned char *calc_ecc)
{ {
struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
struct nand_bch_control *nbc = chip->ecc.priv; unsigned int step_size = nand->ecc.ctx.conf.step_size;
unsigned int *errloc = nbc->errloc; unsigned int *errloc = engine_conf->errloc;
int i, count; int i, count;
count = bch_decode(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, count = bch_decode(engine_conf->bch, NULL, step_size, read_ecc,
NULL, errloc); calc_ecc, NULL, errloc);
if (count > 0) { if (count > 0) {
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
if (errloc[i] < (chip->ecc.size * 8)) if (errloc[i] < (step_size * 8))
/* The error is in the data area: correct it */ /* The error is in the data area: correct it */
buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
@ -97,31 +81,30 @@ EXPORT_SYMBOL(nand_ecc_sw_bch_correct);
* *
* Returns: a pointer to a new NAND BCH control structure, or NULL upon failure * Returns: a pointer to a new NAND BCH control structure, or NULL upon failure
* *
* Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes * Initialize NAND BCH error correction. @nand.ecc parameters 'step_size' and
* are used to compute the following BCH parameters: * 'bytes' are used to compute the following BCH parameters:
* m, the Galois field order * m, the Galois field order
* t, the error correction capability * t, the error correction capability
* @eccbytes should be equal to the number of bytes required to store m * t * 'bytes' should be equal to the number of bytes required to store m * t
* bits, where m is such that 2^m - 1 > step_size * 8. * bits, where m is such that 2^m - 1 > step_size * 8.
* *
* Example: to configure 4 bit correction per 512 bytes, you should pass * Example: to configure 4 bit correction per 512 bytes, you should pass
* @eccsize = 512 (thus, m = 13 is the smallest integer such that 2^m - 1 > 512 * 8) * step_size = 512 (thus, m = 13 is the smallest integer such that 2^m - 1 > 512 * 8)
* @eccbytes = 7 (7 bytes are required to store m * t = 13 * 4 = 52 bits) * bytes = 7 (7 bytes are required to store m * t = 13 * 4 = 52 bits)
*/ */
int nand_ecc_sw_bch_init(struct nand_device *nand) int nand_ecc_sw_bch_init(struct nand_device *nand)
{ {
struct mtd_info *mtd = nanddev_to_mtd(nand); struct mtd_info *mtd = nanddev_to_mtd(nand);
struct nand_chip *chip = mtd_to_nand(mtd);
unsigned int m, t, eccsteps, i; unsigned int m, t, eccsteps, i;
struct nand_bch_control *nbc = NULL; struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
unsigned char *erased_page; unsigned char *erased_page;
unsigned int eccsize = chip->ecc.size; unsigned int eccsize = nand->ecc.ctx.conf.step_size;
unsigned int eccbytes = chip->ecc.bytes; unsigned int eccbytes = engine_conf->code_size;
unsigned int eccstrength = chip->ecc.strength; unsigned int eccstrength = nand->ecc.ctx.conf.strength;
if (!eccbytes && eccstrength) { if (!eccbytes && eccstrength) {
eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
chip->ecc.bytes = eccbytes; engine_conf->code_size = eccbytes;
} }
if (!eccsize || !eccbytes) { if (!eccsize || !eccbytes) {
@ -132,20 +115,14 @@ int nand_ecc_sw_bch_init(struct nand_device *nand)
m = fls(1+8*eccsize); m = fls(1+8*eccsize);
t = (eccbytes*8)/m; t = (eccbytes*8)/m;
nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); engine_conf->bch = bch_init(m, t, 0, false);
if (!nbc) if (!engine_conf->bch)
return -ENOMEM; return -EINVAL;
chip->ecc.priv = nbc;
nbc->bch = bch_init(m, t, 0, false);
if (!nbc->bch)
goto fail;
/* verify that eccbytes has the expected value */ /* verify that eccbytes has the expected value */
if (nbc->bch->ecc_bytes != eccbytes) { if (engine_conf->bch->ecc_bytes != eccbytes) {
pr_warn("invalid eccbytes %u, should be %u\n", pr_warn("invalid eccbytes %u, should be %u\n",
eccbytes, nbc->bch->ecc_bytes); eccbytes, engine_conf->bch->ecc_bytes);
goto fail; goto fail;
} }
@ -163,25 +140,15 @@ int nand_ecc_sw_bch_init(struct nand_device *nand)
goto fail; goto fail;
} }
/*
* ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(),
* which is called by mtd_ooblayout_count_eccbytes().
* Make sure they are properly initialized before calling
* mtd_ooblayout_count_eccbytes().
* FIXME: we should probably rework the sequencing in nand_scan_tail()
* to avoid setting those fields twice.
*/
chip->ecc.steps = eccsteps;
chip->ecc.total = eccsteps * eccbytes;
nand->base.ecc.ctx.total = chip->ecc.total;
if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) {
pr_warn("invalid ecc layout\n"); pr_warn("invalid ecc layout\n");
goto fail; goto fail;
} }
nbc->eccmask = kzalloc(eccbytes, GFP_KERNEL); engine_conf->eccmask = kzalloc(eccbytes, GFP_KERNEL);
nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL); engine_conf->errloc = kmalloc_array(t, sizeof(*engine_conf->errloc),
if (!nbc->eccmask || !nbc->errloc) GFP_KERNEL);
if (!engine_conf->eccmask || !engine_conf->errloc)
goto fail; goto fail;
/* /*
@ -192,14 +159,15 @@ int nand_ecc_sw_bch_init(struct nand_device *nand)
goto fail; goto fail;
memset(erased_page, 0xff, eccsize); memset(erased_page, 0xff, eccsize);
bch_encode(nbc->bch, erased_page, eccsize, nbc->eccmask); bch_encode(engine_conf->bch, erased_page, eccsize,
engine_conf->eccmask);
kfree(erased_page); kfree(erased_page);
for (i = 0; i < eccbytes; i++) for (i = 0; i < eccbytes; i++)
nbc->eccmask[i] ^= 0xff; engine_conf->eccmask[i] ^= 0xff;
if (!eccstrength) if (!eccstrength)
chip->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); nand->ecc.ctx.conf.strength = (eccbytes * 8) / fls(8 * eccsize);
return 0; return 0;
@ -216,14 +184,12 @@ EXPORT_SYMBOL(nand_ecc_sw_bch_init);
*/ */
void nand_ecc_sw_bch_cleanup(struct nand_device *nand) void nand_ecc_sw_bch_cleanup(struct nand_device *nand)
{ {
struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
struct nand_bch_control *nbc = chip->ecc.priv;
if (nbc) { if (engine_conf) {
bch_free(nbc->bch); bch_free(engine_conf->bch);
kfree(nbc->errloc); kfree(engine_conf->errloc);
kfree(nbc->eccmask); kfree(engine_conf->eccmask);
kfree(nbc);
} }
} }
EXPORT_SYMBOL(nand_ecc_sw_bch_cleanup); EXPORT_SYMBOL(nand_ecc_sw_bch_cleanup);

View file

@ -5142,8 +5142,33 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip)
int rawnand_sw_bch_init(struct nand_chip *chip) int rawnand_sw_bch_init(struct nand_chip *chip)
{ {
struct nand_device *base = &chip->base; struct nand_device *base = &chip->base;
struct nand_ecc_sw_bch_conf *engine_conf;
int ret;
return nand_ecc_sw_bch_init(base); base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
base->ecc.user_conf.algo = NAND_ECC_ALGO_BCH;
base->ecc.user_conf.step_size = chip->ecc.size;
base->ecc.user_conf.strength = chip->ecc.strength;
engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL);
if (!engine_conf)
return -ENOMEM;
engine_conf->code_size = chip->ecc.bytes;
base->ecc.ctx.priv = engine_conf;
ret = nand_ecc_sw_bch_init(base);
if (ret)
kfree(base->ecc.ctx.priv);
chip->ecc.size = base->ecc.ctx.conf.step_size;
chip->ecc.strength = base->ecc.ctx.conf.strength;
chip->ecc.total = base->ecc.ctx.total;
chip->ecc.steps = engine_conf->nsteps;
chip->ecc.bytes = engine_conf->code_size;
return ret;
} }
EXPORT_SYMBOL(rawnand_sw_bch_init); EXPORT_SYMBOL(rawnand_sw_bch_init);
@ -5171,7 +5196,7 @@ void rawnand_sw_bch_cleanup(struct nand_chip *chip)
nand_ecc_sw_bch_cleanup(base); nand_ecc_sw_bch_cleanup(base);
chip->ecc.priv = NULL; kfree(base->ecc.ctx.priv);
} }
EXPORT_SYMBOL(rawnand_sw_bch_cleanup); EXPORT_SYMBOL(rawnand_sw_bch_cleanup);
@ -5794,15 +5819,18 @@ static int nand_scan_tail(struct nand_chip *chip)
* 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
* mode. * mode.
*/ */
ecc->steps = mtd->writesize / ecc->size; if (!ecc->steps)
ecc->steps = mtd->writesize / ecc->size;
if (ecc->steps * ecc->size != mtd->writesize) { if (ecc->steps * ecc->size != mtd->writesize) {
WARN(1, "Invalid ECC parameters\n"); WARN(1, "Invalid ECC parameters\n");
ret = -EINVAL; ret = -EINVAL;
goto err_nand_manuf_cleanup; goto err_nand_manuf_cleanup;
} }
ecc->total = ecc->steps * ecc->bytes; if (!ecc->total) {
chip->base.ecc.ctx.total = ecc->total; ecc->total = ecc->steps * ecc->bytes;
chip->base.ecc.ctx.total = ecc->total;
}
if (ecc->total > mtd->oobsize) { if (ecc->total > mtd->oobsize) {
WARN(1, "Total number of ECC bytes exceeded oobsize\n"); WARN(1, "Total number of ECC bytes exceeded oobsize\n");

View file

@ -9,6 +9,31 @@
#define __MTD_NAND_ECC_SW_BCH_H__ #define __MTD_NAND_ECC_SW_BCH_H__
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
#include <linux/bch.h>
/**
* struct nand_ecc_sw_bch_conf - private software BCH ECC engine structure
* @reqooblen: Save the actual user OOB length requested before overwriting it
* @spare_oobbuf: Spare OOB buffer if none is provided
* @code_size: Number of bytes needed to store a code (one code per step)
* @nsteps: Number of steps
* @calc_buf: Buffer to use when calculating ECC bytes
* @code_buf: Buffer to use when reading (raw) ECC bytes from the chip
* @bch: BCH control structure
* @errloc: error location array
* @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
*/
struct nand_ecc_sw_bch_conf {
unsigned int reqooblen;
void *spare_oobbuf;
unsigned int code_size;
unsigned int nsteps;
u8 *calc_buf;
u8 *code_buf;
struct bch_control *bch;
unsigned int *errloc;
unsigned char *eccmask;
};
#if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH) #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)