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>
This commit is contained in:
parent
df8b97024e
commit
2c8f8afa7f
@ -4523,6 +4523,226 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_check_ecc_caps - check the sanity of preset ECC settings
|
||||
* @chip: nand chip info structure
|
||||
* @caps: ECC caps info structure
|
||||
* @oobavail: OOB size that the ECC engine can use
|
||||
*
|
||||
* When ECC step size and strength are already set, check if they are supported
|
||||
* by the controller and the calculated ECC bytes fit within the chip's OOB.
|
||||
* On success, the calculated ECC bytes is set.
|
||||
*/
|
||||
int nand_check_ecc_caps(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_ecc_step_info *stepinfo;
|
||||
int preset_step = chip->ecc.size;
|
||||
int preset_strength = chip->ecc.strength;
|
||||
int nsteps, ecc_bytes;
|
||||
int i, j;
|
||||
|
||||
if (WARN_ON(oobavail < 0))
|
||||
return -EINVAL;
|
||||
|
||||
if (!preset_step || !preset_strength)
|
||||
return -ENODATA;
|
||||
|
||||
nsteps = mtd->writesize / preset_step;
|
||||
|
||||
for (i = 0; i < caps->nstepinfos; i++) {
|
||||
stepinfo = &caps->stepinfos[i];
|
||||
|
||||
if (stepinfo->stepsize != preset_step)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < stepinfo->nstrengths; j++) {
|
||||
if (stepinfo->strengths[j] != preset_strength)
|
||||
continue;
|
||||
|
||||
ecc_bytes = caps->calc_ecc_bytes(preset_step,
|
||||
preset_strength);
|
||||
if (WARN_ON_ONCE(ecc_bytes < 0))
|
||||
return ecc_bytes;
|
||||
|
||||
if (ecc_bytes * nsteps > oobavail) {
|
||||
pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB",
|
||||
preset_step, preset_strength);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
chip->ecc.bytes = ecc_bytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
pr_err("ECC (step, strength) = (%d, %d) not supported on this controller",
|
||||
preset_step, preset_strength);
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_check_ecc_caps);
|
||||
|
||||
/**
|
||||
* nand_match_ecc_req - meet the chip's requirement with least ECC bytes
|
||||
* @chip: nand chip info structure
|
||||
* @caps: ECC engine caps info structure
|
||||
* @oobavail: OOB size that the ECC engine can use
|
||||
*
|
||||
* If a chip's ECC requirement is provided, try to meet it with the least
|
||||
* number of ECC bytes (i.e. with the largest number of OOB-free bytes).
|
||||
* On success, the chosen ECC settings are set.
|
||||
*/
|
||||
int nand_match_ecc_req(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_ecc_step_info *stepinfo;
|
||||
int req_step = chip->ecc_step_ds;
|
||||
int req_strength = chip->ecc_strength_ds;
|
||||
int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total;
|
||||
int best_step, best_strength, best_ecc_bytes;
|
||||
int best_ecc_bytes_total = INT_MAX;
|
||||
int i, j;
|
||||
|
||||
if (WARN_ON(oobavail < 0))
|
||||
return -EINVAL;
|
||||
|
||||
/* No information provided by the NAND chip */
|
||||
if (!req_step || !req_strength)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* number of correctable bits the chip requires in a page */
|
||||
req_corr = mtd->writesize / req_step * req_strength;
|
||||
|
||||
for (i = 0; i < caps->nstepinfos; i++) {
|
||||
stepinfo = &caps->stepinfos[i];
|
||||
step_size = stepinfo->stepsize;
|
||||
|
||||
for (j = 0; j < stepinfo->nstrengths; j++) {
|
||||
strength = stepinfo->strengths[j];
|
||||
|
||||
/*
|
||||
* If both step size and strength are smaller than the
|
||||
* chip's requirement, it is not easy to compare the
|
||||
* resulted reliability.
|
||||
*/
|
||||
if (step_size < req_step && strength < req_strength)
|
||||
continue;
|
||||
|
||||
if (mtd->writesize % step_size)
|
||||
continue;
|
||||
|
||||
nsteps = mtd->writesize / step_size;
|
||||
|
||||
ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
|
||||
if (WARN_ON_ONCE(ecc_bytes < 0))
|
||||
continue;
|
||||
ecc_bytes_total = ecc_bytes * nsteps;
|
||||
|
||||
if (ecc_bytes_total > oobavail ||
|
||||
strength * nsteps < req_corr)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* We assume the best is to meet the chip's requrement
|
||||
* with the least number of ECC bytes.
|
||||
*/
|
||||
if (ecc_bytes_total < best_ecc_bytes_total) {
|
||||
best_ecc_bytes_total = ecc_bytes_total;
|
||||
best_step = step_size;
|
||||
best_strength = strength;
|
||||
best_ecc_bytes = ecc_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_ecc_bytes_total == INT_MAX)
|
||||
return -ENOTSUPP;
|
||||
|
||||
chip->ecc.size = best_step;
|
||||
chip->ecc.strength = best_strength;
|
||||
chip->ecc.bytes = best_ecc_bytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_match_ecc_req);
|
||||
|
||||
/**
|
||||
* nand_maximize_ecc - choose the max ECC strength available
|
||||
* @chip: nand chip info structure
|
||||
* @caps: ECC engine caps info structure
|
||||
* @oobavail: OOB size that the ECC engine can use
|
||||
*
|
||||
* Choose the max ECC strength that is supported on the controller, and can fit
|
||||
* within the chip's OOB. On success, the chosen ECC settings are set.
|
||||
*/
|
||||
int nand_maximize_ecc(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_ecc_step_info *stepinfo;
|
||||
int step_size, strength, nsteps, ecc_bytes, corr;
|
||||
int best_corr = 0;
|
||||
int best_step = 0;
|
||||
int best_strength, best_ecc_bytes;
|
||||
int i, j;
|
||||
|
||||
if (WARN_ON(oobavail < 0))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < caps->nstepinfos; i++) {
|
||||
stepinfo = &caps->stepinfos[i];
|
||||
step_size = stepinfo->stepsize;
|
||||
|
||||
/* If chip->ecc.size is already set, respect it */
|
||||
if (chip->ecc.size && step_size != chip->ecc.size)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < stepinfo->nstrengths; j++) {
|
||||
strength = stepinfo->strengths[j];
|
||||
|
||||
if (mtd->writesize % step_size)
|
||||
continue;
|
||||
|
||||
nsteps = mtd->writesize / step_size;
|
||||
|
||||
ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
|
||||
if (WARN_ON_ONCE(ecc_bytes < 0))
|
||||
continue;
|
||||
|
||||
if (ecc_bytes * nsteps > oobavail)
|
||||
continue;
|
||||
|
||||
corr = strength * nsteps;
|
||||
|
||||
/*
|
||||
* If the number of correctable bits is the same,
|
||||
* bigger step_size has more reliability.
|
||||
*/
|
||||
if (corr > best_corr ||
|
||||
(corr == best_corr && step_size > best_step)) {
|
||||
best_corr = corr;
|
||||
best_step = step_size;
|
||||
best_strength = strength;
|
||||
best_ecc_bytes = ecc_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!best_corr)
|
||||
return -ENOTSUPP;
|
||||
|
||||
chip->ecc.size = best_step;
|
||||
chip->ecc.strength = best_strength;
|
||||
chip->ecc.bytes = best_ecc_bytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_maximize_ecc);
|
||||
|
||||
/*
|
||||
* Check if the chip configuration meet the datasheet requirements.
|
||||
|
||||
|
@ -481,6 +481,30 @@ static inline void nand_hw_control_init(struct nand_hw_control *nfc)
|
||||
init_waitqueue_head(&nfc->wq);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct nand_ecc_step_info - ECC step information of ECC engine
|
||||
* @stepsize: data bytes per ECC step
|
||||
* @strengths: array of supported strengths
|
||||
* @nstrengths: number of supported strengths
|
||||
*/
|
||||
struct nand_ecc_step_info {
|
||||
int stepsize;
|
||||
const int *strengths;
|
||||
int nstrengths;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_ecc_caps - capability of ECC engine
|
||||
* @stepinfos: array of ECC step information
|
||||
* @nstepinfos: number of ECC step information
|
||||
* @calc_ecc_bytes: driver's hook to calculate ECC bytes per step
|
||||
*/
|
||||
struct nand_ecc_caps {
|
||||
const struct nand_ecc_step_info *stepinfos;
|
||||
int nstepinfos;
|
||||
int (*calc_ecc_bytes)(int step_size, int strength);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_ecc_ctrl - Control structure for ECC
|
||||
* @mode: ECC mode
|
||||
@ -1246,6 +1270,15 @@ int nand_check_erased_ecc_chunk(void *data, int datalen,
|
||||
void *extraoob, int extraooblen,
|
||||
int threshold);
|
||||
|
||||
int nand_check_ecc_caps(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail);
|
||||
|
||||
int nand_match_ecc_req(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail);
|
||||
|
||||
int nand_maximize_ecc(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail);
|
||||
|
||||
/* Default write_oob implementation */
|
||||
int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user