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

[Description]
76cd4287 [mt7986][trng][change trng version]
37641870 [openwrt][mt7987][bsp][Add support for the mt7987 platform]
73fe5221 [kernel][mt7987][hnat][Refactor fqos flag assignment when HQoS is enabled for the NETSYSv3.1]
b4d8a1ed [kernel][common][eth][Add nf hw offload]
9872cf38 [kernel][mt7987][eth][Switch to real clock driver for the ETH]
e49e90e7 [set trng clock is ao]

[Release-log]

Change-Id: I47177c3ac8287a95f5bff802123e5bdb068fb009
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7986.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7986.c
index 2238414..a47a9ea 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7986.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/clk/mediatek/clk-mt7986.c
@@ -488,6 +488,16 @@
 		.ops = &mtk_clk_gate_ops_setclr,	\

 	}

 

+#define GATE_INFRA0_FLAGS(_id, _name, _parent, _shift, _flags) {	\

+		.id = _id,				\

+		.name = _name,				\

+		.parent_name = _parent,			\

+		.regs = &infra0_cg_regs,			\

+		.shift = _shift,			\

+		.flags = _flags,            \

+		.ops = &mtk_clk_gate_ops_setclr,	\

+	}

+

 static const struct mtk_gate infra_clks[] __initconst = {

 	/* INFRA0 */

 	GATE_INFRA0(CK_INFRA_PWM_HCK, "infra_pwm_hck", "infra_66m_mck", 1),

@@ -506,7 +516,7 @@
 	GATE_INFRA0(CK_INFRA_AP_DMA_CK, "infra_ap_dma", "infra_66m_mck", 16),

 	GATE_INFRA0(CK_INFRA_SEJ_CK, "infra_sej", "infra_66m_mck", 24),

 	GATE_INFRA0(CK_INFRA_SEJ_13M_CK, "infra_sej_13m", "infra_ck_f26m", 25),

-	GATE_INFRA0(CK_INFRA_TRNG_CK, "infra_trng", "infra_hd_133m", 26),

+	GATE_INFRA0_FLAGS(CK_INFRA_TRNG_CK, "infra_trng", "infra_hd_133m", 26, CLK_IS_CRITICAL),

 	/* INFRA1 */

 	GATE_INFRA1(CK_INFRA_THERM_CK, "infra_therm", "infra_ck_f26m", 0),

 	GATE_INFRA1(CK_INFRA_I2CO_CK, "infra_i2co", "infra_i2cs", 1),

diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
index 20141da..ab68eac 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
@@ -1145,6 +1145,10 @@
 				atomic64_add(bytes, &counter[dir].bytes);
 				atomic64_add(packets, &counter[dir].packets);
 			}
+
+			/* avoid compiler warnings */
+			if (tuple.dst.protonum == IPPROTO_TCP)
+				nf_conntrack_tcp_established(ct);
 		}
 	}
 
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 0a5be88..8804b72 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
@@ -1018,7 +1018,7 @@
 	    !mtk_tnl_decap_offload) {
 		skb->dev->netdev_ops->ndo_flow_offload_check(&hw_path);
 
-		if (hw_path.flags & FLOW_OFFLOAD_PATH_TNL)
+		if (hw_path.flags & BIT(DEV_PATH_TNL))
 			skb_hnat_alg(skb) = 1;
 	}
 
@@ -1168,7 +1168,7 @@
 	struct neighbour *neigh = NULL;
 	struct dst_entry *dst = skb_dst(skb);
 
-	if (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE)
+	if (hw_path->flags & BIT(DEV_PATH_PPPOE))
 		return 0;
 
 	rcu_read_lock_bh();
@@ -1206,7 +1206,7 @@
 	struct rtable *rt = (struct rtable *)dst;
 	struct net_device *dev = (__force struct net_device *)out;
 
-	if (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE)
+	if (hw_path->flags & BIT(DEV_PATH_PPPOE))
 		return 0;
 
 	if (!skb_hnat_cdrt(skb) && dst && dst_xfrm(dst))
@@ -1294,8 +1294,8 @@
 struct foe_entry ppe_fill_info_blk(struct foe_entry entry,
 				   struct flow_offload_hw_path *hw_path)
 {
-	entry.bfib1.psn = (hw_path->flags & FLOW_OFFLOAD_PATH_PPPOE) ? 1 : 0;
-	entry.bfib1.vlan_layer += (hw_path->flags & FLOW_OFFLOAD_PATH_VLAN) ? 1 : 0;
+	entry.bfib1.psn = (hw_path->flags & BIT(DEV_PATH_PPPOE)) ? 1 : 0;
+	entry.bfib1.vlan_layer += (hw_path->flags & BIT(DEV_PATH_VLAN)) ? 1 : 0;
 	entry.bfib1.vpm = (entry.bfib1.vlan_layer) ? 1 : 0;
 	entry.bfib1.cah = 1;
 	entry.bfib1.time_stamp = (hnat_priv->data->version == MTK_HNAT_V2 ||
@@ -1390,7 +1390,7 @@
 {
 	struct dst_entry *dst = skb_dst(skb);
 
-	if ((skb_hnat_tops(skb) && !(hw_path->flags & FLOW_OFFLOAD_PATH_TNL))) {
+	if ((skb_hnat_tops(skb) && !(hw_path->flags & BIT(DEV_PATH_TNL)))) {
 		if (!tnl_toggle)
 			return -1;
 
@@ -2042,17 +2042,11 @@
 						 MTK_HNAT_V3) ?
 						 skb->mark & 0x7f : skb->mark & 0xf;
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
-					if ((IS_HQOS_UL_MODE && IS_WAN(dev)) ||
-					    (IS_HQOS_DL_MODE &&
-					     IS_LAN_GRP(dev)) ||
-					    (IS_PPPQ_MODE &&
-					     IS_PPPQ_PATH(dev, skb)))
-						entry.ipv4_hnapt.tport_id = NR_QDMA_TPORT;
-					else
-						entry.ipv4_hnapt.tport_id = 0;
-#else
-					entry.ipv4_hnapt.iblk2.fqos = 1;
+					entry.ipv4_hnapt.tport_id =
+						HQOS_FLAG(dev, skb, entry.ipv4_hnapt.iblk2.qid);
 #endif
+					entry.ipv4_hnapt.iblk2.fqos =
+						HQOS_FLAG(dev, skb, entry.ipv4_hnapt.iblk2.qid);
 				}
 
 				entry.ipv4_hnapt.bfib1.udp =
@@ -2114,7 +2108,7 @@
 	/* Fill Layer2 Info.*/
 	entry = ppe_fill_L2_info(entry, hw_path);
 
-	if ((skb_hnat_tops(skb) && hw_path->flags & FLOW_OFFLOAD_PATH_TNL) ||
+	if ((skb_hnat_tops(skb) && hw_path->flags & BIT(DEV_PATH_TNL)) ||
 	    (!skb_hnat_cdrt(skb) && skb_hnat_is_encrypt(skb) &&
 	    skb_dst(skb) && dst_xfrm(skb_dst(skb))))
 		goto hnat_entry_skip_bind;
@@ -2259,13 +2253,13 @@
 
 			if (FROM_EXT(skb) || skb_hnat_sport(skb) == NR_QDMA_PORT)
 				entry.ipv4_hnapt.iblk2.fqos = 0;
-			else
+			else {
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
 				entry.ipv4_hnapt.tport_id = HQOS_FLAG(dev, skb, qid) ?
 							    NR_QDMA_TPORT : 0;
-#else
-				entry.ipv4_hnapt.iblk2.fqos = HQOS_FLAG(dev, skb, qid) ? 1 : 0;
 #endif
+				entry.ipv4_hnapt.iblk2.fqos = HQOS_FLAG(dev, skb, qid) ? 1 : 0;
+			}
 		} else {
 			entry.ipv4_hnapt.iblk2.fqos = 0;
 		}
@@ -2312,7 +2306,7 @@
 
 			if (FROM_EXT(skb) || skb_hnat_sport(skb) == NR_QDMA_PORT)
 				entry.ipv6_5t_route.iblk2.fqos = 0;
-			else
+			else {
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
 				switch (entry.bfib1.pkt_type) {
 				case IPV4_MAP_E:
@@ -2330,9 +2324,9 @@
 						HQOS_FLAG(dev, skb, qid) ? NR_QDMA_TPORT : 0;
 					break;
 				}
-#else
-				entry.ipv6_5t_route.iblk2.fqos = HQOS_FLAG(dev, skb, qid) ? 1 : 0;
 #endif
+				entry.ipv6_5t_route.iblk2.fqos = HQOS_FLAG(dev, skb, qid) ? 1 : 0;
+			}
 		} else {
 			entry.ipv6_5t_route.iblk2.fqos = 0;
 		}
@@ -2551,6 +2545,7 @@
 			entry.ipv4_hnapt.winfo.wcid = skb_hnat_wc_id(skb);
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
 			entry.ipv4_hnapt.tport_id = IS_HQOS_DL_MODE ? NR_QDMA_TPORT : 0;
+			entry.ipv4_hnapt.iblk2.fqos = IS_HQOS_DL_MODE ? 1 : 0;
 			entry.ipv4_hnapt.iblk2.rxid = skb_hnat_rx_id(skb);
 			entry.ipv4_hnapt.iblk2.winfoi = 1;
 			entry.ipv4_hnapt.winfo_pao.usr_info =
@@ -2609,6 +2604,7 @@
 		entry.ipv6_hnapt.winfo_pao.hf = skb_hnat_hf(skb);
 		entry.ipv6_hnapt.winfo_pao.amsdu = skb_hnat_amsdu(skb);
 		entry.ipv6_hnapt.tport_id = IS_HQOS_DL_MODE ? NR_QDMA_TPORT : 0;
+		entry.ipv6_hnapt.iblk2.fqos = IS_HQOS_DL_MODE ? 1 : 0;
 	} else if (IS_L2_BRIDGE(&entry)) {
 		entry.l2_bridge.iblk2.dp = gmac_no;
 		entry.l2_bridge.iblk2.rxid = skb_hnat_rx_id(skb);
@@ -2625,6 +2621,7 @@
 		entry.l2_bridge.winfo_pao.is_sp = skb_hnat_is_sp(skb);
 		entry.l2_bridge.winfo_pao.hf = skb_hnat_hf(skb);
 		entry.l2_bridge.winfo_pao.amsdu = skb_hnat_amsdu(skb);
+		entry.l2_bridge.iblk2.fqos = IS_HQOS_DL_MODE ? 1 : 0;
 #endif
 	} else {
 		entry.ipv6_5t_route.iblk2.fqos = 0;
@@ -2641,6 +2638,7 @@
 				entry.ipv4_mape.winfo.bssid = skb_hnat_bss_id(skb);
 				entry.ipv4_mape.winfo.wcid = skb_hnat_wc_id(skb);
 				entry.ipv4_mape.tport_id = IS_HQOS_DL_MODE ? NR_QDMA_TPORT : 0;
+				entry.ipv4_mape.iblk2.fqos = IS_HQOS_DL_MODE ? 1 : 0;
 				entry.ipv4_mape.iblk2.rxid = skb_hnat_rx_id(skb);
 				entry.ipv4_mape.iblk2.winfoi = 1;
 				entry.ipv4_mape.winfo_pao.usr_info =
@@ -2662,6 +2660,7 @@
 				entry.ipv6_5t_route.winfo.bssid = skb_hnat_bss_id(skb);
 				entry.ipv6_5t_route.winfo.wcid = skb_hnat_wc_id(skb);
 				entry.ipv6_5t_route.tport_id = IS_HQOS_DL_MODE ? NR_QDMA_TPORT : 0;
+				entry.ipv6_5t_route.iblk2.fqos = IS_HQOS_DL_MODE ? 1 : 0;
 				entry.ipv6_5t_route.iblk2.rxid = skb_hnat_rx_id(skb);
 				entry.ipv6_5t_route.iblk2.winfoi = 1;
 				entry.ipv6_5t_route.winfo_pao.usr_info =
@@ -3281,7 +3280,7 @@
 		out->netdev_ops->ndo_flow_offload_check(&hw_path);
 
 		out = (IS_GMAC1_MODE) ? hw_path.virt_dev : hw_path.dev;
-		if (hw_path.flags & FLOW_OFFLOAD_PATH_TNL && mtk_tnl_encap_offload) {
+		if (hw_path.flags & BIT(DEV_PATH_TNL) && mtk_tnl_encap_offload) {
 			if (ntohs(skb->protocol) == ETH_P_IP &&
 			    ip_hdr(skb)->protocol == IPPROTO_TCP) {
 				skb_hnat_set_tops(skb, hw_path.tnl_type + 1);
@@ -3309,7 +3308,7 @@
 
 	if (is_virt_dev
 	    && !(skb_hnat_tops(skb) && skb_hnat_is_encap(skb)
-		 && (hw_path.flags & FLOW_OFFLOAD_PATH_TNL)))
+		 && (hw_path.flags & BIT(DEV_PATH_TNL))))
 		return 0;
 
 	if (debug_level >= 7)
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
index 5f64105..4398bae 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
@@ -39,7 +39,7 @@
 	int port_index;
 	u16 dsa_tag;
 
-	if (hw_path->flags & FLOW_OFFLOAD_PATH_VLAN)
+	if (hw_path->flags & BIT(DEV_PATH_VLAN))
 		ndev = hw_path->dev;
 	else
 		ndev = (struct net_device *)netdev;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-ethernet-add-ppe-for-netsys1.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-ethernet-add-ppe-for-netsys1.patch
new file mode 100644
index 0000000..dd0473d
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-ethernet-add-ppe-for-netsys1.patch
@@ -0,0 +1,1929 @@
+From 2ee245f821d960dd389a5c3dd97dc1057aa05554 Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Tue, 4 Jun 2024 09:14:34 +0800
+Subject: [PATCH 01/24] ethernet add ppe for netsys1
+
+---
+ drivers/net/ethernet/mediatek/Makefile        |    3 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c   |   25 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h   |   19 +-
+ drivers/net/ethernet/mediatek/mtk_ppe.c       |  514 +++++++
+ drivers/net/ethernet/mediatek/mtk_ppe.h       |  288 ++++
+ .../net/ethernet/mediatek/mtk_ppe_debugfs.c   |  214 +++
+ .../net/ethernet/mediatek/mtk_ppe_offload.c   |  535 ++++++++
+ drivers/net/ethernet/mediatek/mtk_ppe_regs.h  |  144 ++
+ 8 files changed, 1739 insertions(+), 3 deletions(-)
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe.c
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe.h
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+
+diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
+old mode 100644
+new mode 100755
+index 634640d..5f342f4
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -4,5 +4,6 @@
+ #
+ 
+ obj-$(CONFIG_NET_MEDIATEK_SOC)			+= mtk_eth.o
+-mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o mtk_eth_ptp.o
++mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o mtk_eth_ptp.o	\
++	     mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
+ obj-$(CONFIG_NET_MEDIATEK_HNAT)			+= mtk_hnat/
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+old mode 100644
+new mode 100755
+index 697a197..edc4ecf
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -4010,6 +4010,7 @@ static int mtk_open(struct net_device *dev)
+ 	struct mtk_phylink_priv *phylink_priv = &mac->phylink_priv;
+ 	struct device_node *phy_node;
+ 	const char *mac_addr;
++	u32 gdm_config = MTK_GDMA_TO_PDMA;
+ 	u32 id = mtk_mac2xgmii_id(eth, mac->id);
+ 	int err, i;
+ 
+@@ -4102,7 +4103,10 @@ static int mtk_open(struct net_device *dev)
+ 		regmap_write(eth->sgmii->pcs[id].regmap,
+ 			     SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+ 
+-	mtk_gdm_config(eth, mac->id, MTK_GDMA_TO_PDMA);
++	if (eth->soc->offload_version && mtk_ppe_start(&eth->ppe) == 0)
++		gdm_config = MTK_GDMA_TO_PPE;
++
++	mtk_gdm_config(eth, mac->id, gdm_config);
+ 
+ 	return 0;
+ }
+@@ -4189,6 +4193,9 @@ static int mtk_stop(struct net_device *dev)
+ 
+ 	mtk_dma_free(eth);
+ 
++	if (eth->soc->offload_version)
++		mtk_ppe_stop(&eth->ppe);
++
+ 	return 0;
+ }
+ 
+@@ -5109,6 +5116,7 @@ static const struct net_device_ops mtk_netdev_ops = {
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+ 	.ndo_poll_controller	= mtk_poll_controller,
+ #endif
++	.ndo_setup_tc		= mtk_eth_setup_tc,
+ };
+ 
+ static void mux_poll(struct work_struct *work)
+@@ -5741,6 +5749,17 @@ static int mtk_probe(struct platform_device *pdev)
+ 			goto err_free_dev;
+ 	}
+ 
++	if (eth->soc->offload_version) {
++		err = mtk_ppe_init(&eth->ppe, eth->dev,
++				   eth->base + MTK_ETH_PPE_BASE);
++		if (err)
++			goto err_free_dev;
++
++		err = mtk_eth_offload_init(eth);
++		if (err)
++			goto err_free_dev;
++	}
++
+ 	for (i = 0; i < MTK_MAX_DEVS; i++) {
+ 		if (!eth->netdev[i])
+ 			continue;
+@@ -5842,6 +5861,7 @@ static const struct mtk_soc_data mt2701_data = {
+ 	.required_clks = MT7623_CLKS_BITMAP,
+ 	.required_pctl = true,
+ 	.has_sram = false,
++	.offload_version = 1,
+ 	.rss_num = 0,
+ 	.txrx = {
+ 		.txd_size = sizeof(struct mtk_tx_dma),
+@@ -5862,6 +5882,7 @@ static const struct mtk_soc_data mt7621_data = {
+ 	.required_clks = MT7621_CLKS_BITMAP,
+ 	.required_pctl = false,
+ 	.has_sram = false,
++	.offload_version = 1,
+ 	.rss_num = 0,
+ 	.txrx = {
+ 		.txd_size = sizeof(struct mtk_tx_dma),
+@@ -5883,6 +5904,7 @@ static const struct mtk_soc_data mt7622_data = {
+ 	.required_clks = MT7622_CLKS_BITMAP,
+ 	.required_pctl = false,
+ 	.has_sram = false,
++	.offload_version = 2,
+ 	.rss_num = 0,
+ 	.txrx = {
+ 		.txd_size = sizeof(struct mtk_tx_dma),
+@@ -5903,6 +5925,7 @@ static const struct mtk_soc_data mt7623_data = {
+ 	.required_clks = MT7623_CLKS_BITMAP,
+ 	.required_pctl = true,
+ 	.has_sram = false,
++	.offload_version = 1,
+ 	.rss_num = 0,
+ 	.txrx = {
+ 		.txd_size = sizeof(struct mtk_tx_dma),
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+old mode 100644
+new mode 100755
+index 9fae48f..54b2c7c
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -15,6 +15,8 @@
+ #include <linux/refcount.h>
+ #include <linux/phylink.h>
+ #include <linux/ptp_clock_kernel.h>
++#include <linux/rhashtable.h>
++#include "mtk_ppe.h"
+ 
+ #define MTK_QDMA_PAGE_SIZE	2048
+ #define	MTK_MAX_RX_LENGTH	1536
+@@ -46,7 +48,8 @@
+ 				 NETIF_F_HW_VLAN_CTAG_TX | \
+ 				 NETIF_F_SG | NETIF_F_TSO | \
+ 				 NETIF_F_TSO6 | \
+-				 NETIF_F_IPV6_CSUM)
++				 NETIF_F_IPV6_CSUM |\
++				 NETIF_F_HW_TC)
+ #define MTK_SET_FEATURES	(NETIF_F_LRO | \
+ 				 NETIF_F_HW_VLAN_CTAG_RX)
+ #define MTK_HW_FEATURES_MT7628	(NETIF_F_SG | NETIF_F_RXCSUM)
+@@ -133,6 +136,7 @@
+ #define MTK_GDMA_UCS_EN		BIT(20)
+ #define MTK_GDMA_STRP_CRC	BIT(16)
+ #define MTK_GDMA_TO_PDMA	0x0
++#define MTK_GDMA_TO_PPE		0x4444
+ #define MTK_GDMA_DROP_ALL	0x7777
+ 
+ /* GDM Egress Control Register */
+@@ -686,6 +690,12 @@
+ #define RX_DMA_TCI(_x)		((_x) & (VLAN_PRIO_MASK | VLAN_VID_MASK))
+ #define RX_DMA_VPID(_x)		(((_x) >> 16) & 0xffff)
+ 
++/* QDMA descriptor rxd4 */
++#define MTK_RXD4_FOE_ENTRY	GENMASK(13, 0)
++#define MTK_RXD4_PPE_CPU_REASON	GENMASK(18, 14)
++#define MTK_RXD4_SRC_PORT	GENMASK(21, 19)
++#define MTK_RXD4_ALG		GENMASK(31, 22)
++
+ /* QDMA descriptor rxd4 */
+ #define RX_DMA_L4_VALID		BIT(24)
+ #define RX_DMA_L4_VALID_PDMA	BIT(30)		/* when PDMA is used */
+@@ -1737,6 +1747,7 @@ struct mtk_soc_data {
+ 	u64		caps;
+ 	u64		required_clks;
+ 	bool		required_pctl;
++	u8		offload_version;
+ 	netdev_features_t hw_features;
+ 	bool		has_sram;
+ 	struct {
+@@ -1947,6 +1958,9 @@ struct mtk_eth {
+ 	int				ip_align;
+	spinlock_t			syscfg0_lock;
+ 	struct notifier_block		netdevice_notifier;
++
++	struct mtk_ppe			ppe;
++	struct rhashtable		flow_table;
+ };
+ 
+ /* struct mtk_mac -	the structure that holds the info about the MACs of the
+@@ -2026,6 +2040,9 @@ struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, int id);
+ int mtk_usxgmii_init(struct mtk_eth *eth, struct device_node *r);
+ int mtk_toprgu_init(struct mtk_eth *eth, struct device_node *r);
+ 
++int mtk_eth_offload_init(struct mtk_eth *eth);
++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);
+ u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index);
+ #endif /* MTK_ETH_H */
+diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
+new file mode 100644
+index 0000000..6965d98
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -0,0 +1,513 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#include <linux/kernel.h>
++#include <linux/io.h>
++#include <linux/iopoll.h>
++#include <linux/etherdevice.h>
++#include <linux/platform_device.h>
++#include "mtk_ppe.h"
++#include "mtk_ppe_regs.h"
++
++static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
++{
++	writel(val, ppe->base + reg);
++}
++
++static u32 ppe_r32(struct mtk_ppe *ppe, u32 reg)
++{
++	return readl(ppe->base + reg);
++}
++
++static u32 ppe_m32(struct mtk_ppe *ppe, u32 reg, u32 mask, u32 set)
++{
++	u32 val;
++
++	val = ppe_r32(ppe, reg);
++	val &= ~mask;
++	val |= set;
++	ppe_w32(ppe, reg, val);
++
++	return val;
++}
++
++static u32 ppe_set(struct mtk_ppe *ppe, u32 reg, u32 val)
++{
++	return ppe_m32(ppe, reg, 0, val);
++}
++
++static u32 ppe_clear(struct mtk_ppe *ppe, u32 reg, u32 val)
++{
++	return ppe_m32(ppe, reg, val, 0);
++}
++
++static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
++{
++	int ret;
++	u32 val;
++
++	ret = readl_poll_timeout(ppe->base + MTK_PPE_GLO_CFG, val,
++				 !(val & MTK_PPE_GLO_CFG_BUSY),
++				 20, MTK_PPE_WAIT_TIMEOUT_US);
++
++	if (ret)
++		dev_err(ppe->dev, "PPE table busy");
++
++	return ret;
++}
++
++static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
++{
++	ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
++	ppe_clear(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
++}
++
++static void mtk_ppe_cache_enable(struct mtk_ppe *ppe, bool enable)
++{
++	mtk_ppe_cache_clear(ppe);
++
++	ppe_m32(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_EN,
++		enable * MTK_PPE_CACHE_CTL_EN);
++}
++
++static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e)
++{
++	u32 hv1, hv2, hv3;
++	u32 hash;
++
++	switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) {
++		case MTK_PPE_PKT_TYPE_BRIDGE:
++			hv1 = e->bridge.src_mac_lo;
++			hv1 ^= ((e->bridge.src_mac_hi & 0xffff) << 16);
++			hv2 = e->bridge.src_mac_hi >> 16;
++			hv2 ^= e->bridge.dest_mac_lo;
++			hv3 = e->bridge.dest_mac_hi;
++			break;
++		case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
++		case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
++			hv1 = e->ipv4.orig.ports;
++			hv2 = e->ipv4.orig.dest_ip;
++			hv3 = e->ipv4.orig.src_ip;
++			break;
++		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
++		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
++			hv1 = e->ipv6.src_ip[3] ^ e->ipv6.dest_ip[3];
++			hv1 ^= e->ipv6.ports;
++
++			hv2 = e->ipv6.src_ip[2] ^ e->ipv6.dest_ip[2];
++			hv2 ^= e->ipv6.dest_ip[0];
++
++			hv3 = e->ipv6.src_ip[1] ^ e->ipv6.dest_ip[1];
++			hv3 ^= e->ipv6.src_ip[0];
++			break;
++		case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++		case MTK_PPE_PKT_TYPE_IPV6_6RD:
++		default:
++			WARN_ON_ONCE(1);
++			return MTK_PPE_HASH_MASK;
++	}
++
++	hash = (hv1 & hv2) | ((~hv1) & hv3);
++	hash = (hash >> 24) | ((hash & 0xffffff) << 8);
++	hash ^= hv1 ^ hv2 ^ hv3;
++	hash ^= hash >> 16;
++	hash <<= 1;
++	hash &= MTK_PPE_ENTRIES - 1;
++
++	return hash;
++}
++
++static inline struct mtk_foe_mac_info *
++mtk_foe_entry_l2(struct mtk_foe_entry *entry)
++{
++	int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++
++	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
++		return &entry->ipv6.l2;
++
++	return &entry->ipv4.l2;
++}
++
++static inline u32 *
++mtk_foe_entry_ib2(struct mtk_foe_entry *entry)
++{
++	int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++
++	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
++		return &entry->ipv6.ib2;
++
++	return &entry->ipv4.ib2;
++}
++
++int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
++			  u8 pse_port, u8 *src_mac, u8 *dest_mac)
++{
++	struct mtk_foe_mac_info *l2;
++	u32 ports_pad, val;
++
++	memset(entry, 0, sizeof(*entry));
++
++	val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
++	      FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) |
++	      FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
++	      MTK_FOE_IB1_BIND_TTL |
++	      MTK_FOE_IB1_BIND_CACHE;
++	entry->ib1 = val;
++
++	val = FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) |
++	      FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f) |
++	      FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port);
++
++	if (is_multicast_ether_addr(dest_mac))
++		val |= MTK_FOE_IB2_MULTICAST;
++
++	ports_pad = 0xa5a5a500 | (l4proto & 0xff);
++	if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
++		entry->ipv4.orig.ports = ports_pad;
++	if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
++		entry->ipv6.ports = ports_pad;
++
++	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
++		entry->ipv6.ib2 = val;
++		l2 = &entry->ipv6.l2;
++	} else {
++		entry->ipv4.ib2 = val;
++		l2 = &entry->ipv4.l2;
++	}
++
++	l2->dest_mac_hi = get_unaligned_be32(dest_mac);
++	l2->dest_mac_lo = get_unaligned_be16(dest_mac + 4);
++	l2->src_mac_hi = get_unaligned_be32(src_mac);
++	l2->src_mac_lo = get_unaligned_be16(src_mac + 4);
++
++	if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
++		l2->etype = ETH_P_IPV6;
++	else
++		l2->etype = ETH_P_IP;
++
++	return 0;
++}
++
++int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port)
++{
++	u32 *ib2 = mtk_foe_entry_ib2(entry);
++	u32 val;
++
++	val = *ib2;
++	val &= ~MTK_FOE_IB2_DEST_PORT;
++	val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port);
++	*ib2 = val;
++
++	return 0;
++}
++
++int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool egress,
++				 __be32 src_addr, __be16 src_port,
++				 __be32 dest_addr, __be16 dest_port)
++{
++	int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++	struct mtk_ipv4_tuple *t;
++
++	switch (type) {
++	case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
++		if (egress) {
++			t = &entry->ipv4.new;
++			break;
++		}
++		fallthrough;
++	case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++	case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
++		t = &entry->ipv4.orig;
++		break;
++	case MTK_PPE_PKT_TYPE_IPV6_6RD:
++		entry->ipv6_6rd.tunnel_src_ip = be32_to_cpu(src_addr);
++		entry->ipv6_6rd.tunnel_dest_ip = be32_to_cpu(dest_addr);
++		return 0;
++	default:
++		WARN_ON_ONCE(1);
++		return -EINVAL;
++	}
++
++	t->src_ip = be32_to_cpu(src_addr);
++	t->dest_ip = be32_to_cpu(dest_addr);
++
++	if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
++		return 0;
++
++	t->src_port = be16_to_cpu(src_port);
++	t->dest_port = be16_to_cpu(dest_port);
++
++	return 0;
++}
++
++int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry,
++				 __be32 *src_addr, __be16 src_port,
++				 __be32 *dest_addr, __be16 dest_port)
++{
++	int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++	u32 *src, *dest;
++	int i;
++
++	switch (type) {
++	case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++		src = entry->dslite.tunnel_src_ip;
++		dest = entry->dslite.tunnel_dest_ip;
++		break;
++	case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
++	case MTK_PPE_PKT_TYPE_IPV6_6RD:
++		entry->ipv6.src_port = be16_to_cpu(src_port);
++		entry->ipv6.dest_port = be16_to_cpu(dest_port);
++		fallthrough;
++	case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
++		src = entry->ipv6.src_ip;
++		dest = entry->ipv6.dest_ip;
++		break;
++	default:
++		WARN_ON_ONCE(1);
++		return -EINVAL;
++	}
++
++	for (i = 0; i < 4; i++)
++		src[i] = be32_to_cpu(src_addr[i]);
++	for (i = 0; i < 4; i++)
++		dest[i] = be32_to_cpu(dest_addr[i]);
++
++	return 0;
++}
++
++int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port)
++{
++	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++
++	l2->etype = BIT(port);
++
++	if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER))
++		entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
++	else
++		l2->etype |= BIT(8);
++
++	entry->ib1 &= ~MTK_FOE_IB1_BIND_VLAN_TAG;
++
++	return 0;
++}
++
++int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid)
++{
++	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++
++	switch (FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, entry->ib1)) {
++	case 0:
++		entry->ib1 |= MTK_FOE_IB1_BIND_VLAN_TAG |
++			      FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
++		l2->vlan1 = vid;
++		return 0;
++	case 1:
++		if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG)) {
++			l2->vlan1 = vid;
++			l2->etype |= BIT(8);
++		} else {
++			l2->vlan2 = vid;
++			entry->ib1 += FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
++		}
++		return 0;
++	default:
++		return -ENOSPC;
++	}
++}
++
++int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid)
++{
++	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++
++	if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER) ||
++	    (entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG))
++		l2->etype = ETH_P_PPP_SES;
++
++	entry->ib1 |= MTK_FOE_IB1_BIND_PPPOE;
++	l2->pppoe_id = sid;
++
++	return 0;
++}
++
++static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry)
++{
++	return !(entry->ib1 & MTK_FOE_IB1_STATIC) &&
++	       FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND;
++}
++
++int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
++			 u16 timestamp)
++{
++	struct mtk_foe_entry *hwe;
++	u32 hash;
++
++	timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP;
++	entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
++	entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp);
++
++	hash = mtk_ppe_hash_entry(entry);
++	hwe = &ppe->foe_table[hash];
++	if (!mtk_foe_entry_usable(hwe)) {
++		hwe++;
++		hash++;
++
++		if (!mtk_foe_entry_usable(hwe))
++			return -ENOSPC;
++	}
++
++	memcpy(&hwe->data, &entry->data, sizeof(hwe->data));
++	wmb();
++	hwe->ib1 = entry->ib1;
++
++	dma_wmb();
++
++	mtk_ppe_cache_clear(ppe);
++
++	return hash;
++}
++
++int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base)
++{
++	struct mtk_foe_entry *foe;
++
++	/* need to allocate a separate device, since it PPE DMA access is
++	 * not coherent.
++	 */
++	ppe->base = base;
++	ppe->dev = dev;
++	ppe->version = eth->soc->offload_version;
++
++	foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe),
++				  &ppe->foe_phys, GFP_KERNEL);
++	if (!foe)
++		return -ENOMEM;
++
++	ppe->foe_table = foe;
++
++	mtk_ppe_debugfs_init(ppe);
++
++	return 0;
++}
++
++static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
++{
++	static const u8 skip[] = { 12, 25, 38, 51, 76, 89, 102 };
++	int i, k;
++
++	memset(ppe->foe_table, 0, MTK_PPE_ENTRIES * sizeof(*ppe->foe_table));
++
++	if (!IS_ENABLED(CONFIG_SOC_MT7621))
++		return;
++
++	/* skip all entries that cross the 1024 byte boundary */
++	for (i = 0; i < MTK_PPE_ENTRIES; i += 128)
++		for (k = 0; k < ARRAY_SIZE(skip); k++)
++			ppe->foe_table[i + skip[k]].ib1 |= MTK_FOE_IB1_STATIC;
++}
++
++int mtk_ppe_start(struct mtk_ppe *ppe)
++{
++	u32 val;
++
++	mtk_ppe_init_foe_table(ppe);
++	ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys);
++
++	val = MTK_PPE_TB_CFG_ENTRY_80B |
++	      MTK_PPE_TB_CFG_AGE_NON_L4 |
++	      MTK_PPE_TB_CFG_AGE_UNBIND |
++	      MTK_PPE_TB_CFG_AGE_TCP |
++	      MTK_PPE_TB_CFG_AGE_UDP |
++	      MTK_PPE_TB_CFG_AGE_TCP_FIN |
++	      FIELD_PREP(MTK_PPE_TB_CFG_SEARCH_MISS,
++			 MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD) |
++	      FIELD_PREP(MTK_PPE_TB_CFG_KEEPALIVE,
++			 MTK_PPE_KEEPALIVE_DISABLE) |
++	      FIELD_PREP(MTK_PPE_TB_CFG_HASH_MODE, 1) |
++	      FIELD_PREP(MTK_PPE_TB_CFG_SCAN_MODE,
++			 MTK_PPE_SCAN_MODE_CHECK_AGE) |
++	      FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM,
++			 MTK_PPE_ENTRIES_SHIFT);
++	ppe_w32(ppe, MTK_PPE_TB_CFG, val);
++
++	ppe_w32(ppe, MTK_PPE_IP_PROTO_CHK,
++		MTK_PPE_IP_PROTO_CHK_IPV4 | MTK_PPE_IP_PROTO_CHK_IPV6);
++
++	mtk_ppe_cache_enable(ppe, true);
++
++	val = MTK_PPE_FLOW_CFG_IP4_TCP_FRAG |
++	      MTK_PPE_FLOW_CFG_IP4_UDP_FRAG |
++	      MTK_PPE_FLOW_CFG_IP6_3T_ROUTE |
++	      MTK_PPE_FLOW_CFG_IP6_5T_ROUTE |
++	      MTK_PPE_FLOW_CFG_IP6_6RD |
++	      MTK_PPE_FLOW_CFG_IP4_NAT |
++	      MTK_PPE_FLOW_CFG_IP4_NAPT |
++	      MTK_PPE_FLOW_CFG_IP4_DSLITE |
++	      MTK_PPE_FLOW_CFG_L2_BRIDGE |
++	      MTK_PPE_FLOW_CFG_IP4_NAT_FRAG;
++	ppe_w32(ppe, MTK_PPE_FLOW_CFG, val);
++
++	val = FIELD_PREP(MTK_PPE_UNBIND_AGE_MIN_PACKETS, 1000) |
++	      FIELD_PREP(MTK_PPE_UNBIND_AGE_DELTA, 3);
++	ppe_w32(ppe, MTK_PPE_UNBIND_AGE, val);
++
++	val = FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_UDP, 30) |
++	      FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_NON_L4, 1);
++	ppe_w32(ppe, MTK_PPE_BIND_AGE0, val);
++
++	val = FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP_FIN, 1) |
++	      FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP, 30);
++	ppe_w32(ppe, MTK_PPE_BIND_AGE1, val);
++
++	val = MTK_PPE_BIND_LIMIT0_QUARTER | MTK_PPE_BIND_LIMIT0_HALF;
++	ppe_w32(ppe, MTK_PPE_BIND_LIMIT0, val);
++
++	val = MTK_PPE_BIND_LIMIT1_FULL |
++	      FIELD_PREP(MTK_PPE_BIND_LIMIT1_NON_L4, 1);
++	ppe_w32(ppe, MTK_PPE_BIND_LIMIT1, val);
++
++	val = FIELD_PREP(MTK_PPE_BIND_RATE_BIND, 30) |
++	      FIELD_PREP(MTK_PPE_BIND_RATE_PREBIND, 1);
++	ppe_w32(ppe, MTK_PPE_BIND_RATE, val);
++
++	/* enable PPE */
++	val = MTK_PPE_GLO_CFG_EN |
++	      MTK_PPE_GLO_CFG_IP4_L4_CS_DROP |
++	      MTK_PPE_GLO_CFG_IP4_CS_DROP |
++	      MTK_PPE_GLO_CFG_MCAST_TB_EN |
++	      MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE;
++	ppe_w32(ppe, MTK_PPE_GLO_CFG, val);
++
++	ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0);
++
++	return 0;
++}
++
++int mtk_ppe_stop(struct mtk_ppe *ppe)
++{
++	u32 val;
++	int i;
++
++	for (i = 0; i < MTK_PPE_ENTRIES; i++)
++		ppe->foe_table[i].ib1 = FIELD_PREP(MTK_FOE_IB1_STATE,
++						   MTK_FOE_STATE_INVALID);
++
++	mtk_ppe_cache_enable(ppe, false);
++
++	/* disable aging */
++	val = MTK_PPE_TB_CFG_AGE_NON_L4 |
++	      MTK_PPE_TB_CFG_AGE_UNBIND |
++	      MTK_PPE_TB_CFG_AGE_TCP |
++	      MTK_PPE_TB_CFG_AGE_UDP |
++	      MTK_PPE_TB_CFG_AGE_TCP_FIN |
++	      MTK_PPE_TB_CFG_SCAN_MODE;
++	ppe_clear(ppe, MTK_PPE_TB_CFG, val);
++
++	if (mtk_ppe_wait_busy(ppe))
++		return -ETIMEDOUT;
++
++	/* disable offload engine */
++	ppe_clear(ppe, MTK_PPE_GLO_CFG, MTK_PPE_GLO_CFG_EN);
++	ppe_w32(ppe, MTK_PPE_FLOW_CFG, 0);
++
++	return 0;
++}
+diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
+new file mode 100644
+index 0000000..242fb8f
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -0,0 +1,287 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#ifndef __MTK_PPE_H
++#define __MTK_PPE_H
++
++#include <linux/kernel.h>
++#include <linux/bitfield.h>
++
++#define MTK_ETH_PPE_BASE		0xc00
++
++#define MTK_PPE_ENTRIES_SHIFT		3
++#define MTK_PPE_ENTRIES			(1024 << MTK_PPE_ENTRIES_SHIFT)
++#define MTK_PPE_HASH_MASK		(MTK_PPE_ENTRIES - 1)
++#define MTK_PPE_WAIT_TIMEOUT_US		1000000
++
++#define MTK_FOE_IB1_UNBIND_TIMESTAMP	GENMASK(7, 0)
++#define MTK_FOE_IB1_UNBIND_PACKETS	GENMASK(23, 8)
++#define MTK_FOE_IB1_UNBIND_PREBIND	BIT(24)
++
++#define MTK_FOE_IB1_BIND_TIMESTAMP	GENMASK(14, 0)
++#define MTK_FOE_IB1_BIND_KEEPALIVE	BIT(15)
++#define MTK_FOE_IB1_BIND_VLAN_LAYER	GENMASK(18, 16)
++#define MTK_FOE_IB1_BIND_PPPOE		BIT(19)
++#define MTK_FOE_IB1_BIND_VLAN_TAG	BIT(20)
++#define MTK_FOE_IB1_BIND_PKT_SAMPLE	BIT(21)
++#define MTK_FOE_IB1_BIND_CACHE		BIT(22)
++#define MTK_FOE_IB1_BIND_TUNNEL_DECAP	BIT(23)
++#define MTK_FOE_IB1_BIND_TTL		BIT(24)
++
++#define MTK_FOE_IB1_PACKET_TYPE		GENMASK(27, 25)
++#define MTK_FOE_IB1_STATE		GENMASK(29, 28)
++#define MTK_FOE_IB1_UDP			BIT(30)
++#define MTK_FOE_IB1_STATIC		BIT(31)
++
++enum {
++	MTK_PPE_PKT_TYPE_IPV4_HNAPT = 0,
++	MTK_PPE_PKT_TYPE_IPV4_ROUTE = 1,
++	MTK_PPE_PKT_TYPE_BRIDGE = 2,
++	MTK_PPE_PKT_TYPE_IPV4_DSLITE = 3,
++	MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T = 4,
++	MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T = 5,
++	MTK_PPE_PKT_TYPE_IPV6_6RD = 7,
++};
++
++#define MTK_FOE_IB2_QID			GENMASK(3, 0)
++#define MTK_FOE_IB2_PSE_QOS		BIT(4)
++#define MTK_FOE_IB2_DEST_PORT		GENMASK(7, 5)
++#define MTK_FOE_IB2_MULTICAST		BIT(8)
++
++#define MTK_FOE_IB2_WHNAT_QID2		GENMASK(13, 12)
++#define MTK_FOE_IB2_WHNAT_DEVIDX	BIT(16)
++#define MTK_FOE_IB2_WHNAT_NAT		BIT(17)
++
++#define MTK_FOE_IB2_PORT_MG		GENMASK(17, 12)
++
++#define MTK_FOE_IB2_PORT_AG		GENMASK(23, 18)
++
++#define MTK_FOE_IB2_DSCP		GENMASK(31, 24)
++
++#define MTK_FOE_VLAN2_WHNAT_BSS		GEMMASK(5, 0)
++#define MTK_FOE_VLAN2_WHNAT_WCID	GENMASK(13, 6)
++#define MTK_FOE_VLAN2_WHNAT_RING	GENMASK(15, 14)
++
++enum {
++	MTK_FOE_STATE_INVALID,
++	MTK_FOE_STATE_UNBIND,
++	MTK_FOE_STATE_BIND,
++	MTK_FOE_STATE_FIN
++};
++
++struct mtk_foe_mac_info {
++	u16 vlan1;
++	u16 etype;
++
++	u32 dest_mac_hi;
++
++	u16 vlan2;
++	u16 dest_mac_lo;
++
++	u32 src_mac_hi;
++
++	u16 pppoe_id;
++	u16 src_mac_lo;
++};
++
++struct mtk_foe_bridge {
++	u32 dest_mac_hi;
++
++	u16 src_mac_lo;
++	u16 dest_mac_lo;
++
++	u32 src_mac_hi;
++
++	u32 ib2;
++
++	u32 _rsv[5];
++
++	u32 udf_tsid;
++	struct mtk_foe_mac_info l2;
++};
++
++struct mtk_ipv4_tuple {
++	u32 src_ip;
++	u32 dest_ip;
++	union {
++		struct {
++			u16 dest_port;
++			u16 src_port;
++		};
++		struct {
++			u8 protocol;
++			u8 _pad[3]; /* fill with 0xa5a5a5 */
++		};
++		u32 ports;
++	};
++};
++
++struct mtk_foe_ipv4 {
++	struct mtk_ipv4_tuple orig;
++
++	u32 ib2;
++
++	struct mtk_ipv4_tuple new;
++
++	u16 timestamp;
++	u16 _rsv0[3];
++
++	u32 udf_tsid;
++
++	struct mtk_foe_mac_info l2;
++};
++
++struct mtk_foe_ipv4_dslite {
++	struct mtk_ipv4_tuple ip4;
++
++	u32 tunnel_src_ip[4];
++	u32 tunnel_dest_ip[4];
++
++	u8 flow_label[3];
++	u8 priority;
++
++	u32 udf_tsid;
++
++	u32 ib2;
++
++	struct mtk_foe_mac_info l2;
++};
++
++struct mtk_foe_ipv6 {
++	u32 src_ip[4];
++	u32 dest_ip[4];
++
++	union {
++		struct {
++			u8 protocol;
++			u8 _pad[3]; /* fill with 0xa5a5a5 */
++		}; /* 3-tuple */
++		struct {
++			u16 dest_port;
++			u16 src_port;
++		}; /* 5-tuple */
++		u32 ports;
++	};
++
++	u32 _rsv[3];
++
++	u32 udf;
++
++	u32 ib2;
++	struct mtk_foe_mac_info l2;
++};
++
++struct mtk_foe_ipv6_6rd {
++	u32 src_ip[4];
++	u32 dest_ip[4];
++	u16 dest_port;
++	u16 src_port;
++
++	u32 tunnel_src_ip;
++	u32 tunnel_dest_ip;
++
++	u16 hdr_csum;
++	u8 dscp;
++	u8 ttl;
++
++	u8 flag;
++	u8 pad;
++	u8 per_flow_6rd_id;
++	u8 pad2;
++
++	u32 ib2;
++	struct mtk_foe_mac_info l2;
++};
++
++struct mtk_foe_entry {
++	u32 ib1;
++
++	union {
++		struct mtk_foe_bridge bridge;
++		struct mtk_foe_ipv4 ipv4;
++		struct mtk_foe_ipv4_dslite dslite;
++		struct mtk_foe_ipv6 ipv6;
++		struct mtk_foe_ipv6_6rd ipv6_6rd;
++		u32 data[19];
++	};
++};
++
++enum {
++	MTK_PPE_CPU_REASON_TTL_EXCEEDED			= 0x02,
++	MTK_PPE_CPU_REASON_OPTION_HEADER		= 0x03,
++	MTK_PPE_CPU_REASON_NO_FLOW			= 0x07,
++	MTK_PPE_CPU_REASON_IPV4_FRAG			= 0x08,
++	MTK_PPE_CPU_REASON_IPV4_DSLITE_FRAG		= 0x09,
++	MTK_PPE_CPU_REASON_IPV4_DSLITE_NO_TCP_UDP	= 0x0a,
++	MTK_PPE_CPU_REASON_IPV6_6RD_NO_TCP_UDP		= 0x0b,
++	MTK_PPE_CPU_REASON_TCP_FIN_SYN_RST		= 0x0c,
++	MTK_PPE_CPU_REASON_UN_HIT			= 0x0d,
++	MTK_PPE_CPU_REASON_HIT_UNBIND			= 0x0e,
++	MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED	= 0x0f,
++	MTK_PPE_CPU_REASON_HIT_BIND_TCP_FIN		= 0x10,
++	MTK_PPE_CPU_REASON_HIT_TTL_1			= 0x11,
++	MTK_PPE_CPU_REASON_HIT_BIND_VLAN_VIOLATION	= 0x12,
++	MTK_PPE_CPU_REASON_KEEPALIVE_UC_OLD_HDR		= 0x13,
++	MTK_PPE_CPU_REASON_KEEPALIVE_MC_NEW_HDR		= 0x14,
++	MTK_PPE_CPU_REASON_KEEPALIVE_DUP_OLD_HDR	= 0x15,
++	MTK_PPE_CPU_REASON_HIT_BIND_FORCE_CPU		= 0x16,
++	MTK_PPE_CPU_REASON_TUNNEL_OPTION_HEADER		= 0x17,
++	MTK_PPE_CPU_REASON_MULTICAST_TO_CPU		= 0x18,
++	MTK_PPE_CPU_REASON_MULTICAST_TO_GMAC1_CPU	= 0x19,
++	MTK_PPE_CPU_REASON_HIT_PRE_BIND			= 0x1a,
++	MTK_PPE_CPU_REASON_PACKET_SAMPLING		= 0x1b,
++	MTK_PPE_CPU_REASON_EXCEED_MTU			= 0x1c,
++	MTK_PPE_CPU_REASON_PPE_BYPASS			= 0x1e,
++	MTK_PPE_CPU_REASON_INVALID			= 0x1f,
++};
++
++struct mtk_ppe {
++	struct device *dev;
++	void __iomem *base;
++	int version;
++
++	struct mtk_foe_entry *foe_table;
++	dma_addr_t foe_phys;
++
++	void *acct_table;
++};
++
++int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base);
++int mtk_ppe_start(struct mtk_ppe *ppe);
++int mtk_ppe_stop(struct mtk_ppe *ppe);
++
++static inline void
++mtk_foe_entry_clear(struct mtk_ppe *ppe, u16 hash)
++{
++	ppe->foe_table[hash].ib1 = 0;
++	dma_wmb();
++}
++
++static inline int
++mtk_foe_entry_timestamp(struct mtk_ppe *ppe, u16 hash)
++{
++	u32 ib1 = READ_ONCE(ppe->foe_table[hash].ib1);
++
++	if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND)
++		return -1;
++
++	return FIELD_GET(MTK_FOE_IB1_BIND_TIMESTAMP, ib1);
++}
++
++int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
++			  u8 pse_port, u8 *src_mac, u8 *dest_mac);
++int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port);
++int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool orig,
++				 __be32 src_addr, __be16 src_port,
++				 __be32 dest_addr, __be16 dest_port);
++int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry,
++				 __be32 *src_addr, __be16 src_port,
++				 __be32 *dest_addr, __be16 dest_port);
++int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port);
++int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid);
++int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
++int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
++			 u16 timestamp);
++int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
++
++#endif
+diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+new file mode 100644
+index 0000000..d4b4823
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+@@ -0,0 +1,214 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#include <linux/kernel.h>
++#include <linux/debugfs.h>
++#include "mtk_eth_soc.h"
++
++struct mtk_flow_addr_info
++{
++	void *src, *dest;
++	u16 *src_port, *dest_port;
++	bool ipv6;
++};
++
++static const char *mtk_foe_entry_state_str(int state)
++{
++	static const char * const state_str[] = {
++		[MTK_FOE_STATE_INVALID] = "INV",
++		[MTK_FOE_STATE_UNBIND] = "UNB",
++		[MTK_FOE_STATE_BIND] = "BND",
++		[MTK_FOE_STATE_FIN] = "FIN",
++	};
++
++	if (state >= ARRAY_SIZE(state_str) || !state_str[state])
++		return "UNK";
++
++	return state_str[state];
++}
++
++static const char *mtk_foe_pkt_type_str(int type)
++{
++	static const char * const type_str[] = {
++		[MTK_PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
++		[MTK_PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
++		[MTK_PPE_PKT_TYPE_BRIDGE] = "L2",
++		[MTK_PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
++		[MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
++		[MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",
++		[MTK_PPE_PKT_TYPE_IPV6_6RD] = "6RD",
++	};
++
++	if (type >= ARRAY_SIZE(type_str) || !type_str[type])
++		return "UNKNOWN";
++
++	return type_str[type];
++}
++
++static void
++mtk_print_addr(struct seq_file *m, u32 *addr, bool ipv6)
++{
++	u32 n_addr[4];
++	int i;
++
++	if (!ipv6) {
++		seq_printf(m, "%pI4h", addr);
++		return;
++	}
++
++	for (i = 0; i < ARRAY_SIZE(n_addr); i++)
++		n_addr[i] = htonl(addr[i]);
++	seq_printf(m, "%pI6", n_addr);
++}
++
++static void
++mtk_print_addr_info(struct seq_file *m, struct mtk_flow_addr_info *ai)
++{
++	mtk_print_addr(m, ai->src, ai->ipv6);
++	if (ai->src_port)
++		seq_printf(m, ":%d", *ai->src_port);
++	seq_printf(m, "->");
++	mtk_print_addr(m, ai->dest, ai->ipv6);
++	if (ai->dest_port)
++		seq_printf(m, ":%d", *ai->dest_port);
++}
++
++static int
++mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind)
++{
++	struct mtk_ppe *ppe = m->private;
++	int i;
++
++	for (i = 0; i < MTK_PPE_ENTRIES; i++) {
++		struct mtk_foe_entry *entry = &ppe->foe_table[i];
++		struct mtk_foe_mac_info *l2;
++		struct mtk_flow_addr_info ai = {};
++		unsigned char h_source[ETH_ALEN];
++		unsigned char h_dest[ETH_ALEN];
++		int type, state;
++		u32 ib2;
++
++
++		state = FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1);
++		if (!state)
++			continue;
++
++		if (bind && state != MTK_FOE_STATE_BIND)
++			continue;
++
++		type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++		seq_printf(m, "%05x %s %7s", i,
++			   mtk_foe_entry_state_str(state),
++			   mtk_foe_pkt_type_str(type));
++
++		switch (type) {
++		case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
++		case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++			ai.src_port = &entry->ipv4.orig.src_port;
++			ai.dest_port = &entry->ipv4.orig.dest_port;
++			fallthrough;
++		case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
++			ai.src = &entry->ipv4.orig.src_ip;
++			ai.dest = &entry->ipv4.orig.dest_ip;
++			break;
++		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
++			ai.src_port = &entry->ipv6.src_port;
++			ai.dest_port = &entry->ipv6.dest_port;
++			fallthrough;
++		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
++		case MTK_PPE_PKT_TYPE_IPV6_6RD:
++			ai.src = &entry->ipv6.src_ip;
++			ai.dest = &entry->ipv6.dest_ip;
++			ai.ipv6 = true;
++			break;
++		}
++
++		seq_printf(m, " orig=");
++		mtk_print_addr_info(m, &ai);
++
++		switch (type) {
++		case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
++		case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
++			ai.src_port = &entry->ipv4.new.src_port;
++			ai.dest_port = &entry->ipv4.new.dest_port;
++			fallthrough;
++		case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
++			ai.src = &entry->ipv4.new.src_ip;
++			ai.dest = &entry->ipv4.new.dest_ip;
++			seq_printf(m, " new=");
++			mtk_print_addr_info(m, &ai);
++			break;
++		}
++
++		if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
++			l2 = &entry->ipv6.l2;
++			ib2 = entry->ipv6.ib2;
++		} else {
++			l2 = &entry->ipv4.l2;
++			ib2 = entry->ipv4.ib2;
++		}
++
++		*((__be32 *)h_source) = htonl(l2->src_mac_hi);
++		*((__be16 *)&h_source[4]) = htons(l2->src_mac_lo);
++		*((__be32 *)h_dest) = htonl(l2->dest_mac_hi);
++		*((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo);
++
++		seq_printf(m, " eth=%pM->%pM etype=%04x"
++			      " vlan=%d,%d ib1=%08x ib2=%08x\n",
++			   h_source, h_dest, ntohs(l2->etype),
++			   l2->vlan1, l2->vlan2, entry->ib1, ib2);
++	}
++
++	return 0;
++}
++
++static int
++mtk_ppe_debugfs_foe_show_all(struct seq_file *m, void *private)
++{
++	return mtk_ppe_debugfs_foe_show(m, private, false);
++}
++
++static int
++mtk_ppe_debugfs_foe_show_bind(struct seq_file *m, void *private)
++{
++	return mtk_ppe_debugfs_foe_show(m, private, true);
++}
++
++static int
++mtk_ppe_debugfs_foe_open_all(struct inode *inode, struct file *file)
++{
++	return single_open(file, mtk_ppe_debugfs_foe_show_all,
++			   inode->i_private);
++}
++
++static int
++mtk_ppe_debugfs_foe_open_bind(struct inode *inode, struct file *file)
++{
++	return single_open(file, mtk_ppe_debugfs_foe_show_bind,
++			   inode->i_private);
++}
++
++int mtk_ppe_debugfs_init(struct mtk_ppe *ppe)
++{
++	static const struct file_operations fops_all = {
++		.open = mtk_ppe_debugfs_foe_open_all,
++		.read = seq_read,
++		.llseek = seq_lseek,
++		.release = single_release,
++	};
++
++	static const struct file_operations fops_bind = {
++		.open = mtk_ppe_debugfs_foe_open_bind,
++		.read = seq_read,
++		.llseek = seq_lseek,
++		.release = single_release,
++	};
++
++	struct dentry *root;
++
++	root = debugfs_create_dir("mtk_ppe", NULL);
++	debugfs_create_file("entries", S_IRUGO, root, ppe, &fops_all);
++	debugfs_create_file("bind", S_IRUGO, root, ppe, &fops_bind);
++
++	return 0;
++}
+diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+new file mode 100644
+index 0000000..1380ef0
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -0,0 +1,535 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ *  Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
++ */
++
++#include <linux/if_ether.h>
++#include <linux/rhashtable.h>
++#include <linux/ip.h>
++#include <linux/ipv6.h>
++#include <net/flow_offload.h>
++#include <net/pkt_cls.h>
++#include <net/dsa.h>
++#include "mtk_eth_soc.h"
++
++struct mtk_flow_data {
++	struct ethhdr eth;
++
++	union {
++		struct {
++			__be32 src_addr;
++			__be32 dst_addr;
++		} v4;
++
++		struct {
++			struct in6_addr src_addr;
++			struct in6_addr dst_addr;
++		} v6;
++	};
++
++	__be16 src_port;
++	__be16 dst_port;
++
++	struct {
++		u16 id;
++		__be16 proto;
++		u8 num;
++	} vlan;
++	struct {
++		u16 sid;
++		u8 num;
++	} pppoe;
++};
++
++struct mtk_flow_entry {
++	struct rhash_head node;
++	unsigned long cookie;
++	u16 hash;
++};
++
++static const struct rhashtable_params mtk_flow_ht_params = {
++	.head_offset = offsetof(struct mtk_flow_entry, node),
++	.key_offset = offsetof(struct mtk_flow_entry, cookie),
++	.key_len = sizeof(unsigned long),
++	.automatic_shrinking = true,
++};
++
++static u32
++mtk_eth_timestamp(struct mtk_eth *eth)
++{
++	return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP;
++}
++
++static int
++mtk_flow_set_ipv4_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data,
++		       bool egress)
++{
++	return mtk_foe_entry_set_ipv4_tuple(foe, egress,
++					    data->v4.src_addr, data->src_port,
++					    data->v4.dst_addr, data->dst_port);
++}
++
++static int
++mtk_flow_set_ipv6_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data)
++{
++	return mtk_foe_entry_set_ipv6_tuple(foe,
++					    data->v6.src_addr.s6_addr32, data->src_port,
++					    data->v6.dst_addr.s6_addr32, data->dst_port);
++}
++
++static void
++mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
++{
++	void *dest = eth + act->mangle.offset;
++	const void *src = &act->mangle.val;
++
++	if (act->mangle.offset > 8)
++		return;
++
++	if (act->mangle.mask == 0xffff) {
++		src += 2;
++		dest += 2;
++	}
++
++	memcpy(dest, src, act->mangle.mask ? 2 : 4);
++}
++
++
++static int
++mtk_flow_mangle_ports(const struct flow_action_entry *act,
++		      struct mtk_flow_data *data)
++{
++	u32 val = ntohl(act->mangle.val);
++
++	switch (act->mangle.offset) {
++	case 0:
++		if (act->mangle.mask == ~htonl(0xffff))
++			data->dst_port = cpu_to_be16(val);
++		else
++			data->src_port = cpu_to_be16(val >> 16);
++		break;
++	case 2:
++		data->dst_port = cpu_to_be16(val);
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static int
++mtk_flow_mangle_ipv4(const struct flow_action_entry *act,
++		     struct mtk_flow_data *data)
++{
++	__be32 *dest;
++
++	switch (act->mangle.offset) {
++	case offsetof(struct iphdr, saddr):
++		dest = &data->v4.src_addr;
++		break;
++	case offsetof(struct iphdr, daddr):
++		dest = &data->v4.dst_addr;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	memcpy(dest, &act->mangle.val, sizeof(u32));
++
++	return 0;
++}
++
++static int
++mtk_flow_get_dsa_port(struct net_device **dev)
++{
++#if IS_ENABLED(CONFIG_NET_DSA)
++	struct dsa_port *dp;
++
++	dp = dsa_port_from_netdev(*dev);
++	if (IS_ERR(dp))
++		return -ENODEV;
++
++	if (dp->cpu_dp->tag_ops->proto != DSA_TAG_PROTO_MTK)
++		return -ENODEV;
++
++	*dev = dp->cpu_dp->master;
++
++	return dp->index;
++#else
++	return -ENODEV;
++#endif
++}
++
++static int
++mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
++			   struct net_device *dev)
++{
++	int pse_port, dsa_port;
++
++	dsa_port = mtk_flow_get_dsa_port(&dev);
++	if (dsa_port >= 0)
++		mtk_foe_entry_set_dsa(foe, dsa_port);
++
++	if (dev == eth->netdev[0])
++		pse_port = PSE_GDM1_PORT;
++	else if (dev == eth->netdev[1])
++		pse_port = PSE_GDM2_PORT;
++	else
++		return -EOPNOTSUPP;
++
++	mtk_foe_entry_set_pse_port(foe, pse_port);
++
++	return 0;
++}
++
++static int
++mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
++{
++	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
++	struct flow_action_entry *act;
++	struct mtk_flow_data data = {};
++	struct mtk_foe_entry foe;
++	struct net_device *odev = NULL;
++	struct mtk_flow_entry *entry;
++	int offload_type = 0;
++	u16 addr_type = 0;
++	u32 timestamp;
++	u8 l4proto = 0;
++	int err = 0;
++	int hash;
++	int i;
++
++	if (rhashtable_lookup(&eth->flow_table, &f->cookie, mtk_flow_ht_params))
++		return -EEXIST;
++
++	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) {
++		struct flow_match_meta match;
++
++		flow_rule_match_meta(rule, &match);
++	} else {
++		return -EOPNOTSUPP;
++	}
++
++	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
++		struct flow_match_control match;
++
++		flow_rule_match_control(rule, &match);
++		addr_type = match.key->addr_type;
++	} else {
++		return -EOPNOTSUPP;
++	}
++
++	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
++		struct flow_match_basic match;
++
++		flow_rule_match_basic(rule, &match);
++		l4proto = match.key->ip_proto;
++	} else {
++		return -EOPNOTSUPP;
++	}
++
++	flow_action_for_each(i, act, &rule->action) {
++		switch (act->id) {
++		case FLOW_ACTION_MANGLE:
++			if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH)
++				mtk_flow_offload_mangle_eth(act, &data.eth);
++			break;
++		case FLOW_ACTION_REDIRECT:
++			odev = act->dev;
++			break;
++		case FLOW_ACTION_CSUM:
++			break;
++		case FLOW_ACTION_VLAN_PUSH:
++			if (data.vlan.num == 1 ||
++			    act->vlan.proto != htons(ETH_P_8021Q))
++				return -EOPNOTSUPP;
++
++			data.vlan.id = act->vlan.vid;
++			data.vlan.proto = act->vlan.proto;
++			data.vlan.num++;
++			break;
++		case FLOW_ACTION_VLAN_POP:
++			break;
++		case FLOW_ACTION_PPPOE_PUSH:
++			if (data.pppoe.num == 1)
++				return -EOPNOTSUPP;
++
++			data.pppoe.sid = act->pppoe.sid;
++			data.pppoe.num++;
++			break;
++		default:
++			return -EOPNOTSUPP;
++		}
++	}
++
++	switch (addr_type) {
++	case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
++		offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
++		break;
++	case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
++		offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
++		break;
++	default:
++		return -EOPNOTSUPP;
++	}
++
++	if (!is_valid_ether_addr(data.eth.h_source) ||
++	    !is_valid_ether_addr(data.eth.h_dest))
++		return -EINVAL;
++
++	err = mtk_foe_entry_prepare(&foe, offload_type, l4proto, 0,
++				    data.eth.h_source,
++				    data.eth.h_dest);
++	if (err)
++		return err;
++
++	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
++		struct flow_match_ports ports;
++
++		flow_rule_match_ports(rule, &ports);
++		data.src_port = ports.key->src;
++		data.dst_port = ports.key->dst;
++	} else {
++		return -EOPNOTSUPP;
++	}
++
++	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
++		struct flow_match_ipv4_addrs addrs;
++
++		flow_rule_match_ipv4_addrs(rule, &addrs);
++
++		data.v4.src_addr = addrs.key->src;
++		data.v4.dst_addr = addrs.key->dst;
++
++		mtk_flow_set_ipv4_addr(&foe, &data, false);
++	}
++
++	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
++		struct flow_match_ipv6_addrs addrs;
++
++		flow_rule_match_ipv6_addrs(rule, &addrs);
++
++		data.v6.src_addr = addrs.key->src;
++		data.v6.dst_addr = addrs.key->dst;
++
++		mtk_flow_set_ipv6_addr(&foe, &data);
++	}
++
++	flow_action_for_each(i, act, &rule->action) {
++		if (act->id != FLOW_ACTION_MANGLE)
++			continue;
++
++		switch (act->mangle.htype) {
++		case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
++		case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
++			err = mtk_flow_mangle_ports(act, &data);
++			break;
++		case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
++			err = mtk_flow_mangle_ipv4(act, &data);
++			break;
++		case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
++			/* handled earlier */
++			break;
++		default:
++			return -EOPNOTSUPP;
++		}
++
++		if (err)
++			return err;
++	}
++
++	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
++		err = mtk_flow_set_ipv4_addr(&foe, &data, true);
++		if (err)
++			return err;
++	}
++
++	if (data.vlan.num == 1) {
++		if (data.vlan.proto != htons(ETH_P_8021Q))
++			return -EOPNOTSUPP;
++
++		mtk_foe_entry_set_vlan(&foe, data.vlan.id);
++	}
++	if (data.pppoe.num == 1)
++		mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid);
++
++	err = mtk_flow_set_output_device(eth, &foe, odev);
++	if (err)
++		return err;
++
++	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
++	if (!entry)
++		return -ENOMEM;
++
++	entry->cookie = f->cookie;
++	timestamp = mtk_eth_timestamp(eth);
++	hash = mtk_foe_entry_commit(&eth->ppe, &foe, timestamp);
++	if (hash < 0) {
++		err = hash;
++		goto free;
++	}
++
++	entry->hash = hash;
++	err = rhashtable_insert_fast(&eth->flow_table, &entry->node,
++				     mtk_flow_ht_params);
++	if (err < 0)
++		goto clear_flow;
++
++	return 0;
++clear_flow:
++	mtk_foe_entry_clear(&eth->ppe, hash);
++free:
++	kfree(entry);
++	return err;
++}
++
++static int
++mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f)
++{
++	struct mtk_flow_entry *entry;
++
++	entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
++				  mtk_flow_ht_params);
++	if (!entry)
++		return -ENOENT;
++
++	mtk_foe_entry_clear(&eth->ppe, entry->hash);
++	rhashtable_remove_fast(&eth->flow_table, &entry->node,
++			       mtk_flow_ht_params);
++	kfree(entry);
++
++	return 0;
++}
++
++static int
++mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
++{
++	struct mtk_flow_entry *entry;
++	int timestamp;
++	u32 idle;
++
++	entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
++				  mtk_flow_ht_params);
++	if (!entry)
++		return -ENOENT;
++
++	timestamp = mtk_foe_entry_timestamp(&eth->ppe, entry->hash);
++	if (timestamp < 0)
++		return -ETIMEDOUT;
++
++	idle = mtk_eth_timestamp(eth) - timestamp;
++	f->stats.lastused = jiffies - idle * HZ;
++
++	return 0;
++}
++
++static DEFINE_MUTEX(mtk_flow_offload_mutex);
++
++static int
++mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
++{
++	struct flow_cls_offload *cls = type_data;
++	struct net_device *dev = cb_priv;
++	struct mtk_mac *mac = netdev_priv(dev);
++	struct mtk_eth *eth = mac->hw;
++	int err;
++
++	if (!tc_can_offload(dev))
++		return -EOPNOTSUPP;
++
++	if (type != TC_SETUP_CLSFLOWER)
++		return -EOPNOTSUPP;
++
++	mutex_lock(&mtk_flow_offload_mutex);
++	switch (cls->command) {
++	case FLOW_CLS_REPLACE:
++		err = mtk_flow_offload_replace(eth, cls);
++		break;
++	case FLOW_CLS_DESTROY:
++		err = mtk_flow_offload_destroy(eth, cls);
++		break;
++	case FLOW_CLS_STATS:
++		err = mtk_flow_offload_stats(eth, cls);
++		break;
++	default:
++		err = -EOPNOTSUPP;
++		break;
++	}
++	mutex_unlock(&mtk_flow_offload_mutex);
++
++	return err;
++}
++
++static int
++mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
++{
++	struct mtk_mac *mac = netdev_priv(dev);
++	struct mtk_eth *eth = mac->hw;
++	static LIST_HEAD(block_cb_list);
++	struct flow_block_cb *block_cb;
++	flow_setup_cb_t *cb;
++	int err = 0;
++
++	if (!eth->ppe.foe_table)
++		return -EOPNOTSUPP;
++
++	if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
++		return -EOPNOTSUPP;
++
++	cb = mtk_eth_setup_tc_block_cb;
++	f->driver_block_list = &block_cb_list;
++
++	switch (f->command) {
++	case FLOW_BLOCK_BIND:
++		block_cb = flow_block_cb_lookup(f->block, cb, dev);
++		if (block_cb) {
++			flow_block_cb_incref(block_cb);
++			goto unlock;
++		}
++		block_cb = flow_block_cb_alloc(cb, dev, dev, NULL);
++		if (IS_ERR(block_cb)) {
++			err = PTR_ERR(block_cb);
++			goto unlock;
++		}
++
++		flow_block_cb_add(block_cb, f);
++		list_add_tail(&block_cb->driver_list, &block_cb_list);
++		break;
++	case FLOW_BLOCK_UNBIND:
++		block_cb = flow_block_cb_lookup(f->block, cb, dev);
++		if (!block_cb) {
++			err = -ENOENT;
++			goto unlock;
++		}
++
++		if (flow_block_cb_decref(block_cb)) {
++			flow_block_cb_remove(block_cb, f);
++			list_del(&block_cb->driver_list);
++		}
++		break;
++	default:
++		err = -EOPNOTSUPP;
++		break;
++	}
++
++unlock:
++	return err;
++}
++
++int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
++		     void *type_data)
++{
++	if (type == TC_SETUP_FT)
++		return mtk_eth_setup_tc_block(dev, type_data);
++
++	return -EOPNOTSUPP;
++}
++
++int mtk_eth_offload_init(struct mtk_eth *eth)
++{
++	if (!eth->ppe.foe_table)
++		return 0;
++
++	return rhashtable_init(&eth->flow_table, &mtk_flow_ht_params);
++}
+diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+new file mode 100644
+index 0000000..0c45ea0
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+@@ -0,0 +1,144 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#ifndef __MTK_PPE_REGS_H
++#define __MTK_PPE_REGS_H
++
++#define MTK_PPE_GLO_CFG				0x200
++#define MTK_PPE_GLO_CFG_EN			BIT(0)
++#define MTK_PPE_GLO_CFG_TSID_EN			BIT(1)
++#define MTK_PPE_GLO_CFG_IP4_L4_CS_DROP		BIT(2)
++#define MTK_PPE_GLO_CFG_IP4_CS_DROP		BIT(3)
++#define MTK_PPE_GLO_CFG_TTL0_DROP		BIT(4)
++#define MTK_PPE_GLO_CFG_PPE_BSWAP		BIT(5)
++#define MTK_PPE_GLO_CFG_PSE_HASH_OFS		BIT(6)
++#define MTK_PPE_GLO_CFG_MCAST_TB_EN		BIT(7)
++#define MTK_PPE_GLO_CFG_FLOW_DROP_KA		BIT(8)
++#define MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE	BIT(9)
++#define MTK_PPE_GLO_CFG_UDP_LITE_EN		BIT(10)
++#define MTK_PPE_GLO_CFG_UDP_LEN_DROP		BIT(11)
++#define MTK_PPE_GLO_CFG_MCAST_ENTRIES		GNEMASK(13, 12)
++#define MTK_PPE_GLO_CFG_BUSY			BIT(31)
++
++#define MTK_PPE_FLOW_CFG			0x204
++#define MTK_PPE_FLOW_CFG_IP4_TCP_FRAG		BIT(6)
++#define MTK_PPE_FLOW_CFG_IP4_UDP_FRAG		BIT(7)
++#define MTK_PPE_FLOW_CFG_IP6_3T_ROUTE		BIT(8)
++#define MTK_PPE_FLOW_CFG_IP6_5T_ROUTE		BIT(9)
++#define MTK_PPE_FLOW_CFG_IP6_6RD		BIT(10)
++#define MTK_PPE_FLOW_CFG_IP4_NAT		BIT(12)
++#define MTK_PPE_FLOW_CFG_IP4_NAPT		BIT(13)
++#define MTK_PPE_FLOW_CFG_IP4_DSLITE		BIT(14)
++#define MTK_PPE_FLOW_CFG_L2_BRIDGE		BIT(15)
++#define MTK_PPE_FLOW_CFG_IP_PROTO_BLACKLIST	BIT(16)
++#define MTK_PPE_FLOW_CFG_IP4_NAT_FRAG		BIT(17)
++#define MTK_PPE_FLOW_CFG_IP4_HASH_FLOW_LABEL	BIT(18)
++#define MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY	BIT(19)
++#define MTK_PPE_FLOW_CFG_IP6_HASH_GRE_KEY	BIT(20)
++
++#define MTK_PPE_IP_PROTO_CHK			0x208
++#define MTK_PPE_IP_PROTO_CHK_IPV4		GENMASK(15, 0)
++#define MTK_PPE_IP_PROTO_CHK_IPV6		GENMASK(31, 16)
++
++#define MTK_PPE_TB_CFG				0x21c
++#define MTK_PPE_TB_CFG_ENTRY_NUM		GENMASK(2, 0)
++#define MTK_PPE_TB_CFG_ENTRY_80B		BIT(3)
++#define MTK_PPE_TB_CFG_SEARCH_MISS		GENMASK(5, 4)
++#define MTK_PPE_TB_CFG_AGE_PREBIND		BIT(6)
++#define MTK_PPE_TB_CFG_AGE_NON_L4		BIT(7)
++#define MTK_PPE_TB_CFG_AGE_UNBIND		BIT(8)
++#define MTK_PPE_TB_CFG_AGE_TCP			BIT(9)
++#define MTK_PPE_TB_CFG_AGE_UDP			BIT(10)
++#define MTK_PPE_TB_CFG_AGE_TCP_FIN		BIT(11)
++#define MTK_PPE_TB_CFG_KEEPALIVE		GENMASK(13, 12)
++#define MTK_PPE_TB_CFG_HASH_MODE		GENMASK(15, 14)
++#define MTK_PPE_TB_CFG_SCAN_MODE		GENMASK(17, 16)
++#define MTK_PPE_TB_CFG_HASH_DEBUG		GENMASK(19, 18)
++
++enum {
++	MTK_PPE_SCAN_MODE_DISABLED,
++	MTK_PPE_SCAN_MODE_CHECK_AGE,
++	MTK_PPE_SCAN_MODE_KEEPALIVE_AGE,
++};
++
++enum {
++	MTK_PPE_KEEPALIVE_DISABLE,
++	MTK_PPE_KEEPALIVE_UNICAST_CPU,
++	MTK_PPE_KEEPALIVE_DUP_CPU = 3,
++};
++
++enum {
++	MTK_PPE_SEARCH_MISS_ACTION_DROP,
++	MTK_PPE_SEARCH_MISS_ACTION_FORWARD = 2,
++	MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD = 3,
++};
++
++#define MTK_PPE_TB_BASE				0x220
++
++#define MTK_PPE_TB_USED				0x224
++#define MTK_PPE_TB_USED_NUM			GENMASK(13, 0)
++
++#define MTK_PPE_BIND_RATE			0x228
++#define MTK_PPE_BIND_RATE_BIND			GENMASK(15, 0)
++#define MTK_PPE_BIND_RATE_PREBIND		GENMASK(31, 16)
++
++#define MTK_PPE_BIND_LIMIT0			0x22c
++#define MTK_PPE_BIND_LIMIT0_QUARTER		GENMASK(13, 0)
++#define MTK_PPE_BIND_LIMIT0_HALF		GENMASK(29, 16)
++
++#define MTK_PPE_BIND_LIMIT1			0x230
++#define MTK_PPE_BIND_LIMIT1_FULL		GENMASK(13, 0)
++#define MTK_PPE_BIND_LIMIT1_NON_L4		GENMASK(23, 16)
++
++#define MTK_PPE_KEEPALIVE			0x234
++#define MTK_PPE_KEEPALIVE_TIME			GENMASK(15, 0)
++#define MTK_PPE_KEEPALIVE_TIME_TCP		GENMASK(23, 16)
++#define MTK_PPE_KEEPALIVE_TIME_UDP		GENMASK(31, 24)
++
++#define MTK_PPE_UNBIND_AGE			0x238
++#define MTK_PPE_UNBIND_AGE_MIN_PACKETS		GENMASK(31, 16)
++#define MTK_PPE_UNBIND_AGE_DELTA		GENMASK(7, 0)
++
++#define MTK_PPE_BIND_AGE0			0x23c
++#define MTK_PPE_BIND_AGE0_DELTA_NON_L4		GENMASK(30, 16)
++#define MTK_PPE_BIND_AGE0_DELTA_UDP		GENMASK(14, 0)
++
++#define MTK_PPE_BIND_AGE1			0x240
++#define MTK_PPE_BIND_AGE1_DELTA_TCP_FIN		GENMASK(30, 16)
++#define MTK_PPE_BIND_AGE1_DELTA_TCP		GENMASK(14, 0)
++
++#define MTK_PPE_HASH_SEED			0x244
++
++#define MTK_PPE_DEFAULT_CPU_PORT		0x248
++#define MTK_PPE_DEFAULT_CPU_PORT_MASK(_n)	(GENMASK(2, 0) << ((_n) * 4))
++
++#define MTK_PPE_MTU_DROP			0x308
++
++#define MTK_PPE_VLAN_MTU0			0x30c
++#define MTK_PPE_VLAN_MTU0_NONE			GENMASK(13, 0)
++#define MTK_PPE_VLAN_MTU0_1TAG			GENMASK(29, 16)
++
++#define MTK_PPE_VLAN_MTU1			0x310
++#define MTK_PPE_VLAN_MTU1_2TAG			GENMASK(13, 0)
++#define MTK_PPE_VLAN_MTU1_3TAG			GENMASK(29, 16)
++
++#define MTK_PPE_VPM_TPID			0x318
++
++#define MTK_PPE_CACHE_CTL			0x320
++#define MTK_PPE_CACHE_CTL_EN			BIT(0)
++#define MTK_PPE_CACHE_CTL_LOCK_CLR		BIT(4)
++#define MTK_PPE_CACHE_CTL_REQ			BIT(8)
++#define MTK_PPE_CACHE_CTL_CLEAR			BIT(9)
++#define MTK_PPE_CACHE_CTL_CMD			GENMASK(13, 12)
++
++#define MTK_PPE_MIB_CFG				0x334
++#define MTK_PPE_MIB_CFG_EN			BIT(0)
++#define MTK_PPE_MIB_CFG_RD_CLR			BIT(1)
++
++#define MTK_PPE_MIB_TB_BASE			0x338
++
++#define MTK_PPE_MIB_CACHE_CTL			0x350
++#define MTK_PPE_MIB_CACHE_CTL_EN		BIT(0)
++#define MTK_PPE_MIB_CACHE_CTL_FLUSH		BIT(2)
++
++#endif
+-- 
+2.18.0
+
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3003-add-wed.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3003-add-wed.patch
index bfb726a..79672d4 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3003-add-wed.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3003-add-wed.patch
@@ -35,7 +35,7 @@
  create mode 100644 include/linux/soc/mediatek/mtk_wed.h
 
 diff --git a/arch/arm64/boot/dts/mediatek/mt7622.dtsi b/arch/arm64/boot/dts/mediatek/mt7622.dtsi
-index 753a97a..e46566a 100644
+index 9eeecff..3224350 100644
 --- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi
 +++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi
 @@ -338,7 +338,7 @@
@@ -47,7 +47,7 @@
  			interface-type = "ace";
  			reg = <0x5000 0x1000>;
  		};
-@@ -921,6 +921,11 @@
+@@ -920,6 +920,11 @@
  		};
  	};
  
@@ -59,7 +59,7 @@
  	ethsys: syscon@1b000000 {
  		compatible = "mediatek,mt7622-ethsys",
  			     "syscon";
-@@ -939,6 +944,26 @@
+@@ -938,6 +943,26 @@
  		#dma-cells = <1>;
  	};
  
@@ -84,9 +84,9 @@
 +	};
 +
  	eth: ethernet@1b100000 {
- 		compatible = "mediatek,mt7622-eth",
- 			     "mediatek,mt2701-eth",
-@@ -965,6 +990,11 @@
+ 		compatible = "mediatek,mt7622-eth";
+ 		reg = <0 0x1b100000 0 0x20000>;
+@@ -962,6 +987,11 @@
  		power-domains = <&scpsys MT7622_POWER_DOMAIN_ETHSYS>;
  		mediatek,ethsys = <&ethsys>;
  		mediatek,sgmiisys = <&sgmiisys>;
@@ -134,7 +134,7 @@
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 old mode 100755
 new mode 100644
-index 24b5593..15966b8
+index fe6ede6..4ae4a3b
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 @@ -22,11 +22,13 @@
@@ -151,7 +151,7 @@
  
  #if defined(CONFIG_NET_MEDIATEK_HNAT) || defined(CONFIG_NET_MEDIATEK_HNAT_MODULE)
  #include "mtk_hnat/nf_hnat_mtk.h"
-@@ -2388,6 +2390,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
+@@ -2505,6 +2507,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
  		struct net_device *netdev = NULL;
  		dma_addr_t dma_addr = DMA_MAPPING_ERROR;
  		u64 addr64 = 0;
@@ -159,7 +159,7 @@
  		int mac = 0;
  
  		idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
-@@ -2476,6 +2479,17 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
+@@ -2602,6 +2605,17 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
  			skb_checksum_none_assert(skb);
  		skb->protocol = eth_type_trans(skb, netdev);
  
@@ -177,7 +177,7 @@
  		if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
  			if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) {
  				if (trxd.rxd3 & RX_DMA_VTAG_V2)
-@@ -4293,7 +4307,7 @@ static int mtk_stop(struct net_device *dev)
+@@ -4445,7 +4459,7 @@ static int mtk_stop(struct net_device *dev)
  	mtk_dma_free(eth);
  
  	if (eth->soc->offload_version)
@@ -186,7 +186,7 @@
  
  	return 0;
  }
-@@ -5710,6 +5724,22 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -5908,6 +5922,22 @@ static int mtk_probe(struct platform_device *pdev)
  		}
  	}
  
@@ -209,7 +209,7 @@
  	if (MTK_HAS_CAPS(eth->soc->caps, MTK_PDMA_INT)) {
  		for (i = 0; i < MTK_PDMA_IRQ_NUM; i++)
  			eth->irq_pdma[i] = platform_get_irq(pdev, i);
-@@ -5726,7 +5756,8 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -5924,7 +5954,8 @@ static int mtk_probe(struct platform_device *pdev)
  
  		if (eth->irq_fe[i] < 0) {
  			dev_err(&pdev->dev, "no IRQ%d resource found\n", i);
@@ -219,7 +219,7 @@
  		}
  	}
  
-@@ -5734,12 +5765,15 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -5932,12 +5963,15 @@ static int mtk_probe(struct platform_device *pdev)
  		eth->clks[i] = devm_clk_get(eth->dev,
  					    mtk_clks_source_name[i]);
  		if (IS_ERR(eth->clks[i])) {
@@ -238,7 +238,7 @@
  			}
  			eth->clks[i] = NULL;
  		}
-@@ -5750,7 +5784,7 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -5948,7 +5982,7 @@ static int mtk_probe(struct platform_device *pdev)
  
  	err = mtk_hw_init(eth, MTK_TYPE_COLD_RESET);
  	if (err)
@@ -247,7 +247,7 @@
  
  	eth->hwlro = MTK_HAS_CAPS(eth->soc->caps, MTK_HWLRO);
  
-@@ -5879,14 +5913,15 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -6075,14 +6109,15 @@ static int mtk_probe(struct platform_device *pdev)
  	}
  
  	if (eth->soc->offload_version) {
@@ -268,7 +268,7 @@
  	}
  
  	for (i = 0; i < MTK_MAX_DEVS; i++) {
-@@ -5896,7 +5931,7 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -6092,7 +6127,7 @@ static int mtk_probe(struct platform_device *pdev)
  		err = register_netdev(eth->netdev[i]);
  		if (err) {
  			dev_err(eth->dev, "error bringing up device\n");
@@ -277,7 +277,7 @@
  		} else
  			netif_info(eth, probe, eth->netdev[i],
  				   "mediatek frame engine at 0x%08lx, irq %d\n",
-@@ -5946,12 +5981,15 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -6141,12 +6176,15 @@ static int mtk_probe(struct platform_device *pdev)
  
  	return 0;
  
@@ -294,7 +294,7 @@
  
  	return err;
  }
-@@ -5971,6 +6009,7 @@ static int mtk_remove(struct platform_device *pdev)
+@@ -6166,6 +6204,7 @@ static int mtk_remove(struct platform_device *pdev)
  		phylink_disconnect_phy(mac->phylink);
  	}
  
@@ -302,7 +302,7 @@
  	mtk_hw_deinit(eth);
  
  	for (i = 0; i < MTK_TX_NAPI_NUM; i++) {
-@@ -6002,6 +6041,7 @@ static const struct mtk_soc_data mt2701_data = {
+@@ -6196,6 +6235,7 @@ static const struct mtk_soc_data mt2701_data = {
  	.required_pctl = true,
  	.has_sram = false,
  	.offload_version = 1,
@@ -310,7 +310,7 @@
  	.rss_num = 0,
  	.txrx = {
  		.txd_size = sizeof(struct mtk_tx_dma),
-@@ -6023,6 +6063,7 @@ static const struct mtk_soc_data mt7621_data = {
+@@ -6217,6 +6257,7 @@ static const struct mtk_soc_data mt7621_data = {
  	.required_pctl = false,
  	.has_sram = false,
  	.offload_version = 1,
@@ -318,7 +318,7 @@
  	.rss_num = 0,
  	.txrx = {
  		.txd_size = sizeof(struct mtk_tx_dma),
-@@ -6045,6 +6086,7 @@ static const struct mtk_soc_data mt7622_data = {
+@@ -6239,6 +6280,7 @@ static const struct mtk_soc_data mt7622_data = {
  	.required_pctl = false,
  	.has_sram = false,
  	.offload_version = 2,
@@ -326,7 +326,7 @@
  	.rss_num = 0,
  	.txrx = {
  		.txd_size = sizeof(struct mtk_tx_dma),
-@@ -6066,6 +6108,7 @@ static const struct mtk_soc_data mt7623_data = {
+@@ -6260,6 +6302,7 @@ static const struct mtk_soc_data mt7623_data = {
  	.required_pctl = true,
  	.has_sram = false,
  	.offload_version = 1,
@@ -337,10 +337,10 @@
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 old mode 100755
 new mode 100644
-index c9fb783..b8fd0c9
+index f9d1f91..01672eb
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -638,6 +638,9 @@
+@@ -619,6 +619,9 @@
  #define RX_DMA_SPORT_MASK       0x7
  #define RX_DMA_SPORT_MASK_V2    0xf
  
@@ -350,7 +350,7 @@
  /* QDMA descriptor txd4 */
  #define TX_DMA_CHKSUM		(0x7 << 29)
  #define TX_DMA_TSO		BIT(28)
-@@ -1739,6 +1742,7 @@ struct mtk_reg_map {
+@@ -1809,6 +1812,7 @@ struct mtk_reg_map {
   *				the target SoC
   * @required_pctl		A bool value to show whether the SoC requires
   *				the extra setup for those pins used by GMAC.
@@ -358,7 +358,7 @@
   * @txd_size			Tx DMA descriptor size.
   * @rxd_size			Rx DMA descriptor size.
   * @rx_dma_l4_valid		Rx DMA valid register mask.
-@@ -1753,6 +1757,7 @@ struct mtk_soc_data {
+@@ -1823,6 +1827,7 @@ struct mtk_soc_data {
  	u64		required_clks;
  	bool		required_pctl;
  	u8		offload_version;
@@ -366,18 +366,18 @@
  	netdev_features_t hw_features;
  	bool		has_sram;
  	struct {
-@@ -1964,7 +1969,7 @@ struct mtk_eth {
+@@ -2115,7 +2120,7 @@ struct mtk_eth {
+ 	spinlock_t			syscfg0_lock;
  	struct notifier_block		netdevice_notifier;
- 	struct timer_list		mtk_dma_monitor_timer;
  
 -	struct mtk_ppe			ppe;
 +	struct mtk_ppe			*ppe;
  	struct rhashtable		flow_table;
  };
  
-@@ -2021,6 +2026,14 @@ extern const struct of_device_id of_mtk_match[];
- extern u32 mtk_hwlro_stats_ebl;
- extern u32 dbg_show_level;
+@@ -2178,6 +2183,14 @@ extern u32 (*mtk_get_tnl_netsys_params)(struct sk_buff *skb);
+ extern struct net_device *(*mtk_get_tnl_dev)(u8 tops_crsn);
+ extern void (*mtk_set_tops_crsn)(struct sk_buff *skb, u8 tops_crsn);
  
 +static inline struct mtk_foe_entry *
 +mtk_foe_get_entry(struct mtk_ppe *ppe, u16 hash)
@@ -2957,7 +2957,7 @@
 +
 +#endif
 diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
-index 2d1aa35..85d582c 100644
+index 6c0860b..5020ccd 100644
 --- a/include/linux/netdevice.h
 +++ b/include/linux/netdevice.h
 @@ -849,6 +849,7 @@ enum net_device_path_type {
@@ -2965,10 +2965,10 @@
  	DEV_PATH_PPPOE,
  	DEV_PATH_DSA,
 +	DEV_PATH_MTK_WDMA,
- };
- 
- struct net_device_path {
-@@ -874,6 +875,12 @@ struct net_device_path {
+ 	DEV_PATH_DSLITE,
+ 	DEV_PATH_6RD,
+ 	DEV_PATH_TNL,
+@@ -877,6 +878,12 @@ struct net_device_path {
  			int port;
  			u16 proto;
  		} dsa;
@@ -3119,7 +3119,7 @@
 +
 +#endif
 diff --git a/net/core/dev.c b/net/core/dev.c
-index e9dd11e..64d3e41 100644
+index 7d29e7b..906d101 100644
 --- a/net/core/dev.c
 +++ b/net/core/dev.c
 @@ -758,6 +758,10 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
@@ -3134,5 +3134,5 @@
  	if (!path)
  		return -1;
 -- 
-2.18.0
+2.45.2
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/640-netfilter-nf_flow_table-add-hardware-offload-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/640-netfilter-nf_flow_table-add-hardware-offload-support.patch
deleted file mode 100644
index 88d2cb1..0000000
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/640-netfilter-nf_flow_table-add-hardware-offload-support.patch
+++ /dev/null
@@ -1,564 +0,0 @@
-From: Pablo Neira Ayuso <pablo@netfilter.org>
-Date: Thu, 11 Jan 2018 16:32:00 +0100
-Subject: [PATCH] netfilter: nf_flow_table: add hardware offload support
-
-This patch adds the infrastructure to offload flows to hardware, in case
-the nic/switch comes with built-in flow tables capabilities.
-
-If the hardware comes with no hardware flow tables or they have
-limitations in terms of features, the existing infrastructure falls back
-to the software flow table implementation.
-
-The software flow table garbage collector skips entries that resides in
-the hardware, so the hardware will be responsible for releasing this
-flow table entry too via flow_offload_dead().
-
-Hardware configuration, either to add or to delete entries, is done from
-the hardware offload workqueue, to ensure this is done from user context
-given that we may sleep when grabbing the mdio mutex.
-
-Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
----
- create mode 100644 net/netfilter/nf_flow_table_hw.c
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -931,6 +931,13 @@ struct devlink;
- struct tlsdev_ops;
- 
- 
-+struct flow_offload;
-+
-+enum flow_offload_type {
-+	FLOW_OFFLOAD_ADD	= 0,
-+	FLOW_OFFLOAD_DEL,
-+};
-+
- /*
-  * This structure defines the management hooks for network devices.
-  * The following hooks can be defined; unless noted otherwise, they are
-@@ -1163,6 +1170,10 @@ struct tlsdev_ops;
-  * int (*ndo_bridge_dellink)(struct net_device *dev, struct nlmsghdr *nlh,
-  *			     u16 flags);
-  *
-+ * int (*ndo_flow_offload)(enum flow_offload_type type,
-+ *			   struct flow_offload *flow);
-+ *	Adds/deletes flow entry to/from net device flowtable.
-+ *
-  * int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier);
-  *	Called to change device carrier. Soft-devices (like dummy, team, etc)
-  *	which do not represent real hardware may define this to allow their
-@@ -1410,6 +1421,8 @@ struct net_device_ops {
- 	int			(*ndo_bridge_dellink)(struct net_device *dev,
- 						      struct nlmsghdr *nlh,
- 						      u16 flags);
-+	int			(*ndo_flow_offload)(enum flow_offload_type type,
-+						    struct flow_offload *flow);
- 	int			(*ndo_change_carrier)(struct net_device *dev,
- 						      bool new_carrier);
- 	int			(*ndo_get_phys_port_id)(struct net_device *dev,
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -21,11 +21,17 @@ struct nf_flowtable_type {
- 	struct module			*owner;
- };
- 
-+enum nf_flowtable_flags {
-+	NF_FLOWTABLE_F_HW		= 0x1,
-+};
-+
- struct nf_flowtable {
- 	struct list_head		list;
- 	struct rhashtable		rhashtable;
- 	const struct nf_flowtable_type	*type;
-+	u32				flags;
- 	struct delayed_work		gc_work;
-+	possible_net_t			ft_net;
- };
- 
- enum flow_offload_tuple_dir {
-@@ -71,6 +77,7 @@ struct flow_offload_tuple_rhash {
- #define FLOW_OFFLOAD_DNAT	0x2
- #define FLOW_OFFLOAD_DYING	0x4
- #define FLOW_OFFLOAD_TEARDOWN	0x8
-+#define FLOW_OFFLOAD_HW		0x10
- 
- struct flow_offload {
- 	struct flow_offload_tuple_rhash		tuplehash[FLOW_OFFLOAD_DIR_MAX];
-@@ -123,6 +130,22 @@ unsigned int nf_flow_offload_ip_hook(voi
- unsigned int nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
- 				       const struct nf_hook_state *state);
- 
-+void nf_flow_offload_hw_add(struct net *net, struct flow_offload *flow,
-+			    struct nf_conn *ct);
-+void nf_flow_offload_hw_del(struct net *net, struct flow_offload *flow);
-+
-+struct nf_flow_table_hw {
-+	struct module	*owner;
-+	void		(*add)(struct net *net, struct flow_offload *flow,
-+			       struct nf_conn *ct);
-+	void		(*del)(struct net *net, struct flow_offload *flow);
-+};
-+
-+int nf_flow_table_hw_register(const struct nf_flow_table_hw *offload);
-+void nf_flow_table_hw_unregister(const struct nf_flow_table_hw *offload);
-+
-+extern struct work_struct nf_flow_offload_hw_work;
-+
- #define MODULE_ALIAS_NF_FLOWTABLE(family)	\
- 	MODULE_ALIAS("nf-flowtable-" __stringify(family))
- 
---- a/include/uapi/linux/netfilter/nf_tables.h
-+++ b/include/uapi/linux/netfilter/nf_tables.h
-@@ -1516,6 +1516,7 @@ enum nft_object_attributes {
-  * @NFTA_FLOWTABLE_HOOK: netfilter hook configuration(NLA_U32)
-  * @NFTA_FLOWTABLE_USE: number of references to this flow table (NLA_U32)
-  * @NFTA_FLOWTABLE_HANDLE: object handle (NLA_U64)
-+ * @NFTA_FLOWTABLE_FLAGS: flags (NLA_U32)
-  */
- enum nft_flowtable_attributes {
- 	NFTA_FLOWTABLE_UNSPEC,
-@@ -1525,6 +1526,7 @@ enum nft_flowtable_attributes {
- 	NFTA_FLOWTABLE_USE,
- 	NFTA_FLOWTABLE_HANDLE,
- 	NFTA_FLOWTABLE_PAD,
-+	NFTA_FLOWTABLE_FLAGS,
- 	__NFTA_FLOWTABLE_MAX
- };
- #define NFTA_FLOWTABLE_MAX	(__NFTA_FLOWTABLE_MAX - 1)
---- a/net/netfilter/Kconfig
-+++ b/net/netfilter/Kconfig
-@@ -710,6 +710,15 @@ config NF_FLOW_TABLE
- 
- 	  To compile it as a module, choose M here.
- 
-+config NF_FLOW_TABLE_HW
-+	tristate "Netfilter flow table hardware offload module"
-+	depends on NF_FLOW_TABLE
-+	help
-+	  This option adds hardware offload support for the flow table core
-+	  infrastructure.
-+
-+	  To compile it as a module, choose M here.
-+
- config NETFILTER_XTABLES
- 	tristate "Netfilter Xtables support (required for ip_tables)"
- 	default m if NETFILTER_ADVANCED=n
---- a/net/netfilter/Makefile
-+++ b/net/netfilter/Makefile
-@@ -123,6 +123,7 @@ obj-$(CONFIG_NF_FLOW_TABLE)	+= nf_flow_t
- nf_flow_table-objs := nf_flow_table_core.o nf_flow_table_ip.o
- 
- obj-$(CONFIG_NF_FLOW_TABLE_INET) += nf_flow_table_inet.o
-+obj-$(CONFIG_NF_FLOW_TABLE_HW)	+= nf_flow_table_hw.o
- 
- # generic X tables
- obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
---- a/net/netfilter/nf_flow_table_core.c
-+++ b/net/netfilter/nf_flow_table_core.c
-@@ -261,10 +261,16 @@ static inline bool nf_flow_has_expired(c
- 	return nf_flow_timeout_delta(flow->timeout) <= 0;
- }
- 
-+static inline bool nf_flow_in_hw(const struct flow_offload *flow)
-+{
-+	return flow->flags & FLOW_OFFLOAD_HW;
-+}
-+
- static void flow_offload_del(struct nf_flowtable *flow_table,
- 			     struct flow_offload *flow)
- {
- 	struct flow_offload_entry *e;
-+	struct net *net = read_pnet(&flow_table->ft_net);
- 
- 	rhashtable_remove_fast(&flow_table->rhashtable,
- 			       &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].node,
-@@ -284,6 +290,9 @@ static void flow_offload_del(struct nf_f
- 	if (!(flow->flags & FLOW_OFFLOAD_TEARDOWN))
- 		flow_offload_fixup_ct_state(e->ct);
- 
-+	if (nf_flow_in_hw(flow))
-+		nf_flow_offload_hw_del(net, flow);
-+
- 	flow_offload_free(flow);
- }
- 
-@@ -374,6 +383,9 @@ static void nf_flow_offload_gc_step(stru
- 	if (!teardown)
- 		nf_ct_offload_timeout(flow);
- 
-+	if (nf_flow_in_hw(flow) && !teardown)
-+		return;
-+
- 	if (nf_flow_has_expired(flow) || teardown)
- 		flow_offload_del(flow_table, flow);
- }
-@@ -503,10 +515,43 @@ int nf_flow_dnat_port(const struct flow_
- }
- EXPORT_SYMBOL_GPL(nf_flow_dnat_port);
- 
-+static const struct nf_flow_table_hw __rcu *nf_flow_table_hw_hook __read_mostly;
-+
-+static int nf_flow_offload_hw_init(struct nf_flowtable *flow_table)
-+{
-+	const struct nf_flow_table_hw *offload;
-+
-+	if (!rcu_access_pointer(nf_flow_table_hw_hook))
-+		request_module("nf-flow-table-hw");
-+
-+	rcu_read_lock();
-+	offload = rcu_dereference(nf_flow_table_hw_hook);
-+	if (!offload)
-+		goto err_no_hw_offload;
-+
-+	if (!try_module_get(offload->owner))
-+		goto err_no_hw_offload;
-+
-+	rcu_read_unlock();
-+
-+	return 0;
-+
-+err_no_hw_offload:
-+	rcu_read_unlock();
-+
-+	return -EOPNOTSUPP;
-+}
-+
- int nf_flow_table_init(struct nf_flowtable *flowtable)
- {
- 	int err;
- 
-+	if (flowtable->flags & NF_FLOWTABLE_F_HW) {
-+		err = nf_flow_offload_hw_init(flowtable);
-+		if (err)
-+			return err;
-+	}
-+
- 	INIT_DEFERRABLE_WORK(&flowtable->gc_work, nf_flow_offload_work_gc);
- 
- 	err = rhashtable_init(&flowtable->rhashtable,
-@@ -547,6 +592,8 @@ static void nf_flow_table_iterate_cleanu
- {
- 	nf_flow_table_iterate(flowtable, nf_flow_table_do_cleanup, dev);
- 	flush_delayed_work(&flowtable->gc_work);
-+	if (flowtable->flags & NF_FLOWTABLE_F_HW)
-+		flush_work(&nf_flow_offload_hw_work);
- }
- 
- void nf_flow_table_cleanup(struct net_device *dev)
-@@ -560,6 +607,26 @@ void nf_flow_table_cleanup(struct net_de
- }
- EXPORT_SYMBOL_GPL(nf_flow_table_cleanup);
- 
-+struct work_struct nf_flow_offload_hw_work;
-+EXPORT_SYMBOL_GPL(nf_flow_offload_hw_work);
-+
-+/* Give the hardware workqueue the chance to remove entries from hardware.*/
-+static void nf_flow_offload_hw_free(struct nf_flowtable *flowtable)
-+{
-+	const struct nf_flow_table_hw *offload;
-+
-+	flush_work(&nf_flow_offload_hw_work);
-+
-+	rcu_read_lock();
-+	offload = rcu_dereference(nf_flow_table_hw_hook);
-+	if (!offload) {
-+		rcu_read_unlock();
-+		return;
-+	}
-+	module_put(offload->owner);
-+	rcu_read_unlock();
-+}
-+
- void nf_flow_table_free(struct nf_flowtable *flow_table)
- {
- 	mutex_lock(&flowtable_lock);
-@@ -569,9 +636,58 @@ void nf_flow_table_free(struct nf_flowta
- 	nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL);
- 	nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, flow_table);
- 	rhashtable_destroy(&flow_table->rhashtable);
-+	if (flow_table->flags & NF_FLOWTABLE_F_HW)
-+		nf_flow_offload_hw_free(flow_table);
- }
- EXPORT_SYMBOL_GPL(nf_flow_table_free);
- 
-+/* Must be called from user context. */
-+void nf_flow_offload_hw_add(struct net *net, struct flow_offload *flow,
-+			    struct nf_conn *ct)
-+{
-+	const struct nf_flow_table_hw *offload;
-+
-+	rcu_read_lock();
-+	offload = rcu_dereference(nf_flow_table_hw_hook);
-+	if (offload)
-+		offload->add(net, flow, ct);
-+	rcu_read_unlock();
-+}
-+EXPORT_SYMBOL_GPL(nf_flow_offload_hw_add);
-+
-+/* Must be called from user context. */
-+void nf_flow_offload_hw_del(struct net *net, struct flow_offload *flow)
-+{
-+	const struct nf_flow_table_hw *offload;
-+
-+	rcu_read_lock();
-+	offload = rcu_dereference(nf_flow_table_hw_hook);
-+	if (offload)
-+		offload->del(net, flow);
-+	rcu_read_unlock();
-+}
-+EXPORT_SYMBOL_GPL(nf_flow_offload_hw_del);
-+
-+int nf_flow_table_hw_register(const struct nf_flow_table_hw *offload)
-+{
-+	if (rcu_access_pointer(nf_flow_table_hw_hook))
-+		return -EBUSY;
-+
-+	rcu_assign_pointer(nf_flow_table_hw_hook, offload);
-+
-+	return 0;
-+}
-+EXPORT_SYMBOL_GPL(nf_flow_table_hw_register);
-+
-+void nf_flow_table_hw_unregister(const struct nf_flow_table_hw *offload)
-+{
-+	WARN_ON(rcu_access_pointer(nf_flow_table_hw_hook) != offload);
-+	rcu_assign_pointer(nf_flow_table_hw_hook, NULL);
-+
-+	synchronize_rcu();
-+}
-+EXPORT_SYMBOL_GPL(nf_flow_table_hw_unregister);
-+
- static int nf_flow_table_netdev_event(struct notifier_block *this,
- 				      unsigned long event, void *ptr)
- {
---- /dev/null
-+++ b/net/netfilter/nf_flow_table_hw.c
-@@ -0,0 +1,169 @@
-+#include <linux/kernel.h>
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/netfilter.h>
-+#include <linux/rhashtable.h>
-+#include <linux/netdevice.h>
-+#include <net/netfilter/nf_flow_table.h>
-+#include <net/netfilter/nf_conntrack.h>
-+#include <net/netfilter/nf_conntrack_core.h>
-+#include <net/netfilter/nf_conntrack_tuple.h>
-+
-+static DEFINE_SPINLOCK(flow_offload_hw_pending_list_lock);
-+static LIST_HEAD(flow_offload_hw_pending_list);
-+
-+static DEFINE_MUTEX(nf_flow_offload_hw_mutex);
-+
-+struct flow_offload_hw {
-+	struct list_head	list;
-+	enum flow_offload_type	type;
-+	struct flow_offload	*flow;
-+	struct nf_conn		*ct;
-+	possible_net_t		flow_hw_net;
-+};
-+
-+static int do_flow_offload_hw(struct net *net, struct flow_offload *flow,
-+			      int type)
-+{
-+	struct net_device *indev;
-+	int ret, ifindex;
-+
-+	ifindex = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.iifidx;
-+	indev = dev_get_by_index(net, ifindex);
-+	if (WARN_ON(!indev))
-+		return 0;
-+
-+	mutex_lock(&nf_flow_offload_hw_mutex);
-+	ret = indev->netdev_ops->ndo_flow_offload(type, flow);
-+	mutex_unlock(&nf_flow_offload_hw_mutex);
-+
-+	dev_put(indev);
-+
-+	return ret;
-+}
-+
-+static void flow_offload_hw_work_add(struct flow_offload_hw *offload)
-+{
-+	struct net *net;
-+	int ret;
-+
-+	if (nf_ct_is_dying(offload->ct))
-+		return;
-+
-+	net = read_pnet(&offload->flow_hw_net);
-+	ret = do_flow_offload_hw(net, offload->flow, FLOW_OFFLOAD_ADD);
-+	if (ret >= 0)
-+		offload->flow->flags |= FLOW_OFFLOAD_HW;
-+}
-+
-+static void flow_offload_hw_work_del(struct flow_offload_hw *offload)
-+{
-+	struct net *net = read_pnet(&offload->flow_hw_net);
-+
-+	do_flow_offload_hw(net, offload->flow, FLOW_OFFLOAD_DEL);
-+}
-+
-+static void flow_offload_hw_work(struct work_struct *work)
-+{
-+	struct flow_offload_hw *offload, *next;
-+	LIST_HEAD(hw_offload_pending);
-+
-+	spin_lock_bh(&flow_offload_hw_pending_list_lock);
-+	list_replace_init(&flow_offload_hw_pending_list, &hw_offload_pending);
-+	spin_unlock_bh(&flow_offload_hw_pending_list_lock);
-+
-+	list_for_each_entry_safe(offload, next, &hw_offload_pending, list) {
-+		switch (offload->type) {
-+		case FLOW_OFFLOAD_ADD:
-+			flow_offload_hw_work_add(offload);
-+			break;
-+		case FLOW_OFFLOAD_DEL:
-+			flow_offload_hw_work_del(offload);
-+			break;
-+		}
-+		if (offload->ct)
-+			nf_conntrack_put(&offload->ct->ct_general);
-+		list_del(&offload->list);
-+		kfree(offload);
-+	}
-+}
-+
-+static void flow_offload_queue_work(struct flow_offload_hw *offload)
-+{
-+	spin_lock_bh(&flow_offload_hw_pending_list_lock);
-+	list_add_tail(&offload->list, &flow_offload_hw_pending_list);
-+	spin_unlock_bh(&flow_offload_hw_pending_list_lock);
-+
-+	schedule_work(&nf_flow_offload_hw_work);
-+}
-+
-+static void flow_offload_hw_add(struct net *net, struct flow_offload *flow,
-+				struct nf_conn *ct)
-+{
-+	struct flow_offload_hw *offload;
-+
-+	offload = kmalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
-+	if (!offload)
-+		return;
-+
-+	nf_conntrack_get(&ct->ct_general);
-+	offload->type = FLOW_OFFLOAD_ADD;
-+	offload->ct = ct;
-+	offload->flow = flow;
-+	write_pnet(&offload->flow_hw_net, net);
-+
-+	flow_offload_queue_work(offload);
-+}
-+
-+static void flow_offload_hw_del(struct net *net, struct flow_offload *flow)
-+{
-+	struct flow_offload_hw *offload;
-+
-+	offload = kmalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
-+	if (!offload)
-+		return;
-+
-+	offload->type = FLOW_OFFLOAD_DEL;
-+	offload->ct = NULL;
-+	offload->flow = flow;
-+	write_pnet(&offload->flow_hw_net, net);
-+
-+	flow_offload_queue_work(offload);
-+}
-+
-+static const struct nf_flow_table_hw flow_offload_hw = {
-+	.add	= flow_offload_hw_add,
-+	.del	= flow_offload_hw_del,
-+	.owner	= THIS_MODULE,
-+};
-+
-+static int __init nf_flow_table_hw_module_init(void)
-+{
-+	INIT_WORK(&nf_flow_offload_hw_work, flow_offload_hw_work);
-+	nf_flow_table_hw_register(&flow_offload_hw);
-+
-+	return 0;
-+}
-+
-+static void __exit nf_flow_table_hw_module_exit(void)
-+{
-+	struct flow_offload_hw *offload, *next;
-+	LIST_HEAD(hw_offload_pending);
-+
-+	nf_flow_table_hw_unregister(&flow_offload_hw);
-+	cancel_work_sync(&nf_flow_offload_hw_work);
-+
-+	list_for_each_entry_safe(offload, next, &hw_offload_pending, list) {
-+		if (offload->ct)
-+			nf_conntrack_put(&offload->ct->ct_general);
-+		list_del(&offload->list);
-+		kfree(offload);
-+	}
-+}
-+
-+module_init(nf_flow_table_hw_module_init);
-+module_exit(nf_flow_table_hw_module_exit);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
-+MODULE_ALIAS("nf-flow-table-hw");
---- a/net/netfilter/nf_tables_api.c
-+++ b/net/netfilter/nf_tables_api.c
-@@ -5758,6 +5758,13 @@ static int nf_tables_flowtable_parse_hoo
- 	if (err < 0)
- 		return err;
- 
-+	for (i = 0; i < n; i++) {
-+		if (flowtable->data.flags & NF_FLOWTABLE_F_HW &&
-+		    !dev_array[i]->netdev_ops->ndo_flow_offload) {
-+			return -EOPNOTSUPP;
-+		}
-+	}
-+
- 	ops = kcalloc(n, sizeof(struct nf_hook_ops), GFP_KERNEL);
- 	if (!ops)
- 		return -ENOMEM;
-@@ -5888,10 +5895,19 @@ static int nf_tables_newflowtable(struct
- 	}
- 
- 	flowtable->data.type = type;
-+	write_pnet(&flowtable->data.ft_net, net);
-+
- 	err = type->init(&flowtable->data);
- 	if (err < 0)
- 		goto err3;
- 
-+	if (nla[NFTA_FLOWTABLE_FLAGS]) {
-+		flowtable->data.flags =
-+			ntohl(nla_get_be32(nla[NFTA_FLOWTABLE_FLAGS]));
-+		if (flowtable->data.flags & ~NF_FLOWTABLE_F_HW)
-+			goto err4;
-+	}
-+
- 	err = nf_tables_flowtable_parse_hook(&ctx, nla[NFTA_FLOWTABLE_HOOK],
- 					     flowtable);
- 	if (err < 0)
-@@ -6017,7 +6033,8 @@ static int nf_tables_fill_flowtable_info
- 	    nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) ||
- 	    nla_put_be32(skb, NFTA_FLOWTABLE_USE, htonl(flowtable->use)) ||
- 	    nla_put_be64(skb, NFTA_FLOWTABLE_HANDLE, cpu_to_be64(flowtable->handle),
--			 NFTA_FLOWTABLE_PAD))
-+			 NFTA_FLOWTABLE_PAD) ||
-+	    nla_put_be32(skb, NFTA_FLOWTABLE_FLAGS, htonl(flowtable->data.flags)))
- 		goto nla_put_failure;
- 
- 	nest = nla_nest_start_noflag(skb, NFTA_FLOWTABLE_HOOK);
---- a/net/netfilter/nft_flow_offload.c
-+++ b/net/netfilter/nft_flow_offload.c
-@@ -128,6 +128,9 @@ static void nft_flow_offload_eval(const
- 	if (ret < 0)
- 		goto err_flow_add;
- 
-+	if (flowtable->flags & NF_FLOWTABLE_F_HW)
-+		nf_flow_offload_hw_add(nft_net(pkt), flow, ct);
-+
- 	dst_release(route.tuple[!dir].dst);
- 	return;
- 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/641-netfilter-nf_flow_table-support-hw-offload-through-v.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/641-netfilter-nf_flow_table-support-hw-offload-through-v.patch
deleted file mode 100644
index d766ca9..0000000
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/641-netfilter-nf_flow_table-support-hw-offload-through-v.patch
+++ /dev/null
@@ -1,306 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 15 Mar 2018 20:46:31 +0100
-Subject: [PATCH] netfilter: nf_flow_table: support hw offload through
- virtual interfaces
-
-There are hardware offload devices that support offloading VLANs and
-PPPoE devices. Additionally, it is useful to be able to offload packets
-routed through bridge interfaces as well.
-Add support for finding the path to the offload device through these
-virtual interfaces, while collecting useful parameters for the offload
-device, like VLAN ID/protocol, PPPoE session and Ethernet MAC address.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -932,6 +932,7 @@ struct tlsdev_ops;
- 
- 
- struct flow_offload;
-+struct flow_offload_hw_path;
- 
- enum flow_offload_type {
- 	FLOW_OFFLOAD_ADD	= 0,
-@@ -1170,8 +1171,15 @@ enum flow_offload_type {
-  * int (*ndo_bridge_dellink)(struct net_device *dev, struct nlmsghdr *nlh,
-  *			     u16 flags);
-  *
-+ * int (*ndo_flow_offload_check)(struct flow_offload_hw_path *path);
-+ *	For virtual devices like bridges, vlan, and pppoe, fill in the
-+ *	underlying network device that can be used for offloading connections.
-+ *	Return an error if offloading is not supported.
-+ *
-  * int (*ndo_flow_offload)(enum flow_offload_type type,
-- *			   struct flow_offload *flow);
-+ *			   struct flow_offload *flow,
-+ *			   struct flow_offload_hw_path *src,
-+ *			   struct flow_offload_hw_path *dest);
-  *	Adds/deletes flow entry to/from net device flowtable.
-  *
-  * int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier);
-@@ -1421,8 +1429,11 @@ struct net_device_ops {
- 	int			(*ndo_bridge_dellink)(struct net_device *dev,
- 						      struct nlmsghdr *nlh,
- 						      u16 flags);
-+	int			(*ndo_flow_offload_check)(struct flow_offload_hw_path *path);
- 	int			(*ndo_flow_offload)(enum flow_offload_type type,
--						    struct flow_offload *flow);
-+						    struct flow_offload *flow,
-+						    struct flow_offload_hw_path *src,
-+						    struct flow_offload_hw_path *dest);
- 	int			(*ndo_change_carrier)(struct net_device *dev,
- 						      bool new_carrier);
- 	int			(*ndo_get_phys_port_id)(struct net_device *dev,
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -88,6 +88,21 @@ struct flow_offload {
- 	};
- };
- 
-+#define FLOW_OFFLOAD_PATH_ETHERNET	BIT(0)
-+#define FLOW_OFFLOAD_PATH_VLAN		BIT(1)
-+#define FLOW_OFFLOAD_PATH_PPPOE		BIT(2)
-+
-+struct flow_offload_hw_path {
-+	struct net_device *dev;
-+	u32 flags;
-+
-+	u8 eth_src[ETH_ALEN];
-+	u8 eth_dest[ETH_ALEN];
-+	u16 vlan_proto;
-+	u16 vlan_id;
-+	u16 pppoe_sid;
-+};
-+
- #define NF_FLOW_TIMEOUT (30 * HZ)
- 
- struct nf_flow_route {
---- a/net/netfilter/nf_flow_table_hw.c
-+++ b/net/netfilter/nf_flow_table_hw.c
-@@ -19,48 +19,77 @@ struct flow_offload_hw {
- 	enum flow_offload_type	type;
- 	struct flow_offload	*flow;
- 	struct nf_conn		*ct;
--	possible_net_t		flow_hw_net;
-+
-+	struct flow_offload_hw_path src;
-+	struct flow_offload_hw_path dest;
- };
- 
--static int do_flow_offload_hw(struct net *net, struct flow_offload *flow,
--			      int type)
-+static void flow_offload_check_ethernet(struct flow_offload_tuple *tuple,
-+					struct dst_entry *dst,
-+					struct flow_offload_hw_path *path)
- {
--	struct net_device *indev;
--	int ret, ifindex;
-+	struct net_device *dev = path->dev;
-+	struct neighbour *n;
- 
--	ifindex = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.iifidx;
--	indev = dev_get_by_index(net, ifindex);
--	if (WARN_ON(!indev))
--		return 0;
--
--	mutex_lock(&nf_flow_offload_hw_mutex);
--	ret = indev->netdev_ops->ndo_flow_offload(type, flow);
--	mutex_unlock(&nf_flow_offload_hw_mutex);
-+	if (dev->type != ARPHRD_ETHER)
-+		return;
- 
--	dev_put(indev);
-+	memcpy(path->eth_src, path->dev->dev_addr, ETH_ALEN);
-+	n = dst_neigh_lookup(dst, &tuple->src_v4);
-+	if (!n)
-+		return;
- 
--	return ret;
-+	memcpy(path->eth_dest, n->ha, ETH_ALEN);
-+	path->flags |= FLOW_OFFLOAD_PATH_ETHERNET;
-+	neigh_release(n);
- }
- 
--static void flow_offload_hw_work_add(struct flow_offload_hw *offload)
-+static int flow_offload_check_path(struct net *net,
-+				   struct flow_offload_tuple *tuple,
-+				   struct dst_entry *dst,
-+				   struct flow_offload_hw_path *path)
- {
--	struct net *net;
--	int ret;
-+	struct net_device *dev;
- 
--	if (nf_ct_is_dying(offload->ct))
--		return;
-+	dev = dev_get_by_index_rcu(net, tuple->iifidx);
-+	if (!dev)
-+		return -ENOENT;
-+
-+	path->dev = dev;
-+	flow_offload_check_ethernet(tuple, dst, path);
- 
--	net = read_pnet(&offload->flow_hw_net);
--	ret = do_flow_offload_hw(net, offload->flow, FLOW_OFFLOAD_ADD);
--	if (ret >= 0)
--		offload->flow->flags |= FLOW_OFFLOAD_HW;
-+	if (dev->netdev_ops->ndo_flow_offload_check)
-+		return dev->netdev_ops->ndo_flow_offload_check(path);
-+
-+	return 0;
- }
- 
--static void flow_offload_hw_work_del(struct flow_offload_hw *offload)
-+static int do_flow_offload_hw(struct flow_offload_hw *offload)
- {
--	struct net *net = read_pnet(&offload->flow_hw_net);
-+	struct net_device *src_dev = offload->src.dev;
-+	struct net_device *dest_dev = offload->dest.dev;
-+	int ret;
-+
-+	ret = src_dev->netdev_ops->ndo_flow_offload(offload->type,
-+						    offload->flow,
-+						    &offload->src,
-+						    &offload->dest);
-+
-+	/* restore devices in case the driver mangled them */
-+	offload->src.dev = src_dev;
-+	offload->dest.dev = dest_dev;
- 
--	do_flow_offload_hw(net, offload->flow, FLOW_OFFLOAD_DEL);
-+	return ret;
-+}
-+
-+static void flow_offload_hw_free(struct flow_offload_hw *offload)
-+{
-+	dev_put(offload->src.dev);
-+	dev_put(offload->dest.dev);
-+	if (offload->ct)
-+		nf_conntrack_put(&offload->ct->ct_general);
-+	list_del(&offload->list);
-+	kfree(offload);
- }
- 
- static void flow_offload_hw_work(struct work_struct *work)
-@@ -73,18 +102,22 @@ static void flow_offload_hw_work(struct
- 	spin_unlock_bh(&flow_offload_hw_pending_list_lock);
- 
- 	list_for_each_entry_safe(offload, next, &hw_offload_pending, list) {
-+		mutex_lock(&nf_flow_offload_hw_mutex);
- 		switch (offload->type) {
- 		case FLOW_OFFLOAD_ADD:
--			flow_offload_hw_work_add(offload);
-+			if (nf_ct_is_dying(offload->ct))
-+				break;
-+
-+			if (do_flow_offload_hw(offload) >= 0)
-+				offload->flow->flags |= FLOW_OFFLOAD_HW;
- 			break;
- 		case FLOW_OFFLOAD_DEL:
--			flow_offload_hw_work_del(offload);
-+			do_flow_offload_hw(offload);
- 			break;
- 		}
--		if (offload->ct)
--			nf_conntrack_put(&offload->ct->ct_general);
--		list_del(&offload->list);
--		kfree(offload);
-+		mutex_unlock(&nf_flow_offload_hw_mutex);
-+
-+		flow_offload_hw_free(offload);
- 	}
- }
- 
-@@ -97,20 +130,56 @@ static void flow_offload_queue_work(stru
- 	schedule_work(&nf_flow_offload_hw_work);
- }
- 
-+static struct flow_offload_hw *
-+flow_offload_hw_prepare(struct net *net, struct flow_offload *flow)
-+{
-+	struct flow_offload_hw_path src = {};
-+	struct flow_offload_hw_path dest = {};
-+	struct flow_offload_tuple *tuple_s, *tuple_d;
-+	struct flow_offload_hw *offload = NULL;
-+
-+	rcu_read_lock_bh();
-+
-+	tuple_s = &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple;
-+	tuple_d = &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple;
-+
-+	if (flow_offload_check_path(net, tuple_s, tuple_d->dst_cache, &src))
-+		goto out;
-+
-+	if (flow_offload_check_path(net, tuple_d, tuple_s->dst_cache, &dest))
-+		goto out;
-+
-+	if (!src.dev->netdev_ops->ndo_flow_offload)
-+		goto out;
-+
-+	offload = kzalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
-+	if (!offload)
-+		goto out;
-+
-+	dev_hold(src.dev);
-+	dev_hold(dest.dev);
-+	offload->src = src;
-+	offload->dest = dest;
-+	offload->flow = flow;
-+
-+out:
-+	rcu_read_unlock_bh();
-+
-+	return offload;
-+}
-+
- static void flow_offload_hw_add(struct net *net, struct flow_offload *flow,
- 				struct nf_conn *ct)
- {
- 	struct flow_offload_hw *offload;
- 
--	offload = kmalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
-+	offload = flow_offload_hw_prepare(net, flow);
- 	if (!offload)
- 		return;
- 
- 	nf_conntrack_get(&ct->ct_general);
- 	offload->type = FLOW_OFFLOAD_ADD;
- 	offload->ct = ct;
--	offload->flow = flow;
--	write_pnet(&offload->flow_hw_net, net);
- 
- 	flow_offload_queue_work(offload);
- }
-@@ -119,14 +188,11 @@ static void flow_offload_hw_del(struct n
- {
- 	struct flow_offload_hw *offload;
- 
--	offload = kmalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
-+	offload = flow_offload_hw_prepare(net, flow);
- 	if (!offload)
- 		return;
- 
- 	offload->type = FLOW_OFFLOAD_DEL;
--	offload->ct = NULL;
--	offload->flow = flow;
--	write_pnet(&offload->flow_hw_net, net);
- 
- 	flow_offload_queue_work(offload);
- }
-@@ -153,12 +219,8 @@ static void __exit nf_flow_table_hw_modu
- 	nf_flow_table_hw_unregister(&flow_offload_hw);
- 	cancel_work_sync(&nf_flow_offload_hw_work);
- 
--	list_for_each_entry_safe(offload, next, &hw_offload_pending, list) {
--		if (offload->ct)
--			nf_conntrack_put(&offload->ct->ct_general);
--		list_del(&offload->list);
--		kfree(offload);
--	}
-+	list_for_each_entry_safe(offload, next, &hw_offload_pending, list)
-+		flow_offload_hw_free(offload);
- }
- 
- module_init(nf_flow_table_hw_module_init);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/645-netfilter-nf_flow_table-rework-hardware-offload-time.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/645-netfilter-nf_flow_table-rework-hardware-offload-time.patch
deleted file mode 100644
index f4efbcd..0000000
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/645-netfilter-nf_flow_table-rework-hardware-offload-time.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sun, 25 Mar 2018 21:10:55 +0200
-Subject: [PATCH] netfilter: nf_flow_table: rework hardware offload timeout
- handling
-
-Some offload implementations send keepalive packets + explicit
-notifications of TCP FIN/RST packets. In this case it is more convenient
-to simply let the driver update flow->timeout handling and use the
-regular flow offload gc step.
-
-For drivers that manage their own lifetime, a separate flag can be set
-to avoid gc timeouts.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -78,6 +78,7 @@ struct flow_offload_tuple_rhash {
- #define FLOW_OFFLOAD_DYING	0x4
- #define FLOW_OFFLOAD_TEARDOWN	0x8
- #define FLOW_OFFLOAD_HW		0x10
-+#define FLOW_OFFLOAD_KEEP	0x20
- 
- struct flow_offload {
- 	struct flow_offload_tuple_rhash		tuplehash[FLOW_OFFLOAD_DIR_MAX];
---- a/net/netfilter/nf_flow_table_core.c
-+++ b/net/netfilter/nf_flow_table_core.c
-@@ -383,7 +383,7 @@ static void nf_flow_offload_gc_step(stru
- 	if (!teardown)
- 		nf_ct_offload_timeout(flow);
- 
--	if (nf_flow_in_hw(flow) && !teardown)
-+	if ((flow->flags & FLOW_OFFLOAD_KEEP) && !teardown)
- 		return;
- 
- 	if (nf_flow_has_expired(flow) || teardown)
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/646-netfilter-nf_flow_table-rework-private-driver-data.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/646-netfilter-nf_flow_table-rework-private-driver-data.patch
deleted file mode 100644
index 235f2a3..0000000
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/646-netfilter-nf_flow_table-rework-private-driver-data.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 27 Apr 2018 14:42:14 +0200
-Subject: [PATCH] netfilter: nf_flow_table: rework private driver data
-
-Move the timeout out of the union, since it can be shared between the
-driver and the stack. Add a private pointer that the driver can use to
-point to its own data structures
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -83,9 +83,10 @@ struct flow_offload_tuple_rhash {
- struct flow_offload {
- 	struct flow_offload_tuple_rhash		tuplehash[FLOW_OFFLOAD_DIR_MAX];
- 	u32					flags;
-+	u32					timeout;
- 	union {
- 		/* Your private driver data here. */
--		u32		timeout;
-+		void *priv;
- 	};
- };
- 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/647-netfilter-flow-acct.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/647-netfilter-flow-acct.patch
deleted file mode 100644
index 9f7ca61..0000000
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/647-netfilter-flow-acct.patch
+++ /dev/null
@@ -1,70 +0,0 @@
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -163,6 +163,8 @@ struct nf_flow_table_hw {
- int nf_flow_table_hw_register(const struct nf_flow_table_hw *offload);
- void nf_flow_table_hw_unregister(const struct nf_flow_table_hw *offload);
- 
-+void nf_flow_table_acct(struct flow_offload *flow, struct sk_buff *skb, int dir);
-+
- extern struct work_struct nf_flow_offload_hw_work;
- 
- #define MODULE_ALIAS_NF_FLOWTABLE(family)	\
---- a/net/netfilter/nf_flow_table_core.c
-+++ b/net/netfilter/nf_flow_table_core.c
-@@ -13,6 +13,7 @@
- #include <net/netfilter/nf_conntrack_core.h>
- #include <net/netfilter/nf_conntrack_l4proto.h>
- #include <net/netfilter/nf_conntrack_tuple.h>
-+#include <net/netfilter/nf_conntrack_acct.h>
- 
- struct flow_offload_entry {
- 	struct flow_offload	flow;
-@@ -177,6 +178,22 @@ void flow_offload_free(struct flow_offlo
- }
- EXPORT_SYMBOL_GPL(flow_offload_free);
- 
-+void nf_flow_table_acct(struct flow_offload *flow, struct sk_buff *skb, int dir)
-+{
-+	struct flow_offload_entry *entry;
-+	struct nf_conn_acct *acct;
-+
-+	entry = container_of(flow, struct flow_offload_entry, flow);
-+	acct = nf_conn_acct_find(entry->ct);
-+	if (acct) {
-+		struct nf_conn_counter *counter = acct->counter;
-+
-+		atomic64_inc(&counter[dir].packets);
-+		atomic64_add(skb->len, &counter[dir].bytes);
-+	}
-+}
-+EXPORT_SYMBOL_GPL(nf_flow_table_acct);
-+
- static u32 flow_offload_hash(const void *data, u32 len, u32 seed)
- {
- 	const struct flow_offload_tuple *tuple = data;
---- a/net/netfilter/nf_flow_table_ip.c
-+++ b/net/netfilter/nf_flow_table_ip.c
-@@ -12,6 +12,7 @@
- #include <net/ip6_route.h>
- #include <net/neighbour.h>
- #include <net/netfilter/nf_flow_table.h>
-+
- /* For layer 4 checksum field offset. */
- #include <linux/tcp.h>
- #include <linux/udp.h>
-@@ -288,6 +289,7 @@ nf_flow_offload_ip_hook(void *priv, stru
- 	skb->dev = outdev;
- 	nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
- 	skb_dst_set_noref(skb, &rt->dst);
-+	nf_flow_table_acct(flow, skb, dir);
- 	neigh_xmit(NEIGH_ARP_TABLE, outdev, &nexthop, skb);
- 
- 	return NF_STOLEN;
-@@ -518,6 +520,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
- 	skb->dev = outdev;
- 	nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
- 	skb_dst_set_noref(skb, &rt->dst);
-+	nf_flow_table_acct(flow, skb, dir);
- 	neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb);
- 
- 	return NF_STOLEN;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/650-netfilter-add-xt_OFFLOAD-target.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/650-netfilter-add-xt_OFFLOAD-target.patch
deleted file mode 100644
index 8d9fec0..0000000
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/650-netfilter-add-xt_OFFLOAD-target.patch
+++ /dev/null
@@ -1,589 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Tue, 20 Feb 2018 15:56:02 +0100
-Subject: [PATCH] netfilter: add xt_OFFLOAD target
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
- create mode 100644 net/netfilter/xt_OFFLOAD.c
-
---- a/net/ipv4/netfilter/Kconfig
-+++ b/net/ipv4/netfilter/Kconfig
-@@ -56,8 +56,6 @@ config NF_TABLES_ARP
- 	help
- 	  This option enables the ARP support for nf_tables.
- 
--endif # NF_TABLES
--
- config NF_FLOW_TABLE_IPV4
- 	tristate "Netfilter flow table IPv4 module"
- 	depends on NF_FLOW_TABLE
-@@ -66,6 +64,8 @@ config NF_FLOW_TABLE_IPV4
- 
- 	  To compile it as a module, choose M here.
- 
-+endif # NF_TABLES
-+
- config NF_DUP_IPV4
- 	tristate "Netfilter IPv4 packet duplication to alternate destination"
- 	depends on !NF_CONNTRACK || NF_CONNTRACK
---- a/net/ipv6/netfilter/Kconfig
-+++ b/net/ipv6/netfilter/Kconfig
-@@ -45,7 +45,6 @@ config NFT_FIB_IPV6
- 	  multicast or blackhole.
- 
- endif # NF_TABLES_IPV6
--endif # NF_TABLES
- 
- config NF_FLOW_TABLE_IPV6
- 	tristate "Netfilter flow table IPv6 module"
-@@ -55,6 +54,8 @@ config NF_FLOW_TABLE_IPV6
- 
- 	  To compile it as a module, choose M here.
- 
-+endif # NF_TABLES
-+
- config NF_DUP_IPV6
- 	tristate "Netfilter IPv6 packet duplication to alternate destination"
- 	depends on !NF_CONNTRACK || NF_CONNTRACK
---- a/net/netfilter/Kconfig
-+++ b/net/netfilter/Kconfig
-@@ -689,8 +689,6 @@ config NFT_FIB_NETDEV
- 
- endif # NF_TABLES_NETDEV
- 
--endif # NF_TABLES
--
- config NF_FLOW_TABLE_INET
- 	tristate "Netfilter flow table mixed IPv4/IPv6 module"
- 	depends on NF_FLOW_TABLE
-@@ -699,11 +697,12 @@ config NF_FLOW_TABLE_INET
- 
- 	  To compile it as a module, choose M here.
- 
-+endif # NF_TABLES
-+
- config NF_FLOW_TABLE
- 	tristate "Netfilter flow table module"
- 	depends on NETFILTER_INGRESS
- 	depends on NF_CONNTRACK
--	depends on NF_TABLES
- 	help
- 	  This option adds the flow table core infrastructure.
- 
-@@ -992,6 +991,15 @@ config NETFILTER_XT_TARGET_NOTRACK
- 	depends on NETFILTER_ADVANCED
- 	select NETFILTER_XT_TARGET_CT
- 
-+config NETFILTER_XT_TARGET_FLOWOFFLOAD
-+	tristate '"FLOWOFFLOAD" target support'
-+	depends on NF_FLOW_TABLE
-+	depends on NETFILTER_INGRESS
-+	help
-+	  This option adds a `FLOWOFFLOAD' target, which uses the nf_flow_offload
-+	  module to speed up processing of packets by bypassing the usual
-+	  netfilter chains
-+
- config NETFILTER_XT_TARGET_RATEEST
- 	tristate '"RATEEST" target support'
- 	depends on NETFILTER_ADVANCED
---- a/net/netfilter/Makefile
-+++ b/net/netfilter/Makefile
-@@ -141,6 +141,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIF
- obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
-+obj-$(CONFIG_NETFILTER_XT_TARGET_FLOWOFFLOAD) += xt_FLOWOFFLOAD.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
- obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
---- /dev/null
-+++ b/net/netfilter/xt_FLOWOFFLOAD.c
-@@ -0,0 +1,427 @@
-+/*
-+ * Copyright (C) 2018 Felix Fietkau <nbd@nbd.name>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ */
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/netfilter.h>
-+#include <linux/netfilter/xt_FLOWOFFLOAD.h>
-+#include <net/ip.h>
-+#include <net/netfilter/nf_conntrack.h>
-+#include <net/netfilter/nf_conntrack_extend.h>
-+#include <net/netfilter/nf_conntrack_helper.h>
-+#include <net/netfilter/nf_flow_table.h>
-+
-+static struct nf_flowtable nf_flowtable;
-+static HLIST_HEAD(hooks);
-+static DEFINE_SPINLOCK(hooks_lock);
-+static struct delayed_work hook_work;
-+
-+struct xt_flowoffload_hook {
-+	struct hlist_node list;
-+	struct nf_hook_ops ops;
-+	struct net *net;
-+	bool registered;
-+	bool used;
-+};
-+
-+static unsigned int
-+xt_flowoffload_net_hook(void *priv, struct sk_buff *skb,
-+			  const struct nf_hook_state *state)
-+{
-+	switch (skb->protocol) {
-+	case htons(ETH_P_IP):
-+		return nf_flow_offload_ip_hook(priv, skb, state);
-+	case htons(ETH_P_IPV6):
-+		return nf_flow_offload_ipv6_hook(priv, skb, state);
-+	}
-+
-+	return NF_ACCEPT;
-+}
-+
-+int nf_flow_table_iterate(struct nf_flowtable *flow_table,
-+			   void (*iter)(struct flow_offload *flow, void *data),
-+			   void *data);
-+
-+static int
-+xt_flowoffload_create_hook(struct net_device *dev)
-+{
-+	struct xt_flowoffload_hook *hook;
-+	struct nf_hook_ops *ops;
-+
-+	hook = kzalloc(sizeof(*hook), GFP_ATOMIC);
-+	if (!hook)
-+		return -ENOMEM;
-+
-+	ops = &hook->ops;
-+	ops->pf = NFPROTO_NETDEV;
-+	ops->hooknum = NF_NETDEV_INGRESS;
-+	ops->priority = 10;
-+	ops->priv = &nf_flowtable;
-+	ops->hook = xt_flowoffload_net_hook;
-+	ops->dev = dev;
-+
-+	hlist_add_head(&hook->list, &hooks);
-+	mod_delayed_work(system_power_efficient_wq, &hook_work, 0);
-+
-+	return 0;
-+}
-+
-+static struct xt_flowoffload_hook *
-+flow_offload_lookup_hook(struct net_device *dev)
-+{
-+	struct xt_flowoffload_hook *hook;
-+
-+	hlist_for_each_entry(hook, &hooks, list) {
-+		if (hook->ops.dev == dev)
-+			return hook;
-+	}
-+
-+	return NULL;
-+}
-+
-+static void
-+xt_flowoffload_check_device(struct net_device *dev)
-+{
-+	struct xt_flowoffload_hook *hook;
-+
-+	spin_lock_bh(&hooks_lock);
-+	hook = flow_offload_lookup_hook(dev);
-+	if (hook)
-+		hook->used = true;
-+	else
-+		xt_flowoffload_create_hook(dev);
-+	spin_unlock_bh(&hooks_lock);
-+}
-+
-+static void
-+xt_flowoffload_register_hooks(void)
-+{
-+	struct xt_flowoffload_hook *hook;
-+
-+restart:
-+	hlist_for_each_entry(hook, &hooks, list) {
-+		if (hook->registered)
-+			continue;
-+
-+		hook->registered = true;
-+		hook->net = dev_net(hook->ops.dev);
-+		spin_unlock_bh(&hooks_lock);
-+		nf_register_net_hook(hook->net, &hook->ops);
-+		spin_lock_bh(&hooks_lock);
-+		goto restart;
-+	}
-+
-+}
-+
-+static void
-+xt_flowoffload_cleanup_hooks(void)
-+{
-+	struct xt_flowoffload_hook *hook;
-+
-+restart:
-+	hlist_for_each_entry(hook, &hooks, list) {
-+		if (hook->used || !hook->registered)
-+			continue;
-+
-+		hlist_del(&hook->list);
-+		spin_unlock_bh(&hooks_lock);
-+		nf_unregister_net_hook(hook->net, &hook->ops);
-+		kfree(hook);
-+		spin_lock_bh(&hooks_lock);
-+		goto restart;
-+	}
-+
-+}
-+
-+static void
-+xt_flowoffload_check_hook(struct flow_offload *flow, void *data)
-+{
-+	struct flow_offload_tuple *tuple = &flow->tuplehash[0].tuple;
-+	struct xt_flowoffload_hook *hook;
-+	bool *found = data;
-+	struct rtable *rt = (struct rtable *)tuple->dst_cache;
-+
-+	spin_lock_bh(&hooks_lock);
-+	hlist_for_each_entry(hook, &hooks, list) {
-+		if (hook->ops.dev->ifindex != tuple->iifidx &&
-+		    hook->ops.dev->ifindex != rt->dst.dev->ifindex)
-+			continue;
-+
-+		hook->used = true;
-+		*found = true;
-+	}
-+	spin_unlock_bh(&hooks_lock);
-+}
-+
-+static void
-+xt_flowoffload_hook_work(struct work_struct *work)
-+{
-+	struct xt_flowoffload_hook *hook;
-+	bool found = false;
-+	int err;
-+
-+	spin_lock_bh(&hooks_lock);
-+	xt_flowoffload_register_hooks();
-+	hlist_for_each_entry(hook, &hooks, list)
-+		hook->used = false;
-+	spin_unlock_bh(&hooks_lock);
-+
-+	err = nf_flow_table_iterate(&nf_flowtable, xt_flowoffload_check_hook,
-+				    &found);
-+	if (err && err != -EAGAIN)
-+	    goto out;
-+
-+	spin_lock_bh(&hooks_lock);
-+	xt_flowoffload_cleanup_hooks();
-+	spin_unlock_bh(&hooks_lock);
-+
-+out:
-+	if (found)
-+		queue_delayed_work(system_power_efficient_wq, &hook_work, HZ);
-+}
-+
-+static bool
-+xt_flowoffload_skip(struct sk_buff *skb, int family)
-+{
-+	if (skb_sec_path(skb))
-+		return true;
-+
-+	if (family == NFPROTO_IPV4) {
-+		const struct ip_options *opt = &(IPCB(skb)->opt);
-+
-+		if (unlikely(opt->optlen))
-+			return true;
-+	}
-+
-+	return false;
-+}
-+
-+static struct dst_entry *
-+xt_flowoffload_dst(const struct nf_conn *ct, enum ip_conntrack_dir dir,
-+		   const struct xt_action_param *par, int ifindex)
-+{
-+	struct dst_entry *dst = NULL;
-+	struct flowi fl;
-+
-+	memset(&fl, 0, sizeof(fl));
-+	switch (xt_family(par)) {
-+	case NFPROTO_IPV4:
-+		fl.u.ip4.daddr = ct->tuplehash[dir].tuple.src.u3.ip;
-+		fl.u.ip4.flowi4_oif = ifindex;
-+		break;
-+	case NFPROTO_IPV6:
-+		fl.u.ip6.saddr = ct->tuplehash[dir].tuple.dst.u3.in6;
-+		fl.u.ip6.daddr = ct->tuplehash[dir].tuple.src.u3.in6;
-+		fl.u.ip6.flowi6_oif = ifindex;
-+		break;
-+	}
-+
-+	nf_route(xt_net(par), &dst, &fl, false, xt_family(par));
-+
-+	return dst;
-+}
-+
-+static int
-+xt_flowoffload_route(struct sk_buff *skb, const struct nf_conn *ct,
-+		   const struct xt_action_param *par,
-+		   struct nf_flow_route *route, enum ip_conntrack_dir dir)
-+{
-+	struct dst_entry *this_dst, *other_dst;
-+
-+	this_dst = xt_flowoffload_dst(ct, !dir, par, xt_out(par)->ifindex);
-+	other_dst = xt_flowoffload_dst(ct, dir, par, xt_in(par)->ifindex);
-+
-+	route->tuple[dir].dst		= this_dst;
-+	route->tuple[!dir].dst		= other_dst;
-+
-+	if (!this_dst || !other_dst)
-+		return -ENOENT;
-+
-+	if (dst_xfrm(this_dst) || dst_xfrm(other_dst))
-+		return -EINVAL;
-+
-+	return 0;
-+}
-+
-+static unsigned int
-+flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par)
-+{
-+	const struct xt_flowoffload_target_info *info = par->targinfo;
-+	struct tcphdr _tcph, *tcph = NULL;
-+	enum ip_conntrack_info ctinfo;
-+	enum ip_conntrack_dir dir;
-+	struct nf_flow_route route;
-+	struct flow_offload *flow = NULL;
-+	struct nf_conn *ct;
-+	struct net *net;
-+
-+	if (xt_flowoffload_skip(skb, xt_family(par)))
-+		return XT_CONTINUE;
-+
-+	ct = nf_ct_get(skb, &ctinfo);
-+	if (ct == NULL)
-+		return XT_CONTINUE;
-+
-+	switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) {
-+	case IPPROTO_TCP:
-+		if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED)
-+			return XT_CONTINUE;
-+
-+		tcph = skb_header_pointer(skb, par->thoff,
-+					  sizeof(_tcph), &_tcph);
-+		if (unlikely(!tcph || tcph->fin || tcph->rst))
-+			return XT_CONTINUE;
-+		break;
-+	case IPPROTO_UDP:
-+		break;
-+	default:
-+		return XT_CONTINUE;
-+	}
-+
-+	if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) ||
-+	    ct->status & IPS_SEQ_ADJUST)
-+		return XT_CONTINUE;
-+
-+	if (!nf_ct_is_confirmed(ct))
-+		return XT_CONTINUE;
-+
-+	if (!xt_in(par) || !xt_out(par))
-+		return XT_CONTINUE;
-+
-+	if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status))
-+		return XT_CONTINUE;
-+
-+	dir = CTINFO2DIR(ctinfo);
-+
-+	if (xt_flowoffload_route(skb, ct, par, &route, dir) == 0)
-+		flow = flow_offload_alloc(ct, &route);
-+
-+	dst_release(route.tuple[dir].dst);
-+	dst_release(route.tuple[!dir].dst);
-+
-+	if (!flow)
-+		goto err_flow_route;
-+
-+	if (tcph) {
-+		ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
-+		ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
-+	}
-+
-+	if (flow_offload_add(&nf_flowtable, flow) < 0)
-+		goto err_flow_add;
-+
-+	xt_flowoffload_check_device(xt_in(par));
-+	xt_flowoffload_check_device(xt_out(par));
-+
-+	net = read_pnet(&nf_flowtable.ft_net);
-+	if (!net)
-+		write_pnet(&nf_flowtable.ft_net, xt_net(par));
-+
-+	if (info->flags & XT_FLOWOFFLOAD_HW)
-+		nf_flow_offload_hw_add(xt_net(par), flow, ct);
-+
-+	return XT_CONTINUE;
-+
-+err_flow_add:
-+	flow_offload_free(flow);
-+err_flow_route:
-+	clear_bit(IPS_OFFLOAD_BIT, &ct->status);
-+	return XT_CONTINUE;
-+}
-+
-+
-+static int flowoffload_chk(const struct xt_tgchk_param *par)
-+{
-+	struct xt_flowoffload_target_info *info = par->targinfo;
-+
-+	if (info->flags & ~XT_FLOWOFFLOAD_MASK)
-+		return -EINVAL;
-+
-+	return 0;
-+}
-+
-+static struct xt_target offload_tg_reg __read_mostly = {
-+	.family		= NFPROTO_UNSPEC,
-+	.name		= "FLOWOFFLOAD",
-+	.revision	= 0,
-+	.targetsize	= sizeof(struct xt_flowoffload_target_info),
-+	.usersize	= sizeof(struct xt_flowoffload_target_info),
-+	.checkentry	= flowoffload_chk,
-+	.target		= flowoffload_tg,
-+	.me		= THIS_MODULE,
-+};
-+
-+static int xt_flowoffload_table_init(struct nf_flowtable *table)
-+{
-+	table->flags = NF_FLOWTABLE_F_HW;
-+	nf_flow_table_init(table);
-+	return 0;
-+}
-+
-+static void xt_flowoffload_table_cleanup(struct nf_flowtable *table)
-+{
-+	nf_flow_table_free(table);
-+}
-+
-+static int flow_offload_netdev_event(struct notifier_block *this,
-+				     unsigned long event, void *ptr)
-+{
-+	struct xt_flowoffload_hook *hook = NULL;
-+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
-+
-+	if (event != NETDEV_UNREGISTER)
-+		return NOTIFY_DONE;
-+
-+	spin_lock_bh(&hooks_lock);
-+	hook = flow_offload_lookup_hook(dev);
-+	if (hook) {
-+		hlist_del(&hook->list);
-+	}
-+	spin_unlock_bh(&hooks_lock);
-+	if (hook) {
-+		nf_unregister_net_hook(hook->net, &hook->ops);
-+		kfree(hook);
-+	}
-+
-+	nf_flow_table_cleanup(dev);
-+
-+	return NOTIFY_DONE;
-+}
-+
-+static struct notifier_block flow_offload_netdev_notifier = {
-+	.notifier_call	= flow_offload_netdev_event,
-+};
-+
-+static int __init xt_flowoffload_tg_init(void)
-+{
-+	int ret;
-+
-+	register_netdevice_notifier(&flow_offload_netdev_notifier);
-+
-+	INIT_DELAYED_WORK(&hook_work, xt_flowoffload_hook_work);
-+
-+	ret = xt_flowoffload_table_init(&nf_flowtable);
-+	if (ret)
-+		return ret;
-+
-+	ret = xt_register_target(&offload_tg_reg);
-+	if (ret)
-+		xt_flowoffload_table_cleanup(&nf_flowtable);
-+
-+	return ret;
-+}
-+
-+static void __exit xt_flowoffload_tg_exit(void)
-+{
-+	xt_unregister_target(&offload_tg_reg);
-+	xt_flowoffload_table_cleanup(&nf_flowtable);
-+	unregister_netdevice_notifier(&flow_offload_netdev_notifier);
-+}
-+
-+MODULE_LICENSE("GPL");
-+module_init(xt_flowoffload_tg_init);
-+module_exit(xt_flowoffload_tg_exit);
---- a/net/netfilter/nf_flow_table_core.c
-+++ b/net/netfilter/nf_flow_table_core.c
-@@ -7,7 +7,6 @@
- #include <linux/netdevice.h>
- #include <net/ip.h>
- #include <net/ip6_route.h>
--#include <net/netfilter/nf_tables.h>
- #include <net/netfilter/nf_flow_table.h>
- #include <net/netfilter/nf_conntrack.h>
- #include <net/netfilter/nf_conntrack_core.h>
-@@ -351,8 +350,7 @@ flow_offload_lookup(struct nf_flowtable
- }
- EXPORT_SYMBOL_GPL(flow_offload_lookup);
- 
--static int
--nf_flow_table_iterate(struct nf_flowtable *flow_table,
-+int nf_flow_table_iterate(struct nf_flowtable *flow_table,
- 		      void (*iter)(struct flow_offload *flow, void *data),
- 		      void *data)
- {
-@@ -385,6 +383,7 @@ nf_flow_table_iterate(struct nf_flowtabl
- 
- 	return err;
- }
-+EXPORT_SYMBOL_GPL(nf_flow_table_iterate);
- 
- static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data)
- {
---- /dev/null
-+++ b/include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h
-@@ -0,0 +1,17 @@
-+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-+#ifndef _XT_FLOWOFFLOAD_H
-+#define _XT_FLOWOFFLOAD_H
-+
-+#include <linux/types.h>
-+
-+enum {
-+	XT_FLOWOFFLOAD_HW	= 1 << 0,
-+
-+	XT_FLOWOFFLOAD_MASK	= XT_FLOWOFFLOAD_HW
-+};
-+
-+struct xt_flowoffload_target_info {
-+	__u32 flags;
-+};
-+
-+#endif /* _XT_FLOWOFFLOAD_H */
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -133,6 +133,10 @@ static inline void flow_offload_dead(str
- 	flow->flags |= FLOW_OFFLOAD_DYING;
- }
- 
-+int nf_flow_table_iterate(struct nf_flowtable *flow_table,
-+                      void (*iter)(struct flow_offload *flow, void *data),
-+                      void *data);
-+
- int nf_flow_snat_port(const struct flow_offload *flow,
- 		      struct sk_buff *skb, unsigned int thoff,
- 		      u8 protocol, enum flow_offload_tuple_dir dir);
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1718-v5.15-net-netfilter-add-nf-hw-offload.patch
similarity index 73%
rename from recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
rename to recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1718-v5.15-net-netfilter-add-nf-hw-offload.patch
index 8f28900..f162e51 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1718-v5.15-net-netfilter-add-nf-hw-offload.patch
@@ -1,18 +1,9 @@
 From 3a4256932000744357ba358140fd2895756060e6 Mon Sep 17 00:00:00 2001
 From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
-Date: Tue, 15 Oct 2024 15:23:05 +0800
-Subject: [PATCH 01/24] mt7622 backport nf hw offload framework and upstream
- hnat plus xt-FLOWOFFLOAD update v2
+Date: Tue, 4 Jun 2024 09:14:34 +0800
+Subject: net netfilter add nf hw offload
 
 ---
- drivers/net/ethernet/mediatek/Makefile        |    3 +-
- drivers/net/ethernet/mediatek/mtk_eth_soc.c   |   25 +-
- drivers/net/ethernet/mediatek/mtk_eth_soc.h   |   19 +-
- drivers/net/ethernet/mediatek/mtk_ppe.c       |  513 +++++++
- drivers/net/ethernet/mediatek/mtk_ppe.h       |  287 ++++
- .../net/ethernet/mediatek/mtk_ppe_debugfs.c   |  214 +++
- .../net/ethernet/mediatek/mtk_ppe_offload.c   |  535 ++++++++
- drivers/net/ethernet/mediatek/mtk_ppe_regs.h  |  144 ++
  drivers/net/ppp/ppp_generic.c                 |   22 +
  drivers/net/ppp/pppoe.c                       |   24 +
  include/linux/netdevice.h                     |   60 +
@@ -47,1924 +38,11 @@
  net/netfilter/nf_flow_table_ip.c              |  447 +++---
  net/netfilter/nf_flow_table_offload.c         | 1199 +++++++++++++++++
  net/netfilter/xt_FLOWOFFLOAD.c                |  780 +++++++++++
- 42 files changed, 4981 insertions(+), 452 deletions(-)
- mode change 100644 => 100755 drivers/net/ethernet/mediatek/Makefile
- mode change 100644 => 100755 drivers/net/ethernet/mediatek/mtk_eth_soc.c
- mode change 100644 => 100755 drivers/net/ethernet/mediatek/mtk_eth_soc.h
- create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe.c
- create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe.h
- create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
- create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_offload.c
- create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+ 34 files changed, 3244 insertions(+), 499 deletions(-)
  create mode 100644 include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h
  create mode 100644 net/netfilter/nf_flow_table_offload.c
  create mode 100644 net/netfilter/xt_FLOWOFFLOAD.c
 
-diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
-old mode 100644
-new mode 100755
-index 2f14aa1..0a3b977
---- a/drivers/net/ethernet/mediatek/Makefile
-+++ b/drivers/net/ethernet/mediatek/Makefile
-@@ -4,5 +4,6 @@
- #
- 
- obj-$(CONFIG_NET_MEDIATEK_SOC)			+= mtk_eth.o
--mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o mtk_eth_ptp.o
-+mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o mtk_eth_ptp.o	\
-+	     mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
- obj-$(CONFIG_NET_MEDIATEK_HNAT)			+= mtk_hnat/
-diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-old mode 100644
-new mode 100755
-index e3040fb..088ff2d
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -4307,6 +4307,7 @@ static int mtk_open(struct net_device *dev)
- 	struct mtk_phylink_priv *phylink_priv = &mac->phylink_priv;
- 	struct device_node *phy_node;
- 	const char *mac_addr;
-+	u32 gdm_config = MTK_GDMA_TO_PDMA;
- 	u32 id = mtk_mac2xgmii_id(eth, mac->id);
- 	int err, i;
- 
-@@ -4407,7 +4408,10 @@ static int mtk_open(struct net_device *dev)
- 		regmap_write(eth->sgmii->pcs[id].regmap,
- 			     SGMSYS_QPHY_PWR_STATE_CTRL, 0);
- 
--	mtk_gdm_config(eth, mac->id, MTK_GDMA_TO_PDMA);
-+	if (eth->soc->offload_version && mtk_ppe_start(&eth->ppe) == 0)
-+		gdm_config = MTK_GDMA_TO_PPE;
-+
-+	mtk_gdm_config(eth, mac->id, gdm_config);
- 
- 	return 0;
- }
-@@ -4541,6 +4545,9 @@ static int mtk_stop(struct net_device *dev)
- 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_HWTSTAMP))
- 		ptp_clock_unregister(eth->ptp_clock);
- 
-+	if (eth->soc->offload_version)
-+		mtk_ppe_stop(&eth->ppe);
-+
- 	return 0;
- }
- 
-@@ -5577,6 +5584,7 @@ static const struct net_device_ops mtk_netdev_ops = {
- #ifdef CONFIG_NET_POLL_CONTROLLER
- 	.ndo_poll_controller	= mtk_poll_controller,
- #endif
-+	.ndo_setup_tc		= mtk_eth_setup_tc,
- };
- 
- static void mux_poll(struct work_struct *work)
-@@ -6221,6 +6229,17 @@ static int mtk_probe(struct platform_device *pdev)
- 			goto err_free_dev;
- 	}
- 
-+	if (eth->soc->offload_version) {
-+		err = mtk_ppe_init(&eth->ppe, eth->dev,
-+				   eth->base + MTK_ETH_PPE_BASE);
-+		if (err)
-+			goto err_free_dev;
-+
-+		err = mtk_eth_offload_init(eth);
-+		if (err)
-+			goto err_free_dev;
-+	}
-+
- 	for (i = 0; i < MTK_MAX_DEVS; i++) {
- 		if (!eth->netdev[i])
- 			continue;
-@@ -6331,6 +6350,7 @@ static const struct mtk_soc_data mt2701_data = {
- 	.required_clks = MT7623_CLKS_BITMAP,
- 	.required_pctl = true,
- 	.has_sram = false,
-+	.offload_version = 1,
- 	.rss_num = 0,
- 	.txrx = {
- 		.txd_size = sizeof(struct mtk_tx_dma),
-@@ -6351,6 +6371,7 @@ static const struct mtk_soc_data mt7621_data = {
- 	.required_clks = MT7621_CLKS_BITMAP,
- 	.required_pctl = false,
- 	.has_sram = false,
-+	.offload_version = 1,
- 	.rss_num = 0,
- 	.txrx = {
- 		.txd_size = sizeof(struct mtk_tx_dma),
-@@ -6372,6 +6393,7 @@ static const struct mtk_soc_data mt7622_data = {
- 	.required_clks = MT7622_CLKS_BITMAP,
- 	.required_pctl = false,
- 	.has_sram = false,
-+	.offload_version = 2,
- 	.rss_num = 0,
- 	.txrx = {
- 		.txd_size = sizeof(struct mtk_tx_dma),
-@@ -6392,6 +6414,7 @@ static const struct mtk_soc_data mt7623_data = {
- 	.required_clks = MT7623_CLKS_BITMAP,
- 	.required_pctl = true,
- 	.has_sram = false,
-+	.offload_version = 1,
- 	.rss_num = 0,
- 	.txrx = {
- 		.txd_size = sizeof(struct mtk_tx_dma),
-diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-old mode 100644
-new mode 100755
-index 02feeaf..8ca9c0c
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -16,6 +16,8 @@
- #include <linux/refcount.h>
- #include <linux/phylink.h>
- #include <linux/ptp_clock_kernel.h>
-+#include <linux/rhashtable.h>
-+#include "mtk_ppe.h"
- 
- #define MTK_QDMA_PAGE_SIZE	2048
- #define	MTK_MAX_RX_LENGTH	1536
-@@ -51,7 +53,8 @@
- 				 NETIF_F_HW_VLAN_CTAG_TX | \
- 				 NETIF_F_SG | NETIF_F_TSO | \
- 				 NETIF_F_TSO6 | \
--				 NETIF_F_IPV6_CSUM)
-+				 NETIF_F_IPV6_CSUM |\
-+				 NETIF_F_HW_TC)
- #define MTK_SET_FEATURES	(NETIF_F_LRO | \
- 				 NETIF_F_HW_VLAN_CTAG_RX)
- #define MTK_HW_FEATURES_MT7628	(NETIF_F_SG | NETIF_F_RXCSUM)
-@@ -149,6 +152,7 @@
- #define MTK_GDMA_UCS_EN		BIT(20)
- #define MTK_GDMA_STRP_CRC	BIT(16)
- #define MTK_GDMA_TO_PDMA	0x0
-+#define MTK_GDMA_TO_PPE		0x4444
- #define MTK_GDMA_DROP_ALL	0x7777
- 
- /* GDM Egress Control Register */
-@@ -725,6 +729,12 @@
- #define RX_DMA_TCI(_x)		((_x) & (VLAN_PRIO_MASK | VLAN_VID_MASK))
- #define RX_DMA_VPID(_x)		(((_x) >> 16) & 0xffff)
- 
-+/* QDMA descriptor rxd4 */
-+#define MTK_RXD4_FOE_ENTRY	GENMASK(13, 0)
-+#define MTK_RXD4_PPE_CPU_REASON	GENMASK(18, 14)
-+#define MTK_RXD4_SRC_PORT	GENMASK(21, 19)
-+#define MTK_RXD4_ALG		GENMASK(31, 22)
-+
- /* QDMA descriptor rxd4 */
- #define RX_DMA_L4_VALID		BIT(24)
- #define RX_DMA_L4_VALID_PDMA	BIT(30)		/* when PDMA is used */
-@@ -1967,6 +1977,7 @@ struct mtk_soc_data {
- 	u64		caps;
- 	u64		required_clks;
- 	bool		required_pctl;
-+	u8		offload_version;
- 	netdev_features_t hw_features;
- 	bool		has_sram;
- 	struct {
-@@ -2263,6 +2274,9 @@ struct mtk_eth {
- 	int				ip_align;
- 	spinlock_t			syscfg0_lock;
- 	struct notifier_block		netdevice_notifier;
-+
-+	struct mtk_ppe			ppe;
-+	struct rhashtable		flow_table;
- };
- 
- /* struct mtk_mac -	the structure that holds the info about the MACs of the
-@@ -2348,6 +2362,9 @@ struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, unsigned int
- int mtk_usxgmii_init(struct mtk_eth *eth, struct device_node *r);
- int mtk_toprgu_init(struct mtk_eth *eth, struct device_node *r);
- 
-+int mtk_eth_offload_init(struct mtk_eth *eth);
-+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);
- u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index);
- 
-diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
-new file mode 100644
-index 0000000..8a1437b
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -0,0 +1,513 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-+
-+#include <linux/kernel.h>
-+#include <linux/io.h>
-+#include <linux/iopoll.h>
-+#include <linux/etherdevice.h>
-+#include <linux/platform_device.h>
-+#include "mtk_ppe.h"
-+#include "mtk_ppe_regs.h"
-+
-+static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
-+{
-+	writel(val, ppe->base + reg);
-+}
-+
-+static u32 ppe_r32(struct mtk_ppe *ppe, u32 reg)
-+{
-+	return readl(ppe->base + reg);
-+}
-+
-+static u32 ppe_m32(struct mtk_ppe *ppe, u32 reg, u32 mask, u32 set)
-+{
-+	u32 val;
-+
-+	val = ppe_r32(ppe, reg);
-+	val &= ~mask;
-+	val |= set;
-+	ppe_w32(ppe, reg, val);
-+
-+	return val;
-+}
-+
-+static u32 ppe_set(struct mtk_ppe *ppe, u32 reg, u32 val)
-+{
-+	return ppe_m32(ppe, reg, 0, val);
-+}
-+
-+static u32 ppe_clear(struct mtk_ppe *ppe, u32 reg, u32 val)
-+{
-+	return ppe_m32(ppe, reg, val, 0);
-+}
-+
-+static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
-+{
-+	int ret;
-+	u32 val;
-+
-+	ret = readl_poll_timeout(ppe->base + MTK_PPE_GLO_CFG, val,
-+				 !(val & MTK_PPE_GLO_CFG_BUSY),
-+				 20, MTK_PPE_WAIT_TIMEOUT_US);
-+
-+	if (ret)
-+		dev_err(ppe->dev, "PPE table busy");
-+
-+	return ret;
-+}
-+
-+static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
-+{
-+	ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
-+	ppe_clear(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
-+}
-+
-+static void mtk_ppe_cache_enable(struct mtk_ppe *ppe, bool enable)
-+{
-+	mtk_ppe_cache_clear(ppe);
-+
-+	ppe_m32(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_EN,
-+		enable * MTK_PPE_CACHE_CTL_EN);
-+}
-+
-+static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e)
-+{
-+	u32 hv1, hv2, hv3;
-+	u32 hash;
-+
-+	switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) {
-+		case MTK_PPE_PKT_TYPE_BRIDGE:
-+			hv1 = e->bridge.src_mac_lo;
-+			hv1 ^= ((e->bridge.src_mac_hi & 0xffff) << 16);
-+			hv2 = e->bridge.src_mac_hi >> 16;
-+			hv2 ^= e->bridge.dest_mac_lo;
-+			hv3 = e->bridge.dest_mac_hi;
-+			break;
-+		case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
-+		case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
-+			hv1 = e->ipv4.orig.ports;
-+			hv2 = e->ipv4.orig.dest_ip;
-+			hv3 = e->ipv4.orig.src_ip;
-+			break;
-+		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
-+		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
-+			hv1 = e->ipv6.src_ip[3] ^ e->ipv6.dest_ip[3];
-+			hv1 ^= e->ipv6.ports;
-+
-+			hv2 = e->ipv6.src_ip[2] ^ e->ipv6.dest_ip[2];
-+			hv2 ^= e->ipv6.dest_ip[0];
-+
-+			hv3 = e->ipv6.src_ip[1] ^ e->ipv6.dest_ip[1];
-+			hv3 ^= e->ipv6.src_ip[0];
-+			break;
-+		case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
-+		case MTK_PPE_PKT_TYPE_IPV6_6RD:
-+		default:
-+			WARN_ON_ONCE(1);
-+			return MTK_PPE_HASH_MASK;
-+	}
-+
-+	hash = (hv1 & hv2) | ((~hv1) & hv3);
-+	hash = (hash >> 24) | ((hash & 0xffffff) << 8);
-+	hash ^= hv1 ^ hv2 ^ hv3;
-+	hash ^= hash >> 16;
-+	hash <<= 1;
-+	hash &= MTK_PPE_ENTRIES - 1;
-+
-+	return hash;
-+}
-+
-+static inline struct mtk_foe_mac_info *
-+mtk_foe_entry_l2(struct mtk_foe_entry *entry)
-+{
-+	int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
-+
-+	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
-+		return &entry->ipv6.l2;
-+
-+	return &entry->ipv4.l2;
-+}
-+
-+static inline u32 *
-+mtk_foe_entry_ib2(struct mtk_foe_entry *entry)
-+{
-+	int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
-+
-+	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
-+		return &entry->ipv6.ib2;
-+
-+	return &entry->ipv4.ib2;
-+}
-+
-+int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
-+			  u8 pse_port, u8 *src_mac, u8 *dest_mac)
-+{
-+	struct mtk_foe_mac_info *l2;
-+	u32 ports_pad, val;
-+
-+	memset(entry, 0, sizeof(*entry));
-+
-+	val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
-+	      FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) |
-+	      FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
-+	      MTK_FOE_IB1_BIND_TTL |
-+	      MTK_FOE_IB1_BIND_CACHE;
-+	entry->ib1 = val;
-+
-+	val = FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) |
-+	      FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f) |
-+	      FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port);
-+
-+	if (is_multicast_ether_addr(dest_mac))
-+		val |= MTK_FOE_IB2_MULTICAST;
-+
-+	ports_pad = 0xa5a5a500 | (l4proto & 0xff);
-+	if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
-+		entry->ipv4.orig.ports = ports_pad;
-+	if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
-+		entry->ipv6.ports = ports_pad;
-+
-+	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
-+		entry->ipv6.ib2 = val;
-+		l2 = &entry->ipv6.l2;
-+	} else {
-+		entry->ipv4.ib2 = val;
-+		l2 = &entry->ipv4.l2;
-+	}
-+
-+	l2->dest_mac_hi = get_unaligned_be32(dest_mac);
-+	l2->dest_mac_lo = get_unaligned_be16(dest_mac + 4);
-+	l2->src_mac_hi = get_unaligned_be32(src_mac);
-+	l2->src_mac_lo = get_unaligned_be16(src_mac + 4);
-+
-+	if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
-+		l2->etype = ETH_P_IPV6;
-+	else
-+		l2->etype = ETH_P_IP;
-+
-+	return 0;
-+}
-+
-+int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port)
-+{
-+	u32 *ib2 = mtk_foe_entry_ib2(entry);
-+	u32 val;
-+
-+	val = *ib2;
-+	val &= ~MTK_FOE_IB2_DEST_PORT;
-+	val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port);
-+	*ib2 = val;
-+
-+	return 0;
-+}
-+
-+int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool egress,
-+				 __be32 src_addr, __be16 src_port,
-+				 __be32 dest_addr, __be16 dest_port)
-+{
-+	int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
-+	struct mtk_ipv4_tuple *t;
-+
-+	switch (type) {
-+	case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
-+		if (egress) {
-+			t = &entry->ipv4.new;
-+			break;
-+		}
-+		fallthrough;
-+	case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
-+	case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
-+		t = &entry->ipv4.orig;
-+		break;
-+	case MTK_PPE_PKT_TYPE_IPV6_6RD:
-+		entry->ipv6_6rd.tunnel_src_ip = be32_to_cpu(src_addr);
-+		entry->ipv6_6rd.tunnel_dest_ip = be32_to_cpu(dest_addr);
-+		return 0;
-+	default:
-+		WARN_ON_ONCE(1);
-+		return -EINVAL;
-+	}
-+
-+	t->src_ip = be32_to_cpu(src_addr);
-+	t->dest_ip = be32_to_cpu(dest_addr);
-+
-+	if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
-+		return 0;
-+
-+	t->src_port = be16_to_cpu(src_port);
-+	t->dest_port = be16_to_cpu(dest_port);
-+
-+	return 0;
-+}
-+
-+int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry,
-+				 __be32 *src_addr, __be16 src_port,
-+				 __be32 *dest_addr, __be16 dest_port)
-+{
-+	int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
-+	u32 *src, *dest;
-+	int i;
-+
-+	switch (type) {
-+	case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
-+		src = entry->dslite.tunnel_src_ip;
-+		dest = entry->dslite.tunnel_dest_ip;
-+		break;
-+	case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
-+	case MTK_PPE_PKT_TYPE_IPV6_6RD:
-+		entry->ipv6.src_port = be16_to_cpu(src_port);
-+		entry->ipv6.dest_port = be16_to_cpu(dest_port);
-+		fallthrough;
-+	case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
-+		src = entry->ipv6.src_ip;
-+		dest = entry->ipv6.dest_ip;
-+		break;
-+	default:
-+		WARN_ON_ONCE(1);
-+		return -EINVAL;
-+	}
-+
-+	for (i = 0; i < 4; i++)
-+		src[i] = be32_to_cpu(src_addr[i]);
-+	for (i = 0; i < 4; i++)
-+		dest[i] = be32_to_cpu(dest_addr[i]);
-+
-+	return 0;
-+}
-+
-+int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port)
-+{
-+	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
-+
-+	l2->etype = BIT(port);
-+
-+	if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER))
-+		entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
-+	else
-+		l2->etype |= BIT(8);
-+
-+	entry->ib1 &= ~MTK_FOE_IB1_BIND_VLAN_TAG;
-+
-+	return 0;
-+}
-+
-+int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid)
-+{
-+	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
-+
-+	switch (FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, entry->ib1)) {
-+	case 0:
-+		entry->ib1 |= MTK_FOE_IB1_BIND_VLAN_TAG |
-+			      FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
-+		l2->vlan1 = vid;
-+		return 0;
-+	case 1:
-+		if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG)) {
-+			l2->vlan1 = vid;
-+			l2->etype |= BIT(8);
-+		} else {
-+			l2->vlan2 = vid;
-+			entry->ib1 += FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
-+		}
-+		return 0;
-+	default:
-+		return -ENOSPC;
-+	}
-+}
-+
-+int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid)
-+{
-+	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
-+
-+	if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER) ||
-+	    (entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG))
-+		l2->etype = ETH_P_PPP_SES;
-+
-+	entry->ib1 |= MTK_FOE_IB1_BIND_PPPOE;
-+	l2->pppoe_id = sid;
-+
-+	return 0;
-+}
-+
-+static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry)
-+{
-+	return !(entry->ib1 & MTK_FOE_IB1_STATIC) &&
-+	       FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND;
-+}
-+
-+int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
-+			 u16 timestamp)
-+{
-+	struct mtk_foe_entry *hwe;
-+	u32 hash;
-+
-+	timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP;
-+	entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
-+	entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp);
-+
-+	hash = mtk_ppe_hash_entry(entry);
-+	hwe = &ppe->foe_table[hash];
-+	if (!mtk_foe_entry_usable(hwe)) {
-+		hwe++;
-+		hash++;
-+
-+		if (!mtk_foe_entry_usable(hwe))
-+			return -ENOSPC;
-+	}
-+
-+	memcpy(&hwe->data, &entry->data, sizeof(hwe->data));
-+	wmb();
-+	hwe->ib1 = entry->ib1;
-+
-+	dma_wmb();
-+
-+	mtk_ppe_cache_clear(ppe);
-+
-+	return hash;
-+}
-+
-+int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base)
-+{
-+	struct mtk_foe_entry *foe;
-+
-+	/* need to allocate a separate device, since it PPE DMA access is
-+	 * not coherent.
-+	 */
-+	ppe->base = base;
-+	ppe->dev = dev;
-+	ppe->version = eth->soc->offload_version;
-+
-+	foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe),
-+				  &ppe->foe_phys, GFP_KERNEL);
-+	if (!foe)
-+		return -ENOMEM;
-+
-+	ppe->foe_table = foe;
-+
-+	mtk_ppe_debugfs_init(ppe);
-+
-+	return 0;
-+}
-+
-+static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
-+{
-+	static const u8 skip[] = { 12, 25, 38, 51, 76, 89, 102 };
-+	int i, k;
-+
-+	memset(ppe->foe_table, 0, MTK_PPE_ENTRIES * sizeof(*ppe->foe_table));
-+
-+	if (!IS_ENABLED(CONFIG_SOC_MT7621))
-+		return;
-+
-+	/* skip all entries that cross the 1024 byte boundary */
-+	for (i = 0; i < MTK_PPE_ENTRIES; i += 128)
-+		for (k = 0; k < ARRAY_SIZE(skip); k++)
-+			ppe->foe_table[i + skip[k]].ib1 |= MTK_FOE_IB1_STATIC;
-+}
-+
-+int mtk_ppe_start(struct mtk_ppe *ppe)
-+{
-+	u32 val;
-+
-+	mtk_ppe_init_foe_table(ppe);
-+	ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys);
-+
-+	val = MTK_PPE_TB_CFG_ENTRY_80B |
-+	      MTK_PPE_TB_CFG_AGE_NON_L4 |
-+	      MTK_PPE_TB_CFG_AGE_UNBIND |
-+	      MTK_PPE_TB_CFG_AGE_TCP |
-+	      MTK_PPE_TB_CFG_AGE_UDP |
-+	      MTK_PPE_TB_CFG_AGE_TCP_FIN |
-+	      FIELD_PREP(MTK_PPE_TB_CFG_SEARCH_MISS,
-+			 MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD) |
-+	      FIELD_PREP(MTK_PPE_TB_CFG_KEEPALIVE,
-+			 MTK_PPE_KEEPALIVE_DISABLE) |
-+	      FIELD_PREP(MTK_PPE_TB_CFG_HASH_MODE, 1) |
-+	      FIELD_PREP(MTK_PPE_TB_CFG_SCAN_MODE,
-+			 MTK_PPE_SCAN_MODE_CHECK_AGE) |
-+	      FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM,
-+			 MTK_PPE_ENTRIES_SHIFT);
-+	ppe_w32(ppe, MTK_PPE_TB_CFG, val);
-+
-+	ppe_w32(ppe, MTK_PPE_IP_PROTO_CHK,
-+		MTK_PPE_IP_PROTO_CHK_IPV4 | MTK_PPE_IP_PROTO_CHK_IPV6);
-+
-+	mtk_ppe_cache_enable(ppe, true);
-+
-+	val = MTK_PPE_FLOW_CFG_IP4_TCP_FRAG |
-+	      MTK_PPE_FLOW_CFG_IP4_UDP_FRAG |
-+	      MTK_PPE_FLOW_CFG_IP6_3T_ROUTE |
-+	      MTK_PPE_FLOW_CFG_IP6_5T_ROUTE |
-+	      MTK_PPE_FLOW_CFG_IP6_6RD |
-+	      MTK_PPE_FLOW_CFG_IP4_NAT |
-+	      MTK_PPE_FLOW_CFG_IP4_NAPT |
-+	      MTK_PPE_FLOW_CFG_IP4_DSLITE |
-+	      MTK_PPE_FLOW_CFG_L2_BRIDGE |
-+	      MTK_PPE_FLOW_CFG_IP4_NAT_FRAG;
-+	ppe_w32(ppe, MTK_PPE_FLOW_CFG, val);
-+
-+	val = FIELD_PREP(MTK_PPE_UNBIND_AGE_MIN_PACKETS, 1000) |
-+	      FIELD_PREP(MTK_PPE_UNBIND_AGE_DELTA, 3);
-+	ppe_w32(ppe, MTK_PPE_UNBIND_AGE, val);
-+
-+	val = FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_UDP, 30) |
-+	      FIELD_PREP(MTK_PPE_BIND_AGE0_DELTA_NON_L4, 1);
-+	ppe_w32(ppe, MTK_PPE_BIND_AGE0, val);
-+
-+	val = FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP_FIN, 1) |
-+	      FIELD_PREP(MTK_PPE_BIND_AGE1_DELTA_TCP, 30);
-+	ppe_w32(ppe, MTK_PPE_BIND_AGE1, val);
-+
-+	val = MTK_PPE_BIND_LIMIT0_QUARTER | MTK_PPE_BIND_LIMIT0_HALF;
-+	ppe_w32(ppe, MTK_PPE_BIND_LIMIT0, val);
-+
-+	val = MTK_PPE_BIND_LIMIT1_FULL |
-+	      FIELD_PREP(MTK_PPE_BIND_LIMIT1_NON_L4, 1);
-+	ppe_w32(ppe, MTK_PPE_BIND_LIMIT1, val);
-+
-+	val = FIELD_PREP(MTK_PPE_BIND_RATE_BIND, 30) |
-+	      FIELD_PREP(MTK_PPE_BIND_RATE_PREBIND, 1);
-+	ppe_w32(ppe, MTK_PPE_BIND_RATE, val);
-+
-+	/* enable PPE */
-+	val = MTK_PPE_GLO_CFG_EN |
-+	      MTK_PPE_GLO_CFG_IP4_L4_CS_DROP |
-+	      MTK_PPE_GLO_CFG_IP4_CS_DROP |
-+	      MTK_PPE_GLO_CFG_MCAST_TB_EN |
-+	      MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE;
-+	ppe_w32(ppe, MTK_PPE_GLO_CFG, val);
-+
-+	ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0);
-+
-+	return 0;
-+}
-+
-+int mtk_ppe_stop(struct mtk_ppe *ppe)
-+{
-+	u32 val;
-+	int i;
-+
-+	for (i = 0; i < MTK_PPE_ENTRIES; i++)
-+		ppe->foe_table[i].ib1 = FIELD_PREP(MTK_FOE_IB1_STATE,
-+						   MTK_FOE_STATE_INVALID);
-+
-+	mtk_ppe_cache_enable(ppe, false);
-+
-+	/* disable aging */
-+	val = MTK_PPE_TB_CFG_AGE_NON_L4 |
-+	      MTK_PPE_TB_CFG_AGE_UNBIND |
-+	      MTK_PPE_TB_CFG_AGE_TCP |
-+	      MTK_PPE_TB_CFG_AGE_UDP |
-+	      MTK_PPE_TB_CFG_AGE_TCP_FIN |
-+	      MTK_PPE_TB_CFG_SCAN_MODE;
-+	ppe_clear(ppe, MTK_PPE_TB_CFG, val);
-+
-+	if (mtk_ppe_wait_busy(ppe))
-+		return -ETIMEDOUT;
-+
-+	/* disable offload engine */
-+	ppe_clear(ppe, MTK_PPE_GLO_CFG, MTK_PPE_GLO_CFG_EN);
-+	ppe_w32(ppe, MTK_PPE_FLOW_CFG, 0);
-+
-+	return 0;
-+}
-diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
-new file mode 100644
-index 0000000..4787447
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -0,0 +1,287 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-+
-+#ifndef __MTK_PPE_H
-+#define __MTK_PPE_H
-+
-+#include <linux/kernel.h>
-+#include <linux/bitfield.h>
-+
-+#define MTK_ETH_PPE_BASE		0xc00
-+
-+#define MTK_PPE_ENTRIES_SHIFT		3
-+#define MTK_PPE_ENTRIES			(1024 << MTK_PPE_ENTRIES_SHIFT)
-+#define MTK_PPE_HASH_MASK		(MTK_PPE_ENTRIES - 1)
-+#define MTK_PPE_WAIT_TIMEOUT_US		1000000
-+
-+#define MTK_FOE_IB1_UNBIND_TIMESTAMP	GENMASK(7, 0)
-+#define MTK_FOE_IB1_UNBIND_PACKETS	GENMASK(23, 8)
-+#define MTK_FOE_IB1_UNBIND_PREBIND	BIT(24)
-+
-+#define MTK_FOE_IB1_BIND_TIMESTAMP	GENMASK(14, 0)
-+#define MTK_FOE_IB1_BIND_KEEPALIVE	BIT(15)
-+#define MTK_FOE_IB1_BIND_VLAN_LAYER	GENMASK(18, 16)
-+#define MTK_FOE_IB1_BIND_PPPOE		BIT(19)
-+#define MTK_FOE_IB1_BIND_VLAN_TAG	BIT(20)
-+#define MTK_FOE_IB1_BIND_PKT_SAMPLE	BIT(21)
-+#define MTK_FOE_IB1_BIND_CACHE		BIT(22)
-+#define MTK_FOE_IB1_BIND_TUNNEL_DECAP	BIT(23)
-+#define MTK_FOE_IB1_BIND_TTL		BIT(24)
-+
-+#define MTK_FOE_IB1_PACKET_TYPE		GENMASK(27, 25)
-+#define MTK_FOE_IB1_STATE		GENMASK(29, 28)
-+#define MTK_FOE_IB1_UDP			BIT(30)
-+#define MTK_FOE_IB1_STATIC		BIT(31)
-+
-+enum {
-+	MTK_PPE_PKT_TYPE_IPV4_HNAPT = 0,
-+	MTK_PPE_PKT_TYPE_IPV4_ROUTE = 1,
-+	MTK_PPE_PKT_TYPE_BRIDGE = 2,
-+	MTK_PPE_PKT_TYPE_IPV4_DSLITE = 3,
-+	MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T = 4,
-+	MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T = 5,
-+	MTK_PPE_PKT_TYPE_IPV6_6RD = 7,
-+};
-+
-+#define MTK_FOE_IB2_QID			GENMASK(3, 0)
-+#define MTK_FOE_IB2_PSE_QOS		BIT(4)
-+#define MTK_FOE_IB2_DEST_PORT		GENMASK(7, 5)
-+#define MTK_FOE_IB2_MULTICAST		BIT(8)
-+
-+#define MTK_FOE_IB2_WHNAT_QID2		GENMASK(13, 12)
-+#define MTK_FOE_IB2_WHNAT_DEVIDX	BIT(16)
-+#define MTK_FOE_IB2_WHNAT_NAT		BIT(17)
-+
-+#define MTK_FOE_IB2_PORT_MG		GENMASK(17, 12)
-+
-+#define MTK_FOE_IB2_PORT_AG		GENMASK(23, 18)
-+
-+#define MTK_FOE_IB2_DSCP		GENMASK(31, 24)
-+
-+#define MTK_FOE_VLAN2_WHNAT_BSS		GEMMASK(5, 0)
-+#define MTK_FOE_VLAN2_WHNAT_WCID	GENMASK(13, 6)
-+#define MTK_FOE_VLAN2_WHNAT_RING	GENMASK(15, 14)
-+
-+enum {
-+	MTK_FOE_STATE_INVALID,
-+	MTK_FOE_STATE_UNBIND,
-+	MTK_FOE_STATE_BIND,
-+	MTK_FOE_STATE_FIN
-+};
-+
-+struct mtk_foe_mac_info {
-+	u16 vlan1;
-+	u16 etype;
-+
-+	u32 dest_mac_hi;
-+
-+	u16 vlan2;
-+	u16 dest_mac_lo;
-+
-+	u32 src_mac_hi;
-+
-+	u16 pppoe_id;
-+	u16 src_mac_lo;
-+};
-+
-+struct mtk_foe_bridge {
-+	u32 dest_mac_hi;
-+
-+	u16 src_mac_lo;
-+	u16 dest_mac_lo;
-+
-+	u32 src_mac_hi;
-+
-+	u32 ib2;
-+
-+	u32 _rsv[5];
-+
-+	u32 udf_tsid;
-+	struct mtk_foe_mac_info l2;
-+};
-+
-+struct mtk_ipv4_tuple {
-+	u32 src_ip;
-+	u32 dest_ip;
-+	union {
-+		struct {
-+			u16 dest_port;
-+			u16 src_port;
-+		};
-+		struct {
-+			u8 protocol;
-+			u8 _pad[3]; /* fill with 0xa5a5a5 */
-+		};
-+		u32 ports;
-+	};
-+};
-+
-+struct mtk_foe_ipv4 {
-+	struct mtk_ipv4_tuple orig;
-+
-+	u32 ib2;
-+
-+	struct mtk_ipv4_tuple new;
-+
-+	u16 timestamp;
-+	u16 _rsv0[3];
-+
-+	u32 udf_tsid;
-+
-+	struct mtk_foe_mac_info l2;
-+};
-+
-+struct mtk_foe_ipv4_dslite {
-+	struct mtk_ipv4_tuple ip4;
-+
-+	u32 tunnel_src_ip[4];
-+	u32 tunnel_dest_ip[4];
-+
-+	u8 flow_label[3];
-+	u8 priority;
-+
-+	u32 udf_tsid;
-+
-+	u32 ib2;
-+
-+	struct mtk_foe_mac_info l2;
-+};
-+
-+struct mtk_foe_ipv6 {
-+	u32 src_ip[4];
-+	u32 dest_ip[4];
-+
-+	union {
-+		struct {
-+			u8 protocol;
-+			u8 _pad[3]; /* fill with 0xa5a5a5 */
-+		}; /* 3-tuple */
-+		struct {
-+			u16 dest_port;
-+			u16 src_port;
-+		}; /* 5-tuple */
-+		u32 ports;
-+	};
-+
-+	u32 _rsv[3];
-+
-+	u32 udf;
-+
-+	u32 ib2;
-+	struct mtk_foe_mac_info l2;
-+};
-+
-+struct mtk_foe_ipv6_6rd {
-+	u32 src_ip[4];
-+	u32 dest_ip[4];
-+	u16 dest_port;
-+	u16 src_port;
-+
-+	u32 tunnel_src_ip;
-+	u32 tunnel_dest_ip;
-+
-+	u16 hdr_csum;
-+	u8 dscp;
-+	u8 ttl;
-+
-+	u8 flag;
-+	u8 pad;
-+	u8 per_flow_6rd_id;
-+	u8 pad2;
-+
-+	u32 ib2;
-+	struct mtk_foe_mac_info l2;
-+};
-+
-+struct mtk_foe_entry {
-+	u32 ib1;
-+
-+	union {
-+		struct mtk_foe_bridge bridge;
-+		struct mtk_foe_ipv4 ipv4;
-+		struct mtk_foe_ipv4_dslite dslite;
-+		struct mtk_foe_ipv6 ipv6;
-+		struct mtk_foe_ipv6_6rd ipv6_6rd;
-+		u32 data[19];
-+	};
-+};
-+
-+enum {
-+	MTK_PPE_CPU_REASON_TTL_EXCEEDED			= 0x02,
-+	MTK_PPE_CPU_REASON_OPTION_HEADER		= 0x03,
-+	MTK_PPE_CPU_REASON_NO_FLOW			= 0x07,
-+	MTK_PPE_CPU_REASON_IPV4_FRAG			= 0x08,
-+	MTK_PPE_CPU_REASON_IPV4_DSLITE_FRAG		= 0x09,
-+	MTK_PPE_CPU_REASON_IPV4_DSLITE_NO_TCP_UDP	= 0x0a,
-+	MTK_PPE_CPU_REASON_IPV6_6RD_NO_TCP_UDP		= 0x0b,
-+	MTK_PPE_CPU_REASON_TCP_FIN_SYN_RST		= 0x0c,
-+	MTK_PPE_CPU_REASON_UN_HIT			= 0x0d,
-+	MTK_PPE_CPU_REASON_HIT_UNBIND			= 0x0e,
-+	MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED	= 0x0f,
-+	MTK_PPE_CPU_REASON_HIT_BIND_TCP_FIN		= 0x10,
-+	MTK_PPE_CPU_REASON_HIT_TTL_1			= 0x11,
-+	MTK_PPE_CPU_REASON_HIT_BIND_VLAN_VIOLATION	= 0x12,
-+	MTK_PPE_CPU_REASON_KEEPALIVE_UC_OLD_HDR		= 0x13,
-+	MTK_PPE_CPU_REASON_KEEPALIVE_MC_NEW_HDR		= 0x14,
-+	MTK_PPE_CPU_REASON_KEEPALIVE_DUP_OLD_HDR	= 0x15,
-+	MTK_PPE_CPU_REASON_HIT_BIND_FORCE_CPU		= 0x16,
-+	MTK_PPE_CPU_REASON_TUNNEL_OPTION_HEADER		= 0x17,
-+	MTK_PPE_CPU_REASON_MULTICAST_TO_CPU		= 0x18,
-+	MTK_PPE_CPU_REASON_MULTICAST_TO_GMAC1_CPU	= 0x19,
-+	MTK_PPE_CPU_REASON_HIT_PRE_BIND			= 0x1a,
-+	MTK_PPE_CPU_REASON_PACKET_SAMPLING		= 0x1b,
-+	MTK_PPE_CPU_REASON_EXCEED_MTU			= 0x1c,
-+	MTK_PPE_CPU_REASON_PPE_BYPASS			= 0x1e,
-+	MTK_PPE_CPU_REASON_INVALID			= 0x1f,
-+};
-+
-+struct mtk_ppe {
-+	struct device *dev;
-+	void __iomem *base;
-+	int version;
-+
-+	struct mtk_foe_entry *foe_table;
-+	dma_addr_t foe_phys;
-+
-+	void *acct_table;
-+};
-+
-+int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base);
-+int mtk_ppe_start(struct mtk_ppe *ppe);
-+int mtk_ppe_stop(struct mtk_ppe *ppe);
-+
-+static inline void
-+mtk_foe_entry_clear(struct mtk_ppe *ppe, u16 hash)
-+{
-+	ppe->foe_table[hash].ib1 = 0;
-+	dma_wmb();
-+}
-+
-+static inline int
-+mtk_foe_entry_timestamp(struct mtk_ppe *ppe, u16 hash)
-+{
-+	u32 ib1 = READ_ONCE(ppe->foe_table[hash].ib1);
-+
-+	if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND)
-+		return -1;
-+
-+	return FIELD_GET(MTK_FOE_IB1_BIND_TIMESTAMP, ib1);
-+}
-+
-+int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
-+			  u8 pse_port, u8 *src_mac, u8 *dest_mac);
-+int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port);
-+int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool orig,
-+				 __be32 src_addr, __be16 src_port,
-+				 __be32 dest_addr, __be16 dest_port);
-+int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry,
-+				 __be32 *src_addr, __be16 src_port,
-+				 __be32 *dest_addr, __be16 dest_port);
-+int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port);
-+int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid);
-+int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
-+int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
-+			 u16 timestamp);
-+int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
-+
-+#endif
-diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
-new file mode 100644
-index 0000000..d4b4823
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
-@@ -0,0 +1,214 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-+
-+#include <linux/kernel.h>
-+#include <linux/debugfs.h>
-+#include "mtk_eth_soc.h"
-+
-+struct mtk_flow_addr_info
-+{
-+	void *src, *dest;
-+	u16 *src_port, *dest_port;
-+	bool ipv6;
-+};
-+
-+static const char *mtk_foe_entry_state_str(int state)
-+{
-+	static const char * const state_str[] = {
-+		[MTK_FOE_STATE_INVALID] = "INV",
-+		[MTK_FOE_STATE_UNBIND] = "UNB",
-+		[MTK_FOE_STATE_BIND] = "BND",
-+		[MTK_FOE_STATE_FIN] = "FIN",
-+	};
-+
-+	if (state >= ARRAY_SIZE(state_str) || !state_str[state])
-+		return "UNK";
-+
-+	return state_str[state];
-+}
-+
-+static const char *mtk_foe_pkt_type_str(int type)
-+{
-+	static const char * const type_str[] = {
-+		[MTK_PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
-+		[MTK_PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
-+		[MTK_PPE_PKT_TYPE_BRIDGE] = "L2",
-+		[MTK_PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
-+		[MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
-+		[MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",
-+		[MTK_PPE_PKT_TYPE_IPV6_6RD] = "6RD",
-+	};
-+
-+	if (type >= ARRAY_SIZE(type_str) || !type_str[type])
-+		return "UNKNOWN";
-+
-+	return type_str[type];
-+}
-+
-+static void
-+mtk_print_addr(struct seq_file *m, u32 *addr, bool ipv6)
-+{
-+	u32 n_addr[4];
-+	int i;
-+
-+	if (!ipv6) {
-+		seq_printf(m, "%pI4h", addr);
-+		return;
-+	}
-+
-+	for (i = 0; i < ARRAY_SIZE(n_addr); i++)
-+		n_addr[i] = htonl(addr[i]);
-+	seq_printf(m, "%pI6", n_addr);
-+}
-+
-+static void
-+mtk_print_addr_info(struct seq_file *m, struct mtk_flow_addr_info *ai)
-+{
-+	mtk_print_addr(m, ai->src, ai->ipv6);
-+	if (ai->src_port)
-+		seq_printf(m, ":%d", *ai->src_port);
-+	seq_printf(m, "->");
-+	mtk_print_addr(m, ai->dest, ai->ipv6);
-+	if (ai->dest_port)
-+		seq_printf(m, ":%d", *ai->dest_port);
-+}
-+
-+static int
-+mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind)
-+{
-+	struct mtk_ppe *ppe = m->private;
-+	int i;
-+
-+	for (i = 0; i < MTK_PPE_ENTRIES; i++) {
-+		struct mtk_foe_entry *entry = &ppe->foe_table[i];
-+		struct mtk_foe_mac_info *l2;
-+		struct mtk_flow_addr_info ai = {};
-+		unsigned char h_source[ETH_ALEN];
-+		unsigned char h_dest[ETH_ALEN];
-+		int type, state;
-+		u32 ib2;
-+
-+
-+		state = FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1);
-+		if (!state)
-+			continue;
-+
-+		if (bind && state != MTK_FOE_STATE_BIND)
-+			continue;
-+
-+		type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
-+		seq_printf(m, "%05x %s %7s", i,
-+			   mtk_foe_entry_state_str(state),
-+			   mtk_foe_pkt_type_str(type));
-+
-+		switch (type) {
-+		case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
-+		case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
-+			ai.src_port = &entry->ipv4.orig.src_port;
-+			ai.dest_port = &entry->ipv4.orig.dest_port;
-+			fallthrough;
-+		case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
-+			ai.src = &entry->ipv4.orig.src_ip;
-+			ai.dest = &entry->ipv4.orig.dest_ip;
-+			break;
-+		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T:
-+			ai.src_port = &entry->ipv6.src_port;
-+			ai.dest_port = &entry->ipv6.dest_port;
-+			fallthrough;
-+		case MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T:
-+		case MTK_PPE_PKT_TYPE_IPV6_6RD:
-+			ai.src = &entry->ipv6.src_ip;
-+			ai.dest = &entry->ipv6.dest_ip;
-+			ai.ipv6 = true;
-+			break;
-+		}
-+
-+		seq_printf(m, " orig=");
-+		mtk_print_addr_info(m, &ai);
-+
-+		switch (type) {
-+		case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
-+		case MTK_PPE_PKT_TYPE_IPV4_DSLITE:
-+			ai.src_port = &entry->ipv4.new.src_port;
-+			ai.dest_port = &entry->ipv4.new.dest_port;
-+			fallthrough;
-+		case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
-+			ai.src = &entry->ipv4.new.src_ip;
-+			ai.dest = &entry->ipv4.new.dest_ip;
-+			seq_printf(m, " new=");
-+			mtk_print_addr_info(m, &ai);
-+			break;
-+		}
-+
-+		if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
-+			l2 = &entry->ipv6.l2;
-+			ib2 = entry->ipv6.ib2;
-+		} else {
-+			l2 = &entry->ipv4.l2;
-+			ib2 = entry->ipv4.ib2;
-+		}
-+
-+		*((__be32 *)h_source) = htonl(l2->src_mac_hi);
-+		*((__be16 *)&h_source[4]) = htons(l2->src_mac_lo);
-+		*((__be32 *)h_dest) = htonl(l2->dest_mac_hi);
-+		*((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo);
-+
-+		seq_printf(m, " eth=%pM->%pM etype=%04x"
-+			      " vlan=%d,%d ib1=%08x ib2=%08x\n",
-+			   h_source, h_dest, ntohs(l2->etype),
-+			   l2->vlan1, l2->vlan2, entry->ib1, ib2);
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+mtk_ppe_debugfs_foe_show_all(struct seq_file *m, void *private)
-+{
-+	return mtk_ppe_debugfs_foe_show(m, private, false);
-+}
-+
-+static int
-+mtk_ppe_debugfs_foe_show_bind(struct seq_file *m, void *private)
-+{
-+	return mtk_ppe_debugfs_foe_show(m, private, true);
-+}
-+
-+static int
-+mtk_ppe_debugfs_foe_open_all(struct inode *inode, struct file *file)
-+{
-+	return single_open(file, mtk_ppe_debugfs_foe_show_all,
-+			   inode->i_private);
-+}
-+
-+static int
-+mtk_ppe_debugfs_foe_open_bind(struct inode *inode, struct file *file)
-+{
-+	return single_open(file, mtk_ppe_debugfs_foe_show_bind,
-+			   inode->i_private);
-+}
-+
-+int mtk_ppe_debugfs_init(struct mtk_ppe *ppe)
-+{
-+	static const struct file_operations fops_all = {
-+		.open = mtk_ppe_debugfs_foe_open_all,
-+		.read = seq_read,
-+		.llseek = seq_lseek,
-+		.release = single_release,
-+	};
-+
-+	static const struct file_operations fops_bind = {
-+		.open = mtk_ppe_debugfs_foe_open_bind,
-+		.read = seq_read,
-+		.llseek = seq_lseek,
-+		.release = single_release,
-+	};
-+
-+	struct dentry *root;
-+
-+	root = debugfs_create_dir("mtk_ppe", NULL);
-+	debugfs_create_file("entries", S_IRUGO, root, ppe, &fops_all);
-+	debugfs_create_file("bind", S_IRUGO, root, ppe, &fops_bind);
-+
-+	return 0;
-+}
-diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-new file mode 100644
-index 0000000..1380ef0
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -0,0 +1,535 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/*
-+ *  Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
-+ */
-+
-+#include <linux/if_ether.h>
-+#include <linux/rhashtable.h>
-+#include <linux/ip.h>
-+#include <linux/ipv6.h>
-+#include <net/flow_offload.h>
-+#include <net/pkt_cls.h>
-+#include <net/dsa.h>
-+#include "mtk_eth_soc.h"
-+
-+struct mtk_flow_data {
-+	struct ethhdr eth;
-+
-+	union {
-+		struct {
-+			__be32 src_addr;
-+			__be32 dst_addr;
-+		} v4;
-+
-+		struct {
-+			struct in6_addr src_addr;
-+			struct in6_addr dst_addr;
-+		} v6;
-+	};
-+
-+	__be16 src_port;
-+	__be16 dst_port;
-+
-+	struct {
-+		u16 id;
-+		__be16 proto;
-+		u8 num;
-+	} vlan;
-+	struct {
-+		u16 sid;
-+		u8 num;
-+	} pppoe;
-+};
-+
-+struct mtk_flow_entry {
-+	struct rhash_head node;
-+	unsigned long cookie;
-+	u16 hash;
-+};
-+
-+static const struct rhashtable_params mtk_flow_ht_params = {
-+	.head_offset = offsetof(struct mtk_flow_entry, node),
-+	.key_offset = offsetof(struct mtk_flow_entry, cookie),
-+	.key_len = sizeof(unsigned long),
-+	.automatic_shrinking = true,
-+};
-+
-+static u32
-+mtk_eth_timestamp(struct mtk_eth *eth)
-+{
-+	return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP;
-+}
-+
-+static int
-+mtk_flow_set_ipv4_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data,
-+		       bool egress)
-+{
-+	return mtk_foe_entry_set_ipv4_tuple(foe, egress,
-+					    data->v4.src_addr, data->src_port,
-+					    data->v4.dst_addr, data->dst_port);
-+}
-+
-+static int
-+mtk_flow_set_ipv6_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data)
-+{
-+	return mtk_foe_entry_set_ipv6_tuple(foe,
-+					    data->v6.src_addr.s6_addr32, data->src_port,
-+					    data->v6.dst_addr.s6_addr32, data->dst_port);
-+}
-+
-+static void
-+mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
-+{
-+	void *dest = eth + act->mangle.offset;
-+	const void *src = &act->mangle.val;
-+
-+	if (act->mangle.offset > 8)
-+		return;
-+
-+	if (act->mangle.mask == 0xffff) {
-+		src += 2;
-+		dest += 2;
-+	}
-+
-+	memcpy(dest, src, act->mangle.mask ? 2 : 4);
-+}
-+
-+
-+static int
-+mtk_flow_mangle_ports(const struct flow_action_entry *act,
-+		      struct mtk_flow_data *data)
-+{
-+	u32 val = ntohl(act->mangle.val);
-+
-+	switch (act->mangle.offset) {
-+	case 0:
-+		if (act->mangle.mask == ~htonl(0xffff))
-+			data->dst_port = cpu_to_be16(val);
-+		else
-+			data->src_port = cpu_to_be16(val >> 16);
-+		break;
-+	case 2:
-+		data->dst_port = cpu_to_be16(val);
-+		break;
-+	default:
-+		return -EINVAL;
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+mtk_flow_mangle_ipv4(const struct flow_action_entry *act,
-+		     struct mtk_flow_data *data)
-+{
-+	__be32 *dest;
-+
-+	switch (act->mangle.offset) {
-+	case offsetof(struct iphdr, saddr):
-+		dest = &data->v4.src_addr;
-+		break;
-+	case offsetof(struct iphdr, daddr):
-+		dest = &data->v4.dst_addr;
-+		break;
-+	default:
-+		return -EINVAL;
-+	}
-+
-+	memcpy(dest, &act->mangle.val, sizeof(u32));
-+
-+	return 0;
-+}
-+
-+static int
-+mtk_flow_get_dsa_port(struct net_device **dev)
-+{
-+#if IS_ENABLED(CONFIG_NET_DSA)
-+	struct dsa_port *dp;
-+
-+	dp = dsa_port_from_netdev(*dev);
-+	if (IS_ERR(dp))
-+		return -ENODEV;
-+
-+	if (dp->cpu_dp->tag_ops->proto != DSA_TAG_PROTO_MTK)
-+		return -ENODEV;
-+
-+	*dev = dp->cpu_dp->master;
-+
-+	return dp->index;
-+#else
-+	return -ENODEV;
-+#endif
-+}
-+
-+static int
-+mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
-+			   struct net_device *dev)
-+{
-+	int pse_port, dsa_port;
-+
-+	dsa_port = mtk_flow_get_dsa_port(&dev);
-+	if (dsa_port >= 0)
-+		mtk_foe_entry_set_dsa(foe, dsa_port);
-+
-+	if (dev == eth->netdev[0])
-+		pse_port = PSE_GDM1_PORT;
-+	else if (dev == eth->netdev[1])
-+		pse_port = PSE_GDM2_PORT;
-+	else
-+		return -EOPNOTSUPP;
-+
-+	mtk_foe_entry_set_pse_port(foe, pse_port);
-+
-+	return 0;
-+}
-+
-+static int
-+mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
-+{
-+	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
-+	struct flow_action_entry *act;
-+	struct mtk_flow_data data = {};
-+	struct mtk_foe_entry foe;
-+	struct net_device *odev = NULL;
-+	struct mtk_flow_entry *entry;
-+	int offload_type = 0;
-+	u16 addr_type = 0;
-+	u32 timestamp;
-+	u8 l4proto = 0;
-+	int err = 0;
-+	int hash;
-+	int i;
-+
-+	if (rhashtable_lookup(&eth->flow_table, &f->cookie, mtk_flow_ht_params))
-+		return -EEXIST;
-+
-+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) {
-+		struct flow_match_meta match;
-+
-+		flow_rule_match_meta(rule, &match);
-+	} else {
-+		return -EOPNOTSUPP;
-+	}
-+
-+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
-+		struct flow_match_control match;
-+
-+		flow_rule_match_control(rule, &match);
-+		addr_type = match.key->addr_type;
-+	} else {
-+		return -EOPNOTSUPP;
-+	}
-+
-+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
-+		struct flow_match_basic match;
-+
-+		flow_rule_match_basic(rule, &match);
-+		l4proto = match.key->ip_proto;
-+	} else {
-+		return -EOPNOTSUPP;
-+	}
-+
-+	flow_action_for_each(i, act, &rule->action) {
-+		switch (act->id) {
-+		case FLOW_ACTION_MANGLE:
-+			if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH)
-+				mtk_flow_offload_mangle_eth(act, &data.eth);
-+			break;
-+		case FLOW_ACTION_REDIRECT:
-+			odev = act->dev;
-+			break;
-+		case FLOW_ACTION_CSUM:
-+			break;
-+		case FLOW_ACTION_VLAN_PUSH:
-+			if (data.vlan.num == 1 ||
-+			    act->vlan.proto != htons(ETH_P_8021Q))
-+				return -EOPNOTSUPP;
-+
-+			data.vlan.id = act->vlan.vid;
-+			data.vlan.proto = act->vlan.proto;
-+			data.vlan.num++;
-+			break;
-+		case FLOW_ACTION_VLAN_POP:
-+			break;
-+		case FLOW_ACTION_PPPOE_PUSH:
-+			if (data.pppoe.num == 1)
-+				return -EOPNOTSUPP;
-+
-+			data.pppoe.sid = act->pppoe.sid;
-+			data.pppoe.num++;
-+			break;
-+		default:
-+			return -EOPNOTSUPP;
-+		}
-+	}
-+
-+	switch (addr_type) {
-+	case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
-+		offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
-+		break;
-+	case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
-+		offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
-+		break;
-+	default:
-+		return -EOPNOTSUPP;
-+	}
-+
-+	if (!is_valid_ether_addr(data.eth.h_source) ||
-+	    !is_valid_ether_addr(data.eth.h_dest))
-+		return -EINVAL;
-+
-+	err = mtk_foe_entry_prepare(&foe, offload_type, l4proto, 0,
-+				    data.eth.h_source,
-+				    data.eth.h_dest);
-+	if (err)
-+		return err;
-+
-+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
-+		struct flow_match_ports ports;
-+
-+		flow_rule_match_ports(rule, &ports);
-+		data.src_port = ports.key->src;
-+		data.dst_port = ports.key->dst;
-+	} else {
-+		return -EOPNOTSUPP;
-+	}
-+
-+	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
-+		struct flow_match_ipv4_addrs addrs;
-+
-+		flow_rule_match_ipv4_addrs(rule, &addrs);
-+
-+		data.v4.src_addr = addrs.key->src;
-+		data.v4.dst_addr = addrs.key->dst;
-+
-+		mtk_flow_set_ipv4_addr(&foe, &data, false);
-+	}
-+
-+	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
-+		struct flow_match_ipv6_addrs addrs;
-+
-+		flow_rule_match_ipv6_addrs(rule, &addrs);
-+
-+		data.v6.src_addr = addrs.key->src;
-+		data.v6.dst_addr = addrs.key->dst;
-+
-+		mtk_flow_set_ipv6_addr(&foe, &data);
-+	}
-+
-+	flow_action_for_each(i, act, &rule->action) {
-+		if (act->id != FLOW_ACTION_MANGLE)
-+			continue;
-+
-+		switch (act->mangle.htype) {
-+		case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
-+		case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
-+			err = mtk_flow_mangle_ports(act, &data);
-+			break;
-+		case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
-+			err = mtk_flow_mangle_ipv4(act, &data);
-+			break;
-+		case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
-+			/* handled earlier */
-+			break;
-+		default:
-+			return -EOPNOTSUPP;
-+		}
-+
-+		if (err)
-+			return err;
-+	}
-+
-+	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
-+		err = mtk_flow_set_ipv4_addr(&foe, &data, true);
-+		if (err)
-+			return err;
-+	}
-+
-+	if (data.vlan.num == 1) {
-+		if (data.vlan.proto != htons(ETH_P_8021Q))
-+			return -EOPNOTSUPP;
-+
-+		mtk_foe_entry_set_vlan(&foe, data.vlan.id);
-+	}
-+	if (data.pppoe.num == 1)
-+		mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid);
-+
-+	err = mtk_flow_set_output_device(eth, &foe, odev);
-+	if (err)
-+		return err;
-+
-+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
-+	if (!entry)
-+		return -ENOMEM;
-+
-+	entry->cookie = f->cookie;
-+	timestamp = mtk_eth_timestamp(eth);
-+	hash = mtk_foe_entry_commit(&eth->ppe, &foe, timestamp);
-+	if (hash < 0) {
-+		err = hash;
-+		goto free;
-+	}
-+
-+	entry->hash = hash;
-+	err = rhashtable_insert_fast(&eth->flow_table, &entry->node,
-+				     mtk_flow_ht_params);
-+	if (err < 0)
-+		goto clear_flow;
-+
-+	return 0;
-+clear_flow:
-+	mtk_foe_entry_clear(&eth->ppe, hash);
-+free:
-+	kfree(entry);
-+	return err;
-+}
-+
-+static int
-+mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f)
-+{
-+	struct mtk_flow_entry *entry;
-+
-+	entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
-+				  mtk_flow_ht_params);
-+	if (!entry)
-+		return -ENOENT;
-+
-+	mtk_foe_entry_clear(&eth->ppe, entry->hash);
-+	rhashtable_remove_fast(&eth->flow_table, &entry->node,
-+			       mtk_flow_ht_params);
-+	kfree(entry);
-+
-+	return 0;
-+}
-+
-+static int
-+mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
-+{
-+	struct mtk_flow_entry *entry;
-+	int timestamp;
-+	u32 idle;
-+
-+	entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
-+				  mtk_flow_ht_params);
-+	if (!entry)
-+		return -ENOENT;
-+
-+	timestamp = mtk_foe_entry_timestamp(&eth->ppe, entry->hash);
-+	if (timestamp < 0)
-+		return -ETIMEDOUT;
-+
-+	idle = mtk_eth_timestamp(eth) - timestamp;
-+	f->stats.lastused = jiffies - idle * HZ;
-+
-+	return 0;
-+}
-+
-+static DEFINE_MUTEX(mtk_flow_offload_mutex);
-+
-+static int
-+mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
-+{
-+	struct flow_cls_offload *cls = type_data;
-+	struct net_device *dev = cb_priv;
-+	struct mtk_mac *mac = netdev_priv(dev);
-+	struct mtk_eth *eth = mac->hw;
-+	int err;
-+
-+	if (!tc_can_offload(dev))
-+		return -EOPNOTSUPP;
-+
-+	if (type != TC_SETUP_CLSFLOWER)
-+		return -EOPNOTSUPP;
-+
-+	mutex_lock(&mtk_flow_offload_mutex);
-+	switch (cls->command) {
-+	case FLOW_CLS_REPLACE:
-+		err = mtk_flow_offload_replace(eth, cls);
-+		break;
-+	case FLOW_CLS_DESTROY:
-+		err = mtk_flow_offload_destroy(eth, cls);
-+		break;
-+	case FLOW_CLS_STATS:
-+		err = mtk_flow_offload_stats(eth, cls);
-+		break;
-+	default:
-+		err = -EOPNOTSUPP;
-+		break;
-+	}
-+	mutex_unlock(&mtk_flow_offload_mutex);
-+
-+	return err;
-+}
-+
-+static int
-+mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
-+{
-+	struct mtk_mac *mac = netdev_priv(dev);
-+	struct mtk_eth *eth = mac->hw;
-+	static LIST_HEAD(block_cb_list);
-+	struct flow_block_cb *block_cb;
-+	flow_setup_cb_t *cb;
-+	int err = 0;
-+
-+	if (!eth->ppe.foe_table)
-+		return -EOPNOTSUPP;
-+
-+	if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
-+		return -EOPNOTSUPP;
-+
-+	cb = mtk_eth_setup_tc_block_cb;
-+	f->driver_block_list = &block_cb_list;
-+
-+	switch (f->command) {
-+	case FLOW_BLOCK_BIND:
-+		block_cb = flow_block_cb_lookup(f->block, cb, dev);
-+		if (block_cb) {
-+			flow_block_cb_incref(block_cb);
-+			goto unlock;
-+		}
-+		block_cb = flow_block_cb_alloc(cb, dev, dev, NULL);
-+		if (IS_ERR(block_cb)) {
-+			err = PTR_ERR(block_cb);
-+			goto unlock;
-+		}
-+
-+		flow_block_cb_add(block_cb, f);
-+		list_add_tail(&block_cb->driver_list, &block_cb_list);
-+		break;
-+	case FLOW_BLOCK_UNBIND:
-+		block_cb = flow_block_cb_lookup(f->block, cb, dev);
-+		if (!block_cb) {
-+			err = -ENOENT;
-+			goto unlock;
-+		}
-+
-+		if (flow_block_cb_decref(block_cb)) {
-+			flow_block_cb_remove(block_cb, f);
-+			list_del(&block_cb->driver_list);
-+		}
-+		break;
-+	default:
-+		err = -EOPNOTSUPP;
-+		break;
-+	}
-+
-+unlock:
-+	return err;
-+}
-+
-+int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
-+		     void *type_data)
-+{
-+	if (type == TC_SETUP_FT)
-+		return mtk_eth_setup_tc_block(dev, type_data);
-+
-+	return -EOPNOTSUPP;
-+}
-+
-+int mtk_eth_offload_init(struct mtk_eth *eth)
-+{
-+	if (!eth->ppe.foe_table)
-+		return 0;
-+
-+	return rhashtable_init(&eth->flow_table, &mtk_flow_ht_params);
-+}
-diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
-new file mode 100644
-index 0000000..0c45ea0
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
-@@ -0,0 +1,144 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
-+
-+#ifndef __MTK_PPE_REGS_H
-+#define __MTK_PPE_REGS_H
-+
-+#define MTK_PPE_GLO_CFG				0x200
-+#define MTK_PPE_GLO_CFG_EN			BIT(0)
-+#define MTK_PPE_GLO_CFG_TSID_EN			BIT(1)
-+#define MTK_PPE_GLO_CFG_IP4_L4_CS_DROP		BIT(2)
-+#define MTK_PPE_GLO_CFG_IP4_CS_DROP		BIT(3)
-+#define MTK_PPE_GLO_CFG_TTL0_DROP		BIT(4)
-+#define MTK_PPE_GLO_CFG_PPE_BSWAP		BIT(5)
-+#define MTK_PPE_GLO_CFG_PSE_HASH_OFS		BIT(6)
-+#define MTK_PPE_GLO_CFG_MCAST_TB_EN		BIT(7)
-+#define MTK_PPE_GLO_CFG_FLOW_DROP_KA		BIT(8)
-+#define MTK_PPE_GLO_CFG_FLOW_DROP_UPDATE	BIT(9)
-+#define MTK_PPE_GLO_CFG_UDP_LITE_EN		BIT(10)
-+#define MTK_PPE_GLO_CFG_UDP_LEN_DROP		BIT(11)
-+#define MTK_PPE_GLO_CFG_MCAST_ENTRIES		GNEMASK(13, 12)
-+#define MTK_PPE_GLO_CFG_BUSY			BIT(31)
-+
-+#define MTK_PPE_FLOW_CFG			0x204
-+#define MTK_PPE_FLOW_CFG_IP4_TCP_FRAG		BIT(6)
-+#define MTK_PPE_FLOW_CFG_IP4_UDP_FRAG		BIT(7)
-+#define MTK_PPE_FLOW_CFG_IP6_3T_ROUTE		BIT(8)
-+#define MTK_PPE_FLOW_CFG_IP6_5T_ROUTE		BIT(9)
-+#define MTK_PPE_FLOW_CFG_IP6_6RD		BIT(10)
-+#define MTK_PPE_FLOW_CFG_IP4_NAT		BIT(12)
-+#define MTK_PPE_FLOW_CFG_IP4_NAPT		BIT(13)
-+#define MTK_PPE_FLOW_CFG_IP4_DSLITE		BIT(14)
-+#define MTK_PPE_FLOW_CFG_L2_BRIDGE		BIT(15)
-+#define MTK_PPE_FLOW_CFG_IP_PROTO_BLACKLIST	BIT(16)
-+#define MTK_PPE_FLOW_CFG_IP4_NAT_FRAG		BIT(17)
-+#define MTK_PPE_FLOW_CFG_IP4_HASH_FLOW_LABEL	BIT(18)
-+#define MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY	BIT(19)
-+#define MTK_PPE_FLOW_CFG_IP6_HASH_GRE_KEY	BIT(20)
-+
-+#define MTK_PPE_IP_PROTO_CHK			0x208
-+#define MTK_PPE_IP_PROTO_CHK_IPV4		GENMASK(15, 0)
-+#define MTK_PPE_IP_PROTO_CHK_IPV6		GENMASK(31, 16)
-+
-+#define MTK_PPE_TB_CFG				0x21c
-+#define MTK_PPE_TB_CFG_ENTRY_NUM		GENMASK(2, 0)
-+#define MTK_PPE_TB_CFG_ENTRY_80B		BIT(3)
-+#define MTK_PPE_TB_CFG_SEARCH_MISS		GENMASK(5, 4)
-+#define MTK_PPE_TB_CFG_AGE_PREBIND		BIT(6)
-+#define MTK_PPE_TB_CFG_AGE_NON_L4		BIT(7)
-+#define MTK_PPE_TB_CFG_AGE_UNBIND		BIT(8)
-+#define MTK_PPE_TB_CFG_AGE_TCP			BIT(9)
-+#define MTK_PPE_TB_CFG_AGE_UDP			BIT(10)
-+#define MTK_PPE_TB_CFG_AGE_TCP_FIN		BIT(11)
-+#define MTK_PPE_TB_CFG_KEEPALIVE		GENMASK(13, 12)
-+#define MTK_PPE_TB_CFG_HASH_MODE		GENMASK(15, 14)
-+#define MTK_PPE_TB_CFG_SCAN_MODE		GENMASK(17, 16)
-+#define MTK_PPE_TB_CFG_HASH_DEBUG		GENMASK(19, 18)
-+
-+enum {
-+	MTK_PPE_SCAN_MODE_DISABLED,
-+	MTK_PPE_SCAN_MODE_CHECK_AGE,
-+	MTK_PPE_SCAN_MODE_KEEPALIVE_AGE,
-+};
-+
-+enum {
-+	MTK_PPE_KEEPALIVE_DISABLE,
-+	MTK_PPE_KEEPALIVE_UNICAST_CPU,
-+	MTK_PPE_KEEPALIVE_DUP_CPU = 3,
-+};
-+
-+enum {
-+	MTK_PPE_SEARCH_MISS_ACTION_DROP,
-+	MTK_PPE_SEARCH_MISS_ACTION_FORWARD = 2,
-+	MTK_PPE_SEARCH_MISS_ACTION_FORWARD_BUILD = 3,
-+};
-+
-+#define MTK_PPE_TB_BASE				0x220
-+
-+#define MTK_PPE_TB_USED				0x224
-+#define MTK_PPE_TB_USED_NUM			GENMASK(13, 0)
-+
-+#define MTK_PPE_BIND_RATE			0x228
-+#define MTK_PPE_BIND_RATE_BIND			GENMASK(15, 0)
-+#define MTK_PPE_BIND_RATE_PREBIND		GENMASK(31, 16)
-+
-+#define MTK_PPE_BIND_LIMIT0			0x22c
-+#define MTK_PPE_BIND_LIMIT0_QUARTER		GENMASK(13, 0)
-+#define MTK_PPE_BIND_LIMIT0_HALF		GENMASK(29, 16)
-+
-+#define MTK_PPE_BIND_LIMIT1			0x230
-+#define MTK_PPE_BIND_LIMIT1_FULL		GENMASK(13, 0)
-+#define MTK_PPE_BIND_LIMIT1_NON_L4		GENMASK(23, 16)
-+
-+#define MTK_PPE_KEEPALIVE			0x234
-+#define MTK_PPE_KEEPALIVE_TIME			GENMASK(15, 0)
-+#define MTK_PPE_KEEPALIVE_TIME_TCP		GENMASK(23, 16)
-+#define MTK_PPE_KEEPALIVE_TIME_UDP		GENMASK(31, 24)
-+
-+#define MTK_PPE_UNBIND_AGE			0x238
-+#define MTK_PPE_UNBIND_AGE_MIN_PACKETS		GENMASK(31, 16)
-+#define MTK_PPE_UNBIND_AGE_DELTA		GENMASK(7, 0)
-+
-+#define MTK_PPE_BIND_AGE0			0x23c
-+#define MTK_PPE_BIND_AGE0_DELTA_NON_L4		GENMASK(30, 16)
-+#define MTK_PPE_BIND_AGE0_DELTA_UDP		GENMASK(14, 0)
-+
-+#define MTK_PPE_BIND_AGE1			0x240
-+#define MTK_PPE_BIND_AGE1_DELTA_TCP_FIN		GENMASK(30, 16)
-+#define MTK_PPE_BIND_AGE1_DELTA_TCP		GENMASK(14, 0)
-+
-+#define MTK_PPE_HASH_SEED			0x244
-+
-+#define MTK_PPE_DEFAULT_CPU_PORT		0x248
-+#define MTK_PPE_DEFAULT_CPU_PORT_MASK(_n)	(GENMASK(2, 0) << ((_n) * 4))
-+
-+#define MTK_PPE_MTU_DROP			0x308
-+
-+#define MTK_PPE_VLAN_MTU0			0x30c
-+#define MTK_PPE_VLAN_MTU0_NONE			GENMASK(13, 0)
-+#define MTK_PPE_VLAN_MTU0_1TAG			GENMASK(29, 16)
-+
-+#define MTK_PPE_VLAN_MTU1			0x310
-+#define MTK_PPE_VLAN_MTU1_2TAG			GENMASK(13, 0)
-+#define MTK_PPE_VLAN_MTU1_3TAG			GENMASK(29, 16)
-+
-+#define MTK_PPE_VPM_TPID			0x318
-+
-+#define MTK_PPE_CACHE_CTL			0x320
-+#define MTK_PPE_CACHE_CTL_EN			BIT(0)
-+#define MTK_PPE_CACHE_CTL_LOCK_CLR		BIT(4)
-+#define MTK_PPE_CACHE_CTL_REQ			BIT(8)
-+#define MTK_PPE_CACHE_CTL_CLEAR			BIT(9)
-+#define MTK_PPE_CACHE_CTL_CMD			GENMASK(13, 12)
-+
-+#define MTK_PPE_MIB_CFG				0x334
-+#define MTK_PPE_MIB_CFG_EN			BIT(0)
-+#define MTK_PPE_MIB_CFG_RD_CLR			BIT(1)
-+
-+#define MTK_PPE_MIB_TB_BASE			0x338
-+
-+#define MTK_PPE_MIB_CACHE_CTL			0x350
-+#define MTK_PPE_MIB_CACHE_CTL_EN		BIT(0)
-+#define MTK_PPE_MIB_CACHE_CTL_FLUSH		BIT(2)
-+
-+#endif
 diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
 index 76ed2d4..ec2fbd1 100644
 --- a/drivers/net/ppp/ppp_generic.c
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2101-Add-trngv2-driver-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2101-Add-trngv2-driver-support.patch
index 2169d20..9b11cd0 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2101-Add-trngv2-driver-support.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2101-Add-trngv2-driver-support.patch
@@ -164,7 +164,7 @@
 +};
 +
 +static const struct mtk_rng_of_data mt7986_rng_data = {
-+	.rng_version = 1,
++	.rng_version = 2,
 +};
 +
 +static const struct mtk_rng_of_data mt7623_rng_data = {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2701-netfilter-nf_flow_table-support-hw-offload-through-v.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2701-netfilter-nf_flow_table-support-hw-offload-through-v.patch
new file mode 100644
index 0000000..eb4c823
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2701-netfilter-nf_flow_table-support-hw-offload-through-v.patch
@@ -0,0 +1,69 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 15 Mar 2018 20:46:31 +0100
+Subject: [PATCH] netfilter: nf_flow_table: support hw offload through
+ virtual interfaces
+
+There are hardware offload devices that support offloading VLANs and
+PPPoE devices. Additionally, it is useful to be able to offload packets
+routed through bridge interfaces as well.
+Add support for finding the path to the offload device through these
+virtual interfaces, while collecting useful parameters for the offload
+device, like VLAN ID/protocol, PPPoE session and Ethernet MAC address.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
+index 2d1aa35..b60b506 100644
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -989,6 +989,7 @@ struct dev_ifalias {
+ struct devlink;
+ struct tlsdev_ops;
+ 
++struct flow_offload_hw_path;
+ 
+ /*
+  * This structure defines the management hooks for network devices.
+@@ -1222,6 +1223,11 @@ struct tlsdev_ops;
+  * int (*ndo_bridge_dellink)(struct net_device *dev, struct nlmsghdr *nlh,
+  *			     u16 flags);
+  *
++ * int (*ndo_flow_offload_check)(struct flow_offload_hw_path *path);
++ *	For virtual devices like bridges, vlan, and pppoe, fill in the
++ *	underlying network device that can be used for offloading connections.
++ *	Return an error if offloading is not supported.
++ *
+  * int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier);
+  *	Called to change device carrier. Soft-devices (like dummy, team, etc)
+  *	which do not represent real hardware may define this to allow their
+@@ -1471,6 +1477,7 @@ struct net_device_ops {
+ 	int			(*ndo_bridge_dellink)(struct net_device *dev,
+ 						      struct nlmsghdr *nlh,
+ 						      u16 flags);
++	int			(*ndo_flow_offload_check)(struct flow_offload_hw_path *path);
+ 	int			(*ndo_change_carrier)(struct net_device *dev,
+ 						      bool new_carrier);
+ 	int			(*ndo_get_phys_port_id)(struct net_device *dev,
+diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
+index 7374cb2..e0c0a80 100644
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -179,6 +179,17 @@ struct flow_offload {
+ 	struct rcu_head				rcu_head;
+ };
+ 
++struct flow_offload_hw_path {
++	struct net_device *dev;
++	u32 flags;
++
++	u8 eth_src[ETH_ALEN];
++	u8 eth_dest[ETH_ALEN];
++	u16 vlan_proto;
++	u16 vlan_id;
++	u16 pppoe_sid;
++};
++
+ #define NF_FLOW_TIMEOUT (30 * HZ)
+ #define nf_flowtable_time_stamp	(u32)jiffies
+ 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/642-net-8021q-support-hardware-flow-table-offload.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2702-net-8021q-support-hardware-flow-table-offload.patch
similarity index 78%
rename from recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/642-net-8021q-support-hardware-flow-table-offload.patch
rename to recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2702-net-8021q-support-hardware-flow-table-offload.patch
index cfcc28a..6fa10d4 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/642-net-8021q-support-hardware-flow-table-offload.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2702-net-8021q-support-hardware-flow-table-offload.patch
@@ -7,6 +7,8 @@
 Signed-off-by: Felix Fietkau <nbd@nbd.name>
 ---
 
+diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
+index c373f1d..f45abd5 100644
 --- a/net/8021q/vlan_dev.c
 +++ b/net/8021q/vlan_dev.c
 @@ -27,6 +27,11 @@
@@ -21,8 +23,8 @@
  #include "vlan.h"
  #include "vlanproc.h"
  #include <linux/if_vlan.h>
-@@ -747,6 +752,27 @@ static int vlan_dev_get_iflink(const str
- 	return real_dev->ifindex;
+@@ -791,6 +796,27 @@ static int vlan_dev_fill_forward_path(struct net_device_path_ctx *ctx,
+ 	return 0;
  }
  
 +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
@@ -31,10 +33,10 @@
 +	struct net_device *dev = path->dev;
 +	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
 +
-+	if (path->flags & FLOW_OFFLOAD_PATH_VLAN)
++	if (path->flags & BIT(DEV_PATH_VLAN))
 +		return -EEXIST;
 +
-+	path->flags |= FLOW_OFFLOAD_PATH_VLAN;
++	path->flags |= BIT(DEV_PATH_VLAN);
 +	path->vlan_proto = vlan->vlan_proto;
 +	path->vlan_id = vlan->vlan_id;
 +	path->dev = vlan->real_dev;
@@ -49,10 +51,10 @@
  static const struct ethtool_ops vlan_ethtool_ops = {
  	.get_link_ksettings	= vlan_ethtool_get_link_ksettings,
  	.get_drvinfo	        = vlan_ethtool_get_drvinfo,
-@@ -785,6 +811,9 @@ static const struct net_device_ops vlan_
- #endif
+@@ -830,6 +856,9 @@ static const struct net_device_ops vlan_netdev_ops = {
  	.ndo_fix_features	= vlan_dev_fix_features,
  	.ndo_get_iflink		= vlan_dev_get_iflink,
+ 	.ndo_fill_forward_path	= vlan_dev_fill_forward_path,
 +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
 +	.ndo_flow_offload_check = vlan_dev_flow_offload_check,
 +#endif
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/643-net-bridge-support-hardware-flow-table-offload.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2703-net-bridge-support-hardware-flow-table-offload.patch
similarity index 81%
rename from recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/643-net-bridge-support-hardware-flow-table-offload.patch
rename to recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2703-net-bridge-support-hardware-flow-table-offload.patch
index d47482d..6ec4d74 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/643-net-bridge-support-hardware-flow-table-offload.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2703-net-bridge-support-hardware-flow-table-offload.patch
@@ -7,6 +7,8 @@
 Signed-off-by: Felix Fietkau <nbd@nbd.name>
 ---
 
+diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
+index 03934dd..094b2b1 100644
 --- a/net/bridge/br_device.c
 +++ b/net/bridge/br_device.c
 @@ -14,6 +14,10 @@
@@ -20,7 +22,7 @@
  
  #include <linux/uaccess.h>
  #include "br_private.h"
-@@ -382,6 +386,28 @@ static const struct ethtool_ops br_ethto
+@@ -440,6 +444,28 @@ static const struct ethtool_ops br_ethtool_ops = {
  	.get_link	= ethtool_op_get_link,
  };
  
@@ -31,7 +33,7 @@
 +	struct net_bridge *br = netdev_priv(dev);
 +	struct net_bridge_fdb_entry *dst;
 +
-+	if (!(path->flags & FLOW_OFFLOAD_PATH_ETHERNET))
++	if (!(path->flags & BIT(DEV_PATH_ETHERNET)))
 +		return -EINVAL;
 +
 +	dst = br_fdb_find_rcu(br, path->eth_dest, path->vlan_id);
@@ -49,10 +51,10 @@
  static const struct net_device_ops br_netdev_ops = {
  	.ndo_open		 = br_dev_open,
  	.ndo_stop		 = br_dev_stop,
-@@ -410,6 +436,9 @@ static const struct net_device_ops br_ne
- 	.ndo_bridge_setlink	 = br_setlink,
+@@ -469,6 +495,9 @@ static const struct net_device_ops br_netdev_ops = {
  	.ndo_bridge_dellink	 = br_dellink,
  	.ndo_features_check	 = passthru_features_check,
+ 	.ndo_fill_forward_path	 = br_fill_forward_path,
 +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
 +	.ndo_flow_offload_check	 = br_flow_offload_check,
 +#endif
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/644-net-pppoe-support-hardware-flow-table-offload.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2704-net-pppoe-support-hardware-flow-table-offload.patch
similarity index 76%
rename from recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/644-net-pppoe-support-hardware-flow-table-offload.patch
rename to recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2704-net-pppoe-support-hardware-flow-table-offload.patch
index b09b3bc..d287e52 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/644-net-pppoe-support-hardware-flow-table-offload.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2704-net-pppoe-support-hardware-flow-table-offload.patch
@@ -7,6 +7,8 @@
 Signed-off-by: Felix Fietkau <nbd@nbd.name>
 ---
 
+diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
+index ec2fbd1..f361b52 100644
 --- a/drivers/net/ppp/ppp_generic.c
 +++ b/drivers/net/ppp/ppp_generic.c
 @@ -53,6 +53,11 @@
@@ -21,8 +23,8 @@
  #define PPP_VERSION	"2.4.2"
  
  /*
-@@ -1378,12 +1383,37 @@ static void ppp_dev_priv_destructor(stru
- 		ppp_destroy_interface(ppp);
+@@ -1416,6 +1421,28 @@ static int ppp_fill_forward_path(struct net_device_path_ctx *ctx,
+ 	return chan->ops->fill_forward_path(ctx, path, chan);
  }
  
 +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
@@ -50,15 +52,18 @@
  static const struct net_device_ops ppp_netdev_ops = {
  	.ndo_init	 = ppp_dev_init,
  	.ndo_uninit      = ppp_dev_uninit,
- 	.ndo_start_xmit  = ppp_start_xmit,
+@@ -1423,6 +1450,9 @@ static const struct net_device_ops ppp_netdev_ops = {
  	.ndo_do_ioctl    = ppp_net_ioctl,
  	.ndo_get_stats64 = ppp_get_stats64,
+ 	.ndo_fill_forward_path = ppp_fill_forward_path,
 +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
 +	.ndo_flow_offload_check = ppp_flow_offload_check,
 +#endif
  };
  
  static struct device_type ppp_type = {
+diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
+index 7a8c246..1018464 100644
 --- a/drivers/net/ppp/pppoe.c
 +++ b/drivers/net/ppp/pppoe.c
 @@ -73,6 +73,11 @@
@@ -73,8 +78,8 @@
  #include <linux/nsproxy.h>
  #include <net/net_namespace.h>
  #include <net/netns/generic.h>
-@@ -974,8 +979,36 @@ static int pppoe_xmit(struct ppp_channel
- 	return __pppoe_xmit(sk, skb);
+@@ -997,9 +1002,37 @@ static int pppoe_fill_forward_path(struct net_device_path_ctx *ctx,
+ 	return 0;
  }
  
 +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
@@ -90,7 +95,7 @@
 +		return -ENODEV;
 +
 +	path->dev = po->pppoe_dev;
-+	path->flags |= FLOW_OFFLOAD_PATH_PPPOE;
++	path->flags |= BIT(DEV_PATH_PPPOE);
 +	memcpy(path->eth_src, po->pppoe_dev->dev_addr, ETH_ALEN);
 +	memcpy(path->eth_dest, po->pppoe_pa.remote, ETH_ALEN);
 +	path->pppoe_sid = be16_to_cpu(po->num);
@@ -104,19 +109,21 @@
 +
  static const struct ppp_channel_ops pppoe_chan_ops = {
  	.start_xmit = pppoe_xmit,
+ 	.fill_forward_path = pppoe_fill_forward_path,
 +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
 +	.flow_offload_check = pppoe_flow_offload_check,
 +#endif
  };
  
  static int pppoe_recvmsg(struct socket *sock, struct msghdr *m,
+diff --git a/include/linux/ppp_channel.h b/include/linux/ppp_channel.h
+index 91f9a92..4a1729b 100644
 --- a/include/linux/ppp_channel.h
 +++ b/include/linux/ppp_channel.h
-@@ -28,6 +28,10 @@ struct ppp_channel_ops {
- 	int	(*start_xmit)(struct ppp_channel *, struct sk_buff *);
- 	/* Handle an ioctl call that has come in via /dev/ppp. */
- 	int	(*ioctl)(struct ppp_channel *, unsigned int, unsigned long);
-+
+@@ -31,6 +31,9 @@ struct ppp_channel_ops {
+ 	int	(*fill_forward_path)(struct net_device_path_ctx *,
+ 				     struct net_device_path *,
+ 				     const struct ppp_channel *);
 +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
 +	int	(*flow_offload_check)(struct ppp_channel *, struct flow_offload_hw_path *);
 +#endif
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/647-net-dsa-support-hardware-flow-table-offload.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2705-net-dsa-support-hardware-flow-table-offload.patch
similarity index 71%
rename from recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/647-net-dsa-support-hardware-flow-table-offload.patch
rename to recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2705-net-dsa-support-hardware-flow-table-offload.patch
index ff41a17..54fd0a4 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/647-net-dsa-support-hardware-flow-table-offload.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2705-net-dsa-support-hardware-flow-table-offload.patch
@@ -7,17 +7,11 @@
 Signed-off-by: Felix Fietkau <nbd@nbd.name>
 ---
 
+diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
+index e0c0a80..a1b4ab5 100644
 --- a/include/net/netfilter/nf_flow_table.h
 +++ b/include/net/netfilter/nf_flow_table.h
-@@ -93,6 +93,7 @@ struct flow_offload {
- #define FLOW_OFFLOAD_PATH_ETHERNET	BIT(0)
- #define FLOW_OFFLOAD_PATH_VLAN		BIT(1)
- #define FLOW_OFFLOAD_PATH_PPPOE		BIT(2)
-+#define FLOW_OFFLOAD_PATH_DSA		BIT(3)
- 
- struct flow_offload_hw_path {
- 	struct net_device *dev;
-@@ -103,6 +104,7 @@ struct flow_offload_hw_path {
+@@ -188,6 +188,7 @@ struct flow_offload_hw_path {
  	u16 vlan_proto;
  	u16 vlan_id;
  	u16 pppoe_sid;
@@ -25,6 +19,8 @@
  };
  
  #define NF_FLOW_TIMEOUT (30 * HZ)
+diff --git a/net/dsa/slave.c b/net/dsa/slave.c
+index 2ea9ec1..eab4e4a 100644
 --- a/net/dsa/slave.c
 +++ b/net/dsa/slave.c
 @@ -19,6 +19,10 @@
@@ -38,8 +34,8 @@
  
  #include "dsa_priv.h"
  
-@@ -1224,6 +1228,27 @@ static struct devlink_port *dsa_slave_ge
- 	return dp->ds->devlink ? &dp->devlink_port : NULL;
+@@ -1257,6 +1261,27 @@ static int dsa_slave_fill_forward_path(struct net_device_path_ctx *ctx,
+ 	return 0;
  }
  
 +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
@@ -48,13 +44,13 @@
 +	struct net_device *dev = path->dev;
 +	struct dsa_port *dp;
 +
-+	if (!(path->flags & FLOW_OFFLOAD_PATH_ETHERNET))
++	if (!(path->flags & BIT(DEV_PATH_ETHERNET)))
 +		return -EINVAL;
 +
 +	dp = dsa_slave_to_port(dev);
 +	path->dsa_port = dp->index;
 +	path->dev = dsa_slave_to_master(dev);
-+	path->flags |= FLOW_OFFLOAD_PATH_DSA;
++	path->flags |= BIT(DEV_PATH_DSA);
 +
 +	if (path->dev->netdev_ops->ndo_flow_offload_check)
 +		return path->dev->netdev_ops->ndo_flow_offload_check(path);
@@ -66,10 +62,10 @@
  static const struct net_device_ops dsa_slave_netdev_ops = {
  	.ndo_open	 	= dsa_slave_open,
  	.ndo_stop		= dsa_slave_close,
-@@ -1248,6 +1273,9 @@ static const struct net_device_ops dsa_s
- 	.ndo_vlan_rx_add_vid	= dsa_slave_vlan_rx_add_vid,
+@@ -1282,6 +1307,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
  	.ndo_vlan_rx_kill_vid	= dsa_slave_vlan_rx_kill_vid,
  	.ndo_get_devlink_port	= dsa_slave_get_devlink_port,
+ 	.ndo_fill_forward_path	= dsa_slave_fill_forward_path,
 +#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
 +	.ndo_flow_offload_check	 = dsa_flow_offload_check,
 +#endif
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-2708-mtkhnat-add-support-for-virtual-interface-acceleration.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2708-mtkhnat-add-support-for-virtual-interface-acceleration.patch
similarity index 76%
rename from recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-2708-mtkhnat-add-support-for-virtual-interface-acceleration.patch
rename to recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2708-mtkhnat-add-support-for-virtual-interface-acceleration.patch
index 4d21ebd..8cc6f3d 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-2708-mtkhnat-add-support-for-virtual-interface-acceleration.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2708-mtkhnat-add-support-for-virtual-interface-acceleration.patch
@@ -5,22 +5,31 @@
  [networking][999-2708-mtkhnat-add-support-for-virtual-interface-acceleration.patch]
 
 ---
- include/net/netfilter/nf_flow_table.h |  3 +++
+ include/linux/netdevice.h             |  1 +
+ include/net/netfilter/nf_flow_table.h |  1 +
  net/8021q/vlan_dev.c                  |  1 +
  net/ipv6/ip6_tunnel.c                 | 24 ++++++++++++++++++++++++
  net/ipv6/sit.c                        | 24 ++++++++++++++++++++++++
- 4 files changed, 52 insertions(+)
+ 5 files changed, 51 insertions(+)
 
+diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
+index b60b506..c30952f 100644
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -849,6 +849,8 @@ enum net_device_path_type {
+ 	DEV_PATH_BRIDGE,
+ 	DEV_PATH_PPPOE,
+ 	DEV_PATH_DSA,
++	DEV_PATH_DSLITE,
++	DEV_PATH_6RD,
+ };
+ 
+ struct net_device_path {
 diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
-index 16dbf5461..4cb73a7cb 100644
+index a1b4ab5..7a2945e 100644
 --- a/include/net/netfilter/nf_flow_table.h
 +++ b/include/net/netfilter/nf_flow_table.h
-@@ -94,9 +94,12 @@ struct flow_offload {
- #define FLOW_OFFLOAD_PATH_VLAN		BIT(1)
- #define FLOW_OFFLOAD_PATH_PPPOE		BIT(2)
- #define FLOW_OFFLOAD_PATH_DSA		BIT(3)
-+#define FLOW_OFFLOAD_PATH_DSLITE	BIT(4)
-+#define FLOW_OFFLOAD_PATH_6RD		BIT(5)
+@@ -181,6 +181,7 @@ struct flow_offload {
  
  struct flow_offload_hw_path {
  	struct net_device *dev;
@@ -29,11 +38,11 @@
  
  	u8 eth_src[ETH_ALEN];
 diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
-index f973bec77..dbb3cea5c 100644
+index f45abd5..db5867d 100644
 --- a/net/8021q/vlan_dev.c
 +++ b/net/8021q/vlan_dev.c
-@@ -764,6 +764,7 @@ static int vlan_dev_flow_offload_check(struct flow_offload_hw_path *path)
- 	path->flags |= FLOW_OFFLOAD_PATH_VLAN;
+@@ -808,6 +808,7 @@ static int vlan_dev_flow_offload_check(struct flow_offload_hw_path *path)
+ 	path->flags |= BIT(DEV_PATH_VLAN);
  	path->vlan_proto = vlan->vlan_proto;
  	path->vlan_id = vlan->vlan_id;
 +	path->virt_dev = dev;
@@ -41,7 +50,7 @@
  
  	if (vlan->real_dev->netdev_ops->ndo_flow_offload_check)
 diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
-index 71ef26e60..f8302fdd5 100644
+index 2e8a528..0bf25b0 100644
 --- a/net/ipv6/ip6_tunnel.c
 +++ b/net/ipv6/ip6_tunnel.c
 @@ -57,6 +57,11 @@
@@ -66,10 +75,10 @@
 +	struct net_device *dev = path->dev;
 +	struct ip6_tnl *tnl = netdev_priv(dev);
 +
-+	if (path->flags & FLOW_OFFLOAD_PATH_DSLITE)
++	if (path->flags & BIT(DEV_PATH_DSLITE))
 +		return -EEXIST;
 +
-+	path->flags |= FLOW_OFFLOAD_PATH_DSLITE;
++	path->flags |= BIT(DEV_PATH_DSLITE);
 +	path->dev = tnl->dev;
 +
 +	return 0;
@@ -90,7 +99,7 @@
  
  #define IPXIPX_FEATURES (NETIF_F_SG |		\
 diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
-index 117960895..0844a95e3 100644
+index 8d704ea..c50bbef 100644
 --- a/net/ipv6/sit.c
 +++ b/net/ipv6/sit.c
 @@ -52,6 +52,11 @@
@@ -105,7 +114,7 @@
  /*
     This version of net/ipv6/sit.c is cloned of net/ipv4/ip_gre.c
  
-@@ -1342,6 +1347,22 @@ done:
+@@ -1344,6 +1349,22 @@ ipip6_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
  	return err;
  }
  
@@ -115,10 +124,10 @@
 +	struct net_device *dev = path->dev;
 +	struct ip_tunnel *tnl = netdev_priv(dev);
 +
-+	if (path->flags & FLOW_OFFLOAD_PATH_6RD)
++	if (path->flags & BIT(DEV_PATH_6RD))
 +		return -EEXIST;
 +
-+	path->flags |= FLOW_OFFLOAD_PATH_6RD;
++	path->flags |= BIT(DEV_PATH_6RD);
 +	path->dev = tnl->dev;
 +
 +	return 0;
@@ -128,7 +137,7 @@
  static const struct net_device_ops ipip6_netdev_ops = {
  	.ndo_init	= ipip6_tunnel_init,
  	.ndo_uninit	= ipip6_tunnel_uninit,
-@@ -1349,6 +1370,9 @@ static const struct net_device_ops ipip6_netdev_ops = {
+@@ -1351,6 +1372,9 @@ static const struct net_device_ops ipip6_netdev_ops = {
  	.ndo_do_ioctl	= ipip6_tunnel_ioctl,
  	.ndo_get_stats64 = ip_tunnel_get_stats64,
  	.ndo_get_iflink = ip_tunnel_get_iflink,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-2726-mtkhnat-tnl-interface-offload-check.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2726-mtkhnat-tnl-interface-offload-check.patch
similarity index 70%
rename from recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-2726-mtkhnat-tnl-interface-offload-check.patch
rename to recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2726-mtkhnat-tnl-interface-offload-check.patch
index e94185f..5f8bf62 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/nf_hnat/999-2726-mtkhnat-tnl-interface-offload-check.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2726-mtkhnat-tnl-interface-offload-check.patch
@@ -1,13 +1,17 @@
---- a/include/net/netfilter/nf_flow_table.h
-+++ b/include/net/netfilter/nf_flow_table.h
-@@ -96,6 +96,7 @@ struct flow_offload {
- #define FLOW_OFFLOAD_PATH_DSA		BIT(3)
- #define FLOW_OFFLOAD_PATH_DSLITE	BIT(4)
- #define FLOW_OFFLOAD_PATH_6RD		BIT(5)
-+#define FLOW_OFFLOAD_PATH_TNL		BIT(6)
+diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
+index c30952f..6c0860b 100644
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -851,6 +851,7 @@ enum net_device_path_type {
+ 	DEV_PATH_DSA,
+ 	DEV_PATH_DSLITE,
+ 	DEV_PATH_6RD,
++	DEV_PATH_TNL,
+ };
  
- struct flow_offload_hw_path {
- 	struct net_device *dev;
+ struct net_device_path {
+diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
+index 7d3c782..d7a5a9a 100644
 --- a/net/l2tp/l2tp_ppp.c
 +++ b/net/l2tp/l2tp_ppp.c
 @@ -89,6 +89,7 @@
@@ -33,7 +37,7 @@
  };
  
  static const struct proto_ops pppol2tp_ops;
-@@ -335,6 +341,26 @@ error:
+@@ -335,6 +341,26 @@ static int pppol2tp_sendmsg(struct socket *sock, struct msghdr *m,
  	return error;
  }
  
@@ -44,14 +48,14 @@
 +	struct sock *sk = (struct sock *)chan->private;
 +	struct l2tp_session *session;
 +
-+	if (path->flags & FLOW_OFFLOAD_PATH_TNL)
++	if (path->flags & BIT(DEV_PATH_TNL))
 +		return -EEXIST;
 +
 +	session = pppol2tp_sock_to_session(sk);
 +	if (!session)
 +		return -EINVAL;
 +
-+	path->flags |= FLOW_OFFLOAD_PATH_TNL;
++	path->flags |= BIT(DEV_PATH_TNL);
 +
 +	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 47dfb49..a4ede55 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
@@ -57,6 +57,7 @@
     file://999-1715-v6.2-net-dsa-add-set-queue-mapping.patch \
     file://999-1716-v6.6-net-phy-add-phylink-pcs_enable-and-pcs_disable.patch;apply=no \
     file://999-1717-v5.12-net-phy-sfp-add-debugfs-support.patch \
+    file://999-1718-v5.15-net-netfilter-add-nf-hw-offload.patch \
     file://999-1719-v6.2-net-ptp-introduce-adjust-by-scaled-ppm.patch \
     file://999-1750-v5.18-net-macsec-get-ready-to-backport-from-5-18.patch \
     file://999-1751-01-v5.18-net-macsec-move-some-definitions-in-a-dedicated-header.patch \
@@ -188,10 +189,16 @@
     file://999-2621-xHCI-MT7986-USB-2.0-USBIF-compliance-toolkit.patch \
     file://999-2622-usb-add-embedded-Host-feature-support.patch \
     file://999-2700-netfilter_optional_tcp_window_check.patch \
+    file://999-2701-netfilter-nf_flow_table-support-hw-offload-through-v.patch \
+    file://999-2702-net-8021q-support-hardware-flow-table-offload.patch \
+    file://999-2703-net-bridge-support-hardware-flow-table-offload.patch \
     file://999-2704-en8811h-2p5gphy-support.patch \
+    file://999-2704-net-pppoe-support-hardware-flow-table-offload.patch \
     file://999-2705-hwnat_Kconfig_Makefile.patch \
+    file://999-2705-net-dsa-support-hardware-flow-table-offload.patch \
     file://999-2706-crypto-add-eip197-inside-secure-support.patch \
     file://999-2707-mtkhnat-ipv6-fix-pskb-expand-head-limitation.patch \
+    file://999-2708-mtkhnat-add-support-for-virtual-interface-acceleration.patch \
     file://999-2709-fix-race-inside-napi-enable.patch \
     file://999-2710-net-make-napi-disable-symmetric-with-enable.patch \
     file://999-2711-net-fix-premature-exit-from-napi-state-polling-in-napi-disable-v2.patch \
@@ -207,6 +214,7 @@
     file://999-2723-dt-bindings-phy-Add-PHY_TYPE_XPCS-definition.patch \
     file://999-2724-dt-bindings-phy-Add-DT-bindings-for-Xilinx-ZynqMP-PS.patch \
     file://999-2725-iwconfig-wireless-rate-fix.patch;apply=no \
+    file://999-2726-mtkhnat-tnl-interface-offload-check.patch \
     file://999-2727-net-phy-sfp-add-debug-info.patch.patch \
     file://999-2728-net-phy-aquantia-add-mib-read.patch \
     file://999-2730-net-phy-sfp-change-shared-mod-def0.patch \
diff --git a/recipes-kernel/linux/linux-mediatek_5.4.bb b/recipes-kernel/linux/linux-mediatek_5.4.bb
index e558eb8..7a27349 100644
--- a/recipes-kernel/linux/linux-mediatek_5.4.bb
+++ b/recipes-kernel/linux/linux-mediatek_5.4.bb
@@ -125,8 +125,7 @@
                 for i in ${WORKDIR}/mediatek/flow_patch/*.patch; do patch -p1 < $i; done
             fi
             if [ $DISTRO_logan_ENABLED = 'true' ]; then
-                for i in ${WORKDIR}/mediatek/nf_hnat/6*.patch; do patch -p1 < $i; done
-                for i in ${WORKDIR}/mediatek/nf_hnat/9*.patch; do patch -p1 < $i; done
+                for i in ${WORKDIR}/mediatek/nf_hnat/*.patch; do patch -p1 < $i; done
                 patch -p1 < ${WORKDIR}/004-rdkb-hnat-bind-ifname.patch
             fi
             touch patch_applied