[rdkb][common][bsp][Refactor and sync kernel from openwrt]

[Description]
d5d2a87a [kernel][mt7987][hnat][Add L2 BRIDGE HNAT support for the NETSYSv3.1]
77e47d7f [MAC80211][hnat][Fix patch conflict issue]
4c38df46 [kernel][mt7987][eth][Add IEEE1588v2 basic support for the NETSYSv3.1]
b985c354 [openwrt][mt7988][crypto][Add timer for DDK interrupt]
66ed6ddf [openwrt][mt7988][crypto][Refactor crypto-eip since sperate ddk driver]
6e6ab426 [openwrt][mt7988][crypto][Change DDK from combined driver to seperate driver]
5c364024 [openwrt][mt7988][crypto][Change crypto dts node to compatible with seperated DDK driver]
8e6bd0ed [openwrt][mt7988][crypto][Fix race condition and refactor SG release in DDK driver]
0a899192 [openwrt][mt7988][crypto][Add multiple ring for DDK look-aside]
34937f68 [MAC80211][hnat][Fix patch conflict issue]
5d5d7ee6 [kernel][mt7987][eth][Add error handling for PSE error info to the FE misc ISR for the NETSYSv3.1]
e8ae5058 [kernel][mt7987][eth][Add the private data for the MT7987 SoC]
bb574392 [kernel][mt7987][eth][Add QDMAv1.4 support for the NETSYS v3.1]
6f88ab25 [[Kernel][common][hnat][Fix debug_level in skb_to_hnat_info]]
e435455a [kernel][mt7987][eth][Add XGMACv2 support for the NETSYS v3.1]
665acaf6 [kernel][mt7987][eth][Update the GMAC to USB3 co-Phy path for the NETSYS v3.1]
db2cba01 [kernel][mt7987][eth][Add the default configuration for the NETSYSv3.1]
f3817bbf [openwrt][common][app][Fix base to 0 of strtoul in switch app]
d0d83cbe [openwrt-24][Mac80211][Refactor the autobuild naming]

[Release-log]

Change-Id: I864f0f8d8d9b34e0616a52c865d676a6ba7c03dd
diff --git a/recipes-devtools/switch/files/src/switch_753x.c b/recipes-devtools/switch/files/src/switch_753x.c
index 03e9778..a2cd8a2 100644
--- a/recipes-devtools/switch/files/src/switch_753x.c
+++ b/recipes-devtools/switch/files/src/switch_753x.c
@@ -530,7 +530,7 @@
 
 	switch (op) {
 	case 'r':
-		reg = strtoul(argv[argc - 1], NULL, 16);
+		reg = strtoul(argv[argc - 1], NULL, 0);
 		if (reg >= 0xFFFFFFFF) {
 			printf(" Phy read reg fail\n");
 			ret = -1;
@@ -552,7 +552,7 @@
 				printf(" Phy read reg=0x%x, value=0x%x\n",
 				       reg, value);
 		} else if (argc == 7) {
-			dev_num = strtoul(argv[argc - 2], NULL, 16);
+			dev_num = strtoul(argv[argc - 2], NULL, 0);
 			if (dev_num > 0xFFFFFFFF) {
 				printf(" Phy read reg fail\n");
 				ret = -1;
@@ -577,7 +577,7 @@
 			ret = phy_dump(32);
 		break;
 	case 'w':
-		reg = strtoul(argv[argc - 2], NULL, 16);
+		reg = strtoul(argv[argc - 2], NULL, 0);
 		if (reg >= 0xFFFFFFFF) {
 			printf(" Phy write reg fail\n");
 			ret = -1;
@@ -607,7 +607,7 @@
 				printf(" Phy write reg=0x%x, value=0x%x\n",
 				       reg, cl_value);
 		} else if (argc == 8) {
-			dev_num = strtoul(argv[argc - 3], NULL, 16);
+			dev_num = strtoul(argv[argc - 3], NULL, 0);
 			if (dev_num > 0xFFFFFFFF) {
 				printf(" Phy write reg fail\n");
 				ret = -1;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi
index b2b2fde..1944906 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7988.dtsi
@@ -620,11 +620,12 @@
 		compatible = "inside-secure,safexcel-eip197b",
 			     "security-ip-197-srv";
 		reg = <0 0x15600000 0 0x180000>;
-		interrupts = <GIC_SPI 214 IRQ_TYPE_LEVEL_HIGH>,
+		interrupts = <GIC_SPI 213 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 214 IRQ_TYPE_LEVEL_HIGH>,
 			     <GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>,
 			     <GIC_SPI 216 IRQ_TYPE_LEVEL_HIGH>,
 			     <GIC_SPI 217 IRQ_TYPE_LEVEL_HIGH>;
-		interrupt-names = "ring0", "ring1", "ring2", "ring3";
+		interrupt-names = "global", "ring0", "ring1", "ring2", "ring3";
 		status = "okay";
 		eth = <&eth>;
 		hnat = <&hnat>;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/Makefile b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/Makefile
index 634640d..2f14aa1 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/Makefile
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/Makefile
@@ -4,5 +4,5 @@
 #
 
 obj-$(CONFIG_NET_MEDIATEK_SOC)			+= mtk_eth.o
-mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o
+mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o mtk_eth_ptp.o
 obj-$(CONFIG_NET_MEDIATEK_HNAT)			+= mtk_hnat/
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
index 045fc2b..f48720a 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
@@ -65,6 +65,394 @@
 
 struct mtk_eth_debug eth_debug;
 
+void qdma_qos_shaper_ebl(u32 id, bool enable)
+{
+	struct mtk_eth *eth = g_eth;
+	u32 val;
+
+	mtk_w32(eth, (id / MTK_QTX_PER_PAGE), MTK_QDMA_PAGE);
+	if (enable) {
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_4)) {
+			val = MTK_QTX_SCH_MIN_RATE_EN | MTK_QTX_SCH_MAX_RATE_EN_V2;
+			val |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN_V2, 1) |
+			       FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP_V2, 4) |
+			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN_V2, 1) |
+			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP_V2, 6) |
+			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT_V2, 4);
+		} else {
+			val = MTK_QTX_SCH_MIN_RATE_EN | MTK_QTX_SCH_MAX_RATE_EN;
+			val |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
+			       FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) |
+			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 1) |
+			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 6) |
+			       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 4);
+		}
+
+		mtk_w32(eth, val, MTK_QTX_SCH(id % MTK_QTX_PER_PAGE));
+	} else {
+		mtk_w32(eth, 0, MTK_QTX_SCH(id % MTK_QTX_PER_PAGE));
+	}
+}
+
+void qdma_qos_disable(void)
+{
+	struct mtk_eth *eth = g_eth;
+	u32 num_of_sch = !MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_1) ? 4 : 2;
+	u32 val, i;
+
+	for (i = 0; i < MAX_PPPQ_PORT_NUM; i++) {
+		qdma_qos_shaper_ebl(i, false);
+		mtk_w32(eth,
+			FIELD_PREP(MTK_QTX_CFG_HW_RESV_CNT_OFFSET, 4) |
+			FIELD_PREP(MTK_QTX_CFG_SW_RESV_CNT_OFFSET, 4),
+			MTK_QTX_CFG(i % MTK_QTX_PER_PAGE));
+	}
+
+	val = (MTK_QDMA_TX_SCH_MAX_WFQ) | (MTK_QDMA_TX_SCH_MAX_WFQ << 16);
+	for (i = 0; i < num_of_sch; i += 2) {
+		if (num_of_sch == 4)
+			mtk_w32(eth, val, MTK_QDMA_TX_4SCH_BASE(i));
+		else
+			mtk_w32(eth, val, MTK_QDMA_TX_2SCH_BASE);
+	}
+}
+EXPORT_SYMBOL(qdma_qos_disable);
+
+void qdma_qos_pppq_ebl(u32 enable)
+{
+	struct mtk_eth *eth = g_eth;
+	u32 num_of_sch = !MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_1) ? 4 : 2;
+	u32 val, i;
+
+	for (i = 0; i < MAX_PPPQ_PORT_NUM; i++) {
+		if (enable)
+			qdma_qos_shaper_ebl(i, true);
+		else
+			qdma_qos_shaper_ebl(i, false);
+
+		mtk_w32(eth,
+			FIELD_PREP(MTK_QTX_CFG_HW_RESV_CNT_OFFSET, 4) |
+			FIELD_PREP(MTK_QTX_CFG_SW_RESV_CNT_OFFSET, 4),
+			MTK_QTX_CFG(i % MTK_QTX_PER_PAGE));
+	}
+
+	val = (MTK_QDMA_TX_SCH_MAX_WFQ) | (MTK_QDMA_TX_SCH_MAX_WFQ << 16);
+	for (i = 0; i < num_of_sch; i += 2) {
+		if (num_of_sch == 4)
+			mtk_w32(eth, val, MTK_QDMA_TX_4SCH_BASE(i));
+		else
+			mtk_w32(eth, val, MTK_QDMA_TX_2SCH_BASE);
+	}
+}
+EXPORT_SYMBOL(qdma_qos_pppq_ebl);
+
+static ssize_t qdma_sched_show(struct file *file, char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	struct mtk_eth *eth = g_eth;
+	long id = (long)file->private_data;
+	char *buf;
+	unsigned int len = 0, buf_len = 1500;
+	u32 qdma_tx_sch, sch_reg;
+	int enable, scheduling, max_rate, scheduler, i;
+	ssize_t ret_cnt;
+
+	buf = kzalloc(buf_len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_1))
+		qdma_tx_sch = mtk_r32(eth, MTK_QDMA_TX_4SCH_BASE(id));
+	else
+		qdma_tx_sch = mtk_r32(eth, MTK_QDMA_TX_2SCH_BASE);
+
+	if (id & 0x1)
+		qdma_tx_sch >>= 16;
+
+	qdma_tx_sch &= MTK_QDMA_TX_SCH_MASK;
+	enable = FIELD_GET(MTK_QDMA_TX_SCH_RATE_EN, qdma_tx_sch);
+	scheduling = FIELD_GET(MTK_QDMA_TX_SCH_MAX_WFQ, qdma_tx_sch);
+	max_rate = FIELD_GET(MTK_QDMA_TX_SCH_RATE_MAN, qdma_tx_sch);
+	qdma_tx_sch = FIELD_GET(MTK_QDMA_TX_SCH_RATE_EXP, qdma_tx_sch);
+	while (qdma_tx_sch--)
+		max_rate *= 10;
+
+	len += scnprintf(buf + len, buf_len - len,
+			 "EN\tScheduling\tMAX\tQueue#\n%d\t%s%16d\t", enable,
+			 (scheduling == 1) ? "WRR" : "SP", max_rate);
+
+	for (i = 0; i < MTK_QDMA_TX_NUM; i++) {
+		mtk_w32(eth, (i / MTK_QTX_PER_PAGE), MTK_QDMA_PAGE);
+		sch_reg = mtk_r32(eth, MTK_QTX_SCH(i % MTK_QTX_PER_PAGE));
+		if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_1))
+			scheduler = FIELD_GET(MTK_QTX_SCH_TX_SEL_V2, sch_reg);
+		else
+			scheduler = FIELD_GET(MTK_QTX_SCH_TX_SEL, sch_reg);
+		if (id == scheduler)
+			len += scnprintf(buf + len, buf_len - len, "%d  ", i);
+	}
+
+	len += scnprintf(buf + len, buf_len - len, "\n");
+	if (len > buf_len)
+		len = buf_len;
+
+	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+	kfree(buf);
+
+	return ret_cnt;
+}
+
+static ssize_t qdma_sched_write(struct file *file, const char __user *buf,
+				size_t length, loff_t *offset)
+{
+	struct mtk_eth *eth = g_eth;
+	long id = (long)file->private_data;
+	char line[64] = {0}, scheduling[32];
+	int enable, rate, exp = 0, shift = 0;
+	size_t size;
+	u32 qdma_tx_sch, val = 0;
+
+	if (length >= sizeof(line))
+		return -EINVAL;
+
+	if (copy_from_user(line, buf, length))
+		return -EFAULT;
+
+	if (sscanf(line, "%1d %3s %9d", &enable, scheduling, &rate) != 3)
+		return -EFAULT;
+
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_3) ||
+	    MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_4)) {
+		if (rate > 10000000 || rate < 0)
+			return -EINVAL;
+	} else {
+		if (rate > 1000000 || rate < 0)
+			return -EINVAL;
+	}
+
+	while (rate > 127) {
+		rate /= 10;
+		exp++;
+	}
+
+	line[length] = '\0';
+
+	if (enable)
+		val |= MTK_QDMA_TX_SCH_RATE_EN;
+	if (strcmp(scheduling, "sp") != 0)
+		val |= MTK_QDMA_TX_SCH_MAX_WFQ;
+	val |= FIELD_PREP(MTK_QDMA_TX_SCH_RATE_MAN, rate);
+	val |= FIELD_PREP(MTK_QDMA_TX_SCH_RATE_EXP, exp);
+	if (id & 0x1)
+		shift = 16;
+
+	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_1))
+		qdma_tx_sch = mtk_r32(eth, MTK_QDMA_TX_4SCH_BASE(id));
+	else
+		qdma_tx_sch = mtk_r32(eth, MTK_QDMA_TX_2SCH_BASE);
+
+	qdma_tx_sch &= ~(MTK_QDMA_TX_SCH_MASK << shift);
+	qdma_tx_sch |= val << shift;
+	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_1))
+		mtk_w32(eth, qdma_tx_sch, MTK_QDMA_TX_4SCH_BASE(id));
+	else
+		mtk_w32(eth, qdma_tx_sch, MTK_QDMA_TX_2SCH_BASE);
+
+	size = strlen(line);
+	*offset += size;
+
+	return length;
+}
+
+static const struct file_operations qdma_sched_fops = {
+	.open = simple_open,
+	.read = qdma_sched_show,
+	.write = qdma_sched_write,
+	.llseek = default_llseek,
+};
+
+static ssize_t qdma_queue_show(struct file *file, char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	struct mtk_eth *eth = g_eth;
+	long id = (long)file->private_data;
+	char *buf;
+	unsigned int len = 0, buf_len = 1500;
+	u32 qtx_sch, qtx_cfg;
+	int scheduler;
+	int min_rate_en, min_rate, min_rate_exp;
+	int max_rate_en, max_weight, max_rate, max_rate_exp;
+	ssize_t ret_cnt;
+
+	buf = kzalloc(buf_len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	mtk_w32(eth, (id / MTK_QTX_PER_PAGE), MTK_QDMA_PAGE);
+	qtx_cfg = mtk_r32(eth, MTK_QTX_CFG(id % MTK_QTX_PER_PAGE));
+	qtx_sch = mtk_r32(eth, MTK_QTX_SCH(id % MTK_QTX_PER_PAGE));
+	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_1))
+		scheduler = FIELD_GET(MTK_QTX_SCH_TX_SEL_V2, qtx_sch);
+	else
+		scheduler = FIELD_GET(MTK_QTX_SCH_TX_SEL, qtx_sch);
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_4)) {
+		min_rate_en = FIELD_GET(MTK_QTX_SCH_MIN_RATE_EN, qtx_sch);
+		min_rate = FIELD_GET(MTK_QTX_SCH_MIN_RATE_MAN_V2, qtx_sch);
+		min_rate_exp = FIELD_GET(MTK_QTX_SCH_MIN_RATE_EXP_V2, qtx_sch);
+		max_rate_en = FIELD_GET(MTK_QTX_SCH_MAX_RATE_EN_V2, qtx_sch);
+		max_weight = FIELD_GET(MTK_QTX_SCH_MAX_RATE_WEIGHT_V2, qtx_sch);
+		max_rate = FIELD_GET(MTK_QTX_SCH_MAX_RATE_MAN_V2, qtx_sch);
+		max_rate_exp = FIELD_GET(MTK_QTX_SCH_MAX_RATE_EXP_V2, qtx_sch);
+	} else {
+		min_rate_en = FIELD_GET(MTK_QTX_SCH_MIN_RATE_EN, qtx_sch);
+		min_rate = FIELD_GET(MTK_QTX_SCH_MIN_RATE_MAN, qtx_sch);
+		min_rate_exp = FIELD_GET(MTK_QTX_SCH_MIN_RATE_EXP, qtx_sch);
+		max_rate_en = FIELD_GET(MTK_QTX_SCH_MAX_RATE_EN, qtx_sch);
+		max_weight = FIELD_GET(MTK_QTX_SCH_MAX_RATE_WEIGHT, qtx_sch);
+		max_rate = FIELD_GET(MTK_QTX_SCH_MAX_RATE_MAN, qtx_sch);
+		max_rate_exp = FIELD_GET(MTK_QTX_SCH_MAX_RATE_EXP, qtx_sch);
+	}
+	while (min_rate_exp--)
+		min_rate *= 10;
+
+	while (max_rate_exp--)
+		max_rate *= 10;
+
+	len += scnprintf(buf + len, buf_len - len,
+			 "scheduler: %d\nhw resv: %d\nsw resv: %d\n", scheduler,
+			 (qtx_cfg >> 8) & 0xff, qtx_cfg & 0xff);
+
+	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_1)) {
+		/* Switch to debug mode */
+		mtk_m32(eth, MTK_MIB_ON_QTX_CFG, MTK_MIB_ON_QTX_CFG, MTK_QTX_MIB_IF);
+		mtk_m32(eth, MTK_VQTX_MIB_EN, MTK_VQTX_MIB_EN, MTK_QTX_MIB_IF);
+		qtx_cfg = mtk_r32(eth, MTK_QTX_CFG(id % MTK_QTX_PER_PAGE));
+		qtx_sch = mtk_r32(eth, MTK_QTX_SCH(id % MTK_QTX_PER_PAGE));
+		len += scnprintf(buf + len, buf_len - len,
+				 "packet count: %u\n", qtx_cfg);
+		len += scnprintf(buf + len, buf_len - len,
+				 "packet drop: %u\n\n", qtx_sch);
+
+		/* Recover to normal mode */
+		mtk_m32(eth, MTK_MIB_ON_QTX_CFG, 0, MTK_QTX_MIB_IF);
+		mtk_m32(eth, MTK_VQTX_MIB_EN, 0, MTK_QTX_MIB_IF);
+	}
+
+	len += scnprintf(buf + len, buf_len - len,
+			 "      EN     RATE     WEIGHT\n");
+	len += scnprintf(buf + len, buf_len - len,
+			 "----------------------------\n");
+	len += scnprintf(buf + len, buf_len - len,
+			 "max%5d%9d%9d\n", max_rate_en, max_rate, max_weight);
+	len += scnprintf(buf + len, buf_len - len,
+			 "min%5d%9d        -\n", min_rate_en, min_rate);
+
+	if (len > buf_len)
+		len = buf_len;
+
+	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+	kfree(buf);
+
+	return ret_cnt;
+}
+
+static ssize_t qdma_queue_write(struct file *file, const char __user *buf,
+				size_t length, loff_t *offset)
+{
+	struct mtk_eth *eth = g_eth;
+	long id = (long)file->private_data;
+	char line[64] = {0};
+	int max_enable, max_rate, max_exp = 0;
+	int min_enable, min_rate, min_exp = 0;
+	int weight;
+	int resv;
+	int scheduler;
+	size_t size;
+	u32 qtx_sch = 0;
+
+	mtk_w32(eth, (id / MTK_QTX_PER_PAGE), MTK_QDMA_PAGE);
+	if (length >= sizeof(line))
+		return -EINVAL;
+
+	if (copy_from_user(line, buf, length))
+		return -EFAULT;
+
+	if (sscanf(line, "%d %d %d %d %d %d %d", &scheduler, &min_enable, &min_rate,
+		   &max_enable, &max_rate, &weight, &resv) != 7)
+		return -EFAULT;
+
+	line[length] = '\0';
+
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_3) ||
+	    MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_4)) {
+		if (max_rate > 10000000 || max_rate < 0 ||
+		    min_rate > 10000000 || min_rate < 0)
+			return -EINVAL;
+	} else {
+		if (max_rate > 1000000 || max_rate < 0 ||
+		    min_rate > 1000000 || min_rate < 0)
+			return -EINVAL;
+	}
+
+	while (max_rate > 127) {
+		max_rate /= 10;
+		max_exp++;
+	}
+
+	while (min_rate > 127) {
+		min_rate /= 10;
+		min_exp++;
+	}
+
+	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_1))
+		qtx_sch |= FIELD_PREP(MTK_QTX_SCH_TX_SEL_V2, scheduler);
+	else
+		qtx_sch |= FIELD_PREP(MTK_QTX_SCH_TX_SEL, scheduler);
+
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_4)) {
+		if (min_enable)
+			qtx_sch |= MTK_QTX_SCH_MIN_RATE_EN;
+		qtx_sch |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN_V2, min_rate);
+		qtx_sch |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP_V2, min_exp);
+		if (max_enable)
+			qtx_sch |= MTK_QTX_SCH_MAX_RATE_EN_V2;
+		qtx_sch |= FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT_V2, weight);
+		qtx_sch |= FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN_V2, max_rate);
+		qtx_sch |= FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP_V2, max_exp);
+	} else {
+		if (min_enable)
+			qtx_sch |= MTK_QTX_SCH_MIN_RATE_EN;
+		qtx_sch |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, min_rate);
+		qtx_sch |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, min_exp);
+		if (max_enable)
+			qtx_sch |= MTK_QTX_SCH_MAX_RATE_EN;
+		qtx_sch |= FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, weight);
+		qtx_sch |= FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, max_rate);
+		qtx_sch |= FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, max_exp);
+	}
+	mtk_w32(eth, qtx_sch, MTK_QTX_SCH(id % MTK_QTX_PER_PAGE));
+
+	qtx_sch = mtk_r32(eth, MTK_QTX_CFG(id % MTK_QTX_PER_PAGE));
+	qtx_sch &= 0xffff0000;
+	qtx_sch |= FIELD_PREP(MTK_QTX_CFG_HW_RESV_CNT_OFFSET, resv);
+	qtx_sch |= FIELD_PREP(MTK_QTX_CFG_SW_RESV_CNT_OFFSET, resv);
+	mtk_w32(eth, qtx_sch, MTK_QTX_CFG(id % MTK_QTX_PER_PAGE));
+
+	size = strlen(line);
+	*offset += size;
+
+	return length;
+}
+
+static const struct file_operations qdma_queue_fops = {
+	.open = simple_open,
+	.read = qdma_queue_show,
+	.write = qdma_queue_write,
+	.llseek = default_llseek,
+};
+
 int mt798x_iomap(void)
 {
 	struct device_node *np = NULL;
@@ -800,6 +1188,8 @@
 
 int mtketh_debugfs_init(struct mtk_eth *eth)
 {
+	char name[16];
+	long i;
 	int ret = 0;
 
 	eth_debug.root = debugfs_create_dir("mtketh", NULL);
@@ -828,6 +1218,31 @@
 				    eth_debug.root, eth,
 				    &fops_mt7530sw_reg_w);
 	}
+
+	for (i = 0; i < (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_1) ? 4 : 2); i++) {
+		ret = snprintf(name, sizeof(name), "qdma_sch%ld", i);
+		if (ret != strlen(name)) {
+			ret = -ENOMEM;
+			goto err;
+		}
+		debugfs_create_file(name, 0444, eth_debug.root, (void *)i,
+				    &qdma_sched_fops);
+	}
+
+	for (i = 0; i < MTK_QDMA_TX_NUM; i++) {
+		ret = snprintf(name, sizeof(name), "qdma_txq%ld", i);
+		if (ret != strlen(name)) {
+			ret = -ENOMEM;
+			goto err;
+		}
+		debugfs_create_file(name, 0444, eth_debug.root, (void *)i,
+				    &qdma_queue_fops);
+	}
+
+	return 0;
+
+err:
+	debugfs_remove_recursive(eth_debug.root);
 	return ret;
 }
 
@@ -1157,12 +1572,20 @@
 int xfi_cnt_read(struct seq_file *seq, void *v)
 {
 	struct mtk_eth *eth = g_eth;
+	bool has_xgmac[MTK_MAX_DEVS] = {0,
+					MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC2_2P5GPHY) ||
+					MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC2_2P5GPHY_V2) ||
+					MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC2_USXGMII),
+					MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC3_USXGMII)};
 	int i;
 
 	seq_puts(seq, "+------------------------------------+\n");
 	seq_puts(seq, "|             <<XFI MAC>>            |\n");
 
 	for (i = MTK_GMAC2_ID; i < MTK_GMAC_ID_MAX; i++) {
+		if (!has_xgmac[i])
+			continue;
+
 		xfi_mib_dump(seq, i);
 		mtk_m32(eth, 0x1, 0x1, MTK_XFI_MIB_BASE(i) + MTK_XFI_CNT_CTRL);
 		seq_puts(seq, "|                                    |\n");
@@ -1496,12 +1919,16 @@
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
 		seq_printf(seq, "| XMAC_P1_MCR	: %08x |\n",
 			   mtk_r32(eth, MTK_XMAC_MCR(1)));
-		seq_printf(seq, "| XMAC_P2_MCR	: %08x |\n",
-			   mtk_r32(eth, MTK_XMAC_MCR(2)));
 		seq_printf(seq, "| XMAC_P1_STS	: %08x |\n",
-			   mtk_r32(eth, MTK_XGMAC_STS(1)));
-		seq_printf(seq, "| XMAC_P2_STS	: %08x |\n",
-			   mtk_r32(eth, MTK_XGMAC_STS(2)));
+			   mtk_r32(eth, MTK_HAS_CAPS(eth->soc->caps, MTK_XGMAC_V2) ?
+						     MTK_XMAC_STS(1) : MTK_XGMAC_STS(1)));
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC3_USXGMII)) {
+			seq_printf(seq, "| XMAC_P2_MCR	: %08x |\n",
+				   mtk_r32(eth, MTK_XMAC_MCR(2)));
+			seq_printf(seq, "| XMAC_P2_STS	: %08x |\n",
+				   mtk_r32(eth, MTK_HAS_CAPS(eth->soc->caps, MTK_XGMAC_V2) ?
+							     MTK_XMAC_STS(2) : MTK_XGMAC_STS(2)));
+		}
 	}
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ||
 	    MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h
index 9887835..3c0a9ef 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h
@@ -106,6 +106,8 @@
 #define PROCREG_HW_LRO_AUTO_TLB		"hw_lro_auto_tlb"
 #define PROCREG_RESET_EVENT		"reset_event"
 
+#define MAX_PPPQ_PORT_NUM		6
+
 /* XFI MAC MIB Register */
 #define MTK_XFI_MIB_BASE(x)		(MTK_XMAC_MCR(x))
 #define MTK_XFI_CNT_CTRL		0x100
@@ -424,5 +426,7 @@
 void hw_lro_stats_update(u32 ring_no, struct mtk_rx_dma_v2 *rxd);
 void hw_lro_flush_stats_update(u32 ring_no, struct mtk_rx_dma_v2 *rxd);
 void mt753x_set_port_link_state(bool up);
+void qdma_qos_disable(void);
+void qdma_qos_pppq_ebl(u32 hook_toggle);
 
 #endif /* MTK_ETH_DBG_H */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c
index e61390a..3044ee4 100755
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_path.c
@@ -104,13 +104,14 @@
 	return 0;
 }
 
-static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, u64 path)
+static int set_mux_u3_gmac23_to_qphy(struct mtk_eth *eth, u64 path)
 {
 	unsigned int val = 0,mask=0,reg=0;
 	bool updated = true;
 
 	switch (path) {
 	case MTK_ETH_PATH_GMAC2_SGMII:
+	case MTK_ETH_PATH_GMAC3_SGMII:
 		if (MTK_HAS_CAPS(eth->soc->caps, MTK_U3_COPHY_V2)) {
 			reg = USB_PHY_SWITCH_REG;
 			val = SGMII_QPHY_SEL;
@@ -311,9 +312,9 @@
 		.cap_bit = MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY,
 		.set_path = set_mux_gmac2_gmac0_to_gephy,
 	}, {
-		.name = "mux_u3_gmac2_to_qphy",
-		.cap_bit = MTK_ETH_MUX_U3_GMAC2_TO_QPHY,
-		.set_path = set_mux_u3_gmac2_to_qphy,
+		.name = "mux_u3_gmac23_to_qphy",
+		.cap_bit = MTK_ETH_MUX_U3_GMAC23_TO_QPHY,
+		.set_path = set_mux_u3_gmac23_to_qphy,
 	}, {
 		.name = "mux_gmac2_to_2p5gphy",
 		.cap_bit = MTK_ETH_MUX_GMAC2_TO_2P5GPHY,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_ptp.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_ptp.c
new file mode 100644
index 0000000..e061cb5
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_ptp.c
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IEEE1588v2 PTP support for MediaTek ETH device.
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Authors: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+ */
+
+#include <linux/if_vlan.h>
+#include <linux/net_tstamp.h>
+#include <linux/ptp_classify.h>
+
+#include "mtk_eth_soc.h"
+
+/* Values for the messageType field */
+#define SYNC			0x0
+#define DELAY_REQ		0x1
+#define PDELAY_REQ		0x2
+#define PDELAY_RESP		0x3
+#define FOLLOW_UP		0x8
+#define DELAY_RESP		0x9
+#define PDELAY_RESP_FOLLOW_UP	0xA
+#define ANNOUNCE		0xB
+
+static int mtk_ptp_hwtstamp_enable(struct net_device *dev, int hwtstamp)
+{
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct mtk_eth *eth = mac->hw;
+
+	mtk_m32(eth, CSR_HW_TS_EN(mac->id),
+		hwtstamp ? CSR_HW_TS_EN(mac->id) : 0, MAC_TS_MAC_CFG);
+
+	return 0;
+}
+
+static int mtk_ptp_hwtstamp_get_t1(struct mtk_mac *mac, struct timespec64 *ts)
+{
+	struct mtk_eth *eth = mac->hw;
+	u32 sid[2], dw[4];
+	int i;
+
+	sid[0] = mtk_r32(eth, MAC_TS_T1_SID1(mac->id));
+	for (i = 0; i < 4; i++)
+		dw[i] = mtk_r32(eth, MAC_TS_T1_DW(mac->id) + i * 4);
+	sid[1] = mtk_r32(eth, MAC_TS_T1_SID2(mac->id));
+
+	if (sid[0] != sid[1]) {
+		pr_warn("%s invalid hwtstamp(%d, %d)!\n", __func__, sid[0], sid[1]);
+		return -EINVAL;
+	}
+
+	ts->tv_sec = dw[2] | ((u64)dw[3] << 32);
+	ts->tv_nsec = dw[1];
+
+	return 0;
+}
+
+static int mtk_ptp_hwtstamp_get_t2(struct mtk_mac *mac, struct timespec64 *ts)
+{
+	struct mtk_eth *eth = mac->hw;
+	u32 sid[2], dw[4];
+	int i;
+
+	sid[0] = mtk_r32(eth, MAC_TS_T2_SID1(mac->id));
+	for (i = 0; i < 4; i++)
+		dw[i] = mtk_r32(eth, MAC_TS_T2_DW(mac->id) + i * 4);
+	sid[1] = mtk_r32(eth, MAC_TS_T2_SID2(mac->id));
+
+	if (sid[0] != sid[1]) {
+		pr_warn("%s invalid hwtstamp(%d, %d)!\n", __func__, sid[0], sid[1]);
+		return -EINVAL;
+	}
+
+	ts->tv_sec = dw[2] | ((u64)dw[3] << 32);
+	ts->tv_nsec = dw[1];
+
+	return 0;
+}
+
+static int mtk_ptp_hwtstamp_get_t3(struct mtk_mac *mac, struct timespec64 *ts)
+{
+	struct mtk_eth *eth = mac->hw;
+	u32 sid[2], dw[4];
+	int i;
+
+	sid[0] = mtk_r32(eth, MAC_TS_T3_SID1(mac->id));
+	for (i = 0; i < 4; i++)
+		dw[i] = mtk_r32(eth, MAC_TS_T3_DW(mac->id) + i * 4);
+	sid[1] = mtk_r32(eth, MAC_TS_T3_SID2(mac->id));
+
+	if (sid[0] != sid[1]) {
+		pr_warn("%s invalid hwtstamp(%d, %d)!\n", __func__, sid[0], sid[1]);
+		return -EINVAL;
+	}
+
+	ts->tv_sec = dw[2] | ((u64)dw[3] << 32);
+	ts->tv_nsec = dw[1];
+
+	return 0;
+}
+
+static int mtk_ptp_hwtstamp_get_t4(struct mtk_mac *mac, struct timespec64 *ts)
+{
+	struct mtk_eth *eth = mac->hw;
+	u32 sid[2], dw[4];
+	int i;
+
+	sid[0] = mtk_r32(eth, MAC_TS_T4_SID1(mac->id));
+	for (i = 0; i < 4; i++)
+		dw[i] = mtk_r32(eth, MAC_TS_T4_DW(mac->id) + i * 4);
+	sid[1] = mtk_r32(eth, MAC_TS_T4_SID2(mac->id));
+
+	if (sid[0] != sid[1]) {
+		pr_warn("%s invalid hwtstamp(%d, %d)!\n", __func__, sid[0], sid[1]);
+		return -EINVAL;
+	}
+
+	ts->tv_sec = dw[2] | ((u64)dw[3] << 32);
+	ts->tv_nsec = dw[1];
+
+	return 0;
+}
+
+int mtk_ptp_hwtstamp_process_tx(struct net_device *dev, struct sk_buff *skb)
+{
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct skb_shared_hwtstamps shhwtstamps;
+	struct timespec64 ts;
+	unsigned int ptp_class, offset = 0;
+	int ret;
+	u8 *data, msgtype;
+
+	ptp_class = ptp_classify_raw(skb);
+	if (ptp_class == PTP_CLASS_NONE)
+		return 0;
+
+	if (ptp_class & PTP_CLASS_VLAN)
+		offset += VLAN_HLEN;
+
+	if ((ptp_class & PTP_CLASS_PMASK) == PTP_CLASS_L2)
+		offset += ETH_HLEN;
+	else
+		return 0;
+
+	data = skb_mac_header(skb);
+	msgtype = data[offset] & 0x0f;
+	switch (msgtype) {
+	case SYNC:
+	case PDELAY_REQ:
+		ret = mtk_ptp_hwtstamp_get_t1(mac, &ts);
+		break;
+	case DELAY_REQ:
+	case PDELAY_RESP:
+		ret = mtk_ptp_hwtstamp_get_t3(mac, &ts);
+		break;
+	default:
+		return 0;
+	}
+
+	if (ret)
+		return ret;
+
+	skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+
+	memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+	shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
+	skb_tstamp_tx(skb, &shhwtstamps);
+
+	return 0;
+}
+
+int mtk_ptp_hwtstamp_process_rx(struct net_device *dev, struct sk_buff *skb)
+{
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
+	struct timespec64 ts;
+	unsigned int ptp_class, offset = 0;
+	int ret;
+	u8 *data, msgtype;
+
+	ptp_class = ptp_classify_raw(skb);
+	if (ptp_class == PTP_CLASS_NONE)
+		return 0;
+
+	if (ptp_class & PTP_CLASS_VLAN)
+		offset += VLAN_HLEN;
+
+	if ((ptp_class & PTP_CLASS_PMASK) == PTP_CLASS_L2)
+		offset += ETH_HLEN;
+	else
+		return 0;
+
+	skb_reset_mac_header(skb);
+	data = skb_mac_header(skb);
+	msgtype = data[offset] & 0x0f;
+	switch (msgtype) {
+	case SYNC:
+	case PDELAY_REQ:
+		ret = mtk_ptp_hwtstamp_get_t2(mac, &ts);
+		break;
+	case DELAY_REQ:
+	case PDELAY_RESP:
+		ret = mtk_ptp_hwtstamp_get_t4(mac, &ts);
+		break;
+	default:
+		return 0;
+	}
+
+	if (ret)
+		return ret;
+
+	memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+	shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
+
+	return 0;
+}
+
+int mtk_ptp_hwtstamp_set_config(struct net_device *dev, struct ifreq *ifr)
+{
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct mtk_eth *eth = mac->hw;
+	struct hwtstamp_config cfg;
+
+	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_HWTSTAMP))
+		return -EOPNOTSUPP;
+
+	if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
+		return -EFAULT;
+
+	/* reserved for future extensions */
+	if (cfg.flags)
+		return -EINVAL;
+
+	if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON &&
+	    cfg.tx_type != HWTSTAMP_TX_ONESTEP_SYNC)
+		return -ERANGE;
+
+	switch (cfg.rx_filter) {
+	case HWTSTAMP_FILTER_NONE:
+		eth->rx_ts_enabled = 0;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+		eth->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V2_EVENT;
+		cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	eth->tx_ts_enabled = cfg.tx_type;
+
+	mtk_ptp_hwtstamp_enable(dev, eth->tx_ts_enabled);
+
+	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+}
+
+int mtk_ptp_hwtstamp_get_config(struct net_device *dev, struct ifreq *ifr)
+{
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct mtk_eth *eth = mac->hw;
+	struct hwtstamp_config cfg;
+
+	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_HWTSTAMP))
+		return -EOPNOTSUPP;
+
+	cfg.flags = 0;
+	cfg.tx_type = eth->tx_ts_enabled;
+	cfg.rx_filter = eth->rx_ts_enabled;
+
+	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+}
+
+static int mtk_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+	struct mtk_eth *eth = container_of(ptp, struct mtk_eth, ptp_info);
+	u64 base, adj;
+	u16 data16;
+	bool negative;
+
+	if (scaled_ppm) {
+		base = 0x4 << 16;
+		negative = diff_by_scaled_ppm(base, scaled_ppm, &adj);
+		data16 = (u16)adj;
+
+		if (negative)
+			mtk_w32(eth,
+				FIELD_PREP(CSR_TICK_NANOSECOND, 0x3) |
+				FIELD_PREP(CSR_TICK_SUB_NANOSECOND, 0xFFFF - data16),
+				MAC_TS_TICK_SUBSECOND);
+		else
+			mtk_w32(eth,
+				FIELD_PREP(CSR_TICK_NANOSECOND, 0x4) |
+				FIELD_PREP(CSR_TICK_SUB_NANOSECOND, data16),
+				MAC_TS_TICK_SUBSECOND);
+
+		// update tick configuration
+		mtk_m32(eth, CSR_TICK_UPDATE, CSR_TICK_UPDATE, MAC_TS_TICK_CTRL);
+		mtk_m32(eth, CSR_TICK_UPDATE, 0, MAC_TS_TICK_CTRL);
+	}
+
+	return 0;
+}
+
+static int mtk_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+	struct mtk_eth *eth = container_of(ptp, struct mtk_eth, ptp_info);
+	struct timespec64 ts = ns_to_timespec64(delta);
+
+	mtk_w32(eth, (ts.tv_nsec >> 0) & 0xFFFFFFFF, MAC_TS_SUBSECOND_FIELD1);
+	mtk_w32(eth, (ts.tv_sec >>  0) & 0xFFFFFFFF, MAC_TS_SECOND_FIELD0);
+	mtk_w32(eth, (ts.tv_sec >> 32) & 0x0000FFFF, MAC_TS_SECOND_FIELD1);
+
+	// adjust timestamp
+	mtk_m32(eth, CSR_TS_ADJUST, CSR_TS_ADJUST, MAC_TS_TIMESTAMP_CTRL);
+	mtk_m32(eth, CSR_TS_ADJUST, 0, MAC_TS_TIMESTAMP_CTRL);
+
+	return 0;
+}
+
+static int mtk_ptp_gettime64(struct ptp_clock_info *ptp,
+			     struct timespec64 *ts)
+{
+	struct mtk_eth *eth = container_of(ptp, struct mtk_eth, ptp_info);
+	unsigned long t_start = jiffies;
+	u32 val[4];
+	int i;
+
+	mtk_w32(eth, CPU_TRIG, MAC_TS_CPU_TRIG);
+
+	while (1) {
+		if (!(mtk_r32(eth, MAC_TS_CPU_TRIG) & CPU_TS_VALID))
+			break;
+		if (time_after(jiffies, t_start + jiffies_to_msecs(1000))) {
+			pr_warn("cpu trigger timeout!");
+			return -ETIMEDOUT;
+		}
+		cond_resched();
+	}
+
+	for (i = 0; i < 4; i++)
+		val[i] = mtk_r32(eth, MAC_TS_CPU_TS_DW(i));
+
+	ts->tv_sec = val[2] | ((u64)val[3] << 32);
+	ts->tv_nsec = val[1];
+
+	return 0;
+}
+
+static int mtk_ptp_settime64(struct ptp_clock_info *ptp,
+			     const struct timespec64 *ts)
+{
+	struct mtk_eth *eth = container_of(ptp, struct mtk_eth, ptp_info);
+
+	mtk_w32(eth, (ts->tv_nsec >> 0) & 0xFFFFFFFF, MAC_TS_SUBSECOND_FIELD1);
+	mtk_w32(eth, (ts->tv_sec >>  0) & 0xFFFFFFFF, MAC_TS_SECOND_FIELD0);
+	mtk_w32(eth, (ts->tv_sec >> 32) & 0x0000FFFF, MAC_TS_SECOND_FIELD1);
+
+	// update timestamp
+	mtk_m32(eth, CSR_TS_UPDATE, CSR_TS_UPDATE, MAC_TS_TIMESTAMP_CTRL);
+	mtk_m32(eth, CSR_TS_UPDATE, 0, MAC_TS_TIMESTAMP_CTRL);
+
+	return 0;
+}
+
+static int mtk_ptp_enable(struct ptp_clock_info *ptp,
+			  struct ptp_clock_request *request, int on)
+{
+	struct mtk_eth *eth = container_of(ptp, struct mtk_eth, ptp_info);
+
+	// enable rx T1/T3 timestamp mask
+	mtk_w32(eth, 0x00000077, MAC_TS_RSV);
+	// update tick configuration
+	mtk_m32(eth, CSR_TICK_UPDATE, CSR_TICK_UPDATE, MAC_TS_TICK_CTRL);
+	mtk_m32(eth, CSR_TICK_UPDATE, 0, MAC_TS_TICK_CTRL);
+	// enable tick
+	mtk_m32(eth, CSR_TICK_RUN, on, MAC_TS_TICK_CTRL);
+
+	return 0;
+}
+
+static const struct ptp_clock_info mtk_ptp_caps = {
+	.owner		= THIS_MODULE,
+	.name		= "mtk_ptp",
+	.max_adj	= 24999999,
+	.n_alarm	= 0,
+	.n_ext_ts	= 0,
+	.n_per_out	= 1,
+	.n_pins		= 0,
+	.pps		= 0,
+	.adjfine	= mtk_ptp_adjfine,
+	.adjtime	= mtk_ptp_adjtime,
+	.gettime64	= mtk_ptp_gettime64,
+	.settime64	= mtk_ptp_settime64,
+	.enable		= mtk_ptp_enable,
+};
+
+int mtk_ptp_clock_init(struct mtk_eth *eth)
+{
+	eth->ptp_info = mtk_ptp_caps;
+	eth->ptp_clock = ptp_clock_register(&eth->ptp_info,
+					    eth->dev);
+	if (IS_ERR(eth->ptp_clock)) {
+		eth->ptp_clock = NULL;
+		return -EINVAL;
+	}
+
+	mtk_ptp_enable(&eth->ptp_info, NULL, 1);
+
+	return 0;
+}
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
index 5ebeb97..e9998b9 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_reset.c
@@ -219,10 +219,189 @@
 	return ret;
 }
 
+u32 mtk_gdm_dbg_out(struct mtk_eth *eth, u32 reg, u32 mask, u32 index)
+{
+	mtk_m32(eth, mask, index, reg);
+
+	return mtk_r32(eth, reg + 0x4);
+}
+
+void mtk_gdm_pl_end_page_error(struct mtk_eth *eth, int id)
+{
+	enum mtk_gdm_dbg_index {
+		FS_PL_END_MISMATCH_LGC_INFO_LSB = 20,
+		FS_PL_END_MISMATCH_LGC_INFO_MSB,
+		FS_PL_END_MISMATCH_EXT_INFO_31_0,
+		FS_PL_END_MISMATCH_EXT_INFO_63_32,
+		FS_PL_END_MISMATCH_EXT_INFO_95_64,
+		FS_PL_END_MISMATCH_EXT_INFO_127_96,
+		FS_PL_END_MISMATCH_EXT_INFO_159_128,
+		FS_PL_END_MISMATCH_EXT_INFO_161_160,
+		FS_PL_END_MISMATCH_LINK_LIST_INDEX,
+	};
+
+	if (id < 0 || id > 2)
+		return;
+
+	pr_info("====================================================");
+	pr_info("GDM%d FS_PL_END_MISMATCH_LGC_INFO_LSB      = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_GDM_DBG_CTRL(id), GDM_DBG_IDX_MASK,
+				FIELD_PREP(GDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_LGC_INFO_LSB)));
+	pr_info("GDM%d FS_PL_END_MISMATCH_LGC_INFO_MSB      = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_GDM_DBG_CTRL(id), GDM_DBG_IDX_MASK,
+				FIELD_PREP(GDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_LGC_INFO_MSB)));
+	pr_info("GDM%d FS_PL_END_MISMATCH_EXT_INFO[31:0]    = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_GDM_DBG_CTRL(id), GDM_DBG_IDX_MASK,
+				FIELD_PREP(GDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_31_0)));
+	pr_info("GDM%d FS_PL_END_MISMATCH_EXT_INFO[63:32]   = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_GDM_DBG_CTRL(id), GDM_DBG_IDX_MASK,
+				FIELD_PREP(GDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_63_32)));
+	pr_info("GDM%d FS_PL_END_MISMATCH_EXT_INFO[95:64]   = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_GDM_DBG_CTRL(id), GDM_DBG_IDX_MASK,
+				FIELD_PREP(GDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_95_64)));
+	pr_info("GDM%d FS_PL_END_MISMATCH_EXT_INFO[127:96]  = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_GDM_DBG_CTRL(id), GDM_DBG_IDX_MASK,
+				FIELD_PREP(GDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_127_96)));
+	pr_info("GDM%d FS_PL_END_MISMATCH_EXT_INFO[159:128] = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_GDM_DBG_CTRL(id), GDM_DBG_IDX_MASK,
+				FIELD_PREP(GDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_159_128)));
+	pr_info("GDM%d FS_PL_END_MISMATCH_EXT_INFO[161:160] = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_GDM_DBG_CTRL(id), GDM_DBG_IDX_MASK,
+				FIELD_PREP(GDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_161_160)));
+	pr_info("GDM%d FS_PL_END_MISMATCH_LINK_LIST_INDEX = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_GDM_DBG_CTRL(id), GDM_DBG_IDX_MASK,
+				FIELD_PREP(GDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_LINK_LIST_INDEX)));
+	pr_info("====================================================");
+}
+
+void mtk_cdm_v2_pl_end_page_error(struct mtk_eth *eth, int id)
+{
+	enum mtk_gdm_dbg_index {
+		FS_PL_END_MISMATCH_LGC_INFO_LSB = 24,
+		FS_PL_END_MISMATCH_LGC_INFO_MSB,
+		FS_PL_END_MISMATCH_EXT_INFO_31_0,
+		FS_PL_END_MISMATCH_EXT_INFO_63_32,
+		FS_PL_END_MISMATCH_EXT_INFO_95_64,
+		FS_PL_END_MISMATCH_EXT_INFO_127_96,
+		FS_PL_END_MISMATCH_EXT_INFO_159_128,
+		FS_PL_END_MISMATCH_EXT_INFO_161_160,
+		FS_PL_END_MISMATCH_LINK_LIST_INDEX,
+	};
+
+	if (id < 0 || id > 2)
+		return;
+
+	pr_info("====================================================");
+	pr_info("CDM%d FS_PL_END_MISMATCH_LGC_INFO_LSB      = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_LGC_INFO_LSB)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_LGC_INFO_MSB      = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_LGC_INFO_MSB)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_EXT_INFO[31:0]    = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_31_0)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_EXT_INFO[63:32]   = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_63_32)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_EXT_INFO[95:64]   = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_95_64)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_EXT_INFO[127:96]  = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_127_96)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_EXT_INFO[159:128] = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_159_128)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_EXT_INFO[161:160] = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_161_160)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_LINK_LIST_INDEX = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_LINK_LIST_INDEX)));
+	pr_info("====================================================");
+}
+
+void mtk_cdm_v3_pl_end_page_error(struct mtk_eth *eth, int id)
+{
+	enum mtk_gdm_dbg_index {
+		FS_PL_END_MISMATCH_LGC_INFO_LSB = 27,
+		FS_PL_END_MISMATCH_LGC_INFO_MSB,
+		FS_PL_END_MISMATCH_EXT_INFO_31_0,
+		FS_PL_END_MISMATCH_EXT_INFO_63_32,
+		FS_PL_END_MISMATCH_EXT_INFO_95_64,
+		FS_PL_END_MISMATCH_EXT_INFO_127_96,
+		FS_PL_END_MISMATCH_EXT_INFO_159_128,
+		FS_PL_END_MISMATCH_EXT_INFO_161_160,
+		FS_PL_END_MISMATCH_LINK_LIST_INDEX,
+	};
+
+	if (id < 2 || id > 8)
+		return;
+
+	pr_info("====================================================");
+	pr_info("CDM%d FS_PL_END_MISMATCH_LGC_INFO_LSB      = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_LGC_INFO_LSB)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_LGC_INFO_MSB      = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_LGC_INFO_MSB)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_EXT_INFO[31:0]    = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_31_0)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_EXT_INFO[63:32]   = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_63_32)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_EXT_INFO[95:64]   = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_95_64)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_EXT_INFO[127:96]  = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_127_96)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_EXT_INFO[159:128] = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_159_128)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_EXT_INFO[161:160] = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_EXT_INFO_161_160)));
+	pr_info("CDM%d FS_PL_END_MISMATCH_LINK_LIST_INDEX = %08x\n", id + 1,
+		mtk_gdm_dbg_out(eth, FE_CDM_DBG_CTRL(id), CDM_DBG_IDX_MASK,
+				FIELD_PREP(CDM_DBG_IDX_MASK,
+					   FS_PL_END_MISMATCH_LINK_LIST_INDEX)));
+	pr_info("====================================================");
+}
+
 irqreturn_t mtk_handle_fe_irq(int irq, void *_eth)
 {
 	struct mtk_eth *eth = _eth;
 	u32 status = 0, val = 0;
+	int i;
 
 	status = mtk_r32(eth, MTK_FE_INT_STATUS);
 	pr_info("[%s] Trigger FE Misc ISR: 0x%x\n", __func__, status);
@@ -241,6 +420,66 @@
 	}
 	mtk_w32(eth, 0xFFFFFFFF, MTK_FE_INT_STATUS);
 
+	if (eth->soc->caps == MT7987_CAPS) {
+		bool pl_end_error = false;
+
+		/* handle GDM/CDM l3len_over_run and pl_end_error */
+		status = mtk_r32(eth, MTK_FE_PINFO_INT_STATUS);
+		for (i = 0; i < 3; i++) {
+			if (status & MTK_GDM_L3LEN_OVER_RUN(i))
+				pr_warn("[%s] Detect GDM%d L3 length over run !",
+					__func__, i + 1);
+			if (status & MTK_GDM_PL_END_ERR(i)) {
+				pr_warn("[%s] Detect GDM%d packet end page error !",
+					__func__, i + 1);
+				mtk_gdm_pl_end_page_error(eth, i);
+				pl_end_error = true;
+			}
+		}
+		for (i = 0; i < 9; i++) {
+			if (status & MTK_CDM_L3LEN_OVER_RUN(i))
+				pr_warn("[%s] Detect CDM%d L3 length over run !",
+					__func__, i + 1);
+			if (status & MTK_CDM_PL_END_ERR(i)) {
+				pr_warn("[%s] Detect CDM%d packet end page error !",
+					__func__, i + 1);
+				if (i >= 2)
+					mtk_cdm_v3_pl_end_page_error(eth, i);
+				else
+					mtk_cdm_v2_pl_end_page_error(eth, i);
+				pl_end_error = true;
+			}
+		}
+		mtk_w32(eth, status, MTK_FE_PINFO_INT_STATUS);
+
+		/* handle GDM/CDM page_num_mismatch and runt_pkt_error */
+		status = mtk_r32(eth, FE_CGDM_INT3);
+		for (i = 0; i < 3; i++) {
+			if (status & FE_GDM_PAGE_MISMATCH(i))
+				pr_warn("[%s] Detect GDM%d page number mismatch !",
+					__func__, i + 1);
+		}
+		for (i = 0; i < 9; i++) {
+			if (status & FE_CDM_PAGE_MISMATCH(i))
+				pr_warn("[%s] Detect CDM%d page number mismatch !",
+					__func__, i + 1);
+			if (status & FE_CDM_RUNT_PACKET(i))
+				pr_warn("[%s] Detect CDM%d runt packet error !",
+					__func__, i + 1);
+		}
+		mtk_w32(eth, status, FE_CGDM_INT3);
+
+		if (pl_end_error) {
+			if (!test_bit(MTK_RESETTING, &eth->state)) {
+				/* disable GDM and GDM packet end page error interrupt */
+				mtk_w32(eth, 0x0000ffff, MTK_FE_PINFO_INT_ENABLE);
+				/* trigger an SER to let PSE go back to work normally */
+				atomic_inc(&force);
+				schedule_work(&eth->pending_work);
+			}
+		}
+	}
+
 	return IRQ_HANDLED;
 }
 
@@ -272,11 +511,15 @@
 			"SGMII1", 0, 0x1a0);
 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
 		mtk_dump_reg(eth, "XGMAC0", 0x12000, 0x300);
-		mtk_dump_reg(eth, "XGMAC1", 0x13000, 0x300);
-		mtk_dump_regmap(eth->usxgmii->pcs[0].regmap,
-				"USXGMII0", 0x800, 0x500);
-		mtk_dump_regmap(eth->usxgmii->pcs[1].regmap,
-				"USXGMII1", 0x800, 0x500);
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC2_USXGMII)) {
+			mtk_dump_regmap(eth->usxgmii->pcs[1].regmap,
+					"USXGMII1", 0x800, 0x500);
+		}
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC3_USXGMII)) {
+			mtk_dump_reg(eth, "XGMAC1", 0x13000, 0x300);
+			mtk_dump_regmap(eth->usxgmii->pcs[0].regmap,
+					"USXGMII0", 0x800, 0x500);
+		}
 	}
 }
 
@@ -882,9 +1125,15 @@
 			mcr |= XMAC_MCR_TRX_DISABLE;
 			mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id));
 
-			sts = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
-			sts &= ~MTK_XGMAC_FORCE_LINK(mac->id);
-			mtk_w32(mac->hw, sts, MTK_XGMAC_STS(mac->id));
+			if (MTK_HAS_CAPS(eth->soc->caps, MTK_XGMAC_V2)) {
+				sts = mtk_r32(mac->hw, MTK_XMAC_STS_FRC(mac->id));
+				sts &= ~XMAC_FORCE_LINK;
+				mtk_w32(mac->hw, sts, MTK_XMAC_STS_FRC(mac->id));
+			} else {
+				sts = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
+				sts &= ~MTK_XGMAC_FORCE_LINK(mac->id);
+				mtk_w32(mac->hw, sts, MTK_XGMAC_STS(mac->id));
+			}
 		}
 	}
 }
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 1b9eabc..5626a2d 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -240,6 +240,7 @@
 		.fq_tail	= 0x4724,
 		.fq_count	= 0x4728,
 		.fq_blen	= 0x472c,
+		.fq_fast_cfg	= 0x4738,
 		.tx_sch_rate	= 0x4798,
 	},
 	.gdm1_cnt		= 0x1c00,
@@ -671,19 +672,32 @@
 	struct mtk_mac *mac = container_of(config, struct mtk_mac,
 					   phylink_config);
 	struct mtk_eth *eth = mac->hw;
+	bool has_xgmac[MTK_MAX_DEVS] = {0,
+					MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC2_2P5GPHY) ||
+					MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC2_2P5GPHY_V2) ||
+					MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC2_USXGMII),
+					MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC3_USXGMII)};
 	u32 val;
 
-	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) &&
-	    mac->id != MTK_GMAC1_ID) {
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) && has_xgmac[mac->id]) {
 		val = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id));
 		val &= 0xfffffff0;
 		val |= XMAC_MCR_TRX_DISABLE;
 		mtk_w32(mac->hw, val, MTK_XMAC_MCR(mac->id));
 
-		val = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
-		val |= MTK_XGMAC_FORCE_MODE(mac->id);
-		val &= ~MTK_XGMAC_FORCE_LINK(mac->id);
-		mtk_w32(mac->hw, val, MTK_XGMAC_STS(mac->id));
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_XGMAC_V2)) {
+			val = mtk_r32(mac->hw, MTK_XMAC_STS_FRC(mac->id));
+			val |= XMAC_FORCE_RX_FC_MODE;
+			val |= XMAC_FORCE_TX_FC_MODE;
+			val |= XMAC_FORCE_LINK_MODE;
+			val &= ~XMAC_FORCE_LINK;
+			mtk_w32(mac->hw, val, MTK_XMAC_STS_FRC(mac->id));
+		} else {
+			val = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
+			val |= MTK_XGMAC_FORCE_MODE(mac->id);
+			val &= ~MTK_XGMAC_FORCE_LINK(mac->id);
+			mtk_w32(mac->hw, val, MTK_XGMAC_STS(mac->id));
+		}
 	}
 
 	return 0;
@@ -855,7 +869,8 @@
 		mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id));
 		mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id));
 
-		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) &&
+		    MTK_HAS_CAPS(eth->soc->caps, MTK_ESW)) {
 			if (mac->id == MTK_GMAC1_ID)
 				mtk_setup_bridge_switch(eth);
 		}
@@ -1028,7 +1043,8 @@
 	struct mtk_eth *eth = mac->hw;
 	u32 port = 0;
 
-	if (!up && mac->id == MTK_GMAC2_ID &&
+	if (eth->soc->caps == MT7988_CAPS &&
+	    !up && mac->id == MTK_GMAC2_ID &&
 	    interface == PHY_INTERFACE_MODE_INTERNAL &&
 	    MTK_HAS_CAPS(eth->soc->caps, MTK_2P5GPHY)) {
 		void __iomem *base;
@@ -1070,6 +1086,7 @@
 {
 	struct mtk_mac *mac = container_of(config, struct mtk_mac,
 					   phylink_config);
+	struct mtk_eth *eth = mac->hw;
 	u32 mcr, sts;
 
 	mtk_pse_set_mac_port_link(mac, false, interface);
@@ -1083,9 +1100,15 @@
 		mcr |= XMAC_MCR_TRX_DISABLE;
 		mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id));
 
-		sts = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
-		sts &= ~MTK_XGMAC_FORCE_LINK(mac->id);
-		mtk_w32(mac->hw, sts, MTK_XGMAC_STS(mac->id));
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_XGMAC_V2)) {
+			sts = mtk_r32(mac->hw, MTK_XMAC_STS_FRC(mac->id));
+			sts &= ~XMAC_FORCE_LINK;
+			mtk_w32(mac->hw, sts, MTK_XMAC_STS_FRC(mac->id));
+		} else {
+			sts = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
+			sts &= ~MTK_XGMAC_FORCE_LINK(mac->id);
+			mtk_w32(mac->hw, sts, MTK_XGMAC_STS(mac->id));
+		}
 	}
 }
 
@@ -1177,6 +1200,7 @@
 {
 	struct mtk_mac *mac = container_of(config, struct mtk_mac,
 					   phylink_config);
+	struct mtk_eth *eth = mac->hw;
 	u32 mcr, mcr_cur, sts;
 
 	mac->speed = speed;
@@ -1234,23 +1258,41 @@
 		mdelay(20);
 		mtk_m32(mac->hw, XMAC_GLB_CNTCLR, 0x1, MTK_XMAC_CNT_CTRL(mac->id));
 
-		sts = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
-		sts |= MTK_XGMAC_FORCE_LINK(mac->id);
-		mtk_w32(mac->hw, sts, MTK_XGMAC_STS(mac->id));
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_XGMAC_V2)) {
+			sts = mtk_r32(mac->hw, MTK_XMAC_STS_FRC(mac->id));
+			sts &= ~(XMAC_FORCE_TX_FC | XMAC_FORCE_RX_FC);
+			/* Configure pause modes -
+			 * phylink will avoid these for half duplex
+			 */
+			if (tx_pause)
+				sts |= XMAC_FORCE_TX_FC;
+			if (rx_pause)
+				sts |= XMAC_FORCE_RX_FC;
+			sts |= XMAC_FORCE_LINK;
+			mtk_w32(mac->hw, sts, MTK_XMAC_STS_FRC(mac->id));
 
-		mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id));
+			mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id));
+			mcr &= ~(XMAC_MCR_TRX_DISABLE);
+			mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id));
+		} else {
+			sts = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
+			sts |= MTK_XGMAC_FORCE_LINK(mac->id);
+			mtk_w32(mac->hw, sts, MTK_XGMAC_STS(mac->id));
 
-		mcr &= ~(XMAC_MCR_FORCE_TX_FC |	XMAC_MCR_FORCE_RX_FC);
-		/* Configure pause modes -
-		 * phylink will avoid these for half duplex
-		 */
-		if (tx_pause)
-			mcr |= XMAC_MCR_FORCE_TX_FC;
-		if (rx_pause)
-			mcr |= XMAC_MCR_FORCE_RX_FC;
+			mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id));
 
-		mcr &= ~(XMAC_MCR_TRX_DISABLE);
-		mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id));
+			mcr &= ~(XMAC_MCR_FORCE_TX_FC |	XMAC_MCR_FORCE_RX_FC);
+			/* Configure pause modes -
+			 * phylink will avoid these for half duplex
+			 */
+			if (tx_pause)
+				mcr |= XMAC_MCR_FORCE_TX_FC;
+			if (rx_pause)
+				mcr |= XMAC_MCR_FORCE_RX_FC;
+
+			mcr &= ~(XMAC_MCR_TRX_DISABLE);
+			mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id));
+		}
 	}
 	mtk_pse_set_mac_port_link(mac, true, interface);
 }
@@ -2590,6 +2632,9 @@
 		skb->dev = netdev;
 		skb_put(skb, pktlen);
 
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_HWTSTAMP) && eth->rx_ts_enabled)
+			mtk_ptp_hwtstamp_process_rx(eth->netdev[mac], skb);
+
 		if ((MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)))
 			rxdcsum = &trxd.rxd3;
 		else
@@ -2767,6 +2812,11 @@
 		if (!skb)
 			break;
 
+		if (unlikely(MTK_HAS_CAPS(eth->soc->caps, MTK_HWTSTAMP) &&
+			     skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC &&
+			     skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
+			mtk_ptp_hwtstamp_process_tx(eth->netdev[mac], skb);
+
 		if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) {
 			mtk_poll_tx_done(eth, state, mac, skb);
 			budget--;
@@ -4089,6 +4139,11 @@
 		val = mtk_r32(eth, reg_map->qdma.glo_cfg);
 		if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ||
 		    MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
+			if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_4))
+				mtk_m32(eth, MTK_QDMA_FQ_FASTPATH_EN,
+					MTK_QDMA_FQ_FASTPATH_EN,
+					reg_map->qdma.fq_fast_cfg);
+
 			val &= ~(MTK_RESV_BUF_MASK | MTK_DMA_SIZE_MASK);
 			mtk_w32(eth,
 				val | MTK_TX_DMA_EN | MTK_RX_DMA_EN |
@@ -4291,6 +4346,9 @@
 				break;
 		}
 
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_HWTSTAMP))
+			mtk_ptp_clock_init(eth);
+
 		napi_enable(&eth->rx_napi[0].napi);
 		mtk_rx_irq_enable(eth, MTK_RX_DONE_INT(0));
 
@@ -4355,6 +4413,7 @@
 
 static void mtk_stop_dma(struct mtk_eth *eth, u32 glo_cfg)
 {
+	const struct mtk_reg_map *reg_map = eth->soc->reg_map;
 	u32 val;
 	int i;
 
@@ -4374,6 +4433,45 @@
 		}
 		break;
 	}
+
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA_V1_4) &&
+	    (glo_cfg == eth->soc->reg_map->qdma.glo_cfg)) {
+		spin_lock_bh(&eth->page_lock);
+		/* stop the dma fastpath agent */
+		mtk_m32(eth, MTK_QDMA_FQ_FASTPATH_EN, 0,
+			reg_map->qdma.fq_fast_cfg);
+		/* enable the dma tx engine */
+		mtk_m32(eth, MTK_TX_DMA_EN, MTK_TX_DMA_EN, glo_cfg);
+		/* enable the dma flush mode */
+		mtk_m32(eth, MTK_QDMA_FQ_FLUSH_MODE, MTK_QDMA_FQ_FLUSH_MODE,
+			reg_map->qdma.fq_fast_cfg);
+		spin_unlock_bh(&eth->page_lock);
+
+		/* wait for dma flush complete */
+		for (i = 0; i < 10; i++) {
+			val = mtk_r32(eth, reg_map->qdma.fq_fast_cfg);
+			if (val & MTK_QDMA_FQ_FLUSH_MODE) {
+				mdelay(20);
+				continue;
+			}
+			break;
+		}
+
+		spin_lock_bh(&eth->page_lock);
+		/* disable the dma tx engine */
+		mtk_m32(eth, MTK_TX_DMA_EN, 0, glo_cfg);
+		spin_unlock_bh(&eth->page_lock);
+
+		/* wait for dma tx stop */
+		for (i = 0; i < 10; i++) {
+			val = mtk_r32(eth, glo_cfg);
+			if (val & MTK_TX_DMA_BUSY) {
+				mdelay(20);
+				continue;
+			}
+			break;
+		}
+	}
 }
 
 static int mtk_stop(struct net_device *dev)
@@ -4439,6 +4537,9 @@
 
 	mtk_dma_free(eth);
 
+	if (MTK_HAS_CAPS(eth->soc->caps, MTK_HWTSTAMP))
+		ptp_clock_unregister(eth->ptp_clock);
+
 	return 0;
 }
 
@@ -4672,7 +4773,7 @@
 		if (eth->soc->caps != MT7988_CAPS || eth->hwver != MTK_HWID_V1)
 			mtk_w32(eth, PSE_DUMMY_WORK_GDM(1) |
 				PSE_DUMMY_WORK_GDM(2) |	PSE_DUMMY_WORK_GDM(3) |
-				DUMMY_PAGE_THR, PSE_DUMY_REQ);
+				DUMMY_PAGE_THR(eth->soc->caps), PSE_DUMY_REQ);
 
 		/* PSE should not drop port8 and port9 packets */
 		mtk_w32(eth, 0x00000300, PSE_NO_DROP_CFG);
@@ -4680,17 +4781,38 @@
 		/* PSE should drop p8 and p9 packets when WDMA Rx ring full*/
 		mtk_w32(eth, 0x00000300, PSE_PPE0_DROP);
 
-		/* PSE free buffer drop threshold */
-		mtk_w32(eth, 0x00600009, PSE_IQ_REV(8));
+		if (eth->soc->caps == MT7988_CAPS) {
+			/* PSE free buffer drop threshold */
+			mtk_w32(eth, 0x00600009, PSE_IQ_REV(8));
 
-		/* GDM and CDM Threshold */
-		mtk_w32(eth, 0x08000707, MTK_CDMW0_THRES);
-		mtk_w32(eth, 0x00000077, MTK_CDMW1_THRES);
+			/* GDM and CDM Threshold */
+			mtk_w32(eth, 0x08000707, MTK_CDMW0_THRES);
+			mtk_w32(eth, 0x00000077, MTK_CDMW1_THRES);
+		} else if (eth->soc->caps == MT7987_CAPS) {
+			/* enable PSE info error interrupt */
+			mtk_w32(eth, 0x00ffffff, MTK_FE_PINFO_INT_ENABLE);
+			/* enable CDMP l3_len_ov_drop */
+			mtk_m32(eth, MTK_CDMP_L3_LEN_OV_DROP,
+				MTK_CDMP_L3_LEN_OV_DROP, MTK_CDMP_IG_CTRL);
+			/* enable CDMQ l3_len_ov_drop */
+			mtk_m32(eth, MTK_CDMQ_L3_LEN_OV_DROP,
+				MTK_CDMQ_L3_LEN_OV_DROP, MTK_CDMQ_IG_CTRL);
+			/* enable CDMW0 l3_len_ov_drop */
+			mtk_m32(eth, MTK_CDMW0_L3_LEN_OV_DROP,
+				MTK_CDMW0_L3_LEN_OV_DROP, MTK_CDMW0_IG_CTRL);
+			/* disable GDM page_num_mismatch_det */
+			for (i = 0; i < 3; i++) {
+				mtk_m32(eth, GDM_PAGE_MISMATCH_DET, 0,
+					FE_GDM_DBG_CTRL(i));
+			}
+		}
 
-		/* Disable GDM1 RX CRC stripping */
-		val = mtk_r32(eth, MTK_GDMA_FWD_CFG(0));
-		val &= ~MTK_GDMA_STRP_CRC;
-		mtk_w32(eth, val, MTK_GDMA_FWD_CFG(0));
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_ESW)) {
+			/* Disable GDM1 RX CRC stripping */
+			val = mtk_r32(eth, MTK_GDMA_FWD_CFG(0));
+			val &= ~MTK_GDMA_STRP_CRC;
+			mtk_w32(eth, val, MTK_GDMA_FWD_CFG(0));
+		}
 
 		/* PSE GDM3 MIB counter has incorrect hw default values,
 		 * so the driver ought to read clear the values beforehand
@@ -4808,6 +4930,10 @@
 	struct mtk_mac *mac = netdev_priv(dev);
 
 	switch (cmd) {
+	case SIOCSHWTSTAMP:
+		return mtk_ptp_hwtstamp_set_config(dev, ifr);
+	case SIOCGHWTSTAMP:
+		return mtk_ptp_hwtstamp_get_config(dev, ifr);
 	case SIOCGMIIPHY:
 	case SIOCGMIIREG:
 	case SIOCSMIIREG:
@@ -5296,10 +5422,17 @@
 		pause->rx_pause = !!(val & MAC_MCR_FORCE_RX_FC);
 		pause->tx_pause = !!(val & MAC_MCR_FORCE_TX_FC);
 	} else if (mac->type == MTK_XGDM_TYPE) {
-		val = mtk_r32(eth, MTK_XMAC_MCR(mac->id));
+		if (MTK_HAS_CAPS(eth->soc->caps, MTK_XGMAC_V2)) {
+			val = mtk_r32(mac->hw, MTK_XMAC_STS_FRC(mac->id));
 
-		pause->rx_pause = !!(val & XMAC_MCR_FORCE_RX_FC);
-		pause->tx_pause = !!(val & XMAC_MCR_FORCE_TX_FC);
+			pause->rx_pause = !!(val & XMAC_FORCE_RX_FC);
+			pause->tx_pause = !!(val & XMAC_FORCE_TX_FC);
+		} else {
+			val = mtk_r32(eth, MTK_XMAC_MCR(mac->id));
+
+			pause->rx_pause = !!(val & XMAC_MCR_FORCE_RX_FC);
+			pause->tx_pause = !!(val & XMAC_MCR_FORCE_TX_FC);
+		}
 	}
 }
 
@@ -5310,6 +5443,28 @@
 	return phylink_ethtool_set_pauseparam(mac->phylink, pause);
 }
 
+static int mtk_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
+{
+	struct mtk_mac *mac = netdev_priv(dev);
+	struct mtk_eth *eth = mac->hw;
+
+	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_HWTSTAMP))
+		return -EOPNOTSUPP;
+
+	info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+				SOF_TIMESTAMPING_RX_HARDWARE |
+				SOF_TIMESTAMPING_RAW_HARDWARE;
+	info->phc_index = 0;
+	info->tx_types = (1 << HWTSTAMP_TX_OFF) |
+			 (1 << HWTSTAMP_TX_ON) |
+			 (1 << HWTSTAMP_TX_ONESTEP_SYNC);
+	info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
+			   (1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
+			   (1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ);
+
+	return 0;
+}
+
 static int mtk_get_eee(struct net_device *dev, struct ethtool_eee *eee)
 {
 	struct mtk_mac *mac = netdev_priv(dev);
@@ -5398,6 +5553,7 @@
 	.set_rxfh		= mtk_set_rxfh,
 	.get_pauseparam		= mtk_get_pauseparam,
 	.set_pauseparam		= mtk_set_pauseparam,
+	.get_ts_info		= mtk_get_ts_info,
 	.get_eee		= mtk_get_eee,
 	.set_eee		= mtk_set_eee,
 };
@@ -6332,6 +6488,27 @@
 	},
 };
 
+static const struct mtk_soc_data mt7987_data = {
+	.reg_map = &mt7988_reg_map,
+	.ana_rgc3 = 0x128,
+	.caps = MT7987_CAPS,
+	.hw_features = MTK_HW_FEATURES,
+	.required_clks = MT7988_CLKS_BITMAP,
+	.required_pctl = false,
+	.has_sram = false,
+	.rss_num = 4,
+	.txrx = {
+		.txd_size = sizeof(struct mtk_tx_dma_v2),
+		.rxd_size = sizeof(struct mtk_rx_dma_v2),
+		.tx_dma_size = MTK_DMA_SIZE(4K),
+		.rx_dma_size = MTK_DMA_SIZE(1K),
+		.fq_dma_size = MTK_DMA_SIZE(2K),
+		.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
+		.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+		.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
+	},
+};
+
 static const struct mtk_soc_data rt5350_data = {
 	.reg_map = &mt7628_reg_map,
 	.caps = MT7628_CAPS,
@@ -6360,6 +6537,7 @@
 	{ .compatible = "mediatek,mt7986-eth", .data = &mt7986_data},
 	{ .compatible = "mediatek,mt7981-eth", .data = &mt7981_data},
 	{ .compatible = "mediatek,mt7988-eth", .data = &mt7988_data},
+	{ .compatible = "mediatek,mt7987-eth", .data = &mt7987_data},
 	{ .compatible = "ralink,rt5350-eth", .data = &rt5350_data},
 	{},
 };
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 7805edf..02feeaf 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -15,6 +15,7 @@
 #include <linux/u64_stats_sync.h>
 #include <linux/refcount.h>
 #include <linux/phylink.h>
+#include <linux/ptp_clock_kernel.h>
 
 #define MTK_QDMA_PAGE_SIZE	2048
 #define	MTK_MAX_RX_LENGTH	1536
@@ -109,17 +110,31 @@
 /* Frame Engine Interrupt Grouping Register */
 #define MTK_FE_INT_GRP		0x20
 
+/* PSE Error Interrupt Status Register */
+#define MTK_FE_PINFO_INT_ENABLE	0x58
+#define MTK_FE_PINFO_INT_STATUS	0x5c
+#define MTK_CDM_PL_END_ERR(x)		BIT((x) + 15)
+#define MTK_GDM_PL_END_ERR(x)		BIT((x) + 12)
+#define MTK_CDM_L3LEN_OVER_RUN(x)	BIT((x) + 3)
+#define MTK_GDM_L3LEN_OVER_RUN(x)	BIT((x))
+
 /* Frame Engine LRO auto-learn table info */
 #define MTK_FE_ALT_CF8		0x300
 #define MTK_FE_ALT_SGL_CFC	0x304
 #define MTK_FE_ALT_SEQ_CFC	0x308
 
-/* CDMP Ingress Control Register */
+/* CDMW Ingress Control Register */
+#define MTK_CDMW0_IG_CTRL	0x1600
+#define MTK_CDMW0_L3_LEN_OV_DROP	BIT(1)
+
+/* CDMQ Ingress Control Register */
 #define MTK_CDMQ_IG_CTRL	0x1400
+#define MTK_CDMQ_L3_LEN_OV_DROP	BIT(2)
 #define MTK_CDMQ_STAG_EN	BIT(0)
 
 /* CDMP Ingress Control Register */
 #define MTK_CDMP_IG_CTRL	0x400
+#define MTK_CDMP_L3_LEN_OV_DROP	BIT(2)
 #define MTK_CDMP_STAG_EN	BIT(0)
 
 /* CDMP Exgress Control Register */
@@ -168,7 +183,7 @@
 /* PSE Last FreeQ Page Request Control */
 #define PSE_DUMY_REQ		0x10C
 #define PSE_DUMMY_WORK_GDM(x)	BIT(16 + (x))
-#define DUMMY_PAGE_THR		0x151
+#define DUMMY_PAGE_THR(x)	((x == MT7988_CAPS) ? 0x151 : 0x1)
 
 /* PSE Input Queue Reservation Register*/
 #define PSE_IQ_REV(x)		(0x140 + ((x - 1) * 0x4))
@@ -176,6 +191,19 @@
 /* PSE Output Queue Threshold Register*/
 #define PSE_OQ_TH(x)		(0x160 + ((x - 1) * 0x4))
 
+/* GDM and CDM Debug Control */
+#define FE_CDM_DBG_CTRL(x)	(0x38c + ((x) * 0x8))
+#define CDM_DBG_IDX_MASK	GENMASK(5, 0)
+
+#define FE_GDM_DBG_CTRL(x)	(0x3e0 + ((x) * 0x8))
+#define GDM_DBG_IDX_MASK	GENMASK(5, 1)
+#define GDM_PAGE_MISMATCH_DET	BIT(9)
+
+#define FE_CGDM_INT3		(0x3fc)
+#define FE_CDM_RUNT_PACKET(x)	BIT(22 - (x))
+#define FE_CDM_PAGE_MISMATCH(x)	BIT(11 - (x))
+#define FE_GDM_PAGE_MISMATCH(x)	BIT(2 - (x))
+
 /* GDM and CDM Threshold */
 #define MTK_GDM2_THRES		0x1530
 #define MTK_CDM2_THRES		0x1534
@@ -400,11 +428,17 @@
 #define MTK_QTX_SCH_LEAKY_BUCKET_SIZE	GENMASK(29, 28)
 #define MTK_QTX_SCH_MIN_RATE_EN		BIT(27)
 #define MTK_QTX_SCH_MIN_RATE_MAN	GENMASK(26, 20)
+#define MTK_QTX_SCH_MIN_RATE_MAN_V2	GENMASK(25, 19)
 #define MTK_QTX_SCH_MIN_RATE_EXP	GENMASK(19, 16)
+#define MTK_QTX_SCH_MIN_RATE_EXP_V2	GENMASK(18, 16)
 #define MTK_QTX_SCH_MAX_RATE_WEIGHT	GENMASK(15, 12)
+#define MTK_QTX_SCH_MAX_RATE_WEIGHT_V2	GENMASK(15, 10)
 #define MTK_QTX_SCH_MAX_RATE_EN		BIT(11)
+#define MTK_QTX_SCH_MAX_RATE_EN_V2	BIT(26)
 #define MTK_QTX_SCH_MAX_RATE_MAN	GENMASK(10, 4)
+#define MTK_QTX_SCH_MAX_RATE_MAN_V2	GENMASK(9, 3)
 #define MTK_QTX_SCH_MAX_RATE_EXP	GENMASK(3, 0)
+#define MTK_QTX_SCH_MAX_RATE_EXP_V2	GENMASK(2, 0)
 
 /* QDMA RX Base Pointer Register */
 #define MTK_QRX_BASE_PTR0	(QDMA_BASE + 0x100)
@@ -508,6 +542,11 @@
 /* QDMA Interrupt Mask Register */
 #define MTK_QDMA_HRED2		(QDMA_BASE + 0x244)
 
+/* QDMA TX Queue MIB Interface Register */
+#define MTK_QTX_MIB_IF		(QDMA_BASE + 0x2bc)
+#define MTK_MIB_ON_QTX_CFG	BIT(31)
+#define MTK_VQTX_MIB_EN		BIT(28)
+
 /* QDMA TX Forward CPU Pointer Register */
 #define MTK_QTX_CTX_PTR		(QDMA_BASE +0x300)
 
@@ -535,9 +574,17 @@
 /* QDMA FQ Free Page Buffer Length Register */
 #define MTK_QDMA_FQ_BLEN	(QDMA_BASE +0x32c)
 
+/* QDMA FQ Free Page Fast Path Configuration Register */
+#define MTK_QDMA_FQ_FLUSH_MODE		BIT(3)
+#define MTK_QDMA_FQ_FASTPATH_EN		BIT(0)
+
 /* QDMA TX Scheduler Rate Control Register */
 #define MTK_QDMA_TX_4SCH_BASE(x)	(QDMA_BASE + 0x398 + (((x) >> 1) * 0x4))
 #define MTK_QDMA_TX_SCH_MASK		GENMASK(15, 0)
+#define MTK_QDMA_TX_SCH_MAX_WFQ		BIT(15)
+#define MTK_QDMA_TX_SCH_RATE_EN		BIT(11)
+#define MTK_QDMA_TX_SCH_RATE_MAN	GENMASK(10, 4)
+#define MTK_QDMA_TX_SCH_RATE_EXP	GENMASK(3, 0)
 
 /* WDMA Registers */
 #define MTK_WDMA_CTX_PTR(x)	(WDMA_BASE(x) + 0x8)
@@ -786,6 +833,61 @@
 #define MAC_MCR_FORCE_LINK	BIT(0)
 #define MAC_MCR_FORCE_LINK_DOWN	(MAC_MCR_FORCE_MODE)
 
+/* MAC timestamp registers */
+#define MTK_MAC_TS_T1(x)	(0x13000 + ((x) * 0x80))
+#define MAC_TS_T1_SID1(x)	(MTK_MAC_TS_T1(x) + 0x0)
+#define MAC_TS_T1_DW(x)		(MTK_MAC_TS_T1(x) + 0x4)
+#define MAC_TS_T1_SID2(x)	(MTK_MAC_TS_T1(x) + 0x14)
+
+#define MTK_MAC_TS_T2(x)	(0x13020 + ((x) * 0x80))
+#define MAC_TS_T2_SID1(x)	(MTK_MAC_TS_T2(x) + 0x0)
+#define MAC_TS_T2_DW(x)		(MTK_MAC_TS_T2(x) + 0x4)
+#define MAC_TS_T2_SID2(x)	(MTK_MAC_TS_T2(x) + 0x14)
+
+#define MTK_MAC_TS_T3(x)	(0x13040 + ((x) * 0x80))
+#define MAC_TS_T3_SID1(x)	(MTK_MAC_TS_T3(x) + 0x0)
+#define MAC_TS_T3_DW(x)		(MTK_MAC_TS_T3(x) + 0x4)
+#define MAC_TS_T3_SID2(x)	(MTK_MAC_TS_T3(x) + 0x14)
+
+#define MTK_MAC_TS_T4(x)	(0x13060 + ((x) * 0x80))
+#define MAC_TS_T4_SID1(x)	(MTK_MAC_TS_T4(x) + 0x0)
+#define MAC_TS_T4_DW(x)		(MTK_MAC_TS_T4(x) + 0x4)
+#define MAC_TS_T4_SID2(x)	(MTK_MAC_TS_T4(x) + 0x14)
+
+#define MTK_MAC_TS_CTRL		(0x13800)
+#define MAC_TS_IRQ_EN		(MTK_MAC_TS_CTRL)
+#define MAC_TS_IRQ_STS		(MTK_MAC_TS_CTRL + 0x10)
+#define MAC_TS_IRQ_FRC		(MTK_MAC_TS_CTRL + 0x20)
+#define MAC_TS_MODE_CTRL	(MTK_MAC_TS_CTRL + 0x30)
+#define MAC_TS_CPU_TRIG		(MTK_MAC_TS_CTRL + 0x40)
+#define CPU_TS_VALID		BIT(1)
+#define CPU_TRIG		BIT(0)
+
+#define MAC_TS_CPU_TS_DW(x)	(MTK_MAC_TS_CTRL + 0x44 + ((x) * 4))
+#define MAC_TS_SUBSECOND_FIELD0	(MTK_MAC_TS_CTRL + 0x80)
+#define MAC_TS_SUBSECOND_FIELD1	(MTK_MAC_TS_CTRL + 0x84)
+#define MAC_TS_SECOND_FIELD0	(MTK_MAC_TS_CTRL + 0x88)
+#define MAC_TS_SECOND_FIELD1	(MTK_MAC_TS_CTRL + 0x8C)
+
+#define MAC_TS_TIMESTAMP_CTRL	(MTK_MAC_TS_CTRL + 0x90)
+#define CSR_TS_ADJUST		BIT(1)
+#define CSR_TS_UPDATE		BIT(0)
+
+#define MAC_TS_TICK_SUBSECOND	(MTK_MAC_TS_CTRL + 0xA0)
+#define CSR_TICK_NANOSECOND	GENMASK(31, 16)
+#define CSR_TICK_SUB_NANOSECOND	GENMASK(15, 0)
+
+#define MAC_TS_TICK_CTRL	(MTK_MAC_TS_CTRL + 0xA4)
+#define CSR_TICK_UPDATE		BIT(1)
+#define CSR_TICK_RUN		BIT(0)
+
+#define MAC_TS_MAC_CFG		(MTK_MAC_TS_CTRL + 0xA8)
+#define CSR_HW_TS_EN(x)		BIT(x)
+
+#define MAC_TS_RSV		(MTK_MAC_TS_CTRL + 0xB4)
+#define TS_T1_MASK		GENMASK(2, 0)
+#define TS_T3_MASK		GENMASK(6, 4)
+
 /* XFI Mac control registers */
 #define MTK_XMAC_BASE(x)	(0x12000 + ((x - 1) * 0x1000))
 #define MTK_XMAC_MCR(x)		(MTK_XMAC_BASE(x))
@@ -793,12 +895,34 @@
 #define XMAC_MCR_FORCE_TX_FC	BIT(5)
 #define XMAC_MCR_FORCE_RX_FC	BIT(4)
 
+/* XFI Mac status force registers */
+#define MTK_XMAC_STS(x)		(MTK_XMAC_MCR(x) + 0x14)
+
+/* XFI Mac status force registers */
+#define MTK_XMAC_STS_FRC(x)	(MTK_XMAC_MCR(x) + 0x18)
+#define XMAC_FORCE_RX_FC_MODE	BIT(13)
+#define XMAC_FORCE_TX_FC_MODE	BIT(12)
+#define XMAC_FORCE_SPD_MODE	BIT(10)
+#define XMAC_FORCE_LINK_MODE	BIT(8)
+#define XMAC_FORCE_RX_FC	BIT(5)
+#define XMAC_FORCE_TX_FC	BIT(4)
+#define XMAC_FORCE_SPD_MASK	GENMASK(3, 1)
+#define XMAC_FORCE_SPD_10	FIELD_PREP(XMAC_FORCE_SPD_MASK, 0)
+#define XMAC_FORCE_SPD_100	FIELD_PREP(XMAC_FORCE_SPD_MASK, 1)
+#define XMAC_FORCE_SPD_1000	FIELD_PREP(XMAC_FORCE_SPD_MASK, 2)
+#define XMAC_FORCE_SPD_10000	FIELD_PREP(XMAC_FORCE_SPD_MASK, 3)
+#define XMAC_FORCE_SPD_2500	FIELD_PREP(XMAC_FORCE_SPD_MASK, 4)
+#define XMAC_FORCE_SPD_5000	FIELD_PREP(XMAC_FORCE_SPD_MASK, 5)
+#define XMAC_FORCE_LINK		BIT(0)
+
 /* XFI Mac Rx configuration registers */
 #define MTK_XMAC_RX_CFG2(x)	(MTK_XMAC_MCR(x) + 0xd0)
 #define MTK_XMAC_MAX_RX_MASK	GENMASK(13, 0)
 
 /* XFI Mac logic reset registers */
-#define MTK_XMAC_LOGIC_RST(x)	(MTK_XMAC_BASE(x) + 0x10)
+#define MTK_XMAC_LOGIC_RST(x)	(MTK_XMAC_BASE(x) +				\
+				 (MTK_HAS_CAPS(eth->soc->caps, MTK_XGMAC_V2) ?	\
+				  0x820 : 0x10))
 #define XMAC_LOGIC_RST		BIT(0)
 
 /* XFI Mac count global control */
@@ -1405,6 +1529,14 @@
 	MTK_GDM_TYPE_MAX
 };
 
+enum mtk_qdma_version {
+	MTK_QDMA_V1_1 = 0,
+	MTK_QDMA_V1_2,
+	MTK_QDMA_V1_3,
+	MTK_QDMA_V1_4,
+	MTK_QDMA_MAX
+};
+
 enum mtk_hw_id {
 	MTK_HWID_V1 = 0,
 	MTK_HWID_V2,
@@ -1540,20 +1672,27 @@
 	MTK_PDMA_INT_BIT,
 	MTK_TRGMII_MT7621_CLK_BIT,
 	MTK_QDMA_BIT,
+	MTK_QDMA_V1_1_BIT,
+	MTK_QDMA_V1_2_BIT,
+	MTK_QDMA_V1_3_BIT,
+	MTK_QDMA_V1_4_BIT,
 	MTK_NETSYS_V1_BIT,
 	MTK_NETSYS_V2_BIT,
 	MTK_NETSYS_RX_V2_BIT,
 	MTK_NETSYS_V3_BIT,
+	MTK_HWTSTAMP_BIT,
 	MTK_SOC_MT7628_BIT,
 	MTK_RSTCTRL_PPE1_BIT,
 	MTK_RSTCTRL_PPE2_BIT,
+	MTK_XGMAC_BIT,
+	MTK_XGMAC_V2_BIT,
 	MTK_U3_COPHY_V2_BIT,
 	MTK_36BIT_DMA_BIT,
 
 	/* MUX BITS*/
 	MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT,
 	MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT,
-	MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT,
+	MTK_ETH_MUX_U3_GMAC23_TO_QPHY_BIT,
 	MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT,
 	MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT,
 	MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT,
@@ -1596,9 +1735,12 @@
 #define MTK_NETSYS_V2		BIT_ULL(MTK_NETSYS_V2_BIT)
 #define MTK_NETSYS_RX_V2	BIT(MTK_NETSYS_RX_V2_BIT)
 #define MTK_NETSYS_V3		BIT_ULL(MTK_NETSYS_V3_BIT)
+#define MTK_HWTSTAMP		BIT_ULL(MTK_HWTSTAMP_BIT)
 #define MTK_SOC_MT7628		BIT_ULL(MTK_SOC_MT7628_BIT)
 #define MTK_RSTCTRL_PPE1	BIT_ULL(MTK_RSTCTRL_PPE1_BIT)
 #define MTK_RSTCTRL_PPE2	BIT_ULL(MTK_RSTCTRL_PPE2_BIT)
+#define MTK_XGMAC		BIT_ULL(MTK_XGMAC_BIT)
+#define MTK_XGMAC_V2		BIT_ULL(MTK_XGMAC_V2_BIT)
 #define MTK_U3_COPHY_V2		BIT_ULL(MTK_U3_COPHY_V2_BIT)
 #define MTK_36BIT_DMA		BIT_ULL(MTK_36BIT_DMA_BIT)
 
@@ -1606,8 +1748,8 @@
 	BIT_ULL(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT)
 #define MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY	\
 	BIT_ULL(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT)
-#define MTK_ETH_MUX_U3_GMAC2_TO_QPHY		\
-	BIT_ULL(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT)
+#define MTK_ETH_MUX_U3_GMAC23_TO_QPHY		\
+	BIT_ULL(MTK_ETH_MUX_U3_GMAC23_TO_QPHY_BIT)
 #define MTK_ETH_MUX_GMAC2_TO_2P5GPHY		\
 	BIT_ULL(MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT)
 #define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII	\
@@ -1638,13 +1780,18 @@
 #define MTK_GMAC1_SGMII		(MTK_ETH_PATH_GMAC1_SGMII | MTK_SGMII)
 #define MTK_GMAC2_RGMII		(MTK_ETH_PATH_GMAC2_RGMII | MTK_RGMII)
 #define MTK_GMAC2_SGMII		(MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII)
-#define MTK_GMAC2_2P5GPHY	(MTK_ETH_PATH_GMAC2_2P5GPHY | MTK_2P5GPHY)
+#define MTK_GMAC2_2P5GPHY	(MTK_ETH_PATH_GMAC2_2P5GPHY | MTK_2P5GPHY | MTK_XGMAC)
+#define MTK_GMAC2_2P5GPHY_V2	(MTK_ETH_PATH_GMAC2_2P5GPHY | MTK_2P5GPHY | MTK_XGMAC_V2)
 #define MTK_GMAC2_GEPHY		(MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY)
 #define MTK_GMAC3_SGMII		(MTK_ETH_PATH_GMAC3_SGMII | MTK_SGMII)
 #define MTK_GDM1_ESW		(MTK_ETH_PATH_GDM1_ESW | MTK_ESW)
-#define MTK_GMAC1_USXGMII	(MTK_ETH_PATH_GMAC1_USXGMII | MTK_USXGMII)
-#define MTK_GMAC2_USXGMII	(MTK_ETH_PATH_GMAC2_USXGMII | MTK_USXGMII)
-#define MTK_GMAC3_USXGMII	(MTK_ETH_PATH_GMAC3_USXGMII | MTK_USXGMII)
+#define MTK_GMAC1_USXGMII	(MTK_ETH_PATH_GMAC1_USXGMII | MTK_USXGMII | MTK_XGMAC)
+#define MTK_GMAC2_USXGMII	(MTK_ETH_PATH_GMAC2_USXGMII | MTK_USXGMII | MTK_XGMAC)
+#define MTK_GMAC3_USXGMII	(MTK_ETH_PATH_GMAC3_USXGMII | MTK_USXGMII | MTK_XGMAC)
+#define MTK_QDMA_V1_1		(BIT_ULL(MTK_QDMA_V1_1_BIT) | MTK_QDMA)
+#define MTK_QDMA_V1_2		(BIT_ULL(MTK_QDMA_V1_2_BIT) | MTK_QDMA)
+#define MTK_QDMA_V1_3		(BIT_ULL(MTK_QDMA_V1_3_BIT) | MTK_QDMA)
+#define MTK_QDMA_V1_4		(BIT_ULL(MTK_QDMA_V1_4_BIT) | MTK_QDMA)
 
 /* MUXes present on SoCs */
 /* 0: GDM1 -> GMAC1, 1: GDM1 -> ESW */
@@ -1655,8 +1802,8 @@
 	(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY | MTK_MUX | MTK_INFRA)
 
 /* 0: U3 -> QPHY, 1: GMAC2 -> QPHY */
-#define MTK_MUX_U3_GMAC2_TO_QPHY        \
-	(MTK_ETH_MUX_U3_GMAC2_TO_QPHY | MTK_MUX | MTK_INFRA)
+#define MTK_MUX_U3_GMAC23_TO_QPHY        \
+	(MTK_ETH_MUX_U3_GMAC23_TO_QPHY | MTK_MUX | MTK_INFRA)
 
 /* 2: GMAC1 -> SGMII, 3: GMAC2 -> SGMII */
 #define MTK_MUX_GMAC1_GMAC2_TO_SGMII_RGMII      \
@@ -1681,41 +1828,48 @@
 
 #define MT7621_CAPS  (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | \
 		      MTK_GMAC2_RGMII | MTK_SHARED_INT | \
-		      MTK_TRGMII_MT7621_CLK | MTK_QDMA | MTK_NETSYS_V1)
+		      MTK_TRGMII_MT7621_CLK | MTK_QDMA_V1_1 | MTK_NETSYS_V1)
 
 #define MT7622_CAPS  (MTK_GMAC1_RGMII | MTK_GMAC1_SGMII | MTK_GMAC2_RGMII | \
 		      MTK_GMAC2_SGMII | MTK_GDM1_ESW | \
 		      MTK_MUX_GDM1_TO_GMAC1_ESW | MTK_NETSYS_V1 | \
-		      MTK_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_QDMA)
+		      MTK_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_QDMA_V1_1)
 
 #define MT7623_CAPS  (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | MTK_GMAC2_RGMII | \
-		      MTK_QDMA | MTK_NETSYS_V1)
+		      MTK_QDMA_V1_1 | MTK_NETSYS_V1)
 
 #define MT7628_CAPS  (MTK_SHARED_INT | MTK_SOC_MT7628 | MTK_NETSYS_V1)
 
 #define MT7629_CAPS  (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \
 		      MTK_GDM1_ESW | MTK_MUX_GDM1_TO_GMAC1_ESW | \
 		      MTK_MUX_GMAC2_GMAC0_TO_GEPHY | \
-		      MTK_MUX_U3_GMAC2_TO_QPHY | MTK_NETSYS_V1 | \
-		      MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA)
+		      MTK_MUX_U3_GMAC23_TO_QPHY | MTK_NETSYS_V1 | \
+		      MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA_V1_1)
 
 #define MT7986_CAPS   (MTK_PDMA_INT | MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \
-                       MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
+		       MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA_V1_2 | \
 		       MTK_NETSYS_V2 | MTK_RSS)
 
 #define MT7981_CAPS   (MTK_PDMA_INT | MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \
-		       MTK_GMAC2_GEPHY | MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
-		       MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \
+		       MTK_GMAC2_GEPHY | MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA_V1_2 | \
+		       MTK_MUX_U3_GMAC23_TO_QPHY | MTK_U3_COPHY_V2 | \
 		       MTK_NETSYS_V2 | MTK_RSS)
 
 #define MT7988_CAPS   (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC3_SGMII | \
-		       MTK_PDMA_INT | MTK_MUX_GMAC123_TO_GEPHY_SGMII | MTK_QDMA | \
+		       MTK_PDMA_INT | MTK_MUX_GMAC123_TO_GEPHY_SGMII | MTK_QDMA_V1_3 | \
 		       MTK_NETSYS_V3 | MTK_RSTCTRL_PPE1 | MTK_RSTCTRL_PPE2 | \
-		       MTK_GMAC1_USXGMII | MTK_GMAC2_USXGMII | \
+		       MTK_ESW | MTK_GMAC1_USXGMII | MTK_GMAC2_USXGMII | \
 		       MTK_GMAC3_USXGMII | MTK_MUX_GMAC123_TO_USXGMII | \
 		       MTK_GMAC2_2P5GPHY | MTK_MUX_GMAC2_TO_2P5GPHY | MTK_RSS | \
 		       MTK_HWLRO | MTK_NETSYS_RX_V2 | MTK_36BIT_DMA)
 
+#define MT7987_CAPS   (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC3_SGMII |\
+		       MTK_PDMA_INT | MTK_MUX_GMAC123_TO_GEPHY_SGMII | MTK_QDMA_V1_4 | \
+		       MTK_MUX_U3_GMAC23_TO_QPHY | MTK_U3_COPHY_V2 | \
+		       MTK_GMAC2_2P5GPHY_V2 | MTK_MUX_GMAC2_TO_2P5GPHY | MTK_RSS | \
+		       MTK_NETSYS_V3 | MTK_RSTCTRL_PPE1 | \
+		       MTK_NETSYS_RX_V2 | MTK_36BIT_DMA | MTK_HWTSTAMP)
+
 struct mtk_tx_dma_desc_info {
 	dma_addr_t	addr;
 	u32		size;
@@ -1779,6 +1933,7 @@
 		u32	fq_tail;	/* fq tail pointer */
 		u32	fq_count;	/* fq free page count */
 		u32	fq_blen;	/* fq free page buffer length */
+		u32	fq_fast_cfg;	/* fq free page fast path configuration */
 		u32	tx_sch_rate;	/* tx scheduler rate control
 					   registers */
 	} qdma;
@@ -2087,6 +2242,11 @@
 	struct work_struct		pending_work;
 	unsigned long			state;
 
+	struct ptp_clock_info		ptp_info;
+	struct ptp_clock		*ptp_clock;
+	int				tx_ts_enabled;
+	int				rx_ts_enabled;
+
 	const struct mtk_soc_data	*soc;
 
 	struct {
@@ -2190,4 +2350,10 @@
 
 void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
 u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index);
+
+int mtk_ptp_hwtstamp_process_tx(struct net_device *dev, struct sk_buff *skb);
+int mtk_ptp_hwtstamp_process_rx(struct net_device *dev, struct sk_buff *skb);
+int mtk_ptp_hwtstamp_set_config(struct net_device *dev, struct ifreq *ifr);
+int mtk_ptp_hwtstamp_get_config(struct net_device *dev, struct ifreq *ifr);
+int mtk_ptp_clock_init(struct mtk_eth *eth);
 #endif /* MTK_ETH_H */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
index e9508eb..40d2aec 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/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/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
index 534fc03..422c83a 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
@@ -112,27 +112,6 @@
 #define GDMA2_FWD_CFG 0x1500
 #define GDMA3_FWD_CFG 0x540
 
-/* QDMA Tx queue configuration */
-#define QTX_CFG(x)			(QDMA_BASE + ((x) * 0x10))
-#define QTX_CFG_HW_RESV_CNT_OFFSET	(8)
-#define QTX_CFG_SW_RESV_CNT_OFFSET	(0)
-
-#define QTX_SCH(x)			(QDMA_BASE + 0x4 + ((x) * 0x10))
-#define QTX_SCH_MIN_RATE_EN		BIT(27)
-#define QTX_SCH_MAX_RATE_EN		BIT(11)
-#define QTX_SCH_MIN_RATE_MAN_OFFSET	(20)
-#define QTX_SCH_MIN_RATE_EXP_OFFSET	(16)
-#define QTX_SCH_MAX_RATE_WGHT_OFFSET	(12)
-#define QTX_SCH_MAX_RATE_MAN_OFFSET	(4)
-#define QTX_SCH_MAX_RATE_EXP_OFFSET	(0)
-
-/* QDMA Tx scheduler configuration */
-#define QDMA_PAGE			(QDMA_BASE + 0x1f0)
-#define QDMA_TX_2SCH_BASE		(QDMA_BASE + 0x214)
-#define QTX_MIB_IF			(QDMA_BASE + 0x2bc)
-#define QDMA_TX_4SCH_BASE(x)		(QDMA_BASE + 0x398 + (((x) >> 1) * 0x4))
-#define QDMA_TX_SCH_WFQ_EN		BIT(15)
-
 /*--------------------------------------------------------------------------*/
 /* Register Mask*/
 /*--------------------------------------------------------------------------*/
@@ -213,13 +192,6 @@
 #define GDM_ALL_FRC_MASK                                                      \
 	(GDM_UFRC_MASK | GDM_BFRC_MASK | GDM_MFRC_MASK | GDM_OFRC_MASK)
 
-/*QDMA_PAGE mask*/
-#define QTX_CFG_PAGE (0xf << 0) /* RW */
-
-/*QTX_MIB_IF mask*/
-#define MIB_ON_QTX_CFG (0x1 << 31) /* RW */
-#define VQTX_MIB_EN (0x1 << 28) /* RW */
-
 /* PPE Side Band FIFO Debug Mask */
 #define SB_MED_FULL_DRP_EN (0x1 << 11)
 
@@ -365,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;
@@ -397,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)
@@ -466,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)
@@ -535,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)
@@ -607,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)
@@ -672,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)
@@ -745,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)
@@ -807,7 +821,7 @@
 	};
 
 	u16 vlan1;
-	u16 etype;
+	u16 sp_tag;
 	u32 dmac_hi;
 	u16 vlan2;
 	u16 dmac_lo;
@@ -843,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;
@@ -983,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,
@@ -1031,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)
@@ -1040,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)
@@ -1179,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))
@@ -1238,9 +1259,6 @@
 #define IPV6_SNAT	0
 #define IPV6_DNAT	1
 
-/*QDMA_PAGE value*/
-#define NUM_OF_Q_PER_PAGE 16
-
 /*IPv6 Header*/
 #ifndef NEXTHDR_IPIP
 #define NEXTHDR_IPIP 4
@@ -1268,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;
@@ -1278,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
@@ -1331,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);
@@ -1341,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/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
index 29ea69b..20141da 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_debugfs.c
@@ -26,6 +26,7 @@
 #include "hnat.h"
 #include "nf_hnat_mtk.h"
 #include "../mtk_eth_soc.h"
+#include "../mtk_eth_dbg.h"
 
 int dbg_entry_state = BIND;
 typedef int (*debugfs_write_func)(int par1);
@@ -38,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",
 };
@@ -370,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)
@@ -587,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
 	}
 
@@ -602,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,
@@ -627,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);
@@ -640,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,
@@ -1184,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,
@@ -1210,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,
@@ -1238,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)) {
@@ -1266,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)) {
@@ -1295,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,
@@ -1303,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)
@@ -1331,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,
@@ -1347,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,
@@ -1370,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,
@@ -1400,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,
@@ -1412,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,
@@ -1431,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);
@@ -1458,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)
@@ -1490,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,
@@ -1500,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
@@ -1896,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
 	}
 }
@@ -2232,297 +2323,6 @@
 	.release = single_release,
 };
 
-static ssize_t hnat_sched_show(struct file *file, char __user *user_buf,
-			       size_t count, loff_t *ppos)
-{
-	long id = (long)file->private_data;
-	struct mtk_hnat *h = hnat_priv;
-	u32 qdma_tx_sch;
-	int enable;
-	int scheduling;
-	int max_rate;
-	char *buf;
-	unsigned int len = 0, buf_len = 1500;
-	ssize_t ret_cnt;
-	int scheduler, i;
-	u32 sch_reg;
-
-	buf = kzalloc(buf_len, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	if (hnat_priv->data->num_of_sch == 4)
-		qdma_tx_sch = readl(h->fe_base + QDMA_TX_4SCH_BASE(id));
-	else
-		qdma_tx_sch = readl(h->fe_base + QDMA_TX_2SCH_BASE);
-
-	if (id & 0x1)
-		qdma_tx_sch >>= 16;
-	qdma_tx_sch &= MTK_QDMA_TX_SCH_MASK;
-	enable = !!(qdma_tx_sch & BIT(11));
-	scheduling = !!(qdma_tx_sch & BIT(15));
-	max_rate = ((qdma_tx_sch >> 4) & 0x7f);
-	qdma_tx_sch &= 0xf;
-	while (qdma_tx_sch--)
-		max_rate *= 10;
-
-	len += scnprintf(buf + len, buf_len - len,
-			 "EN\tScheduling\tMAX\tQueue#\n%d\t%s%16d\t", enable,
-			 (scheduling == 1) ? "WRR" : "SP", max_rate);
-
-	for (i = 0; i < MTK_QDMA_TX_NUM; i++) {
-		cr_set_field(h->fe_base + QDMA_PAGE, QTX_CFG_PAGE,
-			     (i / NUM_OF_Q_PER_PAGE));
-		sch_reg = readl(h->fe_base + QTX_SCH(i % NUM_OF_Q_PER_PAGE));
-		if (hnat_priv->data->num_of_sch == 4)
-			scheduler = (sch_reg >> 30) & 0x3;
-		else
-			scheduler = !!(sch_reg & BIT(31));
-		if (id == scheduler)
-			len += scnprintf(buf + len, buf_len - len, "%d  ", i);
-	}
-
-	len += scnprintf(buf + len, buf_len - len, "\n");
-	if (len > buf_len)
-		len = buf_len;
-
-	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-
-	kfree(buf);
-	return ret_cnt;
-}
-
-static ssize_t hnat_sched_write(struct file *file, const char __user *buf,
-				size_t length, loff_t *offset)
-{
-	long id = (long)file->private_data;
-	struct mtk_hnat *h = hnat_priv;
-	char line[64] = {0};
-	int enable, rate, exp = 0, shift = 0;
-	char scheduling[32];
-	size_t size;
-	u32 qdma_tx_sch;
-	u32 val = 0;
-
-	if (length >= sizeof(line))
-		return -EINVAL;
-
-	if (copy_from_user(line, buf, length))
-		return -EFAULT;
-
-	if (sscanf(line, "%1d %3s %9d", &enable, scheduling, &rate) != 3)
-		return -EFAULT;
-
-#if defined(CONFIG_MEDIATEK_NETSYS_V3)
-	if (rate > 10000000 || rate < 0)
-#else
-	if (rate > 1000000 || rate < 0)
-#endif
-		return -EINVAL;
-
-	while (rate > 127) {
-		rate /= 10;
-		exp++;
-	}
-
-	line[length] = '\0';
-
-	if (enable)
-		val |= BIT(11);
-	if (strcmp(scheduling, "sp") != 0)
-		val |= BIT(15);
-	val |= (rate & 0x7f) << 4;
-	val |= exp & 0xf;
-	if (id & 0x1)
-		shift = 16;
-
-	if (hnat_priv->data->num_of_sch == 4)
-		qdma_tx_sch = readl(h->fe_base + QDMA_TX_4SCH_BASE(id));
-	else
-		qdma_tx_sch = readl(h->fe_base + QDMA_TX_2SCH_BASE);
-
-	qdma_tx_sch &= ~(MTK_QDMA_TX_SCH_MASK << shift);
-	qdma_tx_sch |= val << shift;
-	if (hnat_priv->data->num_of_sch == 4)
-		writel(qdma_tx_sch, h->fe_base + QDMA_TX_4SCH_BASE(id));
-	else
-		writel(qdma_tx_sch, h->fe_base + QDMA_TX_2SCH_BASE);
-
-	size = strlen(line);
-	*offset += size;
-
-	return length;
-}
-
-static const struct file_operations hnat_sched_fops = {
-	.open = simple_open,
-	.read = hnat_sched_show,
-	.write = hnat_sched_write,
-	.llseek = default_llseek,
-};
-
-static ssize_t hnat_queue_show(struct file *file, char __user *user_buf,
-			       size_t count, loff_t *ppos)
-{
-	struct mtk_hnat *h = hnat_priv;
-	long id = (long)file->private_data;
-	u32 qtx_sch;
-	u32 qtx_cfg;
-	int scheduler;
-	int min_rate_en;
-	int min_rate;
-	int min_rate_exp;
-	int max_rate_en;
-	int max_weight;
-	int max_rate;
-	int max_rate_exp;
-	char *buf;
-	unsigned int len = 0, buf_len = 1500;
-	ssize_t ret_cnt;
-
-	buf = kzalloc(buf_len, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	cr_set_field(h->fe_base + QDMA_PAGE, QTX_CFG_PAGE, (id / NUM_OF_Q_PER_PAGE));
-	qtx_cfg = readl(h->fe_base + QTX_CFG(id % NUM_OF_Q_PER_PAGE));
-	qtx_sch = readl(h->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE));
-	if (hnat_priv->data->num_of_sch == 4)
-		scheduler = (qtx_sch >> 30) & 0x3;
-	else
-		scheduler = !!(qtx_sch & BIT(31));
-	min_rate_en = !!(qtx_sch & BIT(27));
-	min_rate = (qtx_sch >> 20) & 0x7f;
-	min_rate_exp = (qtx_sch >> 16) & 0xf;
-	max_rate_en = !!(qtx_sch & BIT(11));
-	max_weight = (qtx_sch >> 12) & 0xf;
-	max_rate = (qtx_sch >> 4) & 0x7f;
-	max_rate_exp = qtx_sch & 0xf;
-	while (min_rate_exp--)
-		min_rate *= 10;
-
-	while (max_rate_exp--)
-		max_rate *= 10;
-
-	len += scnprintf(buf + len, buf_len - len,
-			 "scheduler: %d\nhw resv: %d\nsw resv: %d\n", scheduler,
-			 (qtx_cfg >> 8) & 0xff, qtx_cfg & 0xff);
-
-	if (hnat_priv->data->version != MTK_HNAT_V1_1) {
-		/* Switch to debug mode */
-		cr_set_field(h->fe_base + QTX_MIB_IF, MIB_ON_QTX_CFG, 1);
-		cr_set_field(h->fe_base + QTX_MIB_IF, VQTX_MIB_EN, 1);
-		qtx_cfg = readl(h->fe_base + QTX_CFG(id % NUM_OF_Q_PER_PAGE));
-		qtx_sch = readl(h->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE));
-		len += scnprintf(buf + len, buf_len - len,
-				 "packet count: %u\n", qtx_cfg);
-		len += scnprintf(buf + len, buf_len - len,
-				 "packet drop: %u\n\n", qtx_sch);
-
-		/* Recover to normal mode */
-		cr_set_field(hnat_priv->fe_base + QTX_MIB_IF,
-			     MIB_ON_QTX_CFG, 0);
-		cr_set_field(hnat_priv->fe_base + QTX_MIB_IF, VQTX_MIB_EN, 0);
-	}
-
-	len += scnprintf(buf + len, buf_len - len,
-			 "      EN     RATE     WEIGHT\n");
-	len += scnprintf(buf + len, buf_len - len,
-			 "----------------------------\n");
-	len += scnprintf(buf + len, buf_len - len,
-			 "max%5d%9d%9d\n", max_rate_en, max_rate, max_weight);
-	len += scnprintf(buf + len, buf_len - len,
-			 "min%5d%9d        -\n", min_rate_en, min_rate);
-
-	if (len > buf_len)
-		len = buf_len;
-
-	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-
-	kfree(buf);
-	return ret_cnt;
-}
-
-static ssize_t hnat_queue_write(struct file *file, const char __user *buf,
-				size_t length, loff_t *offset)
-{
-	long id = (long)file->private_data;
-	struct mtk_hnat *h = hnat_priv;
-	char line[64] = {0};
-	int max_enable, max_rate, max_exp = 0;
-	int min_enable, min_rate, min_exp = 0;
-	int weight;
-	int resv;
-	int scheduler;
-	size_t size;
-	u32 qtx_sch = 0;
-
-	cr_set_field(h->fe_base + QDMA_PAGE, QTX_CFG_PAGE, (id / NUM_OF_Q_PER_PAGE));
-	if (length >= sizeof(line))
-		return -EINVAL;
-
-	if (copy_from_user(line, buf, length))
-		return -EFAULT;
-
-	if (sscanf(line, "%d %d %d %d %d %d %d", &scheduler, &min_enable, &min_rate,
-		   &max_enable, &max_rate, &weight, &resv) != 7)
-		return -EFAULT;
-
-	line[length] = '\0';
-
-#if defined(CONFIG_MEDIATEK_NETSYS_V3)
-	if (max_rate > 10000000 || max_rate < 0 ||
-	    min_rate > 10000000 || min_rate < 0)
-#else
-	if (max_rate > 1000000 || max_rate < 0 ||
-	    min_rate > 1000000 || min_rate < 0)
-#endif
-		return -EINVAL;
-
-	while (max_rate > 127) {
-		max_rate /= 10;
-		max_exp++;
-	}
-
-	while (min_rate > 127) {
-		min_rate /= 10;
-		min_exp++;
-	}
-
-	if (hnat_priv->data->num_of_sch == 4)
-		qtx_sch |= (scheduler & 0x3) << 30;
-	else
-		qtx_sch |= (scheduler & 0x1) << 31;
-	if (min_enable)
-		qtx_sch |= BIT(27);
-	qtx_sch |= (min_rate & 0x7f) << 20;
-	qtx_sch |= (min_exp & 0xf) << 16;
-	if (max_enable)
-		qtx_sch |= BIT(11);
-	qtx_sch |= (weight & 0xf) << 12;
-	qtx_sch |= (max_rate & 0x7f) << 4;
-	qtx_sch |= max_exp & 0xf;
-	writel(qtx_sch, h->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE));
-
-	resv &= 0xff;
-	qtx_sch = readl(h->fe_base + QTX_CFG(id % NUM_OF_Q_PER_PAGE));
-	qtx_sch &= 0xffff0000;
-	qtx_sch |= (resv << 8) | resv;
-	writel(qtx_sch, h->fe_base + QTX_CFG(id % NUM_OF_Q_PER_PAGE));
-
-	size = strlen(line);
-	*offset += size;
-
-	return length;
-}
-
-static const struct file_operations hnat_queue_fops = {
-	.open = simple_open,
-	.read = hnat_queue_show,
-	.write = hnat_queue_write,
-	.llseek = default_llseek,
-};
-
 static ssize_t hnat_ppd_if_write(struct file *file, const char __user *buffer,
 				 size_t count, loff_t *data)
 {
@@ -2654,7 +2454,6 @@
 {
 	char buf[8] = {0};
 	int len = count;
-	u32 id;
 
 	if ((len > 8) || copy_from_user(buf, buffer, len))
 		return -EFAULT;
@@ -2663,18 +2462,14 @@
 		pr_info("hook is going to be enabled !\n");
 		hnat_enable_hook();
 
-		if (IS_PPPQ_MODE) {
-			for (id = 0; id < MAX_PPPQ_PORT_NUM; id++)
-				hnat_qos_shaper_ebl(id, 1);
-		}
+		if (IS_PPPQ_MODE)
+			qdma_qos_pppq_ebl(true);
 	} else if (buf[0] == '0' && hook_toggle) {
 		pr_info("hook is going to be disabled !\n");
 		hnat_disable_hook();
 
-		if (IS_PPPQ_MODE) {
-			for (id = 0; id < MAX_PPPQ_PORT_NUM; id++)
-				hnat_qos_shaper_ebl(id, 0);
-		}
+		if (IS_PPPQ_MODE)
+			qdma_qos_pppq_ebl(false);
 	}
 
 	return len;
@@ -2975,72 +2770,6 @@
 	return single_open(file, hnat_qos_toggle_read, file->private_data);
 }
 
-void hnat_qos_shaper_ebl(u32 id, u32 enable)
-{
-	struct mtk_hnat *h = hnat_priv;
-	u32 cfg;
-
-	cr_set_field(h->fe_base + QDMA_PAGE, QTX_CFG_PAGE, (id / NUM_OF_Q_PER_PAGE));
-	if (enable) {
-		cfg = QTX_SCH_MIN_RATE_EN | QTX_SCH_MAX_RATE_EN;
-		cfg |= (1 << QTX_SCH_MIN_RATE_MAN_OFFSET) |
-		       (4 << QTX_SCH_MIN_RATE_EXP_OFFSET) |
-		       (25 << QTX_SCH_MAX_RATE_MAN_OFFSET) |
-		       (5 << QTX_SCH_MAX_RATE_EXP_OFFSET) |
-		       (4 << QTX_SCH_MAX_RATE_WGHT_OFFSET);
-
-		writel(cfg, h->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE));
-	} else {
-		writel(0, h->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE));
-	}
-}
-
-static void hnat_qos_disable(void)
-{
-	struct mtk_hnat *h = hnat_priv;
-	u32 id, cfg;
-
-	for (id = 0; id < MAX_PPPQ_PORT_NUM; id++) {
-		hnat_qos_shaper_ebl(id, 0);
-		writel((4 << QTX_CFG_HW_RESV_CNT_OFFSET) |
-		       (4 << QTX_CFG_SW_RESV_CNT_OFFSET),
-		       h->fe_base + QTX_CFG(id % NUM_OF_Q_PER_PAGE));
-	}
-
-	cfg = (QDMA_TX_SCH_WFQ_EN) | (QDMA_TX_SCH_WFQ_EN << 16);
-	for (id = 0; id < h->data->num_of_sch; id += 2) {
-		if (h->data->num_of_sch == 4)
-			writel(cfg, h->fe_base + QDMA_TX_4SCH_BASE(id));
-		else
-			writel(cfg, h->fe_base + QDMA_TX_2SCH_BASE);
-	}
-}
-
-static void hnat_qos_pppq_enable(void)
-{
-	struct mtk_hnat *h = hnat_priv;
-	u32 id, cfg;
-
-	for (id = 0; id < MAX_PPPQ_PORT_NUM; id++) {
-		if (hook_toggle)
-			hnat_qos_shaper_ebl(id, 1);
-		else
-			hnat_qos_shaper_ebl(id, 0);
-
-		writel((4 << QTX_CFG_HW_RESV_CNT_OFFSET) |
-		       (4 << QTX_CFG_SW_RESV_CNT_OFFSET),
-		       h->fe_base + QTX_CFG(id % NUM_OF_Q_PER_PAGE));
-	}
-
-	cfg = (QDMA_TX_SCH_WFQ_EN) | (QDMA_TX_SCH_WFQ_EN << 16);
-	for (id = 0; id < h->data->num_of_sch; id+= 2) {
-		if (h->data->num_of_sch == 4)
-                        writel(cfg, h->fe_base + QDMA_TX_4SCH_BASE(id));
-                else
-                        writel(cfg, h->fe_base + QDMA_TX_2SCH_BASE);
-	}
-}
-
 static ssize_t hnat_qos_toggle_write(struct file *file, const char __user *buffer,
 				     size_t count, loff_t *data)
 {
@@ -3061,7 +2790,7 @@
 		qos_toggle = 0;
 		qos_dl_toggle = 0;
 		qos_ul_toggle = 0;
-		hnat_qos_disable();
+		qdma_qos_disable();
 	} else if (buf[0] == '1') {
 		p_buf = buf;
 		p_token = strsep(&p_buf, " \t");
@@ -3095,7 +2824,7 @@
 		qos_toggle = 2;
 		qos_dl_toggle = 1;
 		qos_ul_toggle = 1;
-		hnat_qos_pppq_enable();
+		qdma_qos_pppq_ebl(hook_toggle);
 	} else if (buf[0] == '3') {
 		hnat_qos_toggle_usage();
 	} else {
@@ -3114,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);
@@ -3138,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:
@@ -3219,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)
@@ -3238,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",
@@ -3289,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",
@@ -3344,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;
@@ -3352,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)
@@ -3505,7 +3365,7 @@
 	struct dentry *root;
 	struct dentry *file;
 	long i;
-	char name[16];
+	char name[16], name_symlink[48];
 
 	root = debugfs_create_dir("hnat", NULL);
 	if (!root) {
@@ -3571,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);
@@ -3578,8 +3440,13 @@
 			ret = -ENOMEM;
 			goto err1;
 		}
-		debugfs_create_file(name, 0444, root, (void *)i,
-				    &hnat_sched_fops);
+		ret = snprintf(name_symlink, sizeof(name_symlink),
+			       "/sys/kernel/debug/mtketh/qdma_sch%ld", i);
+		if (ret != strlen(name_symlink)) {
+			ret = -ENOMEM;
+			goto err1;
+		}
+		debugfs_create_symlink(name, root, name_symlink);
 	}
 
 	for (i = 0; i < MTK_QDMA_TX_NUM; i++) {
@@ -3588,8 +3455,13 @@
 			ret = -ENOMEM;
 			goto err1;
 		}
-		debugfs_create_file(name, 0444, root, (void *)i,
-				    &hnat_queue_fops);
+		ret = snprintf(name_symlink, sizeof(name_symlink),
+			       "/sys/kernel/debug/mtketh/qdma_txq%ld", i);
+		if (ret != strlen(name_symlink)) {
+			ret = -ENOMEM;
+			goto err1;
+		}
+		debugfs_create_symlink(name, root, name_symlink);
 	}
 
 	return 0;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
index 17f5e4e..0a5be88 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/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;
@@ -959,13 +963,13 @@
 	}
 
 setup_rate_limit:
-	cfg = QTX_SCH_MIN_RATE_EN | QTX_SCH_MAX_RATE_EN;
-	cfg |= (1 << QTX_SCH_MIN_RATE_MAN_OFFSET) |
-	       (4 << QTX_SCH_MIN_RATE_EXP_OFFSET) |
-	       (max_man << QTX_SCH_MAX_RATE_MAN_OFFSET) |
-	       (max_exp << QTX_SCH_MAX_RATE_EXP_OFFSET) |
-	       (4 << QTX_SCH_MAX_RATE_WGHT_OFFSET);
-	writel(cfg, hnat_priv->fe_base + QTX_SCH(id % NUM_OF_Q_PER_PAGE));
+	cfg = MTK_QTX_SCH_MIN_RATE_EN | MTK_QTX_SCH_MAX_RATE_EN;
+	cfg |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
+	       FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) |
+	       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, max_man) |
+	       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, max_exp) |
+	       FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 4);
+	writel(cfg, hnat_priv->fe_base + MTK_QTX_SCH(id % MTK_QTX_PER_PAGE));
 }
 
 static unsigned int
@@ -1157,8 +1161,8 @@
 }
 
 static int hnat_ipv6_get_nexthop(struct sk_buff *skb,
-					  const struct net_device *out,
-					  struct flow_offload_hw_path *hw_path)
+				 const struct net_device *out,
+				 struct flow_offload_hw_path *hw_path)
 {
 	const struct in6_addr *ipv6_nexthop;
 	struct neighbour *neigh = NULL;
@@ -1193,8 +1197,8 @@
 }
 
 static int hnat_ipv4_get_nexthop(struct sk_buff *skb,
-					  const struct net_device *out,
-					  struct flow_offload_hw_path *hw_path)
+				 const struct net_device *out,
+				 struct flow_offload_hw_path *hw_path)
 {
 	u32 nexthop;
 	struct neighbour *neigh;
@@ -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 {
@@ -2144,10 +2190,11 @@
 		gmac = -EINVAL;
 	}
 
-	if ((gmac < 0) && (debug_level >= 7)) {
-		printk_ratelimited(KERN_WARNING
-				   "Unknown case of dp, iif=%x --> %s\n",
-				   skb_hnat_iface(skb), dev->name);
+	if (gmac < 0) {
+		if (debug_level >= 7)
+			printk_ratelimited(KERN_WARNING
+					   "Unknown case of dp, iif=%x --> %s\n",
+					   skb_hnat_iface(skb), dev->name);
 		return 0;
 	}
 
@@ -2204,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;
 				}
@@ -2222,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)
@@ -2245,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;
 				}
@@ -2412,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;
@@ -2438,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:
 		/*
@@ -2464,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 {
@@ -2524,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;
 			}
@@ -2547,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;
@@ -2629,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;
 			}
@@ -3093,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,
@@ -3106,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/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
index d48175f..5f64105 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_hnat/hnat_stag.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/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;
 	}
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
index c700dcc..8f28900 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3001-mt7622-backport-nf-hw-offload-framework-and-upstream.patch
@@ -1,6 +1,6 @@
-From 2ee245f821d960dd389a5c3dd97dc1057aa05554 Mon Sep 17 00:00:00 2001
+From 3a4256932000744357ba358140fd2895756060e6 Mon Sep 17 00:00:00 2001
 From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
-Date: Tue, 4 Jun 2024 09:14:34 +0800
+Date: Tue, 15 Oct 2024 15:23:05 +0800
 Subject: [PATCH 01/24] mt7622 backport nf hw offload framework and upstream
  hnat plus xt-FLOWOFFLOAD update v2
 
@@ -8,8 +8,8 @@
  drivers/net/ethernet/mediatek/Makefile        |    3 +-
  drivers/net/ethernet/mediatek/mtk_eth_soc.c   |   25 +-
  drivers/net/ethernet/mediatek/mtk_eth_soc.h   |   19 +-
- drivers/net/ethernet/mediatek/mtk_ppe.c       |  514 +++++++
- drivers/net/ethernet/mediatek/mtk_ppe.h       |  288 ++++
+ drivers/net/ethernet/mediatek/mtk_ppe.c       |  513 +++++++
+ drivers/net/ethernet/mediatek/mtk_ppe.h       |  287 ++++
  .../net/ethernet/mediatek/mtk_ppe_debugfs.c   |  214 +++
  .../net/ethernet/mediatek/mtk_ppe_offload.c   |  535 ++++++++
  drivers/net/ethernet/mediatek/mtk_ppe_regs.h  |  144 ++
@@ -47,7 +47,7 @@
  net/netfilter/nf_flow_table_ip.c              |  447 +++---
  net/netfilter/nf_flow_table_offload.c         | 1199 +++++++++++++++++
  net/netfilter/xt_FLOWOFFLOAD.c                |  780 +++++++++++
- 42 files changed, 4983 insertions(+), 452 deletions(-)
+ 42 files changed, 4981 insertions(+), 452 deletions(-)
  mode change 100644 => 100755 drivers/net/ethernet/mediatek/Makefile
  mode change 100644 => 100755 drivers/net/ethernet/mediatek/mtk_eth_soc.c
  mode change 100644 => 100755 drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -63,24 +63,24 @@
 diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
 old mode 100644
 new mode 100755
-index 634640d..5f342f4
+index 2f14aa1..0a3b977
 --- a/drivers/net/ethernet/mediatek/Makefile
 +++ b/drivers/net/ethernet/mediatek/Makefile
 @@ -4,5 +4,6 @@
  #
  
  obj-$(CONFIG_NET_MEDIATEK_SOC)			+= mtk_eth.o
--mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o
-+mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o	\
+-mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o mtk_eth_ptp.o
++mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o mtk_eth_ptp.o	\
 +	     mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
  obj-$(CONFIG_NET_MEDIATEK_HNAT)			+= mtk_hnat/
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 old mode 100644
 new mode 100755
-index 697a197..edc4ecf
+index e3040fb..088ff2d
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -4010,6 +4010,7 @@ static int mtk_open(struct net_device *dev)
+@@ -4307,6 +4307,7 @@ static int mtk_open(struct net_device *dev)
  	struct mtk_phylink_priv *phylink_priv = &mac->phylink_priv;
  	struct device_node *phy_node;
  	const char *mac_addr;
@@ -88,7 +88,7 @@
  	u32 id = mtk_mac2xgmii_id(eth, mac->id);
  	int err, i;
  
-@@ -4102,7 +4103,10 @@ static int mtk_open(struct net_device *dev)
+@@ -4407,7 +4408,10 @@ static int mtk_open(struct net_device *dev)
  		regmap_write(eth->sgmii->pcs[id].regmap,
  			     SGMSYS_QPHY_PWR_STATE_CTRL, 0);
  
@@ -100,9 +100,9 @@
  
  	return 0;
  }
-@@ -4189,6 +4193,9 @@ static int mtk_stop(struct net_device *dev)
- 
- 	mtk_dma_free(eth);
+@@ -4541,6 +4545,9 @@ static int mtk_stop(struct net_device *dev)
+ 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_HWTSTAMP))
+ 		ptp_clock_unregister(eth->ptp_clock);
  
 +	if (eth->soc->offload_version)
 +		mtk_ppe_stop(&eth->ppe);
@@ -110,7 +110,7 @@
  	return 0;
  }
  
-@@ -5109,6 +5116,7 @@ static const struct net_device_ops mtk_netdev_ops = {
+@@ -5577,6 +5584,7 @@ static const struct net_device_ops mtk_netdev_ops = {
  #ifdef CONFIG_NET_POLL_CONTROLLER
  	.ndo_poll_controller	= mtk_poll_controller,
  #endif
@@ -118,7 +118,7 @@
  };
  
  static void mux_poll(struct work_struct *work)
-@@ -5741,6 +5749,17 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -6221,6 +6229,17 @@ static int mtk_probe(struct platform_device *pdev)
  			goto err_free_dev;
  	}
  
@@ -136,7 +136,7 @@
  	for (i = 0; i < MTK_MAX_DEVS; i++) {
  		if (!eth->netdev[i])
  			continue;
-@@ -5842,6 +5861,7 @@ static const struct mtk_soc_data mt2701_data = {
+@@ -6331,6 +6350,7 @@ static const struct mtk_soc_data mt2701_data = {
  	.required_clks = MT7623_CLKS_BITMAP,
  	.required_pctl = true,
  	.has_sram = false,
@@ -144,7 +144,7 @@
  	.rss_num = 0,
  	.txrx = {
  		.txd_size = sizeof(struct mtk_tx_dma),
-@@ -5862,6 +5882,7 @@ static const struct mtk_soc_data mt7621_data = {
+@@ -6351,6 +6371,7 @@ static const struct mtk_soc_data mt7621_data = {
  	.required_clks = MT7621_CLKS_BITMAP,
  	.required_pctl = false,
  	.has_sram = false,
@@ -152,7 +152,7 @@
  	.rss_num = 0,
  	.txrx = {
  		.txd_size = sizeof(struct mtk_tx_dma),
-@@ -5883,6 +5904,7 @@ static const struct mtk_soc_data mt7622_data = {
+@@ -6372,6 +6393,7 @@ static const struct mtk_soc_data mt7622_data = {
  	.required_clks = MT7622_CLKS_BITMAP,
  	.required_pctl = false,
  	.has_sram = false,
@@ -160,7 +160,7 @@
  	.rss_num = 0,
  	.txrx = {
  		.txd_size = sizeof(struct mtk_tx_dma),
-@@ -5903,6 +5925,7 @@ static const struct mtk_soc_data mt7623_data = {
+@@ -6392,6 +6414,7 @@ static const struct mtk_soc_data mt7623_data = {
  	.required_clks = MT7623_CLKS_BITMAP,
  	.required_pctl = true,
  	.has_sram = false,
@@ -171,19 +171,19 @@
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 old mode 100644
 new mode 100755
-index 9fae48f..54b2c7c
+index 02feeaf..8ca9c0c
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -15,6 +15,8 @@
- #include <linux/u64_stats_sync.h>
+@@ -16,6 +16,8 @@
  #include <linux/refcount.h>
  #include <linux/phylink.h>
+ #include <linux/ptp_clock_kernel.h>
 +#include <linux/rhashtable.h>
 +#include "mtk_ppe.h"
  
  #define MTK_QDMA_PAGE_SIZE	2048
  #define	MTK_MAX_RX_LENGTH	1536
-@@ -46,7 +48,8 @@
+@@ -51,7 +53,8 @@
  				 NETIF_F_HW_VLAN_CTAG_TX | \
  				 NETIF_F_SG | NETIF_F_TSO | \
  				 NETIF_F_TSO6 | \
@@ -193,7 +193,7 @@
  #define MTK_SET_FEATURES	(NETIF_F_LRO | \
  				 NETIF_F_HW_VLAN_CTAG_RX)
  #define MTK_HW_FEATURES_MT7628	(NETIF_F_SG | NETIF_F_RXCSUM)
-@@ -133,6 +136,7 @@
+@@ -149,6 +152,7 @@
  #define MTK_GDMA_UCS_EN		BIT(20)
  #define MTK_GDMA_STRP_CRC	BIT(16)
  #define MTK_GDMA_TO_PDMA	0x0
@@ -201,7 +201,7 @@
  #define MTK_GDMA_DROP_ALL	0x7777
  
  /* GDM Egress Control Register */
-@@ -686,6 +690,12 @@
+@@ -725,6 +729,12 @@
  #define RX_DMA_TCI(_x)		((_x) & (VLAN_PRIO_MASK | VLAN_VID_MASK))
  #define RX_DMA_VPID(_x)		(((_x) >> 16) & 0xffff)
  
@@ -214,7 +214,7 @@
  /* QDMA descriptor rxd4 */
  #define RX_DMA_L4_VALID		BIT(24)
  #define RX_DMA_L4_VALID_PDMA	BIT(30)		/* when PDMA is used */
-@@ -1737,6 +1747,7 @@ struct mtk_soc_data {
+@@ -1967,6 +1977,7 @@ struct mtk_soc_data {
  	u64		caps;
  	u64		required_clks;
  	bool		required_pctl;
@@ -222,9 +222,9 @@
  	netdev_features_t hw_features;
  	bool		has_sram;
  	struct {
-@@ -1947,6 +1958,9 @@ struct mtk_eth {
+@@ -2263,6 +2274,9 @@ struct mtk_eth {
  	int				ip_align;
-	spinlock_t			syscfg0_lock;
+ 	spinlock_t			syscfg0_lock;
  	struct notifier_block		netdevice_notifier;
 +
 +	struct mtk_ppe			ppe;
@@ -232,7 +232,7 @@
  };
  
  /* struct mtk_mac -	the structure that holds the info about the MACs of the
-@@ -2026,6 +2040,9 @@ struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, int id);
+@@ -2348,6 +2362,9 @@ struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, unsigned int
  int mtk_usxgmii_init(struct mtk_eth *eth, struct device_node *r);
  int mtk_toprgu_init(struct mtk_eth *eth, struct device_node *r);
  
@@ -241,10 +241,10 @@
 +		     void *type_data);
  void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
  u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index);
- #endif /* MTK_ETH_H */
+ 
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
 new file mode 100644
-index 0000000..6965d98
+index 0000000..8a1437b
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
 @@ -0,0 +1,513 @@
@@ -763,7 +763,7 @@
 +}
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
 new file mode 100644
-index 0000000..242fb8f
+index 0000000..4787447
 --- /dev/null
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
 @@ -0,0 +1,287 @@
@@ -1966,10 +1966,10 @@
 +
 +#endif
 diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
-index 555ba50..25dcff2 100644
+index 76ed2d4..ec2fbd1 100644
 --- a/drivers/net/ppp/ppp_generic.c
 +++ b/drivers/net/ppp/ppp_generic.c
-@@ -1380,12 +1380,34 @@ static void ppp_dev_priv_destructor(struct net_device *dev)
+@@ -1395,12 +1395,34 @@ static void ppp_dev_priv_destructor(struct net_device *dev)
  		ppp_destroy_interface(ppp);
  }
  
@@ -2912,7 +2912,7 @@
  		     struct bridge_vlan_info *p_vinfo)
  {
 diff --git a/net/core/dev.c b/net/core/dev.c
-index 9da89f2..e9dd11e 100644
+index 6c0aefe..7d29e7b 100644
 --- a/net/core/dev.c
 +++ b/net/core/dev.c
 @@ -722,6 +722,52 @@ int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
@@ -3093,7 +3093,7 @@
  	tristate "Netfilter IPv6 packet duplication to alternate destination"
  	depends on !NF_CONNTRACK || NF_CONNTRACK
 diff --git a/net/ipv6/route.c b/net/ipv6/route.c
-index d99d12a..29d6fd2 100644
+index f0677e1..d928855 100644
 --- a/net/ipv6/route.c
 +++ b/net/ipv6/route.c
 @@ -83,7 +83,7 @@ enum rt6_nud_state {
@@ -3105,7 +3105,7 @@
  static void		ip6_negative_advice(struct sock *sk,
  					    struct dst_entry *dst);
  static void		ip6_dst_destroy(struct dst_entry *);
-@@ -3125,25 +3125,7 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst)
+@@ -3129,25 +3129,7 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst)
  
  static unsigned int ip6_mtu(const struct dst_entry *dst)
  {
@@ -6905,5 +6905,5 @@
 +module_init(xt_flowoffload_tg_init);
 +module_exit(xt_flowoffload_tg_exit);
 -- 
-2.18.0
+2.45.2
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3005-flow-offload-add-mkhnat-dual-ppe-new-v2.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3005-flow-offload-add-mkhnat-dual-ppe-new-v2.patch
index 08b4084..ecc1ffc 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3005-flow-offload-add-mkhnat-dual-ppe-new-v2.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3005-flow-offload-add-mkhnat-dual-ppe-new-v2.patch
@@ -1,6 +1,6 @@
-From fabaa1fcdd08bea6aaa36ab6dabe1cdff5d55d3e Mon Sep 17 00:00:00 2001
+From 89b1ad6fb56c8a868938216596a4ff19f2ebed98 Mon Sep 17 00:00:00 2001
 From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
-Date: Thu, 6 Jun 2024 16:21:53 +0800
+Date: Tue, 15 Oct 2024 16:00:41 +0800
 Subject: [PATCH 06/26] flow-offload-add-mkhnat-dual-ppe-new-v2
 
 ---
@@ -28,10 +28,10 @@
                  #address-cells = <1>;
                  #size-cells = <0>;
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index e3599cd..c6b1aaf 100644
+index 50fb4dd..7068ea8 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -2459,7 +2459,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
+@@ -2663,7 +2663,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
  #endif
  
  		if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
@@ -40,7 +40,7 @@
  
  		if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
  			if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_RX_V2)) {
-@@ -4121,8 +4121,12 @@ static int mtk_open(struct net_device *dev)
+@@ -4426,8 +4426,12 @@ static int mtk_open(struct net_device *dev)
  		regmap_write(eth->sgmii->pcs[id].regmap,
  			     SGMSYS_QPHY_PWR_STATE_CTRL, 0);
  
@@ -55,9 +55,9 @@
  
  	mtk_gdm_config(eth, mac->id, gdm_config);
  
-@@ -4211,8 +4215,10 @@ static int mtk_stop(struct net_device *dev)
- 
- 	mtk_dma_free(eth);
+@@ -4563,8 +4567,10 @@ static int mtk_stop(struct net_device *dev)
+ 	if (MTK_HAS_CAPS(eth->soc->caps, MTK_HWTSTAMP))
+ 		ptp_clock_unregister(eth->ptp_clock);
  
 -	if (eth->soc->offload_version)
 -		mtk_ppe_stop(eth->ppe);
@@ -68,7 +68,7 @@
  
  	return 0;
  }
-@@ -5790,15 +5796,35 @@ static int mtk_probe(struct platform_device *pdev)
+@@ -6268,15 +6274,35 @@ static int mtk_probe(struct platform_device *pdev)
  	}
  
  	if (eth->soc->offload_version) {
@@ -87,8 +87,11 @@
 +		if (eth->ppe_num > MTK_MAX_PPE_NUM) {
 +			dev_warn(&pdev->dev, "%d is not a valid ppe num, please check mtketh-ppe-num in dts !", eth->ppe_num);
 +			eth->ppe_num = MTK_MAX_PPE_NUM;
-+		}
-+
+ 		}
+ 
+-		err = mtk_eth_offload_init(eth);
+-		if (err)
+-			goto err_deinit_ppe;
 +		dev_info(&pdev->dev, "ppe num = %d\n", eth->ppe_num);
 +
 +		for (i = 0; i < eth->ppe_num; i++) {
@@ -102,20 +105,17 @@
 +			err = mtk_eth_offload_init(eth, i);
 +			if (err)
 +				goto err_deinit_ppe;
- 		}
- 
--		err = mtk_eth_offload_init(eth);
--		if (err)
--			goto err_deinit_ppe;
++		}
++
 +		mtk_ppe_debugfs_init(eth);
  	}
  
  	for (i = 0; i < MTK_MAX_DEVS; i++) {
 diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-index 2227eb3..e13b20f 100644
+index d784220..4312636 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -131,7 +131,12 @@
+@@ -152,7 +152,12 @@
  #define MTK_GDMA_UCS_EN		BIT(20)
  #define MTK_GDMA_STRP_CRC	BIT(16)
  #define MTK_GDMA_TO_PDMA	0x0
@@ -129,9 +129,9 @@
  #define MTK_GDMA_DROP_ALL	0x7777
  
  /* GDM Egress Control Register */
-@@ -1962,7 +1967,8 @@ struct mtk_eth {
+@@ -2285,7 +2290,8 @@ struct mtk_eth {
+ 	spinlock_t			syscfg0_lock;
  	struct notifier_block		netdevice_notifier;
- 	struct timer_list		mtk_dma_monitor_timer;
  
 -	struct mtk_ppe			*ppe;
 +	u8				ppe_num;
@@ -139,7 +139,7 @@
  	struct rhashtable		flow_table;
  };
  
-@@ -2043,9 +2049,11 @@ struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, int id);
+@@ -2380,7 +2386,7 @@ struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, unsigned int
  int mtk_usxgmii_init(struct mtk_eth *eth, struct device_node *r);
  int mtk_toprgu_init(struct mtk_eth *eth, struct device_node *r);
  
@@ -148,15 +148,19 @@
  int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
  		     void *type_data);
  void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
- u32 mtk_rss_indr_table(struct mtk_rss_params *rss_params, int index);
+@@ -2391,4 +2397,6 @@ int mtk_ptp_hwtstamp_process_rx(struct net_device *dev, struct sk_buff *skb);
+ int mtk_ptp_hwtstamp_set_config(struct net_device *dev, struct ifreq *ifr);
+ int mtk_ptp_hwtstamp_get_config(struct net_device *dev, struct ifreq *ifr);
+ int mtk_ptp_clock_init(struct mtk_eth *eth);
 +
 +int mtk_ppe_debugfs_init(struct mtk_eth *eth);
  #endif /* MTK_ETH_H */
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
-index 9dfff1f..854e130 100755
+index e9d8111..35fc11b 100755
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -696,6 +696,6 @@ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+@@ -698,7 +698,7 @@ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ 	return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
  }
  
 -struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base)
@@ -164,15 +168,15 @@
  {
  	struct device *dev = eth->dev;
  	struct mtk_foe_entry *foe;
-@@ -715,6 +715,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+@@ -717,6 +717,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base)
  	ppe->eth = eth;
  	ppe->dev = dev;
  	ppe->version = eth->soc->offload_version;
 +	ppe->id = index;
  
- 	foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe),
- 				  &ppe->foe_phys, GFP_KERNEL);
-@@ -723,8 +724,6 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+ 	foe = dmam_alloc_coherent(ppe->dev,
+ 				  MTK_PPE_ENTRIES * soc->foe_entry_size,
+@@ -726,8 +727,6 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base)
  
  	ppe->foe_table = foe;
  
@@ -181,7 +185,7 @@
  	return ppe;
  
  err_free_l2_flows:
-@@ -734,9 +733,13 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+@@ -737,9 +736,13 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base)
  
  void mtk_ppe_deinit(struct mtk_eth *eth)
  {
@@ -199,7 +203,7 @@
  
  static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
-index c5c3c83..27a780f 100644
+index 15c5016..83f1e03 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe.h
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
 @@ -9,8 +9,10 @@
@@ -213,7 +217,7 @@
  #define MTK_ETH_PPE_BASE		0xc00
  #endif
  
-@@ -299,6 +301,7 @@ struct mtk_flow_entry {
+@@ -298,6 +300,7 @@ struct mtk_flow_entry {
  		};
  	};
  	u8 type;
@@ -221,15 +225,15 @@
  	s8 wed_index;
  	u16 hash;
  	union {
-@@ -318,6 +321,7 @@ struct mtk_ppe {
+@@ -317,6 +320,7 @@ struct mtk_ppe {
  	struct device *dev;
  	void __iomem *base;
  	int version;
 +	int id;
  
- 	struct mtk_foe_entry *foe_table;
+ 	void *foe_table;
  	dma_addr_t foe_phys;
-@@ -330,7 +334,7 @@ struct mtk_ppe {
+@@ -329,7 +333,7 @@ struct mtk_ppe {
  	void *acct_table;
  };
  
@@ -238,7 +242,7 @@
  void mtk_ppe_deinit(struct mtk_eth *eth);
  int mtk_ppe_start(struct mtk_ppe *ppe);
  int mtk_ppe_stop(struct mtk_ppe *ppe);
-@@ -382,6 +386,5 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
+@@ -370,6 +374,5 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
  int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
  void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
  int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
@@ -246,7 +250,7 @@
  
  #endif
 diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
-index a591ab1..f4ebe59 100644
+index aec99d4..c742809 100644
 --- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
 +++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
 @@ -73,9 +73,8 @@ mtk_print_addr_info(struct seq_file *m, struct mtk_flow_addr_info *ai)
@@ -475,5 +479,5 @@
  
  /**
 -- 
-2.18.0
+2.45.2
 
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3012-flow-offload-add-mtkhnat-qdma-qos.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3012-flow-offload-add-mtkhnat-qdma-qos.patch
index 944278f..5f2946b 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3012-flow-offload-add-mtkhnat-qdma-qos.patch
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/flow_patch/999-3012-flow-offload-add-mtkhnat-qdma-qos.patch
@@ -6,14 +6,14 @@
 ---
  drivers/net/ethernet/mediatek/Makefile        |   2 +-
  drivers/net/ethernet/mediatek/mtk_eth_soc.c   |  10 +
- drivers/net/ethernet/mediatek/mtk_eth_soc.h   |  44 ++
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h   |  35 ++
  drivers/net/ethernet/mediatek/mtk_ppe.c       |  48 +-
  drivers/net/ethernet/mediatek/mtk_ppe.h       |   4 +
  .../net/ethernet/mediatek/mtk_ppe_offload.c   |  28 +-
  .../net/ethernet/mediatek/mtk_qdma_debugfs.c  | 448 ++++++++++++++++++
  include/net/flow_offload.h                    |   1 +
  net/netfilter/nf_flow_table_offload.c         |   4 +-
- 9 files changed, 584 insertions(+), 5 deletions(-)
+ 9 files changed, 575 insertions(+), 5 deletions(-)
  create mode 100644 drivers/net/ethernet/mediatek/mtk_qdma_debugfs.c
 
 diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
@@ -23,7 +23,7 @@
 @@ -5,7 +5,7 @@
  
  obj-$(CONFIG_NET_MEDIATEK_SOC)			+= mtk_eth.o
- mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o	\
+ mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_usxgmii.o mtk_eth_path.o mtk_eth_dbg.o mtk_eth_reset.o mtk_eth_ptp.o	\
 -	     mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
 +	     mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o  mtk_qdma_debugfs.o
  mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o
@@ -110,29 +110,6 @@
 index 351e66c..2ddadf3 100644
 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
 +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -549,6 +549,11 @@
- /* QDMA Interrupt Mask Register */
- #define MTK_QDMA_HRED2		(QDMA_BASE + 0x244)
- 
-+/* QDMA TX Queue MIB Interface Register */
-+#define MTK_QTX_MIB_IF		(QDMA_BASE + 0x2bc)
-+#define MTK_MIB_ON_QTX_CFG	BIT(31)
-+#define MTK_VQTX_MIB_EN		BIT(28)
-+
- /* QDMA TX Forward CPU Pointer Register */
- #define MTK_QTX_CTX_PTR		(QDMA_BASE +0x300)
- 
-@@ -578,6 +583,10 @@ 
- /* QDMA TX Scheduler Rate Control Register */
- #define MTK_QDMA_TX_4SCH_BASE(x)	(QDMA_BASE + 0x398 + (((x) >> 1) * 0x4))
- #define MTK_QDMA_TX_SCH_MASK		GENMASK(15, 0)
-+#define MTK_QDMA_TX_SCH_MAX_WFQ		BIT(15)
-+#define MTK_QDMA_TX_SCH_RATE_EN		BIT(11)
-+#define MTK_QDMA_TX_SCH_RATE_MAN	GENMASK(10, 4)
-+#define MTK_QDMA_TX_SCH_RATE_EXP	GENMASK(3, 0)
- 
- /* WDMA Registers */
- #define MTK_WDMA_CTX_PTR(x)	(WDMA_BASE(x) + 0x8)
 @@ -1771,6 +1781,7 @@ struct mtk_soc_data {
  		u32	rx_dma_l4_valid;
  		u32	dma_max_len;
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1719-v6.2-net-ptp-introduce-adjust-by-scaled-ppm.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1719-v6.2-net-ptp-introduce-adjust-by-scaled-ppm.patch
new file mode 100644
index 0000000..e9961c3
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1719-v6.2-net-ptp-introduce-adjust-by-scaled-ppm.patch
@@ -0,0 +1,202 @@
+From 85fa9116d39817791f3da4dd642549db8916cb8e Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Tue, 30 Jan 2024 12:29:37 +0800
+Subject: [PATCH] 999-1715-v6.2-net-ptp-introduce-adjust-by-scaled-ppm.patch
+
+---
+ drivers/ptp/ptp_clock.c                       | 21 ------
+ include/linux/math64.h                        | 12 ++++
+ include/linux/ptp_clock_kernel.h              | 72 +++++++++++++++++++
+ lib/math/div64.c                              | 42 +++++++++++
+ 4 files changed, 126 insertions(+), 21 deletions(-)
+
+diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
+index eedf067..5cca99f 100644
+--- a/drivers/ptp/ptp_clock.c
++++ b/drivers/ptp/ptp_clock.c
+@@ -63,27 +63,6 @@ static void enqueue_external_timestamp(struct timestamp_event_queue *queue,
+ 	spin_unlock_irqrestore(&queue->lock, flags);
+ }
+ 
+-long scaled_ppm_to_ppb(long ppm)
+-{
+-	/*
+-	 * The 'freq' field in the 'struct timex' is in parts per
+-	 * million, but with a 16 bit binary fractional field.
+-	 *
+-	 * We want to calculate
+-	 *
+-	 *    ppb = scaled_ppm * 1000 / 2^16
+-	 *
+-	 * which simplifies to
+-	 *
+-	 *    ppb = scaled_ppm * 125 / 2^13
+-	 */
+-	s64 ppb = 1 + ppm;
+-	ppb *= 125;
+-	ppb >>= 13;
+-	return (long) ppb;
+-}
+-EXPORT_SYMBOL(scaled_ppm_to_ppb);
+-
+ /* posix clock implementation */
+ 
+ static int ptp_clock_getres(struct posix_clock *pc, struct timespec64 *tp)
+diff --git a/include/linux/math64.h b/include/linux/math64.h
+index 65bef21..a593096 100644
+--- a/include/linux/math64.h
++++ b/include/linux/math64.h
+@@ -281,6 +281,18 @@ static inline u64 mul_u64_u32_div(u64 a, u32 mul, u32 divisor)
+ }
+ #endif /* mul_u64_u32_div */
+ 
++u64 mul_u64_u64_div_u64(u64 a, u64 mul, u64 div);
++
++/**
++ * DIV64_U64_ROUND_UP - unsigned 64bit divide with 64bit divisor rounded up
++ * @ll: unsigned 64bit dividend
++ * @d: unsigned 64bit divisor
++ *
++ * Divide unsigned 64bit dividend by unsigned 64bit divisor
++ * and round up.
++ *
++ * Return: dividend / divisor rounded up
++ */
+ #define DIV64_U64_ROUND_UP(ll, d)	\
+ 	({ u64 _tmp = (d); div64_u64((ll) + _tmp - 1, _tmp); })
+ 
+diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
+index 874f7e7..2ff9afe 100644
+--- a/include/linux/ptp_clock_kernel.h
++++ b/include/linux/ptp_clock_kernel.h
+@@ -169,6 +169,78 @@ struct ptp_clock_event {
+ 	};
+ };
+ 
++/**
++ * scaled_ppm_to_ppb() - convert scaled ppm to ppb
++ *
++ * @ppm:    Parts per million, but with a 16 bit binary fractional field
++ */
++static inline long scaled_ppm_to_ppb(long ppm)
++{
++	/*
++	 * The 'freq' field in the 'struct timex' is in parts per
++	 * million, but with a 16 bit binary fractional field.
++	 *
++	 * We want to calculate
++	 *
++	 *    ppb = scaled_ppm * 1000 / 2^16
++	 *
++	 * which simplifies to
++	 *
++	 *    ppb = scaled_ppm * 125 / 2^13
++	 */
++	s64 ppb = 1 + ppm;
++
++	ppb *= 125;
++	ppb >>= 13;
++	return (long)ppb;
++}
++
++/**
++ * diff_by_scaled_ppm - Calculate difference using scaled ppm
++ * @base: the base increment value to adjust
++ * @scaled_ppm: scaled parts per million to adjust by
++ * @diff: on return, the absolute value of calculated diff
++ *
++ * Calculate the difference to adjust the base increment using scaled parts
++ * per million.
++ *
++ * Use mul_u64_u64_div_u64 to perform the difference calculation in avoid
++ * possible overflow.
++ *
++ * Returns: true if scaled_ppm is negative, false otherwise
++ */
++static inline bool diff_by_scaled_ppm(u64 base, long scaled_ppm, u64 *diff)
++{
++	bool negative = false;
++
++	if (scaled_ppm < 0) {
++		negative = true;
++		scaled_ppm = -scaled_ppm;
++	}
++
++	*diff = mul_u64_u64_div_u64(base, (u64)scaled_ppm, 1000000ULL << 16);
++
++	return negative;
++}
++
++/**
++ * adjust_by_scaled_ppm - Adjust a base increment by scaled parts per million
++ * @base: the base increment value to adjust
++ * @scaled_ppm: scaled parts per million frequency adjustment
++ *
++ * Helper function which calculates a new increment value based on the
++ * requested scaled parts per million adjustment.
++ */
++static inline u64 adjust_by_scaled_ppm(u64 base, long scaled_ppm)
++{
++	u64 diff;
++
++	if (diff_by_scaled_ppm(base, scaled_ppm, &diff))
++		return base - diff;
++
++	return base + diff;
++}
++
+ #if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
+ 
+ /**
+diff --git a/lib/math/div64.c b/lib/math/div64.c
+index 368ca7f..edd1090 100644
+--- a/lib/math/div64.c
++++ b/lib/math/div64.c
+@@ -190,3 +190,45 @@ u32 iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder)
+ 	return __iter_div_u64_rem(dividend, divisor, remainder);
+ }
+ EXPORT_SYMBOL(iter_div_u64_rem);
++
++#ifndef mul_u64_u64_div_u64
++u64 mul_u64_u64_div_u64(u64 a, u64 b, u64 c)
++{
++	u64 res = 0, div, rem;
++	int shift;
++
++	/* can a * b overflow ? */
++	if (ilog2(a) + ilog2(b) > 62) {
++		/*
++		 * (b * a) / c is equal to
++		 *
++		 *      (b / c) * a +
++		 *      (b % c) * a / c
++		 *
++		 * if nothing overflows. Can the 1st multiplication
++		 * overflow? Yes, but we do not care: this can only
++		 * happen if the end result can't fit in u64 anyway.
++		 *
++		 * So the code below does
++		 *
++		 *      res = (b / c) * a;
++		 *      b = b % c;
++		 */
++		div = div64_u64_rem(b, c, &rem);
++		res = div * a;
++		b = rem;
++
++		shift = ilog2(a) + ilog2(b) - 62;
++		if (shift > 0) {
++			/* drop precision */
++			b >>= shift;
++			c >>= shift;
++			if (!c)
++				return res;
++		}
++	}
++
++	return res + div64_u64(a * b, c);
++}
++EXPORT_SYMBOL(mul_u64_u64_div_u64);
++#endif
+-- 
+2.18.0
\ No newline at end of file
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
index 4e8e55c..31ffdf1 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
@@ -57,6 +57,7 @@
     file://999-1715-v6.2-net-dsa-add-set-queue-mapping.patch \
     file://999-1716-v6.6-net-phy-add-phylink-pcs_enable-and-pcs_disable.patch;apply=no \
     file://999-1717-v5.12-net-phy-sfp-add-debugfs-support.patch \
+    file://999-1719-v6.2-net-ptp-introduce-adjust-by-scaled-ppm.patch \
     file://999-1750-v5.18-net-macsec-get-ready-to-backport-from-5-18.patch \
     file://999-1751-01-v5.18-net-macsec-move-some-definitions-in-a-dedicated-header.patch \
     file://999-1752-02-v5.18-net-macsec-introduce-the-macsec_context-structure.patch \