b6b069d234eec4b6c80cec7202ddcf9c9ac61572
[openwrt/staging/linusw.git] /
1 From ad4944aa0b02cb043afe20bc2a018c161e65c992 Mon Sep 17 00:00:00 2001
2 From: Miquel Raynal <miquel.raynal@bootlin.com>
3 Date: Thu, 16 Dec 2021 12:16:38 +0100
4 Subject: [PATCH 01/15] mtd: nand: ecc: Add infrastructure to support hardware
5 engines
6
7 Add the necessary helpers to register/unregister hardware ECC engines
8 that will be called from ECC engine drivers.
9
10 Also add helpers to get the right engine from the user
11 perspective. Keep a reference of the in use ECC engine in order to
12 prevent modules to be unloaded. Put the reference when the engine gets
13 retired.
14
15 A static list of hardware (only) ECC engines is setup to keep track of
16 the registered engines.
17
18 Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
19 Link: https://lore.kernel.org/linux-mtd/20211216111654.238086-13-miquel.raynal@bootlin.com
20 (cherry picked from commit 96489c1c0b53131b0e1ec33e2060538379ad6152)
21 ---
22 drivers/mtd/nand/core.c | 10 +++--
23 drivers/mtd/nand/ecc.c | 88 ++++++++++++++++++++++++++++++++++++++++
24 include/linux/mtd/nand.h | 28 +++++++++++++
25 3 files changed, 123 insertions(+), 3 deletions(-)
26
27 diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c
28 index 5e13a03d2b32..b228b4d13b7a 100644
29 --- a/drivers/mtd/nand/core.c
30 +++ b/drivers/mtd/nand/core.c
31 @@ -232,7 +232,9 @@ static int nanddev_get_ecc_engine(struct nand_device *nand)
32 nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand);
33 break;
34 case NAND_ECC_ENGINE_TYPE_ON_HOST:
35 - pr_err("On-host hardware ECC engines not supported yet\n");
36 + nand->ecc.engine = nand_ecc_get_on_host_hw_engine(nand);
37 + if (PTR_ERR(nand->ecc.engine) == -EPROBE_DEFER)
38 + return -EPROBE_DEFER;
39 break;
40 default:
41 pr_err("Missing ECC engine type\n");
42 @@ -252,7 +254,7 @@ static int nanddev_put_ecc_engine(struct nand_device *nand)
43 {
44 switch (nand->ecc.ctx.conf.engine_type) {
45 case NAND_ECC_ENGINE_TYPE_ON_HOST:
46 - pr_err("On-host hardware ECC engines not supported yet\n");
47 + nand_ecc_put_on_host_hw_engine(nand);
48 break;
49 case NAND_ECC_ENGINE_TYPE_NONE:
50 case NAND_ECC_ENGINE_TYPE_SOFT:
51 @@ -297,7 +299,9 @@ int nanddev_ecc_engine_init(struct nand_device *nand)
52 /* Look for the ECC engine to use */
53 ret = nanddev_get_ecc_engine(nand);
54 if (ret) {
55 - pr_err("No ECC engine found\n");
56 + if (ret != -EPROBE_DEFER)
57 + pr_err("No ECC engine found\n");
58 +
59 return ret;
60 }
61
62 diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c
63 index 6c43dfda01d4..078f5ec38de3 100644
64 --- a/drivers/mtd/nand/ecc.c
65 +++ b/drivers/mtd/nand/ecc.c
66 @@ -96,6 +96,12 @@
67 #include <linux/module.h>
68 #include <linux/mtd/nand.h>
69 #include <linux/slab.h>
70 +#include <linux/of.h>
71 +#include <linux/of_device.h>
72 +#include <linux/of_platform.h>
73 +
74 +static LIST_HEAD(on_host_hw_engines);
75 +static DEFINE_MUTEX(on_host_hw_engines_mutex);
76
77 /**
78 * nand_ecc_init_ctx - Init the ECC engine context
79 @@ -611,6 +617,88 @@ struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand)
80 }
81 EXPORT_SYMBOL(nand_ecc_get_on_die_hw_engine);
82
83 +int nand_ecc_register_on_host_hw_engine(struct nand_ecc_engine *engine)
84 +{
85 + struct nand_ecc_engine *item;
86 +
87 + if (!engine)
88 + return -EINVAL;
89 +
90 + /* Prevent multiple registrations of one engine */
91 + list_for_each_entry(item, &on_host_hw_engines, node)
92 + if (item == engine)
93 + return 0;
94 +
95 + mutex_lock(&on_host_hw_engines_mutex);
96 + list_add_tail(&engine->node, &on_host_hw_engines);
97 + mutex_unlock(&on_host_hw_engines_mutex);
98 +
99 + return 0;
100 +}
101 +EXPORT_SYMBOL(nand_ecc_register_on_host_hw_engine);
102 +
103 +int nand_ecc_unregister_on_host_hw_engine(struct nand_ecc_engine *engine)
104 +{
105 + if (!engine)
106 + return -EINVAL;
107 +
108 + mutex_lock(&on_host_hw_engines_mutex);
109 + list_del(&engine->node);
110 + mutex_unlock(&on_host_hw_engines_mutex);
111 +
112 + return 0;
113 +}
114 +EXPORT_SYMBOL(nand_ecc_unregister_on_host_hw_engine);
115 +
116 +static struct nand_ecc_engine *nand_ecc_match_on_host_hw_engine(struct device *dev)
117 +{
118 + struct nand_ecc_engine *item;
119 +
120 + list_for_each_entry(item, &on_host_hw_engines, node)
121 + if (item->dev == dev)
122 + return item;
123 +
124 + return NULL;
125 +}
126 +
127 +struct nand_ecc_engine *nand_ecc_get_on_host_hw_engine(struct nand_device *nand)
128 +{
129 + struct nand_ecc_engine *engine = NULL;
130 + struct device *dev = &nand->mtd.dev;
131 + struct platform_device *pdev;
132 + struct device_node *np;
133 +
134 + if (list_empty(&on_host_hw_engines))
135 + return NULL;
136 +
137 + /* Check for an explicit nand-ecc-engine property */
138 + np = of_parse_phandle(dev->of_node, "nand-ecc-engine", 0);
139 + if (np) {
140 + pdev = of_find_device_by_node(np);
141 + if (!pdev)
142 + return ERR_PTR(-EPROBE_DEFER);
143 +
144 + engine = nand_ecc_match_on_host_hw_engine(&pdev->dev);
145 + platform_device_put(pdev);
146 + of_node_put(np);
147 +
148 + if (!engine)
149 + return ERR_PTR(-EPROBE_DEFER);
150 + }
151 +
152 + if (engine)
153 + get_device(engine->dev);
154 +
155 + return engine;
156 +}
157 +EXPORT_SYMBOL(nand_ecc_get_on_host_hw_engine);
158 +
159 +void nand_ecc_put_on_host_hw_engine(struct nand_device *nand)
160 +{
161 + put_device(nand->ecc.engine->dev);
162 +}
163 +EXPORT_SYMBOL(nand_ecc_put_on_host_hw_engine);
164 +
165 MODULE_LICENSE("GPL");
166 MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
167 MODULE_DESCRIPTION("Generic ECC engine");
168 diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
169 index 32fc7edf65b3..4ddd20fe9c9e 100644
170 --- a/include/linux/mtd/nand.h
171 +++ b/include/linux/mtd/nand.h
172 @@ -263,12 +263,36 @@ struct nand_ecc_engine_ops {
173 struct nand_page_io_req *req);
174 };
175
176 +/**
177 + * enum nand_ecc_engine_integration - How the NAND ECC engine is integrated
178 + * @NAND_ECC_ENGINE_INTEGRATION_INVALID: Invalid value
179 + * @NAND_ECC_ENGINE_INTEGRATION_PIPELINED: Pipelined engine, performs on-the-fly
180 + * correction, does not need to copy
181 + * data around
182 + * @NAND_ECC_ENGINE_INTEGRATION_EXTERNAL: External engine, needs to bring the
183 + * data into its own area before use
184 + */
185 +enum nand_ecc_engine_integration {
186 + NAND_ECC_ENGINE_INTEGRATION_INVALID,
187 + NAND_ECC_ENGINE_INTEGRATION_PIPELINED,
188 + NAND_ECC_ENGINE_INTEGRATION_EXTERNAL,
189 +};
190 +
191 /**
192 * struct nand_ecc_engine - ECC engine abstraction for NAND devices
193 + * @dev: Host device
194 + * @node: Private field for registration time
195 * @ops: ECC engine operations
196 + * @integration: How the engine is integrated with the host
197 + * (only relevant on %NAND_ECC_ENGINE_TYPE_ON_HOST engines)
198 + * @priv: Private data
199 */
200 struct nand_ecc_engine {
201 + struct device *dev;
202 + struct list_head node;
203 struct nand_ecc_engine_ops *ops;
204 + enum nand_ecc_engine_integration integration;
205 + void *priv;
206 };
207
208 void of_get_nand_ecc_user_config(struct nand_device *nand);
209 @@ -279,8 +303,12 @@ int nand_ecc_prepare_io_req(struct nand_device *nand,
210 int nand_ecc_finish_io_req(struct nand_device *nand,
211 struct nand_page_io_req *req);
212 bool nand_ecc_is_strong_enough(struct nand_device *nand);
213 +int nand_ecc_register_on_host_hw_engine(struct nand_ecc_engine *engine);
214 +int nand_ecc_unregister_on_host_hw_engine(struct nand_ecc_engine *engine);
215 struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand);
216 struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand);
217 +struct nand_ecc_engine *nand_ecc_get_on_host_hw_engine(struct nand_device *nand);
218 +void nand_ecc_put_on_host_hw_engine(struct nand_device *nand);
219
220 #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING)
221 struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void);
222 --
223 2.35.1
224