[Feature][T106_eSDK]17.09(SDK4.8)diff_19.00(SDK5.0)

Only Configure: No
Affected branch: master
Affected module: unknow
Is it affected on both ZXIC and MTK: only ZXIC
Self-test: Yes
Doc Update: No

Change-Id: I768f6d42285f04acf919b9f8f6cd34af460c3ef4
diff --git a/upstream/linux-5.10/drivers/net/ethernet/zte/zx29_gmac.c b/upstream/linux-5.10/drivers/net/ethernet/zte/zx29_gmac.c
new file mode 100755
index 0000000..668d9d9
--- /dev/null
+++ b/upstream/linux-5.10/drivers/net/ethernet/zte/zx29_gmac.c
@@ -0,0 +1,2063 @@
+/*

+ * Ethernet driver for zte zx2975xx gmac on chip network device

+ * (c)2008 http://www.zte.com.cn

+ * Authors:	zhang dongdong <zhang.dongdong16@zte.com.cn>

+ *

+ * This program is free software; you can redistribute it and/or

+ * modify it under the terms of the GNU General Public License

+ * as published by the Free Software Foundation; either version

+ * 2 of the License, or (at your option) any later version.

+ */

+#include <linux/kernel.h>

+#include <linux/module.h>

+#include <linux/interrupt.h>

+#include <linux/types.h>

+#include <linux/delay.h>

+#include <linux/init.h>

+#include <linux/spinlock.h>

+#include <linux/netdevice.h>

+#include <linux/etherdevice.h>

+#include <linux/phy.h>

+#include <linux/platform_device.h>

+#include <linux/gmac/gmac.h>

+#include <linux/of.h>

+#include <linux/pinctrl/consumer.h>

+#include <linux/gpio.h>

+#include <linux/of_gpio.h>

+#include <linux/device.h>

+#include "zx29_gmac.h"

+

+#define gmac_printk(_format, _args...)		do{printk(KERN_INFO"gmac," _format "\n",##_args);}while(0)

+

+static u8 zx29_gmac_addr[MAC_ADDR_LENTH] = {0xec,0x1d,0x7f,0xb0,0x2f,0x32};

+static struct tasklet_struct *g_gmac_tasklet = NULL;

+/*struct zx29_gmac_dev	*g_gmac_dev = NULL; */ /* no use possible */

+extern void gmac_event_notify(GMAC_NOTIFY_EVENT notify_type, void* puf);

+extern int  gmac_event_init(const char *name);

+static void gmac_hw_deinit(struct net_device *dev);

+//extern void v7_dma_map_area(const void *, size_t, int);

+//extern unsigned long virt_to_phys_ap(unsigned long virt);

+extern void dma_map(const void *addr, size_t len, int flags);

+extern unsigned long virt_to_phys_ap_new(unsigned long virt_addr);

+extern void kobj_gmac_del(struct kobject *kobject);

+

+

+void dump_pkt_trace(unsigned char *data,int len)

+{

+	int i;

+	len = len > 128?128:len;

+    printk("********************\n");

+	for(i=0;i<len;i++){

+		printk("%.2x ",data[i]);

+		if((i&0xf) == 0xf)

+			printk("\n");

+	}

+	printk("\n");

+    printk("********************\n");

+}

+

+static u32 zx29_gmac_get_link(struct net_device *dev)

+{

+	struct zx29_gmac_dev* prv = (struct zx29_gmac_dev*)netdev_priv(dev);

+

+	return prv->link.isup;

+}

+

+

+static void gmac_start(void* io)

+{

+	volatile unsigned *gmac = (unsigned*)io;

+

+    mac_int_enable();

+    dma_enable();

+    mac_enable();

+}

+

+static inline int mod_sub(int left, int right, int mod)

+{

+    return (mod - right + left) % mod;

+}

+

+static struct bd_tx *get_txed_bd(struct net_device *ndev)

+{

+	struct bd_tx *d;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	int n = priv->txed_bd;

+

+	d = (struct bd_tx *)priv->dma_tx_vir;

+

+	if (n == priv->tx_bd_offset) 

+		return 0;

+

+	if (d[n].TDES0 & DMA_OWNER)

+		return 0;

+

+	if (d[n].skb == NULL)

+		return 0;

+

+	priv->txed_bd++;

+	priv->txed_bd %= GMAC_TX_BD_NUM;

+

+	return &d[n];

+}

+

+static inline struct bd_tx *get_tx_bd(struct net_device *ndev)

+{

+	struct zx29_gmac_dev* priv 	= (struct zx29_gmac_dev*)netdev_priv(ndev);

+    int   n						= priv->tx_bd_offset;

+    struct bd_tx *d				= (struct bd_tx*)priv->dma_tx_vir;

+	

+    if (mod_sub(priv->tx_bd_offset, priv->txed_bd, GMAC_TX_BD_NUM) > GMAC_TX_BD_NUM - 2)

+		return 0;

+		

+    if (d[n].TDES0 & DMA_OWNER) {

+        return 0;

+    } else {

+        return &d[n];

+    }

+}

+

+static struct bd_rx *get_rx_bd(struct net_device *dev)

+{

+    struct zx29_gmac_dev* prv 	= (struct zx29_gmac_dev*)netdev_priv(dev);

+    int   n						= prv->rx_bd_offset;

+    struct bd_rx *d				= (struct bd_rx*)prv->dma_rx_vir;

+

+    if(d[n].RDES0 & DMA_OWNER) 

+    {

+        return 0;

+    }

+    else	

+    {

+        return &d[n];

+    }

+}

+

+static void gmac_trig_transmit(void *io)

+{

+	volatile unsigned *gmac = (unsigned *)io;

+	register unsigned status = ((MAC(0x1014) >> 20) & 0x07);

+	switch (status) {

+		case 0:

+			dma_enable();

+			break;

+		case 6:

+			dma_continue_tx();

+			break;

+		case 1:

+		case 2:

+		case 3:

+		case 4:

+		case 5:

+		case 7:

+		default:

+			break;

+						

+	}

+}

+

+static void gmac_trig_receive(void *io)

+{

+	volatile unsigned *gmac = (unsigned *)io;

+	register unsigned status = ((MAC(0x1014) >> 17) & 0x07);

+	switch (status) {

+		case 0:

+			dma_enable();

+			break;

+		case 4:

+			dma_continue_rx();

+			break;

+		default:

+			break;				

+	}

+}

+

+static inline void gmac_update_mac(struct net_device *ndev)

+{

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+	unsigned char *mac = (unsigned char*)ndev->dev_addr;

+

+	MAC(0x0044)	= mac[0] | mac[1] << 8 | mac[2] << 16 | mac[3] << 24;

+    MAC(0x0040) = mac[4] | mac[5] << 8;

+}

+

+static int zx29mii_read(struct mii_bus *bus, int phy_addr, int regnum)

+{

+    unsigned long flags;

+	struct zx29_gmac_dev *prv = (struct zx29_gmac_dev *)bus->priv;

+	volatile unsigned *gmac = (unsigned *)prv->base_addr;

+

+	unsigned val	= ( 1 << 0 					|		// busy 位

+					    0 << 1 					|		// R/W操作指示位

+					    (PHY_CLOCK << 2)		|		// 时钟位

+					    (regnum & 0x1F) << 6	|		// 寄存器

+					    (phy_addr & 0x1F) << 11);		// 物理芯片

+

+	spin_lock_irqsave(&prv->lock,flags);

+	while(mac_mii_is_busy());

+	MAC(0x0010) 	= val;

+	spin_unlock_irqrestore(&prv->lock,flags);

+

+	while(mac_mii_is_busy());

+

+	return (MAC(0x0014) & 0xFFFF);

+}

+

+static int zx29mii_write(struct mii_bus *bus, int phy_addr, int regnum, u16 value)

+{

+	struct zx29_gmac_dev *prv = (struct zx29_gmac_dev *)bus->priv;

+	volatile unsigned *gmac = (unsigned *)prv->base_addr;

+

+	unsigned val = ( 1 << 0 					|		// busy 位

+					 1 << 1 					|		// R/W操作指示位

+					 (PHY_CLOCK << 2)			|		// 时钟位

+					 (regnum & 0x1F) << 6	    |		// 寄存器

+					 (phy_addr & 0x1F) << 11);		    // 物理芯片

+

+	spin_lock_irq(&prv->lock);

+	while(mac_mii_is_busy());

+	MAC(0x0014) = value;

+	MAC(0x0010) = val;

+

+	spin_unlock_irq(&prv->lock);

+	

+	while(mac_mii_is_busy());

+

+	return 0;

+}

+

+static int zx29mii_reset(struct mii_bus *bus)

+{

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)bus->priv;

+	volatile unsigned *gmac = (unsigned *)priv->base_addr;

+

+	gmac_start((void *)priv->base_addr);

+	while (mac_mii_is_busy());

+	return 0;

+}

+

+static inline void zx29_gmac_set_macaddr(struct net_device *ndev)

+{

+    int i = 0;

+#if MAC_ADDR_SET

+    for (i = 0; i < MAC_ADDR_LENTH; i++)

+		ndev->dev_addr[i] = zx29_gmac_addr[i];

+

+	if (!is_valid_ether_addr(ndev->dev_addr))

+		random_ether_addr(ndev->dev_addr);

+#else

+	random_ether_addr(ndev->dev_addr);

+#endif

+}

+

+static void zx29_gmac_tx(struct net_device *ndev)

+{

+	register unsigned status;

+	struct net_device_stats s = ndev->stats;

+	struct bd_tx *tx = get_txed_bd(ndev);

+

+	

+	while (tx) {

+		status = tx->TDES0;

+

+		if (tx->TDES0 & ERR_TX_ES) {

+			s.tx_errors++;

+			if(status & ERR_TX_LC)		s.tx_carrier_errors++;

+            if(status & ERR_TX_NC)		s.tx_carrier_errors++;

+            if(status & ERR_TX_EC)		s.tx_window_errors++;

+            if(status & ERR_TX_LATECOL)	s.tx_window_errors++;

+            if(status & ERR_TX_UF)		s.tx_aborted_errors++;

+            if(status & ERR_TX_ED)		s.tx_aborted_errors++;

+            if(status & ERR_TX_JT)		s.tx_fifo_errors++;

+            if(status & ERR_TX_FF)		s.tx_fifo_errors++;

+

+			printk("%s, status=0x%x, err_cnt=%ld\n", __FUNCTION__,status, s.tx_errors);

+		}

+		dev_kfree_skb_any(tx->skb);

+		tx->skb = NULL;

+		tx = get_txed_bd(ndev);

+		

+	}

+    

+	if (netif_queue_stopped(ndev))

+		netif_wake_queue(ndev);	

+}

+

+static int zx29_gmac_rx(struct net_device *ndev)

+{

+	struct bd_rx 	*rx;

+	struct sk_buff	*skb;

+	struct sk_buff	*skb_new;

+	unsigned		len;

+	int   exhausted = 0;

+	

+	struct zx29_gmac_dev* priv = (struct zx29_gmac_dev*)netdev_priv(ndev);

+

+    rx  = get_rx_bd(ndev);

+

+    if(unlikely(!rx))	goto rcv_done;

+

+	while (rx) {

+		if ((rx->RDES0 & ERR_RX_ES) || (rx->RDES0 & ERR_RX_LE)) {

+			ndev->stats.rx_errors++;

+            if(rx->RDES0 & ERR_RX_LE)	ndev->stats.rx_length_errors++;

+            if(rx->RDES0 & ERR_RX_OE)	ndev->stats.rx_over_errors++;

+            if(rx->RDES0 & ERR_RX_IPC)	ndev->stats.rx_frame_errors++;

+            if(rx->RDES0 & ERR_RX_LC)	ndev->stats.rx_fifo_errors++;

+            if(rx->RDES0 & ERR_RX_CE)	ndev->stats.rx_crc_errors++;

+		} else {

+            		

+			len = ((rx->RDES0 >> 16) & 0x3FFF) - 4;

+            if(len  > (ETH_FRAME_LEN+8)) {

+                ndev->stats.rx_dropped++;

+                goto rx_bd_reset;

+            }

+

+            skb_new = netdev_alloc_skb(ndev, GMAC_FRAME_LEN + NET_IP_ALIGN);

+            if (unlikely(!skb_new)) {

+                ndev->stats.rx_dropped++;

+				exhausted++;

+            } else {

+				exhausted = 0;

+            	ndev->stats.rx_packets++;

+				ndev->stats.rx_bytes += len;

+				

+				dma_sync_single_for_cpu(&ndev->dev, rx->dma_buf, GMAC_FRAME_LEN, DMA_FROM_DEVICE);

+                skb				= rx->skb;

+                skb_put(skb, len); /*in fact , use for?*/

+				

+//				dump_pkt_trace(skb->data, len);

+                skb->protocol 		= eth_type_trans(skb, ndev);

+                netif_rx(skb);

+

+				skb_reserve(skb_new, NET_IP_ALIGN);

+//				rx->dma_buf = virt_to_phys_ap((unsigned long)skb_new->data);

+				rx->dma_buf = virt_to_phys_ap_new((unsigned long)skb_new->data);

+				if(rx->dma_buf == NULL)

+				rx->dma_buf = __pa((unsigned)skb_new->data);

+				rx->skb = skb_new;

+				wmb();

+				dma_sync_single_for_device(&ndev->dev, rx->dma_buf, GMAC_FRAME_LEN, DMA_TO_DEVICE);

+			}

+		}

+rx_bd_reset:

+		rx->RDES0 = rx->RDES0 | DMA_OWNER;

+		priv->rx_bd_offset++;

+		priv->rx_bd_offset %= GMAC_RX_BD_NUM;

+		wmb();

+

+		if (exhausted >= 10)

+			break;

+		gmac_trig_receive((void*)ndev->base_addr);

+		rx = get_rx_bd(ndev);

+	}

+

+rcv_done:

+    

+	gmac_trig_receive((void*)ndev->base_addr);

+

+	return (exhausted > 10);

+}

+

+

+#ifndef GMAC_NO_INT

+static irqreturn_t zx29_gmac_interrupt(int irq, void *dev_id)

+{

+	struct net_device *ndev = (struct net_device *)dev_id;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	volatile unsigned * gmac = (unsigned *)ndev->base_addr;

+

+	priv->int_event = MAC(0x1014);

+	MAC(0x1014) = priv->int_event;

+

+	mac_int_disable();

+	tasklet_schedule(&priv->tasklet);

+

+	return IRQ_HANDLED;

+}

+

+void zx29_gmac_tasklet(unsigned long dev_id)

+{

+	struct net_device *ndev = (struct net_device *)dev_id;

+	struct zx29_gmac_dev *prv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+	unsigned int events = prv->int_event;

+

+	do {

+		if (events & INT_ST_TX)

+			zx29_gmac_tx(ndev);

+

+		if (events & INT_ST_RX)

+			zx29_gmac_rx(ndev);

+

+		events = MAC(0x1014);

+		MAC(0x1014) = events;

+	} while (events & (INT_ST_TX | INT_ST_RX));

+

+	mac_int_enable();

+}

+

+#else 

+void zx29_gmac_tasklet(unsigned long dev_id)

+{

+	struct net_device *ndev = (struct net_device *)dev_id;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+	unsigned events = priv->int_event;

+	

+	do {

+		if (events & INT_ST_TX)

+			zx29_gmac_tx(ndev);

+		

+		if (events & INT_ST_RX) {

+			if (zx29_gmac_rx(ndev))

+				break;

+		}

+		events = MAC(0x1014);

+	    MAC(0x1014) = events;

+	} while (events & (INT_ST_RX | INT_ST_TX));

+}

+

+enum hrtimer_restart gmac_timer_callback(struct hrtimer *timer)

+{

+	unsigned long delay_in_us = GTIMER_INTERVAL;

+	ktime_t gmac_schdule_time = ktime_set(0, delay_in_us * 1000);

+	

+	hrtimer_forward_now(timer, gmac_schdule_time);

+	tasklet_schedule(g_gmac_tasklet);

+	return HRTIMER_RESTART;

+}

+#endif

+

+static inline void zx29_gmac_linkisup(struct net_device *dev, int isup)

+{

+	struct zx29_gmac_dev *priv = netdev_priv(dev);

+	struct phy_device *phydev = priv->phydev;

+

+	priv->link.duplex = phydev->duplex;

+	priv->link.giga = (phydev->speed == 100);

+	if (priv->link.speed != phydev->speed)

+			priv->link.speed = phydev->speed;

+

+

+	priv->link.isup = isup;

+	if (isup)

+		netif_carrier_on(dev);

+	phy_print_status(phydev);

+}

+

+static void zx29_gmac_adjust_link(struct net_device *dev)

+{

+	struct zx29_gmac_dev *priv = netdev_priv(dev);

+	struct phy_device *phydev = priv->phydev;

+	volatile unsigned *gmac = (unsigned *)dev->base_addr;

+

+	if (priv->link.isup &&

+		(!phydev->link ||

+		(priv->link.speed != phydev->speed) ||

+		(priv->link.duplex != phydev->duplex))) {

+			priv->link.isup = 0;

+			netif_tx_disable(dev);

+		    if (!phydev->link) {

+				netif_carrier_off(dev);

+				phy_print_status(phydev);

+			}

+		}

+

+		if (!priv->link.isup && phydev->link) {

+			if (priv->link.duplex != phydev->duplex) {

+				if (phydev->duplex)

+					mac_set_full_duplex_mode();

+				else

+					mac_set_half_duplex_mode();

+			}

+

+		if (priv->link.giga != (phydev->speed == 100)) {

+			if (phydev->speed == 100)

+				mac_set_speed_100m_mode();

+			else

+				mac_set_speed_10m_mode();

+		}

+			netif_wake_queue(dev);

+            zx29_gmac_linkisup(dev, 1);

+		}

+		

+}

+

+static inline int zx29_gmac_phy_start(struct net_device *dev)

+{

+	struct zx29_gmac_dev *priv = netdev_priv(dev);

+	struct phy_device *p = NULL;

+	struct mdio_device *mdio_dev = NULL;

+	int ret = 0;

+

+	//zw.wang Without phy, gmac's gpio output power is removed on 20240328 start

+	int i = 0;

+	for(i = 0;i <= 5;i++)

+	{

+		if (priv->nports == 1) {

+			p = phy_find_first(priv->mii.bus);

+		} else if (priv->rmii_port < PHY_MAX_ADDR) {

+			mdio_dev = priv->mii.bus->mdio_map[priv->rmii_port];

+			p = container_of(mdio_dev, struct phy_device, mdio);

+		}

+

+		if (!p) {

+			if(i == 5){

+				gpio_direction_output(priv->gpio_power[0], 0);

+#ifdef CONFIG_MDIO_C45

+				gpio_direction_output(priv->gpio_power[1], 0);

+#endif

+			}

+			else

+				continue;

+			printk("%s: no PHY found\n", dev->name);

+			return -ENODEV;

+		}

+		else

+			break;

+	}

+	//zw.wang Without phy, gmac's gpio output power is removed on 20240328 end

+

+	ret = phy_connect_direct(dev, p, zx29_gmac_adjust_link, PHY_INTERFACE_MODE_RMII);  /*  phy_start_machine */

+	  /* supported and advertising */

+	priv->phydev = p;

+	return 0;

+}

+

+

+static int gmac_init_rx_bd(struct net_device *ndev, struct zx29_gmac_dev *priv)

+{

+	struct sk_buff *skb = NULL;

+	struct bd_rx *rx = (struct bd_rx *)priv->dma_rx_vir;

+	int i = 0;

+    

+	priv->rx_bd_offset = 0;

+    

+	for (i = 0; i < GMAC_RX_BD_NUM; i++) {

+		skb = netdev_alloc_skb(ndev, GMAC_FRAME_LEN + NET_IP_ALIGN);

+		if (unlikely(!skb)) {

+			gmac_hw_deinit(ndev);

+			return -1;

+		}

+

+		skb_reserve(skb, NET_IP_ALIGN);

+

+		rx[i].RDES0 |= DMA_OWNER;  /* when to change? */

+		rx[i].RDES1 = 0;

+		rx[i].RDES1 = GMAC_FRAME_LEN | 1 << 14;

+		rx[i].dma_buf = __pa((unsigned)skb->data);  /* __pa ? */

+		rx[i].next = priv->dma_rx_phy + ((i + 1) << 5); /* why phy? */

+		rx[i].skb = skb;

+#if 0

+		if(i%4 != 0)

+		{

+		   rx[i].RDES1	|= 0x80000000;

+		}

+#endif

+		dma_sync_single_for_device(&ndev->dev, rx[i].dma_buf, GMAC_FRAME_LEN, DMA_TO_DEVICE);

+		

+	}

+	rx[GMAC_RX_BD_NUM - 1].next = priv->dma_rx_phy;

+	rx[GMAC_RX_BD_NUM - 1].RDES1 = GMAC_FRAME_LEN | 1 << 14 | 1 << 15;

+

+	return 0;

+}

+

+static void gmac_init_tx_bd(struct zx29_gmac_dev *priv)

+{

+	struct bd_tx *tx = (struct bd_tx *)priv->dma_tx_vir;

+	int i = 0;

+	priv->tx_bd_offset = 0;

+	priv->txed_bd = 0;

+

+	for (i = 0; i < GMAC_TX_BD_NUM; i++) {

+		tx[i].TDES0 = (1 << 20 | 1 << 30);

+		tx[i].TDES1 = GMAC_FRAME_LEN;

+		tx[i].next = priv->dma_tx_phy + ((i + 1) << 5);

+	}

+

+	tx[GMAC_TX_BD_NUM - 1].next = priv->dma_tx_phy;

+	tx[GMAC_TX_BD_NUM - 1].TDES0 = 1 << 20 | 1 << 21 | 1 << 30;

+	

+}

+

+static void gmac_stop(void *io)

+{

+	volatile unsigned *gmac = (unsigned *)io;

+

+	dma_disable();

+	mac_disable();

+	mac_int_disable();

+

+	dma_clear_tx_fifo();

+	dma_wait_tx_fifo_cleared();	

+}

+

+static void gmac_set_speed_duplex(struct net_device *ndev, int speed, int duplex)

+{

+	unsigned val;

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+

+	val = MAC(0x0000) | 1 << 11 | 1 << 14;

+	if (SPEED_10 == speed)

+		val &= ~(1 << 14);

+	if (DUPLEX_HALF == duplex) {

+		val &= (~(1 << 11));

+		val |= (1 << 16);

+	}

+	MAC(0x0000) = val;

+}

+

+static void mac_init(struct net_device *ndev)

+{

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+	unsigned int i = 0, j = 0, mac_rst = 0;

+	unsigned long long mac_time_start = 0;

+	unsigned long long mac_time_end = 0;

+

+	mac_provide_clock();

+#ifdef __DEAD_LOOP_POLL__

+	mac_reset();

+	mac_set_gmii_mode();

+	mac_wait_reset_finished();

+#else

+	mac_time_start = local_clock();

+	for (i = 0; i < MAC_RESET_NUM; i++) {

+		mac_reset();

+		mac_set_mii_mode();

+		for (j = 0; j < MAC_WAIT_TIME; j++) {

+//			printk(".");

+			if (!((MAC(0x1000)) & 1)) {

+				mac_time_end = local_clock();

+				printk("ok:time:%llu ns\n", mac_time_end - mac_time_start);

+				mac_rst = 1;

+				goto mac_reset_option;

+			}

+			udelay(100);

+		}

+	}

+    mac_time_end = local_clock();

+mac_reset_option:

+	if(!mac_rst)								

+		printk("gmac reset failed!time:%llu us\n", mac_time_end - mac_time_start);

+#endif

+	while(mac_mii_is_busy());

+}

+

+static void gmac_hw_deinit(struct net_device *ndev)

+{

+	int i;

+	struct bd_rx *rx_bd;

+	struct bd_tx *tx_bd;

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+

+	gmac_stop((void *)ndev->base_addr);

+

+	if (priv->dma_rx_phy) {

+		rx_bd = (struct bd_rx *)priv->dma_rx_vir;

+

+		for (i = 0; i < GMAC_RX_BD_NUM; i++) {

+			if (rx_bd[i].skb)

+				dev_kfree_skb_any(rx_bd[i].skb);

+		}

+

+		tx_bd = (struct bd_tx *)priv->dma_tx_vir;

+		

+		for (i = 0; i < GMAC_TX_BD_NUM; i++) {

+			if (tx_bd[i].skb)

+				dev_kfree_skb_any(tx_bd[i].skb);

+		}

+	}

+

+	dma_set_tx_buffer(0);   //设置首个BD的缓冲区为0;

+    dma_set_rx_buffer(0);

+

+    priv->rx_bd_offset	= 0;

+    priv->tx_bd_offset	= 0;

+    priv->txed_bd		= 0;

+    priv->dma_rx_phy	= 0;

+    priv->dma_rx_vir	= 0;

+    priv->dma_tx_phy	= 0;

+    priv->dma_tx_vir	= 0;

+		

+}

+

+static int gmac_hw_init(struct net_device *ndev)

+{

+	int ret = -1;

+	unsigned val;

+	

+	volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+

+	if (priv->dma_rx_phy)

+		gmac_hw_deinit(ndev);

+

+	priv->dma_rx_vir = priv->dma_rx_vir_init;

+	priv->dma_rx_phy = priv->dma_rx_phy_init;

+    priv->dma_tx_vir	= priv->dma_rx_vir + GMAC_RX_BUF_LEN;

+    priv->dma_tx_phy	= priv->dma_rx_phy + GMAC_RX_BUF_LEN;   /* ifconfig up, clear fifo*/

+

+	memset(priv->dma_rx_vir, 0, GMAC_BUF_LEN);

+

+	ret = gmac_init_rx_bd(ndev, priv);

+	if (ret < 0) {

+		printk("hw_net_init,init_rx_bd fail\n");

+		return ret;

+	}

+	gmac_init_tx_bd(priv);

+

+	mac_init(ndev); 

+

+	dma_disable();

+	mac_disable();

+	mac_int_disable();

+

+	val = MAC(0x1000);

+	val &= ~(0x3F << 8);

+	val |= (0x10 << 8);

+	MAC(0x1000) = val;

+

+	dma_set_rx_buffer(priv->dma_rx_phy);

+	dma_set_tx_buffer(priv->dma_tx_phy);

+

+	mac_int_clear(0x0001FFFF);

+	while (mac_mii_is_busy());

+

+	gmac_set_speed_duplex(ndev, priv->phydev->speed, priv->phydev->duplex);

+

+	mac_rece_all_data();

+

+	gmac_start((void *)ndev->base_addr);

+	return 0;

+}

+

+static int zx29_gmac_open(struct net_device *ndev)

+{

+	struct zx29_gmac_dev *priv = netdev_priv(ndev);

+	unsigned long flags;

+	int ret;

+	int err = 0;

+#ifdef GMAC_NO_INT

+	unsigned long delay_in_us = GTIMER_INTERVAL;

+	ktime_t gmac_schdule_time;

+#endif

+	err = phy_read_status(priv->phydev);  /*interal, phy drv provide*/

+	if (err < 0)

+		return err;

+

+	spin_lock_irqsave(&priv->lock, flags);

+	priv->link.speed = 0;

+

+	zx29_gmac_linkisup(ndev, priv->phydev->link);

+		

+	ret = gmac_hw_init(ndev);

+	if(ret) {

+		spin_unlock_irqrestore(&priv->lock, flags);

+		return ret;

+	}

+

+	netif_carrier_on(ndev);

+	spin_unlock_irqrestore(&priv->lock, flags);

+	

+	phy_start(priv->phydev);

+		

+	netif_start_queue(ndev);

+	

+#ifdef GMAC_NO_INT

+	gmac_schdule_time = ktime_set(0, delay_in_us * 1000);

+	if (priv->timer)

+		hrtimer_start(priv->timer, gmac_schdule_time, HRTIMER_MODE_REL);

+#endif

+

+	priv->stopped = 0;

+

+	printk("TSP zx29 gmac net open\n");

+

+	return 0;

+}

+

+static int zx29_gmac_stop(struct net_device *ndev)

+{

+	unsigned long flags = 0;

+	int ret = 0;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+

+	if (!priv->stopped) {

+		spin_lock_irqsave(&priv->lock, flags);

+#ifdef GMAC_NO_INT

+		ret = hrtimer_cancel(priv->timer);

+		if (ret < 0) {

+			BUG_ON(1);

+			spin_unlock_irqrestore(&priv->lock, flags);

+			return ret;

+		}

+#endif

+

+		priv->stopped = 1;

+		netif_stop_queue(ndev);

+		netif_carrier_off(ndev);

+		phy_stop(priv->phydev);

+		gmac_hw_deinit(ndev);

+

+		memset(&ndev->stats, 0, sizeof(struct net_device_stats));

+		spin_unlock_irqrestore(&priv->lock, flags);

+		printk("TSP zx29 gmac net stop\n");

+		}

+	return 0;

+}

+

+

+static netdev_tx_t zx29_gmac_start_xmit(struct sk_buff *skb, struct net_device *ndev)

+{

+	unsigned long flags;

+	unsigned len;

+	struct sk_buff *skb_old;

+	struct bd_tx *tx;

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	

+	if (0 == priv->link.isup) {

+		dev_kfree_skb_any(skb);

+		printk("TSP zx29 gmac xmit  phy not link\n");	 

+		return NETDEV_TX_OK;  /* ? */

+	}

+

+	spin_lock_irqsave(&priv->lock, flags);

+	if(priv->stopped)

+	{

+		spin_unlock_irqrestore(&priv->lock,flags);

+		dev_kfree_skb_any(skb);

+		

+		printk("zx_net_start_xmit when stopped\n");

+		

+		return NETDEV_TX_OK;

+	}

+

+	tx = get_tx_bd(ndev);

+

+	if (!tx) {

+		spin_unlock_irqrestore(&priv->lock,flags);

+		dev_kfree_skb_any(skb);

+		return NETDEV_TX_OK;

+	}

+

+	priv->tx_bd_offset++;

+	priv->tx_bd_offset %= GMAC_TX_BD_NUM;

+	spin_unlock_irqrestore(&priv->lock, flags);

+

+	if(skb->len > ETH_FRAME_LEN + 4)	/* why 4*/

+		printk("TSP zx29 gmac start xmit len too long\n");

+ 

+//	v7_dma_map_area(skb->data, skb->len, DMA_TO_DEVICE);

+	dma_map(skb->data, skb->len, DMA_TO_DEVICE);

+	if (NULL == skb)

+		BUG_ON(1);

+

+	len = MIN(skb->len, GMAC_FRAME_LEN - NET_IP_ALIGN);

+	

+	tx->TDES0 |= (0x07 << 28);

+//	tx->dma_buf = virt_to_phys_ap((unsigned long)skb->data);

+	tx->dma_buf = virt_to_phys_ap_new((unsigned long)skb->data);

+

+	if(tx->dma_buf == NULL)

+	tx->dma_buf = virt_to_phys((unsigned)skb->data);

+	tx->skb = skb;

+

+	tx->TDES1 = len;

+	tx->TDES0 |= DMA_OWNER;

+

+	wmb();

+	ndev->stats.tx_bytes 		+= len;

+	ndev->stats.tx_packets++;

+/*	ndev->trans_start			= jiffies; */

+

+    

+	gmac_trig_transmit((void*)ndev->base_addr);

+//    dump_pkt_trace(skb->data, len);

+//	printk("[%s]\n", __func__);

+

+	return NETDEV_TX_OK;

+}

+

+static void zx29_gmac_tx_timeout(struct net_device *ndev, unsigned int txqueue)

+{

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+	genphy_update_link(priv->phydev);

+#endif

+	priv->link.isup = priv->phydev->link;

+

+	if (0 == priv->link.isup) {

+		printk("TSP zx29 gmac net timeout phy not link\n");						// PHY 未连接

+		netif_stop_queue(ndev);

+		netif_carrier_off(ndev);

+	} else {

+		printk("TSP zx29 gmac net timeout phy linked\n"); 

+		gmac_trig_transmit(ndev);

+		gmac_trig_receive(ndev);

+

+		netif_carrier_on(ndev);

+		netif_wake_queue(ndev);

+/*		ndev->trans_start		= jiffies; */ /* modify */

+		ndev->stats.tx_errors++;

+		ndev->stats.tx_dropped++;

+	}

+}

+

+void __iomem *base_clk = NULL;

+void __iomem *base_phy_release = NULL;

+

+static int zx29_gmac_phy_disable(struct device *dev)

+{	

+	struct platform_device *pdev	= to_platform_device(dev);

+	struct net_device	*ndev		= platform_get_drvdata(pdev);

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	volatile unsigned int *gmac = NULL;

+	gmac = (unsigned *)ndev->base_addr;

+	

+	enum of_gpio_flags flags;

+    unsigned long flag;

+	int gpio = 0;

+	int ret = 0;

+	

+#ifndef CONFIG_BOOT_WITHOUT_LOCK 	

+	if (ndev && !priv->stopped) {

+		if (netif_running(ndev)) {

+			

+			spin_lock_irqsave(&priv->lock, flag);

+            netif_stop_queue(ndev);

+			netif_carrier_off(ndev);

+			priv->stopped = 1;

+

+#ifdef GMAC_NO_INT

+            hrtimer_cancel(priv->timer);

+#endif			

+			printk("[%s] netif_running\n", __func__);

+

+			gpio_direction_output(priv->gpio_power[0], 0);

+			

+			gmac_stop((void*)ndev->base_addr); 

+            spin_unlock_irqrestore(&priv->lock, flag);

+		

+//			netif_device_detach(ndev);

+		}

+        pm_relax(&pdev->dev);

+       // printk("[%s] sleep\n");

+    }

+#endif

+	//printk("[%s] exit\n", __func__);

+	return 0;

+}

+

+static int zx29_gmac_phy_enable(struct device *dev)

+{

+    struct platform_device *pdev	= to_platform_device(dev);

+	struct net_device *ndev 		= platform_get_drvdata(pdev);

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	volatile unsigned int *gmac = NULL;

+	void __iomem *base = NULL;

+	gmac = (unsigned *)ndev->base_addr;

+	enum of_gpio_flags flags;

+	int gpio = 0;

+	int ret = 0;

+	int status = 0;

+	int islink = 0;

+	unsigned int num= 0;	

+    unsigned long flag = 0;

+	

+#ifndef CONFIG_BOOT_WITHOUT_LOCK 

+	if(ndev && priv->stopped) {

+	    pm_stay_awake(&pdev->dev);

+		if( netif_running(ndev)) {			

+            printk("[%s] enter\n", __func__);

+			spin_lock_irqsave(&priv->lock, flag);

+			gpio_direction_output(priv->gpio_power[0], 1);

+

+			base = base_clk;

+			gmac_set_clk();

+

+			base = base_phy_release;

+			gmac_phy_release();

+

+		    mdelay(500); //icplus ping need

+

+            priv->phydev->drv->config_init(priv->phydev);

+			gmac_hw_init(ndev);

+

+			netif_carrier_on(ndev);						

+			netif_start_queue(ndev);

+	

+#ifdef GMAC_NO_INT	

+            hrtimer_start(priv->timer, ktime_set(0, GTIMER_INTERVAL * 1000), HRTIMER_MODE_REL);

+#endif 

+			priv->stopped = 0;

+            spin_unlock_irqrestore(&priv->lock, flag);

+            printk("[%s] enter\n", __func__);

+//			netif_device_attach(ndev);	  	

+		}

+    }

+#endif

+	return 0;

+}

+

+#define C45_READ 1

+#define C45_WRITE 0

+static int zx29_c22_2_c45(struct phy_device *phydev, int addr, u16 devad, u32 regnum, int rw, int write_val)

+{

+	int val = 0;

+	phy_lock_mdio_bus(phydev);

+	/* Write the desired MMD Devad */

+	__mdiobus_write(phydev->mdio.bus, addr, MII_MMD_CTRL, devad);

+	/* Write the desired MMD register address */

+	__mdiobus_write(phydev->mdio.bus, addr, MII_MMD_DATA, regnum);

+	/* Select the Function : DATA with no post increment */

+	__mdiobus_write(phydev->mdio.bus, addr, MII_MMD_CTRL,

+			devad | MII_MMD_CTRL_NOINCR);

+	/* Read the content of the MMD's selected register */

+	if (rw == C45_READ)

+		val = __mdiobus_read(phydev->mdio.bus, addr, MII_MMD_DATA);

+	else 

+		__mdiobus_write(phydev->mdio.bus, addr, MII_MMD_DATA, write_val);

+	phy_unlock_mdio_bus(phydev);

+

+	return val;

+}

+

+static int zx29_c45_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)

+{

+	struct mii_ioctl_data *mii_data = if_mii(ifr);

+	u16 val = mii_data->val_in;

+	bool change_autoneg = false;

+	int prtad, devad, reg_num;

+

+	switch (cmd) {

+	case SIOCGMIIREG:

+		prtad = mdio_phy_id_prtad(mii_data->phy_id);//phy id

+		devad = mdio_phy_id_devad(mii_data->phy_id);//dev id / mmd

+		reg_num = mii_data->reg_num;

+		mii_data->val_out = zx29_c22_2_c45(phydev, prtad, devad, reg_num, C45_READ, 0);	

+		return 0;

+

+	case SIOCSMIIREG:

+		prtad = mdio_phy_id_prtad(mii_data->phy_id);

+		devad = mdio_phy_id_devad(mii_data->phy_id);

+        reg_num = mii_data->reg_num;

+			

+		if (prtad == phydev->mdio.addr) {

+			switch (devad) {

+			case MII_BMCR:

+				if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) {

+					if (phydev->autoneg == AUTONEG_ENABLE)

+						change_autoneg = true;

+					phydev->autoneg = AUTONEG_DISABLE;

+					if (val & BMCR_FULLDPLX)

+						phydev->duplex = DUPLEX_FULL;

+					else

+						phydev->duplex = DUPLEX_HALF;

+					if (val & BMCR_SPEED1000)

+						phydev->speed = SPEED_1000;

+					else if (val & BMCR_SPEED100)

+						phydev->speed = SPEED_100;

+					else phydev->speed = SPEED_10;

+				}

+				else {

+					if (phydev->autoneg == AUTONEG_DISABLE)

+						change_autoneg = true;

+					phydev->autoneg = AUTONEG_ENABLE;

+				}

+				break;

+			case MII_ADVERTISE:

+				mii_adv_mod_linkmode_adv_t(phydev->advertising,

+							   val);

+				change_autoneg = true;

+				break;

+			case MII_CTRL1000:

+				mii_ctrl1000_mod_linkmode_adv_t(phydev->advertising,

+							        val);

+				change_autoneg = true;

+				break;

+			default:

+				/* do nothing */

+				break;

+			}

+		}

+

+		zx29_c22_2_c45(phydev, prtad, devad, reg_num, C45_WRITE, val);

+

+		if (prtad == phydev->mdio.addr &&

+		    devad == MII_BMCR &&

+		    val & BMCR_RESET)

+			return phy_init_hw(phydev);

+

+		if (change_autoneg)

+			return phy_start_aneg(phydev);

+

+		return 0;

+	default:

+		return -EOPNOTSUPP;

+	}

+}

+

+static int zx29_gmac_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)

+{

+	struct zx29_gmac_dev *priv = netdev_priv(ndev);

+	struct mii_ioctl_data *mii_data = if_mii(ifr);

+	int is_c45 = mdio_phy_id_is_c45(mii_data->phy_id);

+	

+	if (!(netif_running(ndev)))

+		return -EINVAL;

+	if (!priv->phydev)

+		return -EINVAL;

+	if (cmd == SIOCDISABLEPHY)

+		return zx29_gmac_phy_disable(ndev->dev.parent);

+

+	if (cmd == SIOCENABLEPHY)

+		return zx29_gmac_phy_enable(ndev->dev.parent);

+

+	if (is_c45)

+		return zx29_c45_mii_ioctl(priv->phydev, ifr, cmd);

+	

+	return phy_mii_ioctl(priv->phydev, ifr, cmd);

+}

+

+static int eth_change_mtu(struct net_device *ndev, int new_mtu)

+{

+	if (new_mtu < 68 || new_mtu > ETH_DATA_LEN)

+		return -EINVAL;

+	ndev->mtu = new_mtu;

+	return 0;

+}

+

+static int zx29_gmac_set_mac_address(struct net_device *ndev, void *p)

+{

+	int ret = eth_mac_addr(ndev, p);

+	if (!ret) {

+		gmac_update_mac(ndev);	

+		printk(" zx29 gmac set mac addr ok\n");

+	}

+	return ret;

+}

+

+

+static int zx29_gmac_suspend(struct device *dev)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+    unsigned long flag;

+

+    if(ndev) {

+    	if(netif_running(ndev)) {

+//    		netif_device_detach(ndev);

+//    		gmac_stop((void*)ndev->base_addr);

+#ifdef CONFIG_BOOT_WITHOUT_LOCK 

+        phy_stop(priv->phydev);

+		spin_lock_irqsave(&priv->lock, flag);

+		netif_stop_queue(ndev);

+		netif_carrier_off(ndev);

+		priv->stopped = 1;

+

+#ifdef GMAC_NO_INT

+		hrtimer_cancel(priv->timer);

+#endif

+		

+		printk("[%s] netif_running\n", __func__);

+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+		gpio_direction_output(priv->gpio_power[0], 0);

+		gmac_stop((void*)ndev->base_addr);

+#else

+		gmac_stop((void*)ndev->base_addr); 

+		gpio_direction_output(priv->gpio_power[0], 0);

+#endif

+		spin_unlock_irqrestore(&priv->lock, flag);

+

+#endif

+    	}

+    }

+#ifndef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+    pinctrl_pm_select_sleep_state(&pdev->dev);

+#endif

+    return 0;

+}

+

+static int zx29_gmac_resume(struct device *dev)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device *ndev 		= platform_get_drvdata(pdev);

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+

+	volatile unsigned int *gmac = NULL;

+	void __iomem *base = NULL;

+	gmac = (unsigned *)ndev->base_addr;

+    unsigned long flag = 0;

+	

+#ifndef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+    pinctrl_pm_select_default_state(&pdev->dev);

+#endif

+    if(ndev) {

+    	if(netif_running(ndev)) {

+//        	gmac_start((void*)ndev->base_addr);

+//        	netif_device_attach(ndev);

+//            zx29_gmac_phy_enable(dev);

+#ifdef CONFIG_BOOT_WITHOUT_LOCK 

+		printk("[%s] enter\n", __func__);

+		spin_lock_irqsave(&priv->lock, flag);

+		gpio_direction_output(priv->gpio_power[0], 1);

+

+		base = base_clk;

+		gmac_set_clk();

+

+		base = base_phy_release;

+		gmac_phy_release();

+

+		mdelay(500); //icplus ping need

+

+		priv->phydev->drv->config_init(priv->phydev);

+		gmac_hw_init(ndev);

+

+		netif_carrier_on(ndev); 	

+		

+        phy_start(priv->phydev);

+		netif_start_queue(ndev);

+

+#ifdef GMAC_NO_INT

+		hrtimer_start(priv->timer, ktime_set(0, GTIMER_INTERVAL * 1000), HRTIMER_MODE_REL);

+#endif

+

+		priv->stopped = 0;

+		spin_unlock_irqrestore(&priv->lock, flag);

+		printk("[%s] enter\n", __func__);

+#endif

+

+    	}

+    }

+    return 0;

+}

+

+static const struct ethtool_ops zx29_gmac_ethtool_ops = {

+	.get_link = zx29_gmac_get_link,

+	.get_link_ksettings     = phy_ethtool_get_link_ksettings,

+	.set_link_ksettings     = phy_ethtool_set_link_ksettings,

+	/* other func */

+};

+

+static const struct net_device_ops zx29_gmac_netdev_ops = {

+	.ndo_open = zx29_gmac_open,

+	.ndo_stop = zx29_gmac_stop,

+	.ndo_start_xmit = zx29_gmac_start_xmit,

+	.ndo_tx_timeout = zx29_gmac_tx_timeout,

+	.ndo_do_ioctl = zx29_gmac_ioctl,

+	.ndo_change_mtu = eth_change_mtu,

+	.ndo_validate_addr = eth_validate_addr,

+	.ndo_set_mac_address = zx29_gmac_set_mac_address,

+};

+

+

+

+ssize_t show_fun(struct device *dev, struct device_attribute *attr, char *buf)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    int status = 0;

+    volatile unsigned *gmac = (unsigned *)ndev->base_addr;

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+    printk("MAC(1000) :0x%x\n", MAC(0x1000));

+    printk("MAC(1004) :0x%x\n", MAC(0x1004));

+    printk("MAC(1008) :0x%x\n", MAC(0x1008));

+    printk("MAC(100c) :0x%x\n", MAC(0x100c));

+    printk("MAC(1010) :0x%x\n", MAC(0x1010));

+    printk("MAC(1014) int status:0x%x\n", MAC(0x1014));

+    printk("MAC(1018) :0x%x\n", MAC(0x1018));

+    printk("MAC(101c) :0x%x\n", MAC(0x101c));

+    printk("MAC(0000) :0x%x\n", MAC(0x0000));

+    printk("MAC(0004) :0x%x\n", MAC(0x0004));

+    printk("MAC(0010) :0x%x\n", MAC(0x0010));

+

+    status = mdiobus_read(priv->phydev->mdio.bus, 21, 1);

+    printk("phy status:0x%x\n", status);

+    status = mdiobus_read(priv->phydev->mdio.bus, 0, 1);

+    printk("phy status port0:0x%x\n", status);

+    status = mdiobus_read(priv->phydev->mdio.bus, 1, 1);

+    printk("phy status port1:0x%x\n", status);

+    status = mdiobus_read(priv->phydev->mdio.bus, 2, 1);

+    printk("phy status port2:0x%x\n", status);

+    status = mdiobus_read(priv->phydev->mdio.bus, 3, 1);

+    printk("phy status port3:0x%x\n", status);

+    status = mdiobus_read(priv->phydev->mdio.bus, 4, 1);

+    printk("phy status port4:0x%x\n", status);

+

+    status = mdiobus_read(priv->phydev->mdio.bus, 21, 20);

+    status |= 0x4;

+    mdiobus_write(priv->phydev->mdio.bus, 21, 20, status);

+

+    status = mdiobus_read(priv->phydev->mdio.bus, 21, 21);

+    printk("phy status loop port:0x%x\n", status);

+

+

+    return 0;

+}

+

+ssize_t store_fun(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    printk("[%s]", __func__);    

+    return 1;

+}

+

+

+ssize_t mdio_show(struct device *dev, struct device_attribute *attr, char *buf)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	int mmd = 0;

+	int reg = 0;

+	

+    mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+    mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+    mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+	printk("phyaddr:0x%x,devad:0x%x,reg:0x%x,val=0x%x\n", 

+		priv->phydev->mdio.addr,

+		mmd,

+		reg,

+		mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e));

+

+    return 0;

+}

+

+ssize_t mdio_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+    int ret = 0;

+	int mmd = 0;

+	int reg = 0;  

+	int rd_wt = 0;/* rd:0, wt:1 */

+	int val = 0;

+	char *kern_buf = NULL;

+

+	printk(KERN_INFO "%s input str=%s,nbytes=%d \n", __func__, buf, count);

+

+	ret = sscanf(buf, "%x,%x,%x,%x", &rd_wt, &mmd, &reg, &val);

+	if (ret < 4) {

+		printk(KERN_INFO "gmac: failed to read user buf, ret=%d, input 0x%x,0x%x,0x%x,0x%x\n",

+				ret, rd_wt, mmd, reg, val);

+		return count;

+	}

+

+	if (rd_wt !=0 && rd_wt !=1) {

+		printk("please input with format: rd_wt,devad,reg,val\n"

+			   "0:rd, 1:wt,  if rd, val default input 0\n");

+		return ret ? ret : count;

+	}

+

+	if (rd_wt == 0) {

+	    mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		printk("phyaddr:0x%x,devad:0x%x,reg:0x%x,val=0x%x\n", 

+			priv->phydev->mdio.addr,

+			mmd,

+			reg,

+			mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e));

+	}

+

+	if (rd_wt == 1) {

+	    mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+        mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, val);

+

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		printk("phyaddr:0x%x,devad:0x%x,reg:0x%x,val=0x%x\n", 

+			priv->phydev->mdio.addr,

+			mmd,

+			reg,

+			mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e));

+	}

+

+    return count;

+}

+

+

+ssize_t free_mdio_show(struct device *dev, struct device_attribute *attr, char *buf)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	int mmd = 0;

+	int reg = 0;

+	

+    mdiobus_write(priv->phydev->mdio.bus, 8, 0x0d, mmd);

+    mdiobus_write(priv->phydev->mdio.bus, 8, 0x0e, reg);

+    mdiobus_write(priv->phydev->mdio.bus, 8, 0x0d, 0x4000 | mmd);

+	printk("phyaddr:0x%x,devad:0x%x,reg:0x%x,val=0x%x\n", 

+		priv->phydev->mdio.addr,

+		mmd,

+		reg,

+		mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e));

+

+    return 0;

+}

+

+ssize_t free_mdio_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+    int ret = 0;

+	int mmd = 0;

+	int reg = 0;  

+	int rd_wt = 0;/* rd:0, wt:1 */

+	int val = 0;

+	int phy_addr = 8;

+	char *kern_buf = NULL;

+

+	printk(KERN_INFO "%s input str=%s,nbytes=%d \n", __func__, buf, count);

+

+	ret = sscanf(buf, "%x,%x,%x,%x,%x", &rd_wt, &phy_addr, &mmd, &reg, &val);

+	if (ret < 4) {

+		printk(KERN_INFO "gmac: failed to read user buf, ret=%d, input 0x%x,0x%x,0x%x,0x%x,0x%x\n",

+				ret, rd_wt, phy_addr, mmd, reg, val);

+		return count;

+	}

+

+	if (rd_wt !=0 && rd_wt !=1) {

+		printk("please input with format: rd_wt,phy_addr,devad,reg,val\n"

+			   "0:rd, 1:wt,  if rd, val default input 0\n");

+		return ret ? ret : count;

+	}

+

+	if (rd_wt == 0) {

+	    mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0d, 0x4000 | mmd);

+		printk("phyaddr:0x%x,devad:0x%x,reg:0x%x,val=0x%x\n", 

+			phy_addr,

+			mmd,

+			reg,

+			mdiobus_read(priv->phydev->mdio.bus, phy_addr, 0x0e));

+	}

+

+	if (rd_wt == 1) {

+	    mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0d, 0x4000 | mmd);

+        mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0e, val);

+

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, phy_addr, 0x0d, 0x4000 | mmd);

+		printk("phyaddr:0x%x,devad:0x%x,reg:0x%x,val=0x%x\n", 

+			phy_addr,

+			mmd,

+			reg,

+			mdiobus_read(priv->phydev->mdio.bus, phy_addr, 0x0e));

+	}

+

+    return count;

+}

+

+extern int debug_on;

+ssize_t debug_on_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+

+	int val = 0;

+	int ret;

+	ret = sscanf(buf, "%d", &val);

+	if (ret < 1) {

+		printk(KERN_INFO "gmac: failed to read user buf, ret=%d, input %d\n",

+				ret,val);

+		return count;

+	}

+	debug_on = val;	

+    return count;

+}

+

+ssize_t debug_on_show(struct device *dev, struct device_attribute *attr, char *buf)

+{

+	struct platform_device *pdev	= to_platform_device(dev);

+    struct net_device 	*ndev		= platform_get_drvdata(pdev);

+    struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	

+	if (debug_on) 

+		memcpy(buf, "on", 3);

+	else	

+		memcpy(buf, "off", 4);

+    return 0;

+}

+

+/*jb.qi add for gamc power down on 20231116 start*/

+

+extern int gmac_power = 1;

+int gmac_power_flag = 0;

+

+ssize_t gmac_power_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

+{

+    int val = 0;

+    int ret;

+    ret = sscanf(buf, "%d", &val);

+    if(ret < 1)

+    {

+        printk(KERN_INFO "gmac: failed ti read user buf, ret=%d, input %d\n", ret,val);

+        return count;

+    }

+    gmac_power = val;

+    gpio_direction_output(gmac_power_flag, val);

+    return count;

+}

+

+ssize_t gmac_power_show(struct device *dev, struct device_attribute *attr, char *buf)

+{

+    if(gmac_power)

+        memcpy(buf, "on",3);

+    else

+        memcpy(buf, "off", 4);

+

+    printk("gmac_power %s\n", buf);

+    return 0;

+

+}

+/*jb.qi add for gamc power down on 20231116 end */

+

+/*zw.wang add for switching the primary/secondary mode of gmac on 20240118 start*/

+static int mode_type = -1;

+static int enter_only_one = 0;

+

+ssize_t gmac_master_or_slave_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)

+{

+	int mmd = 0;

+	int reg = 0;

+	int val = 0;

+	int ret;

+	struct platform_device *pdev	= to_platform_device(dev);

+	if(!pdev){

+		printk(KERN_ERR "%s : %s pdev : %x \n", __func__,  __LINE__, pdev);

+		return -1;

+	}

+	struct net_device 	*ndev		= platform_get_drvdata(pdev);

+	if(!ndev){

+		printk(KERN_ERR "%s : %s ndev : %x \n", __func__,  __LINE__, ndev);

+		return -1;

+	}

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	if(!priv){

+		printk(KERN_ERR "%s : %s priv : %x \n", __func__,  __LINE__, priv);

+		return -1;

+	}

+

+	///read mode_type

+	ret = sscanf(buf, "%d", &mode_type);

+	if (ret < 1) {

+		printk(KERN_ERR "Please enter the number 0-3 to enable the corresponding mode \n"

+				"Enter values in the non-0-3 range to get pattern description \n");

+		return count;

+	}

+

+	///Judgment model

+	if (mode_type < 0 || mode_type > 3) {

+		printk(KERN_DEBUG "Please enter the number range 0-3\n"

+				"0: Set the slave mode \n"

+				"1: Set the main mode \n"

+				"2: indicates setting SQI value view mode \n"

+				"3: Set the VCT value view mode \n"

+				"After the mode is set, the corresponding value can be obtained\n");

+		return ret ? ret : count;

+	}

+

+	///Set the Ethernet slave mode

+	if (mode_type == 0) {

+		mmd = 0x1;

+		reg = 0x834;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		val = mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e);

+

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, val & (~BIT(14)));

+	}

+	///Set the Ethernet master mode

+	else if (mode_type == 1) {

+		mmd = 0x1;

+		reg = 0x834;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		val = mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e);

+

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, val | BIT(14));

+	}

+	return count;

+}

+

+ssize_t gmac_master_or_slave_show(struct device *dev, struct device_attribute *attr, char *buf)

+{

+	int mmd = 0;

+	int reg = 0;

+	int val = 0;

+	int len = 0;

+	int ret;

+	struct platform_device *pdev	= to_platform_device(dev);

+	if(!pdev){

+		printk(KERN_ERR "%s : %s pdev : %x \n", __func__,  __LINE__, pdev);

+		return -1;

+	}

+	struct net_device 	*ndev		= platform_get_drvdata(pdev);

+	if(!ndev){

+		printk(KERN_ERR "%s : %s ndev : %x \n", __func__,  __LINE__, ndev);

+		return -1;

+	}

+	struct zx29_gmac_dev *priv = (struct zx29_gmac_dev *)netdev_priv(ndev);

+	if(!priv){

+		printk(KERN_ERR "%s : %s priv : %x \n", __func__,  __LINE__, priv);

+		return -1;

+	}

+

+	///Reentrant prevention

+	if(enter_only_one == 1)

+	{

+		return 0;

+	}

+	enter_only_one = 1;

+

+	///Read the network master/slave

+	if (mode_type == 0 || mode_type == 1) {

+		mmd = 0x1;

+		reg = 0x834;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		val = mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e) & BIT(14);

+		if(val)

+			memcpy(buf, "Master\n",7);

+		else

+			memcpy(buf, "Slave\n", 6);

+

+		printk(KERN_DEBUG "mode_type %d - gmac_master_or_slave is %s\n", mode_type, buf);

+

+	}

+	///Obtain the cable quality SQI value

+	else if(mode_type == 2){

+		mmd = 0x1;

+		reg = 0x8B10;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		val = mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e);

+		sprintf(buf, "0x%x\n", val);

+		sprintf(buf, "SQI : 0x%x\n", val);

+		printk(KERN_DEBUG "mode_type %d - SQI is 0x%x", mode_type, val);

+

+	}

+	///Obtain short circuit, open circuit and normal connection of VCT

+	else if(mode_type == 3){

+		///--TDR Enable

+		mmd = 0x1;

+		reg = 0x8B00;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, BIT(14) | BIT(12));

+		msleep(10);

+		///--Read VCT

+		mmd = 0x1;

+		reg = 0x8B02;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		val = mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e);

+		printk(KERN_DEBUG "Open status: %s - Short status: %s\n",

+		 (val & BIT(1)) ? "Open" : "Normal",  (val & BIT(0)) ? "Short" : "Normal");

+		sprintf(buf, "Open status: %s\nShort status: %s\n",

+		 (val & BIT(1)) ? "Open" : "Normal",  (val & BIT(0)) ? "Short" : "Normal");

+		reg = 0x8B01;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		val = mdiobus_read(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e);

+		sprintf(buf, "%sDistance status: 0x%x\n", buf, val);

+		printk(KERN_DEBUG "mode_type %d - Distance status is 0x%x\n", mode_type, val);

+

+		///--TDR Disable

+		mmd = 0x1;

+		reg = 0x8B00;

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, reg);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0d, 0x4000 | mmd);

+		mdiobus_write(priv->phydev->mdio.bus, priv->phydev->mdio.addr, 0x0e, 0);

+

+	}

+	///Get model help information

+	else{

+		sprintf(buf, "Please enter the number range 0-3\n"

+				"0: Set the slave mode \n"

+				"1: Set the main mode \n"

+				"2: indicates setting SQI value view mode \n"

+				"3: Set the VCT value view mode \n"

+				"After the mode is set, the corresponding value can be obtained\n");

+		printk(KERN_DEBUG "Please enter the number range 0-3\n"

+				"0: Set the slave mode \n"

+				"1: Set the main mode \n"

+				"2: indicates setting SQI value view mode \n"

+				"3: Set the VCT value view mode \n"

+				"After the mode is set, the corresponding value can be obtained\n");

+	}

+	enter_only_one = 0;

+	return strlen(buf);

+

+}

+

+/*zw.wang add for switching the primary/secondary mode of gmac on 20240118 end */

+

+static DEVICE_ATTR(gmac_test, 0664, show_fun, store_fun);

+static DEVICE_ATTR(mdio_test, 0664, mdio_show, mdio_store);

+static DEVICE_ATTR(free_mdio, 0664, free_mdio_show, free_mdio_store);

+static DEVICE_ATTR(debug_on, 0664, debug_on_show, debug_on_store);

+static DEVICE_ATTR(gmac_power, 0664, gmac_power_show, gmac_power_store);//jb.qi add for gamc power down on 20231116

+static DEVICE_ATTR(gmac_master_or_slave, 0664, gmac_master_or_slave_show, gmac_master_or_slave_store);//zw.wang add for switching the primary/secondary mode of gmac on 20240118

+

+static int zx29_gmac_probe(struct platform_device *pdev)

+{

+    struct zx29_gmac_dev *prv = NULL;

+	struct net_device *ndev = alloc_etherdev(sizeof(struct zx29_gmac_dev));

+	volatile unsigned int *gmac = NULL;

+	struct device_node *np = pdev->dev.of_node;

+	int ret = -1;

+	unsigned long i;

+	struct mii_bus *mb;

+	struct resource *iomem;

+    void __iomem *base = NULL;

+	struct pinctrl		  *pctrl;

+	struct pinctrl_state  *state0;

+	enum of_gpio_flags flags;

+	int gpio = 0;

+	char board_name[128] = {"init_failed"};

+

+	printk("[%s] #########zx29_gmac_probe begin.\n", __func__);

+	if (!ndev)

+		return -ENOMEM;

+

+    device_create_file(&pdev->dev, &dev_attr_gmac_test);

+	device_create_file(&pdev->dev, &dev_attr_mdio_test);

+	device_create_file(&pdev->dev, &dev_attr_free_mdio);

+	device_create_file(&pdev->dev, &dev_attr_debug_on);

+    device_create_file(&pdev->dev, &dev_attr_gmac_power);//jb.qi add for gamc power down on 20231116

+	device_create_file(&pdev->dev, &dev_attr_gmac_master_or_slave);//zw.wang add for switching the primary/secondary mode of gmac on 20240118

+

+	prv = netdev_priv(ndev);

+	memset(prv, 0, sizeof(*prv));

+	prv->stopped = 1;

+	

+    pctrl = devm_pinctrl_get(&pdev->dev);

+	if (IS_ERR(pctrl)) {

+		dev_warn(&pdev->dev, "Failed to get test pins");

+		pctrl = NULL;

+		goto errirq;

+	}

+

+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+	state0 = pinctrl_lookup_state(pctrl, "state0");

+#else

+	state0 = pinctrl_lookup_state(pctrl, "default");

+#endif

+	if (IS_ERR(state0)) {

+		dev_err(&pdev->dev, "TEST: missing state0\n");

+		goto pinctrl_init_end;

+	}	

+

+	if (pinctrl_select_state(pctrl, state0) < 0) {

+		dev_err(&pdev->dev, "setting state0 failed\n");

+		goto pinctrl_init_end;

+	}

+

+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+	prv->gpio_power[2] = of_get_gpio_flags(pdev->dev.of_node, 2, &flags);

+	ret = gpio_request(prv->gpio_power[2], "phy_power"); /* gpio 51 */

+	gpio_direction_output(prv->gpio_power[2], 1);

+	mdelay(15);

+#endif

+

+    prv->gpio_power[0] = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);

+	ret = gpio_request(prv->gpio_power[0], "gmac_power"); /* gpio 83/124 */

+	gpio_direction_output(prv->gpio_power[0], 1);

+	mdelay(15);

+#ifdef CONFIG_MDIO_C45 //zw.wang Customer chooses phy c22/c45 issues on 20240301

+	prv->gpio_power[1] = of_get_gpio_flags(pdev->dev.of_node, 1, &flags);

+	ret = gpio_request(prv->gpio_power[1], "phy_rst"); /* gpio 63 */

+	gpio_direction_output(prv->gpio_power[1], 0);

+	mdelay(10);

+	gpio_direction_output(prv->gpio_power[1], 1);

+	mdelay(15);

+#endif    

+

+	SET_NETDEV_DEV(ndev, &pdev->dev); //if not, will panic

+	

+	base = devm_platform_ioremap_resource(pdev, 0);

+	gmac_power_flag = prv->gpio_power[0];//jb.qi add for gamc power down on 20231116

+	ndev->base_addr = base;/*iomem->start;*/

+	if (!ndev->base_addr)

+		return -ENXIO;

+

+#ifndef GMAC_NO_INT

+    ndev->irq = platform_get_irq(pdev, 0);

+#endif

+	ndev->netdev_ops = &zx29_gmac_netdev_ops;

+	ndev->ethtool_ops = &zx29_gmac_ethtool_ops;

+

+	gmac = (unsigned *)ndev->base_addr;

+

+	dma_disable();

+	mac_disable();

+	mac_int_disable();

+	

+	spin_lock_init(&prv->lock);

+

+	

+/*	wake_lock_init(&prv->wake_lock, WAKE_LOCK_SUSPEND, "gmac_pm"); //what replace?

+    wake_lock(&prv->wake_lock); */ 

+	device_init_wakeup(&pdev->dev, true);

+

+

+	zx29_gmac_set_macaddr(ndev);

+

+#ifndef GMAC_NO_INT

+	ret = request_irq(ndev->irq, zx29_gmac_interrupt, 0, ndev->name, ndev);

+	if (ret) {

+		printk(KERN_ERR "irq request failed: %d\n", ndev->irq);

+		goto errirq;

+	}

+#endif

+

+	ret = register_netdev(ndev);

+	if (ret) {

+		printk(KERN_ERR "error registering device %s\n",

+			ndev->name);

+		goto errdev;

+	}

+

+	of_property_read_u32(np, "port-nums", &prv->nports); 

+	of_property_read_u32(np, "rmii-ports", &prv->rmii_port);

+	prv->base_addr = ndev->base_addr;

+

+	prv->netdev = ndev;

+

+	mb = mdiobus_alloc();

+	if (!mb) {

+		printk(KERN_ERR "error allocating mii bus\n");

+		goto errmii;

+	}

+	mb->name = "zx29_gmac_mii";

+	mb->read = zx29mii_read;

+	mb->write = zx29mii_write;

+	mb->reset = zx29mii_reset;

+	mb->priv = prv;

+	snprintf(mb->id, MII_BUS_ID_SIZE, "%s-%x", "zx29_gmac", 0);

+	of_property_read_u32(np, "port-mask", &mb->phy_mask);

+/*	mb->irq = &prv->mii.irq[0]; */

+	for (i = 0; i < PHY_MAX_ADDR; i++) {

+		int n = platform_get_irq(pdev, i + 1);  /* devtrrr modify */

+		 if (n < 0)

+		 	n = PHY_POLL;

+		 prv->mii.irq[i] = n;

+		 mb->irq[i] = n;

+	}

+    

+    base = devm_platform_ioremap_resource(pdev, 2);

+	gmac_set_clk();	

+	base_clk = base;

+

+	base = devm_platform_ioremap_resource(pdev, 1);

+	gmac_phy_release();	

+	base_phy_release = base;

+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 

+	mdelay(500);

+#else

+	mdelay(10); //zw.wang@20240306 modify. Here, the jl3103 is set as an example, and other phy peripherals need to be optimized according to different reset stability times

+#endif

+

+	ret = mdiobus_register(mb);

+	if (ret < 0) {

+        printk("[%s] mdiobus register failed!\n", __func__);

+		goto errmdioregister;

+	}

+		

+	prv->mii.bus = mb;

+	ret = zx29_gmac_phy_start(ndev);

+	if (ret)

+		goto errphystart;

+

+	if (!(prv->phydev->phy_id == 0x00000000 || prv->phydev->phy_id == 0xffffffff)) {

+#ifndef CONFIG_BOOT_WITHOUT_LOCK 

+		pm_stay_awake(&pdev->dev);

+#endif		

+        strcpy(board_name, "cpe");

+	

+		printk("[%s] phy id = 0x%x \n", __func__, prv->phydev->phy_id);

+		printk("set gmac wakelock!\n");

+	} else {	

+		strcpy(board_name, "mdl");

+	    netif_device_detach(ndev);

+    }

+    

+	platform_set_drvdata(pdev, ndev);

+

+	tasklet_init(&prv->tasklet, zx29_gmac_tasklet, (unsigned long)ndev);

+	g_gmac_tasklet = &prv->tasklet;

+

+    

+	prv->dma_rx_vir = dma_alloc_coherent(ndev->dev.parent, GMAC_BUF_LEN, &prv->dma_rx_phy, GFP_KERNEL);

+	if (!prv->dma_rx_vir) {             // null, ndev->dev.parent difference?

+		BUG_ON(1);

+		goto errphystart;

+	}

+    	

+	prv->dma_rx_phy_init = prv->dma_rx_phy;

+	prv->dma_rx_vir_init = prv->dma_rx_vir;

+	prv->dma_tx_phy = prv->dma_rx_phy + GMAC_RX_BUF_LEN;

+	prv->dma_tx_vir = prv->dma_rx_vir + GMAC_RX_BUF_LEN;

+

+#ifdef GMAC_NO_INT

+	sema_init(&prv->sem, 0);

+

+	prv->timer = kzalloc(sizeof(struct hrtimer), GFP_KERNEL);

+	if (!prv->timer) {

+		BUG_ON(1);

+		goto errmalloc;

+	}

+	hrtimer_init(prv->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);

+

+	prv->timer->function = gmac_timer_callback;

+#endif

+

+	gmac_event_init(board_name);

+/*	g_gmac_dev = prv; */ /* no use possible*/

+	

+    printk("[%s] probe end\n", __func__);

+	return 0;

+errmalloc:

+	dma_free_coherent(ndev->dev.parent, GMAC_BUF_LEN, &prv->dma_rx_vir, prv->dma_rx_phy);

+	

+	

+errphystart:

+    mdiobus_unregister(mb);

+	

+

+errmdioregister:

+    mdiobus_free(mb);

+	

+errmii:

+	unregister_netdev(ndev);

+

+errdev:

+#ifndef GMAC_NO_INT

+	free_irq(ndev->irq, ndev);

+#endif

+

+pinctrl_init_end:

+

+errirq:

+	free_netdev(ndev);

+

+	printk("#########zx29_gmac_probe fail.\n");

+	return ret;

+}

+

+static int zx29_gmac_remove(struct platform_device *pdev)

+{

+    struct net_device *ndev = platform_get_drvdata(pdev);

+	volatile unsigned *gmac = NULL;

+	if (ndev) {

+		struct zx29_gmac_dev *priv = netdev_priv(ndev);

+

+//	    gpio_direction_output(priv->gpio_power[0], 1);

+//	    msleep(500);

+		unregister_netdev(ndev);

+

+		phy_disconnect(priv->phydev);

+			

+	    kobj_gmac_del(NULL);

+		

+		mdiobus_unregister(priv->mii.bus);

+		mdiobus_free(priv->mii.bus);

+#ifndef GMAC_NO_INT

+		free_irq(ndev->irq, ndev);

+#endif

+		tasklet_disable(&priv->tasklet);

+		tasklet_kill(&priv->tasklet);

+

+		if (priv->dma_rx_vir)

+			dma_free_coherent(ndev->dev.parent, GMAC_BUF_LEN, priv->dma_rx_vir, priv->dma_rx_phy);

+

+		pm_relax(&pdev->dev);

+		free_netdev(ndev);

+	    platform_set_drvdata(pdev, NULL);

+

+#ifdef CONFIG_MDIO_C45 //zw.wang Customer chooses phy c22/c45 issues on 20240301

+		gpio_free(priv->gpio_power[1]);

+#endif

+		gpio_direction_output(priv->gpio_power[0], 0);

+        gpio_free(priv->gpio_power[0]);

+

+		device_remove_file(&pdev->dev, &dev_attr_gmac_test);

+	    device_remove_file(&pdev->dev, &dev_attr_mdio_test);

+	    device_remove_file(&pdev->dev, &dev_attr_free_mdio);

+	    device_remove_file(&pdev->dev, &dev_attr_debug_on);

+        device_remove_file(&pdev->dev, &dev_attr_gmac_power);//jb.qi add for gamc power down on 20231116

+		device_remove_file(&pdev->dev, &dev_attr_gmac_master_or_slave);//zw.wang add for switching the primary/secondary mode of gmac on 20240118

+	}

+	return 0;

+}

+

+static struct dev_pm_ops zx29_gmac_pm_ops = {

+	.suspend = zx29_gmac_suspend,

+	.resume  = zx29_gmac_resume,

+};

+

+static const struct of_device_id gmac_match_table[] = {

+    {.compatible = "zte, zx29_gmac",},

+};

+

+static struct platform_driver zx29_gmac_driver = {

+	.probe  = zx29_gmac_probe,

+	.remove = zx29_gmac_remove,

+	.driver = {

+		.name  = "zx29_gmac",

+		.owner = THIS_MODULE,

+		.pm    = &zx29_gmac_pm_ops,

+		.of_match_table = gmac_match_table,

+	},

+};

+

+static int __init zx29_gmac_init(void)

+{

+	return platform_driver_register(&zx29_gmac_driver);

+}

+

+static void __exit zx29_gmac_exit(void)

+{

+    printk("[%s] start exit!\n", __func__);

+	platform_driver_unregister(&zx29_gmac_driver);

+}

+

+module_init(zx29_gmac_init);

+module_exit(zx29_gmac_exit);

+

+MODULE_LICENSE("GPL");

+MODULE_DESCRIPTION("ZX29 on chip Ethernet driver");

+MODULE_AUTHOR("zhu jianlinag <zhu.jianliang@zte.com.cn>");

diff --git a/upstream/linux-5.10/drivers/net/ethernet/zte/zx29_gmac_event.c b/upstream/linux-5.10/drivers/net/ethernet/zte/zx29_gmac_event.c
new file mode 100755
index 0000000..750580b
--- /dev/null
+++ b/upstream/linux-5.10/drivers/net/ethernet/zte/zx29_gmac_event.c
@@ -0,0 +1,298 @@
+/*

+ * Ethernet driver for zte zx2975xx gmac on chip network device

+ * (c)2008 http://www.zte.com.cn

+ * Authors:	zhang dongdong <zhang.dongdong16@zte.com.cn>

+ *

+ * This program is free software; you can redistribute it and/or

+ * modify it under the terms of the GNU General Public License

+ * as published by the Free Software Foundation; either version

+ * 2 of the License, or (at your option) any later version.

+ */

+#include <linux/kernel.h>

+#include <linux/module.h>

+#include <linux/interrupt.h>

+#include <linux/types.h>

+#include <linux/delay.h>

+#include <linux/init.h>

+#include <linux/spinlock.h>

+#include <linux/netdevice.h>

+#include <linux/etherdevice.h>

+#include <linux/phy.h>

+#include <linux/platform_device.h>

+#include <linux/gmac/gmac.h>

+#include "zx29_gmac.h"

+

+extern void v7_dma_map_area(const void *, size_t, int);

+extern unsigned long virt_to_phys_ap(unsigned long virt);

+

+void dma_map(const void * addr, size_t len, int flags)

+{

+    v7_dma_map_area(addr, len, flags);

+}

+EXPORT_SYMBOL(dma_map);

+

+unsigned long virt_to_phys_ap_new(unsigned long virt_addr)

+{

+    return virt_to_phys_ap(virt_addr);

+}

+EXPORT_SYMBOL(virt_to_phys_ap_new);

+

+int debug_on = 0;

+EXPORT_SYMBOL(debug_on);

+

+struct kset *kset_gmac;

+struct kobject *gmackobj = NULL;

+struct kobject *typekobj = NULL;

+char type[8] = { 0 };

+u32 zx29_gmac_plug_state[3] = {0}; /* 0:phy 1:sw_wan 2:sw_lan */

+

+static struct attribute gmac_phy_plug_attr = {

+	.name = "eth_phy_state",

+	.mode = S_IRWXUGO,

+};

+

+static struct attribute gmac_sw_wan_plug_attr = {

+	.name = "eth_sw_wan_state",

+	.mode = S_IRWXUGO,

+};

+

+static struct attribute gmac_sw_lan_plug_attr = {

+	.name = "eth_sw_lan_state",

+	.mode = S_IRWXUGO,

+};

+

+static struct attribute board_type = {

+	.name = "type",

+	.mode = S_IRWXUGO,

+};

+

+static struct attribute *gmac_status_attrs[] = {

+	&gmac_phy_plug_attr,

+	&gmac_sw_wan_plug_attr,

+	&gmac_sw_lan_plug_attr,

+    &board_type,

+	NULL,

+};

+

+ssize_t kobj_gmac_show(struct kobject *kobject,struct attribute *attr,char *buf)

+{

+    unsigned  link =0;

+

+	if(!strcmp(attr->name,"eth_phy_state")) {

+		if(zx29_gmac_plug_state[0] == 0)

+			sprintf(buf, "%s","0");

+		else

+			sprintf(buf, "%s","1");

+	} else if(!strcmp(attr->name,"eth_sw_wan_state")) {

+		if(zx29_gmac_plug_state[1] == 0)

+			sprintf(buf, "%s","0");

+		else

+			sprintf(buf, "%s","1");

+	} else if(!strcmp(attr->name,"eth_sw_lan_state")) {

+		if(zx29_gmac_plug_state[2] == 0)

+			sprintf(buf, "%s","0");

+		else

+			sprintf(buf, "%s","1");

+	} else if (!strcmp(attr->name,"type")) {

+			sprintf(buf, "%s", type);

+	} else {

+		printk("invalidate attr name.\n");

+	}

+	

+	return strlen(buf);

+}

+

+ssize_t kobj_gmac_store(struct kobject *kobject, struct attribute *attr, const char *buf, size_t size)

+{

+	unsigned int value = 0;

+	value = simple_strtoul(buf, NULL, 4);

+	printk("attrname: %s.\n", attr->name);

+	if (!strcmp(attr->name, "eth_phy_state")) {

+		zx29_gmac_plug_state[0] = value;

+	} else if (!strcmp(attr->name,"eth_sw_wan_state")) {

+		zx29_gmac_plug_state[1] = value;

+	} else if (!strcmp(attr->name,"eth_sw_lan_state")) {

+		zx29_gmac_plug_state[2] = value;

+	} else {

+		printk("invalidate attr name.\n");

+	}

+	return size;

+}

+

+static struct sysfs_ops obj_gmac_sysops = {

+	.show = kobj_gmac_show,

+	.store = kobj_gmac_store,

+};

+

+static void kobj_gmac_release(struct kobject *kobject)

+{

+	printk("[gmac kobj_test: release!]\n");

+}

+

+static void kobj_type_release(struct kobject *kobject)

+{

+	printk("[type kobj_test: release!]\n");

+}

+

+

+void kobj_gmac_del(struct kobject *kobject)

+{

+	kset_unregister(kset_gmac);

+	

+    kobject_uevent(typekobj, KOBJ_REMOVE);

+	kobject_del(typekobj);

+	kobject_put(typekobj);

+	kfree(typekobj);

+	

+	kobject_uevent(gmackobj, KOBJ_REMOVE);

+	kobject_del(gmackobj);

+	kobject_put(gmackobj);

+

+	kfree(gmackobj);

+	

+	printk("[gmac kobj_test: delete!]\n");

+}

+EXPORT_SYMBOL(kobj_gmac_del);

+

+static struct kobj_type gmacktype =

+{       .release = kobj_gmac_release,

+        .sysfs_ops = &obj_gmac_sysops,

+        .default_attrs = gmac_status_attrs,

+};

+

+static struct kobj_type typektype =

+{       .release = kobj_type_release,

+//        .sysfs_ops = &obj_gmac_sysops,

+//        .default_attrs = gmac_status_attrs,

+};

+

+

+static int kset_filter(struct kset *kset,struct kobject *kobj)

+{

+        printk("kset Filter: kobj %s.\n",kobj->name);

+        return 1;

+}

+

+static const char *kset_name(struct kset *kset,struct kobject *kobj)

+{    

+        static char buf[20];

+        printk("Name:  kobj %s.\n",kobj->name);

+        sprintf(buf,"%s","gmac");

+        return buf;

+}

+

+static int kset_uevent(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env)

+{

+        int i = 0;

+        printk("uevent: kobj %s.\n",kobj->name);

+        while (i < env->envp_idx) {

+        	printk("%s.\n",env->envp[i]);

+        	i++;

+		}

+		

+        return 0;

+}

+

+static struct kset_uevent_ops gmac_uevent_ops =

+{

+        .filter = kset_filter,

+        .name = kset_name,

+        .uevent = kset_uevent,

+};

+

+void gmac_event_notify(GMAC_NOTIFY_EVENT notify_type, void *puf)

+{

+	int rtv = -1;

+	enum kobject_action action = KOBJ_UNBIND;

+	char *envp_phy_ext[] = {"GMACEVENT=gmac_eth_phy",NULL};

+	char *envp_sw_wan_ext[] = {"GMACEVENT=gmac_eth_sw_wan",NULL};

+	char *envp_sw_lan_ext[] = {"GMACEVENT=gmac_eth_sw_lan",NULL};

+

+	switch (notify_type) {

+		case GMAC_ETH_PHY_PLUGIN:

+			printk("gmac eth phy plugin \n");

+			action = KOBJ_ADD;

+			zx29_gmac_plug_state[0] = 1;

+			if (gmackobj)

+				rtv = kobject_uevent_env(gmackobj, action, envp_phy_ext);

+			break;

+			

+		case GMAC_ETH_PHY_PLUGOUT:

+			printk("gmac eth phy plugout \n");

+			action = KOBJ_REMOVE;

+			zx29_gmac_plug_state[0] = 0;

+			if(gmackobj)

+				rtv = kobject_uevent_env(gmackobj, action,envp_phy_ext);

+			break;

+			

+		case GMAC_ETH_SW_WAN_PLUGIN:

+			printk("gmac eth switch wan plugin \n");

+			action = KOBJ_ADD;

+			zx29_gmac_plug_state[1] = 1;

+			if(gmackobj)

+				rtv = kobject_uevent_env(gmackobj, action,envp_sw_wan_ext);

+			break;

+			

+		case GMAC_ETH_SW_WAN_PLUGOUT:

+			printk("gmac eth switch wan plugout \n");

+			action = KOBJ_REMOVE;

+			zx29_gmac_plug_state[1] = 0;

+			if(gmackobj)

+				rtv = kobject_uevent_env(gmackobj, action,envp_sw_wan_ext);

+			break;

+			

+		case GMAC_ETH_SW_LAN_PLUGIN:

+			printk("gmac eth switch lan plugin \n");

+			action = KOBJ_ADD;

+			zx29_gmac_plug_state[2] = 1;

+			if(gmackobj)

+				rtv = kobject_uevent_env(gmackobj, action,envp_sw_lan_ext);

+			break;

+			

+		case GMAC_ETH_SW_LAN_PLUGOUT:

+			printk("gmac eth switch lan plugout \n");

+			action = KOBJ_REMOVE;

+			zx29_gmac_plug_state[2] = 0;

+			if(gmackobj)

+				rtv = kobject_uevent_env(gmackobj, action,envp_sw_lan_ext);

+			break;

+

+		default:

+			printk(KERN_WARNING "UNKWON GMAC EVENT \n");

+			break;

+	}

+

+	printk(KERN_WARNING "rtv:%d \n",rtv);

+}

+

+EXPORT_SYMBOL(gmac_event_notify);

+

+int gmac_event_init(const char *name)

+{

+	int ret = 0;

+	/* 创建并注册 kset_p */   

+	gmackobj = kzalloc(sizeof(*gmackobj),GFP_KERNEL);

+	if(!gmackobj){

+		printk(KERN_WARNING "mallock gmackobj failed \n");

+		return 0;

+	}

+	kset_gmac = kset_create_and_add("gmac", &gmac_uevent_ops, NULL); 

+	kobject_init(gmackobj, &gmacktype);

+	kobject_add(gmackobj,&kset_gmac->kobj,"%s","gmacconfig");  

+	gmackobj->kset = kset_gmac;

+

+	typekobj = kzalloc(sizeof(*typekobj),GFP_KERNEL);

+	if(!typekobj){

+		printk(KERN_WARNING "mallock gmackobj failed \n");

+		return 0;

+	}

+//	kset_gmac = kset_create_and_add("gmac", &gmac_uevent_ops, NULL); 

+	kobject_init(typekobj, &typektype);

+	kobject_add(typekobj,&kset_gmac->kobj,"%s",name);  

+	typekobj->kset = kset_gmac;

+

+	strcpy(type, name);

+	

+	return ret;

+}

+EXPORT_SYMBOL(gmac_event_init);

diff --git a/upstream/linux-5.10/drivers/net/phy/phy_device.c b/upstream/linux-5.10/drivers/net/phy/phy_device.c
new file mode 100755
index 0000000..d9b53ba
--- /dev/null
+++ b/upstream/linux-5.10/drivers/net/phy/phy_device.c
@@ -0,0 +1,3113 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Framework for finding and configuring PHYs.
+ * Also contains generic PHY driver
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitmap.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mdio.h>
+#include <linux/mii.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/phy_led_triggers.h>
+#include <linux/property.h>
+#include <linux/sfp.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/unistd.h>
+
+MODULE_DESCRIPTION("PHY library");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_basic_features);
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_t1_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_basic_t1_features);
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_gbit_features);
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_fibre_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_gbit_fibre_features);
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_all_ports_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_gbit_all_ports_features);
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_10gbit_features);
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_fec_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_10gbit_fec_features);
+
+const int phy_basic_ports_array[3] = {
+	ETHTOOL_LINK_MODE_Autoneg_BIT,
+	ETHTOOL_LINK_MODE_TP_BIT,
+	ETHTOOL_LINK_MODE_MII_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_basic_ports_array);
+
+const int phy_fibre_port_array[1] = {
+	ETHTOOL_LINK_MODE_FIBRE_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_fibre_port_array);
+
+const int phy_all_ports_features_array[7] = {
+	ETHTOOL_LINK_MODE_Autoneg_BIT,
+	ETHTOOL_LINK_MODE_TP_BIT,
+	ETHTOOL_LINK_MODE_MII_BIT,
+	ETHTOOL_LINK_MODE_FIBRE_BIT,
+	ETHTOOL_LINK_MODE_AUI_BIT,
+	ETHTOOL_LINK_MODE_BNC_BIT,
+	ETHTOOL_LINK_MODE_Backplane_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_all_ports_features_array);
+
+const int phy_10_100_features_array[4] = {
+	ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+	ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+	ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+	ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_10_100_features_array);
+
+const int phy_basic_t1_features_array[2] = {
+	ETHTOOL_LINK_MODE_TP_BIT,
+	ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_basic_t1_features_array);
+
+const int phy_gbit_features_array[2] = {
+	ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+	ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_gbit_features_array);
+
+const int phy_10gbit_features_array[1] = {
+	ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+};
+EXPORT_SYMBOL_GPL(phy_10gbit_features_array);
+
+static const int phy_10gbit_fec_features_array[1] = {
+	ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
+};
+
+__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_10gbit_full_features);
+
+static const int phy_10gbit_full_features_array[] = {
+	ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+	ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+	ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+	ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+};
+
+//status = phy_read_cl(phydev, MII_BMSR);
+static int phy_read_cl(struct phy_device *phydev, u32 regnum)
+{
+	int val = 0;
+
+	mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, 0x0d, 1);
+	mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, 0x0e, regnum);
+	mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, 0x0d, 0x4000 | 1);
+	val = mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, 0x0e);
+
+	return val;
+}
+
+static void features_init(void)
+{
+	/* 10/100 half/full*/
+	linkmode_set_bit_array(phy_basic_ports_array,
+			       ARRAY_SIZE(phy_basic_ports_array),
+			       phy_basic_features);
+	linkmode_set_bit_array(phy_10_100_features_array,
+			       ARRAY_SIZE(phy_10_100_features_array),
+			       phy_basic_features);
+
+	/* 100 full, TP */
+	linkmode_set_bit_array(phy_basic_t1_features_array,
+			       ARRAY_SIZE(phy_basic_t1_features_array),
+			       phy_basic_t1_features);
+
+	/* 10/100 half/full + 1000 half/full */
+	linkmode_set_bit_array(phy_basic_ports_array,
+			       ARRAY_SIZE(phy_basic_ports_array),
+			       phy_gbit_features);
+	linkmode_set_bit_array(phy_10_100_features_array,
+			       ARRAY_SIZE(phy_10_100_features_array),
+			       phy_gbit_features);
+	linkmode_set_bit_array(phy_gbit_features_array,
+			       ARRAY_SIZE(phy_gbit_features_array),
+			       phy_gbit_features);
+
+	/* 10/100 half/full + 1000 half/full + fibre*/
+	linkmode_set_bit_array(phy_basic_ports_array,
+			       ARRAY_SIZE(phy_basic_ports_array),
+			       phy_gbit_fibre_features);
+	linkmode_set_bit_array(phy_10_100_features_array,
+			       ARRAY_SIZE(phy_10_100_features_array),
+			       phy_gbit_fibre_features);
+	linkmode_set_bit_array(phy_gbit_features_array,
+			       ARRAY_SIZE(phy_gbit_features_array),
+			       phy_gbit_fibre_features);
+	linkmode_set_bit_array(phy_fibre_port_array,
+			       ARRAY_SIZE(phy_fibre_port_array),
+			       phy_gbit_fibre_features);
+
+	/* 10/100 half/full + 1000 half/full + TP/MII/FIBRE/AUI/BNC/Backplane*/
+	linkmode_set_bit_array(phy_all_ports_features_array,
+			       ARRAY_SIZE(phy_all_ports_features_array),
+			       phy_gbit_all_ports_features);
+	linkmode_set_bit_array(phy_10_100_features_array,
+			       ARRAY_SIZE(phy_10_100_features_array),
+			       phy_gbit_all_ports_features);
+	linkmode_set_bit_array(phy_gbit_features_array,
+			       ARRAY_SIZE(phy_gbit_features_array),
+			       phy_gbit_all_ports_features);
+
+	/* 10/100 half/full + 1000 half/full + 10G full*/
+	linkmode_set_bit_array(phy_all_ports_features_array,
+			       ARRAY_SIZE(phy_all_ports_features_array),
+			       phy_10gbit_features);
+	linkmode_set_bit_array(phy_10_100_features_array,
+			       ARRAY_SIZE(phy_10_100_features_array),
+			       phy_10gbit_features);
+	linkmode_set_bit_array(phy_gbit_features_array,
+			       ARRAY_SIZE(phy_gbit_features_array),
+			       phy_10gbit_features);
+	linkmode_set_bit_array(phy_10gbit_features_array,
+			       ARRAY_SIZE(phy_10gbit_features_array),
+			       phy_10gbit_features);
+
+	/* 10/100/1000/10G full */
+	linkmode_set_bit_array(phy_all_ports_features_array,
+			       ARRAY_SIZE(phy_all_ports_features_array),
+			       phy_10gbit_full_features);
+	linkmode_set_bit_array(phy_10gbit_full_features_array,
+			       ARRAY_SIZE(phy_10gbit_full_features_array),
+			       phy_10gbit_full_features);
+	/* 10G FEC only */
+	linkmode_set_bit_array(phy_10gbit_fec_features_array,
+			       ARRAY_SIZE(phy_10gbit_fec_features_array),
+			       phy_10gbit_fec_features);
+}
+
+void phy_device_free(struct phy_device *phydev)
+{
+	put_device(&phydev->mdio.dev);
+}
+EXPORT_SYMBOL(phy_device_free);
+
+static void phy_mdio_device_free(struct mdio_device *mdiodev)
+{
+	struct phy_device *phydev;
+
+	phydev = container_of(mdiodev, struct phy_device, mdio);
+	phy_device_free(phydev);
+}
+
+static void phy_device_release(struct device *dev)
+{
+	kfree(to_phy_device(dev));
+}
+
+static void phy_mdio_device_remove(struct mdio_device *mdiodev)
+{
+	struct phy_device *phydev;
+
+	phydev = container_of(mdiodev, struct phy_device, mdio);
+	phy_device_remove(phydev);
+}
+
+static struct phy_driver genphy_driver;
+
+static LIST_HEAD(phy_fixup_list);
+static DEFINE_MUTEX(phy_fixup_lock);
+
+static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
+{
+	struct device_driver *drv = phydev->mdio.dev.driver;
+	struct phy_driver *phydrv = to_phy_driver(drv);
+	struct net_device *netdev = phydev->attached_dev;
+
+	if (!drv || !phydrv->suspend)
+		return false;
+
+	/* PHY not attached? May suspend if the PHY has not already been
+	 * suspended as part of a prior call to phy_disconnect() ->
+	 * phy_detach() -> phy_suspend() because the parent netdev might be the
+	 * MDIO bus driver and clock gated at this point.
+	 */
+	if (!netdev)
+		goto out;
+
+	if (netdev->wol_enabled)
+		return false;
+
+	/* As long as not all affected network drivers support the
+	 * wol_enabled flag, let's check for hints that WoL is enabled.
+	 * Don't suspend PHY if the attached netdev parent may wake up.
+	 * The parent may point to a PCI device, as in tg3 driver.
+	 */
+	if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))
+		return false;
+
+	/* Also don't suspend PHY if the netdev itself may wakeup. This
+	 * is the case for devices w/o underlaying pwr. mgmt. aware bus,
+	 * e.g. SoC devices.
+	 */
+	if (device_may_wakeup(&netdev->dev))
+		return false;
+
+out:
+	return !phydev->suspended;
+}
+
+static __maybe_unused int mdio_bus_phy_suspend(struct device *dev)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+
+	/* We must stop the state machine manually, otherwise it stops out of
+	 * control, possibly with the phydev->lock held. Upon resume, netdev
+	 * may call phy routines that try to grab the same lock, and that may
+	 * lead to a deadlock.
+	 */
+	if (phydev->attached_dev && phydev->adjust_link)
+		phy_stop_machine(phydev);
+
+	if (!mdio_bus_phy_may_suspend(phydev))
+		return 0;
+
+	phydev->suspended_by_mdio_bus = 1;
+
+	return phy_suspend(phydev);
+}
+
+static __maybe_unused int mdio_bus_phy_resume(struct device *dev)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	int ret;
+
+	if (!phydev->suspended_by_mdio_bus)
+		goto no_resume;
+
+	phydev->suspended_by_mdio_bus = 0;
+
+	ret = phy_init_hw(phydev);
+	if (ret < 0)
+		return ret;
+
+	ret = phy_resume(phydev);
+	if (ret < 0)
+		return ret;
+no_resume:
+	if (phydev->attached_dev && phydev->adjust_link)
+		phy_start_machine(phydev);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mdio_bus_phy_pm_ops, mdio_bus_phy_suspend,
+			 mdio_bus_phy_resume);
+
+/**
+ * phy_register_fixup - creates a new phy_fixup and adds it to the list
+ * @bus_id: A string which matches phydev->mdio.dev.bus_id (or PHY_ANY_ID)
+ * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY)
+ *	It can also be PHY_ANY_UID
+ * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before
+ *	comparison
+ * @run: The actual code to be run when a matching PHY is found
+ */
+int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask,
+		       int (*run)(struct phy_device *))
+{
+	struct phy_fixup *fixup = kzalloc(sizeof(*fixup), GFP_KERNEL);
+
+	if (!fixup)
+		return -ENOMEM;
+
+	strlcpy(fixup->bus_id, bus_id, sizeof(fixup->bus_id));
+	fixup->phy_uid = phy_uid;
+	fixup->phy_uid_mask = phy_uid_mask;
+	fixup->run = run;
+
+	mutex_lock(&phy_fixup_lock);
+	list_add_tail(&fixup->list, &phy_fixup_list);
+	mutex_unlock(&phy_fixup_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_register_fixup);
+
+/* Registers a fixup to be run on any PHY with the UID in phy_uid */
+int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
+			       int (*run)(struct phy_device *))
+{
+	return phy_register_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask, run);
+}
+EXPORT_SYMBOL(phy_register_fixup_for_uid);
+
+/* Registers a fixup to be run on the PHY with id string bus_id */
+int phy_register_fixup_for_id(const char *bus_id,
+			      int (*run)(struct phy_device *))
+{
+	return phy_register_fixup(bus_id, PHY_ANY_UID, 0xffffffff, run);
+}
+EXPORT_SYMBOL(phy_register_fixup_for_id);
+
+/**
+ * phy_unregister_fixup - remove a phy_fixup from the list
+ * @bus_id: A string matches fixup->bus_id (or PHY_ANY_ID) in phy_fixup_list
+ * @phy_uid: A phy id matches fixup->phy_id (or PHY_ANY_UID) in phy_fixup_list
+ * @phy_uid_mask: Applied to phy_uid and fixup->phy_uid before comparison
+ */
+int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask)
+{
+	struct list_head *pos, *n;
+	struct phy_fixup *fixup;
+	int ret;
+
+	ret = -ENODEV;
+
+	mutex_lock(&phy_fixup_lock);
+	list_for_each_safe(pos, n, &phy_fixup_list) {
+		fixup = list_entry(pos, struct phy_fixup, list);
+
+		if ((!strcmp(fixup->bus_id, bus_id)) &&
+		    ((fixup->phy_uid & phy_uid_mask) ==
+		     (phy_uid & phy_uid_mask))) {
+			list_del(&fixup->list);
+			kfree(fixup);
+			ret = 0;
+			break;
+		}
+	}
+	mutex_unlock(&phy_fixup_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(phy_unregister_fixup);
+
+/* Unregisters a fixup of any PHY with the UID in phy_uid */
+int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask)
+{
+	return phy_unregister_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask);
+}
+EXPORT_SYMBOL(phy_unregister_fixup_for_uid);
+
+/* Unregisters a fixup of the PHY with id string bus_id */
+int phy_unregister_fixup_for_id(const char *bus_id)
+{
+	return phy_unregister_fixup(bus_id, PHY_ANY_UID, 0xffffffff);
+}
+EXPORT_SYMBOL(phy_unregister_fixup_for_id);
+
+/* Returns 1 if fixup matches phydev in bus_id and phy_uid.
+ * Fixups can be set to match any in one or more fields.
+ */
+static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup)
+{
+	if (strcmp(fixup->bus_id, phydev_name(phydev)) != 0)
+		if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0)
+			return 0;
+
+	if ((fixup->phy_uid & fixup->phy_uid_mask) !=
+	    (phydev->phy_id & fixup->phy_uid_mask))
+		if (fixup->phy_uid != PHY_ANY_UID)
+			return 0;
+
+	return 1;
+}
+
+/* Runs any matching fixups for this phydev */
+static int phy_scan_fixups(struct phy_device *phydev)
+{
+	struct phy_fixup *fixup;
+
+	mutex_lock(&phy_fixup_lock);
+	list_for_each_entry(fixup, &phy_fixup_list, list) {
+		if (phy_needs_fixup(phydev, fixup)) {
+			int err = fixup->run(phydev);
+
+			if (err < 0) {
+				mutex_unlock(&phy_fixup_lock);
+				return err;
+			}
+			phydev->has_fixups = true;
+		}
+	}
+	mutex_unlock(&phy_fixup_lock);
+
+	return 0;
+}
+
+static int phy_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	struct phy_driver *phydrv = to_phy_driver(drv);
+	const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
+	int i;
+
+	if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
+		return 0;
+
+	if (phydrv->match_phy_device)
+		return phydrv->match_phy_device(phydev);
+
+	if (phydev->is_c45) {
+		for (i = 1; i < num_ids; i++) {
+			if (phydev->c45_ids.device_ids[i] == 0xffffffff)
+				continue;
+
+			if ((phydrv->phy_id & phydrv->phy_id_mask) ==
+			    (phydev->c45_ids.device_ids[i] &
+			     phydrv->phy_id_mask))
+				return 1;
+		}
+		return 0;
+	} else {
+		return (phydrv->phy_id & phydrv->phy_id_mask) ==
+			(phydev->phy_id & phydrv->phy_id_mask);
+	}
+}
+
+static ssize_t
+phy_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+
+	return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id);
+}
+static DEVICE_ATTR_RO(phy_id);
+
+static ssize_t
+phy_interface_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	const char *mode = NULL;
+
+	if (phy_is_internal(phydev))
+		mode = "internal";
+	else
+		mode = phy_modes(phydev->interface);
+
+	return sprintf(buf, "%s\n", mode);
+}
+static DEVICE_ATTR_RO(phy_interface);
+
+static ssize_t
+phy_has_fixups_show(struct device *dev, struct device_attribute *attr,
+		    char *buf)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+
+	return sprintf(buf, "%d\n", phydev->has_fixups);
+}
+static DEVICE_ATTR_RO(phy_has_fixups);
+
+static struct attribute *phy_dev_attrs[] = {
+	&dev_attr_phy_id.attr,
+	&dev_attr_phy_interface.attr,
+	&dev_attr_phy_has_fixups.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(phy_dev);
+
+static const struct device_type mdio_bus_phy_type = {
+	.name = "PHY",
+	.groups = phy_dev_groups,
+	.release = phy_device_release,
+	.pm = pm_ptr(&mdio_bus_phy_pm_ops),
+};
+
+static int phy_request_driver_module(struct phy_device *dev, u32 phy_id)
+{
+	int ret;
+
+	ret = request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
+			     MDIO_ID_ARGS(phy_id));
+	/* We only check for failures in executing the usermode binary,
+	 * not whether a PHY driver module exists for the PHY ID.
+	 * Accept -ENOENT because this may occur in case no initramfs exists,
+	 * then modprobe isn't available.
+	 */
+	if (IS_ENABLED(CONFIG_MODULES) && ret < 0 && ret != -ENOENT) {
+		phydev_err(dev, "error %d loading PHY driver module for ID 0x%08lx\n",
+			   ret, (unsigned long)phy_id);
+		return ret;
+	}
+
+	return 0;
+}
+
+struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
+				     bool is_c45,
+				     struct phy_c45_device_ids *c45_ids)
+{
+	struct phy_device *dev;
+	struct mdio_device *mdiodev;
+	int ret = 0;
+
+	/* We allocate the device, and initialize the default values */
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	mdiodev = &dev->mdio;
+	mdiodev->dev.parent = &bus->dev;
+	mdiodev->dev.bus = &mdio_bus_type;
+	mdiodev->dev.type = &mdio_bus_phy_type;
+	mdiodev->bus = bus;
+	mdiodev->bus_match = phy_bus_match;
+	mdiodev->addr = addr;
+	mdiodev->flags = MDIO_DEVICE_FLAG_PHY;
+	mdiodev->device_free = phy_mdio_device_free;
+	mdiodev->device_remove = phy_mdio_device_remove;
+
+	dev->speed = SPEED_UNKNOWN;
+	dev->duplex = DUPLEX_UNKNOWN;
+	dev->pause = 0;
+	dev->asym_pause = 0;
+	dev->link = 0;
+	dev->port = PORT_TP;
+	dev->interface = PHY_INTERFACE_MODE_GMII;
+
+	dev->autoneg = AUTONEG_ENABLE;
+
+	dev->is_c45 = is_c45;
+	dev->phy_id = phy_id;
+	if (c45_ids)
+		dev->c45_ids = *c45_ids;
+	dev->irq = bus->irq[addr];
+
+	dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
+	device_initialize(&mdiodev->dev);
+
+	dev->state = PHY_DOWN;
+
+	mutex_init(&dev->lock);
+	INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
+
+	/* Request the appropriate module unconditionally; don't
+	 * bother trying to do so only if it isn't already loaded,
+	 * because that gets complicated. A hotplug event would have
+	 * done an unconditional modprobe anyway.
+	 * We don't do normal hotplug because it won't work for MDIO
+	 * -- because it relies on the device staying around for long
+	 * enough for the driver to get loaded. With MDIO, the NIC
+	 * driver will get bored and give up as soon as it finds that
+	 * there's no driver _already_ loaded.
+	 */
+	if (is_c45 && c45_ids) {
+		const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
+		int i;
+
+		for (i = 1; i < num_ids; i++) {
+			if (c45_ids->device_ids[i] == 0xffffffff)
+				continue;
+
+			ret = phy_request_driver_module(dev,
+						c45_ids->device_ids[i]);
+			if (ret)
+				break;
+		}
+	} else {
+		ret = phy_request_driver_module(dev, phy_id);
+	}
+
+	if (ret) {
+		put_device(&mdiodev->dev);
+		dev = ERR_PTR(ret);
+	}
+
+	return dev;
+}
+EXPORT_SYMBOL(phy_device_create);
+
+/* phy_c45_probe_present - checks to see if a MMD is present in the package
+ * @bus: the target MII bus
+ * @prtad: PHY package address on the MII bus
+ * @devad: PHY device (MMD) address
+ *
+ * Read the MDIO_STAT2 register, and check whether a device is responding
+ * at this address.
+ *
+ * Returns: negative error number on bus access error, zero if no device
+ * is responding, or positive if a device is present.
+ */
+static int phy_c45_probe_present(struct mii_bus *bus, int prtad, int devad)
+{
+	int stat2;
+
+	stat2 = mdiobus_c45_read(bus, prtad, devad, MDIO_STAT2);
+	if (stat2 < 0)
+		return stat2;
+
+	return (stat2 & MDIO_STAT2_DEVPRST) == MDIO_STAT2_DEVPRST_VAL;
+}
+
+/* get_phy_c45_devs_in_pkg - reads a MMD's devices in package registers.
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
+ * @dev_addr: MMD address in the PHY.
+ * @devices_in_package: where to store the devices in package information.
+ *
+ * Description: reads devices in package registers of a MMD at @dev_addr
+ * from PHY at @addr on @bus.
+ *
+ * Returns: 0 on success, -EIO on failure.
+ */
+static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr,
+				   u32 *devices_in_package)
+{
+	int phy_reg;
+
+	phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS2);
+	if (phy_reg < 0)
+		return -EIO;
+	*devices_in_package = phy_reg << 16;
+
+	phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS1);
+	if (phy_reg < 0)
+		return -EIO;
+	*devices_in_package |= phy_reg;
+
+	return 0;
+}
+
+/**
+ * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs.
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
+ * @c45_ids: where to store the c45 ID information.
+ *
+ * Read the PHY "devices in package". If this appears to be valid, read
+ * the PHY identifiers for each device. Return the "devices in package"
+ * and identifiers in @c45_ids.
+ *
+ * Returns zero on success, %-EIO on bus access error, or %-ENODEV if
+ * the "devices in package" is invalid.
+ */
+static int get_phy_c45_ids(struct mii_bus *bus, int addr,
+			   struct phy_c45_device_ids *c45_ids)
+{
+	const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
+	u32 devs_in_pkg = 0;
+	int i, ret, phy_reg;
+
+	/* Find first non-zero Devices In package. Device zero is reserved
+	 * for 802.3 c45 complied PHYs, so don't probe it at first.
+	 */
+	for (i = 1; i < MDIO_MMD_NUM && (devs_in_pkg == 0 ||
+	     (devs_in_pkg & 0x1fffffff) == 0x1fffffff); i++) {
+		if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) {
+			/* Check that there is a device present at this
+			 * address before reading the devices-in-package
+			 * register to avoid reading garbage from the PHY.
+			 * Some PHYs (88x3310) vendor space is not IEEE802.3
+			 * compliant.
+			 */
+			ret = phy_c45_probe_present(bus, addr, i);
+			if (ret < 0)
+				return -EIO;
+
+			if (!ret)
+				continue;
+		}
+		phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, &devs_in_pkg);
+		if (phy_reg < 0)
+			return -EIO;
+	}
+
+	if ((devs_in_pkg & 0x1fffffff) == 0x1fffffff) {
+		/* If mostly Fs, there is no device there, then let's probe
+		 * MMD 0, as some 10G PHYs have zero Devices In package,
+		 * e.g. Cortina CS4315/CS4340 PHY.
+		 */
+		phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, &devs_in_pkg);
+		if (phy_reg < 0)
+			return -EIO;
+
+		/* no device there, let's get out of here */
+		if ((devs_in_pkg & 0x1fffffff) == 0x1fffffff)
+			return -ENODEV;
+	}
+
+	/* Now probe Device Identifiers for each device present. */
+	for (i = 1; i < num_ids; i++) {
+		if (!(devs_in_pkg & (1 << i)))
+			continue;
+
+		if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) {
+			/* Probe the "Device Present" bits for the vendor MMDs
+			 * to ignore these if they do not contain IEEE 802.3
+			 * registers.
+			 */
+			ret = phy_c45_probe_present(bus, addr, i);
+			if (ret < 0)
+				return ret;
+
+			if (!ret)
+				continue;
+		}
+
+		phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID1);
+		if (phy_reg < 0)
+			return -EIO;
+		c45_ids->device_ids[i] = phy_reg << 16;
+
+		phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID2);
+		if (phy_reg < 0)
+			return -EIO;
+		c45_ids->device_ids[i] |= phy_reg;
+	}
+
+	c45_ids->devices_in_package = devs_in_pkg;
+	/* Bit 0 doesn't represent a device, it indicates c22 regs presence */
+	c45_ids->mmds_present = devs_in_pkg & ~BIT(0);
+
+	return 0;
+}
+
+/**
+ * get_phy_c22_id - reads the specified addr for its clause 22 ID.
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
+ * @phy_id: where to store the ID retrieved.
+ *
+ * Read the 802.3 clause 22 PHY ID from the PHY at @addr on the @bus,
+ * placing it in @phy_id. Return zero on successful read and the ID is
+ * valid, %-EIO on bus access error, or %-ENODEV if no device responds
+ * or invalid ID.
+ */
+static int get_phy_c22_id(struct mii_bus *bus, int addr, u32 *phy_id)
+{
+	int phy_reg;
+
+	/* Grab the bits from PHYIR1, and put them in the upper half */
+//	phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);
+#ifdef CONFIG_MDIO_C45 //zw.wang Customer chooses phy c22/c45 issues on 20240301
+    mdiobus_write(bus, addr, 0x0d, 1);
+    mdiobus_write(bus, addr, 0x0e, 2);
+    mdiobus_write(bus, addr, 0x0d, 0x4000 | 1);
+    phy_reg = mdiobus_read(bus, addr, 0x0e);
+#else 
+	phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);
+#endif
+    if (phy_reg < 0) {
+		/* returning -ENODEV doesn't stop bus scanning */
+		return (phy_reg == -EIO || phy_reg == -ENODEV) ? -ENODEV : -EIO;
+	}
+
+	*phy_id = phy_reg << 16;
+
+	/* Grab the bits from PHYIR2, and put them in the lower half */
+//	phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);
+#ifdef CONFIG_MDIO_C45	
+    mdiobus_write(bus, addr, 0x0d, 1);
+    mdiobus_write(bus, addr, 0x0e, 3);
+    mdiobus_write(bus, addr, 0x0d, 0x4000 | 1);
+    phy_reg = mdiobus_read(bus, addr, 0x0e);
+#else 
+	phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);
+#endif
+	if (phy_reg < 0) {
+		/* returning -ENODEV doesn't stop bus scanning */
+		return (phy_reg == -EIO || phy_reg == -ENODEV) ? -ENODEV : -EIO;
+	}
+
+	*phy_id |= phy_reg;
+
+#ifdef CONFIG_MDIO_C45	
+    printk("[%s] read with c45 phy id:0x%x\n", __func__, *phy_id);
+#else
+    printk("[%s] read with c22 phy id:0x%x\n", __func__, *phy_id);
+#endif
+	/* If the phy_id is mostly Fs, there is no device there */
+	if ((*phy_id & 0x1fffffff) == 0x1fffffff)
+		return -ENODEV;
+
+	return 0;
+}
+
+/**
+ * get_phy_device - reads the specified PHY device and returns its @phy_device
+ *		    struct
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
+ * @is_c45: If true the PHY uses the 802.3 clause 45 protocol
+ *
+ * Probe for a PHY at @addr on @bus.
+ *
+ * When probing for a clause 22 PHY, then read the ID registers. If we find
+ * a valid ID, allocate and return a &struct phy_device.
+ *
+ * When probing for a clause 45 PHY, read the "devices in package" registers.
+ * If the "devices in package" appears valid, read the ID registers for each
+ * MMD, allocate and return a &struct phy_device.
+ *
+ * Returns an allocated &struct phy_device on success, %-ENODEV if there is
+ * no PHY present, or %-EIO on bus access error.
+ */
+struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
+{
+	struct phy_c45_device_ids c45_ids;
+	u32 phy_id = 0;
+	int r;
+
+	c45_ids.devices_in_package = 0;
+	c45_ids.mmds_present = 0;
+	memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids));
+
+	if (is_c45)
+		r = get_phy_c45_ids(bus, addr, &c45_ids);
+	else
+		r = get_phy_c22_id(bus, addr, &phy_id);
+
+	if (r)
+		return ERR_PTR(r);
+
+	return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);
+}
+EXPORT_SYMBOL(get_phy_device);
+
+/**
+ * phy_device_register - Register the phy device on the MDIO bus
+ * @phydev: phy_device structure to be added to the MDIO bus
+ */
+int phy_device_register(struct phy_device *phydev)
+{
+	int err;
+
+	err = mdiobus_register_device(&phydev->mdio);
+	if (err)
+		return err;
+
+	/* Deassert the reset signal */
+	phy_device_reset(phydev, 0);
+
+	/* Run all of the fixups for this PHY */
+	err = phy_scan_fixups(phydev);
+	if (err) {
+		phydev_err(phydev, "failed to initialize\n");
+		goto out;
+	}
+
+	err = device_add(&phydev->mdio.dev);
+	if (err) {
+		phydev_err(phydev, "failed to add\n");
+		goto out;
+	}
+
+	return 0;
+
+ out:
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
+
+	mdiobus_unregister_device(&phydev->mdio);
+	return err;
+}
+EXPORT_SYMBOL(phy_device_register);
+
+/**
+ * phy_device_remove - Remove a previously registered phy device from the MDIO bus
+ * @phydev: phy_device structure to remove
+ *
+ * This doesn't free the phy_device itself, it merely reverses the effects
+ * of phy_device_register(). Use phy_device_free() to free the device
+ * after calling this function.
+ */
+void phy_device_remove(struct phy_device *phydev)
+{
+	if (phydev->mii_ts)
+		unregister_mii_timestamper(phydev->mii_ts);
+
+	device_del(&phydev->mdio.dev);
+
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
+
+	mdiobus_unregister_device(&phydev->mdio);
+}
+EXPORT_SYMBOL(phy_device_remove);
+
+/**
+ * phy_find_first - finds the first PHY device on the bus
+ * @bus: the target MII bus
+ */
+struct phy_device *phy_find_first(struct mii_bus *bus)
+{
+	struct phy_device *phydev;
+	int addr;
+
+	for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
+		phydev = mdiobus_get_phy(bus, addr);
+		if (phydev) {
+			printk("[%s] addr:%d\n", __func__, addr);
+			return phydev;
+		}
+	}
+	return NULL;
+}
+EXPORT_SYMBOL(phy_find_first);
+
+static void phy_link_change(struct phy_device *phydev, bool up)
+{
+	struct net_device *netdev = phydev->attached_dev;
+
+	if (up)
+		netif_carrier_on(netdev);
+	else
+		netif_carrier_off(netdev);
+	phydev->adjust_link(netdev);
+	if (phydev->mii_ts && phydev->mii_ts->link_state)
+		phydev->mii_ts->link_state(phydev->mii_ts, phydev);
+}
+
+/**
+ * phy_prepare_link - prepares the PHY layer to monitor link status
+ * @phydev: target phy_device struct
+ * @handler: callback function for link status change notifications
+ *
+ * Description: Tells the PHY infrastructure to handle the
+ *   gory details on monitoring link status (whether through
+ *   polling or an interrupt), and to call back to the
+ *   connected device driver when the link status changes.
+ *   If you want to monitor your own link state, don't call
+ *   this function.
+ */
+static void phy_prepare_link(struct phy_device *phydev,
+			     void (*handler)(struct net_device *))
+{
+	phydev->adjust_link = handler;
+}
+
+/**
+ * phy_connect_direct - connect an ethernet device to a specific phy_device
+ * @dev: the network device to connect
+ * @phydev: the pointer to the phy device
+ * @handler: callback function for state change notifications
+ * @interface: PHY device's interface
+ */
+int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,
+		       void (*handler)(struct net_device *),
+		       phy_interface_t interface)
+{
+	int rc;
+
+	if (!dev)
+		return -EINVAL;
+
+	rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);
+	if (rc)
+		return rc;
+
+	phy_prepare_link(phydev, handler);
+	if (phy_interrupt_is_valid(phydev))
+		phy_request_interrupt(phydev);
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_connect_direct);
+
+/**
+ * phy_connect - connect an ethernet device to a PHY device
+ * @dev: the network device to connect
+ * @bus_id: the id string of the PHY device to connect
+ * @handler: callback function for state change notifications
+ * @interface: PHY device's interface
+ *
+ * Description: Convenience function for connecting ethernet
+ *   devices to PHY devices.  The default behavior is for
+ *   the PHY infrastructure to handle everything, and only notify
+ *   the connected driver when the link status changes.  If you
+ *   don't want, or can't use the provided functionality, you may
+ *   choose to call only the subset of functions which provide
+ *   the desired functionality.
+ */
+struct phy_device *phy_connect(struct net_device *dev, const char *bus_id,
+			       void (*handler)(struct net_device *),
+			       phy_interface_t interface)
+{
+	struct phy_device *phydev;
+	struct device *d;
+	int rc;
+
+	/* Search the list of PHY devices on the mdio bus for the
+	 * PHY with the requested name
+	 */
+	d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id);
+	if (!d) {
+		pr_err("PHY %s not found\n", bus_id);
+		return ERR_PTR(-ENODEV);
+	}
+	phydev = to_phy_device(d);
+
+	rc = phy_connect_direct(dev, phydev, handler, interface);
+	put_device(d);
+	if (rc)
+		return ERR_PTR(rc);
+
+	return phydev;
+}
+EXPORT_SYMBOL(phy_connect);
+
+/**
+ * phy_disconnect - disable interrupts, stop state machine, and detach a PHY
+ *		    device
+ * @phydev: target phy_device struct
+ */
+void phy_disconnect(struct phy_device *phydev)
+{
+	if (phy_is_started(phydev))
+		phy_stop(phydev);
+
+	if (phy_interrupt_is_valid(phydev))
+		phy_free_interrupt(phydev);
+
+	phydev->adjust_link = NULL;
+
+	phy_detach(phydev);
+}
+EXPORT_SYMBOL(phy_disconnect);
+
+/**
+ * phy_poll_reset - Safely wait until a PHY reset has properly completed
+ * @phydev: The PHY device to poll
+ *
+ * Description: According to IEEE 802.3, Section 2, Subsection 22.2.4.1.1, as
+ *   published in 2008, a PHY reset may take up to 0.5 seconds.  The MII BMCR
+ *   register must be polled until the BMCR_RESET bit clears.
+ *
+ *   Furthermore, any attempts to write to PHY registers may have no effect
+ *   or even generate MDIO bus errors until this is complete.
+ *
+ *   Some PHYs (such as the Marvell 88E1111) don't entirely conform to the
+ *   standard and do not fully reset after the BMCR_RESET bit is set, and may
+ *   even *REQUIRE* a soft-reset to properly restart autonegotiation.  In an
+ *   effort to support such broken PHYs, this function is separate from the
+ *   standard phy_init_hw() which will zero all the other bits in the BMCR
+ *   and reapply all driver-specific and board-specific fixups.
+ */
+static int phy_poll_reset(struct phy_device *phydev)
+{
+	/* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
+	int ret, val;
+
+	ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
+				    50000, 600000, true);
+	if (ret)
+		return ret;
+	/* Some chips (smsc911x) may still need up to another 1ms after the
+	 * BMCR_RESET bit is cleared before they are usable.
+	 */
+	msleep(1);
+	return 0;
+}
+
+int phy_init_hw(struct phy_device *phydev)
+{
+	int ret = 0;
+
+	/* Deassert the reset signal */
+	phy_device_reset(phydev, 0);
+
+	if (!phydev->drv)
+		return 0;
+
+	if (phydev->drv->soft_reset) {
+		ret = phydev->drv->soft_reset(phydev);
+		/* see comment in genphy_soft_reset for an explanation */
+		if (!ret)
+			phydev->suspended = 0;
+	}
+
+	if (ret < 0)
+		return ret;
+
+	ret = phy_scan_fixups(phydev);
+	if (ret < 0)
+		return ret;
+
+	if (phydev->drv->config_init) {
+		ret = phydev->drv->config_init(phydev);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (phydev->drv->config_intr) {
+		ret = phydev->drv->config_intr(phydev);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_init_hw);
+
+void phy_attached_info(struct phy_device *phydev)
+{
+	phy_attached_print(phydev, NULL);
+}
+EXPORT_SYMBOL(phy_attached_info);
+
+#define ATTACHED_FMT "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%s)"
+char *phy_attached_info_irq(struct phy_device *phydev)
+{
+	char *irq_str;
+	char irq_num[8];
+
+	switch(phydev->irq) {
+	case PHY_POLL:
+		irq_str = "POLL";
+		break;
+	case PHY_IGNORE_INTERRUPT:
+		irq_str = "IGNORE";
+		break;
+	default:
+		snprintf(irq_num, sizeof(irq_num), "%d", phydev->irq);
+		irq_str = irq_num;
+		break;
+	}
+
+	return kasprintf(GFP_KERNEL, "%s", irq_str);
+}
+EXPORT_SYMBOL(phy_attached_info_irq);
+
+void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)
+{
+	const char *drv_name = phydev->drv ? phydev->drv->name : "unbound";
+	char *irq_str = phy_attached_info_irq(phydev);
+
+	if (!fmt) {
+		phydev_info(phydev, ATTACHED_FMT "\n",
+			 drv_name, phydev_name(phydev),
+			 irq_str);
+	} else {
+		va_list ap;
+
+		phydev_info(phydev, ATTACHED_FMT,
+			 drv_name, phydev_name(phydev),
+			 irq_str);
+
+		va_start(ap, fmt);
+		vprintk(fmt, ap);
+		va_end(ap);
+	}
+	kfree(irq_str);
+}
+EXPORT_SYMBOL(phy_attached_print);
+
+static void phy_sysfs_create_links(struct phy_device *phydev)
+{
+	struct net_device *dev = phydev->attached_dev;
+	int err;
+
+	if (!dev)
+		return;
+
+	err = sysfs_create_link(&phydev->mdio.dev.kobj, &dev->dev.kobj,
+				"attached_dev");
+	if (err)
+		return;
+
+	err = sysfs_create_link_nowarn(&dev->dev.kobj,
+				       &phydev->mdio.dev.kobj,
+				       "phydev");
+	if (err) {
+		dev_err(&dev->dev, "could not add device link to %s err %d\n",
+			kobject_name(&phydev->mdio.dev.kobj),
+			err);
+		/* non-fatal - some net drivers can use one netdevice
+		 * with more then one phy
+		 */
+	}
+
+	phydev->sysfs_links = true;
+}
+
+static ssize_t
+phy_standalone_show(struct device *dev, struct device_attribute *attr,
+		    char *buf)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+
+	return sprintf(buf, "%d\n", !phydev->attached_dev);
+}
+static DEVICE_ATTR_RO(phy_standalone);
+
+/**
+ * phy_sfp_attach - attach the SFP bus to the PHY upstream network device
+ * @upstream: pointer to the phy device
+ * @bus: sfp bus representing cage being attached
+ *
+ * This is used to fill in the sfp_upstream_ops .attach member.
+ */
+void phy_sfp_attach(void *upstream, struct sfp_bus *bus)
+{
+	struct phy_device *phydev = upstream;
+
+	if (phydev->attached_dev)
+		phydev->attached_dev->sfp_bus = bus;
+	phydev->sfp_bus_attached = true;
+}
+EXPORT_SYMBOL(phy_sfp_attach);
+
+/**
+ * phy_sfp_detach - detach the SFP bus from the PHY upstream network device
+ * @upstream: pointer to the phy device
+ * @bus: sfp bus representing cage being attached
+ *
+ * This is used to fill in the sfp_upstream_ops .detach member.
+ */
+void phy_sfp_detach(void *upstream, struct sfp_bus *bus)
+{
+	struct phy_device *phydev = upstream;
+
+	if (phydev->attached_dev)
+		phydev->attached_dev->sfp_bus = NULL;
+	phydev->sfp_bus_attached = false;
+}
+EXPORT_SYMBOL(phy_sfp_detach);
+
+/**
+ * phy_sfp_probe - probe for a SFP cage attached to this PHY device
+ * @phydev: Pointer to phy_device
+ * @ops: SFP's upstream operations
+ */
+int phy_sfp_probe(struct phy_device *phydev,
+		  const struct sfp_upstream_ops *ops)
+{
+	struct sfp_bus *bus;
+	int ret = 0;
+
+	if (phydev->mdio.dev.fwnode) {
+		bus = sfp_bus_find_fwnode(phydev->mdio.dev.fwnode);
+		if (IS_ERR(bus))
+			return PTR_ERR(bus);
+
+		phydev->sfp_bus = bus;
+
+		ret = sfp_bus_add_upstream(bus, phydev, ops);
+		sfp_bus_put(bus);
+	}
+	return ret;
+}
+EXPORT_SYMBOL(phy_sfp_probe);
+
+/**
+ * phy_attach_direct - attach a network device to a given PHY device pointer
+ * @dev: network device to attach
+ * @phydev: Pointer to phy_device to attach
+ * @flags: PHY device's dev_flags
+ * @interface: PHY device's interface
+ *
+ * Description: Called by drivers to attach to a particular PHY
+ *     device. The phy_device is found, and properly hooked up
+ *     to the phy_driver.  If no driver is attached, then a
+ *     generic driver is used.  The phy_device is given a ptr to
+ *     the attaching device, and given a callback for link status
+ *     change.  The phy_device is returned to the attaching driver.
+ *     This function takes a reference on the phy device.
+ */
+int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
+		      u32 flags, phy_interface_t interface)
+{
+	struct mii_bus *bus = phydev->mdio.bus;
+	struct device *d = &phydev->mdio.dev;
+	struct module *ndev_owner = NULL;
+	bool using_genphy = false;
+	int err;
+
+	/* For Ethernet device drivers that register their own MDIO bus, we
+	 * will have bus->owner match ndev_mod, so we do not want to increment
+	 * our own module->refcnt here, otherwise we would not be able to
+	 * unload later on.
+	 */
+	if (dev)
+		ndev_owner = dev->dev.parent->driver->owner;
+	if (ndev_owner != bus->owner && !try_module_get(bus->owner)) {
+		phydev_err(phydev, "failed to get the bus module\n");
+		return -EIO;
+	}
+
+	get_device(d);
+
+	/* Assume that if there is no driver, that it doesn't
+	 * exist, and we should use the genphy driver.
+	 */
+	if (!d->driver) {
+		if (phydev->is_c45)
+			d->driver = &genphy_c45_driver.mdiodrv.driver;
+		else
+			d->driver = &genphy_driver.mdiodrv.driver;
+
+		using_genphy = true;
+	}
+
+	if (!try_module_get(d->driver->owner)) {
+		phydev_err(phydev, "failed to get the device driver module\n");
+		err = -EIO;
+		goto error_put_device;
+	}
+
+	if (using_genphy) {
+		err = d->driver->probe(d);
+		if (err >= 0)
+			err = device_bind_driver(d);
+
+		if (err)
+			goto error_module_put;
+	}
+
+	if (phydev->attached_dev) {
+		dev_err(&dev->dev, "PHY already attached\n");
+		err = -EBUSY;
+		goto error;
+	}
+
+	phydev->phy_link_change = phy_link_change;
+	if (dev) {
+		phydev->attached_dev = dev;
+		dev->phydev = phydev;
+
+		if (phydev->sfp_bus_attached)
+			dev->sfp_bus = phydev->sfp_bus;
+	}
+
+	/* Some Ethernet drivers try to connect to a PHY device before
+	 * calling register_netdevice() -> netdev_register_kobject() and
+	 * does the dev->dev.kobj initialization. Here we only check for
+	 * success which indicates that the network device kobject is
+	 * ready. Once we do that we still need to keep track of whether
+	 * links were successfully set up or not for phy_detach() to
+	 * remove them accordingly.
+	 */
+	phydev->sysfs_links = false;
+
+	phy_sysfs_create_links(phydev);
+
+	if (!phydev->attached_dev) {
+		err = sysfs_create_file(&phydev->mdio.dev.kobj,
+					&dev_attr_phy_standalone.attr);
+		if (err)
+			phydev_err(phydev, "error creating 'phy_standalone' sysfs entry\n");
+	}
+
+	phydev->dev_flags |= flags;
+
+	phydev->interface = interface;
+
+	phydev->state = PHY_READY;
+
+	/* Port is set to PORT_TP by default and the actual PHY driver will set
+	 * it to different value depending on the PHY configuration. If we have
+	 * the generic PHY driver we can't figure it out, thus set the old
+	 * legacy PORT_MII value.
+	 */
+	if (using_genphy)
+		phydev->port = PORT_MII;
+
+	/* Initial carrier state is off as the phy is about to be
+	 * (re)initialized.
+	 */
+	if (dev)
+		netif_carrier_off(phydev->attached_dev);
+
+	/* Do initial configuration here, now that
+	 * we have certain key parameters
+	 * (dev_flags and interface)
+	 */
+	err = phy_init_hw(phydev);
+	if (err)
+		goto error;
+
+	err = phy_disable_interrupts(phydev);
+	if (err)
+		return err;
+
+	phy_resume(phydev);
+	phy_led_triggers_register(phydev);
+
+	return err;
+
+error:
+	/* phy_detach() does all of the cleanup below */
+	phy_detach(phydev);
+	return err;
+
+error_module_put:
+	module_put(d->driver->owner);
+error_put_device:
+	put_device(d);
+	if (ndev_owner != bus->owner)
+		module_put(bus->owner);
+	return err;
+}
+EXPORT_SYMBOL(phy_attach_direct);
+
+/**
+ * phy_attach - attach a network device to a particular PHY device
+ * @dev: network device to attach
+ * @bus_id: Bus ID of PHY device to attach
+ * @interface: PHY device's interface
+ *
+ * Description: Same as phy_attach_direct() except that a PHY bus_id
+ *     string is passed instead of a pointer to a struct phy_device.
+ */
+struct phy_device *phy_attach(struct net_device *dev, const char *bus_id,
+			      phy_interface_t interface)
+{
+	struct bus_type *bus = &mdio_bus_type;
+	struct phy_device *phydev;
+	struct device *d;
+	int rc;
+
+	if (!dev)
+		return ERR_PTR(-EINVAL);
+
+	/* Search the list of PHY devices on the mdio bus for the
+	 * PHY with the requested name
+	 */
+	d = bus_find_device_by_name(bus, NULL, bus_id);
+	if (!d) {
+		pr_err("PHY %s not found\n", bus_id);
+		return ERR_PTR(-ENODEV);
+	}
+	phydev = to_phy_device(d);
+
+	rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);
+	put_device(d);
+	if (rc)
+		return ERR_PTR(rc);
+
+	return phydev;
+}
+EXPORT_SYMBOL(phy_attach);
+
+static bool phy_driver_is_genphy_kind(struct phy_device *phydev,
+				      struct device_driver *driver)
+{
+	struct device *d = &phydev->mdio.dev;
+	bool ret = false;
+
+	if (!phydev->drv)
+		return ret;
+
+	get_device(d);
+	ret = d->driver == driver;
+	put_device(d);
+
+	return ret;
+}
+
+bool phy_driver_is_genphy(struct phy_device *phydev)
+{
+	return phy_driver_is_genphy_kind(phydev,
+					 &genphy_driver.mdiodrv.driver);
+}
+EXPORT_SYMBOL_GPL(phy_driver_is_genphy);
+
+bool phy_driver_is_genphy_10g(struct phy_device *phydev)
+{
+	return phy_driver_is_genphy_kind(phydev,
+					 &genphy_c45_driver.mdiodrv.driver);
+}
+EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g);
+
+/**
+ * phy_package_join - join a common PHY group
+ * @phydev: target phy_device struct
+ * @addr: cookie and PHY address for global register access
+ * @priv_size: if non-zero allocate this amount of bytes for private data
+ *
+ * This joins a PHY group and provides a shared storage for all phydevs in
+ * this group. This is intended to be used for packages which contain
+ * more than one PHY, for example a quad PHY transceiver.
+ *
+ * The addr parameter serves as a cookie which has to have the same value
+ * for all members of one group and as a PHY address to access generic
+ * registers of a PHY package. Usually, one of the PHY addresses of the
+ * different PHYs in the package provides access to these global registers.
+ * The address which is given here, will be used in the phy_package_read()
+ * and phy_package_write() convenience functions. If your PHY doesn't have
+ * global registers you can just pick any of the PHY addresses.
+ *
+ * This will set the shared pointer of the phydev to the shared storage.
+ * If this is the first call for a this cookie the shared storage will be
+ * allocated. If priv_size is non-zero, the given amount of bytes are
+ * allocated for the priv member.
+ *
+ * Returns < 1 on error, 0 on success. Esp. calling phy_package_join()
+ * with the same cookie but a different priv_size is an error.
+ */
+int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size)
+{
+	struct mii_bus *bus = phydev->mdio.bus;
+	struct phy_package_shared *shared;
+	int ret;
+
+	if (addr < 0 || addr >= PHY_MAX_ADDR)
+		return -EINVAL;
+
+	mutex_lock(&bus->shared_lock);
+	shared = bus->shared[addr];
+	if (!shared) {
+		ret = -ENOMEM;
+		shared = kzalloc(sizeof(*shared), GFP_KERNEL);
+		if (!shared)
+			goto err_unlock;
+		if (priv_size) {
+			shared->priv = kzalloc(priv_size, GFP_KERNEL);
+			if (!shared->priv)
+				goto err_free;
+			shared->priv_size = priv_size;
+		}
+		shared->addr = addr;
+		refcount_set(&shared->refcnt, 1);
+		bus->shared[addr] = shared;
+	} else {
+		ret = -EINVAL;
+		if (priv_size && priv_size != shared->priv_size)
+			goto err_unlock;
+		refcount_inc(&shared->refcnt);
+	}
+	mutex_unlock(&bus->shared_lock);
+
+	phydev->shared = shared;
+
+	return 0;
+
+err_free:
+	kfree(shared);
+err_unlock:
+	mutex_unlock(&bus->shared_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(phy_package_join);
+
+/**
+ * phy_package_leave - leave a common PHY group
+ * @phydev: target phy_device struct
+ *
+ * This leaves a PHY group created by phy_package_join(). If this phydev
+ * was the last user of the shared data between the group, this data is
+ * freed. Resets the phydev->shared pointer to NULL.
+ */
+void phy_package_leave(struct phy_device *phydev)
+{
+	struct phy_package_shared *shared = phydev->shared;
+	struct mii_bus *bus = phydev->mdio.bus;
+
+	if (!shared)
+		return;
+
+	if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
+		bus->shared[shared->addr] = NULL;
+		mutex_unlock(&bus->shared_lock);
+		kfree(shared->priv);
+		kfree(shared);
+	}
+
+	phydev->shared = NULL;
+}
+EXPORT_SYMBOL_GPL(phy_package_leave);
+
+static void devm_phy_package_leave(struct device *dev, void *res)
+{
+	phy_package_leave(*(struct phy_device **)res);
+}
+
+/**
+ * devm_phy_package_join - resource managed phy_package_join()
+ * @dev: device that is registering this PHY package
+ * @phydev: target phy_device struct
+ * @addr: cookie and PHY address for global register access
+ * @priv_size: if non-zero allocate this amount of bytes for private data
+ *
+ * Managed phy_package_join(). Shared storage fetched by this function,
+ * phy_package_leave() is automatically called on driver detach. See
+ * phy_package_join() for more information.
+ */
+int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
+			  int addr, size_t priv_size)
+{
+	struct phy_device **ptr;
+	int ret;
+
+	ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
+			   GFP_KERNEL);
+	if (!ptr)
+		return -ENOMEM;
+
+	ret = phy_package_join(phydev, addr, priv_size);
+
+	if (!ret) {
+		*ptr = phydev;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(devm_phy_package_join);
+
+/**
+ * phy_detach - detach a PHY device from its network device
+ * @phydev: target phy_device struct
+ *
+ * This detaches the phy device from its network device and the phy
+ * driver, and drops the reference count taken in phy_attach_direct().
+ */
+void phy_detach(struct phy_device *phydev)
+{
+	struct net_device *dev = phydev->attached_dev;
+	struct module *ndev_owner = NULL;
+	struct mii_bus *bus;
+
+	if (phydev->sysfs_links) {
+		if (dev)
+			sysfs_remove_link(&dev->dev.kobj, "phydev");
+		sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev");
+	}
+
+	if (!phydev->attached_dev)
+		sysfs_remove_file(&phydev->mdio.dev.kobj,
+				  &dev_attr_phy_standalone.attr);
+
+	phy_suspend(phydev);
+	if (dev) {
+		phydev->attached_dev->phydev = NULL;
+		phydev->attached_dev = NULL;
+	}
+	phydev->phylink = NULL;
+
+	phy_led_triggers_unregister(phydev);
+
+	if (phydev->mdio.dev.driver)
+		module_put(phydev->mdio.dev.driver->owner);
+
+	/* If the device had no specific driver before (i.e. - it
+	 * was using the generic driver), we unbind the device
+	 * from the generic driver so that there's a chance a
+	 * real driver could be loaded
+	 */
+	if (phy_driver_is_genphy(phydev) ||
+	    phy_driver_is_genphy_10g(phydev))
+		device_release_driver(&phydev->mdio.dev);
+
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
+
+	/*
+	 * The phydev might go away on the put_device() below, so avoid
+	 * a use-after-free bug by reading the underlying bus first.
+	 */
+	bus = phydev->mdio.bus;
+
+	put_device(&phydev->mdio.dev);
+	if (dev)
+		ndev_owner = dev->dev.parent->driver->owner;
+	if (ndev_owner != bus->owner)
+		module_put(bus->owner);
+}
+EXPORT_SYMBOL(phy_detach);
+
+int phy_suspend(struct phy_device *phydev)
+{
+	struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+	struct net_device *netdev = phydev->attached_dev;
+	struct phy_driver *phydrv = phydev->drv;
+	int ret;
+
+	if (phydev->suspended)
+		return 0;
+
+	/* If the device has WOL enabled, we cannot suspend the PHY */
+	phy_ethtool_get_wol(phydev, &wol);
+	if (wol.wolopts || (netdev && netdev->wol_enabled))
+		return -EBUSY;
+
+	if (!phydrv || !phydrv->suspend)
+		return 0;
+
+	ret = phydrv->suspend(phydev);
+	if (!ret)
+		phydev->suspended = true;
+
+	return ret;
+}
+EXPORT_SYMBOL(phy_suspend);
+
+int __phy_resume(struct phy_device *phydev)
+{
+	struct phy_driver *phydrv = phydev->drv;
+	int ret;
+
+	WARN_ON(!mutex_is_locked(&phydev->lock));
+
+	if (!phydrv || !phydrv->resume)
+		return 0;
+
+	ret = phydrv->resume(phydev);
+	if (!ret)
+		phydev->suspended = false;
+
+	return ret;
+}
+EXPORT_SYMBOL(__phy_resume);
+
+int phy_resume(struct phy_device *phydev)
+{
+	int ret;
+
+	mutex_lock(&phydev->lock);
+	ret = __phy_resume(phydev);
+	mutex_unlock(&phydev->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(phy_resume);
+
+int phy_loopback(struct phy_device *phydev, bool enable)
+{
+	struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
+	int ret = 0;
+
+	mutex_lock(&phydev->lock);
+
+	if (enable && phydev->loopback_enabled) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	if (!enable && !phydev->loopback_enabled) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (phydev->drv && phydrv->set_loopback)
+		ret = phydrv->set_loopback(phydev, enable);
+	else
+		ret = -EOPNOTSUPP;
+
+	if (ret)
+		goto out;
+
+	phydev->loopback_enabled = enable;
+
+out:
+	mutex_unlock(&phydev->lock);
+	return ret;
+}
+EXPORT_SYMBOL(phy_loopback);
+
+/**
+ * phy_reset_after_clk_enable - perform a PHY reset if needed
+ * @phydev: target phy_device struct
+ *
+ * Description: Some PHYs are known to need a reset after their refclk was
+ *   enabled. This function evaluates the flags and perform the reset if it's
+ *   needed. Returns < 0 on error, 0 if the phy wasn't reset and 1 if the phy
+ *   was reset.
+ */
+int phy_reset_after_clk_enable(struct phy_device *phydev)
+{
+	if (!phydev || !phydev->drv)
+		return -ENODEV;
+
+	if (phydev->drv->flags & PHY_RST_AFTER_CLK_EN) {
+		phy_device_reset(phydev, 1);
+		phy_device_reset(phydev, 0);
+		return 1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_reset_after_clk_enable);
+
+/* Generic PHY support and helper functions */
+
+/**
+ * genphy_config_advert - sanitize and advertise auto-negotiation parameters
+ * @phydev: target phy_device struct
+ *
+ * Description: Writes MII_ADVERTISE with the appropriate values,
+ *   after sanitizing the values to make sure we only advertise
+ *   what is supported.  Returns < 0 on error, 0 if the PHY's advertisement
+ *   hasn't changed, and > 0 if it has changed.
+ */
+static int genphy_config_advert(struct phy_device *phydev)
+{
+	int err, bmsr, changed = 0;
+	u32 adv;
+
+	/* Only allow advertising what this PHY supports */
+	linkmode_and(phydev->advertising, phydev->advertising,
+		     phydev->supported);
+
+	adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
+
+	/* Setup standard advertisement */
+	err = phy_modify_changed(phydev, MII_ADVERTISE,
+				 ADVERTISE_ALL | ADVERTISE_100BASE4 |
+				 ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
+				 adv);
+	if (err < 0)
+		return err;
+	if (err > 0)
+		changed = 1;
+
+	bmsr = phy_read(phydev, MII_BMSR);
+	if (bmsr < 0)
+		return bmsr;
+
+	/* Per 802.3-2008, Section 22.2.4.2.16 Extended status all
+	 * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a
+	 * logical 1.
+	 */
+	if (!(bmsr & BMSR_ESTATEN))
+		return changed;
+
+	adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
+
+	err = phy_modify_changed(phydev, MII_CTRL1000,
+				 ADVERTISE_1000FULL | ADVERTISE_1000HALF,
+				 adv);
+	if (err < 0)
+		return err;
+	if (err > 0)
+		changed = 1;
+
+	return changed;
+}
+
+/**
+ * genphy_c37_config_advert - sanitize and advertise auto-negotiation parameters
+ * @phydev: target phy_device struct
+ *
+ * Description: Writes MII_ADVERTISE with the appropriate values,
+ *   after sanitizing the values to make sure we only advertise
+ *   what is supported.  Returns < 0 on error, 0 if the PHY's advertisement
+ *   hasn't changed, and > 0 if it has changed. This function is intended
+ *   for Clause 37 1000Base-X mode.
+ */
+static int genphy_c37_config_advert(struct phy_device *phydev)
+{
+	u16 adv = 0;
+
+	/* Only allow advertising what this PHY supports */
+	linkmode_and(phydev->advertising, phydev->advertising,
+		     phydev->supported);
+
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+			      phydev->advertising))
+		adv |= ADVERTISE_1000XFULL;
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+			      phydev->advertising))
+		adv |= ADVERTISE_1000XPAUSE;
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+			      phydev->advertising))
+		adv |= ADVERTISE_1000XPSE_ASYM;
+
+	return phy_modify_changed(phydev, MII_ADVERTISE,
+				  ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE |
+				  ADVERTISE_1000XHALF | ADVERTISE_1000XPSE_ASYM,
+				  adv);
+}
+
+/**
+ * genphy_config_eee_advert - disable unwanted eee mode advertisement
+ * @phydev: target phy_device struct
+ *
+ * Description: Writes MDIO_AN_EEE_ADV after disabling unsupported energy
+ *   efficent ethernet modes. Returns 0 if the PHY's advertisement hasn't
+ *   changed, and 1 if it has changed.
+ */
+int genphy_config_eee_advert(struct phy_device *phydev)
+{
+	int err;
+
+	/* Nothing to disable */
+	if (!phydev->eee_broken_modes)
+		return 0;
+
+	err = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV,
+				     phydev->eee_broken_modes, 0);
+	/* If the call failed, we assume that EEE is not supported */
+	return err < 0 ? 0 : err;
+}
+EXPORT_SYMBOL(genphy_config_eee_advert);
+
+/**
+ * genphy_setup_forced - configures/forces speed/duplex from @phydev
+ * @phydev: target phy_device struct
+ *
+ * Description: Configures MII_BMCR to force speed/duplex
+ *   to the values in phydev. Assumes that the values are valid.
+ *   Please see phy_sanitize_settings().
+ */
+int genphy_setup_forced(struct phy_device *phydev)
+{
+	u16 ctl = 0;
+
+	phydev->pause = 0;
+	phydev->asym_pause = 0;
+
+	if (SPEED_1000 == phydev->speed)
+		ctl |= BMCR_SPEED1000;
+	else if (SPEED_100 == phydev->speed)
+		ctl |= BMCR_SPEED100;
+
+	if (DUPLEX_FULL == phydev->duplex)
+		ctl |= BMCR_FULLDPLX;
+
+	return phy_modify(phydev, MII_BMCR,
+			  ~(BMCR_LOOPBACK | BMCR_ISOLATE | BMCR_PDOWN), ctl);
+}
+EXPORT_SYMBOL(genphy_setup_forced);
+
+static int genphy_setup_master_slave(struct phy_device *phydev)
+{
+	u16 ctl = 0;
+
+	if (!phydev->is_gigabit_capable)
+		return 0;
+
+	switch (phydev->master_slave_set) {
+	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+		ctl |= CTL1000_PREFER_MASTER;
+		break;
+	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+		break;
+	case MASTER_SLAVE_CFG_MASTER_FORCE:
+		ctl |= CTL1000_AS_MASTER;
+		fallthrough;
+	case MASTER_SLAVE_CFG_SLAVE_FORCE:
+		ctl |= CTL1000_ENABLE_MASTER;
+		break;
+	case MASTER_SLAVE_CFG_UNKNOWN:
+	case MASTER_SLAVE_CFG_UNSUPPORTED:
+		return 0;
+	default:
+		phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+		return -EOPNOTSUPP;
+	}
+
+	return phy_modify_changed(phydev, MII_CTRL1000,
+				  (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER |
+				   CTL1000_PREFER_MASTER), ctl);
+}
+
+static int genphy_read_master_slave(struct phy_device *phydev)
+{
+	int cfg, state;
+	int val;
+
+	if (!phydev->is_gigabit_capable) {
+		phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED;
+		phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
+		return 0;
+	}
+
+	phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
+	phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+
+	val = phy_read(phydev, MII_CTRL1000);
+	if (val < 0)
+		return val;
+
+	if (val & CTL1000_ENABLE_MASTER) {
+		if (val & CTL1000_AS_MASTER)
+			cfg = MASTER_SLAVE_CFG_MASTER_FORCE;
+		else
+			cfg = MASTER_SLAVE_CFG_SLAVE_FORCE;
+	} else {
+		if (val & CTL1000_PREFER_MASTER)
+			cfg = MASTER_SLAVE_CFG_MASTER_PREFERRED;
+		else
+			cfg = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
+	}
+
+	val = phy_read(phydev, MII_STAT1000);
+	if (val < 0)
+		return val;
+
+	if (val & LPA_1000MSFAIL) {
+		state = MASTER_SLAVE_STATE_ERR;
+	} else if (phydev->link) {
+		/* this bits are valid only for active link */
+		if (val & LPA_1000MSRES)
+			state = MASTER_SLAVE_STATE_MASTER;
+		else
+			state = MASTER_SLAVE_STATE_SLAVE;
+	} else {
+		state = MASTER_SLAVE_STATE_UNKNOWN;
+	}
+
+	phydev->master_slave_get = cfg;
+	phydev->master_slave_state = state;
+
+	return 0;
+}
+
+/**
+ * genphy_restart_aneg - Enable and Restart Autonegotiation
+ * @phydev: target phy_device struct
+ */
+int genphy_restart_aneg(struct phy_device *phydev)
+{
+	/* Don't isolate the PHY if we're negotiating */
+	return phy_modify(phydev, MII_BMCR, BMCR_ISOLATE,
+			  BMCR_ANENABLE | BMCR_ANRESTART);
+}
+EXPORT_SYMBOL(genphy_restart_aneg);
+
+/**
+ * genphy_check_and_restart_aneg - Enable and restart auto-negotiation
+ * @phydev: target phy_device struct
+ * @restart: whether aneg restart is requested
+ *
+ * Check, and restart auto-negotiation if needed.
+ */
+int genphy_check_and_restart_aneg(struct phy_device *phydev, bool restart)
+{
+	int ret;
+
+	if (!restart) {
+		/* Advertisement hasn't changed, but maybe aneg was never on to
+		 * begin with?  Or maybe phy was isolated?
+		 */
+		ret = phy_read(phydev, MII_BMCR);
+		if (ret < 0)
+			return ret;
+
+		if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE))
+			restart = true;
+	}
+
+	if (restart)
+		return genphy_restart_aneg(phydev);
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_check_and_restart_aneg);
+
+/**
+ * __genphy_config_aneg - restart auto-negotiation or write BMCR
+ * @phydev: target phy_device struct
+ * @changed: whether autoneg is requested
+ *
+ * Description: If auto-negotiation is enabled, we configure the
+ *   advertising, and then restart auto-negotiation.  If it is not
+ *   enabled, then we write the BMCR.
+ */
+int __genphy_config_aneg(struct phy_device *phydev, bool changed)
+{
+	int err;
+
+	if (genphy_config_eee_advert(phydev))
+		changed = true;
+
+	err = genphy_setup_master_slave(phydev);
+	if (err < 0)
+		return err;
+	else if (err)
+		changed = true;
+
+	if (AUTONEG_ENABLE != phydev->autoneg)
+		return genphy_setup_forced(phydev);
+
+	err = genphy_config_advert(phydev);
+	if (err < 0) /* error */
+		return err;
+	else if (err)
+		changed = true;
+
+	return genphy_check_and_restart_aneg(phydev, changed);
+}
+EXPORT_SYMBOL(__genphy_config_aneg);
+
+/**
+ * genphy_c37_config_aneg - restart auto-negotiation or write BMCR
+ * @phydev: target phy_device struct
+ *
+ * Description: If auto-negotiation is enabled, we configure the
+ *   advertising, and then restart auto-negotiation.  If it is not
+ *   enabled, then we write the BMCR. This function is intended
+ *   for use with Clause 37 1000Base-X mode.
+ */
+int genphy_c37_config_aneg(struct phy_device *phydev)
+{
+	int err, changed;
+
+	if (phydev->autoneg != AUTONEG_ENABLE)
+		return genphy_setup_forced(phydev);
+
+	err = phy_modify(phydev, MII_BMCR, BMCR_SPEED1000 | BMCR_SPEED100,
+			 BMCR_SPEED1000);
+	if (err)
+		return err;
+
+	changed = genphy_c37_config_advert(phydev);
+	if (changed < 0) /* error */
+		return changed;
+
+	if (!changed) {
+		/* Advertisement hasn't changed, but maybe aneg was never on to
+		 * begin with?  Or maybe phy was isolated?
+		 */
+		int ctl = phy_read(phydev, MII_BMCR);
+
+		if (ctl < 0)
+			return ctl;
+
+		if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
+			changed = 1; /* do restart aneg */
+	}
+
+	/* Only restart aneg if we are advertising something different
+	 * than we were before.
+	 */
+	if (changed > 0)
+		return genphy_restart_aneg(phydev);
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_c37_config_aneg);
+
+/**
+ * genphy_aneg_done - return auto-negotiation status
+ * @phydev: target phy_device struct
+ *
+ * Description: Reads the status register and returns 0 either if
+ *   auto-negotiation is incomplete, or if there was an error.
+ *   Returns BMSR_ANEGCOMPLETE if auto-negotiation is done.
+ */
+int genphy_aneg_done(struct phy_device *phydev)
+{
+	int retval = phy_read(phydev, MII_BMSR);
+
+	return (retval < 0) ? retval : (retval & BMSR_ANEGCOMPLETE);
+}
+EXPORT_SYMBOL(genphy_aneg_done);
+
+/**
+ * genphy_update_link - update link status in @phydev
+ * @phydev: target phy_device struct
+ *
+ * Description: Update the value in phydev->link to reflect the
+ *   current link value.  In order to do this, we need to read
+ *   the status register twice, keeping the second value.
+ */
+int genphy_update_link(struct phy_device *phydev)
+{
+	int status = 0, bmcr;
+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 
+	bmcr = phy_read_cl(phydev, MII_BMCR);
+#else
+	bmcr = phy_read(phydev, MII_BMCR);
+#endif
+	if (bmcr < 0)
+		return bmcr;
+
+	/* Autoneg is being started, therefore disregard BMSR value and
+	 * report link as down.
+	 */
+	if (bmcr & BMCR_ANRESTART)
+		goto done;
+
+	/* The link state is latched low so that momentary link
+	 * drops can be detected. Do not double-read the status
+	 * in polling mode to detect such short link drops except
+	 * the link was already down.
+	 */
+	if (!phy_polling_mode(phydev) || !phydev->link) {
+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 
+		status = phy_read_cl(phydev, MII_BMSR);
+#else
+		status = phy_read(phydev, MII_BMSR);
+#endif
+		if (status < 0)
+			return status;
+		else if (status & BMSR_LSTATUS)
+			goto done;
+	}
+
+	/* Read link and autonegotiation status */
+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 
+	status = phy_read_cl(phydev, MII_BMSR);
+#else
+	status = phy_read(phydev, MII_BMSR);
+#endif
+	if (status < 0)
+		return status;
+done:
+	phydev->link = status & BMSR_LSTATUS ? 1 : 0;
+	phydev->autoneg_complete = status & BMSR_ANEGCOMPLETE ? 1 : 0;
+
+	/* Consider the case that autoneg was started and "aneg complete"
+	 * bit has been reset, but "link up" bit not yet.
+	 */
+	if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete)
+		phydev->link = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_update_link);
+
+int genphy_read_lpa(struct phy_device *phydev)
+{
+	int lpa, lpagb;
+
+	if (phydev->autoneg == AUTONEG_ENABLE) {
+		if (!phydev->autoneg_complete) {
+			mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
+							0);
+			mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
+			return 0;
+		}
+
+		if (phydev->is_gigabit_capable) {
+			lpagb = phy_read(phydev, MII_STAT1000);
+			if (lpagb < 0)
+				return lpagb;
+
+			if (lpagb & LPA_1000MSFAIL) {
+				int adv = phy_read(phydev, MII_CTRL1000);
+
+				if (adv < 0)
+					return adv;
+
+				if (adv & CTL1000_ENABLE_MASTER)
+					phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n");
+				else
+					phydev_err(phydev, "Master/Slave resolution failed\n");
+				return -ENOLINK;
+			}
+
+			mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
+							lpagb);
+		}
+
+		lpa = phy_read(phydev, MII_LPA);
+		if (lpa < 0)
+			return lpa;
+
+		mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
+	} else {
+		linkmode_zero(phydev->lp_advertising);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_read_lpa);
+
+/**
+ * genphy_read_status_fixed - read the link parameters for !aneg mode
+ * @phydev: target phy_device struct
+ *
+ * Read the current duplex and speed state for a PHY operating with
+ * autonegotiation disabled.
+ */
+int genphy_read_status_fixed(struct phy_device *phydev)
+{
+#ifdef CONFIG_MARVELL_88Q1110 //zw.wang phy driver support for Marvell_88q1110 on 20240417 
+	int bmcr = phy_read_cl(phydev, MII_BMCR);
+#else
+	int bmcr = phy_read(phydev, MII_BMCR);
+#endif
+
+	if (bmcr < 0)
+		return bmcr;
+
+	if (bmcr & BMCR_FULLDPLX)
+		phydev->duplex = DUPLEX_FULL;
+	else
+		phydev->duplex = DUPLEX_HALF;
+
+	if (bmcr & BMCR_SPEED1000)
+		phydev->speed = SPEED_1000;
+	else if (bmcr & BMCR_SPEED100)
+		phydev->speed = SPEED_100;
+	else
+		phydev->speed = SPEED_10;
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_read_status_fixed);
+
+/**
+ * genphy_read_status - check the link status and update current link state
+ * @phydev: target phy_device struct
+ *
+ * Description: Check the link, then figure out the current state
+ *   by comparing what we advertise with what the link partner
+ *   advertises.  Start by checking the gigabit possibilities,
+ *   then move on to 10/100.
+ */
+int genphy_read_status(struct phy_device *phydev)
+{
+	int err, old_link = phydev->link;
+
+	/* Update the link, but return if there was an error */
+	err = genphy_update_link(phydev);
+	if (err)
+		return err;
+
+	/* why bother the PHY if nothing can have changed */
+	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
+		return 0;
+
+	phydev->speed = SPEED_UNKNOWN;
+	phydev->duplex = DUPLEX_UNKNOWN;
+	phydev->pause = 0;
+	phydev->asym_pause = 0;
+
+	err = genphy_read_master_slave(phydev);
+	if (err < 0)
+		return err;
+
+	err = genphy_read_lpa(phydev);
+	if (err < 0)
+		return err;
+
+	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
+		phy_resolve_aneg_linkmode(phydev);
+	} else if (phydev->autoneg == AUTONEG_DISABLE) {
+		err = genphy_read_status_fixed(phydev);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_read_status);
+
+/**
+ * genphy_c37_read_status - check the link status and update current link state
+ * @phydev: target phy_device struct
+ *
+ * Description: Check the link, then figure out the current state
+ *   by comparing what we advertise with what the link partner
+ *   advertises. This function is for Clause 37 1000Base-X mode.
+ */
+int genphy_c37_read_status(struct phy_device *phydev)
+{
+	int lpa, err, old_link = phydev->link;
+
+	/* Update the link, but return if there was an error */
+	err = genphy_update_link(phydev);
+	if (err)
+		return err;
+
+	/* why bother the PHY if nothing can have changed */
+	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
+		return 0;
+
+	phydev->duplex = DUPLEX_UNKNOWN;
+	phydev->pause = 0;
+	phydev->asym_pause = 0;
+
+	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
+		lpa = phy_read(phydev, MII_LPA);
+		if (lpa < 0)
+			return lpa;
+
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+				 phydev->lp_advertising, lpa & LPA_LPACK);
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+				 phydev->lp_advertising, lpa & LPA_1000XFULL);
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+				 phydev->lp_advertising, lpa & LPA_1000XPAUSE);
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+				 phydev->lp_advertising,
+				 lpa & LPA_1000XPAUSE_ASYM);
+
+		phy_resolve_aneg_linkmode(phydev);
+	} else if (phydev->autoneg == AUTONEG_DISABLE) {
+		int bmcr = phy_read(phydev, MII_BMCR);
+
+		if (bmcr < 0)
+			return bmcr;
+
+		if (bmcr & BMCR_FULLDPLX)
+			phydev->duplex = DUPLEX_FULL;
+		else
+			phydev->duplex = DUPLEX_HALF;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_c37_read_status);
+
+/**
+ * genphy_soft_reset - software reset the PHY via BMCR_RESET bit
+ * @phydev: target phy_device struct
+ *
+ * Description: Perform a software PHY reset using the standard
+ * BMCR_RESET bit and poll for the reset bit to be cleared.
+ *
+ * Returns: 0 on success, < 0 on failure
+ */
+int genphy_soft_reset(struct phy_device *phydev)
+{
+	u16 res = BMCR_RESET;
+	int ret;
+
+	if (phydev->autoneg == AUTONEG_ENABLE)
+		res |= BMCR_ANRESTART;
+
+	ret = phy_modify(phydev, MII_BMCR, BMCR_ISOLATE, res);
+	if (ret < 0)
+		return ret;
+
+	/* Clause 22 states that setting bit BMCR_RESET sets control registers
+	 * to their default value. Therefore the POWER DOWN bit is supposed to
+	 * be cleared after soft reset.
+	 */
+	phydev->suspended = 0;
+
+	ret = phy_poll_reset(phydev);
+	if (ret)
+		return ret;
+
+	/* BMCR may be reset to defaults */
+	if (phydev->autoneg == AUTONEG_DISABLE)
+		ret = genphy_setup_forced(phydev);
+
+	return ret;
+}
+EXPORT_SYMBOL(genphy_soft_reset);
+
+/**
+ * genphy_read_abilities - read PHY abilities from Clause 22 registers
+ * @phydev: target phy_device struct
+ *
+ * Description: Reads the PHY's abilities and populates
+ * phydev->supported accordingly.
+ *
+ * Returns: 0 on success, < 0 on failure
+ */
+int genphy_read_abilities(struct phy_device *phydev)
+{
+	int val;
+
+	linkmode_set_bit_array(phy_basic_ports_array,
+			       ARRAY_SIZE(phy_basic_ports_array),
+			       phydev->supported);
+
+	val = phy_read(phydev, MII_BMSR);
+	if (val < 0)
+		return val;
+
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported,
+			 val & BMSR_ANEGCAPABLE);
+
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported,
+			 val & BMSR_100FULL);
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported,
+			 val & BMSR_100HALF);
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported,
+			 val & BMSR_10FULL);
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported,
+			 val & BMSR_10HALF);
+
+	if (val & BMSR_ESTATEN) {
+		val = phy_read(phydev, MII_ESTATUS);
+		if (val < 0)
+			return val;
+
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+				 phydev->supported, val & ESTATUS_1000_TFULL);
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+				 phydev->supported, val & ESTATUS_1000_THALF);
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+				 phydev->supported, val & ESTATUS_1000_XFULL);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(genphy_read_abilities);
+
+/* This is used for the phy device which doesn't support the MMD extended
+ * register access, but it does have side effect when we are trying to access
+ * the MMD register via indirect method.
+ */
+int genphy_read_mmd_unsupported(struct phy_device *phdev, int devad, u16 regnum)
+{
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(genphy_read_mmd_unsupported);
+
+int genphy_write_mmd_unsupported(struct phy_device *phdev, int devnum,
+				 u16 regnum, u16 val)
+{
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(genphy_write_mmd_unsupported);
+
+int genphy_suspend(struct phy_device *phydev)
+{
+	return phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN);
+}
+EXPORT_SYMBOL(genphy_suspend);
+
+int genphy_resume(struct phy_device *phydev)
+{
+	return phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);
+}
+EXPORT_SYMBOL(genphy_resume);
+
+int genphy_loopback(struct phy_device *phydev, bool enable)
+{
+	return phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
+			  enable ? BMCR_LOOPBACK : 0);
+}
+EXPORT_SYMBOL(genphy_loopback);
+
+/**
+ * phy_remove_link_mode - Remove a supported link mode
+ * @phydev: phy_device structure to remove link mode from
+ * @link_mode: Link mode to be removed
+ *
+ * Description: Some MACs don't support all link modes which the PHY
+ * does.  e.g. a 1G MAC often does not support 1000Half. Add a helper
+ * to remove a link mode.
+ */
+void phy_remove_link_mode(struct phy_device *phydev, u32 link_mode)
+{
+	linkmode_clear_bit(link_mode, phydev->supported);
+	phy_advertise_supported(phydev);
+}
+EXPORT_SYMBOL(phy_remove_link_mode);
+
+static void phy_copy_pause_bits(unsigned long *dst, unsigned long *src)
+{
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, dst,
+		linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, src));
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, dst,
+		linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, src));
+}
+
+/**
+ * phy_advertise_supported - Advertise all supported modes
+ * @phydev: target phy_device struct
+ *
+ * Description: Called to advertise all supported modes, doesn't touch
+ * pause mode advertising.
+ */
+void phy_advertise_supported(struct phy_device *phydev)
+{
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(new);
+
+	linkmode_copy(new, phydev->supported);
+	phy_copy_pause_bits(new, phydev->advertising);
+	linkmode_copy(phydev->advertising, new);
+}
+EXPORT_SYMBOL(phy_advertise_supported);
+
+/**
+ * phy_support_sym_pause - Enable support of symmetrical pause
+ * @phydev: target phy_device struct
+ *
+ * Description: Called by the MAC to indicate is supports symmetrical
+ * Pause, but not asym pause.
+ */
+void phy_support_sym_pause(struct phy_device *phydev)
+{
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);
+	phy_copy_pause_bits(phydev->advertising, phydev->supported);
+}
+EXPORT_SYMBOL(phy_support_sym_pause);
+
+/**
+ * phy_support_asym_pause - Enable support of asym pause
+ * @phydev: target phy_device struct
+ *
+ * Description: Called by the MAC to indicate is supports Asym Pause.
+ */
+void phy_support_asym_pause(struct phy_device *phydev)
+{
+	phy_copy_pause_bits(phydev->advertising, phydev->supported);
+}
+EXPORT_SYMBOL(phy_support_asym_pause);
+
+/**
+ * phy_set_sym_pause - Configure symmetric Pause
+ * @phydev: target phy_device struct
+ * @rx: Receiver Pause is supported
+ * @tx: Transmit Pause is supported
+ * @autoneg: Auto neg should be used
+ *
+ * Description: Configure advertised Pause support depending on if
+ * receiver pause and pause auto neg is supported. Generally called
+ * from the set_pauseparam .ndo.
+ */
+void phy_set_sym_pause(struct phy_device *phydev, bool rx, bool tx,
+		       bool autoneg)
+{
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
+
+	if (rx && tx && autoneg)
+		linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+				 phydev->supported);
+
+	linkmode_copy(phydev->advertising, phydev->supported);
+}
+EXPORT_SYMBOL(phy_set_sym_pause);
+
+/**
+ * phy_set_asym_pause - Configure Pause and Asym Pause
+ * @phydev: target phy_device struct
+ * @rx: Receiver Pause is supported
+ * @tx: Transmit Pause is supported
+ *
+ * Description: Configure advertised Pause support depending on if
+ * transmit and receiver pause is supported. If there has been a
+ * change in adverting, trigger a new autoneg. Generally called from
+ * the set_pauseparam .ndo.
+ */
+void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx)
+{
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(oldadv);
+
+	linkmode_copy(oldadv, phydev->advertising);
+	linkmode_set_pause(phydev->advertising, tx, rx);
+
+	if (!linkmode_equal(oldadv, phydev->advertising) &&
+	    phydev->autoneg)
+		phy_start_aneg(phydev);
+}
+EXPORT_SYMBOL(phy_set_asym_pause);
+
+/**
+ * phy_validate_pause - Test if the PHY/MAC support the pause configuration
+ * @phydev: phy_device struct
+ * @pp: requested pause configuration
+ *
+ * Description: Test if the PHY/MAC combination supports the Pause
+ * configuration the user is requesting. Returns True if it is
+ * supported, false otherwise.
+ */
+bool phy_validate_pause(struct phy_device *phydev,
+			struct ethtool_pauseparam *pp)
+{
+	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+			       phydev->supported) && pp->rx_pause)
+		return false;
+
+	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+			       phydev->supported) &&
+	    pp->rx_pause != pp->tx_pause)
+		return false;
+
+	return true;
+}
+EXPORT_SYMBOL(phy_validate_pause);
+
+/**
+ * phy_get_pause - resolve negotiated pause modes
+ * @phydev: phy_device struct
+ * @tx_pause: pointer to bool to indicate whether transmit pause should be
+ * enabled.
+ * @rx_pause: pointer to bool to indicate whether receive pause should be
+ * enabled.
+ *
+ * Resolve and return the flow control modes according to the negotiation
+ * result. This includes checking that we are operating in full duplex mode.
+ * See linkmode_resolve_pause() for further details.
+ */
+void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause)
+{
+	if (phydev->duplex != DUPLEX_FULL) {
+		*tx_pause = false;
+		*rx_pause = false;
+		return;
+	}
+
+	return linkmode_resolve_pause(phydev->advertising,
+				      phydev->lp_advertising,
+				      tx_pause, rx_pause);
+}
+EXPORT_SYMBOL(phy_get_pause);
+
+#if IS_ENABLED(CONFIG_OF_MDIO)
+static int phy_get_int_delay_property(struct device *dev, const char *name)
+{
+	s32 int_delay;
+	int ret;
+
+	ret = device_property_read_u32(dev, name, &int_delay);
+	if (ret)
+		return ret;
+
+	return int_delay;
+}
+#else
+static int phy_get_int_delay_property(struct device *dev, const char *name)
+{
+	return -EINVAL;
+}
+#endif
+
+/**
+ * phy_get_delay_index - returns the index of the internal delay
+ * @phydev: phy_device struct
+ * @dev: pointer to the devices device struct
+ * @delay_values: array of delays the PHY supports
+ * @size: the size of the delay array
+ * @is_rx: boolean to indicate to get the rx internal delay
+ *
+ * Returns the index within the array of internal delay passed in.
+ * If the device property is not present then the interface type is checked
+ * if the interface defines use of internal delay then a 1 is returned otherwise
+ * a 0 is returned.
+ * The array must be in ascending order. If PHY does not have an ascending order
+ * array then size = 0 and the value of the delay property is returned.
+ * Return -EINVAL if the delay is invalid or cannot be found.
+ */
+s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev,
+			   const int *delay_values, int size, bool is_rx)
+{
+	s32 delay;
+	int i;
+
+	if (is_rx) {
+		delay = phy_get_int_delay_property(dev, "rx-internal-delay-ps");
+		if (delay < 0 && size == 0) {
+			if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+			    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+				return 1;
+			else
+				return 0;
+		}
+
+	} else {
+		delay = phy_get_int_delay_property(dev, "tx-internal-delay-ps");
+		if (delay < 0 && size == 0) {
+			if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+			    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+				return 1;
+			else
+				return 0;
+		}
+	}
+
+	if (delay < 0)
+		return delay;
+
+	if (delay && size == 0)
+		return delay;
+
+	if (delay < delay_values[0] || delay > delay_values[size - 1]) {
+		phydev_err(phydev, "Delay %d is out of range\n", delay);
+		return -EINVAL;
+	}
+
+	if (delay == delay_values[0])
+		return 0;
+
+	for (i = 1; i < size; i++) {
+		if (delay == delay_values[i])
+			return i;
+
+		/* Find an approximate index by looking up the table */
+		if (delay > delay_values[i - 1] &&
+		    delay < delay_values[i]) {
+			if (delay - delay_values[i - 1] <
+			    delay_values[i] - delay)
+				return i - 1;
+			else
+				return i;
+		}
+	}
+
+	phydev_err(phydev, "error finding internal delay index for %d\n",
+		   delay);
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(phy_get_internal_delay);
+
+static bool phy_drv_supports_irq(struct phy_driver *phydrv)
+{
+	return phydrv->config_intr && phydrv->ack_interrupt;
+}
+
+/**
+ * phy_probe - probe and init a PHY device
+ * @dev: device to probe and init
+ *
+ * Description: Take care of setting up the phy_device structure,
+ *   set the state to READY (the driver's init function should
+ *   set it to STARTING if needed).
+ */
+static int phy_probe(struct device *dev)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+	struct device_driver *drv = phydev->mdio.dev.driver;
+	struct phy_driver *phydrv = to_phy_driver(drv);
+	int err = 0;
+
+	phydev->drv = phydrv;
+
+	/* Disable the interrupt if the PHY doesn't support it
+	 * but the interrupt is still a valid one
+	 */
+	 if (!phy_drv_supports_irq(phydrv) && phy_interrupt_is_valid(phydev))
+		phydev->irq = PHY_POLL;
+
+	if (phydrv->flags & PHY_IS_INTERNAL)
+		phydev->is_internal = true;
+
+	mutex_lock(&phydev->lock);
+
+	/* Deassert the reset signal */
+	phy_device_reset(phydev, 0);
+
+	if (phydev->drv->probe) {
+		err = phydev->drv->probe(phydev);
+		if (err)
+			goto out;
+	}
+
+	/* Start out supporting everything. Eventually,
+	 * a controller will attach, and may modify one
+	 * or both of these values
+	 */
+	if (phydrv->features) {
+		linkmode_copy(phydev->supported, phydrv->features);
+	} else if (phydrv->get_features) {
+		err = phydrv->get_features(phydev);
+	} else if (phydev->is_c45) {
+		err = genphy_c45_pma_read_abilities(phydev);
+	} else {
+		err = genphy_read_abilities(phydev);
+	}
+
+	if (err)
+		goto out;
+
+	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+			       phydev->supported))
+		phydev->autoneg = 0;
+
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+			      phydev->supported))
+		phydev->is_gigabit_capable = 1;
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+			      phydev->supported))
+		phydev->is_gigabit_capable = 1;
+
+	of_set_phy_supported(phydev);
+	phy_advertise_supported(phydev);
+
+	/* Get the EEE modes we want to prohibit. We will ask
+	 * the PHY stop advertising these mode later on
+	 */
+	of_set_phy_eee_broken(phydev);
+
+	/* The Pause Frame bits indicate that the PHY can support passing
+	 * pause frames. During autonegotiation, the PHYs will determine if
+	 * they should allow pause frames to pass.  The MAC driver should then
+	 * use that result to determine whether to enable flow control via
+	 * pause frames.
+	 *
+	 * Normally, PHY drivers should not set the Pause bits, and instead
+	 * allow phylib to do that.  However, there may be some situations
+	 * (e.g. hardware erratum) where the driver wants to set only one
+	 * of these bits.
+	 */
+	if (!test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported) &&
+	    !test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported)) {
+		linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+				 phydev->supported);
+		linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+				 phydev->supported);
+	}
+
+	/* Set the state to READY by default */
+	phydev->state = PHY_READY;
+
+out:
+	/* Assert the reset signal */
+	if (err)
+		phy_device_reset(phydev, 1);
+
+	mutex_unlock(&phydev->lock);
+
+	return err;
+}
+
+static int phy_remove(struct device *dev)
+{
+	struct phy_device *phydev = to_phy_device(dev);
+
+	cancel_delayed_work_sync(&phydev->state_queue);
+
+	mutex_lock(&phydev->lock);
+	phydev->state = PHY_DOWN;
+	mutex_unlock(&phydev->lock);
+
+	sfp_bus_del_upstream(phydev->sfp_bus);
+	phydev->sfp_bus = NULL;
+
+	if (phydev->drv && phydev->drv->remove)
+		phydev->drv->remove(phydev);
+
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
+
+	phydev->drv = NULL;
+
+	return 0;
+}
+
+/**
+ * phy_driver_register - register a phy_driver with the PHY layer
+ * @new_driver: new phy_driver to register
+ * @owner: module owning this PHY
+ */
+int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
+{
+	int retval;
+
+	/* Either the features are hard coded, or dynamically
+	 * determined. It cannot be both.
+	 */
+	if (WARN_ON(new_driver->features && new_driver->get_features)) {
+		pr_err("%s: features and get_features must not both be set\n",
+		       new_driver->name);
+		return -EINVAL;
+	}
+
+	new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY;
+	new_driver->mdiodrv.driver.name = new_driver->name;
+	new_driver->mdiodrv.driver.bus = &mdio_bus_type;
+	new_driver->mdiodrv.driver.probe = phy_probe;
+	new_driver->mdiodrv.driver.remove = phy_remove;
+	new_driver->mdiodrv.driver.owner = owner;
+	new_driver->mdiodrv.driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
+
+	retval = driver_register(&new_driver->mdiodrv.driver);
+	if (retval) {
+		pr_err("%s: Error %d in registering driver\n",
+		       new_driver->name, retval);
+
+		return retval;
+	}
+
+	pr_debug("%s: Registered new driver\n", new_driver->name);
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_driver_register);
+
+int phy_drivers_register(struct phy_driver *new_driver, int n,
+			 struct module *owner)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < n; i++) {
+		ret = phy_driver_register(new_driver + i, owner);
+		if (ret) {
+			while (i-- > 0)
+				phy_driver_unregister(new_driver + i);
+			break;
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL(phy_drivers_register);
+
+void phy_driver_unregister(struct phy_driver *drv)
+{
+	driver_unregister(&drv->mdiodrv.driver);
+}
+EXPORT_SYMBOL(phy_driver_unregister);
+
+void phy_drivers_unregister(struct phy_driver *drv, int n)
+{
+	int i;
+
+	for (i = 0; i < n; i++)
+		phy_driver_unregister(drv + i);
+}
+EXPORT_SYMBOL(phy_drivers_unregister);
+
+static struct phy_driver genphy_driver = {
+	.phy_id		= 0xffffffff,
+	.phy_id_mask	= 0xffffffff,
+	.name		= "Generic PHY",
+	.get_features	= genphy_read_abilities,
+	.suspend	= genphy_suspend,
+	.resume		= genphy_resume,
+	.set_loopback   = genphy_loopback,
+};
+
+static const struct ethtool_phy_ops phy_ethtool_phy_ops = {
+	.get_sset_count		= phy_ethtool_get_sset_count,
+	.get_strings		= phy_ethtool_get_strings,
+	.get_stats		= phy_ethtool_get_stats,
+	.start_cable_test	= phy_start_cable_test,
+	.start_cable_test_tdr	= phy_start_cable_test_tdr,
+};
+
+static int __init phy_init(void)
+{
+	int rc;
+
+	rc = mdio_bus_init();
+	if (rc)
+		return rc;
+
+	ethtool_set_ethtool_phy_ops(&phy_ethtool_phy_ops);
+	features_init();
+
+	rc = phy_driver_register(&genphy_c45_driver, THIS_MODULE);
+	if (rc)
+		goto err_c45;
+
+	rc = phy_driver_register(&genphy_driver, THIS_MODULE);
+	if (rc) {
+		phy_driver_unregister(&genphy_c45_driver);
+err_c45:
+		mdio_bus_exit();
+	}
+
+	return rc;
+}
+
+static void __exit phy_exit(void)
+{
+	phy_driver_unregister(&genphy_c45_driver);
+	phy_driver_unregister(&genphy_driver);
+	mdio_bus_exit();
+	ethtool_set_ethtool_phy_ops(NULL);
+}
+
+subsys_initcall(phy_init);
+module_exit(phy_exit);