[][Add Dual-PPE Support for NF_HNAT]

[Description]
Add NETSYS V2 Dual-PPE Support for NF_HNAT in order
to achieve peak throughput requirement.

If want to enable dual-ppe mode, just modify dts node
as follows:

	&hnat {
		... ...
	+	mtketh-ppe-num = <2>;
		... ...
	};

[Release-log]
N/A

Change-Id: Ibf6316f69592ad76aac2e5e36dac907127e23103
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/4526946
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
index 3e87791..de869b6 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
@@ -37,14 +37,22 @@
 
 static void hnat_sma_build_entry(struct timer_list *t)
 {
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, SMA, SMA_FWD_CPU_BUILD_ENTRY);
+	int i;
+
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		cr_set_field(hnat_priv->ppe_base[i] + PPE_TB_CFG,
+			     SMA, SMA_FWD_CPU_BUILD_ENTRY);
 }
 
 void hnat_cache_ebl(int enable)
 {
-	cr_set_field(hnat_priv->ppe_base + PPE_CAH_CTRL, CAH_X_MODE, 1);
-	cr_set_field(hnat_priv->ppe_base + PPE_CAH_CTRL, CAH_X_MODE, 0);
-	cr_set_field(hnat_priv->ppe_base + PPE_CAH_CTRL, CAH_EN, enable);
+	int i;
+
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		cr_set_field(hnat_priv->ppe_base[i] + PPE_CAH_CTRL, CAH_X_MODE, 1);
+		cr_set_field(hnat_priv->ppe_base[i] + PPE_CAH_CTRL, CAH_X_MODE, 0);
+		cr_set_field(hnat_priv->ppe_base[i] + PPE_CAH_CTRL, CAH_EN, enable);
+	}
 }
 
 static void hnat_reset_timestamp(struct timer_list *t)
@@ -53,19 +61,19 @@
 	int hash_index;
 
 	hnat_cache_ebl(0);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, TCP_AGE, 0);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, UDP_AGE, 0);
+	cr_set_field(hnat_priv->ppe_base[0] + PPE_TB_CFG, TCP_AGE, 0);
+	cr_set_field(hnat_priv->ppe_base[0] + PPE_TB_CFG, UDP_AGE, 0);
 	writel(0, hnat_priv->fe_base + 0x0010);
 
 	for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) {
-		entry = hnat_priv->foe_table_cpu + hash_index;
+		entry = hnat_priv->foe_table_cpu[0] + hash_index;
 		if (entry->bfib1.state == BIND)
 			entry->bfib1.time_stamp =
 				readl(hnat_priv->fe_base + 0x0010) & (0xFFFF);
 	}
 
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, TCP_AGE, 1);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, UDP_AGE, 1);
+	cr_set_field(hnat_priv->ppe_base[0] + PPE_TB_CFG, TCP_AGE, 1);
+	cr_set_field(hnat_priv->ppe_base[0] + PPE_TB_CFG, UDP_AGE, 1);
 	hnat_cache_ebl(1);
 
 	mod_timer(&hnat_priv->hnat_reset_timestamp_timer, jiffies + 14400 * HZ);
@@ -139,7 +147,7 @@
 			     BITS_GDM_ALL_FRC_P_CPU_PDMA);
 }
 
-static int hnat_start(void)
+static int hnat_start(int ppe_id)
 {
 	u32 foe_table_sz;
 	u32 foe_mib_tb_sz;
@@ -148,123 +156,127 @@
 	/* mapp the FOE table */
 	for (etry_num_cfg = DEF_ETRY_NUM_CFG ; etry_num_cfg >= 0 ; etry_num_cfg--, hnat_priv->foe_etry_num /= 2) {
 		foe_table_sz = hnat_priv->foe_etry_num * sizeof(struct foe_entry);
-		hnat_priv->foe_table_cpu = dma_alloc_coherent(
-			hnat_priv->dev, foe_table_sz, &hnat_priv->foe_table_dev, GFP_KERNEL);
+		hnat_priv->foe_table_cpu[ppe_id] = dma_alloc_coherent(
+				hnat_priv->dev, foe_table_sz,
+				&hnat_priv->foe_table_dev[ppe_id], GFP_KERNEL);
 
-		if (hnat_priv->foe_table_cpu)
+		if (hnat_priv->foe_table_cpu[ppe_id])
 			break;
 	}
 
-	if (!hnat_priv->foe_table_cpu)
+	if (!hnat_priv->foe_table_cpu[ppe_id])
 		return -1;
-	dev_info(hnat_priv->dev, "FOE entry number = %d\n", hnat_priv->foe_etry_num);
+	dev_info(hnat_priv->dev, "PPE%d entry number = %d\n",
+		 ppe_id, hnat_priv->foe_etry_num);
 
-	writel(hnat_priv->foe_table_dev, hnat_priv->ppe_base + PPE_TB_BASE);
-	memset(hnat_priv->foe_table_cpu, 0, foe_table_sz);
+	writel(hnat_priv->foe_table_dev[ppe_id], hnat_priv->ppe_base[ppe_id] + PPE_TB_BASE);
+	memset(hnat_priv->foe_table_cpu[ppe_id], 0, foe_table_sz);
 
 	if (hnat_priv->data->version == MTK_HNAT_V1)
-		exclude_boundary_entry(hnat_priv->foe_table_cpu);
+		exclude_boundary_entry(hnat_priv->foe_table_cpu[ppe_id]);
 
 	if (hnat_priv->data->per_flow_accounting) {
 		foe_mib_tb_sz = hnat_priv->foe_etry_num * sizeof(struct mib_entry);
-		hnat_priv->foe_mib_cpu = dma_alloc_coherent(hnat_priv->dev, foe_mib_tb_sz,
-						       &hnat_priv->foe_mib_dev, GFP_KERNEL);
-		if (!hnat_priv->foe_mib_cpu)
+		hnat_priv->foe_mib_cpu[ppe_id] =
+			dma_alloc_coherent(hnat_priv->dev, foe_mib_tb_sz,
+					   &hnat_priv->foe_mib_dev[ppe_id], GFP_KERNEL);
+		if (!hnat_priv->foe_mib_cpu[ppe_id])
 			return -1;
-		writel(hnat_priv->foe_mib_dev, hnat_priv->ppe_base + PPE_MIB_TB_BASE);
-		memset(hnat_priv->foe_mib_cpu, 0, foe_mib_tb_sz);
+		writel(hnat_priv->foe_mib_dev[ppe_id],
+		       hnat_priv->ppe_base[ppe_id] + PPE_MIB_TB_BASE);
+		memset(hnat_priv->foe_mib_cpu[ppe_id], 0, foe_mib_tb_sz);
 
-		hnat_priv->acct =
+		hnat_priv->acct[ppe_id] =
 			kzalloc(hnat_priv->foe_etry_num * sizeof(struct hnat_accounting),
 				GFP_KERNEL);
-		if (!hnat_priv->acct)
+		if (!hnat_priv->acct[ppe_id])
 			return -1;
 	}
 	/* setup hashing */
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, TB_ETRY_NUM, etry_num_cfg);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, HASH_MODE, HASH_MODE_1);
-	writel(HASH_SEED_KEY, hnat_priv->ppe_base + PPE_HASH_SEED);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, XMODE, 0);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, TB_ENTRY_SIZE, ENTRY_80B);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, SMA, SMA_FWD_CPU_BUILD_ENTRY);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, TB_ETRY_NUM, etry_num_cfg);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, HASH_MODE, HASH_MODE_1);
+	writel(HASH_SEED_KEY, hnat_priv->ppe_base[ppe_id] + PPE_HASH_SEED);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, XMODE, 0);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, TB_ENTRY_SIZE, ENTRY_80B);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, SMA, SMA_FWD_CPU_BUILD_ENTRY);
 
 	/* set ip proto */
-	writel(0xFFFFFFFF, hnat_priv->ppe_base + PPE_IP_PROT_CHK);
+	writel(0xFFFFFFFF, hnat_priv->ppe_base[ppe_id] + PPE_IP_PROT_CHK);
 
 	/* setup caching */
 	hnat_cache_ebl(1);
 
 	/* enable FOE */
-	cr_set_bits(hnat_priv->ppe_base + PPE_FLOW_CFG,
+	cr_set_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG,
 		    BIT_UDP_IP4F_NAT_EN | BIT_IPV4_NAT_EN | BIT_IPV4_NAPT_EN |
 		    BIT_IPV4_NAT_FRAG_EN | BIT_IPV4_HASH_GREK |
 		    BIT_IPV4_DSL_EN | BIT_IPV6_6RD_EN |
 		    BIT_IPV6_3T_ROUTE_EN | BIT_IPV6_5T_ROUTE_EN);
 
 	if (hnat_priv->data->version == MTK_HNAT_V4)
-		cr_set_bits(hnat_priv->ppe_base + PPE_FLOW_CFG,
+		cr_set_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG,
 			    BIT_IPV4_MAPE_EN | BIT_IPV4_MAPT_EN);
 
 	/* setup FOE aging */
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, NTU_AGE, 1);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, UNBD_AGE, 1);
-	cr_set_field(hnat_priv->ppe_base + PPE_UNB_AGE, UNB_MNP, 1000);
-	cr_set_field(hnat_priv->ppe_base + PPE_UNB_AGE, UNB_DLTA, 3);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, TCP_AGE, 1);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, UDP_AGE, 1);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, FIN_AGE, 1);
-	cr_set_field(hnat_priv->ppe_base + PPE_BND_AGE_0, UDP_DLTA, 12);
-	cr_set_field(hnat_priv->ppe_base + PPE_BND_AGE_0, NTU_DLTA, 1);
-	cr_set_field(hnat_priv->ppe_base + PPE_BND_AGE_1, FIN_DLTA, 1);
-	cr_set_field(hnat_priv->ppe_base + PPE_BND_AGE_1, TCP_DLTA, 7);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, NTU_AGE, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, UNBD_AGE, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_UNB_AGE, UNB_MNP, 1000);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_UNB_AGE, UNB_DLTA, 3);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, TCP_AGE, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, UDP_AGE, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, FIN_AGE, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BND_AGE_0, UDP_DLTA, 12);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BND_AGE_0, NTU_DLTA, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BND_AGE_1, FIN_DLTA, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BND_AGE_1, TCP_DLTA, 7);
 
 	/* setup FOE ka */
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, SCAN_MODE, 2);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, KA_CFG, 3);
-	cr_set_field(hnat_priv->ppe_base + PPE_KA, KA_T, 1);
-	cr_set_field(hnat_priv->ppe_base + PPE_KA, TCP_KA, 1);
-	cr_set_field(hnat_priv->ppe_base + PPE_KA, UDP_KA, 1);
-	cr_set_field(hnat_priv->ppe_base + PPE_BIND_LMT_1, NTU_KA, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, SCAN_MODE, 2);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, KA_CFG, 3);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_KA, KA_T, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_KA, TCP_KA, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_KA, UDP_KA, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BIND_LMT_1, NTU_KA, 1);
 
 	/* setup FOE rate limit */
-	cr_set_field(hnat_priv->ppe_base + PPE_BIND_LMT_0, QURT_LMT, 16383);
-	cr_set_field(hnat_priv->ppe_base + PPE_BIND_LMT_0, HALF_LMT, 16383);
-	cr_set_field(hnat_priv->ppe_base + PPE_BIND_LMT_1, FULL_LMT, 16383);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BIND_LMT_0, QURT_LMT, 16383);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BIND_LMT_0, HALF_LMT, 16383);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BIND_LMT_1, FULL_LMT, 16383);
 	/* setup binding threshold as 30 packets per second */
-	cr_set_field(hnat_priv->ppe_base + PPE_BNDR, BIND_RATE, 0x1E);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_BNDR, BIND_RATE, 0x1E);
 
 	/* setup FOE cf gen */
-	cr_set_field(hnat_priv->ppe_base + PPE_GLO_CFG, PPE_EN, 1);
-	writel(0, hnat_priv->ppe_base + PPE_DFT_CPORT); /* pdma */
-	/* writel(0x55555555, hnat_priv->ppe_base + PPE_DFT_CPORT); */ /* qdma */
-	cr_set_field(hnat_priv->ppe_base + PPE_GLO_CFG, TTL0_DRP, 0);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_GLO_CFG, PPE_EN, 1);
+	writel(0, hnat_priv->ppe_base[ppe_id] + PPE_DFT_CPORT); /* pdma */
+	/* writel(0x55555555, hnat_priv->ppe_base[ppe_id] + PPE_DFT_CPORT); */ /* qdma */
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_GLO_CFG, TTL0_DRP, 0);
 
 	if (hnat_priv->data->version == MTK_HNAT_V4) {
-		writel(0xcb777, hnat_priv->ppe_base + PPE_DFT_CPORT1);
-		writel(0x7f, hnat_priv->ppe_base + PPE_SBW_CTRL);
+		writel(0xcb777, hnat_priv->ppe_base[ppe_id] + PPE_DFT_CPORT1);
+		writel(0x7f, hnat_priv->ppe_base[ppe_id] + PPE_SBW_CTRL);
 	}
 
 	/*enable ppe mib counter*/
 	if (hnat_priv->data->per_flow_accounting) {
-		cr_set_field(hnat_priv->ppe_base + PPE_MIB_CFG, MIB_EN, 1);
-		cr_set_field(hnat_priv->ppe_base + PPE_MIB_CFG, MIB_READ_CLEAR, 1);
-		cr_set_field(hnat_priv->ppe_base + PPE_MIB_CAH_CTRL, MIB_CAH_EN, 1);
+		cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MIB_CFG, MIB_EN, 1);
+		cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MIB_CFG, MIB_READ_CLEAR, 1);
+		cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MIB_CAH_CTRL, MIB_CAH_EN, 1);
 	}
 
 	hnat_priv->g_ppdev = dev_get_by_name(&init_net, hnat_priv->ppd);
 
-	dev_info(hnat_priv->dev, "hwnat start\n");
+	dev_info(hnat_priv->dev, "PPE%d hwnat start\n", ppe_id);
 
 	return 0;
 }
 
-static int ppe_busy_wait(void)
+static int ppe_busy_wait(int ppe_id)
 {
 	unsigned long t_start = jiffies;
 	u32 r = 0;
 
 	while (1) {
-		r = readl((hnat_priv->ppe_base + 0x0));
+		r = readl((hnat_priv->ppe_base[ppe_id] + 0x0));
 		if (!(r & BIT(31)))
 			return 0;
 		if (time_after(jiffies, t_start + HZ))
@@ -277,7 +289,7 @@
 	return -1;
 }
 
-static void hnat_stop(void)
+static void hnat_stop(int ppe_id)
 {
 	u32 foe_table_sz;
 	u32 foe_mib_tb_sz;
@@ -290,9 +302,9 @@
 
 	dev_info(hnat_priv->dev, "hwnat stop\n");
 
-	if (hnat_priv->foe_table_cpu) {
-		entry = hnat_priv->foe_table_cpu;
-		end = hnat_priv->foe_table_cpu + hnat_priv->foe_etry_num;
+	if (hnat_priv->foe_table_cpu[ppe_id]) {
+		entry = hnat_priv->foe_table_cpu[ppe_id];
+		end = hnat_priv->foe_table_cpu[ppe_id] + hnat_priv->foe_etry_num;
 		while (entry < end) {
 			entry->bfib1.state = INVALID;
 			entry++;
@@ -302,31 +314,31 @@
 	hnat_cache_ebl(0);
 
 	/* flush cache has to be ahead of hnat disable --*/
-	cr_set_field(hnat_priv->ppe_base + PPE_GLO_CFG, PPE_EN, 0);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_GLO_CFG, PPE_EN, 0);
 
 	/* disable scan mode and keep-alive */
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, SCAN_MODE, 0);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, KA_CFG, 0);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, SCAN_MODE, 0);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, KA_CFG, 0);
 
-	ppe_busy_wait();
+	ppe_busy_wait(ppe_id);
 
 	/* disable FOE */
-	cr_clr_bits(hnat_priv->ppe_base + PPE_FLOW_CFG,
+	cr_clr_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG,
 		    BIT_IPV4_NAPT_EN | BIT_IPV4_NAT_EN | BIT_IPV4_NAT_FRAG_EN |
 		    BIT_IPV6_HASH_GREK | BIT_IPV4_DSL_EN |
 		    BIT_IPV6_6RD_EN | BIT_IPV6_3T_ROUTE_EN |
 		    BIT_IPV6_5T_ROUTE_EN | BIT_FUC_FOE | BIT_FMC_FOE);
 
 	if (hnat_priv->data->version == MTK_HNAT_V4)
-		cr_clr_bits(hnat_priv->ppe_base + PPE_FLOW_CFG,
+		cr_clr_bits(hnat_priv->ppe_base[ppe_id] + PPE_FLOW_CFG,
 			    BIT_IPV4_MAPE_EN | BIT_IPV4_MAPT_EN);
 
 	/* disable FOE aging */
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, NTU_AGE, 0);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, UNBD_AGE, 0);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, TCP_AGE, 0);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, UDP_AGE, 0);
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, FIN_AGE, 0);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, NTU_AGE, 0);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, UNBD_AGE, 0);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, TCP_AGE, 0);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, UDP_AGE, 0);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_TB_CFG, FIN_AGE, 0);
 
 	r1 = readl(hnat_priv->fe_base + 0x100);
 	r2 = readl(hnat_priv->fe_base + 0x10c);
@@ -341,18 +353,20 @@
 
 	/* free the FOE table */
 	foe_table_sz = hnat_priv->foe_etry_num * sizeof(struct foe_entry);
-	if (hnat_priv->foe_table_cpu)
-		dma_free_coherent(hnat_priv->dev, foe_table_sz, hnat_priv->foe_table_cpu,
-				  hnat_priv->foe_table_dev);
-	writel(0, hnat_priv->ppe_base + PPE_TB_BASE);
+	if (hnat_priv->foe_table_cpu[ppe_id])
+		dma_free_coherent(hnat_priv->dev, foe_table_sz,
+				  hnat_priv->foe_table_cpu[ppe_id],
+				  hnat_priv->foe_table_dev[ppe_id]);
+	writel(0, hnat_priv->ppe_base[ppe_id] + PPE_TB_BASE);
 
 	if (hnat_priv->data->per_flow_accounting) {
 		foe_mib_tb_sz = hnat_priv->foe_etry_num * sizeof(struct mib_entry);
-		if (hnat_priv->foe_mib_cpu)
+		if (hnat_priv->foe_mib_cpu[ppe_id])
 			dma_free_coherent(hnat_priv->dev, foe_mib_tb_sz,
-					  hnat_priv->foe_mib_cpu, hnat_priv->foe_mib_dev);
-		writel(0, hnat_priv->ppe_base + PPE_MIB_TB_BASE);
-		kfree(hnat_priv->acct);
+					  hnat_priv->foe_mib_cpu[ppe_id],
+					  hnat_priv->foe_mib_dev[ppe_id]);
+		writel(0, hnat_priv->ppe_base[ppe_id] + PPE_MIB_TB_BASE);
+		kfree(hnat_priv->acct[ppe_id]);
 	}
 }
 
@@ -404,20 +418,24 @@
 
 int hnat_disable_hook(void)
 {
-	int hash_index;
+	int i, hash_index;
 	struct foe_entry *entry;
 
 	ra_sw_nat_hook_tx = NULL;
 	ra_sw_nat_hook_rx = NULL;
 	hnat_unregister_nf_hooks();
 
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, SMA, SMA_ONLY_FWD_CPU);
-	for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) {
-		entry = hnat_priv->foe_table_cpu + hash_index;
-		if (entry->bfib1.state == BIND) {
-			entry->ipv4_hnapt.udib1.state = INVALID;
-			entry->ipv4_hnapt.udib1.time_stamp =
-				readl((hnat_priv->fe_base + 0x0010)) & 0xFF;
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		cr_set_field(hnat_priv->ppe_base[i] + PPE_TB_CFG,
+			     SMA, SMA_ONLY_FWD_CPU);
+
+		for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) {
+			entry = hnat_priv->foe_table_cpu[i] + hash_index;
+			if (entry->bfib1.state == BIND) {
+				entry->ipv4_hnapt.udib1.state = INVALID;
+				entry->ipv4_hnapt.udib1.time_stamp =
+					readl((hnat_priv->fe_base + 0x0010)) & 0xFF;
+			}
 		}
 	}
 
@@ -502,6 +520,15 @@
 		dev_info(&pdev->dev, "wan dsa port = %d\n", hnat_priv->wan_dsa_port);
 	}
 
+	err = of_property_read_u32_index(np, "mtketh-ppe-num", 0, &val);
+
+	if (err < 0)
+		hnat_priv->ppe_num = 1;
+	else
+		hnat_priv->ppe_num = val;
+
+	dev_info(&pdev->dev, "ppe num = %d\n", hnat_priv->ppe_num);
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res)
 		return -ENOENT;
@@ -511,8 +538,14 @@
 	if (!hnat_priv->fe_base)
 		return -EADDRNOTAVAIL;
 
-	hnat_priv->ppe_base = (hnat_priv->data->version == MTK_HNAT_V4) ?
-		hnat_priv->fe_base + 0x2600 : hnat_priv->fe_base + 0xe00;
+#if defined(CONFIG_MEDIATEK_NETSYS_V2)
+	hnat_priv->ppe_base[0] = hnat_priv->fe_base + 0x2200;
+
+	if (CFG_PPE_NUM > 1)
+		hnat_priv->ppe_base[1] = hnat_priv->fe_base + 0x2600;
+#else
+	hnat_priv->ppe_base[0] = hnat_priv->fe_base + 0xe00;
+#endif
 
 	err = hnat_init_debugfs(hnat_priv);
 	if (err)
@@ -538,9 +571,11 @@
 	hnat_priv->lvid = 1;
 	hnat_priv->wvid = 2;
 
-	err = hnat_start();
-	if (err)
-		goto err_out;
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		err = hnat_start(i);
+		if (err)
+			goto err_out;
+	}
 
 	if (hnat_priv->data->whnat) {
 		err = whnat_adjust_nf_hooks();
@@ -554,8 +589,12 @@
 
 	register_netdevice_notifier(&nf_hnat_netdevice_nb);
 	register_netevent_notifier(&nf_hnat_netevent_nb);
-	if (hnat_priv->data->mcast)
-		hnat_mcast_enable();
+
+	if (hnat_priv->data->mcast) {
+		for (i = 0; i < CFG_PPE_NUM; i++)
+			hnat_mcast_enable(i);
+	}
+
 	timer_setup(&hnat_priv->hnat_sma_build_entry_timer, hnat_sma_build_entry, 0);
 	if (hnat_priv->data->version == MTK_HNAT_V3) {
 		timer_setup(&hnat_priv->hnat_reset_timestamp_timer, hnat_reset_timestamp, 0);
@@ -571,7 +610,8 @@
 	return 0;
 
 err_out:
-	hnat_stop();
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		hnat_stop(i);
 err_out1:
 	hnat_deinit_debugfs(hnat_priv);
 	for (i = 0; i < MAX_EXT_DEVS && hnat_priv->ext_if[i]; i++) {
@@ -584,6 +624,8 @@
 
 static int hnat_remove(struct platform_device *pdev)
 {
+	int i;
+
 	unregister_netdevice_notifier(&nf_hnat_netdevice_nb);
 	unregister_netevent_notifier(&nf_hnat_netevent_nb);
 	hnat_disable_hook();
@@ -591,7 +633,9 @@
 	if (hnat_priv->data->mcast)
 		hnat_mcast_disable();
 
-	hnat_stop();
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		hnat_stop(i);
+
 	hnat_deinit_debugfs(hnat_priv);
 	hnat_release_netdev();
 	del_timer_sync(&hnat_priv->hnat_sma_build_entry_timer);
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
index 336b4ad..734b6bd 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
@@ -587,6 +587,13 @@
 #define MAX_EXT_DEVS		(0x3fU)
 #define MAX_IF_NUM		64
 
+#if defined(CONFIG_MEDIATEK_NETSYS_V2)
+#define MAX_PPE_NUM		2
+#else
+#define MAX_PPE_NUM		1
+#endif
+#define CFG_PPE_NUM		(hnat_priv->ppe_num)
+
 struct mib_entry {
 	u32 byt_cnt_l;
 	u16 byt_cnt_h;
@@ -619,17 +626,17 @@
 struct mtk_hnat {
 	struct device *dev;
 	void __iomem *fe_base;
-	void __iomem *ppe_base;
-	struct foe_entry *foe_table_cpu;
-	dma_addr_t foe_table_dev;
+	void __iomem *ppe_base[MAX_PPE_NUM];
+	struct foe_entry *foe_table_cpu[MAX_PPE_NUM];
+	dma_addr_t foe_table_dev[MAX_PPE_NUM];
 	u8 enable;
 	u8 enable1;
 	struct dentry *root;
-	struct debugfs_regset32 *regset;
+	struct debugfs_regset32 *regset[MAX_PPE_NUM];
 
-	struct mib_entry *foe_mib_cpu;
-	dma_addr_t foe_mib_dev;
-	struct hnat_accounting *acct;
+	struct mib_entry *foe_mib_cpu[MAX_PPE_NUM];
+	dma_addr_t foe_mib_dev[MAX_PPE_NUM];
+	struct hnat_accounting *acct[MAX_PPE_NUM];
 	const struct mtk_hnat_data *data;
 
 	/*devices we plays for*/
@@ -641,6 +648,7 @@
 
 	struct reset_control *rstc;
 
+	u8 ppe_num;
 	u8 gmac_num;
 	u8 wan_dsa_port;
 	struct ppe_mcast_table *pmcast;
@@ -725,10 +733,10 @@
 #define BIT_IPV4_MAPT_EN BIT(22)
 
 /*GDMA_FWD_CFG value*/
-#define BITS_GDM_UFRC_P_PPE (NR_PPE_PORT << 12)
-#define BITS_GDM_BFRC_P_PPE (NR_PPE_PORT << 8)
-#define BITS_GDM_MFRC_P_PPE (NR_PPE_PORT << 4)
-#define BITS_GDM_OFRC_P_PPE (NR_PPE_PORT << 0)
+#define BITS_GDM_UFRC_P_PPE (NR_PPE0_PORT << 12)
+#define BITS_GDM_BFRC_P_PPE (NR_PPE0_PORT << 8)
+#define BITS_GDM_MFRC_P_PPE (NR_PPE0_PORT << 4)
+#define BITS_GDM_OFRC_P_PPE (NR_PPE0_PORT << 0)
 #define BITS_GDM_ALL_FRC_P_PPE                                              \
 	(BITS_GDM_UFRC_P_PPE | BITS_GDM_BFRC_P_PPE | BITS_GDM_MFRC_P_PPE |  \
 	 BITS_GDM_OFRC_P_PPE)
@@ -794,8 +802,14 @@
 #define NR_PDMA_PORT 0
 #define NR_GMAC1_PORT 1
 #define NR_GMAC2_PORT 2
+#if defined(CONFIG_MEDIATEK_NETSYS_V2)
+#define NR_WHNAT_WDMA_PORT EINVAL
+#define NR_PPE0_PORT 3
+#define NR_PPE1_PORT 4
+#else
 #define NR_WHNAT_WDMA_PORT 3
-#define NR_PPE_PORT 4
+#define NR_PPE0_PORT 4
+#endif
 #define NR_QDMA_PORT 5
 #define NR_DISCARD 7
 #define NR_WDMA0_PORT 8
@@ -917,7 +931,8 @@
 int hnat_disable_hook(void);
 void hnat_cache_ebl(int enable);
 void set_gmac_ppe_fwd(int gmac_no, int enable);
-int entry_delete(int index);
+int entry_detail(int ppe_id, int index);
+int entry_delete(int ppe_id, int index);
 
 static inline u16 foe_timestamp(struct mtk_hnat *h)
 {
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
index 4ae9128..d8c76ba 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
@@ -96,9 +96,10 @@
 {
 	struct foe_entry *entry;
 
-	entry = &hnat_priv->foe_table_cpu[skb_hnat_entry(skb)];
+	entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)];
 	pr_info("\nRx===<FOE_Entry=%d>=====\n", skb_hnat_entry(skb));
 	pr_info("RcvIF=%s\n", skb->dev->name);
+	pr_info("PPE_ID=%d\n", skb_hnat_ppe(skb));
 	pr_info("FOE_Entry=%d\n", skb_hnat_entry(skb));
 	pr_info("CPU Reason=%s", show_cpu_reason(skb));
 	pr_info("ALG=%d\n", skb_hnat_alg(skb));
@@ -296,8 +297,10 @@
 		debug_level);
 	pr_info("              1       0~3      Change tracking state\n");
 	pr_info("                               (0:invalid; 1:unbind; 2:bind; 3:fin)\n");
-	pr_info("              2   <entry_idx>  Show specific foe entry info. of assigned <entry_idx>\n");
-	pr_info("              3   <entry_idx>  Delete specific foe entry of assigned <entry_idx>\n");
+	pr_info("              2   <entry_idx>  Show PPE0 specific foe entry info. of assigned <entry_idx>\n");
+	pr_info("              3   <entry_idx>  Delete PPE0 specific foe entry of assigned <entry_idx>\n");
+	pr_info("              4   <entry_idx>  Show PPE1 specific foe entry info. of assigned <entry_idx>\n");
+	pr_info("              5   <entry_idx>  Delete PPE1 specific foe entry of assigned <entry_idx>\n");
 
 	return 0;
 }
@@ -313,7 +316,17 @@
 	return 0;
 }
 
-int entry_detail(int index)
+int wrapped_ppe0_entry_detail(int index) {
+	entry_detail(0, index);
+	return 0;
+}
+
+int wrapped_ppe1_entry_detail(int index) {
+	entry_detail(1, index);
+	return 0;
+}
+
+int entry_detail(int ppe_id, int index)
 {
 	struct foe_entry *entry;
 	struct mtk_hnat *h = hnat_priv;
@@ -324,14 +337,17 @@
 	unsigned char h_source[ETH_ALEN];
 	__be32 saddr, daddr, nsaddr, ndaddr;
 
-	entry = h->foe_table_cpu + index;
+	if (ppe_id >= CFG_PPE_NUM)
+		return -EINVAL;
+
+	entry = h->foe_table_cpu[ppe_id] + index;
 	saddr = htonl(entry->ipv4_hnapt.sip);
 	daddr = htonl(entry->ipv4_hnapt.dip);
 	nsaddr = htonl(entry->ipv4_hnapt.new_sip);
 	ndaddr = htonl(entry->ipv4_hnapt.new_dip);
 	p = (uint32_t *)entry;
-	pr_info("==========<Flow Table Entry=%d (%p)>===============\n", index,
-		entry);
+	pr_info("==========<PPE_ID=%d, Flow Table Entry=%d (%p)>===============\n",
+		ppe_id, index, entry);
 	if (debug_level >= 2) {
 		print_cnt = 20;
 		for (i = 0; i < print_cnt; i++)
@@ -488,18 +504,31 @@
 	return 0;
 }
 
+int wrapped_ppe0_entry_delete(int index) {
+	entry_delete(0, index);
+	return 0;
+}
+
-int entry_delete(int index)
+int wrapped_ppe1_entry_delete(int index) {
+	entry_delete(1, index);
+	return 0;
+}
+
+int entry_delete(int ppe_id, int index)
 {
 	struct foe_entry *entry;
 	struct mtk_hnat *h = hnat_priv;
 
-	entry = h->foe_table_cpu + index;
+	if (ppe_id >= CFG_PPE_NUM)
+		return -EINVAL;
+
+	entry = h->foe_table_cpu[ppe_id] + index;
 	memset(entry, 0, sizeof(struct foe_entry));
 
 	/* clear HWNAT cache */
 	hnat_cache_ebl(1);
 
-	pr_info("delete entry idx = %d\n", index);
+	pr_info("delete ppe id = %d, entry idx = %d\n", ppe_id, index);
 
 	return 0;
 }
@@ -526,62 +555,92 @@
 
 int binding_threshold(int threshold)
 {
+	int i;
+
 	pr_info("Binding Threshold =%d\n", threshold);
-	writel(threshold, hnat_priv->ppe_base + PPE_BNDR);
+
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		writel(threshold, hnat_priv->ppe_base[i] + PPE_BNDR);
+
 	return 0;
 }
 
 int tcp_bind_lifetime(int tcp_life)
 {
+	int i;
+
 	pr_info("tcp_life = %d\n", tcp_life);
+
 	/* set Delta time for aging out an bind TCP FOE entry */
-	cr_set_field(hnat_priv->ppe_base + PPE_BND_AGE_1, TCP_DLTA, tcp_life);
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		cr_set_field(hnat_priv->ppe_base[i] + PPE_BND_AGE_1,
+			     TCP_DLTA, tcp_life);
 
 	return 0;
 }
 
 int fin_bind_lifetime(int fin_life)
 {
+	int i;
+
 	pr_info("fin_life = %d\n", fin_life);
+
 	/* set Delta time for aging out an bind TCP FIN FOE entry */
-	cr_set_field(hnat_priv->ppe_base + PPE_BND_AGE_1, FIN_DLTA, fin_life);
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		cr_set_field(hnat_priv->ppe_base[i] + PPE_BND_AGE_1,
+			     FIN_DLTA, fin_life);
 
 	return 0;
 }
 
 int udp_bind_lifetime(int udp_life)
 {
+	int i;
+
 	pr_info("udp_life = %d\n", udp_life);
+
 	/* set Delta time for aging out an bind UDP FOE entry */
-	cr_set_field(hnat_priv->ppe_base + PPE_BND_AGE_0, UDP_DLTA, udp_life);
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		cr_set_field(hnat_priv->ppe_base[i] + PPE_BND_AGE_0,
+			     UDP_DLTA, udp_life);
 
 	return 0;
 }
 
 int tcp_keep_alive(int tcp_interval)
 {
+	int i;
+
 	if (tcp_interval > 255) {
 		tcp_interval = 255;
 		pr_info("TCP keep alive max interval = 255\n");
 	} else {
 		pr_info("tcp_interval = %d\n", tcp_interval);
 	}
+
 	/* Keep alive time for bind FOE TCP entry */
-	cr_set_field(hnat_priv->ppe_base + PPE_KA, TCP_KA, tcp_interval);
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		cr_set_field(hnat_priv->ppe_base[i] + PPE_KA,
+			     TCP_KA, tcp_interval);
 
 	return 0;
 }
 
 int udp_keep_alive(int udp_interval)
 {
+	int i;
+
 	if (udp_interval > 255) {
 		udp_interval = 255;
 		pr_info("TCP/UDP keep alive max interval = 255\n");
 	} else {
 		pr_info("udp_interval = %d\n", udp_interval);
 	}
+
 	/* Keep alive timer for bind FOE UDP entry */
-	cr_set_field(hnat_priv->ppe_base + PPE_KA, UDP_KA, udp_interval);
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		cr_set_field(hnat_priv->ppe_base[i] + PPE_KA,
+			     UDP_KA, udp_interval);
 
 	return 0;
 }
@@ -594,8 +653,10 @@
 static const debugfs_write_func entry_set_func[] = {
 	[0] = entry_set_usage,
 	[1] = entry_set_state,
-	[2] = entry_detail,
-	[3] = entry_delete,
+	[2] = wrapped_ppe0_entry_detail,
+	[3] = wrapped_ppe0_entry_delete,
+	[4] = wrapped_ppe1_entry_detail,
+	[5] = wrapped_ppe1_entry_delete,
 };
 
 static const debugfs_write_func cr_set_func[] = {
@@ -605,7 +666,8 @@
 	[6] = udp_keep_alive,
 };
 
-static struct hnat_accounting *hnat_get_count(struct mtk_hnat *h, u32 index)
+static struct hnat_accounting *hnat_get_count(struct mtk_hnat *h,
+					      int ppe_id, u32 index)
 {
 	struct hnat_accounting *acount;
 	u32 val, cnt_r0, cnt_r1, cnt_r2;
@@ -614,17 +676,17 @@
 	if (!hnat_priv->data->per_flow_accounting)
 		return NULL;
 
-	writel(index | (1 << 16), h->ppe_base + PPE_MIB_SER_CR);
-	ret = readx_poll_timeout_atomic(readl, h->ppe_base + PPE_MIB_SER_CR, val,
-					!(val & BIT_MIB_BUSY), 20, 10000);
+	writel(index | (1 << 16), h->ppe_base[ppe_id] + PPE_MIB_SER_CR);
+	ret = readx_poll_timeout_atomic(readl, h->ppe_base[ppe_id] + PPE_MIB_SER_CR,
+					val, !(val & BIT_MIB_BUSY), 20, 10000);
 	if (ret < 0) {
 		pr_notice("mib busy,please check later\n");
 		return NULL;
 	}
-	cnt_r0 = readl(h->ppe_base + PPE_MIB_SER_R0);
-	cnt_r1 = readl(h->ppe_base + PPE_MIB_SER_R1);
-	cnt_r2 = readl(h->ppe_base + PPE_MIB_SER_R2);
-	acount = &h->acct[index];
+	cnt_r0 = readl(h->ppe_base[ppe_id] + PPE_MIB_SER_R0);
+	cnt_r1 = readl(h->ppe_base[ppe_id] + PPE_MIB_SER_R1);
+	cnt_r2 = readl(h->ppe_base[ppe_id] + PPE_MIB_SER_R2);
+	acount = &h->acct[ppe_id][index];
 	acount->bytes += cnt_r0 + ((u64)(cnt_r1 & 0xffff) << 32);
 	acount->packets +=
 		((cnt_r1 & 0xffff0000) >> 16) + ((cnt_r2 & 0xffffff) << 16);
@@ -635,7 +697,7 @@
 #define PRINT_COUNT(m, acount) {if (acount) \
 		seq_printf(m, "bytes=%llu|packets=%llu|", \
 			   acount->bytes, acount->packets); }
-static int hnat_debug_show(struct seq_file *m, void *private)
+static int __hnat_debug_show(struct seq_file *m, void *private, int ppe_id)
 {
 	struct mtk_hnat *h = hnat_priv;
 	struct foe_entry *entry, *end;
@@ -644,15 +706,15 @@
 	struct hnat_accounting *acount;
 	u32 entry_index = 0;
 
-	entry = h->foe_table_cpu;
-	end = h->foe_table_cpu + hnat_priv->foe_etry_num;
+	entry = h->foe_table_cpu[ppe_id];
+	end = h->foe_table_cpu[ppe_id] + hnat_priv->foe_etry_num;
 	while (entry < end) {
 		if (!entry->bfib1.state) {
 			entry++;
 			entry_index++;
 			continue;
 		}
-		acount = hnat_get_count(h, entry_index);
+		acount = hnat_get_count(h, ppe_id, entry_index);
 		if (IS_IPV4_HNAPT(entry)) {
 			__be32 saddr = htonl(entry->ipv4_hnapt.sip);
 			__be32 daddr = htonl(entry->ipv4_hnapt.dip);
@@ -667,8 +729,9 @@
 				swab16(entry->ipv4_hnapt.dmac_lo);
 			PRINT_COUNT(m, acount);
 			seq_printf(m,
-				   "addr=0x%p|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",
-				   entry, ei(entry, end), es(entry), pt(entry), &saddr,
+				   "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",
+				   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,
@@ -692,8 +755,9 @@
 				swab16(entry->ipv4_hnapt.dmac_lo);
 			PRINT_COUNT(m, acount);
 			seq_printf(m,
-				   "addr=0x%p|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",
-				   entry, ei(entry, end), es(entry), pt(entry), &saddr,
+				   "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",
+				   entry, ppe_id, ei(entry, end),
+				   es(entry), pt(entry), &saddr,
 				   &daddr, &nsaddr, &ndaddr, h_source, h_dest,
 				   ntohs(entry->ipv4_hnapt.etype),
 				   entry->ipv4_hnapt.info_blk1,
@@ -719,8 +783,8 @@
 				swab16(entry->ipv6_5t_route.dmac_lo);
 			PRINT_COUNT(m, acount);
 			seq_printf(m,
-				   "addr=0x%p|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",
-				   entry, ei(entry, end), es(entry), pt(entry), ipv6_sip0,
+				   "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",
+				   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,
@@ -747,8 +811,9 @@
 				swab16(entry->ipv6_5t_route.dmac_lo);
 			PRINT_COUNT(m, acount);
 			seq_printf(m,
-				   "addr=0x%p|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",
-				   entry, ei(entry, end), es(entry), pt(entry), ipv6_sip0,
+				   "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",
+				   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),
@@ -775,8 +840,9 @@
 				swab16(entry->ipv6_5t_route.dmac_lo);
 			PRINT_COUNT(m, acount);
 			seq_printf(m,
-				   "addr=0x%p|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",
-				   entry, ei(entry, end), es(entry), pt(entry), ipv6_sip0,
+				   "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",
+				   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,
@@ -805,8 +871,9 @@
 				swab16(entry->ipv4_dslite.dmac_lo);
 			PRINT_COUNT(m, acount);
 			seq_printf(m,
-				   "addr=0x%p|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",
-				   entry, ei(entry, end), es(entry), pt(entry), &saddr,
+				   "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",
+				   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,
@@ -836,8 +903,9 @@
 				swab16(entry->ipv4_dslite.dmac_lo);
 			PRINT_COUNT(m, acount);
 			seq_printf(m,
-				   "addr=0x%p|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",
-				   entry, ei(entry, end), es(entry), pt(entry),
+				   "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",
+				   entry, ppe_id, ei(entry, end),
+				   es(entry), pt(entry),
 				   &saddr, entry->ipv4_dslite.sport,
 				   &daddr, entry->ipv4_dslite.dport,
 				   &nsaddr, entry->ipv4_dslite.new_sport,
@@ -850,7 +918,7 @@
 				   entry->ipv6_5t_route.info_blk2);
 #endif
 		} else
-			seq_printf(m, "addr=0x%p|index=%d state=%s\n", entry, ei(entry, end),
+			seq_printf(m, "addr=0x%p|ppe=%d|index=%d state=%s\n", entry, ppe_id, ei(entry, end),
 				   es(entry));
 		entry++;
 		entry_index++;
@@ -859,6 +927,16 @@
 	return 0;
 }
 
+static int hnat_debug_show(struct seq_file *m, void *private)
+{
+	int i;
+
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		__hnat_debug_show(m, private, i);
+
+	return 0;
+}
+
 static int hnat_debug_open(struct inode *inode, struct file *file)
 {
 	return single_open(file, hnat_debug_show, file->private_data);
@@ -1103,7 +1181,7 @@
 	}
 }
 
-int hnat_entry_read(struct seq_file *m, void *private)
+int __hnat_entry_read(struct seq_file *m, void *private, int ppe_id)
 {
 	struct mtk_hnat *h = hnat_priv;
 	struct foe_entry *entry, *end;
@@ -1112,8 +1190,11 @@
 
 	hash_index = 0;
 	cnt = 0;
-	entry = h->foe_table_cpu;
-	end = h->foe_table_cpu + hnat_priv->foe_etry_num;
+	entry = h->foe_table_cpu[ppe_id];
+	end = h->foe_table_cpu[ppe_id] + hnat_priv->foe_etry_num;
+
+	seq_printf(m, "============================\n");
+	seq_printf(m, "PPE_ID = %d\n", ppe_id);
 
 	while (entry < end) {
 		if (entry->bfib1.state == dbg_entry_state) {
@@ -1134,6 +1215,16 @@
 	return 0;
 }
 
+int hnat_entry_read(struct seq_file *m, void *private)
+{
+	int i;
+
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		__hnat_entry_read(m, private, i);
+
+	return 0;
+}
+
 ssize_t hnat_entry_write(struct file *file, const char __user *buffer,
 			 size_t count, loff_t *data)
 {
@@ -1168,6 +1259,8 @@
 	case 1:
 	case 2:
 	case 3:
+	case 4:
+	case 5:
 		p_token = strsep(&p_buf, p_delimiter);
 		if (!p_token)
 			arg1 = 0;
@@ -1199,7 +1292,7 @@
 	.release = single_release,
 };
 
-int hnat_setting_read(struct seq_file *m, void *private)
+int __hnat_setting_read(struct seq_file *m, void *private, int ppe_id)
 {
 	struct mtk_hnat *h = hnat_priv;
 	int i;
@@ -1208,14 +1301,26 @@
 	cr_max = 319 * 4;
 	for (i = 0; i < cr_max; i = i + 0x10) {
 		pr_info("0x%p : 0x%08x 0x%08x 0x%08x 0x%08x\n",
-			(void *)h->foe_table_dev + i, readl(h->ppe_base + i),
-			readl(h->ppe_base + i + 4), readl(h->ppe_base + i + 8),
-			readl(h->ppe_base + i + 0xc));
+			(void *)h->foe_table_dev[ppe_id] + i,
+			readl(h->ppe_base[ppe_id] + i),
+			readl(h->ppe_base[ppe_id] + i + 4),
+			readl(h->ppe_base[ppe_id] + i + 8),
+			readl(h->ppe_base[ppe_id] + i + 0xc));
 	}
 
 	return 0;
 }
 
+int hnat_setting_read(struct seq_file *m, void *private)
+{
+	int i;
+
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		__hnat_setting_read(m, private, i);
+
+	return 0;
+}
+
 static int hnat_setting_open(struct inode *inode, struct file *file)
 {
 	return single_open(file, hnat_setting_read, file->private_data);
@@ -1284,7 +1389,7 @@
 	.release = single_release,
 };
 
-int mcast_table_dump(struct seq_file *m, void *private)
+int __mcast_table_dump(struct seq_file *m, void *private, int ppe_id)
 {
 	struct mtk_hnat *h = hnat_priv;
 	struct ppe_mcast_h mcast_h;
@@ -1296,12 +1401,14 @@
 		return 0;
 
 	max = h->pmcast->max_entry;
+	pr_info("============================\n");
+	pr_info("PPE_ID = %d\n", ppe_id);
 	pr_info("MAC | VID | PortMask | QosPortMask\n");
 	for (i = 0; i < max; i++) {
 		if (i < 0x10) {
-			reg = h->ppe_base + PPE_MCAST_H_0 + i * 8;
+			reg = h->ppe_base[ppe_id] + PPE_MCAST_H_0 + i * 8;
 			mcast_h.u.value = readl(reg);
-			reg = h->ppe_base + PPE_MCAST_L_0 + i * 8;
+			reg = h->ppe_base[ppe_id] + PPE_MCAST_L_0 + i * 8;
 			mcast_l.addr = readl(reg);
 		} else {
 			reg = h->fe_base + PPE_MCAST_H_10 + (i - 0x10) * 8;
@@ -1328,6 +1435,16 @@
 	return 0;
 }
 
+int mcast_table_dump(struct seq_file *m, void *private)
+{
+	int i;
+
+	for (i = 0; i < CFG_PPE_NUM; i++)
+		__mcast_table_dump(m, private, i);
+
+	return 0;
+}
+
 static int mcast_table_open(struct inode *inode, struct file *file)
 {
 	return single_open(file, mcast_table_dump, file->private_data);
@@ -1799,14 +1916,17 @@
 	.release = single_release,
 };
 
-int get_ppe_mib(int index, u64 *pkt_cnt, u64 *byte_cnt)
+int get_ppe_mib(int ppe_id, int index, u64 *pkt_cnt, u64 *byte_cnt)
 {
 	struct mtk_hnat *h = hnat_priv;
 	struct hnat_accounting *acount;
 	struct foe_entry *entry;
 
-	acount = hnat_get_count(h, index);
-	entry = hnat_priv->foe_table_cpu + index;
+	if (ppe_id >= CFG_PPE_NUM)
+		return -1;
+
+	acount = hnat_get_count(h, ppe_id, index);
+	entry = hnat_priv->foe_table_cpu[ppe_id] + index;
 
 	if (!acount)
 		return -1;
@@ -1821,11 +1941,14 @@
 }
 EXPORT_SYMBOL(get_ppe_mib);
 
-int is_entry_binding(int index)
+int is_entry_binding(int ppe_id, int index)
 {
 	struct foe_entry *entry;
 
-	entry = hnat_priv->foe_table_cpu + index;
+	if (ppe_id >= CFG_PPE_NUM)
+		return -1;
+
+	entry = hnat_priv->foe_table_cpu[ppe_id] + index;
 
 	return entry->bfib1.state == BIND;
 }
@@ -1886,22 +2009,28 @@
 		goto err0;
 	}
 	h->root = root;
-	h->regset = kzalloc(sizeof(*h->regset), GFP_KERNEL);
-	if (!h->regset) {
-		dev_notice(h->dev, "%s:err at %d\n", __func__, __LINE__);
-		ret = -ENOMEM;
-		goto err1;
-	}
-	h->regset->regs = hnat_regs;
-	h->regset->nregs = ARRAY_SIZE(hnat_regs);
-	h->regset->base = h->ppe_base;
 
-	file = debugfs_create_regset32("regdump", S_IRUGO, root, h->regset);
-	if (!file) {
-		dev_notice(h->dev, "%s:err at %d\n", __func__, __LINE__);
-		ret = -ENOMEM;
-		goto err1;
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		h->regset[i] = kzalloc(sizeof(*h->regset[i]), GFP_KERNEL);
+		if (!h->regset[i]) {
+			dev_notice(h->dev, "%s:err at %d\n", __func__, __LINE__);
+			ret = -ENOMEM;
+			goto err1;
+		}
+		h->regset[i]->regs = hnat_regs;
+		h->regset[i]->nregs = ARRAY_SIZE(hnat_regs);
+		h->regset[i]->base = h->ppe_base[i];
+
+		snprintf(name, sizeof(name), "regdump%ld", i);
+		file = debugfs_create_regset32(name, S_IRUGO,
+					       root, h->regset[i]);
+		if (!file) {
+			dev_notice(h->dev, "%s:err at %d\n", __func__, __LINE__);
+			ret = -ENOMEM;
+			goto err1;
+		}
 	}
+
 	debugfs_create_file("all_entry", S_IRUGO, root, h, &hnat_debug_fops);
 	debugfs_create_file("external_interface", S_IRUGO, root, h,
 			    &hnat_ext_fops);
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c
index 79e4bd0..d11dfcd 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.c
@@ -61,7 +61,7 @@
 }
 
 /*set_hnat_mtbl - set ppe multicast register*/
-static int set_hnat_mtbl(struct ppe_mcast_group *group, int index)
+static int set_hnat_mtbl(struct ppe_mcast_group *group, int ppe_id, int index)
 {
 	struct ppe_mcast_h mcast_h;
 	struct ppe_mcast_l mcast_l;
@@ -83,9 +83,9 @@
 	trace_printk("%s:index=%d,group info=0x%x,addr=0x%x\n",
 		     __func__, index, mcast_h.u.value, mcast_l.addr);
 	if (index < 0x10) {
-		reg = hnat_priv->ppe_base + PPE_MCAST_H_0 + ((index) * 8);
+		reg = hnat_priv->ppe_base[ppe_id] + PPE_MCAST_H_0 + ((index) * 8);
 		writel(mcast_h.u.value, reg);
-		reg = hnat_priv->ppe_base + PPE_MCAST_L_0 + ((index) * 8);
+		reg = hnat_priv->ppe_base[ppe_id] + PPE_MCAST_L_0 + ((index) * 8);
 		writel(mcast_l.addr, reg);
 	} else {
 		index = index - 0x10;
@@ -112,7 +112,7 @@
 	struct net_device *dev;
 	u32 mac_hi;
 	u16 mac_lo;
-	int index;
+	int i, index;
 	struct ppe_mcast_group *group;
 
 	rcu_read_lock();
@@ -164,7 +164,9 @@
 		if (!group->oif && !group->eif)
 			/*nobody in this group,clear the entry*/
 			memset(group, 0, sizeof(struct ppe_mcast_group));
-		set_hnat_mtbl(group, index);
+
+		for (i = 0; i < CFG_PPE_NUM; i++)
+			set_hnat_mtbl(group, i, index);
 	}
 
 	return 0;
@@ -254,24 +256,26 @@
 static void hnat_mcast_check_timestamp(struct timer_list *t)
 {
 	struct foe_entry *entry;
-	int hash_index;
+	int i, hash_index;
 	u16 e_ts, foe_ts;
 
-	for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) {
-		entry = hnat_priv->foe_table_cpu + hash_index;
-		if (entry->bfib1.sta == 1) {
-			e_ts = (entry->ipv4_hnapt.m_timestamp) & 0xffff;
-			foe_ts = foe_timestamp(hnat_priv);
-			if ((foe_ts - e_ts) > 0x3000)
-				foe_ts = (~(foe_ts)) & 0xffff;
-			if (abs(foe_ts - e_ts) > 20)
-				entry_delete(hash_index);
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) {
+			entry = hnat_priv->foe_table_cpu[i] + hash_index;
+			if (entry->bfib1.sta == 1) {
+				e_ts = (entry->ipv4_hnapt.m_timestamp) & 0xffff;
+				foe_ts = foe_timestamp(hnat_priv);
+				if ((foe_ts - e_ts) > 0x3000)
+					foe_ts = (~(foe_ts)) & 0xffff;
+				if (abs(foe_ts - e_ts) > 20)
+					entry_delete(i, hash_index);
+			}
 		}
 	}
 	mod_timer(&hnat_priv->hnat_mcast_check_timer, jiffies + 10 * HZ);
 }
 
-int hnat_mcast_enable(void)
+int hnat_mcast_enable(int ppe_id)
 {
 	struct ppe_mcast_table *pmcast;
 
@@ -304,15 +308,15 @@
 	}
 
 	/* Enable multicast table lookup */
-	cr_set_field(hnat_priv->ppe_base + PPE_GLO_CFG, MCAST_TB_EN, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_GLO_CFG, MCAST_TB_EN, 1);
 	/* multicast port0 map to PDMA */
-	cr_set_field(hnat_priv->ppe_base + PPE_MCAST_PPSE, MC_P0_PPSE, 0);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MCAST_PPSE, MC_P0_PPSE, 0);
 	/* multicast port1 map to GMAC1 */
-	cr_set_field(hnat_priv->ppe_base + PPE_MCAST_PPSE, MC_P1_PPSE, 1);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MCAST_PPSE, MC_P1_PPSE, 1);
 	/* multicast port2 map to GMAC2 */
-	cr_set_field(hnat_priv->ppe_base + PPE_MCAST_PPSE, MC_P2_PPSE, 2);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MCAST_PPSE, MC_P2_PPSE, 2);
 	/* multicast port3 map to QDMA */
-	cr_set_field(hnat_priv->ppe_base + PPE_MCAST_PPSE, MC_P3_PPSE, 5);
+	cr_set_field(hnat_priv->ppe_base[ppe_id] + PPE_MCAST_PPSE, MC_P3_PPSE, 5);
 
 	return 0;
 err:
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.h
index 048bc58..f6993a5 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.h
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_mcast.h
@@ -63,7 +63,7 @@
 	u32 addr;
 };
 
-int hnat_mcast_enable(void);
+int hnat_mcast_enable(int ppe_id);
 int hnat_mcast_disable(void);
 
 #endif
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
index 522a675..293f3c0 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
@@ -181,7 +181,7 @@
 
 void foe_clear_all_bind_entries(struct net_device *dev)
 {
-	int hash_index;
+	int i, hash_index;
 	struct foe_entry *entry;
 
 	if (!IS_LAN(dev) && !IS_WAN(dev) &&
@@ -189,13 +189,17 @@
 	    !dev->netdev_ops->ndo_flow_offload_check)
 		return;
 
-	cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, SMA, SMA_ONLY_FWD_CPU);
-	for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) {
-		entry = hnat_priv->foe_table_cpu + hash_index;
-		if (entry->bfib1.state == BIND) {
-			entry->ipv4_hnapt.udib1.state = INVALID;
-			entry->ipv4_hnapt.udib1.time_stamp =
-				readl((hnat_priv->fe_base + 0x0010)) & 0xFF;
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		cr_set_field(hnat_priv->ppe_base[i] + PPE_TB_CFG,
+			     SMA, SMA_ONLY_FWD_CPU);
+
+		for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) {
+			entry = hnat_priv->foe_table_cpu[i] + hash_index;
+			if (entry->bfib1.state == BIND) {
+				entry->ipv4_hnapt.udib1.state = INVALID;
+				entry->ipv4_hnapt.udib1.time_stamp =
+					readl((hnat_priv->fe_base + 0x0010)) & 0xFF;
+			}
 		}
 	}
 
@@ -246,37 +250,39 @@
 	u32 *daddr = (u32 *)neigh->primary_key;
 	unsigned char h_dest[ETH_ALEN];
 	struct foe_entry *entry;
-	int hash_index;
+	int i, hash_index;
 	u32 dip;
 
 	dip = (u32)(*daddr);
 
-	for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) {
-		entry = hnat_priv->foe_table_cpu + hash_index;
-		if (entry->bfib1.state == BIND &&
-		    entry->ipv4_hnapt.new_dip == ntohl(dip)) {
-			*((u32 *)h_dest) = swab32(entry->ipv4_hnapt.dmac_hi);
-			*((u16 *)&h_dest[4]) =
-				swab16(entry->ipv4_hnapt.dmac_lo);
-			if (strncmp(h_dest, neigh->ha, ETH_ALEN) != 0) {
-				pr_info("%s: state=%d\n", __func__,
-					neigh->nud_state);
-				cr_set_field(hnat_priv->ppe_base + PPE_TB_CFG, SMA,
-					     SMA_ONLY_FWD_CPU);
+	for (i = 0; i < CFG_PPE_NUM; i++) {
+		for (hash_index = 0; hash_index < hnat_priv->foe_etry_num; hash_index++) {
+			entry = hnat_priv->foe_table_cpu[i] + hash_index;
+			if (entry->bfib1.state == BIND &&
+			    entry->ipv4_hnapt.new_dip == ntohl(dip)) {
+				*((u32 *)h_dest) = swab32(entry->ipv4_hnapt.dmac_hi);
+				*((u16 *)&h_dest[4]) =
+					swab16(entry->ipv4_hnapt.dmac_lo);
+				if (strncmp(h_dest, neigh->ha, ETH_ALEN) != 0) {
+					pr_info("%s: state=%d\n", __func__,
+						neigh->nud_state);
+					cr_set_field(hnat_priv->ppe_base[i] + PPE_TB_CFG,
+						     SMA, SMA_ONLY_FWD_CPU);
 
-				entry->ipv4_hnapt.udib1.state = INVALID;
-				entry->ipv4_hnapt.udib1.time_stamp =
-					readl((hnat_priv->fe_base + 0x0010)) & 0xFF;
+					entry->ipv4_hnapt.udib1.state = INVALID;
+					entry->ipv4_hnapt.udib1.time_stamp =
+						readl((hnat_priv->fe_base + 0x0010)) & 0xFF;
 
-				/* clear HWNAT cache */
-				hnat_cache_ebl(1);
+					/* clear HWNAT cache */
+					hnat_cache_ebl(1);
 
-				mod_timer(&hnat_priv->hnat_sma_build_entry_timer,
-					  jiffies + 3 * HZ);
+					mod_timer(&hnat_priv->hnat_sma_build_entry_timer,
+						  jiffies + 3 * HZ);
 
-				pr_info("Delete old entry: dip =%pI4\n", &dip);
-				pr_info("Old mac= %pM\n", h_dest);
-				pr_info("New mac= %pM\n", neigh->ha);
+					pr_info("Delete old entry: dip =%pI4\n", &dip);
+					pr_info("Old mac= %pM\n", h_dest);
+					pr_info("New mac= %pM\n", neigh->ha);
+				}
 			}
 		}
 	}
@@ -421,7 +427,7 @@
 				skb->vlan_proto = 0;
 				skb->vlan_tci = 0;
 				fix_skb_packet_type(skb, skb->dev, eth_hdr(skb));
-				entry = &hnat_priv->foe_table_cpu[skb_hnat_entry(skb)];
+				entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)];
 				entry->bfib1.pkt_type = IPV4_HNAPT;
 				netif_rx(skb);
 				return 0;
@@ -439,7 +445,7 @@
 	struct foe_entry *entry;
 	struct net_device *dev;
 
-	entry = &hnat_priv->foe_table_cpu[skb_hnat_entry(skb)];
+	entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)];
 
 	if (IS_IPV4_GRP(entry))
 		index = entry->ipv4_hnapt.act_dp;
@@ -1507,8 +1513,8 @@
 	memcpy(foe, &entry, sizeof(entry));
 	/*reset statistic for this entry*/
 	if (hnat_priv->data->per_flow_accounting)
-		memset(&hnat_priv->acct[skb_hnat_entry(skb)], 0,
-		       sizeof(struct mib_entry));
+		memset(&hnat_priv->acct[skb_hnat_ppe(skb)][skb_hnat_entry(skb)],
+		       0, sizeof(struct mib_entry));
 
 	wmb();
 	/* The INFO2.port_mg and 2nd VLAN ID fields of PPE entry are redefined
@@ -1538,7 +1544,7 @@
 	if (!skb_hnat_is_hashed(skb))
 		return NF_ACCEPT;
 
-	entry = &hnat_priv->foe_table_cpu[skb_hnat_entry(skb)];
+	entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)];
 	if (entry_hnat_is_bound(entry))
 		return NF_ACCEPT;
 
@@ -1785,7 +1791,7 @@
 	trace_printk("[%s] case hit, %x-->%s, reason=%x\n", __func__,
 		     skb_hnat_iface(skb), out->name, skb_hnat_reason(skb));
 
-	entry = &hnat_priv->foe_table_cpu[skb_hnat_entry(skb)];
+	entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)];
 
 	switch (skb_hnat_reason(skb)) {
 	case HIT_UNBIND_RATE_REACH:
@@ -1841,7 +1847,7 @@
 	if (unlikely(!skb_hnat_is_hashed(skb)))
 		return NF_ACCEPT;
 
-	entry = &hnat_priv->foe_table_cpu[skb_hnat_entry(skb)];
+	entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)];
 	if (skb_hnat_reason(skb) == HIT_UNBIND_RATE_REACH) {
 		ip6h = ipv6_hdr(skb);
 		if (ip6h->nexthdr == NEXTHDR_IPIP) {
@@ -1998,7 +2004,7 @@
 	if (!skb_hnat_is_hashed(skb))
 		return NF_ACCEPT;
 
-	entry = &hnat_priv->foe_table_cpu[skb_hnat_entry(skb)];
+	entry = &hnat_priv->foe_table_cpu[skb_hnat_ppe(skb)][skb_hnat_entry(skb)];
 
 	if (unlikely(skb_headroom(skb) < FOE_INFO_LEN)) {
 		new_skb = skb_realloc_headroom(skb, FOE_INFO_LEN);
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
index bd857f4..c25413d 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
@@ -81,6 +81,8 @@
 #define skb_hnat_rx_id(skb) (((struct hnat_desc *)((skb)->head))->rxid)
 #define skb_hnat_wc_id(skb) (((struct hnat_desc *)((skb)->head))->wcid)
 #define skb_hnat_bss_id(skb) (((struct hnat_desc *)((skb)->head))->bssid)
+#define skb_hnat_ppe(skb)				\
+	((skb_hnat_iface(skb) == FOE_MAGIC_WED1 && CFG_PPE_NUM > 1) ? 1 : 0)
 #define do_ext2ge_fast_try(dev, skb) (IS_EXT(dev) && !is_from_extge(skb))
 #define set_from_extge(skb) (HNAT_SKB_CB2(skb)->magic = 0x78786688)
 #define clr_from_extge(skb) (HNAT_SKB_CB2(skb)->magic = 0x0)