This is required for non-parser drivers handling MTD devices. Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
		
			
				
	
	
		
			315 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
--- a/drivers/mtd/Kconfig
 | 
						|
+++ b/drivers/mtd/Kconfig
 | 
						|
@@ -12,6 +12,25 @@ menuconfig MTD
 | 
						|
 
 | 
						|
 if MTD
 | 
						|
 
 | 
						|
+menu "OpenWrt specific MTD options"
 | 
						|
+
 | 
						|
+config MTD_ROOTFS_ROOT_DEV
 | 
						|
+	bool "Automatically set 'rootfs' partition to be root filesystem"
 | 
						|
+	default y
 | 
						|
+
 | 
						|
+config MTD_SPLIT_FIRMWARE
 | 
						|
+	bool "Automatically split firmware partition for kernel+rootfs"
 | 
						|
+	default y
 | 
						|
+
 | 
						|
+config MTD_SPLIT_FIRMWARE_NAME
 | 
						|
+	string "Firmware partition name"
 | 
						|
+	depends on MTD_SPLIT_FIRMWARE
 | 
						|
+	default "firmware"
 | 
						|
+
 | 
						|
+source "drivers/mtd/mtdsplit/Kconfig"
 | 
						|
+
 | 
						|
+endmenu
 | 
						|
+
 | 
						|
 config MTD_TESTS
 | 
						|
 	tristate "MTD tests support (DANGEROUS)"
 | 
						|
 	depends on m
 | 
						|
--- a/drivers/mtd/mtdpart.c
 | 
						|
+++ b/drivers/mtd/mtdpart.c
 | 
						|
@@ -15,11 +15,13 @@
 | 
						|
 #include <linux/kmod.h>
 | 
						|
 #include <linux/mtd/mtd.h>
 | 
						|
 #include <linux/mtd/partitions.h>
 | 
						|
+#include <linux/magic.h>
 | 
						|
 #include <linux/err.h>
 | 
						|
 #include <linux/of.h>
 | 
						|
 #include <linux/of_platform.h>
 | 
						|
 
 | 
						|
 #include "mtdcore.h"
 | 
						|
+#include "mtdsplit/mtdsplit.h"
 | 
						|
 
 | 
						|
 /*
 | 
						|
  * MTD methods which simply translate the effective address and pass through
 | 
						|
@@ -237,6 +239,146 @@ static int mtd_add_partition_attrs(struc
 | 
						|
 	return ret;
 | 
						|
 }
 | 
						|
 
 | 
						|
+static DEFINE_SPINLOCK(part_parser_lock);
 | 
						|
+static LIST_HEAD(part_parsers);
 | 
						|
+
 | 
						|
+static struct mtd_part_parser *mtd_part_parser_get(const char *name)
 | 
						|
+{
 | 
						|
+	struct mtd_part_parser *p, *ret = NULL;
 | 
						|
+
 | 
						|
+	spin_lock(&part_parser_lock);
 | 
						|
+
 | 
						|
+	list_for_each_entry(p, &part_parsers, list)
 | 
						|
+		if (!strcmp(p->name, name) && try_module_get(p->owner)) {
 | 
						|
+			ret = p;
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+	spin_unlock(&part_parser_lock);
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
 | 
						|
+{
 | 
						|
+	module_put(p->owner);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct mtd_part_parser *
 | 
						|
+get_partition_parser_by_type(enum mtd_parser_type type,
 | 
						|
+			     struct mtd_part_parser *start)
 | 
						|
+{
 | 
						|
+	struct mtd_part_parser *p, *ret = NULL;
 | 
						|
+
 | 
						|
+	spin_lock(&part_parser_lock);
 | 
						|
+
 | 
						|
+	p = list_prepare_entry(start, &part_parsers, list);
 | 
						|
+	if (start)
 | 
						|
+		mtd_part_parser_put(start);
 | 
						|
+
 | 
						|
+	list_for_each_entry_continue(p, &part_parsers, list) {
 | 
						|
+		if (p->type == type && try_module_get(p->owner)) {
 | 
						|
+			ret = p;
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	spin_unlock(&part_parser_lock);
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int parse_mtd_partitions_by_type(struct mtd_info *master,
 | 
						|
+					enum mtd_parser_type type,
 | 
						|
+					const struct mtd_partition **pparts,
 | 
						|
+					struct mtd_part_parser_data *data)
 | 
						|
+{
 | 
						|
+	struct mtd_part_parser *prev = NULL;
 | 
						|
+	int ret = 0;
 | 
						|
+
 | 
						|
+	while (1) {
 | 
						|
+		struct mtd_part_parser *parser;
 | 
						|
+
 | 
						|
+		parser = get_partition_parser_by_type(type, prev);
 | 
						|
+		if (!parser)
 | 
						|
+			break;
 | 
						|
+
 | 
						|
+		ret = (*parser->parse_fn)(master, pparts, data);
 | 
						|
+
 | 
						|
+		if (ret > 0) {
 | 
						|
+			mtd_part_parser_put(parser);
 | 
						|
+			printk(KERN_NOTICE
 | 
						|
+			       "%d %s partitions found on MTD device %s\n",
 | 
						|
+			       ret, parser->name, master->name);
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		prev = parser;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int
 | 
						|
+run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type)
 | 
						|
+{
 | 
						|
+	struct mtd_partition *parts;
 | 
						|
+	int nr_parts;
 | 
						|
+	int i;
 | 
						|
+
 | 
						|
+	nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts,
 | 
						|
+						NULL);
 | 
						|
+	if (nr_parts <= 0)
 | 
						|
+		return nr_parts;
 | 
						|
+
 | 
						|
+	if (WARN_ON(!parts))
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	for (i = 0; i < nr_parts; i++) {
 | 
						|
+		/* adjust partition offsets */
 | 
						|
+		parts[i].offset += child->part.offset;
 | 
						|
+
 | 
						|
+		mtd_add_partition(child->parent,
 | 
						|
+				  parts[i].name,
 | 
						|
+				  parts[i].offset,
 | 
						|
+				  parts[i].size);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	kfree(parts);
 | 
						|
+
 | 
						|
+	return nr_parts;
 | 
						|
+}
 | 
						|
+
 | 
						|
+#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME
 | 
						|
+#define SPLIT_FIRMWARE_NAME	CONFIG_MTD_SPLIT_FIRMWARE_NAME
 | 
						|
+#else
 | 
						|
+#define SPLIT_FIRMWARE_NAME	"unused"
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+static void split_firmware(struct mtd_info *master, struct mtd_info *part)
 | 
						|
+{
 | 
						|
+	run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part)
 | 
						|
+{
 | 
						|
+	static int rootfs_found = 0;
 | 
						|
+
 | 
						|
+	if (rootfs_found)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	if (!strcmp(part->name, "rootfs")) {
 | 
						|
+		run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS);
 | 
						|
+
 | 
						|
+		rootfs_found = 1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) &&
 | 
						|
+	    !strcmp(part->name, SPLIT_FIRMWARE_NAME) &&
 | 
						|
+	    !of_find_property(mtd_get_of_node(part), "compatible", NULL))
 | 
						|
+		split_firmware(master, part);
 | 
						|
+}
 | 
						|
+
 | 
						|
 int mtd_add_partition(struct mtd_info *parent, const char *name,
 | 
						|
 		      long long offset, long long length)
 | 
						|
 {
 | 
						|
@@ -275,6 +417,7 @@ int mtd_add_partition(struct mtd_info *p
 | 
						|
 	if (ret)
 | 
						|
 		goto err_remove_part;
 | 
						|
 
 | 
						|
+	mtd_partition_split(parent, child);
 | 
						|
 	mtd_add_partition_attrs(child);
 | 
						|
 
 | 
						|
 	return 0;
 | 
						|
@@ -423,6 +566,7 @@ int add_mtd_partitions(struct mtd_info *
 | 
						|
 			goto err_del_partitions;
 | 
						|
 		}
 | 
						|
 
 | 
						|
+		mtd_partition_split(master, child);
 | 
						|
 		mtd_add_partition_attrs(child);
 | 
						|
 
 | 
						|
 		/* Look for subpartitions */
 | 
						|
@@ -439,31 +583,6 @@ err_del_partitions:
 | 
						|
 	return ret;
 | 
						|
 }
 | 
						|
 
 | 
						|
-static DEFINE_SPINLOCK(part_parser_lock);
 | 
						|
-static LIST_HEAD(part_parsers);
 | 
						|
-
 | 
						|
-static struct mtd_part_parser *mtd_part_parser_get(const char *name)
 | 
						|
-{
 | 
						|
-	struct mtd_part_parser *p, *ret = NULL;
 | 
						|
-
 | 
						|
-	spin_lock(&part_parser_lock);
 | 
						|
-
 | 
						|
-	list_for_each_entry(p, &part_parsers, list)
 | 
						|
-		if (!strcmp(p->name, name) && try_module_get(p->owner)) {
 | 
						|
-			ret = p;
 | 
						|
-			break;
 | 
						|
-		}
 | 
						|
-
 | 
						|
-	spin_unlock(&part_parser_lock);
 | 
						|
-
 | 
						|
-	return ret;
 | 
						|
-}
 | 
						|
-
 | 
						|
-static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
 | 
						|
-{
 | 
						|
-	module_put(p->owner);
 | 
						|
-}
 | 
						|
-
 | 
						|
 /*
 | 
						|
  * Many partition parsers just expected the core to kfree() all their data in
 | 
						|
  * one chunk. Do that by default.
 | 
						|
--- a/include/linux/mtd/partitions.h
 | 
						|
+++ b/include/linux/mtd/partitions.h
 | 
						|
@@ -75,6 +75,12 @@ struct mtd_part_parser_data {
 | 
						|
  * Functions dealing with the various ways of partitioning the space
 | 
						|
  */
 | 
						|
 
 | 
						|
+enum mtd_parser_type {
 | 
						|
+	MTD_PARSER_TYPE_DEVICE = 0,
 | 
						|
+	MTD_PARSER_TYPE_ROOTFS,
 | 
						|
+	MTD_PARSER_TYPE_FIRMWARE,
 | 
						|
+};
 | 
						|
+
 | 
						|
 struct mtd_part_parser {
 | 
						|
 	struct list_head list;
 | 
						|
 	struct module *owner;
 | 
						|
@@ -83,6 +89,7 @@ struct mtd_part_parser {
 | 
						|
 	int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
 | 
						|
 			struct mtd_part_parser_data *);
 | 
						|
 	void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
 | 
						|
+	enum mtd_parser_type type;
 | 
						|
 };
 | 
						|
 
 | 
						|
 /* Container for passing around a set of parsed partitions */
 | 
						|
--- a/drivers/mtd/Makefile
 | 
						|
+++ b/drivers/mtd/Makefile
 | 
						|
@@ -9,6 +9,8 @@ mtd-y				:= mtdcore.o mtdsuper.o mtdconc
 | 
						|
 
 | 
						|
 obj-y				+= parsers/
 | 
						|
 
 | 
						|
+obj-$(CONFIG_MTD_SPLIT)		+= mtdsplit/
 | 
						|
+
 | 
						|
 # 'Users' - code which presents functionality to userspace.
 | 
						|
 obj-$(CONFIG_MTD_BLKDEVS)	+= mtd_blkdevs.o
 | 
						|
 obj-$(CONFIG_MTD_BLOCK)		+= mtdblock.o
 | 
						|
--- a/include/linux/mtd/mtd.h
 | 
						|
+++ b/include/linux/mtd/mtd.h
 | 
						|
@@ -606,6 +606,24 @@ static inline void mtd_align_erase_req(s
 | 
						|
 		req->len += mtd->erasesize - mod;
 | 
						|
 }
 | 
						|
 
 | 
						|
+static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd)
 | 
						|
+{
 | 
						|
+	if (mtd_mod_by_eb(sz, mtd) == 0)
 | 
						|
+		return sz;
 | 
						|
+
 | 
						|
+	/* Round up to next erase block */
 | 
						|
+	return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd)
 | 
						|
+{
 | 
						|
+	if (mtd_mod_by_eb(sz, mtd) == 0)
 | 
						|
+		return sz;
 | 
						|
+
 | 
						|
+	/* Round down to the start of the current erase block */
 | 
						|
+	return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize;
 | 
						|
+}
 | 
						|
+
 | 
						|
 static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
 | 
						|
 {
 | 
						|
 	if (mtd->writesize_shift)
 | 
						|
@@ -678,6 +696,13 @@ extern void __put_mtd_device(struct mtd_
 | 
						|
 extern struct mtd_info *get_mtd_device_nm(const char *name);
 | 
						|
 extern void put_mtd_device(struct mtd_info *mtd);
 | 
						|
 
 | 
						|
+static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
 | 
						|
+{
 | 
						|
+	if (!mtd_is_partition(mtd))
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	return mtd->part.offset;
 | 
						|
+}
 | 
						|
 
 | 
						|
 struct mtd_notifier {
 | 
						|
 	void (*add)(struct mtd_info *mtd);
 |