[openwrt/openwrt] mediatek: rewrite flow offload code

LEDE Commits lede-commits at lists.infradead.org
Fri Oct 16 15:37:59 EDT 2020


nbd pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/b59d5c8f0eebb6d15d7cefe487c17fad0ee4a524

commit b59d5c8f0eebb6d15d7cefe487c17fad0ee4a524
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Thu Oct 8 13:44:33 2020 +0200

    mediatek: rewrite flow offload code
    
    The code is now much cleaner and works better than the old code.
    Preparation for submitting it upstream (though with a different API)
    Also add back MT7621 support and fix flow table coherence issues on
    MT7622
    
    Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
 ...et-mtk_eth_soc-fix-parsing-packets-in-GDM.patch |   26 +-
 ...t-mtk_eth_soc-set-PPE-flow-hash-as-skb-ha.patch |    2 +-
 ...t-mediatek-mtk_eth_soc-add-support-for-in.patch | 1058 +++++++++++++++++
 ...t-mediatek-mtk_eth_soc-add-flow-offloadin.patch |  401 +++++++
 ...-propagate-resolved-link-config-via-mac_l.patch |   12 +-
 target/linux/mediatek/patches-5.4/0999-hnat.patch  | 1234 --------------------
 ...t-mtk_eth_soc-add-support-for-coherent-DM.patch |   10 +-
 ...-net-ethernet-mediatek-support-net-labels.patch |    4 +-
 8 files changed, 1487 insertions(+), 1260 deletions(-)

diff --git a/target/linux/generic/pending-5.4/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch b/target/linux/generic/pending-5.4/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch
index 36a52bbc06..9ef675dcd1 100644
--- a/target/linux/generic/pending-5.4/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch
+++ b/target/linux/generic/pending-5.4/770-13-net-ethernet-mtk_eth_soc-fix-parsing-packets-in-GDM.patch
@@ -18,14 +18,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  
  #include "mtk_eth_soc.h"
  
-@@ -1240,12 +1241,14 @@ static int mtk_poll_rx(struct napi_struc
- 	u8 *data, *new_data;
- 	struct mtk_rx_dma *rxd, trxd;
- 	int done = 0, bytes = 0;
-+	bool uses_dsa = eth->netdev[0] && netdev_uses_dsa(eth->netdev[0]);
- 
- 	while (done < budget) {
- 		struct net_device *netdev;
+@@ -1246,6 +1247,7 @@ static int mtk_poll_rx(struct napi_struc
  		unsigned int pktlen;
  		dma_addr_t dma_addr;
  		int mac;
@@ -33,27 +26,26 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  
  		ring = mtk_get_rx_ring(eth);
  		if (unlikely(!ring))
-@@ -1259,13 +1262,13 @@ static int mtk_poll_rx(struct napi_struc
+@@ -1259,13 +1261,12 @@ static int mtk_poll_rx(struct napi_struc
  			break;
  
  		/* find out which mac the packet come from. values start at 1 */
 -		if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
-+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
++		if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) ||
++		    (trxd.rxd4 & RX_DMA_SPECIAL_TAG))
  			mac = 0;
 -		} else {
 -			mac = (trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
 -				RX_DMA_FPORT_MASK;
 -			mac--;
 -		}
-+		else if (uses_dsa)
-+			mac = !(trxd.rxd4 >> 22);
 +		else
 +			mac = ((trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
 +			       RX_DMA_FPORT_MASK) - 1;
  
  		if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||
  			     !eth->netdev[mac]))
-@@ -2247,6 +2250,9 @@ static void mtk_gdm_config(struct mtk_et
+@@ -2247,6 +2248,9 @@ static void mtk_gdm_config(struct mtk_et
  
  		val |= config;
  
@@ -73,3 +65,11 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  #define MTK_GDMA_ICS_EN		BIT(22)
  #define MTK_GDMA_TCS_EN		BIT(21)
  #define MTK_GDMA_UCS_EN		BIT(20)
+@@ -311,6 +312,7 @@
+ #define RX_DMA_L4_VALID_PDMA	BIT(30)		/* when PDMA is used */
+ #define RX_DMA_FPORT_SHIFT	19
+ #define RX_DMA_FPORT_MASK	0x7
++#define RX_DMA_SPECIAL_TAG	BIT(22)
+ 
+ /* PHY Indirect Access Control registers */
+ #define MTK_PHY_IAC		0x10004
diff --git a/target/linux/generic/pending-5.4/770-14-net-ethernet-mtk_eth_soc-set-PPE-flow-hash-as-skb-ha.patch b/target/linux/generic/pending-5.4/770-14-net-ethernet-mtk_eth_soc-set-PPE-flow-hash-as-skb-ha.patch
index 384af9d21d..60ac12c013 100644
--- a/target/linux/generic/pending-5.4/770-14-net-ethernet-mtk_eth_soc-set-PPE-flow-hash-as-skb-ha.patch
+++ b/target/linux/generic/pending-5.4/770-14-net-ethernet-mtk_eth_soc-set-PPE-flow-hash-as-skb-ha.patch
@@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1318,6 +1318,10 @@ static int mtk_poll_rx(struct napi_struc
+@@ -1316,6 +1316,10 @@ static int mtk_poll_rx(struct napi_struc
  		skb->protocol = eth_type_trans(skb, netdev);
  		bytes += pktlen;
  
diff --git a/target/linux/generic/pending-5.4/770-15-net-ethernet-mediatek-mtk_eth_soc-add-support-for-in.patch b/target/linux/generic/pending-5.4/770-15-net-ethernet-mediatek-mtk_eth_soc-add-support-for-in.patch
new file mode 100644
index 0000000000..6d729c0f08
--- /dev/null
+++ b/target/linux/generic/pending-5.4/770-15-net-ethernet-mediatek-mtk_eth_soc-add-support-for-in.patch
@@ -0,0 +1,1058 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Sun, 11 Oct 2020 22:23:08 +0200
+Subject: [PATCH] ethernet: mediatek: mtk_eth_soc: add support for
+ initializing the PPE
+
+The PPE (packet processing engine) is used to offload NAT/routed or even
+bridged flows. This patch brings up the PPE and uses it to get a packet
+hash. It also contains some functionality that will be used to bring up
+flow offloading later
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+ 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_regs.h
+
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -4,4 +4,4 @@
+ #
+ 
+ obj-$(CONFIG_NET_MEDIATEK_SOC)                 += mtk_eth.o
+-mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o
++mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2277,12 +2277,17 @@ static int mtk_open(struct net_device *d
+ 
+ 	/* we run 2 netdevs on the same dma ring so we only bring it up once */
+ 	if (!refcount_read(&eth->dma_refcnt)) {
+-		int err = mtk_start_dma(eth);
++		u32 gdm_config = MTK_GDMA_TO_PDMA;
++		int err;
+ 
++		err = mtk_start_dma(eth);
+ 		if (err)
+ 			return err;
+ 
+-		mtk_gdm_config(eth, 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, gdm_config);
+ 
+ 		napi_enable(&eth->tx_napi);
+ 		napi_enable(&eth->rx_napi);
+@@ -2352,6 +2357,9 @@ static int mtk_stop(struct net_device *d
+ 
+ 	mtk_dma_free(eth);
+ 
++	if (eth->soc->offload_version)
++		mtk_ppe_stop(&eth->ppe);
++
+ 	return 0;
+ }
+ 
+@@ -3141,6 +3149,13 @@ static int mtk_probe(struct platform_dev
+ 			goto err_free_dev;
+ 	}
+ 
++	if (eth->soc->offload_version) {
++		err = mtk_ppe_init(&eth->ppe, eth->dev,
++				   eth->base + MTK_ETH_PPE_BASE, 2);
++		if (err)
++			goto err_free_dev;
++	}
++
+ 	for (i = 0; i < MTK_MAX_DEVS; i++) {
+ 		if (!eth->netdev[i])
+ 			continue;
+@@ -3215,6 +3230,7 @@ static const struct mtk_soc_data mt7621_
+ 	.hw_features = MTK_HW_FEATURES,
+ 	.required_clks = MT7621_CLKS_BITMAP,
+ 	.required_pctl = false,
++	.offload_version = 2,
+ };
+ 
+ static const struct mtk_soc_data mt7622_data = {
+@@ -3223,6 +3239,7 @@ static const struct mtk_soc_data mt7622_
+ 	.hw_features = MTK_HW_FEATURES,
+ 	.required_clks = MT7622_CLKS_BITMAP,
+ 	.required_pctl = false,
++	.offload_version = 2,
+ };
+ 
+ static const struct mtk_soc_data mt7623_data = {
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -16,6 +16,7 @@
+ #include <linux/refcount.h>
+ #include <linux/phylink.h>
+ #include <linux/dim.h>
++#include "mtk_ppe.h"
+ 
+ #define MTK_QDMA_PAGE_SIZE	2048
+ #define	MTK_MAX_RX_LENGTH	1536
+@@ -87,6 +88,7 @@
+ #define MTK_GDMA_TCS_EN		BIT(21)
+ #define MTK_GDMA_UCS_EN		BIT(20)
+ #define MTK_GDMA_TO_PDMA	0x0
++#define MTK_GDMA_TO_PPE		0x4444
+ #define MTK_GDMA_DROP_ALL       0x7777
+ 
+ /* Unicast Filter MAC Address Register - Low */
+@@ -308,6 +310,12 @@
+ #define RX_DMA_VID(_x)		((_x) & 0xfff)
+ 
+ /* 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 */
+ #define RX_DMA_FPORT_SHIFT	19
+@@ -807,6 +815,7 @@ struct mtk_soc_data {
+ 	u32		caps;
+ 	u32		required_clks;
+ 	bool		required_pctl;
++	u8		offload_version;
+ 	netdev_features_t hw_features;
+ };
+ 
+@@ -918,6 +927,8 @@ struct mtk_eth {
+ 	u32				tx_int_status_reg;
+ 	u32				rx_dma_l4_valid;
+ 	int				ip_align;
++
++	struct mtk_ppe			ppe;
+ };
+ 
+ /* struct mtk_mac -	the structure that holds the info about the MACs of the
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -0,0 +1,497 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd at nbd.name> */
++
++#include <linux/kernel.h>
++#include <linux/jiffies.h>
++#include <linux/delay.h>
++#include <linux/io.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)
++{
++	unsigned long timeout = jiffies + HZ;
++
++	while (time_is_before_jiffies(timeout)) {
++		if (!(ppe_r32(ppe, MTK_PPE_GLO_CFG) & MTK_PPE_GLO_CFG_BUSY))
++			return 0;
++
++		usleep_range(10, 20);
++	}
++
++	dev_err(ppe->dev, "PPE table busy");
++
++	return -ETIMEDOUT;
++}
++
++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 |
++	      MTK_FOE_IB1_BIND_KEEPALIVE;
++	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_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,
++		 int version)
++{
++	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 = 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;
++
++	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_DUP_CPU) |
++	      FIELD_PREP(MTK_PPE_TB_CFG_HASH_MODE, 1) |
++	      FIELD_PREP(MTK_PPE_TB_CFG_SCAN_MODE,
++			 MTK_PPE_SCAN_MODE_KEEPALIVE_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, 12) |
++	      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, 7);
++	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_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 offload engine */
++	ppe_clear(ppe, MTK_PPE_GLO_CFG, MTK_PPE_GLO_CFG_EN);
++	ppe_w32(ppe, MTK_PPE_FLOW_CFG, 0);
++
++	/* 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;
++	ppe_clear(ppe, MTK_PPE_TB_CFG, val);
++
++	return mtk_ppe_wait_busy(ppe);
++}
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -0,0 +1,274 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd at 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_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 version);
++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();
++}
++
++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_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);
++
++#endif
+--- /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 at 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/target/linux/generic/pending-5.4/770-16-net-ethernet-mediatek-mtk_eth_soc-add-flow-offloadin.patch b/target/linux/generic/pending-5.4/770-16-net-ethernet-mediatek-mtk_eth_soc-add-flow-offloadin.patch
new file mode 100644
index 0000000000..810eeda75c
--- /dev/null
+++ b/target/linux/generic/pending-5.4/770-16-net-ethernet-mediatek-mtk_eth_soc-add-flow-offloadin.patch
@@ -0,0 +1,401 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Sun, 11 Oct 2020 22:28:32 +0200
+Subject: [PATCH] net: ethernet: mediatek: mtk_eth_soc: add flow offloading
+ support
+
+Only supports IPv4 for now
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_offload.c
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -4,4 +4,4 @@
+ #
+ 
+ obj-$(CONFIG_NET_MEDIATEK_SOC)                 += mtk_eth.o
+-mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o
++mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_offload.o
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -19,6 +19,8 @@
+ #include <linux/interrupt.h>
+ #include <linux/pinctrl/devinfo.h>
+ #include <linux/phylink.h>
++#include <linux/netfilter.h>
++#include <net/netfilter/nf_flow_table.h>
+ #include <net/dsa.h>
+ 
+ #include "mtk_eth_soc.h"
+@@ -1324,8 +1326,12 @@ static int mtk_poll_rx(struct napi_struc
+ 		    (trxd.rxd2 & RX_DMA_VTAG))
+ 			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ 					       RX_DMA_VID(trxd.rxd3));
+-		skb_record_rx_queue(skb, 0);
+-		napi_gro_receive(napi, skb);
++		if (mtk_offload_check_rx(eth, skb, trxd.rxd4) == 0) {
++			skb_record_rx_queue(skb, 0);
++			napi_gro_receive(napi, skb);
++		} else {
++			dev_kfree_skb(skb);
++		}
+ 
+ skip_rx:
+ 		ring->data[idx] = new_data;
+@@ -2858,6 +2864,25 @@ static int mtk_set_rxnfc(struct net_devi
+ 	return ret;
+ }
+ 
++static int
++mtk_flow_offload(enum flow_offload_type type, struct flow_offload *flow,
++		struct flow_offload_hw_path *src,
++		struct flow_offload_hw_path *dest)
++{
++	struct mtk_mac *mac = netdev_priv(src->dev);
++	struct mtk_eth *eth = mac->hw;
++
++	if (!eth->soc->offload_version)
++		return -EINVAL;
++
++	if (src->dev->base_addr != dest->dev->base_addr)
++		return -EINVAL;
++
++	mac = netdev_priv(src->dev);
++
++	return mtk_flow_offload_add(eth, type, flow, src, dest);
++}
++
+ static const struct ethtool_ops mtk_ethtool_ops = {
+ 	.get_link_ksettings	= mtk_get_link_ksettings,
+ 	.set_link_ksettings	= mtk_set_link_ksettings,
+@@ -2889,6 +2914,7 @@ static const struct net_device_ops mtk_n
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+ 	.ndo_poll_controller	= mtk_poll_controller,
+ #endif
++	.ndo_flow_offload       = mtk_flow_offload,
+ };
+ 
+ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+@@ -3154,6 +3180,10 @@ static int mtk_probe(struct platform_dev
+ 				   eth->base + MTK_ETH_PPE_BASE, 2);
+ 		if (err)
+ 			goto err_free_dev;
++
++		err = mtk_flow_offload_init(eth);
++		if (err)
++			goto err_free_dev;
+ 	}
+ 
+ 	for (i = 0; i < MTK_MAX_DEVS; i++) {
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -929,6 +929,7 @@ struct mtk_eth {
+ 	int				ip_align;
+ 
+ 	struct mtk_ppe			ppe;
++	struct flow_offload __rcu       **foe_flow_table;
+ };
+ 
+ /* struct mtk_mac -	the structure that holds the info about the MACs of the
+@@ -973,4 +974,12 @@ int mtk_gmac_sgmii_path_setup(struct mtk
+ int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
+ int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
+ 
++int mtk_flow_offload_init(struct mtk_eth *eth);
++int mtk_flow_offload_add(struct mtk_eth *eth,
++			 enum flow_offload_type type,
++			 struct flow_offload *flow,
++			 struct flow_offload_hw_path *src,
++			 struct flow_offload_hw_path *dest);
++int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4);
++
+ #endif /* MTK_ETH_H */
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_offload.c
+@@ -0,0 +1,146 @@
++/*   This program is free software; you can redistribute it and/or modify
++ *   it under the terms of the GNU General Public License as published by
++ *   the Free Software Foundation; version 2 of the License
++ *
++ *   This program is distributed in the hope that it will be useful,
++ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *   GNU General Public License for more details.
++ *
++ *   Copyright (C) 2018 John Crispin <john at phrozen.org>
++ */
++
++#include <net/netfilter/nf_flow_table.h>
++#include "mtk_eth_soc.h"
++
++static int
++mtk_offload_prepare_v4(struct mtk_eth *eth, struct mtk_foe_entry *entry,
++		       struct flow_offload_tuple *s_tuple,
++		       struct flow_offload_tuple *d_tuple,
++		       struct flow_offload_hw_path *src,
++		       struct flow_offload_hw_path *dest)
++{
++	int dest_port = 1;
++
++	if (dest->dev == eth->netdev[1])
++	    dest_port = 2;
++
++	mtk_foe_entry_prepare(entry, MTK_PPE_PKT_TYPE_IPV4_HNAPT, s_tuple->l4proto,
++			      dest_port, dest->eth_src, dest->eth_dest);
++	mtk_foe_entry_set_ipv4_tuple(entry, false,
++				     s_tuple->src_v4.s_addr, s_tuple->src_port,
++				     s_tuple->dst_v4.s_addr, s_tuple->dst_port);
++	mtk_foe_entry_set_ipv4_tuple(entry, true,
++				     d_tuple->dst_v4.s_addr, d_tuple->dst_port,
++				     d_tuple->src_v4.s_addr, d_tuple->src_port);
++
++	if (dest->flags & FLOW_OFFLOAD_PATH_PPPOE)
++		mtk_foe_entry_set_pppoe(entry, dest->pppoe_sid);
++
++	if (dest->flags & FLOW_OFFLOAD_PATH_VLAN)
++		mtk_foe_entry_set_vlan(entry, dest->vlan_id);
++
++	if (dest->flags & FLOW_OFFLOAD_PATH_DSA)
++		mtk_foe_entry_set_dsa(entry, dest->dsa_port);
++
++	return 0;
++}
++
++int mtk_flow_offload_add(struct mtk_eth *eth,
++			 enum flow_offload_type type,
++			 struct flow_offload *flow,
++			 struct flow_offload_hw_path *src,
++			 struct flow_offload_hw_path *dest)
++{
++	struct flow_offload_tuple *otuple = &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple;
++	struct flow_offload_tuple *rtuple = &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple;
++	struct mtk_foe_entry orig, reply;
++	u32 ohash, rhash, timestamp;
++
++	if (otuple->l4proto != IPPROTO_TCP && otuple->l4proto != IPPROTO_UDP)
++		return -EINVAL;
++
++	if (type == FLOW_OFFLOAD_DEL) {
++		ohash = (unsigned long)flow->priv;
++		rhash = ohash >> 16;
++		ohash &= 0xffff;
++		mtk_foe_entry_clear(&eth->ppe, ohash);
++		mtk_foe_entry_clear(&eth->ppe, rhash);
++		rcu_assign_pointer(eth->foe_flow_table[ohash], NULL);
++		rcu_assign_pointer(eth->foe_flow_table[rhash], NULL);
++		synchronize_rcu();
++
++		return 0;
++	}
++
++	switch (otuple->l3proto) {
++	case AF_INET:
++		if (mtk_offload_prepare_v4(eth, &orig, otuple, rtuple, src, dest) ||
++		    mtk_offload_prepare_v4(eth, &reply, rtuple, otuple, dest, src))
++			return -EINVAL;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	timestamp = mtk_r32(eth, 0x0010);
++
++	ohash = mtk_foe_entry_commit(&eth->ppe, &orig, timestamp);
++	if (ohash < 0)
++		return -EINVAL;
++
++	rhash = mtk_foe_entry_commit(&eth->ppe, &reply, timestamp);
++	if (rhash < 0) {
++		mtk_foe_entry_clear(&eth->ppe, ohash);
++		return -EINVAL;
++	}
++
++	rcu_assign_pointer(eth->foe_flow_table[ohash], flow);
++	rcu_assign_pointer(eth->foe_flow_table[rhash], flow);
++
++	ohash |= rhash << 16;
++	flow->priv = (void *)(unsigned long)ohash;
++
++	return 0;
++}
++
++static void mtk_offload_keepalive(struct mtk_eth *eth, unsigned int hash)
++{
++	struct flow_offload *flow;
++
++	rcu_read_lock();
++	flow = rcu_dereference(eth->foe_flow_table[hash]);
++	if (flow)
++		flow->timeout = jiffies + 30 * HZ;
++	rcu_read_unlock();
++}
++
++int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4)
++{
++	unsigned int hash;
++
++	switch (FIELD_GET(MTK_RXD4_PPE_CPU_REASON, rxd4)) {
++	case MTK_PPE_CPU_REASON_KEEPALIVE_UC_OLD_HDR:
++	case MTK_PPE_CPU_REASON_KEEPALIVE_MC_NEW_HDR:
++	case MTK_PPE_CPU_REASON_KEEPALIVE_DUP_OLD_HDR:
++		hash = FIELD_GET(MTK_RXD4_FOE_ENTRY, rxd4);
++		mtk_offload_keepalive(eth, hash);
++		return -1;
++	case MTK_PPE_CPU_REASON_PACKET_SAMPLING:
++		return -1;
++	default:
++		return 0;
++	}
++}
++
++int mtk_flow_offload_init(struct mtk_eth *eth)
++{
++	eth->foe_flow_table = devm_kcalloc(eth->dev, MTK_PPE_ENTRIES,
++					   sizeof(*eth->foe_flow_table),
++					   GFP_KERNEL);
++
++	if (!eth->foe_flow_table)
++		return -ENOMEM;
++
++	return 0;
++}
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -375,6 +375,8 @@ int mtk_ppe_init(struct mtk_ppe *ppe, st
+ 
+ 	ppe->foe_table = foe;
+ 
++	mtk_ppe_debugfs_init(ppe);
++
+ 	return 0;
+ }
+ 
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -271,4 +271,7 @@ int mtk_foe_entry_set_pppoe(struct mtk_f
+ int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
+ 			 u16 timestamp);
+ 
++/* internal */
++int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
++
+ #endif
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+@@ -0,0 +1,114 @@
++/*   This program is free software; you can redistribute it and/or modify
++ *   it under the terms of the GNU General Public License as published by
++ *   the Free Software Foundation; version 2 of the License
++ *
++ *   This program is distributed in the hope that it will be useful,
++ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *   GNU General Public License for more details.
++ *
++ *   Copyright (C) 2014-2016 Sean Wang <sean.wang at mediatek.com>
++ *   Copyright (C) 2016-2017 John Crispin <blogic at openwrt.org>
++ *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
++ */
++
++#include <linux/kernel.h>
++#include <linux/debugfs.h>
++#include "mtk_eth_soc.h"
++
++static const char *mtk_foe_entry_state_str[] = {
++	"INVALID",
++	"UNBIND",
++	"BIND",
++	"FIN"
++};
++
++static const char *mtk_foe_packet_type_str[] = {
++	"IPV4_HNAPT",
++	"IPV4_HNAT",
++	"IPV6_1T_ROUTE",
++	"IPV4_DSLITE",
++	"IPV6_3T_ROUTE",
++	"IPV6_5T_ROUTE",
++	"IPV6_6RD",
++};
++
++#define es(entry)		(mtk_foe_entry_state_str[FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1)])
++//#define ei(entry, end)		(MTK_PPE_TBL_SZ - (int)(end - entry))
++#define pt(entry)		(mtk_foe_packet_type_str[FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1)])
++
++static int mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private)
++{
++	struct mtk_ppe *ppe = m->private;
++	int i, count;
++
++	for (i = 0, count = 0; i < MTK_PPE_ENTRIES; i++) {
++		struct mtk_foe_entry *entry = &ppe->foe_table[i];
++
++		if (!FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1))
++			continue;
++
++		if (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1) ==
++		    MTK_PPE_PKT_TYPE_IPV4_HNAPT) {
++			struct mtk_foe_ipv4 *ip4 = &entry->ipv4;
++			struct mtk_foe_mac_info *l2 = &ip4->l2;
++
++			__be32 saddr = htonl(ip4->orig.src_ip);
++			__be32 daddr = htonl(ip4->orig.dest_ip);
++			__be32 nsaddr = htonl(ip4->new.src_ip);
++			__be32 ndaddr = htonl(ip4->new.dest_ip);
++			unsigned char h_dest[ETH_ALEN];
++			unsigned char h_source[ETH_ALEN];
++
++			*((__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,
++				   "(%x)0x%05x|state=%s|type=%s|"
++				   "%pI4:%d->%pI4:%d=>%pI4:%d->%pI4:%d|%pM=>%pM|"
++				   "etype=0x%04x|info1=0x%x|info2=0x%x|"
++				   "vlan1=%d|vlan2=%d\n",
++				   count, i, es(entry), pt(entry),
++				   &saddr, ip4->orig.src_port,
++				   &daddr, ip4->orig.dest_port,
++				   &nsaddr, ip4->new.src_port,
++				   &ndaddr, ip4->new.dest_port,
++				   h_source, h_dest,
++				   ntohs(l2->etype),
++				   entry->ib1,
++				   ip4->ib2,
++				   l2->vlan1,
++				   l2->vlan2);
++			count++;
++		} else
++			seq_printf(m, "0x%05x state=%s\n", count, es(entry));
++	}
++
++	return 0;
++}
++
++static int mtk_ppe_debugfs_foe_open(struct inode *inode, struct file *file)
++{
++	return single_open(file, mtk_ppe_debugfs_foe_show, inode->i_private);
++}
++
++static const struct file_operations mtk_ppe_debugfs_foe_fops = {
++	.open = mtk_ppe_debugfs_foe_open,
++	.read = seq_read,
++	.llseek = seq_lseek,
++	.release = single_release,
++};
++
++int mtk_ppe_debugfs_init(struct mtk_ppe *ppe)
++{
++	struct dentry *root;
++
++	root = debugfs_create_dir("mtk_ppe", NULL);
++	if (!root)
++		return -ENOMEM;
++
++	debugfs_create_file("entries", S_IRUGO, root, ppe, &mtk_ppe_debugfs_foe_fops);
++
++	return 0;
++}
diff --git a/target/linux/mediatek/patches-5.4/0600-net-phylink-propagate-resolved-link-config-via-mac_l.patch b/target/linux/mediatek/patches-5.4/0600-net-phylink-propagate-resolved-link-config-via-mac_l.patch
index b480490b8d..6473dc19c3 100644
--- a/target/linux/mediatek/patches-5.4/0600-net-phylink-propagate-resolved-link-config-via-mac_l.patch
+++ b/target/linux/mediatek/patches-5.4/0600-net-phylink-propagate-resolved-link-config-via-mac_l.patch
@@ -111,7 +111,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
  	struct mvpp2_port *port = netdev_priv(dev);
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -446,9 +446,10 @@ static void mtk_mac_link_down(struct phy
+@@ -448,9 +448,10 @@ static void mtk_mac_link_down(struct phy
  	mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
  }
  
@@ -199,7 +199,10 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
 + * @duplex: link duplex
 + * @tx_pause: link transmit pause enablement status
 + * @rx_pause: link receive pause enablement status
-+ *
+  *
+- * If @mode is not an in-band negotiation mode (as defined by
+- * phylink_autoneg_inband()), allow the link to come up. If @phy
+- * is non-%NULL, configure Energy Efficient Ethernet by calling
 + * Configure the MAC for an established link.
 + *
 + * @speed, @duplex, @tx_pause and @rx_pause indicate the finalised link
@@ -211,10 +214,7 @@ Signed-off-by: David S. Miller <davem at davemloft.net>
 + * Note that when 802.3z in-band negotiation is in use, it is possible
 + * that the user wishes to override the pause settings, and this should
 + * be allowed when considering the implementation of this method.
-  *
-- * If @mode is not an in-band negotiation mode (as defined by
-- * phylink_autoneg_inband()), allow the link to come up. If @phy
-- * is non-%NULL, configure Energy Efficient Ethernet by calling
++ *
 + * If in-band negotiation mode is disabled, allow the link to come up. If
 + * @phy is non-%NULL, configure Energy Efficient Ethernet by calling
   * phy_init_eee() and perform appropriate MAC configuration for EEE.
diff --git a/target/linux/mediatek/patches-5.4/0999-hnat.patch b/target/linux/mediatek/patches-5.4/0999-hnat.patch
deleted file mode 100644
index f86c882eb2..0000000000
--- a/target/linux/mediatek/patches-5.4/0999-hnat.patch
+++ /dev/null
@@ -1,1234 +0,0 @@
---- a/drivers/net/ethernet/mediatek/Kconfig
-+++ b/drivers/net/ethernet/mediatek/Kconfig
-@@ -15,4 +15,8 @@ config NET_MEDIATEK_SOC
- 	  This driver supports the gigabit ethernet MACs in the
- 	  MediaTek SoC family.
- 
-+config NET_MEDIATEK_OFFLOAD
-+        def_bool NET_MEDIATEK_SOC
-+        depends on NET_MEDIATEK_SOC
-+
- endif #NET_VENDOR_MEDIATEK
---- a/drivers/net/ethernet/mediatek/Makefile
-+++ b/drivers/net/ethernet/mediatek/Makefile
-@@ -5,3 +5,4 @@
- 
- obj-$(CONFIG_NET_MEDIATEK_SOC)                 += mtk_eth.o
- mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o
-+mtk_eth-$(CONFIG_NET_MEDIATEK_OFFLOAD)	+= mtk_offload.o mtk_debugfs.o
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_debugfs.c
-@@ -0,0 +1,117 @@
-+/*   This program is free software; you can redistribute it and/or modify
-+ *   it under the terms of the GNU General Public License as published by
-+ *   the Free Software Foundation; version 2 of the License
-+ *
-+ *   This program is distributed in the hope that it will be useful,
-+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *   GNU General Public License for more details.
-+ *
-+ *   Copyright (C) 2014-2016 Sean Wang <sean.wang at mediatek.com>
-+ *   Copyright (C) 2016-2017 John Crispin <blogic at openwrt.org>
-+ */
-+
-+#include "mtk_offload.h"
-+
-+static const char *mtk_foe_entry_state_str[] = {
-+	"INVALID",
-+	"UNBIND",
-+	"BIND",
-+	"FIN"
-+};
-+
-+static const char *mtk_foe_packet_type_str[] = {
-+	"IPV4_HNAPT",
-+	"IPV4_HNAT",
-+	"IPV6_1T_ROUTE",
-+	"IPV4_DSLITE",
-+	"IPV6_3T_ROUTE",
-+	"IPV6_5T_ROUTE",
-+	"IPV6_6RD",
-+};
-+
-+#define IPV4_HNAPT                      0
-+#define IPV4_HNAT                       1
-+#define IS_IPV4_HNAPT(x)	(((x)->bfib1.pkt_type == IPV4_HNAPT) ? 1: 0)
-+struct mtk_eth *_eth;
-+#define es(entry)		(mtk_foe_entry_state_str[entry->bfib1.state])
-+//#define ei(entry, end)		(MTK_PPE_TBL_SZ - (int)(end - entry))
-+#define ei(entry, end)		(MTK_PPE_ENTRY_CNT - (int)(end - entry))
-+#define pt(entry)		(mtk_foe_packet_type_str[entry->ipv4_hnapt.bfib1.pkt_type])
-+
-+static int mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private)
-+{
-+	struct mtk_eth *eth = _eth;
-+	struct mtk_foe_entry *entry, *end;
-+	int i = 0;
-+
-+	entry = eth->foe_table;
-+	end = eth->foe_table + MTK_PPE_ENTRY_CNT;
-+
-+	while (entry < end) {
-+		if (!entry->bfib1.state) {
-+
-+		} else if (IS_IPV4_HNAPT(entry)) {
-+			__be32 saddr = htonl(entry->ipv4_hnapt.sip);
-+			__be32 daddr = htonl(entry->ipv4_hnapt.dip);
-+			__be32 nsaddr = htonl(entry->ipv4_hnapt.new_sip);
-+			__be32 ndaddr = htonl(entry->ipv4_hnapt.new_dip);
-+			unsigned char h_dest[ETH_ALEN];
-+			unsigned char h_source[ETH_ALEN];
-+
-+			*((u32*) h_source) = swab32(entry->ipv4_hnapt.smac_hi);
-+			*((u16*) &h_source[4]) = swab16(entry->ipv4_hnapt.smac_lo);
-+			*((u32*) h_dest) = swab32(entry->ipv4_hnapt.dmac_hi);
-+			*((u16*) &h_dest[4]) = swab16(entry->ipv4_hnapt.dmac_lo);
-+			seq_printf(m,
-+				   "(%x)0x%05x|state=%s|type=%s|"
-+				   "%pI4:%d->%pI4:%d=>%pI4:%d->%pI4:%d|%pM=>%pM|"
-+				   "etype=0x%04x|info1=0x%x|info2=0x%x|"
-+				   "vlan1=%d|vlan2=%d\n",
-+				   i,
-+				   ei(entry, end), es(entry), pt(entry),
-+				   &saddr, entry->ipv4_hnapt.sport,
-+				   &daddr, entry->ipv4_hnapt.dport,
-+				   &nsaddr, entry->ipv4_hnapt.new_sport,
-+				   &ndaddr, entry->ipv4_hnapt.new_dport, h_source,
-+				   h_dest, ntohs(entry->ipv4_hnapt.etype),
-+				   entry->ipv4_hnapt.info_blk1,
-+				   entry->ipv4_hnapt.info_blk2,
-+				   entry->ipv4_hnapt.vlan1,
-+				   entry->ipv4_hnapt.vlan2);
-+		} else
-+			seq_printf(m, "0x%05x state=%s\n",
-+				   ei(entry, end), es(entry));
-+		entry++;
-+		i++;
-+	}
-+
-+	return 0;
-+}
-+
-+static int mtk_ppe_debugfs_foe_open(struct inode *inode, struct file *file)
-+{
-+	return single_open(file, mtk_ppe_debugfs_foe_show, file->private_data);
-+}
-+
-+static const struct file_operations mtk_ppe_debugfs_foe_fops = {
-+	.open = mtk_ppe_debugfs_foe_open,
-+	.read = seq_read,
-+	.llseek = seq_lseek,
-+	.release = single_release,
-+};
-+
-+int mtk_ppe_debugfs_init(struct mtk_eth *eth)
-+{
-+	struct dentry *root;
-+
-+	_eth = eth;
-+
-+	root = debugfs_create_dir("mtk_ppe", NULL);
-+	if (!root)
-+		return -ENOMEM;
-+
-+	debugfs_create_file("all_entry", S_IRUGO, root, eth, &mtk_ppe_debugfs_foe_fops);
-+
-+	return 0;
-+}
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -19,6 +19,8 @@
- #include <linux/interrupt.h>
- #include <linux/pinctrl/devinfo.h>
- #include <linux/phylink.h>
-+#include <linux/netfilter.h>
-+#include <net/netfilter/nf_flow_table.h>
- #include <net/dsa.h>
- 
- #include "mtk_eth_soc.h"
-@@ -1327,8 +1329,16 @@ static int mtk_poll_rx(struct napi_struc
- 		    (trxd.rxd2 & RX_DMA_VTAG))
- 			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
- 					       RX_DMA_VID(trxd.rxd3));
--		skb_record_rx_queue(skb, 0);
--		napi_gro_receive(napi, skb);
-+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
-+                if (mtk_offload_check_rx(eth, skb, trxd.rxd4) == 0) {
-+#endif
-+			skb_record_rx_queue(skb, 0);
-+			napi_gro_receive(napi, skb);
-+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
-+		} else {
-+			dev_kfree_skb(skb);
-+		}
-+#endif
- 
- skip_rx:
- 		ring->data[idx] = new_data;
-@@ -2292,6 +2302,9 @@ static int mtk_open(struct net_device *d
- 		mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
- 		mtk_rx_irq_enable(eth, MTK_RX_DONE_INT);
- 		refcount_set(&eth->dma_refcnt, 1);
-+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
-+		mtk_ppe_probe(eth);
-+#endif
- 	}
- 	else
- 		refcount_inc(&eth->dma_refcnt);
-@@ -2355,6 +2368,9 @@ static int mtk_stop(struct net_device *d
- 
- 	mtk_dma_free(eth);
- 
-+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
-+	mtk_ppe_remove(eth);
-+#endif
- 	return 0;
- }
- 
-@@ -2853,6 +2869,27 @@ static int mtk_set_rxnfc(struct net_devi
- 	return ret;
- }
- 
-+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
-+static int
-+mtk_flow_offload(enum flow_offload_type type, struct flow_offload *flow,
-+		struct flow_offload_hw_path *src,
-+		struct flow_offload_hw_path *dest)
-+{
-+	struct mtk_mac *mac = netdev_priv(src->dev);
-+	struct mtk_eth *eth = mac->hw;
-+
-+	if (!eth->soc->offload_version)
-+		return -EINVAL;
-+
-+	if (src->dev->base_addr != dest->dev->base_addr)
-+		return -EINVAL;
-+
-+	mac = netdev_priv(src->dev);
-+
-+	return mtk_flow_offload_add(eth, type, flow, src, dest);
-+}
-+#endif
-+
- static const struct ethtool_ops mtk_ethtool_ops = {
- 	.get_link_ksettings	= mtk_get_link_ksettings,
- 	.set_link_ksettings	= mtk_set_link_ksettings,
-@@ -2884,6 +2921,9 @@ static const struct net_device_ops mtk_n
- #ifdef CONFIG_NET_POLL_CONTROLLER
- 	.ndo_poll_controller	= mtk_poll_controller,
- #endif
-+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
-+	.ndo_flow_offload       = mtk_flow_offload,
-+#endif
- };
- 
- static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
-@@ -3226,6 +3266,7 @@ static const struct mtk_soc_data mt7622_
- 	.hw_features = MTK_HW_FEATURES,
- 	.required_clks = MT7622_CLKS_BITMAP,
- 	.required_pctl = false,
-+	.offload_version = MTK_OFFLOAD_V2,
- };
- 
- static const struct mtk_soc_data mt7623_data = {
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -790,6 +790,13 @@ enum mkt_eth_capabilities {
- 		      MTK_MUX_U3_GMAC2_TO_QPHY | \
- 		      MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA)
- 
-+enum mtk_flow_offload_version {
-+	MTK_OFFLOAD_NONE = 0,
-+	MTK_OFFLOAD_V1,
-+	MTK_OFFLOAD_V2,
-+	MTK_OFFLOAD_V3,
-+};
-+
- /* struct mtk_eth_data -	This is the structure holding all differences
-  *				among various plaforms
-  * @ana_rgc3:                   The offset for register ANA_RGC3 related to
-@@ -807,6 +814,7 @@ struct mtk_soc_data {
- 	u32		required_clks;
- 	bool		required_pctl;
- 	netdev_features_t hw_features;
-+	enum mtk_flow_offload_version offload_version;
- };
- 
- /* currently no SoC has more than 2 macs */
-@@ -832,6 +840,23 @@ struct mtk_sgmii {
- 	u32             ana_rgc3;
- };
- 
-+
-+struct mib_entry {
-+	u32 byt_cnt_l;
-+	u16 byt_cnt_h;
-+	u32 pkt_cnt_l;
-+	u8 pkt_cnt_h;
-+	u8 resv0;
-+	u32 resv1;
-+} __packed __aligned(4);
-+
-+struct hnat_accounting {
-+	u64 bytes;
-+	u64 packets;
-+};
-+
-+
-+
- /* struct mtk_eth -	This is the main datasructure for holding the state
-  *			of the driver
-  * @dev:		The device pointer
-@@ -917,6 +942,16 @@ struct mtk_eth {
- 	u32				tx_int_status_reg;
- 	u32				rx_dma_l4_valid;
- 	int				ip_align;
-+
-+	struct reset_control            *rst_ppe;
-+	struct mtk_foe_entry            *foe_table;
-+	dma_addr_t                      foe_table_phys;
-+	struct flow_offload __rcu       **foe_flow_table;
-+
-+	struct mib_entry		*foe_mib_cpu;
-+	dma_addr_t			foe_mib_dev;
-+	struct hnat_accounting		*acct;
-+	bool				per_flow_accounting;
- };
- 
- /* struct mtk_mac -	the structure that holds the info about the MACs of the
-@@ -949,6 +984,7 @@ void mtk_stats_update_mac(struct mtk_mac
- 
- void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
- u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
-+u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned reg);
- 
- int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
- 		   u32 ana_rgc3);
-@@ -961,4 +997,13 @@ int mtk_gmac_sgmii_path_setup(struct mtk
- int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
- int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
- 
-+int mtk_ppe_probe(struct mtk_eth *eth);
-+void mtk_ppe_remove(struct mtk_eth *eth);
-+int mtk_flow_offload_add(struct mtk_eth *eth,
-+			 enum flow_offload_type type,
-+			 struct flow_offload *flow,
-+			 struct flow_offload_hw_path *src,
-+			 struct flow_offload_hw_path *dest);
-+int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4);
-+
- #endif /* MTK_ETH_H */
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_offload.c
-@@ -0,0 +1,609 @@
-+/*   This program is free software; you can redistribute it and/or modify
-+ *   it under the terms of the GNU General Public License as published by
-+ *   the Free Software Foundation; version 2 of the License
-+ *
-+ *   This program is distributed in the hope that it will be useful,
-+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *   GNU General Public License for more details.
-+ *
-+ *   Copyright (C) 2018 John Crispin <john at phrozen.org>
-+ */
-+
-+#include "mtk_offload.h"
-+
-+#define INVALID	0
-+#define UNBIND	1
-+#define BIND	2
-+#define FIN	3
-+
-+#define IPV4_HNAPT			0
-+#define IPV4_HNAT			1
-+
-+static u32
-+mtk_flow_hash_v4(struct flow_offload_tuple *tuple)
-+{
-+	u32 ports = ntohs(tuple->src_port)  << 16 | ntohs(tuple->dst_port);
-+	u32 src = ntohl(tuple->dst_v4.s_addr);
-+	u32 dst = ntohl(tuple->src_v4.s_addr);
-+	u32 hash = (ports & src) | ((~ports) & dst);
-+	u32 hash_23_0 = hash & 0xffffff;
-+	u32 hash_31_24 = hash & 0xff000000;
-+
-+	hash = ports ^ src ^ dst ^ ((hash_23_0 << 8) | (hash_31_24 >> 24));
-+	hash = ((hash & 0xffff0000) >> 16 ) ^ (hash & 0xfffff);
-+	hash &= 0x7ff;
-+	hash *= 2;;
-+
-+	return hash;
-+}
-+
-+static int
-+mtk_foe_prepare_v4(struct mtk_foe_entry *entry,
-+		   struct flow_offload_tuple *tuple,
-+		   struct flow_offload_tuple *dest_tuple,
-+		   struct flow_offload_hw_path *src,
-+		   struct flow_offload_hw_path *dest)
-+{
-+	int is_mcast = !!is_multicast_ether_addr(dest->eth_dest);
-+
-+	if (tuple->l4proto == IPPROTO_UDP)
-+		entry->ipv4_hnapt.bfib1.udp = 1;
-+
-+	entry->ipv4_hnapt.etype = htons(ETH_P_IP);
-+	entry->ipv4_hnapt.bfib1.pkt_type = IPV4_HNAPT;
-+	entry->ipv4_hnapt.iblk2.fqos = 0;
-+	entry->ipv4_hnapt.bfib1.ttl = 1;
-+	entry->ipv4_hnapt.bfib1.cah = 1;
-+	entry->ipv4_hnapt.bfib1.ka = 1;
-+	entry->ipv4_hnapt.iblk2.mcast = is_mcast;
-+	entry->ipv4_hnapt.iblk2.dscp = 0;
-+	entry->ipv4_hnapt.iblk2.port_mg = 0x3f;
-+	entry->ipv4_hnapt.iblk2.port_ag = 0x1f;
-+#ifdef CONFIG_NET_MEDIATEK_HW_QOS
-+	entry->ipv4_hnapt.iblk2.qid = 1;
-+	entry->ipv4_hnapt.iblk2.fqos = 1;
-+#endif
-+#ifdef CONFIG_RALINK
-+	entry->ipv4_hnapt.iblk2.dp = 1;
-+	if ((dest->flags & FLOW_OFFLOAD_PATH_VLAN) && (dest->vlan_id > 1))
-+		entry->ipv4_hnapt.iblk2.qid += 8;
-+#else
-+	entry->ipv4_hnapt.iblk2.dp = (dest->dev->name[3] - '0') + 1;
-+#endif
-+
-+	entry->ipv4_hnapt.sip = ntohl(tuple->src_v4.s_addr);
-+	entry->ipv4_hnapt.dip = ntohl(tuple->dst_v4.s_addr);
-+	entry->ipv4_hnapt.sport = ntohs(tuple->src_port);
-+	entry->ipv4_hnapt.dport = ntohs(tuple->dst_port);
-+
-+	entry->ipv4_hnapt.new_sip = ntohl(dest_tuple->dst_v4.s_addr);
-+	entry->ipv4_hnapt.new_dip = ntohl(dest_tuple->src_v4.s_addr);
-+	entry->ipv4_hnapt.new_sport = ntohs(dest_tuple->dst_port);
-+	entry->ipv4_hnapt.new_dport = ntohs(dest_tuple->src_port);
-+
-+	entry->bfib1.state = BIND;
-+
-+	if (dest->flags & FLOW_OFFLOAD_PATH_PPPOE) {
-+		entry->bfib1.psn = 1;
-+		entry->ipv4_hnapt.etype = htons(ETH_P_PPP_SES);
-+		entry->ipv4_hnapt.pppoe_id = dest->pppoe_sid;
-+	}
-+
-+	if (dest->flags & FLOW_OFFLOAD_PATH_VLAN) {
-+		entry->ipv4_hnapt.vlan1 = dest->vlan_id;
-+		entry->bfib1.vlan_layer = 1;
-+
-+		switch (dest->vlan_proto) {
-+		case htons(ETH_P_8021Q):
-+			entry->ipv4_hnapt.bfib1.vpm = 1;
-+			break;
-+		case htons(ETH_P_8021AD):
-+			entry->ipv4_hnapt.bfib1.vpm = 2;
-+			break;
-+		default:
-+			return -EINVAL;
-+		}
-+	}
-+
-+	if (dest->flags & FLOW_OFFLOAD_PATH_DSA) {
-+		entry->bfib1.vlan_layer = 1;
-+
-+		entry->ipv4_hnapt.bfib1.vpm = 0;
-+		entry->ipv4_hnapt.etype = BIT(dest->dsa_port);
-+
-+		if (dest->flags & FLOW_OFFLOAD_PATH_VLAN) {
-+			if (dest->vlan_proto != htons(ETH_P_8021Q))
-+				return -EINVAL;
-+
-+			entry->ipv4_hnapt.etype |= BIT(8);
-+		} else {
-+			entry->ipv4_hnapt.vlan1 = 0;
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+static void
-+mtk_foe_set_mac(struct mtk_foe_entry *entry, u8 *smac, u8 *dmac)
-+{
-+	entry->ipv4_hnapt.dmac_hi = swab32(*((u32*) dmac));
-+	entry->ipv4_hnapt.dmac_lo = swab16(*((u16*) &dmac[4]));
-+	entry->ipv4_hnapt.smac_hi = swab32(*((u32*) smac));
-+	entry->ipv4_hnapt.smac_lo = swab16(*((u16*) &smac[4]));
-+}
-+
-+static int
-+mtk_check_entry_available(struct mtk_eth *eth, u32 hash)
-+{
-+	struct mtk_foe_entry entry = ((struct mtk_foe_entry *)eth->foe_table)[hash];
-+
-+	return (entry.bfib1.state == BIND)? 0:1;
-+}
-+
-+static void
-+mtk_foe_write(struct mtk_eth *eth, u32 hash,
-+	      struct mtk_foe_entry *entry)
-+{
-+	struct mtk_foe_entry *table = (struct mtk_foe_entry *)eth->foe_table;
-+
-+	memcpy(&table[hash], entry, sizeof(*entry));
-+}
-+
-+int mtk_flow_offload_add(struct mtk_eth *eth,
-+			 enum flow_offload_type type,
-+			 struct flow_offload *flow,
-+			 struct flow_offload_hw_path *src,
-+			 struct flow_offload_hw_path *dest)
-+{
-+	struct flow_offload_tuple *otuple = &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple;
-+	struct flow_offload_tuple *rtuple = &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple;
-+	u32 time_stamp = mtk_r32(eth, 0x0010) & (0x7fff);
-+	u32 ohash, rhash;
-+	struct mtk_foe_entry orig = {
-+		.bfib1.time_stamp = time_stamp,
-+		.bfib1.psn = 0,
-+	};
-+	struct mtk_foe_entry reply = {
-+		.bfib1.time_stamp = time_stamp,
-+		.bfib1.psn = 0,
-+	};
-+
-+	if (otuple->l4proto != IPPROTO_TCP && otuple->l4proto != IPPROTO_UDP)
-+		return -EINVAL;
-+
-+	if (type == FLOW_OFFLOAD_DEL) {
-+		flow = NULL;
-+		synchronize_rcu();
-+		return 0;
-+	}
-+
-+	switch (otuple->l3proto) {
-+	case AF_INET:
-+		if (mtk_foe_prepare_v4(&orig, otuple, rtuple, src, dest) ||
-+		    mtk_foe_prepare_v4(&reply, rtuple, otuple, dest, src))
-+			return -EINVAL;
-+
-+		ohash = mtk_flow_hash_v4(otuple);
-+		rhash = mtk_flow_hash_v4(rtuple);
-+		break;
-+
-+	case AF_INET6:
-+		return -EINVAL;
-+
-+	default:
-+		return -EINVAL;
-+	}
-+
-+	/* Two-way hash: when hash collision occurs, the hash value will be shifted to the next position. */
-+	if (!mtk_check_entry_available(eth, ohash)){       
-+		if (!mtk_check_entry_available(eth, ohash + 1))
-+			return -EINVAL;
-+                ohash += 1;
-+        }
-+	if (!mtk_check_entry_available(eth, rhash)){
-+		if (!mtk_check_entry_available(eth, rhash + 1))
-+                        return -EINVAL;
-+                rhash += 1;
-+	}
-+
-+	mtk_foe_set_mac(&orig, dest->eth_src, dest->eth_dest);
-+	mtk_foe_set_mac(&reply, src->eth_src, src->eth_dest);
-+	mtk_foe_write(eth, ohash, &orig);
-+	mtk_foe_write(eth, rhash, &reply);
-+	rcu_assign_pointer(eth->foe_flow_table[ohash], flow);
-+	rcu_assign_pointer(eth->foe_flow_table[rhash], flow);
-+
-+	return 0;
-+}
-+
-+#ifdef CONFIG_NET_MEDIATEK_HW_QOS
-+
-+#define QDMA_TX_SCH_TX	  0x1a14
-+
-+static void mtk_ppe_scheduler(struct mtk_eth *eth, int id, u32 rate)
-+{
-+	int exp = 0, shift = 0;
-+	u32 reg = mtk_r32(eth, QDMA_TX_SCH_TX);
-+	u32 val = 0;
-+
-+	if (rate)
-+		val = BIT(11);
-+
-+	while (rate > 127) {
-+		rate /= 10;
-+		exp++;
-+	}
-+
-+	val |= (rate & 0x7f) << 4;
-+	val |= exp & 0xf;
-+	if (id)
-+		shift = 16;
-+	reg &= ~(0xffff << shift);
-+	reg |= val << shift;
-+	mtk_w32(eth, val, QDMA_TX_SCH_TX);
-+}
-+
-+#define QTX_CFG(x)	(0x1800 + (x * 0x10))
-+#define QTX_SCH(x)	(0x1804 + (x * 0x10))
-+
-+static void mtk_ppe_queue(struct mtk_eth *eth, int id, int sched, int weight, int resv, u32 min_rate, u32 max_rate)
-+{
-+	int max_exp = 0, min_exp = 0;
-+	u32 reg;
-+
-+	if (id >= 16)
-+		return;
-+
-+	reg = mtk_r32(eth, QTX_SCH(id));
-+	reg &= 0x70000000;
-+
-+	if (sched)
-+		reg |= BIT(31);
-+
-+	if (min_rate)
-+		reg |= BIT(27);
-+
-+	if (max_rate)
-+		reg |= BIT(11);
-+
-+	while (max_rate > 127) {
-+		max_rate /= 10;
-+		max_exp++;
-+	}
-+
-+	while (min_rate > 127) {
-+		min_rate /= 10;
-+		min_exp++;
-+	}
-+
-+	reg |= (min_rate & 0x7f) << 20;
-+	reg |= (min_exp & 0xf) << 16;
-+	reg |= (weight & 0xf) << 12;
-+	reg |= (max_rate & 0x7f) << 4;
-+	reg |= max_exp & 0xf;
-+	mtk_w32(eth, reg, QTX_SCH(id));
-+
-+	resv &= 0xff;
-+	reg = mtk_r32(eth, QTX_CFG(id));
-+	reg &= 0xffff0000;
-+	reg |= (resv << 8) | resv;
-+	mtk_w32(eth, reg, QTX_CFG(id));
-+}
-+#endif
-+
-+static int mtk_init_foe_table(struct mtk_eth *eth)
-+{
-+	if (eth->foe_table)
-+		return 0;
-+
-+	eth->foe_flow_table = devm_kcalloc(eth->dev, MTK_PPE_ENTRY_CNT,
-+					   sizeof(*eth->foe_flow_table),
-+					   GFP_KERNEL);
-+	if (!eth->foe_flow_table)
-+		return -EINVAL;
-+
-+	/* map the FOE table */
-+	eth->foe_table = dmam_alloc_coherent(eth->dev, MTK_PPE_TBL_SZ,
-+					     &eth->foe_table_phys, GFP_KERNEL);
-+	if (!eth->foe_table) {
-+		dev_err(eth->dev, "failed to allocate foe table\n");
-+		kfree(eth->foe_flow_table);
-+		return -ENOMEM;
-+	}
-+
-+
-+	return 0;
-+}
-+
-+static int mtk_ppe_start(struct mtk_eth *eth)
-+{
-+	u32 foe_mib_tb_sz;
-+	u32 foe_etry_num = MTK_PPE_ENTRY_CNT;
-+
-+	int ret;
-+
-+	ret = mtk_init_foe_table(eth);
-+	if (ret)
-+		return ret;
-+
-+	/* tell the PPE about the tables base address */
-+	mtk_w32(eth, eth->foe_table_phys, MTK_REG_PPE_TB_BASE);
-+
-+	/* flush the table */
-+	memset(eth->foe_table, 0, MTK_PPE_TBL_SZ);
-+
-+	eth->per_flow_accounting = false; //true;
-+
-+	if (eth->per_flow_accounting) {
-+		foe_mib_tb_sz = foe_etry_num * sizeof(struct mib_entry);
-+		eth->foe_mib_cpu = dma_alloc_coherent(eth->dev, foe_mib_tb_sz,
-+						       &eth->foe_mib_dev, GFP_KERNEL);
-+		if (!eth->foe_mib_cpu)
-+			return -1;
-+		mtk_w32(eth, eth->foe_mib_dev, MTK_REG_PPE_MIB_TB_BASE);
-+		memset(eth->foe_mib_cpu, 0, foe_mib_tb_sz);
-+
-+		eth->acct =
-+			kzalloc(foe_etry_num * sizeof(struct hnat_accounting),
-+				GFP_KERNEL);
-+			if (!eth->acct)
-+				return -1;
-+	}
-+
-+	/* setup hashing */
-+	mtk_m32(eth,
-+		MTK_PPE_TB_CFG_HASH_MODE_MASK | MTK_PPE_TB_CFG_TBL_SZ_MASK,
-+		MTK_PPE_TB_CFG_HASH_MODE1 | MTK_PPE_TB_CFG_TBL_SZ_4K,
-+		MTK_REG_PPE_TB_CFG);
-+
-+	/* set the default hashing seed */
-+	mtk_w32(eth, MTK_PPE_HASH_SEED, MTK_REG_PPE_HASH_SEED);
-+
-+	/* each foe entry is 80bytes and is setup by cpu forwarding*/
-+	mtk_m32(eth, MTK_PPE_CAH_CTRL_X_MODE | MTK_PPE_TB_CFG_ENTRY_SZ_MASK |
-+		MTK_PPE_TB_CFG_SMA_MASK,
-+		MTK_PPE_TB_CFG_ENTRY_SZ_64B |  MTK_PPE_TB_CFG_SMA_FWD_CPU,
-+		MTK_REG_PPE_TB_CFG);
-+
-+	/* set ip proto */
-+	//writel(0xFFFFFFFF, host->ppe_base + PPE_IP_PROT_CHK);
-+	mtk_w32(eth, 0xFFFFFFFF, MTK_REG_PPE_IP_PROT_CHK);
-+
-+	/* setup caching */
-+	// cr_set_field(host->ppe_base + PPE_CAH_CTRL, CAH_X_MODE, 1);
-+	mtk_m32(eth, 1, MTK_PPE_CAH_CTRL_X_MODE, MTK_REG_PPE_CAH_CTRL);
-+	// cr_set_field(host->ppe_base + PPE_CAH_CTRL, CAH_X_MODE, 0);
-+	mtk_m32(eth, 0, MTK_PPE_CAH_CTRL_X_MODE, MTK_REG_PPE_CAH_CTRL);
-+	// cr_set_field(host->ppe_base + PPE_CAH_CTRL, CAH_EN, 1);
-+	mtk_m32(eth, MTK_PPE_CAH_CTRL_X_MODE, MTK_PPE_CAH_CTRL_EN,
-+		MTK_REG_PPE_CAH_CTRL);
-+
-+	/* enable FOE */
-+	/*  cr_set_bits(host->ppe_base + PPE_FLOW_CFG,
-+                    BIT_UDP_IP4F_NAT_EN | BIT_IPV4_NAT_EN | BIT_IPV4_NAPT_EN |
-+                    BIT_IPV4_NAT_FRAG_EN | BIT_IPV4_HASH_GREK |
-+                    BIT_IPV4_DSL_EN | BIT_IPV6_6RD_EN |
-+                    BIT_IPV6_3T_ROUTE_EN | BIT_IPV6_5T_ROUTE_EN); */
-+	mtk_m32(eth, 0, MTK_PPE_FLOW_CFG_IPV4_NAT_FRAG_EN |
-+		MTK_PPE_FLOW_CFG_IPV4_NAPT_EN | MTK_PPE_FLOW_CFG_IPV4_NAT_EN |
-+		MTK_PPE_FLOW_CFG_IPV4_GREK_EN,
-+		MTK_REG_PPE_FLOW_CFG);
-+
-+	mtk_w32(eth, 0x000a7780, MTK_REG_PPE_FLOW_CFG);
-+
-+	/* setup flow entry un/bind aging */
-+        // cr_set_field(host->ppe_base + PPE_TB_CFG, NTU_AGE, 1);
-+        // cr_set_field(host->ppe_base + PPE_TB_CFG, UNBD_AGE, 1);
-+        // cr_set_field(host->ppe_base + PPE_TB_CFG, TCP_AGE, 1);
-+        // cr_set_field(host->ppe_base + PPE_TB_CFG, UDP_AGE, 1);
-+        // cr_set_field(host->ppe_base + PPE_TB_CFG, FIN_AGE, 1);
-+	mtk_m32(eth, 0,
-+		MTK_PPE_TB_CFG_UNBD_AGE | MTK_PPE_TB_CFG_NTU_AGE |
-+		MTK_PPE_TB_CFG_FIN_AGE | MTK_PPE_TB_CFG_UDP_AGE |
-+		MTK_PPE_TB_CFG_TCP_AGE,
-+		MTK_REG_PPE_TB_CFG);
-+
-+	// cr_set_field(host->ppe_base + PPE_UNB_AGE, UNB_MNP, 1000);
-+	// cr_set_field(host->ppe_base + PPE_UNB_AGE, UNB_DLTA, 3);
-+	mtk_m32(eth, MTK_PPE_UNB_AGE_MNP_MASK | MTK_PPE_UNB_AGE_DLTA_MASK,
-+		MTK_PPE_UNB_AGE_MNP | MTK_PPE_UNB_AGE_DLTA,
-+		MTK_REG_PPE_UNB_AGE);
-+
-+	// cr_set_field(host->ppe_base + PPE_BND_AGE_0, UDP_DLTA, 12);
-+	// cr_set_field(host->ppe_base + PPE_BND_AGE_0, NTU_DLTA, 1);
-+	mtk_m32(eth, MTK_PPE_BND_AGE0_NTU_DLTA_MASK |
-+		MTK_PPE_BND_AGE0_UDP_DLTA_MASK,
-+		MTK_PPE_BND_AGE0_NTU_DLTA | MTK_PPE_BND_AGE0_UDP_DLTA,
-+		MTK_REG_PPE_BND_AGE0);
-+	mtk_w32(eth, 0x0001000c, MTK_REG_PPE_BND_AGE0);
-+
-+	// cr_set_field(host->ppe_base + PPE_BND_AGE_1, FIN_DLTA, 1);
-+	// cr_set_field(host->ppe_base + PPE_BND_AGE_1, TCP_DLTA, 7);
-+	mtk_m32(eth, MTK_PPE_BND_AGE1_FIN_DLTA_MASK |
-+		MTK_PPE_BND_AGE1_TCP_DLTA_MASK,
-+		MTK_PPE_BND_AGE1_FIN_DLTA | MTK_PPE_BND_AGE1_TCP_DLTA,
-+		MTK_REG_PPE_BND_AGE1);
-+	mtk_w32(eth, 0x00010007, MTK_REG_PPE_BND_AGE1);
-+
-+	/* setup flow entry keep alive */
-+	// cr_set_field(host->ppe_base + PPE_TB_CFG, SCAN_MODE, 2);
-+	// cr_set_field(host->ppe_base + PPE_TB_CFG, KA_CFG, 3);
-+	mtk_m32(eth, MTK_PPE_TB_CFG_KA_MASK | MTK_PPE_TB_CFG_SCAN_MODE_MASK,
-+		MTK_PPE_TB_CFG_KA | MTK_PPE_TB_CFG_SCAN_MODE,
-+		MTK_REG_PPE_TB_CFG);
-+	// cr_set_field(host->ppe_base + PPE_KA, KA_T, 1);
-+	// cr_set_field(host->ppe_base + PPE_KA, TCP_KA, 1);
-+	// cr_set_field(host->ppe_base + PPE_KA, UDP_KA, 1);
-+	mtk_w32(eth, MTK_PPE_KA_UDP | MTK_PPE_KA_TCP | MTK_PPE_KA_T, MTK_REG_PPE_KA);
-+
-+	/* setup flow entry rate limit */
-+	mtk_w32(eth, (0x3fff << 16) | 0x3fff, MTK_REG_PPE_BIND_LMT_0);
-+	mtk_w32(eth, 0x2000000 | MTK_PPE_NTU_KA | 0x3fff, MTK_REG_PPE_BIND_LMT_1);
-+	/* 30 packets per second */
-+	mtk_m32(eth, MTK_PPE_BNDR_RATE_MASK, 0x1e, MTK_REG_PPE_BNDR);
-+
-+	/* enable the PPE */
-+	mtk_m32(eth, 0, MTK_PPE_GLO_CFG_EN, MTK_REG_PPE_GLO_CFG);
-+
-+	/* set the default forwarding port to PDMA */
-+	mtk_w32(eth, 0x0, MTK_REG_PPE_DFT_CPORT);
-+
-+	/* disallow packets with TTL=0 */
-+	mtk_m32(eth, 0, MTK_PPE_GLO_CFG_TTL0_DROP, MTK_REG_PPE_GLO_CFG);
-+
-+        /*enable ppe mib counter*/
-+	if (eth->per_flow_accounting) {
-+		mtk_w32(eth, 0x3, MTK_REG_PPE_MIB_CFG);
-+		mtk_w32(eth, 0x3, MTK_REG_PPE_MIB_CAH_CTRL);
-+	}
-+
-+	/* send all traffic from gmac to the ppe */
-+	mtk_m32(eth, 0xffff, 0x4444, MTK_GDMA_FWD_CFG(0));
-+	mtk_m32(eth, 0xffff, 0x4444, MTK_GDMA_FWD_CFG(1));
-+
-+	mtk_w32(eth, 0x00027fb4, MTK_REG_PPE_TB_CFG);
-+
-+	dev_info(eth->dev, "PPE started\n");
-+
-+#ifdef CONFIG_NET_MEDIATEK_HW_QOS
-+	mtk_ppe_scheduler(eth, 0, 500000);
-+	mtk_ppe_scheduler(eth, 1, 500000);
-+	mtk_ppe_queue(eth, 0, 0, 7, 32, 250000, 0);
-+	mtk_ppe_queue(eth, 1, 0, 7, 32, 250000, 0);
-+	mtk_ppe_queue(eth, 8, 1, 7, 32, 250000, 0);
-+	mtk_ppe_queue(eth, 9, 1, 7, 32, 250000, 0);
-+#endif
-+
-+	return 0;
-+}
-+
-+static int mtk_ppe_busy_wait(struct mtk_eth *eth)
-+{
-+	unsigned long t_start = jiffies;
-+	u32 r = 0;
-+
-+	while (1) {
-+		r = mtk_r32(eth, MTK_REG_PPE_GLO_CFG);
-+		if (!(r & MTK_PPE_GLO_CFG_BUSY))
-+			return 0;
-+		if (time_after(jiffies, t_start + HZ))
-+			break;
-+		usleep_range(10, 20);
-+	}
-+
-+	dev_err(eth->dev, "ppe: table busy timeout - resetting\n");
-+	reset_control_reset(eth->rst_ppe);
-+
-+	return -ETIMEDOUT;
-+}
-+
-+static int mtk_ppe_stop(struct mtk_eth *eth)
-+{
-+	u32 r1 = 0, r2 = 0;
-+	int i;
-+
-+	/* discard all traffic while we disable the PPE */
-+	mtk_m32(eth, 0xffff, 0x7777, MTK_GDMA_FWD_CFG(0));
-+	mtk_m32(eth, 0xffff, 0x7777, MTK_GDMA_FWD_CFG(1));
-+
-+	if (mtk_ppe_busy_wait(eth))
-+		return -ETIMEDOUT;
-+
-+	/* invalidate all flow table entries */
-+	for (i = 0; i < MTK_PPE_ENTRY_CNT; i++)
-+		eth->foe_table[i].bfib1.state = FOE_STATE_INVALID;
-+
-+	/* disable caching */
-+	mtk_m32(eth, 0, MTK_PPE_CAH_CTRL_X_MODE, MTK_REG_PPE_CAH_CTRL);
-+	mtk_m32(eth, MTK_PPE_CAH_CTRL_X_MODE | MTK_PPE_CAH_CTRL_EN, 0,
-+		MTK_REG_PPE_CAH_CTRL);
-+
-+	/* flush cache has to be ahead of hnat diable --*/
-+	mtk_m32(eth, MTK_PPE_GLO_CFG_EN, 0, MTK_REG_PPE_GLO_CFG);
-+
-+	/* disable FOE */
-+	mtk_m32(eth,
-+		MTK_PPE_FLOW_CFG_IPV4_NAT_FRAG_EN |
-+		MTK_PPE_FLOW_CFG_IPV4_NAPT_EN | MTK_PPE_FLOW_CFG_IPV4_NAT_EN |
-+		MTK_PPE_FLOW_CFG_FUC_FOE | MTK_PPE_FLOW_CFG_FMC_FOE,
-+		0, MTK_REG_PPE_FLOW_CFG);
-+
-+	/* disable FOE aging */
-+	mtk_m32(eth, 0,
-+		MTK_PPE_TB_CFG_FIN_AGE | MTK_PPE_TB_CFG_UDP_AGE |
-+		MTK_PPE_TB_CFG_TCP_AGE | MTK_PPE_TB_CFG_UNBD_AGE |
-+		MTK_PPE_TB_CFG_NTU_AGE, MTK_REG_PPE_TB_CFG);
-+
-+	r1 = mtk_r32(eth, 0x100);
-+	r2 = mtk_r32(eth, 0x10c);
-+
-+	dev_info(eth->dev, "0x100 = 0x%x, 0x10c = 0x%x\n", r1, r2);
-+
-+	if (((r1 & 0xff00) >> 0x8) >= (r1 & 0xff) ||
-+	    ((r1 & 0xff00) >> 0x8) >= (r2 & 0xff)) {
-+		dev_info(eth->dev, "reset pse\n");
-+		mtk_w32(eth, 0x1, 0x4);
-+	}
-+
-+	/* set the foe entry base address to 0 */
-+	mtk_w32(eth, 0, MTK_REG_PPE_TB_BASE);
-+
-+	if (mtk_ppe_busy_wait(eth))
-+		return -ETIMEDOUT;
-+
-+	/* send all traffic back to the DMA engine */
-+	mtk_m32(eth, 0xffff, 0x0, MTK_GDMA_FWD_CFG(0));
-+	mtk_m32(eth, 0xffff, 0x0, MTK_GDMA_FWD_CFG(1));
-+	return 0;
-+}
-+
-+static void mtk_offload_keepalive(struct mtk_eth *eth, unsigned int hash)
-+{
-+	struct flow_offload *flow;
-+
-+	rcu_read_lock();
-+	flow = rcu_dereference(eth->foe_flow_table[hash]);
-+	if (flow)
-+		flow->timeout = jiffies + 30 * HZ;
-+	rcu_read_unlock();
-+}
-+
-+int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4)
-+{
-+	unsigned int hash;
-+
-+	switch (FIELD_GET(MTK_RXD4_CPU_REASON, rxd4)) {
-+	case MTK_CPU_REASON_KEEPALIVE_UC_OLD_HDR:
-+	case MTK_CPU_REASON_KEEPALIVE_MC_NEW_HDR:
-+	case MTK_CPU_REASON_KEEPALIVE_DUP_OLD_HDR:
-+		hash = FIELD_GET(MTK_RXD4_FOE_ENTRY, rxd4);
-+		mtk_offload_keepalive(eth, hash);
-+		return -1;
-+	case MTK_CPU_REASON_PACKET_SAMPLING:
-+		return -1;
-+	default:
-+		return 0;
-+	}
-+}
-+
-+int mtk_ppe_probe(struct mtk_eth *eth)
-+{
-+	int err;
-+
-+	err = mtk_ppe_start(eth);
-+	if (err)
-+		return err;
-+
-+	err = mtk_ppe_debugfs_init(eth);
-+	if (err)
-+		return err;
-+
-+	return 0;
-+}
-+
-+void mtk_ppe_remove(struct mtk_eth *eth)
-+{
-+	mtk_ppe_stop(eth);
-+}
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_offload.h
-@@ -0,0 +1,298 @@
-+/*   This program is free software; you can redistribute it and/or modify
-+ *   it under the terms of the GNU General Public License as published by
-+ *   the Free Software Foundation; version 2 of the License
-+ *
-+ *   This program is distributed in the hope that it will be useful,
-+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *   GNU General Public License for more details.
-+ *
-+ *   Copyright (C) 2014-2016 Sean Wang <sean.wang at mediatek.com>
-+ *   Copyright (C) 2016-2017 John Crispin <blogic at openwrt.org>
-+ */
-+
-+#include <linux/dma-mapping.h>
-+#include <linux/delay.h>
-+#include <linux/if.h>
-+#include <linux/io.h>
-+#include <linux/module.h>
-+#include <linux/of_device.h>
-+#include <linux/platform_device.h>
-+#include <linux/reset.h>
-+#include <linux/netfilter.h>
-+#include <linux/netdevice.h>
-+#include <net/netfilter/nf_flow_table.h>
-+#include <linux/debugfs.h>
-+#include <linux/etherdevice.h>
-+#include <linux/bitfield.h>
-+
-+#include "mtk_eth_soc.h"
-+
-+#ifdef CONFIG_RALINK
-+/* ramips compat */
-+#define mtk_eth					fe_priv
-+#define MTK_GDMA_FWD_CFG(x)			(0x500 + (x * 0x1000))
-+#define mtk_m32					fe_m32
-+
-+static inline u32
-+mtk_r32(struct mtk_eth *eth, u32 reg)
-+{
-+	return fe_r32(reg);
-+}
-+
-+static inline void
-+mtk_w32(struct mtk_eth *eth, u32 val, u32 reg)
-+{
-+	fe_w32(val, reg);
-+}
-+#endif
-+
-+#define MTK_REG_PPE_GLO_CFG			0xe00
-+#define   MTK_PPE_GLO_CFG_BUSY			BIT(31)
-+#define   MTK_PPE_GLO_CFG_TTL0_DROP		BIT(4)
-+#define   MTK_PPE_GLO_CFG_EN			BIT(0)
-+
-+#define MTK_REG_PPE_FLOW_CFG			0xe04
-+#define   MTK_PPE_FLOW_CFG_IPV4_GREK_EN		BIT(19)
-+#define   MTK_PPE_FLOW_CFG_IPV4_NAT_FRAG_EN	BIT(17)
-+#define   MTK_PPE_FLOW_CFG_IPV4_NAPT_EN		BIT(13)
-+#define   MTK_PPE_FLOW_CFG_IPV4_NAT_EN		BIT(12)
-+#define   MTK_PPE_FLOW_CFG_FUC_FOE		BIT(2)
-+#define   MTK_PPE_FLOW_CFG_FMC_FOE		BIT(1)
-+
-+#define MTK_REG_PPE_IP_PROT_CHK			0xe08
-+
-+#define MTK_REG_PPE_TB_BASE			0xe20
-+
-+#define MTK_REG_PPE_BNDR			0xe28
-+#define   MTK_PPE_BNDR_RATE_MASK		0xffff
-+
-+#define MTK_REG_PPE_BIND_LMT_0			0xe2C
-+
-+#define MTK_REG_PPE_BIND_LMT_1			0xe30
-+#define   MTK_PPE_NTU_KA			BIT(16)
-+
-+#define MTK_REG_PPE_KA				0xe34
-+#define   MTK_PPE_KA_T				BIT(0)
-+#define   MTK_PPE_KA_TCP			BIT(16)
-+#define   MTK_PPE_KA_UDP			BIT(24)
-+
-+#define MTK_REG_PPE_UNB_AGE			0xe38
-+#define   MTK_PPE_UNB_AGE_MNP_MASK		(0xffff << 16)
-+#define   MTK_PPE_UNB_AGE_MNP			(1000 << 16)
-+#define   MTK_PPE_UNB_AGE_DLTA_MASK		0xff
-+#define   MTK_PPE_UNB_AGE_DLTA			3
-+
-+#define MTK_REG_PPE_BND_AGE0			0xe3c
-+#define   MTK_PPE_BND_AGE0_NTU_DLTA_MASK	(0xffff << 16)
-+#define   MTK_PPE_BND_AGE0_NTU_DLTA		(5 << 16)
-+#define   MTK_PPE_BND_AGE0_UDP_DLTA_MASK	0xffff
-+#define   MTK_PPE_BND_AGE0_UDP_DLTA		5
-+
-+#define MTK_REG_PPE_BND_AGE1			0xe40
-+#define   MTK_PPE_BND_AGE1_FIN_DLTA_MASK	(0xffff << 16)
-+#define   MTK_PPE_BND_AGE1_FIN_DLTA		(5 << 16)
-+#define   MTK_PPE_BND_AGE1_TCP_DLTA_MASK	0xffff
-+#define   MTK_PPE_BND_AGE1_TCP_DLTA		5
-+
-+#define MTK_REG_PPE_DFT_CPORT			0xe48
-+
-+#define MTK_REG_PPE_TB_CFG			0xe1c
-+#define   MTK_PPE_TB_CFG_X_MODE_MASK		(3 << 18)
-+#define   MTK_PPE_TB_CFG_HASH_MODE1		BIT(14)
-+#define   MTK_PPE_TB_CFG_HASH_MODE_MASK		(0x3 << 14)
-+#define   MTK_PPE_TB_CFG_KA			(3 << 12)
-+#define   MTK_PPE_TB_CFG_KA_MASK		(0x3 << 12)
-+#define   MTK_PPE_TB_CFG_SCAN_MODE		(2 << 16)
-+#define   MTK_PPE_TB_CFG_SCAN_MODE_MASK		(0x3 << 16)
-+#define   MTK_PPE_TB_CFG_FIN_AGE		BIT(11)
-+#define   MTK_PPE_TB_CFG_UDP_AGE		BIT(10)
-+#define   MTK_PPE_TB_CFG_TCP_AGE		BIT(9)
-+#define   MTK_PPE_TB_CFG_UNBD_AGE		BIT(8)
-+#define   MTK_PPE_TB_CFG_NTU_AGE		BIT(7)
-+#define   MTK_PPE_TB_CFG_SMA_FWD_CPU		(0x3 << 4)
-+#define   MTK_PPE_TB_CFG_SMA_MASK		(0x3 << 4)
-+#define   MTK_PPE_TB_CFG_ENTRY_SZ_64B		0
-+#define   MTK_PPE_TB_CFG_ENTRY_SZ_80B		1
-+#define   MTK_PPE_TB_CFG_ENTRY_SZ_MASK		BIT(3)
-+#define   MTK_PPE_TB_CFG_TBL_SZ_4K		4
-+#define   MTK_PPE_TB_CFG_TBL_SZ_MASK		0x7
-+
-+#define MTK_REG_PPE_HASH_SEED			0xe44
-+#define   MTK_PPE_HASH_SEED			0x12345678
-+
-+
-+#define MTK_REG_PPE_CAH_CTRL			0xf20
-+#define   MTK_PPE_CAH_CTRL_X_MODE		BIT(9)
-+#define   MTK_PPE_CAH_CTRL_EN			BIT(0)
-+
-+#define MTK_REG_PPE_MIB_CFG			0xf34
-+#define MTK_REG_PPE_MIB_TB_BASE			0xf38
-+#define MTK_REG_PPE_MIB_CAH_CTRL		0Xf50
-+
-+
-+struct mtk_foe_unbind_info_blk {
-+	u32 time_stamp:8;
-+	u32 pcnt:16;		/* packet count */
-+	u32 preb:1;
-+	u32 pkt_type:3;
-+	u32 state:2;
-+	u32 udp:1;
-+	u32 sta:1;		/* static entry */
-+} __attribute__ ((packed));
-+
-+struct mtk_foe_bind_info_blk {
-+	u32 time_stamp:15;
-+	u32 ka:1;		/* keep alive */
-+	u32 vlan_layer:3;
-+	u32 psn:1;		/* egress packet has PPPoE session */
-+#ifdef CONFIG_RALINK
-+	u32 vpm:2;		/* 0:ethertype remark, 1:0x8100(CR default) */
-+#else
-+	u32 vpm:1;		/* 0:ethertype remark, 1:0x8100(CR default) */
-+	u32 ps:1;		/* packet sampling */
-+#endif
-+	u32 cah:1;		/* cacheable flag */
-+	u32 rmt:1;		/* remove tunnel ip header (6rd/dslite only) */
-+	u32 ttl:1;
-+	u32 pkt_type:3;
-+	u32 state:2;
-+	u32 udp:1;
-+	u32 sta:1;		/* static entry */
-+} __attribute__ ((packed));
-+
-+struct mtk_foe_info_blk2 {
-+	u32 qid:4;		/* QID in Qos Port */
-+	u32 fqos:1;		/* force to PSE QoS port */
-+	u32 dp:3;		/* force to PSE port x 
-+				 0:PSE,1:GSW, 2:GMAC,4:PPE,5:QDMA,7=DROP */
-+	u32 mcast:1;		/* multicast this packet to CPU */
-+	u32 pcpl:1;		/* OSBN */
-+	u32 mlen:1;		/* 0:post 1:pre packet length in meter */
-+	u32 alen:1;		/* 0:post 1:pre packet length in accounting */
-+	u32 port_mg:6;		/* port meter group */
-+	u32 port_ag:6;		/* port account group */
-+	u32 dscp:8;		/* DSCP value */
-+} __attribute__ ((packed));
-+
-+/* info blk2 for WHNAT */
-+struct hnat_info_blk2_whnat {
-+	u32 qid : 4;		/* QID[3:0] in Qos Port */
-+	u32 fqos : 1;		/* force to PSE QoS port */
-+	u32 dp : 3;		/* force to PSE port x
-+				 * 0:PSE,1:GSW, 2:GMAC,4:PPE,5:QDMA,7=DROP
-+				 */
-+	u32 mcast : 1;		/* multicast this packet to CPU */
-+	u32 pcpl : 1;		/* OSBN */
-+	u32 mibf : 1;		/* 0:off 1:on PPE MIB counter */
-+	u32 alen : 1;		/* 0:post 1:pre packet length in accounting */
-+	u32 qid2 : 2;		/* QID[5:4] in Qos Port */
-+	u32 resv : 2;
-+	u32 wdmaid : 1;		/* 0:to pcie0 dev 1:to pcie1 dev */
-+	u32 winfoi : 1;		/* 0:off 1:on Wi-Fi hwnat support */
-+	u32 port_ag : 6;	/* port account group */
-+	u32 dscp : 8;		/* DSCP value */
-+} __attribute__ ((packed));
-+
-+struct hnat_winfo {
-+	u32 bssid : 6;		/* WiFi Bssidx */
-+	u32 wcid : 8;		/* WiFi wtable Idx */
-+	u32 rxid : 2;		/* WiFi Ring idx */
-+} __attribute__ ((packed));
-+
-+struct mtk_foe_ipv4_hnapt {
-+	union {
-+		struct mtk_foe_bind_info_blk bfib1;
-+		struct mtk_foe_unbind_info_blk udib1;
-+		u32 info_blk1;
-+	};
-+	u32 sip;
-+	u32 dip;
-+	u16 dport;
-+	u16 sport;
-+	union {
-+		struct mtk_foe_info_blk2 iblk2;
-+		struct hnat_info_blk2_whnat iblk2w;
-+		u32 info_blk2;
-+	};
-+	u32 new_sip;
-+	u32 new_dip;
-+	u16 new_dport;
-+	u16 new_sport;
-+	u32 resv1;
-+	u32 resv2;
-+	u32 resv3:26;
-+	u32 act_dp:6;		/* UDF */
-+	u16 vlan1;
-+	u16 etype;
-+	u32 dmac_hi;
-+	union {
-+		struct hnat_winfo winfo;
-+		u16 vlan2;
-+	};
-+	u16 dmac_lo;
-+	u32 smac_hi;
-+	u16 pppoe_id;
-+	u16 smac_lo;
-+} __attribute__ ((packed));
-+
-+struct mtk_foe_entry {
-+	union {
-+		struct mtk_foe_unbind_info_blk udib1;
-+		struct mtk_foe_bind_info_blk bfib1;
-+		struct mtk_foe_ipv4_hnapt ipv4_hnapt;
-+	};
-+};
-+
-+enum mtk_foe_entry_state {
-+	FOE_STATE_INVALID = 0,
-+	FOE_STATE_UNBIND = 1,
-+	FOE_STATE_BIND = 2,
-+	FOE_STATE_FIN = 3
-+};
-+
-+
-+#define MTK_RXD4_FOE_ENTRY		GENMASK(13, 0)
-+#define MTK_RXD4_CPU_REASON		GENMASK(18, 14)
-+#define MTK_RXD4_SRC_PORT		GENMASK(21, 19)
-+#define MTK_RXD4_ALG			GENMASK(31, 22)
-+
-+enum mtk_foe_cpu_reason {
-+	MTK_CPU_REASON_TTL_EXCEEDED		= 0x02,
-+	MTK_CPU_REASON_OPTION_HEADER		= 0x03,
-+	MTK_CPU_REASON_NO_FLOW			= 0x07,
-+	MTK_CPU_REASON_IPV4_FRAG		= 0x08,
-+	MTK_CPU_REASON_IPV4_DSLITE_FRAG		= 0x09,
-+	MTK_CPU_REASON_IPV4_DSLITE_NO_TCP_UDP	= 0x0a,
-+	MTK_CPU_REASON_IPV6_6RD_NO_TCP_UDP	= 0x0b,
-+	MTK_CPU_REASON_TCP_FIN_SYN_RST		= 0x0c,
-+	MTK_CPU_REASON_UN_HIT			= 0x0d,
-+	MTK_CPU_REASON_HIT_UNBIND		= 0x0e,
-+	MTK_CPU_REASON_HIT_UNBIND_RATE_REACHED	= 0x0f,
-+	MTK_CPU_REASON_HIT_BIND_TCP_FIN		= 0x10,
-+	MTK_CPU_REASON_HIT_TTL_1		= 0x11,
-+	MTK_CPU_REASON_HIT_BIND_VLAN_VIOLATION	= 0x12,
-+	MTK_CPU_REASON_KEEPALIVE_UC_OLD_HDR	= 0x13,
-+	MTK_CPU_REASON_KEEPALIVE_MC_NEW_HDR	= 0x14,
-+	MTK_CPU_REASON_KEEPALIVE_DUP_OLD_HDR	= 0x15,
-+	MTK_CPU_REASON_HIT_BIND_FORCE_CPU	= 0x16,
-+	MTK_CPU_REASON_TUNNEL_OPTION_HEADER	= 0x17,
-+	MTK_CPU_REASON_MULTICAST_TO_CPU		= 0x18,
-+	MTK_CPU_REASON_MULTICAST_TO_GMAC1_CPU	= 0x19,
-+	MTK_CPU_REASON_HIT_PRE_BIND		= 0x1a,
-+	MTK_CPU_REASON_PACKET_SAMPLING		= 0x1b,
-+	MTK_CPU_REASON_EXCEED_MTU		= 0x1c,
-+	MTK_CPU_REASON_PPE_BYPASS		= 0x1e,
-+	MTK_CPU_REASON_INVALID			= 0x1f,
-+};
-+
-+
-+/* our table size is 4K */
-+#define MTK_PPE_ENTRY_CNT		0x4000
-+#define MTK_PPE_TBL_SZ			\
-+			(MTK_PPE_ENTRY_CNT * sizeof(struct mtk_foe_entry))
-+
-+int mtk_ppe_debugfs_init(struct mtk_eth *eth);
-+
-+
-+
diff --git a/target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch b/target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch
index ed9aecdf74..18ddd0863e 100644
--- a/target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch
+++ b/target/linux/mediatek/patches-5.4/1011-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch
@@ -37,12 +37,13 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  #include <linux/mfd/syscon.h>
  #include <linux/regmap.h>
  #include <linux/clk.h>
-@@ -2486,6 +2487,12 @@ static int mtk_hw_init(struct mtk_eth *e
+@@ -2482,6 +2483,13 @@ static int mtk_hw_init(struct mtk_eth *e
  	if (ret)
  		goto err_disable_pm;
  
 +	if (of_dma_is_coherent(eth->dev->of_node)) {
-+		u32 mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA;
++		u32 mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
++			   ETHSYS_DMA_AG_MAP_PPE;
 +
 +		regmap_update_bits(eth->ethsys, ETHSYS_DMA_AG_MAP, mask, mask);
 +	}
@@ -50,7 +51,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  	if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
  		ret = device_reset(eth->dev);
  		if (ret) {
-@@ -3088,6 +3095,16 @@ static int mtk_probe(struct platform_dev
+@@ -3080,6 +3088,16 @@ static int mtk_probe(struct platform_dev
  		}
  	}
  
@@ -69,7 +70,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  					  GFP_KERNEL);
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -426,6 +426,11 @@
+@@ -435,6 +435,12 @@
  #define RSTCTRL_FE		BIT(6)
  #define RSTCTRL_PPE		BIT(31)
  
@@ -77,6 +78,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 +#define ETHSYS_DMA_AG_MAP	0x408
 +#define ETHSYS_DMA_AG_MAP_PDMA	BIT(0)
 +#define ETHSYS_DMA_AG_MAP_QDMA	BIT(1)
++#define ETHSYS_DMA_AG_MAP_PPE	BIT(2)
 +
  /* SGMII subsystem config registers */
  /* Register to auto-negotiation restart */
diff --git a/target/linux/ramips/patches-5.4/401-net-ethernet-mediatek-support-net-labels.patch b/target/linux/ramips/patches-5.4/401-net-ethernet-mediatek-support-net-labels.patch
index 39989ecca1..babd3d833c 100644
--- a/target/linux/ramips/patches-5.4/401-net-ethernet-mediatek-support-net-labels.patch
+++ b/target/linux/ramips/patches-5.4/401-net-ethernet-mediatek-support-net-labels.patch
@@ -14,7 +14,7 @@ Signed-off-by: René van Dorst <opensource at vdorst.com>
 
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -2887,6 +2887,7 @@ static const struct net_device_ops mtk_n
+@@ -2919,6 +2919,7 @@ static const struct net_device_ops mtk_n
  
  static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
  {
@@ -22,7 +22,7 @@ Signed-off-by: René van Dorst <opensource at vdorst.com>
  	const __be32 *_id = of_get_property(np, "reg", NULL);
  	struct phylink *phylink;
  	int phy_mode, id, err;
-@@ -2979,6 +2980,9 @@ static int mtk_add_mac(struct mtk_eth *e
+@@ -3011,6 +3012,9 @@ static int mtk_add_mac(struct mtk_eth *e
  
  	eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
  



More information about the lede-commits mailing list