[][kernel][common][eth][Fix issue with XGDM hang when reconnecting SFP+ Optical module]

[Description]
Fix issue with XGDM hang when reconnecting SFP+ Optical module.

This patch corrects the down/up sequence of the XGAMC link and adds a
delay between the USXGMII PCS link up and the XGMAC link up to avoid
runt packet issues.

Without this patch, the users may encounter a traffic stuck issue when
reconnecting the SFP+ optical module.

[Release-log]
N/A


Change-Id: I3981638e9be270ae8256cdc4070c319c6cc80e8b
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/8269169
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index bad8511..05bf2d7 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -444,7 +444,7 @@
 
 	/* Force Port1 XGMAC Link Up */
 	val = mtk_r32(eth, MTK_XGMAC_STS(MTK_GMAC1_ID));
-	mtk_w32(eth, val | MTK_XGMAC_FORCE_LINK(MTK_GMAC1_ID),
+	mtk_w32(eth, val | MTK_XGMAC_FORCE_MODE(MTK_GMAC1_ID),
 		MTK_XGMAC_STS(MTK_GMAC1_ID));
 
 	/* Adjust GSW bridge IPG to 11*/
@@ -573,6 +573,28 @@
 	return NULL;
 }
 
+static int mtk_mac_prepare(struct phylink_config *config, unsigned int mode,
+			   phy_interface_t iface)
+{
+	struct mtk_mac *mac = container_of(config, struct mtk_mac,
+					   phylink_config);
+	u32 val;
+
+	if (mac->type == MTK_XGDM_TYPE && mac->id != MTK_GMAC1_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));
+	}
+
+	return 0;
+}
+
 static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
 			   const struct phylink_link_state *state)
 {
@@ -997,7 +1019,7 @@
 {
 	struct mtk_mac *mac = container_of(config, struct mtk_mac,
 					   phylink_config);
-	u32 mcr, mcr_cur, sts, force_link;
+	u32 mcr, mcr_cur, sts;
 
 	mac->speed = speed;
 
@@ -1043,27 +1065,17 @@
 		if (mode == MLO_AN_PHY && phy)
 			mtk_setup_eee(mac, phy_init_eee(phy, false) >= 0);
 	} else if (mac->type == MTK_XGDM_TYPE && mac->id != MTK_GMAC1_ID) {
+		if (mode == MLO_AN_INBAND)
+			mdelay(1000);
+
 		/* Eliminate the interference(before link-up) caused by PHY noise */
 		mtk_m32(mac->hw, XMAC_LOGIC_RST, 0x0, MTK_XMAC_LOGIC_RST(mac->id));
 		mdelay(20);
 		mtk_m32(mac->hw, XMAC_GLB_CNTCLR, 0x1, MTK_XMAC_CNT_CTRL(mac->id));
 
-		switch (mac->id) {
-		case MTK_GMAC2_ID:
-			force_link = (mac->interface ==
-				      PHY_INTERFACE_MODE_XGMII) ?
-				      MTK_XGMAC_FORCE_LINK(mac->id) : 0;
-			sts = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
-			mtk_w32(mac->hw, sts | force_link,
-				MTK_XGMAC_STS(mac->id));
-			break;
-		case MTK_GMAC3_ID:
-			sts = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id));
-			mtk_w32(mac->hw,
-				sts | MTK_XGMAC_FORCE_LINK(mac->id),
-				MTK_XGMAC_STS(mac->id));
-			break;
-		}
+		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 = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id));
 
@@ -1221,6 +1233,7 @@
 	.validate = mtk_validate,
 	.mac_select_pcs = mtk_mac_select_pcs,
 	.mac_link_state = mtk_mac_pcs_get_state,
+	.mac_prepare = mtk_mac_prepare,
 	.mac_config = mtk_mac_config,
 	.mac_finish = mtk_mac_finish,
 	.mac_link_down = mtk_mac_link_down,
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 7811de7..566bb21 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -676,7 +676,8 @@
 
 /* XMAC status registers */
 #define MTK_XGMAC_STS(x)	((x == MTK_GMAC3_ID) ? 0x1001C : 0x1000C)
-#define MTK_XGMAC_FORCE_LINK(x)	((x == MTK_GMAC2_ID) ? BIT(31) : BIT(15))
+#define MTK_XGMAC_FORCE_MODE(x)	((x == MTK_GMAC2_ID) ? BIT(31) : BIT(15))
+#define MTK_XGMAC_FORCE_LINK(x)	((x == MTK_GMAC2_ID) ? BIT(27) : BIT(11))
 #define MTK_USXGMII_PCS_LINK	BIT(8)
 #define MTK_XGMAC_RX_FC		BIT(5)
 #define MTK_XGMAC_TX_FC		BIT(4)
@@ -1733,6 +1734,7 @@
 	struct regmap		*regmap;
 	struct regmap		*regmap_pextp;
 	struct delayed_work	link_poll;
+	struct completion	link_poll_completion;
 	phy_interface_t		interface;
 	unsigned int		mode;
 	u8			id;
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
index 0cadc85..bcfe750 100644
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_usxgmii.c
@@ -757,6 +757,8 @@
 		queue_delayed_work(system_power_efficient_wq, &mpcs->link_poll,
 				   msecs_to_jiffies(1000));
 	}
+
+	complete(&mpcs->link_poll_completion);
 }
 
 static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
@@ -771,8 +773,14 @@
 	mtk_usxgmii_pcs_config(pcs, mode,
 			       interface, NULL, false);
 
+	reinit_completion(&mpcs->link_poll_completion);
+
 	queue_delayed_work(system_power_efficient_wq, &mpcs->link_poll,
 			   msecs_to_jiffies(1000));
+
+	if (!wait_for_completion_timeout(&mpcs->link_poll_completion,
+					 msecs_to_jiffies(3000)))
+		pr_warn("%s wait link poll complete timeout!\n", __func__);
 }
 
 static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = {
@@ -804,6 +812,7 @@
 		ss->pcs[i].pcs.poll = true;
 		ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
 
+		init_completion(&ss->pcs[i].link_poll_completion);
 		INIT_DELAYED_WORK(&ss->pcs[i].link_poll, mtk_usxgmii_link_poll);
 
 		of_node_put(np);