[][[Panther][AX8400][MT7986A][MT7976][ePA][WiFi]The 2.5G wan interface didn't show any link up/down log when cable plug/unplug]

[Description]
Add a external interrupt handler for GPY211 single phy chip.

This interrupt handler will display link up/down message and notify kernel when receiving link status change interrupt.

Note1: This patch will disable UART2 on the 2500WAN RFB, since single phy external interrupt occupied UART2_RXD and UART2_TXD.
Note2: The interrupt handler will not notify kernel on the 2500WAN GSW RFB.

[Release-log]
NA

Change-Id: Ibbecb437049f02a4ce479fee2cb035b7c17dac66
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/6194248
diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-emmc-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-emmc-rfb.dts
index d190872..a050c88 100644
--- a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-emmc-rfb.dts
+++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-emmc-rfb.dts
@@ -68,7 +68,7 @@
 &uart2 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart2_pins>;
-	status = "okay";
+	status = "disabled";
 };
 
 &i2c0 {
@@ -102,6 +102,9 @@
 			speed = <2500>;
 			full-duplex;
 			pause;
+			link-gpio = <&pio 47 0>;
+			phy-handle = <&phy5>;
+			label = "lan5";
 		};
 	};
 
@@ -114,6 +117,8 @@
 			speed = <2500>;
 			full-duplex;
 			pause;
+			link-gpio = <&pio 46 0>;
+			phy-handle = <&phy6>;
 		};
 	};
 
diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-gsw-spim-nand-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-gsw-spim-nand-rfb.dts
index 457c351..4c4f987 100644
--- a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-gsw-spim-nand-rfb.dts
+++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-gsw-spim-nand-rfb.dts
@@ -57,7 +57,7 @@
 &uart2 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart2_pins>;
-	status = "okay";
+	status = "disabled";
 };
 
 &i2c0 {
@@ -91,6 +91,8 @@
 			speed = <2500>;
 			full-duplex;
 			pause;
+			link-gpio = <&pio 47 0>;
+			phy-handle = <&phy5>;
 		};
 	};
 
@@ -103,6 +105,8 @@
 			speed = <2500>;
 			full-duplex;
 			pause;
+			link-gpio = <&pio 46 0>;
+			phy-handle = <&phy6>;
 		};
 	};
 
diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-sd-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-sd-rfb.dts
index 5163536..6af58aa 100644
--- a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-sd-rfb.dts
+++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-sd-rfb.dts
@@ -59,7 +59,7 @@
 &uart2 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart2_pins>;
-	status = "okay";
+	status = "disabled";
 };
 
 &i2c0 {
@@ -93,6 +93,9 @@
 			speed = <2500>;
 			full-duplex;
 			pause;
+			link-gpio = <&pio 47 0>;
+			phy-handle = <&phy5>;
+			label = "lan5";
 		};
 	};
 
@@ -105,6 +108,8 @@
 			speed = <2500>;
 			full-duplex;
 			pause;
+			link-gpio = <&pio 46 0>;
+			phy-handle = <&phy6>;
 		};
 	};
 
diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nand-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nand-rfb.dts
index 53356cb..e5024b5 100644
--- a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nand-rfb.dts
+++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nand-rfb.dts
@@ -50,7 +50,7 @@
 &uart2 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart2_pins>;
-	status = "okay";
+	status = "disabled";
 };
 
 &i2c0 {
@@ -84,6 +84,9 @@
 			speed = <2500>;
 			full-duplex;
 			pause;
+			link-gpio = <&pio 47 0>;
+			phy-handle = <&phy5>;
+			label = "lan5";
 		};
 	};
 
@@ -96,6 +99,8 @@
 			speed = <2500>;
 			full-duplex;
 			pause;
+			link-gpio = <&pio 46 0>;
+			phy-handle = <&phy6>;
 		};
 	};
 
diff --git a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nor-rfb.dts b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nor-rfb.dts
index f7de3dd..15e9773 100644
--- a/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nor-rfb.dts
+++ b/target/linux/mediatek/files-5.4/arch/arm64/boot/dts/mediatek/mt7986a-2500wan-spim-nor-rfb.dts
@@ -50,7 +50,7 @@
 &uart2 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart2_pins>;
-	status = "okay";
+	status = "disabled";
 };
 
 &i2c0 {
@@ -84,6 +84,9 @@
 			speed = <2500>;
 			full-duplex;
 			pause;
+			link-gpio = <&pio 47 0>;
+			phy-handle = <&phy5>;
+			label = "lan5";
 		};
 	};
 
@@ -96,6 +99,8 @@
 			speed = <2500>;
 			full-duplex;
 			pause;
+			link-gpio = <&pio 46 0>;
+			phy-handle = <&phy6>;
 		};
 	};
 
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 df737e3..e6f6cce 100755
--- 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
@@ -19,6 +19,7 @@
 #include <linux/interrupt.h>
 #include <linux/pinctrl/devinfo.h>
 #include <linux/phylink.h>
+#include <linux/gpio/consumer.h>
 #include <net/dsa.h>
 
 #include "mtk_eth_soc.h"
@@ -2547,6 +2548,36 @@
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t mtk_handle_irq_fixed_link(int irq, void *_mac)
+{
+	struct mtk_mac *mac = _mac;
+	struct mtk_eth *eth = mac->hw;
+	struct mtk_phylink_priv *phylink_priv = &mac->phylink_priv;
+	struct net_device *dev = phylink_priv->dev;
+	int link_old, link_new;
+
+	// clear interrupt status for gpy211
+	_mtk_mdio_read(eth, phylink_priv->phyaddr, 0x1A);
+
+	link_old = phylink_priv->link;
+	link_new = _mtk_mdio_read(eth, phylink_priv->phyaddr, MII_BMSR) & BMSR_LSTATUS;
+
+	if (link_old != link_new) {
+		phylink_priv->link = link_new;
+		if (link_new) {
+			printk("phylink.%d %s: Link is Up\n", phylink_priv->id, dev->name);
+			if (dev)
+				netif_carrier_on(dev);
+		} else {
+			printk("phylink.%d %s: Link is Down\n", phylink_priv->id, dev->name);
+			if (dev)
+				netif_carrier_off(dev);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void mtk_poll_controller(struct net_device *dev)
 {
@@ -2640,6 +2671,7 @@
 {
 	struct mtk_mac *mac = netdev_priv(dev);
 	struct mtk_eth *eth = mac->hw;
+	struct mtk_phylink_priv *phylink_priv = &mac->phylink_priv;
 	int err, i;
 	struct device_node *phy_node;
 
@@ -2685,6 +2717,34 @@
 	else
 		refcount_inc(&eth->dma_refcnt);
 
+	if (phylink_priv->desc) {
+		/*Notice: This programming sequence is only for GPY211 single PHY chip.
+		  If single PHY chip is not GPY211, the following step you should do:
+		  1. Contact your Single PHY chip vendor and get the details of
+		    - how to enables link status change interrupt
+		    - how to clears interrupt source
+		 */
+
+		// clear interrupt source for gpy211
+		_mtk_mdio_read(eth, phylink_priv->phyaddr, 0x1A);
+
+		// enable link status change interrupt for gpy211
+		_mtk_mdio_write(eth, phylink_priv->phyaddr, 0x19, 0x0001);
+
+		phylink_priv->dev = dev;
+
+		// override dev pointer for single PHY chip 0
+		if (phylink_priv->id == 0) {
+			struct net_device *tmp;
+
+			tmp = __dev_get_by_name(&init_net, phylink_priv->label);
+			if (tmp)
+				phylink_priv->dev = tmp;
+			else
+				phylink_priv->dev = NULL;
+		}
+	}
+
 	phylink_start(mac->phylink);
 	netif_start_queue(dev);
 	phy_node = of_parse_phandle(mac->of_node, "phy-handle", 0);
@@ -3400,6 +3460,9 @@
 	struct phylink *phylink;
 	int phy_mode, id, err;
 	struct mtk_mac *mac;
+	struct mtk_phylink_priv *phylink_priv;
+	struct fwnode_handle *fixed_node;
+	struct gpio_desc *desc;
 
 	if (!_id) {
 		dev_err(eth->dev, "missing mac id\n");
@@ -3469,6 +3532,41 @@
 
 	mac->phylink = phylink;
 
+	fixed_node = fwnode_get_named_child_node(of_fwnode_handle(mac->of_node),
+						 "fixed-link");
+	if (fixed_node) {
+		desc = fwnode_get_named_gpiod(fixed_node, "link-gpio",
+					      0, GPIOD_IN, "?");
+		if (!IS_ERR(desc)) {
+			struct device_node *phy_np;
+			const char *label;
+			int irq, phyaddr;
+
+			phylink_priv = &mac->phylink_priv;
+
+			phylink_priv->desc = desc;
+			phylink_priv->id = id;
+			phylink_priv->link = -1;
+
+			irq = gpiod_to_irq(desc);
+			if (irq > 0) {
+				devm_request_irq(eth->dev, irq, mtk_handle_irq_fixed_link,
+						 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+					         "ethernet:fixed link", mac);
+			}
+
+			if (!of_property_read_string(to_of_node(fixed_node), "label", &label))
+				strcpy(phylink_priv->label, label);
+
+			phy_np = of_parse_phandle(to_of_node(fixed_node), "phy-handle", 0);
+			if (phy_np) {
+				if (!of_property_read_u32(phy_np, "reg", &phyaddr))
+					phylink_priv->phyaddr = phyaddr;
+			}
+		}
+		fwnode_handle_put(fixed_node);
+	}
+
 	SET_NETDEV_DEV(eth->netdev[id], eth->dev);
 	eth->netdev[id]->watchdog_timeo = 5 * HZ;
 	eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
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 d74107b..88f9280 100755
--- 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
@@ -1196,6 +1196,21 @@
 	u32 count[32];
 };
 
+/* struct mtk_phylink_priv - This is the structure holding private data for phylink
+ * @desc:		Pointer to the memory holding info about the phylink gpio
+ * @id:			The element is used to record the phy index of phylink
+ * @phyaddr:		The element is used to record the phy address of phylink
+ * @link:		The element is used to record the phy link status of phylink
+ */
+struct mtk_phylink_priv {
+	struct net_device	*dev;
+	struct gpio_desc	*desc;
+	char			label[16];
+	int			id;
+	int			phyaddr;
+	int			link;
+};
+
 /* struct mtk_eth -	This is the main datasructure for holding the state
  *			of the driver
  * @dev:		The device pointer
@@ -1290,6 +1305,7 @@
 	struct device_node		*of_node;
 	struct phylink			*phylink;
 	struct phylink_config		phylink_config;
+	struct mtk_phylink_priv		phylink_priv;
 	struct mtk_eth			*hw;
 	struct mtk_hw_stats		*hw_stats;
 	__be32				hwlro_ip[MTK_MAX_LRO_IP_CNT];