Skip to content

Commit 2c8f8af

Browse files
masahir0yBoris Brezillon
authored andcommitted
mtd: nand: add generic helpers to check, match, maximize ECC settings
Driver are responsible for setting up ECC parameters correctly. Those include: - Check if ECC parameters specified (usually by DT) are valid - Meet the chip's ECC requirement - Maximize ECC strength if NAND_ECC_MAXIMIZE flag is set The logic can be generalized by factoring out common code. This commit adds 3 helpers to the NAND framework: nand_check_ecc_caps - Check if preset step_size and strength are valid nand_match_ecc_req - Match the chip's requirement nand_maximize_ecc - Maximize the ECC strength To use the helpers above, a driver needs to provide: - Data array of supported ECC step size and strength - A hook that calculates ECC bytes from the combination of step_size and strength. By using those helpers, code duplication among drivers will be reduced. Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
1 parent df8b970 commit 2c8f8af

File tree

2 files changed

+253
-0
lines changed

2 files changed

+253
-0
lines changed

drivers/mtd/nand/nand_base.c

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4523,6 +4523,226 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
45234523
}
45244524
}
45254525

4526+
/**
4527+
* nand_check_ecc_caps - check the sanity of preset ECC settings
4528+
* @chip: nand chip info structure
4529+
* @caps: ECC caps info structure
4530+
* @oobavail: OOB size that the ECC engine can use
4531+
*
4532+
* When ECC step size and strength are already set, check if they are supported
4533+
* by the controller and the calculated ECC bytes fit within the chip's OOB.
4534+
* On success, the calculated ECC bytes is set.
4535+
*/
4536+
int nand_check_ecc_caps(struct nand_chip *chip,
4537+
const struct nand_ecc_caps *caps, int oobavail)
4538+
{
4539+
struct mtd_info *mtd = nand_to_mtd(chip);
4540+
const struct nand_ecc_step_info *stepinfo;
4541+
int preset_step = chip->ecc.size;
4542+
int preset_strength = chip->ecc.strength;
4543+
int nsteps, ecc_bytes;
4544+
int i, j;
4545+
4546+
if (WARN_ON(oobavail < 0))
4547+
return -EINVAL;
4548+
4549+
if (!preset_step || !preset_strength)
4550+
return -ENODATA;
4551+
4552+
nsteps = mtd->writesize / preset_step;
4553+
4554+
for (i = 0; i < caps->nstepinfos; i++) {
4555+
stepinfo = &caps->stepinfos[i];
4556+
4557+
if (stepinfo->stepsize != preset_step)
4558+
continue;
4559+
4560+
for (j = 0; j < stepinfo->nstrengths; j++) {
4561+
if (stepinfo->strengths[j] != preset_strength)
4562+
continue;
4563+
4564+
ecc_bytes = caps->calc_ecc_bytes(preset_step,
4565+
preset_strength);
4566+
if (WARN_ON_ONCE(ecc_bytes < 0))
4567+
return ecc_bytes;
4568+
4569+
if (ecc_bytes * nsteps > oobavail) {
4570+
pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB",
4571+
preset_step, preset_strength);
4572+
return -ENOSPC;
4573+
}
4574+
4575+
chip->ecc.bytes = ecc_bytes;
4576+
4577+
return 0;
4578+
}
4579+
}
4580+
4581+
pr_err("ECC (step, strength) = (%d, %d) not supported on this controller",
4582+
preset_step, preset_strength);
4583+
4584+
return -ENOTSUPP;
4585+
}
4586+
EXPORT_SYMBOL_GPL(nand_check_ecc_caps);
4587+
4588+
/**
4589+
* nand_match_ecc_req - meet the chip's requirement with least ECC bytes
4590+
* @chip: nand chip info structure
4591+
* @caps: ECC engine caps info structure
4592+
* @oobavail: OOB size that the ECC engine can use
4593+
*
4594+
* If a chip's ECC requirement is provided, try to meet it with the least
4595+
* number of ECC bytes (i.e. with the largest number of OOB-free bytes).
4596+
* On success, the chosen ECC settings are set.
4597+
*/
4598+
int nand_match_ecc_req(struct nand_chip *chip,
4599+
const struct nand_ecc_caps *caps, int oobavail)
4600+
{
4601+
struct mtd_info *mtd = nand_to_mtd(chip);
4602+
const struct nand_ecc_step_info *stepinfo;
4603+
int req_step = chip->ecc_step_ds;
4604+
int req_strength = chip->ecc_strength_ds;
4605+
int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total;
4606+
int best_step, best_strength, best_ecc_bytes;
4607+
int best_ecc_bytes_total = INT_MAX;
4608+
int i, j;
4609+
4610+
if (WARN_ON(oobavail < 0))
4611+
return -EINVAL;
4612+
4613+
/* No information provided by the NAND chip */
4614+
if (!req_step || !req_strength)
4615+
return -ENOTSUPP;
4616+
4617+
/* number of correctable bits the chip requires in a page */
4618+
req_corr = mtd->writesize / req_step * req_strength;
4619+
4620+
for (i = 0; i < caps->nstepinfos; i++) {
4621+
stepinfo = &caps->stepinfos[i];
4622+
step_size = stepinfo->stepsize;
4623+
4624+
for (j = 0; j < stepinfo->nstrengths; j++) {
4625+
strength = stepinfo->strengths[j];
4626+
4627+
/*
4628+
* If both step size and strength are smaller than the
4629+
* chip's requirement, it is not easy to compare the
4630+
* resulted reliability.
4631+
*/
4632+
if (step_size < req_step && strength < req_strength)
4633+
continue;
4634+
4635+
if (mtd->writesize % step_size)
4636+
continue;
4637+
4638+
nsteps = mtd->writesize / step_size;
4639+
4640+
ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
4641+
if (WARN_ON_ONCE(ecc_bytes < 0))
4642+
continue;
4643+
ecc_bytes_total = ecc_bytes * nsteps;
4644+
4645+
if (ecc_bytes_total > oobavail ||
4646+
strength * nsteps < req_corr)
4647+
continue;
4648+
4649+
/*
4650+
* We assume the best is to meet the chip's requrement
4651+
* with the least number of ECC bytes.
4652+
*/
4653+
if (ecc_bytes_total < best_ecc_bytes_total) {
4654+
best_ecc_bytes_total = ecc_bytes_total;
4655+
best_step = step_size;
4656+
best_strength = strength;
4657+
best_ecc_bytes = ecc_bytes;
4658+
}
4659+
}
4660+
}
4661+
4662+
if (best_ecc_bytes_total == INT_MAX)
4663+
return -ENOTSUPP;
4664+
4665+
chip->ecc.size = best_step;
4666+
chip->ecc.strength = best_strength;
4667+
chip->ecc.bytes = best_ecc_bytes;
4668+
4669+
return 0;
4670+
}
4671+
EXPORT_SYMBOL_GPL(nand_match_ecc_req);
4672+
4673+
/**
4674+
* nand_maximize_ecc - choose the max ECC strength available
4675+
* @chip: nand chip info structure
4676+
* @caps: ECC engine caps info structure
4677+
* @oobavail: OOB size that the ECC engine can use
4678+
*
4679+
* Choose the max ECC strength that is supported on the controller, and can fit
4680+
* within the chip's OOB. On success, the chosen ECC settings are set.
4681+
*/
4682+
int nand_maximize_ecc(struct nand_chip *chip,
4683+
const struct nand_ecc_caps *caps, int oobavail)
4684+
{
4685+
struct mtd_info *mtd = nand_to_mtd(chip);
4686+
const struct nand_ecc_step_info *stepinfo;
4687+
int step_size, strength, nsteps, ecc_bytes, corr;
4688+
int best_corr = 0;
4689+
int best_step = 0;
4690+
int best_strength, best_ecc_bytes;
4691+
int i, j;
4692+
4693+
if (WARN_ON(oobavail < 0))
4694+
return -EINVAL;
4695+
4696+
for (i = 0; i < caps->nstepinfos; i++) {
4697+
stepinfo = &caps->stepinfos[i];
4698+
step_size = stepinfo->stepsize;
4699+
4700+
/* If chip->ecc.size is already set, respect it */
4701+
if (chip->ecc.size && step_size != chip->ecc.size)
4702+
continue;
4703+
4704+
for (j = 0; j < stepinfo->nstrengths; j++) {
4705+
strength = stepinfo->strengths[j];
4706+
4707+
if (mtd->writesize % step_size)
4708+
continue;
4709+
4710+
nsteps = mtd->writesize / step_size;
4711+
4712+
ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
4713+
if (WARN_ON_ONCE(ecc_bytes < 0))
4714+
continue;
4715+
4716+
if (ecc_bytes * nsteps > oobavail)
4717+
continue;
4718+
4719+
corr = strength * nsteps;
4720+
4721+
/*
4722+
* If the number of correctable bits is the same,
4723+
* bigger step_size has more reliability.
4724+
*/
4725+
if (corr > best_corr ||
4726+
(corr == best_corr && step_size > best_step)) {
4727+
best_corr = corr;
4728+
best_step = step_size;
4729+
best_strength = strength;
4730+
best_ecc_bytes = ecc_bytes;
4731+
}
4732+
}
4733+
}
4734+
4735+
if (!best_corr)
4736+
return -ENOTSUPP;
4737+
4738+
chip->ecc.size = best_step;
4739+
chip->ecc.strength = best_strength;
4740+
chip->ecc.bytes = best_ecc_bytes;
4741+
4742+
return 0;
4743+
}
4744+
EXPORT_SYMBOL_GPL(nand_maximize_ecc);
4745+
45264746
/*
45274747
* Check if the chip configuration meet the datasheet requirements.
45284748

include/linux/mtd/nand.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,30 @@ static inline void nand_hw_control_init(struct nand_hw_control *nfc)
481481
init_waitqueue_head(&nfc->wq);
482482
}
483483

484+
/**
485+
* struct nand_ecc_step_info - ECC step information of ECC engine
486+
* @stepsize: data bytes per ECC step
487+
* @strengths: array of supported strengths
488+
* @nstrengths: number of supported strengths
489+
*/
490+
struct nand_ecc_step_info {
491+
int stepsize;
492+
const int *strengths;
493+
int nstrengths;
494+
};
495+
496+
/**
497+
* struct nand_ecc_caps - capability of ECC engine
498+
* @stepinfos: array of ECC step information
499+
* @nstepinfos: number of ECC step information
500+
* @calc_ecc_bytes: driver's hook to calculate ECC bytes per step
501+
*/
502+
struct nand_ecc_caps {
503+
const struct nand_ecc_step_info *stepinfos;
504+
int nstepinfos;
505+
int (*calc_ecc_bytes)(int step_size, int strength);
506+
};
507+
484508
/**
485509
* struct nand_ecc_ctrl - Control structure for ECC
486510
* @mode: ECC mode
@@ -1246,6 +1270,15 @@ int nand_check_erased_ecc_chunk(void *data, int datalen,
12461270
void *extraoob, int extraooblen,
12471271
int threshold);
12481272

1273+
int nand_check_ecc_caps(struct nand_chip *chip,
1274+
const struct nand_ecc_caps *caps, int oobavail);
1275+
1276+
int nand_match_ecc_req(struct nand_chip *chip,
1277+
const struct nand_ecc_caps *caps, int oobavail);
1278+
1279+
int nand_maximize_ecc(struct nand_chip *chip,
1280+
const struct nand_ecc_caps *caps, int oobavail);
1281+
12491282
/* Default write_oob implementation */
12501283
int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page);
12511284

0 commit comments

Comments
 (0)