|
@@ -4077,6 +4077,226 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
|
|
}
|
|
|
EXPORT_SYMBOL(nand_scan_ident);
|
|
|
|
|
|
+/**
|
|
|
+ * 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.
|
|
|
|