All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next 0/5] net: WangXun ethernet drivers
@ 2022-11-08 11:19 Mengyuan Lou
  2022-11-08 11:19 ` [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module Mengyuan Lou
                   ` (4 more replies)
  0 siblings, 5 replies; 25+ messages in thread
From: Mengyuan Lou @ 2022-11-08 11:19 UTC (permalink / raw)
  To: netdev; +Cc: jiawenwu, Mengyuan Lou

This patch series adds support for WangXun NICS, to initialize
phy ops and start service task to check link status.

Jiawen Wu (3):
  net: txgbe: Identify PHY and SFP module
  net: txgbe: Initialize service task
  net: txgbe: Support to setup link

Mengyuan Lou (2):
  net: ngbe: Initialize phy information
  net: ngbe: Initialize service task

 .../device_drivers/ethernet/wangxun/txgbe.rst |   42 +
 drivers/net/ethernet/wangxun/libwx/wx_hw.c    |    9 +-
 drivers/net/ethernet/wangxun/libwx/wx_hw.h    |    3 +
 drivers/net/ethernet/wangxun/libwx/wx_type.h  |    7 +
 drivers/net/ethernet/wangxun/ngbe/Makefile    |    2 +-
 drivers/net/ethernet/wangxun/ngbe/ngbe.h      |   23 +
 drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c   |   36 +
 drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h   |    1 +
 drivers/net/ethernet/wangxun/ngbe/ngbe_main.c |  408 ++++-
 drivers/net/ethernet/wangxun/ngbe/ngbe_phy.c  | 1113 ++++++++++++
 drivers/net/ethernet/wangxun/ngbe/ngbe_phy.h  |   22 +
 drivers/net/ethernet/wangxun/ngbe/ngbe_type.h |  114 +-
 drivers/net/ethernet/wangxun/txgbe/txgbe.h    |   31 +
 drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c | 1590 ++++++++++++++++-
 drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h |    9 +
 .../net/ethernet/wangxun/txgbe/txgbe_main.c   |  475 ++++-
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  227 +++
 17 files changed, 4082 insertions(+), 30 deletions(-)
 create mode 100644 drivers/net/ethernet/wangxun/ngbe/ngbe_phy.c
 create mode 100644 drivers/net/ethernet/wangxun/ngbe/ngbe_phy.h

-- 
2.38.1


^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module
  2022-11-08 11:19 [PATCH net-next 0/5] net: WangXun ethernet drivers Mengyuan Lou
@ 2022-11-08 11:19 ` Mengyuan Lou
  2022-11-08 20:52   ` Andrew Lunn
  2022-11-14 15:34   ` Alexander Lobakin
  2022-11-08 11:19 ` [PATCH net-next 2/5] net: txgbe: Initialize service task Mengyuan Lou
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 25+ messages in thread
From: Mengyuan Lou @ 2022-11-08 11:19 UTC (permalink / raw)
  To: netdev; +Cc: jiawenwu

From: Jiawen Wu <jiawenwu@trustnetic.com>

Add to get media type and physical layer module, support I2C access.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
 .../device_drivers/ethernet/wangxun/txgbe.rst |  37 ++
 drivers/net/ethernet/wangxun/libwx/wx_hw.c    |   6 +-
 drivers/net/ethernet/wangxun/libwx/wx_hw.h    |   2 +
 drivers/net/ethernet/wangxun/libwx/wx_type.h  |   1 +
 drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c | 421 ++++++++++++++++++
 .../net/ethernet/wangxun/txgbe/txgbe_main.c   |  29 +-
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   | 124 ++++++
 7 files changed, 614 insertions(+), 6 deletions(-)

diff --git a/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst b/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
index eaa87dbe8848..3cb9549fb7b0 100644
--- a/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
+++ b/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
@@ -14,6 +14,43 @@ Contents
 - Support
 
 
+Identifying Your Adapter
+========================
+The driver is compatible with WangXun Sapphire Dual ports Ethernet Adapters.
+
+SFP+ Devices with Pluggable Optics
+----------------------------------
+The following is a list of 3rd party SFP+ modules that have been tested and verified.
+
++----------+----------------------+----------------------+
+| Supplier | Type                 | Part Numbers         |
++==========+======================+======================+
+| Avago    | SFP+                 | AFBR-709SMZ          |
++----------+----------------------+----------------------+
+| F-tone   | SFP+                 | FTCS-851X-02D        |
++----------+----------------------+----------------------+
+| Finisar  | SFP+                 | FTLX8574D3BCL        |
++----------+----------------------+----------------------+
+| Hasense  | SFP+                 | AFBR-709SMZ          |
++----------+----------------------+----------------------+
+| HGTECH   | SFP+                 | MTRS-01X11-G         |
++----------+----------------------+----------------------+
+| HP       | SFP+                 | SR SFP+ 456096-001   |
++----------+----------------------+----------------------+
+| Huawei   | SFP+                 | AFBR-709SMZ          |
++----------+----------------------+----------------------+
+| Intel    | SFP+                 | FTLX8571D3BCV-IT     |
++----------+----------------------+----------------------+
+| JDSU     | SFP+                 | PLRXPL-SC-S43        |
++----------+----------------------+----------------------+
+| SONT     | SFP+                 | XP-8G10-01           |
++----------+----------------------+----------------------+
+| Trixon   | SFP+                 | TPS-TGM3-85DCR       |
++----------+----------------------+----------------------+
+| WTD      | SFP+                 | RTXM228-551          |
++----------+----------------------+----------------------+
+
+
 Support
 =======
 If you got any problem, contact Wangxun support team via support@trustnetic.com
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index 1eb7388f1dd5..045d6e978598 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -114,12 +114,13 @@ static DEFINE_MUTEX(wx_sw_sync_lock);
  *  Releases the SW semaphore for the specified
  *  function (CSR, PHY0, PHY1, EEPROM, Flash)
  **/
-static void wx_release_sw_sync(struct wx_hw *wxhw, u32 mask)
+void wx_release_sw_sync(struct wx_hw *wxhw, u32 mask)
 {
 	mutex_lock(&wx_sw_sync_lock);
 	wr32m(wxhw, WX_MNG_SWFW_SYNC, mask, 0);
 	mutex_unlock(&wx_sw_sync_lock);
 }
+EXPORT_SYMBOL(wx_release_sw_sync);
 
 /**
  *  wx_acquire_sw_sync - Acquire SW semaphore
@@ -129,7 +130,7 @@ static void wx_release_sw_sync(struct wx_hw *wxhw, u32 mask)
  *  Acquires the SW semaphore for the specified
  *  function (CSR, PHY0, PHY1, EEPROM, Flash)
  **/
-static int wx_acquire_sw_sync(struct wx_hw *wxhw, u32 mask)
+int wx_acquire_sw_sync(struct wx_hw *wxhw, u32 mask)
 {
 	u32 sem = 0;
 	int ret = 0;
@@ -147,6 +148,7 @@ static int wx_acquire_sw_sync(struct wx_hw *wxhw, u32 mask)
 
 	return ret;
 }
+EXPORT_SYMBOL(wx_acquire_sw_sync);
 
 /**
  *  wx_host_interface_command - Issue command to manageability block
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h
index a0652f5e9939..5058774381c1 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h
@@ -7,6 +7,8 @@
 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);
+void wx_release_sw_sync(struct wx_hw *wxhw, u32 mask);
+int wx_acquire_sw_sync(struct wx_hw *wxhw, u32 mask);
 int wx_host_interface_command(struct wx_hw *wxhw, u32 *buffer,
 			      u32 length, u32 timeout, bool return_data);
 int wx_read_ee_hostif(struct wx_hw *wxhw, u16 offset, u16 *data);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index 1cbeef8230bf..c95cda53bf67 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -123,6 +123,7 @@
 
 /************************************** MNG ********************************/
 #define WX_MNG_SWFW_SYNC             0x1E008
+#define WX_MNG_SWFW_SYNC_SW_PHY      BIT(0)
 #define WX_MNG_SWFW_SYNC_SW_MB       BIT(2)
 #define WX_MNG_SWFW_SYNC_SW_FLASH    BIT(3)
 #define WX_MNG_MBOX                  0x1E100
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
index 0b1032195859..77a44e48fc9e 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
@@ -14,6 +14,375 @@
 #include "txgbe_hw.h"
 #include "txgbe.h"
 
+static u32 txgbe_rd32_epcs(struct txgbe_hw *hw, u32 addr)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 offset;
+
+	/* Set the LAN port indicator to offset[1] */
+	/* 1st, write the offset to IDA_ADDR register */
+	offset = TXGBE_XPCS_IDA_ADDR;
+	wr32(wxhw, offset, addr);
+
+	/* 2nd, read the data from IDA_DATA register */
+	offset = TXGBE_XPCS_IDA_DATA;
+	return rd32(wxhw, offset);
+}
+
+static void txgbe_init_i2c(struct txgbe_hw *hw)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+
+	wr32(wxhw, TXGBE_I2C_ENABLE, 0);
+
+	wr32(wxhw, TXGBE_I2C_CON,
+	     (TXGBE_I2C_CON_MASTER_MODE |
+	      TXGBE_I2C_CON_SPEED(1) |
+	      TXGBE_I2C_CON_RESTART_EN |
+	      TXGBE_I2C_CON_SLAVE_DISABLE));
+	/* Default addr is 0xA0 ,bit 0 is configure for read/write! */
+	wr32(wxhw, TXGBE_I2C_TAR, TXGBE_I2C_SLAVE_ADDR);
+	wr32(wxhw, TXGBE_I2C_SS_SCL_HCNT, 600);
+	wr32(wxhw, TXGBE_I2C_SS_SCL_LCNT, 600);
+	wr32(wxhw, TXGBE_I2C_RX_TL, 0); /* 1byte for rx full signal */
+	wr32(wxhw, TXGBE_I2C_TX_TL, 4);
+	wr32(wxhw, TXGBE_I2C_SCL_STUCK_TIMEOUT, 0xFFFFFF);
+	wr32(wxhw, TXGBE_I2C_SDA_STUCK_TIMEOUT, 0xFFFFFF);
+
+	wr32(wxhw, TXGBE_I2C_INTR_MASK, 0);
+	wr32(wxhw, TXGBE_I2C_ENABLE, 1);
+}
+
+/**
+ *  txgbe_read_i2c_byte_int - Reads 8 bit word over I2C
+ *  @hw: pointer to hardware structure
+ *  @byte_offset: byte offset to read
+ *  @data: value read
+ *
+ *  Performs byte read operation to SFP module's EEPROM over I2C interface at
+ *  a specified device address.
+ **/
+static int txgbe_read_i2c_byte_int(struct txgbe_hw *hw, u8 byte_offset, u8 *data)
+{
+	u32 sync = WX_MNG_SWFW_SYNC_SW_PHY;
+	struct wx_hw *wxhw = &hw->wxhw;
+	int status = 0;
+	u32 val;
+
+	status = wx_acquire_sw_sync(wxhw, sync);
+	if (status != 0)
+		return status;
+
+	/* wait tx empty */
+	status = read_poll_timeout(rd32, val,
+				   (val & TXGBE_I2C_INTR_STAT_TEMP) == TXGBE_I2C_INTR_STAT_TEMP,
+				   100, 1000, false, wxhw, TXGBE_I2C_RAW_INTR_STAT);
+	if (status != 0)
+		goto out;
+
+	/* read data */
+	wr32(wxhw, TXGBE_I2C_DATA_CMD, byte_offset | TXGBE_I2C_DATA_CMD_STOP);
+	wr32(wxhw, TXGBE_I2C_DATA_CMD, TXGBE_I2C_DATA_CMD_READ);
+
+	/* wait for read complete */
+	status = read_poll_timeout(rd32, val,
+				   (val & TXGBE_I2C_INTR_STAT_RFUL) == TXGBE_I2C_INTR_STAT_RFUL,
+				   100, 1000, false, wxhw, TXGBE_I2C_RAW_INTR_STAT);
+	if (status != 0)
+		goto out;
+
+	*data = 0xFF & rd32(wxhw, TXGBE_I2C_DATA_CMD);
+
+out:
+	wx_release_sw_sync(wxhw, sync);
+	return status;
+}
+
+/**
+ *  txgbe_switch_i2c_slave_addr - Switch I2C slave address
+ *  @hw: pointer to hardware structure
+ *  @dev_addr: slave addr to switch
+ **/
+static void txgbe_switch_i2c_slave_addr(struct txgbe_hw *hw, u8 dev_addr)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+
+	wr32(wxhw, TXGBE_I2C_ENABLE, 0);
+	wr32(wxhw, TXGBE_I2C_TAR, dev_addr >> 1);
+	wr32(wxhw, TXGBE_I2C_ENABLE, 1);
+}
+
+/**
+ *  txgbe_read_i2c_byte - Reads 8 bit word over I2C
+ *  @hw: pointer to hardware structure
+ *  @byte_offset: byte offset to read
+ *  @dev_addr: device address
+ *  @data: value read
+ *
+ *  Performs byte read operation to SFP module's EEPROM over I2C interface at
+ *  a specified device address.
+ **/
+static int txgbe_read_i2c_byte(struct txgbe_hw *hw, u8 byte_offset,
+			       u8 dev_addr, u8 *data)
+{
+	txgbe_switch_i2c_slave_addr(hw, dev_addr);
+
+	return txgbe_read_i2c_byte_int(hw, byte_offset, data);
+}
+
+/**
+ *  txgbe_read_i2c_eeprom - Reads 8 bit EEPROM word over I2C interface
+ *  @hw: pointer to hardware structure
+ *  @byte_offset: EEPROM byte offset to read
+ *  @eeprom_data: value read
+ *
+ *  Performs byte read operation to SFP module's EEPROM over I2C interface.
+ **/
+static int txgbe_read_i2c_eeprom(struct txgbe_hw *hw, u8 byte_offset,
+				 u8 *eeprom_data)
+{
+	return txgbe_read_i2c_byte(hw, byte_offset,
+				   TXGBE_I2C_EEPROM_DEV_ADDR,
+				   eeprom_data);
+}
+
+/**
+ *  txgbe_identify_sfp_module - Identifies SFP modules
+ *  @hw: pointer to hardware structure
+ *
+ *  Searches for and identifies the SFP module and assigns appropriate PHY type.
+ **/
+static int txgbe_identify_sfp_module(struct txgbe_hw *hw)
+{
+	u8 oui_bytes[3] = {0, 0, 0};
+	u8 comp_codes_10g = 0;
+	u8 comp_codes_1g = 0;
+	int status = -EFAULT;
+	u32 vendor_oui = 0;
+	u8 identifier = 0;
+	u8 cable_tech = 0;
+	u8 cable_spec = 0;
+
+	/* LAN ID is needed for I2C access */
+	txgbe_init_i2c(hw);
+
+	status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_IDENTIFIER, &identifier);
+	if (status != 0)
+		goto err_read_i2c_eeprom;
+
+	if (identifier != TXGBE_SFF_IDENTIFIER_SFP) {
+		hw->phy.type = txgbe_phy_sfp_unsupported;
+		status = -ENODEV;
+	} else {
+		status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_1GBE_COMP_CODES,
+					       &comp_codes_1g);
+		if (status != 0)
+			goto err_read_i2c_eeprom;
+
+		status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_10GBE_COMP_CODES,
+					       &comp_codes_10g);
+		if (status != 0)
+			goto err_read_i2c_eeprom;
+
+		status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_CABLE_TECHNOLOGY,
+					       &cable_tech);
+		if (status != 0)
+			goto err_read_i2c_eeprom;
+
+		 /* ID Module
+		  * =========
+		  * 1   SFP_DA_CORE
+		  * 2   SFP_SR/LR_CORE
+		  * 3   SFP_act_lmt_DA_CORE
+		  * 4   SFP_1g_cu_CORE
+		  * 5   SFP_1g_sx_CORE
+		  * 6   SFP_1g_lx_CORE
+		  */
+		if (cable_tech & TXGBE_SFF_DA_PASSIVE_CABLE) {
+			hw->phy.sfp_type = txgbe_sfp_type_da_cu_core;
+		} else if (cable_tech & TXGBE_SFF_DA_ACTIVE_CABLE) {
+			txgbe_read_i2c_eeprom(hw, TXGBE_SFF_CABLE_SPEC_COMP,
+					      &cable_spec);
+			if (cable_spec & TXGBE_SFF_DA_SPEC_ACTIVE_LIMITING)
+				hw->phy.sfp_type = txgbe_sfp_type_da_act_lmt_core;
+			else
+				hw->phy.sfp_type = txgbe_sfp_type_unknown;
+		} else if (comp_codes_10g & (TXGBE_SFF_10GBASESR_CAPABLE |
+					     TXGBE_SFF_10GBASELR_CAPABLE)) {
+			hw->phy.sfp_type = txgbe_sfp_type_srlr_core;
+		} else if (comp_codes_1g & TXGBE_SFF_1GBASET_CAPABLE) {
+			hw->phy.sfp_type = txgbe_sfp_type_1g_cu_core;
+		} else if (comp_codes_1g & TXGBE_SFF_1GBASESX_CAPABLE) {
+			hw->phy.sfp_type = txgbe_sfp_type_1g_sx_core;
+		} else if (comp_codes_1g & TXGBE_SFF_1GBASELX_CAPABLE) {
+			hw->phy.sfp_type = txgbe_sfp_type_1g_lx_core;
+		} else {
+			hw->phy.sfp_type = txgbe_sfp_type_unknown;
+		}
+
+		/* Determine if the SFP+ PHY is dual speed or not. */
+		hw->phy.multispeed_fiber = false;
+		if (((comp_codes_1g & TXGBE_SFF_1GBASESX_CAPABLE) &&
+		     (comp_codes_10g & TXGBE_SFF_10GBASESR_CAPABLE)) ||
+		    ((comp_codes_1g & TXGBE_SFF_1GBASELX_CAPABLE) &&
+		     (comp_codes_10g & TXGBE_SFF_10GBASELR_CAPABLE)))
+			hw->phy.multispeed_fiber = true;
+
+		/* Determine PHY vendor */
+		status = txgbe_read_i2c_eeprom(hw,
+					       TXGBE_SFF_VENDOR_OUI_BYTE0,
+					       &oui_bytes[0]);
+		if (status != 0)
+			goto err_read_i2c_eeprom;
+
+		status = txgbe_read_i2c_eeprom(hw,
+					       TXGBE_SFF_VENDOR_OUI_BYTE1,
+					       &oui_bytes[1]);
+		if (status != 0)
+			goto err_read_i2c_eeprom;
+
+		status = txgbe_read_i2c_eeprom(hw,
+					       TXGBE_SFF_VENDOR_OUI_BYTE2,
+					       &oui_bytes[2]);
+		if (status != 0)
+			goto err_read_i2c_eeprom;
+
+		vendor_oui =
+			((oui_bytes[0] << TXGBE_SFF_VENDOR_OUI_BYTE0_SHIFT) |
+			 (oui_bytes[1] << TXGBE_SFF_VENDOR_OUI_BYTE1_SHIFT) |
+			 (oui_bytes[2] << TXGBE_SFF_VENDOR_OUI_BYTE2_SHIFT));
+
+		switch (vendor_oui) {
+		case TXGBE_SFF_VENDOR_OUI_TYCO:
+			if (cable_tech & TXGBE_SFF_DA_PASSIVE_CABLE)
+				hw->phy.type = txgbe_phy_sfp_passive_tyco;
+			break;
+		case TXGBE_SFF_VENDOR_OUI_FTL:
+			if (cable_tech & TXGBE_SFF_DA_ACTIVE_CABLE)
+				hw->phy.type = txgbe_phy_sfp_ftl_active;
+			else
+				hw->phy.type = txgbe_phy_sfp_ftl;
+			break;
+		case TXGBE_SFF_VENDOR_OUI_AVAGO:
+			hw->phy.type = txgbe_phy_sfp_avago;
+			break;
+		case TXGBE_SFF_VENDOR_OUI_INTEL:
+			hw->phy.type = txgbe_phy_sfp_intel;
+			break;
+		default:
+			if (cable_tech & TXGBE_SFF_DA_PASSIVE_CABLE)
+				hw->phy.type = txgbe_phy_sfp_passive_unknown;
+			else if (cable_tech & TXGBE_SFF_DA_ACTIVE_CABLE)
+				hw->phy.type = txgbe_phy_sfp_active_unknown;
+			else
+				hw->phy.type = txgbe_phy_sfp_unknown;
+			break;
+		}
+
+		/* Allow any DA cable vendor */
+		if (cable_tech & (TXGBE_SFF_DA_PASSIVE_CABLE |
+				  TXGBE_SFF_DA_ACTIVE_CABLE))
+			return 0;
+
+		/* Verify supported 1G SFP modules */
+		if (comp_codes_10g == 0 &&
+		    !(hw->phy.sfp_type == txgbe_sfp_type_1g_cu_core ||
+		      hw->phy.sfp_type == txgbe_sfp_type_1g_lx_core ||
+		      hw->phy.sfp_type == txgbe_sfp_type_1g_sx_core)) {
+			hw->phy.type = txgbe_phy_sfp_unsupported;
+			return -ENODEV;
+		}
+	}
+
+	return status;
+
+err_read_i2c_eeprom:
+	hw->phy.sfp_type = txgbe_sfp_type_not_present;
+	hw->phy.type = txgbe_phy_unknown;
+
+	return -ENODEV;
+}
+
+/**
+ *  txgbe_get_media_type - Get media type
+ *  @hw: pointer to hardware structure
+ *
+ *  Returns the media type (fiber, copper, backplane)
+ **/
+static enum txgbe_media_type txgbe_get_media_type(struct txgbe_hw *hw)
+{
+	enum txgbe_media_type media_type;
+	struct wx_hw *wxhw = &hw->wxhw;
+	u8 device_type;
+
+	device_type = wxhw->subsystem_device_id & 0xF0;
+	switch (device_type) {
+	case TXGBE_ID_MAC_XAUI:
+	case TXGBE_ID_MAC_SGMII:
+	case TXGBE_ID_KR_KX_KX4:
+		/* Default device ID is mezzanine card KX/KX4 */
+		media_type = txgbe_media_type_backplane;
+		break;
+	case TXGBE_ID_SFP:
+		media_type = txgbe_media_type_fiber;
+		break;
+	case TXGBE_ID_XAUI:
+	case TXGBE_ID_SGMII:
+		media_type = txgbe_media_type_copper;
+		break;
+	case TXGBE_ID_SFI_XAUI:
+		if (wxhw->bus.func == 0)
+			media_type = txgbe_media_type_fiber;
+		else
+			media_type = txgbe_media_type_copper;
+		break;
+	default:
+		media_type = txgbe_media_type_unknown;
+		break;
+	}
+
+	return media_type;
+}
+
+/**
+ *  txgbe_identify_phy - Get physical layer module
+ *  @hw: pointer to hardware structure
+ *
+ *  Determines the physical layer module found on the current adapter.
+ *  If PHY already detected, maintains current PHY type in hw struct,
+ *  otherwise executes the PHY detection routine.
+ **/
+static int txgbe_identify_phy(struct txgbe_hw *hw)
+{
+	int status;
+
+	/* Detect PHY if not unknown - returns success if already detected. */
+	hw->phy.media_type = txgbe_get_media_type(hw);
+	if (hw->phy.media_type == txgbe_media_type_fiber) {
+		status = txgbe_identify_sfp_module(hw);
+	} else {
+		hw->phy.type = txgbe_phy_none;
+		status = 0;
+	}
+
+	return status;
+}
+
+/**
+ *  txgbe_init_phy - PHY/SFP specific init
+ *  @hw: pointer to hardware structure
+ **/
+static int txgbe_init_phy(struct txgbe_hw *hw)
+{
+	int ret_val = 0;
+
+	txgbe_init_i2c(hw);
+	/* Identify the PHY or SFP module */
+	ret_val = txgbe_identify_phy(hw);
+
+	return ret_val;
+}
+
 /**
  *  txgbe_init_thermal_sensor_thresh - Inits thermal sensor thresholds
  *  @hw: pointer to hardware structure
@@ -260,6 +629,7 @@ static void txgbe_reset_misc(struct txgbe_hw *hw)
 {
 	struct wx_hw *wxhw = &hw->wxhw;
 
+	txgbe_init_i2c(hw);
 	wx_reset_misc(wxhw);
 	txgbe_init_thermal_sensor_thresh(hw);
 }
@@ -277,11 +647,30 @@ int txgbe_reset_hw(struct txgbe_hw *hw)
 	struct wx_hw *wxhw = &hw->wxhw;
 	int status;
 
+	u32 sr_pcs_ctl, sr_pma_mmd_ctl1, sr_an_mmd_ctl, sr_an_mmd_adv_reg2;
+	u32 vr_xs_or_pcs_mmd_digi_ctl1, curr_vr_xs_or_pcs_mmd_digi_ctl1;
+	u32 curr_sr_an_mmd_ctl, curr_sr_an_mmd_adv_reg2;
+	u32 curr_sr_pcs_ctl, curr_sr_pma_mmd_ctl1;
+
 	/* Call adapter stop to disable tx/rx and clear interrupts */
 	status = wx_stop_adapter(wxhw);
 	if (status != 0)
 		return status;
 
+	/* Identify PHY and related function pointers */
+	status = txgbe_init_phy(hw);
+	if (status != 0 && hw->phy.type == txgbe_phy_sfp_unsupported)
+		return status;
+
+	/* Remember internal phy regs from before we reset */
+	curr_sr_pcs_ctl = txgbe_rd32_epcs(hw, TXGBE_SR_PCS_CTL2);
+	curr_sr_pma_mmd_ctl1 = txgbe_rd32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1);
+	curr_sr_an_mmd_ctl = txgbe_rd32_epcs(hw, TXGBE_SR_AN_MMD_CTL);
+	curr_sr_an_mmd_adv_reg2 = txgbe_rd32_epcs(hw,
+						  TXGBE_SR_AN_MMD_ADV_REG2);
+	curr_vr_xs_or_pcs_mmd_digi_ctl1 =
+		txgbe_rd32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1);
+
 	if (!(((wxhw->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) ||
 	      ((wxhw->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP)))
 		wx_reset_hostif(wxhw);
@@ -294,6 +683,38 @@ int txgbe_reset_hw(struct txgbe_hw *hw)
 
 	txgbe_reset_misc(hw);
 
+	/* Store the original values if they have not been stored
+	 * off yet.  Otherwise restore the stored original values
+	 * since the reset operation sets back to defaults.
+	 */
+	sr_pcs_ctl = txgbe_rd32_epcs(hw, TXGBE_SR_PCS_CTL2);
+	sr_pma_mmd_ctl1 = txgbe_rd32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1);
+	sr_an_mmd_ctl = txgbe_rd32_epcs(hw, TXGBE_SR_AN_MMD_CTL);
+	sr_an_mmd_adv_reg2 = txgbe_rd32_epcs(hw, TXGBE_SR_AN_MMD_ADV_REG2);
+	vr_xs_or_pcs_mmd_digi_ctl1 =
+		txgbe_rd32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1);
+
+	if (!hw->phy.orig_link_settings_stored) {
+		hw->phy.orig_sr_pcs_ctl2 = sr_pcs_ctl;
+		hw->phy.orig_sr_pma_mmd_ctl1 = sr_pma_mmd_ctl1;
+		hw->phy.orig_sr_an_mmd_ctl = sr_an_mmd_ctl;
+		hw->phy.orig_sr_an_mmd_adv_reg2 = sr_an_mmd_adv_reg2;
+		hw->phy.orig_vr_xs_or_pcs_mmd_digi_ctl1 =
+						vr_xs_or_pcs_mmd_digi_ctl1;
+		hw->phy.orig_link_settings_stored = true;
+	} else {
+		hw->phy.orig_sr_pcs_ctl2 = curr_sr_pcs_ctl;
+		hw->phy.orig_sr_pma_mmd_ctl1 = curr_sr_pma_mmd_ctl1;
+		hw->phy.orig_sr_an_mmd_ctl = curr_sr_an_mmd_ctl;
+		hw->phy.orig_sr_an_mmd_adv_reg2 =
+					curr_sr_an_mmd_adv_reg2;
+		hw->phy.orig_vr_xs_or_pcs_mmd_digi_ctl1 =
+					curr_vr_xs_or_pcs_mmd_digi_ctl1;
+	}
+
+	/*make sure phy power is up*/
+	msleep(100);
+
 	/* Store the permanent mac address */
 	wx_get_mac_addr(wxhw, wxhw->mac.perm_addr);
 
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index 36780e7f05b7..1c00ecbc1c6a 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -174,7 +174,9 @@ static void txgbe_reset(struct txgbe_adapter *adapter)
 	int err;
 
 	err = txgbe_reset_hw(hw);
-	if (err != 0)
+	if (err != 0 &&
+	    hw->phy.type != txgbe_phy_sfp_unsupported &&
+	    hw->phy.sfp_type != txgbe_sfp_type_not_present)
 		dev_err(&adapter->pdev->dev, "Hardware Error: %d\n", err);
 
 	/* do not flush user set addresses */
@@ -487,9 +489,19 @@ static int txgbe_probe(struct pci_dev *pdev,
 	}
 
 	err = txgbe_reset_hw(hw);
-	if (err) {
-		dev_err(&pdev->dev, "HW Init failed: %d\n", err);
-		goto err_free_mac_table;
+	if (err != 0) {
+		if (hw->phy.sfp_type == txgbe_sfp_type_not_present) {
+			err = 0;
+		} else if (hw->phy.type == txgbe_phy_sfp_unsupported) {
+			dev_err(&pdev->dev,
+				"An unsupported SFP+ module type was detected.\n");
+			dev_err(&pdev->dev,
+				"Reload the driver after installing a supported module.\n");
+			goto err_free_mac_table;
+		} else {
+			dev_err(&pdev->dev, "HW Init failed: %d\n", err);
+			goto err_free_mac_table;
+		}
 	}
 
 	netdev->features |= NETIF_F_HIGHDMA;
@@ -568,6 +580,15 @@ static int txgbe_probe(struct pci_dev *pdev,
 	err = txgbe_read_pba_string(hw, part_str, TXGBE_PBANUM_LENGTH);
 	if (err)
 		strncpy(part_str, "Unknown", TXGBE_PBANUM_LENGTH);
+	if (hw->phy.media_type == txgbe_media_type_fiber &&
+	    hw->phy.sfp_type != txgbe_sfp_type_not_present)
+		netif_info(adapter, probe, netdev,
+			   "PHY: %d, SFP+: %d, PBA No: %s\n",
+			   hw->phy.type, hw->phy.sfp_type, part_str);
+	else
+		netif_info(adapter, probe, netdev,
+			   "PHY: %d, PBA No: %s\n",
+			   hw->phy.type, part_str);
 
 	netif_info(adapter, probe, netdev, "%pM\n", netdev->dev_addr);
 
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index 740a1c447e20..2f8be0118157 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -53,6 +53,72 @@
 #define TXGBE_TS_CTL                            0x10300
 #define TXGBE_TS_CTL_EVAL_MD                    BIT(31)
 
+/*********************** ETH PHY ***********************/
+#define TXGBE_XPCS_IDA_ADDR                     0x13000
+#define TXGBE_XPCS_IDA_DATA                     0x13004
+/* ETH PHY Registers */
+#define TXGBE_SR_PCS_CTL2                       0x30007
+#define TXGBE_SR_PMA_MMD_CTL1                   0x10000
+#define TXGBE_SR_AN_MMD_CTL                     0x70000
+#define TXGBE_SR_AN_MMD_ADV_REG2                0x70011
+#define TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1        0x38000
+/* I2C registers */
+#define TXGBE_I2C_CON                           0x14900 /* I2C Control */
+#define TXGBE_I2C_CON_SLAVE_DISABLE             BIT(6)
+#define TXGBE_I2C_CON_RESTART_EN                BIT(5)
+#define TXGBE_I2C_CON_SPEED(_v)                 (((_v) & 0x3) << 1)
+#define TXGBE_I2C_CON_MASTER_MODE               BIT(0)
+#define TXGBE_I2C_TAR                           0x14904 /* I2C Target Address */
+#define TXGBE_I2C_DATA_CMD                      0x14910 /* I2C Rx/Tx Data Buf and Cmd */
+#define TXGBE_I2C_DATA_CMD_STOP                 BIT(9)
+#define TXGBE_I2C_DATA_CMD_READ                 (BIT(8) | TXGBE_I2C_DATA_CMD_STOP)
+#define TXGBE_I2C_SS_SCL_HCNT                   0x14914
+#define TXGBE_I2C_SS_SCL_LCNT                   0x14918
+#define TXGBE_I2C_INTR_MASK                     0x14930 /* I2C Interrupt Mask */
+#define TXGBE_I2C_RAW_INTR_STAT                 0x14934 /* I2C Raw Interrupt Status */
+#define TXGBE_I2C_INTR_STAT_RFUL                BIT(2)
+#define TXGBE_I2C_INTR_STAT_TEMP                BIT(4)
+#define TXGBE_I2C_RX_TL                         0x14938 /* I2C Receive FIFO Threshold */
+#define TXGBE_I2C_TX_TL                         0x1493C /* I2C TX FIFO Threshold */
+#define TXGBE_I2C_ENABLE                        0x1496C /* I2C Enable */
+#define TXGBE_I2C_SCL_STUCK_TIMEOUT             0x149AC
+#define TXGBE_I2C_SDA_STUCK_TIMEOUT             0x149B0
+
+#define TXGBE_I2C_SLAVE_ADDR                    (0xA0 >> 1)
+#define TXGBE_I2C_EEPROM_DEV_ADDR               0xA0
+
+/* EEPROM byte offsets */
+#define TXGBE_SFF_IDENTIFIER                    0x0
+#define TXGBE_SFF_IDENTIFIER_SFP                0x3
+#define TXGBE_SFF_VENDOR_OUI_BYTE0              0x25
+#define TXGBE_SFF_VENDOR_OUI_BYTE1              0x26
+#define TXGBE_SFF_VENDOR_OUI_BYTE2              0x27
+#define TXGBE_SFF_1GBE_COMP_CODES               0x6
+#define TXGBE_SFF_10GBE_COMP_CODES              0x3
+#define TXGBE_SFF_CABLE_TECHNOLOGY              0x8
+#define TXGBE_SFF_CABLE_SPEC_COMP               0x3C
+
+/* Bitmasks */
+#define TXGBE_SFF_DA_PASSIVE_CABLE              BIT(2)
+#define TXGBE_SFF_DA_ACTIVE_CABLE               BIT(3)
+#define TXGBE_SFF_DA_SPEC_ACTIVE_LIMITING       BIT(2)
+#define TXGBE_SFF_1GBASESX_CAPABLE              BIT(0)
+#define TXGBE_SFF_1GBASELX_CAPABLE              BIT(1)
+#define TXGBE_SFF_1GBASET_CAPABLE               BIT(3)
+#define TXGBE_SFF_10GBASESR_CAPABLE             BIT(4)
+#define TXGBE_SFF_10GBASELR_CAPABLE             BIT(5)
+
+/* Bit-shift macros */
+#define TXGBE_SFF_VENDOR_OUI_BYTE0_SHIFT        24
+#define TXGBE_SFF_VENDOR_OUI_BYTE1_SHIFT        16
+#define TXGBE_SFF_VENDOR_OUI_BYTE2_SHIFT        8
+
+/* Vendor OUIs: format of OUI is 0x[byte0][byte1][byte2][00] */
+#define TXGBE_SFF_VENDOR_OUI_TYCO               0x00407600
+#define TXGBE_SFF_VENDOR_OUI_FTL                0x00906500
+#define TXGBE_SFF_VENDOR_OUI_AVAGO              0x00176A00
+#define TXGBE_SFF_VENDOR_OUI_INTEL              0x001B2100
+
 /* Part Number String Length */
 #define TXGBE_PBANUM_LENGTH                     32
 
@@ -67,8 +133,66 @@
 #define TXGBE_PBANUM1_PTR                       0x06
 #define TXGBE_PBANUM_PTR_GUARD                  0xFAFA
 
+/* SFP+ module type IDs:
+ *
+ * ID   Module Type
+ * =============
+ * 0    SFP_DA_CU
+ * 1    SFP_SR
+ * 2    SFP_LR
+ * 3    SFP_DA_CU_CORE0
+ * 4    SFP_DA_CU_CORE1
+ * 5    SFP_SR/LR_CORE0
+ * 6    SFP_SR/LR_CORE1
+ */
+enum txgbe_sfp_type {
+	txgbe_sfp_type_da_cu_core = 1,
+	txgbe_sfp_type_srlr_core = 2,
+	txgbe_sfp_type_da_act_lmt_core = 3,
+	txgbe_sfp_type_1g_cu_core = 4,
+	txgbe_sfp_type_1g_sx_core = 5,
+	txgbe_sfp_type_1g_lx_core = 6,
+	txgbe_sfp_type_not_present = 0xFFFE,
+	txgbe_sfp_type_unknown = 0xFFFF
+};
+
+enum txgbe_phy_type {
+	txgbe_phy_unknown = 0,
+	txgbe_phy_none,
+	txgbe_phy_sfp_passive_tyco,
+	txgbe_phy_sfp_passive_unknown,
+	txgbe_phy_sfp_active_unknown,
+	txgbe_phy_sfp_avago,
+	txgbe_phy_sfp_ftl,
+	txgbe_phy_sfp_ftl_active,
+	txgbe_phy_sfp_unknown,
+	txgbe_phy_sfp_intel,
+	txgbe_phy_sfp_unsupported
+};
+
+enum txgbe_media_type {
+	txgbe_media_type_unknown = 0,
+	txgbe_media_type_fiber,
+	txgbe_media_type_copper,
+	txgbe_media_type_backplane
+};
+
+struct txgbe_phy_info {
+	enum txgbe_sfp_type sfp_type;
+	enum txgbe_phy_type type;
+	enum txgbe_media_type media_type;
+	u32 orig_sr_pcs_ctl2;
+	u32 orig_sr_pma_mmd_ctl1;
+	u32 orig_sr_an_mmd_ctl;
+	u32 orig_sr_an_mmd_adv_reg2;
+	u32 orig_vr_xs_or_pcs_mmd_digi_ctl1;
+	bool orig_link_settings_stored;
+	bool multispeed_fiber;
+};
+
 struct txgbe_hw {
 	struct wx_hw wxhw;
+	struct txgbe_phy_info phy;
 };
 
 #endif /* _TXGBE_TYPE_H_ */
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH net-next 2/5] net: txgbe: Initialize service task
  2022-11-08 11:19 [PATCH net-next 0/5] net: WangXun ethernet drivers Mengyuan Lou
  2022-11-08 11:19 ` [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module Mengyuan Lou
@ 2022-11-08 11:19 ` Mengyuan Lou
  2022-11-08 23:55   ` Jakub Kicinski
  2022-11-14 15:39   ` Alexander Lobakin
  2022-11-08 11:19 ` [PATCH net-next 3/5] net: txgbe: Support to setup link Mengyuan Lou
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 25+ messages in thread
From: Mengyuan Lou @ 2022-11-08 11:19 UTC (permalink / raw)
  To: netdev; +Cc: jiawenwu

From: Jiawen Wu <jiawenwu@trustnetic.com>

Setup work queue, and initialize service task to process the following
tasks.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
 drivers/net/ethernet/wangxun/txgbe/txgbe.h    |  15 +++
 .../net/ethernet/wangxun/txgbe/txgbe_main.c   | 113 +++++++++++++++++-
 2 files changed, 124 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe.h b/drivers/net/ethernet/wangxun/txgbe/txgbe.h
index 19e61377bd00..fb8fd413b755 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe.h
@@ -24,6 +24,17 @@ struct txgbe_mac_addr {
 #define TXGBE_MAC_STATE_MODIFIED        0x2
 #define TXGBE_MAC_STATE_IN_USE          0x4
 
+enum txgbe_state_t {
+	__TXGBE_TESTING,
+	__TXGBE_RESETTING,
+	__TXGBE_DOWN,
+	__TXGBE_HANGING,
+	__TXGBE_DISABLED,
+	__TXGBE_REMOVING,
+	__TXGBE_SERVICE_SCHED,
+	__TXGBE_SERVICE_INITED,
+};
+
 /* board specific private data structure */
 struct txgbe_adapter {
 	u8 __iomem *io_addr;    /* Mainly for iounmap use */
@@ -31,6 +42,10 @@ struct txgbe_adapter {
 	struct net_device *netdev;
 	struct pci_dev *pdev;
 
+	unsigned long state;
+	struct timer_list service_timer;
+	struct work_struct service_task;
+
 	/* structs defined in txgbe_type.h */
 	struct txgbe_hw hw;
 	u16 msg_enable;
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index 1c00ecbc1c6a..cb86c001baa6 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -35,6 +35,8 @@ static const struct pci_device_id txgbe_pci_tbl[] = {
 
 #define DEFAULT_DEBUG_LEVEL_SHIFT 3
 
+static struct workqueue_struct *txgbe_wq;
+
 static void txgbe_check_minimum_link(struct txgbe_adapter *adapter)
 {
 	struct pci_dev *pdev;
@@ -73,6 +75,50 @@ static int txgbe_enumerate_functions(struct txgbe_adapter *adapter)
 	return physfns;
 }
 
+static void txgbe_service_event_schedule(struct txgbe_adapter *adapter)
+{
+	if (!test_bit(__TXGBE_DOWN, &adapter->state) &&
+	    !test_bit(__TXGBE_REMOVING, &adapter->state) &&
+	    !test_and_set_bit(__TXGBE_SERVICE_SCHED, &adapter->state))
+		queue_work(txgbe_wq, &adapter->service_task);
+}
+
+static void txgbe_service_event_complete(struct txgbe_adapter *adapter)
+{
+	if (WARN_ON(!test_bit(__TXGBE_SERVICE_SCHED, &adapter->state)))
+		return;
+
+	/* flush memory to make sure state is correct before next watchdog */
+	smp_mb__before_atomic();
+	clear_bit(__TXGBE_SERVICE_SCHED, &adapter->state);
+}
+
+static void txgbe_service_timer(struct timer_list *t)
+{
+	struct txgbe_adapter *adapter = from_timer(adapter, t, service_timer);
+	unsigned long next_event_offset;
+
+	next_event_offset = HZ * 2;
+
+	/* Reset the timer */
+	mod_timer(&adapter->service_timer, next_event_offset + jiffies);
+
+	txgbe_service_event_schedule(adapter);
+}
+
+/**
+ * txgbe_service_task - manages and runs subtasks
+ * @work: pointer to work_struct containing our data
+ **/
+static void txgbe_service_task(struct work_struct *work)
+{
+	struct txgbe_adapter *adapter = container_of(work,
+						     struct txgbe_adapter,
+						     service_task);
+
+	txgbe_service_event_complete(adapter);
+}
+
 static void txgbe_sync_mac_table(struct txgbe_adapter *adapter)
 {
 	struct txgbe_hw *hw = &adapter->hw;
@@ -190,6 +236,10 @@ static void txgbe_disable_device(struct txgbe_adapter *adapter)
 	struct net_device *netdev = adapter->netdev;
 	struct wx_hw *wxhw = &adapter->hw.wxhw;
 
+	/* signal that we are down to the interrupt handler */
+	if (test_and_set_bit(__TXGBE_DOWN, &adapter->state))
+		return; /* do nothing if already down */
+
 	wx_disable_pcie_master(wxhw);
 	/* disable receives */
 	wx_disable_rx(wxhw);
@@ -197,6 +247,8 @@ static void txgbe_disable_device(struct txgbe_adapter *adapter)
 	netif_carrier_off(netdev);
 	netif_tx_disable(netdev);
 
+	del_timer_sync(&adapter->service_timer);
+
 	if (wxhw->bus.func < 2)
 		wr32m(wxhw, TXGBE_MIS_PRB_CTL, TXGBE_MIS_PRB_CTL_LAN_UP(wxhw->bus.func), 0);
 	else
@@ -266,6 +318,8 @@ static int txgbe_sw_init(struct txgbe_adapter *adapter)
 		return -ENOMEM;
 	}
 
+	set_bit(__TXGBE_DOWN, &adapter->state);
+
 	return 0;
 }
 
@@ -336,7 +390,8 @@ static void txgbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)
 
 	wx_control_hw(wxhw, false);
 
-	pci_disable_device(pdev);
+	if (!test_and_set_bit(__TXGBE_DISABLED, &adapter->state))
+		pci_disable_device(pdev);
 }
 
 static void txgbe_shutdown(struct pci_dev *pdev)
@@ -410,6 +465,7 @@ static int txgbe_probe(struct pci_dev *pdev,
 	struct txgbe_hw *hw = NULL;
 	struct wx_hw *wxhw = NULL;
 	struct net_device *netdev;
+	bool disable_dev = false;
 	int err, expected_gts;
 
 	u16 eeprom_verh = 0, eeprom_verl = 0, offset = 0;
@@ -468,6 +524,7 @@ static int txgbe_probe(struct pci_dev *pdev,
 	}
 
 	netdev->netdev_ops = &txgbe_netdev_ops;
+	netdev->watchdog_timeo = 5 * HZ;
 
 	/* setup the private structure */
 	err = txgbe_sw_init(adapter);
@@ -518,6 +575,11 @@ static int txgbe_probe(struct pci_dev *pdev,
 	eth_hw_addr_set(netdev, wxhw->mac.perm_addr);
 	txgbe_mac_set_default_filter(adapter, wxhw->mac.perm_addr);
 
+	timer_setup(&adapter->service_timer, txgbe_service_timer, 0);
+	INIT_WORK(&adapter->service_task, txgbe_service_task);
+	set_bit(__TXGBE_SERVICE_INITED, &adapter->state);
+	clear_bit(__TXGBE_SERVICE_SCHED, &adapter->state);
+
 	/* Save off EEPROM version number and Option Rom version which
 	 * together make a unique identify for the eeprom
 	 */
@@ -599,11 +661,13 @@ static int txgbe_probe(struct pci_dev *pdev,
 err_free_mac_table:
 	kfree(adapter->mac_table);
 err_pci_release_regions:
+	disable_dev = !test_and_set_bit(__TXGBE_DISABLED, &adapter->state);
 	pci_disable_pcie_error_reporting(pdev);
 	pci_release_selected_regions(pdev,
 				     pci_select_bars(pdev, IORESOURCE_MEM));
 err_pci_disable_dev:
-	pci_disable_device(pdev);
+	if (!adapter || disable_dev)
+		pci_disable_device(pdev);
 	return err;
 }
 
@@ -620,18 +684,25 @@ static void txgbe_remove(struct pci_dev *pdev)
 {
 	struct txgbe_adapter *adapter = pci_get_drvdata(pdev);
 	struct net_device *netdev;
+	bool disable_dev;
 
 	netdev = adapter->netdev;
+
+	set_bit(__TXGBE_REMOVING, &adapter->state);
+	cancel_work_sync(&adapter->service_task);
+
 	unregister_netdev(netdev);
 
 	pci_release_selected_regions(pdev,
 				     pci_select_bars(pdev, IORESOURCE_MEM));
 
 	kfree(adapter->mac_table);
+	disable_dev = !test_and_set_bit(__TXGBE_DISABLED, &adapter->state);
 
 	pci_disable_pcie_error_reporting(pdev);
 
-	pci_disable_device(pdev);
+	if (disable_dev)
+		pci_disable_device(pdev);
 }
 
 static struct pci_driver txgbe_driver = {
@@ -642,7 +713,41 @@ static struct pci_driver txgbe_driver = {
 	.shutdown = txgbe_shutdown,
 };
 
-module_pci_driver(txgbe_driver);
+/**
+ * txgbe_init_module - Driver Registration Routine
+ *
+ * txgbe_init_module is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ **/
+static int __init txgbe_init_module(void)
+{
+	int ret;
+
+	txgbe_wq = create_singlethread_workqueue(txgbe_driver_name);
+	if (!txgbe_wq) {
+		pr_err("%s: Failed to create workqueue\n", txgbe_driver_name);
+		return -ENOMEM;
+	}
+
+	ret = pci_register_driver(&txgbe_driver);
+	return ret;
+}
+
+module_init(txgbe_init_module);
+
+/**
+ * txgbe_exit_module - Driver Exit Cleanup Routine
+ *
+ * txgbe_exit_module is called just before the driver is removed
+ * from memory.
+ **/
+static void __exit txgbe_exit_module(void)
+{
+	pci_unregister_driver(&txgbe_driver);
+	destroy_workqueue(txgbe_wq);
+}
+
+module_exit(txgbe_exit_module);
 
 MODULE_DEVICE_TABLE(pci, txgbe_pci_tbl);
 MODULE_AUTHOR("Beijing WangXun Technology Co., Ltd, <software@trustnetic.com>");
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH net-next 3/5] net: txgbe: Support to setup link
  2022-11-08 11:19 [PATCH net-next 0/5] net: WangXun ethernet drivers Mengyuan Lou
  2022-11-08 11:19 ` [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module Mengyuan Lou
  2022-11-08 11:19 ` [PATCH net-next 2/5] net: txgbe: Initialize service task Mengyuan Lou
@ 2022-11-08 11:19 ` Mengyuan Lou
  2022-11-08 21:03   ` Andrew Lunn
  2022-11-14 15:48   ` Alexander Lobakin
  2022-11-08 11:19 ` [PATCH net-next 4/5] net: ngbe: Initialize phy information Mengyuan Lou
  2022-11-08 11:19 ` [PATCH net-next 5/5] net: ngbe: Initialize service task Mengyuan Lou
  4 siblings, 2 replies; 25+ messages in thread
From: Mengyuan Lou @ 2022-11-08 11:19 UTC (permalink / raw)
  To: netdev; +Cc: jiawenwu

From: Jiawen Wu <jiawenwu@trustnetic.com>

Get link capabilities, setup MAC and PHY link, and support to enable
or disable Tx laser.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
 .../device_drivers/ethernet/wangxun/txgbe.rst |    5 +
 drivers/net/ethernet/wangxun/libwx/wx_type.h  |    6 +
 drivers/net/ethernet/wangxun/txgbe/txgbe.h    |   16 +
 drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c | 1171 ++++++++++++++++-
 drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h |    9 +
 .../net/ethernet/wangxun/txgbe/txgbe_main.c   |  335 ++++-
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  103 ++
 7 files changed, 1640 insertions(+), 5 deletions(-)

diff --git a/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst b/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
index 3cb9549fb7b0..a41acd88bfb6 100644
--- a/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
+++ b/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
@@ -50,6 +50,11 @@ The following is a list of 3rd party SFP+ modules that have been tested and veri
 | WTD      | SFP+                 | RTXM228-551          |
 +----------+----------------------+----------------------+
 
+Laser turns off for SFP+ when ifconfig ethX down
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"ifconfig ethX down" turns off the laser for SFP+ fiber adapters.
+"ifconfig ethX up" turns on the laser.
+
 
 Support
 =======
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index c95cda53bf67..a5de626608d0 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -22,6 +22,7 @@
 /* chip control Registers */
 #define WX_MIS_PWR                   0x10000
 #define WX_MIS_RST                   0x1000C
+#define WX_MIS_RST_LAN_ETH_MODE(_i)  BIT((_i) + 29)
 #define WX_MIS_RST_LAN_RST(_i)       BIT((_i) + 1)
 #define WX_MIS_RST_SW_RST            BIT(0)
 #define WX_MIS_ST                    0x10028
@@ -63,6 +64,7 @@
 /************************* Port Registers ************************************/
 /* port cfg Registers */
 #define WX_CFG_PORT_CTL              0x14400
+#define WX_CFG_PORT_CTL_PFRSTD       BIT(14) /* Phy Function Reset Done */
 #define WX_CFG_PORT_CTL_DRV_LOAD     BIT(3)
 
 /*********************** Transmit DMA registers **************************/
@@ -134,11 +136,15 @@
 /************************************* ETH MAC *****************************/
 #define WX_MAC_TX_CFG                0x11000
 #define WX_MAC_TX_CFG_TE             BIT(0)
+#define WX_MAC_TX_CFG_SPEED_MASK     (0x3 << 29)
+#define WX_MAC_TX_CFG_SPEED_10G      (0x0 << 29)
+#define WX_MAC_TX_CFG_SPEED_1G       (0x3 << 29)
 #define WX_MAC_RX_CFG                0x11004
 #define WX_MAC_RX_CFG_RE             BIT(0)
 #define WX_MAC_RX_CFG_JE             BIT(8)
 #define WX_MAC_PKT_FLT               0x11008
 #define WX_MAC_PKT_FLT_PR            BIT(0) /* promiscuous mode */
+#define WX_MAC_WDG_TIMEOUT           0x1100C
 #define WX_MAC_RX_FLOW_CTRL          0x11090
 #define WX_MAC_RX_FLOW_CTRL_RFE      BIT(0) /* receive fc enable */
 #define WX_MMC_CONTROL               0x11800
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe.h b/drivers/net/ethernet/wangxun/txgbe/txgbe.h
index fb8fd413b755..021c348a2784 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe.h
@@ -33,8 +33,17 @@ enum txgbe_state_t {
 	__TXGBE_REMOVING,
 	__TXGBE_SERVICE_SCHED,
 	__TXGBE_SERVICE_INITED,
+	__TXGBE_IN_SFP_INIT,
 };
 
+#define TXGBE_TRY_LINK_TIMEOUT  (4 * HZ)       /* trying for four seconds */
+#define TXGBE_SFP_POLL_JIFFIES  (2 * HZ)       /* SFP poll every 2 seconds */
+
+/* txgbe_adapter.flag */
+#define TXGBE_FLAG_NEED_LINK_UPDATE             BIT(0)
+#define TXGBE_FLAG_NEED_LINK_CONFIG             BIT(1)
+#define TXGBE_FLAG_SFP_NEEDS_RESET              BIT(2)
+
 /* board specific private data structure */
 struct txgbe_adapter {
 	u8 __iomem *io_addr;    /* Mainly for iounmap use */
@@ -46,6 +55,13 @@ struct txgbe_adapter {
 	struct timer_list service_timer;
 	struct work_struct service_task;
 
+	u32 flags;
+
+	bool link_up;
+	u32 link_speed;
+	unsigned long sfp_poll_time;
+	unsigned long link_check_timeout;
+
 	/* structs defined in txgbe_type.h */
 	struct txgbe_hw hw;
 	u16 msg_enable;
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
index 77a44e48fc9e..2fcc097854dc 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
@@ -3,6 +3,7 @@
 
 #include <linux/etherdevice.h>
 #include <linux/if_ether.h>
+#include <linux/ethtool.h>
 #include <linux/string.h>
 #include <linux/iopoll.h>
 #include <linux/types.h>
@@ -29,6 +30,36 @@ static u32 txgbe_rd32_epcs(struct txgbe_hw *hw, u32 addr)
 	return rd32(wxhw, offset);
 }
 
+static void txgbe_wr32_ephy(struct txgbe_hw *hw, u32 addr, u32 data)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 offset;
+
+	/* Set the LAN port indicator to offset[1] */
+	/* 1st, write the offset to IDA_ADDR register */
+	offset = TXGBE_ETHPHY_IDA_ADDR;
+	wr32(wxhw, offset, addr);
+
+	/* 2nd, read the data from IDA_DATA register */
+	offset = TXGBE_ETHPHY_IDA_DATA;
+	wr32(wxhw, offset, data);
+}
+
+static void txgbe_wr32_epcs(struct txgbe_hw *hw, u32 addr, u32 data)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 offset;
+
+	/* Set the LAN port indicator to offset[1] */
+	/* 1st, write the offset to IDA_ADDR register */
+	offset = TXGBE_XPCS_IDA_ADDR;
+	wr32(wxhw, offset, addr);
+
+	/* 2nd, read the data from IDA_DATA register */
+	offset = TXGBE_XPCS_IDA_DATA;
+	wr32(wxhw, offset, data);
+}
+
 static void txgbe_init_i2c(struct txgbe_hw *hw)
 {
 	struct wx_hw *wxhw = &hw->wxhw;
@@ -152,7 +183,7 @@ static int txgbe_read_i2c_eeprom(struct txgbe_hw *hw, u8 byte_offset,
  *
  *  Searches for and identifies the SFP module and assigns appropriate PHY type.
  **/
-static int txgbe_identify_sfp_module(struct txgbe_hw *hw)
+int txgbe_identify_sfp_module(struct txgbe_hw *hw)
 {
 	u8 oui_bytes[3] = {0, 0, 0};
 	u8 comp_codes_10g = 0;
@@ -383,6 +414,1102 @@ static int txgbe_init_phy(struct txgbe_hw *hw)
 	return ret_val;
 }
 
+/**
+ *  txgbe_set_hard_rate_select_speed - Set module link speed
+ *  @hw: pointer to hardware structure
+ *  @speed: link speed to set
+ *
+ *  Set module link speed via RS0/RS1 rate select pins.
+ */
+static void txgbe_set_hard_rate_select_speed(struct txgbe_hw *hw, u32 speed)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 esdp_reg;
+
+	esdp_reg = rd32(wxhw, TXGBE_GPIO_DR);
+
+	switch (speed) {
+	case SPEED_10000:
+		esdp_reg |= TXGBE_GPIO_DR_5 | TXGBE_GPIO_DR_4;
+		break;
+	case SPEED_1000:
+		esdp_reg &= ~(TXGBE_GPIO_DR_5 | TXGBE_GPIO_DR_4);
+		break;
+	default:
+		wx_err(wxhw, "Invalid fixed module speed\n");
+		return;
+	}
+
+	wr32(wxhw, TXGBE_GPIO_DDR,
+	     TXGBE_GPIO_DDR_5 | TXGBE_GPIO_DDR_4 |
+	     TXGBE_GPIO_DDR_1 | TXGBE_GPIO_DDR_0);
+
+	wr32(wxhw, TXGBE_GPIO_DR, esdp_reg);
+
+	WX_WRITE_FLUSH(wxhw);
+}
+
+static void txgbe_set_sgmii_an37_ability(struct txgbe_hw *hw)
+{
+	u16 sub_dev_id = hw->wxhw.subsystem_device_id & TXGBE_DEV_MASK;
+	u32 value;
+
+	txgbe_wr32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1, 0x3002);
+	/* for sgmii + external phy, set to 0x0105 (phy sgmii mode) */
+	/* for sgmii direct link, set to 0x010c (mac sgmii mode) */
+	if (sub_dev_id == TXGBE_ID_MAC_SGMII ||
+	    hw->phy.media_type == txgbe_media_type_fiber) {
+		txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_AN_CTL, 0x010c);
+	} else if (sub_dev_id == TXGBE_ID_SGMII ||
+		   sub_dev_id == TXGBE_ID_XAUI) {
+		txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_AN_CTL, 0x0105);
+	}
+	txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_DIGI_CTL, TXGBE_SR_MII_MMD_DIGI_CTL_AS);
+	value = txgbe_rd32_epcs(hw, TXGBE_SR_MII_MMD_CTL);
+	value |= TXGBE_SR_MII_MMD_CTL_AN_EN | TXGBE_SR_MII_MMD_CTL_RESTART_AN;
+	txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_CTL, value);
+}
+
+static void txgbe_set_link_to_kr(struct txgbe_hw *hw, bool autoneg)
+{
+	int status = 0;
+	u32 value;
+
+	/* 1. Wait xpcs power-up good */
+	status = read_poll_timeout(txgbe_rd32_epcs, value,
+				   (value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_M) ==
+				   TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_PG,
+				   10000, 1000000, false,
+				   hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST);
+	if (status < 0) {
+		wx_err(&hw->wxhw, "xpcs power-up timeout.\n");
+		return;
+	}
+
+	wx_dbg(&hw->wxhw, "It is set to kr.\n");
+
+	txgbe_wr32_epcs(hw, TXGBE_VR_AN_INTR_MSK,
+			TXGBE_VR_AN_INTR_MSK_AN_PG_RCV_IE |
+			TXGBE_VR_AN_INTR_MSK_AN_INC_LINK_IE |
+			TXGBE_VR_AN_INTR_MSK_AN_INT_CMPLT_IE);
+
+	/* 2. Disable xpcs AN-73 */
+	txgbe_wr32_epcs(hw, TXGBE_SR_AN_MMD_CTL,
+			TXGBE_SR_AN_MMD_CTL_EXT_NP_CTL | TXGBE_SR_AN_MMD_CTL_ENABLE);
+	txgbe_wr32_epcs(hw, TXGBE_VR_AN_KR_MODE_CL, TXGBE_VR_AN_KR_MODE_CL_PDET_EN);
+
+	/* 3. Set VR_XS_PMA_Gen5_12G_MPLLA_CTRL3 Register */
+	/* Bit[10:0](MPLLA_BANDWIDTH) = 11'd123 (default: 11'd16) */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL3, 0x7B);
+
+	/* 4. Set VR_XS_PMA_Gen5_12G_MISC_CTRL0 Register */
+	/* Bit[12:8](RX_VREF_CTRL) = 5'hF (default: 5'h11) */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_MISC_CTL0, 0xCF00);
+
+	/* 5. Set VR_XS_PMA_Gen5_12G_RX_EQ_CTRL0 Register */
+	/* Bit[15:8](VGA1/2_GAIN_0) = 8'h77,
+	 * Bit[7:5](CTLE_POLE_0) = 3'h2
+	 * Bit[4:0](CTLE_BOOST_0) = 4'hA
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0, 0x774A);
+
+	/* 6. Set VR_MII_Gen5_12G_RX_GENCTRL3 Register */
+	/* Bit[2:0](LOS_TRSHLD_0) = 3'h4 (default: 3) */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL3, 0x0004);
+	/* 7. Initialize the mode by setting VR XS or PCS MMD Digital */
+	/* Control1 Register Bit[15](VR_RST) */
+	txgbe_wr32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1, 0xA000);
+	/* wait phy initialization done */
+	status = read_poll_timeout(txgbe_rd32_epcs, value,
+				   !(value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1_VR_RST),
+				   100000, 10000000, false,
+				   hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1);
+	if (status < 0)
+		wx_err(&hw->wxhw, "PHY initialization timeout.\n");
+}
+
+static void txgbe_set_link_to_kx4(struct txgbe_hw *hw, bool autoneg)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+	int status = 0;
+	u32 value, i;
+
+	/* check link status, if already set, skip setting it again */
+	if (hw->link_status == TXGBE_LINK_STATUS_KX4)
+		return;
+
+	wx_dbg(wxhw, "It is set to kx4.\n");
+
+	/* Wait xpcs power-up good */
+	status = read_poll_timeout(txgbe_rd32_epcs, value,
+				   (value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_M) ==
+				   TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_PG,
+				   10000, 1000000, false,
+				   hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST);
+	if (status < 0) {
+		wx_err(wxhw, "xpcs power-up timeout.\n");
+		return;
+	}
+
+	wr32m(wxhw, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0);
+
+	/* Disable xpcs AN-73 */
+	if (!autoneg)
+		txgbe_wr32_epcs(hw, TXGBE_SR_AN_MMD_CTL, 0);
+	else
+		txgbe_wr32_epcs(hw, TXGBE_SR_AN_MMD_CTL,
+				TXGBE_SR_AN_MMD_CTL_EXT_NP_CTL | TXGBE_SR_AN_MMD_CTL_ENABLE);
+
+	/* Disable PHY MPLLA for eth mode change(after ECO) */
+	txgbe_wr32_ephy(hw, TXGBE_SUP_DIG_MPLLA_OVRD_IN_0, 0x250A);
+	WX_WRITE_FLUSH(wxhw);
+	msleep(20);
+
+	/* Set the eth change_mode bit first in mis_rst register
+	 * for corresponding LAN port
+	 */
+	wr32(wxhw, WX_MIS_RST, WX_MIS_RST_LAN_ETH_MODE(wxhw->bus.func));
+
+	/* Set SR PCS Control2 Register Bits[1:0] = 2'b01
+	 * PCS_TYPE_SEL: non KR
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_SR_PCS_CTL2,
+			TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_X);
+	/* Set SR PMA MMD Control1 Register Bit[13] = 1'b1  SS13: 10G speed */
+	txgbe_wr32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1,
+			TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_10G);
+
+	value = (0xf5f0 & ~0x7F0) | (0x5 << 8) | (0x7 << 5) | 0xF0;
+	txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GENCTRL1, value);
+
+	if ((wxhw->subsystem_device_id & TXGBE_DEV_MASK) == TXGBE_ID_MAC_XAUI)
+		txgbe_wr32_epcs(hw, TXGBE_PHY_MISC_CTL0, 0xCF00);
+	else
+		txgbe_wr32_epcs(hw, TXGBE_PHY_MISC_CTL0, 0x4F00);
+
+	for (i = 0; i < 4; i++) {
+		if (i == 0)
+			value = (0x45 & ~0xFFFF) | (0x7 << 12) | (0x7 << 8) | 0x6;
+		else
+			value = (0xff06 & ~0xFFFF) | (0x7 << 12) | (0x7 << 8) | 0x6;
+
+		txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0 + i, value);
+	}
+
+	txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_ATT_LVL0, 0x0);
+
+	txgbe_wr32_epcs(hw, TXGBE_PHY_DFE_TAP_CTL0, 0x0);
+
+	value = (0x6db & ~0xFFF) | (0x1 << 9) | (0x1 << 6) | (0x1 << 3) | 0x1;
+	txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL3, value);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY MPLLA */
+	/* Control 0 Register Bit[7:0] = 8'd40  MPLLA_MULTIPLIER */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL0, 0x28);
+
+	/* Set VR XS, PMA or MII Synopsys Enterprise Gen5 12G PHY MPLLA */
+	/* Control 3 Register Bit[10:0] = 11'd86  MPLLA_BANDWIDTH */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL3, 0x56);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO */
+	/* Calibration Load 0 Register  Bit[12:0] = 13'd1360 VCO_LD_VAL_0 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD0, 0x550);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO */
+	/* Calibration Load 1 Register  Bit[12:0] = 13'd1360 VCO_LD_VAL_1 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD1, 0x550);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO */
+	/* Calibration Load 2 Register  Bit[12:0] = 13'd1360 VCO_LD_VAL_2 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD2, 0x550);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO */
+	/* Calibration Load 3 Register  Bit[12:0] = 13'd1360 VCO_LD_VAL_3 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD3, 0x550);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO */
+	/* Calibration Reference 0 Register Bit[5:0] = 6'd34 VCO_REF_LD_0/1 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_REF0, 0x2222);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO */
+	/* Calibration Reference 1 Register Bit[5:0] = 6'd34 VCO_REF_LD_2/3 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_REF1, 0x2222);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY AFE-DFE */
+	/* Enable Register Bit[7:0] = 8'd0  AFE_EN_0/3_1, DFE_EN_0/3_1 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE, 0x0);
+
+	/* Set  VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx */
+	/* Equalization Control 4 Register Bit[3:0] = 4'd0 CONT_ADAPT_0/3_1 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL, 0x00F0);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Tx Rate */
+	/* Control Register Bit[14:12], Bit[10:8], Bit[6:4], Bit[2:0],
+	 * all rates to 3'b010  TX0/1/2/3_RATE
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_TX_RATE_CTL, 0x2222);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx Rate */
+	/* Control Register Bit[13:12], Bit[9:8], Bit[5:4], Bit[1:0],
+	 * all rates to 2'b10  RX0/1/2/3_RATE
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_RX_RATE_CTL, 0x2222);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Tx General */
+	/* Control 2 Register Bit[15:8] = 2'b01  TX0/1/2/3_WIDTH: 10bits */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GEN_CTL2, 0x5500);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx General */
+	/* Control 2 Register Bit[15:8] = 2'b01  RX0/1/2/3_WIDTH: 10bits */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL2, 0x5500);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY MPLLA Control
+	 * 2 Register Bit[10:8] = 3'b010
+	 * MPLLA_DIV16P5_CLK_EN=0, MPLLA_DIV10_CLK_EN=1, MPLLA_DIV8_CLK_EN=0
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL2, 0x200);
+
+	txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_CTL, 0x0);
+	txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_AN_CTL, 0x0);
+	txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_DIGI_CTL, 0x0);
+
+	/* Initialize the mode by setting VR XS or PCS MMD Digital Control1
+	 * Register Bit[15](VR_RST)
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1, 0xA000);
+
+	/* wait phy initialization done */
+	status = read_poll_timeout(txgbe_rd32_epcs, value,
+				   !(value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1_VR_RST),
+				   100000, 10000000, false,
+				   hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1);
+	if (status < 0) {
+		wx_err(wxhw, "PHY initialization timeout.\n");
+		return;
+	}
+
+	/* if success, set link status */
+	hw->link_status = TXGBE_LINK_STATUS_KX4;
+}
+
+static void txgbe_set_link_to_kx(struct txgbe_hw *hw, u32 speed, bool autoneg)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+	int status = 0;
+	u32 wdata = 0;
+	u32 value, i;
+
+	/* check link status, if already set, skip setting it again */
+	if (hw->link_status == TXGBE_LINK_STATUS_KX)
+		return;
+
+	wx_dbg(wxhw, "It is set to kx. speed =0x%x\n", speed);
+
+	/* Wait xpcs power-up good */
+	status = read_poll_timeout(txgbe_rd32_epcs, value,
+				   (value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_M) ==
+				   TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_PG,
+				   10000, 1000000, false,
+				   hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST);
+	if (status < 0) {
+		wx_err(wxhw, "xpcs power-up timeout.\n");
+		return;
+	}
+
+	wr32m(wxhw, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0);
+
+	/* Disable xpcs AN-73 */
+	if (!autoneg)
+		txgbe_wr32_epcs(hw, TXGBE_SR_AN_MMD_CTL, 0);
+	else
+		txgbe_wr32_epcs(hw, TXGBE_SR_AN_MMD_CTL,
+				TXGBE_SR_AN_MMD_CTL_EXT_NP_CTL | TXGBE_SR_AN_MMD_CTL_ENABLE);
+
+	/* Disable PHY MPLLA for eth mode change(after ECO) */
+	txgbe_wr32_ephy(hw, TXGBE_SUP_DIG_MPLLA_OVRD_IN_0, 0x240A);
+	WX_WRITE_FLUSH(wxhw);
+	msleep(20);
+
+	/* Set the eth change_mode bit first in mis_rst register */
+	/* for corresponding LAN port */
+	wr32(wxhw, WX_MIS_RST, WX_MIS_RST_LAN_ETH_MODE(wxhw->bus.func));
+
+	/* Set SR PCS Control2 Register Bits[1:0] = 2'b01
+	 * PCS_TYPE_SEL: non KR
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_SR_PCS_CTL2,
+			TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_X);
+
+	/* Set SR PMA MMD Control1 Register Bit[13] = 1'b0 SS13: 1G speed */
+	txgbe_wr32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1,
+			TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_1G);
+
+	/* Set SR MII MMD Control Register to corresponding speed: {Bit[6],
+	 * Bit[13]}=[2'b00,2'b01,2'b10]->[10M,100M,1G]
+	 */
+	wdata = TXGBE_SR_MII_MMD_CTL_DUPLEX_MODE;
+	if (speed == SPEED_100)
+		wdata |= TXGBE_SR_MII_MMD_CTL_SGMII_100;
+	else if (speed == SPEED_1000)
+		wdata |= TXGBE_SR_MII_MMD_CTL_SGMII_1000;
+	else if (speed == SPEED_10)
+		wdata |= TXGBE_SR_MII_MMD_CTL_SGMII_10;
+	txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_CTL, wdata);
+
+	value = (0xf5f0 & ~0x710) | (0x5 << 8) | 0x10;
+	txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GENCTRL1, value);
+
+	txgbe_wr32_epcs(hw, TXGBE_PHY_MISC_CTL0, 0xCF00);
+
+	for (i = 0; i < 4; i++) {
+		if (i != 0)
+			value = 0xff06;
+		else
+			value = (0x45 & ~0xFFFF) | (0x7 << 12) |
+				(0x7 << 8) | 0x6;
+
+		txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0 + i, value);
+	}
+
+	txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_ATT_LVL0, 0x0);
+
+	txgbe_wr32_epcs(hw, TXGBE_PHY_DFE_TAP_CTL0, 0x0);
+
+	value = (0x6db & ~0x7) | 0x4;
+	txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL3, value);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY MPLLA Control
+	 * 0 Register Bit[7:0] = 8'd32  MPLLA_MULTIPLIER
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL0, 0x20);
+
+	/* Set VR XS, PMA or MII Synopsys Enterprise Gen5 12G PHY MPLLA Control
+	 * 3 Register Bit[10:0] = 11'd70  MPLLA_BANDWIDTH
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL3, 0x56);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO
+	 * Calibration Load 0 Register  Bit[12:0] = 13'd1344  VCO_LD_VAL_0
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD0, 0x540);
+
+	txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD1, 0x549);
+	txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD2, 0x549);
+	txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD3, 0x549);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO
+	 * Calibration Reference 0 Register Bit[5:0] = 6'd42  VCO_REF_LD_0
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_REF0, 0x2A);
+
+	txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_REF1, 0x2929);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY AFE-DFE
+	 * Enable Register Bit[4], Bit[0] = 1'b0  AFE_EN_0, DFE_EN_0
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE, 0x0);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx
+	 * Equalization Control 4 Register Bit[0] = 1'b0  CONT_ADAPT_0
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL, 0x0010);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Tx Rate
+	 * Control Register Bit[2:0] = 3'b011  TX0_RATE
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_TX_RATE_CTL, 0x3);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx Rate
+	 * Control Register Bit[2:0] = 3'b011 RX0_RATE
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_RX_RATE_CTL, 0x3);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Tx General
+	 * Control 2 Register Bit[9:8] = 2'b01  TX0_WIDTH: 10bits
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GEN_CTL2, 0x100);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx General
+	 * Control 2 Register Bit[9:8] = 2'b01  RX0_WIDTH: 10bits
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL2, 0x100);
+
+	/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY MPLLA Control
+	 * 2 Register Bit[10:8] = 3'b010	MPLLA_DIV16P5_CLK_EN=0,
+	 * MPLLA_DIV10_CLK_EN=1, MPLLA_DIV8_CLK_EN=0
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL2, 0x200);
+
+	/* VR MII MMD AN Control Register Bit[8] = 1'b1 MII_CTRL */
+	/* Set to 8bit MII (required in 10M/100M SGMII) */
+	txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_AN_CTL, 0x100);
+
+	/* Initialize the mode by setting VR XS or PCS MMD Digital Control1
+	 * Register Bit[15](VR_RST)
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1, 0xA000);
+	/* wait phy initialization done */
+	status = read_poll_timeout(txgbe_rd32_epcs, value,
+				   !(value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1_VR_RST),
+				   100000, 10000000, false,
+				   hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1);
+	if (status < 0) {
+		wx_err(wxhw, "PHY initialization timeout.\n");
+		return;
+	}
+
+	/* if success, set link status */
+	hw->link_status = TXGBE_LINK_STATUS_KX;
+}
+
+static void txgbe_set_link_to_sfi(struct txgbe_hw *hw, u32 speed)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+	int status = 0;
+	u32 value = 0;
+
+	/* Set the module link speed */
+	txgbe_set_hard_rate_select_speed(hw, speed);
+
+	/* Wait xpcs power-up good */
+	status = read_poll_timeout(txgbe_rd32_epcs, value,
+				   (value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_M) ==
+				   TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_PG,
+				   10000, 1000000, false,
+				   hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST);
+	if (status < 0) {
+		wx_err(wxhw, "xpcs power-up timeout.\n");
+		return;
+	}
+
+	wr32m(wxhw, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0);
+
+	/* Disable xpcs AN-73 */
+	txgbe_wr32_epcs(hw, TXGBE_SR_AN_MMD_CTL, 0x0);
+
+	/* Disable PHY MPLLA for eth mode change(after ECO) */
+	txgbe_wr32_ephy(hw, TXGBE_SUP_DIG_MPLLA_OVRD_IN_0, 0x243A);
+	WX_WRITE_FLUSH(wxhw);
+	msleep(20);
+
+	/* Set the eth change_mode bit first in mis_rst register
+	 * for corresponding LAN port
+	 */
+	wr32(wxhw, WX_MIS_RST, WX_MIS_RST_LAN_ETH_MODE(wxhw->bus.func));
+
+	if (speed == SPEED_10000) {
+		/* Set SR PCS Control2 Register Bits[1:0] = 2'b00
+		 * PCS_TYPE_SEL: KR
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_SR_PCS_CTL2, 0);
+		value = txgbe_rd32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1);
+		value |= TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_10G;
+		txgbe_wr32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1, value);
+		/* Set VR_XS_PMA_Gen5_12G_MPLLA_CTRL0 Register Bit[7:0] = 8'd33
+		 * MPLLA_MULTIPLIER
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL0, 0x0021);
+		/* Set VR_XS_PMA_Gen5_12G_MPLLA_CTRL3 Register
+		 * Bit[10:0](MPLLA_BANDWIDTH) = 11'd0
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL3, 0);
+		value = txgbe_rd32_epcs(hw, TXGBE_PHY_TX_GENCTRL1);
+		value = (value & ~0x700) | 0x500;
+		txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GENCTRL1, value);
+		/* Set VR_XS_PMA_Gen5_12G_MISC_CTRL0 Register
+		 * Bit[12:8](RX_VREF_CTRL) = 5'hF
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_MISC_CTL0, 0xCF00);
+		/* Set VR_XS_PMA_Gen5_12G_VCO_CAL_LD0 Register
+		 * Bit[12:0] = 13'd1353 VCO_LD_VAL_0
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD0, 0x0549);
+		/* Set VR_XS_PMA_Gen5_12G_VCO_CAL_REF0 Register Bit[5:0] = 6'd41
+		 * VCO_REF_LD_0
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_REF0, 0x0029);
+		/* Set VR_XS_PMA_Gen5_12G_TX_RATE_CTRL Register
+		 * Bit[2:0] = 3'b000 TX0_RATE
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_TX_RATE_CTL, 0);
+		/* Set VR_XS_PMA_Gen5_12G_RX_RATE_CTRL Register
+		 * Bit[2:0] = 3'b000 RX0_RATE
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_RX_RATE_CTL, 0);
+		/* Set VR_XS_PMA_Gen5_12G_TX_GENCTRL2 Register Bit[9:8] = 2'b11
+		 * TX0_WIDTH: 20bits
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GEN_CTL2, 0x0300);
+		/* Set VR_XS_PMA_Gen5_12G_RX_GENCTRL2 Register Bit[9:8] = 2'b11
+		 * RX0_WIDTH: 20bits
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL2, 0x0300);
+		/* Set VR_XS_PMA_Gen5_12G_MPLLA_CTRL2 Register
+		 * Bit[10:8] = 3'b110 MPLLA_DIV16P5_CLK_EN=1,
+		 * MPLLA_DIV10_CLK_EN=1, MPLLA_DIV8_CLK_EN=0
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL2, 0x0600);
+
+		if (hw->phy.sfp_type == txgbe_sfp_type_da_cu_core) {
+			/* Set VR_XS_PMA_Gen5_12G_RX_EQ_CTRL0 Register
+			 * Bit[15:8](VGA1/2_GAIN_0) = 8'h77, Bit[7:5]
+			 * (CTLE_POLE_0) = 3'h2, Bit[4:0](CTLE_BOOST_0) = 4'hF
+			 */
+			txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0, 0x774F);
+		} else {
+			/* Set VR_XS_PMA_Gen5_12G_RX_EQ_CTRL0 Register
+			 * Bit[15:8] (VGA1/2_GAIN_0) = 8'h00,
+			 * Bit[7:5](CTLE_POLE_0) = 3'h2,
+			 * Bit[4:0](CTLE_BOOST_0) = 4'hA
+			 */
+			value = txgbe_rd32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0);
+			value = (value & ~0xFFFF) | (2 << 5) | 0x05;
+			txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0, value);
+		}
+		value = txgbe_rd32_epcs(hw, TXGBE_PHY_RX_EQ_ATT_LVL0);
+		value &= ~0x7;
+		txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_ATT_LVL0, value);
+
+		if (hw->phy.sfp_type == txgbe_sfp_type_da_cu_core) {
+			/* Set VR_XS_PMA_Gen5_12G_DFE_TAP_CTRL0 Register
+			 * Bit[7:0](DFE_TAP1_0) = 8'd20
+			 */
+			txgbe_wr32_epcs(hw, TXGBE_PHY_DFE_TAP_CTL0, 0x0014);
+			value = txgbe_rd32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE);
+			value |= 0x11;
+			txgbe_wr32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE, value);
+		} else {
+			/* Set VR_XS_PMA_Gen5_12G_DFE_TAP_CTRL0 Register
+			 * Bit[7:0](DFE_TAP1_0) = 8'd20
+			 */
+			txgbe_wr32_epcs(hw, TXGBE_PHY_DFE_TAP_CTL0, 0xBE);
+			/* Set VR_MII_Gen5_12G_AFE_DFE_EN_CTRL Register
+			 * Bit[4](DFE_EN_0) = 1'b0, Bit[0](AFE_EN_0) = 1'b0
+			 */
+			value = txgbe_rd32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE);
+			value &= ~0x11;
+			txgbe_wr32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE, value);
+		}
+		value = txgbe_rd32_epcs(hw, TXGBE_PHY_RX_EQ_CTL);
+		value &= ~0x1;
+		txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL, value);
+	} else {
+		/* Set SR PCS Control2 Register Bits[1:0] = 2'b00
+		 * PCS_TYPE_SEL: KR
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_SR_PCS_CTL2, 0x1);
+		/* Set SR PMA MMD Control1 Register Bit[13] = 1'b0
+		 * SS13: 1G speed
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1, 0x0);
+		/* Set SR MII MMD Control Register to corresponding speed: */
+		txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_CTL, 0x0140);
+
+		value = txgbe_rd32_epcs(hw, TXGBE_PHY_TX_GENCTRL1);
+		value = (value & ~0x710) | 0x500;
+		txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GENCTRL1, value);
+		/* Set VR_XS_PMA_Gen5_12G_MISC_CTRL0 Register
+		 * Bit[12:8](RX_VREF_CTRL) = 5'hF
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_MISC_CTL0, 0xCF00);
+
+		if (hw->phy.sfp_type == txgbe_sfp_type_da_cu_core) {
+			txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0, 0x774F);
+		} else {
+			/* Set VR_XS_PMA_Gen5_12G_RX_EQ_CTRL0 Register
+			 * Bit[15:8] (VGA1/2_GAIN_0) = 8'h00,
+			 * Bit[7:5](CTLE_POLE_0) = 3'h2,
+			 * Bit[4:0](CTLE_BOOST_0) = 4'hA
+			 */
+			value = txgbe_rd32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0);
+			value = (value & ~0xFFFF) | 0x7706;
+			txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL0, value);
+		}
+		value = txgbe_rd32_epcs(hw, TXGBE_PHY_RX_EQ_ATT_LVL0);
+		value &= ~0x7;
+		txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_ATT_LVL0, value);
+		/* Set VR_XS_PMA_Gen5_12G_DFE_TAP_CTRL0 Register
+		 * Bit[7:0](DFE_TAP1_0) = 8'd00
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_DFE_TAP_CTL0, 0x0);
+		/* Set VR_XS_PMA_Gen5_12G_RX_GENCTRL3 Register
+		 * Bit[2:0] LOS_TRSHLD_0 = 4
+		 */
+		value = txgbe_rd32_epcs(hw, TXGBE_PHY_RX_GEN_CTL3);
+		value = (value & ~0x7) | 0x4;
+		txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL3, value);
+		/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY
+		 * MPLLA Control 0 Register Bit[7:0] = 8'd32  MPLLA_MULTIPLIER
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL0, 0x0020);
+		/* Set VR XS, PMA or MII Synopsys Enterprise Gen5 12G PHY MPLLA
+		 * Control 3 Register Bit[10:0] = 11'd70  MPLLA_BANDWIDTH
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL3, 0x0046);
+		/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO
+		 * Calibration Load 0 Register
+		 * Bit[12:0] = 13'd1344  VCO_LD_VAL_0
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_LD0, 0x0540);
+		/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY VCO
+		 * Calibration Reference 0 Register
+		 * Bit[5:0] = 6'd42 VCO_REF_LD_0
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_VCO_CAL_REF0, 0x002A);
+		/* Set VR XS, PMA, MII Synopsys Enterprise Gen5 12G PHY AFE-DFE
+		 * Enable Register Bit[4], Bit[0] = 1'b0  AFE_EN_0, DFE_EN_0
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_AFE_DFE_ENABLE, 0x0);
+		/* Set  VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY Rx
+		 * Equalization Control 4 Register Bit[0] = 1'b0  CONT_ADAPT_0
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_RX_EQ_CTL, 0x0010);
+		/* Set VR XS, PMA, MII Synopsys Enterprise Gen5 12G PHY Tx Rate
+		 * Control Register Bit[2:0] = 3'b011  TX0_RATE
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_TX_RATE_CTL, 0x0003);
+		/* Set VR XS, PMA, MII Synopsys Enterprise Gen5 12G PHY Rx Rate
+		 * Control Register Bit[2:0] = 3'b011
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_RX_RATE_CTL, 0x0003);
+		/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY
+		 * Tx General Control 2 Register
+		 * Bit[9:8] = 2'b01  TX0_WIDTH: 10bits
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_TX_GEN_CTL2, 0x0100);
+		/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY
+		 * Rx General Control 2 Register
+		 * Bit[9:8] = 2'b01  RX0_WIDTH: 10bits
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_RX_GEN_CTL2, 0x0100);
+		/* Set VR XS, PMA, or MII Synopsys Enterprise Gen5 12G PHY MPLLA
+		 * Control 2 Register Bit[10:8] = 3'b010 MPLLA_DIV16P5_CLK_EN=0,
+		 * MPLLA_DIV10_CLK_EN=1, MPLLA_DIV8_CLK_EN=0
+		 */
+		txgbe_wr32_epcs(hw, TXGBE_PHY_MPLLA_CTL2, 0x0200);
+		/* VR MII MMD AN Control Register Bit[8] = 1'b1 MII_CTRL */
+		txgbe_wr32_epcs(hw, TXGBE_SR_MII_MMD_AN_CTL, 0x0100);
+	}
+	/* Initialize the mode by setting VR XS or PCS MMD Digital Control1
+	 * Register Bit[15](VR_RST)
+	 */
+	txgbe_wr32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1, 0xA000);
+	/* wait phy initialization done */
+	status = read_poll_timeout(txgbe_rd32_epcs, value,
+				   !(value & TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1_VR_RST),
+				   100000, 10000000, false,
+				   hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1);
+	if (status < 0)
+		wx_err(wxhw, "PHY initialization timeout.\n");
+}
+
+/**
+ *  txgbe_check_mac_link - 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
+ **/
+void txgbe_check_mac_link(struct txgbe_hw *hw, u32 *speed,
+			  bool *link_up, bool link_up_wait_to_complete)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 links_reg = 0;
+	int ret;
+
+	*link_up = false;
+
+	if (link_up_wait_to_complete) {
+		ret = read_poll_timeout(rd32, links_reg,
+					links_reg & TXGBE_CFG_PORT_ST_LINK_UP,
+					10000, 10000 * 900, false,
+					wxhw, TXGBE_CFG_PORT_ST);
+		if (!ret)
+			*link_up = true;
+	} else {
+		links_reg = rd32(wxhw, TXGBE_CFG_PORT_ST);
+		if (links_reg & TXGBE_CFG_PORT_ST_LINK_UP)
+			*link_up = true;
+	}
+
+	if (*link_up) {
+		if (links_reg & TXGBE_CFG_PORT_ST_LINK_10G)
+			*speed = SPEED_10000;
+		else if (links_reg & TXGBE_CFG_PORT_ST_LINK_1G)
+			*speed = SPEED_1000;
+		else if (links_reg & TXGBE_CFG_PORT_ST_LINK_100M)
+			*speed = SPEED_100;
+		else
+			*speed = SPEED_10;
+	} else {
+		*speed = SPEED_UNKNOWN;
+	}
+}
+
+/**
+ *  txgbe_get_link_capabilities - Determines link capabilities
+ *  @hw: pointer to hardware structure
+ *  @speed: pointer to link speed
+ *  @autoneg: true when autoneg or autotry is enabled
+ **/
+int txgbe_get_link_capabilities(struct txgbe_hw *hw,
+				u32 *speed,
+				bool *autoneg)
+{
+	u16 sub_dev_id = hw->wxhw.subsystem_device_id & TXGBE_DEV_MASK;
+	u32 sr_pcs_ctl, sr_pma_mmd_ctl1, sr_an_mmd_ctl;
+	u32 sr_an_mmd_adv_reg2;
+
+	/* Check if 1G SFP module. */
+	if (hw->phy.sfp_type == txgbe_sfp_type_1g_cu_core ||
+	    hw->phy.sfp_type == txgbe_sfp_type_1g_lx_core ||
+	    hw->phy.sfp_type == txgbe_sfp_type_1g_sx_core) {
+		*speed = SPEED_1000;
+		*autoneg = false;
+	} else if (hw->phy.multispeed_fiber) {
+		*speed = SPEED_10000 | SPEED_1000;
+		*autoneg = true;
+	}
+	/* SFP */
+	else if (hw->phy.media_type == txgbe_media_type_fiber) {
+		*speed = SPEED_10000;
+		*autoneg = false;
+	}
+	/* SGMII */
+	else if (sub_dev_id == TXGBE_ID_SGMII) {
+		*speed = SPEED_1000 | SPEED_100 | SPEED_10;
+		*autoneg = false;
+		hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_1000BASE_T |
+				TXGBE_PHYSICAL_LAYER_100BASE_TX;
+	/* MAC XAUI */
+	} else if (sub_dev_id == TXGBE_ID_MAC_XAUI) {
+		*speed = SPEED_10000;
+		*autoneg = false;
+		hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_10GBASE_KX4;
+	/* MAC SGMII */
+	} else if (sub_dev_id == TXGBE_ID_MAC_SGMII) {
+		*speed = SPEED_1000;
+		*autoneg = false;
+		hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_1000BASE_KX;
+	} else { /* KR KX KX4 */
+		/* Determine link capabilities based on the stored value,
+		 * which represents EEPROM defaults.  If value has not
+		 * been stored, use the current register values.
+		 */
+		if (hw->phy.orig_link_settings_stored) {
+			sr_pcs_ctl = hw->phy.orig_sr_pcs_ctl2;
+			sr_pma_mmd_ctl1 = hw->phy.orig_sr_pma_mmd_ctl1;
+			sr_an_mmd_ctl = hw->phy.orig_sr_an_mmd_ctl;
+			sr_an_mmd_adv_reg2 = hw->phy.orig_sr_an_mmd_adv_reg2;
+		} else {
+			sr_pcs_ctl = txgbe_rd32_epcs(hw, TXGBE_SR_PCS_CTL2);
+			sr_pma_mmd_ctl1 = txgbe_rd32_epcs(hw, TXGBE_SR_PMA_MMD_CTL1);
+			sr_an_mmd_ctl = txgbe_rd32_epcs(hw, TXGBE_SR_AN_MMD_CTL);
+			sr_an_mmd_adv_reg2 = txgbe_rd32_epcs(hw, TXGBE_SR_AN_MMD_ADV_REG2);
+		}
+
+		if ((sr_pcs_ctl & TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_MASK) ==
+				TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_X &&
+		    (sr_pma_mmd_ctl1 & TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_MASK) ==
+				TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_1G &&
+		    (sr_an_mmd_ctl & TXGBE_SR_AN_MMD_CTL_ENABLE) == 0) {
+			/* 1G or KX - no backplane auto-negotiation */
+			*speed = SPEED_1000;
+			*autoneg = false;
+			hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_1000BASE_KX;
+		} else if ((sr_pcs_ctl & TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_MASK) ==
+				TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_X &&
+			   (sr_pma_mmd_ctl1 & TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_MASK) ==
+				TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_10G &&
+			   (sr_an_mmd_ctl & TXGBE_SR_AN_MMD_CTL_ENABLE) == 0) {
+			*speed = SPEED_10000;
+			*autoneg = false;
+			hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_10GBASE_KX4;
+		} else if ((sr_pcs_ctl & TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_MASK) ==
+				TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_R &&
+			   (sr_an_mmd_ctl & TXGBE_SR_AN_MMD_CTL_ENABLE) == 0) {
+			/* 10 GbE serial link (KR -no backplane auto-negotiation) */
+			*speed = SPEED_10000;
+			*autoneg = false;
+			hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_10GBASE_KR;
+		} else if (sr_an_mmd_ctl & TXGBE_SR_AN_MMD_CTL_ENABLE) {
+			/* KX/KX4/KR backplane auto-negotiation enable */
+			*speed = SPEED_UNKNOWN;
+			if (sr_an_mmd_adv_reg2 & TXGBE_SR_AN_MMD_ADV_REG2_BP_TYPE_KR)
+				*speed |= SPEED_10000;
+			if (sr_an_mmd_adv_reg2 & TXGBE_SR_AN_MMD_ADV_REG2_BP_TYPE_KX4)
+				*speed |= SPEED_10000;
+			if (sr_an_mmd_adv_reg2 & TXGBE_SR_AN_MMD_ADV_REG2_BP_TYPE_KX)
+				*speed |= SPEED_1000;
+			*autoneg = true;
+			hw->phy.link_mode = TXGBE_PHYSICAL_LAYER_10GBASE_KR |
+					TXGBE_PHYSICAL_LAYER_10GBASE_KX4 |
+					TXGBE_PHYSICAL_LAYER_1000BASE_KX;
+		} else {
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ *  txgbe_disable_tx_laser - Disable Tx laser
+ *  @hw: pointer to hardware structure
+ *
+ *  The base drivers may require better control over SFP+ module
+ *  PHY states.  This includes selectively shutting down the Tx
+ *  laser on the PHY, effectively halting physical link.
+ **/
+void txgbe_disable_tx_laser(struct txgbe_hw *hw)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 esdp_reg;
+
+	if (hw->phy.media_type != txgbe_media_type_fiber)
+		return;
+
+	/* Disable Tx laser; allow 100us to go dark per spec */
+	esdp_reg = rd32(wxhw, TXGBE_GPIO_DR);
+	esdp_reg |= TXGBE_GPIO_DR_1 | TXGBE_GPIO_DR_0;
+	wr32(wxhw, TXGBE_GPIO_DR, esdp_reg);
+	WX_WRITE_FLUSH(wxhw);
+	usleep_range(100, 200);
+}
+
+/**
+ *  txgbe_enable_tx_laser - Enable Tx laser
+ *  @hw: pointer to hardware structure
+ *
+ *  The base drivers may require better control over SFP+ module
+ *  PHY states.  This includes selectively turning on the Tx
+ *  laser on the PHY, effectively starting physical link.
+ **/
+void txgbe_enable_tx_laser(struct txgbe_hw *hw)
+{
+	struct wx_hw *wxhw = &hw->wxhw;
+
+	if (hw->phy.media_type != txgbe_media_type_fiber)
+		return;
+
+	/* Enable Tx laser; allow 100ms to light up */
+	wr32m(wxhw, TXGBE_GPIO_DR,
+	      TXGBE_GPIO_DR_0 | TXGBE_GPIO_DR_1, 0);
+	WX_WRITE_FLUSH(wxhw);
+	msleep(100);
+}
+
+/**
+ *  txgbe_flap_tx_laser - Flap Tx laser
+ *  @hw: pointer to hardware structure
+ *
+ *  When the driver changes the link speeds that it can support,
+ *  it sets autotry_restart to true to indicate that we need to
+ *  initiate a new autotry session with the link partner.  To do
+ *  so, we set the speed then disable and re-enable the Tx laser, to
+ *  alert the link partner that it also needs to restart autotry on its
+ *  end.  This is consistent with true clause 37 autoneg, which also
+ *  involves a loss of signal.
+ **/
+static void txgbe_flap_tx_laser(struct txgbe_hw *hw)
+{
+	if (hw->phy.media_type != txgbe_media_type_fiber)
+		return;
+
+	if (hw->phy.autotry_restart) {
+		txgbe_disable_tx_laser(hw);
+		txgbe_enable_tx_laser(hw);
+		hw->phy.autotry_restart = false;
+	}
+}
+
+/**
+ *  txgbe_setup_mac_link - Set MAC link speed
+ *  @hw: pointer to hardware structure
+ *  @speed: new link speed
+ *
+ *  Set the link speed.
+ **/
+static int txgbe_setup_mac_link(struct txgbe_hw *hw, u32 speed)
+{
+	u16 sub_dev_id = hw->wxhw.subsystem_device_id & TXGBE_DEV_MASK;
+	u32 link_capabilities = SPEED_UNKNOWN;
+	u32 link_speed = SPEED_UNKNOWN;
+	bool autoneg = false;
+	bool link_up = false;
+	int status = 0;
+
+	/* Check to see if speed passed in is supported. */
+	status = txgbe_get_link_capabilities(hw, &link_capabilities, &autoneg);
+	if (status)
+		return status;
+
+	speed &= link_capabilities;
+	if (speed == SPEED_UNKNOWN)
+		return -EINVAL;
+
+	if (!(sub_dev_id == TXGBE_ID_KR_KX_KX4 ||
+	      sub_dev_id == TXGBE_ID_MAC_XAUI ||
+	      sub_dev_id == TXGBE_ID_MAC_SGMII)) {
+		txgbe_check_mac_link(hw, &link_speed, &link_up, false);
+		if (link_speed == speed && link_up)
+			return 0;
+	}
+
+	if (sub_dev_id == TXGBE_ID_KR_KX_KX4) {
+		if (!autoneg) {
+			switch (hw->phy.link_mode) {
+			case TXGBE_PHYSICAL_LAYER_10GBASE_KR:
+				txgbe_set_link_to_kr(hw, autoneg);
+				break;
+			case TXGBE_PHYSICAL_LAYER_10GBASE_KX4:
+				txgbe_set_link_to_kx4(hw, autoneg);
+				break;
+			case TXGBE_PHYSICAL_LAYER_1000BASE_KX:
+				txgbe_set_link_to_kx(hw, speed, autoneg);
+				break;
+			default:
+				return -ENODEV;
+			}
+		} else {
+			txgbe_set_link_to_kr(hw, autoneg);
+		}
+	} else if (sub_dev_id == TXGBE_ID_XAUI ||
+		   sub_dev_id == TXGBE_ID_MAC_XAUI ||
+		   sub_dev_id == TXGBE_ID_SGMII ||
+		   sub_dev_id == TXGBE_ID_MAC_SGMII) {
+		if (speed == SPEED_10000) {
+			txgbe_set_link_to_kx4(hw, 0);
+		} else {
+			txgbe_set_link_to_kx(hw, speed, 0);
+			txgbe_set_sgmii_an37_ability(hw);
+		}
+	} else if (hw->phy.media_type == txgbe_media_type_fiber) {
+		txgbe_set_link_to_sfi(hw, speed);
+		if (speed == SPEED_1000)
+			txgbe_set_sgmii_an37_ability(hw);
+	}
+
+	return 0;
+}
+
+/**
+ *  txgbe_setup_link - Set MAC link speed
+ *  @hw: pointer to hardware structure
+ *  @speed: new link speed
+ *  @autoneg_wait_to_complete: true when waiting for completion is needed
+ *
+ *  Set the link speed in the MAC and/or PHY register and restarts link.
+ **/
+int txgbe_setup_link(struct txgbe_hw *hw, u32 speed,
+		     bool autoneg_wait_to_complete)
+{
+	u32 highest_link_speed = SPEED_UNKNOWN;
+	u32 link_speed = SPEED_UNKNOWN;
+	bool autoneg, link_up = false;
+	u32 speedcnt = 0;
+	int status = 0;
+	u32 i = 0;
+
+	if (!hw->phy.multispeed_fiber)
+		return txgbe_setup_mac_link(hw, speed);
+
+	/* Mask off requested but non-supported speeds */
+	status = txgbe_get_link_capabilities(hw, &link_speed, &autoneg);
+	if (status != 0)
+		return status;
+
+	speed &= link_speed;
+
+	/* Try each speed one by one, highest priority first.  We do this in
+	 * software because 10Gb fiber doesn't support speed autonegotiation.
+	 */
+	if (speed & SPEED_10000) {
+		speedcnt++;
+		highest_link_speed = SPEED_10000;
+
+		/* If we already have link at this speed, just jump out */
+		txgbe_check_mac_link(hw, &link_speed, &link_up, false);
+		if (link_speed == SPEED_10000 && link_up)
+			goto out;
+
+		/* Allow module to change analog characteristics (1G->10G) */
+		msleep(40);
+
+		status = txgbe_setup_mac_link(hw, SPEED_10000);
+		if (status != 0)
+			return status;
+
+		/* Flap the Tx laser if it has not already been done */
+		txgbe_flap_tx_laser(hw);
+
+		/* Wait for the controller to acquire link.  Per IEEE 802.3ap,
+		 * Section 73.10.2, we may have to wait up to 500ms if KR is
+		 * attempted.  sapphire uses the same timing for 10g SFI.
+		 */
+		for (i = 0; i < 5; i++) {
+			/* Wait for the link partner to also set speed */
+			msleep(100);
+
+			/* If we have link, just jump out */
+			txgbe_check_mac_link(hw, &link_speed, &link_up, false);
+			if (link_up)
+				goto out;
+		}
+	}
+
+	if (speed & SPEED_1000) {
+		speedcnt++;
+		if (highest_link_speed == SPEED_UNKNOWN)
+			highest_link_speed = SPEED_1000;
+
+		/* If we already have link at this speed, just jump out */
+		txgbe_check_mac_link(hw, &link_speed, &link_up, false);
+		if (link_speed == SPEED_1000 && link_up)
+			goto out;
+
+		/* Allow module to change analog characteristics (10G->1G) */
+		msleep(40);
+
+		status = txgbe_setup_mac_link(hw, SPEED_1000);
+		if (status != 0)
+			return status;
+
+		/* Flap the Tx laser if it has not already been done */
+		txgbe_flap_tx_laser(hw);
+
+		/* Wait for the link partner to also set speed */
+		msleep(100);
+
+		/* If we have link, just jump out */
+		txgbe_check_mac_link(hw, &link_speed, &link_up, false);
+		if (link_up)
+			goto out;
+	}
+
+	/* We didn't get link.  Configure back to the highest speed we tried,
+	 * (if there was more than one).  We call ourselves back with just the
+	 * single highest speed that the user requested.
+	 */
+	if (speedcnt > 1)
+		status = txgbe_setup_link(hw, highest_link_speed,
+					  autoneg_wait_to_complete);
+
+out:
+	/* Set autoneg_advertised value based on input link speed */
+	hw->phy.autoneg_advertised = 0;
+
+	if (speed & SPEED_10000)
+		hw->phy.autoneg_advertised |= SPEED_10000;
+
+	if (speed & SPEED_1000)
+		hw->phy.autoneg_advertised |= SPEED_1000;
+
+	return status;
+}
+
 /**
  *  txgbe_init_thermal_sensor_thresh - Inits thermal sensor thresholds
  *  @hw: pointer to hardware structure
@@ -628,8 +1755,14 @@ int txgbe_validate_eeprom_checksum(struct txgbe_hw *hw, u16 *checksum_val)
 static void txgbe_reset_misc(struct txgbe_hw *hw)
 {
 	struct wx_hw *wxhw = &hw->wxhw;
+	u32 value;
 
 	txgbe_init_i2c(hw);
+
+	value = txgbe_rd32_epcs(hw, TXGBE_SR_PCS_CTL2);
+	if ((value & 0x3) != TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_X)
+		hw->link_status = TXGBE_LINK_STATUS_NONE;
+
 	wx_reset_misc(wxhw);
 	txgbe_init_thermal_sensor_thresh(hw);
 }
@@ -672,8 +1805,11 @@ int txgbe_reset_hw(struct txgbe_hw *hw)
 		txgbe_rd32_epcs(hw, TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1);
 
 	if (!(((wxhw->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) ||
-	      ((wxhw->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP)))
-		wx_reset_hostif(wxhw);
+	      ((wxhw->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP))) {
+		status = wx_reset_hostif(wxhw);
+		if (status == 0)
+			hw->link_status = TXGBE_LINK_STATUS_NONE;
+	}
 
 	usleep_range(10, 100);
 
@@ -729,3 +1865,32 @@ int txgbe_reset_hw(struct txgbe_hw *hw)
 
 	return 0;
 }
+
+/**
+ *  txgbe_start_hw - Prepare hardware for Tx/Rx
+ *  @hw: pointer to hardware structure
+ *
+ *  Starts the hardware using the generic start_hw function
+ *  and the generation start_hw function.
+ *  Then performs revision-specific operations, if any.
+ **/
+void txgbe_start_hw(struct txgbe_hw *hw)
+{
+	/* We need to run link autotry after the driver loads */
+	hw->phy.autotry_restart = true;
+}
+
+int txgbe_init_hw(struct txgbe_hw *hw)
+{
+	int status;
+
+	/* Reset the hardware */
+	status = txgbe_reset_hw(hw);
+
+	if (status == 0) {
+		/* Start the HW */
+		txgbe_start_hw(hw);
+	}
+
+	return status;
+}
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h
index 6a751a69177b..32d3f9d7c0e5 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.h
@@ -4,8 +4,17 @@
 #ifndef _TXGBE_HW_H_
 #define _TXGBE_HW_H_
 
+int txgbe_identify_sfp_module(struct txgbe_hw *hw);
+void txgbe_check_mac_link(struct txgbe_hw *hw, u32 *speed,
+			  bool *link_up, bool link_up_wait_to_complete);
+int txgbe_get_link_capabilities(struct txgbe_hw *hw, u32 *speed, bool *autoneg);
+void txgbe_disable_tx_laser(struct txgbe_hw *hw);
+void txgbe_enable_tx_laser(struct txgbe_hw *hw);
+int txgbe_setup_link(struct txgbe_hw *hw, u32 speed, bool autoneg_wait_to_complete);
 int txgbe_read_pba_string(struct txgbe_hw *hw, u8 *pba_num, u32 pba_num_size);
 int txgbe_validate_eeprom_checksum(struct txgbe_hw *hw, u16 *checksum_val);
 int txgbe_reset_hw(struct txgbe_hw *hw);
+void txgbe_start_hw(struct txgbe_hw *hw);
+int txgbe_init_hw(struct txgbe_hw *hw);
 
 #endif /* _TXGBE_HW_H_ */
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index cb86c001baa6..a0efe4259fdc 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -7,7 +7,9 @@
 #include <linux/netdevice.h>
 #include <linux/string.h>
 #include <linux/aer.h>
+#include <linux/timecounter.h>
 #include <linux/etherdevice.h>
+#include <linux/ethtool.h>
 #include <net/ip.h>
 
 #include "../libwx/wx_type.h"
@@ -93,12 +95,250 @@ static void txgbe_service_event_complete(struct txgbe_adapter *adapter)
 	clear_bit(__TXGBE_SERVICE_SCHED, &adapter->state);
 }
 
+/**
+ * txgbe_watchdog_update_link - update the link status
+ * @adapter: pointer to the device adapter structure
+ **/
+static void txgbe_watchdog_update_link(struct txgbe_adapter *adapter)
+{
+	u32 link_speed = adapter->link_speed;
+	struct txgbe_hw *hw = &adapter->hw;
+	bool link_up = adapter->link_up;
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 i, reg;
+
+	if (!(adapter->flags & TXGBE_FLAG_NEED_LINK_UPDATE))
+		return;
+
+	link_speed = SPEED_10000;
+	link_up = true;
+	txgbe_check_mac_link(hw, &link_speed, &link_up, false);
+
+	if (link_up || time_after(jiffies, (adapter->link_check_timeout +
+					    TXGBE_TRY_LINK_TIMEOUT))) {
+		adapter->flags &= ~TXGBE_FLAG_NEED_LINK_UPDATE;
+	}
+
+	for (i = 0; i < 3; i++) {
+		txgbe_check_mac_link(hw, &link_speed, &link_up, false);
+		msleep(20);
+	}
+
+	adapter->link_up = link_up;
+	adapter->link_speed = link_speed;
+
+	if (link_up) {
+		if (link_speed & SPEED_10000) {
+			reg = rd32(wxhw, WX_MAC_TX_CFG);
+			reg &= ~WX_MAC_TX_CFG_SPEED_MASK;
+			reg |= WX_MAC_TX_CFG_SPEED_10G | WX_MAC_TX_CFG_TE;
+			wr32(wxhw, WX_MAC_TX_CFG, reg);
+		} else if (link_speed & (SPEED_1000 | SPEED_100 | SPEED_10)) {
+			reg = rd32(wxhw, WX_MAC_TX_CFG);
+			reg &= ~WX_MAC_TX_CFG_SPEED_MASK;
+			reg |= WX_MAC_TX_CFG_SPEED_1G | WX_MAC_TX_CFG_TE;
+			wr32(wxhw, WX_MAC_TX_CFG, reg);
+		}
+
+		/* Re configure MAC RX */
+		reg = rd32(wxhw, WX_MAC_RX_CFG);
+		wr32(wxhw, WX_MAC_RX_CFG, reg);
+		wr32(wxhw, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR);
+		reg = rd32(wxhw, WX_MAC_WDG_TIMEOUT);
+		wr32(wxhw, WX_MAC_WDG_TIMEOUT, reg);
+	}
+}
+
+/**
+ * txgbe_watchdog_link_is_up - update netif_carrier status and
+ *                             print link up message
+ * @adapter: pointer to the device adapter structure
+ **/
+static void txgbe_watchdog_link_is_up(struct txgbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	const char *speed_str;
+
+	/* only continue if link was previously down */
+	if (netif_carrier_ok(netdev))
+		return;
+
+	switch (adapter->link_speed) {
+	case SPEED_10000:
+		speed_str = "10 Gbps";
+		break;
+	case SPEED_1000:
+		speed_str = "1 Gbps";
+		break;
+	case SPEED_100:
+		speed_str = "100 Mbps";
+		break;
+	case SPEED_10:
+		speed_str = "10 Mbps";
+		break;
+	default:
+		speed_str = "unknown speed";
+		break;
+	}
+
+	netif_info(adapter, drv, netdev,
+		   "NIC Link is Up %s\n", speed_str);
+
+	netif_carrier_on(netdev);
+}
+
+/**
+ * txgbe_watchdog_link_is_down - update netif_carrier status and
+ *                               print link down message
+ * @adapter: pointer to the adapter structure
+ **/
+static void txgbe_watchdog_link_is_down(struct txgbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+
+	adapter->link_up = false;
+	adapter->link_speed = 0;
+
+	/* only continue if link was up previously */
+	if (!netif_carrier_ok(netdev))
+		return;
+
+	netif_info(adapter, drv, netdev, "NIC Link is Down\n");
+	netif_carrier_off(netdev);
+}
+
+/**
+ * txgbe_watchdog_subtask - check and bring link up
+ * @adapter: pointer to the device adapter structure
+ **/
+static void txgbe_watchdog_subtask(struct txgbe_adapter *adapter)
+{
+	/* if interface is down do nothing */
+	if (test_bit(__TXGBE_DOWN, &adapter->state) ||
+	    test_bit(__TXGBE_REMOVING, &adapter->state) ||
+	    test_bit(__TXGBE_RESETTING, &adapter->state))
+		return;
+
+	txgbe_watchdog_update_link(adapter);
+
+	if (adapter->link_up)
+		txgbe_watchdog_link_is_up(adapter);
+	else
+		txgbe_watchdog_link_is_down(adapter);
+}
+
+/**
+ * txgbe_sfp_detection_subtask - poll for SFP+ cable
+ * @adapter: the txgbe adapter structure
+ **/
+static void txgbe_sfp_detection_subtask(struct txgbe_adapter *adapter)
+{
+	struct txgbe_hw *hw = &adapter->hw;
+	int err;
+
+	/* not searching for SFP so there is nothing to do here */
+	if (!(adapter->flags & TXGBE_FLAG_SFP_NEEDS_RESET))
+		return;
+
+	if (adapter->sfp_poll_time &&
+	    time_after(adapter->sfp_poll_time, jiffies))
+		return; /* If not yet time to poll for SFP */
+
+	/* someone else is in init, wait until next service event */
+	if (test_and_set_bit(__TXGBE_IN_SFP_INIT, &adapter->state))
+		return;
+
+	adapter->sfp_poll_time = jiffies + TXGBE_SFP_POLL_JIFFIES - 1;
+
+	err = txgbe_identify_sfp_module(hw);
+	if (err != 0) {
+		if (hw->phy.sfp_type == txgbe_sfp_type_not_present) {
+			/* If no cable is present, then we need to reset
+			 * the next time we find a good cable.
+			 */
+			adapter->flags |= TXGBE_FLAG_SFP_NEEDS_RESET;
+		}
+		/* exit on error */
+		goto sfp_out;
+	}
+
+	/* exit if reset not needed */
+	if (!(adapter->flags & TXGBE_FLAG_SFP_NEEDS_RESET))
+		goto sfp_out;
+
+	adapter->flags &= ~TXGBE_FLAG_SFP_NEEDS_RESET;
+
+	if (!hw->phy.multispeed_fiber)
+		hw->phy.autoneg_advertised = 0;
+
+	adapter->flags |= TXGBE_FLAG_NEED_LINK_CONFIG;
+	netif_info(adapter, probe, adapter->netdev,
+		   "detected SFP+: %d\n", hw->phy.sfp_type);
+
+sfp_out:
+	clear_bit(__TXGBE_IN_SFP_INIT, &adapter->state);
+
+	if (hw->phy.type == txgbe_phy_sfp_unsupported)
+		dev_err(&adapter->pdev->dev,
+			"failed to initialize because an unsupported SFP+ module type was detected.\n");
+}
+
+/**
+ * txgbe_sfp_link_config_subtask - set up link SFP after module install
+ * @adapter: the txgbe adapter structure
+ **/
+static void txgbe_sfp_link_config_subtask(struct txgbe_adapter *adapter)
+{
+	struct txgbe_hw *hw = &adapter->hw;
+	bool autoneg = false;
+	u32 speed;
+
+	if (!(adapter->flags & TXGBE_FLAG_NEED_LINK_CONFIG))
+		return;
+
+	/* someone else is in init, wait until next service event */
+	if (test_and_set_bit(__TXGBE_IN_SFP_INIT, &adapter->state))
+		return;
+
+	adapter->flags &= ~TXGBE_FLAG_NEED_LINK_CONFIG;
+
+	if ((hw->wxhw.subsystem_device_id & TXGBE_DEV_MASK) ==
+	    TXGBE_ID_MAC_SGMII) {
+		speed = SPEED_10000;
+	} else {
+		speed = hw->phy.autoneg_advertised;
+		if (!speed) {
+			txgbe_get_link_capabilities(hw, &speed, &autoneg);
+			/* setup the highest link when no autoneg */
+			if (!autoneg) {
+				if (speed & SPEED_10000)
+					speed = SPEED_10000;
+			}
+		}
+	}
+
+	txgbe_setup_link(hw, speed, false);
+
+	adapter->flags |= TXGBE_FLAG_NEED_LINK_UPDATE;
+	adapter->link_check_timeout = jiffies;
+	clear_bit(__TXGBE_IN_SFP_INIT, &adapter->state);
+}
+
 static void txgbe_service_timer(struct timer_list *t)
 {
 	struct txgbe_adapter *adapter = from_timer(adapter, t, service_timer);
 	unsigned long next_event_offset;
 
-	next_event_offset = HZ * 2;
+	/* poll faster when waiting for link */
+	if (adapter->flags & TXGBE_FLAG_NEED_LINK_UPDATE) {
+		if ((adapter->hw.wxhw.subsystem_device_id & TXGBE_DEV_MASK) ==
+		    TXGBE_ID_KR_KX_KX4)
+			next_event_offset = HZ;
+		else
+			next_event_offset = HZ / 10;
+	} else {
+		next_event_offset = HZ * 2;
+	}
 
 	/* Reset the timer */
 	mod_timer(&adapter->service_timer, next_event_offset + jiffies);
@@ -116,6 +356,9 @@ static void txgbe_service_task(struct work_struct *work)
 						     struct txgbe_adapter,
 						     service_task);
 
+	txgbe_sfp_detection_subtask(adapter);
+	txgbe_sfp_link_config_subtask(adapter);
+	txgbe_watchdog_subtask(adapter);
 	txgbe_service_event_complete(adapter);
 }
 
@@ -208,8 +451,55 @@ static void txgbe_up_complete(struct txgbe_adapter *adapter)
 {
 	struct txgbe_hw *hw = &adapter->hw;
 	struct wx_hw *wxhw = &hw->wxhw;
+	u32 links_reg;
 
 	wx_control_hw(wxhw, true);
+
+	/* enable the optics for SFP+ fiber */
+	txgbe_enable_tx_laser(hw);
+
+	/* make sure to complete pre-operations */
+	smp_mb__before_atomic();
+	clear_bit(__TXGBE_DOWN, &adapter->state);
+
+	if (hw->phy.media_type == txgbe_media_type_fiber) {
+		/* We are assuming the worst case scenerio here, and that
+		 * is that an SFP was inserted/removed after the reset
+		 * but before SFP detection was enabled.  As such the best
+		 * solution is to just start searching as soon as we start
+		 */
+		adapter->flags |= TXGBE_FLAG_SFP_NEEDS_RESET;
+		adapter->sfp_poll_time = 0;
+	} else if (hw->phy.media_type == txgbe_media_type_backplane) {
+		adapter->flags |= TXGBE_FLAG_NEED_LINK_CONFIG;
+		txgbe_service_event_schedule(adapter);
+	}
+
+	links_reg = rd32(wxhw, TXGBE_CFG_PORT_ST);
+	if (links_reg & TXGBE_CFG_PORT_ST_LINK_UP) {
+		if (links_reg & TXGBE_CFG_PORT_ST_LINK_10G) {
+			wr32(wxhw, WX_MAC_TX_CFG,
+			     (rd32(wxhw, WX_MAC_TX_CFG) & ~WX_MAC_TX_CFG_SPEED_MASK) |
+			     WX_MAC_TX_CFG_SPEED_10G);
+		} else if (links_reg & (TXGBE_CFG_PORT_ST_LINK_1G |
+					TXGBE_CFG_PORT_ST_LINK_100M)) {
+			wr32(wxhw, WX_MAC_TX_CFG,
+			     (rd32(wxhw, WX_MAC_TX_CFG) & ~WX_MAC_TX_CFG_SPEED_MASK) |
+			     WX_MAC_TX_CFG_SPEED_1G);
+		}
+	}
+
+	/* bring the link up in the watchdog, this could race with our first
+	 * link up interrupt but shouldn't be a problem
+	 */
+	adapter->flags |= TXGBE_FLAG_NEED_LINK_UPDATE;
+	adapter->link_check_timeout = jiffies;
+
+	mod_timer(&adapter->service_timer, jiffies);
+
+	/* Set PF Reset Done bit so PF/VF Mail Ops can work */
+	wr32m(wxhw, WX_CFG_PORT_CTL,
+	      WX_CFG_PORT_CTL_PFRSTD, WX_CFG_PORT_CTL_PFRSTD);
 }
 
 static void txgbe_reset(struct txgbe_adapter *adapter)
@@ -219,12 +509,21 @@ static void txgbe_reset(struct txgbe_adapter *adapter)
 	u8 old_addr[ETH_ALEN];
 	int err;
 
-	err = txgbe_reset_hw(hw);
+	/* lock SFP init bit to prevent race conditions with the watchdog */
+	while (test_and_set_bit(__TXGBE_IN_SFP_INIT, &adapter->state))
+		usleep_range(1000, 2000);
+
+	/* clear all SFP and link config related flags while holding SFP_INIT */
+	adapter->flags &= ~TXGBE_FLAG_SFP_NEEDS_RESET;
+	adapter->flags &= ~TXGBE_FLAG_NEED_LINK_CONFIG;
+
+	err = txgbe_init_hw(hw);
 	if (err != 0 &&
 	    hw->phy.type != txgbe_phy_sfp_unsupported &&
 	    hw->phy.sfp_type != txgbe_sfp_type_not_present)
 		dev_err(&adapter->pdev->dev, "Hardware Error: %d\n", err);
 
+	clear_bit(__TXGBE_IN_SFP_INIT, &adapter->state);
 	/* do not flush user set addresses */
 	memcpy(old_addr, &adapter->mac_table[0].addr, netdev->addr_len);
 	txgbe_flush_sw_mac_table(adapter);
@@ -247,6 +546,8 @@ static void txgbe_disable_device(struct txgbe_adapter *adapter)
 	netif_carrier_off(netdev);
 	netif_tx_disable(netdev);
 
+	adapter->flags &= ~TXGBE_FLAG_NEED_LINK_UPDATE;
+
 	del_timer_sync(&adapter->service_timer);
 
 	if (wxhw->bus.func < 2)
@@ -268,8 +569,14 @@ static void txgbe_disable_device(struct txgbe_adapter *adapter)
 
 static void txgbe_down(struct txgbe_adapter *adapter)
 {
+	struct txgbe_hw *hw = &adapter->hw;
+
 	txgbe_disable_device(adapter);
 	txgbe_reset(adapter);
+
+	if ((hw->wxhw.subsystem_device_id & WX_NCSI_MASK) != WX_NCSI_SUP)
+		/* power down the optics for SFP+ fiber */
+		txgbe_disable_tx_laser(hw);
 }
 
 /**
@@ -350,7 +657,11 @@ static int txgbe_open(struct net_device *netdev)
  */
 static void txgbe_close_suspend(struct txgbe_adapter *adapter)
 {
+	struct txgbe_hw *hw = &adapter->hw;
+
 	txgbe_disable_device(adapter);
+	if ((hw->wxhw.subsystem_device_id & WX_NCSI_MASK) != WX_NCSI_SUP)
+		txgbe_disable_tx_laser(hw);
 }
 
 /**
@@ -618,12 +929,18 @@ static int txgbe_probe(struct pci_dev *pdev,
 			 "0x%08x", etrack_id);
 	}
 
+	txgbe_start_hw(hw);
+
 	err = register_netdev(netdev);
 	if (err)
 		goto err_release_hw;
 
 	pci_set_drvdata(pdev, adapter);
 
+	if (!((wxhw->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP))
+		/* power down the optics for SFP+ fiber */
+		txgbe_disable_tx_laser(hw);
+
 	/* calculate the expected PCIe bandwidth required for optimal
 	 * performance. Note that some older parts will never have enough
 	 * bandwidth due to being older generation PCIe parts. We clamp these
@@ -638,6 +955,11 @@ static int txgbe_probe(struct pci_dev *pdev,
 	else
 		dev_warn(&pdev->dev, "Failed to enumerate PF devices.\n");
 
+	if ((wxhw->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP)
+		netif_info(adapter, probe, netdev, "NCSI : support");
+	else
+		netif_info(adapter, probe, netdev, "NCSI : unsupported");
+
 	/* First try to read PBA as a string */
 	err = txgbe_read_pba_string(hw, part_str, TXGBE_PBANUM_LENGTH);
 	if (err)
@@ -654,6 +976,14 @@ static int txgbe_probe(struct pci_dev *pdev,
 
 	netif_info(adapter, probe, netdev, "%pM\n", netdev->dev_addr);
 
+	netif_info(adapter, probe, netdev,
+		   "WangXun(R) 10 Gigabit Network Connection\n");
+
+	/* setup link for SFP devices with MNG FW, else wait for TXGBE_UP */
+	if (hw->phy.media_type == txgbe_media_type_fiber &&
+	    ((wxhw->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP))
+		txgbe_setup_link(hw, SPEED_10000 | SPEED_1000, true);
+
 	return 0;
 
 err_release_hw:
@@ -692,6 +1022,7 @@ static void txgbe_remove(struct pci_dev *pdev)
 	cancel_work_sync(&adapter->service_task);
 
 	unregister_netdev(netdev);
+	wx_control_hw(&adapter->hw.wxhw, false);
 
 	pci_release_selected_regions(pdev,
 				     pci_select_bars(pdev, IORESOURCE_MEM));
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index 2f8be0118157..0c5d62892ba1 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -36,6 +36,8 @@
 /* Combined interface*/
 #define TXGBE_ID_SFI_XAUI			0x50
 
+#define TXGBE_DEV_MASK                          0xF0
+
 /* Revision ID */
 #define TXGBE_SP_MPW  1
 
@@ -56,12 +58,67 @@
 /*********************** ETH PHY ***********************/
 #define TXGBE_XPCS_IDA_ADDR                     0x13000
 #define TXGBE_XPCS_IDA_DATA                     0x13004
+#define TXGBE_ETHPHY_IDA_ADDR                   0x13008
+#define TXGBE_ETHPHY_IDA_DATA                   0x1300C
 /* ETH PHY Registers */
 #define TXGBE_SR_PCS_CTL2                       0x30007
+#define TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_R        0x0
+#define TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_X        BIT(0)
+#define TXGBE_SR_PCS_CTL2_PCS_TYPE_SEL_MASK     0x3
 #define TXGBE_SR_PMA_MMD_CTL1                   0x10000
+#define TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_1G      0x0
+#define TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_10G     BIT(13)
+#define TXGBE_SR_PMA_MMD_CTL1_SPEED_SEL_MASK    BIT(13)
 #define TXGBE_SR_AN_MMD_CTL                     0x70000
+#define TXGBE_SR_AN_MMD_CTL_EXT_NP_CTL          BIT(13)
+#define TXGBE_SR_AN_MMD_CTL_ENABLE              BIT(12)
 #define TXGBE_SR_AN_MMD_ADV_REG2                0x70011
+#define TXGBE_SR_AN_MMD_ADV_REG2_BP_TYPE_KX     BIT(5)
+#define TXGBE_SR_AN_MMD_ADV_REG2_BP_TYPE_KX4    BIT(6)
+#define TXGBE_SR_AN_MMD_ADV_REG2_BP_TYPE_KR     BIT(7)
+#define TXGBE_SR_MII_MMD_CTL                    0x1F0000
+#define TXGBE_SR_MII_MMD_CTL_AN_EN              BIT(12)
+#define TXGBE_SR_MII_MMD_CTL_RESTART_AN         BIT(9)
+#define TXGBE_SR_MII_MMD_CTL_DUPLEX_MODE        BIT(8)
+#define TXGBE_SR_MII_MMD_CTL_SGMII_1000         BIT(6)
+#define TXGBE_SR_MII_MMD_CTL_SGMII_100          BIT(13)
+#define TXGBE_SR_MII_MMD_CTL_SGMII_10           0
+#define TXGBE_SR_MII_MMD_DIGI_CTL               0x1F8000
+#define TXGBE_SR_MII_MMD_DIGI_CTL_AS            BIT(9)
+#define TXGBE_SR_MII_MMD_AN_CTL                 0x1F8001
 #define TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1        0x38000
+#define TXGBE_VR_XS_OR_PCS_MMD_DIGI_CTL1_VR_RST BIT(15)
+#define TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST          0x38010
+#define TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_M     (0x7 << 2)
+#define TXGBE_VR_XS_OR_PCS_MMD_DIGI_ST_PS_PG    (BIT(2) << 2)
+#define TXGBE_VR_AN_INTR_MSK                    0x78001
+#define TXGBE_VR_AN_INTR_MSK_AN_PG_RCV_IE       BIT(2)
+#define TXGBE_VR_AN_INTR_MSK_AN_INC_LINK_IE     BIT(1)
+#define TXGBE_VR_AN_INTR_MSK_AN_INT_CMPLT_IE    BIT(0)
+#define TXGBE_VR_AN_KR_MODE_CL                  0x78003
+#define TXGBE_VR_AN_KR_MODE_CL_PDET_EN          BIT(0)
+#define TXGBE_PHY_TX_GENCTRL1                   0x18031
+#define TXGBE_PHY_TX_GEN_CTL2                   0x18032
+#define TXGBE_PHY_TX_RATE_CTL                   0x18034
+#define TXGBE_PHY_RX_GEN_CTL2                   0x18052
+#define TXGBE_PHY_RX_GEN_CTL3                   0x18053
+#define TXGBE_PHY_RX_RATE_CTL                   0x18054
+#define TXGBE_PHY_RX_EQ_ATT_LVL0                0x18057
+#define TXGBE_PHY_RX_EQ_CTL0                    0x18058
+#define TXGBE_PHY_RX_EQ_CTL                     0x1805C
+#define TXGBE_PHY_AFE_DFE_ENABLE                0x1805D
+#define TXGBE_PHY_DFE_TAP_CTL0                  0x1805E
+#define TXGBE_PHY_MPLLA_CTL0                    0x18071
+#define TXGBE_PHY_MPLLA_CTL2                    0x18073
+#define TXGBE_PHY_MPLLA_CTL3                    0x18077
+#define TXGBE_PHY_MISC_CTL0                     0x18090
+#define TXGBE_PHY_VCO_CAL_LD0                   0x18092
+#define TXGBE_PHY_VCO_CAL_LD1                   0x18093
+#define TXGBE_PHY_VCO_CAL_LD2                   0x18094
+#define TXGBE_PHY_VCO_CAL_LD3                   0x18095
+#define TXGBE_PHY_VCO_CAL_REF0                  0x18096
+#define TXGBE_PHY_VCO_CAL_REF1                  0x18097
+#define TXGBE_SUP_DIG_MPLLA_OVRD_IN_0           0x4
 /* I2C registers */
 #define TXGBE_I2C_CON                           0x14900 /* I2C Control */
 #define TXGBE_I2C_CON_SLAVE_DISABLE             BIT(6)
@@ -87,6 +144,25 @@
 #define TXGBE_I2C_SLAVE_ADDR                    (0xA0 >> 1)
 #define TXGBE_I2C_EEPROM_DEV_ADDR               0xA0
 
+/* port cfg Registers */
+#define TXGBE_CFG_PORT_ST                       0x14404
+#define TXGBE_CFG_PORT_ST_LINK_UP               BIT(0)
+#define TXGBE_CFG_PORT_ST_LINK_10G              BIT(1)
+#define TXGBE_CFG_PORT_ST_LINK_1G               BIT(2)
+#define TXGBE_CFG_PORT_ST_LINK_100M             BIT(3)
+
+/* GPIO Registers */
+#define TXGBE_GPIO_DR                           0x14800
+#define TXGBE_GPIO_DR_0                         BIT(0) /* SDP0 Data Value */
+#define TXGBE_GPIO_DR_1                         BIT(1) /* SDP1 Data Value */
+#define TXGBE_GPIO_DR_4                         BIT(4) /* SDP4 Data Value */
+#define TXGBE_GPIO_DR_5                         BIT(5) /* SDP5 Data Value */
+#define TXGBE_GPIO_DDR                          0x14804
+#define TXGBE_GPIO_DDR_0                        BIT(0) /* SDP0 IO direction */
+#define TXGBE_GPIO_DDR_1                        BIT(1) /* SDP1 IO direction */
+#define TXGBE_GPIO_DDR_4                        BIT(4) /* SDP4 IO direction */
+#define TXGBE_GPIO_DDR_5                        BIT(5) /* SDP5 IO direction */
+
 /* EEPROM byte offsets */
 #define TXGBE_SFF_IDENTIFIER                    0x0
 #define TXGBE_SFF_IDENTIFIER_SFP                0x3
@@ -133,6 +209,22 @@
 #define TXGBE_PBANUM1_PTR                       0x06
 #define TXGBE_PBANUM_PTR_GUARD                  0xFAFA
 
+/* Link speed */
+#define TXGBE_LINK_SPEED_AUTONEG                (SPEED_10 | \
+						 SPEED_100 | \
+						 SPEED_1000 | \
+						 SPEED_10000)
+
+/* Physical layer type */
+#define TXGBE_PHYSICAL_LAYER_UNKNOWN            0
+#define TXGBE_PHYSICAL_LAYER_10GBASE_T          BIT(0)
+#define TXGBE_PHYSICAL_LAYER_1000BASE_T         BIT(1)
+#define TXGBE_PHYSICAL_LAYER_100BASE_TX         BIT(2)
+#define TXGBE_PHYSICAL_LAYER_10GBASE_KX4        BIT(3)
+#define TXGBE_PHYSICAL_LAYER_1000BASE_KX        BIT(4)
+#define TXGBE_PHYSICAL_LAYER_10GBASE_KR         BIT(5)
+#define TXGBE_PHYSICAL_LAYER_10GBASE_XAUI       BIT(6)
+
 /* SFP+ module type IDs:
  *
  * ID   Module Type
@@ -188,11 +280,22 @@ struct txgbe_phy_info {
 	u32 orig_vr_xs_or_pcs_mmd_digi_ctl1;
 	bool orig_link_settings_stored;
 	bool multispeed_fiber;
+	bool autotry_restart;
+	u32 autoneg_advertised;
+	u32 link_mode;
+};
+
+/* link status for KX/KX4 */
+enum txgbe_link_status {
+	TXGBE_LINK_STATUS_NONE = 0,
+	TXGBE_LINK_STATUS_KX,
+	TXGBE_LINK_STATUS_KX4
 };
 
 struct txgbe_hw {
 	struct wx_hw wxhw;
 	struct txgbe_phy_info phy;
+	enum txgbe_link_status link_status;
 };
 
 #endif /* _TXGBE_TYPE_H_ */
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH net-next 4/5] net: ngbe: Initialize phy information
  2022-11-08 11:19 [PATCH net-next 0/5] net: WangXun ethernet drivers Mengyuan Lou
                   ` (2 preceding siblings ...)
  2022-11-08 11:19 ` [PATCH net-next 3/5] net: txgbe: Support to setup link Mengyuan Lou
@ 2022-11-08 11:19 ` Mengyuan Lou
  2022-11-08 21:10   ` Andrew Lunn
  2022-11-14 16:16   ` Alexander Lobakin
  2022-11-08 11:19 ` [PATCH net-next 5/5] net: ngbe: Initialize service task Mengyuan Lou
  4 siblings, 2 replies; 25+ messages in thread
From: Mengyuan Lou @ 2022-11-08 11:19 UTC (permalink / raw)
  To: netdev; +Cc: jiawenwu, Mengyuan Lou

Initialize phy media type.
Initialize phy ops functions.

Signed-off-by: Mengyuan Lou <mengyuanlou@net-swift.com>
---
 drivers/net/ethernet/wangxun/libwx/wx_hw.c    |    3 +-
 drivers/net/ethernet/wangxun/libwx/wx_hw.h    |    1 +
 drivers/net/ethernet/wangxun/ngbe/Makefile    |    2 +-
 drivers/net/ethernet/wangxun/ngbe/ngbe.h      |    2 +
 drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c   |   31 +
 drivers/net/ethernet/wangxun/ngbe/ngbe_hw.h   |    1 +
 drivers/net/ethernet/wangxun/ngbe/ngbe_main.c |   28 +-
 drivers/net/ethernet/wangxun/ngbe/ngbe_phy.c  | 1113 +++++++++++++++++
 drivers/net/ethernet/wangxun/ngbe/ngbe_phy.h  |   22 +
 drivers/net/ethernet/wangxun/ngbe/ngbe_type.h |  113 +-
 10 files changed, 1305 insertions(+), 11 deletions(-)
 create mode 100644 drivers/net/ethernet/wangxun/ngbe/ngbe_phy.c
 create mode 100644 drivers/net/ethernet/wangxun/ngbe/ngbe_phy.h

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_ */
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH net-next 5/5] net: ngbe: Initialize service task
  2022-11-08 11:19 [PATCH net-next 0/5] net: WangXun ethernet drivers Mengyuan Lou
                   ` (3 preceding siblings ...)
  2022-11-08 11:19 ` [PATCH net-next 4/5] net: ngbe: Initialize phy information Mengyuan Lou
@ 2022-11-08 11:19 ` Mengyuan Lou
  4 siblings, 0 replies; 25+ messages in thread
From: Mengyuan Lou @ 2022-11-08 11:19 UTC (permalink / raw)
  To: netdev; +Cc: jiawenwu, Mengyuan Lou

Initialize ngbe_wq, building the framework for task checking.

Signed-off-by: Mengyuan Lou <mengyuanlou@net-swift.com>
---
 drivers/net/ethernet/wangxun/ngbe/ngbe.h      |  21 +
 drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c   |   5 +
 drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 380 +++++++++++++++++-
 drivers/net/ethernet/wangxun/ngbe/ngbe_type.h |   1 +
 4 files changed, 401 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe.h b/drivers/net/ethernet/wangxun/ngbe/ngbe.h
index ac67c0403592..17b241264c2d 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe.h
@@ -36,6 +36,17 @@ struct ngbe_mac_addr {
 	u64 pools;
 };
 
+enum ngbe_adapter_state {
+	NGBE_TESTING,
+	NGBE_DOWN,
+	NGBE_REMOVING,
+	NGBE_RESETTING,
+	NGBE_NEEDS_RESTART,
+	NGBE_SERVICE_SCHED,
+	NGBE_SERVICE_DIS,
+	NGBE_STATE_NBITS		/* must be last */
+};
+
 /* board specific private data structure */
 struct ngbe_adapter {
 	u8 __iomem *io_addr;    /* Mainly for iounmap use */
@@ -48,7 +59,17 @@ struct ngbe_adapter {
 	struct ngbe_mac_addr *mac_table;
 	u16 msg_enable;
 
+	DECLARE_BITMAP(state, NGBE_STATE_NBITS);
 	DECLARE_BITMAP(flags, NGBE_FLAGS_NBITS);
+	unsigned long serv_tmr_period;
+	unsigned long serv_tmr_prev;
+	struct timer_list serv_tmr;
+	struct work_struct serv_task;
+
+	unsigned long link_check_timeout;
+
+	u32 link_speed;
+	bool link_up;
 
 	/* Tx fast path data */
 	int num_tx_queues;
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c
index 274d54832579..915b27b25c1c 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_hw.c
@@ -8,6 +8,7 @@
 #include "../libwx/wx_type.h"
 #include "../libwx/wx_hw.h"
 #include "ngbe_type.h"
+#include "ngbe_phy.h"
 #include "ngbe_hw.h"
 #include "ngbe.h"
 
@@ -106,6 +107,10 @@ int ngbe_reset_hw(struct ngbe_hw *hw)
 	wr32(wxhw, WX_MIS_RST, reset | rd32(wxhw, WX_MIS_RST));
 	ngbe_reset_misc(hw);
 
+	status = ngbe_phy_init(hw);
+	if (status)
+		return status;
+
 	/* Store the permanent mac address */
 	wx_get_mac_addr(wxhw, wxhw->mac.perm_addr);
 
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index ebf3fcdc4719..83cc61eb3be2 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -9,14 +9,18 @@
 #include <linux/aer.h>
 #include <linux/etherdevice.h>
 #include <net/ip.h>
+#include <linux/ethtool.h>
 
 #include "../libwx/wx_type.h"
 #include "../libwx/wx_hw.h"
 #include "ngbe_type.h"
+#include "ngbe_phy.h"
 #include "ngbe_hw.h"
 #include "ngbe.h"
 char ngbe_driver_name[] = "ngbe";
 
+static struct workqueue_struct *ngbe_wq;
+
 /* ngbe_pci_tbl - PCI Device ID Table
  *
  * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
@@ -39,6 +43,81 @@ static const struct pci_device_id ngbe_pci_tbl[] = {
 	{ .device = 0 }
 };
 
+/**
+ * ngbe_service_event_schedule - schedule the service task to wake up
+ * @adapter: board private structure
+ *
+ * If not already scheduled, this puts the task into the work queue.
+ */
+static void ngbe_service_event_schedule(struct ngbe_adapter *adapter)
+{
+	if (!test_bit(NGBE_SERVICE_DIS, adapter->state) &&
+	    !test_and_set_bit(NGBE_SERVICE_SCHED, adapter->state) &&
+	    !test_bit(NGBE_NEEDS_RESTART, adapter->state))
+		queue_work(ngbe_wq, &adapter->serv_task);
+}
+
+static void ngbe_service_event_complete(struct ngbe_adapter *adapter)
+{
+	WARN_ON(!test_bit(NGBE_SERVICE_SCHED, adapter->state));
+
+	/* flush memory to make sure state is correct before next watchdog */
+	smp_mb__before_atomic();
+	clear_bit(NGBE_SERVICE_SCHED, adapter->state);
+}
+
+/**
+ * ngbe_service_event_stop - stop service task and cancel works
+ * @adapter: board private structure
+ */
+static int ngbe_service_event_stop(struct ngbe_adapter *adapter)
+{
+	int ret;
+
+	ret = test_and_set_bit(NGBE_SERVICE_DIS, adapter->state);
+	if (adapter->serv_tmr.function)
+		del_timer_sync(&adapter->serv_tmr);
+	if (adapter->serv_task.func)
+		cancel_work_sync(&adapter->serv_task);
+	clear_bit(NGBE_SERVICE_SCHED, adapter->state);
+
+	return ret;
+}
+
+/**
+ * ngbe_service_timer - timer callback to schedule service task
+ * @t: pointer to timer_list
+ */
+static void ngbe_service_timer(struct timer_list *t)
+{
+	struct ngbe_adapter *adapter =
+			from_timer(adapter, t, serv_tmr);
+
+	mod_timer(&adapter->serv_tmr, round_jiffies(adapter->serv_tmr_period + jiffies));
+	ngbe_service_event_schedule(adapter);
+}
+
+static void ngbe_sync_mac_table(struct ngbe_adapter *adapter)
+{
+	struct ngbe_hw *hw = &adapter->hw;
+	struct wx_hw *wxhw = &hw->wxhw;
+	int i;
+
+	for (i = 0; i < wxhw->mac.num_rar_entries; i++) {
+		if (adapter->mac_table[i].state & NGBE_MAC_STATE_MODIFIED) {
+			if (adapter->mac_table[i].state & NGBE_MAC_STATE_IN_USE) {
+				wx_set_rar(wxhw, i,
+					   adapter->mac_table[i].addr,
+					   adapter->mac_table[i].pools,
+					   WX_PSR_MAC_SWC_AD_H_AV);
+			} else {
+				wx_clear_rar(wxhw, i);
+			}
+			adapter->mac_table[i].state &= ~(NGBE_MAC_STATE_MODIFIED);
+		}
+	}
+}
+
 static void ngbe_mac_set_default_filter(struct ngbe_adapter *adapter, u8 *addr)
 {
 	struct ngbe_hw *hw = &adapter->hw;
@@ -52,6 +131,161 @@ static void ngbe_mac_set_default_filter(struct ngbe_adapter *adapter, u8 *addr)
 		   WX_PSR_MAC_SWC_AD_H_AV);
 }
 
+static void ngbe_flush_sw_mac_table(struct ngbe_adapter *adapter)
+{
+	struct ngbe_hw *hw = &adapter->hw;
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 i;
+
+	for (i = 0; i < wxhw->mac.num_rar_entries; i++) {
+		adapter->mac_table[i].state |= NGBE_MAC_STATE_MODIFIED;
+		adapter->mac_table[i].state &= ~NGBE_MAC_STATE_IN_USE;
+		memset(adapter->mac_table[i].addr, 0, ETH_ALEN);
+		adapter->mac_table[i].pools = 0;
+	}
+	ngbe_sync_mac_table(adapter);
+}
+
+/**
+ * ngbe_watchdog_update_link_status - update the link status
+ * @adapter: pointer to the device adapter structure
+ **/
+static void ngbe_watchdog_update_link_status(struct ngbe_adapter *adapter)
+{
+	u32 link_speed = adapter->link_speed;
+	struct ngbe_hw *hw = &adapter->hw;
+	bool link_up = adapter->link_up;
+	struct wx_hw *wxhw = &hw->wxhw;
+	u32 lan_speed = 0;
+	u32 reg;
+
+	if (!test_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags))
+		return;
+
+	link_speed = SPEED_1000;
+	link_up = true;
+	ngbe_phy_check_link(hw, &link_speed, &link_up, false);
+
+	if (link_up || time_after(jiffies, (adapter->link_check_timeout +
+					    4 * HZ)))
+		set_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags);
+
+	adapter->link_speed = link_speed;
+	switch (link_speed) {
+	case SPEED_1000:
+		lan_speed = 2;
+		break;
+	case SPEED_100:
+		lan_speed = 1;
+		break;
+	case SPEED_10:
+		lan_speed = 0;
+		break;
+	default:
+		break;
+	}
+	wr32m(wxhw, NGBE_CFG_LAN_SPEED, 0x3, lan_speed);
+
+	adapter->link_up = link_up;
+	adapter->link_speed = link_speed;
+	if (link_up) {
+		if (link_speed & (SPEED_1000 | SPEED_100 | SPEED_10)) {
+			reg = rd32(wxhw, WX_MAC_TX_CFG);
+			reg &= ~WX_MAC_TX_CFG_SPEED_MASK;
+			reg |= WX_MAC_TX_CFG_SPEED_1G | WX_MAC_TX_CFG_TE;
+			wr32(wxhw, WX_MAC_TX_CFG, reg);
+		}
+		/* Re configure MAC RX */
+		reg = rd32(wxhw, WX_MAC_RX_CFG);
+		wr32(wxhw, WX_MAC_RX_CFG, reg);
+		wr32(wxhw, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR);
+		reg = rd32(wxhw, WX_MAC_WDG_TIMEOUT);
+		wr32(wxhw, WX_MAC_WDG_TIMEOUT, reg);
+	}
+}
+
+static void ngbe_watchdog_link_is_up(struct ngbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	const char *speed_str;
+
+	/* only continue if link was previously down */
+	if (netif_carrier_ok(netdev))
+		return;
+
+	switch (adapter->link_speed) {
+	case SPEED_1000:
+		speed_str = "1 Gbps";
+		break;
+	case SPEED_100:
+		speed_str = "100 Mbps";
+		break;
+	case SPEED_10:
+		speed_str = "10 Mbps";
+		break;
+	default:
+		speed_str = "unknown speed";
+		break;
+	}
+
+	netif_info(adapter, drv, netdev,
+		   "NIC Link is Up %s\n", speed_str);
+
+	netif_carrier_on(netdev);
+}
+
+static void ngbe_watchdog_link_is_down(struct ngbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+
+	adapter->link_up = false;
+	adapter->link_speed = 0;
+
+	/* only continue if link was up previously */
+	if (!netif_carrier_ok(netdev))
+		return;
+
+	netif_info(adapter, drv, netdev, "NIC Link is Down\n");
+	netif_carrier_off(netdev);
+}
+
+/**
+ * ngbe_watchdog_subtask - check and bring link up
+ * @adapter: pointer to the device adapter structure
+ **/
+static void ngbe_watchdog_subtask(struct ngbe_adapter *adapter)
+{
+	/* if interface is down do nothing */
+	if (test_bit(NGBE_DOWN, adapter->state) ||
+	    test_bit(NGBE_REMOVING, adapter->state) ||
+	    test_bit(NGBE_RESETTING, adapter->state))
+		return;
+
+	ngbe_watchdog_update_link_status(adapter);
+
+	if (adapter->link_up)
+		ngbe_watchdog_link_is_up(adapter);
+	else
+		ngbe_watchdog_link_is_down(adapter);
+}
+
+/**
+ * ngbe_service_task - manages and runs subtasks
+ * @work: pointer to work_struct containing our data
+ **/
+static void ngbe_service_task(struct work_struct *work)
+{
+	struct ngbe_adapter *adapter = container_of(work,
+						    struct ngbe_adapter,
+						    serv_task);
+
+	if (test_bit(NGBE_DOWN, adapter->state))
+		return;
+
+	ngbe_watchdog_subtask(adapter);
+	ngbe_service_event_complete(adapter);
+}
+
 /**
  *  ngbe_init_type_code - Initialize the shared code
  *  @hw: pointer to hardware structure
@@ -221,12 +455,96 @@ static void ngbe_oem_conf_in_rom(struct ngbe_hw *hw)
 			    &hw->phy.gphy_efuse[1]);
 }
 
+static void ngbe_reset(struct ngbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	struct ngbe_hw *hw = &adapter->hw;
+	u8 old_addr[ETH_ALEN];
+	int err;
+
+	err = ngbe_reset_hw(hw);
+	if (err)
+		dev_err(&adapter->pdev->dev, "Hardware Error: %d\n", err);
+	/* do not flush user set addresses */
+	memcpy(old_addr, &adapter->mac_table[0].addr, netdev->addr_len);
+	ngbe_flush_sw_mac_table(adapter);
+	ngbe_mac_set_default_filter(adapter, old_addr);
+}
+
+static void ngbe_disable_device(struct ngbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	struct ngbe_hw *hw = &adapter->hw;
+
+	/* signal that we are down to the interrupt handler */
+	if (test_and_set_bit(NGBE_DOWN, adapter->state))
+		return; /* do nothing if already down */
+
+	wx_disable_pcie_master(&hw->wxhw);
+	/* disable receives */
+	wx_disable_rx(&hw->wxhw);
+
+	/* call carrier off first to avoid false dev_watchdog timeouts */
+	netif_carrier_off(netdev);
+	netif_tx_disable(netdev);
+
+	clear_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags);
+
+	del_timer_sync(&adapter->serv_tmr);
+}
+
 static void ngbe_down(struct ngbe_adapter *adapter)
 {
-	netif_carrier_off(adapter->netdev);
-	netif_tx_disable(adapter->netdev);
+	ngbe_disable_device(adapter);
+
+	ngbe_reset(adapter);
 };
 
+/**
+ * ngbe_phy_setlink_config - set up non-SFP+ link
+ * @hw: pointer to private hardware struct
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int ngbe_phy_setlink_config(struct ngbe_hw *hw)
+{
+	u32 speed = 0;
+	int ret = 0;
+
+	hw->phy.autoneg = 1;
+	speed = SPEED_1000 | SPEED_100 | SPEED_10;
+
+	if (hw->wol_enabled || hw->ncsi_enabled)
+		return 0;
+
+	ret = ngbe_phy_setup_link(hw, speed, false);
+
+	return ret;
+}
+
+static void ngbe_up_complete(struct ngbe_adapter *adapter)
+{
+	struct ngbe_hw *hw = &adapter->hw;
+	int err;
+
+	wx_control_hw(&hw->wxhw, true);
+
+	/* make sure to complete pre-operations */
+	smp_mb__before_atomic();
+	clear_bit(NGBE_DOWN, adapter->state);
+
+	err = ngbe_phy_setlink_config(hw);
+	if (err)
+		dev_err(&adapter->pdev->dev, "Phy hardware Error: %d\n", err);
+
+	/* bring the link up in the watchdog, this could race with our first
+	 * link up interrupt but shouldn't be a problem
+	 */
+	set_bit(NGBE_FLAG_NEED_LINK_UPDATE, adapter->flags);
+	adapter->link_check_timeout = jiffies;
+	mod_timer(&adapter->serv_tmr, round_jiffies(jiffies + adapter->serv_tmr_period));
+}
+
 /**
  * ngbe_open - Called when a network interface is made active
  * @netdev: network interface device structure
@@ -239,10 +557,8 @@ static void ngbe_down(struct ngbe_adapter *adapter)
 static int ngbe_open(struct net_device *netdev)
 {
 	struct ngbe_adapter *adapter = netdev_priv(netdev);
-	struct ngbe_hw *hw = &adapter->hw;
-	struct wx_hw *wxhw = &hw->wxhw;
 
-	wx_control_hw(wxhw, true);
+	ngbe_up_complete(adapter);
 
 	return 0;
 }
@@ -491,6 +807,15 @@ static int ngbe_probe(struct pci_dev *pdev,
 	eth_hw_addr_set(netdev, wxhw->mac.perm_addr);
 	ngbe_mac_set_default_filter(adapter, wxhw->mac.perm_addr);
 
+	/* setup service timer and periodic service task */
+	timer_setup(&adapter->serv_tmr, ngbe_service_timer, 0);
+	adapter->serv_tmr_period = HZ;
+	INIT_WORK(&adapter->serv_task, ngbe_service_task);
+	clear_bit(NGBE_SERVICE_SCHED, adapter->state);
+	clear_bit(NGBE_SERVICE_DIS, adapter->state);
+	/* since everything is good, start the service timer */
+	mod_timer(&adapter->serv_tmr, round_jiffies(jiffies + adapter->serv_tmr_period));
+
 	err = register_netdev(netdev);
 	if (err)
 		goto err_register;
@@ -505,6 +830,7 @@ static int ngbe_probe(struct pci_dev *pdev,
 	return 0;
 
 err_register:
+	ngbe_service_event_stop(adapter);
 	wx_control_hw(wxhw, false);
 err_free_mac_table:
 	kfree(adapter->mac_table);
@@ -531,6 +857,10 @@ static void ngbe_remove(struct pci_dev *pdev)
 	struct ngbe_adapter *adapter = pci_get_drvdata(pdev);
 	struct net_device *netdev;
 
+	set_bit(NGBE_REMOVING, adapter->state);
+	cancel_work_sync(&adapter->serv_task);
+	ngbe_service_event_stop(adapter);
+
 	netdev = adapter->netdev;
 	unregister_netdev(netdev);
 	pci_release_selected_regions(pdev,
@@ -550,7 +880,45 @@ static struct pci_driver ngbe_driver = {
 	.shutdown = ngbe_shutdown,
 };
 
-module_pci_driver(ngbe_driver);
+/**
+ * ngbe_module_init - Driver registration routine
+ *
+ * ngbe_module_init is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ */
+static int __init ngbe_module_init(void)
+{
+	int status;
+
+	ngbe_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
+	if (!ngbe_wq) {
+		pr_err("Failed to create workqueue\n");
+		return -ENOMEM;
+	}
+
+	status = pci_register_driver(&ngbe_driver);
+	if (status) {
+		pr_err("failed to register PCI driver, err %d\n", status);
+		destroy_workqueue(ngbe_wq);
+	}
+
+	return status;
+}
+module_init(ngbe_module_init);
+
+/**
+ * ngbe_module_exit - Driver exit cleanup routine
+ *
+ * ngbe_module_exit is called just before the driver is removed
+ * from memory.
+ */
+static void __exit ngbe_module_exit(void)
+{
+	pci_unregister_driver(&ngbe_driver);
+	destroy_workqueue(ngbe_wq);
+	pr_info("module unloaded\n");
+}
+module_exit(ngbe_module_exit);
 
 MODULE_DEVICE_TABLE(pci, ngbe_pci_tbl);
 MODULE_AUTHOR("Beijing WangXun Technology Co., Ltd, <software@net-swift.com>");
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
index f6b257e84319..672716e47042 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -83,6 +83,7 @@ enum NGBE_MSCA_CMD_value {
 #define NGBE_MSCC_BUSY				BIT(22)
 #define NGBE_MDIO_CLK(v)			((0x7 & (v)) << 19)
 
+#define NGBE_CFG_LAN_SPEED			0x14440
 /* GPIO Registers */
 #define NGBE_GPIO_DR				0x14800
 #define NGBE_GPIO_DDR				0x14804
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module
  2022-11-08 11:19 ` [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module Mengyuan Lou
@ 2022-11-08 20:52   ` Andrew Lunn
  2022-11-09  6:32     ` Jiawen Wu
  2022-11-14 15:34   ` Alexander Lobakin
  1 sibling, 1 reply; 25+ messages in thread
From: Andrew Lunn @ 2022-11-08 20:52 UTC (permalink / raw)
  To: Mengyuan Lou; +Cc: netdev, jiawenwu

> +/**
> + *  txgbe_identify_sfp_module - Identifies SFP modules
> + *  @hw: pointer to hardware structure
> + *
> + *  Searches for and identifies the SFP module and assigns appropriate PHY type.
> + **/
> +static int txgbe_identify_sfp_module(struct txgbe_hw *hw)
> +{
> +	u8 oui_bytes[3] = {0, 0, 0};
> +	u8 comp_codes_10g = 0;
> +	u8 comp_codes_1g = 0;
> +	int status = -EFAULT;
> +	u32 vendor_oui = 0;
> +	u8 identifier = 0;
> +	u8 cable_tech = 0;
> +	u8 cable_spec = 0;
> +
> +	/* LAN ID is needed for I2C access */
> +	txgbe_init_i2c(hw);
> +
> +	status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_IDENTIFIER, &identifier);
> +	if (status != 0)
> +		goto err_read_i2c_eeprom;
> +
> +	if (identifier != TXGBE_SFF_IDENTIFIER_SFP) {
> +		hw->phy.type = txgbe_phy_sfp_unsupported;
> +		status = -ENODEV;
> +	} else {
> +		status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_1GBE_COMP_CODES,
> +					       &comp_codes_1g);
> +		if (status != 0)
> +			goto err_read_i2c_eeprom;
> +
> +		status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_10GBE_COMP_CODES,
> +					       &comp_codes_10g);
> +		if (status != 0)
> +			goto err_read_i2c_eeprom;
> +
> +		status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_CABLE_TECHNOLOGY,
> +					       &cable_tech);
> +		if (status != 0)
> +			goto err_read_i2c_eeprom;
> +
> +		 /* ID Module
> +		  * =========
> +		  * 1   SFP_DA_CORE
> +		  * 2   SFP_SR/LR_CORE
> +		  * 3   SFP_act_lmt_DA_CORE
> +		  * 4   SFP_1g_cu_CORE
> +		  * 5   SFP_1g_sx_CORE
> +		  * 6   SFP_1g_lx_CORE
> +		  */

So it looks like you have Linux driving the SFP, not firmware. In that
case, please throw all this code away. Implement a standard Linux I2C
bus master driver, and make use of driver/net/phy/sfp*.[ch].

    Andrew

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 3/5] net: txgbe: Support to setup link
  2022-11-08 11:19 ` [PATCH net-next 3/5] net: txgbe: Support to setup link Mengyuan Lou
@ 2022-11-08 21:03   ` Andrew Lunn
  2022-11-14 15:48   ` Alexander Lobakin
  1 sibling, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2022-11-08 21:03 UTC (permalink / raw)
  To: Mengyuan Lou; +Cc: netdev, jiawenwu

On Tue, Nov 08, 2022 at 07:19:05PM +0800, Mengyuan Lou wrote:
> From: Jiawen Wu <jiawenwu@trustnetic.com>
> 
> Get link capabilities, setup MAC and PHY link, and support to enable
> or disable Tx laser.

You should probably take a look in driver/net/pcs, drivers/phy/ for
configuring low level SERDES properties, and
driver/net/phy/phylink*. You need to use these frameworks, not invent
your own.

	Andrew

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 4/5] net: ngbe: Initialize phy information
  2022-11-08 11:19 ` [PATCH net-next 4/5] net: ngbe: Initialize phy information Mengyuan Lou
@ 2022-11-08 21:10   ` Andrew Lunn
  2022-11-11  3:13     ` mengyuanlou
  2022-11-14 16:16   ` Alexander Lobakin
  1 sibling, 1 reply; 25+ messages in thread
From: Andrew Lunn @ 2022-11-08 21:10 UTC (permalink / raw)
  To: Mengyuan Lou; +Cc: netdev, jiawenwu

> +/**
> + *  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");
> +}

This appears to be an MDIO bus? Although you seem to be limited to
just 1 of the 32 addresses? Anyway, please create a standard Linux
MDIO bus driver. The Linux PHY drivers will then drive the PHYs for
you. You can throw most of this file away.

	Andrew

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 2/5] net: txgbe: Initialize service task
  2022-11-08 11:19 ` [PATCH net-next 2/5] net: txgbe: Initialize service task Mengyuan Lou
@ 2022-11-08 23:55   ` Jakub Kicinski
  2022-11-09  2:16     ` Jiawen Wu
  2022-11-14 15:39   ` Alexander Lobakin
  1 sibling, 1 reply; 25+ messages in thread
From: Jakub Kicinski @ 2022-11-08 23:55 UTC (permalink / raw)
  To: Mengyuan Lou; +Cc: netdev, jiawenwu

On Tue,  8 Nov 2022 19:19:04 +0800 Mengyuan Lou wrote:
> +	__TXGBE_TESTING,
> +	__TXGBE_RESETTING,
> +	__TXGBE_DOWN,
> +	__TXGBE_HANGING,
> +	__TXGBE_DISABLED,
> +	__TXGBE_REMOVING,
> +	__TXGBE_SERVICE_SCHED,
> +	__TXGBE_SERVICE_INITED,

Please don't try to implement a state machine in the driver.
Protect data structures with locks, like a normal piece of SW.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* RE: [PATCH net-next 2/5] net: txgbe: Initialize service task
  2022-11-08 23:55   ` Jakub Kicinski
@ 2022-11-09  2:16     ` Jiawen Wu
  2022-11-09  2:36       ` Andrew Lunn
  0 siblings, 1 reply; 25+ messages in thread
From: Jiawen Wu @ 2022-11-09  2:16 UTC (permalink / raw)
  To: 'Jakub Kicinski', 'Mengyuan Lou'; +Cc: netdev

On Wednesday, November 9, 2022 7:56 AM, Jakub wrote:
> On Tue,  8 Nov 2022 19:19:04 +0800 Mengyuan Lou wrote:
> > +	__TXGBE_TESTING,
> > +	__TXGBE_RESETTING,
> > +	__TXGBE_DOWN,
> > +	__TXGBE_HANGING,
> > +	__TXGBE_DISABLED,
> > +	__TXGBE_REMOVING,
> > +	__TXGBE_SERVICE_SCHED,
> > +	__TXGBE_SERVICE_INITED,
> 
> Please don't try to implement a state machine in the driver.
> Protect data structures with locks, like a normal piece of SW.
> 

The state machine will be used in interrupt events, locks don't seem to fit it.


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 2/5] net: txgbe: Initialize service task
  2022-11-09  2:16     ` Jiawen Wu
@ 2022-11-09  2:36       ` Andrew Lunn
  0 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2022-11-09  2:36 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: 'Jakub Kicinski', 'Mengyuan Lou', netdev

On Wed, Nov 09, 2022 at 10:16:32AM +0800, Jiawen Wu wrote:
> On Wednesday, November 9, 2022 7:56 AM, Jakub wrote:
> > On Tue,  8 Nov 2022 19:19:04 +0800 Mengyuan Lou wrote:
> > > +	__TXGBE_TESTING,
> > > +	__TXGBE_RESETTING,
> > > +	__TXGBE_DOWN,
> > > +	__TXGBE_HANGING,
> > > +	__TXGBE_DISABLED,
> > > +	__TXGBE_REMOVING,
> > > +	__TXGBE_SERVICE_SCHED,
> > > +	__TXGBE_SERVICE_INITED,
> > 
> > Please don't try to implement a state machine in the driver.
> > Protect data structures with locks, like a normal piece of SW.
> > 
> 
> The state machine will be used in interrupt events, locks don't seem to fit it.

spinlock can be used with interrupts.

Also, once you make use of phylink, you might not need any of this.

	 Andrew

^ permalink raw reply	[flat|nested] 25+ messages in thread

* RE: [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module
  2022-11-08 20:52   ` Andrew Lunn
@ 2022-11-09  6:32     ` Jiawen Wu
  2022-11-09 13:26       ` Andrew Lunn
  0 siblings, 1 reply; 25+ messages in thread
From: Jiawen Wu @ 2022-11-09  6:32 UTC (permalink / raw)
  To: 'Andrew Lunn', 'Mengyuan Lou'; +Cc: netdev

On Wednesday, November 9, 2022 4:53 AM, Andrew Lunn wrote:
> > +/**
> > + *  txgbe_identify_sfp_module - Identifies SFP modules
> > + *  @hw: pointer to hardware structure
> > + *
> > + *  Searches for and identifies the SFP module and assigns appropriate PHY type.
> > + **/
> > +static int txgbe_identify_sfp_module(struct txgbe_hw *hw) {
> > +	u8 oui_bytes[3] = {0, 0, 0};
> > +	u8 comp_codes_10g = 0;
> > +	u8 comp_codes_1g = 0;
> > +	int status = -EFAULT;
> > +	u32 vendor_oui = 0;
> > +	u8 identifier = 0;
> > +	u8 cable_tech = 0;
> > +	u8 cable_spec = 0;
> > +
> > +	/* LAN ID is needed for I2C access */
> > +	txgbe_init_i2c(hw);
> > +
> > +	status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_IDENTIFIER, &identifier);
> > +	if (status != 0)
> > +		goto err_read_i2c_eeprom;
> > +
> > +	if (identifier != TXGBE_SFF_IDENTIFIER_SFP) {
> > +		hw->phy.type = txgbe_phy_sfp_unsupported;
> > +		status = -ENODEV;
> > +	} else {
> > +		status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_1GBE_COMP_CODES,
> > +					       &comp_codes_1g);
> > +		if (status != 0)
> > +			goto err_read_i2c_eeprom;
> > +
> > +		status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_10GBE_COMP_CODES,
> > +					       &comp_codes_10g);
> > +		if (status != 0)
> > +			goto err_read_i2c_eeprom;
> > +
> > +		status = txgbe_read_i2c_eeprom(hw, TXGBE_SFF_CABLE_TECHNOLOGY,
> > +					       &cable_tech);
> > +		if (status != 0)
> > +			goto err_read_i2c_eeprom;
> > +
> > +		 /* ID Module
> > +		  * =========
> > +		  * 1   SFP_DA_CORE
> > +		  * 2   SFP_SR/LR_CORE
> > +		  * 3   SFP_act_lmt_DA_CORE
> > +		  * 4   SFP_1g_cu_CORE
> > +		  * 5   SFP_1g_sx_CORE
> > +		  * 6   SFP_1g_lx_CORE
> > +		  */
> 
> So it looks like you have Linux driving the SFP, not firmware. In that case, please throw all this
code away.
> Implement a standard Linux I2C bus master driver, and make use of driver/net/phy/sfp*.[ch].
> 
>     Andrew
> 

I don't quite understand how to use driver/net/phy/sfp* files. In txgbe driver, I2C infos are read
from CAB registers, then SFP type identified.
Perhaps implement 'struct sfp_upstream_ops' ? And could you please guide me an example driver of
some docs?



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module
  2022-11-09  6:32     ` Jiawen Wu
@ 2022-11-09 13:26       ` Andrew Lunn
  2022-11-14  9:34         ` Jiawen Wu
  2022-11-29  8:20         ` Jiawen Wu
  0 siblings, 2 replies; 25+ messages in thread
From: Andrew Lunn @ 2022-11-09 13:26 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: 'Mengyuan Lou', netdev

> > So it looks like you have Linux driving the SFP, not firmware. In that case, please throw all this
> code away.
> > Implement a standard Linux I2C bus master driver, and make use of driver/net/phy/sfp*.[ch].
> > 
> >     Andrew
> > 
> 
> I don't quite understand how to use driver/net/phy/sfp* files. In txgbe driver, I2C infos are read
> from CAB registers, then SFP type identified.
> Perhaps implement 'struct sfp_upstream_ops' ? And could you please guide me an example driver of
> some docs?

The SFP driver is currently device tree only, but it should be easy to
add support for a platform device and platform data. That driver needs
to be told about a standard Linux i2c master device, and optionally a
collection of GPIO which connect to the SFP socket.

So you need to implement a standard Linux I2C bus master. Which
basically means being able to send and receive an I2C message. Take a
look at for example drivers/net/ethernet/mellanox/mlxsw/i2c.c . This
driver however does not use it with the SFP driver, since the Mellanox
devices have firmware controlling the SFP. But it will give you the
idea how you can embed an I2C bus driver inside another driver.

For the GPIOs to the SFP socket, TX Enable, LOS, MODDEF etc, you want
a standard Linux GPIO driver. For an example, look at
drivers/net/dsa/vitesse-vsc73xx-core.c.

https://github.com/lunn/linux/blob/v5.0.7-rap/drivers/platform/x86/zii-rap.c
contains an example of registering a bit-bang MDIO
controller. zii_rap_mdio_gpiod_table would become a list of SFP
GPIOs. zii_rap_mdio_init() registers a platform devices which
instantiaces an MDIO bus. You would register a platform device which
instantiates an SFP device.

Once you have an SFP devices you need to extend phylink with a
platform data binding. So you can pass it your SFP device.

This should all be reasonably simple code.

     Andrew

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 4/5] net: ngbe: Initialize phy information
  2022-11-08 21:10   ` Andrew Lunn
@ 2022-11-11  3:13     ` mengyuanlou
  2022-11-11  3:28       ` Andrew Lunn
  0 siblings, 1 reply; 25+ messages in thread
From: mengyuanlou @ 2022-11-11  3:13 UTC (permalink / raw)
  To: Andrew Lunn; +Cc: netdev, jiawenwu



> 2022年11月9日 05:10,Andrew Lunn <andrew@lunn.ch> 写道:
> 
>> +/**
>> + *  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");
>> +}
> 
> This appears to be an MDIO bus? Although you seem to be limited to
> just 1 of the 32 addresses? Anyway, please create a standard Linux
> MDIO bus driver. The Linux PHY drivers will then drive the PHYs for
> you. You can throw most of this file away.
> 
> 	Andrew
If mdio bus is not directly connected to the cpu mac, but can only be converted through the mac chip.
If we need to add an mdio bus driver, how can we provide access to mdio bus the mdio bus driver?
I haven't found the code in for reference at the moment, could you please provide some reference。
sincere thanks。
	Mengyuan Lou

> 


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 4/5] net: ngbe: Initialize phy information
  2022-11-11  3:13     ` mengyuanlou
@ 2022-11-11  3:28       ` Andrew Lunn
  0 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2022-11-11  3:28 UTC (permalink / raw)
  To: mengyuanlou; +Cc: netdev, jiawenwu

> >> +/**
> >> + *  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");
> >> +}
> > 
> > This appears to be an MDIO bus? Although you seem to be limited to
> > just 1 of the 32 addresses? Anyway, please create a standard Linux
> > MDIO bus driver. The Linux PHY drivers will then drive the PHYs for
> > you. You can throw most of this file away.
> > 
> > 	Andrew

> If mdio bus is not directly connected to the cpu mac, but can only be converted through the mac chip.
> If we need to add an mdio bus driver, how can we provide access to mdio bus the mdio bus driver?

The code above gives you 2/3 of an MDIO bus driver.

struct mii_bus {
	struct module *owner;
	const char *name;
	char id[MII_BUS_ID_SIZE];
	void *priv;
	/** @read: Perform a read transfer on the bus */
	int (*read)(struct mii_bus *bus, int addr, int regnum);
	/** @write: Perform a write transfer on the bus */
	int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
	/** @reset: Perform a reset of the bus */
	int (*reset)(struct mii_bus *bus);

You have most of read and write. The rest is optional. So you can
allocate an mii_bus structure using mdiobus_alloc(), fill in read and
write, and register the bus using mdiobus_register().

Many drivers do this. Just picking a random example:
drivers/net/ethernet/dnet.c

     Andrew

^ permalink raw reply	[flat|nested] 25+ messages in thread

* RE: [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module
  2022-11-09 13:26       ` Andrew Lunn
@ 2022-11-14  9:34         ` Jiawen Wu
  2022-11-14 13:25           ` Andrew Lunn
  2022-11-29  8:20         ` Jiawen Wu
  1 sibling, 1 reply; 25+ messages in thread
From: Jiawen Wu @ 2022-11-14  9:34 UTC (permalink / raw)
  To: 'Andrew Lunn'; +Cc: 'Mengyuan Lou', netdev

On Wednesday, November 9, 2022 9:27 PM, Andrew Lunn wrote:
> > > So it looks like you have Linux driving the SFP, not firmware. In
> > > that case, please throw all this
> > code away.
> > > Implement a standard Linux I2C bus master driver, and make use of driver/net/phy/sfp*.[ch].
> > >
> > >     Andrew
> > >
> >
> > I don't quite understand how to use driver/net/phy/sfp* files. In
> > txgbe driver, I2C infos are read from CAB registers, then SFP type identified.
> > Perhaps implement 'struct sfp_upstream_ops' ? And could you please
> > guide me an example driver of some docs?
> 
> The SFP driver is currently device tree only, but it should be easy to add support for a platform
device and
> platform data. That driver needs to be told about a standard Linux i2c master device, and
optionally a
> collection of GPIO which connect to the SFP socket.
> 
> So you need to implement a standard Linux I2C bus master. Which basically means being able to send
> and receive an I2C message. Take a look at for example drivers/net/ethernet/mellanox/mlxsw/i2c.c .
> This driver however does not use it with the SFP driver, since the Mellanox devices have firmware
> controlling the SFP. But it will give you the idea how you can embed an I2C bus driver inside
another
> driver.
> 
> For the GPIOs to the SFP socket, TX Enable, LOS, MODDEF etc, you want a standard Linux GPIO
driver.
> For an example, look at drivers/net/dsa/vitesse-vsc73xx-core.c.
> 
> https://github.com/lunn/linux/blob/v5.0.7-rap/drivers/platform/x86/zii-rap.c
> contains an example of registering a bit-bang MDIO controller. zii_rap_mdio_gpiod_table would
become
> a list of SFP GPIOs. zii_rap_mdio_init() registers a platform devices which instantiaces an MDIO
bus. You
> would register a platform device which instantiates an SFP device.
> 
> Once you have an SFP devices you need to extend phylink with a platform data binding. So you can
pass it
> your SFP device.
> 
> This should all be reasonably simple code.
> 
>      Andrew
> 

When ethernet driver does a reset to the hardware, like 'txgbe_reset_hw', the I2C configuration will
be reset.
It needs to be reconfigured once. So how could I call the I2C function here? Can I treat the I2C
driver as a lib?



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module
  2022-11-14  9:34         ` Jiawen Wu
@ 2022-11-14 13:25           ` Andrew Lunn
  0 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2022-11-14 13:25 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: 'Mengyuan Lou', netdev

> When ethernet driver does a reset to the hardware, like
> 'txgbe_reset_hw', the I2C configuration will be reset.  It needs to
> be reconfigured once. So how could I call the I2C function here? Can
> I treat the I2C driver as a lib?

The I2C driver will be embedded within your MAC driver. So you have
control over it.

How often do you need to call txgbe_reset_hw()?  Hopefully just once
early in the probe? So you can register the I2C bus master with the
I2C core after the reset.

If you need to use txgbe_reset_hw() at other times, you will need a
mutex or similar in .master_xfer function so you don't perform a reset
while an I2C transfer is happening, or start another transfer while a
reset is happening.

	Andrew


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module
  2022-11-08 11:19 ` [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module Mengyuan Lou
  2022-11-08 20:52   ` Andrew Lunn
@ 2022-11-14 15:34   ` Alexander Lobakin
  2022-11-14 20:18     ` Andrew Lunn
  1 sibling, 1 reply; 25+ messages in thread
From: Alexander Lobakin @ 2022-11-14 15:34 UTC (permalink / raw)
  To: Mengyuan Lou; +Cc: Alexander Lobakin, netdev, jiawenwu

From: Mengyuan Lou <mengyuanlou@net-swift.com>
Date: Tue,  8 Nov 2022 19:19:03 +0800

> From: Jiawen Wu <jiawenwu@trustnetic.com>
> 
> Add to get media type and physical layer module, support I2C access.
> 
> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> ---

[...]

> @@ -277,11 +647,30 @@ int txgbe_reset_hw(struct txgbe_hw *hw)
>  	struct wx_hw *wxhw = &hw->wxhw;
>  	int status;
>  
> +	u32 sr_pcs_ctl, sr_pma_mmd_ctl1, sr_an_mmd_ctl, sr_an_mmd_adv_reg2;
> +	u32 vr_xs_or_pcs_mmd_digi_ctl1, curr_vr_xs_or_pcs_mmd_digi_ctl1;
> +	u32 curr_sr_an_mmd_ctl, curr_sr_an_mmd_adv_reg2;
> +	u32 curr_sr_pcs_ctl, curr_sr_pma_mmd_ctl1;

Please merge this with the first declaration block, there must be
only one.
Also, are you sure you need all this simultaneously? Maybe reuse
some of them?

> +
>  	/* Call adapter stop to disable tx/rx and clear interrupts */
>  	status = wx_stop_adapter(wxhw);
>  	if (status != 0)
>  		return status;
>  
> +	/* Identify PHY and related function pointers */

[...]

> +	if (!hw->phy.orig_link_settings_stored) {
> +		hw->phy.orig_sr_pcs_ctl2 = sr_pcs_ctl;
> +		hw->phy.orig_sr_pma_mmd_ctl1 = sr_pma_mmd_ctl1;
> +		hw->phy.orig_sr_an_mmd_ctl = sr_an_mmd_ctl;
> +		hw->phy.orig_sr_an_mmd_adv_reg2 = sr_an_mmd_adv_reg2;
> +		hw->phy.orig_vr_xs_or_pcs_mmd_digi_ctl1 =
> +						vr_xs_or_pcs_mmd_digi_ctl1;
> +		hw->phy.orig_link_settings_stored = true;
> +	} else {
> +		hw->phy.orig_sr_pcs_ctl2 = curr_sr_pcs_ctl;
> +		hw->phy.orig_sr_pma_mmd_ctl1 = curr_sr_pma_mmd_ctl1;
> +		hw->phy.orig_sr_an_mmd_ctl = curr_sr_an_mmd_ctl;
> +		hw->phy.orig_sr_an_mmd_adv_reg2 =
> +					curr_sr_an_mmd_adv_reg2;
> +		hw->phy.orig_vr_xs_or_pcs_mmd_digi_ctl1 =
> +					curr_vr_xs_or_pcs_mmd_digi_ctl1;
> +	}
> +
> +	/*make sure phy power is up*/
> +	msleep(100);

Can we poll something to check if it is up already? 100 ms is a ton,
I would try to avoid such huge sleeps if possible.

> +
>  	/* Store the permanent mac address */
>  	wx_get_mac_addr(wxhw, wxhw->mac.perm_addr);

[...]

> -- 
> 2.38.1

Thanks,
Olek

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 2/5] net: txgbe: Initialize service task
  2022-11-08 11:19 ` [PATCH net-next 2/5] net: txgbe: Initialize service task Mengyuan Lou
  2022-11-08 23:55   ` Jakub Kicinski
@ 2022-11-14 15:39   ` Alexander Lobakin
  1 sibling, 0 replies; 25+ messages in thread
From: Alexander Lobakin @ 2022-11-14 15:39 UTC (permalink / raw)
  To: Mengyuan Lou; +Cc: Alexander Lobakin, netdev, jiawenwu

From: Mengyuan Lou <mengyuanlou@net-swift.com>
Date: Tue,  8 Nov 2022 19:19:04 +0800

> From: Jiawen Wu <jiawenwu@trustnetic.com>
> 
> Setup work queue, and initialize service task to process the following
> tasks.
> 
> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> ---

[...]

> @@ -468,6 +524,7 @@ static int txgbe_probe(struct pci_dev *pdev,
>  	}
>  
>  	netdev->netdev_ops = &txgbe_netdev_ops;
> +	netdev->watchdog_timeo = 5 * HZ;

Default value is 5 sec already[0], why...

>  
>  	/* setup the private structure */
>  	err = txgbe_sw_init(adapter);
> @@ -518,6 +575,11 @@ static int txgbe_probe(struct pci_dev *pdev,
>  	eth_hw_addr_set(netdev, wxhw->mac.perm_addr);
>  	txgbe_mac_set_default_filter(adapter, wxhw->mac.perm_addr);

[...]

> +static int __init txgbe_init_module(void)
> +{
> +	int ret;
> +
> +	txgbe_wq = create_singlethread_workqueue(txgbe_driver_name);
> +	if (!txgbe_wq) {
> +		pr_err("%s: Failed to create workqueue\n", txgbe_driver_name);
> +		return -ENOMEM;
> +	}
> +
> +	ret = pci_register_driver(&txgbe_driver);
> +	return ret;
> +}
> +
> +module_init(txgbe_init_module);

I think no empty lines in between the function and module/initcall
init/exit declaration is preferred.

> +
> +/**
> + * txgbe_exit_module - Driver Exit Cleanup Routine
> + *
> + * txgbe_exit_module is called just before the driver is removed
> + * from memory.
> + **/
> +static void __exit txgbe_exit_module(void)
> +{
> +	pci_unregister_driver(&txgbe_driver);
> +	destroy_workqueue(txgbe_wq);
> +}
> +
> +module_exit(txgbe_exit_module);

^

>  
>  MODULE_DEVICE_TABLE(pci, txgbe_pci_tbl);
>  MODULE_AUTHOR("Beijing WangXun Technology Co., Ltd, <software@trustnetic.com>");
> -- 
> 2.38.1

[0] https://elixir.bootlin.com/linux/v6.1-rc5/source/net/sched/sch_generic.c#L547

Thanks,
Olek

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 3/5] net: txgbe: Support to setup link
  2022-11-08 11:19 ` [PATCH net-next 3/5] net: txgbe: Support to setup link Mengyuan Lou
  2022-11-08 21:03   ` Andrew Lunn
@ 2022-11-14 15:48   ` Alexander Lobakin
  2022-11-14 20:20     ` Andrew Lunn
  1 sibling, 1 reply; 25+ messages in thread
From: Alexander Lobakin @ 2022-11-14 15:48 UTC (permalink / raw)
  To: Mengyuan Lou; +Cc: Alexander Lobakin, netdev, jiawenwu

From: Mengyuan Lou <mengyuanlou@net-swift.com>
Date: Tue,  8 Nov 2022 19:19:05 +0800

> From: Jiawen Wu <jiawenwu@trustnetic.com>
> 
> Get link capabilities, setup MAC and PHY link, and support to enable
> or disable Tx laser.
> 
> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> ---
> 
> [...]
> 
> @@ -46,6 +55,13 @@ struct txgbe_adapter {
>  	struct timer_list service_timer;
>  	struct work_struct service_task;
>  
> +	u32 flags;
> +
> +	bool link_up;

Use can reuse the previous field flags to store link status.

Also pls try to avoid placing bools in structures, there are lots of
discussions around it and how compilers place/declare them, but
better to know for sure it would work the same way on each setup,
right?

> +	u32 link_speed;
> +	unsigned long sfp_poll_time;
> +	unsigned long link_check_timeout;
> +
>  	/* structs defined in txgbe_type.h */
>  	struct txgbe_hw hw;
>  	u16 msg_enable;

[...]

> @@ -188,11 +280,22 @@ struct txgbe_phy_info {
>  	u32 orig_vr_xs_or_pcs_mmd_digi_ctl1;
>  	bool orig_link_settings_stored;
>  	bool multispeed_fiber;
> +	bool autotry_restart;

Could be also one u32, e.g. via bitfields:

	u32 orig_link_settings_stored:1;
	u32 multispeed_fiber:1;
	u32 autotry_restart:1;

There will be 29 free slots more here then for new flags.

> +	u32 autoneg_advertised;
> +	u32 link_mode;
> +};
> +
> +/* link status for KX/KX4 */
> +enum txgbe_link_status {
> +	TXGBE_LINK_STATUS_NONE = 0,
> +	TXGBE_LINK_STATUS_KX,
> +	TXGBE_LINK_STATUS_KX4
>  };
>  
>  struct txgbe_hw {
>  	struct wx_hw wxhw;
>  	struct txgbe_phy_info phy;
> +	enum txgbe_link_status link_status;
>  };
>  
>  #endif /* _TXGBE_TYPE_H_ */
> -- 
> 2.38.1

Thanks,
Olek

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 4/5] net: ngbe: Initialize phy information
  2022-11-08 11:19 ` [PATCH net-next 4/5] net: ngbe: Initialize phy information Mengyuan Lou
  2022-11-08 21:10   ` Andrew Lunn
@ 2022-11-14 16:16   ` Alexander Lobakin
  1 sibling, 0 replies; 25+ messages in thread
From: Alexander Lobakin @ 2022-11-14 16:16 UTC (permalink / raw)
  To: Mengyuan Lou; +Cc: Alexander Lobakin, netdev, jiawenwu

From: Mengyuan Lou <mengyuanlou@net-swift.com>
Date: Tue,  8 Nov 2022 19:19:06 +0800

> Initialize phy media type.
> Initialize phy ops functions.
> 
> Signed-off-by: Mengyuan Lou <mengyuanlou@net-swift.com>
> ---

[...]

> +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;

Just declare @buffer with `= { };` and you won't need to initialize
4 fields (2 in req, 2 here) at runtime.

> +
> +	status = wx_host_interface_command(wxhw, (u32 *)&buffer, sizeof(buffer),

Please don't cast structures to scalars. Can it be done via a union
or so?

> +					   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;

Why perform the first read into @data if it will be overwritten in
100% cases? Would just that work

	if (rd32a(...) == NGBE_...)
		*data = rd32a(...);
	else
		*data = 0xffffffff;

	return 0;

?

> +
> +	return 0;
> +}
> +
>  static int ngbe_reset_misc(struct ngbe_hw *hw)
>  {
>  	struct wx_hw *wxhw = &hw->wxhw;

[...]

> +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 u16 ngbe_...(...)
{
	return rd32(&hw->wxhw, ...);
}

and that's it.

> +}
> +
> +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);
> +}

[...]

> +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;

Same here:

{
	ngbe_phy_write_...(...);
	return ngbe_phy_read_...(...);
}

> +}
> +
> +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;

"val assigned to a value which is never used" -- you don't need to
initialize it with 0 since it's overwritten two lines below.

> +
> +	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) {

	if (hw->led_conf != 0xffffffff)
		return;

Saves one indent level.

> +		/* 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;

I think you could use smth like u32_replace_bits() to do that in a
more robust way.

> +		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;

^ (don't initialize)

> +
> +	if (hw->led_conf != 0xffffffff)
> +		val = hw->led_conf & 0xffff;
> +	else
> +		val = 0x205B;

[...]

> +static int ngbe_gphy_wait_mdio_access_on(struct ngbe_hw *hw)
> +{
> +	u16 val = 0;
> +	int ret = 0;

^, @ret can be left uninitialized.

> +
> +	/* 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;

^, both of them can be left uninitialized.

> +
> +	/* 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;

Is it correct that if the function returns 0, you bail out? Not
`if (ret) return ret`? If it is intended, return 0 here then.

> +
> +	/* 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 void ngbe_check_phy_id(struct ngbe_hw *hw)
> +{
> +	u16 phy_id_high = 0, phy_id_low = 0;
> +	u32 phy_id = 0xffffffff;

All three of them don't need to be initialized.

> +
> +	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;
> +}

[...]

> +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;

		return 0; // would look more digestible

> +	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;

Shouldn't we exit here with -%EINVAL if it's not supported anyway?

> +	}
> +
> +	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;

	int ret;

> +	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;

Same?

> +	}
> +	/* write interrupts bits to register */
> +	ngbe_phy_write_reg(hw, NGBE_PHY_INTM_REG, val);
> +
> +	return ret;
> +}

[...]

> +static int ngbe_gphy_efuse_calibration(struct ngbe_hw *hw)
> +{
> +	u32 efuse[2] = {0, 0};

You can always just use `= { };` next time, for both arrays and
structures. So that you wouldn't need to adjust initializer when
adding new fields or elements (to avoid
-Wmissing-field-initializers).

> +
> +	ngbe_gphy_wait_mdio_access_on(hw);
> +
> +	efuse[0] = hw->phy.gphy_efuse[0];
> +	efuse[1] = hw->phy.gphy_efuse[1];

Nevermind, you don't need to initialize the array at all since you
overwrite both elements.

> +
> +	if (!efuse[0] && !efuse[1]) {
> +		efuse[0] = 0xFFFFFFFF;
> +		efuse[1] = 0xFFFFFFFF;
> +	}

[...]

> +static void ngbe_phy_setup_powerup(struct ngbe_hw *hw)
> +{
> +	struct wx_hw *wxhw = &hw->wxhw;
> +	u16 val = 0;
> +	int ret = 0;

Ok here I got tired of cosplaying a compiler / semantic checker,
could you please maybe recheck all your code and remove initializers
when they aren't needed? You can use `make W=1` if needed to make
sure you didn't miss anything.

> +
> +	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");

[...]

> +		/* open auto sensing */
> +		val = ngbe_phy_read_reg_sds_ext_yt(hw, 0xA5);
> +		val |= 0x8000;

Can such magic values be defined somewhere with some meaningful
names? Like

#define NGBE_SDS_EXT_SOMETHING	BIT(15)

> +		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);

[...]

> @@ -122,9 +216,16 @@ struct ngbe_phy_info {
>  
>  	u32 addr;
>  	u32 id;
> +	u8 phy_mode;

+ a 3-byte hole. Maybe at least combine with ::autoneg?

	u32 gphy_efuse[2];
	u8 phy_mode;

	u8 autoneg:1;
	u32 autoneg_advertised;

This would give you a 2-byte hole instead of 3 and 7 free bits to
store some more flags.

> +	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_ */
> -- 
> 2.38.1

Thanks,
Olek

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module
  2022-11-14 15:34   ` Alexander Lobakin
@ 2022-11-14 20:18     ` Andrew Lunn
  0 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2022-11-14 20:18 UTC (permalink / raw)
  To: Alexander Lobakin; +Cc: Mengyuan Lou, netdev, jiawenwu

On Mon, Nov 14, 2022 at 04:34:38PM +0100, Alexander Lobakin wrote:
> From: Mengyuan Lou <mengyuanlou@net-swift.com>
> Date: Tue,  8 Nov 2022 19:19:03 +0800
> 
> > From: Jiawen Wu <jiawenwu@trustnetic.com>
> > 
> > Add to get media type and physical layer module, support I2C access.
> > 
> > Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> > ---
> 
> [...]
> 
> > @@ -277,11 +647,30 @@ int txgbe_reset_hw(struct txgbe_hw *hw)
> >  	struct wx_hw *wxhw = &hw->wxhw;
> >  	int status;
> >  
> > +	u32 sr_pcs_ctl, sr_pma_mmd_ctl1, sr_an_mmd_ctl, sr_an_mmd_adv_reg2;
> > +	u32 vr_xs_or_pcs_mmd_digi_ctl1, curr_vr_xs_or_pcs_mmd_digi_ctl1;
> > +	u32 curr_sr_an_mmd_ctl, curr_sr_an_mmd_adv_reg2;
> > +	u32 curr_sr_pcs_ctl, curr_sr_pma_mmd_ctl1;
> 
> Please merge this with the first declaration block, there must be
> only one.
> Also, are you sure you need all this simultaneously? Maybe reuse
> some of them?

I would not actually spend too much time reviewing this code. It needs
restructuring into a pcs driver, maybe in driver/net/pcs. And some of
it might get turning into generic PHY driver.

Very likely, this whole driver will get re-written as it is merged.

	Andrew

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH net-next 3/5] net: txgbe: Support to setup link
  2022-11-14 15:48   ` Alexander Lobakin
@ 2022-11-14 20:20     ` Andrew Lunn
  0 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2022-11-14 20:20 UTC (permalink / raw)
  To: Alexander Lobakin; +Cc: Mengyuan Lou, netdev, jiawenwu

On Mon, Nov 14, 2022 at 04:48:24PM +0100, Alexander Lobakin wrote:
> From: Mengyuan Lou <mengyuanlou@net-swift.com>
> Date: Tue,  8 Nov 2022 19:19:05 +0800
> 
> > From: Jiawen Wu <jiawenwu@trustnetic.com>
> > 
> > Get link capabilities, setup MAC and PHY link, and support to enable
> > or disable Tx laser.
> > 
> > Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> > ---
> > 
> > [...]
> > 
> > @@ -46,6 +55,13 @@ struct txgbe_adapter {
> >  	struct timer_list service_timer;
> >  	struct work_struct service_task;
> >  
> > +	u32 flags;
> > +
> > +	bool link_up;
> 
> Use can reuse the previous field flags to store link status.
> 
> Also pls try to avoid placing bools in structures, there are lots of
> discussions around it and how compilers place/declare them, but
> better to know for sure it would work the same way on each setup,
> right?

It is likely a lot of this code goes away one phylink it used.

   Andrew

^ permalink raw reply	[flat|nested] 25+ messages in thread

* RE: [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module
  2022-11-09 13:26       ` Andrew Lunn
  2022-11-14  9:34         ` Jiawen Wu
@ 2022-11-29  8:20         ` Jiawen Wu
  1 sibling, 0 replies; 25+ messages in thread
From: Jiawen Wu @ 2022-11-29  8:20 UTC (permalink / raw)
  To: 'Andrew Lunn'; +Cc: 'Mengyuan Lou', netdev

> The SFP driver is currently device tree only, but it should be easy to add support for a platform
device and
> platform data. That driver needs to be told about a standard Linux i2c master device, and
optionally a
> collection of GPIO which connect to the SFP socket.
> 
> So you need to implement a standard Linux I2C bus master. Which basically means being able to send
> and receive an I2C message. Take a look at for example drivers/net/ethernet/mellanox/mlxsw/i2c.c .
> This driver however does not use it with the SFP driver, since the Mellanox devices have firmware
> controlling the SFP. But it will give you the idea how you can embed an I2C bus driver inside
another
> driver.
> 
> For the GPIOs to the SFP socket, TX Enable, LOS, MODDEF etc, you want a standard Linux GPIO
driver.
> For an example, look at drivers/net/dsa/vitesse-vsc73xx-core.c.
> 
> https://github.com/lunn/linux/blob/v5.0.7-rap/drivers/platform/x86/zii-rap.c
> contains an example of registering a bit-bang MDIO controller. zii_rap_mdio_gpiod_table would
become
> a list of SFP GPIOs. zii_rap_mdio_init() registers a platform devices which instantiaces an MDIO
bus. You
> would register a platform device which instantiates an SFP device.
> 
> Once you have an SFP devices you need to extend phylink with a platform data binding. So you can
pass it
> your SFP device.
> 
> This should all be reasonably simple code.
> 
>      Andrew
> 

Hi Andrew,

I have encountered some difficulties.
When I register a platform device for SFP, then it matches the SFP driver by name. I found that SFP
driver needs to find I2C adapter by device node.
Which device node it needs? There was no device node when I created the I2C device. So it always
probe failed.
Could you please give me some guidance? Thanks.



^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2022-11-29  8:21 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-08 11:19 [PATCH net-next 0/5] net: WangXun ethernet drivers Mengyuan Lou
2022-11-08 11:19 ` [PATCH net-next 1/5] net: txgbe: Identify PHY and SFP module Mengyuan Lou
2022-11-08 20:52   ` Andrew Lunn
2022-11-09  6:32     ` Jiawen Wu
2022-11-09 13:26       ` Andrew Lunn
2022-11-14  9:34         ` Jiawen Wu
2022-11-14 13:25           ` Andrew Lunn
2022-11-29  8:20         ` Jiawen Wu
2022-11-14 15:34   ` Alexander Lobakin
2022-11-14 20:18     ` Andrew Lunn
2022-11-08 11:19 ` [PATCH net-next 2/5] net: txgbe: Initialize service task Mengyuan Lou
2022-11-08 23:55   ` Jakub Kicinski
2022-11-09  2:16     ` Jiawen Wu
2022-11-09  2:36       ` Andrew Lunn
2022-11-14 15:39   ` Alexander Lobakin
2022-11-08 11:19 ` [PATCH net-next 3/5] net: txgbe: Support to setup link Mengyuan Lou
2022-11-08 21:03   ` Andrew Lunn
2022-11-14 15:48   ` Alexander Lobakin
2022-11-14 20:20     ` Andrew Lunn
2022-11-08 11:19 ` [PATCH net-next 4/5] net: ngbe: Initialize phy information Mengyuan Lou
2022-11-08 21:10   ` Andrew Lunn
2022-11-11  3:13     ` mengyuanlou
2022-11-11  3:28       ` Andrew Lunn
2022-11-14 16:16   ` Alexander Lobakin
2022-11-08 11:19 ` [PATCH net-next 5/5] net: ngbe: Initialize service task Mengyuan Lou

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.