18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Generic Error-Correcting Code (ECC) engine
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Macronix
68c2ecf20Sopenharmony_ci * Author:
78c2ecf20Sopenharmony_ci *     Miquèl RAYNAL <miquel.raynal@bootlin.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This file describes the abstraction of any NAND ECC engine. It has been
118c2ecf20Sopenharmony_ci * designed to fit most cases, including parallel NANDs and SPI-NANDs.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * There are three main situations where instantiating this ECC engine makes
148c2ecf20Sopenharmony_ci * sense:
158c2ecf20Sopenharmony_ci *   - external: The ECC engine is outside the NAND pipeline, typically this
168c2ecf20Sopenharmony_ci *               is a software ECC engine, or an hardware engine that is
178c2ecf20Sopenharmony_ci *               outside the NAND controller pipeline.
188c2ecf20Sopenharmony_ci *   - pipelined: The ECC engine is inside the NAND pipeline, ie. on the
198c2ecf20Sopenharmony_ci *                controller's side. This is the case of most of the raw NAND
208c2ecf20Sopenharmony_ci *                controllers. In the pipeline case, the ECC bytes are
218c2ecf20Sopenharmony_ci *                generated/data corrected on the fly when a page is
228c2ecf20Sopenharmony_ci *                written/read.
238c2ecf20Sopenharmony_ci *   - ondie: The ECC engine is inside the NAND pipeline, on the chip's side.
248c2ecf20Sopenharmony_ci *            Some NAND chips can correct themselves the data.
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * Besides the initial setup and final cleanups, the interfaces are rather
278c2ecf20Sopenharmony_ci * simple:
288c2ecf20Sopenharmony_ci *   - prepare: Prepare an I/O request. Enable/disable the ECC engine based on
298c2ecf20Sopenharmony_ci *              the I/O request type. In case of software correction or external
308c2ecf20Sopenharmony_ci *              engine, this step may involve to derive the ECC bytes and place
318c2ecf20Sopenharmony_ci *              them in the OOB area before a write.
328c2ecf20Sopenharmony_ci *   - finish: Finish an I/O request. Correct the data in case of a read
338c2ecf20Sopenharmony_ci *             request and report the number of corrected bits/uncorrectable
348c2ecf20Sopenharmony_ci *             errors. Most likely empty for write operations, unless you have
358c2ecf20Sopenharmony_ci *             hardware specific stuff to do, like shutting down the engine to
368c2ecf20Sopenharmony_ci *             save power.
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * The I/O request should be enclosed in a prepare()/finish() pair of calls
398c2ecf20Sopenharmony_ci * and will behave differently depending on the requested I/O type:
408c2ecf20Sopenharmony_ci *   - raw: Correction disabled
418c2ecf20Sopenharmony_ci *   - ecc: Correction enabled
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci * The request direction is impacting the logic as well:
448c2ecf20Sopenharmony_ci *   - read: Load data from the NAND chip
458c2ecf20Sopenharmony_ci *   - write: Store data in the NAND chip
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * Mixing all this combinations together gives the following behavior.
488c2ecf20Sopenharmony_ci * Those are just examples, drivers are free to add custom steps in their
498c2ecf20Sopenharmony_ci * prepare/finish hook.
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci * [external ECC engine]
528c2ecf20Sopenharmony_ci *   - external + prepare + raw + read: do nothing
538c2ecf20Sopenharmony_ci *   - external + finish  + raw + read: do nothing
548c2ecf20Sopenharmony_ci *   - external + prepare + raw + write: do nothing
558c2ecf20Sopenharmony_ci *   - external + finish  + raw + write: do nothing
568c2ecf20Sopenharmony_ci *   - external + prepare + ecc + read: do nothing
578c2ecf20Sopenharmony_ci *   - external + finish  + ecc + read: calculate expected ECC bytes, extract
588c2ecf20Sopenharmony_ci *                                      ECC bytes from OOB buffer, correct
598c2ecf20Sopenharmony_ci *                                      and report any bitflip/error
608c2ecf20Sopenharmony_ci *   - external + prepare + ecc + write: calculate ECC bytes and store them at
618c2ecf20Sopenharmony_ci *                                       the right place in the OOB buffer based
628c2ecf20Sopenharmony_ci *                                       on the OOB layout
638c2ecf20Sopenharmony_ci *   - external + finish  + ecc + write: do nothing
648c2ecf20Sopenharmony_ci *
658c2ecf20Sopenharmony_ci * [pipelined ECC engine]
668c2ecf20Sopenharmony_ci *   - pipelined + prepare + raw + read: disable the controller's ECC engine if
678c2ecf20Sopenharmony_ci *                                       activated
688c2ecf20Sopenharmony_ci *   - pipelined + finish  + raw + read: do nothing
698c2ecf20Sopenharmony_ci *   - pipelined + prepare + raw + write: disable the controller's ECC engine if
708c2ecf20Sopenharmony_ci *                                        activated
718c2ecf20Sopenharmony_ci *   - pipelined + finish  + raw + write: do nothing
728c2ecf20Sopenharmony_ci *   - pipelined + prepare + ecc + read: enable the controller's ECC engine if
738c2ecf20Sopenharmony_ci *                                       deactivated
748c2ecf20Sopenharmony_ci *   - pipelined + finish  + ecc + read: check the status, report any
758c2ecf20Sopenharmony_ci *                                       error/bitflip
768c2ecf20Sopenharmony_ci *   - pipelined + prepare + ecc + write: enable the controller's ECC engine if
778c2ecf20Sopenharmony_ci *                                        deactivated
788c2ecf20Sopenharmony_ci *   - pipelined + finish  + ecc + write: do nothing
798c2ecf20Sopenharmony_ci *
808c2ecf20Sopenharmony_ci * [ondie ECC engine]
818c2ecf20Sopenharmony_ci *   - ondie + prepare + raw + read: send commands to disable the on-chip ECC
828c2ecf20Sopenharmony_ci *                                   engine if activated
838c2ecf20Sopenharmony_ci *   - ondie + finish  + raw + read: do nothing
848c2ecf20Sopenharmony_ci *   - ondie + prepare + raw + write: send commands to disable the on-chip ECC
858c2ecf20Sopenharmony_ci *                                    engine if activated
868c2ecf20Sopenharmony_ci *   - ondie + finish  + raw + write: do nothing
878c2ecf20Sopenharmony_ci *   - ondie + prepare + ecc + read: send commands to enable the on-chip ECC
888c2ecf20Sopenharmony_ci *                                   engine if deactivated
898c2ecf20Sopenharmony_ci *   - ondie + finish  + ecc + read: send commands to check the status, report
908c2ecf20Sopenharmony_ci *                                   any error/bitflip
918c2ecf20Sopenharmony_ci *   - ondie + prepare + ecc + write: send commands to enable the on-chip ECC
928c2ecf20Sopenharmony_ci *                                    engine if deactivated
938c2ecf20Sopenharmony_ci *   - ondie + finish  + ecc + write: do nothing
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci#include <linux/module.h>
978c2ecf20Sopenharmony_ci#include <linux/mtd/nand.h>
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/**
1008c2ecf20Sopenharmony_ci * nand_ecc_init_ctx - Init the ECC engine context
1018c2ecf20Sopenharmony_ci * @nand: the NAND device
1028c2ecf20Sopenharmony_ci *
1038c2ecf20Sopenharmony_ci * On success, the caller is responsible of calling @nand_ecc_cleanup_ctx().
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_ciint nand_ecc_init_ctx(struct nand_device *nand)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	if (!nand->ecc.engine->ops->init_ctx)
1088c2ecf20Sopenharmony_ci		return 0;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return nand->ecc.engine->ops->init_ctx(nand);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_init_ctx);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/**
1158c2ecf20Sopenharmony_ci * nand_ecc_cleanup_ctx - Cleanup the ECC engine context
1168c2ecf20Sopenharmony_ci * @nand: the NAND device
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_civoid nand_ecc_cleanup_ctx(struct nand_device *nand)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	if (nand->ecc.engine->ops->cleanup_ctx)
1218c2ecf20Sopenharmony_ci		nand->ecc.engine->ops->cleanup_ctx(nand);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_cleanup_ctx);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/**
1268c2ecf20Sopenharmony_ci * nand_ecc_prepare_io_req - Prepare an I/O request
1278c2ecf20Sopenharmony_ci * @nand: the NAND device
1288c2ecf20Sopenharmony_ci * @req: the I/O request
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_ciint nand_ecc_prepare_io_req(struct nand_device *nand,
1318c2ecf20Sopenharmony_ci			    struct nand_page_io_req *req)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	if (!nand->ecc.engine->ops->prepare_io_req)
1348c2ecf20Sopenharmony_ci		return 0;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	return nand->ecc.engine->ops->prepare_io_req(nand, req);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_prepare_io_req);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/**
1418c2ecf20Sopenharmony_ci * nand_ecc_finish_io_req - Finish an I/O request
1428c2ecf20Sopenharmony_ci * @nand: the NAND device
1438c2ecf20Sopenharmony_ci * @req: the I/O request
1448c2ecf20Sopenharmony_ci */
1458c2ecf20Sopenharmony_ciint nand_ecc_finish_io_req(struct nand_device *nand,
1468c2ecf20Sopenharmony_ci			   struct nand_page_io_req *req)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	if (!nand->ecc.engine->ops->finish_io_req)
1498c2ecf20Sopenharmony_ci		return 0;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return nand->ecc.engine->ops->finish_io_req(nand, req);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_finish_io_req);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/* Define default OOB placement schemes for large and small page devices */
1568c2ecf20Sopenharmony_cistatic int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
1578c2ecf20Sopenharmony_ci				 struct mtd_oob_region *oobregion)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct nand_device *nand = mtd_to_nanddev(mtd);
1608c2ecf20Sopenharmony_ci	unsigned int total_ecc_bytes = nand->ecc.ctx.total;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (section > 1)
1638c2ecf20Sopenharmony_ci		return -ERANGE;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (!section) {
1668c2ecf20Sopenharmony_ci		oobregion->offset = 0;
1678c2ecf20Sopenharmony_ci		if (mtd->oobsize == 16)
1688c2ecf20Sopenharmony_ci			oobregion->length = 4;
1698c2ecf20Sopenharmony_ci		else
1708c2ecf20Sopenharmony_ci			oobregion->length = 3;
1718c2ecf20Sopenharmony_ci	} else {
1728c2ecf20Sopenharmony_ci		if (mtd->oobsize == 8)
1738c2ecf20Sopenharmony_ci			return -ERANGE;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		oobregion->offset = 6;
1768c2ecf20Sopenharmony_ci		oobregion->length = total_ecc_bytes - 4;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return 0;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
1838c2ecf20Sopenharmony_ci				  struct mtd_oob_region *oobregion)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	if (section > 1)
1868c2ecf20Sopenharmony_ci		return -ERANGE;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (mtd->oobsize == 16) {
1898c2ecf20Sopenharmony_ci		if (section)
1908c2ecf20Sopenharmony_ci			return -ERANGE;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		oobregion->length = 8;
1938c2ecf20Sopenharmony_ci		oobregion->offset = 8;
1948c2ecf20Sopenharmony_ci	} else {
1958c2ecf20Sopenharmony_ci		oobregion->length = 2;
1968c2ecf20Sopenharmony_ci		if (!section)
1978c2ecf20Sopenharmony_ci			oobregion->offset = 3;
1988c2ecf20Sopenharmony_ci		else
1998c2ecf20Sopenharmony_ci			oobregion->offset = 6;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
2068c2ecf20Sopenharmony_ci	.ecc = nand_ooblayout_ecc_sp,
2078c2ecf20Sopenharmony_ci	.free = nand_ooblayout_free_sp,
2088c2ecf20Sopenharmony_ci};
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ciconst struct mtd_ooblayout_ops *nand_get_small_page_ooblayout(void)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	return &nand_ooblayout_sp_ops;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nand_get_small_page_ooblayout);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
2178c2ecf20Sopenharmony_ci				 struct mtd_oob_region *oobregion)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct nand_device *nand = mtd_to_nanddev(mtd);
2208c2ecf20Sopenharmony_ci	unsigned int total_ecc_bytes = nand->ecc.ctx.total;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (section || !total_ecc_bytes)
2238c2ecf20Sopenharmony_ci		return -ERANGE;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	oobregion->length = total_ecc_bytes;
2268c2ecf20Sopenharmony_ci	oobregion->offset = mtd->oobsize - oobregion->length;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	return 0;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
2328c2ecf20Sopenharmony_ci				  struct mtd_oob_region *oobregion)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct nand_device *nand = mtd_to_nanddev(mtd);
2358c2ecf20Sopenharmony_ci	unsigned int total_ecc_bytes = nand->ecc.ctx.total;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (section)
2388c2ecf20Sopenharmony_ci		return -ERANGE;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	oobregion->length = mtd->oobsize - total_ecc_bytes - 2;
2418c2ecf20Sopenharmony_ci	oobregion->offset = 2;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return 0;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
2478c2ecf20Sopenharmony_ci	.ecc = nand_ooblayout_ecc_lp,
2488c2ecf20Sopenharmony_ci	.free = nand_ooblayout_free_lp,
2498c2ecf20Sopenharmony_ci};
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ciconst struct mtd_ooblayout_ops *nand_get_large_page_ooblayout(void)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	return &nand_ooblayout_lp_ops;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nand_get_large_page_ooblayout);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/*
2588c2ecf20Sopenharmony_ci * Support the old "large page" layout used for 1-bit Hamming ECC where ECC
2598c2ecf20Sopenharmony_ci * are placed at a fixed offset.
2608c2ecf20Sopenharmony_ci */
2618c2ecf20Sopenharmony_cistatic int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section,
2628c2ecf20Sopenharmony_ci					 struct mtd_oob_region *oobregion)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct nand_device *nand = mtd_to_nanddev(mtd);
2658c2ecf20Sopenharmony_ci	unsigned int total_ecc_bytes = nand->ecc.ctx.total;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (section)
2688c2ecf20Sopenharmony_ci		return -ERANGE;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	switch (mtd->oobsize) {
2718c2ecf20Sopenharmony_ci	case 64:
2728c2ecf20Sopenharmony_ci		oobregion->offset = 40;
2738c2ecf20Sopenharmony_ci		break;
2748c2ecf20Sopenharmony_ci	case 128:
2758c2ecf20Sopenharmony_ci		oobregion->offset = 80;
2768c2ecf20Sopenharmony_ci		break;
2778c2ecf20Sopenharmony_ci	default:
2788c2ecf20Sopenharmony_ci		return -EINVAL;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	oobregion->length = total_ecc_bytes;
2828c2ecf20Sopenharmony_ci	if (oobregion->offset + oobregion->length > mtd->oobsize)
2838c2ecf20Sopenharmony_ci		return -ERANGE;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section,
2898c2ecf20Sopenharmony_ci					  struct mtd_oob_region *oobregion)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct nand_device *nand = mtd_to_nanddev(mtd);
2928c2ecf20Sopenharmony_ci	unsigned int total_ecc_bytes = nand->ecc.ctx.total;
2938c2ecf20Sopenharmony_ci	int ecc_offset = 0;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (section < 0 || section > 1)
2968c2ecf20Sopenharmony_ci		return -ERANGE;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	switch (mtd->oobsize) {
2998c2ecf20Sopenharmony_ci	case 64:
3008c2ecf20Sopenharmony_ci		ecc_offset = 40;
3018c2ecf20Sopenharmony_ci		break;
3028c2ecf20Sopenharmony_ci	case 128:
3038c2ecf20Sopenharmony_ci		ecc_offset = 80;
3048c2ecf20Sopenharmony_ci		break;
3058c2ecf20Sopenharmony_ci	default:
3068c2ecf20Sopenharmony_ci		return -EINVAL;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (section == 0) {
3108c2ecf20Sopenharmony_ci		oobregion->offset = 2;
3118c2ecf20Sopenharmony_ci		oobregion->length = ecc_offset - 2;
3128c2ecf20Sopenharmony_ci	} else {
3138c2ecf20Sopenharmony_ci		oobregion->offset = ecc_offset + total_ecc_bytes;
3148c2ecf20Sopenharmony_ci		oobregion->length = mtd->oobsize - oobregion->offset;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return 0;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = {
3218c2ecf20Sopenharmony_ci	.ecc = nand_ooblayout_ecc_lp_hamming,
3228c2ecf20Sopenharmony_ci	.free = nand_ooblayout_free_lp_hamming,
3238c2ecf20Sopenharmony_ci};
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ciconst struct mtd_ooblayout_ops *nand_get_large_page_hamming_ooblayout(void)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	return &nand_ooblayout_lp_hamming_ops;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nand_get_large_page_hamming_ooblayout);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic enum nand_ecc_engine_type
3328c2ecf20Sopenharmony_ciof_get_nand_ecc_engine_type(struct device_node *np)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	struct device_node *eng_np;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (of_property_read_bool(np, "nand-no-ecc-engine"))
3378c2ecf20Sopenharmony_ci		return NAND_ECC_ENGINE_TYPE_NONE;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (of_property_read_bool(np, "nand-use-soft-ecc-engine"))
3408c2ecf20Sopenharmony_ci		return NAND_ECC_ENGINE_TYPE_SOFT;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	eng_np = of_parse_phandle(np, "nand-ecc-engine", 0);
3438c2ecf20Sopenharmony_ci	of_node_put(eng_np);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (eng_np) {
3468c2ecf20Sopenharmony_ci		if (eng_np == np)
3478c2ecf20Sopenharmony_ci			return NAND_ECC_ENGINE_TYPE_ON_DIE;
3488c2ecf20Sopenharmony_ci		else
3498c2ecf20Sopenharmony_ci			return NAND_ECC_ENGINE_TYPE_ON_HOST;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	return NAND_ECC_ENGINE_TYPE_INVALID;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic const char * const nand_ecc_placement[] = {
3568c2ecf20Sopenharmony_ci	[NAND_ECC_PLACEMENT_OOB] = "oob",
3578c2ecf20Sopenharmony_ci	[NAND_ECC_PLACEMENT_INTERLEAVED] = "interleaved",
3588c2ecf20Sopenharmony_ci};
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic enum nand_ecc_placement of_get_nand_ecc_placement(struct device_node *np)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	enum nand_ecc_placement placement;
3638c2ecf20Sopenharmony_ci	const char *pm;
3648c2ecf20Sopenharmony_ci	int err;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	err = of_property_read_string(np, "nand-ecc-placement", &pm);
3678c2ecf20Sopenharmony_ci	if (!err) {
3688c2ecf20Sopenharmony_ci		for (placement = NAND_ECC_PLACEMENT_OOB;
3698c2ecf20Sopenharmony_ci		     placement < ARRAY_SIZE(nand_ecc_placement); placement++) {
3708c2ecf20Sopenharmony_ci			if (!strcasecmp(pm, nand_ecc_placement[placement]))
3718c2ecf20Sopenharmony_ci				return placement;
3728c2ecf20Sopenharmony_ci		}
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	return NAND_ECC_PLACEMENT_UNKNOWN;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic const char * const nand_ecc_algos[] = {
3798c2ecf20Sopenharmony_ci	[NAND_ECC_ALGO_HAMMING] = "hamming",
3808c2ecf20Sopenharmony_ci	[NAND_ECC_ALGO_BCH] = "bch",
3818c2ecf20Sopenharmony_ci	[NAND_ECC_ALGO_RS] = "rs",
3828c2ecf20Sopenharmony_ci};
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	enum nand_ecc_algo ecc_algo;
3878c2ecf20Sopenharmony_ci	const char *pm;
3888c2ecf20Sopenharmony_ci	int err;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	err = of_property_read_string(np, "nand-ecc-algo", &pm);
3918c2ecf20Sopenharmony_ci	if (!err) {
3928c2ecf20Sopenharmony_ci		for (ecc_algo = NAND_ECC_ALGO_HAMMING;
3938c2ecf20Sopenharmony_ci		     ecc_algo < ARRAY_SIZE(nand_ecc_algos);
3948c2ecf20Sopenharmony_ci		     ecc_algo++) {
3958c2ecf20Sopenharmony_ci			if (!strcasecmp(pm, nand_ecc_algos[ecc_algo]))
3968c2ecf20Sopenharmony_ci				return ecc_algo;
3978c2ecf20Sopenharmony_ci		}
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return NAND_ECC_ALGO_UNKNOWN;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic int of_get_nand_ecc_step_size(struct device_node *np)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	int ret;
4068c2ecf20Sopenharmony_ci	u32 val;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
4098c2ecf20Sopenharmony_ci	return ret ? ret : val;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic int of_get_nand_ecc_strength(struct device_node *np)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	int ret;
4158c2ecf20Sopenharmony_ci	u32 val;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "nand-ecc-strength", &val);
4188c2ecf20Sopenharmony_ci	return ret ? ret : val;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_civoid of_get_nand_ecc_user_config(struct nand_device *nand)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct device_node *dn = nanddev_get_of_node(nand);
4248c2ecf20Sopenharmony_ci	int strength, size;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	nand->ecc.user_conf.engine_type = of_get_nand_ecc_engine_type(dn);
4278c2ecf20Sopenharmony_ci	nand->ecc.user_conf.algo = of_get_nand_ecc_algo(dn);
4288c2ecf20Sopenharmony_ci	nand->ecc.user_conf.placement = of_get_nand_ecc_placement(dn);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	strength = of_get_nand_ecc_strength(dn);
4318c2ecf20Sopenharmony_ci	if (strength >= 0)
4328c2ecf20Sopenharmony_ci		nand->ecc.user_conf.strength = strength;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	size = of_get_nand_ecc_step_size(dn);
4358c2ecf20Sopenharmony_ci	if (size >= 0)
4368c2ecf20Sopenharmony_ci		nand->ecc.user_conf.step_size = size;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (of_property_read_bool(dn, "nand-ecc-maximize"))
4398c2ecf20Sopenharmony_ci		nand->ecc.user_conf.flags |= NAND_ECC_MAXIMIZE_STRENGTH;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_get_nand_ecc_user_config);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci/**
4448c2ecf20Sopenharmony_ci * nand_ecc_is_strong_enough - Check if the chip configuration meets the
4458c2ecf20Sopenharmony_ci *                             datasheet requirements.
4468c2ecf20Sopenharmony_ci *
4478c2ecf20Sopenharmony_ci * @nand: Device to check
4488c2ecf20Sopenharmony_ci *
4498c2ecf20Sopenharmony_ci * If our configuration corrects A bits per B bytes and the minimum
4508c2ecf20Sopenharmony_ci * required correction level is X bits per Y bytes, then we must ensure
4518c2ecf20Sopenharmony_ci * both of the following are true:
4528c2ecf20Sopenharmony_ci *
4538c2ecf20Sopenharmony_ci * (1) A / B >= X / Y
4548c2ecf20Sopenharmony_ci * (2) A >= X
4558c2ecf20Sopenharmony_ci *
4568c2ecf20Sopenharmony_ci * Requirement (1) ensures we can correct for the required bitflip density.
4578c2ecf20Sopenharmony_ci * Requirement (2) ensures we can correct even when all bitflips are clumped
4588c2ecf20Sopenharmony_ci * in the same sector.
4598c2ecf20Sopenharmony_ci */
4608c2ecf20Sopenharmony_cibool nand_ecc_is_strong_enough(struct nand_device *nand)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	const struct nand_ecc_props *reqs = nanddev_get_ecc_requirements(nand);
4638c2ecf20Sopenharmony_ci	const struct nand_ecc_props *conf = nanddev_get_ecc_conf(nand);
4648c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nanddev_to_mtd(nand);
4658c2ecf20Sopenharmony_ci	int corr, ds_corr;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (conf->step_size == 0 || reqs->step_size == 0)
4688c2ecf20Sopenharmony_ci		/* Not enough information */
4698c2ecf20Sopenharmony_ci		return true;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/*
4728c2ecf20Sopenharmony_ci	 * We get the number of corrected bits per page to compare
4738c2ecf20Sopenharmony_ci	 * the correction density.
4748c2ecf20Sopenharmony_ci	 */
4758c2ecf20Sopenharmony_ci	corr = (mtd->writesize * conf->strength) / conf->step_size;
4768c2ecf20Sopenharmony_ci	ds_corr = (mtd->writesize * reqs->strength) / reqs->step_size;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	return corr >= ds_corr && conf->strength >= reqs->strength;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_is_strong_enough);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4838c2ecf20Sopenharmony_ciMODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
4848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Generic ECC engine");
485