kernel: support "linux,rootfs" DT property for splitting rootfs
[openwrt/staging/hauke.git] / target / linux / generic / pending-5.10 / 400-mtd-mtdsplit-support.patch
1 From: Gabor Juhos <juhosg@openwrt.org>
2 Subject: mtd: Add new Kconfig option for firmware partition split
3
4 Add a new kernel config option for generic firmware partition
5 split support and change the uImage split support to depend on
6 the new option. Aslo rename the MTD_UIMAGE_SPLIT_NAME option to
7 MTD_SPLIT_FIRMWARE_NAME to make it more generic.
8
9 The patch is in preparation for multiple firmware format
10 support.
11
12 Submitted-by: Gabor Juhos <juhosg@openwrt.org>
13
14 SVN-Revision: 38002
15 ---
16 drivers/mtd/Kconfig | 19 +
17 drivers/mtd/mtdpart.c | 144 +++++++++++++-----
18 include/linux/mtd/partitions.h | 7 +
19 drivers/mtd/Makefile | 2 +
20 include/linux/mtd/mtd.h | 25 +
21 5 files changed, 171 insertions(+), 25 deletions(-)
22
23 --- a/drivers/mtd/Kconfig
24 +++ b/drivers/mtd/Kconfig
25 @@ -12,6 +12,25 @@ menuconfig MTD
26
27 if MTD
28
29 +menu "OpenWrt specific MTD options"
30 +
31 +config MTD_ROOTFS_ROOT_DEV
32 + bool "Automatically set 'rootfs' partition to be root filesystem"
33 + default y
34 +
35 +config MTD_SPLIT_FIRMWARE
36 + bool "Automatically split firmware partition for kernel+rootfs"
37 + default y
38 +
39 +config MTD_SPLIT_FIRMWARE_NAME
40 + string "Firmware partition name"
41 + depends on MTD_SPLIT_FIRMWARE
42 + default "firmware"
43 +
44 +source "drivers/mtd/mtdsplit/Kconfig"
45 +
46 +endmenu
47 +
48 config MTD_TESTS
49 tristate "MTD tests support (DANGEROUS)"
50 depends on m
51 --- a/drivers/mtd/mtdpart.c
52 +++ b/drivers/mtd/mtdpart.c
53 @@ -15,11 +15,13 @@
54 #include <linux/kmod.h>
55 #include <linux/mtd/mtd.h>
56 #include <linux/mtd/partitions.h>
57 +#include <linux/magic.h>
58 #include <linux/err.h>
59 #include <linux/of.h>
60 #include <linux/of_platform.h>
61
62 #include "mtdcore.h"
63 +#include "mtdsplit/mtdsplit.h"
64
65 /*
66 * MTD methods which simply translate the effective address and pass through
67 @@ -237,6 +239,147 @@ static int mtd_add_partition_attrs(struc
68 return ret;
69 }
70
71 +static DEFINE_SPINLOCK(part_parser_lock);
72 +static LIST_HEAD(part_parsers);
73 +
74 +static struct mtd_part_parser *mtd_part_parser_get(const char *name)
75 +{
76 + struct mtd_part_parser *p, *ret = NULL;
77 +
78 + spin_lock(&part_parser_lock);
79 +
80 + list_for_each_entry(p, &part_parsers, list)
81 + if (!strcmp(p->name, name) && try_module_get(p->owner)) {
82 + ret = p;
83 + break;
84 + }
85 +
86 + spin_unlock(&part_parser_lock);
87 +
88 + return ret;
89 +}
90 +
91 +static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
92 +{
93 + module_put(p->owner);
94 +}
95 +
96 +static struct mtd_part_parser *
97 +get_partition_parser_by_type(enum mtd_parser_type type,
98 + struct mtd_part_parser *start)
99 +{
100 + struct mtd_part_parser *p, *ret = NULL;
101 +
102 + spin_lock(&part_parser_lock);
103 +
104 + p = list_prepare_entry(start, &part_parsers, list);
105 + if (start)
106 + mtd_part_parser_put(start);
107 +
108 + list_for_each_entry_continue(p, &part_parsers, list) {
109 + if (p->type == type && try_module_get(p->owner)) {
110 + ret = p;
111 + break;
112 + }
113 + }
114 +
115 + spin_unlock(&part_parser_lock);
116 +
117 + return ret;
118 +}
119 +
120 +static int parse_mtd_partitions_by_type(struct mtd_info *master,
121 + enum mtd_parser_type type,
122 + const struct mtd_partition **pparts,
123 + struct mtd_part_parser_data *data)
124 +{
125 + struct mtd_part_parser *prev = NULL;
126 + int ret = 0;
127 +
128 + while (1) {
129 + struct mtd_part_parser *parser;
130 +
131 + parser = get_partition_parser_by_type(type, prev);
132 + if (!parser)
133 + break;
134 +
135 + ret = (*parser->parse_fn)(master, pparts, data);
136 +
137 + if (ret > 0) {
138 + mtd_part_parser_put(parser);
139 + printk(KERN_NOTICE
140 + "%d %s partitions found on MTD device %s\n",
141 + ret, parser->name, master->name);
142 + break;
143 + }
144 +
145 + prev = parser;
146 + }
147 +
148 + return ret;
149 +}
150 +
151 +static int
152 +run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type)
153 +{
154 + struct mtd_partition *parts;
155 + int nr_parts;
156 + int i;
157 +
158 + nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts,
159 + NULL);
160 + if (nr_parts <= 0)
161 + return nr_parts;
162 +
163 + if (WARN_ON(!parts))
164 + return 0;
165 +
166 + for (i = 0; i < nr_parts; i++) {
167 + /* adjust partition offsets */
168 + parts[i].offset += child->part.offset;
169 +
170 + mtd_add_partition(child->parent,
171 + parts[i].name,
172 + parts[i].offset,
173 + parts[i].size);
174 + }
175 +
176 + kfree(parts);
177 +
178 + return nr_parts;
179 +}
180 +
181 +#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME
182 +#define SPLIT_FIRMWARE_NAME CONFIG_MTD_SPLIT_FIRMWARE_NAME
183 +#else
184 +#define SPLIT_FIRMWARE_NAME "unused"
185 +#endif
186 +
187 +static void split_firmware(struct mtd_info *master, struct mtd_info *part)
188 +{
189 + run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE);
190 +}
191 +
192 +static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part)
193 +{
194 + static int rootfs_found = 0;
195 +
196 + if (rootfs_found)
197 + return;
198 +
199 + if (of_find_property(mtd_get_of_node(part), "linux,rootfs", NULL) ||
200 + !strcmp(part->name, "rootfs")) {
201 + run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS);
202 +
203 + rootfs_found = 1;
204 + }
205 +
206 + if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) &&
207 + !strcmp(part->name, SPLIT_FIRMWARE_NAME) &&
208 + !of_find_property(mtd_get_of_node(part), "compatible", NULL))
209 + split_firmware(master, part);
210 +}
211 +
212 int mtd_add_partition(struct mtd_info *parent, const char *name,
213 long long offset, long long length)
214 {
215 @@ -275,6 +418,7 @@ int mtd_add_partition(struct mtd_info *p
216 if (ret)
217 goto err_remove_part;
218
219 + mtd_partition_split(parent, child);
220 mtd_add_partition_attrs(child);
221
222 return 0;
223 @@ -423,6 +567,7 @@ int add_mtd_partitions(struct mtd_info *
224 goto err_del_partitions;
225 }
226
227 + mtd_partition_split(master, child);
228 mtd_add_partition_attrs(child);
229
230 /* Look for subpartitions */
231 @@ -439,31 +584,6 @@ err_del_partitions:
232 return ret;
233 }
234
235 -static DEFINE_SPINLOCK(part_parser_lock);
236 -static LIST_HEAD(part_parsers);
237 -
238 -static struct mtd_part_parser *mtd_part_parser_get(const char *name)
239 -{
240 - struct mtd_part_parser *p, *ret = NULL;
241 -
242 - spin_lock(&part_parser_lock);
243 -
244 - list_for_each_entry(p, &part_parsers, list)
245 - if (!strcmp(p->name, name) && try_module_get(p->owner)) {
246 - ret = p;
247 - break;
248 - }
249 -
250 - spin_unlock(&part_parser_lock);
251 -
252 - return ret;
253 -}
254 -
255 -static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
256 -{
257 - module_put(p->owner);
258 -}
259 -
260 /*
261 * Many partition parsers just expected the core to kfree() all their data in
262 * one chunk. Do that by default.
263 --- a/include/linux/mtd/partitions.h
264 +++ b/include/linux/mtd/partitions.h
265 @@ -75,6 +75,12 @@ struct mtd_part_parser_data {
266 * Functions dealing with the various ways of partitioning the space
267 */
268
269 +enum mtd_parser_type {
270 + MTD_PARSER_TYPE_DEVICE = 0,
271 + MTD_PARSER_TYPE_ROOTFS,
272 + MTD_PARSER_TYPE_FIRMWARE,
273 +};
274 +
275 struct mtd_part_parser {
276 struct list_head list;
277 struct module *owner;
278 @@ -83,6 +89,7 @@ struct mtd_part_parser {
279 int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
280 struct mtd_part_parser_data *);
281 void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
282 + enum mtd_parser_type type;
283 };
284
285 /* Container for passing around a set of parsed partitions */
286 --- a/drivers/mtd/Makefile
287 +++ b/drivers/mtd/Makefile
288 @@ -9,6 +9,8 @@ mtd-y := mtdcore.o mtdsuper.o mtdconc
289
290 obj-y += parsers/
291
292 +obj-$(CONFIG_MTD_SPLIT) += mtdsplit/
293 +
294 # 'Users' - code which presents functionality to userspace.
295 obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
296 obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
297 --- a/include/linux/mtd/mtd.h
298 +++ b/include/linux/mtd/mtd.h
299 @@ -606,6 +606,24 @@ static inline void mtd_align_erase_req(s
300 req->len += mtd->erasesize - mod;
301 }
302
303 +static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd)
304 +{
305 + if (mtd_mod_by_eb(sz, mtd) == 0)
306 + return sz;
307 +
308 + /* Round up to next erase block */
309 + return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize;
310 +}
311 +
312 +static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd)
313 +{
314 + if (mtd_mod_by_eb(sz, mtd) == 0)
315 + return sz;
316 +
317 + /* Round down to the start of the current erase block */
318 + return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize;
319 +}
320 +
321 static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
322 {
323 if (mtd->writesize_shift)
324 @@ -679,6 +697,13 @@ extern struct mtd_info *of_get_mtd_devic
325 extern struct mtd_info *get_mtd_device_nm(const char *name);
326 extern void put_mtd_device(struct mtd_info *mtd);
327
328 +static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
329 +{
330 + if (!mtd_is_partition(mtd))
331 + return 0;
332 +
333 + return mtd->part.offset;
334 +}
335
336 struct mtd_notifier {
337 void (*add)(struct mtd_info *mtd);