diff options
Diffstat (limited to 'target/linux/mediatek/patches-5.15/120-01-v5.18-mtd-nand-ecc-Add-infrastructure-to-support-hardware-.patch')
-rw-r--r-- | target/linux/mediatek/patches-5.15/120-01-v5.18-mtd-nand-ecc-Add-infrastructure-to-support-hardware-.patch | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/target/linux/mediatek/patches-5.15/120-01-v5.18-mtd-nand-ecc-Add-infrastructure-to-support-hardware-.patch b/target/linux/mediatek/patches-5.15/120-01-v5.18-mtd-nand-ecc-Add-infrastructure-to-support-hardware-.patch new file mode 100644 index 0000000000..b6b069d234 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/120-01-v5.18-mtd-nand-ecc-Add-infrastructure-to-support-hardware-.patch @@ -0,0 +1,224 @@ +From ad4944aa0b02cb043afe20bc2a018c161e65c992 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal <miquel.raynal@bootlin.com> +Date: Thu, 16 Dec 2021 12:16:38 +0100 +Subject: [PATCH 01/15] mtd: nand: ecc: Add infrastructure to support hardware + engines + +Add the necessary helpers to register/unregister hardware ECC engines +that will be called from ECC engine drivers. + +Also add helpers to get the right engine from the user +perspective. Keep a reference of the in use ECC engine in order to +prevent modules to be unloaded. Put the reference when the engine gets +retired. + +A static list of hardware (only) ECC engines is setup to keep track of +the registered engines. + +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Link: https://lore.kernel.org/linux-mtd/20211216111654.238086-13-miquel.raynal@bootlin.com +(cherry picked from commit 96489c1c0b53131b0e1ec33e2060538379ad6152) +--- + drivers/mtd/nand/core.c | 10 +++-- + drivers/mtd/nand/ecc.c | 88 ++++++++++++++++++++++++++++++++++++++++ + include/linux/mtd/nand.h | 28 +++++++++++++ + 3 files changed, 123 insertions(+), 3 deletions(-) + +diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c +index 5e13a03d2b32..b228b4d13b7a 100644 +--- a/drivers/mtd/nand/core.c ++++ b/drivers/mtd/nand/core.c +@@ -232,7 +232,9 @@ static int nanddev_get_ecc_engine(struct nand_device *nand) + nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand); + break; + case NAND_ECC_ENGINE_TYPE_ON_HOST: +- pr_err("On-host hardware ECC engines not supported yet\n"); ++ nand->ecc.engine = nand_ecc_get_on_host_hw_engine(nand); ++ if (PTR_ERR(nand->ecc.engine) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + break; + default: + pr_err("Missing ECC engine type\n"); +@@ -252,7 +254,7 @@ static int nanddev_put_ecc_engine(struct nand_device *nand) + { + switch (nand->ecc.ctx.conf.engine_type) { + case NAND_ECC_ENGINE_TYPE_ON_HOST: +- pr_err("On-host hardware ECC engines not supported yet\n"); ++ nand_ecc_put_on_host_hw_engine(nand); + break; + case NAND_ECC_ENGINE_TYPE_NONE: + case NAND_ECC_ENGINE_TYPE_SOFT: +@@ -297,7 +299,9 @@ int nanddev_ecc_engine_init(struct nand_device *nand) + /* Look for the ECC engine to use */ + ret = nanddev_get_ecc_engine(nand); + if (ret) { +- pr_err("No ECC engine found\n"); ++ if (ret != -EPROBE_DEFER) ++ pr_err("No ECC engine found\n"); ++ + return ret; + } + +diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c +index 6c43dfda01d4..078f5ec38de3 100644 +--- a/drivers/mtd/nand/ecc.c ++++ b/drivers/mtd/nand/ecc.c +@@ -96,6 +96,12 @@ + #include <linux/module.h> + #include <linux/mtd/nand.h> + #include <linux/slab.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/of_platform.h> ++ ++static LIST_HEAD(on_host_hw_engines); ++static DEFINE_MUTEX(on_host_hw_engines_mutex); + + /** + * nand_ecc_init_ctx - Init the ECC engine context +@@ -611,6 +617,88 @@ struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand) + } + EXPORT_SYMBOL(nand_ecc_get_on_die_hw_engine); + ++int nand_ecc_register_on_host_hw_engine(struct nand_ecc_engine *engine) ++{ ++ struct nand_ecc_engine *item; ++ ++ if (!engine) ++ return -EINVAL; ++ ++ /* Prevent multiple registrations of one engine */ ++ list_for_each_entry(item, &on_host_hw_engines, node) ++ if (item == engine) ++ return 0; ++ ++ mutex_lock(&on_host_hw_engines_mutex); ++ list_add_tail(&engine->node, &on_host_hw_engines); ++ mutex_unlock(&on_host_hw_engines_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(nand_ecc_register_on_host_hw_engine); ++ ++int nand_ecc_unregister_on_host_hw_engine(struct nand_ecc_engine *engine) ++{ ++ if (!engine) ++ return -EINVAL; ++ ++ mutex_lock(&on_host_hw_engines_mutex); ++ list_del(&engine->node); ++ mutex_unlock(&on_host_hw_engines_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(nand_ecc_unregister_on_host_hw_engine); ++ ++static struct nand_ecc_engine *nand_ecc_match_on_host_hw_engine(struct device *dev) ++{ ++ struct nand_ecc_engine *item; ++ ++ list_for_each_entry(item, &on_host_hw_engines, node) ++ if (item->dev == dev) ++ return item; ++ ++ return NULL; ++} ++ ++struct nand_ecc_engine *nand_ecc_get_on_host_hw_engine(struct nand_device *nand) ++{ ++ struct nand_ecc_engine *engine = NULL; ++ struct device *dev = &nand->mtd.dev; ++ struct platform_device *pdev; ++ struct device_node *np; ++ ++ if (list_empty(&on_host_hw_engines)) ++ return NULL; ++ ++ /* Check for an explicit nand-ecc-engine property */ ++ np = of_parse_phandle(dev->of_node, "nand-ecc-engine", 0); ++ if (np) { ++ pdev = of_find_device_by_node(np); ++ if (!pdev) ++ return ERR_PTR(-EPROBE_DEFER); ++ ++ engine = nand_ecc_match_on_host_hw_engine(&pdev->dev); ++ platform_device_put(pdev); ++ of_node_put(np); ++ ++ if (!engine) ++ return ERR_PTR(-EPROBE_DEFER); ++ } ++ ++ if (engine) ++ get_device(engine->dev); ++ ++ return engine; ++} ++EXPORT_SYMBOL(nand_ecc_get_on_host_hw_engine); ++ ++void nand_ecc_put_on_host_hw_engine(struct nand_device *nand) ++{ ++ put_device(nand->ecc.engine->dev); ++} ++EXPORT_SYMBOL(nand_ecc_put_on_host_hw_engine); ++ + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>"); + MODULE_DESCRIPTION("Generic ECC engine"); +diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h +index 32fc7edf65b3..4ddd20fe9c9e 100644 +--- a/include/linux/mtd/nand.h ++++ b/include/linux/mtd/nand.h +@@ -263,12 +263,36 @@ struct nand_ecc_engine_ops { + struct nand_page_io_req *req); + }; + ++/** ++ * enum nand_ecc_engine_integration - How the NAND ECC engine is integrated ++ * @NAND_ECC_ENGINE_INTEGRATION_INVALID: Invalid value ++ * @NAND_ECC_ENGINE_INTEGRATION_PIPELINED: Pipelined engine, performs on-the-fly ++ * correction, does not need to copy ++ * data around ++ * @NAND_ECC_ENGINE_INTEGRATION_EXTERNAL: External engine, needs to bring the ++ * data into its own area before use ++ */ ++enum nand_ecc_engine_integration { ++ NAND_ECC_ENGINE_INTEGRATION_INVALID, ++ NAND_ECC_ENGINE_INTEGRATION_PIPELINED, ++ NAND_ECC_ENGINE_INTEGRATION_EXTERNAL, ++}; ++ + /** + * struct nand_ecc_engine - ECC engine abstraction for NAND devices ++ * @dev: Host device ++ * @node: Private field for registration time + * @ops: ECC engine operations ++ * @integration: How the engine is integrated with the host ++ * (only relevant on %NAND_ECC_ENGINE_TYPE_ON_HOST engines) ++ * @priv: Private data + */ + struct nand_ecc_engine { ++ struct device *dev; ++ struct list_head node; + struct nand_ecc_engine_ops *ops; ++ enum nand_ecc_engine_integration integration; ++ void *priv; + }; + + void of_get_nand_ecc_user_config(struct nand_device *nand); +@@ -279,8 +303,12 @@ int nand_ecc_prepare_io_req(struct nand_device *nand, + int nand_ecc_finish_io_req(struct nand_device *nand, + struct nand_page_io_req *req); + bool nand_ecc_is_strong_enough(struct nand_device *nand); ++int nand_ecc_register_on_host_hw_engine(struct nand_ecc_engine *engine); ++int nand_ecc_unregister_on_host_hw_engine(struct nand_ecc_engine *engine); + struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand); + struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand); ++struct nand_ecc_engine *nand_ecc_get_on_host_hw_engine(struct nand_device *nand); ++void nand_ecc_put_on_host_hw_engine(struct nand_device *nand); + + #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING) + struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void); +-- +2.35.1 + |