net/txgbe: add link status change
authorJiawen Wu <jiawenwu@trustnetic.com>
Mon, 19 Oct 2020 08:53:32 +0000 (16:53 +0800)
committerFerruh Yigit <ferruh.yigit@intel.com>
Tue, 3 Nov 2020 22:24:26 +0000 (23:24 +0100)
Add ethdev link interrupt handler, MAC setup link
and check link status and get capabilities.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
drivers/net/txgbe/base/txgbe_eeprom.h
drivers/net/txgbe/base/txgbe_hw.c
drivers/net/txgbe/base/txgbe_hw.h
drivers/net/txgbe/base/txgbe_phy.c
drivers/net/txgbe/base/txgbe_phy.h
drivers/net/txgbe/base/txgbe_regs.h
drivers/net/txgbe/base/txgbe_type.h
drivers/net/txgbe/txgbe_ethdev.c
drivers/net/txgbe/txgbe_ethdev.h

index 25d0342..26cf084 100644 (file)
@@ -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);
index 31f8688..5b34674 100644 (file)
 #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
@@ -93,6 +97,117 @@ 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 = false;
+                       } else {
+                               *link_up = true;
+                               break;
+                       }
+                       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
@@ -232,7 +347,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;
 }
 
 /**
@@ -246,6 +362,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;
 
@@ -267,6 +384,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;
 }
@@ -297,6 +431,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;
@@ -307,6 +443,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;
@@ -328,6 +468,289 @@ 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_10G:
+               *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)
+                               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_10G;
+               }
+       } else if ((pma_pmd_10gs == TXGBE_AUTOC_10GS_SFI) &&
+                  (link_mode == TXGBE_AUTOC_LMS_10G)) {
+               /* 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)
 {
@@ -481,3 +904,76 @@ mac_reset_top:
        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;
+}
+
index 22a54da..506e4b1 100644 (file)
@@ -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);
+
+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_ */
index 347641c..8dd0fc1 100644 (file)
@@ -425,6 +425,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
index 750934e..9f9b52a 100644 (file)
@@ -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);
index 9acbd3b..607e1df 100644 (file)
@@ -52,7 +52,7 @@
 #define   TXGBE_AUTOC_1G_KX_BX            LS64(1, 9, 0x7)
 #define   TXGBE_AUTOC_AN_RESTART          MS64(12, 0x1)
 #define   TXGBE_AUTOC_LMS_MASK            MS64(13, 0x7)
-#define   TXGBE_AUTOC_LMS_10Gs            LS64(3, 13, 0x7)
+#define   TXGBE_AUTOC_LMS_10G             LS64(3, 13, 0x7)
 #define   TXGBE_AUTOC_LMS_KX4_KX_KR       LS64(4, 13, 0x7)
 #define   TXGBE_AUTOC_LMS_SGMII_1G_100M   LS64(5, 13, 0x7)
 #define   TXGBE_AUTOC_LMS_KX4_KX_KR_1G_AN LS64(6, 13, 0x7)
 #define   TXGBE_AUTOC_KX_SUPP             MS64(30, 0x1)
 #define   TXGBE_AUTOC_KX4_SUPP            MS64(31, 0x1)
 
-#define   TXGBE_AUTOC_10Gs_PMA_PMD_MASK   MS64(48, 0x3)  /* serial */
-#define   TXGBE_AUTOC_10Gs_KR             LS64(0, 48, 0x3)
-#define   TXGBE_AUTOC_10Gs_XFI            LS64(1, 48, 0x3)
-#define   TXGBE_AUTOC_10Gs_SFI            LS64(2, 48, 0x3)
+#define   TXGBE_AUTOC_10GS_PMA_PMD_MASK   MS64(48, 0x3)  /* serial */
+#define   TXGBE_AUTOC_10GS_KR             LS64(0, 48, 0x3)
+#define   TXGBE_AUTOC_10GS_XFI            LS64(1, 48, 0x3)
+#define   TXGBE_AUTOC_10GS_SFI            LS64(2, 48, 0x3)
 #define   TXGBE_AUTOC_LINK_DIA_MASK       MS64(60, 0x7)
 #define   TXGBE_AUTOC_LINK_DIA_D3_MASK    LS64(5, 60, 0x7)
 
index 01af0c9..459f2b8 100644 (file)
@@ -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,
@@ -307,6 +317,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;
@@ -360,6 +371,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;
@@ -402,9 +417,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,
index 92dc197..58d72bb 100644 (file)
 #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 int 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);
@@ -507,6 +511,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.
  */
@@ -611,12 +655,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);
index 8dd6c36..ec8eaaf 100644 (file)
@@ -58,7 +58,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)
@@ -69,6 +69,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. */
@@ -86,4 +90,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_ */