--- /dev/null
+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
+
--- /dev/null
+From 840b2f8dd2d0579e517140e1f9bbc482eaf4ed07 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Thu, 16 Dec 2021 12:16:39 +0100
+Subject: [PATCH 02/15] mtd: nand: Add a new helper to retrieve the ECC context
+
+Introduce nand_to_ecc_ctx() which will allow to easily jump to the
+private pointer of an ECC context given a NAND device. This is very
+handy, from the prepare or finish ECC hook, to get the internal context
+out of the NAND device object.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20211216111654.238086-14-miquel.raynal@bootlin.com
+(cherry picked from commit cda32a618debd3fad8e42757b198719ae180f8f4)
+---
+ include/linux/mtd/nand.h | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
+index 4ddd20fe9c9e..b617efa0a881 100644
+--- a/include/linux/mtd/nand.h
++++ b/include/linux/mtd/nand.h
+@@ -990,6 +990,11 @@ int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos);
+ int nanddev_ecc_engine_init(struct nand_device *nand);
+ void nanddev_ecc_engine_cleanup(struct nand_device *nand);
+
++static inline void *nand_to_ecc_ctx(struct nand_device *nand)
++{
++ return nand->ecc.ctx.priv;
++}
++
+ /* BBT related functions */
+ enum nand_bbt_block_status {
+ NAND_BBT_BLOCK_STATUS_UNKNOWN,
+--
+2.35.1
+
--- /dev/null
+From 784866bc4f9f25e0494b77750f95af2a2619e498 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Thu, 16 Dec 2021 12:16:41 +0100
+Subject: [PATCH 03/15] mtd: nand: ecc: Provide a helper to retrieve a
+ pilelined engine device
+
+In a pipelined engine situation, we might either have the host which
+internally has support for error correction, or have it using an
+external hardware block for this purpose. In the former case, the host
+is also the ECC engine. In the latter case, it is not. In order to get
+the right pointers on the right devices (for example: in order to devm_*
+allocate variables), let's introduce this helper which can safely be
+called by pipelined ECC engines in order to retrieve the right device
+structure.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20211216111654.238086-16-miquel.raynal@bootlin.com
+(cherry picked from commit 5145abeb0649acf810a32e63bd762e617a9b3309)
+---
+ drivers/mtd/nand/ecc.c | 31 +++++++++++++++++++++++++++++++
+ include/linux/mtd/nand.h | 1 +
+ 2 files changed, 32 insertions(+)
+
+diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c
+index 078f5ec38de3..5250764cedee 100644
+--- a/drivers/mtd/nand/ecc.c
++++ b/drivers/mtd/nand/ecc.c
+@@ -699,6 +699,37 @@ void nand_ecc_put_on_host_hw_engine(struct nand_device *nand)
+ }
+ EXPORT_SYMBOL(nand_ecc_put_on_host_hw_engine);
+
++/*
++ * 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;
++ }
++
++ platform_device_put(ecc_pdev);
++ of_node_put(np);
++
++ return &ecc_pdev->dev;
++}
++
+ 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 b617efa0a881..615b3e3a3920 100644
+--- a/include/linux/mtd/nand.h
++++ b/include/linux/mtd/nand.h
+@@ -309,6 +309,7 @@ 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);
++struct device *nand_ecc_get_engine_dev(struct device *host);
+
+ #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING)
+ struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void);
+--
+2.35.1
+
--- /dev/null
+From 3e45577e70cbf8fdc5c13033114989794a3797d5 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Thu, 27 Jan 2022 10:17:56 +0100
+Subject: [PATCH 04/15] spi: spi-mem: Introduce a capability structure
+
+Create a spi_controller_mem_caps structure and put it within the
+spi_controller structure close to the spi_controller_mem_ops
+strucure. So far the only field in this structure is the support for dtr
+operations, but soon we will add another parameter.
+
+Also create a helper to parse the capabilities and check if the
+requested capability has been set or not.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Pratyush Yadav <p.yadav@ti.com>
+Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
+Reviewed-by: Tudor Ambarus <tudor.ambarus@microchip.com>
+Reviewed-by: Mark Brown <broonie@kernel.org>
+Link: https://lore.kernel.org/linux-mtd/20220127091808.1043392-2-miquel.raynal@bootlin.com
+(cherry picked from commit 4a3cc7fb6e63bcfdedec25364738f1493345bd20)
+---
+ include/linux/spi/spi-mem.h | 11 +++++++++++
+ include/linux/spi/spi.h | 3 +++
+ 2 files changed, 14 insertions(+)
+
+diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
+index 85e2ff7b840d..38e5d45c9842 100644
+--- a/include/linux/spi/spi-mem.h
++++ b/include/linux/spi/spi-mem.h
+@@ -285,6 +285,17 @@ struct spi_controller_mem_ops {
+ unsigned long timeout_ms);
+ };
+
++/**
++ * struct spi_controller_mem_caps - SPI memory controller capabilities
++ * @dtr: Supports DTR operations
++ */
++struct spi_controller_mem_caps {
++ bool dtr;
++};
++
++#define spi_mem_controller_is_capable(ctlr, cap) \
++ ((ctlr)->mem_caps && (ctlr)->mem_caps->cap)
++
+ /**
+ * struct spi_mem_driver - SPI memory driver
+ * @spidrv: inherit from a SPI driver
+diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
+index 6b0b686f6f90..8ac58b1a2a9f 100644
+--- a/include/linux/spi/spi.h
++++ b/include/linux/spi/spi.h
+@@ -23,6 +23,7 @@ struct software_node;
+ struct spi_controller;
+ struct spi_transfer;
+ struct spi_controller_mem_ops;
++struct spi_controller_mem_caps;
+
+ /*
+ * INTERFACES between SPI master-side drivers and SPI slave protocol handlers,
+@@ -419,6 +420,7 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
+ * @mem_ops: optimized/dedicated operations for interactions with SPI memory.
+ * This field is optional and should only be implemented if the
+ * controller has native support for memory like operations.
++ * @mem_caps: controller capabilities for the handling of memory operations.
+ * @unprepare_message: undo any work done by prepare_message().
+ * @slave_abort: abort the ongoing transfer request on an SPI slave controller
+ * @cs_gpios: LEGACY: array of GPIO descs to use as chip select lines; one per
+@@ -643,6 +645,7 @@ struct spi_controller {
+
+ /* Optimized handlers for SPI memory-like operations. */
+ const struct spi_controller_mem_ops *mem_ops;
++ const struct spi_controller_mem_caps *mem_caps;
+
+ /* gpio chip select */
+ int *cs_gpios;
+--
+2.35.1
+
--- /dev/null
+From c9cae7e1e5c87d0aa76b7bededa5191a0c8cf25a Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Thu, 27 Jan 2022 10:17:57 +0100
+Subject: [PATCH 05/15] spi: spi-mem: Check the controller extra capabilities
+
+Controllers can now provide a spi-mem capabilities structure. Let's make
+use of it in spi_mem_controller_default_supports_op(). As we want to
+check for DTR operations as well as normal operations in a single
+helper, let's pull the necessary checks from spi_mem_dtr_supports_op()
+for now.
+
+However, because no controller provide these extra capabilities, this
+change has no effect so far.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Pratyush Yadav <p.yadav@ti.com>
+Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
+Reviewed-by: Tudor Ambarus <tudor.ambarus@microchip.com>
+Link: https://lore.kernel.org/linux-mtd/20220127091808.1043392-3-miquel.raynal@bootlin.com
+(cherry picked from commit cb7e96ee81edaa48c67d84c14df2cbe464391c37)
+---
+ drivers/spi/spi-mem.c | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
+index 37f4443ce9a0..86e6597bc3dc 100644
+--- a/drivers/spi/spi-mem.c
++++ b/drivers/spi/spi-mem.c
+@@ -173,11 +173,20 @@ EXPORT_SYMBOL_GPL(spi_mem_dtr_supports_op);
+ bool spi_mem_default_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+ {
+- if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
+- return false;
++ struct spi_controller *ctlr = mem->spi->controller;
++ bool op_is_dtr =
++ op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr;
+
+- if (op->cmd.nbytes != 1)
+- return false;
++ if (op_is_dtr) {
++ if (!spi_mem_controller_is_capable(ctlr, dtr))
++ return false;
++
++ if (op->cmd.nbytes != 2)
++ return false;
++ } else {
++ if (op->cmd.nbytes != 1)
++ return false;
++ }
+
+ return spi_mem_check_buswidth(mem, op);
+ }
+--
+2.35.1
+
--- /dev/null
+From 2e5fba82e4aeb72d71230eef2541881615aaf7cf Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Thu, 27 Jan 2022 10:18:00 +0100
+Subject: [PATCH 06/15] spi: spi-mem: Kill the spi_mem_dtr_supports_op() helper
+
+Now that spi_mem_default_supports_op() has access to the static
+controller capabilities (relating to memory operations), and now that
+these capabilities have been filled by the relevant controllers, there
+is no need for a specific helper checking only DTR operations, so let's
+just kill spi_mem_dtr_supports_op() and simply use
+spi_mem_default_supports_op() instead.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Pratyush Yadav <p.yadav@ti.com>
+Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
+Reviewed-by: Tudor Ambarus <tudor.ambarus@microchip.com>
+Link: https://lore.kernel.org/linux-mtd/20220127091808.1043392-6-miquel.raynal@bootlin.com
+(cherry picked from commit 9a15efc5d5e6b5beaed0883e5bdcd0b1384c1b20)
+---
+ drivers/spi/spi-cadence-quadspi.c | 5 +----
+ drivers/spi/spi-mem.c | 10 ----------
+ drivers/spi/spi-mxic.c | 10 +---------
+ include/linux/spi/spi-mem.h | 11 -----------
+ 4 files changed, 2 insertions(+), 34 deletions(-)
+
+diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
+index 101cc71bffa7..2c98d6a9a2aa 100644
+--- a/drivers/spi/spi-cadence-quadspi.c
++++ b/drivers/spi/spi-cadence-quadspi.c
+@@ -1252,10 +1252,7 @@ static bool cqspi_supports_mem_op(struct spi_mem *mem,
+ if (!(all_true || all_false))
+ return false;
+
+- if (all_true)
+- return spi_mem_dtr_supports_op(mem, op);
+- else
+- return spi_mem_default_supports_op(mem, op);
++ return spi_mem_default_supports_op(mem, op);
+ }
+
+ static int cqspi_of_get_flash_pdata(struct platform_device *pdev,
+diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
+index 86e6597bc3dc..ed966d8129eb 100644
+--- a/drivers/spi/spi-mem.c
++++ b/drivers/spi/spi-mem.c
+@@ -160,16 +160,6 @@ static bool spi_mem_check_buswidth(struct spi_mem *mem,
+ return true;
+ }
+
+-bool spi_mem_dtr_supports_op(struct spi_mem *mem,
+- const struct spi_mem_op *op)
+-{
+- if (op->cmd.nbytes != 2)
+- return false;
+-
+- return spi_mem_check_buswidth(mem, op);
+-}
+-EXPORT_SYMBOL_GPL(spi_mem_dtr_supports_op);
+-
+ bool spi_mem_default_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+ {
+diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c
+index 45889947afed..e895df09896a 100644
+--- a/drivers/spi/spi-mxic.c
++++ b/drivers/spi/spi-mxic.c
+@@ -335,8 +335,6 @@ static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf,
+ static bool mxic_spi_mem_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+ {
+- bool all_false;
+-
+ if (op->data.buswidth > 8 || op->addr.buswidth > 8 ||
+ op->dummy.buswidth > 8 || op->cmd.buswidth > 8)
+ return false;
+@@ -348,13 +346,7 @@ static bool mxic_spi_mem_supports_op(struct spi_mem *mem,
+ if (op->addr.nbytes > 7)
+ return false;
+
+- all_false = !op->cmd.dtr && !op->addr.dtr && !op->dummy.dtr &&
+- !op->data.dtr;
+-
+- if (all_false)
+- return spi_mem_default_supports_op(mem, op);
+- else
+- return spi_mem_dtr_supports_op(mem, op);
++ return spi_mem_default_supports_op(mem, op);
+ }
+
+ static int mxic_spi_mem_exec_op(struct spi_mem *mem,
+diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
+index 38e5d45c9842..4a1bfe689872 100644
+--- a/include/linux/spi/spi-mem.h
++++ b/include/linux/spi/spi-mem.h
+@@ -330,10 +330,6 @@ void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
+
+ bool spi_mem_default_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op);
+-
+-bool spi_mem_dtr_supports_op(struct spi_mem *mem,
+- const struct spi_mem_op *op);
+-
+ #else
+ static inline int
+ spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
+@@ -356,13 +352,6 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
+ {
+ return false;
+ }
+-
+-static inline
+-bool spi_mem_dtr_supports_op(struct spi_mem *mem,
+- const struct spi_mem_op *op)
+-{
+- return false;
+-}
+ #endif /* CONFIG_SPI_MEM */
+
+ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
+--
+2.35.1
+
--- /dev/null
+From 9e7eb0ea442ecb1c3fe443289e288694f10c5148 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Thu, 27 Jan 2022 10:18:01 +0100
+Subject: [PATCH 07/15] spi: spi-mem: Add an ecc parameter to the spi_mem_op
+ structure
+
+Soon the SPI-NAND core will need a way to request a SPI controller to
+enable ECC support for a given operation. This is because of the
+pipelined integration of certain ECC engines, which are directly managed
+by the SPI controller itself.
+
+Introduce a spi_mem_op additional field for this purpose: ecc.
+
+So far this field is left unset and checked to be false by all
+the SPI controller drivers in their ->supports_op() hook, as they all
+call spi_mem_default_supports_op().
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Acked-by: Pratyush Yadav <p.yadav@ti.com>
+Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
+Reviewed-by: Tudor Ambarus <tudor.ambarus@microchip.com>
+Link: https://lore.kernel.org/linux-mtd/20220127091808.1043392-7-miquel.raynal@bootlin.com
+(cherry picked from commit a433c2cbd75ab76f277364f44e76f32c7df306e7)
+---
+ drivers/spi/spi-mem.c | 5 +++++
+ include/linux/spi/spi-mem.h | 4 ++++
+ 2 files changed, 9 insertions(+)
+
+diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
+index ed966d8129eb..f38ac31961c9 100644
+--- a/drivers/spi/spi-mem.c
++++ b/drivers/spi/spi-mem.c
+@@ -178,6 +178,11 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
+ return false;
+ }
+
++ if (op->data.ecc) {
++ if (!spi_mem_controller_is_capable(ctlr, ecc))
++ return false;
++ }
++
+ return spi_mem_check_buswidth(mem, op);
+ }
+ EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
+diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
+index 4a1bfe689872..2ba044d0d5e5 100644
+--- a/include/linux/spi/spi-mem.h
++++ b/include/linux/spi/spi-mem.h
+@@ -89,6 +89,7 @@ enum spi_mem_data_dir {
+ * @dummy.dtr: whether the dummy bytes should be sent in DTR mode or not
+ * @data.buswidth: number of IO lanes used to send/receive the data
+ * @data.dtr: whether the data should be sent in DTR mode or not
++ * @data.ecc: whether error correction is required or not
+ * @data.dir: direction of the transfer
+ * @data.nbytes: number of data bytes to send/receive. Can be zero if the
+ * operation does not involve transferring data
+@@ -119,6 +120,7 @@ struct spi_mem_op {
+ struct {
+ u8 buswidth;
+ u8 dtr : 1;
++ u8 ecc : 1;
+ enum spi_mem_data_dir dir;
+ unsigned int nbytes;
+ union {
+@@ -288,9 +290,11 @@ struct spi_controller_mem_ops {
+ /**
+ * struct spi_controller_mem_caps - SPI memory controller capabilities
+ * @dtr: Supports DTR operations
++ * @ecc: Supports operations with error correction
+ */
+ struct spi_controller_mem_caps {
+ bool dtr;
++ bool ecc;
+ };
+
+ #define spi_mem_controller_is_capable(ctlr, cap) \
+--
+2.35.1
+
--- /dev/null
+From 94ef3c35b935a63f6c156957c92f6cf33c9a8dae Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Thu, 27 Jan 2022 10:18:02 +0100
+Subject: [PATCH 08/15] mtd: spinand: Delay a little bit the dirmap creation
+
+As we will soon tweak the dirmap creation to act a little bit
+differently depending on the picked ECC engine, we need to initialize
+dirmaps after ECC engines. This should not have any effect as dirmaps
+are not yet used at this point.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
+Link: https://lore.kernel.org/linux-mtd/20220127091808.1043392-8-miquel.raynal@bootlin.com
+(cherry picked from commit dc4c2cbf0be2d4a8e2a65013ea2815bb2c8ba949)
+---
+ drivers/mtd/nand/spi/core.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
+index 2c8685f1f2fa..bb6b026b558b 100644
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -1208,14 +1208,6 @@ static int spinand_init(struct spinand_device *spinand)
+ if (ret)
+ goto err_free_bufs;
+
+- ret = spinand_create_dirmaps(spinand);
+- if (ret) {
+- dev_err(dev,
+- "Failed to create direct mappings for read/write operations (err = %d)\n",
+- ret);
+- goto err_manuf_cleanup;
+- }
+-
+ ret = nanddev_init(nand, &spinand_ops, THIS_MODULE);
+ if (ret)
+ goto err_manuf_cleanup;
+@@ -1250,6 +1242,14 @@ static int spinand_init(struct spinand_device *spinand)
+ mtd->ecc_strength = nanddev_get_ecc_conf(nand)->strength;
+ mtd->ecc_step_size = nanddev_get_ecc_conf(nand)->step_size;
+
++ ret = spinand_create_dirmaps(spinand);
++ if (ret) {
++ dev_err(dev,
++ "Failed to create direct mappings for read/write operations (err = %d)\n",
++ ret);
++ goto err_cleanup_ecc_engine;
++ }
++
+ return 0;
+
+ err_cleanup_ecc_engine:
+--
+2.35.1
+
--- /dev/null
+From eb4a2d282c3c5752211d69be6dff2674119e5583 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Thu, 27 Jan 2022 10:18:03 +0100
+Subject: [PATCH 09/15] mtd: spinand: Create direct mapping descriptors for ECC
+ operations
+
+In order for pipelined ECC engines to be able to enable/disable the ECC
+engine only when needed and avoid races when future parallel-operations
+will be supported, we need to provide the information about the use of
+the ECC engine in the direct mapping hooks. As direct mapping
+configurations are meant to be static, it is best to create two new
+mappings: one for regular 'raw' accesses and one for accesses involving
+correction. It is up to the driver to use or not the new ECC enable
+boolean contained in the spi-mem operation.
+
+As dirmaps are not free (they consume a few pages of MMIO address space)
+and because these extra entries are only meant to be used by pipelined
+engines, let's limit their use to this specific type of engine and save
+a bit of memory with all the other setups.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
+Link: https://lore.kernel.org/linux-mtd/20220127091808.1043392-9-miquel.raynal@bootlin.com
+(cherry picked from commit f9d7c7265bcff7d9a17425a8cddf702e8fe159c2)
+---
+ drivers/mtd/nand/spi/core.c | 35 +++++++++++++++++++++++++++++++++--
+ include/linux/mtd/spinand.h | 2 ++
+ 2 files changed, 35 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
+index bb6b026b558b..ff8336870bc0 100644
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -381,7 +381,10 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
+ }
+ }
+
+- rdesc = spinand->dirmaps[req->pos.plane].rdesc;
++ if (req->mode == MTD_OPS_RAW)
++ rdesc = spinand->dirmaps[req->pos.plane].rdesc;
++ else
++ rdesc = spinand->dirmaps[req->pos.plane].rdesc_ecc;
+
+ while (nbytes) {
+ ret = spi_mem_dirmap_read(rdesc, column, nbytes, buf);
+@@ -452,7 +455,10 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
+ req->ooblen);
+ }
+
+- wdesc = spinand->dirmaps[req->pos.plane].wdesc;
++ if (req->mode == MTD_OPS_RAW)
++ wdesc = spinand->dirmaps[req->pos.plane].wdesc;
++ else
++ wdesc = spinand->dirmaps[req->pos.plane].wdesc_ecc;
+
+ while (nbytes) {
+ ret = spi_mem_dirmap_write(wdesc, column, nbytes, buf);
+@@ -865,6 +871,31 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
+
+ spinand->dirmaps[plane].rdesc = desc;
+
++ if (nand->ecc.engine->integration != NAND_ECC_ENGINE_INTEGRATION_PIPELINED) {
++ spinand->dirmaps[plane].wdesc_ecc = spinand->dirmaps[plane].wdesc;
++ spinand->dirmaps[plane].rdesc_ecc = spinand->dirmaps[plane].rdesc;
++
++ return 0;
++ }
++
++ info.op_tmpl = *spinand->op_templates.update_cache;
++ info.op_tmpl.data.ecc = true;
++ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
++ spinand->spimem, &info);
++ if (IS_ERR(desc))
++ return PTR_ERR(desc);
++
++ spinand->dirmaps[plane].wdesc_ecc = desc;
++
++ info.op_tmpl = *spinand->op_templates.read_cache;
++ info.op_tmpl.data.ecc = true;
++ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
++ spinand->spimem, &info);
++ if (IS_ERR(desc))
++ return PTR_ERR(desc);
++
++ spinand->dirmaps[plane].rdesc_ecc = desc;
++
+ return 0;
+ }
+
+diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
+index 6988956b8492..3aa28240a77f 100644
+--- a/include/linux/mtd/spinand.h
++++ b/include/linux/mtd/spinand.h
+@@ -389,6 +389,8 @@ struct spinand_info {
+ struct spinand_dirmap {
+ struct spi_mem_dirmap_desc *wdesc;
+ struct spi_mem_dirmap_desc *rdesc;
++ struct spi_mem_dirmap_desc *wdesc_ecc;
++ struct spi_mem_dirmap_desc *rdesc_ecc;
+ };
+
+ /**
+--
+2.35.1
+
--- /dev/null
+From 41825166744c6e5664281611f5e6d9a2e9333c2b Mon Sep 17 00:00:00 2001
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Sat, 2 Apr 2022 22:31:20 +0800
+Subject: [PATCH 10/15] mtd: nand: fix ecc parameters for mt7622
+
+According to the datasheet, mt7622 only has 5 ECC capabilities instead
+of 7, and the decoding error register is arranged as follows:
++------+---------+---------+---------+---------+
+| Bits | 19:15 | 14:10 | 9:5 | 4:0 |
++------+---------+---------+---------+---------+
+| Name | ERRNUM3 | ERRNUM2 | ERRNUM1 | ERRNUM0 |
++------+---------+---------+---------+---------+
+This means err_mask should be 0x1f instead of 0x3f and the number of
+bits shifted in mtk_ecc_get_stats should be 5 instead of 8.
+
+This commit introduces err_shift for the difference in this register
+and fix other existing parameters.
+
+Public MT7622 reference manual can be found on [0] and the info this
+commit is based on is from page 656 and page 660.
+
+[0]: https://wiki.banana-pi.org/Banana_Pi_BPI-R64#Documents
+
+Fixes: 98dea8d71931 ("mtd: nand: mtk: Support MT7622 NAND flash controller.")
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+(cherry picked from commit 088b769abd1bd21753002b17b696ae1778b16e8c)
+---
+ drivers/mtd/nand/raw/mtk_ecc.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c
+index c437d97debb8..ec9d1fb07006 100644
+--- a/drivers/mtd/nand/raw/mtk_ecc.c
++++ b/drivers/mtd/nand/raw/mtk_ecc.c
+@@ -43,6 +43,7 @@
+
+ struct mtk_ecc_caps {
+ u32 err_mask;
++ u32 err_shift;
+ const u8 *ecc_strength;
+ const u32 *ecc_regs;
+ u8 num_ecc_strength;
+@@ -76,7 +77,7 @@ static const u8 ecc_strength_mt2712[] = {
+ };
+
+ static const u8 ecc_strength_mt7622[] = {
+- 4, 6, 8, 10, 12, 14, 16
++ 4, 6, 8, 10, 12
+ };
+
+ enum mtk_ecc_regs {
+@@ -221,7 +222,7 @@ void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats,
+ for (i = 0; i < sectors; i++) {
+ offset = (i >> 2) << 2;
+ err = readl(ecc->regs + ECC_DECENUM0 + offset);
+- err = err >> ((i % 4) * 8);
++ err = err >> ((i % 4) * ecc->caps->err_shift);
+ err &= ecc->caps->err_mask;
+ if (err == ecc->caps->err_mask) {
+ /* uncorrectable errors */
+@@ -449,6 +450,7 @@ EXPORT_SYMBOL(mtk_ecc_get_parity_bits);
+
+ static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = {
+ .err_mask = 0x3f,
++ .err_shift = 8,
+ .ecc_strength = ecc_strength_mt2701,
+ .ecc_regs = mt2701_ecc_regs,
+ .num_ecc_strength = 20,
+@@ -459,6 +461,7 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = {
+
+ static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = {
+ .err_mask = 0x7f,
++ .err_shift = 8,
+ .ecc_strength = ecc_strength_mt2712,
+ .ecc_regs = mt2712_ecc_regs,
+ .num_ecc_strength = 23,
+@@ -468,10 +471,11 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = {
+ };
+
+ static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = {
+- .err_mask = 0x3f,
++ .err_mask = 0x1f,
++ .err_shift = 5,
+ .ecc_strength = ecc_strength_mt7622,
+ .ecc_regs = mt7622_ecc_regs,
+- .num_ecc_strength = 7,
++ .num_ecc_strength = 5,
+ .ecc_mode_shift = 4,
+ .parity_bits = 13,
+ .pg_irq_sel = 0,
+--
+2.35.1
+