[][kernel][mt7987][hnat][Add L2 BRIDGE HNAT support for the NETSYSv3.1]

[Description]
Add L2 BRIDGE HNAT support for MT7987
  - Add l2br_toggle in debugfs to control function of L2 BRIDGE.
    echo 0 > /sys/kernel/debug/hnat/l2br_toggle
      * Disable L2 HNAT functions.
    echo 1 > /sys/kernel/debug/hnat/l2br_toggle
      * Enable L2_BRIDGE HNAT learning mode.
      * PPE automatically learns and creates new L2_BRIDGE  entries.
    echo 2 > /sys/kernel/debug/hnat/l2br_toggle
      * Enable L2_BRIDGE HNAT look-up mode.
      * For L2 packet, PPE only checks existing L2_BRIDGE entries
        without creating new ones.
      * Normally for static entry usage.
  - Add support in debugfs to dump L2_BRIDGE HNAT info
  - Add support in debugfs to add L2_BRIDGE static entry

[Release-log]
N/A


Change-Id: I129c3196304318a8be9f806701de64d39c0c3b80
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/9750534
diff --git a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
index e9508eb..40d2aec 100644
--- a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
+++ b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
@@ -119,7 +119,7 @@
 	mod_timer(&hnat_priv->hnat_reset_timestamp_timer, jiffies + 14400 * HZ);
 }
 
-static void cr_set_bits(void __iomem *reg, u32 bs)
+void cr_set_bits(void __iomem *reg, u32 bs)
 {
 	u32 val = readl(reg);
 
@@ -127,7 +127,7 @@
 	writel(val, reg);
 }
 
-static void cr_clr_bits(void __iomem *reg, u32 bs)
+void cr_clr_bits(void __iomem *reg, u32 bs)
 {
 	u32 val = readl(reg);
 
@@ -581,6 +581,7 @@
 
 	if (hnat_priv->data->version == MTK_HNAT_V3)
 		cr_set_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG,
+			    BIT_L2_HASH_VID | BIT_L2_HASH_ETH |
 			    BIT_IPV6_NAT_EN | BIT_IPV6_NAPT_EN |
 			    BIT_CS0_RM_ALL_IP6_IP_EN);
 
@@ -785,6 +786,7 @@
 
 	if (hnat_priv->data->version == MTK_HNAT_V3)
 		cr_clr_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG,
+			    BIT_L2_HASH_VID | BIT_L2_HASH_ETH |
 			    BIT_IPV6_NAT_EN | BIT_IPV6_NAPT_EN |
 			    BIT_CS0_RM_ALL_IP6_IP_EN);
 
diff --git a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
index 0393c10..422c83a 100644
--- a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
+++ b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
@@ -337,6 +337,48 @@
 	u32 dscp : 8; /* DSCP value */
 } __packed;
 
+struct hnat_l2_bridge {
+	union {
+		struct hnat_bind_info_blk bfib1;
+		struct hnat_unbind_info_blk udib1;
+		u32 info_blk1;
+	};
+	u32 dmac_hi;
+	u16 smac_lo;
+	u16 dmac_lo;
+	u32 smac_hi;
+	u16 etype;
+	u16 hph; /* hash placeholder */
+	u16 vlan1;
+	u16 vlan2;
+	u32 resv1;
+	u32 resv2;
+	union {
+		struct hnat_info_blk2 iblk2;
+		struct hnat_info_blk2_whnat iblk2w;
+		u32 info_blk2;
+	};
+	u32 resv3;
+	u32 resv4 : 24;
+	u32 act_dp : 8; /* UDF */
+	u16 new_vlan1;
+	u16 sp_tag;
+	u32 new_dmac_hi;
+	u16 new_vlan2;
+	u16 new_dmac_lo;
+	u32 new_smac_hi;
+	u16 resv5;
+	u16 new_smac_lo;
+#if defined(CONFIG_MEDIATEK_NETSYS_V3)
+	u32 resv6;
+	struct hnat_winfo winfo;
+	struct hnat_winfo_pao winfo_pao;
+#elif defined(CONFIG_MEDIATEK_NETSYS_V2)
+	u16 minfo;
+	struct hnat_winfo winfo;
+#endif
+} __packed;
+
 struct hnat_ipv4_hnapt {
 	union {
 		struct hnat_bind_info_blk bfib1;
@@ -369,7 +411,7 @@
 #endif
 	u32 act_dp : 8; /* UDF */
 	u16 vlan1;
-	u16 etype;
+	u16 sp_tag;
 	u32 dmac_hi;
 	union {
 #if !defined(CONFIG_MEDIATEK_NETSYS_V2) && !defined(CONFIG_MEDIATEK_NETSYS_V3)
@@ -438,7 +480,7 @@
 	};
 
 	u16 vlan1;
-	u16 etype;
+	u16 sp_tag;
 	u32 dmac_hi;
 	union {
 #if !defined(CONFIG_MEDIATEK_NETSYS_V2) && !defined(CONFIG_MEDIATEK_NETSYS_V3)
@@ -507,7 +549,7 @@
 	};
 
 	u16 vlan1;
-	u16 etype;
+	u16 sp_tag;
 	u32 dmac_hi;
 	union {
 #if !defined(CONFIG_MEDIATEK_NETSYS_V2) && !defined(CONFIG_MEDIATEK_NETSYS_V3)
@@ -579,7 +621,7 @@
 		u32 info_blk2;
 	};
 	u16 vlan1;
-	u16 etype;
+	u16 sp_tag;
 	u32 dmac_hi;
 	union {
 #if !defined(CONFIG_MEDIATEK_NETSYS_V2) && !defined(CONFIG_MEDIATEK_NETSYS_V3)
@@ -644,7 +686,7 @@
 	};
 
 	u16 vlan1;
-	u16 etype;
+	u16 sp_tag;
 	u32 dmac_hi;
 	union {
 #if !defined(CONFIG_MEDIATEK_NETSYS_V2) && !defined(CONFIG_MEDIATEK_NETSYS_V3)
@@ -717,7 +759,7 @@
 	};
 
 	u16 vlan1;
-	u16 etype;
+	u16 sp_tag;
 	u32 dmac_hi;
 	union {
 #if !defined(CONFIG_MEDIATEK_NETSYS_V2) && !defined(CONFIG_MEDIATEK_NETSYS_V3)
@@ -779,7 +821,7 @@
 	};
 
 	u16 vlan1;
-	u16 etype;
+	u16 sp_tag;
 	u32 dmac_hi;
 	u16 vlan2;
 	u16 dmac_lo;
@@ -815,6 +857,7 @@
 	union {
 		struct hnat_unbind_info_blk udib1;
 		struct hnat_bind_info_blk bfib1;
+		struct hnat_l2_bridge l2_bridge;
 		struct hnat_ipv4_hnapt ipv4_hnapt;
 		struct hnat_ipv4_dslite ipv4_dslite;
 		struct hnat_ipv4_mape ipv4_mape;
@@ -955,6 +998,7 @@
 enum FoeIpAct {
 	IPV4_HNAPT = 0,
 	IPV4_HNAT = 1,
+	L2_BRIDGE = 2,
 	IPV4_DSLITE = 3,
 	IPV6_3T_ROUTE = 4,
 	IPV6_5T_ROUTE = 5,
@@ -1003,6 +1047,7 @@
 #define BIT_IPV4_NAT_EN BIT(12)
 #define BIT_IPV4_NAPT_EN BIT(13)
 #define BIT_IPV4_DSL_EN BIT(14)
+#define BIT_L2_BRG_EN BIT(15)
 #define BIT_MIB_BUSY BIT(16)
 #define BIT_IPV4_NAT_FRAG_EN BIT(17)
 #define BIT_IPV4_HASH_GREK BIT(19)
@@ -1012,6 +1057,9 @@
 #define BIT_IPV6_NAT_EN BIT(23)
 #define BIT_IPV6_NAPT_EN BIT(24)
 #define BIT_CS0_RM_ALL_IP6_IP_EN BIT(25)
+#define BIT_L2_HASH_ETH BIT(29)
+#define BIT_L2_HASH_VID BIT(30)
+#define BIT_L2_LRN_EN BIT(31)
 
 /*GDMA_FWD_CFG value*/
 #define BITS_GDM_UFRC_P_PPE (NR_PPE0_PORT << 12)
@@ -1151,6 +1199,7 @@
 	 (get_wifi_hook_if_index_from_dev(dev) != 0)) ? 1 : 0)
 #define IS_EXT(dev) ((get_index_from_dev(dev) != 0) ? 1 : 0)
 #define IS_PPD(dev) (!strcmp(dev->name, hnat_priv->ppd))
+#define IS_L2_BRIDGE(x) (((x)->bfib1.pkt_type == L2_BRIDGE) ? 1 : 0)
 #define IS_IPV4_HNAPT(x) (((x)->bfib1.pkt_type == IPV4_HNAPT) ? 1 : 0)
 #define IS_IPV4_HNAT(x) (((x)->bfib1.pkt_type == IPV4_HNAT) ? 1 : 0)
 #define IS_IPV4_GRP(x) (IS_IPV4_HNAPT(x) | IS_IPV4_HNAT(x))
@@ -1237,7 +1286,7 @@
 {
 	u32 udf = 0;
 
-	if (IS_IPV4_GRP(entry))
+	if (IS_IPV4_GRP(entry) || IS_L2_BRIDGE(entry))
 		udf = entry->ipv4_hnapt.act_dp;
 	else
 		udf = entry->ipv6_5t_route.act_dp;
@@ -1247,7 +1296,7 @@
 
 static inline void hnat_set_entry_lock(struct foe_entry *entry, bool locked)
 {
-	if (IS_IPV4_GRP(entry)) {
+	if (IS_IPV4_GRP(entry) || IS_L2_BRIDGE(entry)) {
 		if (locked)
 			entry->ipv4_hnapt.act_dp |= UDF_HNAT_ENTRY_LOCKED;
 		else
@@ -1300,6 +1349,7 @@
 extern int hook_toggle;
 extern int mape_toggle;
 extern int qos_toggle;
+extern int l2br_toggle;
 extern int tnl_toggle;
 extern int (*mtk_tnl_encap_offload)(struct sk_buff *skb, struct ethhdr *eth);
 extern int (*mtk_tnl_decap_offload)(struct sk_buff *skb);
@@ -1310,6 +1360,8 @@
 				  int fill_inner_info);
 int ext_if_add(struct extdev_entry *ext_entry);
 int ext_if_del(struct extdev_entry *ext_entry);
+void cr_set_bits(void __iomem *reg, u32 bs);
+void cr_clr_bits(void __iomem *reg, u32 bs);
 void cr_set_field(void __iomem *reg, u32 field, u32 val);
 int mtk_sw_nat_hook_tx(struct sk_buff *skb, int gmac_no);
 int mtk_sw_nat_hook_rx(struct sk_buff *skb);
diff --git a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
index 38838a4..20141da 100644
--- a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
+++ b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
@@ -39,13 +39,14 @@
 int qos_ul_toggle = 1;
 int tnl_toggle;
 int xlat_toggle;
+int l2br_toggle;
 struct hnat_desc headroom[DEF_ETRY_NUM];
 unsigned int dbg_cpu_reason_cnt[MAX_CRSN_NUM];
 
 static const char * const entry_state[] = { "INVALID", "UNBIND", "BIND", "FIN" };
 
 static const char * const packet_type[] = {
-	"IPV4_HNAPT",    "IPV4_HNAT",     "IPV6_1T_ROUTE", "IPV4_DSLITE",
+	"IPV4_HNAPT",    "IPV4_HNAT",     "L2_BRIDGE",     "IPV4_DSLITE",
 	"IPV6_3T_ROUTE", "IPV6_5T_ROUTE", "REV",	   "IPV6_6RD",
 	"IPV4_MAP_T",    "IPV4_MAP_E",    "IPV6_HNAPT",    "IPV6_HNAT",
 };
@@ -371,6 +372,8 @@
 	u32 print_cnt;
 	unsigned char h_dest[ETH_ALEN];
 	unsigned char h_source[ETH_ALEN];
+	unsigned char new_h_dest[ETH_ALEN];
+	unsigned char new_h_source[ETH_ALEN];
 	__be32 saddr, daddr, nsaddr, ndaddr;
 
 	if (ppe_id >= CFG_PPE_NUM)
@@ -588,6 +591,13 @@
 				entry->ipv6_hnapt.new_ipv6_ip2,
 				entry->ipv6_hnapt.new_ipv6_ip3);
 		}
+	} else if (IS_L2_BRIDGE(entry)) {
+		pr_info("Information Block 2: %08X (FP=%d FQOS=%d QID=%d)",
+			entry->l2_bridge.info_blk2,
+			entry->l2_bridge.iblk2.dp,
+			entry->l2_bridge.iblk2.fqos,
+			entry->l2_bridge.iblk2.qid);
+		pr_info("Create L2 BRIDGE entry\n");
 #endif
 	}
 
@@ -603,8 +613,8 @@
 			"BIND" : entry->bfib1.state == 3 ?
 			"FIN" : "Unknown");
 		pr_info("Vlan_Layer = %u, ", entry->bfib1.vlan_layer);
-		pr_info("Eth_type = 0x%x, Vid1 = 0x%x, Vid2 = 0x%x\n",
-			entry->ipv4_hnapt.etype, entry->ipv4_hnapt.vlan1,
+		pr_info("SP_TAG = 0x%x, Vid1 = 0x%x, Vid2 = 0x%x\n",
+			entry->ipv4_hnapt.sp_tag, entry->ipv4_hnapt.vlan1,
 			entry->ipv4_hnapt.vlan2);
 		pr_info("multicast = %d, pppoe = %d, proto = %s\n",
 			entry->ipv4_hnapt.iblk2.mcast,
@@ -628,6 +638,32 @@
 			entry->ipv4_hnapt.winfo_pao.is_sp);
 #endif
 		pr_info("=========================================\n\n");
+	} else if (IS_L2_BRIDGE(entry)) {
+		*((u32 *)h_source) = swab32(entry->l2_bridge.smac_hi);
+		*((u16 *)&h_source[4]) = swab16(entry->l2_bridge.smac_lo);
+		*((u32 *)h_dest) = swab32(entry->l2_bridge.dmac_hi);
+		*((u16 *)&h_dest[4]) = swab16(entry->l2_bridge.dmac_lo);
+		*((u32 *)new_h_source) = swab32(entry->l2_bridge.new_smac_hi);
+		*((u16 *)&new_h_source[4]) = swab16(entry->l2_bridge.new_smac_lo);
+		*((u32 *)new_h_dest) = swab32(entry->l2_bridge.new_dmac_hi);
+		*((u16 *)&new_h_dest[4]) = swab16(entry->l2_bridge.new_dmac_lo);
+		pr_info("Org SMAC=%pM => DMAC=%pM\n", h_source, h_dest);
+		pr_info("New SMAC=%pM => DMAC=%pM\n", new_h_source, new_h_dest);
+		pr_info("Eth_type = 0x%04x, Vid1 = 0x%x, Vid2 = 0x%x\n",
+			entry->l2_bridge.etype, entry->l2_bridge.vlan1,
+			entry->l2_bridge.vlan2);
+		pr_info("State = %s, ",	entry->bfib1.state == 0 ?
+			"Invalid" : entry->bfib1.state == 1 ?
+			"Unbind" : entry->bfib1.state == 2 ?
+			"BIND" : entry->bfib1.state == 3 ?
+			"FIN" : "Unknown");
+		pr_info("DR_IDX = %x\n", entry->l2_bridge.iblk2.rxid);
+		pr_info("Vlan_Layer = %u, ", entry->bfib1.vlan_layer);
+		pr_info("SP_TAG = 0x%04x, New_Vid1 = 0x%x, New_Vid2 = 0x%x\n",
+			entry->l2_bridge.sp_tag, entry->l2_bridge.new_vlan1,
+			entry->l2_bridge.new_vlan2);
+		pr_info("multicast = %d\n", entry->l2_bridge.iblk2.mcast);
+		pr_info("=========================================\n\n");
 	} else {
 		*((u32 *)h_source) = swab32(entry->ipv6_5t_route.smac_hi);
 		*((u16 *)&h_source[4]) = swab16(entry->ipv6_5t_route.smac_lo);
@@ -641,8 +677,8 @@
 			"FIN" : "Unknown");
 
 		pr_info("Vlan_Layer = %u, ", entry->bfib1.vlan_layer);
-		pr_info("Eth_type = 0x%x, Vid1 = 0x%x, Vid2 = 0x%x\n",
-			entry->ipv6_5t_route.etype, entry->ipv6_5t_route.vlan1,
+		pr_info("SP_TAG = 0x%x, Vid1 = 0x%x, Vid2 = 0x%x\n",
+			entry->ipv6_5t_route.sp_tag, entry->ipv6_5t_route.vlan1,
 			entry->ipv6_5t_route.vlan2);
 		pr_info("multicast = %d, pppoe = %d, proto = %s\n",
 			entry->ipv6_5t_route.iblk2.mcast,
@@ -1185,14 +1221,14 @@
 				swab16(entry->ipv4_hnapt.dmac_lo);
 			PRINT_COUNT(m, acct);
 			seq_printf(m,
-				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|%pI4:%d->%pI4:%d=>%pI4:%d->%pI4:%d|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n",
+				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|%pI4:%d->%pI4:%d=>%pI4:%d->%pI4:%d|%pM=>%pM|sp_tag=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n",
 				   entry, ppe_id, ei(entry, end),
 				   es(entry), pt(entry), &saddr,
 				   entry->ipv4_hnapt.sport, &daddr,
 				   entry->ipv4_hnapt.dport, &nsaddr,
 				   entry->ipv4_hnapt.new_sport, &ndaddr,
 				   entry->ipv4_hnapt.new_dport, h_source, h_dest,
-				   ntohs(entry->ipv4_hnapt.etype),
+				   ntohs(entry->ipv4_hnapt.sp_tag),
 				   entry->ipv4_hnapt.info_blk1,
 				   entry->ipv4_hnapt.info_blk2,
 				   entry->ipv4_hnapt.vlan1,
@@ -1211,11 +1247,11 @@
 				swab16(entry->ipv4_hnapt.dmac_lo);
 			PRINT_COUNT(m, acct);
 			seq_printf(m,
-				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|%pI4->%pI4=>%pI4->%pI4|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n",
+				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|%pI4->%pI4=>%pI4->%pI4|%pM=>%pM|sp_tag=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n",
 				   entry, ppe_id, ei(entry, end),
 				   es(entry), pt(entry), &saddr,
 				   &daddr, &nsaddr, &ndaddr, h_source, h_dest,
-				   ntohs(entry->ipv4_hnapt.etype),
+				   ntohs(entry->ipv4_hnapt.sp_tag),
 				   entry->ipv4_hnapt.info_blk1,
 				   entry->ipv4_hnapt.info_blk2,
 				   entry->ipv4_hnapt.vlan1,
@@ -1239,13 +1275,13 @@
 				swab16(entry->ipv6_5t_route.dmac_lo);
 			PRINT_COUNT(m, acct);
 			seq_printf(m,
-				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x(sp=%d)->DIP=%08x:%08x:%08x:%08x(dp=%d)|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x\n",
+				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x(sp=%d)->DIP=%08x:%08x:%08x:%08x(dp=%d)|%pM=>%pM|sp_tag=0x%04x|info1=0x%x|info2=0x%x\n",
 				   entry, ppe_id, ei(entry, end), es(entry), pt(entry), ipv6_sip0,
 				   ipv6_sip1, ipv6_sip2, ipv6_sip3,
 				   entry->ipv6_5t_route.sport, ipv6_dip0,
 				   ipv6_dip1, ipv6_dip2, ipv6_dip3,
 				   entry->ipv6_5t_route.dport, h_source, h_dest,
-				   ntohs(entry->ipv6_5t_route.etype),
+				   ntohs(entry->ipv6_5t_route.sp_tag),
 				   entry->ipv6_5t_route.info_blk1,
 				   entry->ipv6_5t_route.info_blk2);
 		} else if (IS_IPV6_3T_ROUTE(entry)) {
@@ -1267,12 +1303,12 @@
 				swab16(entry->ipv6_5t_route.dmac_lo);
 			PRINT_COUNT(m, acct);
 			seq_printf(m,
-				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x->DIP=%08x:%08x:%08x:%08x|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x\n",
+				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x->DIP=%08x:%08x:%08x:%08x|%pM=>%pM|sp_tag=0x%04x|info1=0x%x|info2=0x%x\n",
 				   entry, ppe_id, ei(entry, end),
 				   es(entry), pt(entry), ipv6_sip0,
 				   ipv6_sip1, ipv6_sip2, ipv6_sip3, ipv6_dip0,
 				   ipv6_dip1, ipv6_dip2, ipv6_dip3, h_source,
-				   h_dest, ntohs(entry->ipv6_5t_route.etype),
+				   h_dest, ntohs(entry->ipv6_5t_route.sp_tag),
 				   entry->ipv6_5t_route.info_blk1,
 				   entry->ipv6_5t_route.info_blk2);
 		} else if (IS_IPV6_6RD(entry)) {
@@ -1296,7 +1332,7 @@
 				swab16(entry->ipv6_5t_route.dmac_lo);
 			PRINT_COUNT(m, acct);
 			seq_printf(m,
-				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x(sp=%d)->DIP=%08x:%08x:%08x:%08x(dp=%d)|TSIP=%pI4->TDIP=%pI4|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x\n",
+				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x(sp=%d)->DIP=%08x:%08x:%08x:%08x(dp=%d)|TSIP=%pI4->TDIP=%pI4|%pM=>%pM|sp_tag=0x%04x|info1=0x%x|info2=0x%x\n",
 				   entry, ppe_id, ei(entry, end),
 				   es(entry), pt(entry), ipv6_sip0,
 				   ipv6_sip1, ipv6_sip2, ipv6_sip3,
@@ -1304,7 +1340,7 @@
 				   ipv6_dip1, ipv6_dip2, ipv6_dip3,
 				   entry->ipv6_5t_route.dport, &tsaddr, &tdaddr,
 				   h_source, h_dest,
-				   ntohs(entry->ipv6_5t_route.etype),
+				   ntohs(entry->ipv6_5t_route.sp_tag),
 				   entry->ipv6_5t_route.info_blk1,
 				   entry->ipv6_5t_route.info_blk2);
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
@@ -1332,7 +1368,7 @@
 
 			if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_SNAT) {
 				seq_printf(m,
-					   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x(sp=%d)->DIP=%08x:%08x:%08x:%08x(dp=%d)|NEW_SIP=%08x:%08x:%08x:%08x(sp=%d)->NEW_DIP=%08x:%08x:%08x:%08x(dp=%d)|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n",
+					   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x(sp=%d)->DIP=%08x:%08x:%08x:%08x(dp=%d)|NEW_SIP=%08x:%08x:%08x:%08x(sp=%d)->NEW_DIP=%08x:%08x:%08x:%08x(dp=%d)|%pM=>%pM|sp_tag=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n",
 					   entry, ppe_id, ei(entry, end),
 					   es(entry), pt(entry),
 					   ipv6_sip0, ipv6_sip1,
@@ -1348,14 +1384,14 @@
 					   ipv6_dip2, ipv6_dip3,
 					   entry->ipv6_hnapt.new_dport,
 					   h_source, h_dest,
-					   ntohs(entry->ipv6_hnapt.etype),
+					   ntohs(entry->ipv6_hnapt.sp_tag),
 					   entry->ipv6_hnapt.info_blk1,
 					   entry->ipv6_hnapt.info_blk2,
 					   entry->ipv6_hnapt.vlan1,
 					   entry->ipv6_hnapt.vlan2);
 			} else if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_DNAT) {
 				seq_printf(m,
-					   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x(sp=%d)->DIP=%08x:%08x:%08x:%08x(dp=%d)|NEW_SIP=%08x:%08x:%08x:%08x(sp=%d)->NEW_DIP=%08x:%08x:%08x:%08x(dp=%d)|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n",
+					   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x(sp=%d)->DIP=%08x:%08x:%08x:%08x(dp=%d)|NEW_SIP=%08x:%08x:%08x:%08x(sp=%d)->NEW_DIP=%08x:%08x:%08x:%08x(dp=%d)|%pM=>%pM|sp_tag=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n",
 					   entry, ppe_id, ei(entry, end),
 					   es(entry), pt(entry),
 					   ipv6_sip0, ipv6_sip1,
@@ -1371,7 +1407,7 @@
 					   new_ipv6_ip2, new_ipv6_ip3,
 					   entry->ipv6_hnapt.new_dport,
 					   h_source, h_dest,
-					   ntohs(entry->ipv6_hnapt.etype),
+					   ntohs(entry->ipv6_hnapt.sp_tag),
 					   entry->ipv6_hnapt.info_blk1,
 					   entry->ipv6_hnapt.info_blk2,
 					   entry->ipv6_hnapt.vlan1,
@@ -1401,7 +1437,7 @@
 
 			if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_SNAT) {
 				seq_printf(m,
-					   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x->DIP=%08x:%08x:%08x:%08x|NEW_SIP=%08x:%08x:%08x:%08x->NEW_DIP=%08x:%08x:%08x:%08x|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n",
+					   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x->DIP=%08x:%08x:%08x:%08x|NEW_SIP=%08x:%08x:%08x:%08x->NEW_DIP=%08x:%08x:%08x:%08x|%pM=>%pM|sp_tag=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n",
 					   entry, ppe_id, ei(entry, end),
 					   es(entry), pt(entry),
 					   ipv6_sip0, ipv6_sip1,
@@ -1413,14 +1449,14 @@
 					   ipv6_dip0, ipv6_dip1,
 					   ipv6_dip2, ipv6_dip3,
 					   h_source, h_dest,
-					   ntohs(entry->ipv6_hnapt.etype),
+					   ntohs(entry->ipv6_hnapt.sp_tag),
 					   entry->ipv6_hnapt.info_blk1,
 					   entry->ipv6_hnapt.info_blk2,
 					   entry->ipv6_hnapt.vlan1,
 					   entry->ipv6_hnapt.vlan2);
 			} else if (entry->ipv6_hnapt.eg_ipv6_dir == IPV6_DNAT) {
 				seq_printf(m,
-					   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x->DIP=%08x:%08x:%08x:%08x|NEW_SIP=%08x:%08x:%08x:%08x->NEW_DIP=%08x:%08x:%08x:%08x|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n",
+					   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%08x:%08x:%08x:%08x->DIP=%08x:%08x:%08x:%08x|NEW_SIP=%08x:%08x:%08x:%08x->NEW_DIP=%08x:%08x:%08x:%08x|%pM=>%pM|sp_tag=0x%04x|info1=0x%x|info2=0x%x|vlan1=%d|vlan2=%d\n",
 					   entry, ppe_id, ei(entry, end),
 					   es(entry), pt(entry),
 					   ipv6_sip0, ipv6_sip1,
@@ -1432,12 +1468,44 @@
 					   new_ipv6_ip0, new_ipv6_ip1,
 					   new_ipv6_ip2, new_ipv6_ip3,
 					   h_source, h_dest,
-					   ntohs(entry->ipv6_hnapt.etype),
+					   ntohs(entry->ipv6_hnapt.sp_tag),
 					   entry->ipv6_hnapt.info_blk1,
 					   entry->ipv6_hnapt.info_blk2,
 					   entry->ipv6_hnapt.vlan1,
 					   entry->ipv6_hnapt.vlan2);
 			}
+		} else if (IS_L2_BRIDGE(entry)) {
+			unsigned char new_h_dest[ETH_ALEN];
+			unsigned char new_h_source[ETH_ALEN];
+
+			*((u32 *)h_source) = swab32(entry->l2_bridge.smac_hi);
+			*((u16 *)&h_source[4]) =
+				swab16(entry->l2_bridge.smac_lo);
+			*((u32 *)h_dest) = swab32(entry->l2_bridge.dmac_hi);
+			*((u16 *)&h_dest[4]) =
+				swab16(entry->l2_bridge.dmac_lo);
+
+			*((u32 *)new_h_source) = swab32(entry->l2_bridge.new_smac_hi);
+			*((u16 *)&new_h_source[4]) =
+				swab16(entry->l2_bridge.new_smac_lo);
+			*((u32 *)new_h_dest) = swab32(entry->l2_bridge.new_dmac_hi);
+			*((u16 *)&new_h_dest[4]) =
+				swab16(entry->l2_bridge.new_dmac_lo);
+
+			PRINT_COUNT(m, acct);
+			seq_printf(m,
+				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|%pM->%pM=>%pM->%pM|eth=0x%04x|sp_tag=%04x|info1=0x%x|info2=0x%x|vlan1=%d=>%d|vlan2=%d=>%d\n",
+				   entry, ppe_id, ei(entry, end),
+				   es(entry), pt(entry),
+				   h_source, h_dest, new_h_source, new_h_dest,
+				   entry->l2_bridge.etype,
+				   entry->l2_bridge.sp_tag,
+				   entry->l2_bridge.info_blk1,
+				   entry->l2_bridge.info_blk2,
+				   entry->l2_bridge.vlan1,
+				   entry->l2_bridge.new_vlan1,
+				   entry->l2_bridge.vlan2,
+				   entry->l2_bridge.new_vlan2);
 #endif
 		} else if (IS_IPV4_DSLITE(entry)) {
 			__be32 saddr = htonl(entry->ipv4_hnapt.sip);
@@ -1459,13 +1527,13 @@
 				swab16(entry->ipv4_dslite.dmac_lo);
 			PRINT_COUNT(m, acct);
 			seq_printf(m,
-				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%pI4->DIP=%pI4|TSIP=%08x:%08x:%08x:%08x->TDIP=%08x:%08x:%08x:%08x|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x\n",
+				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%pI4->DIP=%pI4|TSIP=%08x:%08x:%08x:%08x->TDIP=%08x:%08x:%08x:%08x|%pM=>%pM|sp_tag=0x%04x|info1=0x%x|info2=0x%x\n",
 				   entry, ppe_id, ei(entry, end),
 				   es(entry), pt(entry), &saddr,
 				   &daddr, ipv6_tsip0, ipv6_tsip1, ipv6_tsip2,
 				   ipv6_tsip3, ipv6_tdip0, ipv6_tdip1, ipv6_tdip2,
 				   ipv6_tdip3, h_source, h_dest,
-				   ntohs(entry->ipv6_5t_route.etype),
+				   ntohs(entry->ipv6_5t_route.sp_tag),
 				   entry->ipv6_5t_route.info_blk1,
 				   entry->ipv6_5t_route.info_blk2);
 #if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
@@ -1491,7 +1559,7 @@
 				swab16(entry->ipv4_dslite.dmac_lo);
 			PRINT_COUNT(m, acct);
 			seq_printf(m,
-				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%pI4:%d->DIP=%pI4:%d|NSIP=%pI4:%d->NDIP=%pI4:%d|TSIP=%08x:%08x:%08x:%08x->TDIP=%08x:%08x:%08x:%08x|%pM=>%pM|etype=0x%04x|info1=0x%x|info2=0x%x\n",
+				   "addr=0x%p|ppe=%d|index=%d|state=%s|type=%s|SIP=%pI4:%d->DIP=%pI4:%d|NSIP=%pI4:%d->NDIP=%pI4:%d|TSIP=%08x:%08x:%08x:%08x->TDIP=%08x:%08x:%08x:%08x|%pM=>%pM|sp_tag=0x%04x|info1=0x%x|info2=0x%x\n",
 				   entry, ppe_id, ei(entry, end),
 				   es(entry), pt(entry),
 				   &saddr, entry->ipv4_dslite.sport,
@@ -1501,7 +1569,7 @@
 				   ipv6_tsip0, ipv6_tsip1, ipv6_tsip2,
 				   ipv6_tsip3, ipv6_tdip0, ipv6_tdip1,
 				   ipv6_tdip2, ipv6_tdip3, h_source, h_dest,
-				   ntohs(entry->ipv6_5t_route.etype),
+				   ntohs(entry->ipv6_5t_route.sp_tag),
 				   entry->ipv6_5t_route.info_blk1,
 				   entry->ipv6_5t_route.info_blk2);
 #endif
@@ -1897,6 +1965,28 @@
 				   entry->ipv6_hnapt.new_ipv6_ip2,
 				   entry->ipv6_hnapt.new_ipv6_ip3);
 		}
+	} else if (IS_L2_BRIDGE(entry)) {
+		unsigned char h_dest[ETH_ALEN];
+		unsigned char h_source[ETH_ALEN];
+		unsigned char new_h_dest[ETH_ALEN];
+		unsigned char new_h_source[ETH_ALEN];
+
+		*((u32 *)h_source) = swab32(entry->l2_bridge.smac_hi);
+		*((u16 *)&h_source[4]) =
+			swab16(entry->l2_bridge.smac_lo);
+		*((u32 *)h_dest) = swab32(entry->l2_bridge.dmac_hi);
+		*((u16 *)&h_dest[4]) =
+			swab16(entry->l2_bridge.dmac_lo);
+
+		*((u32 *)new_h_source) = swab32(entry->l2_bridge.new_smac_hi);
+		*((u16 *)&new_h_source[4]) =
+			swab16(entry->l2_bridge.new_smac_lo);
+		*((u32 *)new_h_dest) = swab32(entry->l2_bridge.new_dmac_hi);
+		*((u16 *)&new_h_dest[4]) =
+			swab16(entry->l2_bridge.new_dmac_lo);
+		seq_printf(m,
+			   "L2_BRIDGE(%d): %pM->%pM => %pM -> %pM\n",
+			   index, h_source, h_dest, new_h_source, new_h_dest);
 #endif
 	}
 }
@@ -2753,6 +2843,95 @@
 	.release = single_release,
 };
 
+static void hnat_l2br_toggle_usage(void)
+{
+	pr_info("\n*** L2_BRIDGE HNAT is only supported on MT7987 platform ***\n");
+	pr_info("-------------------- Usage --------------------\n");
+	pr_info("Show L2_BRIDGE HNAT mode:\n");
+	pr_info("    cat /sys/kernel/debug/hnat/l2br_toggle\n");
+	pr_info("Disable L2_BRIDGE HNAT:\n");
+	pr_info("    echo 0 > /sys/kernel/debug/hnat/l2br_toggle\n");
+	pr_info("Enable L2_BRIDGE HNAT learning mode:\n");
+	pr_info("    echo 1 > /sys/kernel/debug/hnat/l2br_toggle\n");
+	pr_info("Enable L2_BRIDGE HNAT look-up mode:\n");
+	pr_info("    echo 2 > /sys/kernel/debug/hnat/l2br_toggle\n");
+	pr_info("Show L2_BRIDGE toggle usage:\n");
+	pr_info("    echo 3 > /sys/kernel/debug/hnat/l2br_toggle\n");
+	pr_info("-------------------- Details ------------------\n");
+	pr_info("Learning mode: PPE automatically learns and creates new L2_BRIDGE entries\n");
+	pr_info("Look-up mode : For L2 packets, PPE only checks existing entries without creating new ones\n");
+}
+
+static int hnat_l2br_toggle_read(struct seq_file *m, void *private)
+{
+	if (l2br_toggle == 0)
+		pr_info("L2_BRIDGE HNAT is disabled now!\n");
+	else if (l2br_toggle == 1)
+		pr_info("L2_BRIDGE HNAT learning mode is enabled now!\n");
+	else if (l2br_toggle == 2)
+		pr_info("L2_BRIDGE HNAT look up mode is enabled now!\n");
+
+	return 0;
+}
+
+static int hnat_l2br_toggle_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, hnat_l2br_toggle_read, file->private_data);
+}
+
+static ssize_t hnat_l2br_toggle_write(struct file *file, const char __user *buffer,
+				     size_t count, loff_t *data)
+{
+	char buf[32] = {0};
+	int len = count;
+	u32 i, ppe_cfg = 0;
+
+	if (len  >= sizeof(buf))
+		return -EFAULT;
+
+	if (copy_from_user(buf, buffer, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+
+	if (buf[0] == '0') {
+		pr_info("L2_BRIDGE HNAT is going to be disabled!\n");
+		l2br_toggle = 0;
+	} else if (buf[0] == '1') {
+		pr_info("L2_BRIDGE HNAT learning mode is going to be enabled!\n");
+		l2br_toggle = 1;
+		ppe_cfg = BIT_L2_BRG_EN | BIT_L2_LRN_EN;
+	} else if (buf[0] == '2') {
+		pr_info("L2_BRIDGE HNAT look-up mode is going to be enabled!\n");
+		l2br_toggle = 2;
+		ppe_cfg = BIT_L2_BRG_EN;
+	} else if (buf[0] == '3') {
+		hnat_l2br_toggle_usage();
+		goto skip_ppe_cfg;
+	} else {
+		pr_err("Input error!\n");
+		hnat_l2br_toggle_usage();
+		goto skip_ppe_cfg;
+	}
+
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		cr_clr_bits(hnat_priv->ppe_base[i] + PPE_FLOW_CFG,
+			    BIT_L2_BRG_EN | BIT_L2_LRN_EN);
+		cr_set_bits(hnat_priv->ppe_base[i] + PPE_FLOW_CFG, ppe_cfg);
+	}
+
+skip_ppe_cfg:
+	return len;
+}
+
+static const struct file_operations hnat_l2br_toggle_fops = {
+	.open = hnat_l2br_toggle_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.write = hnat_l2br_toggle_write,
+	.release = single_release,
+};
+
 static int hnat_version_read(struct seq_file *m, void *private)
 {
 	pr_info("HNAT SW version : %s\nHNAT HW version : %d\n", HNAT_SW_VER, hnat_priv->data->version);
@@ -2777,6 +2956,13 @@
 	u32 hv1 = 0, hv2 = 0, hv3 = 0, hash = 0;
 
 	switch (entry->bfib1.pkt_type) {
+	case L2_BRIDGE:
+		hv1 = (entry->l2_bridge.etype << 16) |
+		      (entry->l2_bridge.vlan2 ^ entry->l2_bridge.vlan1);
+		hv2 = 0x5a5a |
+		      ((entry->l2_bridge.smac_lo ^ entry->l2_bridge.dmac_lo) << 16);
+		hv3 = entry->l2_bridge.dmac_hi ^ entry->l2_bridge.smac_hi;
+		break;
 	case IPV4_HNAPT:
 	case IPV4_HNAT:
 	case IPV4_DSLITE:
@@ -2858,6 +3044,7 @@
 {
 	struct foe_entry *foe, entry = { 0 };
 	char buf[256], dmac_str[18], smac_str[18], dmac[6], smac[6];
+	char new_dmac_str[18], new_smac_str[18], new_dmac[6], new_smac[6];
 	int len = count, hash, coll = 0;
 	u32 ppe_id = 0;
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
@@ -2877,7 +3064,7 @@
 		return -EFAULT;
 	}
 
-	if (entry.ipv4_hnapt.bfib1.pkt_type == IPV4_HNAPT) {
+	if (entry.bfib1.pkt_type == IPV4_HNAPT) {
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
 		if (sscanf(buf,
 			"%5d %8x %8x %8x %hx %hx %8x %8x %8x %hx %hx %18s %18s %4x %4x %4x",
@@ -2928,7 +3115,7 @@
 			return -EFAULT;
 		}
 #endif
-	} else if (entry.ipv4_hnapt.bfib1.pkt_type == IPV6_5T_ROUTE) {
+	} else if (entry.bfib1.pkt_type == IPV6_5T_ROUTE) {
 #if defined(CONFIG_MEDIATEK_NETSYS_V3)
 		if (sscanf(buf,
 			"%5d %8x %8x%8x%8x%8x %8x%8x%8x%8x %hx %hx %8x %18s %18s %4x %4x %4x",
@@ -2983,6 +3170,31 @@
 			return -EFAULT;
 		}
 #endif
+	} else if (entry.bfib1.pkt_type == L2_BRIDGE) {
+		if (sscanf(buf,
+			"%5d %8x %18s %18s %hx %hx %hx %8x %18s %18s %hx %hx",
+			&hash,
+			&entry.l2_bridge.info_blk1,
+			dmac_str,
+			smac_str,
+			&entry.l2_bridge.etype,
+			&entry.l2_bridge.vlan1,
+			&entry.l2_bridge.vlan2,
+			&entry.l2_bridge.info_blk2,
+			new_dmac_str,
+			new_smac_str,
+			&entry.l2_bridge.new_vlan1,
+			&entry.l2_bridge.new_vlan2) != 12)
+			return -EFAULT;
+
+		if ((hash >= (int)hnat_priv->foe_etry_num) || (hash < -1)) {
+			hnat_static_entry_help();
+			return -EFAULT;
+		}
+
+		entry.l2_bridge.hph = 0xa5a5;
+		hnat_parse_mac(new_smac_str, new_smac);
+		hnat_parse_mac(new_dmac_str, new_dmac);
 	} else {
 		pr_info("Unknown packet type!\n");
 		return -EFAULT;
@@ -2991,16 +3203,25 @@
 
 	hnat_parse_mac(smac_str, smac);
 	hnat_parse_mac(dmac_str, dmac);
-	if (entry.ipv4_hnapt.bfib1.pkt_type == IPV4_HNAPT) {
+	if (entry.bfib1.pkt_type == IPV4_HNAPT) {
 		entry.ipv4_hnapt.dmac_hi = swab32(*((u32 *)dmac));
 		entry.ipv4_hnapt.dmac_lo = swab16(*((u16 *)&dmac[4]));
 		entry.ipv4_hnapt.smac_hi = swab32(*((u32 *)smac));
 		entry.ipv4_hnapt.smac_lo = swab16(*((u16 *)&smac[4]));
-	} else if (entry.ipv4_hnapt.bfib1.pkt_type == IPV6_5T_ROUTE) {
+	} else if (entry.bfib1.pkt_type == IPV6_5T_ROUTE) {
 		entry.ipv6_5t_route.dmac_hi = swab32(*((u32 *)dmac));
 		entry.ipv6_5t_route.dmac_lo = swab16(*((u16 *)&dmac[4]));
 		entry.ipv6_5t_route.smac_hi = swab32(*((u32 *)smac));
 		entry.ipv6_5t_route.smac_lo = swab16(*((u16 *)&smac[4]));
+	} else if (entry.bfib1.pkt_type == L2_BRIDGE) {
+		entry.l2_bridge.dmac_hi = swab32(*((u32 *)dmac));
+		entry.l2_bridge.dmac_lo = swab16(*((u16 *)&dmac[4]));
+		entry.l2_bridge.smac_hi = swab32(*((u32 *)smac));
+		entry.l2_bridge.smac_lo = swab16(*((u16 *)&smac[4]));
+		entry.l2_bridge.new_dmac_hi = swab32(*((u32 *)new_dmac));
+		entry.l2_bridge.new_dmac_lo = swab16(*((u16 *)&new_dmac[4]));
+		entry.l2_bridge.new_smac_hi = swab32(*((u32 *)new_smac));
+		entry.l2_bridge.new_smac_lo = swab16(*((u16 *)&new_smac[4]));
 	}
 
 	if (hash == -1)
@@ -3210,6 +3431,8 @@
 			    &hnat_xlat_toggle_fops);
 	debugfs_create_file("xlat_cfg", 0444, root, h,
 			    &hnat_xlat_cfg_fops);
+	debugfs_create_file("l2br_toggle", 0444, root, h,
+			    &hnat_l2br_toggle_fops);
 
 	for (i = 0; i < hnat_priv->data->num_of_sch; i++) {
 		ret = snprintf(name, sizeof(name), "qdma_sch%ld", i);
diff --git a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
index 72df937..0a5be88 100644
--- a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+++ b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
@@ -231,8 +231,8 @@
 						entry->ipv6_5t_route.vlan1 == dsa_tag;
 				} else {
 					match_dev = (IS_IPV4_GRP(entry)) ?
-						!!(entry->ipv4_hnapt.etype & dsa_tag) :
-						!!(entry->ipv6_5t_route.etype & dsa_tag);
+						!!(entry->ipv4_hnapt.sp_tag & dsa_tag) :
+						!!(entry->ipv6_5t_route.sp_tag & dsa_tag);
 				}
 			}
 
@@ -861,6 +861,10 @@
 		break;
 	case ETH_P_8021Q:
 		return 1;
+	default:
+		if (l2br_toggle == 1)
+			return 1;
+		break;
 	}
 
 	return 0;
@@ -1256,6 +1260,12 @@
 				  struct flow_offload_hw_path *hw_path)
 {
 	switch ((int)entry.bfib1.pkt_type) {
+	case L2_BRIDGE:
+		entry.l2_bridge.new_dmac_hi = swab32(*((u32 *)hw_path->eth_dest));
+		entry.l2_bridge.new_dmac_lo = swab16(*((u16 *)&hw_path->eth_dest[4]));
+		entry.l2_bridge.new_smac_hi = swab32(*((u32 *)hw_path->eth_src));
+		entry.l2_bridge.new_smac_lo = swab16(*((u16 *)&hw_path->eth_src[4]));
+		break;
 	case IPV4_HNAPT:
 	case IPV4_HNAT:
 		entry.ipv4_hnapt.dmac_hi = swab32(*((u32 *)hw_path->eth_dest));
@@ -1294,6 +1304,15 @@
 		readl(hnat_priv->fe_base + 0x0010) & (0x7FFF);
 
 	switch ((int)entry.bfib1.pkt_type) {
+	case L2_BRIDGE:
+		if (hnat_priv->data->mcast &&
+		    is_multicast_ether_addr(&hw_path->eth_dest[0]))
+			entry.l2_bridge.iblk2.mcast = 1;
+		else
+			entry.l2_bridge.iblk2.mcast = 0;
+
+		entry.l2_bridge.iblk2.port_ag = 0xf;
+		break;
 	case IPV4_HNAPT:
 	case IPV4_HNAT:
 		if (hnat_priv->data->mcast &&
@@ -1479,7 +1498,7 @@
 			udp = 1;
 			/* fallthrough */
 		case IPPROTO_TCP:
-			entry.ipv4_hnapt.etype = htons(ETH_P_IP);
+			entry.ipv4_hnapt.sp_tag = htons(ETH_P_IP);
 			if (IS_IPV4_GRP(&entry)) {
 				entry.ipv4_hnapt.iblk2.dscp = iph->tos;
 				if (hnat_priv->data->per_flow_accounting)
@@ -1656,7 +1675,7 @@
 			udp = 1;
 			/* fallthrough */
 		case IPPROTO_TCP:
-			entry.ipv4_hnapt.etype = htons(ETH_P_IP);
+			entry.ipv4_hnapt.sp_tag = htons(ETH_P_IP);
 
 			/* DS-Lite WAN->LAN */
 			if (IS_IPV4_DSLITE(&entry) || IS_IPV4_MAPE(&entry)) {
@@ -1819,7 +1838,7 @@
 			udp = 1;
 			/* fallthrough */
 		case NEXTHDR_TCP: /* IPv6-5T or IPv6-3T */
-			entry.ipv6_5t_route.etype = htons(ETH_P_IPV6);
+			entry.ipv6_5t_route.sp_tag = htons(ETH_P_IPV6);
 
 			entry.ipv6_5t_route.vlan1 = hw_path->vlan_id;
 
@@ -2013,7 +2032,7 @@
 					foe->ipv4_hnapt.new_sip;
 				entry.ipv4_hnapt.new_dip =
 					foe->ipv4_hnapt.new_dip;
-				entry.ipv4_hnapt.etype = htons(ETH_P_IP);
+				entry.ipv4_hnapt.sp_tag = htons(ETH_P_IP);
 
 				if (IS_HQOS_MODE) {
 					entry.ipv4_hnapt.iblk2.qid =
@@ -2062,6 +2081,33 @@
 		break;
 
 	default:
+		if (IS_L2_BRIDGE(&entry)) {
+			entry.l2_bridge.dmac_hi = foe->l2_bridge.dmac_hi;
+			entry.l2_bridge.dmac_lo = foe->l2_bridge.dmac_lo;
+			entry.l2_bridge.smac_hi = foe->l2_bridge.smac_hi;
+			entry.l2_bridge.smac_lo = foe->l2_bridge.smac_lo;
+			entry.l2_bridge.etype = foe->l2_bridge.etype;
+			entry.l2_bridge.hph = foe->l2_bridge.hph;
+			entry.l2_bridge.vlan1 = foe->l2_bridge.vlan1;
+			entry.l2_bridge.vlan2 = foe->l2_bridge.vlan2;
+			entry.l2_bridge.sp_tag = htons(h_proto);
+
+			if (hnat_priv->data->per_flow_accounting)
+				entry.l2_bridge.iblk2.mibf = 1;
+
+			entry.l2_bridge.new_vlan1 = hw_path->vlan_id;
+			if (skb_vlan_tagged(skb)) {
+				entry.bfib1.vlan_layer += 1;
+
+				if (entry.l2_bridge.new_vlan1)
+					entry.l2_bridge.new_vlan2 =
+						skb->vlan_tci;
+				else
+					entry.l2_bridge.new_vlan1 =
+						skb->vlan_tci;
+			}
+			break;
+		}
 		return -1;
 	}
 
@@ -2133,7 +2179,7 @@
 		 * Current setting is PDMA RX.
 		 */
 		gmac = NR_PDMA_PORT;
-		if (IS_IPV4_GRP(foe)) {
+		if (IS_IPV4_GRP(foe) || IS_L2_BRIDGE(foe)) {
 			entry.ipv4_hnapt.act_dp &= ~UDF_PINGPONG_IFIDX;
 			entry.ipv4_hnapt.act_dp |= dev->ifindex & UDF_PINGPONG_IFIDX;
 		} else {
@@ -2205,7 +2251,7 @@
 				      FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb))) ||
 				      ((mape_toggle && mape == 1) && !FROM_EXT(skb))) &&
 				      (!whnat)) {
-					entry.ipv4_hnapt.etype = htons(HQOS_MAGIC_TAG);
+					entry.ipv4_hnapt.sp_tag = htons(HQOS_MAGIC_TAG);
 					entry.ipv4_hnapt.vlan1 = skb_hnat_entry(skb);
 					entry.bfib1.vlan_layer = 1;
 				}
@@ -2223,6 +2269,18 @@
 		} else {
 			entry.ipv4_hnapt.iblk2.fqos = 0;
 		}
+	} else if (IS_L2_BRIDGE(&entry)) {
+		entry.l2_bridge.iblk2.dp = gmac;
+		entry.l2_bridge.iblk2.port_mg = 0;
+		if (qos_toggle) {
+			entry.l2_bridge.iblk2.qid = qid & 0x7f;
+			if (FROM_EXT(skb) || skb_hnat_sport(skb) == NR_QDMA_PORT)
+				entry.l2_bridge.iblk2.fqos = 0;
+			else
+				entry.l2_bridge.iblk2.fqos = HQOS_FLAG(dev, skb, qid) ? 1 : 0;
+		} else {
+			entry.l2_bridge.iblk2.fqos = 0;
+		}
 	} else {
 		entry.ipv6_5t_route.iblk2.dp = gmac & 0xf;
 #if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
@@ -2246,7 +2304,7 @@
 				if (IS_EXT(dev) && (FROM_GE_LAN_GRP(skb) ||
 				    FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb)) &&
 				    (!whnat)) {
-					entry.ipv6_5t_route.etype = htons(HQOS_MAGIC_TAG);
+					entry.ipv6_5t_route.sp_tag = htons(HQOS_MAGIC_TAG);
 					entry.ipv6_5t_route.vlan1 = skb_hnat_entry(skb);
 					entry.bfib1.vlan_layer = 1;
 				}
@@ -2413,7 +2471,9 @@
 		if (is_multicast_ether_addr(eth->h_dest))
 			goto check_release_entry_lock;
 
-		if (IS_IPV4_GRP(&entry))
+		if (IS_L2_BRIDGE(&entry))
+			entry.l2_bridge.iblk2.mcast = 0;
+		else if (IS_IPV4_GRP(&entry))
 			entry.ipv4_hnapt.iblk2.mcast = 0;
 		else
 			entry.ipv6_5t_route.iblk2.mcast = 0;
@@ -2439,6 +2499,7 @@
 	 * will change the smac for specail purpose.
 	 */
 	switch ((int)entry.bfib1.pkt_type) {
+	case L2_BRIDGE:
 	case IPV4_HNAPT:
 	case IPV4_HNAT:
 		/*
@@ -2465,11 +2526,11 @@
 	if (skb_vlan_tagged(skb)) {
 		entry.bfib1.vlan_layer = 1;
 		entry.bfib1.vpm = 1;
-		if (IS_IPV4_GRP(&entry)) {
-			entry.ipv4_hnapt.etype = htons(ETH_P_8021Q);
+		if (IS_IPV4_GRP(&entry) || IS_L2_BRIDGE(&entry)) {
+			entry.ipv4_hnapt.sp_tag = htons(ETH_P_8021Q);
 			entry.ipv4_hnapt.vlan1 = skb->vlan_tci;
 		} else if (IS_IPV6_GRP(&entry)) {
-			entry.ipv6_5t_route.etype = htons(ETH_P_8021Q);
+			entry.ipv6_5t_route.sp_tag = htons(ETH_P_8021Q);
 			entry.ipv6_5t_route.vlan1 = skb->vlan_tci;
 		}
 	} else {
@@ -2525,7 +2586,7 @@
 			    (FROM_GE_LAN_GRP(skb) || FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb))) {
 				entry.bfib1.vpm = 0;
 				entry.bfib1.vlan_layer = 1;
-				entry.ipv4_hnapt.etype = htons(HQOS_MAGIC_TAG);
+				entry.ipv4_hnapt.sp_tag = htons(HQOS_MAGIC_TAG);
 				entry.ipv4_hnapt.vlan1 = skb_hnat_entry(skb);
 				entry.ipv4_hnapt.iblk2.fqos = 1;
 			}
@@ -2548,6 +2609,22 @@
 		entry.ipv6_hnapt.winfo_pao.hf = skb_hnat_hf(skb);
 		entry.ipv6_hnapt.winfo_pao.amsdu = skb_hnat_amsdu(skb);
 		entry.ipv6_hnapt.tport_id = IS_HQOS_DL_MODE ? NR_QDMA_TPORT : 0;
+	} else if (IS_L2_BRIDGE(&entry)) {
+		entry.l2_bridge.iblk2.dp = gmac_no;
+		entry.l2_bridge.iblk2.rxid = skb_hnat_rx_id(skb);
+		entry.l2_bridge.iblk2.winfoi = 1;
+
+		entry.l2_bridge.winfo.bssid = skb_hnat_bss_id(skb);
+		entry.l2_bridge.winfo.wcid = skb_hnat_wc_id(skb);
+		entry.l2_bridge.winfo_pao.usr_info =
+			skb_hnat_usr_info(skb);
+		entry.l2_bridge.winfo_pao.tid = skb_hnat_tid(skb);
+		entry.l2_bridge.winfo_pao.is_fixedrate =
+			skb_hnat_is_fixedrate(skb);
+		entry.l2_bridge.winfo_pao.is_prior = skb_hnat_is_prior(skb);
+		entry.l2_bridge.winfo_pao.is_sp = skb_hnat_is_sp(skb);
+		entry.l2_bridge.winfo_pao.hf = skb_hnat_hf(skb);
+		entry.l2_bridge.winfo_pao.amsdu = skb_hnat_amsdu(skb);
 #endif
 	} else {
 		entry.ipv6_5t_route.iblk2.fqos = 0;
@@ -2630,7 +2707,7 @@
 			    (FROM_GE_LAN_GRP(skb) || FROM_GE_WAN(skb) || FROM_GE_VIRTUAL(skb))) {
 				entry.bfib1.vpm = 0;
 				entry.bfib1.vlan_layer = 1;
-				entry.ipv6_5t_route.etype = htons(HQOS_MAGIC_TAG);
+				entry.ipv6_5t_route.sp_tag = htons(HQOS_MAGIC_TAG);
 				entry.ipv6_5t_route.vlan1 = skb_hnat_entry(skb);
 				entry.ipv6_5t_route.iblk2.fqos = 1;
 			}
@@ -3094,7 +3171,7 @@
 	u16 sp_tag;
 
 	if (l2w)
-		entry->ipv4_dslite.etype = ETH_P_IP;
+		entry->ipv4_dslite.sp_tag = ETH_P_IP;
 	else {
 		if (IS_DSA_LAN(dev)) {
 			port_reg = of_get_property(dev->dev.of_node,
@@ -3107,9 +3184,9 @@
 
 			entry->bfib1.vlan_layer = 1;
 			entry->bfib1.vpm = 0;
-			entry->ipv6_6rd.etype = sp_tag;
+			entry->ipv6_6rd.sp_tag = sp_tag;
 		} else
-			entry->ipv6_6rd.etype = ETH_P_IPV6;
+			entry->ipv6_6rd.sp_tag = ETH_P_IPV6;
 	}
 
 	if (mtk_464xlat_fill_mac(entry, skb, dev, l2w))
diff --git a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
index d48175f..5f64105 100644
--- a/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
+++ b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
@@ -99,9 +99,11 @@
 			dsa_tag |= BIT(8);
 
 		if (IS_IPV4_GRP(entry))
-			entry->ipv4_hnapt.etype = dsa_tag;
-		else
-			entry->ipv6_5t_route.etype = dsa_tag;
+			entry->ipv4_hnapt.sp_tag = dsa_tag;
+		else if (IS_IPV6_GRP(entry))
+			entry->ipv6_5t_route.sp_tag = dsa_tag;
+		else if (IS_L2_BRIDGE(entry))
+			entry->l2_bridge.sp_tag = dsa_tag;
 
 		entry->bfib1.vpm = 0;
 	}