1 --- a/drivers/mtd/Kconfig
2 +++ b/drivers/mtd/Kconfig
3 @@ -12,6 +12,25 @@ menuconfig MTD
7 +menu "OpenWrt specific MTD options"
9 +config MTD_ROOTFS_ROOT_DEV
10 + bool "Automatically set 'rootfs' partition to be root filesystem"
13 +config MTD_SPLIT_FIRMWARE
14 + bool "Automatically split firmware partition for kernel+rootfs"
17 +config MTD_SPLIT_FIRMWARE_NAME
18 + string "Firmware partition name"
19 + depends on MTD_SPLIT_FIRMWARE
22 +source "drivers/mtd/mtdsplit/Kconfig"
27 tristate "MTD tests support (DANGEROUS)"
29 --- a/drivers/mtd/mtdpart.c
30 +++ b/drivers/mtd/mtdpart.c
32 #include <linux/kmod.h>
33 #include <linux/mtd/mtd.h>
34 #include <linux/mtd/partitions.h>
35 +#include <linux/magic.h>
36 #include <linux/err.h>
38 #include <linux/of_platform.h>
41 +#include "mtdsplit/mtdsplit.h"
44 * MTD methods which simply translate the effective address and pass through
45 @@ -236,6 +238,146 @@ static int mtd_add_partition_attrs(struc
49 +static DEFINE_SPINLOCK(part_parser_lock);
50 +static LIST_HEAD(part_parsers);
52 +static struct mtd_part_parser *mtd_part_parser_get(const char *name)
54 + struct mtd_part_parser *p, *ret = NULL;
56 + spin_lock(&part_parser_lock);
58 + list_for_each_entry(p, &part_parsers, list)
59 + if (!strcmp(p->name, name) && try_module_get(p->owner)) {
64 + spin_unlock(&part_parser_lock);
69 +static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
71 + module_put(p->owner);
74 +static struct mtd_part_parser *
75 +get_partition_parser_by_type(enum mtd_parser_type type,
76 + struct mtd_part_parser *start)
78 + struct mtd_part_parser *p, *ret = NULL;
80 + spin_lock(&part_parser_lock);
82 + p = list_prepare_entry(start, &part_parsers, list);
84 + mtd_part_parser_put(start);
86 + list_for_each_entry_continue(p, &part_parsers, list) {
87 + if (p->type == type && try_module_get(p->owner)) {
93 + spin_unlock(&part_parser_lock);
98 +static int parse_mtd_partitions_by_type(struct mtd_info *master,
99 + enum mtd_parser_type type,
100 + const struct mtd_partition **pparts,
101 + struct mtd_part_parser_data *data)
103 + struct mtd_part_parser *prev = NULL;
107 + struct mtd_part_parser *parser;
109 + parser = get_partition_parser_by_type(type, prev);
113 + ret = (*parser->parse_fn)(master, pparts, data);
116 + mtd_part_parser_put(parser);
118 + "%d %s partitions found on MTD device %s\n",
119 + ret, parser->name, master->name);
130 +run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type)
132 + struct mtd_partition *parts;
136 + nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts,
141 + if (WARN_ON(!parts))
144 + for (i = 0; i < nr_parts; i++) {
145 + /* adjust partition offsets */
146 + parts[i].offset += child->part.offset;
148 + mtd_add_partition(child->parent,
159 +#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME
160 +#define SPLIT_FIRMWARE_NAME CONFIG_MTD_SPLIT_FIRMWARE_NAME
162 +#define SPLIT_FIRMWARE_NAME "unused"
165 +static void split_firmware(struct mtd_info *master, struct mtd_info *part)
167 + run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE);
170 +static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part)
172 + static int rootfs_found = 0;
177 + if (!strcmp(part->name, "rootfs")) {
178 + run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS);
183 + if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) &&
184 + !strcmp(part->name, SPLIT_FIRMWARE_NAME) &&
185 + !of_find_property(mtd_get_of_node(part), "compatible", NULL))
186 + split_firmware(master, part);
189 int mtd_add_partition(struct mtd_info *parent, const char *name,
190 long long offset, long long length)
192 @@ -274,6 +416,7 @@ int mtd_add_partition(struct mtd_info *p
194 goto err_remove_part;
196 + mtd_partition_split(parent, child);
197 mtd_add_partition_attrs(child);
200 @@ -422,6 +565,7 @@ int add_mtd_partitions(struct mtd_info *
201 goto err_del_partitions;
204 + mtd_partition_split(master, child);
205 mtd_add_partition_attrs(child);
207 /* Look for subpartitions */
208 @@ -438,31 +582,6 @@ err_del_partitions:
212 -static DEFINE_SPINLOCK(part_parser_lock);
213 -static LIST_HEAD(part_parsers);
215 -static struct mtd_part_parser *mtd_part_parser_get(const char *name)
217 - struct mtd_part_parser *p, *ret = NULL;
219 - spin_lock(&part_parser_lock);
221 - list_for_each_entry(p, &part_parsers, list)
222 - if (!strcmp(p->name, name) && try_module_get(p->owner)) {
227 - spin_unlock(&part_parser_lock);
232 -static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
234 - module_put(p->owner);
238 * Many partition parsers just expected the core to kfree() all their data in
239 * one chunk. Do that by default.
240 --- a/include/linux/mtd/partitions.h
241 +++ b/include/linux/mtd/partitions.h
242 @@ -75,6 +75,12 @@ struct mtd_part_parser_data {
243 * Functions dealing with the various ways of partitioning the space
246 +enum mtd_parser_type {
247 + MTD_PARSER_TYPE_DEVICE = 0,
248 + MTD_PARSER_TYPE_ROOTFS,
249 + MTD_PARSER_TYPE_FIRMWARE,
252 struct mtd_part_parser {
253 struct list_head list;
254 struct module *owner;
255 @@ -83,6 +89,7 @@ struct mtd_part_parser {
256 int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
257 struct mtd_part_parser_data *);
258 void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
259 + enum mtd_parser_type type;
262 /* Container for passing around a set of parsed partitions */
263 --- a/drivers/mtd/Makefile
264 +++ b/drivers/mtd/Makefile
265 @@ -9,6 +9,8 @@ mtd-y := mtdcore.o mtdsuper.o mtdconc
269 +obj-$(CONFIG_MTD_SPLIT) += mtdsplit/
271 # 'Users' - code which presents functionality to userspace.
272 obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
273 obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
274 --- a/include/linux/mtd/mtd.h
275 +++ b/include/linux/mtd/mtd.h
276 @@ -613,6 +613,24 @@ static inline void mtd_align_erase_req(s
277 req->len += mtd->erasesize - mod;
280 +static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd)
282 + if (mtd_mod_by_eb(sz, mtd) == 0)
285 + /* Round up to next erase block */
286 + return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize;
289 +static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd)
291 + if (mtd_mod_by_eb(sz, mtd) == 0)
294 + /* Round down to the start of the current erase block */
295 + return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize;
298 static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
300 if (mtd->writesize_shift)
301 @@ -685,6 +703,13 @@ extern void __put_mtd_device(struct mtd_
302 extern struct mtd_info *get_mtd_device_nm(const char *name);
303 extern void put_mtd_device(struct mtd_info *mtd);
305 +static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
307 + if (!mtd_is_partition(mtd))
310 + return mtd->part.offset;
313 struct mtd_notifier {
314 void (*add)(struct mtd_info *mtd);