[][kernel][common][eth][Add nf hw offload]

[Description]
Add nf hw offload.

Without this patch, users are unable to utilize Flowblock's fastpath to
decrease CPU usage.

[Release-log]
N/A


Change-Id: I5831095dba69db985da63c35a35f4773724cf320
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/9720507
diff --git a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
index 20141da..ab68eac 100644
--- a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
+++ b/21.02/files/target/linux/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/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
index 0a5be88..06e2317 100644
--- a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+++ b/21.02/files/target/linux/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;
 
@@ -2114,7 +2114,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;
@@ -3281,7 +3281,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 +3309,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/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
index 5f64105..4398bae 100644
--- a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
+++ b/21.02/files/target/linux/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/autobuild/autobuild_5.4_mac80211_release/target/linux/mediatek/patches-5.4/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch b/21.02/files/target/linux/mediatek/patches-5.4/999-1718-v5.15-net-netfilter-add-nf-hw-offload.patch
similarity index 73%
rename from autobuild/autobuild_5.4_mac80211_release/target/linux/mediatek/patches-5.4/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
rename to 21.02/files/target/linux/mediatek/patches-5.4/999-1718-v5.15-net-netfilter-add-nf-hw-offload.patch
index 8f28900..f162e51 100644
--- a/autobuild/autobuild_5.4_mac80211_release/target/linux/mediatek/patches-5.4/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
+++ b/21.02/files/target/linux/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/21.02/files/target/linux/mediatek/patches-5.4/999-2701-netfilter-nf_flow_table-support-hw-offload-through-v.patch b/21.02/files/target/linux/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/21.02/files/target/linux/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/21.02/files/target/linux/mediatek/patches-5.4/999-2702-net-8021q-support-hardware-flow-table-offload.patch b/21.02/files/target/linux/mediatek/patches-5.4/999-2702-net-8021q-support-hardware-flow-table-offload.patch
new file mode 100644
index 0000000..6fa10d4
--- /dev/null
+++ b/21.02/files/target/linux/mediatek/patches-5.4/999-2702-net-8021q-support-hardware-flow-table-offload.patch
@@ -0,0 +1,63 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 15 Mar 2018 20:49:58 +0100
+Subject: [PATCH] net: 8021q: support hardware flow table offload
+
+Add the VLAN ID and protocol information
+
+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 @@
+ #include <linux/phy.h>
+ #include <net/arp.h>
+ 
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++#include <linux/netfilter.h>
++#include <net/netfilter/nf_flow_table.h>
++#endif
++
+ #include "vlan.h"
+ #include "vlanproc.h"
+ #include <linux/if_vlan.h>
+@@ -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)
++static int vlan_dev_flow_offload_check(struct flow_offload_hw_path *path)
++{
++	struct net_device *dev = path->dev;
++	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
++
++	if (path->flags & BIT(DEV_PATH_VLAN))
++		return -EEXIST;
++
++	path->flags |= BIT(DEV_PATH_VLAN);
++	path->vlan_proto = vlan->vlan_proto;
++	path->vlan_id = vlan->vlan_id;
++	path->dev = vlan->real_dev;
++
++	if (vlan->real_dev->netdev_ops->ndo_flow_offload_check)
++		return vlan->real_dev->netdev_ops->ndo_flow_offload_check(path);
++
++	return 0;
++}
++#endif /* CONFIG_NF_FLOW_TABLE */
++
+ static const struct ethtool_ops vlan_ethtool_ops = {
+ 	.get_link_ksettings	= vlan_ethtool_get_link_ksettings,
+ 	.get_drvinfo	        = vlan_ethtool_get_drvinfo,
+@@ -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
+ };
+ 
+ static void vlan_dev_free(struct net_device *dev)
diff --git a/21.02/files/target/linux/mediatek/patches-5.4/999-2703-net-bridge-support-hardware-flow-table-offload.patch b/21.02/files/target/linux/mediatek/patches-5.4/999-2703-net-bridge-support-hardware-flow-table-offload.patch
new file mode 100644
index 0000000..6ec4d74
--- /dev/null
+++ b/21.02/files/target/linux/mediatek/patches-5.4/999-2703-net-bridge-support-hardware-flow-table-offload.patch
@@ -0,0 +1,63 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 15 Mar 2018 20:50:37 +0100
+Subject: [PATCH] net: bridge: support hardware flow table offload
+
+Look up the real device and pass it on
+
+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 @@
+ #include <linux/ethtool.h>
+ #include <linux/list.h>
+ #include <linux/netfilter_bridge.h>
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++#include <linux/netfilter.h>
++#include <net/netfilter/nf_flow_table.h>
++#endif
+ 
+ #include <linux/uaccess.h>
+ #include "br_private.h"
+@@ -440,6 +444,28 @@ static const struct ethtool_ops br_ethtool_ops = {
+ 	.get_link	= ethtool_op_get_link,
+ };
+ 
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++static int br_flow_offload_check(struct flow_offload_hw_path *path)
++{
++	struct net_device *dev = path->dev;
++	struct net_bridge *br = netdev_priv(dev);
++	struct net_bridge_fdb_entry *dst;
++
++	if (!(path->flags & BIT(DEV_PATH_ETHERNET)))
++		return -EINVAL;
++
++	dst = br_fdb_find_rcu(br, path->eth_dest, path->vlan_id);
++	if (!dst || !dst->dst)
++		return -ENOENT;
++
++	path->dev = dst->dst->dev;
++	if (path->dev->netdev_ops->ndo_flow_offload_check)
++		return path->dev->netdev_ops->ndo_flow_offload_check(path);
++
++	return 0;
++}
++#endif /* CONFIG_NF_FLOW_TABLE */
++
+ static const struct net_device_ops br_netdev_ops = {
+ 	.ndo_open		 = br_dev_open,
+ 	.ndo_stop		 = br_dev_stop,
+@@ -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
+ };
+ 
+ static struct device_type br_type = {
diff --git a/21.02/files/target/linux/mediatek/patches-5.4/999-2704-net-pppoe-support-hardware-flow-table-offload.patch b/21.02/files/target/linux/mediatek/patches-5.4/999-2704-net-pppoe-support-hardware-flow-table-offload.patch
new file mode 100644
index 0000000..d287e52
--- /dev/null
+++ b/21.02/files/target/linux/mediatek/patches-5.4/999-2704-net-pppoe-support-hardware-flow-table-offload.patch
@@ -0,0 +1,132 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 15 Mar 2018 21:15:00 +0100
+Subject: [PATCH] net: pppoe: support hardware flow table offload
+
+Pass on the PPPoE session ID and the remote MAC address
+
+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 @@
+ #include <net/net_namespace.h>
+ #include <net/netns/generic.h>
+ 
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++#include <linux/netfilter.h>
++#include <net/netfilter/nf_flow_table.h>
++#endif
++
+ #define PPP_VERSION	"2.4.2"
+ 
+ /*
+@@ -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)
++static int ppp_flow_offload_check(struct flow_offload_hw_path *path)
++{
++	struct ppp *ppp = netdev_priv(path->dev);
++	struct ppp_channel *chan;
++	struct channel *pch;
++
++	if (ppp->flags & SC_MULTILINK)
++		return -EOPNOTSUPP;
++
++	if (list_empty(&ppp->channels))
++		return -ENODEV;
++
++	pch = list_first_entry(&ppp->channels, struct channel, clist);
++	chan = pch->chan;
++	if (!chan->ops->flow_offload_check)
++		return -EOPNOTSUPP;
++
++	return chan->ops->flow_offload_check(chan, path);
++}
++#endif /* CONFIG_NF_FLOW_TABLE */
++
+ static const struct net_device_ops ppp_netdev_ops = {
+ 	.ndo_init	 = ppp_dev_init,
+ 	.ndo_uninit      = ppp_dev_uninit,
+@@ -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 @@
+ #include <linux/proc_fs.h>
+ #include <linux/seq_file.h>
+ 
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++#include <linux/netfilter.h>
++#include <net/netfilter/nf_flow_table.h>
++#endif
++
+ #include <linux/nsproxy.h>
+ #include <net/net_namespace.h>
+ #include <net/netns/generic.h>
+@@ -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)
++static int pppoe_flow_offload_check(struct ppp_channel *chan,
++				    struct flow_offload_hw_path *path)
++{
++	struct sock *sk = (struct sock *)chan->private;
++	struct pppox_sock *po = pppox_sk(sk);
++	struct net_device *dev = po->pppoe_dev;
++
++	if (sock_flag(sk, SOCK_DEAD) ||
++	    !(sk->sk_state & PPPOX_CONNECTED) || !dev)
++		return -ENODEV;
++
++	path->dev = po->pppoe_dev;
++	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);
++
++	if (path->dev->netdev_ops->ndo_flow_offload_check)
++		return path->dev->netdev_ops->ndo_flow_offload_check(path);
++
++	return 0;
++}
++#endif /* CONFIG_NF_FLOW_TABLE */
++
+ 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
+@@ -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
+ };
+ 
+ struct ppp_channel {
diff --git a/21.02/files/target/linux/mediatek/patches-5.4/999-2705-net-dsa-support-hardware-flow-table-offload.patch b/21.02/files/target/linux/mediatek/patches-5.4/999-2705-net-dsa-support-hardware-flow-table-offload.patch
new file mode 100644
index 0000000..54fd0a4
--- /dev/null
+++ b/21.02/files/target/linux/mediatek/patches-5.4/999-2705-net-dsa-support-hardware-flow-table-offload.patch
@@ -0,0 +1,74 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 17 Sep 2020 18:41:23 +0200
+Subject: [PATCH] net: dsa: support hardware flow table offload
+
+Look up the master device and the port id
+
+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
+@@ -188,6 +188,7 @@ struct flow_offload_hw_path {
+ 	u16 vlan_proto;
+ 	u16 vlan_id;
+ 	u16 pppoe_sid;
++	u16 dsa_port;
+ };
+ 
+ #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 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netpoll.h>
+ #include <linux/ptp_classify.h>
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++#include <linux/netfilter.h>
++#include <net/netfilter/nf_flow_table.h>
++#endif
+ 
+ #include "dsa_priv.h"
+ 
+@@ -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)
++static int dsa_flow_offload_check(struct flow_offload_hw_path *path)
++{
++	struct net_device *dev = path->dev;
++	struct dsa_port *dp;
++
++	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 |= BIT(DEV_PATH_DSA);
++
++	if (path->dev->netdev_ops->ndo_flow_offload_check)
++		return path->dev->netdev_ops->ndo_flow_offload_check(path);
++
++	return 0;
++}
++#endif /* CONFIG_NF_FLOW_TABLE */
++
+ static const struct net_device_ops dsa_slave_netdev_ops = {
+ 	.ndo_open	 	= dsa_slave_open,
+ 	.ndo_stop		= dsa_slave_close,
+@@ -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
+ };
+ 
+ static struct device_type dsa_type = {
diff --git a/21.02/files/target/linux/mediatek/patches-5.4/999-2708-mtkhnat-add-support-for-virtual-interface-acceleration.patch b/21.02/files/target/linux/mediatek/patches-5.4/999-2708-mtkhnat-add-support-for-virtual-interface-acceleration.patch
index 4d21ebd..8cc6f3d 100644
--- a/21.02/files/target/linux/mediatek/patches-5.4/999-2708-mtkhnat-add-support-for-virtual-interface-acceleration.patch
+++ b/21.02/files/target/linux/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/21.02/files/target/linux/mediatek/patches-5.4/999-2726-mtkhnat-tnl-interface-offload-check.patch b/21.02/files/target/linux/mediatek/patches-5.4/999-2726-mtkhnat-tnl-interface-offload-check.patch
index e94185f..5f8bf62 100644
--- a/21.02/files/target/linux/mediatek/patches-5.4/999-2726-mtkhnat-tnl-interface-offload-check.patch
+++ b/21.02/files/target/linux/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/autobuild/autobuild_5.4_mac80211_release/0004-2102-netfilter-remove-nf_flow_table_hw.patch b/21.02/patches-base/2102-netfilter-remove-nf_flow_table_hw.patch
similarity index 100%
rename from autobuild/autobuild_5.4_mac80211_release/0004-2102-netfilter-remove-nf_flow_table_hw.patch
rename to 21.02/patches-base/2102-netfilter-remove-nf_flow_table_hw.patch
diff --git a/21.02/remove_list-mtwifi.txt b/21.02/remove_list-mtwifi.txt
index 9c58e6d..ee47880 100644
--- a/21.02/remove_list-mtwifi.txt
+++ b/21.02/remove_list-mtwifi.txt
@@ -4,6 +4,9 @@
 target/linux/generic/backport-5.4/762-net-ethernet-mediatek-Enable-GDM-GDMA_DROP_ALL-mode.patch
 target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch
 target/linux/mediatek/patches-5.4/1012-pci-pcie-mediatek-add-support-for-coherent-DMA.patch
+target/linux/generic/hack-5.4/647-netfilter-flow-acct.patch
+target/linux/generic/hack-5.4/650-netfilter-add-xt_OFFLOAD-target.patch
+target/linux/generic/pending-5.4/64*.patch
 target/linux/generic/pending-5.4/770-*.patch
 target/linux/generic/pending-5.4/447-mtd-spinand-gigadevice-Add-support-for-GD5F4GQ4xC.patch
 target/linux/generic/backport-5.10/610-v5.13-*-ethernet-*.patch
diff --git a/autobuild/autobuild_5.4_mac80211_release/target/linux/mediatek/patches-5.4/999-3001-ethernet-add-ppe-for-netsys1.patch b/autobuild/autobuild_5.4_mac80211_release/target/linux/mediatek/patches-5.4/999-3001-ethernet-add-ppe-for-netsys1.patch
new file mode 100644
index 0000000..dd0473d
--- /dev/null
+++ b/autobuild/autobuild_5.4_mac80211_release/target/linux/mediatek/patches-5.4/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/autobuild/autobuild_5.4_mac80211_release/target/linux/mediatek/patches-5.4/999-3003-add-wed.patch b/autobuild/autobuild_5.4_mac80211_release/target/linux/mediatek/patches-5.4/999-3003-add-wed.patch
index bfb726a..79672d4 100644
--- a/autobuild/autobuild_5.4_mac80211_release/target/linux/mediatek/patches-5.4/999-3003-add-wed.patch
+++ b/autobuild/autobuild_5.4_mac80211_release/target/linux/mediatek/patches-5.4/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