]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
ixgbe: correct disable_rx_buff timeout
[karo-tx-linux.git] / drivers / net / ethernet / intel / ixgbe / ixgbe_common.c
index 383b9413292e8b3f1e3fcad83716f0adafb6ac4d..77ac41feb0fef0360d671a550f7811f83ee14e61 100644 (file)
@@ -47,13 +47,6 @@ static void ixgbe_lower_eeprom_clk(struct ixgbe_hw *hw, u32 *eec);
 static void ixgbe_release_eeprom(struct ixgbe_hw *hw);
 
 static s32 ixgbe_mta_vector(struct ixgbe_hw *hw, u8 *mc_addr);
-static s32 ixgbe_fc_autoneg_fiber(struct ixgbe_hw *hw);
-static s32 ixgbe_fc_autoneg_backplane(struct ixgbe_hw *hw);
-static s32 ixgbe_fc_autoneg_copper(struct ixgbe_hw *hw);
-static s32 ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw);
-static s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg,
-                             u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm);
-static s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num);
 static s32 ixgbe_poll_eerd_eewr_done(struct ixgbe_hw *hw, u32 ee_reg);
 static s32 ixgbe_read_eeprom_buffer_bit_bang(struct ixgbe_hw *hw, u16 offset,
                                             u16 words, u16 *data);
@@ -63,6 +56,172 @@ static s32 ixgbe_detect_eeprom_page_size_generic(struct ixgbe_hw *hw,
                                                 u16 offset);
 static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw);
 
+/**
+ *  ixgbe_device_supports_autoneg_fc - Check if phy supports autoneg flow
+ *  control
+ *  @hw: pointer to hardware structure
+ *
+ *  There are several phys that do not support autoneg flow control. This
+ *  function check the device id to see if the associated phy supports
+ *  autoneg flow control.
+ **/
+static s32 ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw)
+{
+
+       switch (hw->device_id) {
+       case IXGBE_DEV_ID_X540T:
+               return 0;
+       case IXGBE_DEV_ID_82599_T3_LOM:
+               return 0;
+       default:
+               return IXGBE_ERR_FC_NOT_SUPPORTED;
+       }
+}
+
+/**
+ *  ixgbe_setup_fc - Set up flow control
+ *  @hw: pointer to hardware structure
+ *
+ *  Called at init time to set up flow control.
+ **/
+static s32 ixgbe_setup_fc(struct ixgbe_hw *hw)
+{
+       s32 ret_val = 0;
+       u32 reg = 0, reg_bp = 0;
+       u16 reg_cu = 0;
+
+       /*
+        * Validate the requested mode.  Strict IEEE mode does not allow
+        * ixgbe_fc_rx_pause because it will cause us to fail at UNH.
+        */
+       if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) {
+               hw_dbg(hw, "ixgbe_fc_rx_pause not valid in strict IEEE mode\n");
+               ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
+               goto out;
+       }
+
+       /*
+        * 10gig parts do not have a word in the EEPROM to determine the
+        * default flow control setting, so we explicitly set it to full.
+        */
+       if (hw->fc.requested_mode == ixgbe_fc_default)
+               hw->fc.requested_mode = ixgbe_fc_full;
+
+       /*
+        * Set up the 1G and 10G flow control advertisement registers so the
+        * HW will be able to do fc autoneg once the cable is plugged in.  If
+        * we link at 10G, the 1G advertisement is harmless and vice versa.
+        */
+       switch (hw->phy.media_type) {
+       case ixgbe_media_type_fiber:
+       case ixgbe_media_type_backplane:
+               reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANA);
+               reg_bp = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+               break;
+       case ixgbe_media_type_copper:
+               hw->phy.ops.read_reg(hw, MDIO_AN_ADVERTISE,
+                                       MDIO_MMD_AN, &reg_cu);
+               break;
+       default:
+               break;
+       }
+
+       /*
+        * The possible values of fc.requested_mode are:
+        * 0: Flow control is completely disabled
+        * 1: Rx flow control is enabled (we can receive pause frames,
+        *    but not send pause frames).
+        * 2: Tx flow control is enabled (we can send pause frames but
+        *    we do not support receiving pause frames).
+        * 3: Both Rx and Tx flow control (symmetric) are enabled.
+        * other: Invalid.
+        */
+       switch (hw->fc.requested_mode) {
+       case ixgbe_fc_none:
+               /* Flow control completely disabled by software override. */
+               reg &= ~(IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE);
+               if (hw->phy.media_type == ixgbe_media_type_backplane)
+                       reg_bp &= ~(IXGBE_AUTOC_SYM_PAUSE |
+                                   IXGBE_AUTOC_ASM_PAUSE);
+               else if (hw->phy.media_type == ixgbe_media_type_copper)
+                       reg_cu &= ~(IXGBE_TAF_SYM_PAUSE | IXGBE_TAF_ASM_PAUSE);
+               break;
+       case ixgbe_fc_tx_pause:
+               /*
+                * Tx Flow control is enabled, and Rx Flow control is
+                * disabled by software override.
+                */
+               reg |= IXGBE_PCS1GANA_ASM_PAUSE;
+               reg &= ~IXGBE_PCS1GANA_SYM_PAUSE;
+               if (hw->phy.media_type == ixgbe_media_type_backplane) {
+                       reg_bp |= IXGBE_AUTOC_ASM_PAUSE;
+                       reg_bp &= ~IXGBE_AUTOC_SYM_PAUSE;
+               } else if (hw->phy.media_type == ixgbe_media_type_copper) {
+                       reg_cu |= IXGBE_TAF_ASM_PAUSE;
+                       reg_cu &= ~IXGBE_TAF_SYM_PAUSE;
+               }
+               break;
+       case ixgbe_fc_rx_pause:
+               /*
+                * Rx Flow control is enabled and Tx Flow control is
+                * disabled by software override. Since there really
+                * isn't a way to advertise that we are capable of RX
+                * Pause ONLY, we will advertise that we support both
+                * symmetric and asymmetric Rx PAUSE, as such we fall
+                * through to the fc_full statement.  Later, we will
+                * disable the adapter's ability to send PAUSE frames.
+                */
+       case ixgbe_fc_full:
+               /* Flow control (both Rx and Tx) is enabled by SW override. */
+               reg |= IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE;
+               if (hw->phy.media_type == ixgbe_media_type_backplane)
+                       reg_bp |= IXGBE_AUTOC_SYM_PAUSE |
+                                 IXGBE_AUTOC_ASM_PAUSE;
+               else if (hw->phy.media_type == ixgbe_media_type_copper)
+                       reg_cu |= IXGBE_TAF_SYM_PAUSE | IXGBE_TAF_ASM_PAUSE;
+               break;
+       default:
+               hw_dbg(hw, "Flow control param set incorrectly\n");
+               ret_val = IXGBE_ERR_CONFIG;
+               goto out;
+               break;
+       }
+
+       if (hw->mac.type != ixgbe_mac_X540) {
+               /*
+                * Enable auto-negotiation between the MAC & PHY;
+                * the MAC will advertise clause 37 flow control.
+                */
+               IXGBE_WRITE_REG(hw, IXGBE_PCS1GANA, reg);
+               reg = IXGBE_READ_REG(hw, IXGBE_PCS1GLCTL);
+
+               /* Disable AN timeout */
+               if (hw->fc.strict_ieee)
+                       reg &= ~IXGBE_PCS1GLCTL_AN_1G_TIMEOUT_EN;
+
+               IXGBE_WRITE_REG(hw, IXGBE_PCS1GLCTL, reg);
+               hw_dbg(hw, "Set up FC; PCS1GLCTL = 0x%08X\n", reg);
+       }
+
+       /*
+        * AUTOC restart handles negotiation of 1G and 10G on backplane
+        * and copper. There is no need to set the PCS1GCTL register.
+        *
+        */
+       if (hw->phy.media_type == ixgbe_media_type_backplane) {
+               reg_bp |= IXGBE_AUTOC_AN_RESTART;
+               IXGBE_WRITE_REG(hw, IXGBE_AUTOC, reg_bp);
+       } else if ((hw->phy.media_type == ixgbe_media_type_copper) &&
+                   (ixgbe_device_supports_autoneg_fc(hw) == 0)) {
+               hw->phy.ops.write_reg(hw, MDIO_AN_ADVERTISE,
+                                     MDIO_MMD_AN, reg_cu);
+       }
+
+       hw_dbg(hw, "Set up FC; IXGBE_AUTOC = 0x%08X\n", reg);
+out:
+       return ret_val;
+}
+
 /**
  *  ixgbe_start_hw_generic - Prepare hardware for Tx/Rx
  *  @hw: pointer to hardware structure
@@ -95,7 +254,7 @@ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw)
        IXGBE_WRITE_FLUSH(hw);
 
        /* Setup flow control */
-       ixgbe_setup_fc(hw, 0);
+       ixgbe_setup_fc(hw);
 
        /* Clear adapter stopped flag */
        hw->adapter_stopped = false;
@@ -128,14 +287,14 @@ s32 ixgbe_start_hw_gen2(struct ixgbe_hw *hw)
        /* Disable relaxed ordering */
        for (i = 0; i < hw->mac.max_tx_queues; i++) {
                regval = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL_82599(i));
-               regval &= ~IXGBE_DCA_TXCTRL_TX_WB_RO_EN;
+               regval &= ~IXGBE_DCA_TXCTRL_DESC_WRO_EN;
                IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL_82599(i), regval);
        }
 
        for (i = 0; i < hw->mac.max_rx_queues; i++) {
                regval = IXGBE_READ_REG(hw, IXGBE_DCA_RXCTRL(i));
-               regval &= ~(IXGBE_DCA_RXCTRL_DESC_WRO_EN |
-                                       IXGBE_DCA_RXCTRL_DESC_HSRO_EN);
+               regval &= ~(IXGBE_DCA_RXCTRL_DATA_WRO_EN |
+                           IXGBE_DCA_RXCTRL_HEAD_WRO_EN);
                IXGBE_WRITE_REG(hw, IXGBE_DCA_RXCTRL(i), regval);
        }
 
@@ -1923,30 +2082,36 @@ s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw)
 /**
  *  ixgbe_fc_enable_generic - Enable flow control
  *  @hw: pointer to hardware structure
- *  @packetbuf_num: packet buffer number (0-7)
  *
  *  Enable flow control according to the current settings.
  **/
-s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num)
+s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw)
 {
        s32 ret_val = 0;
        u32 mflcn_reg, fccfg_reg;
        u32 reg;
        u32 fcrtl, fcrth;
+       int i;
 
-#ifdef CONFIG_DCB
-       if (hw->fc.requested_mode == ixgbe_fc_pfc)
+       /*
+        * Validate the water mark configuration for packet buffer 0.  Zero
+        * water marks indicate that the packet buffer was not configured
+        * and the watermarks for packet buffer 0 should always be configured.
+        */
+       if (!hw->fc.low_water ||
+           !hw->fc.high_water[0] ||
+           !hw->fc.pause_time) {
+               hw_dbg(hw, "Invalid water mark configuration\n");
+               ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
                goto out;
+       }
 
-#endif /* CONFIG_DCB */
        /* Negotiate the fc mode to use */
-       ret_val = ixgbe_fc_autoneg(hw);
-       if (ret_val == IXGBE_ERR_FLOW_CONTROL)
-               goto out;
+       ixgbe_fc_autoneg(hw);
 
        /* Disable any previous flow control settings */
        mflcn_reg = IXGBE_READ_REG(hw, IXGBE_MFLCN);
-       mflcn_reg &= ~(IXGBE_MFLCN_RFCE | IXGBE_MFLCN_RPFCE);
+       mflcn_reg &= ~(IXGBE_MFLCN_RPFCE_MASK | IXGBE_MFLCN_RFCE);
 
        fccfg_reg = IXGBE_READ_REG(hw, IXGBE_FCCFG);
        fccfg_reg &= ~(IXGBE_FCCFG_TFCE_802_3X | IXGBE_FCCFG_TFCE_PRIORITY);
@@ -1959,9 +2124,6 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num)
         * 2: Tx flow control is enabled (we can send pause frames but
         *    we do not support receiving pause frames).
         * 3: Both Rx and Tx flow control (symmetric) are enabled.
-#ifdef CONFIG_DCB
-        * 4: Priority Flow Control is enabled.
-#endif
         * other: Invalid.
         */
        switch (hw->fc.current_mode) {
@@ -1994,11 +2156,6 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num)
                mflcn_reg |= IXGBE_MFLCN_RFCE;
                fccfg_reg |= IXGBE_FCCFG_TFCE_802_3X;
                break;
-#ifdef CONFIG_DCB
-       case ixgbe_fc_pfc:
-               goto out;
-               break;
-#endif /* CONFIG_DCB */
        default:
                hw_dbg(hw, "Flow control param set incorrectly\n");
                ret_val = IXGBE_ERR_CONFIG;
@@ -2011,121 +2168,112 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num)
        IXGBE_WRITE_REG(hw, IXGBE_MFLCN, mflcn_reg);
        IXGBE_WRITE_REG(hw, IXGBE_FCCFG, fccfg_reg);
 
-       fcrth = hw->fc.high_water[packetbuf_num] << 10;
-       fcrtl = hw->fc.low_water << 10;
+       fcrtl = (hw->fc.low_water << 10) | IXGBE_FCRTL_XONE;
 
-       if (hw->fc.current_mode & ixgbe_fc_tx_pause) {
-               fcrth |= IXGBE_FCRTH_FCEN;
-               if (hw->fc.send_xon)
-                       fcrtl |= IXGBE_FCRTL_XONE;
-       }
+       /* Set up and enable Rx high/low water mark thresholds, enable XON. */
+       for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
+               if ((hw->fc.current_mode & ixgbe_fc_tx_pause) &&
+                   hw->fc.high_water[i]) {
+                       IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), fcrtl);
+                       fcrth = (hw->fc.high_water[i] << 10) | IXGBE_FCRTH_FCEN;
+               } else {
+                       IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), 0);
+                       /*
+                        * In order to prevent Tx hangs when the internal Tx
+                        * switch is enabled we must set the high water mark
+                        * to the maximum FCRTH value.  This allows the Tx
+                        * switch to function even under heavy Rx workloads.
+                        */
+                       fcrth = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 32;
+               }
 
-       IXGBE_WRITE_REG(hw, IXGBE_FCRTH_82599(packetbuf_num), fcrth);
-       IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(packetbuf_num), fcrtl);
+               IXGBE_WRITE_REG(hw, IXGBE_FCRTH_82599(i), fcrth);
+       }
 
        /* Configure pause time (2 TCs per register) */
-       reg = IXGBE_READ_REG(hw, IXGBE_FCTTV(packetbuf_num / 2));
-       if ((packetbuf_num & 1) == 0)
-               reg = (reg & 0xFFFF0000) | hw->fc.pause_time;
-       else
-               reg = (reg & 0x0000FFFF) | (hw->fc.pause_time << 16);
-       IXGBE_WRITE_REG(hw, IXGBE_FCTTV(packetbuf_num / 2), reg);
+       reg = hw->fc.pause_time * 0x00010001;
+       for (i = 0; i < (MAX_TRAFFIC_CLASS / 2); i++)
+               IXGBE_WRITE_REG(hw, IXGBE_FCTTV(i), reg);
 
-       IXGBE_WRITE_REG(hw, IXGBE_FCRTV, (hw->fc.pause_time >> 1));
+       IXGBE_WRITE_REG(hw, IXGBE_FCRTV, hw->fc.pause_time / 2);
 
 out:
        return ret_val;
 }
 
 /**
- *  ixgbe_fc_autoneg - Configure flow control
+ *  ixgbe_negotiate_fc - Negotiate flow control
  *  @hw: pointer to hardware structure
+ *  @adv_reg: flow control advertised settings
+ *  @lp_reg: link partner's flow control settings
+ *  @adv_sym: symmetric pause bit in advertisement
+ *  @adv_asm: asymmetric pause bit in advertisement
+ *  @lp_sym: symmetric pause bit in link partner advertisement
+ *  @lp_asm: asymmetric pause bit in link partner advertisement
  *
- *  Compares our advertised flow control capabilities to those advertised by
- *  our link partner, and determines the proper flow control mode to use.
+ *  Find the intersection between advertised settings and link partner's
+ *  advertised settings
  **/
-s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw)
+static s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg,
+                             u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm)
 {
-       s32 ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED;
-       ixgbe_link_speed speed;
-       bool link_up;
+       if ((!(adv_reg)) ||  (!(lp_reg)))
+               return IXGBE_ERR_FC_NOT_NEGOTIATED;
 
-       if (hw->fc.disable_fc_autoneg)
-               goto out;
+       if ((adv_reg & adv_sym) && (lp_reg & lp_sym)) {
+               /*
+                * Now we need to check if the user selected Rx ONLY
+                * of pause frames.  In this case, we had to advertise
+                * FULL flow control because we could not advertise RX
+                * ONLY. Hence, we must now check to see if we need to
+                * turn OFF the TRANSMISSION of PAUSE frames.
+                */
+               if (hw->fc.requested_mode == ixgbe_fc_full) {
+                       hw->fc.current_mode = ixgbe_fc_full;
+                       hw_dbg(hw, "Flow Control = FULL.\n");
+               } else {
+                       hw->fc.current_mode = ixgbe_fc_rx_pause;
+                       hw_dbg(hw, "Flow Control=RX PAUSE frames only\n");
+               }
+       } else if (!(adv_reg & adv_sym) && (adv_reg & adv_asm) &&
+                  (lp_reg & lp_sym) && (lp_reg & lp_asm)) {
+               hw->fc.current_mode = ixgbe_fc_tx_pause;
+               hw_dbg(hw, "Flow Control = TX PAUSE frames only.\n");
+       } else if ((adv_reg & adv_sym) && (adv_reg & adv_asm) &&
+                  !(lp_reg & lp_sym) && (lp_reg & lp_asm)) {
+               hw->fc.current_mode = ixgbe_fc_rx_pause;
+               hw_dbg(hw, "Flow Control = RX PAUSE frames only.\n");
+       } else {
+               hw->fc.current_mode = ixgbe_fc_none;
+               hw_dbg(hw, "Flow Control = NONE.\n");
+       }
+       return 0;
+}
+
+/**
+ *  ixgbe_fc_autoneg_fiber - Enable flow control on 1 gig fiber
+ *  @hw: pointer to hardware structure
+ *
+ *  Enable flow control according on 1 gig fiber.
+ **/
+static s32 ixgbe_fc_autoneg_fiber(struct ixgbe_hw *hw)
+{
+       u32 pcs_anadv_reg, pcs_lpab_reg, linkstat;
+       s32 ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED;
 
        /*
-        * AN should have completed when the cable was plugged in.
-        * Look for reasons to bail out.  Bail out if:
-        * - FC autoneg is disabled, or if
-        * - link is not up.
-        *
-        * Since we're being called from an LSC, link is already known to be up.
-        * So use link_up_wait_to_complete=false.
+        * On multispeed fiber at 1g, bail out if
+        * - link is up but AN did not complete, or if
+        * - link is up and AN completed but timed out
         */
-       hw->mac.ops.check_link(hw, &speed, &link_up, false);
-       if (!link_up) {
-               ret_val = IXGBE_ERR_FLOW_CONTROL;
+
+       linkstat = IXGBE_READ_REG(hw, IXGBE_PCS1GLSTA);
+       if ((!!(linkstat & IXGBE_PCS1GLSTA_AN_COMPLETE) == 0) ||
+           (!!(linkstat & IXGBE_PCS1GLSTA_AN_TIMED_OUT) == 1))
                goto out;
-       }
 
-       switch (hw->phy.media_type) {
-       /* Autoneg flow control on fiber adapters */
-       case ixgbe_media_type_fiber:
-               if (speed == IXGBE_LINK_SPEED_1GB_FULL)
-                       ret_val = ixgbe_fc_autoneg_fiber(hw);
-               break;
-
-       /* Autoneg flow control on backplane adapters */
-       case ixgbe_media_type_backplane:
-               ret_val = ixgbe_fc_autoneg_backplane(hw);
-               break;
-
-       /* Autoneg flow control on copper adapters */
-       case ixgbe_media_type_copper:
-               if (ixgbe_device_supports_autoneg_fc(hw) == 0)
-                       ret_val = ixgbe_fc_autoneg_copper(hw);
-               break;
-
-       default:
-               break;
-       }
-
-out:
-       if (ret_val == 0) {
-               hw->fc.fc_was_autonegged = true;
-       } else {
-               hw->fc.fc_was_autonegged = false;
-               hw->fc.current_mode = hw->fc.requested_mode;
-       }
-       return ret_val;
-}
-
-/**
- *  ixgbe_fc_autoneg_fiber - Enable flow control on 1 gig fiber
- *  @hw: pointer to hardware structure
- *
- *  Enable flow control according on 1 gig fiber.
- **/
-static s32 ixgbe_fc_autoneg_fiber(struct ixgbe_hw *hw)
-{
-       u32 pcs_anadv_reg, pcs_lpab_reg, linkstat;
-       s32 ret_val;
-
-       /*
-        * On multispeed fiber at 1g, bail out if
-        * - link is up but AN did not complete, or if
-        * - link is up and AN completed but timed out
-        */
-
-       linkstat = IXGBE_READ_REG(hw, IXGBE_PCS1GLSTA);
-       if ((!!(linkstat & IXGBE_PCS1GLSTA_AN_COMPLETE) == 0) ||
-           (!!(linkstat & IXGBE_PCS1GLSTA_AN_TIMED_OUT) == 1)) {
-               ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED;
-               goto out;
-       }
-
-       pcs_anadv_reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANA);
-       pcs_lpab_reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANLP);
+       pcs_anadv_reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANA);
+       pcs_lpab_reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANLP);
 
        ret_val =  ixgbe_negotiate_fc(hw, pcs_anadv_reg,
                               pcs_lpab_reg, IXGBE_PCS1GANA_SYM_PAUSE,
@@ -2146,7 +2294,7 @@ out:
 static s32 ixgbe_fc_autoneg_backplane(struct ixgbe_hw *hw)
 {
        u32 links2, anlp1_reg, autoc_reg, links;
-       s32 ret_val;
+       s32 ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED;
 
        /*
         * On backplane, bail out if
@@ -2154,21 +2302,13 @@ static s32 ixgbe_fc_autoneg_backplane(struct ixgbe_hw *hw)
         * - we are 82599 and link partner is not AN enabled
         */
        links = IXGBE_READ_REG(hw, IXGBE_LINKS);
-       if ((links & IXGBE_LINKS_KX_AN_COMP) == 0) {
-               hw->fc.fc_was_autonegged = false;
-               hw->fc.current_mode = hw->fc.requested_mode;
-               ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED;
+       if ((links & IXGBE_LINKS_KX_AN_COMP) == 0)
                goto out;
-       }
 
        if (hw->mac.type == ixgbe_mac_82599EB) {
                links2 = IXGBE_READ_REG(hw, IXGBE_LINKS2);
-               if ((links2 & IXGBE_LINKS2_AN_SUPPORTED) == 0) {
-                       hw->fc.fc_was_autonegged = false;
-                       hw->fc.current_mode = hw->fc.requested_mode;
-                       ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED;
+               if ((links2 & IXGBE_LINKS2_AN_SUPPORTED) == 0)
                        goto out;
-               }
        }
        /*
         * Read the 10g AN autoc and LP ability registers and resolve
@@ -2210,241 +2350,63 @@ static s32 ixgbe_fc_autoneg_copper(struct ixgbe_hw *hw)
 }
 
 /**
- *  ixgbe_negotiate_fc - Negotiate flow control
- *  @hw: pointer to hardware structure
- *  @adv_reg: flow control advertised settings
- *  @lp_reg: link partner's flow control settings
- *  @adv_sym: symmetric pause bit in advertisement
- *  @adv_asm: asymmetric pause bit in advertisement
- *  @lp_sym: symmetric pause bit in link partner advertisement
- *  @lp_asm: asymmetric pause bit in link partner advertisement
- *
- *  Find the intersection between advertised settings and link partner's
- *  advertised settings
- **/
-static s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg,
-                             u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm)
-{
-       if ((!(adv_reg)) ||  (!(lp_reg)))
-               return IXGBE_ERR_FC_NOT_NEGOTIATED;
-
-       if ((adv_reg & adv_sym) && (lp_reg & lp_sym)) {
-               /*
-                * Now we need to check if the user selected Rx ONLY
-                * of pause frames.  In this case, we had to advertise
-                * FULL flow control because we could not advertise RX
-                * ONLY. Hence, we must now check to see if we need to
-                * turn OFF the TRANSMISSION of PAUSE frames.
-                */
-               if (hw->fc.requested_mode == ixgbe_fc_full) {
-                       hw->fc.current_mode = ixgbe_fc_full;
-                       hw_dbg(hw, "Flow Control = FULL.\n");
-               } else {
-                       hw->fc.current_mode = ixgbe_fc_rx_pause;
-                       hw_dbg(hw, "Flow Control=RX PAUSE frames only\n");
-               }
-       } else if (!(adv_reg & adv_sym) && (adv_reg & adv_asm) &&
-                  (lp_reg & lp_sym) && (lp_reg & lp_asm)) {
-               hw->fc.current_mode = ixgbe_fc_tx_pause;
-               hw_dbg(hw, "Flow Control = TX PAUSE frames only.\n");
-       } else if ((adv_reg & adv_sym) && (adv_reg & adv_asm) &&
-                  !(lp_reg & lp_sym) && (lp_reg & lp_asm)) {
-               hw->fc.current_mode = ixgbe_fc_rx_pause;
-               hw_dbg(hw, "Flow Control = RX PAUSE frames only.\n");
-       } else {
-               hw->fc.current_mode = ixgbe_fc_none;
-               hw_dbg(hw, "Flow Control = NONE.\n");
-       }
-       return 0;
-}
-
-/**
- *  ixgbe_setup_fc - Set up flow control
+ *  ixgbe_fc_autoneg - Configure flow control
  *  @hw: pointer to hardware structure
  *
- *  Called at init time to set up flow control.
+ *  Compares our advertised flow control capabilities to those advertised by
+ *  our link partner, and determines the proper flow control mode to use.
  **/
-static s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num)
+void ixgbe_fc_autoneg(struct ixgbe_hw *hw)
 {
-       s32 ret_val = 0;
-       u32 reg = 0, reg_bp = 0;
-       u16 reg_cu = 0;
-
-#ifdef CONFIG_DCB
-       if (hw->fc.requested_mode == ixgbe_fc_pfc) {
-               hw->fc.current_mode = hw->fc.requested_mode;
-               goto out;
-       }
-
-#endif /* CONFIG_DCB */
-       /* Validate the packetbuf configuration */
-       if (packetbuf_num < 0 || packetbuf_num > 7) {
-               hw_dbg(hw, "Invalid packet buffer number [%d], expected range "
-                      "is 0-7\n", packetbuf_num);
-               ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
-               goto out;
-       }
+       s32 ret_val = IXGBE_ERR_FC_NOT_NEGOTIATED;
+       ixgbe_link_speed speed;
+       bool link_up;
 
        /*
-        * Validate the water mark configuration.  Zero water marks are invalid
-        * because it causes the controller to just blast out fc packets.
+        * AN should have completed when the cable was plugged in.
+        * Look for reasons to bail out.  Bail out if:
+        * - FC autoneg is disabled, or if
+        * - link is not up.
+        *
+        * Since we're being called from an LSC, link is already known to be up.
+        * So use link_up_wait_to_complete=false.
         */
-       if (!hw->fc.low_water ||
-           !hw->fc.high_water[packetbuf_num] ||
-           !hw->fc.pause_time) {
-               hw_dbg(hw, "Invalid water mark configuration\n");
-               ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
+       if (hw->fc.disable_fc_autoneg)
                goto out;
-       }
 
-       /*
-        * Validate the requested mode.  Strict IEEE mode does not allow
-        * ixgbe_fc_rx_pause because it will cause us to fail at UNH.
-        */
-       if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) {
-               hw_dbg(hw, "ixgbe_fc_rx_pause not valid in strict "
-                      "IEEE mode\n");
-               ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
+       hw->mac.ops.check_link(hw, &speed, &link_up, false);
+       if (!link_up)
                goto out;
-       }
-
-       /*
-        * 10gig parts do not have a word in the EEPROM to determine the
-        * default flow control setting, so we explicitly set it to full.
-        */
-       if (hw->fc.requested_mode == ixgbe_fc_default)
-               hw->fc.requested_mode = ixgbe_fc_full;
-
-       /*
-        * Set up the 1G and 10G flow control advertisement registers so the
-        * HW will be able to do fc autoneg once the cable is plugged in.  If
-        * we link at 10G, the 1G advertisement is harmless and vice versa.
-        */
 
        switch (hw->phy.media_type) {
+       /* Autoneg flow control on fiber adapters */
        case ixgbe_media_type_fiber:
+               if (speed == IXGBE_LINK_SPEED_1GB_FULL)
+                       ret_val = ixgbe_fc_autoneg_fiber(hw);
+               break;
+
+       /* Autoneg flow control on backplane adapters */
        case ixgbe_media_type_backplane:
-               reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANA);
-               reg_bp = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+               ret_val = ixgbe_fc_autoneg_backplane(hw);
                break;
 
+       /* Autoneg flow control on copper adapters */
        case ixgbe_media_type_copper:
-               hw->phy.ops.read_reg(hw, MDIO_AN_ADVERTISE,
-                                       MDIO_MMD_AN, &reg_cu);
+               if (ixgbe_device_supports_autoneg_fc(hw) == 0)
+                       ret_val = ixgbe_fc_autoneg_copper(hw);
                break;
 
        default:
-               ;
-       }
-
-       /*
-        * The possible values of fc.requested_mode are:
-        * 0: Flow control is completely disabled
-        * 1: Rx flow control is enabled (we can receive pause frames,
-        *    but not send pause frames).
-        * 2: Tx flow control is enabled (we can send pause frames but
-        *    we do not support receiving pause frames).
-        * 3: Both Rx and Tx flow control (symmetric) are enabled.
-#ifdef CONFIG_DCB
-        * 4: Priority Flow Control is enabled.
-#endif
-        * other: Invalid.
-        */
-       switch (hw->fc.requested_mode) {
-       case ixgbe_fc_none:
-               /* Flow control completely disabled by software override. */
-               reg &= ~(IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE);
-               if (hw->phy.media_type == ixgbe_media_type_backplane)
-                       reg_bp &= ~(IXGBE_AUTOC_SYM_PAUSE |
-                                   IXGBE_AUTOC_ASM_PAUSE);
-               else if (hw->phy.media_type == ixgbe_media_type_copper)
-                       reg_cu &= ~(IXGBE_TAF_SYM_PAUSE | IXGBE_TAF_ASM_PAUSE);
-               break;
-       case ixgbe_fc_rx_pause:
-               /*
-                * Rx Flow control is enabled and Tx Flow control is
-                * disabled by software override. Since there really
-                * isn't a way to advertise that we are capable of RX
-                * Pause ONLY, we will advertise that we support both
-                * symmetric and asymmetric Rx PAUSE.  Later, we will
-                * disable the adapter's ability to send PAUSE frames.
-                */
-               reg |= (IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE);
-               if (hw->phy.media_type == ixgbe_media_type_backplane)
-                       reg_bp |= (IXGBE_AUTOC_SYM_PAUSE |
-                                  IXGBE_AUTOC_ASM_PAUSE);
-               else if (hw->phy.media_type == ixgbe_media_type_copper)
-                       reg_cu |= (IXGBE_TAF_SYM_PAUSE | IXGBE_TAF_ASM_PAUSE);
-               break;
-       case ixgbe_fc_tx_pause:
-               /*
-                * Tx Flow control is enabled, and Rx Flow control is
-                * disabled by software override.
-                */
-               reg |= (IXGBE_PCS1GANA_ASM_PAUSE);
-               reg &= ~(IXGBE_PCS1GANA_SYM_PAUSE);
-               if (hw->phy.media_type == ixgbe_media_type_backplane) {
-                       reg_bp |= (IXGBE_AUTOC_ASM_PAUSE);
-                       reg_bp &= ~(IXGBE_AUTOC_SYM_PAUSE);
-               } else if (hw->phy.media_type == ixgbe_media_type_copper) {
-                       reg_cu |= (IXGBE_TAF_ASM_PAUSE);
-                       reg_cu &= ~(IXGBE_TAF_SYM_PAUSE);
-               }
-               break;
-       case ixgbe_fc_full:
-               /* Flow control (both Rx and Tx) is enabled by SW override. */
-               reg |= (IXGBE_PCS1GANA_SYM_PAUSE | IXGBE_PCS1GANA_ASM_PAUSE);
-               if (hw->phy.media_type == ixgbe_media_type_backplane)
-                       reg_bp |= (IXGBE_AUTOC_SYM_PAUSE |
-                                  IXGBE_AUTOC_ASM_PAUSE);
-               else if (hw->phy.media_type == ixgbe_media_type_copper)
-                       reg_cu |= (IXGBE_TAF_SYM_PAUSE | IXGBE_TAF_ASM_PAUSE);
-               break;
-#ifdef CONFIG_DCB
-       case ixgbe_fc_pfc:
-               goto out;
-               break;
-#endif /* CONFIG_DCB */
-       default:
-               hw_dbg(hw, "Flow control param set incorrectly\n");
-               ret_val = IXGBE_ERR_CONFIG;
-               goto out;
                break;
        }
 
-       if (hw->mac.type != ixgbe_mac_X540) {
-               /*
-                * Enable auto-negotiation between the MAC & PHY;
-                * the MAC will advertise clause 37 flow control.
-                */
-               IXGBE_WRITE_REG(hw, IXGBE_PCS1GANA, reg);
-               reg = IXGBE_READ_REG(hw, IXGBE_PCS1GLCTL);
-
-               /* Disable AN timeout */
-               if (hw->fc.strict_ieee)
-                       reg &= ~IXGBE_PCS1GLCTL_AN_1G_TIMEOUT_EN;
-
-               IXGBE_WRITE_REG(hw, IXGBE_PCS1GLCTL, reg);
-               hw_dbg(hw, "Set up FC; PCS1GLCTL = 0x%08X\n", reg);
-       }
-
-       /*
-        * AUTOC restart handles negotiation of 1G and 10G on backplane
-        * and copper. There is no need to set the PCS1GCTL register.
-        *
-        */
-       if (hw->phy.media_type == ixgbe_media_type_backplane) {
-               reg_bp |= IXGBE_AUTOC_AN_RESTART;
-               IXGBE_WRITE_REG(hw, IXGBE_AUTOC, reg_bp);
-       } else if ((hw->phy.media_type == ixgbe_media_type_copper) &&
-                   (ixgbe_device_supports_autoneg_fc(hw) == 0)) {
-               hw->phy.ops.write_reg(hw, MDIO_AN_ADVERTISE,
-                                     MDIO_MMD_AN, reg_cu);
-       }
-
-       hw_dbg(hw, "Set up FC; IXGBE_AUTOC = 0x%08X\n", reg);
 out:
-       return ret_val;
+       if (ret_val == 0) {
+               hw->fc.fc_was_autonegged = true;
+       } else {
+               hw->fc.fc_was_autonegged = false;
+               hw->fc.current_mode = hw->fc.requested_mode;
+       }
 }
 
 /**
@@ -2577,6 +2539,58 @@ void ixgbe_release_swfw_sync(struct ixgbe_hw *hw, u16 mask)
        ixgbe_release_eeprom_semaphore(hw);
 }
 
+/**
+ *  ixgbe_disable_rx_buff_generic - Stops the receive data path
+ *  @hw: pointer to hardware structure
+ *
+ *  Stops the receive data path and waits for the HW to internally
+ *  empty the Rx security block.
+ **/
+s32 ixgbe_disable_rx_buff_generic(struct ixgbe_hw *hw)
+{
+#define IXGBE_MAX_SECRX_POLL 40
+       int i;
+       int secrxreg;
+
+       secrxreg = IXGBE_READ_REG(hw, IXGBE_SECRXCTRL);
+       secrxreg |= IXGBE_SECRXCTRL_RX_DIS;
+       IXGBE_WRITE_REG(hw, IXGBE_SECRXCTRL, secrxreg);
+       for (i = 0; i < IXGBE_MAX_SECRX_POLL; i++) {
+               secrxreg = IXGBE_READ_REG(hw, IXGBE_SECRXSTAT);
+               if (secrxreg & IXGBE_SECRXSTAT_SECRX_RDY)
+                       break;
+               else
+                       /* Use interrupt-safe sleep just in case */
+                       udelay(1000);
+       }
+
+       /* For informational purposes only */
+       if (i >= IXGBE_MAX_SECRX_POLL)
+               hw_dbg(hw, "Rx unit being enabled before security "
+                      "path fully disabled.  Continuing with init.\n");
+
+       return 0;
+
+}
+
+/**
+ *  ixgbe_enable_rx_buff - Enables the receive data path
+ *  @hw: pointer to hardware structure
+ *
+ *  Enables the receive data path
+ **/
+s32 ixgbe_enable_rx_buff_generic(struct ixgbe_hw *hw)
+{
+       int secrxreg;
+
+       secrxreg = IXGBE_READ_REG(hw, IXGBE_SECRXCTRL);
+       secrxreg &= ~IXGBE_SECRXCTRL_RX_DIS;
+       IXGBE_WRITE_REG(hw, IXGBE_SECRXCTRL, secrxreg);
+       IXGBE_WRITE_FLUSH(hw);
+
+       return 0;
+}
+
 /**
  *  ixgbe_enable_rx_dma_generic - Enable the Rx DMA unit
  *  @hw: pointer to hardware structure
@@ -2724,17 +2738,36 @@ san_mac_addr_out:
  *  Read PCIe configuration space, and get the MSI-X vector count from
  *  the capabilities table.
  **/
-u32 ixgbe_get_pcie_msix_count_generic(struct ixgbe_hw *hw)
+u16 ixgbe_get_pcie_msix_count_generic(struct ixgbe_hw *hw)
 {
        struct ixgbe_adapter *adapter = hw->back;
-       u16 msix_count;
-       pci_read_config_word(adapter->pdev, IXGBE_PCIE_MSIX_82599_CAPS,
-                            &msix_count);
+       u16 msix_count = 1;
+       u16 max_msix_count;
+       u16 pcie_offset;
+
+       switch (hw->mac.type) {
+       case ixgbe_mac_82598EB:
+               pcie_offset = IXGBE_PCIE_MSIX_82598_CAPS;
+               max_msix_count = IXGBE_MAX_MSIX_VECTORS_82598;
+               break;
+       case ixgbe_mac_82599EB:
+       case ixgbe_mac_X540:
+               pcie_offset = IXGBE_PCIE_MSIX_82599_CAPS;
+               max_msix_count = IXGBE_MAX_MSIX_VECTORS_82599;
+               break;
+       default:
+               return msix_count;
+       }
+
+       pci_read_config_word(adapter->pdev, pcie_offset, &msix_count);
        msix_count &= IXGBE_PCIE_MSIX_TBL_SZ_MASK;
 
-       /* MSI-X count is zero-based in HW, so increment to give proper value */
+       /* MSI-X count is zero-based in HW */
        msix_count++;
 
+       if (msix_count > max_msix_count)
+               msix_count = max_msix_count;
+
        return msix_count;
 }
 
@@ -3143,28 +3176,6 @@ wwn_prefix_out:
        return 0;
 }
 
-/**
- *  ixgbe_device_supports_autoneg_fc - Check if phy supports autoneg flow
- *  control
- *  @hw: pointer to hardware structure
- *
- *  There are several phys that do not support autoneg flow control. This
- *  function check the device id to see if the associated phy supports
- *  autoneg flow control.
- **/
-static s32 ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw)
-{
-
-       switch (hw->device_id) {
-       case IXGBE_DEV_ID_X540T:
-               return 0;
-       case IXGBE_DEV_ID_82599_T3_LOM:
-               return 0;
-       default:
-               return IXGBE_ERR_FC_NOT_SUPPORTED;
-       }
-}
-
 /**
  *  ixgbe_set_mac_anti_spoofing - Enable/Disable MAC anti-spoofing
  *  @hw: pointer to hardware structure
@@ -3336,7 +3347,7 @@ static u8 ixgbe_calculate_checksum(u8 *buffer, u32 length)
  *  @hw: pointer to the HW structure
  *  @buffer: contains the command to write and where the return status will
  *           be placed
- *  @lenght: lenght of buffer, must be multiple of 4 bytes
+ *  @length: length of buffer, must be multiple of 4 bytes
  *
  *  Communicates with the manageability block.  On success return 0
  *  else return IXGBE_ERR_HOST_INTERFACE_COMMAND.
@@ -3526,3 +3537,172 @@ void ixgbe_clear_tx_pending(struct ixgbe_hw *hw)
        IXGBE_WRITE_REG(hw, IXGBE_GCR_EXT, gcr_ext);
        IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg0);
 }
+
+static const u8 ixgbe_emc_temp_data[4] = {
+       IXGBE_EMC_INTERNAL_DATA,
+       IXGBE_EMC_DIODE1_DATA,
+       IXGBE_EMC_DIODE2_DATA,
+       IXGBE_EMC_DIODE3_DATA
+};
+static const u8 ixgbe_emc_therm_limit[4] = {
+       IXGBE_EMC_INTERNAL_THERM_LIMIT,
+       IXGBE_EMC_DIODE1_THERM_LIMIT,
+       IXGBE_EMC_DIODE2_THERM_LIMIT,
+       IXGBE_EMC_DIODE3_THERM_LIMIT
+};
+
+/**
+ *  ixgbe_get_ets_data - Extracts the ETS bit data
+ *  @hw: pointer to hardware structure
+ *  @ets_cfg: extected ETS data
+ *  @ets_offset: offset of ETS data
+ *
+ *  Returns error code.
+ **/
+static s32 ixgbe_get_ets_data(struct ixgbe_hw *hw, u16 *ets_cfg,
+                             u16 *ets_offset)
+{
+       s32 status = 0;
+
+       status = hw->eeprom.ops.read(hw, IXGBE_ETS_CFG, ets_offset);
+       if (status)
+               goto out;
+
+       if ((*ets_offset == 0x0000) || (*ets_offset == 0xFFFF)) {
+               status = IXGBE_NOT_IMPLEMENTED;
+               goto out;
+       }
+
+       status = hw->eeprom.ops.read(hw, *ets_offset, ets_cfg);
+       if (status)
+               goto out;
+
+       if ((*ets_cfg & IXGBE_ETS_TYPE_MASK) != IXGBE_ETS_TYPE_EMC_SHIFTED) {
+               status = IXGBE_NOT_IMPLEMENTED;
+               goto out;
+       }
+
+out:
+       return status;
+}
+
+/**
+ *  ixgbe_get_thermal_sensor_data - Gathers thermal sensor data
+ *  @hw: pointer to hardware structure
+ *
+ *  Returns the thermal sensor data structure
+ **/
+s32 ixgbe_get_thermal_sensor_data_generic(struct ixgbe_hw *hw)
+{
+       s32 status = 0;
+       u16 ets_offset;
+       u16 ets_cfg;
+       u16 ets_sensor;
+       u8  num_sensors;
+       u8  i;
+       struct ixgbe_thermal_sensor_data *data = &hw->mac.thermal_sensor_data;
+
+       /* Only support thermal sensors attached to physical port 0 */
+       if ((IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_LAN_ID_1)) {
+               status = IXGBE_NOT_IMPLEMENTED;
+               goto out;
+       }
+
+       status = ixgbe_get_ets_data(hw, &ets_cfg, &ets_offset);
+       if (status)
+               goto out;
+
+       num_sensors = (ets_cfg & IXGBE_ETS_NUM_SENSORS_MASK);
+       if (num_sensors > IXGBE_MAX_SENSORS)
+               num_sensors = IXGBE_MAX_SENSORS;
+
+       for (i = 0; i < num_sensors; i++) {
+               u8  sensor_index;
+               u8  sensor_location;
+
+               status = hw->eeprom.ops.read(hw, (ets_offset + 1 + i),
+                                            &ets_sensor);
+               if (status)
+                       goto out;
+
+               sensor_index = ((ets_sensor & IXGBE_ETS_DATA_INDEX_MASK) >>
+                               IXGBE_ETS_DATA_INDEX_SHIFT);
+               sensor_location = ((ets_sensor & IXGBE_ETS_DATA_LOC_MASK) >>
+                                  IXGBE_ETS_DATA_LOC_SHIFT);
+
+               if (sensor_location != 0) {
+                       status = hw->phy.ops.read_i2c_byte(hw,
+                                       ixgbe_emc_temp_data[sensor_index],
+                                       IXGBE_I2C_THERMAL_SENSOR_ADDR,
+                                       &data->sensor[i].temp);
+                       if (status)
+                               goto out;
+               }
+       }
+out:
+       return status;
+}
+
+/**
+ * ixgbe_init_thermal_sensor_thresh_generic - Inits thermal sensor thresholds
+ * @hw: pointer to hardware structure
+ *
+ * Inits the thermal sensor thresholds according to the NVM map
+ * and save off the threshold and location values into mac.thermal_sensor_data
+ **/
+s32 ixgbe_init_thermal_sensor_thresh_generic(struct ixgbe_hw *hw)
+{
+       s32 status = 0;
+       u16 ets_offset;
+       u16 ets_cfg;
+       u16 ets_sensor;
+       u8  low_thresh_delta;
+       u8  num_sensors;
+       u8  therm_limit;
+       u8  i;
+       struct ixgbe_thermal_sensor_data *data = &hw->mac.thermal_sensor_data;
+
+       memset(data, 0, sizeof(struct ixgbe_thermal_sensor_data));
+
+       /* Only support thermal sensors attached to physical port 0 */
+       if ((IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_LAN_ID_1)) {
+               status = IXGBE_NOT_IMPLEMENTED;
+               goto out;
+       }
+
+       status = ixgbe_get_ets_data(hw, &ets_cfg, &ets_offset);
+       if (status)
+               goto out;
+
+       low_thresh_delta = ((ets_cfg & IXGBE_ETS_LTHRES_DELTA_MASK) >>
+                            IXGBE_ETS_LTHRES_DELTA_SHIFT);
+       num_sensors = (ets_cfg & IXGBE_ETS_NUM_SENSORS_MASK);
+       if (num_sensors > IXGBE_MAX_SENSORS)
+               num_sensors = IXGBE_MAX_SENSORS;
+
+       for (i = 0; i < num_sensors; i++) {
+               u8  sensor_index;
+               u8  sensor_location;
+
+               hw->eeprom.ops.read(hw, (ets_offset + 1 + i), &ets_sensor);
+               sensor_index = ((ets_sensor & IXGBE_ETS_DATA_INDEX_MASK) >>
+                               IXGBE_ETS_DATA_INDEX_SHIFT);
+               sensor_location = ((ets_sensor & IXGBE_ETS_DATA_LOC_MASK) >>
+                                  IXGBE_ETS_DATA_LOC_SHIFT);
+               therm_limit = ets_sensor & IXGBE_ETS_DATA_HTHRESH_MASK;
+
+               hw->phy.ops.write_i2c_byte(hw,
+                       ixgbe_emc_therm_limit[sensor_index],
+                       IXGBE_I2C_THERMAL_SENSOR_ADDR, therm_limit);
+
+               if (sensor_location == 0)
+                       continue;
+
+               data->sensor[i].location = sensor_location;
+               data->sensor[i].caution_thresh = therm_limit;
+               data->sensor[i].max_op_thresh = therm_limit - low_thresh_delta;
+       }
+out:
+       return status;
+}
+