// SPDX-License-Identifier: GPL-2.0+ /* * Generic Error-Correcting Code (ECC) engine * * Copyright (C) 2019 Macronix * Author: * Miquèl RAYNAL <miquel.raynal@bootlin.com> * * * This file describes the abstraction of any NAND ECC engine. It has been * designed to fit most cases, including parallel NANDs and SPI-NANDs. * * There are three main situations where instantiating this ECC engine makes * sense: * - external: The ECC engine is outside the NAND pipeline, typically this * is a software ECC engine, or an hardware engine that is * outside the NAND controller pipeline. * - pipelined: The ECC engine is inside the NAND pipeline, ie. on the * controller's side. This is the case of most of the raw NAND * controllers. In the pipeline case, the ECC bytes are * generated/data corrected on the fly when a page is * written/read. * - ondie: The ECC engine is inside the NAND pipeline, on the chip's side. * Some NAND chips can correct themselves the data. * * Besides the initial setup and final cleanups, the interfaces are rather * simple: * - prepare: Prepare an I/O request. Enable/disable the ECC engine based on * the I/O request type. In case of software correction or external * engine, this step may involve to derive the ECC bytes and place * them in the OOB area before a write. * - finish: Finish an I/O request. Correct the data in case of a read * request and report the number of corrected bits/uncorrectable * errors. Most likely empty for write operations, unless you have * hardware specific stuff to do, like shutting down the engine to * save power. * * The I/O request should be enclosed in a prepare()/finish() pair of calls * and will behave differently depending on the requested I/O type: * - raw: Correction disabled * - ecc: Correction enabled * * The request direction is impacting the logic as well: * - read: Load data from the NAND chip * - write: Store data in the NAND chip * * Mixing all this combinations together gives the following behavior. * Those are just examples, drivers are free to add custom steps in their * prepare/finish hook. * * [external ECC engine] * - external + prepare + raw + read: do nothing * - external + finish + raw + read: do nothing * - external + prepare + raw + write: do nothing * - external + finish + raw + write: do nothing * - external + prepare + ecc + read: do nothing * - external + finish + ecc + read: calculate expected ECC bytes, extract * ECC bytes from OOB buffer, correct * and report any bitflip/error * - external + prepare + ecc + write: calculate ECC bytes and store them at * the right place in the OOB buffer based * on the OOB layout * - external + finish + ecc + write: do nothing * * [pipelined ECC engine] * - pipelined + prepare + raw + read: disable the controller's ECC engine if * activated * - pipelined + finish + raw + read: do nothing * - pipelined + prepare + raw + write: disable the controller's ECC engine if * activated * - pipelined + finish + raw + write: do nothing * - pipelined + prepare + ecc + read: enable the controller's ECC engine if * deactivated * - pipelined + finish + ecc + read: check the status, report any * error/bitflip * - pipelined + prepare + ecc + write: enable the controller's ECC engine if * deactivated * - pipelined + finish + ecc + write: do nothing * * [ondie ECC engine] * - ondie + prepare + raw + read: send commands to disable the on-chip ECC * engine if activated * - ondie + finish + raw + read: do nothing * - ondie + prepare + raw + write: send commands to disable the on-chip ECC * engine if activated * - ondie + finish + raw + write: do nothing * - ondie + prepare + ecc + read: send commands to enable the on-chip ECC * engine if deactivated * - ondie + finish + ecc + read: send commands to check the status, report * any error/bitflip * - ondie + prepare + ecc + write: send commands to enable the on-chip ECC * engine if deactivated * - ondie + finish + ecc + write: do nothing
*/
/** * nand_ecc_init_ctx - Init the ECC engine context * @nand: the NAND device * * On success, the caller is responsible of calling @nand_ecc_cleanup_ctx().
*/ int nand_ecc_init_ctx(struct nand_device *nand)
{ if (!nand->ecc.engine || !nand->ecc.engine->ops->init_ctx) return 0;
/* * Support the old "large page" layout used for 1-bit Hamming ECC where ECC * are placed at a fixed offset.
*/ staticint nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion)
{ struct nand_device *nand = mtd_to_nanddev(mtd); unsignedint total_ecc_bytes = nand->ecc.ctx.total;
if (section) return -ERANGE;
switch (mtd->oobsize) { case 64:
oobregion->offset = 40; break; case 128:
oobregion->offset = 80; break; default: return -EINVAL;
}
strength = of_get_nand_ecc_strength(dn); if (strength >= 0)
nand->ecc.user_conf.strength = strength;
size = of_get_nand_ecc_step_size(dn); if (size >= 0)
nand->ecc.user_conf.step_size = size;
if (of_property_read_bool(dn, "nand-ecc-maximize"))
nand->ecc.user_conf.flags |= NAND_ECC_MAXIMIZE_STRENGTH;
}
EXPORT_SYMBOL(of_get_nand_ecc_user_config);
/** * nand_ecc_is_strong_enough - Check if the chip configuration meets the * datasheet requirements. * * @nand: Device to check * * If our configuration corrects A bits per B bytes and the minimum * required correction level is X bits per Y bytes, then we must ensure * both of the following are true: * * (1) A / B >= X / Y * (2) A >= X * * Requirement (1) ensures we can correct for the required bitflip density. * Requirement (2) ensures we can correct even when all bitflips are clumped * in the same sector.
*/ bool nand_ecc_is_strong_enough(struct nand_device *nand)
{ conststruct nand_ecc_props *reqs = nanddev_get_ecc_requirements(nand); conststruct nand_ecc_props *conf = nanddev_get_ecc_conf(nand); struct mtd_info *mtd = nanddev_to_mtd(nand); int corr, ds_corr;
if (conf->step_size == 0 || reqs->step_size == 0) /* Not enough information */ returntrue;
/* * We get the number of corrected bits per page to compare * the correction density.
*/
corr = (mtd->writesize * conf->strength) / conf->step_size;
ds_corr = (mtd->writesize * reqs->strength) / reqs->step_size;
/* Let the user decide the exact length of each buffer */ if (!ctx->page_buffer_size)
ctx->page_buffer_size = nanddev_page_size(nand); if (!ctx->oob_buffer_size)
ctx->oob_buffer_size = nanddev_per_page_oobsize(nand);
/* * Ensure data and OOB area is fully read/written otherwise the correction might * not work as expected.
*/ void nand_ecc_tweak_req(struct nand_ecc_req_tweak_ctx *ctx, struct nand_page_io_req *req)
{ struct nand_device *nand = ctx->nand; struct nand_page_io_req *orig, *tweak;
/* Save the original request */
ctx->orig_req = *req;
ctx->bounce_data = false;
ctx->bounce_oob = false;
orig = &ctx->orig_req;
tweak = req;
/* Ensure the request covers the entire page */ if (orig->datalen < nanddev_page_size(nand)) {
ctx->bounce_data = true;
tweak->dataoffs = 0;
tweak->datalen = nanddev_page_size(nand);
tweak->databuf.in = ctx->spare_databuf;
memset(tweak->databuf.in, 0xFF, ctx->page_buffer_size);
}
/* Copy the data that must be writen in the bounce buffers, if needed */ if (orig->type == NAND_PAGE_WRITE) { if (ctx->bounce_data)
memcpy((void *)tweak->databuf.out + orig->dataoffs,
orig->databuf.out, orig->datalen);
/* Restore the data read from the bounce buffers, if needed */ if (orig->type == NAND_PAGE_READ) { if (ctx->bounce_data)
memcpy(orig->databuf.in,
tweak->databuf.in + orig->dataoffs,
orig->datalen);
if (ctx->bounce_oob)
memcpy(orig->oobbuf.in,
tweak->oobbuf.in + orig->ooboffs,
orig->ooblen);
}
/* Ensure the original request is restored */
*req = *orig;
}
EXPORT_SYMBOL_GPL(nand_ecc_restore_req);
/* * In the case of a pipelined engine, the device registering the ECC * engine is not necessarily the ECC engine itself but may be a host controller. * It is then useful to provide a helper to retrieve the right device object * which actually represents the ECC engine.
*/ struct device *nand_ecc_get_engine_dev(struct device *host)
{ struct platform_device *ecc_pdev; struct device_node *np;
/* * If the device node contains this property, it means we need to follow * it in order to get the right ECC engine device we are looking for.
*/
np = of_parse_phandle(host->of_node, "nand-ecc-engine", 0); if (!np) return host;
ecc_pdev = of_find_device_by_node(np); if (!ecc_pdev) {
of_node_put(np); return NULL;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.