[dpdk-dev] [PATCH v2 15/56] net/txgbe: add link status change

Jiawen Wu jiawenwu at trustnetic.com
Mon Oct 5 14:08:29 CEST 2020


From: jiawenwu <jiawenwu at trustnetic.com>

Add ethdev link interrupt handler, MAC setup link and check link status and get capabilities.

Signed-off-by: jiawenwu <jiawenwu at trustnetic.com>
---
 drivers/net/txgbe/base/txgbe_eeprom.h |   3 +
 drivers/net/txgbe/base/txgbe_hw.c     | 504 +++++++++++++++++++++++++-
 drivers/net/txgbe/base/txgbe_hw.h     |  15 +
 drivers/net/txgbe/base/txgbe_phy.c    | 312 ++++++++++++++++
 drivers/net/txgbe/base/txgbe_phy.h    |  12 +
 drivers/net/txgbe/base/txgbe_type.h   |  21 ++
 drivers/net/txgbe/txgbe_ethdev.c      | 148 +++++++-
 drivers/net/txgbe/txgbe_ethdev.h      |   8 +-
 8 files changed, 1018 insertions(+), 5 deletions(-)

diff --git a/drivers/net/txgbe/base/txgbe_eeprom.h b/drivers/net/txgbe/base/txgbe_eeprom.h
index 29973e624..3b7f75a5c 100644
--- a/drivers/net/txgbe/base/txgbe_eeprom.h
+++ b/drivers/net/txgbe/base/txgbe_eeprom.h
@@ -24,6 +24,9 @@
 #define TXGBE_ISCSI_BOOT_CONFIG         0x07
 
 #define TXGBE_DEVICE_CAPS_ALLOW_ANY_SFP		0x1
+#define TXGBE_FW_LESM_PARAMETERS_PTR		0x2
+#define TXGBE_FW_LESM_STATE_1			0x1
+#define TXGBE_FW_LESM_STATE_ENABLED		0x8000 /* LESM Enable bit */
 
 s32 txgbe_init_eeprom_params(struct txgbe_hw *hw);
 s32 txgbe_calc_eeprom_checksum(struct txgbe_hw *hw);
diff --git a/drivers/net/txgbe/base/txgbe_hw.c b/drivers/net/txgbe/base/txgbe_hw.c
index 084879331..385a0f892 100644
--- a/drivers/net/txgbe/base/txgbe_hw.c
+++ b/drivers/net/txgbe/base/txgbe_hw.c
@@ -12,6 +12,10 @@
 #define TXGBE_RAPTOR_MAX_RX_QUEUES 128
 #define TXGBE_RAPTOR_RAR_ENTRIES   128
 
+STATIC s32 txgbe_setup_copper_link_raptor(struct txgbe_hw *hw,
+					 u32 speed,
+					 bool autoneg_wait_to_complete);
+
 /**
  *  txgbe_init_hw - Generic hardware initialization
  *  @hw: pointer to hardware structure
@@ -96,6 +100,118 @@ s32 txgbe_validate_mac_addr(u8 *mac_addr)
 	return status;
 }
 
+/**
+ *  txgbe_need_crosstalk_fix - Determine if we need to do cross talk fix
+ *  @hw: pointer to hardware structure
+ *
+ *  Contains the logic to identify if we need to verify link for the
+ *  crosstalk fix
+ **/
+static bool txgbe_need_crosstalk_fix(struct txgbe_hw *hw)
+{
+
+	/* Does FW say we need the fix */
+	if (!hw->need_crosstalk_fix)
+		return false;
+
+	/* Only consider SFP+ PHYs i.e. media type fiber */
+	switch (hw->phy.media_type) {
+	case txgbe_media_type_fiber:
+	case txgbe_media_type_fiber_qsfp:
+		break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ *  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
+ **/
+s32 txgbe_check_mac_link(struct txgbe_hw *hw, u32 *speed,
+				 bool *link_up, bool link_up_wait_to_complete)
+{
+	u32 links_reg, links_orig;
+	u32 i;
+
+	DEBUGFUNC("txgbe_check_mac_link");
+
+	/* If Crosstalk fix enabled do the sanity check of making sure
+	 * the SFP+ cage is full.
+	 */
+	if (txgbe_need_crosstalk_fix(hw)) {
+		u32 sfp_cage_full;
+
+		switch (hw->mac.type) {
+		case txgbe_mac_raptor:
+			sfp_cage_full = !rd32m(hw, TXGBE_GPIODATA,
+					TXGBE_GPIOBIT_2);
+			break;
+		default:
+			/* sanity check - No SFP+ devices here */
+			sfp_cage_full = false;
+			break;
+		}
+
+		if (!sfp_cage_full) {
+			*link_up = false;
+			*speed = TXGBE_LINK_SPEED_UNKNOWN;
+			return 0;
+		}
+	}
+
+	/* clear the old state */
+	links_orig = rd32(hw, TXGBE_PORTSTAT);
+
+	links_reg = rd32(hw, TXGBE_PORTSTAT);
+
+	if (links_orig != links_reg) {
+		DEBUGOUT("LINKS changed from %08X to %08X\n",
+			  links_orig, links_reg);
+	}
+
+	if (link_up_wait_to_complete) {
+		for (i = 0; i < hw->mac.max_link_up_time; i++) {
+			if (links_reg & TXGBE_PORTSTAT_UP) {
+				*link_up = true;
+				break;
+			} else {
+				*link_up = false;
+			}
+			msec_delay(100);
+			links_reg = rd32(hw, TXGBE_PORTSTAT);
+		}
+	} else {
+		if (links_reg & TXGBE_PORTSTAT_UP)
+			*link_up = true;
+		else
+			*link_up = false;
+	}
+
+	switch (links_reg & TXGBE_PORTSTAT_BW_MASK) {
+	case TXGBE_PORTSTAT_BW_10G:
+		*speed = TXGBE_LINK_SPEED_10GB_FULL;
+		break;
+	case TXGBE_PORTSTAT_BW_1G:
+		*speed = TXGBE_LINK_SPEED_1GB_FULL;
+		break;
+	case TXGBE_PORTSTAT_BW_100M:
+		*speed = TXGBE_LINK_SPEED_100M_FULL;
+		break;
+	default:
+		*speed = TXGBE_LINK_SPEED_UNKNOWN;
+	}
+
+	return 0;
+}
+
 /**
  * txgbe_clear_tx_pending - Clear pending TX work from the PCIe fifo
  * @hw: pointer to the hardware structure
@@ -236,7 +352,8 @@ void txgbe_init_mac_link_ops(struct txgbe_hw *hw)
 	struct txgbe_mac_info *mac = &hw->mac;
 
 	DEBUGFUNC("txgbe_init_mac_link_ops");
-	RTE_SET_USED(mac);
+
+	mac->setup_link = txgbe_setup_mac_link;
 }
 
 /**
@@ -250,6 +367,7 @@ void txgbe_init_mac_link_ops(struct txgbe_hw *hw)
  **/
 s32 txgbe_init_phy_raptor(struct txgbe_hw *hw)
 {
+	struct txgbe_mac_info *mac = &hw->mac;
 	struct txgbe_phy_info *phy = &hw->phy;
 	s32 err = 0;
 
@@ -271,6 +389,23 @@ s32 txgbe_init_phy_raptor(struct txgbe_hw *hw)
 	/* Setup function pointers based on detected SFP module and speeds */
 	txgbe_init_mac_link_ops(hw);
 
+	/* If copper media, overwrite with copper function pointers */
+	if (phy->media_type == txgbe_media_type_copper) {
+		mac->setup_link = txgbe_setup_copper_link_raptor;
+		mac->get_link_capabilities =
+				  txgbe_get_copper_link_capabilities;
+	}
+
+	/* Set necessary function pointers based on PHY type */
+	switch (hw->phy.type) {
+	case txgbe_phy_tn:
+		phy->setup_link = txgbe_setup_phy_link_tnx;
+		phy->check_link = txgbe_check_phy_link_tnx;
+		break;
+	default:
+		break;
+	}
+
 init_phy_ops_out:
 	return err;
 }
@@ -301,6 +436,8 @@ s32 txgbe_init_ops_pf(struct txgbe_hw *hw)
 	phy->write_reg = txgbe_write_phy_reg;
 	phy->read_reg_mdi = txgbe_read_phy_reg_mdi;
 	phy->write_reg_mdi = txgbe_write_phy_reg_mdi;
+	phy->setup_link = txgbe_setup_phy_link;
+	phy->setup_link_speed = txgbe_setup_phy_link_speed;
 	phy->read_i2c_byte = txgbe_read_i2c_byte;
 	phy->write_i2c_byte = txgbe_write_i2c_byte;
 	phy->read_i2c_eeprom = txgbe_read_i2c_eeprom;
@@ -311,6 +448,10 @@ s32 txgbe_init_ops_pf(struct txgbe_hw *hw)
 	mac->init_hw = txgbe_init_hw;
 	mac->reset_hw = txgbe_reset_hw;
 
+	/* Link */
+	mac->get_link_capabilities = txgbe_get_link_capabilities_raptor;
+	mac->check_link = txgbe_check_mac_link;
+
 	/* EEPROM */
 	rom->init_params = txgbe_init_eeprom_params;
 	rom->read16 = txgbe_ee_read16;
@@ -332,6 +473,291 @@ s32 txgbe_init_ops_pf(struct txgbe_hw *hw)
 	return 0;
 }
 
+/**
+ *  txgbe_get_link_capabilities_raptor - Determines link capabilities
+ *  @hw: pointer to hardware structure
+ *  @speed: pointer to link speed
+ *  @autoneg: true when autoneg or autotry is enabled
+ *
+ *  Determines the link capabilities by reading the AUTOC register.
+ **/
+s32 txgbe_get_link_capabilities_raptor(struct txgbe_hw *hw,
+				      u32 *speed,
+				      bool *autoneg)
+{
+	s32 status = 0;
+	u32 autoc = 0;
+
+	DEBUGFUNC("txgbe_get_link_capabilities_raptor");
+
+	/* Check if 1G SFP module. */
+	if (hw->phy.sfp_type == txgbe_sfp_type_1g_cu_core0 ||
+	    hw->phy.sfp_type == txgbe_sfp_type_1g_cu_core1 ||
+	    hw->phy.sfp_type == txgbe_sfp_type_1g_lx_core0 ||
+	    hw->phy.sfp_type == txgbe_sfp_type_1g_lx_core1 ||
+	    hw->phy.sfp_type == txgbe_sfp_type_1g_sx_core0 ||
+	    hw->phy.sfp_type == txgbe_sfp_type_1g_sx_core1) {
+		*speed = TXGBE_LINK_SPEED_1GB_FULL;
+		*autoneg = true;
+		return 0;
+	}
+
+	/*
+	 * Determine link capabilities based on the stored value of AUTOC,
+	 * which represents EEPROM defaults.  If AUTOC value has not
+	 * been stored, use the current register values.
+	 */
+	if (hw->mac.orig_link_settings_stored)
+		autoc = hw->mac.orig_autoc;
+	else
+		autoc = hw->mac.autoc_read(hw);
+
+	switch (autoc & TXGBE_AUTOC_LMS_MASK) {
+	case TXGBE_AUTOC_LMS_1G_LINK_NO_AN:
+		*speed = TXGBE_LINK_SPEED_1GB_FULL;
+		*autoneg = false;
+		break;
+
+	case TXGBE_AUTOC_LMS_10G_LINK_NO_AN:
+		*speed = TXGBE_LINK_SPEED_10GB_FULL;
+		*autoneg = false;
+		break;
+
+	case TXGBE_AUTOC_LMS_1G_AN:
+		*speed = TXGBE_LINK_SPEED_1GB_FULL;
+		*autoneg = true;
+		break;
+
+	case TXGBE_AUTOC_LMS_10Gs:
+		*speed = TXGBE_LINK_SPEED_10GB_FULL;
+		*autoneg = false;
+		break;
+
+	case TXGBE_AUTOC_LMS_KX4_KX_KR:
+	case TXGBE_AUTOC_LMS_KX4_KX_KR_1G_AN:
+		*speed = TXGBE_LINK_SPEED_UNKNOWN;
+		if (autoc & TXGBE_AUTOC_KR_SUPP)
+			*speed |= TXGBE_LINK_SPEED_10GB_FULL;
+		if (autoc & TXGBE_AUTOC_KX4_SUPP)
+			*speed |= TXGBE_LINK_SPEED_10GB_FULL;
+		if (autoc & TXGBE_AUTOC_KX_SUPP)
+			*speed |= TXGBE_LINK_SPEED_1GB_FULL;
+		*autoneg = true;
+		break;
+
+	case TXGBE_AUTOC_LMS_KX4_KX_KR_SGMII:
+		*speed = TXGBE_LINK_SPEED_100M_FULL;
+		if (autoc & TXGBE_AUTOC_KR_SUPP)
+			*speed |= TXGBE_LINK_SPEED_10GB_FULL;
+		if (autoc & TXGBE_AUTOC_KX4_SUPP)
+			*speed |= TXGBE_LINK_SPEED_10GB_FULL;
+		if (autoc & TXGBE_AUTOC_KX_SUPP)
+			*speed |= TXGBE_LINK_SPEED_1GB_FULL;
+		*autoneg = true;
+		break;
+
+	case TXGBE_AUTOC_LMS_SGMII_1G_100M:
+		*speed = TXGBE_LINK_SPEED_1GB_FULL |
+			 TXGBE_LINK_SPEED_100M_FULL |
+			 TXGBE_LINK_SPEED_10M_FULL;
+		*autoneg = false;
+		break;
+
+	default:
+		return TXGBE_ERR_LINK_SETUP;
+	}
+
+	return status;
+}
+
+/**
+ *  txgbe_start_mac_link_raptor - Setup MAC link settings
+ *  @hw: pointer to hardware structure
+ *  @autoneg_wait_to_complete: true when waiting for completion is needed
+ *
+ *  Configures link settings based on values in the txgbe_hw struct.
+ *  Restarts the link.  Performs autonegotiation if needed.
+ **/
+s32 txgbe_start_mac_link_raptor(struct txgbe_hw *hw,
+			       bool autoneg_wait_to_complete)
+{
+	s32 status = 0;
+	bool got_lock = false;
+
+	DEBUGFUNC("txgbe_start_mac_link_raptor");
+
+	UNREFERENCED_PARAMETER(autoneg_wait_to_complete);
+
+	/*  reset_pipeline requires us to hold this lock as it writes to
+	 *  AUTOC.
+	 */
+	if (txgbe_verify_lesm_fw_enabled_raptor(hw)) {
+		status = hw->mac.acquire_swfw_sync(hw, TXGBE_MNGSEM_SWPHY);
+		if (status != 0)
+			goto out;
+
+		got_lock = true;
+	}
+
+	/* Restart link */
+	txgbe_reset_pipeline_raptor(hw);
+
+	if (got_lock)
+		hw->mac.release_swfw_sync(hw, TXGBE_MNGSEM_SWPHY);
+
+	/* Add delay to filter out noises during initial link setup */
+	msec_delay(50);
+
+out:
+	return status;
+}
+
+/**
+ *  txgbe_setup_mac_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 AUTOC register and restarts link.
+ **/
+s32 txgbe_setup_mac_link(struct txgbe_hw *hw,
+			       u32 speed,
+			       bool autoneg_wait_to_complete)
+{
+	bool autoneg = false;
+	s32 status = 0;
+
+	u64 autoc = hw->mac.autoc_read(hw);
+	u64 pma_pmd_10gs = autoc & TXGBE_AUTOC_10Gs_PMA_PMD_MASK;
+	u64 pma_pmd_1g = autoc & TXGBE_AUTOC_1G_PMA_PMD_MASK;
+	u64 link_mode = autoc & TXGBE_AUTOC_LMS_MASK;
+	u64 current_autoc = autoc;
+	u64 orig_autoc = 0;
+	u32 links_reg;
+	u32 i;
+	u32 link_capabilities = TXGBE_LINK_SPEED_UNKNOWN;
+
+	DEBUGFUNC("txgbe_setup_mac_link");
+
+	/* Check to see if speed passed in is supported. */
+	status = hw->mac.get_link_capabilities(hw,
+			&link_capabilities, &autoneg);
+	if (status)
+		return status;
+
+	speed &= link_capabilities;
+	if (speed == TXGBE_LINK_SPEED_UNKNOWN) {
+		return TXGBE_ERR_LINK_SETUP;
+	}
+
+	/* Use stored value (EEPROM defaults) of AUTOC to find KR/KX4 support*/
+	if (hw->mac.orig_link_settings_stored)
+		orig_autoc = hw->mac.orig_autoc;
+	else
+		orig_autoc = autoc;
+
+	link_mode = autoc & TXGBE_AUTOC_LMS_MASK;
+	pma_pmd_1g = autoc & TXGBE_AUTOC_1G_PMA_PMD_MASK;
+
+	if (link_mode == TXGBE_AUTOC_LMS_KX4_KX_KR ||
+	    link_mode == TXGBE_AUTOC_LMS_KX4_KX_KR_1G_AN ||
+	    link_mode == TXGBE_AUTOC_LMS_KX4_KX_KR_SGMII) {
+		/* Set KX4/KX/KR support according to speed requested */
+		autoc &= ~(TXGBE_AUTOC_KX_SUPP |
+			   TXGBE_AUTOC_KX4_SUPP |
+			   TXGBE_AUTOC_KR_SUPP);
+		if (speed & TXGBE_LINK_SPEED_10GB_FULL) {
+			if (orig_autoc & TXGBE_AUTOC_KX4_SUPP)
+				autoc |= TXGBE_AUTOC_KX4_SUPP;
+			if ((orig_autoc & TXGBE_AUTOC_KR_SUPP) &&
+			    (hw->phy.smart_speed_active == false))
+				autoc |= TXGBE_AUTOC_KR_SUPP;
+		}
+		if (speed & TXGBE_LINK_SPEED_1GB_FULL)
+			autoc |= TXGBE_AUTOC_KX_SUPP;
+	} else if ((pma_pmd_1g == TXGBE_AUTOC_1G_SFI) &&
+		   (link_mode == TXGBE_AUTOC_LMS_1G_LINK_NO_AN ||
+		    link_mode == TXGBE_AUTOC_LMS_1G_AN)) {
+		/* Switch from 1G SFI to 10G SFI if requested */
+		if ((speed == TXGBE_LINK_SPEED_10GB_FULL) &&
+		    (pma_pmd_10gs == TXGBE_AUTOC_10Gs_SFI)) {
+			autoc &= ~TXGBE_AUTOC_LMS_MASK;
+			autoc |= TXGBE_AUTOC_LMS_10Gs;
+		}
+	} else if ((pma_pmd_10gs == TXGBE_AUTOC_10Gs_SFI) &&
+		   (link_mode == TXGBE_AUTOC_LMS_10Gs)) {
+		/* Switch from 10G SFI to 1G SFI if requested */
+		if ((speed == TXGBE_LINK_SPEED_1GB_FULL) &&
+		    (pma_pmd_1g == TXGBE_AUTOC_1G_SFI)) {
+			autoc &= ~TXGBE_AUTOC_LMS_MASK;
+			if (autoneg || hw->phy.type == txgbe_phy_qsfp_intel)
+				autoc |= TXGBE_AUTOC_LMS_1G_AN;
+			else
+				autoc |= TXGBE_AUTOC_LMS_1G_LINK_NO_AN;
+		}
+	}
+
+	if (autoc == current_autoc) {
+		return status;
+	}
+
+	autoc &= ~TXGBE_AUTOC_SPEED_MASK;
+	autoc |= TXGBE_AUTOC_SPEED(speed);
+	autoc |= (autoneg ? TXGBE_AUTOC_AUTONEG : 0);
+
+	/* Restart link */
+	hw->mac.autoc_write(hw, autoc);
+
+	/* Only poll for autoneg to complete if specified to do so */
+	if (autoneg_wait_to_complete) {
+		if (link_mode == TXGBE_AUTOC_LMS_KX4_KX_KR ||
+		    link_mode == TXGBE_AUTOC_LMS_KX4_KX_KR_1G_AN ||
+		    link_mode == TXGBE_AUTOC_LMS_KX4_KX_KR_SGMII) {
+			links_reg = 0; /*Just in case Autoneg time=0*/
+			for (i = 0; i < TXGBE_AUTO_NEG_TIME; i++) {
+				links_reg = rd32(hw, TXGBE_PORTSTAT);
+				if (links_reg & TXGBE_PORTSTAT_UP)
+					break;
+				msec_delay(100);
+			}
+			if (!(links_reg & TXGBE_PORTSTAT_UP)) {
+				status = TXGBE_ERR_AUTONEG_NOT_COMPLETE;
+				DEBUGOUT("Autoneg did not complete.\n");
+			}
+		}
+	}
+
+	/* Add delay to filter out noises during initial link setup */
+	msec_delay(50);
+
+	return status;
+}
+
+/**
+ *  txgbe_setup_copper_link_raptor - Set the PHY autoneg advertised field
+ *  @hw: pointer to hardware structure
+ *  @speed: new link speed
+ *  @autoneg_wait_to_complete: true if waiting is needed to complete
+ *
+ *  Restarts link on PHY and MAC based on settings passed in.
+ **/
+STATIC s32 txgbe_setup_copper_link_raptor(struct txgbe_hw *hw,
+					 u32 speed,
+					 bool autoneg_wait_to_complete)
+{
+	s32 status;
+
+	DEBUGFUNC("txgbe_setup_copper_link_raptor");
+
+	/* Setup the PHY according to input speed */
+	status = hw->phy.setup_link_speed(hw, speed,
+					      autoneg_wait_to_complete);
+	/* Set up MAC */
+	txgbe_start_mac_link_raptor(hw, autoneg_wait_to_complete);
+
+	return status;
+}
+
 static int
 txgbe_check_flash_load(struct txgbe_hw *hw, u32 check_bit)
 {
@@ -486,3 +912,79 @@ s32 txgbe_reset_hw(struct txgbe_hw *hw)
 	return status;
 }
 
+/**
+ *  txgbe_verify_lesm_fw_enabled_raptor - Checks LESM FW module state.
+ *  @hw: pointer to hardware structure
+ *
+ *  Returns true if the LESM FW module is present and enabled. Otherwise
+ *  returns false. Smart Speed must be disabled if LESM FW module is enabled.
+ **/
+bool txgbe_verify_lesm_fw_enabled_raptor(struct txgbe_hw *hw)
+{
+	bool lesm_enabled = false;
+	u16 fw_offset, fw_lesm_param_offset, fw_lesm_state;
+	s32 status;
+
+	DEBUGFUNC("txgbe_verify_lesm_fw_enabled_raptor");
+
+	/* get the offset to the Firmware Module block */
+	status = hw->rom.read16(hw, TXGBE_FW_PTR, &fw_offset);
+
+	if ((status != 0) ||
+	    (fw_offset == 0) || (fw_offset == 0xFFFF))
+		goto out;
+
+	/* get the offset to the LESM Parameters block */
+	status = hw->rom.read16(hw, (fw_offset +
+				     TXGBE_FW_LESM_PARAMETERS_PTR),
+				     &fw_lesm_param_offset);
+
+	if ((status != 0) ||
+	    (fw_lesm_param_offset == 0) || (fw_lesm_param_offset == 0xFFFF))
+		goto out;
+
+	/* get the LESM state word */
+	status = hw->rom.read16(hw, (fw_lesm_param_offset +
+				     TXGBE_FW_LESM_STATE_1),
+				     &fw_lesm_state);
+
+	if ((status == 0) &&
+	    (fw_lesm_state & TXGBE_FW_LESM_STATE_ENABLED))
+		lesm_enabled = true;
+
+out:
+	lesm_enabled = false;
+	return lesm_enabled;
+}
+
+/**
+ * txgbe_reset_pipeline_raptor - perform pipeline reset
+ *
+ *  @hw: pointer to hardware structure
+ *
+ * Reset pipeline by asserting Restart_AN together with LMS change to ensure
+ * full pipeline reset.  This function assumes the SW/FW lock is held.
+ **/
+s32 txgbe_reset_pipeline_raptor(struct txgbe_hw *hw)
+{
+	s32 err = 0;
+	u64 autoc;
+
+	autoc = hw->mac.autoc_read(hw);
+
+	/* Enable link if disabled in NVM */
+	if (autoc & TXGBE_AUTOC_LINK_DIA_MASK) {
+		autoc &= ~TXGBE_AUTOC_LINK_DIA_MASK;
+	}
+
+	autoc |= TXGBE_AUTOC_AN_RESTART;
+	/* Write AUTOC register with toggled LMS[2] bit and Restart_AN */
+	hw->mac.autoc_write(hw, autoc ^ TXGBE_AUTOC_LMS_AN);
+
+	/* Write AUTOC register with original LMS field and Restart_AN */
+	hw->mac.autoc_write(hw, autoc);
+	txgbe_flush(hw);
+
+	return err;
+}
+
diff --git a/drivers/net/txgbe/base/txgbe_hw.h b/drivers/net/txgbe/base/txgbe_hw.h
index 22a54da37..4ee4d5654 100644
--- a/drivers/net/txgbe/base/txgbe_hw.h
+++ b/drivers/net/txgbe/base/txgbe_hw.h
@@ -12,11 +12,26 @@ s32 txgbe_init_hw(struct txgbe_hw *hw);
 void txgbe_set_lan_id_multi_port(struct txgbe_hw *hw);
 
 s32 txgbe_validate_mac_addr(u8 *mac_addr);
+
+s32 txgbe_check_mac_link(struct txgbe_hw *hw,
+			       u32 *speed,
+			       bool *link_up, bool link_up_wait_to_complete);
+
 void txgbe_clear_tx_pending(struct txgbe_hw *hw);
+
+extern s32 txgbe_reset_pipeline_raptor(struct txgbe_hw *hw);
+
 s32 txgbe_init_shared_code(struct txgbe_hw *hw);
 s32 txgbe_set_mac_type(struct txgbe_hw *hw);
 s32 txgbe_init_ops_pf(struct txgbe_hw *hw);
+s32 txgbe_get_link_capabilities_raptor(struct txgbe_hw *hw,
+				      u32 *speed, bool *autoneg);
+s32 txgbe_start_mac_link_raptor(struct txgbe_hw *hw,
+			       bool autoneg_wait_to_complete);
+s32 txgbe_setup_mac_link(struct txgbe_hw *hw, u32 speed,
+			       bool autoneg_wait_to_complete);
 void txgbe_init_mac_link_ops(struct txgbe_hw *hw);
 s32 txgbe_reset_hw(struct txgbe_hw *hw);
 s32 txgbe_init_phy_raptor(struct txgbe_hw *hw);
+bool txgbe_verify_lesm_fw_enabled_raptor(struct txgbe_hw *hw);
 #endif /* _TXGBE_HW_H_ */
diff --git a/drivers/net/txgbe/base/txgbe_phy.c b/drivers/net/txgbe/base/txgbe_phy.c
index 5e42dfa23..59d28506e 100644
--- a/drivers/net/txgbe/base/txgbe_phy.c
+++ b/drivers/net/txgbe/base/txgbe_phy.c
@@ -426,6 +426,318 @@ s32 txgbe_write_phy_reg(struct txgbe_hw *hw, u32 reg_addr,
 
 	return err;
 }
+
+/**
+ *  txgbe_setup_phy_link - Set and restart auto-neg
+ *  @hw: pointer to hardware structure
+ *
+ *  Restart auto-negotiation and PHY and waits for completion.
+ **/
+s32 txgbe_setup_phy_link(struct txgbe_hw *hw)
+{
+	s32 err = 0;
+	u16 autoneg_reg = TXGBE_MII_AUTONEG_REG;
+	bool autoneg = false;
+	u32 speed;
+
+	DEBUGFUNC("txgbe_setup_phy_link");
+
+	txgbe_get_copper_link_capabilities(hw, &speed, &autoneg);
+
+	/* Set or unset auto-negotiation 10G advertisement */
+	hw->phy.read_reg(hw, TXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG,
+			     TXGBE_MD_DEV_AUTO_NEG,
+			     &autoneg_reg);
+
+	autoneg_reg &= ~TXGBE_MII_10GBASE_T_ADVERTISE;
+	if ((hw->phy.autoneg_advertised & TXGBE_LINK_SPEED_10GB_FULL) &&
+	    (speed & TXGBE_LINK_SPEED_10GB_FULL))
+		autoneg_reg |= TXGBE_MII_10GBASE_T_ADVERTISE;
+
+	hw->phy.write_reg(hw, TXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG,
+			      TXGBE_MD_DEV_AUTO_NEG,
+			      autoneg_reg);
+
+	hw->phy.read_reg(hw, TXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG,
+			     TXGBE_MD_DEV_AUTO_NEG,
+			     &autoneg_reg);
+
+	/* Set or unset auto-negotiation 5G advertisement */
+	autoneg_reg &= ~TXGBE_MII_5GBASE_T_ADVERTISE;
+	if ((hw->phy.autoneg_advertised & TXGBE_LINK_SPEED_5GB_FULL) &&
+	    (speed & TXGBE_LINK_SPEED_5GB_FULL))
+		autoneg_reg |= TXGBE_MII_5GBASE_T_ADVERTISE;
+
+	/* Set or unset auto-negotiation 2.5G advertisement */
+	autoneg_reg &= ~TXGBE_MII_2_5GBASE_T_ADVERTISE;
+	if ((hw->phy.autoneg_advertised &
+	     TXGBE_LINK_SPEED_2_5GB_FULL) &&
+	    (speed & TXGBE_LINK_SPEED_2_5GB_FULL))
+		autoneg_reg |= TXGBE_MII_2_5GBASE_T_ADVERTISE;
+	/* Set or unset auto-negotiation 1G advertisement */
+	autoneg_reg &= ~TXGBE_MII_1GBASE_T_ADVERTISE;
+	if ((hw->phy.autoneg_advertised & TXGBE_LINK_SPEED_1GB_FULL) &&
+	    (speed & TXGBE_LINK_SPEED_1GB_FULL))
+		autoneg_reg |= TXGBE_MII_1GBASE_T_ADVERTISE;
+
+	hw->phy.write_reg(hw, TXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG,
+			      TXGBE_MD_DEV_AUTO_NEG,
+			      autoneg_reg);
+
+	/* Set or unset auto-negotiation 100M advertisement */
+	hw->phy.read_reg(hw, TXGBE_MII_AUTONEG_ADVERTISE_REG,
+			     TXGBE_MD_DEV_AUTO_NEG,
+			     &autoneg_reg);
+
+	autoneg_reg &= ~(TXGBE_MII_100BASE_T_ADVERTISE |
+			 TXGBE_MII_100BASE_T_ADVERTISE_HALF);
+	if ((hw->phy.autoneg_advertised & TXGBE_LINK_SPEED_100M_FULL) &&
+	    (speed & TXGBE_LINK_SPEED_100M_FULL))
+		autoneg_reg |= TXGBE_MII_100BASE_T_ADVERTISE;
+
+	hw->phy.write_reg(hw, TXGBE_MII_AUTONEG_ADVERTISE_REG,
+			      TXGBE_MD_DEV_AUTO_NEG,
+			      autoneg_reg);
+
+	/* Blocked by MNG FW so don't reset PHY */
+	if (txgbe_check_reset_blocked(hw))
+		return err;
+
+	/* Restart PHY auto-negotiation. */
+	hw->phy.read_reg(hw, TXGBE_MD_AUTO_NEG_CONTROL,
+			     TXGBE_MD_DEV_AUTO_NEG, &autoneg_reg);
+
+	autoneg_reg |= TXGBE_MII_RESTART;
+
+	hw->phy.write_reg(hw, TXGBE_MD_AUTO_NEG_CONTROL,
+			      TXGBE_MD_DEV_AUTO_NEG, autoneg_reg);
+
+	return err;
+}
+
+/**
+ *  txgbe_setup_phy_link_speed - Sets the auto advertised capabilities
+ *  @hw: pointer to hardware structure
+ *  @speed: new link speed
+ *  @autoneg_wait_to_complete: unused
+ **/
+s32 txgbe_setup_phy_link_speed(struct txgbe_hw *hw,
+				       u32 speed,
+				       bool autoneg_wait_to_complete)
+{
+	UNREFERENCED_PARAMETER(autoneg_wait_to_complete);
+
+	DEBUGFUNC("txgbe_setup_phy_link_speed");
+
+	/*
+	 * Clear autoneg_advertised and set new values based on input link
+	 * speed.
+	 */
+	hw->phy.autoneg_advertised = 0;
+
+	if (speed & TXGBE_LINK_SPEED_10GB_FULL)
+		hw->phy.autoneg_advertised |= TXGBE_LINK_SPEED_10GB_FULL;
+
+	if (speed & TXGBE_LINK_SPEED_5GB_FULL)
+		hw->phy.autoneg_advertised |= TXGBE_LINK_SPEED_5GB_FULL;
+
+	if (speed & TXGBE_LINK_SPEED_2_5GB_FULL)
+		hw->phy.autoneg_advertised |= TXGBE_LINK_SPEED_2_5GB_FULL;
+
+	if (speed & TXGBE_LINK_SPEED_1GB_FULL)
+		hw->phy.autoneg_advertised |= TXGBE_LINK_SPEED_1GB_FULL;
+
+	if (speed & TXGBE_LINK_SPEED_100M_FULL)
+		hw->phy.autoneg_advertised |= TXGBE_LINK_SPEED_100M_FULL;
+
+	if (speed & TXGBE_LINK_SPEED_10M_FULL)
+		hw->phy.autoneg_advertised |= TXGBE_LINK_SPEED_10M_FULL;
+
+	/* Setup link based on the new speed settings */
+	hw->phy.setup_link(hw);
+
+	return 0;
+}
+
+/**
+ * txgbe_get_copper_speeds_supported - Get copper link speeds from phy
+ * @hw: pointer to hardware structure
+ *
+ * Determines the supported link capabilities by reading the PHY auto
+ * negotiation register.
+ **/
+static s32 txgbe_get_copper_speeds_supported(struct txgbe_hw *hw)
+{
+	s32 err;
+	u16 speed_ability;
+
+	err = hw->phy.read_reg(hw, TXGBE_MD_PHY_SPEED_ABILITY,
+				      TXGBE_MD_DEV_PMA_PMD,
+				      &speed_ability);
+	if (err)
+		return err;
+
+	if (speed_ability & TXGBE_MD_PHY_SPEED_10G)
+		hw->phy.speeds_supported |= TXGBE_LINK_SPEED_10GB_FULL;
+	if (speed_ability & TXGBE_MD_PHY_SPEED_1G)
+		hw->phy.speeds_supported |= TXGBE_LINK_SPEED_1GB_FULL;
+	if (speed_ability & TXGBE_MD_PHY_SPEED_100M)
+		hw->phy.speeds_supported |= TXGBE_LINK_SPEED_100M_FULL;
+
+	return err;
+}
+
+/**
+ *  txgbe_get_copper_link_capabilities - Determines link capabilities
+ *  @hw: pointer to hardware structure
+ *  @speed: pointer to link speed
+ *  @autoneg: boolean auto-negotiation value
+ **/
+s32 txgbe_get_copper_link_capabilities(struct txgbe_hw *hw,
+					       u32 *speed,
+					       bool *autoneg)
+{
+	s32 err = 0;
+
+	DEBUGFUNC("txgbe_get_copper_link_capabilities");
+
+	*autoneg = true;
+	if (!hw->phy.speeds_supported)
+		err = txgbe_get_copper_speeds_supported(hw);
+
+	*speed = hw->phy.speeds_supported;
+	return err;
+}
+
+/**
+ *  txgbe_check_phy_link_tnx - Determine link and speed status
+ *  @hw: pointer to hardware structure
+ *  @speed: current link speed
+ *  @link_up: true is link is up, false otherwise
+ *
+ *  Reads the VS1 register to determine if link is up and the current speed for
+ *  the PHY.
+ **/
+s32 txgbe_check_phy_link_tnx(struct txgbe_hw *hw, u32 *speed,
+			     bool *link_up)
+{
+	s32 err = 0;
+	u32 time_out;
+	u32 max_time_out = 10;
+	u16 phy_link = 0;
+	u16 phy_speed = 0;
+	u16 phy_data = 0;
+
+	DEBUGFUNC("txgbe_check_phy_link_tnx");
+
+	/* Initialize speed and link to default case */
+	*link_up = false;
+	*speed = TXGBE_LINK_SPEED_10GB_FULL;
+
+	/*
+	 * Check current speed and link status of the PHY register.
+	 * This is a vendor specific register and may have to
+	 * be changed for other copper PHYs.
+	 */
+	for (time_out = 0; time_out < max_time_out; time_out++) {
+		usec_delay(10);
+		err = hw->phy.read_reg(hw,
+					TXGBE_MD_VENDOR_SPECIFIC_1_STATUS,
+					TXGBE_MD_DEV_VENDOR_1,
+					&phy_data);
+		phy_link = phy_data & TXGBE_MD_VENDOR_SPECIFIC_1_LINK_STATUS;
+		phy_speed = phy_data &
+				 TXGBE_MD_VENDOR_SPECIFIC_1_SPEED_STATUS;
+		if (phy_link == TXGBE_MD_VENDOR_SPECIFIC_1_LINK_STATUS) {
+			*link_up = true;
+			if (phy_speed ==
+			    TXGBE_MD_VENDOR_SPECIFIC_1_SPEED_STATUS)
+				*speed = TXGBE_LINK_SPEED_1GB_FULL;
+			break;
+		}
+	}
+
+	return err;
+}
+
+/**
+ *  txgbe_setup_phy_link_tnx - Set and restart auto-neg
+ *  @hw: pointer to hardware structure
+ *
+ *  Restart auto-negotiation and PHY and waits for completion.
+ **/
+s32 txgbe_setup_phy_link_tnx(struct txgbe_hw *hw)
+{
+	s32 err = 0;
+	u16 autoneg_reg = TXGBE_MII_AUTONEG_REG;
+	bool autoneg = false;
+	u32 speed;
+
+	DEBUGFUNC("txgbe_setup_phy_link_tnx");
+
+	txgbe_get_copper_link_capabilities(hw, &speed, &autoneg);
+
+	if (speed & TXGBE_LINK_SPEED_10GB_FULL) {
+		/* Set or unset auto-negotiation 10G advertisement */
+		hw->phy.read_reg(hw, TXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG,
+				     TXGBE_MD_DEV_AUTO_NEG,
+				     &autoneg_reg);
+
+		autoneg_reg &= ~TXGBE_MII_10GBASE_T_ADVERTISE;
+		if (hw->phy.autoneg_advertised & TXGBE_LINK_SPEED_10GB_FULL)
+			autoneg_reg |= TXGBE_MII_10GBASE_T_ADVERTISE;
+
+		hw->phy.write_reg(hw, TXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG,
+				      TXGBE_MD_DEV_AUTO_NEG,
+				      autoneg_reg);
+	}
+
+	if (speed & TXGBE_LINK_SPEED_1GB_FULL) {
+		/* Set or unset auto-negotiation 1G advertisement */
+		hw->phy.read_reg(hw, TXGBE_MII_AUTONEG_XNP_TX_REG,
+				     TXGBE_MD_DEV_AUTO_NEG,
+				     &autoneg_reg);
+
+		autoneg_reg &= ~TXGBE_MII_1GBASE_T_ADVERTISE_XNP_TX;
+		if (hw->phy.autoneg_advertised & TXGBE_LINK_SPEED_1GB_FULL)
+			autoneg_reg |= TXGBE_MII_1GBASE_T_ADVERTISE_XNP_TX;
+
+		hw->phy.write_reg(hw, TXGBE_MII_AUTONEG_XNP_TX_REG,
+				      TXGBE_MD_DEV_AUTO_NEG,
+				      autoneg_reg);
+	}
+
+	if (speed & TXGBE_LINK_SPEED_100M_FULL) {
+		/* Set or unset auto-negotiation 100M advertisement */
+		hw->phy.read_reg(hw, TXGBE_MII_AUTONEG_ADVERTISE_REG,
+				     TXGBE_MD_DEV_AUTO_NEG,
+				     &autoneg_reg);
+
+		autoneg_reg &= ~TXGBE_MII_100BASE_T_ADVERTISE;
+		if (hw->phy.autoneg_advertised & TXGBE_LINK_SPEED_100M_FULL)
+			autoneg_reg |= TXGBE_MII_100BASE_T_ADVERTISE;
+
+		hw->phy.write_reg(hw, TXGBE_MII_AUTONEG_ADVERTISE_REG,
+				      TXGBE_MD_DEV_AUTO_NEG,
+				      autoneg_reg);
+	}
+
+	/* Blocked by MNG FW so don't reset PHY */
+	if (txgbe_check_reset_blocked(hw))
+		return err;
+
+	/* Restart PHY auto-negotiation. */
+	hw->phy.read_reg(hw, TXGBE_MD_AUTO_NEG_CONTROL,
+			     TXGBE_MD_DEV_AUTO_NEG, &autoneg_reg);
+
+	autoneg_reg |= TXGBE_MII_RESTART;
+
+	hw->phy.write_reg(hw, TXGBE_MD_AUTO_NEG_CONTROL,
+			      TXGBE_MD_DEV_AUTO_NEG, autoneg_reg);
+
+	return err;
+}
+
 /**
  *  txgbe_identify_module - Identifies module type
  *  @hw: pointer to hardware structure
diff --git a/drivers/net/txgbe/base/txgbe_phy.h b/drivers/net/txgbe/base/txgbe_phy.h
index 318dca61c..56959b837 100644
--- a/drivers/net/txgbe/base/txgbe_phy.h
+++ b/drivers/net/txgbe/base/txgbe_phy.h
@@ -336,9 +336,21 @@ s32 txgbe_read_phy_reg(struct txgbe_hw *hw, u32 reg_addr,
 			       u32 device_type, u16 *phy_data);
 s32 txgbe_write_phy_reg(struct txgbe_hw *hw, u32 reg_addr,
 				u32 device_type, u16 phy_data);
+s32 txgbe_setup_phy_link(struct txgbe_hw *hw);
+s32 txgbe_setup_phy_link_speed(struct txgbe_hw *hw,
+				       u32 speed,
+				       bool autoneg_wait_to_complete);
+s32 txgbe_get_copper_link_capabilities(struct txgbe_hw *hw,
+					       u32 *speed,
+					       bool *autoneg);
 s32 txgbe_check_reset_blocked(struct txgbe_hw *hw);
 
 /* PHY specific */
+s32 txgbe_check_phy_link_tnx(struct txgbe_hw *hw,
+			     u32 *speed,
+			     bool *link_up);
+s32 txgbe_setup_phy_link_tnx(struct txgbe_hw *hw);
+
 s32 txgbe_identify_module(struct txgbe_hw *hw);
 s32 txgbe_identify_sfp_module(struct txgbe_hw *hw);
 s32 txgbe_identify_qsfp_module(struct txgbe_hw *hw);
diff --git a/drivers/net/txgbe/base/txgbe_type.h b/drivers/net/txgbe/base/txgbe_type.h
index 499603c61..39f8b3565 100644
--- a/drivers/net/txgbe/base/txgbe_type.h
+++ b/drivers/net/txgbe/base/txgbe_type.h
@@ -6,6 +6,7 @@
 #define _TXGBE_TYPE_H_
 
 #define TXGBE_LINK_UP_TIME	90 /* 9.0 Seconds */
+#define TXGBE_AUTO_NEG_TIME	45 /* 4.5 Seconds */
 
 #define TXGBE_ALIGN				128 /* as intel did */
 
@@ -100,6 +101,15 @@ enum txgbe_media_type {
 	txgbe_media_type_virtual
 };
 
+
+/* Smart Speed Settings */
+#define TXGBE_SMARTSPEED_MAX_RETRIES	3
+enum txgbe_smart_speed {
+	txgbe_smart_speed_auto = 0,
+	txgbe_smart_speed_on,
+	txgbe_smart_speed_off
+};
+
 /* PCI bus types */
 enum txgbe_bus_type {
 	txgbe_bus_type_unknown = 0,
@@ -320,6 +330,7 @@ struct txgbe_mac_info {
 	u32 max_rx_queues;
 
 	u8  san_mac_rar_index;
+	bool get_link_status;
 	u64 orig_autoc;  /* cached value of AUTOC */
 	bool orig_link_settings_stored;
 	bool autotry_restart;
@@ -366,6 +377,10 @@ struct txgbe_phy_info {
 	u32 media_type;
 	u32 phy_semaphore_mask;
 	bool reset_disable;
+	u32 autoneg_advertised;
+	u32 speeds_supported;
+	enum txgbe_smart_speed smart_speed;
+	bool smart_speed_active;
 	bool multispeed_fiber;
 	bool qsfp_shared_i2c_bus;
 	u32 nw_mng_if_sel;
@@ -406,9 +421,15 @@ struct txgbe_hw {
 	u16 subsystem_vendor_id;
 
 	bool allow_unsupported_sfp;
+	bool need_crosstalk_fix;
 
 	uint64_t isb_dma;
 	void IOMEM *isb_mem;
+	enum txgbe_link_status {
+		TXGBE_LINK_STATUS_NONE = 0,
+		TXGBE_LINK_STATUS_KX,
+		TXGBE_LINK_STATUS_KX4
+	} link_status;
 	enum txgbe_reset_type {
 		TXGBE_LAN_RESET = 0,
 		TXGBE_SW_RESET,
diff --git a/drivers/net/txgbe/txgbe_ethdev.c b/drivers/net/txgbe/txgbe_ethdev.c
index 1389bd461..062007f02 100644
--- a/drivers/net/txgbe/txgbe_ethdev.c
+++ b/drivers/net/txgbe/txgbe_ethdev.c
@@ -20,7 +20,11 @@
 #include "txgbe_ethdev.h"
 #include "txgbe_rxtx.h"
 
+static int  txgbe_dev_set_link_up(struct rte_eth_dev *dev);
+static int  txgbe_dev_set_link_down(struct rte_eth_dev *dev);
 static void txgbe_dev_close(struct rte_eth_dev *dev);
+static int txgbe_dev_link_update(struct rte_eth_dev *dev,
+				int wait_to_complete);
 
 static void txgbe_dev_link_status_print(struct rte_eth_dev *dev);
 static int txgbe_dev_lsc_interrupt_setup(struct rte_eth_dev *dev, uint8_t on);
@@ -508,6 +512,46 @@ txgbe_dev_phy_intr_setup(struct rte_eth_dev *dev)
 	intr->mask_misc |= TXGBE_ICRMISC_GPIO;
 }
 
+/*
+ * Set device link up: enable tx.
+ */
+static int
+txgbe_dev_set_link_up(struct rte_eth_dev *dev)
+{
+	struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
+
+	if (hw->phy.media_type == txgbe_media_type_copper) {
+		/* Turn on the copper */
+		hw->phy.set_phy_power(hw, true);
+	} else {
+		/* Turn on the laser */
+		hw->mac.enable_tx_laser(hw);
+		txgbe_dev_link_update(dev, 0);
+	}
+
+	return 0;
+}
+
+/*
+ * Set device link down: disable tx.
+ */
+static int
+txgbe_dev_set_link_down(struct rte_eth_dev *dev)
+{
+	struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
+
+	if (hw->phy.media_type == txgbe_media_type_copper) {
+		/* Turn off the copper */
+		hw->phy.set_phy_power(hw, false);
+	} else {
+		/* Turn off the laser */
+		hw->mac.disable_tx_laser(hw);
+		txgbe_dev_link_update(dev, 0);
+	}
+
+	return 0;
+}
+
 /*
  * Reset and stop device.
  */
@@ -612,12 +656,108 @@ txgbe_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 	return 0;
 }
 
+void
+txgbe_dev_setup_link_alarm_handler(void *param)
+{
+	struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
+	struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
+	struct txgbe_interrupt *intr = TXGBE_DEV_INTR(dev);
+	u32 speed;
+	bool autoneg = false;
+
+	speed = hw->phy.autoneg_advertised;
+	if (!speed)
+		hw->mac.get_link_capabilities(hw, &speed, &autoneg);
+
+	hw->mac.setup_link(hw, speed, true);
+
+	intr->flags &= ~TXGBE_FLAG_NEED_LINK_CONFIG;
+}
+
+/* return 0 means link status changed, -1 means not changed */
+int
+txgbe_dev_link_update_share(struct rte_eth_dev *dev,
+			    int wait_to_complete)
+{
+	struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
+	struct rte_eth_link link;
+	u32 link_speed = TXGBE_LINK_SPEED_UNKNOWN;
+	struct txgbe_interrupt *intr = TXGBE_DEV_INTR(dev);
+	bool link_up;
+	int err;
+	int wait = 1;
+
+	memset(&link, 0, sizeof(link));
+	link.link_status = ETH_LINK_DOWN;
+	link.link_speed = ETH_SPEED_NUM_NONE;
+	link.link_duplex = ETH_LINK_HALF_DUPLEX;
+	link.link_autoneg = ETH_LINK_AUTONEG;
+
+	hw->mac.get_link_status = true;
+
+	if (intr->flags & TXGBE_FLAG_NEED_LINK_CONFIG)
+		return rte_eth_linkstatus_set(dev, &link);
+
+	/* check if it needs to wait to complete, if lsc interrupt is enabled */
+	if (wait_to_complete == 0 || dev->data->dev_conf.intr_conf.lsc != 0)
+		wait = 0;
+
+	err = hw->mac.check_link(hw, &link_speed, &link_up, wait);
+
+	if (err != 0) {
+		link.link_speed = ETH_SPEED_NUM_100M;
+		link.link_duplex = ETH_LINK_FULL_DUPLEX;
+		return rte_eth_linkstatus_set(dev, &link);
+	}
+
+	if (link_up == 0) {
+		if (hw->phy.media_type == txgbe_media_type_fiber) {
+			intr->flags |= TXGBE_FLAG_NEED_LINK_CONFIG;
+			rte_eal_alarm_set(10,
+				txgbe_dev_setup_link_alarm_handler, dev);
+		}
+		return rte_eth_linkstatus_set(dev, &link);
+	}
+
+	intr->flags &= ~TXGBE_FLAG_NEED_LINK_CONFIG;
+	link.link_status = ETH_LINK_UP;
+	link.link_duplex = ETH_LINK_FULL_DUPLEX;
+
+	switch (link_speed) {
+	default:
+	case TXGBE_LINK_SPEED_UNKNOWN:
+		link.link_duplex = ETH_LINK_FULL_DUPLEX;
+		link.link_speed = ETH_SPEED_NUM_100M;
+		break;
+
+	case TXGBE_LINK_SPEED_100M_FULL:
+		link.link_speed = ETH_SPEED_NUM_100M;
+		break;
+
+	case TXGBE_LINK_SPEED_1GB_FULL:
+		link.link_speed = ETH_SPEED_NUM_1G;
+		break;
+
+	case TXGBE_LINK_SPEED_2_5GB_FULL:
+		link.link_speed = ETH_SPEED_NUM_2_5G;
+		break;
+
+	case TXGBE_LINK_SPEED_5GB_FULL:
+		link.link_speed = ETH_SPEED_NUM_5G;
+		break;
+
+	case TXGBE_LINK_SPEED_10GB_FULL:
+		link.link_speed = ETH_SPEED_NUM_10G;
+		break;
+	}
+
+	return rte_eth_linkstatus_set(dev, &link);
+}
+
 static int
 txgbe_dev_link_update(struct rte_eth_dev *dev, int wait_to_complete)
 {
-	RTE_SET_USED(dev);
-	RTE_SET_USED(wait_to_complete);
-	return 0;
+	return txgbe_dev_link_update_share(dev, wait_to_complete);
 }
 
 /**
@@ -1002,6 +1142,8 @@ txgbe_configure_msix(struct rte_eth_dev *dev)
 static const struct eth_dev_ops txgbe_eth_dev_ops = {
 	.dev_configure              = txgbe_dev_configure,
 	.dev_infos_get              = txgbe_dev_info_get,
+	.dev_set_link_up            = txgbe_dev_set_link_up,
+	.dev_set_link_down          = txgbe_dev_set_link_down,
 };
 
 RTE_PMD_REGISTER_PCI(net_txgbe, rte_txgbe_pmd);
diff --git a/drivers/net/txgbe/txgbe_ethdev.h b/drivers/net/txgbe/txgbe_ethdev.h
index a3f073b41..1b34ce15c 100644
--- a/drivers/net/txgbe/txgbe_ethdev.h
+++ b/drivers/net/txgbe/txgbe_ethdev.h
@@ -59,7 +59,7 @@ struct txgbe_adapter {
 };
 
 #define TXGBE_DEV_ADAPTER(dev) \
-		((struct txgbe_adapter *)(dev)->data->dev_private)
+	((struct txgbe_adapter *)(dev)->data->dev_private)
 
 #define TXGBE_DEV_HW(dev) \
 	(&((struct txgbe_adapter *)(dev)->data->dev_private)->hw)
@@ -70,6 +70,10 @@ struct txgbe_adapter {
 void txgbe_set_ivar_map(struct txgbe_hw *hw, int8_t direction,
 			       uint8_t queue, uint8_t msix_vector);
 
+int
+txgbe_dev_link_update_share(struct rte_eth_dev *dev,
+		int wait_to_complete);
+
 #define TXGBE_LINK_DOWN_CHECK_TIMEOUT 4000 /* ms */
 #define TXGBE_LINK_UP_CHECK_TIMEOUT   1000 /* ms */
 #define TXGBE_VMDQ_NUM_UC_MAC         4096 /* Maximum nb. of UC MAC addr. */
@@ -87,4 +91,6 @@ void txgbe_set_ivar_map(struct txgbe_hw *hw, int8_t direction,
 #define TXGBE_DEFAULT_TX_HTHRESH      0
 #define TXGBE_DEFAULT_TX_WTHRESH      0
 
+void txgbe_dev_setup_link_alarm_handler(void *param);
+
 #endif /* _TXGBE_ETHDEV_H_ */
-- 
2.18.4





More information about the dev mailing list