162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Generic Error-Correcting Code (ECC) engine
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2019 Macronix
662306a36Sopenharmony_ci * Author:
762306a36Sopenharmony_ci *     Miquèl RAYNAL <miquel.raynal@bootlin.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * This file describes the abstraction of any NAND ECC engine. It has been
1162306a36Sopenharmony_ci * designed to fit most cases, including parallel NANDs and SPI-NANDs.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * There are three main situations where instantiating this ECC engine makes
1462306a36Sopenharmony_ci * sense:
1562306a36Sopenharmony_ci *   - external: The ECC engine is outside the NAND pipeline, typically this
1662306a36Sopenharmony_ci *               is a software ECC engine, or an hardware engine that is
1762306a36Sopenharmony_ci *               outside the NAND controller pipeline.
1862306a36Sopenharmony_ci *   - pipelined: The ECC engine is inside the NAND pipeline, ie. on the
1962306a36Sopenharmony_ci *                controller's side. This is the case of most of the raw NAND
2062306a36Sopenharmony_ci *                controllers. In the pipeline case, the ECC bytes are
2162306a36Sopenharmony_ci *                generated/data corrected on the fly when a page is
2262306a36Sopenharmony_ci *                written/read.
2362306a36Sopenharmony_ci *   - ondie: The ECC engine is inside the NAND pipeline, on the chip's side.
2462306a36Sopenharmony_ci *            Some NAND chips can correct themselves the data.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * Besides the initial setup and final cleanups, the interfaces are rather
2762306a36Sopenharmony_ci * simple:
2862306a36Sopenharmony_ci *   - prepare: Prepare an I/O request. Enable/disable the ECC engine based on
2962306a36Sopenharmony_ci *              the I/O request type. In case of software correction or external
3062306a36Sopenharmony_ci *              engine, this step may involve to derive the ECC bytes and place
3162306a36Sopenharmony_ci *              them in the OOB area before a write.
3262306a36Sopenharmony_ci *   - finish: Finish an I/O request. Correct the data in case of a read
3362306a36Sopenharmony_ci *             request and report the number of corrected bits/uncorrectable
3462306a36Sopenharmony_ci *             errors. Most likely empty for write operations, unless you have
3562306a36Sopenharmony_ci *             hardware specific stuff to do, like shutting down the engine to
3662306a36Sopenharmony_ci *             save power.
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci * The I/O request should be enclosed in a prepare()/finish() pair of calls
3962306a36Sopenharmony_ci * and will behave differently depending on the requested I/O type:
4062306a36Sopenharmony_ci *   - raw: Correction disabled
4162306a36Sopenharmony_ci *   - ecc: Correction enabled
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * The request direction is impacting the logic as well:
4462306a36Sopenharmony_ci *   - read: Load data from the NAND chip
4562306a36Sopenharmony_ci *   - write: Store data in the NAND chip
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * Mixing all this combinations together gives the following behavior.
4862306a36Sopenharmony_ci * Those are just examples, drivers are free to add custom steps in their
4962306a36Sopenharmony_ci * prepare/finish hook.
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci * [external ECC engine]
5262306a36Sopenharmony_ci *   - external + prepare + raw + read: do nothing
5362306a36Sopenharmony_ci *   - external + finish  + raw + read: do nothing
5462306a36Sopenharmony_ci *   - external + prepare + raw + write: do nothing
5562306a36Sopenharmony_ci *   - external + finish  + raw + write: do nothing
5662306a36Sopenharmony_ci *   - external + prepare + ecc + read: do nothing
5762306a36Sopenharmony_ci *   - external + finish  + ecc + read: calculate expected ECC bytes, extract
5862306a36Sopenharmony_ci *                                      ECC bytes from OOB buffer, correct
5962306a36Sopenharmony_ci *                                      and report any bitflip/error
6062306a36Sopenharmony_ci *   - external + prepare + ecc + write: calculate ECC bytes and store them at
6162306a36Sopenharmony_ci *                                       the right place in the OOB buffer based
6262306a36Sopenharmony_ci *                                       on the OOB layout
6362306a36Sopenharmony_ci *   - external + finish  + ecc + write: do nothing
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * [pipelined ECC engine]
6662306a36Sopenharmony_ci *   - pipelined + prepare + raw + read: disable the controller's ECC engine if
6762306a36Sopenharmony_ci *                                       activated
6862306a36Sopenharmony_ci *   - pipelined + finish  + raw + read: do nothing
6962306a36Sopenharmony_ci *   - pipelined + prepare + raw + write: disable the controller's ECC engine if
7062306a36Sopenharmony_ci *                                        activated
7162306a36Sopenharmony_ci *   - pipelined + finish  + raw + write: do nothing
7262306a36Sopenharmony_ci *   - pipelined + prepare + ecc + read: enable the controller's ECC engine if
7362306a36Sopenharmony_ci *                                       deactivated
7462306a36Sopenharmony_ci *   - pipelined + finish  + ecc + read: check the status, report any
7562306a36Sopenharmony_ci *                                       error/bitflip
7662306a36Sopenharmony_ci *   - pipelined + prepare + ecc + write: enable the controller's ECC engine if
7762306a36Sopenharmony_ci *                                        deactivated
7862306a36Sopenharmony_ci *   - pipelined + finish  + ecc + write: do nothing
7962306a36Sopenharmony_ci *
8062306a36Sopenharmony_ci * [ondie ECC engine]
8162306a36Sopenharmony_ci *   - ondie + prepare + raw + read: send commands to disable the on-chip ECC
8262306a36Sopenharmony_ci *                                   engine if activated
8362306a36Sopenharmony_ci *   - ondie + finish  + raw + read: do nothing
8462306a36Sopenharmony_ci *   - ondie + prepare + raw + write: send commands to disable the on-chip ECC
8562306a36Sopenharmony_ci *                                    engine if activated
8662306a36Sopenharmony_ci *   - ondie + finish  + raw + write: do nothing
8762306a36Sopenharmony_ci *   - ondie + prepare + ecc + read: send commands to enable the on-chip ECC
8862306a36Sopenharmony_ci *                                   engine if deactivated
8962306a36Sopenharmony_ci *   - ondie + finish  + ecc + read: send commands to check the status, report
9062306a36Sopenharmony_ci *                                   any error/bitflip
9162306a36Sopenharmony_ci *   - ondie + prepare + ecc + write: send commands to enable the on-chip ECC
9262306a36Sopenharmony_ci *                                    engine if deactivated
9362306a36Sopenharmony_ci *   - ondie + finish  + ecc + write: do nothing
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#include <linux/module.h>
9762306a36Sopenharmony_ci#include <linux/mtd/nand.h>
9862306a36Sopenharmony_ci#include <linux/platform_device.h>
9962306a36Sopenharmony_ci#include <linux/slab.h>
10062306a36Sopenharmony_ci#include <linux/of.h>
10162306a36Sopenharmony_ci#include <linux/of_platform.h>
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic LIST_HEAD(on_host_hw_engines);
10462306a36Sopenharmony_cistatic DEFINE_MUTEX(on_host_hw_engines_mutex);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/**
10762306a36Sopenharmony_ci * nand_ecc_init_ctx - Init the ECC engine context
10862306a36Sopenharmony_ci * @nand: the NAND device
10962306a36Sopenharmony_ci *
11062306a36Sopenharmony_ci * On success, the caller is responsible of calling @nand_ecc_cleanup_ctx().
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_ciint nand_ecc_init_ctx(struct nand_device *nand)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	if (!nand->ecc.engine || !nand->ecc.engine->ops->init_ctx)
11562306a36Sopenharmony_ci		return 0;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return nand->ecc.engine->ops->init_ctx(nand);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_init_ctx);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/**
12262306a36Sopenharmony_ci * nand_ecc_cleanup_ctx - Cleanup the ECC engine context
12362306a36Sopenharmony_ci * @nand: the NAND device
12462306a36Sopenharmony_ci */
12562306a36Sopenharmony_civoid nand_ecc_cleanup_ctx(struct nand_device *nand)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	if (nand->ecc.engine && nand->ecc.engine->ops->cleanup_ctx)
12862306a36Sopenharmony_ci		nand->ecc.engine->ops->cleanup_ctx(nand);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_cleanup_ctx);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/**
13362306a36Sopenharmony_ci * nand_ecc_prepare_io_req - Prepare an I/O request
13462306a36Sopenharmony_ci * @nand: the NAND device
13562306a36Sopenharmony_ci * @req: the I/O request
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_ciint nand_ecc_prepare_io_req(struct nand_device *nand,
13862306a36Sopenharmony_ci			    struct nand_page_io_req *req)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	if (!nand->ecc.engine || !nand->ecc.engine->ops->prepare_io_req)
14162306a36Sopenharmony_ci		return 0;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return nand->ecc.engine->ops->prepare_io_req(nand, req);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_prepare_io_req);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/**
14862306a36Sopenharmony_ci * nand_ecc_finish_io_req - Finish an I/O request
14962306a36Sopenharmony_ci * @nand: the NAND device
15062306a36Sopenharmony_ci * @req: the I/O request
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_ciint nand_ecc_finish_io_req(struct nand_device *nand,
15362306a36Sopenharmony_ci			   struct nand_page_io_req *req)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	if (!nand->ecc.engine || !nand->ecc.engine->ops->finish_io_req)
15662306a36Sopenharmony_ci		return 0;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return nand->ecc.engine->ops->finish_io_req(nand, req);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_finish_io_req);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/* Define default OOB placement schemes for large and small page devices */
16362306a36Sopenharmony_cistatic int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
16462306a36Sopenharmony_ci				 struct mtd_oob_region *oobregion)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct nand_device *nand = mtd_to_nanddev(mtd);
16762306a36Sopenharmony_ci	unsigned int total_ecc_bytes = nand->ecc.ctx.total;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (section > 1)
17062306a36Sopenharmony_ci		return -ERANGE;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (!section) {
17362306a36Sopenharmony_ci		oobregion->offset = 0;
17462306a36Sopenharmony_ci		if (mtd->oobsize == 16)
17562306a36Sopenharmony_ci			oobregion->length = 4;
17662306a36Sopenharmony_ci		else
17762306a36Sopenharmony_ci			oobregion->length = 3;
17862306a36Sopenharmony_ci	} else {
17962306a36Sopenharmony_ci		if (mtd->oobsize == 8)
18062306a36Sopenharmony_ci			return -ERANGE;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		oobregion->offset = 6;
18362306a36Sopenharmony_ci		oobregion->length = total_ecc_bytes - 4;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return 0;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
19062306a36Sopenharmony_ci				  struct mtd_oob_region *oobregion)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	if (section > 1)
19362306a36Sopenharmony_ci		return -ERANGE;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (mtd->oobsize == 16) {
19662306a36Sopenharmony_ci		if (section)
19762306a36Sopenharmony_ci			return -ERANGE;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		oobregion->length = 8;
20062306a36Sopenharmony_ci		oobregion->offset = 8;
20162306a36Sopenharmony_ci	} else {
20262306a36Sopenharmony_ci		oobregion->length = 2;
20362306a36Sopenharmony_ci		if (!section)
20462306a36Sopenharmony_ci			oobregion->offset = 3;
20562306a36Sopenharmony_ci		else
20662306a36Sopenharmony_ci			oobregion->offset = 6;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
21362306a36Sopenharmony_ci	.ecc = nand_ooblayout_ecc_sp,
21462306a36Sopenharmony_ci	.free = nand_ooblayout_free_sp,
21562306a36Sopenharmony_ci};
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ciconst struct mtd_ooblayout_ops *nand_get_small_page_ooblayout(void)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	return &nand_ooblayout_sp_ops;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nand_get_small_page_ooblayout);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
22462306a36Sopenharmony_ci				 struct mtd_oob_region *oobregion)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct nand_device *nand = mtd_to_nanddev(mtd);
22762306a36Sopenharmony_ci	unsigned int total_ecc_bytes = nand->ecc.ctx.total;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (section || !total_ecc_bytes)
23062306a36Sopenharmony_ci		return -ERANGE;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	oobregion->length = total_ecc_bytes;
23362306a36Sopenharmony_ci	oobregion->offset = mtd->oobsize - oobregion->length;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return 0;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
23962306a36Sopenharmony_ci				  struct mtd_oob_region *oobregion)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct nand_device *nand = mtd_to_nanddev(mtd);
24262306a36Sopenharmony_ci	unsigned int total_ecc_bytes = nand->ecc.ctx.total;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (section)
24562306a36Sopenharmony_ci		return -ERANGE;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	oobregion->length = mtd->oobsize - total_ecc_bytes - 2;
24862306a36Sopenharmony_ci	oobregion->offset = 2;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	return 0;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
25462306a36Sopenharmony_ci	.ecc = nand_ooblayout_ecc_lp,
25562306a36Sopenharmony_ci	.free = nand_ooblayout_free_lp,
25662306a36Sopenharmony_ci};
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ciconst struct mtd_ooblayout_ops *nand_get_large_page_ooblayout(void)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	return &nand_ooblayout_lp_ops;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nand_get_large_page_ooblayout);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci/*
26562306a36Sopenharmony_ci * Support the old "large page" layout used for 1-bit Hamming ECC where ECC
26662306a36Sopenharmony_ci * are placed at a fixed offset.
26762306a36Sopenharmony_ci */
26862306a36Sopenharmony_cistatic int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section,
26962306a36Sopenharmony_ci					 struct mtd_oob_region *oobregion)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct nand_device *nand = mtd_to_nanddev(mtd);
27262306a36Sopenharmony_ci	unsigned int total_ecc_bytes = nand->ecc.ctx.total;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (section)
27562306a36Sopenharmony_ci		return -ERANGE;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	switch (mtd->oobsize) {
27862306a36Sopenharmony_ci	case 64:
27962306a36Sopenharmony_ci		oobregion->offset = 40;
28062306a36Sopenharmony_ci		break;
28162306a36Sopenharmony_ci	case 128:
28262306a36Sopenharmony_ci		oobregion->offset = 80;
28362306a36Sopenharmony_ci		break;
28462306a36Sopenharmony_ci	default:
28562306a36Sopenharmony_ci		return -EINVAL;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	oobregion->length = total_ecc_bytes;
28962306a36Sopenharmony_ci	if (oobregion->offset + oobregion->length > mtd->oobsize)
29062306a36Sopenharmony_ci		return -ERANGE;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return 0;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section,
29662306a36Sopenharmony_ci					  struct mtd_oob_region *oobregion)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct nand_device *nand = mtd_to_nanddev(mtd);
29962306a36Sopenharmony_ci	unsigned int total_ecc_bytes = nand->ecc.ctx.total;
30062306a36Sopenharmony_ci	int ecc_offset = 0;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (section < 0 || section > 1)
30362306a36Sopenharmony_ci		return -ERANGE;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	switch (mtd->oobsize) {
30662306a36Sopenharmony_ci	case 64:
30762306a36Sopenharmony_ci		ecc_offset = 40;
30862306a36Sopenharmony_ci		break;
30962306a36Sopenharmony_ci	case 128:
31062306a36Sopenharmony_ci		ecc_offset = 80;
31162306a36Sopenharmony_ci		break;
31262306a36Sopenharmony_ci	default:
31362306a36Sopenharmony_ci		return -EINVAL;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (section == 0) {
31762306a36Sopenharmony_ci		oobregion->offset = 2;
31862306a36Sopenharmony_ci		oobregion->length = ecc_offset - 2;
31962306a36Sopenharmony_ci	} else {
32062306a36Sopenharmony_ci		oobregion->offset = ecc_offset + total_ecc_bytes;
32162306a36Sopenharmony_ci		oobregion->length = mtd->oobsize - oobregion->offset;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return 0;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = {
32862306a36Sopenharmony_ci	.ecc = nand_ooblayout_ecc_lp_hamming,
32962306a36Sopenharmony_ci	.free = nand_ooblayout_free_lp_hamming,
33062306a36Sopenharmony_ci};
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ciconst struct mtd_ooblayout_ops *nand_get_large_page_hamming_ooblayout(void)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	return &nand_ooblayout_lp_hamming_ops;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nand_get_large_page_hamming_ooblayout);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic enum nand_ecc_engine_type
33962306a36Sopenharmony_ciof_get_nand_ecc_engine_type(struct device_node *np)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct device_node *eng_np;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (of_property_read_bool(np, "nand-no-ecc-engine"))
34462306a36Sopenharmony_ci		return NAND_ECC_ENGINE_TYPE_NONE;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (of_property_read_bool(np, "nand-use-soft-ecc-engine"))
34762306a36Sopenharmony_ci		return NAND_ECC_ENGINE_TYPE_SOFT;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	eng_np = of_parse_phandle(np, "nand-ecc-engine", 0);
35062306a36Sopenharmony_ci	of_node_put(eng_np);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (eng_np) {
35362306a36Sopenharmony_ci		if (eng_np == np)
35462306a36Sopenharmony_ci			return NAND_ECC_ENGINE_TYPE_ON_DIE;
35562306a36Sopenharmony_ci		else
35662306a36Sopenharmony_ci			return NAND_ECC_ENGINE_TYPE_ON_HOST;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return NAND_ECC_ENGINE_TYPE_INVALID;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic const char * const nand_ecc_placement[] = {
36362306a36Sopenharmony_ci	[NAND_ECC_PLACEMENT_OOB] = "oob",
36462306a36Sopenharmony_ci	[NAND_ECC_PLACEMENT_INTERLEAVED] = "interleaved",
36562306a36Sopenharmony_ci};
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic enum nand_ecc_placement of_get_nand_ecc_placement(struct device_node *np)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	enum nand_ecc_placement placement;
37062306a36Sopenharmony_ci	const char *pm;
37162306a36Sopenharmony_ci	int err;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	err = of_property_read_string(np, "nand-ecc-placement", &pm);
37462306a36Sopenharmony_ci	if (!err) {
37562306a36Sopenharmony_ci		for (placement = NAND_ECC_PLACEMENT_OOB;
37662306a36Sopenharmony_ci		     placement < ARRAY_SIZE(nand_ecc_placement); placement++) {
37762306a36Sopenharmony_ci			if (!strcasecmp(pm, nand_ecc_placement[placement]))
37862306a36Sopenharmony_ci				return placement;
37962306a36Sopenharmony_ci		}
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return NAND_ECC_PLACEMENT_UNKNOWN;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic const char * const nand_ecc_algos[] = {
38662306a36Sopenharmony_ci	[NAND_ECC_ALGO_HAMMING] = "hamming",
38762306a36Sopenharmony_ci	[NAND_ECC_ALGO_BCH] = "bch",
38862306a36Sopenharmony_ci	[NAND_ECC_ALGO_RS] = "rs",
38962306a36Sopenharmony_ci};
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	enum nand_ecc_algo ecc_algo;
39462306a36Sopenharmony_ci	const char *pm;
39562306a36Sopenharmony_ci	int err;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	err = of_property_read_string(np, "nand-ecc-algo", &pm);
39862306a36Sopenharmony_ci	if (!err) {
39962306a36Sopenharmony_ci		for (ecc_algo = NAND_ECC_ALGO_HAMMING;
40062306a36Sopenharmony_ci		     ecc_algo < ARRAY_SIZE(nand_ecc_algos);
40162306a36Sopenharmony_ci		     ecc_algo++) {
40262306a36Sopenharmony_ci			if (!strcasecmp(pm, nand_ecc_algos[ecc_algo]))
40362306a36Sopenharmony_ci				return ecc_algo;
40462306a36Sopenharmony_ci		}
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return NAND_ECC_ALGO_UNKNOWN;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic int of_get_nand_ecc_step_size(struct device_node *np)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	int ret;
41362306a36Sopenharmony_ci	u32 val;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
41662306a36Sopenharmony_ci	return ret ? ret : val;
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic int of_get_nand_ecc_strength(struct device_node *np)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	int ret;
42262306a36Sopenharmony_ci	u32 val;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	ret = of_property_read_u32(np, "nand-ecc-strength", &val);
42562306a36Sopenharmony_ci	return ret ? ret : val;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_civoid of_get_nand_ecc_user_config(struct nand_device *nand)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	struct device_node *dn = nanddev_get_of_node(nand);
43162306a36Sopenharmony_ci	int strength, size;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	nand->ecc.user_conf.engine_type = of_get_nand_ecc_engine_type(dn);
43462306a36Sopenharmony_ci	nand->ecc.user_conf.algo = of_get_nand_ecc_algo(dn);
43562306a36Sopenharmony_ci	nand->ecc.user_conf.placement = of_get_nand_ecc_placement(dn);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	strength = of_get_nand_ecc_strength(dn);
43862306a36Sopenharmony_ci	if (strength >= 0)
43962306a36Sopenharmony_ci		nand->ecc.user_conf.strength = strength;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	size = of_get_nand_ecc_step_size(dn);
44262306a36Sopenharmony_ci	if (size >= 0)
44362306a36Sopenharmony_ci		nand->ecc.user_conf.step_size = size;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (of_property_read_bool(dn, "nand-ecc-maximize"))
44662306a36Sopenharmony_ci		nand->ecc.user_conf.flags |= NAND_ECC_MAXIMIZE_STRENGTH;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ciEXPORT_SYMBOL(of_get_nand_ecc_user_config);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci/**
45162306a36Sopenharmony_ci * nand_ecc_is_strong_enough - Check if the chip configuration meets the
45262306a36Sopenharmony_ci *                             datasheet requirements.
45362306a36Sopenharmony_ci *
45462306a36Sopenharmony_ci * @nand: Device to check
45562306a36Sopenharmony_ci *
45662306a36Sopenharmony_ci * If our configuration corrects A bits per B bytes and the minimum
45762306a36Sopenharmony_ci * required correction level is X bits per Y bytes, then we must ensure
45862306a36Sopenharmony_ci * both of the following are true:
45962306a36Sopenharmony_ci *
46062306a36Sopenharmony_ci * (1) A / B >= X / Y
46162306a36Sopenharmony_ci * (2) A >= X
46262306a36Sopenharmony_ci *
46362306a36Sopenharmony_ci * Requirement (1) ensures we can correct for the required bitflip density.
46462306a36Sopenharmony_ci * Requirement (2) ensures we can correct even when all bitflips are clumped
46562306a36Sopenharmony_ci * in the same sector.
46662306a36Sopenharmony_ci */
46762306a36Sopenharmony_cibool nand_ecc_is_strong_enough(struct nand_device *nand)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	const struct nand_ecc_props *reqs = nanddev_get_ecc_requirements(nand);
47062306a36Sopenharmony_ci	const struct nand_ecc_props *conf = nanddev_get_ecc_conf(nand);
47162306a36Sopenharmony_ci	struct mtd_info *mtd = nanddev_to_mtd(nand);
47262306a36Sopenharmony_ci	int corr, ds_corr;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (conf->step_size == 0 || reqs->step_size == 0)
47562306a36Sopenharmony_ci		/* Not enough information */
47662306a36Sopenharmony_ci		return true;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	/*
47962306a36Sopenharmony_ci	 * We get the number of corrected bits per page to compare
48062306a36Sopenharmony_ci	 * the correction density.
48162306a36Sopenharmony_ci	 */
48262306a36Sopenharmony_ci	corr = (mtd->writesize * conf->strength) / conf->step_size;
48362306a36Sopenharmony_ci	ds_corr = (mtd->writesize * reqs->strength) / reqs->step_size;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return corr >= ds_corr && conf->strength >= reqs->strength;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_is_strong_enough);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci/* ECC engine driver internal helpers */
49062306a36Sopenharmony_ciint nand_ecc_init_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx,
49162306a36Sopenharmony_ci			       struct nand_device *nand)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	unsigned int total_buffer_size;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	ctx->nand = nand;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/* Let the user decide the exact length of each buffer */
49862306a36Sopenharmony_ci	if (!ctx->page_buffer_size)
49962306a36Sopenharmony_ci		ctx->page_buffer_size = nanddev_page_size(nand);
50062306a36Sopenharmony_ci	if (!ctx->oob_buffer_size)
50162306a36Sopenharmony_ci		ctx->oob_buffer_size = nanddev_per_page_oobsize(nand);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	total_buffer_size = ctx->page_buffer_size + ctx->oob_buffer_size;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	ctx->spare_databuf = kzalloc(total_buffer_size, GFP_KERNEL);
50662306a36Sopenharmony_ci	if (!ctx->spare_databuf)
50762306a36Sopenharmony_ci		return -ENOMEM;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	ctx->spare_oobbuf = ctx->spare_databuf + ctx->page_buffer_size;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	return 0;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nand_ecc_init_req_tweaking);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_civoid nand_ecc_cleanup_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	kfree(ctx->spare_databuf);
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nand_ecc_cleanup_req_tweaking);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci/*
52262306a36Sopenharmony_ci * Ensure data and OOB area is fully read/written otherwise the correction might
52362306a36Sopenharmony_ci * not work as expected.
52462306a36Sopenharmony_ci */
52562306a36Sopenharmony_civoid nand_ecc_tweak_req(struct nand_ecc_req_tweak_ctx *ctx,
52662306a36Sopenharmony_ci			struct nand_page_io_req *req)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct nand_device *nand = ctx->nand;
52962306a36Sopenharmony_ci	struct nand_page_io_req *orig, *tweak;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* Save the original request */
53262306a36Sopenharmony_ci	ctx->orig_req = *req;
53362306a36Sopenharmony_ci	ctx->bounce_data = false;
53462306a36Sopenharmony_ci	ctx->bounce_oob = false;
53562306a36Sopenharmony_ci	orig = &ctx->orig_req;
53662306a36Sopenharmony_ci	tweak = req;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	/* Ensure the request covers the entire page */
53962306a36Sopenharmony_ci	if (orig->datalen < nanddev_page_size(nand)) {
54062306a36Sopenharmony_ci		ctx->bounce_data = true;
54162306a36Sopenharmony_ci		tweak->dataoffs = 0;
54262306a36Sopenharmony_ci		tweak->datalen = nanddev_page_size(nand);
54362306a36Sopenharmony_ci		tweak->databuf.in = ctx->spare_databuf;
54462306a36Sopenharmony_ci		memset(tweak->databuf.in, 0xFF, ctx->page_buffer_size);
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (orig->ooblen < nanddev_per_page_oobsize(nand)) {
54862306a36Sopenharmony_ci		ctx->bounce_oob = true;
54962306a36Sopenharmony_ci		tweak->ooboffs = 0;
55062306a36Sopenharmony_ci		tweak->ooblen = nanddev_per_page_oobsize(nand);
55162306a36Sopenharmony_ci		tweak->oobbuf.in = ctx->spare_oobbuf;
55262306a36Sopenharmony_ci		memset(tweak->oobbuf.in, 0xFF, ctx->oob_buffer_size);
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* Copy the data that must be writen in the bounce buffers, if needed */
55662306a36Sopenharmony_ci	if (orig->type == NAND_PAGE_WRITE) {
55762306a36Sopenharmony_ci		if (ctx->bounce_data)
55862306a36Sopenharmony_ci			memcpy((void *)tweak->databuf.out + orig->dataoffs,
55962306a36Sopenharmony_ci			       orig->databuf.out, orig->datalen);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci		if (ctx->bounce_oob)
56262306a36Sopenharmony_ci			memcpy((void *)tweak->oobbuf.out + orig->ooboffs,
56362306a36Sopenharmony_ci			       orig->oobbuf.out, orig->ooblen);
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nand_ecc_tweak_req);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_civoid nand_ecc_restore_req(struct nand_ecc_req_tweak_ctx *ctx,
56962306a36Sopenharmony_ci			  struct nand_page_io_req *req)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	struct nand_page_io_req *orig, *tweak;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	orig = &ctx->orig_req;
57462306a36Sopenharmony_ci	tweak = req;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	/* Restore the data read from the bounce buffers, if needed */
57762306a36Sopenharmony_ci	if (orig->type == NAND_PAGE_READ) {
57862306a36Sopenharmony_ci		if (ctx->bounce_data)
57962306a36Sopenharmony_ci			memcpy(orig->databuf.in,
58062306a36Sopenharmony_ci			       tweak->databuf.in + orig->dataoffs,
58162306a36Sopenharmony_ci			       orig->datalen);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci		if (ctx->bounce_oob)
58462306a36Sopenharmony_ci			memcpy(orig->oobbuf.in,
58562306a36Sopenharmony_ci			       tweak->oobbuf.in + orig->ooboffs,
58662306a36Sopenharmony_ci			       orig->ooblen);
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/* Ensure the original request is restored */
59062306a36Sopenharmony_ci	*req = *orig;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nand_ecc_restore_req);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistruct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	unsigned int algo = nand->ecc.user_conf.algo;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	if (algo == NAND_ECC_ALGO_UNKNOWN)
59962306a36Sopenharmony_ci		algo = nand->ecc.defaults.algo;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	switch (algo) {
60262306a36Sopenharmony_ci	case NAND_ECC_ALGO_HAMMING:
60362306a36Sopenharmony_ci		return nand_ecc_sw_hamming_get_engine();
60462306a36Sopenharmony_ci	case NAND_ECC_ALGO_BCH:
60562306a36Sopenharmony_ci		return nand_ecc_sw_bch_get_engine();
60662306a36Sopenharmony_ci	default:
60762306a36Sopenharmony_ci		break;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return NULL;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_get_sw_engine);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistruct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	return nand->ecc.ondie_engine;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_get_on_die_hw_engine);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ciint nand_ecc_register_on_host_hw_engine(struct nand_ecc_engine *engine)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct nand_ecc_engine *item;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (!engine)
62562306a36Sopenharmony_ci		return -EINVAL;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	/* Prevent multiple registrations of one engine */
62862306a36Sopenharmony_ci	list_for_each_entry(item, &on_host_hw_engines, node)
62962306a36Sopenharmony_ci		if (item == engine)
63062306a36Sopenharmony_ci			return 0;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	mutex_lock(&on_host_hw_engines_mutex);
63362306a36Sopenharmony_ci	list_add_tail(&engine->node, &on_host_hw_engines);
63462306a36Sopenharmony_ci	mutex_unlock(&on_host_hw_engines_mutex);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return 0;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_register_on_host_hw_engine);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ciint nand_ecc_unregister_on_host_hw_engine(struct nand_ecc_engine *engine)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	if (!engine)
64362306a36Sopenharmony_ci		return -EINVAL;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	mutex_lock(&on_host_hw_engines_mutex);
64662306a36Sopenharmony_ci	list_del(&engine->node);
64762306a36Sopenharmony_ci	mutex_unlock(&on_host_hw_engines_mutex);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	return 0;
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_unregister_on_host_hw_engine);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic struct nand_ecc_engine *nand_ecc_match_on_host_hw_engine(struct device *dev)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	struct nand_ecc_engine *item;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	list_for_each_entry(item, &on_host_hw_engines, node)
65862306a36Sopenharmony_ci		if (item->dev == dev)
65962306a36Sopenharmony_ci			return item;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	return NULL;
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistruct nand_ecc_engine *nand_ecc_get_on_host_hw_engine(struct nand_device *nand)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct nand_ecc_engine *engine = NULL;
66762306a36Sopenharmony_ci	struct device *dev = &nand->mtd.dev;
66862306a36Sopenharmony_ci	struct platform_device *pdev;
66962306a36Sopenharmony_ci	struct device_node *np;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	if (list_empty(&on_host_hw_engines))
67262306a36Sopenharmony_ci		return NULL;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	/* Check for an explicit nand-ecc-engine property */
67562306a36Sopenharmony_ci	np = of_parse_phandle(dev->of_node, "nand-ecc-engine", 0);
67662306a36Sopenharmony_ci	if (np) {
67762306a36Sopenharmony_ci		pdev = of_find_device_by_node(np);
67862306a36Sopenharmony_ci		if (!pdev)
67962306a36Sopenharmony_ci			return ERR_PTR(-EPROBE_DEFER);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		engine = nand_ecc_match_on_host_hw_engine(&pdev->dev);
68262306a36Sopenharmony_ci		platform_device_put(pdev);
68362306a36Sopenharmony_ci		of_node_put(np);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		if (!engine)
68662306a36Sopenharmony_ci			return ERR_PTR(-EPROBE_DEFER);
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (engine)
69062306a36Sopenharmony_ci		get_device(engine->dev);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	return engine;
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_get_on_host_hw_engine);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_civoid nand_ecc_put_on_host_hw_engine(struct nand_device *nand)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	put_device(nand->ecc.engine->dev);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ciEXPORT_SYMBOL(nand_ecc_put_on_host_hw_engine);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci/*
70362306a36Sopenharmony_ci * In the case of a pipelined engine, the device registering the ECC
70462306a36Sopenharmony_ci * engine is not necessarily the ECC engine itself but may be a host controller.
70562306a36Sopenharmony_ci * It is then useful to provide a helper to retrieve the right device object
70662306a36Sopenharmony_ci * which actually represents the ECC engine.
70762306a36Sopenharmony_ci */
70862306a36Sopenharmony_cistruct device *nand_ecc_get_engine_dev(struct device *host)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	struct platform_device *ecc_pdev;
71162306a36Sopenharmony_ci	struct device_node *np;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	/*
71462306a36Sopenharmony_ci	 * If the device node contains this property, it means we need to follow
71562306a36Sopenharmony_ci	 * it in order to get the right ECC engine device we are looking for.
71662306a36Sopenharmony_ci	 */
71762306a36Sopenharmony_ci	np = of_parse_phandle(host->of_node, "nand-ecc-engine", 0);
71862306a36Sopenharmony_ci	if (!np)
71962306a36Sopenharmony_ci		return host;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	ecc_pdev = of_find_device_by_node(np);
72262306a36Sopenharmony_ci	if (!ecc_pdev) {
72362306a36Sopenharmony_ci		of_node_put(np);
72462306a36Sopenharmony_ci		return NULL;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	platform_device_put(ecc_pdev);
72862306a36Sopenharmony_ci	of_node_put(np);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	return &ecc_pdev->dev;
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
73462306a36Sopenharmony_ciMODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
73562306a36Sopenharmony_ciMODULE_DESCRIPTION("Generic ECC engine");
736