+ }
+
+ if (mtk_is_netsys_v3_or_greater(eth) && (mac->sgmii_pcs || mac->usxgmii_pcs)) {
-+ mac->pextp = devm_of_phy_get(eth->dev, mac->of_node, NULL);
++ mac->pextp = devm_of_phy_optional_get(eth->dev, mac->of_node, NULL);
+ if (IS_ERR(mac->pextp)) {
+ if (PTR_ERR(mac->pextp) != -EPROBE_DEFER)
+ dev_err(eth->dev, "cannot get PHY, error %ld\n",
--- /dev/null
+From 6e9ec5ade644eeb136c6b827d72fac80bf2c3817 Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Fri, 9 May 2025 13:22:14 +0800
+Subject: [PATCH] net: pcs: mtk_lynxi add mt7987 support
+
+Signed-off-by: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+---
+ drivers/net/pcs/pcs-mtk-lynxi.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/pcs/pcs-mtk-lynxi.c
++++ b/drivers/net/pcs/pcs-mtk-lynxi.c
+@@ -413,9 +413,12 @@ static int mtk_pcs_lynxi_probe(struct pl
+ if (of_property_read_bool(np->parent, "mediatek,pnswap"))
+ flags |= MTK_SGMII_FLAG_PN_SWAP;
+
+- mpcs->rstc = of_reset_control_get_shared(np->parent, NULL);
+- if (IS_ERR(mpcs->rstc))
+- return PTR_ERR(mpcs->rstc);
++ if (of_parse_phandle(np->parent, "resets", 0)) {
++ mpcs->rstc = of_reset_control_get_shared(np->parent, NULL);
++ if (IS_ERR(mpcs->rstc))
++ return PTR_ERR(mpcs->rstc);
++ } else
++ mpcs->rstc = NULL;
+
+ reset_control_deassert(mpcs->rstc);
+ mpcs->sgmii_sel = devm_clk_get_enabled(dev, "sgmii_sel");
+@@ -462,6 +465,7 @@ static void mtk_pcs_lynxi_remove(struct
+ }
+
+ static const struct of_device_id mtk_pcs_lynxi_of_match[] = {
++ { .compatible = "mediatek,mt7987-sgmii", .data = (void *)MTK_NETSYS_V3_AMA_RGC3 },
+ { .compatible = "mediatek,mt7988-sgmii", .data = (void *)MTK_NETSYS_V3_AMA_RGC3 },
+ { /* sentinel */ },
+ };
--- /dev/null
+From be193994deca7cc3ca6ddedc6efd06182b032f21 Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Tue, 6 May 2025 12:53:37 +0800
+Subject: [PATCH] net: pcs: mtk-lynxi: add phya tx rx clock path
+
+In NETSYSv3.1, the SGMII hardware introduces a new clock path from PHYA.
+Consequently, users can switch the SGMII PCS to this new clock source
+for better performance on the MT7987.
+
+Signed-off-by: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+---
+--- a/drivers/net/pcs/pcs-mtk-lynxi.c
++++ b/drivers/net/pcs/pcs-mtk-lynxi.c
+@@ -25,6 +25,7 @@
+ #define SGMSYS_PCS_CONTROL_1 0x0
+ #define SGMII_BMCR GENMASK(15, 0)
+ #define SGMII_BMSR GENMASK(31, 16)
++#define SGMII_REF_CK_SEL BIT(24)
+
+ #define SGMSYS_PCS_DEVICE_ID 0x4
+ #define SGMII_LYNXI_DEV_ID 0x4d544950
+@@ -52,6 +53,8 @@
+ #define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
+ #define SGMII_DUPLEX_HALF BIT(4)
+ #define SGMII_REMOTE_FAULT_DIS BIT(8)
++#define SGMII_TRXBUF_THR_MASK GENMASK(31, 16)
++#define SGMII_TRXBUF_THR(x) FIELD_PREP(SGMII_TRXBUF_THR_MASK, (x))
+
+ /* Register to reset SGMII design */
+ #define SGMSYS_RESERVED_0 0x34
+@@ -166,7 +169,7 @@ static int mtk_pcs_lynxi_config(struct p
+ {
+ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
+ bool mode_changed = false, changed;
+- unsigned int rgc3, sgm_mode, bmcr = 0;
++ unsigned int rgc3, sgm_mode, bmcr = 0, trxbuf_thr = 0x3112;
+ int advertise, link_timer;
+
+ advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
+@@ -193,6 +196,12 @@ static int mtk_pcs_lynxi_config(struct p
+ bmcr = BMCR_ANENABLE;
+ }
+
++ /* Configure SGMII PCS clock source */
++ if (mpcs->flags & MTK_SGMII_FLAG_PHYA_TRX_CK) {
++ bmcr |= SGMII_REF_CK_SEL;
++ trxbuf_thr = 0x2111;
++ }
++
+ if (mpcs->interface != interface) {
+ link_timer = phylink_get_link_timer_ns(interface);
+ if (link_timer < 0)
+@@ -235,12 +244,14 @@ static int mtk_pcs_lynxi_config(struct p
+
+ /* Update the sgmsys mode register */
+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
++ SGMII_TRXBUF_THR_MASK |
+ SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
+- SGMII_IF_MODE_SGMII, sgm_mode);
++ SGMII_IF_MODE_SGMII,
++ SGMII_TRXBUF_THR(trxbuf_thr) | sgm_mode);
+
+ /* Update the BMCR */
+ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
+- BMCR_ANENABLE, bmcr);
++ SGMII_REF_CK_SEL | BMCR_ANENABLE, bmcr);
+
+ /* Release PHYA power down state
+ * Only removing bit SGMII_PHYA_PWD isn't enough.
+@@ -413,6 +424,9 @@ static int mtk_pcs_lynxi_probe(struct pl
+ if (of_property_read_bool(np->parent, "mediatek,pnswap"))
+ flags |= MTK_SGMII_FLAG_PN_SWAP;
+
++ if (of_property_read_bool(np->parent, "mediatek,phya_trx_ck"))
++ flags |= MTK_SGMII_FLAG_PHYA_TRX_CK;
++
+ if (of_parse_phandle(np->parent, "resets", 0)) {
+ mpcs->rstc = of_reset_control_get_shared(np->parent, NULL);
+ if (IS_ERR(mpcs->rstc))
+--- a/include/linux/pcs/pcs-mtk-lynxi.h
++++ b/include/linux/pcs/pcs-mtk-lynxi.h
+@@ -6,6 +6,7 @@
+ #include <linux/regmap.h>
+
+ #define MTK_SGMII_FLAG_PN_SWAP BIT(0)
++#define MTK_SGMII_FLAG_PHYA_TRX_CK BIT(2)
+ struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
+ struct regmap *regmap,
+ u32 ana_rgc3, u32 flags);
--- /dev/null
+From 56973433cbea9f91f5f7eddebbc361ffc2bd6156 Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Mon, 26 May 2025 13:20:42 +0800
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add mt7987 support
+
+Without this patch, users are unable to bring up ETH driver on the
+mt7987.
+
+Signed-off-by: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_path.c | 9 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 138 ++++++++++++++++---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 70 ++++++++--
+ 3 files changed, 179 insertions(+), 38 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_path.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c
+@@ -106,13 +106,14 @@ static int set_mux_gmac2_gmac0_to_gephy(
+ 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;
+@@ -281,9 +282,9 @@ static const struct mtk_eth_muxc mtk_eth
+ .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,
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -817,10 +817,16 @@ static void mtk_set_queue_speed(struct m
+ return;
+
+ val = MTK_QTX_SCH_MIN_RATE_EN |
+- /* minimum: 10 Mbps */
+- FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
+- FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) |
+ MTK_QTX_SCH_LEAKY_BUCKET_SIZE;
++ /* minimum: 10 Mbps */
++ if (mtk_is_netsys_v3_or_greater(eth) &&
++ (eth->soc->caps != MT7988_CAPS)) {
++ val |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN_V3, 1) |
++ FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP_V3, 4);
++ } else {
++ val |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
++ FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4);
++ }
+ if (mtk_is_netsys_v1(eth))
+ val |= MTK_QTX_SCH_LEAKY_BUCKET_EN;
+
+@@ -847,6 +853,30 @@ static void mtk_set_queue_speed(struct m
+ default:
+ break;
+ }
++ } else if (mtk_is_netsys_v3_or_greater(eth) &&
++ (eth->soc->caps != MT7988_CAPS)) {
++ switch (speed) {
++ case SPEED_10:
++ val |= MTK_QTX_SCH_MAX_RATE_EN_V3 |
++ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN_V3, 1) |
++ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP_V3, 4) |
++ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT_V3, 1);
++ break;
++ case SPEED_100:
++ val |= MTK_QTX_SCH_MAX_RATE_EN_V3 |
++ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN_V3, 1) |
++ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP_V3, 5) |
++ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT_V3, 1);
++ break;
++ case SPEED_1000:
++ val |= MTK_QTX_SCH_MAX_RATE_EN_V3 |
++ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN_V3, 1) |
++ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP_V3, 6) |
++ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT_V3, 10);
++ break;
++ default:
++ break;
++ }
+ } else {
+ switch (speed) {
+ case SPEED_10:
+@@ -935,7 +965,7 @@ static void mtk_xgdm_mac_link_up(struct
+ return;
+
+ /* Eliminate the interference(before link-up) caused by PHY noise */
+- mtk_m32(mac->hw, XMAC_LOGIC_RST, 0, MTK_XMAC_LOGIC_RST(mac->id));
++ mtk_m32(mac->hw, XMAC_LOGIC_RST, 0, MTK_XMAC_LOGIC_RST(mac->hw, mac->id));
+ mdelay(20);
+ mtk_m32(mac->hw, XMAC_GLB_CNTCLR, XMAC_GLB_CNTCLR, MTK_XMAC_CNT_CTRL(mac->id));
+
+@@ -2901,10 +2931,16 @@ static int mtk_tx_alloc(struct mtk_eth *
+ mtk_w32(eth, val, soc->reg_map->qdma.qtx_cfg + ofs);
+
+ val = MTK_QTX_SCH_MIN_RATE_EN |
+- /* minimum: 10 Mbps */
+- FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
+- FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) |
+ MTK_QTX_SCH_LEAKY_BUCKET_SIZE;
++ /* minimum: 10 Mbps */
++ if (mtk_is_netsys_v3_or_greater(eth) &&
++ (eth->soc->caps != MT7988_CAPS)) {
++ val |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN_V3, 1) |
++ FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP_V3, 4);
++ } else {
++ val |= FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
++ FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4);
++ }
+ if (mtk_is_netsys_v1(eth))
+ val |= MTK_QTX_SCH_LEAKY_BUCKET_EN;
+ mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs);
+@@ -5873,6 +5909,36 @@ static const struct mtk_soc_data mt7986_
+ },
+ };
+
++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 = MT7987_CLKS_BITMAP,
++ .required_pctl = false,
++ .version = 3,
++ .offload_version = 2,
++ .ppe_num = 2,
++ .hash_offset = 4,
++ .has_accounting = true,
++ .foe_entry_size = MTK_FOE_ENTRY_V3_SIZE,
++ .tx = {
++ DESC_SIZE(struct mtk_tx_dma_v2),
++ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
++ .dma_len_offset = 8,
++ .dma_size = MTK_DMA_SIZE(2K),
++ .fq_dma_size = MTK_DMA_SIZE(4K),
++ },
++ .rx = {
++ DESC_SIZE(struct mtk_rx_dma_v2),
++ .irq_done_mask = MTK_RX_DONE_INT_V2,
++ .dma_l4_valid = RX_DMA_L4_VALID_V2,
++ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
++ .dma_len_offset = 8,
++ .dma_size = MTK_DMA_SIZE(2K),
++ },
++};
++
+ static const struct mtk_soc_data mt7988_data = {
+ .reg_map = &mt7988_reg_map,
+ .ana_rgc3 = 0x128,
+@@ -5934,6 +6000,7 @@ const struct of_device_id of_mtk_match[]
+ { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data },
+ { .compatible = "mediatek,mt7981-eth", .data = &mt7981_data },
+ { .compatible = "mediatek,mt7986-eth", .data = &mt7986_data },
++ { .compatible = "mediatek,mt7987-eth", .data = &mt7987_data },
+ { .compatible = "mediatek,mt7988-eth", .data = &mt7988_data },
+ { .compatible = "ralink,rt5350-eth", .data = &rt5350_data },
+ {},
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -262,6 +262,13 @@
+ #define MTK_QTX_SCH_MAX_RATE_MAN GENMASK(10, 4)
+ #define MTK_QTX_SCH_MAX_RATE_EXP GENMASK(3, 0)
+
++#define MTK_QTX_SCH_MAX_RATE_EN_V3 BIT(26)
++#define MTK_QTX_SCH_MIN_RATE_MAN_V3 GENMASK(25, 19)
++#define MTK_QTX_SCH_MIN_RATE_EXP_V3 GENMASK(18, 16)
++#define MTK_QTX_SCH_MAX_RATE_WEIGHT_V3 GENMASK(15, 10)
++#define MTK_QTX_SCH_MAX_RATE_MAN_V3 GENMASK(9, 3)
++#define MTK_QTX_SCH_MAX_RATE_EXP_V3 GENMASK(2, 0)
++
+ /* QDMA TX Scheduler Rate Control Register */
+ #define MTK_QDMA_TX_SCH_MAX_WFQ BIT(15)
+
+@@ -536,9 +543,23 @@
+ #define XMAC_MCR_FORCE_RX_FC BIT(4)
+
+ /* XFI Mac logic reset registers */
+-#define MTK_XMAC_LOGIC_RST(x) (MTK_XMAC_BASE(x) + 0x10)
++#define MTK_XMAC_LOGIC_RST(eth, x) (MTK_XMAC_BASE(x) + \
++ (MTK_HAS_CAPS((eth)->soc->caps, MTK_XGMAC_V2) ? \
++ 0x820 : 0x10))
+ #define XMAC_LOGIC_RST BIT(0)
+
++/* 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_LINK_MODE BIT(8)
++#define XMAC_FORCE_RX_FC BIT(5)
++#define XMAC_FORCE_TX_FC BIT(4)
++#define XMAC_FORCE_LINK BIT(0)
++
+ /* XFI Mac count global control */
+ #define MTK_XMAC_CNT_CTRL(x) (MTK_XMAC_BASE(x) + 0x100)
+ #define XMAC_GLB_CNTCLR BIT(0)
+@@ -834,6 +855,17 @@ enum mtk_clks_map {
+ BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \
+ BIT_ULL(MTK_CLK_SGMII2_CDR_REF) | \
+ BIT_ULL(MTK_CLK_SGMII2_CDR_FB))
++#define MT7987_CLKS_BITMAP (BIT_ULL(MTK_CLK_FE) | BIT_ULL(MTK_CLK_GP1) | \
++ BIT_ULL(MTK_CLK_GP2) | BIT_ULL(MTK_CLK_GP3) | \
++ BIT_ULL(MTK_CLK_TOP_ETH_GMII_SEL) | \
++ BIT_ULL(MTK_CLK_TOP_ETH_REFCK_50M_SEL) | \
++ BIT_ULL(MTK_CLK_TOP_ETH_SYS_200M_SEL) | \
++ BIT_ULL(MTK_CLK_TOP_ETH_SYS_SEL) | \
++ BIT_ULL(MTK_CLK_TOP_ETH_XGMII_SEL) | \
++ BIT_ULL(MTK_CLK_TOP_ETH_MII_SEL) | \
++ BIT_ULL(MTK_CLK_TOP_NETSYS_SEL) | \
++ BIT_ULL(MTK_CLK_TOP_NETSYS_500M_SEL) | \
++ BIT_ULL(MTK_CLK_TOP_NETSYS_PAO_2X_SEL))
+ #define MT7988_CLKS_BITMAP (BIT_ULL(MTK_CLK_FE) | BIT_ULL(MTK_CLK_ESW) | \
+ BIT_ULL(MTK_CLK_GP1) | BIT_ULL(MTK_CLK_GP2) | \
+ BIT_ULL(MTK_CLK_GP3) | BIT_ULL(MTK_CLK_XGP1) | \
+@@ -990,12 +1022,14 @@ enum mkt_eth_capabilities {
+ MTK_RSTCTRL_PPE2_BIT,
+ MTK_U3_COPHY_V2_BIT,
+ MTK_SRAM_BIT,
++ MTK_XGMAC_BIT,
++ MTK_XGMAC_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,
+@@ -1037,14 +1071,16 @@ enum mkt_eth_capabilities {
+ #define MTK_RSTCTRL_PPE2 BIT_ULL(MTK_RSTCTRL_PPE2_BIT)
+ #define MTK_U3_COPHY_V2 BIT_ULL(MTK_U3_COPHY_V2_BIT)
+ #define MTK_SRAM BIT_ULL(MTK_SRAM_BIT)
++#define MTK_XGMAC BIT_ULL(MTK_XGMAC_BIT)
++#define MTK_XGMAC_V2 BIT_ULL(MTK_XGMAC_V2_BIT)
+ #define MTK_36BIT_DMA BIT_ULL(MTK_36BIT_DMA_BIT)
+
+ #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \
+ 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 \
+@@ -1076,12 +1112,13 @@ enum mkt_eth_capabilities {
+ #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_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY)
+-#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_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)
+
+ /* MUXes present on SoCs */
+ /* 0: GDM1 -> GMAC1, 1: GDM1 -> ESW */
+@@ -1091,9 +1128,9 @@ enum mkt_eth_capabilities {
+ #define MTK_MUX_GMAC2_GMAC0_TO_GEPHY \
+ (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)
++/* 0: U3 -> QPHY, 1: GMACx -> QPHY where x is 2 or 3 */
++#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 \
+@@ -1133,18 +1170,24 @@ enum mkt_eth_capabilities {
+ #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_MUX_U3_GMAC23_TO_QPHY | \
+ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA)
+
+ #define MT7981_CAPS (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_MUX_U3_GMAC23_TO_QPHY | MTK_U3_COPHY_V2 | \
+ MTK_RSTCTRL_PPE1 | MTK_SRAM)
+
+ #define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \
+ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
+ MTK_RSTCTRL_PPE1 | MTK_SRAM)
+
++#define MT7987_CAPS (MTK_36BIT_DMA | MTK_GMAC1_SGMII | \
++ MTK_GMAC2_2P5GPHY_V2 | MTK_GMAC2_SGMII | MTK_GMAC3_SGMII | \
++ MTK_MUX_GMAC123_TO_GEPHY_SGMII | MTK_MUX_GMAC2_TO_2P5GPHY | \
++ MTK_MUX_U3_GMAC23_TO_QPHY | MTK_U3_COPHY_V2 | \
++ MTK_QDMA | MTK_RSTCTRL_PPE1)
++
+ #define MT7988_CAPS (MTK_36BIT_DMA | MTK_GDM1_ESW | MTK_GMAC1_SGMII | \
+ MTK_GMAC2_2P5GPHY | MTK_GMAC2_SGMII | MTK_GMAC2_USXGMII | \
+ MTK_GMAC3_SGMII | MTK_GMAC3_USXGMII | \
--- /dev/null
+From 5ef0b04d30efff8f171e30bfbe876c00e3b9036a Mon Sep 17 00:00:00 2001
+From: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+Date: Fri, 9 May 2025 09:49:04 +0800
+Subject: [PATCH] net: ethernet: mtk_eth_soc: revise hardware configuration for
+ mt7987
+
+Change hardware configuration for the MT7987.
+ - Enable PSE drop mechanism when the WDMA Rx ring full
+ - Enable PSE no-drop mechanism for packets from the WDMA Tx
+
+Signed-off-by: Bo-Cun Chen <bc-bocun.chen@mediatek.com>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 49 +++++++++++++--------
+ 1 file changed, 31 insertions(+), 18 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -4445,27 +4445,40 @@ static int mtk_hw_init(struct mtk_eth *e
+ mtk_w32(eth, PSE_DUMMY_WORK_GDM(1) | PSE_DUMMY_WORK_GDM(2) |
+ PSE_DUMMY_WORK_GDM(3) | DUMMY_PAGE_THR, PSE_DUMY_REQ);
+
+- /* PSE free buffer drop threshold */
+- mtk_w32(eth, 0x00600009, PSE_IQ_REV(8));
+-
+- /* PSE should not drop port8, port9 and port13 packets from
+- * WDMA Tx
+- */
+- mtk_w32(eth, 0x00002300, PSE_DROP_CFG);
+-
+- /* PSE should drop packets to port8, port9 and port13 on WDMA Rx
+- * ring full
+- */
+- mtk_w32(eth, 0x00002300, PSE_PPE_DROP(0));
+- mtk_w32(eth, 0x00002300, PSE_PPE_DROP(1));
+- mtk_w32(eth, 0x00002300, PSE_PPE_DROP(2));
+-
+- /* GDM and CDM Threshold */
+- mtk_w32(eth, 0x08000707, MTK_CDMW0_THRES);
+- mtk_w32(eth, 0x00000077, MTK_CDMW1_THRES);
+-
+- /* Disable GDM1 RX CRC stripping */
+- mtk_m32(eth, MTK_GDMA_STRP_CRC, 0, MTK_GDMA_FWD_CFG(0));
++ if (eth->soc->caps == MT7988_CAPS) {
++ /* PSE free buffer drop threshold */
++ mtk_w32(eth, 0x00600009, PSE_IQ_REV(8));
++
++ /* PSE should not drop port8, port9 and port13 packets
++ * from WDMA Tx
++ */
++ mtk_w32(eth, 0x00002300, PSE_DROP_CFG);
++
++ /* PSE should drop packets to port8, port9 and port13
++ * on WDMA Rx ring full
++ */
++ mtk_w32(eth, 0x00002300, PSE_PPE_DROP(0));
++ mtk_w32(eth, 0x00002300, PSE_PPE_DROP(1));
++ mtk_w32(eth, 0x00002300, PSE_PPE_DROP(2));
++
++ /* 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) {
++ /* PSE should not drop port8 packets from WDMA Tx */
++ mtk_w32(eth, 0x00000100, PSE_DROP_CFG);
++
++ /* PSE should drop packets to port8 on WDMA Rx ring
++ * full
++ */
++ mtk_w32(eth, 0x00000100, PSE_PPE_DROP(0));
++ mtk_w32(eth, 0x00000100, PSE_PPE_DROP(1));
++ }
++
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_ESW)) {
++ /* Disable GDM1 RX CRC stripping */
++ mtk_m32(eth, MTK_GDMA_STRP_CRC, 0, MTK_GDMA_FWD_CFG(0));
++ }
+
+ /* PSE GDM3 MIB counter has incorrect hw default values,
+ * so the driver ought to read clear the values beforehand
--- /dev/null
+--- a/drivers/net/phy/mediatek/mtk-2p5ge.c
++++ b/drivers/net/phy/mediatek/mtk-2p5ge.c
+@@ -12,13 +12,77 @@
+
+ #include "mtk.h"
+
++#define MTK_2P5GPHY_ID_MT7987 (0x00339c91)
+ #define MTK_2P5GPHY_ID_MT7988 (0x00339c11)
+
++#define MT7987_2P5GE_PMB_FW "mediatek/mt7987/i2p5ge-phy-pmb.bin"
++#define MT7987_2P5GE_PMB_FW_SIZE (0x18000)
++#define MT7987_2P5GE_DSPBITTB \
++ "mediatek/mt7987/i2p5ge-phy-DSPBitTb.bin"
++#define MT7987_2P5GE_DSPBITTB_SIZE (0x7000)
++
+ #define MT7988_2P5GE_PMB_FW "mediatek/mt7988/i2p5ge-phy-pmb.bin"
+ #define MT7988_2P5GE_PMB_FW_SIZE (0x20000)
++
++#define MTK_2P5GPHY_PMD_REG_BASE (0x0f010000)
++#define MTK_2P5GPHY_PMD_REG_LEN (0x210)
++#define DO_NOT_RESET (0x28)
++#define DO_NOT_RESET_XBZ BIT(0)
++#define DO_NOT_RESET_PMA BIT(3)
++#define DO_NOT_RESET_RX BIT(5)
++#define FNPLL_PWR_CTRL1 (0x208)
++#define RG_SPEED_MASK GENMASK(3, 0)
++#define RG_SPEED_2500 BIT(3)
++#define RG_SPEED_100 BIT(0)
++#define FNPLL_PWR_CTRL_STATUS (0x20c)
++#define RG_STABLE_MASK GENMASK(3, 0)
++#define RG_SPEED_2500_STABLE BIT(3)
++#define RG_SPEED_100_STABLE BIT(0)
++
++#define MTK_2P5GPHY_XBZ_PCS_REG_BASE (0x0f030000)
++#define MTK_2P5GPHY_XBZ_PCS_REG_LEN (0x844)
++#define PHY_CTRL_CONFIG (0x200)
++#define PMU_WP (0x800)
++#define WRITE_PROTECT_KEY (0xCAFEF00D)
++#define PMU_PMA_AUTO_CFG (0x820)
++#define POWER_ON_AUTO_MODE BIT(16)
++#define PMU_AUTO_MODE_EN BIT(0)
++#define PMU_PMA_STATUS (0x840)
++#define CLK_IS_DISABLED BIT(3)
++
++#define MTK_2P5GPHY_XBZ_PMA_RX_BASE (0x0f080000)
++#define MTK_2P5GPHY_XBZ_PMA_RX_LEN (0x5228)
++#define SMEM_WDAT0 (0x5000)
++#define SMEM_WDAT1 (0x5004)
++#define SMEM_WDAT2 (0x5008)
++#define SMEM_WDAT3 (0x500c)
++#define SMEM_CTRL (0x5024)
++#define SMEM_HW_RDATA_ZERO BIT(24)
++#define SMEM_ADDR_REF_ADDR (0x502c)
++#define CM_CTRL_P01 (0x5100)
++#define CM_CTRL_P23 (0x5124)
++#define DM_CTRL_P01 (0x5200)
++#define DM_CTRL_P23 (0x5224)
++
++#define MTK_2P5GPHY_CHIP_SCU_BASE (0x0f0cf800)
++#define MTK_2P5GPHY_CHIP_SCU_LEN (0x12c)
++#define SYS_SW_RESET (0x128)
++#define RESET_RST_CNT BIT(0)
++
++#define MTK_2P5GPHY_MCU_CSR_BASE (0x0f0f0000)
++#define MTK_2P5GPHY_MCU_CSR_LEN (0x20)
+ #define MD32_EN_CFG (0x18)
+ #define MD32_EN BIT(0)
+
++#define MTK_2P5GPHY_PMB_FW_BASE (0x0f100000)
++//#define MTK_2P5GPHY_PMB_FW_LEN MT7988_2P5GE_PMB_FW_SIZE
++
++#define MTK_2P5GPHY_APB_BASE (0x11c30000)
++#define MTK_2P5GPHY_APB_LEN (0x9c)
++#define SW_RESET (0x94)
++#define MD32_RESTART_EN_CLEAR BIT(9)
++
++
+ #define BASE100T_STATUS_EXTEND (0x10)
+ #define BASE1000T_STATUS_EXTEND (0x11)
+ #define EXTEND_CTRL_AND_STATUS (0x16)
+@@ -31,6 +95,14 @@
+ #define MTK_PHY_LPI_PCS_DSP_CTRL (0x121)
+ #define MTK_PHY_LPI_SIG_EN_LO_THRESH100_MASK GENMASK(12, 8)
+
++#define MTK_PHY_LINK_STATUS_RELATED (0x147)
++#define MTK_PHY_BYPASS_LINK_STATUS_OK BIT(4)
++#define MTK_PHY_FORCE_LINK_STATUS_HCD BIT(3)
++
++#define MTK_PHY_AN_FORCE_SPEED_REG (0x313)
++#define MTK_PHY_MASTER_FORCE_SPEED_SEL_EN BIT(7)
++#define MTK_PHY_MASTER_FORCE_SPEED_SEL_MASK GENMASK(6, 0)
++
+ #define MTK_PHY_HOST_CMD1 0x800e
+ #define MTK_PHY_HOST_CMD2 0x800f
+ /* Registers on Token Ring debug nodes */
+@@ -48,7 +120,249 @@ enum {
+ PHY_AUX_SPD_2500,
+ };
+
+-static int mt798x_2p5ge_phy_load_fw(struct phy_device *phydev)
++static int mt7987_2p5ge_phy_load_fw(struct phy_device *phydev)
++{
++ struct mtk_i2p5ge_phy_priv *priv = phydev->priv;
++ struct device *dev = &phydev->mdio.dev;
++ void __iomem *xbz_pcs_reg_base;
++ void __iomem *xbz_pma_rx_base;
++ void __iomem *chip_scu_base;
++ void __iomem *pmd_reg_base;
++ void __iomem *mcu_csr_base;
++ const struct firmware *fw;
++ void __iomem *apb_base;
++ void __iomem *pmb_addr;
++ int ret, i;
++ u32 reg;
++
++ if (priv->fw_loaded)
++ return 0;
++
++ apb_base = ioremap(MTK_2P5GPHY_APB_BASE,
++ MTK_2P5GPHY_APB_LEN);
++ if (!apb_base)
++ return -ENOMEM;
++
++ pmd_reg_base = ioremap(MTK_2P5GPHY_PMD_REG_BASE,
++ MTK_2P5GPHY_PMD_REG_LEN);
++ if (!pmd_reg_base) {
++ ret = -ENOMEM;
++ goto free_apb;
++ }
++
++ xbz_pcs_reg_base = ioremap(MTK_2P5GPHY_XBZ_PCS_REG_BASE,
++ MTK_2P5GPHY_XBZ_PCS_REG_LEN);
++ if (!xbz_pcs_reg_base) {
++ ret = -ENOMEM;
++ goto free_pmd;
++ }
++
++ xbz_pma_rx_base = ioremap(MTK_2P5GPHY_XBZ_PMA_RX_BASE,
++ MTK_2P5GPHY_XBZ_PMA_RX_LEN);
++ if (!xbz_pma_rx_base) {
++ ret = -ENOMEM;
++ goto free_pcs;
++ }
++
++ chip_scu_base = ioremap(MTK_2P5GPHY_CHIP_SCU_BASE,
++ MTK_2P5GPHY_CHIP_SCU_LEN);
++ if (!chip_scu_base) {
++ ret = -ENOMEM;
++ goto free_pma;
++ }
++
++ mcu_csr_base = ioremap(MTK_2P5GPHY_MCU_CSR_BASE,
++ MTK_2P5GPHY_MCU_CSR_LEN);
++ if (!mcu_csr_base) {
++ ret = -ENOMEM;
++ goto free_chip_scu;
++ }
++
++ pmb_addr = ioremap(MTK_2P5GPHY_PMB_FW_BASE, MT7987_2P5GE_PMB_FW_SIZE);
++ if (!pmb_addr) {
++ return -ENOMEM;
++ goto free_mcu_csr;
++ }
++
++ ret = request_firmware(&fw, MT7987_2P5GE_PMB_FW, dev);
++ if (ret) {
++ dev_err(dev, "failed to load firmware: %s, ret: %d\n",
++ MT7987_2P5GE_PMB_FW, ret);
++ goto free_pmb_addr;
++ }
++
++ if (fw->size != MT7987_2P5GE_PMB_FW_SIZE) {
++ dev_err(dev, "PMb firmware size 0x%zx != 0x%x\n",
++ fw->size, MT7987_2P5GE_PMB_FW_SIZE);
++ ret = -EINVAL;
++ goto release_fw;
++ }
++
++ /* Force 2.5Gphy back to AN state */
++ phy_set_bits(phydev, MII_BMCR, BMCR_RESET);
++ usleep_range(5000, 6000);
++ phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN);
++
++ reg = readw(apb_base + SW_RESET);
++ writew(reg & ~MD32_RESTART_EN_CLEAR, apb_base + SW_RESET);
++ writew(reg | MD32_RESTART_EN_CLEAR, apb_base + SW_RESET);
++ writew(reg & ~MD32_RESTART_EN_CLEAR, apb_base + SW_RESET);
++
++ reg = readw(mcu_csr_base + MD32_EN_CFG);
++ writew(reg & ~MD32_EN, mcu_csr_base + MD32_EN_CFG);
++
++ for (i = 0; i < MT7987_2P5GE_PMB_FW_SIZE - 1; i += 4)
++ writel(*((uint32_t *)(fw->data + i)), pmb_addr + i);
++ dev_info(dev, "Firmware date code: %x/%x/%x, version: %x.%x\n",
++ be16_to_cpu(*((__be16 *)(fw->data +
++ MT7987_2P5GE_PMB_FW_SIZE - 8))),
++ *(fw->data + MT7987_2P5GE_PMB_FW_SIZE - 6),
++ *(fw->data + MT7987_2P5GE_PMB_FW_SIZE - 5),
++ *(fw->data + MT7987_2P5GE_PMB_FW_SIZE - 2),
++ *(fw->data + MT7987_2P5GE_PMB_FW_SIZE - 1));
++ release_firmware(fw);
++
++ /* Enable 100Mbps module clock. */
++ writel(FIELD_PREP(RG_SPEED_MASK, RG_SPEED_100),
++ pmd_reg_base + FNPLL_PWR_CTRL1);
++
++ /* Check if 100Mbps module clock is ready. */
++ ret = readl_poll_timeout(pmd_reg_base + FNPLL_PWR_CTRL_STATUS, reg,
++ reg & RG_SPEED_100_STABLE, 1, 10000);
++ if (ret)
++ dev_err(dev, "Fail to enable 100Mbps module clock: %d\n", ret);
++
++ /* Enable 2.5Gbps module clock. */
++ writel(FIELD_PREP(RG_SPEED_MASK, RG_SPEED_2500),
++ pmd_reg_base + FNPLL_PWR_CTRL1);
++
++ /* Check if 2.5Gbps module clock is ready. */
++ ret = readl_poll_timeout(pmd_reg_base + FNPLL_PWR_CTRL_STATUS, reg,
++ reg & RG_SPEED_2500_STABLE, 1, 10000);
++
++ if (ret)
++ dev_err(dev, "Fail to enable 2.5Gbps module clock: %d\n", ret);
++
++ /* Disable AN */
++ phy_clear_bits(phydev, MII_BMCR, BMCR_ANENABLE);
++
++ /* Force to run at 2.5G speed */
++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_AN_FORCE_SPEED_REG,
++ MTK_PHY_MASTER_FORCE_SPEED_SEL_MASK,
++ MTK_PHY_MASTER_FORCE_SPEED_SEL_EN |
++ FIELD_PREP(MTK_PHY_MASTER_FORCE_SPEED_SEL_MASK, 0x1b));
++
++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_RELATED,
++ MTK_PHY_BYPASS_LINK_STATUS_OK |
++ MTK_PHY_FORCE_LINK_STATUS_HCD);
++
++ /* Set xbz, pma and rx as "do not reset" in order to input DSP code. */
++ reg = readl(pmd_reg_base + DO_NOT_RESET);
++ reg |= DO_NOT_RESET_XBZ | DO_NOT_RESET_PMA | DO_NOT_RESET_RX;
++ writel(reg, pmd_reg_base + DO_NOT_RESET);
++
++ reg = readl(chip_scu_base + SYS_SW_RESET);
++ writel(reg & ~RESET_RST_CNT, chip_scu_base + SYS_SW_RESET);
++
++ writel(WRITE_PROTECT_KEY, xbz_pcs_reg_base + PMU_WP);
++
++ reg = readl(xbz_pcs_reg_base + PMU_PMA_AUTO_CFG);
++ reg |= PMU_AUTO_MODE_EN | POWER_ON_AUTO_MODE;
++ writel(reg, xbz_pcs_reg_base + PMU_PMA_AUTO_CFG);
++
++ /* Check if clock in auto mode is disabled. */
++ ret = readl_poll_timeout(xbz_pcs_reg_base + PMU_PMA_STATUS, reg,
++ (reg & CLK_IS_DISABLED) == 0x0, 1, 100000);
++ if (ret)
++ dev_err(dev, "Clock isn't disabled in auto mode: %d\n", ret);
++
++ reg = readl(xbz_pma_rx_base + SMEM_CTRL);
++ writel(reg | SMEM_HW_RDATA_ZERO, xbz_pma_rx_base + SMEM_CTRL);
++
++ reg = readl(xbz_pcs_reg_base + PHY_CTRL_CONFIG);
++ writel(reg | BIT(16), xbz_pcs_reg_base + PHY_CTRL_CONFIG);
++
++ /* Initialize data memory */
++ reg = readl(xbz_pma_rx_base + DM_CTRL_P01);
++ writel(reg | BIT(28), xbz_pma_rx_base + DM_CTRL_P01);
++ reg = readl(xbz_pma_rx_base + DM_CTRL_P23);
++ writel(reg | BIT(28), xbz_pma_rx_base + DM_CTRL_P23);
++
++ /* Initialize coefficient memory */
++ reg = readl(xbz_pma_rx_base + CM_CTRL_P01);
++ writel(reg | BIT(28), xbz_pma_rx_base + CM_CTRL_P01);
++ reg = readl(xbz_pma_rx_base + CM_CTRL_P23);
++ writel(reg | BIT(28), xbz_pma_rx_base + CM_CTRL_P23);
++
++ /* Initilize PM offset */
++ writel(0, xbz_pma_rx_base + SMEM_ADDR_REF_ADDR);
++
++ ret = request_firmware(&fw, MT7987_2P5GE_DSPBITTB, dev);
++ if (ret) {
++ dev_err(dev, "failed to load firmware: %s, ret: %d\n",
++ MT7987_2P5GE_DSPBITTB, ret);
++ goto free_pmb_addr;
++ }
++ if (fw->size != MT7987_2P5GE_DSPBITTB_SIZE) {
++ dev_err(dev, "DSPBITTB size 0x%zx != 0x%x\n",
++ fw->size, MT7987_2P5GE_DSPBITTB_SIZE);
++ ret = -EINVAL;
++ goto release_fw;
++ }
++
++ for (i = 0; i < fw->size - 1; i += 16) {
++ writel(*((uint32_t *)(fw->data + i)),
++ xbz_pma_rx_base + SMEM_WDAT0);
++ writel(*((uint32_t *)(fw->data + i + 0x4)),
++ xbz_pma_rx_base + SMEM_WDAT1);
++ writel(*((uint32_t *)(fw->data + i + 0x8)),
++ xbz_pma_rx_base + SMEM_WDAT2);
++ writel(*((uint32_t *)(fw->data + i + 0xc)),
++ xbz_pma_rx_base + SMEM_WDAT3);
++ }
++
++ reg = readl(xbz_pma_rx_base + DM_CTRL_P01);
++ writel(reg & ~BIT(28), xbz_pma_rx_base + DM_CTRL_P01);
++
++ reg = readl(xbz_pma_rx_base + DM_CTRL_P23);
++ writel(reg & ~BIT(28), xbz_pma_rx_base + DM_CTRL_P23);
++
++ reg = readl(xbz_pma_rx_base + CM_CTRL_P01);
++ writel(reg & ~BIT(28), xbz_pma_rx_base + CM_CTRL_P01);
++
++ reg = readl(xbz_pma_rx_base + CM_CTRL_P23);
++ writel(reg & ~BIT(28), xbz_pma_rx_base + CM_CTRL_P23);
++
++ reg = readw(mcu_csr_base + MD32_EN_CFG);
++ writew(reg | MD32_EN, mcu_csr_base + MD32_EN_CFG);
++ phy_set_bits(phydev, MII_BMCR, BMCR_RESET);
++ /* We need a delay here to stabilize initialization of MCU */
++ usleep_range(7000, 8000);
++ dev_info(dev, "Firmware loading/trigger ok.\n");
++
++ priv->fw_loaded = true;
++
++release_fw:
++ release_firmware(fw);
++free_pmb_addr:
++ iounmap(pmb_addr);
++free_mcu_csr:
++ iounmap(mcu_csr_base);
++free_chip_scu:
++ iounmap(chip_scu_base);
++free_pma:
++ iounmap(xbz_pma_rx_base);
++free_pcs:
++ iounmap(xbz_pcs_reg_base);
++free_pmd:
++ iounmap(pmd_reg_base);
++free_apb:
++ iounmap(apb_base);
++
++ return ret;
++}
++
++static int mt7988_2p5ge_phy_load_fw(struct phy_device *phydev)
+ {
+ struct mtk_i2p5ge_phy_priv *priv = phydev->priv;
+ void __iomem *mcu_csr_base, *pmb_addr;
+@@ -135,7 +449,20 @@ static int mt798x_2p5ge_phy_config_init(
+ if (phydev->interface != PHY_INTERFACE_MODE_INTERNAL)
+ return -ENODEV;
+
+- ret = mt798x_2p5ge_phy_load_fw(phydev);
++ switch (phydev->drv->phy_id) {
++ case MTK_2P5GPHY_ID_MT7987:
++ ret = mt7987_2p5ge_phy_load_fw(phydev);
++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
++ MTK_PHY_LED_ON_POLARITY);
++ break;
++ case MTK_2P5GPHY_ID_MT7988:
++ ret = mt7988_2p5ge_phy_load_fw(phydev);
++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
++ MTK_PHY_LED_ON_POLARITY);
++ break;
++ default:
++ return -EINVAL;
++ }
+ if (ret < 0)
+ return ret;
+
+@@ -293,6 +620,7 @@ static int mt798x_2p5ge_phy_probe(struct
+ return -ENOMEM;
+
+ switch (phydev->drv->phy_id) {
++ case MTK_2P5GPHY_ID_MT7987:
+ case MTK_2P5GPHY_ID_MT7988:
+ /* The original hardware only sets MDIO_DEVS_PMAPMD */
+ phydev->c45_ids.mmds_present |= MDIO_DEVS_PCS |
+@@ -312,6 +640,20 @@ static int mt798x_2p5ge_phy_probe(struct
+
+ static struct phy_driver mtk_2p5gephy_driver[] = {
+ {
++ PHY_ID_MATCH_MODEL(MTK_2P5GPHY_ID_MT7987),
++ .name = "MediaTek MT7987 2.5GbE PHY",
++ .probe = mt798x_2p5ge_phy_probe,
++ .config_init = mt798x_2p5ge_phy_config_init,
++ .config_aneg = mt798x_2p5ge_phy_config_aneg,
++ .get_features = mt798x_2p5ge_phy_get_features,
++ .read_status = mt798x_2p5ge_phy_read_status,
++ .get_rate_matching = mt798x_2p5ge_phy_get_rate_matching,
++ .suspend = genphy_suspend,
++ .resume = genphy_resume,
++ .read_page = mtk_phy_read_page,
++ .write_page = mtk_phy_write_page,
++ },
++ {
+ PHY_ID_MATCH_MODEL(MTK_2P5GPHY_ID_MT7988),
+ .name = "MediaTek MT7988 2.5GbE PHY",
+ .probe = mt798x_2p5ge_phy_probe,