--- a/arch/mips/lantiq/xway/Makefile
+++ b/arch/mips/lantiq/xway/Makefile
@@ -1,6 +1,6 @@
 obj-y := prom.o sysctrl.o clk.o reset.o dma.o timer.o dcdc.o
 
-obj-y += vmmc.o tffs.o
+obj-y += vmmc.o tffs.o mtd_split.o
 
 obj-y += eth_mac.o
 obj-$(CONFIG_PCI) += ath_eep.o rt_eep.o pci-ath-fixup.o
--- /dev/null
+++ b/arch/mips/lantiq/xway/mtd_split.c
@@ -0,0 +1,129 @@
+#include <linux/magic.h>
+#include <linux/root_dev.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#define ROOTFS_SPLIT_NAME "rootfs_data"
+
+struct squashfs_super_block {
+	__le32 s_magic;
+	__le32 pad0[9];
+	__le64 bytes_used;
+};
+
+static void split_brnimage_kernel(struct mtd_info *master, const char *name,
+                                       int offset, int size)
+{
+	unsigned long buf[4];
+	// Assume at most 2MB of kernel image
+	unsigned long end = offset + (2 << 20);
+	unsigned long part_size = offset + 0x400 - 12;
+	size_t len;
+	int ret;
+
+	if (strcmp(name, "firmware") != 0)
+		return;
+	while (part_size < end) {
+		long size_min = part_size - 0x400 - 12 - offset;
+		long size_max = part_size + 12 - offset;
+		ret = mtd_read(master, part_size, 16, &len, (void *)buf);
+		if (ret || len != 16)
+			return;
+
+		if (le32_to_cpu(buf[0]) < size_min ||
+				le32_to_cpu(buf[0]) > size_max) {
+			part_size += 0x400;
+			continue;
+		}
+
+		if (le32_to_cpu(buf[3]) == SQUASHFS_MAGIC) {
+			part_size += 12 - offset;
+			__mtd_add_partition(master, "rootfs", offset + part_size,
+			                            size - part_size, false);
+			return;
+		}
+		part_size += 0x400;
+	}
+}
+
+static void split_eva_kernel(struct mtd_info *master, const char *name,
+				int offset, int size)
+{
+#define EVA_MAGIC   0xfeed1281
+	unsigned long magic = 0;
+	unsigned long part_size = 0, p;
+	size_t len;
+	int ret;
+
+	if (strcmp(name, CONFIG_MTD_SPLIT_FIRMWARE_NAME) != 0)
+		return;
+
+	ret = mtd_read(master, offset, 4, &len, (void *)&magic);
+	if (ret || len != sizeof(magic))
+		return;
+
+	if (le32_to_cpu(magic) != EVA_MAGIC)
+		return;
+
+	ret = mtd_read(master, offset + 4, 4, &len, (void *)&part_size);
+	if (ret || len != sizeof(part_size))
+		return;
+
+	p = part_size = le32_to_cpu(part_size) + 0x18;
+	p &= ~0xffff;
+	p += 0x10000;
+
+	ret = mtd_read(master, offset + p, 4, &len, (void *)&magic);
+	if (ret || len != sizeof(magic))
+		return;
+
+	if (magic == SQUASHFS_MAGIC)
+		part_size = p + 0x100;
+	else
+	        part_size = mtd_pad_erasesize(master, offset, len);
+
+	if (part_size + master->erasesize > size)
+		return;
+
+	__mtd_add_partition(master, "rootfs", offset + part_size,
+			    size - part_size, false);
+}
+
+static void split_tplink_kernel(struct mtd_info *master, const char *name,
+				int offset, int size)
+{
+#define TPLINK_MAGIC   0x00000002
+	unsigned long magic = 0;
+	unsigned long part_size = 0;
+	size_t len;
+	int ret;
+
+	if (strcmp(name, CONFIG_MTD_SPLIT_FIRMWARE_NAME) != 0)
+		return;
+
+	ret = mtd_read(master, offset, 4, &len, (void *)&magic);
+	if (ret || len != sizeof(magic))
+		return;
+
+	if (le32_to_cpu(magic) != TPLINK_MAGIC)
+		return;
+
+	ret = mtd_read(master, offset + 0x78, 4, &len, (void *)&part_size);
+	if (ret || len != sizeof(part_size))
+		return;
+
+	part_size = be32_to_cpu(part_size) + 0x200;
+	if (part_size + master->erasesize > size)
+		return;
+
+	__mtd_add_partition(master, "rootfs", offset + part_size,
+			    size - part_size, false);
+}
+
+void arch_split_mtd_part(struct mtd_info *master, const char *name,
+				int offset, int size)
+{
+	split_tplink_kernel(master, name, offset, size);
+	split_eva_kernel(master, name, offset, size);
+	split_brnimage_kernel(master, name, offset, size);
+}
--- a/include/linux/mtd/partitions.h
+++ b/include/linux/mtd/partitions.h
@@ -89,12 +89,17 @@ extern void deregister_mtd_parser(struct
 int mtd_is_partition(const struct mtd_info *mtd);
 int mtd_add_partition(struct mtd_info *master, const char *name,
 		      long long offset, long long length);
+int __mtd_add_partition(struct mtd_info *master, const char *name,
+		    long long offset, long long length, bool dup_check);
+
 int mtd_del_partition(struct mtd_info *master, int partno);
 struct mtd_info *mtdpart_get_master(const struct mtd_info *mtd);
 uint64_t mtdpart_get_offset(const struct mtd_info *mtd);
 uint64_t mtd_get_device_size(const struct mtd_info *mtd);
-extern void __weak arch_split_mtd_part(struct mtd_info *master,
-				       const char *name, int offset, int size);
+void __weak arch_split_mtd_part(struct mtd_info *master,
+ 				       const char *name, int offset, int size);
+unsigned long
+mtd_pad_erasesize(struct mtd_info *mtd, int offset, int len);
 
 int parse_mtd_partitions_by_type(struct mtd_info *master,
 				 enum mtd_parser_type type,
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -617,7 +617,7 @@ out_register:
 }
 
 
-static int
+int
 __mtd_add_partition(struct mtd_info *master, const char *name,
 		    long long offset, long long length, bool dup_check)
 {
@@ -738,7 +738,7 @@ run_parsers_by_type(struct mtd_part *sla
 	return nr_parts;
 }
 
-static inline unsigned long
+unsigned long
 mtd_pad_erasesize(struct mtd_info *mtd, int offset, int len)
 {
 	unsigned long mask = mtd->erasesize - 1;
@@ -808,7 +808,6 @@ static void split_uimage(struct mtd_info
 		return;
 
 	len = be32_to_cpu(hdr.size) + 0x40;
-	len = mtd_pad_erasesize(master, part->offset, len);
 	if (len + master->erasesize > part->mtd.size)
 		return;
 
