ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/target/linux/generic/pending-5.4/411-mtd-partial_eraseblock_write.patch b/target/linux/generic/pending-5.4/411-mtd-partial_eraseblock_write.patch
new file mode 100644
index 0000000..fd0834f
--- /dev/null
+++ b/target/linux/generic/pending-5.4/411-mtd-partial_eraseblock_write.patch
@@ -0,0 +1,130 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: mtd: implement write support for partitions covering only a part of an eraseblock (buffer data that would otherwise be erased)
+
+lede-commit: 87a8e8ac1067f58ba831c4aae443f3655c31cd80
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/mtd/mtdpart.c   | 90 ++++++++++++++++++++++++++++++++++++++++++++-----
+ include/linux/mtd/mtd.h |  4 +++
+ 2 files changed, 85 insertions(+), 9 deletions(-)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -23,6 +23,8 @@
+ #include "mtdcore.h"
+ #include "mtdsplit/mtdsplit.h"
+ 
++#define MTD_ERASE_PARTIAL	0x8000 /* partition only covers parts of an erase block */
++
+ /* Our partition linked list */
+ static LIST_HEAD(mtd_partitions);
+ static DEFINE_MUTEX(mtd_partitions_mutex);
+@@ -207,11 +209,77 @@ static int part_erase(struct mtd_info *m
+ {
+ 	struct mtd_part *part = mtd_to_part(mtd);
+ 	int ret;
++	size_t wrlen = 0;
++	u8 *erase_buf = NULL;
++	u32 erase_buf_ofs = 0;
++	bool partial_start = false;
++
++	if (mtd->flags & MTD_ERASE_PARTIAL) {
++		size_t readlen = 0;
++		u64 mtd_ofs;
++
++		erase_buf = kmalloc(part->parent->erasesize, GFP_ATOMIC);
++		if (!erase_buf)
++			return -ENOMEM;
++
++		mtd_ofs = part->offset + instr->addr;
++		erase_buf_ofs = do_div(mtd_ofs, part->parent->erasesize);
++
++		if (erase_buf_ofs > 0) {
++			instr->addr -= erase_buf_ofs;
++			ret = mtd_read(part->parent,
++				instr->addr + part->offset,
++				part->parent->erasesize,
++				&readlen, erase_buf);
++
++			instr->len += erase_buf_ofs;
++			partial_start = true;
++		} else {
++			mtd_ofs = part->offset + part->mtd.size;
++			erase_buf_ofs = part->parent->erasesize -
++				do_div(mtd_ofs, part->parent->erasesize);
++
++			if (erase_buf_ofs > 0) {
++				instr->len += erase_buf_ofs;
++				ret = mtd_read(part->parent,
++					part->offset + instr->addr +
++					instr->len - part->parent->erasesize,
++					part->parent->erasesize, &readlen,
++					erase_buf);
++			} else {
++				ret = 0;
++			}
++		}
++		if (ret < 0) {
++			kfree(erase_buf);
++			return ret;
++		}
++
++	}
+ 
+ 	instr->addr += part->offset;
+ 	ret = part->parent->_erase(part->parent, instr);
+ 	if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
+ 		instr->fail_addr -= part->offset;
++
++	if (mtd->flags & MTD_ERASE_PARTIAL) {
++		if (partial_start) {
++			part->parent->_write(part->parent,
++				instr->addr, erase_buf_ofs,
++				&wrlen, erase_buf);
++			instr->addr += erase_buf_ofs;
++		} else {
++			instr->len -= erase_buf_ofs;
++			part->parent->_write(part->parent,
++				instr->addr + instr->len,
++				erase_buf_ofs, &wrlen,
++				erase_buf +
++				part->parent->erasesize -
++				erase_buf_ofs);
++		}
++		kfree(erase_buf);
++	}
++
+ 	instr->addr -= part->offset;
+ 
+ 	return ret;
+@@ -526,19 +594,22 @@ static struct mtd_part *allocate_partiti
+ 	remainder = do_div(tmp, wr_alignment);
+ 	if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
+ 		/* Doesn't start on a boundary of major erase size */
+-		/* FIXME: Let it be writable if it is on a boundary of
+-		 * _minor_ erase size though */
+-		slave->mtd.flags &= ~MTD_WRITEABLE;
+-		printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
+-			part->name);
++		slave->mtd.flags |= MTD_ERASE_PARTIAL;
++		if (((u32)slave->mtd.size) > parent->erasesize)
++			slave->mtd.flags &= ~MTD_WRITEABLE;
++		else
++			slave->mtd.erasesize = slave->mtd.size;
+ 	}
+ 
+ 	tmp = part_absolute_offset(parent) + slave->offset + slave->mtd.size;
+ 	remainder = do_div(tmp, wr_alignment);
+ 	if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
+-		slave->mtd.flags &= ~MTD_WRITEABLE;
+-		printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
+-			part->name);
++		slave->mtd.flags |= MTD_ERASE_PARTIAL;
++
++		if ((u32)slave->mtd.size > parent->erasesize)
++			slave->mtd.flags &= ~MTD_WRITEABLE;
++		else
++			slave->mtd.erasesize = slave->mtd.size;
+ 	}
+ 
+ 	mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops);