Index: linux-3.10.20/drivers/mtd/Makefile =================================================================== --- linux-3.10.20.orig/drivers/mtd/Makefile +++ linux-3.10.20/drivers/mtd/Makefile @@ -4,7 +4,12 @@ # Core functionality. obj-$(CONFIG_MTD) += mtd.o -mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o +mtd-y += mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o +mtd-$(CONFIG_MTD_SPLIT) += mtdsplit.o +mtd-$(CONFIG_MTD_SPLIT_SEAMA_FW) += mtdsplit_seama.o +mtd-$(CONFIG_MTD_SPLIT_SQUASHFS_ROOT) += mtdsplit_squashfs.o +mtd-$(CONFIG_MTD_SPLIT_UIMAGE_FW) += mtdsplit_uimage.o +mtd-$(CONFIG_MTD_SPLIT_LZMA_FW) += mtdsplit_lzma.o obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o Index: linux-3.10.20/drivers/mtd/Kconfig =================================================================== --- linux-3.10.20.orig/drivers/mtd/Kconfig +++ linux-3.10.20/drivers/mtd/Kconfig @@ -12,6 +12,68 @@ menuconfig MTD if MTD +if SUPPORT_OPENWRT +menu "OpenWrt specific MTD options" + +config MTD_ROOTFS_ROOT_DEV + bool "Automatically set 'rootfs' partition to be root filesystem" + default n + +config MTD_ROOTFS_SPLIT + bool "Automatically split 'rootfs' partition for squashfs" + select MTD_SPLIT + default n + +config MTD_SPLIT_FIRMWARE + bool "Automatically split firmware partition for kernel+rootfs" + default n + +config MTD_SPLIT_FIRMWARE_NAME + string "Firmware partition name" + depends on MTD_SPLIT_FIRMWARE + default "firmware" + default n + +config MTD_UIMAGE_SPLIT + bool "Enable split support for firmware partitions containing a uImage" + depends on MTD_SPLIT_FIRMWARE + default n + +comment "Rootfs partition parsers" + +config MTD_SPLIT_SQUASHFS_ROOT + bool "Squashfs based root partition parser" + select MTD_SPLIT + default n + help + This provides a parsing function which allows to detect the + offset and size of the unused portion of a rootfs partition + containing a squashfs. +comment "Firmware partition parsers" + +config MTD_SPLIT_SEAMA_FW + bool "Seama firmware parser" + select MTD_SPLIT + default n + +config MTD_SPLIT_UIMAGE_FW + bool "uImage based firmware partition parser" + select MTD_SPLIT + default n + +config MTD_SPLIT_LZMA_FW + bool "LZMA compressed kernel based firmware partition parser" + select MTD_SPLIT + default n + +config MTD_SPLIT + def_bool n + depends on SUPPORT_OPENWRT + help + Generic MTD split support. + +endmenu +endif # SUPPORT_OPENWRT config MTD_TESTS tristate "MTD tests support (DANGEROUS)" depends on m @@ -309,10 +371,10 @@ config MTD_SWAP OOB. config MTK_MTD_NOR - depends on ARCH_MT7623 + depends on ARCH_MT7623 tristate "MTK NOR Flash Support" help - Support MTK NOR flash controller. + Support MTK NOR flash controller. Index: linux-3.10.20/drivers/mtd/mtdpart.c =================================================================== --- linux-3.10.20.orig/drivers/mtd/mtdpart.c +++ linux-3.10.20/drivers/mtd/mtdpart.c @@ -29,10 +29,16 @@ #include #include #include +#if defined(CONFIG_SUPPORT_OPENWRT) +#include +#endif #include #include "mtdcore.h" - +#if defined(CONFIG_SUPPORT_OPENWRT) +#include "mtdsplit.h" +#define MTD_ERASE_PARTIAL 0x8000 /* partition only covers parts of an erase block */ +#endif #define DYNAMIC_CHANGE_MTD_WRITEABLE #ifdef DYNAMIC_CHANGE_MTD_WRITEABLE //wschen 2011-01-05 #include @@ -60,6 +66,10 @@ struct mtd_part { struct list_head list; }; +#if defined(CONFIG_SUPPORT_OPENWRT) +static void mtd_partition_split(struct mtd_info *master, struct mtd_part *part); +#endif + /* * Given a pointer to the MTD object in the mtd_part structure, we can retrieve * the pointer to that structure with this macro. @@ -243,12 +253,61 @@ static int part_erase(struct mtd_info *m struct mtd_part *part = PART(mtd); int ret; +#if defined(CONFIG_SUPPORT_OPENWRT) + instr->partial_start = false; + if (mtd->flags & MTD_ERASE_PARTIAL) { + size_t readlen = 0; + u64 mtd_ofs; + + instr->erase_buf = kmalloc(part->master->erasesize, GFP_ATOMIC); + if (!instr->erase_buf) + return -ENOMEM; + + mtd_ofs = part->offset + instr->addr; + instr->erase_buf_ofs = do_div(mtd_ofs, part->master->erasesize); + + if (instr->erase_buf_ofs > 0) { + instr->addr -= instr->erase_buf_ofs; + ret = mtd_read(part->master, + instr->addr + part->offset, + part->master->erasesize, + &readlen, instr->erase_buf); + + instr->partial_start = true; + } else { + mtd_ofs = part->offset + part->mtd.size; + instr->erase_buf_ofs = part->master->erasesize - + do_div(mtd_ofs, part->master->erasesize); + + if (instr->erase_buf_ofs > 0) { + instr->len += instr->erase_buf_ofs; + ret = mtd_read(part->master, + part->offset + instr->addr + + instr->len - part->master->erasesize, + part->master->erasesize, &readlen, + instr->erase_buf); + } else { + ret = 0; + } + } + if (ret < 0) { + kfree(instr->erase_buf); + return ret; + } + + } +#endif + instr->addr += part->offset; ret = part->master->_erase(part->master, instr); if (ret) { if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) instr->fail_addr -= part->offset; instr->addr -= part->offset; +#if defined(CONFIG_SUPPORT_OPENWRT) + if (mtd->flags & MTD_ERASE_PARTIAL) + kfree(instr->erase_buf); +#endif } return ret; } @@ -257,7 +316,27 @@ void mtd_erase_callback(struct erase_inf { if (instr->mtd->_erase == part_erase) { struct mtd_part *part = PART(instr->mtd); +#if defined(CONFIG_SUPPORT_OPENWRT) + size_t wrlen = 0; + if (instr->mtd->flags & MTD_ERASE_PARTIAL) { + if (instr->partial_start) { + part->master->_write(part->master, + instr->addr, instr->erase_buf_ofs, + &wrlen, instr->erase_buf); + instr->addr += instr->erase_buf_ofs; + } else { + instr->len -= instr->erase_buf_ofs; + part->master->_write(part->master, + instr->addr + instr->len, + instr->erase_buf_ofs, &wrlen, + instr->erase_buf + + part->master->erasesize - + instr->erase_buf_ofs); + } + kfree(instr->erase_buf); + } +#endif if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) instr->fail_addr -= part->offset; instr->addr -= part->offset; @@ -276,7 +355,17 @@ static int part_lock(struct mtd_info *mt static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct mtd_part *part = PART(mtd); +#if defined(CONFIG_SUPPORT_OPENWRT) + ofs += part->offset; + if (mtd->flags & MTD_ERASE_PARTIAL) { + /* round up len to next erasesize and round down offset to prev block */ + len = (mtd_div_by_eb(len, part->master) + 1) * part->master->erasesize; + ofs &= ~(part->master->erasesize - 1); + } + return part->master->_unlock(part->master, ofs, len); +#else return part->master->_unlock(part->master, ofs + part->offset, len); +#endif } static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) @@ -450,6 +539,14 @@ static struct mtd_part *allocate_partiti if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; if (slave->offset == MTDPART_OFS_NXTBLK) { +#if defined(CONFIG_SUPPORT_OPENWRT) + /* Round up to next erasesize */ + slave->offset = mtd_roundup_to_eb(cur_offset, master); + if (slave->offset != cur_offset) + printk(KERN_NOTICE "Moving partition %d: " + "0x%012llx -> 0x%012llx\n", partno, + (unsigned long long)cur_offset, (unsigned long long)slave->offset); +#else slave->offset = cur_offset; if (mtd_mod_by_eb(cur_offset, master) != 0) { /* Round up to next erasesize */ @@ -458,6 +555,7 @@ static struct mtd_part *allocate_partiti "0x%012llx -> 0x%012llx\n", partno, (unsigned long long)cur_offset, (unsigned long long)slave->offset); } +#endif } if (slave->offset == MTDPART_OFS_RETAIN) { slave->offset = cur_offset; @@ -518,6 +616,29 @@ static struct mtd_part *allocate_partiti slave->mtd.erasesize = master->erasesize; } +#if defined(CONFIG_SUPPORT_OPENWRT) + if ((slave->mtd.flags & MTD_WRITEABLE) && + mtd_mod_by_eb(slave->offset, &slave->mtd)) { + /* Doesn't start on a boundary of major erase size */ + slave->mtd.flags |= MTD_ERASE_PARTIAL; + if (((u32) slave->mtd.size) > master->erasesize) + slave->mtd.flags &= ~MTD_WRITEABLE; + else + slave->mtd.erasesize = slave->mtd.size; + } + if ((slave->mtd.flags & MTD_WRITEABLE) && + mtd_mod_by_eb(slave->offset + slave->mtd.size, &slave->mtd)) { + slave->mtd.flags |= MTD_ERASE_PARTIAL; + + if ((u32) slave->mtd.size > master->erasesize) + slave->mtd.flags &= ~MTD_WRITEABLE; + else + slave->mtd.erasesize = slave->mtd.size; + } + if ((slave->mtd.flags & (MTD_ERASE_PARTIAL|MTD_WRITEABLE)) == MTD_ERASE_PARTIAL) + printk(KERN_WARNING"mtd: partition \"%s\" must either start or end on erase block boundary or be smaller than an erase block -- forcing read-only\n", + part->name); +#else if ((slave->mtd.flags & MTD_WRITEABLE) && mtd_mod_by_eb(slave->offset, &slave->mtd)) { /* Doesn't start on a boundary of major erase size */ @@ -533,6 +654,7 @@ static struct mtd_part *allocate_partiti printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", part->name); } +#endif slave->mtd.ecclayout = master->ecclayout; slave->mtd.ecc_strength = master->ecc_strength; @@ -552,6 +674,74 @@ out_register: return slave; } +#if defined(CONFIG_SUPPORT_OPENWRT) +static int +__mtd_add_partition(struct mtd_info *master, char *name, + long long offset, long long length, bool dup_check) +{ + struct mtd_partition part; + struct mtd_part *p, *new; + uint64_t start, end; + int ret = 0; + + /* the direct offset is expected */ + if (offset == MTDPART_OFS_APPEND || + offset == MTDPART_OFS_NXTBLK) + return -EINVAL; + + if (length == MTDPART_SIZ_FULL) + length = master->size - offset; + + if (length <= 0) + return -EINVAL; + + part.name = name; + part.size = length; + part.offset = offset; + part.mask_flags = 0; + part.ecclayout = NULL; + + new = allocate_partition(master, &part, -1, offset); + if (IS_ERR(new)) + return PTR_ERR(new); + + start = offset; + end = offset + length; + + mutex_lock(&mtd_partitions_mutex); + if (dup_check) { + list_for_each_entry(p, &mtd_partitions, list) + if (p->master == master) { + if ((start >= p->offset) && + (start < (p->offset + p->mtd.size))) + goto err_inv; + + if ((end >= p->offset) && + (end < (p->offset + p->mtd.size))) + goto err_inv; + } + } + + list_add(&new->list, &mtd_partitions); + mutex_unlock(&mtd_partitions_mutex); + + add_mtd_device(&new->mtd); + mtd_partition_split(master, new); + + return ret; +err_inv: + mutex_unlock(&mtd_partitions_mutex); + free_partition(new); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(mtd_add_partition); + +int mtd_add_partition(struct mtd_info *master, char *name, + long long offset, long long length) +{ + return __mtd_add_partition(master, name, offset, length, true); +} +#else /* CONFIG_SUPPORT_OPENWRT */ int mtd_add_partition(struct mtd_info *master, char *name, long long offset, long long length) { @@ -608,6 +798,7 @@ err_inv: return -EINVAL; } EXPORT_SYMBOL_GPL(mtd_add_partition); +#endif /* CONFIG_SUPPORT_OPENWRT */ int mtd_del_partition(struct mtd_info *master, int partno) { @@ -632,6 +823,164 @@ int mtd_del_partition(struct mtd_info *m } EXPORT_SYMBOL_GPL(mtd_del_partition); +#if defined(CONFIG_SUPPORT_OPENWRT) +static int +run_parsers_by_type(struct mtd_part *slave, enum mtd_parser_type type) +{ + struct mtd_partition *parts; + int nr_parts; + int i; + + nr_parts = parse_mtd_partitions_by_type(&slave->mtd, type, &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 += slave->offset; + + __mtd_add_partition(slave->master, + parts[i].name, + parts[i].offset, + parts[i].size, + false); + } + + kfree(parts); + + return nr_parts; +} + +static inline unsigned long +mtd_pad_erasesize(struct mtd_info *mtd, int offset, int len) +{ + unsigned long mask = mtd->erasesize - 1; + + len += offset & mask; + len = (len + mask) & ~mask; + len -= offset & mask; + return len; +} + +static int split_squashfs(struct mtd_info *master, int offset, int *split_offset) +{ + size_t squashfs_len; + int len, ret; + + ret = mtd_get_squashfs_len(master, offset, &squashfs_len); + if (ret) + return ret; + + len = mtd_pad_erasesize(master, offset, squashfs_len); + *split_offset = offset + len; + + return 0; +} + +static void split_rootfs_data(struct mtd_info *master, struct mtd_part *part) +{ + unsigned int split_offset = 0; + unsigned int split_size; + int ret; + + ret = run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS); + if (ret > 0) + return; + + ret = split_squashfs(master, part->offset, &split_offset); + if (ret) + return; + + if (split_offset <= 0) + return; + + split_size = part->mtd.size - (split_offset - part->offset); + printk(KERN_INFO "mtd: partition \"%s\" created automatically, ofs=0x%x, len=0x%x\n", + ROOTFS_SPLIT_NAME, split_offset, split_size); + + __mtd_add_partition(master, ROOTFS_SPLIT_NAME, split_offset, + split_size, false); +} + +#define UBOOT_MAGIC 0x27051956 + +static void split_uimage(struct mtd_info *master, struct mtd_part *part) +{ + struct { + __be32 magic; + __be32 pad0[2]; + __be32 size; + __be32 pad1[4]; + __be32 name[7]; + __be32 kern_size; + } hdr; + size_t len; + + if (mtd_read(master, part->offset, sizeof(hdr), &len, (void *) &hdr)) + return; + + if (len != sizeof(hdr) || hdr.magic != cpu_to_be32(UBOOT_MAGIC)) + return; + + if (hdr.kern_size != 0 && hdr.name[0] == 0) + len = be32_to_cpu(hdr.kern_size); + else + len = be32_to_cpu(hdr.size) + 0x40; + + __mtd_add_partition(master, "rootfs", part->offset + len, + part->mtd.size - len, false); +} + +#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_part *part) +{ + int ret; + + ret = run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE); + if (ret > 0) + return; + + if (config_enabled(CONFIG_MTD_UIMAGE_SPLIT)) + split_uimage(master, part); +} + +void __weak arch_split_mtd_part(struct mtd_info *master, const char *name, + int offset, int size) +{ +} + +static void mtd_partition_split(struct mtd_info *master, struct mtd_part *part) +{ + static int rootfs_found = 0; + + if (rootfs_found) + return; + + if (!strcmp(part->mtd.name, "rootfs")) { + rootfs_found = 1; + + if (config_enabled(CONFIG_MTD_ROOTFS_SPLIT)) + split_rootfs_data(master, part); + } + + if (!strcmp(part->mtd.name, SPLIT_FIRMWARE_NAME) && + config_enabled(CONFIG_MTD_SPLIT_FIRMWARE)) + split_firmware(master, part); + + arch_split_mtd_part(master, part->mtd.name, part->offset, + part->mtd.size); +} +#endif /* CONFIG_SUPPORT_OPENWRT */ + /* * This function, given a master MTD object and a partition table, creates * and registers slave MTD objects which are bound to the master according to @@ -661,7 +1010,9 @@ int add_mtd_partitions(struct mtd_info * mutex_unlock(&mtd_partitions_mutex); add_mtd_device(&slave->mtd); - +#if defined(CONFIG_SUPPORT_OPENWRT) + mtd_partition_split(master, slave); +#endif cur_offset = slave->offset + slave->mtd.size; } #ifdef DYNAMIC_CHANGE_MTD_WRITEABLE //wschen 2011-01-05 @@ -692,6 +1043,32 @@ static struct mtd_part_parser *get_parti #define put_partition_parser(p) do { module_put((p)->owner); } while (0) +#if defined(CONFIG_SUPPORT_OPENWRT) +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) + put_partition_parser(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; +} +#endif + int register_mtd_parser(struct mtd_part_parser *p) { spin_lock(&part_parser_lock); @@ -768,6 +1145,40 @@ int parse_mtd_partitions(struct mtd_info return ret; } +#if defined(CONFIG_SUPPORT_OPENWRT) +int parse_mtd_partitions_by_type(struct mtd_info *master, + enum mtd_parser_type type, + 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) { + put_partition_parser(parser); + printk(KERN_NOTICE + "%d %s partitions found on MTD device %s\n", + ret, parser->name, master->name); + break; + } + + prev = parser; + } + + return ret; +} +EXPORT_SYMBOL_GPL(parse_mtd_partitions_by_type); +#endif /* CONFIG_SUPPORT_OPENWRT */ + int mtd_is_partition(const struct mtd_info *mtd) { struct mtd_part *part; @@ -785,6 +1196,26 @@ int mtd_is_partition(const struct mtd_in } EXPORT_SYMBOL_GPL(mtd_is_partition); +#if defined(CONFIG_SUPPORT_OPENWRT) +struct mtd_info *mtdpart_get_master(const struct mtd_info *mtd) +{ + if (!mtd_is_partition(mtd)) + return (struct mtd_info *)mtd; + + return PART(mtd)->master; +} +EXPORT_SYMBOL_GPL(mtdpart_get_master); + +uint64_t mtdpart_get_offset(const struct mtd_info *mtd) +{ + if (!mtd_is_partition(mtd)) + return 0; + + return PART(mtd)->offset; +} +EXPORT_SYMBOL_GPL(mtdpart_get_offset); +#endif /* CONFIG_SUPPORT_OPENWRT */ + #ifdef CONFIG_MTK_MTD_NAND u64 mtd_partition_start_address(struct mtd_info *mtd) { Index: linux-3.10.20/include/linux/mtd/map.h =================================================================== --- linux-3.10.20.orig/include/linux/mtd/map.h +++ linux-3.10.20/include/linux/mtd/map.h @@ -365,7 +365,7 @@ static inline map_word map_word_load_par bitpos = (map_bankwidth(map)-1-i)*8; #endif orig.x[0] &= ~(0xff << bitpos); - orig.x[0] |= buf[i-start] << bitpos; + orig.x[0] |= (unsigned long)buf[i-start] << bitpos; } } return orig; @@ -384,7 +384,7 @@ static inline map_word map_word_ff(struc if (map_bankwidth(map) < MAP_FF_LIMIT) { int bw = 8 * map_bankwidth(map); - r.x[0] = (1 << bw) - 1; + r.x[0] = (1UL << bw) - 1; } else { for (i=0; ierasesize); } +#if defined(CONFIG_SUPPORT_OPENWRT) +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; +} +#endif + static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd) { if (mtd->writesize_shift) Index: linux-3.10.20/include/linux/mtd/nand.h =================================================================== --- linux-3.10.20.orig/include/linux/mtd/nand.h +++ linux-3.10.20/include/linux/mtd/nand.h @@ -679,6 +679,7 @@ struct platform_nand_chip { unsigned int options; unsigned int bbt_options; const char **part_probe_types; + int (*chip_fixup)(struct mtd_info *mtd); }; /* Keep gcc happy */ Index: linux-3.10.20/include/linux/mtd/partitions.h =================================================================== --- linux-3.10.20.orig/include/linux/mtd/partitions.h +++ linux-3.10.20/include/linux/mtd/partitions.h @@ -68,12 +68,23 @@ struct mtd_part_parser_data { * Functions dealing with the various ways of partitioning the space */ +#if defined(CONFIG_SUPPORT_OPENWRT) +enum mtd_parser_type { + MTD_PARSER_TYPE_DEVICE = 0, + MTD_PARSER_TYPE_ROOTFS, + MTD_PARSER_TYPE_FIRMWARE, +}; +#endif + struct mtd_part_parser { struct list_head list; struct module *owner; const char *name; int (*parse_fn)(struct mtd_info *, struct mtd_partition **, struct mtd_part_parser_data *); +#if defined(CONFIG_SUPPORT_OPENWRT) + enum mtd_parser_type type; +#endif }; extern int register_mtd_parser(struct mtd_part_parser *parser); @@ -83,6 +94,18 @@ int mtd_is_partition(const struct mtd_in int mtd_add_partition(struct mtd_info *master, char *name, long long offset, long long length); int mtd_del_partition(struct mtd_info *master, int partno); +#if defined(CONFIG_SUPPORT_OPENWRT) +struct mtd_info *mtdpart_get_master(const struct mtd_info *mtd); +uint64_t mtdpart_get_offset(const struct mtd_info *mtd); +#endif uint64_t mtd_get_device_size(const struct mtd_info *mtd); +#if defined(CONFIG_SUPPORT_OPENWRT) +extern void __weak arch_split_mtd_part(struct mtd_info *master, + const char *name, int offset, int size); +int parse_mtd_partitions_by_type(struct mtd_info *master, + enum mtd_parser_type type, + struct mtd_partition **pparts, + struct mtd_part_parser_data *data); +#endif #endif