diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index 045d6e978598..fb76a19ba613 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -41,7 +41,7 @@ static int wx_fmgr_cmd_op(struct wx_hw *wxhw, u32 cmd, u32 cmd_addr)
 				 false, wxhw, WX_SPI_STATUS);
 }
 
-static int wx_flash_read_dword(struct wx_hw *wxhw, u32 addr, u32 *data)
+int wx_flash_read_dword(struct wx_hw *wxhw, u32 addr, u32 *data)
 {
 	int ret = 0;
 
@@ -53,6 +53,7 @@ static int wx_flash_read_dword(struct wx_hw *wxhw, u32 addr, u32 *data)
 
 	return ret;
 }
+EXPORT_SYMBOL(wx_flash_read_dword);
 
 int wx_check_flash_load(struct wx_hw *hw, u32 check_bit)
 {
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h
index 5058774381c1..f562dcfa2d0f 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h
@@ -4,6 +4,7 @@
 #ifndef _WX_HW_H_
 #define _WX_HW_H_
 
+int wx_flash_read_dword(struct wx_hw *wxhw, u32 addr, u32 *data);
 int wx_check_flash_load(struct wx_hw *hw, u32 check_bit);
 void wx_control_hw(struct wx_hw *wxhw, bool drv);
 int wx_mng_present(struct wx_hw *wxhw);
diff --git a/drivers/net/ethernet/wangxun/ngbe/Makefile b/drivers/net/ethernet/wangxun/ngbe/Makefile
index 391c2cbc1bb4..56bd8cf800c8 100644
--- a/drivers/net/ethernet/wangxun/ngbe/Makefile
+++ b/drivers/net/ethernet/wangxun/ngbe/Makefile
@@ -6,4 +6,4 @@
 
 obj-$(CONFIG_NGBE) += ngbe.o
 
-ngbe-objs := ngbe_main.o ngbe_hw.o
+ngbe-objs := ngbe_main.o ngbe_hw.o ngbe_phy.o
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe.h b/drivers/net/ethernet/wangxun/ngbe/ngbe.h
index af147ca8605c..ac67c0403592 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe.h
@@ -48,6 +48,8 @@ struct ngbe_adapter {
 	struct ngbe_mac_addr *mac_table;
 	u16 msg_enable;
 
+	DECLARE_BITMAP(flags, NGBE_FLAGS_NBITS);
+
 	/* Tx fast path data */
 	int num_tx_queues;
 	u16 tx_itr_setting;
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c
index 0e3923b3737e..274d54832579 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c
@@ -38,6 +38,37 @@ int ngbe_eeprom_chksum_hostif(struct ngbe_hw *hw)
 	return -EIO;
 }
 
+int ngbe_phy_led_oem_hostif(struct ngbe_hw *hw, u32 *data)
+{
+	struct wx_hic_read_shadow_ram buffer;
+	struct wx_hw *wxhw = &hw->wxhw;
+	int status;
+
+	buffer.hdr.req.cmd = NGBE_FW_PHY_LED_CONF;
+	buffer.hdr.req.buf_lenh = 0;
+	buffer.hdr.req.buf_lenl = 0;
+	buffer.hdr.req.checksum = NGBE_FW_CMD_DEFAULT_CHECKSUM;
+
+	/* convert offset from words to bytes */
+	buffer.address = 0;
+	/* one word */
+	buffer.length = 0;
+
+	status = wx_host_interface_command(wxhw, (u32 *)&buffer, sizeof(buffer),
+					   WX_HI_COMMAND_TIMEOUT, false);
+
+	if (status)
+		return status;
+
+	*data = rd32a(wxhw, WX_MNG_MBOX, 1);
+	if (*data == NGBE_FW_CMD_ST_PASS)
+		*data = rd32a(wxhw, WX_MNG_MBOX, 2);
+	else
+		*data = 0xffffffff;
+
+	return 0;
+}
+
 static int ngbe_reset_misc(struct ngbe_hw *hw)
 {
 	struct wx_hw *wxhw = &hw->wxhw;
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h
index 42476a3fe57c..cf4008a2b5ec 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h
@@ -8,5 +8,6 @@
 #define _NGBE_HW_H_
 
 int ngbe_eeprom_chksum_hostif(struct ngbe_hw *hw);
+int ngbe_phy_led_oem_hostif(struct ngbe_hw *hw, u32 *data);
 int ngbe_reset_hw(struct ngbe_hw *hw);
 #endif /* _NGBE_HW_H_ */
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index f0b24366da18..ebf3fcdc4719 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -76,16 +76,16 @@ static void ngbe_init_type_code(struct ngbe_hw *hw)
 		hw->phy.type = ngbe_phy_m88e1512;
 		break;
 	case NGBE_SUBID_M88E1512_MIX:
-		hw->phy.type = ngbe_phy_m88e1512_unknown;
+		hw->phy.type = ngbe_phy_m88e1512_mix;
 		break;
 	case NGBE_SUBID_YT8521S_SFP:
 	case NGBE_SUBID_YT8521S_SFP_GPIO:
 	case NGBE_SUBID_LY_YT8521S_SFP:
-		hw->phy.type = ngbe_phy_yt8521s_sfi;
+		hw->phy.type = ngbe_phy_yt_mix;
 		break;
 	case NGBE_SUBID_INTERNAL_YT8521S_SFP:
 	case NGBE_SUBID_INTERNAL_YT8521S_SFP_GPIO:
-		hw->phy.type = ngbe_phy_internal_yt8521s_sfi;
+		hw->phy.type = ngbe_phy_internal_yt_sfi;
 		break;
 	case NGBE_SUBID_RGMII_FPGA:
 	case NGBE_SUBID_OCP_CARD:
@@ -96,7 +96,7 @@ static void ngbe_init_type_code(struct ngbe_hw *hw)
 	}
 
 	if (hw->phy.type == ngbe_phy_internal ||
-	    hw->phy.type == ngbe_phy_internal_yt8521s_sfi)
+	    hw->phy.type == ngbe_phy_internal_yt_sfi)
 		hw->mac_type = ngbe_mac_type_mdi;
 	else
 		hw->mac_type = ngbe_mac_type_rgmii;
@@ -116,6 +116,8 @@ static void ngbe_init_type_code(struct ngbe_hw *hw)
 		hw->gpio_ctrl = 0;
 		break;
 	}
+
+	hw->phy.id = 0;
 }
 
 /**
@@ -203,6 +205,22 @@ static int ngbe_sw_init(struct ngbe_adapter *adapter)
 	return 0;
 }
 
+static void ngbe_oem_conf_in_rom(struct ngbe_hw *hw)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+
+	/* phy led oem conf*/
+	if (ngbe_phy_led_oem_hostif(hw, &hw->led_conf))
+		dev_err(&wxhw->pdev->dev, "The led_oem is not supported\n");
+
+	wx_flash_read_dword(wxhw,
+			    0xfe010 + wxhw->bus.func * 8,
+			    &hw->phy.gphy_efuse[0]);
+	wx_flash_read_dword(wxhw,
+			    0xfe010 + wxhw->bus.func + 4,
+			    &hw->phy.gphy_efuse[1]);
+}
+
 static void ngbe_down(struct ngbe_adapter *adapter)
 {
 	netif_carrier_off(adapter->netdev);
@@ -442,6 +460,8 @@ static int ngbe_probe(struct pci_dev *pdev,
 		}
 	}
 
+	ngbe_oem_conf_in_rom(hw);
+
 	adapter->wol = 0;
 	if (hw->wol_enabled)
 		adapter->wol = NGBE_PSR_WKUP_CTL_MAG;
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_phy.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_phy.c
new file mode 100644
index 000000000000..874d929285b0
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_phy.c
@@ -0,0 +1,1113 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */
+
+#include <linux/ethtool.h>
+#include <linux/iopoll.h>
+#include <linux/pci.h>
+
+#include "../libwx/wx_type.h"
+#include "../libwx/wx_hw.h"
+#include "ngbe_type.h"
+#include "ngbe_phy.h"
+#include "ngbe.h"
+
+static u16 ngbe_phy_read_reg_internal(struct ngbe_hw *hw, u32 reg_addr)
+{
+	u16 phy_data = 0;
+
+	phy_data = (u16)rd32(&hw->wxhw, NGBE_PHY_CONFIG(reg_addr));
+	return phy_data;
+}
+
+static void ngbe_phy_write_reg_internal(struct ngbe_hw *hw, u32 reg_addr, u16 phy_data)
+{
+	wr32(&hw->wxhw, NGBE_PHY_CONFIG(reg_addr), phy_data);
+}
+
+/**
+ *  ngbe_phy_read_reg_mdi - Reads a val from an external PHY register
+ *  @hw: pointer to hardware structure
+ *  @reg_addr: 32 bit address of PHY register to read
+ **/
+static u16 ngbe_phy_read_reg_mdi(struct ngbe_hw *hw, u32 reg_addr)
+{
+	u32 command = 0, device_type = 0;
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 phy_addr = 0;
+	u16 phy_data = 0;
+	u32 val = 0;
+	int ret = 0;
+
+	/* setup and write the address cycle command */
+	command = NGBE_MSCA_RA(reg_addr) |
+		  NGBE_MSCA_PA(phy_addr) |
+		  NGBE_MSCA_DA(device_type);
+	wr32(wxhw, NGBE_MSCA, command);
+
+	command = NGBE_MSCC_CMD(NGBE_MSCA_CMD_READ) |
+		  NGBE_MSCC_BUSY |
+		  NGBE_MDIO_CLK(6);
+	wr32(wxhw, NGBE_MSCC, command);
+
+	/* wait to complete */
+	ret = read_poll_timeout(rd32, val, val & NGBE_MSCC_BUSY, 1000,
+				20000, false, wxhw, NGBE_MSCC);
+	if (ret)
+		wx_dbg(wxhw, "PHY address command did not complete.\n");
+
+	/* read data from MSCC */
+	phy_data = (u16)rd32(wxhw, NGBE_MSCC);
+
+	return phy_data;
+}
+
+/**
+ *  ngbe_phy_write_reg_mdi - Writes a val to external PHY register
+ *  @hw: pointer to hardware structure
+ *  @reg_addr: 32 bit PHY register to write
+ *  @phy_data: Data to write to the PHY register
+ **/
+static void ngbe_phy_write_reg_mdi(struct ngbe_hw *hw, u32 reg_addr, u16 phy_data)
+{
+	u32 command = 0, device_type = 0;
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 phy_addr = 0;
+	int ret = 0;
+	u16 val = 0;
+
+	/* setup and write the address cycle command */
+	command = NGBE_MSCA_RA(reg_addr) |
+		  NGBE_MSCA_PA(phy_addr) |
+		  NGBE_MSCA_DA(device_type);
+	wr32(wxhw, NGBE_MSCA, command);
+
+	command = phy_data |
+		  NGBE_MSCC_CMD(NGBE_MSCA_CMD_WRITE) |
+		  NGBE_MSCC_BUSY |
+		  NGBE_MDIO_CLK(6);
+	wr32(wxhw, NGBE_MSCC, command);
+
+	/* wait to complete */
+	ret = read_poll_timeout(rd32, val, val & NGBE_MSCC_BUSY, 1000,
+				20000, false, wxhw, NGBE_MSCC);
+	if (ret)
+		wx_dbg(wxhw, "PHY address command did not complete.\n");
+}
+
+static u16 ngbe_phy_read_reg_ext_yt(struct ngbe_hw *hw,
+				    u32 reg_addr)
+{
+	u16 val = 0;
+
+	ngbe_phy_write_reg_mdi(hw, 0x1e, reg_addr);
+	val = ngbe_phy_read_reg_mdi(hw, 0x1f);
+
+	return val;
+}
+
+static void ngbe_phy_write_reg_ext_yt(struct ngbe_hw *hw,
+				      u32 reg_addr,
+				      u16 phy_data)
+{
+	ngbe_phy_write_reg_mdi(hw, 0x1e, reg_addr);
+	ngbe_phy_write_reg_mdi(hw, 0x1f, phy_data);
+}
+
+static u16 ngbe_phy_read_reg_sds_ext_yt(struct ngbe_hw *hw,
+					u32 reg_addr)
+{
+	u16 val = 0;
+
+	ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x02);
+	val = ngbe_phy_read_reg_ext_yt(hw, reg_addr);
+	ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x00);
+
+	return val;
+}
+
+static void ngbe_phy_write_reg_sds_ext_yt(struct ngbe_hw *hw,
+					  u32 reg_addr,
+					  u16 phy_data)
+{
+	ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x02);
+	ngbe_phy_write_reg_ext_yt(hw, reg_addr, phy_data);
+	ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x00);
+}
+
+static u16 ngbe_phy_read_reg_sds_mii_yt(struct ngbe_hw *hw,
+					u32 reg_addr)
+{
+	u16 val = 0;
+
+	ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x02);
+	val = ngbe_phy_read_reg_mdi(hw, reg_addr);
+	ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x00);
+
+	return val;
+}
+
+static void ngbe_phy_write_reg_sds_mii_yt(struct ngbe_hw *hw,
+					  u32 reg_addr,
+					  u16 phy_data)
+{
+	ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x02);
+	ngbe_phy_write_reg_mdi(hw, reg_addr, phy_data);
+	ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x00);
+}
+
+static void ngbe_phy_led_ctrl_mv(struct ngbe_hw *hw)
+{
+	u16 val = 0;
+
+	if (hw->led_conf == 0xffffffff) {
+		/* LED control */
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 3);
+		val = ngbe_phy_read_reg(hw, NGBE_PHY_LED_FUNC_CTRL_REG_MV);
+		val &= ~0x00FF;
+		val |= (NGBE_LED1_CONF_MV << 4) | NGBE_LED0_CONF_MV;
+		ngbe_phy_write_reg(hw, NGBE_PHY_LED_FUNC_CTRL_REG_MV, val);
+		val = ngbe_phy_read_reg(hw, NGBE_PHY_LED_POL_CTRL_REG_MV);
+		val &= ~0x000F;
+		val |= (NGBE_LED1_POL_MV << 2) | NGBE_LED0_POL_MV;
+		ngbe_phy_write_reg(hw, NGBE_PHY_LED_POL_CTRL_REG_MV, val);
+	}
+}
+
+static void ngbe_phy_led_ctrl_internal(struct ngbe_hw *hw)
+{
+	u16 val = 0;
+
+	if (hw->led_conf != 0xffffffff)
+		val = hw->led_conf & 0xffff;
+	else
+		val = 0x205B;
+
+	/* select page to 0xd04 */
+	ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xd04);
+	ngbe_phy_write_reg(hw, 0x10, val);
+	ngbe_phy_write_reg(hw, 0x11, 0x0);
+
+	val = ngbe_phy_read_reg(hw, 0x12);
+	if (hw->led_conf != 0xffffffff) {
+		val &= ~0x73;
+		val |= hw->led_conf >> 16;
+	} else {
+		val = val & 0xFFFC;
+		/*act led blinking mode set to 60ms*/
+		val |= 0x2;
+	}
+	ngbe_phy_write_reg(hw, 0x12, val);
+}
+
+static int ngbe_gphy_wait_mdio_access_on(struct ngbe_hw *hw)
+{
+	u16 val = 0;
+	int ret = 0;
+
+	/* select page to 0xa43*/
+	ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa43);
+	/* wait to phy can access */
+	ret = read_poll_timeout(ngbe_phy_read_reg, val, val & 0x20, 1000,
+				100000, false, hw, 0x1d);
+
+	if (ret)
+		wx_dbg(&hw->wxhw, "Access to phy timeout\n");
+
+	return ret;
+}
+
+static int ngbe_phy_init_m88e1512(struct ngbe_hw *hw)
+{
+	u16 val = 0;
+	int ret = 0;
+
+	/* select page to 0x2 */
+	ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x2);
+	val = ngbe_phy_read_reg(hw, 0x15);
+	val &= ~NGBE_RGM_TTC_MV;
+	val |= NGBE_RGM_RTC_MV;
+	ngbe_phy_write_reg(hw, 0x15, val);
+
+	/* phy reset */
+	ret = ngbe_phy_reset(hw);
+	if (!ret)
+		return ret;
+
+	/* set LED2 to interrupt output and INTn active low */
+	/* select page to 0x3 */
+	ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x3);
+	val = ngbe_phy_read_reg(hw, NGBE_PHY_INTM_REG);
+	val |= NGBE_INT_EN_MV;
+	val &= ~(NGBE_INT_POL_MV);
+	ngbe_phy_write_reg(hw, NGBE_PHY_INTM_REG, val);
+
+	return 0;
+}
+
+static int ngbe_get_phy_media_type(struct ngbe_hw *hw)
+{
+	u8 phy_mode = 0;
+	u32 val = 0;
+
+	if (hw->phy.media_type != ngbe_media_type_unknown)
+		return 0;
+
+	switch (hw->phy.type) {
+	case ngbe_phy_internal_yt_sfi:
+	case ngbe_phy_internal:
+	case ngbe_phy_m88e1512:
+		hw->phy.media_type = ngbe_media_type_copper;
+		break;
+	case ngbe_phy_m88e1512_sfi:
+		hw->phy.media_type = ngbe_media_type_fiber;
+		break;
+	case ngbe_phy_m88e1512_mix:
+	case ngbe_phy_yt_mix:
+		hw->phy.media_type = ngbe_media_type_unknown;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (hw->phy.media_type)
+		return 0;
+
+	if (hw->phy.type == ngbe_phy_yt_mix) {
+		ngbe_phy_write_reg(hw, 0x1e, 0xa001);
+		val = ngbe_phy_read_reg(hw, 0x1f);
+		phy_mode = val & 0x7;
+		switch (phy_mode) {
+		case 0:
+			hw->phy.media_type = ngbe_media_type_copper;
+			break;
+		case 1:
+		case 2:
+			hw->phy.media_type = ngbe_media_type_fiber;
+			break;
+		case 4:
+		case 5:
+			hw->phy.media_type = ngbe_media_type_backplane;
+			break;
+		default:
+			hw->phy.media_type = ngbe_media_type_unknown;
+			return -EINVAL;
+		}
+	} else if (hw->phy.type == ngbe_phy_m88e1512_mix) {
+		wx_flash_read_dword(&hw->wxhw, 0xff010, &val);
+
+		phy_mode = (u8)(val >> (8 * hw->wxhw.bus.func));
+		phy_mode = phy_mode & 0x7;
+		switch (phy_mode) {
+		case 0:
+			hw->phy.media_type = ngbe_media_type_copper;
+			break;
+		case 2:
+			hw->phy.media_type = ngbe_media_type_fiber;
+			break;
+		default:
+			hw->phy.media_type = ngbe_media_type_unknown;
+			return -EINVAL;
+		}
+	}
+
+	hw->phy.phy_mode = phy_mode;
+	return 0;
+}
+
+static void ngbe_check_phy_id(struct ngbe_hw *hw)
+{
+	u16 phy_id_high = 0, phy_id_low = 0;
+	u32 phy_id = 0xffffffff;
+
+	phy_id_high = ngbe_phy_read_reg(hw, NGBE_PHY_ID1_REG);
+	phy_id_low = ngbe_phy_read_reg(hw, NGBE_PHY_ID2_REG);
+
+	phy_id = phy_id_high << 6;
+	phy_id |= (phy_id_low & NGBE_PHY_ID_MASK) >> 10;
+
+	/* for yt 8521s phy id is 0 */
+	if (!phy_id) {
+		if (phy_id_low)
+			hw->phy.id = phy_id_low;
+		else
+			wx_dbg(&hw->wxhw, "Can not get phy id.\n");
+	}
+	hw->phy.id = phy_id;
+}
+
+/**
+ *  ngbe_phy_identify - Identifies phy
+ *  @hw: pointer to hardware structure
+ *
+ *  Check whether the phy is accessible
+ **/
+static int ngbe_phy_identify(struct ngbe_hw *hw)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 phy_id = 0;
+	int ret = 0;
+
+	if (hw->phy.id)
+		return ret;
+	switch (hw->phy.type) {
+	case ngbe_phy_internal:
+	case ngbe_phy_internal_yt_sfi:
+		ngbe_gphy_wait_mdio_access_on(hw);
+		phy_id = NGBE_PHY_ID_INTERNAL;
+		break;
+	case ngbe_phy_m88e1512:
+	case ngbe_phy_m88e1512_sfi:
+	case ngbe_phy_m88e1512_mix:
+		phy_id = NGBE_PHY_ID_MV;
+		break;
+	case ngbe_phy_yt_mix:
+		phy_id = NGBE_PHY_ID_YT8521S | NGBE_PHY_ID_YT8531S;
+		break;
+	default:
+		ret =  -EINVAL;
+	}
+
+	ngbe_check_phy_id(hw);
+	if ((hw->phy.id & phy_id) != hw->phy.id) {
+		wx_err(wxhw, "Phy id 0x%x not supported.\n", phy_id);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ *  ngbe_phy_init - PHY specific init
+ *  @hw: pointer to hardware structure
+ *
+ *  Check phy id, Initialize phy mode and media type, Enable the required interrupt.
+ **/
+int ngbe_phy_init(struct ngbe_hw *hw)
+{
+	int ret = 0;
+	u16 val = 0;
+
+	/* Identify the PHY*/
+	ret = ngbe_phy_identify(hw);
+	if (ret)
+		return ret;
+
+	ret = ngbe_get_phy_media_type(hw);
+	if (ret) {
+		wx_err(&hw->wxhw, "The phy mode is not supported.\n");
+		return ret;
+	}
+
+	switch (hw->phy.type) {
+	case ngbe_phy_internal:
+	case ngbe_phy_internal_yt_sfi:
+		val = NGBE_PHY_INT_STATUS_LSC_INTERNAL |
+		      NGBE_PHY_INT_STATUS_ANC_INTERNAL;
+		/* select page to 0xa42 */
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa42);
+		break;
+	case ngbe_phy_m88e1512:
+		ngbe_phy_init_m88e1512(hw);
+		/* select page to 0x0 */
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x0);
+		/* enable link status change and AN complete interrupts */
+		val = NGBE_PHY_INT_STATUS_ANC_MV | NGBE_PHY_INT_STATUS_LSC_MV;
+		break;
+	case ngbe_phy_m88e1512_sfi:
+		ngbe_phy_init_m88e1512(hw);
+		/* select page to 0x1 */
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x1);
+		val = ngbe_phy_read_reg(hw, 0x10);
+		val &= ~0x4;
+		ngbe_phy_write_reg(hw, 0x10, val);
+
+		/* enable link status change and AN complete interrupts */
+		val = NGBE_PHY_INT_STATUS_ANC_MV | NGBE_PHY_INT_STATUS_LSC_MV;
+		break;
+	case ngbe_phy_yt_mix:
+		/* select sds area register */
+		ngbe_phy_write_reg(hw, 0x1e, 0xa000);
+		ngbe_phy_write_reg(hw, 0x1f, 0x0);
+
+		/* enable interrupt */
+		val = NGBE_PHY_INT_STATUS_SDSLNKUP_YT |
+		      NGBE_PHY_INT_STATUS_SDSLNKDN_YT |
+		      NGBE_PHY_INT_STATUS_UTPLNKUP_YT |
+		      NGBE_PHY_INT_STATUS_UTPLNKDN_YT;
+		break;
+	default:
+		ret =  -EINVAL;
+	}
+	/* write interrupts bits to register */
+	ngbe_phy_write_reg(hw, NGBE_PHY_INTM_REG, val);
+
+	return ret;
+}
+
+static void ngbe_phy_setup_link_copper(struct ngbe_hw *hw,
+				       u32 speed)
+{
+	u16 value_r4 = 0, value_r9 = 0;
+	u16 val = 0;
+
+	if (!hw->phy.autoneg) {
+		switch (speed) {
+		case SPEED_1000:
+			val = NGBE_PHY_SPEED_SELECT1;
+			break;
+		case SPEED_100:
+			val = NGBE_PHY_SPEED_SELECT0;
+			break;
+		case SPEED_10:
+			val = 0;
+			break;
+		default:
+			val = NGBE_PHY_SPEED_SELECT1 | NGBE_PHY_SPEED_SELECT0;
+			wx_dbg(&hw->wxhw, "unknown speed = 0x%x.\n", speed);
+		}
+		/* duplex full */
+		val |= NGBE_PHY_DUPLEX;
+		ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val);
+	}
+	if (speed & SPEED_1000)
+		value_r9 |= NGBE_PHY_1000BASET_FULL;
+	if (speed & SPEED_100)
+		value_r4 |= NGBE_PHY_100BASETX_FULL;
+	if (speed & SPEED_10)
+		value_r4 |= NGBE_PHY_10BASET_FULL;
+
+	val = ngbe_phy_read_reg(hw, NGBE_PHY_ANAR_REG);
+	/* clear all speed 100/10 full/half bit mask  */
+	val &= ~(NGBE_PHY_100BASETX_HALF |
+		 NGBE_PHY_100BASETX_FULL |
+		 NGBE_PHY_10BASET_HALF |
+		 NGBE_PHY_10BASET_FULL);
+	/* set current speed 100/10 bit mask */
+	value_r4 |= val;
+	ngbe_phy_write_reg(hw, NGBE_PHY_ANAR_REG, value_r4);
+
+	val = ngbe_phy_read_reg(hw, NGBE_PHY_GBCR_REG);
+	/* clear all speed 1000 full/half bit mask  */
+	val &= ~(NGBE_PHY_1000BASET_HALF |
+		 NGBE_PHY_1000BASET_FULL);
+	/* set current speed 1000 bit mask */
+	value_r9 |= val;
+	ngbe_phy_write_reg(hw, NGBE_PHY_GBCR_REG, value_r9);
+}
+
+static void ngbe_phy_dis_eee_internal(struct ngbe_hw *hw)
+{
+	ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa4b);
+	ngbe_phy_write_reg(hw, 0x11, 0x1110);
+	ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0x0);
+	ngbe_phy_write_reg(hw, 0xd, 0x0007);
+	ngbe_phy_write_reg(hw, 0xe, 0x003c);
+	ngbe_phy_write_reg(hw, 0xd, 0x4007);
+	ngbe_phy_write_reg(hw, 0xe, 0x0000);
+}
+
+static int ngbe_gphy_efuse_calibration(struct ngbe_hw *hw)
+{
+	u32 efuse[2] = {0, 0};
+
+	ngbe_gphy_wait_mdio_access_on(hw);
+
+	efuse[0] = hw->phy.gphy_efuse[0];
+	efuse[1] = hw->phy.gphy_efuse[1];
+
+	if (!efuse[0] && !efuse[1]) {
+		efuse[0] = 0xFFFFFFFF;
+		efuse[1] = 0xFFFFFFFF;
+	}
+
+	/* calibration */
+	efuse[0] |= 0xF0000100;
+	efuse[1] |= 0xFF807FFF;
+
+	/* EODR, Efuse Output Data Register */
+	ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa46);
+	ngbe_phy_write_reg(hw, 16, (efuse[0] >>  0) & 0xFFFF);
+	ngbe_phy_write_reg(hw, 17, (efuse[0] >> 16) & 0xFFFF);
+	ngbe_phy_write_reg(hw, 18, (efuse[1] >>  0) & 0xFFFF);
+	ngbe_phy_write_reg(hw, 19, (efuse[1] >> 16) & 0xFFFF);
+
+	/* set efuse ready */
+	ngbe_phy_write_reg(hw, 20, 0x01);
+	ngbe_gphy_wait_mdio_access_on(hw);
+	ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa43);
+	ngbe_phy_write_reg(hw, 27, 0x8011);
+	ngbe_phy_write_reg(hw, 28, 0x5737);
+	ngbe_phy_dis_eee_internal(hw);
+
+	return 0;
+}
+
+static void ngbe_phy_setup_powerup(struct ngbe_hw *hw)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+	u16 val = 0;
+	int ret = 0;
+
+	ret = read_poll_timeout(rd32, val,
+				!(val & (BIT(9) << wxhw->bus.func)), 1000,
+				100000, false, wxhw, 0x10028);
+
+	if (ret)
+		wx_dbg(wxhw, "Lan reset exceeds maximum times.\n");
+
+	ngbe_gphy_efuse_calibration(hw);
+	ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa46);
+	/* set efuse ready */
+	ngbe_phy_write_reg(hw, 20, 0x02);
+	ngbe_gphy_wait_mdio_access_on(hw);
+
+	ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa42);
+	ret = read_poll_timeout(ngbe_phy_read_reg, val, ((val & 0x7) == 3),
+				1000, 20000, false, hw, 0x10);
+
+	if (ret)
+		wx_dbg(wxhw, "PHY reset exceeds maximum times.\n");
+}
+
+static int ngbe_phy_setup_link_internal(struct ngbe_hw *hw,
+					u32 speed,
+					bool need_restart_AN)
+{
+	u16 val = 0;
+
+	ngbe_phy_setup_powerup(hw);
+	/* select page to 0x0 */
+	ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0x0);
+	ngbe_phy_setup_link_copper(hw, speed);
+
+	val = ngbe_phy_read_reg(hw, NGBE_PHY_BASE_CTRL_REG);
+	if (hw->phy.autoneg) {
+		/* restart AN and wait AN done interrupt */
+		if (hw->ncsi_enabled) {
+			val |= NGBE_PHY_ANE;
+			if (need_restart_AN)
+				val |= NGBE_PHY_ANE;
+		} else {
+			val |= NGBE_PHY_RESTART_AN | NGBE_PHY_ANE;
+		}
+	}
+	ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val);
+	ngbe_phy_led_ctrl_internal(hw);
+	ngbe_check_phy_event(hw);
+
+	return 0;
+}
+
+static int ngbe_phy_setup_link_mv(struct ngbe_hw *hw,
+				  u32 speed,
+				  bool autoneg_wait_to_complete)
+{
+	u16 val;
+
+	if (hw->phy.media_type == ngbe_media_type_copper) {
+		ngbe_phy_setup_link_copper(hw, speed);
+		val = ngbe_phy_read_reg(hw, NGBE_PHY_BASE_CTRL_REG);
+		if (hw->phy.autoneg)
+			val = NGBE_PHY_RESTART_AN | NGBE_PHY_ANE;
+		val |= NGBE_PHY_RESET;
+		ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val);
+	} else {
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 1);
+		val = ngbe_phy_read_reg(hw, NGBE_PHY_ANAR_REG);
+		val |= NGBE_PHY_1000BASEX_FULL_MV;
+		ngbe_phy_write_reg(hw, NGBE_PHY_ANAR_REG, val);
+
+		val = NGBE_PHY_RESET |
+		      NGBE_PHY_DUPLEX |
+		      NGBE_PHY_SPEED_SELECT1;
+		if (hw->phy.autoneg)
+			val |= NGBE_PHY_RESTART_AN |
+			      NGBE_PHY_ANE;
+		ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val);
+	}
+	/* sw reset power down bit is retain */
+	val = ngbe_phy_read_reg(hw, NGBE_PHY_BASE_CTRL_REG);
+	val &= ~NGBE_PHY_POWER_DOWN;
+	ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val);
+	ngbe_phy_led_ctrl_mv(hw);
+	ngbe_check_phy_event(hw);
+
+	return 0;
+}
+
+static int ngbe_phy_setup_link_yt(struct ngbe_hw *hw,
+				  u32 speed,
+				  bool autoneg_wait_to_complete)
+{
+	u16 value_r4 = 0;
+	u16 value_r9 = 0;
+	int ret_val = 0;
+	u16 val;
+
+	hw->phy.autoneg_advertised = 0;
+	if (hw->phy.phy_mode == 0) {/* utp_to_rgmii */
+		ngbe_phy_setup_link_copper(hw, speed);
+		/* software reset to make the above configuration take effect*/
+		val = ngbe_phy_read_reg(hw, NGBE_PHY_BASE_CTRL_REG);
+		if (hw->phy.autoneg)
+			val = NGBE_PHY_RESTART_AN | NGBE_PHY_ANE;
+		val |= NGBE_PHY_RESET;
+		ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val);
+	} else if (hw->phy.phy_mode == 1) {/* fiber_to_rgmii */
+		if (!hw->phy.autoneg) {
+			switch (speed) {
+			case SPEED_1000:
+				val = SPEED_1000;
+				break;
+			case SPEED_100:
+				val = SPEED_100;
+				break;
+			default:
+				val = SPEED_1000;
+				break;
+			}
+
+			val = ngbe_phy_read_reg_ext_yt(hw, 0xA006);
+			if (hw->phy.autoneg_advertised & SPEED_1000)
+				val |= 0x1;
+			else if (hw->phy.autoneg_advertised & SPEED_100)
+				val &= ~0x1;
+			ngbe_phy_write_reg_ext_yt(hw, 0xA006, val);
+
+			/* close auto sensing */
+			val = ngbe_phy_read_reg_sds_ext_yt(hw, 0xA5);
+			val &= ~0x8000;
+			ngbe_phy_write_reg_sds_ext_yt(hw, 0xA5, val);
+
+			val = ngbe_phy_read_reg_ext_yt(hw, 0xA001);
+			val &= ~0x8000;
+			ngbe_phy_write_reg_ext_yt(hw, 0xA001, val);
+
+			goto skip_an_fiber;
+		}
+
+		/* open auto sensing */
+		val = ngbe_phy_read_reg_sds_ext_yt(hw, 0xA5);
+		val |= 0x8000;
+		ngbe_phy_write_reg_sds_ext_yt(hw, 0xA5, val);
+
+		val = ngbe_phy_read_reg_ext_yt(hw, 0xA006);
+		val |= 0x1;
+		ngbe_phy_write_reg_ext_yt(hw, 0xA006, val);
+skip_an_fiber:
+		/* RGMII_Config1 : Config rx and tx training delay */
+		ngbe_phy_write_reg_ext_yt(hw, 0xA003, 0x3cf1);
+		ngbe_phy_write_reg_ext_yt(hw, 0xA001, 0x8041);
+
+		/* software reset */
+		if (hw->phy.autoneg)
+			ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, 0x9340);
+		else
+			ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, 0x8140);
+	} else if (hw->phy.phy_mode == 2) {
+		/* power on in UTP mode */
+		val = ngbe_phy_read_reg(hw, 0x0);
+		val &= ~0x800;
+		ngbe_phy_write_reg(hw, 0x0, val);
+
+		/* power on in Fiber mode */
+		val = ngbe_phy_read_reg_sds_mii_yt(hw, 0x0);
+		val &= ~0x800;
+		ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, val);
+
+		val = ngbe_phy_read_reg_sds_mii_yt(hw, 0x11);
+
+		if (val & 0x400) { /* fiber up */
+			hw->phy.autoneg_advertised |= SPEED_1000;
+		} else { /* utp up */
+			value_r4 = 0x1E0;
+			value_r9 = 0x300;
+			/*disable 100/10base-T Self-negotiation ability*/
+			val = ngbe_phy_read_reg_mdi(hw, 0x4);
+			val &= ~value_r4;
+			ngbe_phy_write_reg_mdi(hw, 0x4, val);
+
+			/*disable 1000base-T Self-negotiation ability*/
+			val = ngbe_phy_read_reg_mdi(hw, 0x9);
+			val &= ~value_r9;
+			ngbe_phy_write_reg_mdi(hw, 0x9, val);
+
+			value_r4 = 0x0;
+			value_r9 = 0x0;
+
+			if (speed & SPEED_1000) {
+				hw->phy.autoneg_advertised |= SPEED_1000;
+				value_r9 |= 0x200;
+			}
+			if (speed & SPEED_100) {
+				hw->phy.autoneg_advertised |= SPEED_1000;
+				value_r4 |= 0x100;
+			}
+			if (speed & SPEED_10) {
+				hw->phy.autoneg_advertised |= SPEED_1000;
+				value_r4 |= 0x40;
+			}
+
+			/* enable 1000base-T Self-negotiation ability */
+			val = ngbe_phy_read_reg_mdi(hw, 0x9);
+			val |= value_r9;
+			ngbe_phy_write_reg_mdi(hw, 0x9, val);
+
+			/* enable 100/10base-T Self-negotiation ability */
+			val = ngbe_phy_read_reg_mdi(hw, 0x4);
+			val |= value_r4;
+			ngbe_phy_write_reg_mdi(hw, 0x4, val);
+
+			/* software reset to make the above configuration take effect*/
+			val = ngbe_phy_read_reg_mdi(hw, 0x0);
+			val |= 0x8000;
+			ngbe_phy_write_reg_mdi(hw, 0x0, val);
+		}
+	} else if (hw->phy.phy_mode == 4) {
+		hw->phy.autoneg_advertised |= SPEED_1000;
+
+		val = ngbe_phy_read_reg_ext_yt(hw, 0xA003);
+		val |= 0x8000;
+		ngbe_phy_write_reg_ext_yt(hw, 0xA003, val);
+
+		val = ngbe_phy_read_reg_ext_yt(hw, 0xA004);
+		val &= ~0xf0;
+		val |= 0xb0;
+		ngbe_phy_write_reg_ext_yt(hw, 0xA004, val);
+
+		val = ngbe_phy_read_reg_ext_yt(hw, 0xA001);
+		val &= ~0x8000;
+		ngbe_phy_write_reg_ext_yt(hw, 0xA001, val);
+
+		/* power on phy */
+		val = ngbe_phy_read_reg_sds_mii_yt(hw, 0x0);
+		val &= ~0x800;
+		ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, val);
+	} else if (hw->phy.phy_mode == 5) {/* sgmii_to_rgmii */
+		if (!hw->phy.autoneg) {
+			switch (speed) {
+			case SPEED_1000:
+				val = NGBE_PHY_SPEED_SELECT1;
+				break;
+			case SPEED_100:
+				val = NGBE_PHY_SPEED_SELECT0;
+				break;
+			case SPEED_10:
+				val = 0;
+				break;
+			default:
+				val = NGBE_PHY_SPEED_SELECT0 | NGBE_PHY_SPEED_SELECT1;
+				wx_dbg(&hw->wxhw, "unknown speed = 0x%x.\n", speed);
+				break;
+			}
+			/* duplex full */
+			val |= NGBE_PHY_DUPLEX | NGBE_PHY_RESET;
+			ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, val);
+
+			goto skip_an_sr;
+		}
+
+		val = 0;
+		if (speed & SPEED_1000)
+			val |= 0x40;
+		if (speed & SPEED_100)
+			val |= 0x2000;
+		if (speed & SPEED_100)
+			val |= 0x0;
+		/* duplex full */
+		val |= NGBE_PHY_DUPLEX | 0x8000;
+		ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, val);
+
+		/* software reset to make the above configuration take effect */
+		val = ngbe_phy_read_reg_sds_mii_yt(hw, 0x0);
+		val |= 0x9200;
+		ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, val);
+skip_an_sr:
+		/* power on in UTP mode */
+		val = ngbe_phy_read_reg_sds_mii_yt(hw, 0x0);
+		val &= ~0x800;
+		ngbe_phy_write_reg_sds_mii_yt(hw, 0x0, val);
+	}
+	ngbe_check_phy_event(hw);
+
+	return ret_val;
+}
+
+/**
+ *  ngbe_check_phy_link_internal - Determine link and speed status
+ *  @hw: pointer to hardware structure
+ *  @speed: pointer to link speed
+ *  @link_up: true when link is up
+ *  @link_up_wait_to_complete: bool used to wait for link up or not
+ *
+ *  Reads the links register to determine if link is up and the current speed
+ **/
+static void ngbe_check_phy_link_internal(struct ngbe_hw *hw,
+					 u32 *speed,
+					 bool *link_up,
+					 bool link_up_wait_to_complete)
+{
+	u16 speed_sta = 0;
+	u16 val = 0;
+
+	/* select page to 0xa43 */
+	ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa43);
+	val = ngbe_phy_read_reg(hw, NGBE_PHY_PHYSR_REG_INTERNAL);
+	if (val & 0x4)
+		*link_up = true;
+	else
+		*link_up = false;
+
+	speed_sta = val & 0x38;
+	if (*link_up) {
+		if (speed_sta == 0x28)
+			*speed = SPEED_1000;
+		else if (speed_sta == 0x18)
+			*speed = SPEED_100;
+		else if (speed_sta == 0x8)
+			*speed = SPEED_10;
+	} else {
+		*speed = 0;
+	}
+	if (*speed == SPEED_1000) {
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa43);
+		val = ngbe_phy_read_reg(hw, 0xa);
+		if (!(val & 0x2000))
+			*link_up = false;
+	}
+}
+
+static void ngbe_check_phy_link_mv(struct ngbe_hw *hw,
+				   u32 *speed,
+				   bool *link_up,
+				   bool link_up_wait_to_complete)
+{
+	u16 speed_sta = 0;
+	u16 val = 0;
+
+	if (hw->phy.media_type == ngbe_media_type_copper)
+		/* select page to 0x0 */
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x0);
+	else
+		/* select page to 0x1 */
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x1);
+	val = ngbe_phy_read_reg(hw, 0x11);
+	if (val & 0x400)
+		*link_up = true;
+	else
+		*link_up = false;
+
+	speed_sta = val & 0xC000;
+	if (*link_up) {
+		if (speed_sta == 0x8000)
+			*speed = SPEED_1000;
+		else if (speed_sta == 0x4000)
+			*speed = SPEED_100;
+		else if (speed_sta == 0x0000)
+			*speed = SPEED_10;
+	} else {
+		*speed = 0;
+	}
+}
+
+static void ngbe_check_phy_link_yt(struct ngbe_hw *hw,
+				   u32 *speed,
+				   bool *link_up,
+				   bool link_up_wait_to_complete)
+{
+	u16 speed_sta = 0;
+	u16 val = 0;
+
+	val = ngbe_phy_read_reg_sds_mii_yt(hw, 0x11);
+	if (val & 0x400) {
+		*link_up = true;
+	} else {
+		*link_up = false;
+
+		val = ngbe_phy_read_reg(hw, 0x11);
+		if (val & 0x400)
+			*link_up = true;
+		else
+			*link_up = false;
+	}
+
+	speed_sta = val & 0xC000;
+	if (*link_up) {
+		if (speed_sta == 0x8000) {
+			*speed = SPEED_1000;
+			wr32m(&hw->wxhw, NGBE_CFG_LED_CTL, 0xE | BIT(17), BIT(1) | BIT(17));
+		} else if (speed_sta == 0x4000) {
+			*speed = SPEED_100;
+			wr32m(&hw->wxhw, NGBE_CFG_LED_CTL, 0xE | BIT(17), BIT(2) | BIT(17));
+		} else if (speed_sta == 0x0000) {
+			*speed = SPEED_10;
+			wr32m(&hw->wxhw, NGBE_CFG_LED_CTL, 0xE | BIT(17), BIT(3) | BIT(17));
+		}
+	} else {
+		*speed = 0;
+		wr32m(&hw->wxhw, NGBE_CFG_LED_CTL, 0xE | BIT(17), 0);
+	}
+}
+
+u16 ngbe_phy_read_reg(struct ngbe_hw *hw, u32 reg_addr)
+{
+	u16 phy_data = 0;
+
+	if (hw->mac_type == ngbe_mac_type_mdi)
+		phy_data = ngbe_phy_read_reg_internal(hw, reg_addr);
+	else if (hw->mac_type == ngbe_mac_type_rgmii)
+		phy_data = ngbe_phy_read_reg_mdi(hw, reg_addr);
+
+	return phy_data;
+}
+
+void ngbe_phy_write_reg(struct ngbe_hw *hw, u32 reg_addr, u16 phy_data)
+{
+	if (hw->mac_type == ngbe_mac_type_mdi)
+		ngbe_phy_write_reg_internal(hw, reg_addr, phy_data);
+	else if (hw->mac_type == ngbe_mac_type_rgmii)
+		ngbe_phy_write_reg_mdi(hw, reg_addr, phy_data);
+}
+
+int ngbe_phy_setup_link(struct ngbe_hw *hw,
+			u32 speed,
+			bool autoneg_wait_to_complete)
+{
+	int ret = 0;
+
+	switch (hw->phy.type) {
+	case ngbe_phy_m88e1512:
+	case ngbe_phy_m88e1512_sfi:
+	case ngbe_phy_m88e1512_mix:
+		ret = ngbe_phy_setup_link_mv(hw, speed, false);
+		break;
+	case ngbe_phy_yt_mix:
+		ret = ngbe_phy_setup_link_yt(hw, speed, false);
+		break;
+	case ngbe_phy_internal_yt_sfi:
+	case ngbe_phy_internal:
+		ret = ngbe_phy_setup_link_internal(hw, speed, false);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+int ngbe_phy_check_link(struct ngbe_hw *hw,
+			u32 *speed,
+			bool *link_up,
+			bool link_up_wait_to_complete)
+{
+	int ret = 0;
+
+	switch (hw->phy.type) {
+	case ngbe_phy_m88e1512_sfi:
+	case ngbe_phy_m88e1512:
+		ngbe_check_phy_link_mv(hw, speed, link_up, false);
+		break;
+	case ngbe_phy_yt_mix:
+		ngbe_check_phy_link_yt(hw, speed, link_up, false);
+		break;
+	case ngbe_phy_internal_yt_sfi:
+	case ngbe_phy_internal:
+		/* select page to 0x0 */
+		ngbe_check_phy_link_internal(hw, speed, link_up, false);
+		break;
+	default:
+		ret = -EIO;
+		break;
+	}
+
+	return ret;
+}
+
+int ngbe_phy_reset(struct ngbe_hw *hw)
+{
+	int ret = 0;
+	u16 val = 0;
+
+	switch (hw->phy.type) {
+	case ngbe_phy_m88e1512_sfi:
+		/* select page to 0x1 */
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x1);
+		break;
+	case ngbe_phy_m88e1512:
+		/* select page to 0x0 */
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x0);
+		break;
+	case ngbe_phy_yt_mix:
+		if (hw->phy.media_type == ngbe_media_type_fiber) {
+			ngbe_phy_write_reg(hw, 0x1e, 0xa000);
+			ngbe_phy_write_reg(hw, 0x1f, 0x2);
+		}
+		break;
+	case ngbe_phy_internal_yt_sfi:
+	case ngbe_phy_internal:
+		/* select page to 0x0 */
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0x0);
+		break;
+	default:
+		ret =  -EIO;
+	}
+	val = ngbe_phy_read_reg(hw, NGBE_PHY_BASE_CTRL_REG);
+	val |= NGBE_PHY_RESET;
+	ngbe_phy_write_reg(hw, NGBE_PHY_BASE_CTRL_REG, val);
+
+	ret = read_poll_timeout(ngbe_phy_read_reg, val, val & NGBE_PHY_RESET, 1000,
+				100000, false, hw, 0x0);
+
+	if (!ret)
+		wx_err(&hw->wxhw,
+		       "phy reset exceeds maximum waiting period.\n");
+
+	return ret;
+}
+
+void ngbe_check_phy_event(struct ngbe_hw *hw)
+{
+	struct ngbe_adapter *adapter =
+			container_of(hw, struct ngbe_adapter, hw);
+	u16 int_mask = 0;
+	u16 val = 0;
+
+	switch (hw->phy.type) {
+	case ngbe_phy_m88e1512:
+		int_mask = NGBE_PHY_INT_STATUS_ANC_MV |
+			   NGBE_PHY_INT_STATUS_LSC_MV;
+		/* select page to 0x0 */
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x0);
+		val = ngbe_phy_read_reg(hw, NGBE_PHY_INT_STATUS_REG_MV);
+		break;
+	case ngbe_phy_m88e1512_sfi:
+		int_mask = NGBE_PHY_INT_STATUS_ANC_MV |
+			   NGBE_PHY_INT_STATUS_LSC_MV;
+		/* select page to 0x1 */
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_MV, 0x1);
+		val = ngbe_phy_read_reg(hw, NGBE_PHY_INT_STATUS_REG_MV);
+		break;
+	case ngbe_phy_yt_mix:
+		int_mask = NGBE_PHY_INT_STATUS_SDSLNKUP_YT |
+			   NGBE_PHY_INT_STATUS_SDSLNKDN_YT |
+			   NGBE_PHY_INT_STATUS_UTPLNKUP_YT |
+			   NGBE_PHY_INT_STATUS_UTPLNKDN_YT;
+		ngbe_phy_write_reg_ext_yt(hw, 0xa000, 0x0);
+		val = ngbe_phy_read_reg(hw, NGBE_PHY_INT_STATUS_REG_YT);
+		break;
+	case ngbe_phy_internal_yt_sfi:
+	case ngbe_phy_internal:
+		int_mask = NGBE_PHY_INT_STATUS_LSC_INTERNAL |
+			   NGBE_PHY_INT_STATUS_ANC_INTERNAL;
+		/* select page to 0xa43 */
+		ngbe_phy_write_reg(hw, NGBE_PHY_PAGE_ACCESS_INTERNAL, 0xa43);
+		val = ngbe_phy_read_reg(hw, NGBE_PHY_INT_STATUS_REG_INTERNAL);
+		break;
+	default:
+		break;
+	}
+
+	if (val | int_mask)
+		set_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags);
+}
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_phy.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_phy.h
new file mode 100644
index 000000000000..5d94f00194cf
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_phy.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * WangXun Gigabit PCI Express Linux driver
+ * Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd.
+ */
+
+#ifndef _NGBE_PHY_H_
+#define _NGBE_PHY_H_
+
+u16 ngbe_phy_read_reg(struct ngbe_hw *hw, u32 reg_addr);
+void ngbe_phy_write_reg(struct ngbe_hw *hw, u32 reg_addr, u16 phy_data);
+int ngbe_phy_init(struct ngbe_hw *hw);
+int ngbe_phy_reset(struct ngbe_hw *hw);
+int ngbe_phy_setup_link(struct ngbe_hw *hw,
+			u32 speed,
+			bool autoneg_wait_to_complete);
+int ngbe_phy_check_link(struct ngbe_hw *hw,
+			u32 *speed,
+			bool *link_up,
+			bool link_up_wait_to_complete);
+void ngbe_check_phy_event(struct ngbe_hw *hw);
+#endif /* _NGBE_HW_H_ */
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
index 39f6c03f1a54..f6b257e84319 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -63,6 +63,26 @@
 /* Media-dependent registers. */
 #define NGBE_MDIO_CLAUSE_SELECT			0x11220
 
+/* mdio access */
+#define NGBE_MSCA				0x11200
+#define NGBE_MSCA_RA(v)				((0xFFFF & (v)))
+#define NGBE_MSCA_PA(v)				((0x1F & (v)) << 16)
+#define NGBE_MSCA_DA(v)				((0x1F & (v)) << 21)
+#define NGBE_MSCC				0x11204
+#define NGBE_MSCC_DATA(v)			((0xFFFF & (v)))
+#define NGBE_MSCC_CMD(v)			((0x3 & (v)) << 16)
+
+enum NGBE_MSCA_CMD_value {
+	NGBE_MSCA_CMD_RSV = 0,
+	NGBE_MSCA_CMD_WRITE,
+	NGBE_MSCA_CMD_POST_READ,
+	NGBE_MSCA_CMD_READ,
+};
+
+#define NGBE_MSCC_SADDR				BIT(18)
+#define NGBE_MSCC_BUSY				BIT(22)
+#define NGBE_MDIO_CLK(v)			((0x7 & (v)) << 19)
+
 /* GPIO Registers */
 #define NGBE_GPIO_DR				0x14800
 #define NGBE_GPIO_DDR				0x14804
@@ -85,21 +105,95 @@
 #define NGBE_PSR_WKUP_CTL_IPV6			BIT(7) /* Directed IPv6 Pkt Wakeup Enable */
 
 #define NGBE_FW_EEPROM_CHECKSUM_CMD		0xE9
+#define NGBE_FW_PHY_LED_CONF			0xF1
 #define NGBE_FW_NVM_DATA_OFFSET			3
 #define NGBE_FW_CMD_DEFAULT_CHECKSUM		0xFF /* checksum always 0xFF */
 #define NGBE_FW_CMD_ST_PASS			0x80658383
 #define NGBE_FW_CMD_ST_FAIL			0x70657376
 
+/* NGBE phy base registers and BITs*/
+#define NGBE_PHY_BASE_CTRL_REG			0x0
+#define NGBE_PHY_ID1_REG			0x2
+#define NGBE_PHY_ID2_REG			0x3
+#define NGBE_PHY_ANAR_REG			0x4
+#define NGBE_PHY_GBCR_REG			0x9
+#define NGBE_PHY_INTM_REG			0x12
+#define NGBE_PHY_ID_MASK			0xFFFFFC00U
+
+/* NGBE_PHY_BASE_CTRL_REG bit mask*/
+#define NGBE_PHY_SPEED_SELECT1			BIT(6)
+#define NGBE_PHY_DUPLEX				BIT(8)
+#define NGBE_PHY_RESTART_AN			BIT(9)
+#define NGBE_PHY_POWER_DOWN			BIT(11)
+#define NGBE_PHY_ANE				BIT(12)
+#define NGBE_PHY_SPEED_SELECT0			BIT(13)
+#define NGBE_PHY_RESET				BIT(15)
+
+/* NGBE_PHY_ANAR_REG bit mask */
+#define NGBE_PHY_10BASET_HALF			BIT(5)
+#define NGBE_PHY_10BASET_FULL			BIT(6)
+#define NGBE_PHY_100BASETX_HALF			BIT(7)
+#define NGBE_PHY_100BASETX_FULL			BIT(8)
+
+#define NGBE_PHY_1000BASEX_FULL_MV		BIT(5)
+
+/* NGBE_PHY_GBCR_REG bit mask*/
+#define NGBE_PHY_1000BASET_HALF			BIT(8)
+#define NGBE_PHY_1000BASET_FULL			BIT(9)
+
+/* M88E1512 */
+#define NGBE_PHY_ID_MV				0x005043
+#define NGBE_PHY_PAGE_ACCESS_MV			0x16
+#define NGBE_PHY_INT_STATUS_REG_MV		0x13
+#define NGBE_PHY_LED_FUNC_CTRL_REG_MV		0x10
+#define NGBE_PHY_LED_POL_CTRL_REG_MV		0x11
+
+/* reg 19_0 INT status*/
+#define NGBE_PHY_INT_STATUS_ANC_MV		BIT(11)
+#define NGBE_PHY_INT_STATUS_LSC_MV		BIT(10)
+/* reg 21_2 */
+#define NGBE_RGM_TTC_MV				BIT(4)
+#define NGBE_RGM_RTC_MV				BIT(5)
+/* reg 18_3 */
+#define NGBE_INT_EN_MV				BIT(7)
+#define NGBE_INT_POL_MV				BIT(11)
+
+/* LED conf */
+#define NGBE_LED1_CONF_MV			0x6
+#define NGBE_LED0_CONF_MV			0x1
+/* LED polarity */
+#define NGBE_LED1_POL_MV			0x1
+#define NGBE_LED0_POL_MV			0x1
+
+/* YT8521s/YT8531s */
+#define NGBE_PHY_ID_YT8521S			0x011a
+#define NGBE_PHY_ID_YT8531S			0xe91a
+#define NGBE_PHY_INT_STATUS_REG_YT		0x13
+#define NGBE_PHY_INT_STATUS_SDSLNKUP_YT		BIT(2)
+#define NGBE_PHY_INT_STATUS_SDSLNKDN_YT		BIT(3)
+#define NGBE_PHY_INT_STATUS_UTPLNKUP_YT		BIT(10)
+#define NGBE_PHY_INT_STATUS_UTPLNKDN_YT		BIT(11)
+
+/* INTERNAL PHY*/
+#define NGBE_CFG_LED_CTL			0x14424
+#define NGBE_PHY_ID_INTERNAL			0x000732
+#define NGBE_PHY_CONFIG(reg_offset)		(0x14000 + ((reg_offset) * 4))
+#define NGBE_PHY_PAGE_ACCESS_INTERNAL		0x1F
+#define NGBE_PHY_INT_STATUS_REG_INTERNAL	0x1d
+#define NGBE_PHY_PHYSR_REG_INTERNAL		0x1a
+#define NGBE_PHY_INT_STATUS_LSC_INTERNAL	BIT(4)
+#define NGBE_PHY_INT_STATUS_ANC_INTERNAL	BIT(3)
+
 enum ngbe_phy_type {
 	ngbe_phy_unknown = 0,
 	ngbe_phy_none,
 	ngbe_phy_internal,
 	ngbe_phy_m88e1512,
 	ngbe_phy_m88e1512_sfi,
-	ngbe_phy_m88e1512_unknown,
-	ngbe_phy_yt8521s,
-	ngbe_phy_yt8521s_sfi,
-	ngbe_phy_internal_yt8521s_sfi,
+	ngbe_phy_m88e1512_mix,
+	ngbe_phy_yt,
+	ngbe_phy_yt_mix,
+	ngbe_phy_internal_yt_sfi,
 	ngbe_phy_generic
 };
 
@@ -122,9 +216,16 @@ struct ngbe_phy_info {
 
 	u32 addr;
 	u32 id;
+	u8 phy_mode;
+	u32 gphy_efuse[2];
 
-	bool reset_if_overtemp;
+	bool autoneg;
+	u32 autoneg_advertised;
+};
 
+enum ngbe_pf_flags {
+	NGBE_FLAG_NEED_LINK_UPDATE,
+	NGBE_FLAGS_NBITS		/* must be last */
 };
 
 struct ngbe_hw {
@@ -135,5 +236,7 @@ struct ngbe_hw {
 	bool wol_enabled;
 	bool ncsi_enabled;
 	bool gpio_ctrl;
+
+	u32 led_conf;
 };
 #endif /* _NGBE_TYPE_H_ */
