[][openwrt][mt7988][tops][support v1.1.0]

[Description]
Add support to TOPS v1.1.0 with latest driver and firmware
1. Support TOPS mini network stack
   Refactor codes to improve the reusability of tunnel parameter fetching
   process.
2. Change flow table's tunnel arrangement to align with TOPS mini network
   stack
3. TOPS firmware support reorder Tx/Rx process
   Add some flexibilities to support more applications.
4. Update debug commands
   4.1 Add command to show registered tunnel parameters
   4.2 Update command to register debug tunnel parameters
5. Fix minor bugs
   5.1 coding defects
   5.2 tunnel binding mechanism bugs
6. Support tunnel protocol
   6.1 L2oGRE

[Release-log]
N/A

Change-Id: Idd6b6e0fb0495d21062ededd8b010dc14af317ff
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/8319846
diff --git a/package-21.02/kernel/tops/Config-protocols.in b/package-21.02/kernel/tops/Config-protocols.in
index ea06d87..a226263 100644
--- a/package-21.02/kernel/tops/Config-protocols.in
+++ b/package-21.02/kernel/tops/Config-protocols.in
@@ -24,13 +24,13 @@
 	bool
 	default n
 
-config MTK_TOPS_UDP_L2TP_DATA
-	bool "Mediatek TOPS UDP L2TP Data HW Offload"
+config MTK_TOPS_L2TP_V2
+	bool "Mediatek TOPS L2TPv2 HW Offload"
 	default y
 	select MTK_TOPS_L2TP
 	select PACKAGE_kmod-l2tp
 	select PACKAGE_xl2tpd
 	help
-	  select y for UDP L2TP data offload by tunnel offload processing system
+	  select y for L2TPv2 offload by tunnel offload processing system
 
 endmenu
diff --git a/package-21.02/kernel/tops/Config.in b/package-21.02/kernel/tops/Config.in
index c550231..c4e14cf 100644
--- a/package-21.02/kernel/tops/Config.in
+++ b/package-21.02/kernel/tops/Config.in
@@ -45,6 +45,10 @@
 	  Configuration for TOPS tunnel map bit. This value should be the log
 	  of TOPS_TNL_NUM.
 
+config TOPS_TNL_TYPE_NUM
+	int
+	default 32 if TARGET_mediatek_mt7988
+
 config MTK_TOPS_SECURE_FW
 	bool "TOPS Secure Firmware Load"
 	default n
diff --git a/package-21.02/kernel/tops/firmware/rebb/mt7988_mgmt/tops-mgmt.img b/package-21.02/kernel/tops/firmware/rebb/mt7988_mgmt/tops-mgmt.img
index 90b8be8..67f42dc 100644
--- a/package-21.02/kernel/tops/firmware/rebb/mt7988_mgmt/tops-mgmt.img
+++ b/package-21.02/kernel/tops/firmware/rebb/mt7988_mgmt/tops-mgmt.img
Binary files differ
diff --git a/package-21.02/kernel/tops/firmware/rebb/mt7988_offload/tops-offload.img b/package-21.02/kernel/tops/firmware/rebb/mt7988_offload/tops-offload.img
index d562bc2..8738b5e 100644
--- a/package-21.02/kernel/tops/firmware/rebb/mt7988_offload/tops-offload.img
+++ b/package-21.02/kernel/tops/firmware/rebb/mt7988_offload/tops-offload.img
Binary files differ
diff --git a/package-21.02/kernel/tops/src/Makefile b/package-21.02/kernel/tops/src/Makefile
index 845f657..86f860d 100644
--- a/package-21.02/kernel/tops/src/Makefile
+++ b/package-21.02/kernel/tops/src/Makefile
@@ -11,6 +11,7 @@
 ccflags-y += -I$(src)/protocol/inc
 
 tops-y += ctrl.o
+tops-y += debugfs.o
 tops-y += firmware.o
 tops-y += init.o
 tops-y += hpdma.o
@@ -19,6 +20,7 @@
 tops-y += mcu.o
 tops-y += netsys.o
 tops-y += net-event.o
+tops-y += tops_params.o
 tops-y += tnl_offload.o
 tops-y += ser.o
 tops-y += tdma.o
@@ -28,7 +30,11 @@
 tops-y += trm.o
 tops-y += wdt.o
 
-tops-$(CONFIG_MTK_TOPS_GRETAP) += protocol/gre/gretap.o
-tops-$(CONFIG_MTK_TOPS_UDP_L2TP_DATA) += protocol/l2tp/udp_l2tp_data.o
+tops-y += protocol/mac/eth.o
+tops-y += protocol/mac/ppp.o
+tops-y += protocol/network/ip.o
+tops-y += protocol/transport/udp.o
+tops-$(CONFIG_MTK_TOPS_GRETAP) += protocol/tunnel/gre/gretap.o
+tops-$(CONFIG_MTK_TOPS_L2TP_V2) += protocol/tunnel/l2tp/l2tpv2.o
 
 include $(wildcard $(src)/*.mk)
diff --git a/package-21.02/kernel/tops/src/ctrl.c b/package-21.02/kernel/tops/src/ctrl.c
index ed1c95b..df41715 100644
--- a/package-21.02/kernel/tops/src/ctrl.c
+++ b/package-21.02/kernel/tops/src/ctrl.c
@@ -7,198 +7,12 @@
 
 #include <linux/device.h>
 
-#include "firmware.h"
-#include "hpdma.h"
-#include "internal.h"
-#include "mcu.h"
-#include "netsys.h"
-#include "trm.h"
-#include "tunnel.h"
-#include "wdt.h"
-
-static const char *tops_role_name[__TOPS_ROLE_TYPE_MAX] = {
-	[TOPS_ROLE_TYPE_MGMT] = "tops-mgmt",
-	[TOPS_ROLE_TYPE_CLUSTER] = "tops-offload",
-};
-
-static ssize_t mtk_tops_fw_info_show(struct device *dev,
-				     struct device_attribute *attr,
-				     char *buf)
-{
-	enum tops_role_type rtype;
-	struct tm tm = {0};
-	const char *value;
-	const char *prop;
-	int len = 0;
-	u32 nattr;
-	u32 i;
-
-	for (rtype = TOPS_ROLE_TYPE_MGMT; rtype < __TOPS_ROLE_TYPE_MAX; rtype++) {
-		mtk_tops_fw_get_built_date(rtype, &tm);
-
-		len += snprintf(buf + len, PAGE_SIZE - len,
-				"%s FW information:\n", tops_role_name[rtype]);
-		len += snprintf(buf + len, PAGE_SIZE - len,
-				"Git revision:\t%llx\n",
-				mtk_tops_fw_get_git_commit_id(rtype));
-		len += snprintf(buf + len, PAGE_SIZE - len,
-				"Build date:\t%04ld/%02d/%02d %02d:%02d:%02d\n",
-				tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
-				tm.tm_hour, tm.tm_min, tm.tm_sec);
-
-		nattr = mtk_tops_fw_attr_get_num(rtype);
-
-		for (i = 0; i < nattr; i++) {
-			prop = mtk_tops_fw_attr_get_property(rtype, i);
-			if (!prop)
-				continue;
-
-			value = mtk_tops_fw_attr_get_value(rtype, prop);
-
-			len += snprintf(buf + len, PAGE_SIZE - len,
-					"%s:\t%s\n", prop, value);
-		}
-		len += snprintf(buf + len, PAGE_SIZE - len, "\n");
-	}
-
-	return len;
-}
-
-static int mtk_tops_ctrl_fetch_port(const char *buf, int *ofs, u16 *port)
-{
-	int nchar = 0;
-	int ret;
-	u16 p = 0;
-
-	ret = sscanf(buf + *ofs, "%hu %n", &p, &nchar);
-	if (ret != 1)
-		return -EPERM;
-
-	*port = htons(p);
-
-	*ofs += nchar;
-
-	return nchar;
-}
-
-static int mtk_tops_ctrl_fetch_ip(const char *buf, int *ofs, u32 *ip)
-{
-	int nchar = 0;
-	int ret = 0;
-	u8 tmp[4];
-
-	ret = sscanf(buf + *ofs, "%hhu.%hhu.%hhu.%hhu %n",
-		&tmp[3], &tmp[2], &tmp[1], &tmp[0], &nchar);
-	if (ret != 4)
-		return -EPERM;
-
-	*ip = tmp[0] | tmp[1] << 8 | tmp[2] << 16 | tmp[3] << 24;
-
-	*ofs += nchar;
-
-	return nchar;
-}
-
-static int mtk_tops_ctrl_fetch_mac(const char *buf, int *ofs, u8 *mac)
-{
-	int nchar = 0;
-	int ret = 0;
-
-	ret = sscanf(buf + *ofs, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %n",
-		&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5], &nchar);
-	if (ret != 6)
-		return -EPERM;
-
-	*ofs += nchar;
-
-	return 0;
-}
-
-static int mtk_tops_ctrl_add_tnl(const char *buf)
-{
-	struct tops_tnl_params tnl_params = {0};
-	struct tops_tnl_info *tnl_info;
-	struct tops_tnl_type *tnl_type;
-	char tnl_type_name[21] = {0};
-	int ofs = 0;
-	int ret = 0;
-
-	ret = sscanf(buf, "%20s %n", tnl_type_name, &ofs);
-	if (ret != 1)
-		return -EPERM;
-
-	tnl_type = mtk_tops_tnl_type_get_by_name(tnl_type_name);
-	if (unlikely(!tnl_type || !tnl_type->tnl_debug_param_setup))
-		return -ENODEV;
-
-	ret = mtk_tops_ctrl_fetch_mac(buf, &ofs, tnl_params.daddr);
-	if (ret < 0)
-		return ret;
-
-	ret = mtk_tops_ctrl_fetch_mac(buf, &ofs, tnl_params.saddr);
-	if (ret < 0)
-		return ret;
-
-	ret = mtk_tops_ctrl_fetch_ip(buf, &ofs, &tnl_params.dip);
-	if (ret < 0)
-		return ret;
-
-	ret = mtk_tops_ctrl_fetch_ip(buf, &ofs, &tnl_params.sip);
-	if (ret < 0)
-		return ret;
-
-	ret = mtk_tops_ctrl_fetch_port(buf, &ofs, &tnl_params.dport);
-	if (ret < 0)
-		return ret;
-
-	ret = mtk_tops_ctrl_fetch_port(buf, &ofs, &tnl_params.sport);
-	if (ret < 0)
-		return ret;
-
-	ret = tnl_type->tnl_debug_param_setup(buf, &ofs, &tnl_params);
-	if (ret < 0)
-		return ret;
-
-	tnl_params.flag |= TNL_DECAP_ENABLE;
-	tnl_params.flag |= TNL_ENCAP_ENABLE;
-	tnl_params.tops_entry_proto = tnl_type->tops_entry;
-
-	tnl_info = mtk_tops_tnl_info_alloc(tnl_type);
-	if (IS_ERR(tnl_info))
-		return -ENOMEM;
-
-	tnl_info->flag |= TNL_INFO_DEBUG;
-	memcpy(&tnl_info->cache, &tnl_params, sizeof(struct tops_tnl_params));
-
-	mtk_tops_tnl_info_hash(tnl_info);
-
-	mtk_tops_tnl_info_submit(tnl_info);
-
-	return 0;
-}
-
-static ssize_t mtk_tops_tnl_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf,
-				  size_t count)
-{
-	char cmd[21] = {0};
-	int nchar = 0;
-	int ret = 0;
-
-	ret = sscanf(buf, "%20s %n", cmd, &nchar);
-
-	if (ret != 1)
-		return -EPERM;
-
-	if (!strcmp(cmd, "NEW_TNL")) {
-		ret = mtk_tops_ctrl_add_tnl(buf + nchar);
-		if (ret)
-			return ret;
-	}
-
-	return count;
-}
+#include "tops/firmware.h"
+#include "tops/internal.h"
+#include "tops/mcu.h"
+#include "tops/trm.h"
+#include "tops/tunnel.h"
+#include "tops/wdt.h"
 
 static int mtk_tops_trm_fetch_setting(const char *buf,
 				      int *ofs,
@@ -283,14 +97,10 @@
 	return count;
 }
 
-static DEVICE_ATTR_RO(mtk_tops_fw_info);
-static DEVICE_ATTR_WO(mtk_tops_tnl);
 static DEVICE_ATTR_WO(mtk_tops_trm);
 static DEVICE_ATTR_WO(mtk_tops_wdt);
 
 static struct attribute *mtk_tops_attributes[] = {
-	&dev_attr_mtk_tops_fw_info.attr,
-	&dev_attr_mtk_tops_tnl.attr,
 	&dev_attr_mtk_tops_trm.attr,
 	&dev_attr_mtk_tops_wdt.attr,
 	NULL,
diff --git a/package-21.02/kernel/tops/src/debugfs.c b/package-21.02/kernel/tops/src/debugfs.c
new file mode 100644
index 0000000..d2c40cf
--- /dev/null
+++ b/package-21.02/kernel/tops/src/debugfs.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 Mediatek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#include <linux/debugfs.h>
+#include <linux/inet.h>
+#include <linux/uaccess.h>
+
+#include "tops/debugfs.h"
+#include "tops/firmware.h"
+#include "tops/internal.h"
+#include "tops/mcu.h"
+#include "tops/trm.h"
+#include "tops/tunnel.h"
+#include "tops/wdt.h"
+
+static const char *tops_role_name[__TOPS_ROLE_TYPE_MAX] = {
+	[TOPS_ROLE_TYPE_MGMT] = "tops-mgmt",
+	[TOPS_ROLE_TYPE_CLUSTER] = "tops-offload",
+};
+
+static struct dentry *tops_fw_debugfs_root;
+
+static int tops_fw_info_show(struct seq_file *s, void *private)
+{
+	enum tops_role_type rtype;
+	struct tm tm = {0};
+	const char *value;
+	const char *prop;
+	u32 nattr;
+	u32 i;
+
+	for (rtype = TOPS_ROLE_TYPE_MGMT; rtype < __TOPS_ROLE_TYPE_MAX; rtype++) {
+		mtk_tops_fw_get_built_date(rtype, &tm);
+
+		seq_printf(s, "%s FW information:\n", tops_role_name[rtype]);
+		seq_printf(s, "Git revision:\t%llx\n",
+			   mtk_tops_fw_get_git_commit_id(rtype));
+		seq_printf(s, "Build date:\t%04ld/%02d/%02d %02d:%02d:%02d\n",
+			   tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+			   tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+		nattr = mtk_tops_fw_attr_get_num(rtype);
+
+		for (i = 0; i < nattr; i++) {
+			prop = mtk_tops_fw_attr_get_property(rtype, i);
+			if (!prop)
+				continue;
+
+			value = mtk_tops_fw_attr_get_value(rtype, prop);
+
+			seq_printf(s, "%s:\t%s\n", prop, value);
+		}
+		seq_puts(s, "\n");
+	}
+
+	return 0;
+}
+
+static int tops_tnl_show(struct seq_file *s, void *private)
+{
+	struct tops_tnl_info *tnl_info;
+	struct tops_tnl_params *tnl_params;
+	u32 i;
+
+	for (i = 0; i < CONFIG_TOPS_TNL_NUM; i++) {
+		tnl_info = mtk_tops_tnl_info_get_by_idx(i);
+		if (IS_ERR(tnl_info))
+			/* tunnel not enabled */
+			continue;
+
+		tnl_params = &tnl_info->tnl_params;
+		if (!tnl_info->tnl_type || !tnl_info->tnl_type->tnl_param_dump)
+			continue;
+
+		seq_printf(s, "Tunnel Index: %02u\n", i);
+
+		mtk_tops_mac_param_dump(s, &tnl_params->params);
+
+		mtk_tops_network_param_dump(s, &tnl_params->params);
+
+		mtk_tops_transport_param_dump(s, &tnl_params->params);
+
+		tnl_info->tnl_type->tnl_param_dump(s, &tnl_params->params);
+
+		seq_printf(s, "\tTOPS Entry: %02u CLS Entry: %02u CDRT: %02u Flag: 0x%x\n",
+			   tnl_params->tops_entry_proto,
+			   tnl_params->cls_entry,
+			   tnl_params->cdrt,
+			   tnl_params->flag);
+	}
+
+	return 0;
+}
+
+static int tops_tnl_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tops_tnl_show, file->private_data);
+}
+
+static int tops_tnl_add_new_tnl(const char *buf)
+{
+	struct tops_tnl_params tnl_params;
+	struct tops_params *params;
+	struct tops_tnl_info *tnl_info;
+	struct tops_tnl_type *tnl_type;
+	char proto[DEBUG_PROTO_LEN];
+	int ofs = 0;
+	int ret = 0;
+
+	memset(&tnl_params, 0, sizeof(struct tops_tnl_params));
+	memset(proto, 0, sizeof(proto));
+
+	params = &tnl_params.params;
+
+	ret = mtk_tops_debug_param_setup(buf, &ofs, params);
+	if (ret)
+		return ret;
+
+	ret = mtk_tops_debug_param_proto_peek(buf, ofs, proto);
+	if (ret < 0)
+		return ret;
+
+	ofs += ret;
+
+	tnl_type = mtk_tops_tnl_type_get_by_name(proto);
+	if (!tnl_type || !tnl_type->tnl_debug_param_setup)
+		return -ENODEV;
+
+	ret = tnl_type->tnl_debug_param_setup(buf, &ofs, params);
+	if (ret < 0)
+		return ret;
+
+	tnl_params.flag |= TNL_DECAP_ENABLE;
+	tnl_params.flag |= TNL_ENCAP_ENABLE;
+	tnl_params.tops_entry_proto = tnl_type->tnl_proto_type;
+
+	tnl_info = mtk_tops_tnl_info_alloc(tnl_type);
+	if (IS_ERR(tnl_info))
+		return -ENOMEM;
+
+	tnl_info->flag |= TNL_INFO_DEBUG;
+	memcpy(&tnl_info->cache, &tnl_params, sizeof(struct tops_tnl_params));
+
+	mtk_tops_tnl_info_hash(tnl_info);
+
+	mtk_tops_tnl_info_submit(tnl_info);
+
+	return 0;
+}
+
+static ssize_t tops_tnl_write(struct file *file, const char __user *buffer,
+			      size_t count, loff_t *data)
+{
+	char cmd[21] = {0};
+	char buf[512];
+	int nchar = 0;
+	int ret = 0;
+
+	if (count > sizeof(buf))
+		return -ENOMEM;
+
+	if (copy_from_user(buf, buffer, count))
+		return -EFAULT;
+
+	buf[count] = '\0';
+
+	ret = sscanf(buf, "%20s %n", cmd, &nchar);
+
+	if (ret != 1)
+		return -EPERM;
+
+	if (!strcmp(cmd, "NEW_TNL")) {
+		ret = tops_tnl_add_new_tnl(buf + nchar);
+		if (ret)
+			return ret;
+	} else {
+		return -EINVAL;
+	}
+
+	return count;
+}
+
+DEFINE_SHOW_ATTRIBUTE(tops_fw_info);
+
+static const struct file_operations tops_tnl_fops = {
+	.open = tops_tnl_open,
+	.read = seq_read,
+	.write = tops_tnl_write,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+int mtk_tops_debugfs_init(struct platform_device *pdev)
+{
+	tops_fw_debugfs_root = debugfs_create_dir("fw", tops_debugfs_root);
+
+	debugfs_create_file("firmware_info", 0400, tops_fw_debugfs_root, NULL,
+			    &tops_fw_info_fops);
+
+	debugfs_create_file("tunnel", 0444, tops_fw_debugfs_root, NULL,
+			    &tops_tnl_fops);
+
+	return 0;
+}
+
+void mtk_tops_debugfs_deinit(struct platform_device *pdev)
+{
+	debugfs_remove_recursive(tops_fw_debugfs_root);
+}
diff --git a/package-21.02/kernel/tops/src/firmware.c b/package-21.02/kernel/tops/src/firmware.c
index 646212f..438cbda 100644
--- a/package-21.02/kernel/tops/src/firmware.c
+++ b/package-21.02/kernel/tops/src/firmware.c
@@ -16,9 +16,9 @@
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 
-#include "firmware.h"
-#include "internal.h"
-#include "mcu.h"
+#include "tops/firmware.h"
+#include "tops/internal.h"
+#include "tops/mcu.h"
 
 #define TOPS_MGMT_IMG				"mediatek/tops-mgmt.img"
 #define TOPS_OFFLOAD_IMG			"mediatek/tops-offload.img"
diff --git a/package-21.02/kernel/tops/src/hpdma.c b/package-21.02/kernel/tops/src/hpdma.c
index 18f17e4..4bcae0f 100644
--- a/package-21.02/kernel/tops/src/hpdma.c
+++ b/package-21.02/kernel/tops/src/hpdma.c
@@ -17,11 +17,11 @@
 
 #include <virt-dma.h>
 
-#include "hpdma.h"
-#include "hwspinlock.h"
-#include "internal.h"
-#include "mbox.h"
-#include "mcu.h"
+#include "tops/hpdma.h"
+#include "tops/hwspinlock.h"
+#include "tops/internal.h"
+#include "tops/mbox.h"
+#include "tops/mcu.h"
 
 #define HPDMA_CHAN_NUM			(4)
 
@@ -307,12 +307,9 @@
 	 * axsize can be 1, 2, 4, 8, 16 bytes
 	 * calculate axsize
 	 */
-	while (hvdesc->axsize >= 0 && hvdesc->len % (0x1 << hvdesc->axsize))
+	while (hvdesc->axsize > 0 && hvdesc->len % (0x1 << hvdesc->axsize))
 		hvdesc->axsize--;
 
-	if (hvdesc->axsize < 0)
-		return -EINVAL;
-
 	hvdesc->total_num = hvdesc->len / (0x1 << hvdesc->axsize);
 
 	return 0;
diff --git a/package-21.02/kernel/tops/src/hwspinlock.c b/package-21.02/kernel/tops/src/hwspinlock.c
index 1723cf4..774c56f 100644
--- a/package-21.02/kernel/tops/src/hwspinlock.c
+++ b/package-21.02/kernel/tops/src/hwspinlock.c
@@ -11,7 +11,8 @@
 #include <linux/device.h>
 #include <linux/platform_device.h>
 
-#include "hwspinlock.h"
+#include "tops/hwspinlock.h"
+#include "tops/tops.h"
 
 #define SEMA_ID				(BIT(CORE_AP))
 
diff --git a/package-21.02/kernel/tops/src/inc/ctrl.h b/package-21.02/kernel/tops/src/inc/tops/ctrl.h
similarity index 100%
rename from package-21.02/kernel/tops/src/inc/ctrl.h
rename to package-21.02/kernel/tops/src/inc/tops/ctrl.h
diff --git a/package-21.02/kernel/tops/src/inc/debugfs.h b/package-21.02/kernel/tops/src/inc/tops/debugfs.h
similarity index 61%
rename from package-21.02/kernel/tops/src/inc/debugfs.h
rename to package-21.02/kernel/tops/src/inc/tops/debugfs.h
index ea1847c..e4a6da1 100644
--- a/package-21.02/kernel/tops/src/inc/debugfs.h
+++ b/package-21.02/kernel/tops/src/inc/tops/debugfs.h
@@ -8,6 +8,11 @@
 #ifndef _TOPS_DEBUGFS_H_
 #define _TOPS_DEBUGFS_H_
 
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+
 extern struct dentry *tops_debugfs_root;
 
+int mtk_tops_debugfs_init(struct platform_device *pdev);
+void mtk_tops_debugfs_deinit(struct platform_device *pdev);
 #endif /* _TOPS_DEBUGFS_H_ */
diff --git a/package-21.02/kernel/tops/src/inc/firmware.h b/package-21.02/kernel/tops/src/inc/tops/firmware.h
similarity index 100%
rename from package-21.02/kernel/tops/src/inc/firmware.h
rename to package-21.02/kernel/tops/src/inc/tops/firmware.h
diff --git a/package-21.02/kernel/tops/src/inc/hpdma.h b/package-21.02/kernel/tops/src/inc/tops/hpdma.h
similarity index 100%
rename from package-21.02/kernel/tops/src/inc/hpdma.h
rename to package-21.02/kernel/tops/src/inc/tops/hpdma.h
diff --git a/package-21.02/kernel/tops/src/inc/hwspinlock.h b/package-21.02/kernel/tops/src/inc/tops/hwspinlock.h
similarity index 98%
rename from package-21.02/kernel/tops/src/inc/hwspinlock.h
rename to package-21.02/kernel/tops/src/inc/tops/hwspinlock.h
index ee9e343..1ce5004 100644
--- a/package-21.02/kernel/tops/src/inc/hwspinlock.h
+++ b/package-21.02/kernel/tops/src/inc/tops/hwspinlock.h
@@ -10,8 +10,6 @@
 
 #include <linux/types.h>
 
-#include "mbox.h"
-
 #define HWSPINLOCK_SLOT_MAX		16
 
 #define HWSPINLOCK_TOP_BASE		0x10100
diff --git a/package-21.02/kernel/tops/src/inc/internal.h b/package-21.02/kernel/tops/src/inc/tops/internal.h
similarity index 100%
rename from package-21.02/kernel/tops/src/inc/internal.h
rename to package-21.02/kernel/tops/src/inc/tops/internal.h
diff --git a/package-21.02/kernel/tops/src/inc/mbox.h b/package-21.02/kernel/tops/src/inc/tops/mbox.h
similarity index 98%
rename from package-21.02/kernel/tops/src/inc/mbox.h
rename to package-21.02/kernel/tops/src/inc/tops/mbox.h
index 002f2c6..c116162 100644
--- a/package-21.02/kernel/tops/src/inc/mbox.h
+++ b/package-21.02/kernel/tops/src/inc/tops/mbox.h
@@ -10,8 +10,8 @@
 
 #include <linux/list.h>
 
-#include "mbox_id.h"
-#include "tops.h"
+#include "tops/mbox_id.h"
+#include "tops/tops.h"
 
 /* mbox device macros */
 #define MBOX_DEV(core_id, cmd)				\
diff --git a/package-21.02/kernel/tops/src/inc/mbox_id.h b/package-21.02/kernel/tops/src/inc/tops/mbox_id.h
similarity index 100%
rename from package-21.02/kernel/tops/src/inc/mbox_id.h
rename to package-21.02/kernel/tops/src/inc/tops/mbox_id.h
diff --git a/package-21.02/kernel/tops/src/inc/mcu.h b/package-21.02/kernel/tops/src/inc/tops/mcu.h
similarity index 99%
rename from package-21.02/kernel/tops/src/inc/mcu.h
rename to package-21.02/kernel/tops/src/inc/tops/mcu.h
index 7c463eb..df9b059 100644
--- a/package-21.02/kernel/tops/src/inc/mcu.h
+++ b/package-21.02/kernel/tops/src/inc/tops/mcu.h
@@ -18,7 +18,7 @@
 #include <linux/workqueue.h>
 #include <linux/platform_device.h>
 
-#include "tops.h"
+#include "tops/tops.h"
 
 struct mcu_state;
 
diff --git a/package-21.02/kernel/tops/src/inc/net-event.h b/package-21.02/kernel/tops/src/inc/tops/net-event.h
similarity index 100%
rename from package-21.02/kernel/tops/src/inc/net-event.h
rename to package-21.02/kernel/tops/src/inc/tops/net-event.h
diff --git a/package-21.02/kernel/tops/src/inc/netsys.h b/package-21.02/kernel/tops/src/inc/tops/netsys.h
similarity index 97%
rename from package-21.02/kernel/tops/src/inc/netsys.h
rename to package-21.02/kernel/tops/src/inc/tops/netsys.h
index 1f51695..b319be4 100644
--- a/package-21.02/kernel/tops/src/inc/netsys.h
+++ b/package-21.02/kernel/tops/src/inc/tops/netsys.h
@@ -12,7 +12,7 @@
 #include <linux/bitfield.h>
 #include <linux/platform_device.h>
 
-#include "tunnel.h"
+#include "tops/tunnel.h"
 
 /* FE BASE */
 #define FE_BASE					(0x0000)
diff --git a/package-21.02/kernel/tops/src/inc/ser.h b/package-21.02/kernel/tops/src/inc/tops/ser.h
similarity index 92%
rename from package-21.02/kernel/tops/src/inc/ser.h
rename to package-21.02/kernel/tops/src/inc/tops/ser.h
index 99f9d3d..72f1f7d 100644
--- a/package-21.02/kernel/tops/src/inc/ser.h
+++ b/package-21.02/kernel/tops/src/inc/tops/ser.h
@@ -12,9 +12,9 @@
 #include <linux/spinlock.h>
 #include <linux/platform_device.h>
 
-#include "net-event.h"
-#include "mcu.h"
-#include "wdt.h"
+#include "tops/net-event.h"
+#include "tops/mcu.h"
+#include "tops/wdt.h"
 
 enum tops_ser_type {
 	TOPS_SER_NETSYS_FE_RST,
diff --git a/package-21.02/kernel/tops/src/inc/tdma.h b/package-21.02/kernel/tops/src/inc/tops/tdma.h
similarity index 100%
rename from package-21.02/kernel/tops/src/inc/tdma.h
rename to package-21.02/kernel/tops/src/inc/tops/tdma.h
diff --git a/package-21.02/kernel/tops/src/inc/tops.h b/package-21.02/kernel/tops/src/inc/tops/tops.h
similarity index 100%
rename from package-21.02/kernel/tops/src/inc/tops.h
rename to package-21.02/kernel/tops/src/inc/tops/tops.h
diff --git a/package-21.02/kernel/tops/src/inc/tops/tops_params.h b/package-21.02/kernel/tops/src/inc/tops/tops_params.h
new file mode 100644
index 0000000..0bdb9b5
--- /dev/null
+++ b/package-21.02/kernel/tops/src/inc/tops/tops_params.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#ifndef _TOPS_PARAMS_H_
+#define _TOPS_PARAMS_H_
+
+#include <linux/if_ether.h>
+#include <linux/seq_file.h>
+#include <linux/types.h>
+
+#include "tops/protocol/network/ip_params.h"
+#include "tops/protocol/transport/udp_params.h"
+#include "tops/protocol/tunnel/l2tp/l2tp_params.h"
+
+/* tunnel params flags */
+#define TNL_DECAP_ENABLE	(BIT(TNL_PARAMS_DECAP_ENABLE_BIT))
+#define TNL_ENCAP_ENABLE	(BIT(TNL_PARAMS_ENCAP_ENABLE_BIT))
+
+#define DEBUG_PROTO_LEN		(21)
+#define DEBUG_PROTO_ETH		"eth"
+#define DEBUG_PROTO_IP		"ipv4"
+#define DEBUG_PROTO_UDP		"udp"
+#define DEBUG_PROTO_GRETAP	"gretap"
+#define DEBUG_PROTO_L2TP_V2	"l2tpv2"
+
+enum tops_mac_type {
+	TOPS_MAC_NONE,
+	TOPS_MAC_ETH,
+
+	__TOPS_MAC_TYPE_MAX,
+};
+
+enum tops_network_type {
+	TOPS_NETWORK_NONE,
+	TOPS_NETWORK_IPV4,
+
+	__TOPS_NETWORK_TYPE_MAX,
+};
+
+enum tops_transport_type {
+	TOPS_TRANSPORT_NONE,
+	TOPS_TRANSPORT_UDP,
+
+	__TOPS_TRANSPORT_TYPE_MAX,
+};
+
+enum tops_tunnel_type {
+	TOPS_TUNNEL_NONE = 0,
+	TOPS_TUNNEL_GRETAP,
+	TOPS_TUNNEL_PPTP,
+	TOPS_TUNNEL_L2TP_V2,
+	TOPS_TUNNEL_L2TP_V3 = 5,
+	TOPS_TUNNEL_VXLAN,
+	TOPS_TUNNEL_NATT,
+	TOPS_TUNNEL_CAPWAP_CTRL,
+	TOPS_TUNNEL_CAPWAP_DATA,
+	TOPS_TUNNEL_CAPWAP_DTLS = 10,
+	TOPS_TUNNEL_IPSEC_ESP,
+	TOPS_TUNNEL_IPSEC_AH,
+
+	__TOPS_TUNNEL_TYPE_MAX = CONFIG_TOPS_TNL_TYPE_NUM,
+};
+
+enum tops_tnl_params_flag {
+	TNL_PARAMS_DECAP_ENABLE_BIT,
+	TNL_PARAMS_ENCAP_ENABLE_BIT,
+};
+
+struct tops_mac_params {
+	union {
+		struct ethhdr eth;
+	};
+	enum tops_mac_type type;
+};
+
+struct tops_network_params {
+	union {
+		struct tops_ip_params ip;
+	};
+	enum tops_network_type type;
+};
+
+struct tops_transport_params {
+	union {
+		struct tops_udp_params udp;
+	};
+	enum tops_transport_type type;
+};
+
+struct tops_tunnel_params {
+	union {
+		struct tops_l2tp_params l2tp;
+	};
+	enum tops_tunnel_type type;
+};
+
+struct tops_params {
+	struct tops_mac_params mac;
+	struct tops_network_params network;
+	struct tops_transport_params transport;
+	struct tops_tunnel_params tunnel;
+};
+
+/* record outer tunnel header data for HW offloading */
+struct tops_tnl_params {
+	struct tops_params params;
+	u8 tops_entry_proto;
+	u8 cls_entry;
+	u8 cdrt;
+	u8 flag; /* bit: enum tops_tnl_params_flag */
+} __packed __aligned(16);
+
+int
+mtk_tops_encap_param_setup(struct sk_buff *skb,
+			   struct tops_params *params,
+			   int (*tnl_encap_param_setup)(struct sk_buff *skb,
+							struct tops_params *params));
+int
+mtk_tops_decap_param_setup(struct sk_buff *skb,
+			   struct tops_params *params,
+			   int (*tnl_decap_param_setup)(struct sk_buff *skb,
+							struct tops_params *params));
+
+int mtk_tops_transport_decap_param_setup(struct sk_buff *skb,
+					 struct tops_params *params);
+int mtk_tops_network_decap_param_setup(struct sk_buff *skb,
+				       struct tops_params *params);
+int mtk_tops_mac_decap_param_setup(struct sk_buff *skb,
+				       struct tops_params *params);
+
+int mtk_tops_debug_param_proto_peek(const char *buf, int ofs, char *out);
+int mtk_tops_debug_param_setup(const char *buf, int *ofs,
+				   struct tops_params *params);
+void mtk_tops_mac_param_dump(struct seq_file *s, struct tops_params *params);
+void mtk_tops_network_param_dump(struct seq_file *s, struct tops_params *params);
+void mtk_tops_transport_param_dump(struct seq_file *s, struct tops_params *params);
+
+bool mtk_tops_params_match(struct tops_params *p1, struct tops_params *p2);
+#endif /* _TOPS_PARAMS_H_ */
diff --git a/package-21.02/kernel/tops/src/inc/trm-debugfs.h b/package-21.02/kernel/tops/src/inc/tops/trm-debugfs.h
similarity index 100%
rename from package-21.02/kernel/tops/src/inc/trm-debugfs.h
rename to package-21.02/kernel/tops/src/inc/tops/trm-debugfs.h
diff --git a/package-21.02/kernel/tops/src/inc/trm-fs.h b/package-21.02/kernel/tops/src/inc/tops/trm-fs.h
similarity index 100%
rename from package-21.02/kernel/tops/src/inc/trm-fs.h
rename to package-21.02/kernel/tops/src/inc/tops/trm-fs.h
diff --git a/package-21.02/kernel/tops/src/inc/trm-mcu.h b/package-21.02/kernel/tops/src/inc/tops/trm-mcu.h
similarity index 97%
rename from package-21.02/kernel/tops/src/inc/trm-mcu.h
rename to package-21.02/kernel/tops/src/inc/tops/trm-mcu.h
index e3f9e3f..87cf699 100644
--- a/package-21.02/kernel/tops/src/inc/trm-mcu.h
+++ b/package-21.02/kernel/tops/src/inc/tops/trm-mcu.h
@@ -9,7 +9,7 @@
 #ifndef _TOPS_TRM_MCU_H_
 #define _TOPS_TRM_MCU_H_
 
-#include "tops.h"
+#include "tops/tops.h"
 
 #define XCHAL_NUM_AREG                  (32)
 #define CORE_DUMP_FRAM_MAGIC            (0x00BE00BE)
diff --git a/package-21.02/kernel/tops/src/inc/trm.h b/package-21.02/kernel/tops/src/inc/tops/trm.h
similarity index 100%
rename from package-21.02/kernel/tops/src/inc/trm.h
rename to package-21.02/kernel/tops/src/inc/tops/trm.h
diff --git a/package-21.02/kernel/tops/src/inc/tunnel.h b/package-21.02/kernel/tops/src/inc/tops/tunnel.h
similarity index 72%
rename from package-21.02/kernel/tops/src/inc/tunnel.h
rename to package-21.02/kernel/tops/src/inc/tops/tunnel.h
index d00eebe..cbe4dc6 100644
--- a/package-21.02/kernel/tops/src/inc/tunnel.h
+++ b/package-21.02/kernel/tops/src/inc/tops/tunnel.h
@@ -15,12 +15,13 @@
 #include <linux/kthread.h>
 #include <linux/netdevice.h>
 #include <linux/refcount.h>
+#include <linux/seq_file.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
 
 #include <pce/cls.h>
 
-#include "protocol/l2tp/l2tp.h"
+#include "tops/tops_params.h"
 
 /* tunnel info status */
 #define TNL_STA_UNINIT		(BIT(TNL_STATUS_UNINIT))
@@ -31,10 +32,6 @@
 #define TNL_STA_DIP_UPDATE	(BIT(TNL_STATUS_DIP_UPDATE))
 #define TNL_STA_DELETING	(BIT(TNL_STATUS_DELETING))
 
-/* tunnel params flags */
-#define TNL_DECAP_ENABLE	(BIT(TNL_PARAMS_DECAP_ENABLE_BIT))
-#define TNL_ENCAP_ENABLE	(BIT(TNL_PARAMS_ENCAP_ENABLE_BIT))
-
 /* tunnel info flags */
 #define TNL_INFO_DEBUG		(BIT(TNL_INFO_DEBUG_BIT))
 
@@ -54,24 +51,6 @@
 	TOPS_CRSN_TNL_ID_END = 0x2F,
 };
 
-enum tops_entry_type {
-	TOPS_ENTRY_NONE = 0,
-	TOPS_ENTRY_GRETAP,
-	TOPS_ENTRY_PPTP,
-	TOPS_ENTRY_IP_L2TP,
-	TOPS_ENTRY_UDP_L2TP_CTRL,
-	TOPS_ENTRY_UDP_L2TP_DATA = 5,
-	TOPS_ENTRY_VXLAN,
-	TOPS_ENTRY_NATT,
-	TOPS_ENTRY_CAPWAP_CTRL,
-	TOPS_ENTRY_CAPWAP_DATA,
-	TOPS_ENTRY_CAPWAP_DTLS = 10,
-	TOPS_ENTRY_IPSEC_ESP,
-	TOPS_ENTRY_IPSEC_AH,
-
-	__TOPS_ENTRY_MAX = CONFIG_TOPS_TNL_NUM,
-};
-
 enum tops_tunnel_mbox_cmd {
 	TOPS_TNL_MBOX_CMD_RESV,
 	TOPS_TNL_START_ADDR_SYNC,
@@ -100,11 +79,6 @@
 	__TNL_STATUS_MAX,
 };
 
-enum tops_tnl_params_flag {
-	TNL_PARAMS_DECAP_ENABLE_BIT,
-	TNL_PARAMS_ENCAP_ENABLE_BIT,
-};
-
 enum tops_tnl_info_flag {
 	TNL_INFO_DEBUG_BIT,
 };
@@ -116,24 +90,6 @@
 	bool updated;
 };
 
-/* record outer tunnel header data for HW offloading */
-struct tops_tnl_params {
-	u8 daddr[ETH_ALEN];
-	u8 saddr[ETH_ALEN];
-	__be32 dip;
-	__be32 sip;
-	__be16 dport;
-	__be16 sport;
-	u16 protocol;
-	u8 tops_entry_proto;
-	u8 cls_entry;
-	u8 cdrt;
-	u8 flag; /* bit: enum tops_tnl_params_flag */
-	union {
-		struct l2tp_param l2tp; /* 4B */
-	} priv;
-} __packed __aligned(16);
-
 struct tops_tnl_info {
 	struct tops_tnl_params tnl_params;
 	struct tops_tnl_params cache;
@@ -157,31 +113,38 @@
  */
 struct tops_tnl_type {
 	const char *type_name;
-	enum tops_entry_type tops_entry;
+	enum tops_tunnel_type tnl_proto_type;
 
 	int (*cls_entry_setup)(struct tops_tnl_info *tnl_info,
 			       struct cls_desc *cdesc);
 	struct list_head tcls_head;
 	bool use_multi_cls;
 
+	/* parameter setup */
 	int (*tnl_decap_param_setup)(struct sk_buff *skb,
-				     struct tops_tnl_params *tnl_params);
+				     struct tops_params *params);
 	int (*tnl_encap_param_setup)(struct sk_buff *skb,
-				     struct tops_tnl_params *tnl_params);
+				     struct tops_params *params);
 	int (*tnl_debug_param_setup)(const char *buf, int *ofs,
-				     struct tops_tnl_params *tnl_params);
+				     struct tops_params *params);
 	int (*tnl_l2_param_update)(struct sk_buff *skb,
-				   struct tops_tnl_params *tnl_params);
-	int (*tnl_dump_param)(char *buf, struct tops_tnl_params *tnl_params);
-	bool (*tnl_info_match)(struct tops_tnl_params *params1,
-			       struct tops_tnl_params *params2);
+				   struct tops_params *params);
+	/* parameter debug dump */
+	void (*tnl_param_dump)(struct seq_file *s, struct tops_params *params);
+	/* check skb content can be offloaded */
 	bool (*tnl_decap_offloadable)(struct sk_buff *skb);
+	/* match between 2 parameters */
+	bool (*tnl_param_match)(struct tops_params *p, struct tops_params *target);
+	/* recover essential parameters before updating */
+	void (*tnl_param_restore)(struct tops_params *old, struct tops_params *new);
 	bool has_inner_eth;
 };
 
 void mtk_tops_tnl_info_submit_no_tnl_lock(struct tops_tnl_info *tnl_info);
 void mtk_tops_tnl_info_submit(struct tops_tnl_info *tnl_info);
-struct tops_tnl_info *mtk_tops_tnl_info_find(struct tops_tnl_params *tnl_params);
+struct tops_tnl_info *mtk_tops_tnl_info_get_by_idx(u32 tnl_idx);
+struct tops_tnl_info *mtk_tops_tnl_info_find(struct tops_tnl_type *tnl_type,
+					     struct tops_tnl_params *tnl_params);
 struct tops_tnl_info *mtk_tops_tnl_info_alloc(struct tops_tnl_type *tnl_type);
 void mtk_tops_tnl_info_hash(struct tops_tnl_info *tnl_info);
 
diff --git a/package-21.02/kernel/tops/src/inc/wdt.h b/package-21.02/kernel/tops/src/inc/tops/wdt.h
similarity index 95%
rename from package-21.02/kernel/tops/src/inc/wdt.h
rename to package-21.02/kernel/tops/src/inc/tops/wdt.h
index b70bf63..22918a0 100644
--- a/package-21.02/kernel/tops/src/inc/wdt.h
+++ b/package-21.02/kernel/tops/src/inc/tops/wdt.h
@@ -10,7 +10,7 @@
 
 #include <linux/platform_device.h>
 
-#include "tops.h"
+#include "tops/tops.h"
 
 enum wdt_cmd {
 	WDT_CMD_TRIGGER_TIMEOUT,
diff --git a/package-21.02/kernel/tops/src/init.c b/package-21.02/kernel/tops/src/init.c
index 62c7806..478a45a 100644
--- a/package-21.02/kernel/tops/src/init.c
+++ b/package-21.02/kernel/tops/src/init.c
@@ -15,21 +15,22 @@
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 
-#include "ctrl.h"
-#include "firmware.h"
-#include "hpdma.h"
-#include "hwspinlock.h"
-#include "internal.h"
-#include "mbox.h"
-#include "mcu.h"
-#include "netsys.h"
-#include "net-event.h"
-#include "ser.h"
-#include "tdma.h"
-#include "trm-mcu.h"
-#include "trm.h"
-#include "tunnel.h"
-#include "wdt.h"
+#include "tops/ctrl.h"
+#include "tops/debugfs.h"
+#include "tops/firmware.h"
+#include "tops/hpdma.h"
+#include "tops/hwspinlock.h"
+#include "tops/internal.h"
+#include "tops/mbox.h"
+#include "tops/mcu.h"
+#include "tops/netsys.h"
+#include "tops/net-event.h"
+#include "tops/ser.h"
+#include "tops/tdma.h"
+#include "tops/trm-mcu.h"
+#include "tops/trm.h"
+#include "tops/tunnel.h"
+#include "tops/wdt.h"
 
 #define EFUSE_TOPS_POWER_OFF		(0xD08)
 
@@ -79,8 +80,17 @@
 		goto err_ser_deinit;
 	}
 
+	ret = mtk_tops_debugfs_init(pdev);
+	if (ret) {
+		TOPS_ERR("debugfs init failed: %d\n", ret);
+		goto err_wdt_deinit;
+	}
+
 	return ret;
 
+err_wdt_deinit:
+	mtk_tops_wdt_deinit(pdev);
+
 err_ser_deinit:
 	mtk_tops_ser_deinit(pdev);
 
@@ -165,6 +175,8 @@
 
 static int mtk_tops_remove(struct platform_device *pdev)
 {
+	mtk_tops_debugfs_init(pdev);
+
 	mtk_tops_wdt_deinit(pdev);
 
 	mtk_tops_ser_deinit(pdev);
diff --git a/package-21.02/kernel/tops/src/mbox.c b/package-21.02/kernel/tops/src/mbox.c
index c10cbca..dc902c4 100644
--- a/package-21.02/kernel/tops/src/mbox.c
+++ b/package-21.02/kernel/tops/src/mbox.c
@@ -15,9 +15,9 @@
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
 
-#include "mcu.h"
-#include "mbox.h"
-#include "internal.h"
+#include "tops/mcu.h"
+#include "tops/mbox.h"
+#include "tops/internal.h"
 
 #define MBOX_SEND_TIMEOUT		(2000)
 
diff --git a/package-21.02/kernel/tops/src/mcu.c b/package-21.02/kernel/tops/src/mcu.c
index 30cb032..6e8643a 100644
--- a/package-21.02/kernel/tops/src/mcu.c
+++ b/package-21.02/kernel/tops/src/mcu.c
@@ -22,15 +22,15 @@
 
 #include <pce/pce.h>
 
-#include "ctrl.h"
-#include "firmware.h"
-#include "hpdma.h"
-#include "internal.h"
-#include "mbox.h"
-#include "mcu.h"
-#include "netsys.h"
-#include "tdma.h"
-#include "trm.h"
+#include "tops/ctrl.h"
+#include "tops/firmware.h"
+#include "tops/hpdma.h"
+#include "tops/internal.h"
+#include "tops/mbox.h"
+#include "tops/mcu.h"
+#include "tops/netsys.h"
+#include "tops/tdma.h"
+#include "tops/trm.h"
 
 #define TDMA_TIMEOUT_MAX_CNT			(3)
 #define TDMA_TIMEOUT_DELAY			(100) /* 100ms */
diff --git a/package-21.02/kernel/tops/src/net-event.c b/package-21.02/kernel/tops/src/net-event.c
index 7cc4553..6143a03 100644
--- a/package-21.02/kernel/tops/src/net-event.c
+++ b/package-21.02/kernel/tops/src/net-event.c
@@ -20,13 +20,13 @@
 #include <net/neighbour.h>
 #include <net/route.h>
 
-#include "internal.h"
-#include "netsys.h"
-#include "net-event.h"
-#include "mcu.h"
-#include "ser.h"
-#include "trm.h"
-#include "tunnel.h"
+#include "tops/internal.h"
+#include "tops/netsys.h"
+#include "tops/net-event.h"
+#include "tops/mcu.h"
+#include "tops/ser.h"
+#include "tops/trm.h"
+#include "tops/tunnel.h"
 
 static struct completion wait_fe_reset_done;
 
diff --git a/package-21.02/kernel/tops/src/netsys.c b/package-21.02/kernel/tops/src/netsys.c
index 7deb502..50513d8 100644
--- a/package-21.02/kernel/tops/src/netsys.c
+++ b/package-21.02/kernel/tops/src/netsys.c
@@ -16,12 +16,12 @@
 
 #include <pce/netsys.h>
 
-#include "hpdma.h"
-#include "internal.h"
-#include "mcu.h"
-#include "netsys.h"
-#include "tdma.h"
-#include "trm.h"
+#include "tops/hpdma.h"
+#include "tops/internal.h"
+#include "tops/mcu.h"
+#include "tops/netsys.h"
+#include "tops/tdma.h"
+#include "tops/trm.h"
 
 /* Netsys dump length */
 #define FE_BASE_LEN				(0x2900)
diff --git a/package-21.02/kernel/tops/src/protocol/inc/protocol/l2tp/udp_l2tp_data.h b/package-21.02/kernel/tops/src/protocol/inc/protocol/l2tp/udp_l2tp_data.h
deleted file mode 100644
index 25f9dc3..0000000
--- a/package-21.02/kernel/tops/src/protocol/inc/protocol/l2tp/udp_l2tp_data.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
- *
- * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
- */
-
-#ifndef _TOPS_UDP_L2TP_DATA_H_
-#define _TOPS_UDP_L2TP_DATA_H_
-
-#if defined(CONFIG_MTK_TOPS_UDP_L2TP_DATA)
-int mtk_tops_udp_l2tp_data_init(void);
-void mtk_tops_udp_l2tp_data_deinit(void);
-#else /* !defined(CONFIG_MTK_TOPS_UDP_L2TP_DATA) */
-static inline int mtk_tops_udp_l2tp_data_init(void)
-{
-	return 0;
-}
-
-static inline void mtk_tops_udp_l2tp_data_deinit(void)
-{
-}
-#endif /* defined(CONFIG_MTK_TOPS_UDP_L2TP_DATA) */
-#endif /* _TOPS_UDP_L2TP_DATA_H_ */
diff --git a/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/mac/eth.h b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/mac/eth.h
new file mode 100644
index 0000000..f306230
--- /dev/null
+++ b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/mac/eth.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#ifndef _TOPS_ETH_H_
+#define _TOPS_ETH_H_
+
+#include <linux/if_ether.h>
+
+#include "tops/tops_params.h"
+
+int
+mtk_tops_eth_encap_param_setup(
+			struct sk_buff *skb,
+			struct tops_params *params,
+			int (*tnl_encap_param_setup)(struct sk_buff *skb,
+						     struct tops_params *params));
+int mtk_tops_eth_decap_param_setup(struct sk_buff *skb, struct tops_params *params);
+int mtk_tops_eth_debug_param_setup(const char *buf, int *ofs,
+				   struct tops_params *params);
+void mtk_tops_eth_param_dump(struct seq_file *s, struct tops_params *params);
+#endif /* _TOPS_ETH_H_ */
diff --git a/package-21.02/kernel/tops/src/protocol/inc/protocol/ppp/ppp.h b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/mac/ppp.h
similarity index 61%
rename from package-21.02/kernel/tops/src/protocol/inc/protocol/ppp/ppp.h
rename to package-21.02/kernel/tops/src/protocol/inc/tops/protocol/mac/ppp.h
index 274dfde..b5f1cb4 100644
--- a/package-21.02/kernel/tops/src/protocol/inc/protocol/ppp/ppp.h
+++ b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/mac/ppp.h
@@ -2,12 +2,16 @@
 /*
  * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
  *
- * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ * Author: Frank-zj Lin <rank-zj.lin@mediatek.com>
  */
 
 #ifndef _TOPS_PPP_H_
 #define _TOPS_PPP_H_
 
+#include <linux/ppp_defs.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+
 /* Limited support: ppp header, no options */
 struct ppp_hdr {
 	u8 addr;
@@ -15,4 +19,5 @@
 	u16 proto;
 };
 
+bool mtk_tops_ppp_valid(struct sk_buff *skb);
 #endif /* _TOPS_PPP_H_ */
diff --git a/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/network/ip.h b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/network/ip.h
new file mode 100644
index 0000000..3182b5d
--- /dev/null
+++ b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/network/ip.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#ifndef _TOPS_IP_H_
+#define _TOPS_IP_H_
+
+#include <linux/ip.h>
+#include <uapi/linux/in.h>
+
+#include "tops/protocol/network/ip_params.h"
+#include "tops/tops_params.h"
+
+int mtk_tops_ip_encap_param_setup(
+			struct sk_buff *skb,
+			struct tops_params *params,
+			int (*tnl_encap_param_setup)(struct sk_buff *skb,
+						     struct tops_params *params));
+int mtk_tops_ip_decap_param_setup(struct sk_buff *skb, struct tops_params *params);
+int mtk_tops_ip_debug_param_setup(const char *buf, int *ofs,
+				  struct tops_params *params);
+void mtk_tops_ip_param_dump(struct seq_file *s, struct tops_params *params);
+#endif /* _TOPS_IP_H_ */
diff --git a/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/network/ip_params.h b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/network/ip_params.h
new file mode 100644
index 0000000..9583862
--- /dev/null
+++ b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/network/ip_params.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#ifndef _TOPS_IP_PARAMS_H_
+#define _TOPS_IP_PARAMS_H_
+
+#include <linux/types.h>
+
+struct tops_ip_params {
+	__be32 sip;
+	__be32 dip;
+	u8 proto;
+	u8 tos;
+	u8 ttl;
+};
+#endif /* _TOPS_IP_PARAMS_H_ */
diff --git a/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/transport/udp.h b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/transport/udp.h
new file mode 100644
index 0000000..9ee8977
--- /dev/null
+++ b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/transport/udp.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#ifndef _TOPS_UDP_H_
+#define _TOPS_UDP_H_
+
+#include "tops/protocol/transport/udp_params.h"
+#include "tops/tops_params.h"
+
+int mtk_tops_udp_encap_param_setup(
+			struct sk_buff *skb,
+			struct tops_params *params,
+			int (*tnl_encap_param_setup)(struct sk_buff *skb,
+						     struct tops_params *params));
+int mtk_tops_udp_decap_param_setup(struct sk_buff *skb, struct tops_params *params);
+int mtk_tops_udp_debug_param_setup(const char *buf, int *ofs,
+				   struct tops_params *params);
+void mtk_tops_udp_param_dump(struct seq_file *s, struct tops_params *params);
+#endif /* _TOPS_UDP_H_ */
diff --git a/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/transport/udp_params.h b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/transport/udp_params.h
new file mode 100644
index 0000000..b66476c
--- /dev/null
+++ b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/transport/udp_params.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#ifndef _TOPS_UDP_PARAMS_H_
+#define _TOPS_UDP_PARAMS_H_
+
+#include <linux/types.h>
+#include <linux/udp.h>
+
+struct tops_udp_params {
+	u16 sport;
+	u16 dport;
+};
+#endif /* _TOPS_UDP_PARAMS_H_ */
diff --git a/package-21.02/kernel/tops/src/protocol/inc/protocol/gre/gretap.h b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/tunnel/gre/gretap.h
similarity index 68%
rename from package-21.02/kernel/tops/src/protocol/inc/protocol/gre/gretap.h
rename to package-21.02/kernel/tops/src/protocol/inc/tops/protocol/tunnel/gre/gretap.h
index e308fde..46a68db 100644
--- a/package-21.02/kernel/tops/src/protocol/inc/protocol/gre/gretap.h
+++ b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/tunnel/gre/gretap.h
@@ -8,10 +8,19 @@
 #ifndef _TOPS_GRETAP_H_
 #define _TOPS_GRETAP_H_
 
+#include "tops/tops_params.h"
+
 #if defined(CONFIG_MTK_TOPS_GRETAP)
+int mtk_tops_gretap_decap_param_setup(struct sk_buff *skb, struct tops_params *params);
 int mtk_tops_gretap_init(void);
 void mtk_tops_gretap_deinit(void);
 #else /* !defined(CONFIG_MTK_TOPS_GRETAP) */
+static inline int mtk_tops_gretap_decap_param_setup(struct sk_buff *skb,
+						    struct tops_params *params)
+{
+	return -ENODEV;
+}
+
 static inline int mtk_tops_gretap_init(void)
 {
 	return 0;
diff --git a/package-21.02/kernel/tops/src/protocol/inc/protocol/l2tp/l2tp.h b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/tunnel/l2tp/l2tp_params.h
similarity index 95%
rename from package-21.02/kernel/tops/src/protocol/inc/protocol/l2tp/l2tp.h
rename to package-21.02/kernel/tops/src/protocol/inc/tops/protocol/tunnel/l2tp/l2tp_params.h
index 34449c1..dc266ef 100644
--- a/package-21.02/kernel/tops/src/protocol/inc/protocol/l2tp/l2tp.h
+++ b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/tunnel/l2tp/l2tp_params.h
@@ -18,7 +18,7 @@
 
 #define UDP_L2TP_PORT		1701
 
-struct l2tp_param {
+struct tops_l2tp_params {
 	u16 tid; /* l2tp tunnel id */
 	u16 sid; /* l2tp session id */
 };
@@ -29,5 +29,4 @@
 	u16 tid;
 	u16 sid;
 };
-
 #endif /* _TOPS_L2TP_H_ */
diff --git a/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/tunnel/l2tp/l2tpv2.h b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/tunnel/l2tp/l2tpv2.h
new file mode 100644
index 0000000..a757f11
--- /dev/null
+++ b/package-21.02/kernel/tops/src/protocol/inc/tops/protocol/tunnel/l2tp/l2tpv2.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#ifndef _TOPS_L2TP_V2_H_
+#define _TOPS_L2TP_V2_H_
+
+#include "tops/protocol/tunnel/l2tp/l2tp_params.h"
+
+#if defined(CONFIG_MTK_TOPS_L2TP_V2)
+int mtk_tops_l2tpv2_init(void);
+void mtk_tops_l2tpv2_deinit(void);
+#else /* !defined(CONFIG_MTK_TOPS_L2TP_V2) */
+static inline int mtk_tops_l2tpv2_init(void)
+{
+	return 0;
+}
+
+static inline void mtk_tops_l2tpv2_deinit(void)
+{
+}
+#endif /* defined(CONFIG_MTK_TOPS_L2TP_V2) */
+#endif /* _TOPS_L2TP_V2_H_ */
diff --git a/package-21.02/kernel/tops/src/protocol/mac/eth.c b/package-21.02/kernel/tops/src/protocol/mac/eth.c
new file mode 100644
index 0000000..37d4d59
--- /dev/null
+++ b/package-21.02/kernel/tops/src/protocol/mac/eth.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#include "tops/internal.h"
+#include "tops/protocol/mac/eth.h"
+#include "tops/protocol/network/ip.h"
+
+#include <mtk_hnat/nf_hnat_mtk.h>
+
+int
+mtk_tops_eth_encap_param_setup(
+			struct sk_buff *skb,
+			struct tops_params *params,
+			int (*tnl_encap_param_setup)(struct sk_buff *skb,
+						     struct tops_params *params))
+{
+	struct ethhdr *eth = eth_hdr(skb);
+
+	params->mac.type = TOPS_MAC_ETH;
+
+	memcpy(&params->mac.eth.h_source, eth->h_source, ETH_ALEN);
+	memcpy(&params->mac.eth.h_dest, eth->h_dest, ETH_ALEN);
+	params->mac.eth.h_proto = htons(ETH_P_IP);
+
+	/*
+	 * either has contrusted ethernet header with IP
+	 * or the packet is going to do xfrm encryption
+	 */
+	if ((ntohs(eth->h_proto) == ETH_P_IP)
+	    || (!skb_hnat_cdrt(skb) && skb_dst(skb) && dst_xfrm(skb_dst(skb)))) {
+		return mtk_tops_ip_encap_param_setup(skb,
+						     params,
+						     tnl_encap_param_setup);
+	}
+
+	TOPS_NOTICE("eth proto not support, proto: 0x%x\n",
+		    ntohs(eth->h_proto));
+
+	return -EINVAL;
+}
+
+int mtk_tops_eth_decap_param_setup(struct sk_buff *skb, struct tops_params *params)
+{
+	struct ethhdr *eth;
+	struct ethhdr ethh;
+	int ret = 0;
+
+	skb_push(skb, sizeof(struct ethhdr));
+	eth = skb_header_pointer(skb, 0, sizeof(struct ethhdr), &ethh);
+	if (unlikely(!eth)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (unlikely(ntohs(eth->h_proto) != ETH_P_IP)) {
+		TOPS_NOTICE("eth proto not support, proto: 0x%x\n",
+			    ntohs(eth->h_proto));
+		ret = -EINVAL;
+		goto out;
+	}
+
+	params->mac.type = TOPS_MAC_ETH;
+
+	memcpy(&params->mac.eth.h_source, eth->h_dest, ETH_ALEN);
+	memcpy(&params->mac.eth.h_dest, eth->h_source, ETH_ALEN);
+	params->mac.eth.h_proto = htons(ETH_P_IP);
+
+out:
+	skb_pull(skb, sizeof(struct ethhdr));
+
+	return ret;
+}
+
+static int tops_eth_debug_param_fetch_mac(const char *buf, int *ofs, u8 *mac)
+{
+	int nchar = 0;
+	int ret;
+
+	ret = sscanf(buf + *ofs, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %n",
+		&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5], &nchar);
+	if (ret != 6)
+		return -EPERM;
+
+	*ofs += nchar;
+
+	return 0;
+}
+
+int mtk_tops_eth_debug_param_setup(const char *buf, int *ofs,
+				   struct tops_params *params)
+{
+	char proto[DEBUG_PROTO_LEN] = {0};
+	int ret;
+
+	params->mac.type = TOPS_MAC_ETH;
+
+	ret = tops_eth_debug_param_fetch_mac(buf, ofs, params->mac.eth.h_source);
+	if (ret)
+		return ret;
+
+	ret = tops_eth_debug_param_fetch_mac(buf, ofs, params->mac.eth.h_dest);
+	if (ret)
+		return ret;
+
+	ret = mtk_tops_debug_param_proto_peek(buf, *ofs, proto);
+	if (ret < 0)
+		return ret;
+
+	*ofs += ret;
+
+	if (!strcmp(proto, DEBUG_PROTO_IP)) {
+		params->mac.eth.h_proto = htons(ETH_P_IP);
+		ret = mtk_tops_ip_debug_param_setup(buf, ofs, params);
+	} else {
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+void mtk_tops_eth_param_dump(struct seq_file *s, struct tops_params *params)
+{
+	seq_puts(s, "\tMAC Type: Ethernet ");
+	seq_printf(s, "saddr: %pM daddr: %pM\n",
+		   params->mac.eth.h_source, params->mac.eth.h_dest);
+}
diff --git a/package-21.02/kernel/tops/src/protocol/mac/ppp.c b/package-21.02/kernel/tops/src/protocol/mac/ppp.c
new file mode 100644
index 0000000..8b8400b
--- /dev/null
+++ b/package-21.02/kernel/tops/src/protocol/mac/ppp.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Frank-zj Lin <rank-zj.lin@mediatek.com>
+ *         Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#include "tops/protocol/mac/ppp.h"
+
+bool mtk_tops_ppp_valid(struct sk_buff *skb)
+{
+	struct ppp_hdr *ppp;
+	struct ppp_hdr ppph;
+
+	ppp = skb_header_pointer(skb, 0, sizeof(struct ppp_hdr), &ppph);
+
+	if (unlikely(!ppp))
+		return false;
+
+	return (ppp->addr == PPP_ALLSTATIONS &&
+		ppp->ctrl == PPP_UI && ntohs(ppp->proto) == PPP_IP);
+}
diff --git a/package-21.02/kernel/tops/src/protocol/network/ip.c b/package-21.02/kernel/tops/src/protocol/network/ip.c
new file mode 100644
index 0000000..abe7f10
--- /dev/null
+++ b/package-21.02/kernel/tops/src/protocol/network/ip.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#include "tops/internal.h"
+#include "tops/protocol/network/ip.h"
+#include "tops/protocol/transport/udp.h"
+
+int mtk_tops_ip_encap_param_setup(
+			struct sk_buff *skb,
+			struct tops_params *params,
+			int (*tnl_encap_param_setup)(struct sk_buff *skb,
+						     struct tops_params *params))
+{
+	struct tops_ip_params *ipp = &params->network.ip;
+	struct iphdr *ip;
+	struct iphdr iph;
+	int ret;
+
+	ip = skb_header_pointer(skb, 0, sizeof(struct iphdr), &iph);
+	if (unlikely(!ip))
+		return -EINVAL;
+
+	if (unlikely(ip->version != IPVERSION)) {
+		TOPS_NOTICE("ip ver: 0x%x invalid\n", ip->version);
+		return -EINVAL;
+	}
+
+	params->network.type = TOPS_NETWORK_IPV4;
+
+	ipp->proto = ip->protocol;
+	ipp->sip = ip->saddr;
+	ipp->dip = ip->daddr;
+	ipp->tos = ip->tos;
+	ipp->ttl = ip->ttl;
+
+	skb_pull(skb, sizeof(struct iphdr));
+
+	switch (ip->protocol) {
+	case IPPROTO_UDP:
+		ret = mtk_tops_udp_encap_param_setup(skb,
+						     params,
+						     tnl_encap_param_setup);
+		break;
+	case IPPROTO_GRE:
+		ret = tnl_encap_param_setup(skb, params);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	};
+
+	skb_push(skb, sizeof(struct iphdr));
+
+	return ret;
+}
+
+int mtk_tops_ip_decap_param_setup(struct sk_buff *skb, struct tops_params *params)
+{
+	struct tops_ip_params *ipp;
+	struct iphdr *ip;
+	struct iphdr iph;
+	int ret;
+
+	skb_push(skb, sizeof(struct iphdr));
+	ip = skb_header_pointer(skb, 0, sizeof(struct iphdr), &iph);
+	if (unlikely(!ip)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (unlikely(ip->version != IPVERSION)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	params->network.type = TOPS_NETWORK_IPV4;
+
+	ipp = &params->network.ip;
+
+	ipp->proto = ip->protocol;
+	ipp->sip = ip->daddr;
+	ipp->dip = ip->saddr;
+	ipp->tos = ip->tos;
+	/*
+	 * if encapsulation parameter is already configured, TTL will remain as
+	 * encapsulation's data
+	 */
+	ipp->ttl = 128;
+
+	ret = mtk_tops_mac_decap_param_setup(skb, params);
+
+out:
+	skb_pull(skb, sizeof(struct iphdr));
+
+	return ret;
+}
+
+static int tops_ip_debug_param_fetch_ip(const char *buf, int *ofs, u32 *ip)
+{
+	int nchar = 0;
+	int ret = 0;
+	u8 tmp[4];
+
+	ret = sscanf(buf + *ofs, "%hhu.%hhu.%hhu.%hhu %n",
+		&tmp[3], &tmp[2], &tmp[1], &tmp[0], &nchar);
+	if (ret != 4)
+		return -EPERM;
+
+	*ip = tmp[3] | tmp[2] << 8 | tmp[1] << 16 | tmp[0] << 24;
+
+	*ofs += nchar;
+
+	return 0;
+}
+
+int mtk_tops_ip_debug_param_setup(const char *buf, int *ofs,
+				  struct tops_params *params)
+{
+	char proto[DEBUG_PROTO_LEN] = {0};
+	int ret;
+
+	params->network.type = TOPS_NETWORK_IPV4;
+
+	ret = tops_ip_debug_param_fetch_ip(buf, ofs, &params->network.ip.sip);
+	if (ret)
+		return ret;
+
+	ret = tops_ip_debug_param_fetch_ip(buf, ofs, &params->network.ip.dip);
+	if (ret)
+		return ret;
+
+	ret = mtk_tops_debug_param_proto_peek(buf, *ofs, proto);
+	if (ret < 0)
+		return ret;
+
+	if (!strcmp(proto, DEBUG_PROTO_UDP)) {
+		params->network.ip.proto = IPPROTO_UDP;
+		*ofs += ret;
+		ret = mtk_tops_udp_debug_param_setup(buf, ofs, params);
+	} else if (!strcmp(proto, DEBUG_PROTO_GRETAP)) {
+		params->network.ip.proto = IPPROTO_GRE;
+		ret = 0;
+	} else {
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+void mtk_tops_ip_param_dump(struct seq_file *s, struct tops_params *params)
+{
+	struct tops_ip_params *ipp = &params->network.ip;
+	u32 sip = params->network.ip.sip;
+	u32 dip = params->network.ip.dip;
+
+	seq_puts(s, "\tNetwork Type: IPv4 ");
+	seq_printf(s, "sip: %pI4 dip: %pI4 protocol: 0x%02x tos: 0x%02x ttl: %03u\n",
+		   &sip, &dip, ipp->proto, ipp->tos, ipp->ttl);
+}
diff --git a/package-21.02/kernel/tops/src/protocol/transport/udp.c b/package-21.02/kernel/tops/src/protocol/transport/udp.c
new file mode 100644
index 0000000..ab653e0
--- /dev/null
+++ b/package-21.02/kernel/tops/src/protocol/transport/udp.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#include "tops/protocol/network/ip.h"
+#include "tops/protocol/transport/udp.h"
+
+int mtk_tops_udp_encap_param_setup(
+			struct sk_buff *skb,
+			struct tops_params *params,
+			int (*tnl_encap_param_setup)(struct sk_buff *skb,
+						     struct tops_params *params))
+{
+	struct tops_udp_params *udpp = &params->transport.udp;
+	struct udphdr *udp;
+	struct udphdr udph;
+	int ret;
+
+	udp = skb_header_pointer(skb, 0, sizeof(struct udphdr), &udph);
+	if (unlikely(!udp))
+		return -EINVAL;
+
+	params->transport.type = TOPS_TRANSPORT_UDP;
+
+	udpp->sport = udp->source;
+	udpp->dport = udp->dest;
+
+	skb_pull(skb, sizeof(struct udphdr));
+
+	/* udp must be the end of a tunnel */
+	ret = tnl_encap_param_setup(skb, params);
+
+	skb_push(skb, sizeof(struct udphdr));
+
+	return ret;
+}
+
+int mtk_tops_udp_decap_param_setup(struct sk_buff *skb, struct tops_params *params)
+{
+	struct tops_udp_params *udpp = &params->transport.udp;
+	struct udphdr *udp;
+	struct udphdr udph;
+	int ret;
+
+	skb_push(skb, sizeof(struct udphdr));
+	udp = skb_header_pointer(skb, 0, sizeof(struct udphdr), &udph);
+	if (unlikely(!udp)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	params->transport.type = TOPS_TRANSPORT_UDP;
+
+	udpp->sport = udp->dest;
+	udpp->dport = udp->source;
+
+	ret = mtk_tops_network_decap_param_setup(skb, params);
+
+out:
+	skb_pull(skb, sizeof(struct udphdr));
+
+	return ret;
+}
+
+static int tops_udp_debug_param_fetch_port(const char *buf, int *ofs, u16 *port)
+{
+	int nchar = 0;
+	int ret;
+	u16 p = 0;
+
+	ret = sscanf(buf + *ofs, "%hu %n", &p, &nchar);
+	if (ret != 1)
+		return -EPERM;
+
+	*port = htons(p);
+
+	*ofs += nchar;
+
+	return 0;
+}
+
+int mtk_tops_udp_debug_param_setup(const char *buf, int *ofs,
+				   struct tops_params *params)
+{
+	int ret;
+
+	params->transport.type = TOPS_TRANSPORT_UDP;
+
+	ret = tops_udp_debug_param_fetch_port(buf, ofs, &params->transport.udp.sport);
+	if (ret)
+		return ret;
+
+	ret = tops_udp_debug_param_fetch_port(buf, ofs, &params->transport.udp.dport);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+void mtk_tops_udp_param_dump(struct seq_file *s, struct tops_params *params)
+{
+	struct tops_udp_params *udpp = &params->transport.udp;
+
+	seq_puts(s, "\tTransport Type: UDP ");
+	seq_printf(s, "sport: %05u dport: %05u\n",
+		   ntohs(udpp->sport), ntohs(udpp->dport));
+}
diff --git a/package-21.02/kernel/tops/src/protocol/tunnel/gre/gretap.c b/package-21.02/kernel/tops/src/protocol/tunnel/gre/gretap.c
new file mode 100644
index 0000000..13618ce
--- /dev/null
+++ b/package-21.02/kernel/tops/src/protocol/tunnel/gre/gretap.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#include <net/gre.h>
+
+#include <pce/cls.h>
+#include <pce/netsys.h>
+#include <pce/pce.h>
+
+#include "tops/internal.h"
+#include "tops/protocol/tunnel/gre/gretap.h"
+#include "tops/tunnel.h"
+
+static int gretap_cls_entry_setup(struct tops_tnl_info *tnl_info,
+				  struct cls_desc *cdesc)
+{
+	CLS_DESC_DATA(cdesc, fport, PSE_PORT_PPE0);
+	CLS_DESC_DATA(cdesc, tport_idx, 0x4);
+	CLS_DESC_MASK_DATA(cdesc, tag, CLS_DESC_TAG_MASK, CLS_DESC_TAG_MATCH_L4_HDR);
+	CLS_DESC_MASK_DATA(cdesc, dip_match, CLS_DESC_DIP_MATCH, CLS_DESC_DIP_MATCH);
+	CLS_DESC_MASK_DATA(cdesc, l4_type, CLS_DESC_L4_TYPE_MASK, IPPROTO_GRE);
+	CLS_DESC_MASK_DATA(cdesc, l4_udp_hdr_nez,
+			   CLS_DESC_UDPLITE_L4_HDR_NEZ_MASK,
+			   CLS_DESC_UDPLITE_L4_HDR_NEZ_MASK);
+	CLS_DESC_MASK_DATA(cdesc, l4_valid,
+			   CLS_DESC_L4_VALID_MASK,
+			   CLS_DESC_VALID_UPPER_HALF_WORD_BIT |
+			   CLS_DESC_VALID_LOWER_HALF_WORD_BIT);
+	CLS_DESC_MASK_DATA(cdesc, l4_hdr_usr_data, 0x0000FFFF, 0x00006558);
+
+	return 0;
+}
+
+static int gretap_tnl_encap_param_setup(struct sk_buff *skb, struct tops_params *params)
+{
+	params->tunnel.type = TOPS_TUNNEL_GRETAP;
+
+	return 0;
+}
+
+static int gretap_tnl_decap_param_setup(struct sk_buff *skb, struct tops_params *params)
+{
+	struct gre_base_hdr *pgre;
+	struct gre_base_hdr greh;
+	int ret;
+
+	if (!skb->dev->rtnl_link_ops
+	    || strcmp(skb->dev->rtnl_link_ops->kind, "gretap"))
+		return -EAGAIN;
+
+	skb_push(skb, sizeof(struct gre_base_hdr));
+	pgre = skb_header_pointer(skb, 0, sizeof(struct gre_base_hdr), &greh);
+	if (unlikely(!pgre)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (unlikely(ntohs(pgre->protocol) != ETH_P_TEB)) {
+		TOPS_NOTICE("gre: %p protocol unmatched, proto: 0x%x\n",
+			    pgre, ntohs(pgre->protocol));
+		ret = -EINVAL;
+		goto out;
+	}
+
+	params->tunnel.type = TOPS_TUNNEL_GRETAP;
+
+	ret = mtk_tops_network_decap_param_setup(skb, params);
+
+out:
+	skb_pull(skb, sizeof(struct gre_base_hdr));
+
+	return ret;
+}
+
+static int gretap_tnl_debug_param_setup(const char *buf, int *ofs,
+					struct tops_params *params)
+{
+	params->tunnel.type = TOPS_TUNNEL_GRETAP;
+
+	return 0;
+}
+
+static bool gretap_tnl_decap_offloadable(struct sk_buff *skb)
+{
+	struct iphdr *ip = ip_hdr(skb);
+
+	if (ip->protocol != IPPROTO_GRE)
+		return false;
+
+	return true;
+}
+
+static void gretap_tnl_param_dump(struct seq_file *s, struct tops_params *params)
+{
+	seq_puts(s, "\tTunnel Type: GRETAP\n");
+}
+
+static bool gretap_tnl_param_match(struct tops_params *p, struct tops_params *target)
+{
+	return !memcmp(&p->tunnel, &target->tunnel, sizeof(struct tops_tunnel_params));
+}
+
+static struct tops_tnl_type gretap_type = {
+	.type_name = "gretap",
+	.cls_entry_setup = gretap_cls_entry_setup,
+	.tnl_decap_param_setup = gretap_tnl_decap_param_setup,
+	.tnl_encap_param_setup = gretap_tnl_encap_param_setup,
+	.tnl_debug_param_setup = gretap_tnl_debug_param_setup,
+	.tnl_decap_offloadable = gretap_tnl_decap_offloadable,
+	.tnl_param_match = gretap_tnl_param_match,
+	.tnl_param_dump = gretap_tnl_param_dump,
+	.tnl_proto_type = TOPS_TUNNEL_GRETAP,
+	.has_inner_eth = true,
+};
+
+int mtk_tops_gretap_init(void)
+{
+	return mtk_tops_tnl_type_register(&gretap_type);
+}
+
+void mtk_tops_gretap_deinit(void)
+{
+	mtk_tops_tnl_type_unregister(&gretap_type);
+}
diff --git a/package-21.02/kernel/tops/src/protocol/tunnel/l2tp/l2tpv2.c b/package-21.02/kernel/tops/src/protocol/tunnel/l2tp/l2tpv2.c
new file mode 100644
index 0000000..d00d3c0
--- /dev/null
+++ b/package-21.02/kernel/tops/src/protocol/tunnel/l2tp/l2tpv2.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Frank-zj Lin <rank-zj.lin@mediatek.com>
+ *         Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#include <linux/netdevice.h>
+
+#include <pce/cls.h>
+#include <pce/netsys.h>
+#include <pce/pce.h>
+
+#include "tops/internal.h"
+#include "tops/protocol/mac/ppp.h"
+#include "tops/protocol/transport/udp.h"
+#include "tops/protocol/tunnel/l2tp/l2tpv2.h"
+#include "tops/tunnel.h"
+
+static int l2tpv2_cls_entry_setup(struct tops_tnl_info *tnl_info,
+				  struct cls_desc *cdesc)
+{
+	CLS_DESC_DATA(cdesc, fport, PSE_PORT_PPE0);
+	CLS_DESC_DATA(cdesc, tport_idx, 0x4);
+	CLS_DESC_MASK_DATA(cdesc, tag, CLS_DESC_TAG_MASK, CLS_DESC_TAG_MATCH_L4_USR);
+	CLS_DESC_MASK_DATA(cdesc, dip_match, CLS_DESC_DIP_MATCH, CLS_DESC_DIP_MATCH);
+	CLS_DESC_MASK_DATA(cdesc, l4_type, CLS_DESC_L4_TYPE_MASK, IPPROTO_UDP);
+	CLS_DESC_MASK_DATA(cdesc, l4_valid,
+			   CLS_DESC_L4_VALID_MASK,
+			   CLS_DESC_VALID_UPPER_HALF_WORD_BIT |
+			   CLS_DESC_VALID_LOWER_HALF_WORD_BIT |
+			   CLS_DESC_VALID_DPORT_BIT);
+	CLS_DESC_MASK_DATA(cdesc, l4_dport, CLS_DESC_L4_DPORT_MASK, 1701);
+	CLS_DESC_MASK_DATA(cdesc, l4_hdr_usr_data, 0x80030000, 0x00020000);
+
+	return 0;
+}
+
+static inline bool l2tpv2_offload_valid(struct sk_buff *skb)
+{
+	struct udp_l2tp_data_hdr *l2tp;
+	struct udp_l2tp_data_hdr l2tph;
+	u16 hdrflags;
+
+	l2tp = skb_header_pointer(skb, 0, sizeof(struct udp_l2tp_data_hdr), &l2tph);
+	if (!l2tp)
+		return false;
+
+	hdrflags = ntohs(l2tp->flag_ver);
+
+	return ((hdrflags & L2TP_HDR_VER_MASK) == L2TP_HDR_VER_2 &&
+		!(hdrflags & L2TP_HDRFLAG_T));
+}
+
+static int l2tpv2_tnl_decap_param_setup(struct sk_buff *skb,
+					struct tops_params *params)
+{
+	struct tops_l2tp_params *l2tpp;
+	struct udp_l2tp_data_hdr *l2tp;
+	struct udp_l2tp_data_hdr l2tph;
+	int ret = 0;
+
+	/* ppp */
+	skb_push(skb, sizeof(struct ppp_hdr));
+	if (unlikely(!mtk_tops_ppp_valid(skb))) {
+		ret = -EINVAL;
+		goto restore_ppp;
+	}
+
+	/* l2tp */
+	skb_push(skb, sizeof(struct udp_l2tp_data_hdr));
+	if (unlikely(!l2tpv2_offload_valid(skb))) {
+		ret = -EINVAL;
+		goto restore_l2tp;
+	}
+
+	l2tp = skb_header_pointer(skb, 0, sizeof(struct udp_l2tp_data_hdr), &l2tph);
+	if (unlikely(!l2tp)) {
+		ret = -EINVAL;
+		goto restore_l2tp;
+	}
+
+	params->tunnel.type = TOPS_TUNNEL_L2TP_V2;
+
+	l2tpp = &params->tunnel.l2tp;
+	l2tpp->tid = l2tp->tid;
+	l2tpp->sid = l2tp->sid;
+
+	ret = mtk_tops_transport_decap_param_setup(skb, params);
+
+restore_l2tp:
+	skb_pull(skb, sizeof(struct udp_l2tp_data_hdr));
+
+restore_ppp:
+	skb_pull(skb, sizeof(struct ppp_hdr));
+
+	return ret;
+}
+
+static int l2tpv2_tnl_encap_param_setup(struct sk_buff *skb,
+					struct tops_params *params)
+{
+	struct tops_l2tp_params *l2tpp;
+	struct udp_l2tp_data_hdr *l2tp;
+	struct udp_l2tp_data_hdr l2tph;
+
+	if (unlikely(!l2tpv2_offload_valid(skb)))
+		return -EINVAL;
+
+	l2tp = skb_header_pointer(skb, 0, sizeof(struct udp_l2tp_data_hdr), &l2tph);
+	if (unlikely(!l2tp))
+		return -EINVAL;
+
+	params->tunnel.type = TOPS_TUNNEL_L2TP_V2;
+
+	l2tpp = &params->tunnel.l2tp;
+	l2tpp->tid = l2tp->tid;
+	l2tpp->sid = l2tp->sid;
+
+	return 0;
+}
+
+static int l2tpv2_tnl_debug_param_setup(const char *buf, int *ofs,
+					struct tops_params *params)
+{
+	struct tops_l2tp_params *l2tpp;
+	int nchar = 0;
+	int ret;
+	u16 tid = 0;
+	u16 sid = 0;
+
+	params->tunnel.type = TOPS_TUNNEL_L2TP_V2;
+	l2tpp = &params->tunnel.l2tp;
+
+	ret = sscanf(buf + *ofs, "%hu %hu %n", &tid, &sid, &nchar);
+	if (ret != 2)
+		return -EINVAL;
+
+	l2tpp->tid = htons(tid);
+	l2tpp->sid = htons(sid);
+
+	*ofs += nchar;
+
+	return 0;
+}
+
+static int l2tpv2_tnl_l2_param_update(struct sk_buff *skb,
+				      struct tops_params *params)
+{
+	struct ethhdr *eth = eth_hdr(skb);
+	struct tops_mac_params *mac = &params->mac;
+
+	memcpy(&mac->eth.h_source, eth->h_source, sizeof(u8) * ETH_ALEN);
+	memcpy(&mac->eth.h_dest, eth->h_dest, sizeof(u8) * ETH_ALEN);
+
+	return 1;
+}
+
+static bool l2tpv2_tnl_decap_offloadable(struct sk_buff *skb)
+{
+	struct iphdr *ip;
+	bool ret = true;
+	u32 ip_len;
+
+	ip = ip_hdr(skb);
+	if (ip->protocol != IPPROTO_UDP)
+		return false;
+
+	ip_len = ip_hdr(skb)->ihl * 4;
+
+	skb_pull(skb, ip_len + sizeof(struct udphdr));
+	if (!l2tpv2_offload_valid(skb)) {
+		ret = false;
+		goto restore_ip_udp;
+	}
+
+	skb_pull(skb, sizeof(struct udp_l2tp_data_hdr));
+	if (!mtk_tops_ppp_valid(skb)) {
+		ret = false;
+		goto restore_l2tp;
+	}
+
+restore_l2tp:
+	skb_push(skb, sizeof(struct udp_l2tp_data_hdr));
+restore_ip_udp:
+	skb_push(skb, ip_len + sizeof(struct udphdr));
+
+	return ret;
+}
+
+static void l2tpv2_tnl_param_dump(struct seq_file *s, struct tops_params *params)
+{
+	struct tops_l2tp_params *l2tpp = &params->tunnel.l2tp;
+
+	seq_puts(s, "\tTunnel Type: L2TPv2 ");
+	seq_printf(s, "tunnel ID: %05u session ID: %05u\n",
+		   ntohs(l2tpp->tid), ntohs(l2tpp->sid));
+}
+
+static struct tops_tnl_type l2tpv2_type = {
+	.type_name = "l2tpv2",
+	.cls_entry_setup = l2tpv2_cls_entry_setup,
+	.tnl_decap_param_setup = l2tpv2_tnl_decap_param_setup,
+	.tnl_encap_param_setup = l2tpv2_tnl_encap_param_setup,
+	.tnl_debug_param_setup = l2tpv2_tnl_debug_param_setup,
+	.tnl_decap_offloadable = l2tpv2_tnl_decap_offloadable,
+	.tnl_l2_param_update = l2tpv2_tnl_l2_param_update,
+	.tnl_param_dump = l2tpv2_tnl_param_dump,
+	.tnl_proto_type = TOPS_TUNNEL_L2TP_V2,
+	.has_inner_eth = false,
+};
+
+int mtk_tops_l2tpv2_init(void)
+{
+	return mtk_tops_tnl_type_register(&l2tpv2_type);
+}
+
+void mtk_tops_l2tpv2_deinit(void)
+{
+	mtk_tops_tnl_type_unregister(&l2tpv2_type);
+}
diff --git a/package-21.02/kernel/tops/src/ser.c b/package-21.02/kernel/tops/src/ser.c
index a126185..0a0fa6c 100644
--- a/package-21.02/kernel/tops/src/ser.c
+++ b/package-21.02/kernel/tops/src/ser.c
@@ -10,10 +10,10 @@
 #include <linux/netdevice.h>
 #include <linux/rtnetlink.h>
 
-#include "internal.h"
-#include "net-event.h"
-#include "ser.h"
-#include "trm.h"
+#include "tops/internal.h"
+#include "tops/net-event.h"
+#include "tops/ser.h"
+#include "tops/trm.h"
 
 struct tops_ser {
 	struct work_struct work;
diff --git a/package-21.02/kernel/tops/src/tdma.c b/package-21.02/kernel/tops/src/tdma.c
index c7053e7..7eff39f 100644
--- a/package-21.02/kernel/tops/src/tdma.c
+++ b/package-21.02/kernel/tops/src/tdma.c
@@ -14,12 +14,12 @@
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
 
-#include "internal.h"
-#include "mbox.h"
-#include "mcu.h"
-#include "tdma.h"
-#include "tops.h"
-#include "trm.h"
+#include "tops/internal.h"
+#include "tops/mbox.h"
+#include "tops/mcu.h"
+#include "tops/tdma.h"
+#include "tops/tops.h"
+#include "tops/trm.h"
 
 /* TDMA dump length */
 #define TDMA_BASE_LEN				(0x400)
diff --git a/package-21.02/kernel/tops/src/tnl_offload.c b/package-21.02/kernel/tops/src/tnl_offload.c
index 268e3ee..714139b 100644
--- a/package-21.02/kernel/tops/src/tnl_offload.c
+++ b/package-21.02/kernel/tops/src/tnl_offload.c
@@ -28,20 +28,20 @@
 #include <pce/netsys.h>
 #include <pce/pce.h>
 
-#include "internal.h"
-#include "mbox.h"
-#include "mcu.h"
-#include "netsys.h"
-#include "protocol/gre/gretap.h"
-#include "protocol/l2tp/udp_l2tp_data.h"
-#include "tunnel.h"
+#include "tops/internal.h"
+#include "tops/mbox.h"
+#include "tops/mcu.h"
+#include "tops/netsys.h"
+#include "tops/protocol/tunnel/gre/gretap.h"
+#include "tops/protocol/tunnel/l2tp/l2tpv2.h"
+#include "tops/tunnel.h"
 
 #define TOPS_PPE_ENTRY_BUCKETS		(64)
 #define TOPS_PPE_ENTRY_BUCKETS_BIT	(6)
 
 struct tops_tnl {
 	/* tunnel types */
-	struct tops_tnl_type *offload_tnl_types[__TOPS_ENTRY_MAX];
+	struct tops_tnl_type *offload_tnl_types[__TOPS_TUNNEL_TYPE_MAX];
 	u32 offload_tnl_type_num;
 	u32 tnl_base_addr;
 
@@ -104,7 +104,7 @@
 		    &&  entry->ipv4_hnapt.tport_id != NR_TDMA_QDMA_TPORT)
 			return;
 
-		bind_tnl_idx = entry->ipv4_hnapt.tops_entry - __TOPS_ENTRY_MAX;
+		bind_tnl_idx = entry->ipv4_hnapt.tops_entry - __TOPS_TUNNEL_TYPE_MAX;
 
 		break;
 	default:
@@ -112,39 +112,39 @@
 	}
 
 	/* unexpected tunnel index */
-	if (bind_tnl_idx >= __TOPS_ENTRY_MAX)
+	if (bind_tnl_idx >= __TOPS_TUNNEL_TYPE_MAX)
 		return;
 
-	if (tnl_idx == __TOPS_ENTRY_MAX || tnl_idx == bind_tnl_idx)
+	if (tnl_idx == __TOPS_TUNNEL_TYPE_MAX || tnl_idx == bind_tnl_idx)
 		memset(entry, 0, sizeof(*entry));
 }
 
 static inline void skb_set_tops_tnl_idx(struct sk_buff *skb, u32 tnl_idx)
 {
-	skb_hnat_tops(skb) = tnl_idx + __TOPS_ENTRY_MAX;
+	skb_hnat_tops(skb) = tnl_idx + __TOPS_TUNNEL_TYPE_MAX;
 }
 
 static inline bool skb_tops_valid(struct sk_buff *skb)
 {
-	return (skb && skb_hnat_tops(skb) < __TOPS_ENTRY_MAX);
+	return (skb && skb_hnat_tops(skb) < __TOPS_TUNNEL_TYPE_MAX);
 }
 
 static inline struct tops_tnl_type *skb_to_tnl_type(struct sk_buff *skb)
 {
-	enum tops_entry_type tops_entry = skb_hnat_tops(skb);
+	enum tops_tunnel_type tnl_proto_type = skb_hnat_tops(skb);
 	struct tops_tnl_type *tnl_type;
 
-	if (unlikely(!tops_entry || tops_entry >= __TOPS_ENTRY_MAX))
+	if (unlikely(!tnl_proto_type || tnl_proto_type >= __TOPS_TUNNEL_TYPE_MAX))
 		return ERR_PTR(-EINVAL);
 
-	tnl_type = tops_tnl.offload_tnl_types[tops_entry];
+	tnl_type = tops_tnl.offload_tnl_types[tnl_proto_type];
 
 	return tnl_type ? tnl_type : ERR_PTR(-ENODEV);
 }
 
 static inline struct tops_tnl_info *skb_to_tnl_info(struct sk_buff *skb)
 {
-	u32 tnl_idx = skb_hnat_tops(skb) - __TOPS_ENTRY_MAX;
+	u32 tnl_idx = skb_hnat_tops(skb) - __TOPS_TUNNEL_TYPE_MAX;
 
 	if (tnl_idx >= CONFIG_TOPS_TNL_NUM)
 		return ERR_PTR(-EINVAL);
@@ -168,7 +168,7 @@
 		return 0;
 
 	/* TODO: check collision possibility? */
-	return (tnl_params->sip ^ tnl_params->dip);
+	return (tnl_params->params.network.ip.sip ^ tnl_params->params.network.ip.dip);
 }
 
 static inline bool tnl_info_decap_is_enable(struct tops_tnl_info *tnl_info)
@@ -404,6 +404,10 @@
 			ret = PTR_ERR(cdrt);
 			goto free_tcls;
 		}
+		if (unlikely(!cdrt->cls)) {
+			ret = -ENODEV;
+			goto free_tcls;
+		}
 
 		tcls->cls = cdrt->cls;
 	}
@@ -593,7 +597,7 @@
 		if (IS_ERR(cdrt)) {
 			TOPS_ERR("no cdrt idx: %u related CDRT found\n",
 				 tnl_params->cdrt);
-			return ret;
+			return PTR_ERR(cdrt);
 		}
 
 		memcpy(&cdesc, &cdrt->cls->cdesc, sizeof(struct cls_desc));
@@ -657,7 +661,7 @@
 
 	memset(&dipd, 0, sizeof(struct dip_desc));
 
-	dipd.ipv4 = be32_to_cpu(tnl_info->tnl_params.sip);
+	dipd.ipv4 = be32_to_cpu(tnl_info->tnl_params.params.network.ip.sip);
 	dipd.tag = DIPFILTER_IPV4;
 
 	return mtk_pce_dipfilter_entry_del(&dipd);
@@ -670,7 +674,7 @@
 	/* setup dipfilter */
 	memset(&dipd, 0, sizeof(struct dip_desc));
 
-	dipd.ipv4 = be32_to_cpu(tnl_info->tnl_params.sip);
+	dipd.ipv4 = be32_to_cpu(tnl_info->tnl_params.params.network.ip.sip);
 	dipd.tag = DIPFILTER_IPV4;
 
 	return mtk_pce_dipfilter_entry_add(&dipd);
@@ -731,69 +735,92 @@
 	spin_unlock_irqrestore(&tops_tnl.tbl_lock, flag);
 }
 
+struct tops_tnl_info *mtk_tops_tnl_info_get_by_idx(u32 tnl_idx)
+{
+	if (tnl_idx >= CONFIG_TOPS_TNL_NUM)
+		return ERR_PTR(-EINVAL);
+
+	if (!test_bit(tnl_idx, tops_tnl.tnl_used))
+		return ERR_PTR(-EACCES);
+
+	return &tops_tnl.tnl_infos[tnl_idx];
+}
+
 static bool mtk_tops_tnl_info_match(struct tops_tnl_type *tnl_type,
 				    struct tops_tnl_info *tnl_info,
-				    struct tops_tnl_params *match_data)
+				    struct tops_params *target)
 {
+	struct tops_params *p = &tnl_info->cache.params;
 	unsigned long flag = 0;
 	bool match;
 
 	spin_lock_irqsave(&tnl_info->lock, flag);
 
-	match = tnl_type->tnl_info_match(&tnl_info->cache, match_data);
+	match = (p->tunnel.type == target->tunnel.type
+		 && mtk_tops_params_match(p, target)
+		 && tnl_type->tnl_param_match(p, target));
 
 	spin_unlock_irqrestore(&tnl_info->lock, flag);
 
 	return match;
 }
 
-struct tops_tnl_info *mtk_tops_tnl_info_find(struct tops_tnl_params *tnl_params)
+struct tops_tnl_info *mtk_tops_tnl_info_find(struct tops_tnl_type *tnl_type,
+					     struct tops_tnl_params *tnl_params)
 {
 	struct tops_tnl_info *tnl_info;
-	struct tops_tnl_type *tnl_type;
 
 	lockdep_assert_held(&tops_tnl.tbl_lock);
 
 	if (unlikely(!tnl_params->tops_entry_proto
-		     || tnl_params->tops_entry_proto >= __TOPS_ENTRY_MAX))
-		return ERR_PTR(-EINVAL);
-
-	tnl_type = tops_tnl.offload_tnl_types[tnl_params->tops_entry_proto];
-	if (unlikely(!tnl_type))
+		     || tnl_params->tops_entry_proto >= __TOPS_TUNNEL_TYPE_MAX))
 		return ERR_PTR(-EINVAL);
 
-	if (unlikely(!tnl_type->tnl_info_match))
-		return ERR_PTR(-ENXIO);
-
 	hash_for_each_possible(tops_tnl.ht,
 			       tnl_info,
 			       hlist,
 			       tnl_params_hash(tnl_params))
-		if (mtk_tops_tnl_info_match(tnl_type, tnl_info, tnl_params))
+		if (mtk_tops_tnl_info_match(tnl_type, tnl_info, &tnl_params->params))
 			return tnl_info;
 
 	return ERR_PTR(-ENODEV);
 }
 
+static inline void mtk_tops_tnl_info_preserve(struct tops_tnl_type *tnl_type,
+					      struct tops_tnl_params *old,
+					      struct tops_tnl_params *new)
+{
+	new->flag |= old->flag;
+	new->cls_entry = old->cls_entry;
+	if (old->cdrt)
+		new->cdrt = old->cdrt;
+
+	/* we can only get ttl from encapsulation */
+	if (new->params.network.ip.ttl == 128 && old->params.network.ip.ttl != 0)
+		new->params.network.ip.ttl = old->params.network.ip.ttl;
+
+	if (tnl_type->tnl_param_restore)
+		tnl_type->tnl_param_restore(&old->params, &new->params);
+}
+
 /* tnl_info->lock should be held before calling this function */
 static int mtk_tops_tnl_info_setup(struct sk_buff *skb,
+				   struct tops_tnl_type *tnl_type,
 				   struct tops_tnl_info *tnl_info,
 				   struct tops_tnl_params *tnl_params)
 {
+	bool has_diff = false;
+
 	if (unlikely(!skb || !tnl_info || !tnl_params))
 		return -EPERM;
 
 	lockdep_assert_held(&tnl_info->lock);
 
-	/* manually preserve essential data among encapsulation and decapsulation */
-	tnl_params->flag |= tnl_info->cache.flag;
-	tnl_params->cls_entry = tnl_info->cache.cls_entry;
-	if (tnl_info->cache.cdrt)
-		tnl_params->cdrt = tnl_info->cache.cdrt;
-
-	if (memcmp(&tnl_info->cache, tnl_params, sizeof(struct tops_tnl_params))) {
-		memcpy(&tnl_info->cache, tnl_params, sizeof(struct tops_tnl_params));
+	mtk_tops_tnl_info_preserve(tnl_type, &tnl_info->cache, tnl_params);
 
+	has_diff = memcmp(&tnl_info->cache, tnl_params, sizeof(*tnl_params));
+	if (has_diff) {
+		memcpy(&tnl_info->cache, tnl_params, sizeof(*tnl_params));
 		mtk_tops_tnl_info_hash_no_lock(tnl_info);
 	}
 
@@ -801,20 +828,21 @@
 		/* the net_device is used to forward pkt to decap'ed inf when Rx */
 		tnl_info->dev = skb->dev;
 		if (!tnl_info_decap_is_enable(tnl_info)) {
+			has_diff = true;
 			tnl_info_decap_enable(tnl_info);
-
-			mtk_tops_tnl_info_submit_no_tnl_lock(tnl_info);
 		}
 	} else if (skb_hnat_is_encap(skb)) {
 		/* set skb_hnat_tops(skb) to tunnel index for ppe binding */
 		skb_set_tops_tnl_idx(skb, tnl_info->tnl_idx);
 		if (!tnl_info_encap_is_enable(tnl_info)) {
+			has_diff = true;
 			tnl_info_encap_enable(tnl_info);
-
-			mtk_tops_tnl_info_submit_no_tnl_lock(tnl_info);
 		}
 	}
 
+	if (has_diff)
+		mtk_tops_tnl_info_submit_no_tnl_lock(tnl_info);
+
 	return 0;
 }
 
@@ -926,7 +954,7 @@
 	/* prepare tnl_info */
 	spin_lock_irqsave(&tops_tnl.tbl_lock, flag);
 
-	tnl_info = mtk_tops_tnl_info_find(tnl_params);
+	tnl_info = mtk_tops_tnl_info_find(tnl_type, tnl_params);
 	if (IS_ERR(tnl_info) && PTR_ERR(tnl_info) != -ENODEV) {
 		/* error */
 		ret = PTR_ERR(tnl_info);
@@ -943,7 +971,7 @@
 	}
 
 	spin_lock(&tnl_info->lock);
-	ret = mtk_tops_tnl_info_setup(skb, tnl_info, tnl_params);
+	ret = mtk_tops_tnl_info_setup(skb, tnl_type, tnl_info, tnl_params);
 	spin_unlock(&tnl_info->lock);
 
 err_out:
@@ -968,7 +996,7 @@
 
 	spin_lock_irqsave(&tnl_info->lock, flag);
 
-	ret = tnl_type->tnl_l2_param_update(skb, &tnl_info->cache);
+	ret = tnl_type->tnl_l2_param_update(skb, &tnl_info->cache.params);
 	/* tnl params need to be updated */
 	if (ret == 1) {
 		mtk_tops_tnl_info_submit_no_tnl_lock(tnl_info);
@@ -1003,8 +1031,8 @@
 		return false;
 
 	/* TODO: may can be optimized */
-	for (i = TOPS_ENTRY_GRETAP, cnt = 0;
-	     i < __TOPS_ENTRY_MAX && cnt < tops_tnl.offload_tnl_type_num;
+	for (i = TOPS_TUNNEL_GRETAP, cnt = 0;
+	     i < __TOPS_TUNNEL_TYPE_MAX && cnt < tops_tnl.offload_tnl_type_num;
 	     i++) {
 		tnl_type = tops_tnl.offload_tnl_types[i];
 		if (unlikely(!tnl_type))
@@ -1013,7 +1041,7 @@
 		cnt++;
 		if (tnl_type->tnl_decap_offloadable
 		    && tnl_type->tnl_decap_offloadable(skb)) {
-			skb_hnat_tops(skb) = tnl_type->tops_entry;
+			skb_hnat_tops(skb) = tnl_type->tnl_proto_type;
 			return true;
 		}
 	}
@@ -1043,7 +1071,7 @@
 		return PTR_ERR(tnl_type);
 	}
 
-	if (unlikely(!tnl_type->tnl_decap_param_setup)) {
+	if (unlikely(!tnl_type->tnl_decap_param_setup || !tnl_type->tnl_param_match)) {
 		skb_mark_unbind(skb);
 		return -ENODEV;
 	}
@@ -1054,7 +1082,9 @@
 	if (tnl_type->has_inner_eth)
 		skb_push(skb, sizeof(struct ethhdr));
 
-	ret = tnl_type->tnl_decap_param_setup(skb, &tnl_params);
+	ret = mtk_tops_decap_param_setup(skb,
+					 &tnl_params.params,
+					 tnl_type->tnl_decap_param_setup);
 
 	/* pull ethernet header to restore skb->data to ip start */
 	if (tnl_type->has_inner_eth)
@@ -1065,7 +1095,7 @@
 		return ret;
 	}
 
-	tnl_params.tops_entry_proto = tnl_type->tops_entry;
+	tnl_params.tops_entry_proto = tnl_type->tnl_proto_type;
 	tnl_params.cdrt = skb_hnat_cdrt(skb);
 
 	ret = mtk_tops_tnl_offload(skb, tnl_type, &tnl_params);
@@ -1091,15 +1121,18 @@
 	if (IS_ERR(tnl_type))
 		return PTR_ERR(tnl_type);
 
-	if (unlikely(!tnl_type->tnl_encap_param_setup))
+	if (unlikely(!tnl_type->tnl_encap_param_setup || !tnl_type->tnl_param_match))
 		return -ENODEV;
 
 	memset(&tnl_params, 0, sizeof(struct tops_tnl_params));
 
-	ret = tnl_type->tnl_encap_param_setup(skb, &tnl_params);
+	ret = mtk_tops_encap_param_setup(skb,
+					 &tnl_params.params,
+					 tnl_type->tnl_encap_param_setup);
 	if (unlikely(ret))
 		return ret;
-	tnl_params.tops_entry_proto = tnl_type->tops_entry;
+
+	tnl_params.tops_entry_proto = tnl_type->tnl_proto_type;
 	tnl_params.cdrt = skb_hnat_cdrt(skb);
 
 	return mtk_tops_tnl_offload(skb, tnl_type, &tnl_params);
@@ -1535,7 +1568,7 @@
 			if (!entry_hnat_is_bound(entry))
 				continue;
 
-			tnl_flush_ppe_entry(entry, __TOPS_ENTRY_MAX);
+			tnl_flush_ppe_entry(entry, __TOPS_TUNNEL_TYPE_MAX);
 		}
 	}
 	hnat_cache_ebl(1);
@@ -1672,7 +1705,7 @@
 {
 	mtk_tops_gretap_init();
 
-	mtk_tops_udp_l2tp_data_init();
+	mtk_tops_l2tpv2_init();
 
 	return 0;
 }
@@ -1681,19 +1714,19 @@
 {
 	mtk_tops_gretap_deinit();
 
-	mtk_tops_udp_l2tp_data_deinit();
+	mtk_tops_l2tpv2_deinit();
 }
 
 struct tops_tnl_type *mtk_tops_tnl_type_get_by_name(const char *name)
 {
-	enum tops_entry_type tops_entry = TOPS_ENTRY_NONE + 1;
+	enum tops_tunnel_type tnl_proto_type = TOPS_TUNNEL_NONE + 1;
 	struct tops_tnl_type *tnl_type;
 
 	if (unlikely(!name))
 		return ERR_PTR(-EPERM);
 
-	for (; tops_entry < __TOPS_ENTRY_MAX; tops_entry++) {
-		tnl_type = tops_tnl.offload_tnl_types[tops_entry];
+	for (; tnl_proto_type < __TOPS_TUNNEL_TYPE_MAX; tnl_proto_type++) {
+		tnl_type = tops_tnl.offload_tnl_types[tnl_proto_type];
 		if (tnl_type && !strcmp(name, tnl_type->type_name))
 			break;
 	}
@@ -1703,24 +1736,25 @@
 
 int mtk_tops_tnl_type_register(struct tops_tnl_type *tnl_type)
 {
-	enum tops_entry_type tops_entry = tnl_type->tops_entry;
+	enum tops_tunnel_type tnl_proto_type = tnl_type->tnl_proto_type;
 
-	if (unlikely(tops_entry == TOPS_ENTRY_NONE
-		     || tops_entry >= __TOPS_ENTRY_MAX)) {
-		TOPS_ERR("invalid tops_entry: %u\n", tops_entry);
+	if (unlikely(tnl_proto_type == TOPS_TUNNEL_NONE
+		     || tnl_proto_type >= __TOPS_TUNNEL_TYPE_MAX)) {
+		TOPS_ERR("invalid tnl_proto_type: %u\n", tnl_proto_type);
 		return -EINVAL;
 	}
 
 	if (unlikely(!tnl_type))
 		return -EINVAL;
 
-	if (tops_tnl.offload_tnl_types[tops_entry]) {
-		TOPS_ERR("offload tnl type is already registered: %u\n", tops_entry);
+	if (tops_tnl.offload_tnl_types[tnl_proto_type]) {
+		TOPS_ERR("offload tnl type is already registered: %u\n",
+			 tnl_proto_type);
 		return -EBUSY;
 	}
 
 	INIT_LIST_HEAD(&tnl_type->tcls_head);
-	tops_tnl.offload_tnl_types[tops_entry] = tnl_type;
+	tops_tnl.offload_tnl_types[tnl_proto_type] = tnl_type;
 	tops_tnl.offload_tnl_type_num++;
 
 	return 0;
@@ -1728,22 +1762,22 @@
 
 void mtk_tops_tnl_type_unregister(struct tops_tnl_type *tnl_type)
 {
-	enum tops_entry_type tops_entry = tnl_type->tops_entry;
+	enum tops_tunnel_type tnl_proto_type = tnl_type->tnl_proto_type;
 
-	if (unlikely(tops_entry == TOPS_ENTRY_NONE
-		     || tops_entry >= __TOPS_ENTRY_MAX)) {
-		TOPS_ERR("invalid tops_entry: %u\n", tops_entry);
+	if (unlikely(tnl_proto_type == TOPS_TUNNEL_NONE
+		     || tnl_proto_type >= __TOPS_TUNNEL_TYPE_MAX)) {
+		TOPS_ERR("invalid tnl_proto_type: %u\n", tnl_proto_type);
 		return;
 	}
 
 	if (unlikely(!tnl_type))
 		return;
 
-	if (tops_tnl.offload_tnl_types[tops_entry] != tnl_type) {
+	if (tops_tnl.offload_tnl_types[tnl_proto_type] != tnl_type) {
 		TOPS_ERR("offload tnl type is registered by others\n");
 		return;
 	}
 
-	tops_tnl.offload_tnl_types[tops_entry] = NULL;
+	tops_tnl.offload_tnl_types[tnl_proto_type] = NULL;
 	tops_tnl.offload_tnl_type_num--;
 }
diff --git a/package-21.02/kernel/tops/src/tops_params.c b/package-21.02/kernel/tops/src/tops_params.c
new file mode 100644
index 0000000..c23a44a
--- /dev/null
+++ b/package-21.02/kernel/tops/src/tops_params.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
+ */
+
+#include "tops/tops_params.h"
+
+#include "tops/protocol/mac/eth.h"
+#include "tops/protocol/network/ip.h"
+#include "tops/protocol/transport/udp.h"
+
+int
+mtk_tops_encap_param_setup(struct sk_buff *skb,
+			   struct tops_params *params,
+			   int (*tnl_encap_param_setup)(struct sk_buff *skb,
+							struct tops_params *params))
+{
+	return mtk_tops_eth_encap_param_setup(skb, params, tnl_encap_param_setup);
+}
+
+int
+mtk_tops_decap_param_setup(struct sk_buff *skb,
+			   struct tops_params *params,
+			   int (*tnl_decap_param_setup)(struct sk_buff *skb,
+							struct tops_params *params))
+{
+	return tnl_decap_param_setup(skb, params);
+}
+
+int mtk_tops_transport_decap_param_setup(struct sk_buff *skb,
+					 struct tops_params *params)
+{
+	return mtk_tops_udp_decap_param_setup(skb, params);
+}
+
+int mtk_tops_network_decap_param_setup(struct sk_buff *skb,
+				       struct tops_params *params)
+{
+	/* TODO: IPv6 */
+	return mtk_tops_ip_decap_param_setup(skb, params);
+}
+
+int mtk_tops_mac_decap_param_setup(struct sk_buff *skb,
+				   struct tops_params *params)
+{
+	return mtk_tops_eth_decap_param_setup(skb, params);
+}
+
+int mtk_tops_debug_param_proto_peek(const char *buf, int ofs, char *proto)
+{
+	int nchar = 0;
+	int ret;
+
+	if (!proto)
+		return -EINVAL;
+
+	ret = sscanf(buf + ofs, "%20s %n", proto, &nchar);
+	if (ret != 1)
+		return -EPERM;
+
+	return nchar;
+}
+
+int mtk_tops_debug_param_setup(const char *buf, int *ofs,
+				   struct tops_params *params)
+{
+	char proto[DEBUG_PROTO_LEN];
+	int ret;
+
+	memset(proto, 0, sizeof(proto));
+
+	ret = mtk_tops_debug_param_proto_peek(buf, *ofs, proto);
+	if (ret < 0)
+		return ret;
+
+	*ofs += ret;
+
+	if (!strcmp(proto, DEBUG_PROTO_ETH))
+		return mtk_tops_eth_debug_param_setup(buf, ofs, params);
+
+	/* not support mac protocols other than Ethernet */
+	return -EINVAL;
+}
+
+void mtk_tops_mac_param_dump(struct seq_file *s, struct tops_params *params)
+{
+	if (params->mac.type == TOPS_MAC_ETH)
+		mtk_tops_eth_param_dump(s, params);
+}
+
+void mtk_tops_network_param_dump(struct seq_file *s, struct tops_params *params)
+{
+	if (params->network.type == TOPS_NETWORK_IPV4)
+		mtk_tops_ip_param_dump(s, params);
+}
+
+void mtk_tops_transport_param_dump(struct seq_file *s, struct tops_params *params)
+{
+	if (params->transport.type == TOPS_TRANSPORT_UDP)
+		mtk_tops_udp_param_dump(s, params);
+}
+
+static bool tops_transport_params_match(struct tops_transport_params *t1,
+					struct tops_transport_params *t2)
+{
+	return !memcmp(t1, t2, sizeof(*t1));
+}
+
+static bool tops_network_params_match(struct tops_network_params *n1,
+				      struct tops_network_params *n2)
+{
+	if (n1->type != n2->type)
+		return false;
+
+	if (n1->type == TOPS_NETWORK_IPV4)
+		return (n1->ip.sip == n2->ip.sip
+			&& n1->ip.dip == n2->ip.dip
+			&& n1->ip.proto == n2->ip.proto
+			&& n1->ip.tos == n2->ip.tos);
+
+	/* TODO: support IPv6 */
+	return false;
+}
+
+bool mtk_tops_params_match(struct tops_params *p1, struct tops_params *p2)
+{
+	return (tops_network_params_match(&p1->network, &p2->network)
+		&& tops_transport_params_match(&p1->transport, &p2->transport));
+}
diff --git a/package-21.02/kernel/tops/src/trm-debugfs.c b/package-21.02/kernel/tops/src/trm-debugfs.c
index 66c64f3..2566b6e 100644
--- a/package-21.02/kernel/tops/src/trm-debugfs.c
+++ b/package-21.02/kernel/tops/src/trm-debugfs.c
@@ -8,11 +8,11 @@
 #include <linux/debugfs.h>
 #include <linux/uaccess.h>
 
-#include "debugfs.h"
-#include "internal.h"
-#include "tops.h"
-#include "trm-debugfs.h"
-#include "trm.h"
+#include "tops/debugfs.h"
+#include "tops/internal.h"
+#include "tops/tops.h"
+#include "tops/trm-debugfs.h"
+#include "tops/trm.h"
 
 struct dentry *trm_debugfs_root;
 
diff --git a/package-21.02/kernel/tops/src/trm-fs.c b/package-21.02/kernel/tops/src/trm-fs.c
index 81ae77a..130a569 100644
--- a/package-21.02/kernel/tops/src/trm-fs.c
+++ b/package-21.02/kernel/tops/src/trm-fs.c
@@ -11,10 +11,10 @@
 #include <linux/err.h>
 #include <linux/relay.h>
 
-#include "trm-debugfs.h"
-#include "trm-fs.h"
-#include "trm-mcu.h"
-#include "trm.h"
+#include "tops/trm-debugfs.h"
+#include "tops/trm-fs.h"
+#include "tops/trm-mcu.h"
+#include "tops/trm.h"
 
 #define RLY_RETRY_NUM				3
 
diff --git a/package-21.02/kernel/tops/src/trm-mcu.c b/package-21.02/kernel/tops/src/trm-mcu.c
index b475dc0..e7a8268 100644
--- a/package-21.02/kernel/tops/src/trm-mcu.c
+++ b/package-21.02/kernel/tops/src/trm-mcu.c
@@ -12,12 +12,12 @@
 #include <linux/io.h>
 #include <linux/of.h>
 
-#include "internal.h"
-#include "mcu.h"
-#include "trm-debugfs.h"
-#include "trm-fs.h"
-#include "trm-mcu.h"
-#include "trm.h"
+#include "tops/internal.h"
+#include "tops/mcu.h"
+#include "tops/trm-debugfs.h"
+#include "tops/trm-fs.h"
+#include "tops/trm-mcu.h"
+#include "tops/trm.h"
 
 #define TOPS_OCD_RETRY_TIMES		(3)
 
diff --git a/package-21.02/kernel/tops/src/trm.c b/package-21.02/kernel/tops/src/trm.c
index a3f959f..0b61f87 100644
--- a/package-21.02/kernel/tops/src/trm.c
+++ b/package-21.02/kernel/tops/src/trm.c
@@ -17,13 +17,13 @@
 #include <linux/relay.h>
 #include <linux/types.h>
 
-#include "internal.h"
-#include "mbox.h"
-#include "mcu.h"
-#include "netsys.h"
-#include "trm-fs.h"
-#include "trm-mcu.h"
-#include "trm.h"
+#include "tops/internal.h"
+#include "tops/mbox.h"
+#include "tops/mcu.h"
+#include "tops/netsys.h"
+#include "tops/trm-fs.h"
+#include "tops/trm-mcu.h"
+#include "tops/trm.h"
 
 #define TRM_HDR_LEN				(sizeof(struct trm_header))
 
@@ -75,6 +75,7 @@
 	memset(trm_hdr, 0, TRM_HDR_LEN);
 
 	strncpy(trm_hdr->info.name, trm_cfg->name, TRM_CONFIG_NAME_MAX_LEN);
+	trm_hdr->info.name[TRM_CONFIG_NAME_MAX_LEN - 1] = '\0';
 	trm_hdr->info.start_addr = trm_cfg->addr + trm_cfg->offset;
 	trm_hdr->info.size = size;
 	trm_hdr->info.dump_time = dump_time;
diff --git a/package-21.02/kernel/tops/src/wdt.c b/package-21.02/kernel/tops/src/wdt.c
index 5d450c5..b632d4e 100644
--- a/package-21.02/kernel/tops/src/wdt.c
+++ b/package-21.02/kernel/tops/src/wdt.c
@@ -8,11 +8,11 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 
-#include "internal.h"
-#include "mbox.h"
-#include "ser.h"
-#include "trm.h"
-#include "wdt.h"
+#include "tops/internal.h"
+#include "tops/mbox.h"
+#include "tops/ser.h"
+#include "tops/trm.h"
+#include "tops/wdt.h"
 
 #define WDT_IRQ_STATUS			0x0140B0
 #define TOP_WDT_MODE			0x012000
diff --git a/package-21.02/kernel/tops/tops.mk b/package-21.02/kernel/tops/tops.mk
index dab1b4b..09d2a98 100644
--- a/package-21.02/kernel/tops/tops.mk
+++ b/package-21.02/kernel/tops/tops.mk
@@ -9,7 +9,7 @@
 	CONFIG_MTK_TOPS_GRE=$(CONFIG_MTK_TOPS_GRE) \
 	CONFIG_MTK_TOPS_GRETAP=$(CONFIG_MTK_TOPS_GRETAP) \
 	CONFIG_MTK_TOPS_L2TP=$(CONFIG_MTK_TOPS_L2TP) \
-	CONFIG_MTK_TOPS_UDP_L2TP_DATA=$(CONFIG_MTK_TOPS_UDP_L2TP_DATA) \
+	CONFIG_MTK_TOPS_L2TP_V2=$(CONFIG_MTK_TOPS_L2TP_V2) \
 	CONFIG_MTK_TOPS_SECURE_FW=$(CONFIG_MTK_TOPS_SECURE_FW)
 
 EXTRA_CFLAGS+= \
@@ -22,4 +22,5 @@
 	-I$(KERNEL_BUILD_DIR)/pce/inc/ \
 	-DCONFIG_TOPS_TNL_NUM=$(CONFIG_TOPS_TNL_NUM) \
 	-DCONFIG_TOPS_TNL_MAP_BIT=$(CONFIG_TOPS_TNL_MAP_BIT) \
+	-DCONFIG_TOPS_TNL_TYPE_NUM=$(CONFIG_TOPS_TNL_TYPE_NUM) \
 	-Wall -Werror
diff --git a/target/linux/mediatek/patches-5.4/999-4100-mtk-tunnel-offload-support.patch b/target/linux/mediatek/patches-5.4/999-4100-mtk-tunnel-offload-support.patch
index 299c7b4..6c26c22 100644
--- a/target/linux/mediatek/patches-5.4/999-4100-mtk-tunnel-offload-support.patch
+++ b/target/linux/mediatek/patches-5.4/999-4100-mtk-tunnel-offload-support.patch
@@ -1,489 +1,488 @@
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c

-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c

-@@ -245,6 +245,9 @@ static const char * const mtk_clks_source_name[] = {

- 	"top_netsys_warp_sel",

- };

- 

-+struct net_device *(*mtk_get_tnl_dev)(int tnl_idx) = NULL;

-+EXPORT_SYMBOL(mtk_get_tnl_dev);

-+

- void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg)

- {

- 	__raw_writel(val, eth->base + reg);

-@@ -2186,6 +2189,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,

- 	u64 addr64 = 0;

- 	u8 *data, *new_data;

- 	struct mtk_rx_dma_v2 *rxd, trxd;

-+	int tnl_idx = 0;

- 	int done = 0;

- 

- 	if (unlikely(!ring))

-@@ -2229,11 +2233,20 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,

- 				      0 : RX_DMA_GET_SPORT(trxd.rxd4) - 1;

- 		}

- 

--		if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||

--			     !eth->netdev[mac]))

--			goto release_desc;

-+		tnl_idx = RX_DMA_GET_TOPS_CRSN(trxd.rxd6);

-+		if (mtk_get_tnl_dev && tnl_idx) {

-+			netdev = mtk_get_tnl_dev(tnl_idx);

-+			if (unlikely(IS_ERR(netdev)))

-+				netdev = NULL;

-+		}

- 

--		netdev = eth->netdev[mac];

-+		if (!netdev) {

-+			if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||

-+				     !eth->netdev[mac]))

-+				goto release_desc;

-+

-+			netdev = eth->netdev[mac];

-+		}

- 

- 		if (unlikely(test_bit(MTK_RESETTING, &eth->state)))

- 			goto release_desc;

-@@ -2318,6 +2331,8 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,

- 		skb_hnat_alg(skb) = 0;

- 		skb_hnat_filled(skb) = 0;

- 		skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;

-+		skb_hnat_set_tops(skb, 0);

-+		skb_hnat_set_is_decap(skb, 0);

- 

- 		if (skb_hnat_reason(skb) == HIT_BIND_FORCE_TO_CPU) {

- 			trace_printk("[%s] reason=0x%x(force to CPU) from WAN to Ext\n",

---- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c

-+++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c

-@@ -43,6 +43,12 @@ void (*ppe_dev_register_hook)(struct net_device *dev) = NULL;

- EXPORT_SYMBOL(ppe_dev_register_hook);

- void (*ppe_dev_unregister_hook)(struct net_device *dev) = NULL;

- EXPORT_SYMBOL(ppe_dev_unregister_hook);

-+int (*mtk_tnl_encap_offload)(struct sk_buff *skb) = NULL;

-+EXPORT_SYMBOL(mtk_tnl_encap_offload);

-+int (*mtk_tnl_decap_offload)(struct sk_buff *skb) = NULL;

-+EXPORT_SYMBOL(mtk_tnl_decap_offload);

-+bool (*mtk_tnl_decap_offloadable)(struct sk_buff *skb) = NULL;

-+EXPORT_SYMBOL(mtk_tnl_decap_offloadable);

- 

- static void hnat_sma_build_entry(struct timer_list *t)

- {

-@@ -53,6 +59,16 @@ static void hnat_sma_build_entry(struct timer_list *t)

- 			     SMA, SMA_FWD_CPU_BUILD_ENTRY);

- }

- 

-+struct foe_entry *hnat_get_foe_entry(u32 ppe_id, u32 index)

-+{

-+	if (index == 0x7fff || index >= hnat_priv->foe_etry_num

-+	    || ppe_id >= CFG_PPE_NUM)

-+		return ERR_PTR(-EINVAL);

-+

-+	return &hnat_priv->foe_table_cpu[ppe_id][index];

-+}

-+EXPORT_SYMBOL(hnat_get_foe_entry);

-+

- void hnat_cache_ebl(int enable)

- {

- 	int i;

-@@ -63,6 +79,7 @@ void hnat_cache_ebl(int enable)

- 		cr_set_field(hnat_priv->ppe_base[i] + PPE_CAH_CTRL, CAH_EN, enable);

- 	}

- }

-+EXPORT_SYMBOL(hnat_cache_ebl);

- 

- static void hnat_reset_timestamp(struct timer_list *t)

- {

---- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h

-+++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h

-@@ -1133,6 +1133,8 @@ enum FoeIpAct {

- #define NR_WDMA1_PORT 9

- #define NR_WDMA2_PORT 13

- #define NR_GMAC3_PORT 15

-+#define NR_TDMA_TPORT 4

-+#define NR_TDMA_QDMA_TPORT 5

- #define LAN_DEV_NAME hnat_priv->lan

- #define LAN2_DEV_NAME hnat_priv->lan2

- #define IS_WAN(dev)                                                            \

-@@ -1256,6 +1258,8 @@ static inline bool hnat_dsa_is_enable(struct mtk_hnat *priv)

- }

- #endif

- 

-+struct foe_entry *hnat_get_foe_entry(u32 ppe_id, u32 index);

-+

- void hnat_deinit_debugfs(struct mtk_hnat *h);

- int hnat_init_debugfs(struct mtk_hnat *h);

- int hnat_register_nf_hooks(void);

-@@ -1272,6 +1276,9 @@ extern int qos_ul_toggle;

- extern int hook_toggle;

- extern int mape_toggle;

- extern int qos_toggle;

-+extern int (*mtk_tnl_encap_offload)(struct sk_buff *skb);

-+extern int (*mtk_tnl_decap_offload)(struct sk_buff *skb);

-+extern bool (*mtk_tnl_decap_offloadable)(struct sk_buff *skb);

- 

- int ext_if_add(struct extdev_entry *ext_entry);

- int ext_if_del(struct extdev_entry *ext_entry);

---- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c

-+++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c

-@@ -726,10 +726,14 @@ static unsigned int is_ppe_support_type(struct sk_buff *skb)

- 	case ETH_P_IP:

- 		iph = ip_hdr(skb);

- 

--		/* do not accelerate non tcp/udp traffic */

--		if ((iph->protocol == IPPROTO_TCP) ||

-+		if (mtk_tnl_decap_offloadable && mtk_tnl_decap_offloadable(skb)) {

-+			/* tunnel protocol is offloadable */

-+			skb_hnat_set_is_decap(skb, 1);

-+			return 1;

-+		} else if ((iph->protocol == IPPROTO_TCP) ||

- 		    (iph->protocol == IPPROTO_UDP) ||

- 		    (iph->protocol == IPPROTO_IPV6)) {

-+			/* do not accelerate non tcp/udp traffic */

- 			return 1;

- 		}

- 

-@@ -846,6 +850,13 @@ mtk_hnat_ipv4_nf_pre_routing(void *priv, struct sk_buff *skb,

- 

- 	hnat_set_head_frags(state, skb, -1, hnat_set_iif);

- 

-+	if (skb_hnat_tops(skb) && skb_hnat_is_decap(skb)

-+	    && is_magic_tag_valid(skb)

-+	    && skb_hnat_iface(skb) == FOE_MAGIC_GE_VIRTUAL

-+	    && mtk_tnl_decap_offload && mtk_tnl_decap_offload(skb)) {

-+		return NF_ACCEPT;

-+	}

-+

- 	/*

- 	 * Avoid mistakenly binding of outer IP, ports in SW L2TP decap flow.

- 	 * In pre-routing, if dev is virtual iface, TOPS module is not loaded,

-@@ -921,6 +932,13 @@ mtk_hnat_br_nf_local_in(void *priv, struct sk_buff *skb,

- 

- 	hnat_set_head_frags(state, skb, -1, hnat_set_iif);

- 

-+	if (skb_hnat_tops(skb) && skb_hnat_is_decap(skb)

-+	    && is_magic_tag_valid(skb)

-+	    && skb_hnat_iface(skb) == FOE_MAGIC_GE_VIRTUAL

-+	    && mtk_tnl_decap_offload && mtk_tnl_decap_offload(skb)) {

-+		return NF_ACCEPT;

-+	}

-+

- 	pre_routing_print(skb, state->in, state->out, __func__);

- 

- 	if (unlikely(debug_level >= 7)) {

-@@ -1073,9 +1091,22 @@ static unsigned int hnat_ipv4_get_nexthop(struct sk_buff *skb,

- 		return -1;

- 	}

- 

-+	/*

-+	 * if this packet is a tunnel packet and is about to construct

-+	 * outer header, we must update its outer mac header pointer

-+	 * before filling outer mac or it may screw up inner mac

-+	 */

-+	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) {

-+		skb_push(skb, sizeof(struct ethhdr));

-+		skb_reset_mac_header(skb);

-+	}

-+

- 	memcpy(eth_hdr(skb)->h_dest, neigh->ha, ETH_ALEN);

- 	memcpy(eth_hdr(skb)->h_source, out->dev_addr, ETH_ALEN);

- 

-+	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb))

-+		skb_pull(skb, sizeof(struct ethhdr));

-+

- 	rcu_read_unlock_bh();

- 

- 	return 0;

-@@ -1201,6 +1232,81 @@ static struct ethhdr *get_ipv6_ipip_ethhdr(struct sk_buff *skb,

- 	return eth;

- }

- 

-+static inline void hnat_get_filled_unbind_entry(struct sk_buff *skb,

-+						struct foe_entry *entry)

-+{

-+	if (unlikely(!skb || !entry))

-+		return;

-+

-+	memcpy(entry,

-+	       &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)],

-+	       sizeof(*entry));

-+

-+#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)

-+	entry->bfib1.mc = 0;

-+#endif /* defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) */

-+	entry->bfib1.ka = 0;

-+	entry->bfib1.vlan_layer = 0;

-+	entry->bfib1.psn = 0;

-+	entry->bfib1.vpm = 0;

-+	entry->bfib1.ps = 0;

-+}

-+

-+static inline void hnat_qos_tnl(u32 id, const struct net_device *dev)

-+{

-+	u32 cfg;

-+	u32 max_man = 0;

-+	u32 max_exp = 0;

-+	const struct mtk_mac *mac;

-+

-+	if (!dev)

-+		return;

-+	mac = netdev_priv(dev);

-+

-+	switch (mac->speed) {

-+	case SPEED_100:

-+	case SPEED_1000:

-+	case SPEED_2500:

-+	case SPEED_5000:

-+	case SPEED_10000:

-+		max_man = mac->speed / SPEED_100;

-+		max_exp = 5;

-+		break;

-+	default:

-+		return;

-+	}

-+

-+	cfg = QTX_SCH_MIN_RATE_EN | QTX_SCH_MAX_RATE_EN;

-+	cfg |= (1 << QTX_SCH_MIN_RATE_MAN_OFFSET) |

-+	       (4 << QTX_SCH_MIN_RATE_EXP_OFFSET) |

-+	       (max_man << QTX_SCH_MAX_RATE_MAN_OFFSET) |

-+	       (max_exp << QTX_SCH_MAX_RATE_EXP_OFFSET) |

-+	       (4 << QTX_SCH_MAX_RATE_WGHT_OFFSET);

-+	writel(cfg, hnat_priv->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE));

-+}

-+

-+static inline void hnat_fill_offload_engine_entry(struct sk_buff *skb,

-+						  struct foe_entry *entry,

-+						  const struct net_device *dev)

-+{

-+#if defined(CONFIG_MEDIATEK_NETSYS_V3)

-+	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) {

-+		/*

-+		 * if skb_hnat_tops(skb) is setup for encapsulation,

-+		 * we fill in hnat tport and tops_entry for tunnel encapsulation

-+		 * offloading

-+		 */

-+		entry->ipv4_hnapt.tport_id = NR_TDMA_QDMA_TPORT;

-+		entry->ipv4_hnapt.tops_entry = skb_hnat_tops(skb);

-+	} else {

-+		return;

-+	}

-+

-+	entry->ipv4_hnapt.iblk2.qid = 12; /* offload engine use QID 12 */

-+	hnat_qos_tnl(12, dev); /* set rate limit to line rate */

-+#endif /* defined(CONFIG_MEDIATEK_NETSYS_V3) */

-+}

-+

- static unsigned int skb_to_hnat_info(struct sk_buff *skb,

- 				     const struct net_device *dev,

- 				     struct foe_entry *foe,

-@@ -1237,6 +1343,11 @@ static unsigned int skb_to_hnat_info(struct sk_buff *skb,

- 	if (whnat && is_hnat_pre_filled(foe))

- 		return 0;

- 

-+	if (skb_hnat_tops(skb) && !(hw_path->flags & FLOW_OFFLOAD_PATH_TNL)) {

-+		hnat_get_filled_unbind_entry(skb, &entry);

-+		goto hnat_entry_bind;

-+	}

-+

- 	entry.bfib1.pkt_type = foe->udib1.pkt_type; /* Get packte type state*/

- 	entry.bfib1.state = foe->udib1.state;

- 

-@@ -1679,6 +1790,10 @@ static unsigned int skb_to_hnat_info(struct sk_buff *skb,

- 	/* Fill Layer2 Info.*/

- 	entry = ppe_fill_L2_info(eth, entry, hw_path);

- 

-+	if (skb_hnat_tops(skb) && hw_path->flags & FLOW_OFFLOAD_PATH_TNL)

-+		goto hnat_entry_skip_bind;

-+

-+hnat_entry_bind:

- 	/* Fill Info Blk*/

- 	entry = ppe_fill_info_blk(eth, entry, hw_path);

- 

-@@ -1879,7 +1994,20 @@ static unsigned int skb_to_hnat_info(struct sk_buff *skb,

- 			entry.ipv6_5t_route.act_dp |= UDF_HNAT_PRE_FILLED;

- 	}

- 

-+#if defined(CONFIG_MEDIATEK_NETSYS_V3)

-+	hnat_fill_offload_engine_entry(skb, &entry, dev);

-+#endif

-+

-+hnat_entry_skip_bind:

- 	wmb();

-+

-+	/*

-+	 * final check before we write BIND info.

-+	 * If this entry is already bound, we should not modify it right now

-+	 */

-+	if (entry_hnat_is_bound(foe))

-+		return 0;

-+

- 	memcpy(foe, &entry, sizeof(entry));

- 	/*reset statistic for this entry*/

- 	if (hnat_priv->data->per_flow_accounting &&

-@@ -1951,6 +2079,12 @@ int mtk_sw_nat_hook_tx(struct sk_buff *skb, int gmac_no)

- 	switch ((int)entry.bfib1.pkt_type) {

- 	case IPV4_HNAPT:

- 	case IPV4_HNAT:

-+		/*

-+		 * skip if packet is an encap tnl packet or it may

-+		 * screw up inner mac header

-+		 */

-+		if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb))

-+			break;

- 		entry.ipv4_hnapt.smac_hi = swab32(*((u32 *)eth->h_source));

- 		entry.ipv4_hnapt.smac_lo = swab16(*((u16 *)&eth->h_source[4]));

- 		break;

-@@ -2112,6 +2246,10 @@ int mtk_sw_nat_hook_tx(struct sk_buff *skb, int gmac_no)

- 		entry.ipv6_5t_route.iblk2.dp = gmac_no;

- 	}

- 

-+#if defined(CONFIG_MEDIATEK_NETSYS_V3)

-+	hnat_fill_offload_engine_entry(skb, &entry, NULL);

-+#endif

-+

- 	entry.bfib1.ttl = 1;

- 	entry.bfib1.state = BIND;

- 

-@@ -2187,6 +2325,7 @@ int mtk_sw_nat_hook_rx(struct sk_buff *skb)

- 	}

- 

- 	skb_hnat_alg(skb) = 0;

-+	skb_hnat_set_tops(skb, 0);

- 	skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;

- 

- 	if (skb_hnat_iface(skb) == FOE_MAGIC_WED0)

-@@ -2636,6 +2775,7 @@ static unsigned int mtk_hnat_nf_post_routing(

- 	struct flow_offload_hw_path hw_path = { .dev = (struct net_device*)out,

- 						.virt_dev = (struct net_device*)out };

- 	const struct net_device *arp_dev = out;

-+	bool is_virt_dev = false;

- 

- 	if (xlat_toggle && !mtk_464xlat_post_process(skb, out))

- 		return 0;

-@@ -2652,10 +2792,29 @@ static unsigned int mtk_hnat_nf_post_routing(

- 

- 	if (out->netdev_ops->ndo_flow_offload_check) {

- 		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 (ntohs(skb->protocol) == ETH_P_IP

-+			    && ip_hdr(skb)->protocol == IPPROTO_TCP) {

-+				skb_hnat_set_tops(skb, hw_path.tnl_type + 1);

-+			} else {

-+				/*

-+				 * we are not support protocols other than IPv4 TCP

-+				 * for tunnel protocol offload yet

-+				 */

-+				skb_hnat_alg(skb) = 1;

-+				return 0;

-+			}

-+		}

- 	}

- 

- 	if (!IS_LAN_GRP(out) && !IS_WAN(out) && !IS_EXT(out))

-+		is_virt_dev = true;

-+

-+	if (is_virt_dev

-+	    && !(skb_hnat_tops(skb) && skb_hnat_is_encap(skb)

-+		 && (hw_path.flags & FLOW_OFFLOAD_PATH_TNL)))

- 		return 0;

- 

- 	trace_printk("[%s] case hit, %x-->%s, reason=%x\n", __func__,

-@@ -2675,9 +2834,18 @@ static unsigned int mtk_hnat_nf_post_routing(

- 		if (fn && !mtk_hnat_accel_type(skb))

- 			break;

- 

--		if (fn && fn(skb, arp_dev, &hw_path))

-+		if (!is_virt_dev && fn && fn(skb, arp_dev, &hw_path))

- 			break;

- 

-+		/* skb_hnat_tops(skb) is updated in mtk_tnl_offload() */

-+		if (skb_hnat_tops(skb)) {

-+			if (skb_hnat_is_encap(skb) && !is_virt_dev

-+			    && mtk_tnl_encap_offload && mtk_tnl_encap_offload(skb))

-+				break;

-+			if (skb_hnat_is_decap(skb))

-+				break;

-+		}

-+

- 		spin_lock(&hnat_priv->entry_lock);

- 		skb_to_hnat_info(skb, out, entry, &hw_path);

- 		spin_unlock(&hnat_priv->entry_lock);

-@@ -2951,7 +3119,7 @@ mtk_hnat_ipv4_nf_local_out(void *priv, struct sk_buff *skb,

- 	if (iph->protocol == IPPROTO_IPV6) {

- 		entry->udib1.pkt_type = IPV6_6RD;

- 		hnat_set_head_frags(state, skb, 0, hnat_set_alg);

--	} else {

-+	} else if (!skb_hnat_tops(skb)) {

- 		hnat_set_head_frags(state, skb, 1, hnat_set_alg);

- 	}

- 

---- a/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h

-+++ b/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h

-@@ -44,7 +44,9 @@ struct hnat_desc {

- 	u32 is_sp : 1;

- 	u32 hf : 1;

- 	u32 amsdu : 1;

--	u32 resv3 : 19;

-+	u32 tops : 6;

-+	u32 is_decap : 1;

-+	u32 resv3 : 12;

- 	u32 magic_tag_protect : 16;

- } __packed;

- #elif defined(CONFIG_MEDIATEK_NETSYS_RX_V2)

-@@ -91,6 +93,19 @@ struct hnat_desc {

- 	((((skb_headroom(skb) >= FOE_INFO_LEN) ? 1 : 0)))

- 

- #define skb_hnat_info(skb) ((struct hnat_desc *)(skb->head))

-+#if defined(CONFIG_MEDIATEK_NETSYS_V3)

-+#define skb_hnat_tops(skb) (((struct hnat_desc *)((skb)->head))->tops)

-+#define skb_hnat_is_decap(skb) (((struct hnat_desc *)((skb)->head))->is_decap)

-+#define skb_hnat_is_encap(skb) (!skb_hnat_is_decap(skb))

-+#define skb_hnat_set_tops(skb, tops) ((skb_hnat_tops(skb)) = (tops))

-+#define skb_hnat_set_is_decap(skb, is_decap) ((skb_hnat_is_decap(skb)) = (is_decap))

-+#else /* !defined(CONFIG_MEDIATEK_NETSYS_V3) */

-+#define skb_hnat_tops(skb) (0)

-+#define skb_hnat_is_decap(skb) (0)

-+#define skb_hnat_is_encap(skb) (0)

-+#define skb_hnat_set_tops(skb, tops)

-+#define skb_hnat_set_is_decap(skb, is_decap)

-+#endif /* defined(CONFIG_MEDIATEK_NETSYS_V3) */

- #define skb_hnat_magic(skb) (((struct hnat_desc *)(skb->head))->magic)

- #define skb_hnat_reason(skb) (((struct hnat_desc *)(skb->head))->crsn)

- #define skb_hnat_entry(skb) (((struct hnat_desc *)(skb->head))->entry)

---- a/include/net/netfilter/nf_flow_table.h

-+++ b/include/net/netfilter/nf_flow_table.h

-@@ -98,10 +98,22 @@ struct flow_offload {

- #define FLOW_OFFLOAD_PATH_6RD		BIT(5)

- #define FLOW_OFFLOAD_PATH_TNL		BIT(6)

- 

-+enum flow_offload_tnl {

-+	FLOW_OFFLOAD_TNL_GRETAP,

-+	FLOW_OFFLOAD_TNL_PPTP,

-+	FLOW_OFFLOAD_TNL_IP_L2TP,

-+	FLOW_OFFLOAD_TNL_UDP_L2TP_CTRL,

-+	FLOW_OFFLOAD_TNL_UDP_L2TP_DATA,

-+	FLOW_OFFLOAD_VXLAN,

-+	FLOW_OFFLOAD_NATT,

-+	__FLOW_OFFLOAD_MAX,

-+};

-+

- struct flow_offload_hw_path {

- 	struct net_device *dev;

- 	struct net_device *virt_dev;

- 	u32 flags;

-+	u32 tnl_type;

- 

- 	u8 eth_src[ETH_ALEN];

- 	u8 eth_dest[ETH_ALEN];

---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h

-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h

-@@ -1904,6 +1904,9 @@ extern const struct of_device_id of_mtk_match[];

- extern u32 mtk_hwlro_stats_ebl;

- extern u32 dbg_show_level;

- 

-+/* tunnel offload related */

-+extern struct net_device *(*mtk_get_tnl_dev)(int tnl_idx);

-+

- /* read the hardware status register */

- void mtk_stats_update_mac(struct mtk_mac *mac);

- 

+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -245,6 +245,9 @@ static const char * const mtk_clks_sourc
+ 	"top_netsys_warp_sel",
+ };
+ 
++struct net_device *(*mtk_get_tnl_dev)(int tnl_idx) = NULL;
++EXPORT_SYMBOL(mtk_get_tnl_dev);
++
+ void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg)
+ {
+ 	__raw_writel(val, eth->base + reg);
+@@ -2168,6 +2171,7 @@ static int mtk_poll_rx(struct napi_struc
+ 	u64 addr64 = 0;
+ 	u8 *data, *new_data;
+ 	struct mtk_rx_dma_v2 *rxd, trxd;
++	int tnl_idx = 0;
+ 	int done = 0;
+ 
+ 	if (unlikely(!ring))
+@@ -2205,11 +2209,20 @@ static int mtk_poll_rx(struct napi_struc
+ 				      0 : RX_DMA_GET_SPORT(trxd.rxd4) - 1;
+ 		}
+ 
+-		if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||
+-			     !eth->netdev[mac]))
+-			goto release_desc;
++		tnl_idx = RX_DMA_GET_TOPS_CRSN(trxd.rxd6);
++		if (mtk_get_tnl_dev && tnl_idx) {
++			netdev = mtk_get_tnl_dev(tnl_idx);
++			if (unlikely(IS_ERR(netdev)))
++				netdev = NULL;
++		}
+ 
+-		netdev = eth->netdev[mac];
++		if (!netdev) {
++			if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||
++				     !eth->netdev[mac]))
++				goto release_desc;
++
++			netdev = eth->netdev[mac];
++		}
+ 
+ 		if (unlikely(test_bit(MTK_RESETTING, &eth->state)))
+ 			goto release_desc;
+@@ -2294,6 +2307,8 @@ static int mtk_poll_rx(struct napi_struc
+ 		skb_hnat_alg(skb) = 0;
+ 		skb_hnat_filled(skb) = 0;
+ 		skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;
++		skb_hnat_set_tops(skb, 0);
++		skb_hnat_set_is_decap(skb, 0);
+ 
+ 		if (skb_hnat_reason(skb) == HIT_BIND_FORCE_TO_CPU) {
+ 			trace_printk("[%s] reason=0x%x(force to CPU) from WAN to Ext\n",
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1915,6 +1915,9 @@ extern const struct of_device_id of_mtk_
+ extern u32 mtk_hwlro_stats_ebl;
+ extern u32 dbg_show_level;
+ 
++/* tunnel offload related */
++extern struct net_device *(*mtk_get_tnl_dev)(int tnl_idx);
++
+ /* read the hardware status register */
+ void mtk_stats_update_mac(struct mtk_mac *mac);
+ 
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
+@@ -43,6 +43,12 @@ void (*ppe_dev_register_hook)(struct net
+ EXPORT_SYMBOL(ppe_dev_register_hook);
+ void (*ppe_dev_unregister_hook)(struct net_device *dev) = NULL;
+ EXPORT_SYMBOL(ppe_dev_unregister_hook);
++int (*mtk_tnl_encap_offload)(struct sk_buff *skb) = NULL;
++EXPORT_SYMBOL(mtk_tnl_encap_offload);
++int (*mtk_tnl_decap_offload)(struct sk_buff *skb) = NULL;
++EXPORT_SYMBOL(mtk_tnl_decap_offload);
++bool (*mtk_tnl_decap_offloadable)(struct sk_buff *skb) = NULL;
++EXPORT_SYMBOL(mtk_tnl_decap_offloadable);
+ 
+ static void hnat_sma_build_entry(struct timer_list *t)
+ {
+@@ -53,6 +59,16 @@ static void hnat_sma_build_entry(struct
+ 			     SMA, SMA_FWD_CPU_BUILD_ENTRY);
+ }
+ 
++struct foe_entry *hnat_get_foe_entry(u32 ppe_id, u32 index)
++{
++	if (index == 0x7fff || index >= hnat_priv->foe_etry_num
++	    || ppe_id >= CFG_PPE_NUM)
++		return ERR_PTR(-EINVAL);
++
++	return &hnat_priv->foe_table_cpu[ppe_id][index];
++}
++EXPORT_SYMBOL(hnat_get_foe_entry);
++
+ void hnat_cache_ebl(int enable)
+ {
+ 	int i;
+@@ -63,6 +79,7 @@ void hnat_cache_ebl(int enable)
+ 		cr_set_field(hnat_priv->ppe_base[i] + PPE_CAH_CTRL, CAH_EN, enable);
+ 	}
+ }
++EXPORT_SYMBOL(hnat_cache_ebl);
+ 
+ static void hnat_reset_timestamp(struct timer_list *t)
+ {
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
+@@ -1140,6 +1140,8 @@ enum FoeIpAct {
+ #define NR_WDMA1_PORT 9
+ #define NR_WDMA2_PORT 13
+ #define NR_GMAC3_PORT 15
++#define NR_TDMA_TPORT 4
++#define NR_TDMA_QDMA_TPORT 5
+ #define LAN_DEV_NAME hnat_priv->lan
+ #define LAN2_DEV_NAME hnat_priv->lan2
+ #define IS_WAN(dev)                                                            \
+@@ -1269,6 +1271,8 @@ static inline bool hnat_dsa_is_enable(st
+ }
+ #endif
+ 
++struct foe_entry *hnat_get_foe_entry(u32 ppe_id, u32 index);
++
+ void hnat_deinit_debugfs(struct mtk_hnat *h);
+ int hnat_init_debugfs(struct mtk_hnat *h);
+ int hnat_register_nf_hooks(void);
+@@ -1285,6 +1289,9 @@ extern int qos_ul_toggle;
+ extern int hook_toggle;
+ extern int mape_toggle;
+ extern int qos_toggle;
++extern int (*mtk_tnl_encap_offload)(struct sk_buff *skb);
++extern int (*mtk_tnl_decap_offload)(struct sk_buff *skb);
++extern bool (*mtk_tnl_decap_offloadable)(struct sk_buff *skb);
+ 
+ int ext_if_add(struct extdev_entry *ext_entry);
+ int ext_if_del(struct extdev_entry *ext_entry);
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+@@ -728,10 +728,14 @@ static unsigned int is_ppe_support_type(
+ 	case ETH_P_IP:
+ 		iph = ip_hdr(skb);
+ 
+-		/* do not accelerate non tcp/udp traffic */
+-		if ((iph->protocol == IPPROTO_TCP) ||
++		if (mtk_tnl_decap_offloadable && mtk_tnl_decap_offloadable(skb)) {
++			/* tunnel protocol is offloadable */
++			skb_hnat_set_is_decap(skb, 1);
++			return 1;
++		} else if ((iph->protocol == IPPROTO_TCP) ||
+ 		    (iph->protocol == IPPROTO_UDP) ||
+ 		    (iph->protocol == IPPROTO_IPV6)) {
++			/* do not accelerate non tcp/udp traffic */
+ 			return 1;
+ 		}
+ 
+@@ -848,6 +852,13 @@ mtk_hnat_ipv4_nf_pre_routing(void *priv,
+ 
+ 	hnat_set_head_frags(state, skb, -1, hnat_set_iif);
+ 
++	if (skb_hnat_tops(skb) && skb_hnat_is_decap(skb)
++	    && is_magic_tag_valid(skb)
++	    && skb_hnat_iface(skb) == FOE_MAGIC_GE_VIRTUAL
++	    && mtk_tnl_decap_offload && mtk_tnl_decap_offload(skb)) {
++		return NF_ACCEPT;
++	}
++
+ 	/*
+ 	 * Avoid mistakenly binding of outer IP, ports in SW L2TP decap flow.
+ 	 * In pre-routing, if dev is virtual iface, TOPS module is not loaded,
+@@ -923,6 +934,13 @@ mtk_hnat_br_nf_local_in(void *priv, stru
+ 
+ 	hnat_set_head_frags(state, skb, -1, hnat_set_iif);
+ 
++	if (skb_hnat_tops(skb) && skb_hnat_is_decap(skb)
++	    && is_magic_tag_valid(skb)
++	    && skb_hnat_iface(skb) == FOE_MAGIC_GE_VIRTUAL
++	    && mtk_tnl_decap_offload && mtk_tnl_decap_offload(skb)) {
++		return NF_ACCEPT;
++	}
++
+ 	pre_routing_print(skb, state->in, state->out, __func__);
+ 
+ 	if (unlikely(debug_level >= 7)) {
+@@ -1075,8 +1093,22 @@ static unsigned int hnat_ipv4_get_nextho
+ 		return -1;
+ 	}
+ 
++	/*
++	 * if this packet is a tunnel packet and is about to construct
++	 * outer header, we must update its outer mac header pointer
++	 * before filling outer mac or it may screw up inner mac
++	 */
++	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) {
++		skb_push(skb, sizeof(struct ethhdr));
++		skb_reset_mac_header(skb);
++	}
++
+ 	memcpy(eth_hdr(skb)->h_dest, neigh->ha, ETH_ALEN);
+ 	memcpy(eth_hdr(skb)->h_source, out->dev_addr, ETH_ALEN);
++	eth_hdr(skb)->h_proto = htons(ETH_P_IP);
++
++	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb))
++		skb_pull(skb, sizeof(struct ethhdr));
+ 
+ 	rcu_read_unlock_bh();
+ 
+@@ -1203,6 +1235,81 @@ static struct ethhdr *get_ipv6_ipip_ethh
+ 	return eth;
+ }
+ 
++static inline void hnat_get_filled_unbind_entry(struct sk_buff *skb,
++						struct foe_entry *entry)
++{
++	if (unlikely(!skb || !entry))
++		return;
++
++	memcpy(entry,
++	       &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)],
++	       sizeof(*entry));
++
++#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
++	entry->bfib1.mc = 0;
++#endif /* defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3) */
++	entry->bfib1.ka = 0;
++	entry->bfib1.vlan_layer = 0;
++	entry->bfib1.psn = 0;
++	entry->bfib1.vpm = 0;
++	entry->bfib1.ps = 0;
++}
++
++static inline void hnat_qos_tnl(u32 id, const struct net_device *dev)
++{
++	u32 cfg;
++	u32 max_man = 0;
++	u32 max_exp = 0;
++	const struct mtk_mac *mac;
++
++	if (!dev)
++		return;
++	mac = netdev_priv(dev);
++
++	switch (mac->speed) {
++	case SPEED_100:
++	case SPEED_1000:
++	case SPEED_2500:
++	case SPEED_5000:
++	case SPEED_10000:
++		max_man = mac->speed / SPEED_100;
++		max_exp = 5;
++		break;
++	default:
++		return;
++	}
++
++	cfg = QTX_SCH_MIN_RATE_EN | QTX_SCH_MAX_RATE_EN;
++	cfg |= (1 << QTX_SCH_MIN_RATE_MAN_OFFSET) |
++	       (4 << QTX_SCH_MIN_RATE_EXP_OFFSET) |
++	       (max_man << QTX_SCH_MAX_RATE_MAN_OFFSET) |
++	       (max_exp << QTX_SCH_MAX_RATE_EXP_OFFSET) |
++	       (4 << QTX_SCH_MAX_RATE_WGHT_OFFSET);
++	writel(cfg, hnat_priv->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE));
++}
++
++static inline void hnat_fill_offload_engine_entry(struct sk_buff *skb,
++						  struct foe_entry *entry,
++						  const struct net_device *dev)
++{
++#if defined(CONFIG_MEDIATEK_NETSYS_V3)
++	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) {
++		/*
++		 * if skb_hnat_tops(skb) is setup for encapsulation,
++		 * we fill in hnat tport and tops_entry for tunnel encapsulation
++		 * offloading
++		 */
++		entry->ipv4_hnapt.tport_id = NR_TDMA_QDMA_TPORT;
++		entry->ipv4_hnapt.tops_entry = skb_hnat_tops(skb);
++	} else {
++		return;
++	}
++
++	entry->ipv4_hnapt.iblk2.qid = 12; /* offload engine use QID 12 */
++	hnat_qos_tnl(12, dev); /* set rate limit to line rate */
++#endif /* defined(CONFIG_MEDIATEK_NETSYS_V3) */
++}
++
+ static unsigned int skb_to_hnat_info(struct sk_buff *skb,
+ 				     const struct net_device *dev,
+ 				     struct foe_entry *foe,
+@@ -1240,6 +1347,11 @@ static unsigned int skb_to_hnat_info(str
+ 	if (whnat && is_hnat_pre_filled(foe))
+ 		return 0;
+ 
++	if (skb_hnat_tops(skb) && !(hw_path->flags & FLOW_OFFLOAD_PATH_TNL)) {
++		hnat_get_filled_unbind_entry(skb, &entry);
++		goto hnat_entry_bind;
++	}
++
+ 	entry.bfib1.pkt_type = foe->udib1.pkt_type; /* Get packte type state*/
+ 	entry.bfib1.state = foe->udib1.state;
+ 
+@@ -1683,6 +1795,10 @@ static unsigned int skb_to_hnat_info(str
+ 	/* Fill Layer2 Info.*/
+ 	entry = ppe_fill_L2_info(eth, entry, hw_path);
+ 
++	if (skb_hnat_tops(skb) && hw_path->flags & FLOW_OFFLOAD_PATH_TNL)
++		goto hnat_entry_skip_bind;
++
++hnat_entry_bind:
+ 	/* Fill Info Blk*/
+ 	entry = ppe_fill_info_blk(eth, entry, hw_path);
+ 
+@@ -1881,7 +1997,20 @@ static unsigned int skb_to_hnat_info(str
+ 			entry.ipv6_5t_route.act_dp |= UDF_HNAT_PRE_FILLED;
+ 	}
+ 
++#if defined(CONFIG_MEDIATEK_NETSYS_V3)
++	hnat_fill_offload_engine_entry(skb, &entry, dev);
++#endif
++
++hnat_entry_skip_bind:
+ 	wmb();
++
++	/*
++	 * final check before we write BIND info.
++	 * If this entry is already bound, we should not modify it right now
++	 */
++	if (entry_hnat_is_bound(foe))
++		return 0;
++
+ 	memcpy(foe, &entry, sizeof(entry));
+ 	/*reset statistic for this entry*/
+ 	if (hnat_priv->data->per_flow_accounting &&
+@@ -1953,6 +2082,12 @@ int mtk_sw_nat_hook_tx(struct sk_buff *s
+ 	switch ((int)entry.bfib1.pkt_type) {
+ 	case IPV4_HNAPT:
+ 	case IPV4_HNAT:
++		/*
++		 * skip if packet is an encap tnl packet or it may
++		 * screw up inner mac header
++		 */
++		if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb))
++			break;
+ 		entry.ipv4_hnapt.smac_hi = swab32(*((u32 *)eth->h_source));
+ 		entry.ipv4_hnapt.smac_lo = swab16(*((u16 *)&eth->h_source[4]));
+ 		break;
+@@ -2144,6 +2279,10 @@ int mtk_sw_nat_hook_tx(struct sk_buff *s
+ 		entry.ipv6_5t_route.iblk2.dp = gmac_no;
+ 	}
+ 
++#if defined(CONFIG_MEDIATEK_NETSYS_V3)
++	hnat_fill_offload_engine_entry(skb, &entry, NULL);
++#endif
++
+ 	entry.bfib1.ttl = 1;
+ 	entry.bfib1.state = BIND;
+ 	if (IS_IPV4_GRP(&entry))
+@@ -2219,6 +2358,7 @@ int mtk_sw_nat_hook_rx(struct sk_buff *s
+ 	}
+ 
+ 	skb_hnat_alg(skb) = 0;
++	skb_hnat_set_tops(skb, 0);
+ 	skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;
+ 
+ 	if (skb_hnat_iface(skb) == FOE_MAGIC_WED0)
+@@ -2672,6 +2812,7 @@ static unsigned int mtk_hnat_nf_post_rou
+ 	struct flow_offload_hw_path hw_path = { .dev = (struct net_device*)out,
+ 						.virt_dev = (struct net_device*)out };
+ 	const struct net_device *arp_dev = out;
++	bool is_virt_dev = false;
+ 
+ 	if (xlat_toggle && !mtk_464xlat_post_process(skb, out))
+ 		return 0;
+@@ -2691,10 +2832,29 @@ static unsigned int mtk_hnat_nf_post_rou
+ 
+ 	if (out->netdev_ops->ndo_flow_offload_check) {
+ 		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 (ntohs(skb->protocol) == ETH_P_IP
++			    && ip_hdr(skb)->protocol == IPPROTO_TCP) {
++				skb_hnat_set_tops(skb, hw_path.tnl_type + 1);
++			} else {
++				/*
++				 * we are not support protocols other than IPv4 TCP
++				 * for tunnel protocol offload yet
++				 */
++				skb_hnat_alg(skb) = 1;
++				return 0;
++			}
++		}
+ 	}
+ 
+ 	if (!IS_LAN_GRP(out) && !IS_WAN(out) && !IS_EXT(out))
++		is_virt_dev = true;
++
++	if (is_virt_dev
++	    && !(skb_hnat_tops(skb) && skb_hnat_is_encap(skb)
++		 && (hw_path.flags & FLOW_OFFLOAD_PATH_TNL)))
+ 		return 0;
+ 
+ 	trace_printk("[%s] case hit, %x-->%s, reason=%x\n", __func__,
+@@ -2714,9 +2874,18 @@ static unsigned int mtk_hnat_nf_post_rou
+ 		if (fn && !mtk_hnat_accel_type(skb))
+ 			break;
+ 
+-		if (fn && fn(skb, arp_dev, &hw_path))
++		if (!is_virt_dev && fn && fn(skb, arp_dev, &hw_path))
+ 			break;
+ 
++		/* skb_hnat_tops(skb) is updated in mtk_tnl_offload() */
++		if (skb_hnat_tops(skb)) {
++			if (skb_hnat_is_encap(skb) && !is_virt_dev
++			    && mtk_tnl_encap_offload && mtk_tnl_encap_offload(skb))
++				break;
++			if (skb_hnat_is_decap(skb))
++				break;
++		}
++
+ 		spin_lock(&hnat_priv->entry_lock);
+ 		skb_to_hnat_info(skb, out, entry, &hw_path);
+ 		spin_unlock(&hnat_priv->entry_lock);
+@@ -2989,7 +3158,7 @@ mtk_hnat_ipv4_nf_local_out(void *priv, s
+ 	if (iph->protocol == IPPROTO_IPV6) {
+ 		entry->udib1.pkt_type = IPV6_6RD;
+ 		hnat_set_head_frags(state, skb, 0, hnat_set_alg);
+-	} else {
++	} else if (!skb_hnat_tops(skb)) {
+ 		hnat_set_head_frags(state, skb, 1, hnat_set_alg);
+ 	}
+ 
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
+@@ -44,7 +44,9 @@ struct hnat_desc {
+ 	u32 is_sp : 1;
+ 	u32 hf : 1;
+ 	u32 amsdu : 1;
+-	u32 resv3 : 19;
++	u32 tops : 6;
++	u32 is_decap : 1;
++	u32 resv3 : 12;
+ 	u32 magic_tag_protect : 16;
+ } __packed;
+ #elif defined(CONFIG_MEDIATEK_NETSYS_RX_V2)
+@@ -91,6 +93,19 @@ struct hnat_desc {
+ 	((((skb_headroom(skb) >= FOE_INFO_LEN) ? 1 : 0)))
+ 
+ #define skb_hnat_info(skb) ((struct hnat_desc *)(skb->head))
++#if defined(CONFIG_MEDIATEK_NETSYS_V3)
++#define skb_hnat_tops(skb) (((struct hnat_desc *)((skb)->head))->tops)
++#define skb_hnat_is_decap(skb) (((struct hnat_desc *)((skb)->head))->is_decap)
++#define skb_hnat_is_encap(skb) (!skb_hnat_is_decap(skb))
++#define skb_hnat_set_tops(skb, tops) ((skb_hnat_tops(skb)) = (tops))
++#define skb_hnat_set_is_decap(skb, is_decap) ((skb_hnat_is_decap(skb)) = (is_decap))
++#else /* !defined(CONFIG_MEDIATEK_NETSYS_V3) */
++#define skb_hnat_tops(skb) (0)
++#define skb_hnat_is_decap(skb) (0)
++#define skb_hnat_is_encap(skb) (0)
++#define skb_hnat_set_tops(skb, tops)
++#define skb_hnat_set_is_decap(skb, is_decap)
++#endif /* defined(CONFIG_MEDIATEK_NETSYS_V3) */
+ #define skb_hnat_magic(skb) (((struct hnat_desc *)(skb->head))->magic)
+ #define skb_hnat_reason(skb) (((struct hnat_desc *)(skb->head))->crsn)
+ #define skb_hnat_entry(skb) (((struct hnat_desc *)(skb->head))->entry)
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -98,10 +98,21 @@ struct flow_offload {
+ #define FLOW_OFFLOAD_PATH_6RD		BIT(5)
+ #define FLOW_OFFLOAD_PATH_TNL		BIT(6)
+ 
++enum flow_offload_tnl {
++	FLOW_OFFLOAD_TNL_GRETAP,
++	FLOW_OFFLOAD_TNL_PPTP,
++	FLOW_OFFLOAD_TNL_L2TP_V2,
++	FLOW_OFFLOAD_TNL_L2TP_V3,
++	FLOW_OFFLOAD_VXLAN,
++	FLOW_OFFLOAD_NATT,
++	__FLOW_OFFLOAD_MAX,
++};
++
+ struct flow_offload_hw_path {
+ 	struct net_device *dev;
+ 	struct net_device *virt_dev;
+ 	u32 flags;
++	u32 tnl_type;
+ 
+ 	u8 eth_src[ETH_ALEN];
+ 	u8 eth_dest[ETH_ALEN];
diff --git a/target/linux/mediatek/patches-5.4/999-4101-mtk-tunnel-network-service-error-recover-support.patch b/target/linux/mediatek/patches-5.4/999-4101-mtk-tunnel-network-service-error-recover-support.patch
index 9f9e26a..a2579c0 100644
--- a/target/linux/mediatek/patches-5.4/999-4101-mtk-tunnel-network-service-error-recover-support.patch
+++ b/target/linux/mediatek/patches-5.4/999-4101-mtk-tunnel-network-service-error-recover-support.patch
@@ -38,7 +38,7 @@
  
  #define MTK_ETHTOOL_STAT(x) { #x, \
  			      offsetof(struct mtk_hw_stats, x) / sizeof(u64) }
-@@ -4141,6 +4142,8 @@ static void mtk_pending_work(struct work
+@@ -4178,6 +4179,8 @@ static void mtk_pending_work(struct work
  				}
  			pr_warn("wait for MTK_FE_START_RESET\n");
  		}
diff --git a/target/linux/mediatek/patches-5.4/999-4102-mtk-crypto-offload-support.patch b/target/linux/mediatek/patches-5.4/999-4102-mtk-crypto-offload-support.patch
index dae8997..09650e1 100644
--- a/target/linux/mediatek/patches-5.4/999-4102-mtk-crypto-offload-support.patch
+++ b/target/linux/mediatek/patches-5.4/999-4102-mtk-crypto-offload-support.patch
@@ -1,6 +1,6 @@
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1843,6 +1843,12 @@ static void mtk_tx_set_dma_desc_v3(struc
+@@ -1857,6 +1857,12 @@ static void mtk_tx_set_dma_desc_v3(struc
  
  	trace_printk("[%s] skb_shinfo(skb)->nr_frags=%x HNAT_SKB_CB2(skb)->magic=%x txd4=%x<-----\n",
  		     __func__, skb_shinfo(skb)->nr_frags, HNAT_SKB_CB2(skb)->magic, data);
@@ -13,7 +13,7 @@
  #endif
  	WRITE_ONCE(desc->txd4, data);
  
-@@ -1866,6 +1872,17 @@ static void mtk_tx_set_dma_desc_v3(struc
+@@ -1880,6 +1886,17 @@ static void mtk_tx_set_dma_desc_v3(struc
  
  	WRITE_ONCE(desc->txd7, 0);
  	WRITE_ONCE(desc->txd8, 0);
@@ -31,7 +31,7 @@
  }
  
  static void mtk_tx_set_dma_desc(struct sk_buff *skb, struct net_device *dev, void *txd,
-@@ -2293,6 +2310,7 @@ static int mtk_poll_rx(struct napi_struc
+@@ -2307,6 +2324,7 @@ static int mtk_poll_rx(struct napi_struc
  
  		skb_hnat_alg(skb) = 0;
  		skb_hnat_filled(skb) = 0;
@@ -41,7 +41,7 @@
  		skb_hnat_set_is_decap(skb, 0);
 --- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
 +++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
-@@ -1078,6 +1078,9 @@ static unsigned int hnat_ipv4_get_nextho
+@@ -1077,6 +1077,9 @@ static unsigned int hnat_ipv4_get_nextho
  		return 0;
  	}
  
@@ -137,7 +137,7 @@
  static unsigned int skb_to_hnat_info(struct sk_buff *skb,
  				     const struct net_device *dev,
  				     struct foe_entry *foe,
-@@ -2369,6 +2444,7 @@ int mtk_sw_nat_hook_rx(struct sk_buff *s
+@@ -2359,6 +2434,7 @@ int mtk_sw_nat_hook_rx(struct sk_buff *s
  
  	skb_hnat_alg(skb) = 0;
  	skb_hnat_set_tops(skb, 0);
@@ -145,7 +145,7 @@
  	skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;
  
  	if (skb_hnat_iface(skb) == FOE_MAGIC_WED0)
-@@ -2455,7 +2531,8 @@ static unsigned int mtk_hnat_accel_type(
+@@ -2445,7 +2521,8 @@ static unsigned int mtk_hnat_accel_type(
  	 * is from local_out which is also filtered in sanity check.
  	 */
  	dst = skb_dst(skb);
@@ -155,7 +155,7 @@
  		return 0;
  
  	ct = nf_ct_get(skb, &ctinfo);
-@@ -2856,6 +2933,14 @@ static unsigned int mtk_hnat_nf_post_rou
+@@ -2849,6 +2926,14 @@ static unsigned int mtk_hnat_nf_post_rou
  		}
  	}
  
@@ -170,7 +170,7 @@
  	if (!IS_LAN_GRP(out) && !IS_WAN(out) && !IS_EXT(out))
  		is_virt_dev = true;
  
-@@ -3165,7 +3250,10 @@ mtk_hnat_ipv4_nf_local_out(void *priv, s
+@@ -3158,7 +3243,10 @@ mtk_hnat_ipv4_nf_local_out(void *priv, s
  	if (iph->protocol == IPPROTO_IPV6) {
  		entry->udib1.pkt_type = IPV6_6RD;
  		hnat_set_head_frags(state, skb, 0, hnat_set_alg);
@@ -224,7 +224,7 @@
  {
 --- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
 +++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
-@@ -1135,6 +1135,8 @@ enum FoeIpAct {
+@@ -1140,6 +1140,8 @@ enum FoeIpAct {
  #define NR_WDMA1_PORT 9
  #define NR_WDMA2_PORT 13
  #define NR_GMAC3_PORT 15
@@ -233,7 +233,7 @@
  #define NR_TDMA_TPORT 4
  #define NR_TDMA_QDMA_TPORT 5
  #define LAN_DEV_NAME hnat_priv->lan
-@@ -1289,6 +1291,8 @@ extern int qos_toggle;
+@@ -1292,6 +1294,8 @@ extern int qos_toggle;
  extern int (*mtk_tnl_encap_offload)(struct sk_buff *skb);
  extern int (*mtk_tnl_decap_offload)(struct sk_buff *skb);
  extern bool (*mtk_tnl_decap_offloadable)(struct sk_buff *skb);
diff --git a/target/linux/mediatek/patches-5.4/999-4103-mtk-tunnel-crypto-offload-support.patch b/target/linux/mediatek/patches-5.4/999-4103-mtk-tunnel-crypto-offload-support.patch
index 608bc91..b96f080 100644
--- a/target/linux/mediatek/patches-5.4/999-4103-mtk-tunnel-crypto-offload-support.patch
+++ b/target/linux/mediatek/patches-5.4/999-4103-mtk-tunnel-crypto-offload-support.patch
@@ -1,6 +1,6 @@
 --- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
 +++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
-@@ -1139,6 +1139,8 @@ enum FoeIpAct {
+@@ -1144,6 +1144,8 @@ enum FoeIpAct {
  #define NR_EIP197_QDMA_TPORT 3
  #define NR_TDMA_TPORT 4
  #define NR_TDMA_QDMA_TPORT 5
@@ -21,9 +21,9 @@
  		skb_push(skb, sizeof(struct ethhdr));
  		skb_reset_mac_header(skb);
  	}
-@@ -1109,7 +1110,8 @@ static unsigned int hnat_ipv4_get_nextho
- 	memcpy(eth_hdr(skb)->h_dest, neigh->ha, ETH_ALEN);
+@@ -1110,7 +1111,8 @@ static unsigned int hnat_ipv4_get_nextho
  	memcpy(eth_hdr(skb)->h_source, out->dev_addr, ETH_ALEN);
+ 	eth_hdr(skb)->h_proto = htons(ETH_P_IP);
  
 -	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb))
 +	if ((skb_hnat_tops(skb) && skb_hnat_is_encap(skb))
@@ -31,7 +31,7 @@
  		skb_pull(skb, sizeof(struct ethhdr));
  
  	rcu_read_unlock_bh();
-@@ -1257,6 +1259,38 @@ static inline void hnat_get_filled_unbin
+@@ -1258,6 +1260,38 @@ static inline void hnat_get_filled_unbin
  	entry->bfib1.ps = 0;
  }
  
@@ -70,7 +70,7 @@
  static inline void hnat_qos_tnl(u32 id, const struct net_device *dev)
  {
  	u32 cfg;
-@@ -1301,9 +1335,15 @@ static inline void hnat_fill_offload_eng
+@@ -1302,9 +1336,15 @@ static inline void hnat_fill_offload_eng
  		 * we fill in hnat tport and tops_entry for tunnel encapsulation
  		 * offloading
  		 */
@@ -88,7 +88,7 @@
  		entry->ipv4_hnapt.tport_id = NR_EIP197_QDMA_TPORT;
  		entry->ipv4_hnapt.cdrt_id = skb_hnat_cdrt(skb);
  	} else {
-@@ -1404,6 +1444,7 @@ static unsigned int skb_to_hnat_info(str
+@@ -1405,6 +1445,7 @@ static unsigned int skb_to_hnat_info(str
  	u32 port_id = 0;
  	u32 payload_len = 0;
  	int mape = 0;
@@ -96,7 +96,7 @@
  	struct mtk_mac *mac = netdev_priv(dev);
  
  	ct = nf_ct_get(skb, &ctinfo);
-@@ -1421,9 +1462,12 @@ static unsigned int skb_to_hnat_info(str
+@@ -1422,9 +1463,12 @@ static unsigned int skb_to_hnat_info(str
  	if (whnat && is_hnat_pre_filled(foe))
  		return 0;
  
@@ -110,7 +110,7 @@
  	}
  
  	entry.bfib1.pkt_type = foe->udib1.pkt_type; /* Get packte type state*/
-@@ -1869,7 +1913,9 @@ static unsigned int skb_to_hnat_info(str
+@@ -1870,7 +1914,9 @@ static unsigned int skb_to_hnat_info(str
  	/* Fill Layer2 Info.*/
  	entry = ppe_fill_L2_info(eth, entry, hw_path);
  
@@ -121,7 +121,7 @@
  		goto hnat_entry_skip_bind;
  
  hnat_entry_bind:
-@@ -2445,6 +2491,7 @@ int mtk_sw_nat_hook_rx(struct sk_buff *s
+@@ -2435,6 +2481,7 @@ int mtk_sw_nat_hook_rx(struct sk_buff *s
  	skb_hnat_alg(skb) = 0;
  	skb_hnat_set_tops(skb, 0);
  	skb_hnat_set_cdrt(skb, 0);
@@ -129,7 +129,7 @@
  	skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;
  
  	if (skb_hnat_iface(skb) == FOE_MAGIC_WED0)
-@@ -3248,7 +3295,8 @@ mtk_hnat_ipv4_nf_local_out(void *priv, s
+@@ -3244,7 +3291,8 @@ mtk_hnat_ipv4_nf_local_out(void *priv, s
  		entry->udib1.pkt_type = IPV6_6RD;
  		hnat_set_head_frags(state, skb, 0, hnat_set_alg);
  	} else if (is_magic_tag_valid(skb)
@@ -141,7 +141,7 @@
  		hnat_set_head_frags(state, skb, 1, hnat_set_alg);
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -2306,10 +2306,11 @@ static int mtk_poll_rx(struct napi_struc
+@@ -2324,10 +2324,11 @@ static int mtk_poll_rx(struct napi_struc
  
  		skb_hnat_alg(skb) = 0;
  		skb_hnat_filled(skb) = 0;
@@ -156,7 +156,7 @@
  			trace_printk("[%s] reason=0x%x(force to CPU) from WAN to Ext\n",
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -652,6 +652,9 @@
+@@ -656,6 +656,9 @@
  #define RX_DMA_GET_AGG_CNT_V2(_x)	(((_x) >> 16) & 0xff)
  #define RX_DMA_GET_TOPS_CRSN(_x)	(((_x) >> 24) & 0xff)
  
diff --git a/target/linux/mediatek/patches-5.4/999-4500-mtk-l2tp-offload-support.patch b/target/linux/mediatek/patches-5.4/999-4500-mtk-l2tp-offload-support.patch
index e6583b6..5e16ba8 100644
--- a/target/linux/mediatek/patches-5.4/999-4500-mtk-l2tp-offload-support.patch
+++ b/target/linux/mediatek/patches-5.4/999-4500-mtk-l2tp-offload-support.patch
@@ -13,7 +13,7 @@
  	 * make room. Adjust truesize.
 --- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
 +++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
-@@ -855,7 +855,8 @@ mtk_hnat_ipv4_nf_pre_routing(void *priv,
+@@ -865,7 +865,8 @@ mtk_hnat_ipv4_nf_pre_routing(void *priv,
  	 * and it's L2TP flow, then do not bind.
  	 */
  	if (skb_hnat_iface(skb) == FOE_MAGIC_GE_VIRTUAL
@@ -29,7 +29,7 @@
  		return -EINVAL;
  
  	path->flags |= FLOW_OFFLOAD_PATH_TNL;
-+	path->tnl_type = FLOW_OFFLOAD_TNL_UDP_L2TP_DATA;
++	path->tnl_type = FLOW_OFFLOAD_TNL_L2TP_V2;
  
  	return 0;
  }