mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 22:44:27 -05:00 
			
		
		
		
	Run `make target/linux/refresh` to align patches with OpenWrt style. Signed-off-by: Daniel Golle <daniel@makrotopia.org>
		
			
				
	
	
		
			215 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
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(-)
 | 
						|
 | 
						|
--- a/drivers/mtd/nand/core.c
 | 
						|
+++ b/drivers/mtd/nand/core.c
 | 
						|
@@ -232,7 +232,9 @@ static int nanddev_get_ecc_engine(struct
 | 
						|
 		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
 | 
						|
 {
 | 
						|
 	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_
 | 
						|
 	/* 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;
 | 
						|
 	}
 | 
						|
 
 | 
						|
--- 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_
 | 
						|
 }
 | 
						|
 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");
 | 
						|
--- a/include/linux/mtd/nand.h
 | 
						|
+++ b/include/linux/mtd/nand.h
 | 
						|
@@ -264,11 +264,35 @@ struct nand_ecc_engine_ops {
 | 
						|
 };
 | 
						|
 
 | 
						|
 /**
 | 
						|
+ * 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_
 | 
						|
 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);
 |