Commit 9994bb3f authored by Miquel Raynal's avatar Miquel Raynal

mtd: nand: ecc-bch: Create the software BCH engine

Let's continue introducing the generic ECC engine abstraction in the
NAND subsystem by instantiating a first ECC engine: the software
BCH one.

While at it, make a very tidy ecc_sw_bch_init() function and move all
the sanity checks and user input management in
nand_ecc_sw_bch_init_ctx(). This second helper will be called from the
raw RAND core.
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-10-miquel.raynal@bootlin.com
parent 80fe6031
This diff is collapsed.
...@@ -5150,17 +5150,11 @@ int rawnand_sw_bch_init(struct nand_chip *chip) ...@@ -5150,17 +5150,11 @@ int rawnand_sw_bch_init(struct nand_chip *chip)
base->ecc.user_conf.step_size = chip->ecc.size; base->ecc.user_conf.step_size = chip->ecc.size;
base->ecc.user_conf.strength = chip->ecc.strength; base->ecc.user_conf.strength = chip->ecc.strength;
engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); ret = nand_ecc_sw_bch_init_ctx(base);
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) if (ret)
kfree(base->ecc.ctx.priv); return ret;
engine_conf = base->ecc.ctx.priv;
chip->ecc.size = base->ecc.ctx.conf.step_size; chip->ecc.size = base->ecc.ctx.conf.step_size;
chip->ecc.strength = base->ecc.ctx.conf.strength; chip->ecc.strength = base->ecc.ctx.conf.strength;
...@@ -5168,7 +5162,7 @@ int rawnand_sw_bch_init(struct nand_chip *chip) ...@@ -5168,7 +5162,7 @@ int rawnand_sw_bch_init(struct nand_chip *chip)
chip->ecc.steps = engine_conf->nsteps; chip->ecc.steps = engine_conf->nsteps;
chip->ecc.bytes = engine_conf->code_size; chip->ecc.bytes = engine_conf->code_size;
return ret; return 0;
} }
EXPORT_SYMBOL(rawnand_sw_bch_init); EXPORT_SYMBOL(rawnand_sw_bch_init);
...@@ -5194,9 +5188,7 @@ void rawnand_sw_bch_cleanup(struct nand_chip *chip) ...@@ -5194,9 +5188,7 @@ void rawnand_sw_bch_cleanup(struct nand_chip *chip)
{ {
struct nand_device *base = &chip->base; struct nand_device *base = &chip->base;
nand_ecc_sw_bch_cleanup(base); nand_ecc_sw_bch_cleanup_ctx(base);
kfree(base->ecc.ctx.priv);
} }
EXPORT_SYMBOL(rawnand_sw_bch_cleanup); EXPORT_SYMBOL(rawnand_sw_bch_cleanup);
...@@ -5308,51 +5300,15 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) ...@@ -5308,51 +5300,15 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
ecc->read_oob = nand_read_oob_std; ecc->read_oob = nand_read_oob_std;
ecc->write_oob = nand_write_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_get_large_page_ooblayout());
}
/* /*
* We can only maximize ECC config when the default layout is * We can only maximize ECC config when the default layout is
* used, otherwise we don't know how many bytes can really be * used, otherwise we don't know how many bytes can really be
* used. * used.
*/ */
if (mtd->ooblayout == nand_get_large_page_ooblayout() && if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH &&
nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { mtd->ooblayout != nand_get_large_page_ooblayout())
int steps, bytes; nanddev->ecc.user_conf.flags &= ~NAND_ECC_MAXIMIZE_STRENGTH;
/* Always prefer 1k blocks over 512bytes ones */
ecc->size = 1024;
steps = mtd->writesize / ecc->size;
/* Reserve 2 bytes for the BBM */
bytes = (mtd->oobsize - 2) / steps;
ecc->strength = bytes * 8 / fls(8 * ecc->size);
}
/* See the software BCH ECC initialization for details */
ecc->bytes = 0;
ret = rawnand_sw_bch_init(chip); ret = rawnand_sw_bch_init(chip);
if (ret) { if (ret) {
WARN(1, "BCH ECC initialization failed!\n"); WARN(1, "BCH ECC initialization failed!\n");
......
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
/** /**
* struct nand_ecc_sw_bch_conf - private software BCH ECC engine structure * struct nand_ecc_sw_bch_conf - private software BCH ECC engine structure
* @reqooblen: Save the actual user OOB length requested before overwriting it * @req_ctx: Save request context and tweak the original request to fit the
* @spare_oobbuf: Spare OOB buffer if none is provided * engine needs
* @code_size: Number of bytes needed to store a code (one code per step) * @code_size: Number of bytes needed to store a code (one code per step)
* @nsteps: Number of steps * @nsteps: Number of steps
* @calc_buf: Buffer to use when calculating ECC bytes * @calc_buf: Buffer to use when calculating ECC bytes
...@@ -24,8 +24,7 @@ ...@@ -24,8 +24,7 @@
* @eccmask: XOR ecc mask, allows erased pages to be decoded as valid * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
*/ */
struct nand_ecc_sw_bch_conf { struct nand_ecc_sw_bch_conf {
unsigned int reqooblen; struct nand_ecc_req_tweak_ctx req_ctx;
void *spare_oobbuf;
unsigned int code_size; unsigned int code_size;
unsigned int nsteps; unsigned int nsteps;
u8 *calc_buf; u8 *calc_buf;
...@@ -41,8 +40,9 @@ int nand_ecc_sw_bch_calculate(struct nand_device *nand, ...@@ -41,8 +40,9 @@ int nand_ecc_sw_bch_calculate(struct nand_device *nand,
const unsigned char *buf, unsigned char *code); const unsigned char *buf, unsigned char *code);
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);
int nand_ecc_sw_bch_init(struct nand_device *nand); int nand_ecc_sw_bch_init_ctx(struct nand_device *nand);
void nand_ecc_sw_bch_cleanup(struct nand_device *nand); void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand);
struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void);
#else /* !CONFIG_MTD_NAND_ECC_SW_BCH */ #else /* !CONFIG_MTD_NAND_ECC_SW_BCH */
...@@ -61,12 +61,12 @@ static inline int nand_ecc_sw_bch_correct(struct nand_device *nand, ...@@ -61,12 +61,12 @@ static inline int nand_ecc_sw_bch_correct(struct nand_device *nand,
return -ENOTSUPP; return -ENOTSUPP;
} }
static inline int nand_ecc_sw_bch_init(struct nand_device *nand) static inline int nand_ecc_sw_bch_init_ctx(struct nand_device *nand)
{ {
return -ENOTSUPP; return -ENOTSUPP;
} }
static inline void nand_ecc_sw_bch_cleanup(struct nand_device *nand) {} static inline void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand) {}
#endif /* CONFIG_MTD_NAND_ECC_SW_BCH */ #endif /* CONFIG_MTD_NAND_ECC_SW_BCH */
......
...@@ -278,6 +278,15 @@ int nand_ecc_finish_io_req(struct nand_device *nand, ...@@ -278,6 +278,15 @@ int nand_ecc_finish_io_req(struct nand_device *nand,
struct nand_page_io_req *req); struct nand_page_io_req *req);
bool nand_ecc_is_strong_enough(struct nand_device *nand); bool nand_ecc_is_strong_enough(struct nand_device *nand);
#if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)
struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void);
#else
static inline struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void)
{
return NULL;
}
#endif /* CONFIG_MTD_NAND_ECC_SW_BCH */
/** /**
* struct nand_ecc_req_tweak_ctx - Help for automatically tweaking requests * struct nand_ecc_req_tweak_ctx - Help for automatically tweaking requests
* @orig_req: Pointer to the original IO request * @orig_req: Pointer to the original IO request
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment