[rdkb][Common][bsp][Refactor and sync kernel from openwrt]

[Description]
8a990d32 [kernel][mt7986][eth][mediatek-ge: Remove Kconfig option to pass build]
18edade5 [kernel][mt7981][mtd][spinand: Add Etron SPI-NAND support]
c79f5ed5 [kernel][mt7988][eth][fix TDMA Tx monitor]
9f3e8d96 [kernel][common][eth][fix missing DMA tx/rx monitor]
6bb9353b [SW framework][common][bsp][add panic write support for nmbm]
3f7796b4 [SW framework][common][snfi][add panic write support for mtk-snand]
adbd59a5 [Kernel][hnat][Fix log print level in foe_clear_entry]
1b4a861d [kernel][mt7988][eth][i2.5Gphy: Add 20230621 version firmware]
f8eec9df [Kernel][hnat][Fix possibly null pointer and memory leak]
891b26c2 [MAC80211][hnat][Fix patch fail issue]
11c3ac54 [Upgrade kernel version from 5.4.236 to 5.4.246]
00d07458 [kernel][common][eth][Add Airoha EN8811H v1.1.3 2.5G PHY firmware bins]
b23de2f3 [kernel][mt7988][eth][mediatek-ge-soc: Fix VCM K failure]

[Release-log]

Change-Id: Ie610fc884cf39d07d3f8625e0d0106af89dfa001
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/generic/backport-5.4/430-v6.3-ubi-Fix-failure-attaching-when-vid_hdr-offset-equals.patch b/recipes-kernel/linux/linux-mediatek-5.4/generic/backport-5.4/430-v6.3-ubi-Fix-failure-attaching-when-vid_hdr-offset-equals.patch
index afb8e5e..e69de29 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/generic/backport-5.4/430-v6.3-ubi-Fix-failure-attaching-when-vid_hdr-offset-equals.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/generic/backport-5.4/430-v6.3-ubi-Fix-failure-attaching-when-vid_hdr-offset-equals.patch
@@ -1,69 +0,0 @@
-From 1e020e1b96afdecd20680b5b5be2a6ffc3d27628 Mon Sep 17 00:00:00 2001
-From: Zhihao Cheng <chengzhihao1@huawei.com>
-Date: Mon, 6 Mar 2023 09:33:08 +0800
-Subject: [PATCH] ubi: Fix failure attaching when vid_hdr offset equals to
- (sub)page size
-
-Following process will make ubi attaching failed since commit
-1b42b1a36fc946 ("ubi: ensure that VID header offset ... size"):
-
-ID="0xec,0xa1,0x00,0x15" # 128M 128KB 2KB
-modprobe nandsim id_bytes=$ID
-flash_eraseall /dev/mtd0
-modprobe ubi mtd="0,2048"  # set vid_hdr offset as 2048 (one page)
-(dmesg):
-  ubi0 error: ubi_attach_mtd_dev [ubi]: VID header offset 2048 too large.
-  UBI error: cannot attach mtd0
-  UBI error: cannot initialize UBI, error -22
-
-Rework original solution, the key point is making sure
-'vid_hdr_shift + UBI_VID_HDR_SIZE < ubi->vid_hdr_alsize',
-so we should check vid_hdr_shift rather not vid_hdr_offset.
-Then, ubi still support (sub)page aligined VID header offset.
-
-Fixes: 1b42b1a36fc946 ("ubi: ensure that VID header offset ... size")
-Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
-Tested-by: Nicolas Schichan <nschichan@freebox.fr>
-Tested-by: Miquel Raynal <miquel.raynal@bootlin.com> # v5.10, v4.19
-Signed-off-by: Richard Weinberger <richard@nod.at>
----
- drivers/mtd/ubi/build.c | 21 +++++++++++++++------
- 1 file changed, 15 insertions(+), 6 deletions(-)
-
---- a/drivers/mtd/ubi/build.c
-+++ b/drivers/mtd/ubi/build.c
-@@ -644,12 +644,6 @@ static int io_init(struct ubi_device *ub
- 	ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size);
- 	ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size);
- 
--	if (ubi->vid_hdr_offset && ((ubi->vid_hdr_offset + UBI_VID_HDR_SIZE) >
--	    ubi->vid_hdr_alsize)) {
--		ubi_err(ubi, "VID header offset %d too large.", ubi->vid_hdr_offset);
--		return -EINVAL;
--	}
--
- 	dbg_gen("min_io_size      %d", ubi->min_io_size);
- 	dbg_gen("max_write_size   %d", ubi->max_write_size);
- 	dbg_gen("hdrs_min_io_size %d", ubi->hdrs_min_io_size);
-@@ -667,6 +661,21 @@ static int io_init(struct ubi_device *ub
- 						ubi->vid_hdr_aloffset;
- 	}
- 
-+	/*
-+	 * Memory allocation for VID header is ubi->vid_hdr_alsize
-+	 * which is described in comments in io.c.
-+	 * Make sure VID header shift + UBI_VID_HDR_SIZE not exceeds
-+	 * ubi->vid_hdr_alsize, so that all vid header operations
-+	 * won't access memory out of bounds.
-+	 */
-+	if ((ubi->vid_hdr_shift + UBI_VID_HDR_SIZE) > ubi->vid_hdr_alsize) {
-+		ubi_err(ubi, "Invalid VID header offset %d, VID header shift(%d)"
-+			" + VID header size(%zu) > VID header aligned size(%d).",
-+			ubi->vid_hdr_offset, ubi->vid_hdr_shift,
-+			UBI_VID_HDR_SIZE, ubi->vid_hdr_alsize);
-+		return -EINVAL;
-+	}
-+
- 	/* Similar for the data offset */
- 	ubi->leb_start = ubi->vid_hdr_offset + UBI_VID_HDR_SIZE;
- 	ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/generic/backport-5.4/backport-5.4.inc b/recipes-kernel/linux/linux-mediatek-5.4/generic/backport-5.4/backport-5.4.inc
index 0ed63ca..c7ea67c 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/generic/backport-5.4/backport-5.4.inc
+++ b/recipes-kernel/linux/linux-mediatek-5.4/generic/backport-5.4/backport-5.4.inc
@@ -172,7 +172,7 @@
     file://417-v6.2-0001-mtd-core-simplify-a-bit-code-find-partition-matching.patch \
     file://417-v6.2-0002-mtd-core-try-to-find-OF-node-for-every-MTD-partition.patch \
     file://420-v6.2-mtd-parsers-add-TP-Link-SafeLoader-partitions-table-.patch \
-    file://430-v6.3-ubi-Fix-failure-attaching-when-vid_hdr-offset-equals.patch \
+    file://430-v6.3-ubi-Fix-failure-attaching-when-vid_hdr-offset-equals.patch;apply=no \
     file://600-v5.12-net-extract-napi-poll-functionality-to-__napi_poll.patch \
     file://601-v5.12-net-implement-threaded-able-napi-poll-loop-support.patch \
     file://602-v5.12-net-add-sysfs-attribute-to-control-napi-threaded-mod.patch \
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/generic/files-5.4/drivers/mtd/nmbm/nmbm-core.c b/recipes-kernel/linux/linux-mediatek-5.4/generic/files-5.4/drivers/mtd/nmbm/nmbm-core.c
index b80ea42..9dc4a10 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/generic/files-5.4/drivers/mtd/nmbm/nmbm-core.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/generic/files-5.4/drivers/mtd/nmbm/nmbm-core.c
@@ -257,6 +257,37 @@
 }
 
 /*
+ * nmbm_panic_write_phys_page - Panic write page with retry
+ * @ni: NMBM instance structure
+ * @addr: linear address where the data will be written to
+ * @data: the main data to be written
+ *
+ * Write a page for at most NMBM_TRY_COUNT times.
+ */
+static bool nmbm_panic_write_phys_page(struct nmbm_instance *ni, uint64_t addr,
+				       const void *data)
+{
+	int tries, ret;
+
+	if (ni->lower.flags & NMBM_F_READ_ONLY) {
+		nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr);
+		return false;
+	}
+
+	for (tries = 0; tries < NMBM_TRY_COUNT; tries++) {
+		ret = ni->lower.panic_write_page(ni->lower.arg, addr, data);
+		if (!ret)
+			return true;
+
+		nmbm_reset_chip(ni);
+	}
+
+	nlog_err(ni, "Panic page write failed at address 0x%08llx\n", addr);
+
+	return false;
+}
+
+/*
  * nmbm_erase_phys_block - Erase a block with retry
  * @ni: NMBM instance structure
  * @addr: Linear address
@@ -2730,6 +2761,56 @@
 }
 
 /*
+ * nmbm_panic_write_logic_page - Panic write page based on logic address
+ * @ni: NMBM instance structure
+ * @addr: logic linear address
+ * @data: buffer contains main data. optional.
+ */
+static int nmbm_panic_write_logic_page(struct nmbm_instance *ni, uint64_t addr,
+				       const void *data)
+{
+	uint32_t lb, pb, offset;
+	uint64_t paddr;
+	bool success;
+
+	if (!ni->lower.panic_write_page)
+		return -ENOTSUPP;
+
+	/* Extract block address and in-block offset */
+	lb = addr2ba(ni, addr);
+	offset = addr & ni->erasesize_mask;
+
+	/* Map logic block to physical block */
+	pb = ni->block_mapping[lb];
+
+	/* Whether the logic block is good (has valid mapping) */
+	if ((int32_t)pb < 0) {
+		nlog_debug(ni, "Logic block %u is a bad block\n", lb);
+		return -EIO;
+	}
+
+	/* Fail if physical block is marked bad */
+	if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
+		return -EIO;
+
+	/* Assemble new address */
+	paddr = ba2addr(ni, pb) + offset;
+
+	success = nmbm_panic_write_phys_page(ni, paddr, data);
+	if (success)
+		return 0;
+
+	/*
+	 * Do not remap bad block here. Just mark this block in state table.
+	 * Remap this block on erasing.
+	 */
+	nmbm_set_block_state(ni, pb, BLOCK_ST_NEED_REMAP);
+	nmbm_update_info_table(ni);
+
+	return -EIO;
+}
+
+/*
  * nmbm_write_single_page - Write one page based on logic address
  * @ni: NMBM instance structure
  * @addr: logic linear address
@@ -2759,6 +2840,32 @@
 }
 
 /*
+ * nmbm_panic_write_single_page - Panic write one page based on logic address
+ * @ni: NMBM instance structure
+ * @addr: logic linear address
+ * @data: buffer contains main data. optional.
+ */
+int nmbm_panic_write_single_page(struct nmbm_instance *ni, uint64_t addr,
+				 const void *data)
+{
+	if (!ni)
+		return -EINVAL;
+
+	/* Sanity check */
+	if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
+		nlog_debug(ni, "Device is forced read-only\n");
+		return -EROFS;
+	}
+
+	if (addr >= ba2addr(ni, ni->data_block_count)) {
+		nlog_err(ni, "Address 0x%llx is invalid\n", addr);
+		return -EINVAL;
+	}
+
+	return nmbm_panic_write_logic_page(ni, addr, data);
+}
+
+/*
  * nmbm_write_range - Write data without oob
  * @ni: NMBM instance structure
  * @addr: logic linear address
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/generic/files-5.4/drivers/mtd/nmbm/nmbm-mtd.c b/recipes-kernel/linux/linux-mediatek-5.4/generic/files-5.4/drivers/mtd/nmbm/nmbm-mtd.c
index a3e9e18..94413a3 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/generic/files-5.4/drivers/mtd/nmbm/nmbm-mtd.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/generic/files-5.4/drivers/mtd/nmbm/nmbm-mtd.c
@@ -141,6 +141,16 @@
 	return mtd_write_oob(nm->lower, addr, &ops);
 }
 
+static int nmbm_lower_panic_write_page(void *arg, uint64_t addr,
+				       const void *buf)
+{
+	struct nmbm_mtd *nm = arg;
+	size_t retlen;
+
+	return mtd_panic_write(nm->lower, addr, nm->lower->writesize, &retlen,
+			       buf);
+}
+
 static int nmbm_lower_erase_block(void *arg, uint64_t addr)
 {
 	struct nmbm_mtd *nm = arg;
@@ -538,6 +548,44 @@
 	return ret;
 }
 
+static int nmbm_mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+				size_t *retlen, const u_char *buf)
+{
+	struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
+	size_t chklen, wrlen = 0;
+	uint32_t col;
+	int ret;
+
+	col = to & nm->lower->writesize_mask;
+	to &= ~nm->lower->writesize_mask;
+
+	while (len) {
+		/* Move data */
+		chklen = nm->lower->writesize - col;
+		if (chklen > len)
+			chklen = len;
+
+		if (chklen < nm->lower->writesize)
+			memset(nm->page_cache, 0xff, nm->lower->writesize);
+		memcpy(nm->page_cache + col, buf + wrlen, chklen);
+
+		len -= chklen;
+		col = 0; /* (col + chklen) %  */
+		wrlen += chklen;
+
+		ret = nmbm_panic_write_single_page(nm->ni, to, nm->page_cache);
+		if (ret)
+			break;
+
+		to += nm->lower->writesize;
+	}
+
+	if (retlen)
+		*retlen = wrlen;
+
+	return 0;
+}
+
 static int nmbm_mtd_block_isbad(struct mtd_info *mtd, loff_t offs)
 {
 	struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
@@ -634,6 +682,7 @@
 
 	nld.read_page = nmbm_lower_read_page;
 	nld.write_page = nmbm_lower_write_page;
+	nld.panic_write_page = nmbm_lower_panic_write_page;
 	nld.erase_block = nmbm_lower_erase_block;
 	nld.is_bad_block = nmbm_lower_is_bad_block;
 	nld.mark_bad_block = nmbm_lower_mark_bad_block;
@@ -697,6 +746,7 @@
 	mtd->_erase = nmbm_mtd_erase;
 	mtd->_read_oob = nmbm_mtd_read_oob;
 	mtd->_write_oob = nmbm_mtd_write_oob;
+	mtd->_panic_write = nmbm_mtd_panic_write;
 	mtd->_block_isbad = nmbm_mtd_block_isbad;
 	mtd->_block_markbad = nmbm_mtd_block_markbad;
 	mtd->_reboot = nmbm_mtd_shutdown;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/generic/files-5.4/include/nmbm/nmbm.h b/recipes-kernel/linux/linux-mediatek-5.4/generic/files-5.4/include/nmbm/nmbm.h
index c040098..0f43466 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/generic/files-5.4/include/nmbm/nmbm.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/generic/files-5.4/include/nmbm/nmbm.h
@@ -52,6 +52,7 @@
 	 */
 	int (*read_page)(void *arg, uint64_t addr, void *buf, void *oob, enum nmbm_oob_mode mode);
 	int (*write_page)(void *arg, uint64_t addr, const void *buf, const void *oob, enum nmbm_oob_mode mode);
+	int (*panic_write_page)(void *arg, uint64_t addr, const void *buf);
 	int (*erase_block)(void *arg, uint64_t addr);
 
 	int (*is_bad_block)(void *arg, uint64_t addr);
@@ -88,6 +89,8 @@
 int nmbm_write_single_page(struct nmbm_instance *ni, uint64_t addr,
 			   const void *data, const void *oob,
 			   enum nmbm_oob_mode mode);
+int nmbm_panic_write_single_page(struct nmbm_instance *ni, uint64_t addr,
+				 const void *data);
 int nmbm_write_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
 		     const void *data, enum nmbm_oob_mode mode,
 		     size_t *retlen);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-mtd.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-mtd.c
index 09dc34d..61d75d3 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-mtd.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-mtd.c
@@ -352,6 +352,50 @@
 	return ret;
 }
 
+static int mtk_snand_mtd_panic_write(struct mtd_info *mtd, loff_t to,
+				     size_t len, size_t *retlen,
+				     const u_char *buf)
+{
+	struct mtk_snand_mtd *msm = mtd_to_msm(mtd);
+	size_t chklen, wrlen = 0;
+	uint32_t col;
+	int ret;
+
+	/* Disable IRQ and enter poll mode */
+	disable_irq(msm->irq);
+	mtk_snand_control_poll_mode(&msm->pdev, true);
+
+	col = to & mtd->writesize_mask;
+	to &= ~mtd->writesize_mask;
+
+	while (len) {
+		/* Move data */
+		chklen = mtd->writesize - col;
+		if (chklen > len)
+			chklen = len;
+
+		if (chklen < mtd->writesize)
+			memset(msm->page_cache, 0xff, mtd->writesize);
+		memcpy(msm->page_cache + col, buf + wrlen, chklen);
+
+		len -= chklen;
+		col = 0; /* (col + chklen) %  */
+		wrlen += chklen;
+
+		ret = mtk_snand_write_page(msm->snf, to, msm->page_cache,
+					   NULL, false);
+		if (ret)
+			break;
+
+		to += mtd->writesize;
+	}
+
+	if (retlen)
+		*retlen = wrlen;
+
+	return ret;
+}
+
 static int mtk_snand_mtd_block_isbad(struct mtd_info *mtd, loff_t offs)
 {
 	struct mtk_snand_mtd *msm = mtd_to_msm(mtd);
@@ -663,6 +707,7 @@
 	mtd->_erase = mtk_snand_mtd_erase;
 	mtd->_read_oob = mtk_snand_mtd_read_oob;
 	mtd->_write_oob = mtk_snand_mtd_write_oob;
+	mtd->_panic_write = mtk_snand_mtd_panic_write;
 	mtd->_block_isbad = mtk_snand_mtd_block_isbad;
 	mtd->_block_markbad = mtk_snand_mtd_block_markbad;
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.c
index 0c3ffec..416a6a2 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.c
@@ -46,3 +46,15 @@
 
 	return 0;
 }
+
+void mtk_snand_control_poll_mode(struct mtk_snand_plat_dev *pdev, bool enable)
+{
+	if (!pdev)
+		return;
+
+	if (pdev->poll_mode != enable)
+		dev_info(pdev->dev, "Poll mode %s\b", enable ? "enabled" :
+			 "disabled");
+
+	pdev->poll_mode = enable;
+}
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.h
index 223f73f..b392f34 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/mtd/mtk-snand/mtk-snand-os.h
@@ -24,6 +24,7 @@
 struct mtk_snand_plat_dev {
 	struct device *dev;
 	struct completion done;
+	bool poll_mode;
 };
 
 /* Polling helpers */
@@ -102,32 +103,34 @@
 /* Interrupt helpers */
 static inline void irq_completion_done(struct mtk_snand_plat_dev *pdev)
 {
-	complete(&pdev->done);
+	if (!pdev->poll_mode)
+		complete(&pdev->done);
 }
 
 static inline void irq_completion_init(struct mtk_snand_plat_dev *pdev)
 {
-	init_completion(&pdev->done);
+	if (!pdev->poll_mode)
+		init_completion(&pdev->done);
 }
 
 static inline int irq_completion_wait(struct mtk_snand_plat_dev *pdev,
 				       void __iomem *reg, uint32_t bit,
 				       uint32_t timeout_us)
 {
-#if 0
 	uint32_t val;
-
-	return read32_poll_timeout(reg, val, val & bit, 0, timeout_us);
-#else
 	int ret;
 
+	if (pdev->poll_mode)
+		return read32_poll_timeout(reg, val, val & bit, 0, timeout_us);
+
 	ret = wait_for_completion_timeout(&pdev->done,
 					  usecs_to_jiffies(timeout_us));
 	if (!ret)
 		return -ETIMEDOUT;
 
 	return 0;
-#endif
 }
 
+void mtk_snand_control_poll_mode(struct mtk_snand_plat_dev *pdev, bool enable);
+
 #endif /* _MTK_SNAND_OS_H_ */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
index 1b5d356..a1db345 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
@@ -1124,6 +1124,24 @@
 	.release = single_release
 };
 
+static int mtk_rss_set_indr_tbl(struct mtk_eth *eth, int num)
+{
+	struct mtk_rss_params *rss_params = &eth->rss_params;
+	u32 i;
+
+	if (num <= 0 || num > MTK_RX_NAPI_NUM)
+		return -EOPNOTSUPP;
+
+	for (i = 0; i < MTK_RSS_MAX_INDIRECTION_TABLE; i++)
+		rss_params->indirection_table[i] = i % num;
+
+	for (i = 0; i < MTK_RSS_MAX_INDIRECTION_TABLE / 16; i++)
+		mtk_w32(eth, mtk_rss_indr_table(rss_params, i),
+			MTK_RSS_INDR_TABLE_DW(i));
+
+	return 0;
+}
+
 ssize_t rss_ctrl_write(struct file *file, const char __user *buffer,
 		       size_t count, loff_t *data)
 {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
index 66b4646..1042685 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
@@ -431,22 +431,29 @@
 u32 mtk_monitor_tdma_tx(struct mtk_eth *eth)
 {
 	static u32 err_cnt_ttx;
+	static u32 pre_ipq10;
 	static u32 pre_fsm;
 	u32 err_flag = 0;
 	u32 cur_fsm = 0;
 	u32 tx_busy = 0;
+	u32 ipq10;
 
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
+		ipq10 = mtk_r32(eth, MTK_PSE_IQ_STA(6)) & 0xFFF;
 		cur_fsm = (mtk_r32(eth, MTK_FE_CDM6_FSM) & 0x1FFF) != 0;
 		tx_busy = ((mtk_r32(eth, MTK_TDMA_GLO_CFG) & 0x2) != 0);
 
-		if (cur_fsm == pre_fsm && cur_fsm != 0 && tx_busy) {
+		if (ipq10 && cur_fsm && tx_busy
+		    && cur_fsm == pre_fsm
+		    && ipq10 == pre_ipq10) {
 			err_cnt_ttx++;
 			if (err_cnt_ttx >= 3) {
 				pr_info("TDMA Tx Info\n");
 				pr_info("err_cnt = %d", err_cnt_ttx);
-				pr_info("CDM6_FSM = %d\n",
-					mtk_r32(eth, MTK_FE_CDM6_FSM));
+				pr_info("CDM6_FSM = 0x%x, PRE_CDM6_FSM = 0x%x\n",
+					mtk_r32(eth, MTK_FE_CDM6_FSM), pre_fsm);
+				pr_info("PSE_IQ_P10 = 0x%x, PRE_PSE_IQ_P10 = 0x%x\n",
+					mtk_r32(eth, MTK_PSE_IQ_STA(6)), pre_ipq10);
 				pr_info("DMA CFG = 0x%x\n",
 					mtk_r32(eth, MTK_TDMA_GLO_CFG));
 				pr_info("==============================\n");
@@ -456,6 +463,7 @@
 			err_cnt_ttx = 0;
 
 		pre_fsm = cur_fsm;
+		pre_ipq10 = ipq10;
 	}
 
 	if (err_flag)
@@ -515,7 +523,7 @@
 	struct mtk_eth *eth = from_timer(eth, t, mtk_dma_monitor_timer);
 	u32 i = 0, ret = 0;
 
-	for (i = 0; i < 6; i++) {
+	for (i = 0; i < ARRAY_SIZE(mtk_reset_monitor_func); i++) {
 		ret = (*mtk_reset_monitor_func[i]) (eth);
 		if ((ret == MTK_FE_START_RESET) ||
 			(ret == MTK_FE_STOP_TRAFFIC)) {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 78ac67e..0c94e93 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -3015,27 +3015,33 @@
 	return 0;
 }
 
-int mtk_rss_set_indr_tbl(struct mtk_eth *eth, int num)
+u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index)
 {
-	u32 i, config;
-
-	if (num <= 0 || num > MTK_RX_NAPI_NUM)
-		return -EOPNOTSUPP;
-
-	for (i = 0, config = 0; i < 16; i++) {
-		config <<= 2;
-		config |= (i % num);
-	}
+	u32 val = 0;
+	int i;
 
-	for (i = 0; i < 8; i++)
-		mtk_w32(eth, config, MTK_RSS_INDR_TABLE_DW(i));
+	for (i = 16 * index; i < 16 * index + 16; i++)
+		val |= (rss_params->indirection_table[i] << (2 * (i % 16)));
 
-	return 0;
+	return val;
 }
 
 static int mtk_rss_init(struct mtk_eth *eth)
 {
+	struct mtk_rss_params *rss_params = &eth->rss_params;
+	static u8 hash_key[MTK_RSS_HASH_KEYSIZE] = {
+		0xfa, 0x01, 0xac, 0xbe, 0x3b, 0xb7, 0x42, 0x6a,
+		0x0c, 0xf2, 0x30, 0x80, 0xa3, 0x2d, 0xcb, 0x77,
+		0xb4, 0x30, 0x7b, 0xae, 0xcb, 0x2b, 0xca, 0xd0,
+		0xb0, 0x8f, 0xa3, 0x43, 0x3d, 0x25, 0x67, 0x41,
+		0xc2, 0x0e, 0x5b, 0x25, 0xda, 0x56, 0x5a, 0x6d};
 	u32 val;
+	int i;
+
+	memcpy(rss_params->hash_key, hash_key, MTK_RSS_HASH_KEYSIZE);
+
+	for (i = 0; i < MTK_RSS_MAX_INDIRECTION_TABLE; i++)
+		rss_params->indirection_table[i] = i % eth->soc->rss_num;
 
 	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) {
 		/* Set RSS rings to PSE modes */
@@ -3062,8 +3068,14 @@
 	val |= MTK_RSS_IPV6_STATIC_HASH;
 	mtk_w32(eth, val, MTK_PDMA_RSS_GLO_CFG);
 
+	/* Hash Key */
+	for (i = 0; i < MTK_RSS_HASH_KEYSIZE / sizeof(u32); i++)
+		mtk_w32(eth, rss_params->hash_key[i], MTK_RSS_HASH_KEY_DW(i));
+
 	/* Select the size of indirection table */
-	mtk_rss_set_indr_tbl(eth, eth->soc->rss_num);
+	for (i = 0; i < MTK_RSS_MAX_INDIRECTION_TABLE / 16; i++)
+		mtk_w32(eth, mtk_rss_indr_table(rss_params, i),
+			MTK_RSS_INDR_TABLE_DW(i));
 
 	/* Pause */
 	val |= MTK_RSS_CFG_REQ;
@@ -4255,6 +4267,8 @@
 static int mtk_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
 			 u32 *rule_locs)
 {
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct mtk_eth *eth = mac->hw;
 	int ret = -EOPNOTSUPP;
 
 	switch (cmd->cmd) {
@@ -4262,12 +4276,13 @@
 		if (dev->hw_features & NETIF_F_LRO) {
 			cmd->data = MTK_MAX_RX_RING_NUM;
 			ret = 0;
+		} else if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSS)) {
+			cmd->data = eth->soc->rss_num;
+			ret = 0;
 		}
 		break;
 	case ETHTOOL_GRXCLSRLCNT:
 		if (dev->hw_features & NETIF_F_LRO) {
-			struct mtk_mac *mac = netdev_priv(dev);
-
 			cmd->rule_cnt = mac->hwlro_ip_cnt;
 			ret = 0;
 		}
@@ -4308,6 +4323,73 @@
 	return ret;
 }
 
+static u32 mtk_get_rxfh_key_size(struct net_device *dev)
+{
+	return MTK_RSS_HASH_KEYSIZE;
+}
+
+static u32 mtk_get_rxfh_indir_size(struct net_device *dev)
+{
+	return MTK_RSS_MAX_INDIRECTION_TABLE;
+}
+
+static int mtk_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
+			u8 *hfunc)
+{
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct mtk_eth *eth = mac->hw;
+	struct mtk_rss_params *rss_params = &eth->rss_params;
+	int i;
+
+	if (hfunc)
+		*hfunc = ETH_RSS_HASH_TOP;	/* Toeplitz */
+
+	if (key) {
+		memcpy(key, rss_params->hash_key,
+		       sizeof(rss_params->hash_key));
+	}
+
+	if (indir) {
+		for (i = 0; i < MTK_RSS_MAX_INDIRECTION_TABLE; i++)
+			indir[i] = rss_params->indirection_table[i];
+	}
+
+	return 0;
+}
+
+static int mtk_set_rxfh(struct net_device *dev, const u32 *indir,
+			const u8 *key, const u8 hfunc)
+{
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct mtk_eth *eth = mac->hw;
+	struct mtk_rss_params *rss_params = &eth->rss_params;
+	int i;
+
+	if (hfunc != ETH_RSS_HASH_NO_CHANGE &&
+	    hfunc != ETH_RSS_HASH_TOP)
+		return -EOPNOTSUPP;
+
+	if (key) {
+		memcpy(rss_params->hash_key, key,
+		       sizeof(rss_params->hash_key));
+
+		for (i = 0; i < MTK_RSS_HASH_KEYSIZE / sizeof(u32); i++)
+			mtk_w32(eth, rss_params->hash_key[i],
+				MTK_RSS_HASH_KEY_DW(i));
+	}
+
+	if (indir) {
+		for (i = 0; i < MTK_RSS_MAX_INDIRECTION_TABLE; i++)
+			rss_params->indirection_table[i] = indir[i];
+
+		for (i = 0; i < MTK_RSS_MAX_INDIRECTION_TABLE / 16; i++)
+			mtk_w32(eth, mtk_rss_indr_table(rss_params, i),
+				MTK_RSS_INDR_TABLE_DW(i));
+	}
+
+	return 0;
+}
+
 static void mtk_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause)
 {
 	struct mtk_mac *mac = netdev_priv(dev);
@@ -4381,6 +4463,10 @@
 	.get_ethtool_stats	= mtk_get_ethtool_stats,
 	.get_rxnfc		= mtk_get_rxnfc,
 	.set_rxnfc              = mtk_set_rxnfc,
+	.get_rxfh_key_size	= mtk_get_rxfh_key_size,
+	.get_rxfh_indir_size	= mtk_get_rxfh_indir_size,
+	.get_rxfh		= mtk_get_rxfh,
+	.set_rxfh		= mtk_set_rxfh,
 	.get_pauseparam		= mtk_get_pauseparam,
 	.set_pauseparam		= mtk_set_pauseparam,
 	.get_eee		= mtk_get_eee,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 01e15fe..181c56a 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -65,6 +65,9 @@
 #define	MTK_HW_LRO_REPLACE_DELTA	1000
 #define	MTK_HW_LRO_SDL_REMAIN_ROOM	1522
 
+#define MTK_RSS_HASH_KEYSIZE		40
+#define MTK_RSS_MAX_INDIRECTION_TABLE	128
+
 /* Frame Engine Global Configuration */
 #define MTK_FE_GLO_CFG		0x00
 #define MTK_FE_LINK_DOWN_P3	BIT(11)
@@ -267,6 +270,8 @@
 #define MTK_RSS_CFG_REQ			BIT(2)
 #define MTK_RSS_IPV6_STATIC_HASH	(0x7 << 8)
 #define MTK_RSS_IPV4_STATIC_HASH	(0x7 << 12)
+#define MTK_RSS_HASH_KEY_DW(x)		(MTK_PDMA_RSS_GLO_CFG + 0x20 +	\
+					 ((x) * 0x4))
 #define MTK_RSS_INDR_TABLE_DW(x)	(MTK_PDMA_RSS_GLO_CFG + 0x50 +	\
 					 ((x) * 0x4))
 
@@ -1325,6 +1330,18 @@
 	u32 ring_no;
 };
 
+/* struct mtk_rss_params -	This is the structure holding parameters
+				for the RSS ring
+ * @hash_key			The element is used to record the
+				secret key for the RSS ring
+ * indirection_table		The element is used to record the
+				indirection table for the RSS ring
+ */
+struct mtk_rss_params {
+	u32		hash_key[MTK_RSS_HASH_KEYSIZE / sizeof(u32)];
+	u8		indirection_table[MTK_RSS_MAX_INDIRECTION_TABLE];
+};
+
 /* struct mtk_napi -	This is the structure holding NAPI-related information,
  *			and a mtk_napi struct is binding to one interrupt group
  * @napi:		The NAPI struct
@@ -1787,6 +1804,7 @@
 	struct mtk_rx_ring		rx_ring_qdma;
 	struct napi_struct		tx_napi;
 	struct mtk_napi			rx_napi[MTK_RX_NAPI_NUM];
+	struct mtk_rss_params		rss_params;
 	void				*scratch_ring;
 	struct mtk_reset_event		reset_event;
 	dma_addr_t			phy_scratch_ring;
@@ -1864,5 +1882,5 @@
 int mtk_dump_usxgmii(struct regmap *pmap, char *name, u32 offset, u32 range);
 
 void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
-int mtk_rss_set_indr_tbl(struct mtk_eth *eth, int num);
+u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index);
 #endif /* MTK_ETH_H */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
index ac266b7..63315df 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
@@ -297,8 +297,6 @@
 				*((u16 *)&h_dest[4]) =
 					swab16(entry->ipv4_hnapt.dmac_lo);
 				if (strncmp(h_dest, neigh->ha, ETH_ALEN) != 0) {
-					pr_info("%s: state=%d\n", __func__,
-						neigh->nud_state);
 					cr_set_field(hnat_priv->ppe_base[i] + PPE_TB_CFG,
 						     SMA, SMA_ONLY_FWD_CPU);
 
@@ -312,9 +310,13 @@
 					mod_timer(&hnat_priv->hnat_sma_build_entry_timer,
 						  jiffies + 3 * HZ);
 
-					pr_info("Delete old entry: dip =%pI4\n", &dip);
-					pr_info("Old mac= %pM\n", h_dest);
-					pr_info("New mac= %pM\n", neigh->ha);
+					if (debug_level >= 7) {
+						pr_info("%s: state=%d\n", __func__,
+							neigh->nud_state);
+						pr_info("Delete old entry: dip =%pI4\n", &dip);
+						pr_info("Old mac= %pM\n", h_dest);
+						pr_info("New mac= %pM\n", neigh->ha);
+					}
 				}
 			}
 		}
@@ -2794,13 +2796,15 @@
 mtk_hnat_ipv4_nf_local_out(void *priv, struct sk_buff *skb,
 			   const struct nf_hook_state *state)
 {
-	struct sk_buff *new_skb;
 	struct foe_entry *entry;
 	struct iphdr *iph;
 
 	if (!is_magic_tag_valid(skb))
 		return NF_ACCEPT;
 
+	if (unlikely(skb_headroom(skb) < FOE_INFO_LEN))
+		return NF_ACCEPT;
+
 	if (!skb_hnat_is_hashed(skb))
 		return NF_ACCEPT;
 
@@ -2810,16 +2814,6 @@
 
 	entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)];
 
-	if (unlikely(skb_headroom(skb) < FOE_INFO_LEN)) {
-		new_skb = skb_realloc_headroom(skb, FOE_INFO_LEN);
-		if (!new_skb) {
-			dev_info(hnat_priv->dev, "%s:drop\n", __func__);
-			return NF_DROP;
-		}
-		dev_kfree_skb(skb);
-		skb = new_skb;
-	}
-
 	/* Make the flow from local not be bound. */
 	iph = ip_hdr(skb);
 	if (iph->protocol == IPPROTO_IPV6) {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-ge-soc.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-ge-soc.c
index 489e524..40a5e0f 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-ge-soc.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/phy/mediatek-ge-soc.c
@@ -679,7 +679,7 @@
 			  MTK_PHY_DA_RX_PSBN_LP_MASK,
 			  upper_idx << 12 | upper_idx << 8 |
 			  upper_idx << 4 | upper_idx);
-		phydev_dbg(phydev, "TX-VCM SW cal result: 0x%x\n", upper_idx);
+		phydev_info(phydev, "TX-VCM SW cal result: 0x%x\n", upper_idx);
 	} else if (lower_idx == TXRESERVE_MIN && upper_ret == 1 &&
 		   lower_ret == 1) {
 		ret = 0;
@@ -845,10 +845,6 @@
 	/* TCT finetune */
 	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5);
 
-	/* Disable TX power saving */
-	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7,
-		       MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8);
-
 	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
 	/* ResetSyncOffset = 5 */
 	__phy_write(phydev, 0x11, 0x500);
@@ -1137,7 +1133,7 @@
 	mt798x_phy_common_finetune(phydev);
 	mt798x_phy_eee(phydev);
 
-	return mt798x_phy_calibration(phydev);
+	return 0;
 }
 
 static int mt7988_phy_setup_led(struct phy_device *phydev)
@@ -1229,6 +1225,13 @@
 			return err;
 	}
 
+	/* Disable TX power saving at probing to:
+	 * 1. Meet common mode compliance test criteria
+	 * 2. Make sure that TX-VCM calibration works fine
+	 */
+	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7,
+		       MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8);
+
 	mt7988_phy_setup_led(phydev);
 
 	return mt798x_phy_calibration(phydev);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3007-flow-offload-add-mkhnat-dual-ppe-new-v2.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3007-flow-offload-add-mkhnat-dual-ppe-new-v2.patch
index 954c6e2..ee370b6 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3007-flow-offload-add-mkhnat-dual-ppe-new-v2.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3007-flow-offload-add-mkhnat-dual-ppe-new-v2.patch
@@ -174,7 +174,7 @@
  int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
  		     void *type_data);
  void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
- int mtk_rss_set_indr_tbl(struct mtk_eth *eth, int num);
+ u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index);
 +
 +int mtk_ppe_debugfs_init(struct mtk_eth *eth);
  #endif /* MTK_ETH_H */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7986.cfg b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7986.cfg
index 59aa655..2f3f416 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7986.cfg
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7986.cfg
@@ -302,7 +302,7 @@
 CONFIG_MDIO_DEVICE=y
 # CONFIG_MEDIATEK_2P5GE_PHY is not set
 # CONFIG_MEDIATEK_GE_PHY is not set
-# CONFIG_MEDIATEK_GE_PHY_SOC is not set
+# CONFIG_MEDIATEK_GE_SOC_PHY is not set
 CONFIG_MEDIATEK_MT6577_AUXADC=y
 CONFIG_MEDIATEK_NETSYS_V2=y
 # CONFIG_MEDIATEK_NETSYS_V3 is not set
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg
index cb07304..d661a18 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg
@@ -514,3 +514,6 @@
 CONFIG_ZLIB_DEFLATE=y
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZONE_DMA32=y
+# CONFIG_BPF_KPROBE_OVERRIDE is not set
+# CONFIG_HIST_TRIGGERS is not set
+# CONFIG_FUNCTION_ERROR_INJECTION is not set
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2341-mtd-spinand-Add-support-etron.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2341-mtd-spinand-Add-support-etron.patch
new file mode 100644
index 0000000..68a6f12
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2341-mtd-spinand-Add-support-etron.patch
@@ -0,0 +1,234 @@
+Index: a/drivers/mtd/nand/spi/Makefile
+===================================================================
+--- a/drivers/mtd/nand/spi/Makefile
++++ b/drivers/mtd/nand/spi/Makefile
+@@ -1,3 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0
+-spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o
++spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o etron.o
+ obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
+Index: a/drivers/mtd/nand/spi/core.c
+===================================================================
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -754,6 +754,7 @@ static const struct nand_ops spinand_ops
+ };
+ 
+ static const struct spinand_manufacturer *spinand_manufacturers[] = {
++	&etron_spinand_manufacturer,
+ 	&gigadevice_spinand_manufacturer,
+ 	&macronix_spinand_manufacturer,
+ 	&micron_spinand_manufacturer,
+Index: a/drivers/mtd/nand/spi/etron.c
+===================================================================
+--- /dev/null
++++ b/drivers/mtd/nand/spi/etron.c
+@@ -0,0 +1,196 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2020 Etron Technology, Inc.
++ *
++ */
++
++#include <linux/device.h>
++#include <linux/kernel.h>
++#include <linux/mtd/spinand.h>
++
++#define SPINAND_MFR_ETRON		0xD5
++
++#define STATUS_ECC_LIMIT_BITFLIPS (3 << 4)
++
++static SPINAND_OP_VARIANTS(read_cache_variants,
++		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
++		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
++		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
++		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
++		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
++		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
++
++static SPINAND_OP_VARIANTS(write_cache_variants,
++		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
++		SPINAND_PROG_LOAD(true, 0, NULL, 0));
++
++static SPINAND_OP_VARIANTS(update_cache_variants,
++		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
++		SPINAND_PROG_LOAD(false, 0, NULL, 0));
++
++static int etron_ooblayout_ecc(struct mtd_info *mtd, int section,
++				  struct mtd_oob_region *region)
++{
++	if (section > 3)
++		return -ERANGE;
++
++	region->offset = (14 * section) + 72;
++	region->length = 14;
++
++	return 0;
++}
++
++static int etron_ooblayout_free(struct mtd_info *mtd, int section,
++				   struct mtd_oob_region *region)
++{
++	if (section > 3)
++		return -ERANGE;
++
++	if (section) {
++		region->offset = 18 * section;
++		region->length = 18;
++	} else {
++		/* section 0 has one byte reserved for bad block mark */
++		region->offset = 2;
++		region->length = 16;
++	}
++
++	return 0;
++}
++
++static const struct mtd_ooblayout_ops etron_ooblayout = {
++	.ecc = etron_ooblayout_ecc,
++	.free = etron_ooblayout_free,
++};
++
++static int etron_ecc_get_status(struct spinand_device *spinand,
++				   u8 status)
++{
++	switch (status & STATUS_ECC_MASK) {
++	case STATUS_ECC_NO_BITFLIPS:
++		return 0;
++
++	case STATUS_ECC_UNCOR_ERROR:
++		return -EBADMSG;
++
++	case STATUS_ECC_HAS_BITFLIPS:
++		return 1;
++
++	case STATUS_ECC_LIMIT_BITFLIPS:
++		return 3;
++
++
++	default:
++		break;
++	}
++
++	return -EINVAL;
++}
++
++static const struct spinand_info etron_spinand_table[] = {
++	/* EM73C 1Gb 3.3V */
++	SPINAND_INFO("EM73C044VCF",
++		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x25),
++		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
++		     NAND_ECCREQ(4, 512),
++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++					      &write_cache_variants,
++					      &update_cache_variants),
++		     SPINAND_HAS_QE_BIT,
++		     SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)),
++	/* EM7xD 2Gb */
++	SPINAND_INFO("EM73D044VCR",
++		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x41),
++		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
++		     NAND_ECCREQ(4, 512),
++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++					      &write_cache_variants,
++					      &update_cache_variants),
++		     SPINAND_HAS_QE_BIT,
++		     SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)),
++	SPINAND_INFO("EM73D044VCO",
++		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x3A),
++		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
++		     NAND_ECCREQ(8, 512),
++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++					      &write_cache_variants,
++					      &update_cache_variants),
++		     SPINAND_HAS_QE_BIT,
++		     SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)),
++	SPINAND_INFO("EM78D044VCM",
++		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x8E),
++		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
++		     NAND_ECCREQ(8, 512),
++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++					      &write_cache_variants,
++					      &update_cache_variants),
++		     SPINAND_HAS_QE_BIT,
++		     SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)),
++	/* EM7xE 4Gb */
++	SPINAND_INFO("EM73E044VCE",
++		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x3B),
++		     NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
++		     NAND_ECCREQ(8, 512),
++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++					      &write_cache_variants,
++					      &update_cache_variants),
++		     SPINAND_HAS_QE_BIT,
++		     SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)),
++	SPINAND_INFO("EM78E044VCD",
++		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x8F),
++		     NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
++		     NAND_ECCREQ(8, 512),
++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++					      &write_cache_variants,
++					      &update_cache_variants),
++		     SPINAND_HAS_QE_BIT,
++		     SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)),
++	/* EM7xF044VCA 8Gb */
++	SPINAND_INFO("EM73F044VCA",
++		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15),
++		     NAND_MEMORG(1, 4096, 256, 64, 4096, 80, 1, 1, 1),
++		     NAND_ECCREQ(8, 512),
++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++					      &write_cache_variants,
++					      &update_cache_variants),
++		     SPINAND_HAS_QE_BIT,
++		     SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)),
++	SPINAND_INFO("EM78F044VCA",
++		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x8D),
++		     NAND_MEMORG(1, 4096, 256, 64, 4096, 80, 1, 1, 1),
++		     NAND_ECCREQ(8, 512),
++		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++					      &write_cache_variants,
++					      &update_cache_variants),
++		     SPINAND_HAS_QE_BIT,
++		     SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)),
++};
++
++static int etron_spinand_detect(struct spinand_device *spinand)
++{
++	u8 *id = spinand->id.data;
++	int ret;
++
++	if (id[1] != SPINAND_MFR_ETRON)
++		return 0;
++
++	ret = spinand_match_and_init(spinand, etron_spinand_table,
++				     ARRAY_SIZE(etron_spinand_table),
++				     id[2]);
++	if (ret)
++		return ret;
++
++	return 1;
++}
++
++static const struct spinand_manufacturer_ops etron_spinand_manuf_ops = {
++};
++
++const struct spinand_manufacturer etron_spinand_manufacturer = {
++	.id = SPINAND_MFR_ETRON,
++	.name = "Etron",
++	.chips = etron_spinand_table,
++	.nchips = ARRAY_SIZE(etron_spinand_table),
++	.ops = &etron_spinand_manuf_ops,
++};
++
+Index: a/include/linux/mtd/spinand.h
+===================================================================
+--- a/include/linux/mtd/spinand.h
++++ b/include/linux/mtd/spinand.h
+@@ -238,6 +238,7 @@ struct spinand_manufacturer {
+ };
+ 
+ /* SPI NAND manufacturers */
++extern const struct spinand_manufacturer etron_spinand_manufacturer;
+ extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
+ extern const struct spinand_manufacturer macronix_spinand_manufacturer;
+ extern const struct spinand_manufacturer micron_spinand_manufacturer;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2380-fix-dirty-race-between-do_tmpfile.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2380-fix-dirty-race-between-do_tmpfile.patch
index 63943f8..cece259 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2380-fix-dirty-race-between-do_tmpfile.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2380-fix-dirty-race-between-do_tmpfile.patch
@@ -45,7 +45,7 @@
  static int do_tmpfile(struct inode *dir, struct dentry *dentry,
  		      umode_t mode, struct inode **whiteout)
  {
-@@ -364,7 +390,7 @@ static int do_tmpfile(struct inode *dir, struct dentry *dentry,
+@@ -364,7 +390,7 @@ static int do_tmpfile(struct inode *dir,
  	struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
  					.dirtied_ino = 1};
  	struct ubifs_budget_req ino_req = { .dirtied_ino = 1 };
@@ -54,7 +54,7 @@
  	int err, instantiated = 0;
  	struct fscrypt_name nm;
  
-@@ -426,18 +452,18 @@ static int do_tmpfile(struct inode *dir, struct dentry *dentry,
+@@ -426,11 +452,11 @@ static int do_tmpfile(struct inode *dir,
  	instantiated = 1;
  	mutex_unlock(&ui->ui_mutex);
  
@@ -67,7 +67,8 @@
 +	unlock_2_inodes(dir, inode);
  
  	ubifs_release_budget(c, &req);
- 
+ 	fscrypt_free_filename(&nm);
+@@ -438,7 +464,7 @@ static int do_tmpfile(struct inode *dir,
  	return 0;
  
  out_cancel:
@@ -76,7 +77,7 @@
  out_inode:
  	make_bad_inode(inode);
  	if (!instantiated)
-@@ -672,32 +698,6 @@ static int ubifs_dir_release(struct inode *dir, struct file *file)
+@@ -673,32 +699,6 @@ static int ubifs_dir_release(struct inod
  	return 0;
  }
  
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
index e21e5f5..fc5ecff 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
@@ -88,6 +88,7 @@
     file://999-2338-mtd-tests-fix-pagetest-load.patch \
     file://999-2339-drivers-mtd-spinand-Add-calibration-support-for-spinand.patch \
     file://999-2340-drivers-mtd-spi-nor-Add-calibration-support-for-spi-nor.patch \
+    file://999-2341-mtd-spinand-Add-support-etron.patch \
     file://999-2350-nvmem-core-Add-functions-to-make-number-reading-easy.patch \
     file://999-2351-nvmem-mtk-efuse-support-minimum-one-byte-access-stri.patch \
     file://999-2361-add-spimem-support-to-mtk-spi.patch \
diff --git a/recipes-kernel/linux/linux-mediatek_5.4.bb b/recipes-kernel/linux/linux-mediatek_5.4.bb
index f6e8bc0..a9cfda2 100644
--- a/recipes-kernel/linux/linux-mediatek_5.4.bb
+++ b/recipes-kernel/linux/linux-mediatek_5.4.bb
@@ -9,8 +9,8 @@
 
 KBRANCH ?= "linux-5.4.y"
 
-LINUX_VERSION ?= "5.4.238"
-SRCREV_machine ?= "6849d8c4a61a93bb3abf2f65c84ec1ebfa9a9fb6"
+LINUX_VERSION ?= "5.4.246"
+SRCREV_machine ?= "f568a20f058fa1e37069cff4aac4187c1650a0e9"
 KMETA = "kernel-meta"
 SRCREV_meta ?= "feeb59687bc0f054af837a5061f8d413ec7c93e9"