e1000: update base driver
[dpdk.git] / lib / librte_pmd_e1000 / e1000 / e1000_ich8lan.c
index db26a5c..d382f77 100644 (file)
@@ -58,10 +58,25 @@ POSSIBILITY OF SUCH DAMAGE.
  * 82578DC Gigabit Network Connection
  * 82579LM Gigabit Network Connection
  * 82579V Gigabit Network Connection
+ * Ethernet Connection I217-LM
+ * Ethernet Connection I217-V
+ * Ethernet Connection I218-V
+ * Ethernet Connection I218-LM
+#ifdef NAHUM6_LPTH_I218_HW
+ * Ethernet Connection (2) I218-LM
+ * Ethernet Connection (2) I218-V
+#endif
+#ifdef NAHUM6_WPT_HW
+ * Ethernet Connection (3) I218-LM
+ * Ethernet Connection (3) I218-V
+#endif
  */
 
 #include "e1000_api.h"
 
+#if defined(NAHUM6LP_HW) && defined(ULP_IN_D0_SUPPORT)
+static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state);
+#endif /* NAHUM6LP_HW && ULP_SUPPORT */
 STATIC s32  e1000_acquire_swflag_ich8lan(struct e1000_hw *hw);
 STATIC void e1000_release_swflag_ich8lan(struct e1000_hw *hw);
 STATIC s32  e1000_acquire_nvm_ich8lan(struct e1000_hw *hw);
@@ -69,6 +84,13 @@ STATIC void e1000_release_nvm_ich8lan(struct e1000_hw *hw);
 STATIC bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw);
 STATIC bool e1000_check_mng_mode_pchlan(struct e1000_hw *hw);
 STATIC void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index);
+STATIC void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index);
+STATIC s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw);
+#ifndef NO_NON_BLOCKING_PHY_MTA_UPDATE_SUPPORT
+STATIC void e1000_update_mc_addr_list_pch2lan(struct e1000_hw *hw,
+                                             u8 *mc_addr_list,
+                                             u32 mc_addr_count);
+#endif /* NO_NON_BLOCKING_PHY_MTA_UPDATE_SUPPORT */
 STATIC s32  e1000_check_reset_block_ich8lan(struct e1000_hw *hw);
 STATIC s32  e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw);
 STATIC s32  e1000_set_lplu_state_pchlan(struct e1000_hw *hw, bool active);
@@ -90,6 +112,7 @@ STATIC s32  e1000_reset_hw_ich8lan(struct e1000_hw *hw);
 STATIC s32  e1000_init_hw_ich8lan(struct e1000_hw *hw);
 STATIC s32  e1000_setup_link_ich8lan(struct e1000_hw *hw);
 STATIC s32  e1000_setup_copper_link_ich8lan(struct e1000_hw *hw);
+STATIC s32  e1000_setup_copper_link_pch_lpt(struct e1000_hw *hw);
 STATIC s32  e1000_get_link_up_info_ich8lan(struct e1000_hw *hw,
                                           u16 *speed, u16 *duplex);
 STATIC s32  e1000_cleanup_led_ich8lan(struct e1000_hw *hw);
@@ -174,8 +197,9 @@ static bool e1000_phy_is_accessible_pchlan(struct e1000_hw *hw)
 {
        u16 phy_reg = 0;
        u32 phy_id = 0;
-       s32 ret_val;
+       s32 ret_val = 0;
        u16 retry_count;
+       u32 mac_reg = 0;
 
        for (retry_count = 0; retry_count < 2; retry_count++) {
                ret_val = hw->phy.ops.read_reg_locked(hw, PHY_ID1, &phy_reg);
@@ -194,24 +218,84 @@ static bool e1000_phy_is_accessible_pchlan(struct e1000_hw *hw)
 
        if (hw->phy.id) {
                if  (hw->phy.id == phy_id)
-                       return true;
+                       goto out;
        } else if (phy_id) {
                hw->phy.id = phy_id;
                hw->phy.revision = (u32)(phy_reg & ~PHY_REVISION_MASK);
-               return true;
+               goto out;
        }
 
-       /*
-        * In case the PHY needs to be in mdio slow mode,
+       /* In case the PHY needs to be in mdio slow mode,
         * set slow mode and try to get the PHY id again.
         */
-       hw->phy.ops.release(hw);
-       ret_val = e1000_set_mdio_slow_mode_hv(hw);
-       if (!ret_val)
-               ret_val = e1000_get_phy_id(hw);
-       hw->phy.ops.acquire(hw);
+       if (hw->mac.type < e1000_pch_lpt) {
+               hw->phy.ops.release(hw);
+               ret_val = e1000_set_mdio_slow_mode_hv(hw);
+               if (!ret_val)
+                       ret_val = e1000_get_phy_id(hw);
+               hw->phy.ops.acquire(hw);
+       }
 
-       return !ret_val;
+       if (ret_val)
+               return false;
+out:
+       if (hw->mac.type == e1000_pch_lpt) {
+               /* Unforce SMBus mode in PHY */
+               hw->phy.ops.read_reg_locked(hw, CV_SMB_CTRL, &phy_reg);
+               phy_reg &= ~CV_SMB_CTRL_FORCE_SMBUS;
+               hw->phy.ops.write_reg_locked(hw, CV_SMB_CTRL, phy_reg);
+
+               /* Unforce SMBus mode in MAC */
+               mac_reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
+               mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS;
+               E1000_WRITE_REG(hw, E1000_CTRL_EXT, mac_reg);
+       }
+
+       return true;
+}
+
+/**
+ *  e1000_toggle_lanphypc_pch_lpt - toggle the LANPHYPC pin value
+ *  @hw: pointer to the HW structure
+ *
+ *  Toggling the LANPHYPC pin value fully power-cycles the PHY and is
+ *  used to reset the PHY to a quiescent state when necessary.
+ **/
+void e1000_toggle_lanphypc_pch_lpt(struct e1000_hw *hw)
+{
+       u32 mac_reg;
+
+       DEBUGFUNC("e1000_toggle_lanphypc_pch_lpt");
+
+       /* Set Phy Config Counter to 50msec */
+       mac_reg = E1000_READ_REG(hw, E1000_FEXTNVM3);
+       mac_reg &= ~E1000_FEXTNVM3_PHY_CFG_COUNTER_MASK;
+       mac_reg |= E1000_FEXTNVM3_PHY_CFG_COUNTER_50MSEC;
+       E1000_WRITE_REG(hw, E1000_FEXTNVM3, mac_reg);
+
+       /* Toggle LANPHYPC Value bit */
+       mac_reg = E1000_READ_REG(hw, E1000_CTRL);
+       mac_reg |= E1000_CTRL_LANPHYPC_OVERRIDE;
+       mac_reg &= ~E1000_CTRL_LANPHYPC_VALUE;
+       E1000_WRITE_REG(hw, E1000_CTRL, mac_reg);
+       E1000_WRITE_FLUSH(hw);
+       usec_delay(10);
+       mac_reg &= ~E1000_CTRL_LANPHYPC_OVERRIDE;
+       E1000_WRITE_REG(hw, E1000_CTRL, mac_reg);
+       E1000_WRITE_FLUSH(hw);
+
+       if (hw->mac.type < e1000_pch_lpt) {
+               msec_delay(50);
+       } else {
+               u16 count = 20;
+
+               do {
+                       msec_delay(5);
+               } while (!(E1000_READ_REG(hw, E1000_CTRL_EXT) &
+                          E1000_CTRL_EXT_LPCD) && count--);
+
+               msec_delay(30);
+       }
 }
 
 /**
@@ -228,30 +312,50 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
 
        DEBUGFUNC("e1000_init_phy_workarounds_pchlan");
 
+       /* Gate automatic PHY configuration by hardware on managed and
+        * non-managed 82579 and newer adapters.
+        */
+       e1000_gate_hw_phy_config_ich8lan(hw, true);
+
+#if defined(NAHUM6LP_HW) && defined(ULP_SUPPORT)
+       /* It is not possible to be certain of the current state of ULP
+        * so forcibly disable it.
+        */
+       hw->dev_spec.ich8lan.ulp_state = e1000_ulp_state_unknown;
+
+#endif /* NAHUM6LP_HW && ULP_SUPPORT */
        ret_val = hw->phy.ops.acquire(hw);
        if (ret_val) {
                DEBUGOUT("Failed to initialize PHY flow\n");
-               return ret_val;
+               goto out;
        }
 
-       /*
-        * The MAC-PHY interconnect may be in SMBus mode.  If the PHY is
+       /* The MAC-PHY interconnect may be in SMBus mode.  If the PHY is
         * inaccessible and resetting the PHY is not blocked, toggle the
         * LANPHYPC Value bit to force the interconnect to PCIe mode.
         */
        switch (hw->mac.type) {
-       case e1000_pch2lan:
-               /*
-                * Gate automatic PHY configuration by hardware on
-                * non-managed 82579
+       case e1000_pch_lpt:
+               if (e1000_phy_is_accessible_pchlan(hw))
+                       break;
+
+               /* Before toggling LANPHYPC, see if PHY is accessible by
+                * forcing MAC to SMBus mode first.
                 */
-               if ((hw->mac.type == e1000_pch2lan) &&
-                   !(fwsm & E1000_ICH_FWSM_FW_VALID))
-                       e1000_gate_hw_phy_config_ich8lan(hw, true);
+               mac_reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
+               mac_reg |= E1000_CTRL_EXT_FORCE_SMBUS;
+               E1000_WRITE_REG(hw, E1000_CTRL_EXT, mac_reg);
+
+               /* Wait 50 milliseconds for MAC to finish any retries
+                * that it might be trying to perform from previous
+                * attempts to acknowledge any phy read requests.
+                */
+                msec_delay(50);
 
-               if (e1000_phy_is_accessible_pchlan(hw)) {
+               /* fall-through */
+       case e1000_pch2lan:
+               if (e1000_phy_is_accessible_pchlan(hw))
                        break;
-               }
 
                /* fall-through */
        case e1000_pchlan:
@@ -261,43 +365,63 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
 
                if (hw->phy.ops.check_reset_block(hw)) {
                        DEBUGOUT("Required LANPHYPC toggle blocked by ME\n");
+                       ret_val = -E1000_ERR_PHY;
                        break;
                }
 
-               DEBUGOUT("Toggling LANPHYPC\n");
+               /* Toggle LANPHYPC Value bit */
+               e1000_toggle_lanphypc_pch_lpt(hw);
+               if (hw->mac.type >= e1000_pch_lpt) {
+                       if (e1000_phy_is_accessible_pchlan(hw))
+                               break;
+
+                       /* Toggling LANPHYPC brings the PHY out of SMBus mode
+                        * so ensure that the MAC is also out of SMBus mode
+                        */
+                       mac_reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
+                       mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS;
+                       E1000_WRITE_REG(hw, E1000_CTRL_EXT, mac_reg);
 
-               /* Set Phy Config Counter to 50msec */
-               mac_reg = E1000_READ_REG(hw, E1000_FEXTNVM3);
-               mac_reg &= ~E1000_FEXTNVM3_PHY_CFG_COUNTER_MASK;
-               mac_reg |= E1000_FEXTNVM3_PHY_CFG_COUNTER_50MSEC;
-               E1000_WRITE_REG(hw, E1000_FEXTNVM3, mac_reg);
+                       if (e1000_phy_is_accessible_pchlan(hw))
+                               break;
 
-               /* Toggle LANPHYPC Value bit */
-               mac_reg = E1000_READ_REG(hw, E1000_CTRL);
-               mac_reg |= E1000_CTRL_LANPHYPC_OVERRIDE;
-               mac_reg &= ~E1000_CTRL_LANPHYPC_VALUE;
-               E1000_WRITE_REG(hw, E1000_CTRL, mac_reg);
-               E1000_WRITE_FLUSH(hw);
-               usec_delay(10);
-               mac_reg &= ~E1000_CTRL_LANPHYPC_OVERRIDE;
-               E1000_WRITE_REG(hw, E1000_CTRL, mac_reg);
-               E1000_WRITE_FLUSH(hw);
-               msec_delay(50);
+                       ret_val = -E1000_ERR_PHY;
+               }
                break;
        default:
                break;
        }
 
        hw->phy.ops.release(hw);
+       if (!ret_val) {
 
-       /*
-        * Reset the PHY before any access to it.  Doing so, ensures
-        * that the PHY is in a known good state before we read/write
-        * PHY registers.  The generic reset is sufficient here,
-        * because we haven't determined the PHY type yet.
-        */
-       ret_val = e1000_phy_hw_reset_generic(hw);
+               /* Check to see if able to reset PHY.  Print error if not */
+               if (hw->phy.ops.check_reset_block(hw)) {
+                       ERROR_REPORT("Reset blocked by ME\n");
+                       goto out;
+               }
 
+               /* Reset the PHY before any access to it.  Doing so, ensures
+                * that the PHY is in a known good state before we read/write
+                * PHY registers.  The generic reset is sufficient here,
+                * because we haven't determined the PHY type yet.
+                */
+               ret_val = e1000_phy_hw_reset_generic(hw);
+               if (ret_val)
+                       goto out;
+
+               /* On a successful reset, possibly need to wait for the PHY
+                * to quiesce to an accessible state before returning control
+                * to the calling function.  If the PHY does not quiesce, then
+                * return E1000E_BLK_PHY_RESET, as this is the condition that
+                *  the PHY is in.
+                */
+               ret_val = hw->phy.ops.check_reset_block(hw);
+               if (ret_val)
+                       ERROR_REPORT("ME blocked access to PHY after reset\n");
+       }
+
+out:
        /* Ungate automatic PHY configuration on non-managed 82579 */
        if ((hw->mac.type == e1000_pch2lan) &&
            !(fwsm & E1000_ICH_FWSM_FW_VALID)) {
@@ -566,6 +690,9 @@ STATIC s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw)
 STATIC s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
 {
        struct e1000_mac_info *mac = &hw->mac;
+#if defined(QV_RELEASE) || !defined(NO_PCH_LPT_B0_SUPPORT)
+       u16 pci_cfg;
+#endif /* QV_RELEASE || !defined(NO_PCH_LPT_B0_SUPPORT) */
 
        DEBUGFUNC("e1000_init_mac_params_ich8lan");
 
@@ -633,7 +760,18 @@ STATIC s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
                mac->rar_entry_count = E1000_PCH2_RAR_ENTRIES;
                mac->ops.rar_set = e1000_rar_set_pch2lan;
                /* fall-through */
+       case e1000_pch_lpt:
+#ifndef NO_NON_BLOCKING_PHY_MTA_UPDATE_SUPPORT
+               /* multicast address update for pch2 */
+               mac->ops.update_mc_addr_list =
+                       e1000_update_mc_addr_list_pch2lan;
+#endif
        case e1000_pchlan:
+#if defined(QV_RELEASE) || !defined(NO_PCH_LPT_B0_SUPPORT)
+               /* save PCH revision_id */
+               e1000_read_pci_cfg(hw, E1000_PCI_REVISION_ID_REG, &pci_cfg);
+               hw->revision_id = (u8)(pci_cfg &= 0x000F);
+#endif /* QV_RELEASE || !defined(NO_PCH_LPT_B0_SUPPORT) */
                /* check management mode */
                mac->ops.check_mng_mode = e1000_check_mng_mode_pchlan;
                /* ID LED init */
@@ -650,15 +788,16 @@ STATIC s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
                break;
        }
 
+       if (mac->type == e1000_pch_lpt) {
+               mac->rar_entry_count = E1000_PCH_LPT_RAR_ENTRIES;
+               mac->ops.rar_set = e1000_rar_set_pch_lpt;
+               mac->ops.setup_physical_interface = e1000_setup_copper_link_pch_lpt;
+       }
+
        /* Enable PCS Lock-loss workaround for ICH8 */
        if (mac->type == e1000_ich8lan)
                e1000_set_kmrn_lock_loss_workaround_ich8lan(hw, true);
 
-       /* Gate automatic PHY configuration by hardware on managed 82579 */
-       if ((mac->type == e1000_pch2lan) &&
-           (E1000_READ_REG(hw, E1000_FWSM) & E1000_ICH_FWSM_FW_VALID))
-               e1000_gate_hw_phy_config_ich8lan(hw, true);
-
        return E1000_SUCCESS;
 }
 
@@ -715,7 +854,7 @@ s32 e1000_read_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 *data)
  *
  *  Assumes the SW/FW/HW Semaphore is already acquired.
  **/
-STATIC s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data)
+s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data)
 {
        DEBUGFUNC("e1000_read_emi_reg_locked");
 
@@ -729,18 +868,35 @@ STATIC s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data)
  *  Enable/disable EEE based on setting in dev_spec structure, the duplex of
  *  the link and the EEE capabilities of the link partner.  The LPI Control
  *  register bits will remain set only if/when link is up.
+ *
+ *  EEE LPI must not be asserted earlier than one second after link is up.
+ *  On 82579, EEE LPI should not be enabled until such time otherwise there
+ *  can be link issues with some switches.  Other devices can have EEE LPI
+ *  enabled immediately upon link up since they have a timer in hardware which
+ *  prevents LPI from being asserted too early.
  **/
-STATIC s32 e1000_set_eee_pchlan(struct e1000_hw *hw)
+s32 e1000_set_eee_pchlan(struct e1000_hw *hw)
 {
        struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
        s32 ret_val;
-       u16 lpi_ctrl;
+       u16 lpa, pcs_status, adv, adv_addr, lpi_ctrl, data;
 
        DEBUGFUNC("e1000_set_eee_pchlan");
 
-       if ((hw->phy.type != e1000_phy_82579) &&
-           (hw->phy.type != e1000_phy_i217))
+       switch (hw->phy.type) {
+       case e1000_phy_82579:
+               lpa = I82579_EEE_LP_ABILITY;
+               pcs_status = I82579_EEE_PCS_STATUS;
+               adv_addr = I82579_EEE_ADVERTISEMENT;
+               break;
+       case e1000_phy_i217:
+               lpa = I217_EEE_LP_ABILITY;
+               pcs_status = I217_EEE_PCS_STATUS;
+               adv_addr = I217_EEE_ADVERTISEMENT;
+               break;
+       default:
                return E1000_SUCCESS;
+       }
 
        ret_val = hw->phy.ops.acquire(hw);
        if (ret_val)
@@ -755,61 +911,543 @@ STATIC s32 e1000_set_eee_pchlan(struct e1000_hw *hw)
 
        /* Enable EEE if not disabled by user */
        if (!dev_spec->eee_disable) {
-               u16 lpa, pcs_status, data;
-
                /* Save off link partner's EEE ability */
-               switch (hw->phy.type) {
-               case e1000_phy_82579:
-                       lpa = I82579_EEE_LP_ABILITY;
-                       pcs_status = I82579_EEE_PCS_STATUS;
-                       break;
-               case e1000_phy_i217:
-                       lpa = I217_EEE_LP_ABILITY;
-                       pcs_status = I217_EEE_PCS_STATUS;
-                       break;
-               default:
-                       ret_val = -E1000_ERR_PHY;
-                       goto release;
-               }
                ret_val = e1000_read_emi_reg_locked(hw, lpa,
                                                    &dev_spec->eee_lp_ability);
                if (ret_val)
                        goto release;
 
-               /*
-                * Enable EEE only for speeds in which the link partner is
-                * EEE capable.
+               /* Read EEE advertisement */
+               ret_val = e1000_read_emi_reg_locked(hw, adv_addr, &adv);
+               if (ret_val)
+                       goto release;
+
+               /* Enable EEE only for speeds in which the link partner is
+                * EEE capable and for which we advertise EEE.
                 */
-               if (dev_spec->eee_lp_ability & I82579_EEE_1000_SUPPORTED)
+               if (adv & dev_spec->eee_lp_ability & I82579_EEE_1000_SUPPORTED)
                        lpi_ctrl |= I82579_LPI_CTRL_1000_ENABLE;
 
-               if (dev_spec->eee_lp_ability & I82579_EEE_100_SUPPORTED) {
+               if (adv & dev_spec->eee_lp_ability & I82579_EEE_100_SUPPORTED) {
                        hw->phy.ops.read_reg_locked(hw, PHY_LP_ABILITY, &data);
                        if (data & NWAY_LPAR_100TX_FD_CAPS)
                                lpi_ctrl |= I82579_LPI_CTRL_100_ENABLE;
                        else
-                               /*
-                                * EEE is not supported in 100Half, so ignore
+                               /* EEE is not supported in 100Half, so ignore
                                 * partner's EEE in 100 ability if full-duplex
                                 * is not advertised.
                                 */
                                dev_spec->eee_lp_ability &=
                                    ~I82579_EEE_100_SUPPORTED;
                }
+       }
+
+       /* R/Clr IEEE MMD 3.1 bits 11:10 - Tx/Rx LPI Received */
+       ret_val = e1000_read_emi_reg_locked(hw, pcs_status, &data);
+       if (ret_val)
+               goto release;
+
+       ret_val = hw->phy.ops.write_reg_locked(hw, I82579_LPI_CTRL, lpi_ctrl);
+release:
+       hw->phy.ops.release(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_k1_workaround_lpt_lp - K1 workaround on Lynxpoint-LP
+ *  @hw:   pointer to the HW structure
+ *  @link: link up bool flag
+ *
+ *  When K1 is enabled for 1Gbps, the MAC can miss 2 DMA completion indications
+ *  preventing further DMA write requests.  Workaround the issue by disabling
+ *  the de-assertion of the clock request when in 1Gpbs mode.
+ *  Also, set appropriate Tx re-transmission timeouts for 10 and 100Half link
+ *  speeds in order to avoid Tx hangs.
+ **/
+static s32 e1000_k1_workaround_lpt_lp(struct e1000_hw *hw, bool link)
+{
+       u32 fextnvm6 = E1000_READ_REG(hw, E1000_FEXTNVM6);
+       u32 status = E1000_READ_REG(hw, E1000_STATUS);
+       s32 ret_val = E1000_SUCCESS;
+       u16 reg;
+
+       if (link && (status & E1000_STATUS_SPEED_1000)) {
+               ret_val = hw->phy.ops.acquire(hw);
+               if (ret_val)
+                       return ret_val;
+
+               ret_val =
+                   e1000_read_kmrn_reg_locked(hw, E1000_KMRNCTRLSTA_K1_CONFIG,
+                                              &reg);
+               if (ret_val)
+                       goto release;
+
+               ret_val =
+                   e1000_write_kmrn_reg_locked(hw,
+                                               E1000_KMRNCTRLSTA_K1_CONFIG,
+                                               reg &
+                                               ~E1000_KMRNCTRLSTA_K1_ENABLE);
+               if (ret_val)
+                       goto release;
+
+               usec_delay(10);
+
+               E1000_WRITE_REG(hw, E1000_FEXTNVM6,
+                               fextnvm6 | E1000_FEXTNVM6_REQ_PLL_CLK);
+
+               ret_val =
+                   e1000_write_kmrn_reg_locked(hw,
+                                               E1000_KMRNCTRLSTA_K1_CONFIG,
+                                               reg);
+release:
+               hw->phy.ops.release(hw);
+       } else {
+               /* clear FEXTNVM6 bit 8 on link down or 10/100 */
+               fextnvm6 &= ~E1000_FEXTNVM6_REQ_PLL_CLK;
+
+               if (!link || ((status & E1000_STATUS_SPEED_100) &&
+                             (status & E1000_STATUS_FD)))
+                       goto update_fextnvm6;
+
+               ret_val = hw->phy.ops.read_reg(hw, I217_INBAND_CTRL, &reg);
+               if (ret_val)
+                       return ret_val;
+
+               /* Clear link status transmit timeout */
+               reg &= ~I217_INBAND_CTRL_LINK_STAT_TX_TIMEOUT_MASK;
+
+               if (status & E1000_STATUS_SPEED_100) {
+                       /* Set inband Tx timeout to 5x10us for 100Half */
+                       reg |= 5 << I217_INBAND_CTRL_LINK_STAT_TX_TIMEOUT_SHIFT;
+
+                       /* Do not extend the K1 entry latency for 100Half */
+                       fextnvm6 &= ~E1000_FEXTNVM6_ENABLE_K1_ENTRY_CONDITION;
+               } else {
+                       /* Set inband Tx timeout to 50x10us for 10Full/Half */
+                       reg |= 50 <<
+                              I217_INBAND_CTRL_LINK_STAT_TX_TIMEOUT_SHIFT;
+
+                       /* Extend the K1 entry latency for 10 Mbps */
+                       fextnvm6 |= E1000_FEXTNVM6_ENABLE_K1_ENTRY_CONDITION;
+               }
+
+               ret_val = hw->phy.ops.write_reg(hw, I217_INBAND_CTRL, reg);
+               if (ret_val)
+                       return ret_val;
+
+update_fextnvm6:
+               E1000_WRITE_REG(hw, E1000_FEXTNVM6, fextnvm6);
+       }
+
+       return ret_val;
+}
+
+#ifdef C10_SUPPORT
+/**
+ * e1000_demote_ltr - Demote/Promote the LTR value
+ * @hw: pointer to the HW structure
+ * @demote: boolean value to control whether we are demoting or promoting
+ *    the LTR value (promoting allows deeper C-States).
+ * @link: boolean value stating whether we currently have link
+ *
+ * Configure the LTRV register with the proper LTR value
+ **/
+void e1000_demote_ltr(struct e1000_hw *hw, bool demote, bool link)
+{
+       u32 reg = link << (E1000_LTRV_REQ_SHIFT + E1000_LTRV_NOSNOOP_SHIFT) |
+                 link << E1000_LTRV_REQ_SHIFT | E1000_LTRV_SEND;
+
+       if ((hw->device_id != E1000_DEV_ID_PCH_I218_LM3) &&
+           (hw->device_id != E1000_DEV_ID_PCH_I218_V3))
+               return;
+
+       if (demote) {
+               reg |= hw->dev_spec.ich8lan.lat_enc |
+                      (hw->dev_spec.ich8lan.lat_enc <<
+                       E1000_LTRV_NOSNOOP_SHIFT);
+       } else {
+               reg |= hw->dev_spec.ich8lan.max_ltr_enc |
+                      (hw->dev_spec.ich8lan.max_ltr_enc <<
+                       E1000_LTRV_NOSNOOP_SHIFT);
+       }
+
+       E1000_WRITE_REG(hw, E1000_LTRV, reg);
+       return;
+}
+
+#endif /* C10_SUPPORT */
+#if defined(NAHUM6LP_HW) && defined(ULP_SUPPORT)
+/**
+ *  e1000_enable_ulp_lpt_lp - configure Ultra Low Power mode for LynxPoint-LP
+ *  @hw: pointer to the HW structure
+ *  @to_sx: boolean indicating a system power state transition to Sx
+ *
+ *  When link is down, configure ULP mode to significantly reduce the power
+ *  to the PHY.  If on a Manageability Engine (ME) enabled system, tell the
+ *  ME firmware to start the ULP configuration.  If not on an ME enabled
+ *  system, configure the ULP mode by software.
+ */
+s32 e1000_enable_ulp_lpt_lp(struct e1000_hw *hw, bool to_sx)
+{
+       u32 mac_reg;
+       s32 ret_val = E1000_SUCCESS;
+       u16 phy_reg;
+
+       if ((hw->mac.type < e1000_pch_lpt) ||
+           (hw->device_id == E1000_DEV_ID_PCH_LPT_I217_LM) ||
+           (hw->device_id == E1000_DEV_ID_PCH_LPT_I217_V) ||
+#ifdef NAHUM6_LPTH_I218_HW
+           (hw->device_id == E1000_DEV_ID_PCH_I218_LM2) ||
+           (hw->device_id == E1000_DEV_ID_PCH_I218_V2) ||
+#endif
+           (hw->dev_spec.ich8lan.ulp_state == e1000_ulp_state_on))
+               return 0;
+
+#ifdef ULP_IN_D0_SUPPORT
+       if (!to_sx) {
+               int i = 0;
+
+               /* Poll up to 5 seconds for Cable Disconnected indication */
+               while (!(E1000_READ_REG(hw, E1000_FEXT) &
+                        E1000_FEXT_PHY_CABLE_DISCONNECTED)) {
+                       /* Bail if link is re-acquired */
+                       if (E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)
+                               return -E1000_ERR_PHY;
+
+                       if (i++ == 100)
+                               break;
+
+                       msec_delay(50);
+               }
+               DEBUGOUT2("CABLE_DISCONNECTED %s set after %dmsec\n",
+                         (E1000_READ_REG(hw, E1000_FEXT) &
+                          E1000_FEXT_PHY_CABLE_DISCONNECTED) ? "" : "not",
+                         i * 50);
+       }
+
+#endif /* ULP_IN_D0_SUPPORT */
+       if (E1000_READ_REG(hw, E1000_FWSM) & E1000_ICH_FWSM_FW_VALID) {
+               /* Request ME configure ULP mode in the PHY */
+               mac_reg = E1000_READ_REG(hw, E1000_H2ME);
+               mac_reg |= E1000_H2ME_ULP | E1000_H2ME_ENFORCE_SETTINGS;
+               E1000_WRITE_REG(hw, E1000_H2ME, mac_reg);
+
+               goto out;
+       }
+
+#ifndef ULP_IN_D0_SUPPORT
+       if (!to_sx) {
+               int i = 0;
+
+               /* Poll up to 5 seconds for Cable Disconnected indication */
+               while (!(E1000_READ_REG(hw, E1000_FEXT) &
+                        E1000_FEXT_PHY_CABLE_DISCONNECTED)) {
+                       /* Bail if link is re-acquired */
+                       if (E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)
+                               return -E1000_ERR_PHY;
+
+                       if (i++ == 100)
+                               break;
+
+                       msec_delay(50);
+               }
+               DEBUGOUT("CABLE_DISCONNECTED %s set after %dmsec\n",
+                        (E1000_READ_REG(hw, E1000_FEXT) &
+                         E1000_FEXT_PHY_CABLE_DISCONNECTED) ? "" : "not",
+                        i * 50);
+       }
 
-               /* R/Clr IEEE MMD 3.1 bits 11:10 - Tx/Rx LPI Received */
-               ret_val = e1000_read_emi_reg_locked(hw, pcs_status, &data);
+#endif /* !ULP_IN_D0_SUPPORT */
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val)
+               goto out;
+
+       /* Force SMBus mode in PHY */
+       ret_val = e1000_read_phy_reg_hv_locked(hw, CV_SMB_CTRL, &phy_reg);
+       if (ret_val)
+               goto release;
+       phy_reg |= CV_SMB_CTRL_FORCE_SMBUS;
+       e1000_write_phy_reg_hv_locked(hw, CV_SMB_CTRL, phy_reg);
+
+       /* Force SMBus mode in MAC */
+       mac_reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
+       mac_reg |= E1000_CTRL_EXT_FORCE_SMBUS;
+       E1000_WRITE_REG(hw, E1000_CTRL_EXT, mac_reg);
+
+#ifdef ULP_IN_D0_SUPPORT
+       if (!to_sx) {
+               /* Change the 'Link Status Change' interrupt to trigger
+                * on 'Cable Status Change'
+                */
+               ret_val = e1000_read_kmrn_reg_locked(hw,
+                                                    E1000_KMRNCTRLSTA_OP_MODES,
+                                                    &phy_reg);
                if (ret_val)
                        goto release;
+               phy_reg |= E1000_KMRNCTRLSTA_OP_MODES_LSC2CSC;
+               e1000_write_kmrn_reg_locked(hw, E1000_KMRNCTRLSTA_OP_MODES,
+                                           phy_reg);
        }
 
-       ret_val = hw->phy.ops.write_reg_locked(hw, I82579_LPI_CTRL, lpi_ctrl);
+#endif /* ULP_IN_D0_SUPPORT */
+       /* Set Inband ULP Exit, Reset to SMBus mode and
+        * Disable SMBus Release on PERST# in PHY
+        */
+       ret_val = e1000_read_phy_reg_hv_locked(hw, I218_ULP_CONFIG1, &phy_reg);
+       if (ret_val)
+               goto release;
+       phy_reg |= (I218_ULP_CONFIG1_RESET_TO_SMBUS |
+                   I218_ULP_CONFIG1_DISABLE_SMB_PERST);
+       if (to_sx) {
+               if (E1000_READ_REG(hw, E1000_WUFC) & E1000_WUFC_LNKC)
+                       phy_reg |= I218_ULP_CONFIG1_WOL_HOST;
+
+               phy_reg |= I218_ULP_CONFIG1_STICKY_ULP;
+       } else {
+               phy_reg |= I218_ULP_CONFIG1_INBAND_EXIT;
+       }
+       e1000_write_phy_reg_hv_locked(hw, I218_ULP_CONFIG1, phy_reg);
+
+       /* Set Disable SMBus Release on PERST# in MAC */
+       mac_reg = E1000_READ_REG(hw, E1000_FEXTNVM7);
+       mac_reg |= E1000_FEXTNVM7_DISABLE_SMB_PERST;
+       E1000_WRITE_REG(hw, E1000_FEXTNVM7, mac_reg);
+
+       /* Commit ULP changes in PHY by starting auto ULP configuration */
+       phy_reg |= I218_ULP_CONFIG1_START;
+       e1000_write_phy_reg_hv_locked(hw, I218_ULP_CONFIG1, phy_reg);
+#ifdef ULP_IN_D0_SUPPORT
+
+       if (!to_sx) {
+               /* Disable Tx so that the MAC doesn't send any (buffered)
+                * packets to the PHY.
+                */
+               mac_reg = E1000_READ_REG(hw, E1000_TCTL);
+               mac_reg &= ~E1000_TCTL_EN;
+               E1000_WRITE_REG(hw, E1000_TCTL, mac_reg);
+       }
+#endif
 release:
        hw->phy.ops.release(hw);
+out:
+       if (ret_val)
+               DEBUGOUT1("Error in ULP enable flow: %d\n", ret_val);
+       else
+               hw->dev_spec.ich8lan.ulp_state = e1000_ulp_state_on;
 
        return ret_val;
 }
 
+/**
+ *  e1000_disable_ulp_lpt_lp - unconfigure Ultra Low Power mode for LynxPoint-LP
+ *  @hw: pointer to the HW structure
+ *  @force: boolean indicating whether or not to force disabling ULP
+ *
+ *  Un-configure ULP mode when link is up, the system is transitioned from
+ *  Sx or the driver is unloaded.  If on a Manageability Engine (ME) enabled
+ *  system, poll for an indication from ME that ULP has been un-configured.
+ *  If not on an ME enabled system, un-configure the ULP mode by software.
+ *
+ *  During nominal operation, this function is called when link is acquired
+ *  to disable ULP mode (force=false); otherwise, for example when unloading
+ *  the driver or during Sx->S0 transitions, this is called with force=true
+ *  to forcibly disable ULP.
+#ifdef ULP_IN_D0_SUPPORT
+
+ *  When the cable is plugged in while the device is in D0, a Cable Status
+ *  Change interrupt is generated which causes this function to be called
+ *  to partially disable ULP mode and restart autonegotiation.  This function
+ *  is then called again due to the resulting Link Status Change interrupt
+ *  to finish cleaning up after the ULP flow.
+#endif
+ */
+s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u32 mac_reg;
+       u16 phy_reg;
+       int i = 0;
+
+       if ((hw->mac.type < e1000_pch_lpt) ||
+           (hw->device_id == E1000_DEV_ID_PCH_LPT_I217_LM) ||
+           (hw->device_id == E1000_DEV_ID_PCH_LPT_I217_V) ||
+#ifdef NAHUM6_LPTH_I218_HW
+           (hw->device_id == E1000_DEV_ID_PCH_I218_LM2) ||
+           (hw->device_id == E1000_DEV_ID_PCH_I218_V2) ||
+#endif
+           (hw->dev_spec.ich8lan.ulp_state == e1000_ulp_state_off))
+               return 0;
+
+       if (E1000_READ_REG(hw, E1000_FWSM) & E1000_ICH_FWSM_FW_VALID) {
+               if (force) {
+                       /* Request ME un-configure ULP mode in the PHY */
+                       mac_reg = E1000_READ_REG(hw, E1000_H2ME);
+                       mac_reg &= ~E1000_H2ME_ULP;
+                       mac_reg |= E1000_H2ME_ENFORCE_SETTINGS;
+                       E1000_WRITE_REG(hw, E1000_H2ME, mac_reg);
+               }
+
+               /* Poll up to 100msec for ME to clear ULP_CFG_DONE */
+               while (E1000_READ_REG(hw, E1000_FWSM) &
+                      E1000_FWSM_ULP_CFG_DONE) {
+                       if (i++ == 10) {
+                               ret_val = -E1000_ERR_PHY;
+                               goto out;
+                       }
+
+                       msec_delay(10);
+               }
+               DEBUGOUT1("ULP_CONFIG_DONE cleared after %dmsec\n", i * 10);
+
+               if (force) {
+                       mac_reg = E1000_READ_REG(hw, E1000_H2ME);
+                       mac_reg &= ~E1000_H2ME_ENFORCE_SETTINGS;
+                       E1000_WRITE_REG(hw, E1000_H2ME, mac_reg);
+               } else {
+                       /* Clear H2ME.ULP after ME ULP configuration */
+                       mac_reg = E1000_READ_REG(hw, E1000_H2ME);
+                       mac_reg &= ~E1000_H2ME_ULP;
+                       E1000_WRITE_REG(hw, E1000_H2ME, mac_reg);
+#ifdef ULP_IN_D0_SUPPORT
+
+                       /* Restore link speed advertisements and restart
+                        * Auto-negotiation
+                        */
+                       ret_val = e1000_phy_setup_autoneg(hw);
+                       if (ret_val)
+                               goto out;
+
+                       ret_val = e1000_oem_bits_config_ich8lan(hw, true);
+#endif
+               }
+
+               goto out;
+       }
+
+       if (force)
+               /* Toggle LANPHYPC Value bit */
+               e1000_toggle_lanphypc_pch_lpt(hw);
+
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val)
+               goto out;
+
+#ifdef ULP_IN_D0_SUPPORT
+       /* Revert the change to the 'Link Status Change'
+        * interrupt to trigger on 'Cable Status Change'
+        */
+       ret_val = e1000_read_kmrn_reg_locked(hw, E1000_KMRNCTRLSTA_OP_MODES,
+                                            &phy_reg);
+       if (ret_val)
+               goto release;
+       phy_reg &= ~E1000_KMRNCTRLSTA_OP_MODES_LSC2CSC;
+       e1000_write_kmrn_reg_locked(hw, E1000_KMRNCTRLSTA_OP_MODES, phy_reg);
+
+#endif
+       /* Unforce SMBus mode in PHY */
+       ret_val = e1000_read_phy_reg_hv_locked(hw, CV_SMB_CTRL, &phy_reg);
+       if (ret_val) {
+               /* The MAC might be in PCIe mode, so temporarily force to
+                * SMBus mode in order to access the PHY.
+                */
+               mac_reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
+               mac_reg |= E1000_CTRL_EXT_FORCE_SMBUS;
+               E1000_WRITE_REG(hw, E1000_CTRL_EXT, mac_reg);
+
+               msec_delay(50);
+
+               ret_val = e1000_read_phy_reg_hv_locked(hw, CV_SMB_CTRL,
+                                                      &phy_reg);
+               if (ret_val)
+                       goto release;
+       }
+       phy_reg &= ~CV_SMB_CTRL_FORCE_SMBUS;
+       e1000_write_phy_reg_hv_locked(hw, CV_SMB_CTRL, phy_reg);
+
+       /* Unforce SMBus mode in MAC */
+       mac_reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
+       mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS;
+       E1000_WRITE_REG(hw, E1000_CTRL_EXT, mac_reg);
+
+       /* When ULP mode was previously entered, K1 was disabled by the
+        * hardware.  Re-Enable K1 in the PHY when exiting ULP.
+        */
+       ret_val = e1000_read_phy_reg_hv_locked(hw, HV_PM_CTRL, &phy_reg);
+       if (ret_val)
+               goto release;
+       phy_reg |= HV_PM_CTRL_K1_ENABLE;
+       e1000_write_phy_reg_hv_locked(hw, HV_PM_CTRL, phy_reg);
+
+       /* Clear ULP enabled configuration */
+       ret_val = e1000_read_phy_reg_hv_locked(hw, I218_ULP_CONFIG1, &phy_reg);
+       if (ret_val)
+               goto release;
+#ifdef ULP_IN_D0_SUPPORT
+       /* CSC interrupt received due to ULP Indication */
+       if ((phy_reg & I218_ULP_CONFIG1_IND) || force) {
+#endif
+               phy_reg &= ~(I218_ULP_CONFIG1_IND |
+                            I218_ULP_CONFIG1_STICKY_ULP |
+                            I218_ULP_CONFIG1_RESET_TO_SMBUS |
+                            I218_ULP_CONFIG1_WOL_HOST |
+                            I218_ULP_CONFIG1_INBAND_EXIT |
+                            I218_ULP_CONFIG1_DISABLE_SMB_PERST);
+               e1000_write_phy_reg_hv_locked(hw, I218_ULP_CONFIG1, phy_reg);
+
+               /* Commit ULP changes by starting auto ULP configuration */
+               phy_reg |= I218_ULP_CONFIG1_START;
+               e1000_write_phy_reg_hv_locked(hw, I218_ULP_CONFIG1, phy_reg);
+
+               /* Clear Disable SMBus Release on PERST# in MAC */
+               mac_reg = E1000_READ_REG(hw, E1000_FEXTNVM7);
+               mac_reg &= ~E1000_FEXTNVM7_DISABLE_SMB_PERST;
+               E1000_WRITE_REG(hw, E1000_FEXTNVM7, mac_reg);
+
+#ifdef ULP_IN_D0_SUPPORT
+               if (!force) {
+                       hw->phy.ops.release(hw);
+
+                       if (hw->mac.autoneg)
+                               e1000_phy_setup_autoneg(hw);
+
+                       e1000_sw_lcd_config_ich8lan(hw);
+
+                       e1000_oem_bits_config_ich8lan(hw, true);
+
+                       /* Set ULP state to unknown and return non-zero to
+                        * indicate no link (yet) and re-enter on the next LSC
+                        * to finish disabling ULP flow.
+                        */
+                       hw->dev_spec.ich8lan.ulp_state =
+                           e1000_ulp_state_unknown;
+
+                       return 1;
+               }
+       }
+
+       /* Re-enable Tx */
+       mac_reg = E1000_READ_REG(hw, E1000_TCTL);
+       mac_reg |= E1000_TCTL_EN;
+       E1000_WRITE_REG(hw, E1000_TCTL, mac_reg);
+
+#endif
+release:
+       hw->phy.ops.release(hw);
+       if (force) {
+               hw->phy.ops.reset(hw);
+               msec_delay(50);
+       }
+out:
+       if (ret_val)
+               DEBUGOUT1("Error in ULP disable flow: %d\n", ret_val);
+       else
+               hw->dev_spec.ich8lan.ulp_state = e1000_ulp_state_off;
+
+       return ret_val;
+}
+
+#endif /* NAHUM6LP_HW && ULP_SUPPORT */
 /**
  *  e1000_check_for_copper_link_ich8lan - Check for link (Copper)
  *  @hw: pointer to the HW structure
@@ -822,7 +1460,11 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
 {
        struct e1000_mac_info *mac = &hw->mac;
        s32 ret_val;
+#if defined(NAHUM6LP_HW) && defined(ULP_IN_D0_SUPPORT)
+       bool link = false;
+#else
        bool link;
+#endif
        u16 phy_reg;
 
        DEBUGFUNC("e1000_check_for_copper_link_ich8lan");
@@ -835,14 +1477,33 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
        if (!mac->get_link_status)
                return E1000_SUCCESS;
 
-       /*
-        * First we want to see if the MII Status Register reports
-        * link.  If so, then we want to get the current speed/duplex
-        * of the PHY.
-        */
-       ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
-       if (ret_val)
-               return ret_val;
+#if defined(NAHUM6LP_HW) && defined(ULP_IN_D0_SUPPORT)
+       if ((hw->mac.type < e1000_pch_lpt) ||
+           (hw->device_id == E1000_DEV_ID_PCH_LPT_I217_LM) ||
+           (hw->device_id == E1000_DEV_ID_PCH_LPT_I217_V)) {
+#endif /* NAHUM6LP_HW && ULP_IN_D0_SUPPORT */
+               /* First we want to see if the MII Status Register reports
+                * link.  If so, then we want to get the current speed/duplex
+                * of the PHY.
+                */
+               ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
+               if (ret_val)
+                       return ret_val;
+#if defined(NAHUM6LP_HW) && defined(ULP_IN_D0_SUPPORT)
+       } else {
+               /* Check the MAC's STATUS register to determine link state
+                * since the PHY could be inaccessible while in ULP mode.
+                */
+               link = !!(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU);
+               if (link)
+                       ret_val = e1000_disable_ulp_lpt_lp(hw, false);
+               else
+                       ret_val = e1000_enable_ulp_lpt_lp(hw, false);
+
+               if (ret_val)
+                       return ret_val;
+       }
+#endif /* NAHUM6LP_HW && ULP_IN_D0_SUPPORT */
 
        if (hw->mac.type == e1000_pchlan) {
                ret_val = e1000_k1_gig_workaround_hv(hw, link);
@@ -850,6 +1511,53 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
                        return ret_val;
        }
 
+       /* When connected at 10Mbps half-duplex, 82579 parts are excessively
+        * aggressive resulting in many collisions. To avoid this, increase
+        * the IPG and reduce Rx latency in the PHY.
+        */
+       if ((hw->mac.type == e1000_pch2lan) && link) {
+               u32 reg;
+               reg = E1000_READ_REG(hw, E1000_STATUS);
+               if (!(reg & (E1000_STATUS_FD | E1000_STATUS_SPEED_MASK))) {
+                       reg = E1000_READ_REG(hw, E1000_TIPG);
+                       reg &= ~E1000_TIPG_IPGT_MASK;
+                       reg |= 0xFF;
+                       E1000_WRITE_REG(hw, E1000_TIPG, reg);
+
+                       /* Reduce Rx latency in analog PHY */
+                       ret_val = hw->phy.ops.acquire(hw);
+                       if (ret_val)
+                               return ret_val;
+
+                       ret_val = e1000_write_emi_reg_locked(hw, I82579_RX_CONFIG, 0);
+
+                       hw->phy.ops.release(hw);
+
+                       if (ret_val)
+                               return ret_val;
+               }
+       }
+
+#if defined(NAHUM6LP_HW) && defined(NAHUM6_WPT_HW)
+       /* Work-around I218 hang issue */
+       if ((hw->device_id == E1000_DEV_ID_PCH_LPTLP_I218_LM) ||
+           (hw->device_id == E1000_DEV_ID_PCH_LPTLP_I218_V) ||
+           (hw->device_id == E1000_DEV_ID_PCH_I218_LM3) ||
+           (hw->device_id == E1000_DEV_ID_PCH_I218_V3)) {
+               ret_val = e1000_k1_workaround_lpt_lp(hw, link);
+               if (ret_val)
+                       return ret_val;
+       }
+#else
+       /* Work-around I218 hang issue */
+       if ((hw->device_id == E1000_DEV_ID_PCH_LPTLP_I218_LM) ||
+           (hw->device_id == E1000_DEV_ID_PCH_LPTLP_I218_V)) {
+               ret_val = e1000_k1_workaround_lpt_lp(hw, link);
+               if (ret_val)
+                       return ret_val;
+       }
+
+#endif /* defined(NAHUM6LP_HW) && defined(NAHUM6_WPT_HW) */
        /* Clear link partner's EEE ability */
        hw->dev_spec.ich8lan.eee_lp_ability = 0;
 
@@ -895,9 +1603,11 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
        e1000_check_downshift_generic(hw);
 
        /* Enable/Disable EEE after link up */
-       ret_val = e1000_set_eee_pchlan(hw);
-       if (ret_val)
-               return ret_val;
+       if (hw->phy.type > e1000_phy_82579) {
+               ret_val = e1000_set_eee_pchlan(hw);
+               if (ret_val)
+                       return ret_val;
+       }
 
        /* If we are forcing speed/duplex, then we simply return since
         * we have already determined whether we have link or not.
@@ -943,6 +1653,7 @@ void e1000_init_function_pointers_ich8lan(struct e1000_hw *hw)
                break;
        case e1000_pchlan:
        case e1000_pch2lan:
+       case e1000_pch_lpt:
                hw->phy.ops.init_params = e1000_init_phy_params_pchlan;
                break;
        default:
@@ -1147,7 +1858,10 @@ STATIC void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
                return;
        }
 
-       if (index < hw->mac.rar_entry_count) {
+       /* RAR[1-6] are owned by manageability.  Skip those and program the
+        * next address into the SHRA register array.
+        */
+       if (index < (u32) (hw->mac.rar_entry_count - 6)) {
                s32 ret_val;
 
                ret_val = e1000_acquire_swflag_ich8lan(hw);
@@ -1174,6 +1888,130 @@ out:
        DEBUGOUT1("Failed to write receive address at index %d\n", index);
 }
 
+/**
+ *  e1000_rar_set_pch_lpt - Set receive address registers
+ *  @hw: pointer to the HW structure
+ *  @addr: pointer to the receive address
+ *  @index: receive address array register
+ *
+ *  Sets the receive address register array at index to the address passed
+ *  in by addr. For LPT, RAR[0] is the base address register that is to
+ *  contain the MAC address. SHRA[0-10] are the shared receive address
+ *  registers that are shared between the Host and manageability engine (ME).
+ **/
+STATIC void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index)
+{
+       u32 rar_low, rar_high;
+       u32 wlock_mac;
+
+       DEBUGFUNC("e1000_rar_set_pch_lpt");
+
+       /* HW expects these in little endian so we reverse the byte order
+        * from network order (big endian) to little endian
+        */
+       rar_low = ((u32) addr[0] | ((u32) addr[1] << 8) |
+                  ((u32) addr[2] << 16) | ((u32) addr[3] << 24));
+
+       rar_high = ((u32) addr[4] | ((u32) addr[5] << 8));
+
+       /* If MAC address zero, no need to set the AV bit */
+       if (rar_low || rar_high)
+               rar_high |= E1000_RAH_AV;
+
+       if (index == 0) {
+               E1000_WRITE_REG(hw, E1000_RAL(index), rar_low);
+               E1000_WRITE_FLUSH(hw);
+               E1000_WRITE_REG(hw, E1000_RAH(index), rar_high);
+               E1000_WRITE_FLUSH(hw);
+               return;
+       }
+
+       /* The manageability engine (ME) can lock certain SHRAR registers that
+        * it is using - those registers are unavailable for use.
+        */
+       if (index < hw->mac.rar_entry_count) {
+               wlock_mac = E1000_READ_REG(hw, E1000_FWSM) &
+                           E1000_FWSM_WLOCK_MAC_MASK;
+               wlock_mac >>= E1000_FWSM_WLOCK_MAC_SHIFT;
+
+               /* Check if all SHRAR registers are locked */
+               if (wlock_mac == 1)
+                       goto out;
+
+               if ((wlock_mac == 0) || (index <= wlock_mac)) {
+                       s32 ret_val;
+
+                       ret_val = e1000_acquire_swflag_ich8lan(hw);
+
+                       if (ret_val)
+                               goto out;
+
+                       E1000_WRITE_REG(hw, E1000_SHRAL_PCH_LPT(index - 1),
+                                       rar_low);
+                       E1000_WRITE_FLUSH(hw);
+                       E1000_WRITE_REG(hw, E1000_SHRAH_PCH_LPT(index - 1),
+                                       rar_high);
+                       E1000_WRITE_FLUSH(hw);
+
+                       e1000_release_swflag_ich8lan(hw);
+
+                       /* verify the register updates */
+                       if ((E1000_READ_REG(hw, E1000_SHRAL_PCH_LPT(index - 1)) == rar_low) &&
+                           (E1000_READ_REG(hw, E1000_SHRAH_PCH_LPT(index - 1)) == rar_high))
+                               return;
+               }
+       }
+
+out:
+       DEBUGOUT1("Failed to write receive address at index %d\n", index);
+}
+
+#ifndef NO_NON_BLOCKING_PHY_MTA_UPDATE_SUPPORT
+/**
+ *  e1000_update_mc_addr_list_pch2lan - Update Multicast addresses
+ *  @hw: pointer to the HW structure
+ *  @mc_addr_list: array of multicast addresses to program
+ *  @mc_addr_count: number of multicast addresses to program
+ *
+ *  Updates entire Multicast Table Array of the PCH2 MAC and PHY.
+ *  The caller must have a packed mc_addr_list of multicast addresses.
+ **/
+STATIC void e1000_update_mc_addr_list_pch2lan(struct e1000_hw *hw,
+                                             u8 *mc_addr_list,
+                                             u32 mc_addr_count)
+{
+       u16 phy_reg = 0;
+       int i;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_update_mc_addr_list_pch2lan");
+
+       e1000_update_mc_addr_list_generic(hw, mc_addr_list, mc_addr_count);
+
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val)
+               return;
+
+       ret_val = e1000_enable_phy_wakeup_reg_access_bm(hw, &phy_reg);
+       if (ret_val)
+               goto release;
+
+       for (i = 0; i < hw->mac.mta_reg_count; i++) {
+               hw->phy.ops.write_reg_page(hw, BM_MTA(i),
+                                          (u16)(hw->mac.mta_shadow[i] &
+                                                0xFFFF));
+               hw->phy.ops.write_reg_page(hw, (BM_MTA(i) + 1),
+                                          (u16)((hw->mac.mta_shadow[i] >> 16) &
+                                                0xFFFF));
+       }
+
+       e1000_disable_phy_wakeup_reg_access_bm(hw, &phy_reg);
+
+release:
+       hw->phy.ops.release(hw);
+}
+
+#endif /* NO_NON_BLOCKING_PHY_MTA_UPDATE_SUPPORT */
 /**
  *  e1000_check_reset_block_ich8lan - Check if PHY reset is blocked
  *  @hw: pointer to the HW structure
@@ -1185,13 +2023,21 @@ out:
 STATIC s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw)
 {
        u32 fwsm;
+       bool blocked = false;
+       int i = 0;
 
        DEBUGFUNC("e1000_check_reset_block_ich8lan");
 
-       fwsm = E1000_READ_REG(hw, E1000_FWSM);
-
-       return (fwsm & E1000_ICH_FWSM_RSPCIPHY) ? E1000_SUCCESS
-                                               : E1000_BLK_PHY_RESET;
+       do {
+               fwsm = E1000_READ_REG(hw, E1000_FWSM);
+               if (!(fwsm & E1000_ICH_FWSM_RSPCIPHY)) {
+                       blocked = true;
+                       msec_delay(10);
+                       continue;
+               }
+               blocked = false;
+       } while (blocked && (i++ < 10));
+       return blocked ? E1000_BLK_PHY_RESET : E1000_SUCCESS;
 }
 
 /**
@@ -1270,6 +2116,7 @@ STATIC s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw)
                /* Fall-thru */
        case e1000_pchlan:
        case e1000_pch2lan:
+       case e1000_pch_lpt:
                sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M;
                break;
        default:
@@ -1690,8 +2537,8 @@ void e1000_copy_rx_addrs_to_phy_ich8lan(struct e1000_hw *hw)
        if (ret_val)
                goto release;
 
-       /* Copy both RAL/H (rar_entry_count) and SHRAL/H (+4) to PHY */
-       for (i = 0; i < (hw->mac.rar_entry_count + 4); i++) {
+       /* Copy both RAL/H (rar_entry_count) and SHRAL/H to PHY */
+       for (i = 0; i < (hw->mac.rar_entry_count); i++) {
                mac_reg = E1000_READ_REG(hw, E1000_RAL(i));
                hw->phy.ops.write_reg_page(hw, BM_RAR_L(i),
                                           (u16)(mac_reg & 0xFFFF));
@@ -1712,6 +2559,7 @@ release:
        hw->phy.ops.release(hw);
 }
 
+#ifndef CRC32_OS_SUPPORT
 static u32 e1000_calc_rx_da_crc(u8 mac[])
 {
        u32 poly = 0xEDB88320;  /* Polynomial for 802.3 CRC calculation */
@@ -1730,6 +2578,7 @@ static u32 e1000_calc_rx_da_crc(u8 mac[])
        return ~crc;
 }
 
+#endif /* CRC32_OS_SUPPORT */
 /**
  *  e1000_lv_jumbo_workaround_ich8lan - required for jumbo frame operation
  *  with 82579 PHY
@@ -1745,7 +2594,7 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable)
 
        DEBUGFUNC("e1000_lv_jumbo_workaround_ich8lan");
 
-       if (hw->mac.type != e1000_pch2lan)
+       if (hw->mac.type < e1000_pch2lan)
                return E1000_SUCCESS;
 
        /* disable Rx path while enabling/disabling workaround */
@@ -1759,7 +2608,7 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable)
                /* Write Rx addresses (rar_entry_count for RAL/H, and
                 * SHRAL/H) and initial CRC values to the MAC
                 */
-               for (i = 0; i < (hw->mac.rar_entry_count + 4); i++) {
+               for (i = 0; i < hw->mac.rar_entry_count; i++) {
                        u8 mac_addr[ETH_ADDR_LEN] = {0};
                        u32 addr_high, addr_low;
 
@@ -1774,8 +2623,13 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable)
                        mac_addr[4] = (addr_high & 0xFF);
                        mac_addr[5] = ((addr_high >> 8) & 0xFF);
 
+#ifndef CRC32_OS_SUPPORT
                        E1000_WRITE_REG(hw, E1000_PCH_RAICC(i),
                                        e1000_calc_rx_da_crc(mac_addr));
+#else /* CRC32_OS_SUPPORT */
+                       E1000_WRITE_REG(hw, E1000_PCH_RAICC(i),
+                                       E1000_CRC32(ETH_ADDR_LEN, mac_addr));
+#endif /* CRC32_OS_SUPPORT */
                }
 
                /* Write Rx addresses to the PHY */
@@ -2010,7 +2864,7 @@ STATIC void e1000_gate_hw_phy_config_ich8lan(struct e1000_hw *hw, bool gate)
 
        DEBUGFUNC("e1000_gate_hw_phy_config_ich8lan");
 
-       if (hw->mac.type != e1000_pch2lan)
+       if (hw->mac.type < e1000_pch2lan)
                return;
 
        extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
@@ -2228,6 +3082,8 @@ STATIC s32 e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw, bool active)
                ret_val = phy->ops.read_reg(hw,
                                            IGP01E1000_PHY_PORT_CONFIG,
                                            &data);
+               if (ret_val)
+                       return ret_val;
                data &= ~IGP01E1000_PSCFR_SMART_SPEED;
                ret_val = phy->ops.write_reg(hw,
                                             IGP01E1000_PHY_PORT_CONFIG,
@@ -2949,6 +3805,10 @@ STATIC s32 e1000_validate_nvm_checksum_ich8lan(struct e1000_hw *hw)
         * the checksum...a likely scenario.
         */
        switch (hw->mac.type) {
+       case e1000_pch_lpt:
+               word = NVM_COMPAT;
+               valid_csum_mask = NVM_COMPAT_VALID_CSUM;
+               break;
        default:
                word = NVM_FUTURE_INIT_WORD1;
                valid_csum_mask = NVM_FUTURE_INIT_WORD1_VALID_CSUM;
@@ -3611,6 +4471,17 @@ static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw)
                reg |= (E1000_RFCTL_IPV6_EX_DIS | E1000_RFCTL_NEW_IPV6_EXT_DIS);
        E1000_WRITE_REG(hw, E1000_RFCTL, reg);
 
+       /* Enable ECC on Lynxpoint */
+       if (hw->mac.type == e1000_pch_lpt) {
+               reg = E1000_READ_REG(hw, E1000_PBECCSTS);
+               reg |= E1000_PBECCSTS_ECC_ENABLE;
+               E1000_WRITE_REG(hw, E1000_PBECCSTS, reg);
+
+               reg = E1000_READ_REG(hw, E1000_CTRL);
+               reg |= E1000_CTRL_MEHE;
+               E1000_WRITE_REG(hw, E1000_CTRL, reg);
+       }
+
        return;
 }
 
@@ -3725,7 +4596,6 @@ STATIC s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw)
                break;
        case e1000_phy_82577:
        case e1000_phy_82579:
-       case e1000_phy_i217:
                ret_val = e1000_copper_link_setup_82577(hw);
                if (ret_val)
                        return ret_val;
@@ -3762,6 +4632,33 @@ STATIC s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw)
        return e1000_setup_copper_link_generic(hw);
 }
 
+/**
+ *  e1000_setup_copper_link_pch_lpt - Configure MAC/PHY interface
+ *  @hw: pointer to the HW structure
+ *
+ *  Calls the PHY specific link setup function and then calls the
+ *  generic setup_copper_link to finish configuring the link for
+ *  Lynxpoint PCH devices
+ **/
+STATIC s32 e1000_setup_copper_link_pch_lpt(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_setup_copper_link_pch_lpt");
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+       ctrl |= E1000_CTRL_SLU;
+       ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
+       E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+
+       ret_val = e1000_copper_link_setup_82577(hw);
+       if (ret_val)
+               return ret_val;
+
+       return e1000_setup_copper_link_generic(hw);
+}
+
 /**
  *  e1000_get_link_up_info_ich8lan - Get current link speed and duplex
  *  @hw: pointer to the HW structure
@@ -3972,9 +4869,8 @@ void e1000_gig_downshift_workaround_ich8lan(struct e1000_hw *hw)
        if (ret_val)
                return;
        reg_data &= ~E1000_KMRNCTRLSTA_DIAG_NELPBK;
-       ret_val = e1000_write_kmrn_reg_generic(hw,
-                                              E1000_KMRNCTRLSTA_DIAG_OFFSET,
-                                              reg_data);
+       e1000_write_kmrn_reg_generic(hw, E1000_KMRNCTRLSTA_DIAG_OFFSET,
+                                    reg_data);
 }
 
 /**
@@ -4003,7 +4899,27 @@ void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw)
        phy_ctrl |= E1000_PHY_CTRL_GBE_DISABLE;
 
        if (hw->phy.type == e1000_phy_i217) {
-               u16 phy_reg;
+               u16 phy_reg, device_id = hw->device_id;
+
+#ifdef NAHUM6_WPT_HW
+               if ((device_id == E1000_DEV_ID_PCH_LPTLP_I218_LM) ||
+                   (device_id == E1000_DEV_ID_PCH_LPTLP_I218_V) ||
+                   (device_id == E1000_DEV_ID_PCH_I218_LM3) ||
+                   (device_id == E1000_DEV_ID_PCH_I218_V3)) {
+                       u32 fextnvm6 = E1000_READ_REG(hw, E1000_FEXTNVM6);
+
+                       E1000_WRITE_REG(hw, E1000_FEXTNVM6,
+                                       fextnvm6 & ~E1000_FEXTNVM6_REQ_PLL_CLK);
+               }
+#else
+               if ((device_id == E1000_DEV_ID_PCH_LPTLP_I218_LM) ||
+                   (device_id == E1000_DEV_ID_PCH_LPTLP_I218_V)) {
+                       u32 fextnvm6 = E1000_READ_REG(hw, E1000_FEXTNVM6);
+
+                       E1000_WRITE_REG(hw, E1000_FEXTNVM6,
+                                       fextnvm6 & ~E1000_FEXTNVM6_REQ_PLL_CLK);
+               }
+#endif
 
                ret_val = hw->phy.ops.acquire(hw);
                if (ret_val)
@@ -4021,14 +4937,25 @@ void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw)
 
                        /* Disable LPLU if both link partners support 100BaseT
                         * EEE and 100Full is advertised on both ends of the
-                        * link.
+                        * link, and enable Auto Enable LPI since there will
+                        * be no driver to enable LPI while in Sx.
                         */
                        if ((eee_advert & I82579_EEE_100_SUPPORTED) &&
                            (dev_spec->eee_lp_ability &
                             I82579_EEE_100_SUPPORTED) &&
-                           (hw->phy.autoneg_advertised & ADVERTISE_100_FULL))
+                           (hw->phy.autoneg_advertised & ADVERTISE_100_FULL)) {
                                phy_ctrl &= ~(E1000_PHY_CTRL_D0A_LPLU |
                                              E1000_PHY_CTRL_NOND0A_LPLU);
+
+                               /* Set Auto Enable LPI after link up */
+                               hw->phy.ops.read_reg_locked(hw,
+                                                           I217_LPI_GPIO_CTRL,
+                                                           &phy_reg);
+                               phy_reg |= I217_LPI_GPIO_CTRL_AUTO_EN_LPI;
+                               hw->phy.ops.write_reg_locked(hw,
+                                                            I217_LPI_GPIO_CTRL,
+                                                            phy_reg);
+                       }
                }
 
                /* For i217 Intel Rapid Start Technology support,
@@ -4132,6 +5059,11 @@ void e1000_resume_workarounds_pchlan(struct e1000_hw *hw)
                        return;
                }
 
+               /* Clear Auto Enable LPI after link up */
+               hw->phy.ops.read_reg_locked(hw, I217_LPI_GPIO_CTRL, &phy_reg);
+               phy_reg &= ~I217_LPI_GPIO_CTRL_AUTO_EN_LPI;
+               hw->phy.ops.write_reg_locked(hw, I217_LPI_GPIO_CTRL, phy_reg);
+
                if (!(E1000_READ_REG(hw, E1000_FWSM) &
                    E1000_ICH_FWSM_FW_VALID)) {
                        /* Restore clear on SMB if no manageability engine