e1000: more NICs in base driver
authorIntel <intel.com>
Wed, 19 Dec 2012 23:00:00 +0000 (00:00 +0100)
committerThomas Monjalon <thomas.monjalon@6wind.com>
Thu, 25 Jul 2013 13:23:29 +0000 (15:23 +0200)
Signed-off-by: Intel
15 files changed:
lib/librte_pmd_e1000/Makefile
lib/librte_pmd_e1000/e1000/e1000_80003es2lan.c [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_80003es2lan.h [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_82540.c [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_82541.c [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_82541.h [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_82542.c [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_82543.c [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_82543.h [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_82571.c [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_82571.h [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_i210.c [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_i210.h [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_ich8lan.c [new file with mode: 0644]
lib/librte_pmd_e1000/e1000/e1000_ich8lan.h [new file with mode: 0644]

index a3c88e8..890b13e 100644 (file)
@@ -40,11 +40,21 @@ LIB = librte_pmd_e1000.a
 CFLAGS += -O3
 CFLAGS += $(WERROR_FLAGS)
 
+VPATH += $(RTE_SDK)/lib/librte_pmd_e1000/e1000
+
 #
 # all source are stored in SRCS-y
 #
+SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_80003es2lan.c
+SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_82540.c
+SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_82541.c
+SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_82542.c
+SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_82543.c
+SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_82571.c
 SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_82575.c
+SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_i210.c
 SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_api.c
+SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_ich8lan.c
 SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_mac.c
 SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_manage.c
 SRCS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += e1000_mbx.c
diff --git a/lib/librte_pmd_e1000/e1000/e1000_80003es2lan.c b/lib/librte_pmd_e1000/e1000/e1000_80003es2lan.c
new file mode 100644 (file)
index 0000000..c53873a
--- /dev/null
@@ -0,0 +1,1527 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+/*
+ * 80003ES2LAN Gigabit Ethernet Controller (Copper)
+ * 80003ES2LAN Gigabit Ethernet Controller (Serdes)
+ */
+
+#include "e1000_api.h"
+
+STATIC s32  e1000_init_phy_params_80003es2lan(struct e1000_hw *hw);
+STATIC s32  e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw);
+STATIC s32  e1000_init_mac_params_80003es2lan(struct e1000_hw *hw);
+STATIC s32  e1000_acquire_phy_80003es2lan(struct e1000_hw *hw);
+STATIC void e1000_release_phy_80003es2lan(struct e1000_hw *hw);
+STATIC s32  e1000_acquire_nvm_80003es2lan(struct e1000_hw *hw);
+STATIC void e1000_release_nvm_80003es2lan(struct e1000_hw *hw);
+STATIC s32  e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
+                                                  u32 offset,
+                                                  u16 *data);
+STATIC s32  e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
+                                                   u32 offset,
+                                                   u16 data);
+STATIC s32  e1000_write_nvm_80003es2lan(struct e1000_hw *hw, u16 offset,
+                                       u16 words, u16 *data);
+STATIC s32  e1000_get_cfg_done_80003es2lan(struct e1000_hw *hw);
+STATIC s32  e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw);
+STATIC s32  e1000_get_cable_length_80003es2lan(struct e1000_hw *hw);
+STATIC s32  e1000_get_link_up_info_80003es2lan(struct e1000_hw *hw, u16 *speed,
+                                              u16 *duplex);
+STATIC s32  e1000_reset_hw_80003es2lan(struct e1000_hw *hw);
+STATIC s32  e1000_init_hw_80003es2lan(struct e1000_hw *hw);
+STATIC s32  e1000_setup_copper_link_80003es2lan(struct e1000_hw *hw);
+STATIC void e1000_clear_hw_cntrs_80003es2lan(struct e1000_hw *hw);
+static s32  e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask);
+static s32  e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex);
+static s32  e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw);
+static s32  e1000_cfg_on_link_up_80003es2lan(struct e1000_hw *hw);
+static s32  e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
+                                           u16 *data);
+static s32  e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
+                                            u16 data);
+static s32  e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw);
+static void e1000_initialize_hw_bits_80003es2lan(struct e1000_hw *hw);
+static void e1000_release_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask);
+STATIC s32  e1000_read_mac_addr_80003es2lan(struct e1000_hw *hw);
+STATIC void e1000_power_down_phy_copper_80003es2lan(struct e1000_hw *hw);
+
+/*
+ * A table for the GG82563 cable length where the range is defined
+ * with a lower bound at "index" and the upper bound at
+ * "index + 5".
+ */
+static const u16 e1000_gg82563_cable_length_table[] = {
+       0, 60, 115, 150, 150, 60, 115, 150, 180, 180, 0xFF };
+#define GG82563_CABLE_LENGTH_TABLE_SIZE \
+               (sizeof(e1000_gg82563_cable_length_table) / \
+                sizeof(e1000_gg82563_cable_length_table[0]))
+
+/**
+ *  e1000_init_phy_params_80003es2lan - Init ESB2 PHY func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_init_phy_params_80003es2lan");
+
+       if (hw->phy.media_type != e1000_media_type_copper) {
+               phy->type = e1000_phy_none;
+               return E1000_SUCCESS;
+       } else {
+               phy->ops.power_up = e1000_power_up_phy_copper;
+               phy->ops.power_down = e1000_power_down_phy_copper_80003es2lan;
+       }
+
+       phy->addr               = 1;
+       phy->autoneg_mask       = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+       phy->reset_delay_us     = 100;
+       phy->type               = e1000_phy_gg82563;
+
+       phy->ops.acquire        = e1000_acquire_phy_80003es2lan;
+       phy->ops.check_polarity = e1000_check_polarity_m88;
+       phy->ops.check_reset_block = e1000_check_reset_block_generic;
+       phy->ops.commit         = e1000_phy_sw_reset_generic;
+       phy->ops.get_cfg_done   = e1000_get_cfg_done_80003es2lan;
+       phy->ops.get_info       = e1000_get_phy_info_m88;
+       phy->ops.release        = e1000_release_phy_80003es2lan;
+       phy->ops.reset          = e1000_phy_hw_reset_generic;
+       phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_generic;
+
+       phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_80003es2lan;
+       phy->ops.get_cable_length = e1000_get_cable_length_80003es2lan;
+       phy->ops.read_reg       = e1000_read_phy_reg_gg82563_80003es2lan;
+       phy->ops.write_reg      = e1000_write_phy_reg_gg82563_80003es2lan;
+
+       phy->ops.cfg_on_link_up = e1000_cfg_on_link_up_80003es2lan;
+
+       /* This can only be done after all function pointers are setup. */
+       ret_val = e1000_get_phy_id(hw);
+
+       /* Verify phy id */
+       if (phy->id != GG82563_E_PHY_ID)
+               return -E1000_ERR_PHY;
+
+       return ret_val;
+}
+
+/**
+ *  e1000_init_nvm_params_80003es2lan - Init ESB2 NVM func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       u32 eecd = E1000_READ_REG(hw, E1000_EECD);
+       u16 size;
+
+       DEBUGFUNC("e1000_init_nvm_params_80003es2lan");
+
+       nvm->opcode_bits = 8;
+       nvm->delay_usec = 1;
+       switch (nvm->override) {
+       case e1000_nvm_override_spi_large:
+               nvm->page_size = 32;
+               nvm->address_bits = 16;
+               break;
+       case e1000_nvm_override_spi_small:
+               nvm->page_size = 8;
+               nvm->address_bits = 8;
+               break;
+       default:
+               nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8;
+               nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ? 16 : 8;
+               break;
+       }
+
+       nvm->type = e1000_nvm_eeprom_spi;
+
+       size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >>
+                    E1000_EECD_SIZE_EX_SHIFT);
+
+       /*
+        * Added to a constant, "size" becomes the left-shift value
+        * for setting word_size.
+        */
+       size += NVM_WORD_SIZE_BASE_SHIFT;
+
+       /* EEPROM access above 16k is unsupported */
+       if (size > 14)
+               size = 14;
+       nvm->word_size = 1 << size;
+
+       /* Function Pointers */
+       nvm->ops.acquire        = e1000_acquire_nvm_80003es2lan;
+       nvm->ops.read           = e1000_read_nvm_eerd;
+       nvm->ops.release        = e1000_release_nvm_80003es2lan;
+       nvm->ops.update         = e1000_update_nvm_checksum_generic;
+       nvm->ops.valid_led_default = e1000_valid_led_default_generic;
+       nvm->ops.validate       = e1000_validate_nvm_checksum_generic;
+       nvm->ops.write          = e1000_write_nvm_80003es2lan;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_mac_params_80003es2lan - Init ESB2 MAC func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_mac_params_80003es2lan(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+
+       DEBUGFUNC("e1000_init_mac_params_80003es2lan");
+
+       /* Set media type and media-dependent function pointers */
+       switch (hw->device_id) {
+       case E1000_DEV_ID_80003ES2LAN_SERDES_DPT:
+               hw->phy.media_type = e1000_media_type_internal_serdes;
+               mac->ops.check_for_link = e1000_check_for_serdes_link_generic;
+               mac->ops.setup_physical_interface =
+                                       e1000_setup_fiber_serdes_link_generic;
+               break;
+       default:
+               hw->phy.media_type = e1000_media_type_copper;
+               mac->ops.check_for_link = e1000_check_for_copper_link_generic;
+               mac->ops.setup_physical_interface =
+                                       e1000_setup_copper_link_80003es2lan;
+               break;
+       }
+
+       /* Set mta register count */
+       mac->mta_reg_count = 128;
+       /* Set rar entry count */
+       mac->rar_entry_count = E1000_RAR_ENTRIES;
+       /* Set if part includes ASF firmware */
+       mac->asf_firmware_present = true;
+       /* FWSM register */
+       mac->has_fwsm = true;
+       /* ARC supported; valid only if manageability features are enabled. */
+       mac->arc_subsystem_valid = !!(E1000_READ_REG(hw, E1000_FWSM) &
+                                     E1000_FWSM_MODE_MASK);
+       /* Adaptive IFS not supported */
+       mac->adaptive_ifs = false;
+
+       /* Function pointers */
+
+       /* bus type/speed/width */
+       mac->ops.get_bus_info = e1000_get_bus_info_pcie_generic;
+       /* reset */
+       mac->ops.reset_hw = e1000_reset_hw_80003es2lan;
+       /* hw initialization */
+       mac->ops.init_hw = e1000_init_hw_80003es2lan;
+       /* link setup */
+       mac->ops.setup_link = e1000_setup_link_generic;
+       /* check management mode */
+       mac->ops.check_mng_mode = e1000_check_mng_mode_generic;
+       /* multicast address update */
+       mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
+       /* writing VFTA */
+       mac->ops.write_vfta = e1000_write_vfta_generic;
+       /* clearing VFTA */
+       mac->ops.clear_vfta = e1000_clear_vfta_generic;
+       /* read mac address */
+       mac->ops.read_mac_addr = e1000_read_mac_addr_80003es2lan;
+       /* ID LED init */
+       mac->ops.id_led_init = e1000_id_led_init_generic;
+       /* blink LED */
+       mac->ops.blink_led = e1000_blink_led_generic;
+       /* setup LED */
+       mac->ops.setup_led = e1000_setup_led_generic;
+       /* cleanup LED */
+       mac->ops.cleanup_led = e1000_cleanup_led_generic;
+       /* turn on/off LED */
+       mac->ops.led_on = e1000_led_on_generic;
+       mac->ops.led_off = e1000_led_off_generic;
+       /* clear hardware counters */
+       mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_80003es2lan;
+       /* link info */
+       mac->ops.get_link_up_info = e1000_get_link_up_info_80003es2lan;
+
+       /* set lan id for port to determine which phy lock to use */
+       hw->mac.ops.set_lan_id(hw);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_function_pointers_80003es2lan - Init ESB2 func ptrs.
+ *  @hw: pointer to the HW structure
+ *
+ *  Called to initialize all function pointers and parameters.
+ **/
+void e1000_init_function_pointers_80003es2lan(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_init_function_pointers_80003es2lan");
+
+       hw->mac.ops.init_params = e1000_init_mac_params_80003es2lan;
+       hw->nvm.ops.init_params = e1000_init_nvm_params_80003es2lan;
+       hw->phy.ops.init_params = e1000_init_phy_params_80003es2lan;
+}
+
+/**
+ *  e1000_acquire_phy_80003es2lan - Acquire rights to access PHY
+ *  @hw: pointer to the HW structure
+ *
+ *  A wrapper to acquire access rights to the correct PHY.
+ **/
+STATIC s32 e1000_acquire_phy_80003es2lan(struct e1000_hw *hw)
+{
+       u16 mask;
+
+       DEBUGFUNC("e1000_acquire_phy_80003es2lan");
+
+       mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM;
+       return e1000_acquire_swfw_sync_80003es2lan(hw, mask);
+}
+
+/**
+ *  e1000_release_phy_80003es2lan - Release rights to access PHY
+ *  @hw: pointer to the HW structure
+ *
+ *  A wrapper to release access rights to the correct PHY.
+ **/
+STATIC void e1000_release_phy_80003es2lan(struct e1000_hw *hw)
+{
+       u16 mask;
+
+       DEBUGFUNC("e1000_release_phy_80003es2lan");
+
+       mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM;
+       e1000_release_swfw_sync_80003es2lan(hw, mask);
+}
+
+/**
+ *  e1000_acquire_mac_csr_80003es2lan - Acquire right to access Kumeran register
+ *  @hw: pointer to the HW structure
+ *
+ *  Acquire the semaphore to access the Kumeran interface.
+ *
+ **/
+STATIC s32 e1000_acquire_mac_csr_80003es2lan(struct e1000_hw *hw)
+{
+       u16 mask;
+
+       DEBUGFUNC("e1000_acquire_mac_csr_80003es2lan");
+
+       mask = E1000_SWFW_CSR_SM;
+
+       return e1000_acquire_swfw_sync_80003es2lan(hw, mask);
+}
+
+/**
+ *  e1000_release_mac_csr_80003es2lan - Release right to access Kumeran Register
+ *  @hw: pointer to the HW structure
+ *
+ *  Release the semaphore used to access the Kumeran interface
+ **/
+STATIC void e1000_release_mac_csr_80003es2lan(struct e1000_hw *hw)
+{
+       u16 mask;
+
+       DEBUGFUNC("e1000_release_mac_csr_80003es2lan");
+
+       mask = E1000_SWFW_CSR_SM;
+
+       e1000_release_swfw_sync_80003es2lan(hw, mask);
+}
+
+/**
+ *  e1000_acquire_nvm_80003es2lan - Acquire rights to access NVM
+ *  @hw: pointer to the HW structure
+ *
+ *  Acquire the semaphore to access the EEPROM.
+ **/
+STATIC s32 e1000_acquire_nvm_80003es2lan(struct e1000_hw *hw)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_acquire_nvm_80003es2lan");
+
+       ret_val = e1000_acquire_swfw_sync_80003es2lan(hw, E1000_SWFW_EEP_SM);
+       if (ret_val)
+               return ret_val;
+
+       ret_val = e1000_acquire_nvm_generic(hw);
+
+       if (ret_val)
+               e1000_release_swfw_sync_80003es2lan(hw, E1000_SWFW_EEP_SM);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_release_nvm_80003es2lan - Relinquish rights to access NVM
+ *  @hw: pointer to the HW structure
+ *
+ *  Release the semaphore used to access the EEPROM.
+ **/
+STATIC void e1000_release_nvm_80003es2lan(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_release_nvm_80003es2lan");
+
+       e1000_release_nvm_generic(hw);
+       e1000_release_swfw_sync_80003es2lan(hw, E1000_SWFW_EEP_SM);
+}
+
+/**
+ *  e1000_acquire_swfw_sync_80003es2lan - Acquire SW/FW semaphore
+ *  @hw: pointer to the HW structure
+ *  @mask: specifies which semaphore to acquire
+ *
+ *  Acquire the SW/FW semaphore to access the PHY or NVM.  The mask
+ *  will also specify which port we're acquiring the lock for.
+ **/
+static s32 e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask)
+{
+       u32 swfw_sync;
+       u32 swmask = mask;
+       u32 fwmask = mask << 16;
+       s32 i = 0;
+       s32 timeout = 50;
+
+       DEBUGFUNC("e1000_acquire_swfw_sync_80003es2lan");
+
+       while (i < timeout) {
+               if (e1000_get_hw_semaphore_generic(hw))
+                       return -E1000_ERR_SWFW_SYNC;
+
+               swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC);
+               if (!(swfw_sync & (fwmask | swmask)))
+                       break;
+
+               /*
+                * Firmware currently using resource (fwmask)
+                * or other software thread using resource (swmask)
+                */
+               e1000_put_hw_semaphore_generic(hw);
+               msec_delay_irq(5);
+               i++;
+       }
+
+       if (i == timeout) {
+               DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n");
+               return -E1000_ERR_SWFW_SYNC;
+       }
+
+       swfw_sync |= swmask;
+       E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync);
+
+       e1000_put_hw_semaphore_generic(hw);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_release_swfw_sync_80003es2lan - Release SW/FW semaphore
+ *  @hw: pointer to the HW structure
+ *  @mask: specifies which semaphore to acquire
+ *
+ *  Release the SW/FW semaphore used to access the PHY or NVM.  The mask
+ *  will also specify which port we're releasing the lock for.
+ **/
+static void e1000_release_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask)
+{
+       u32 swfw_sync;
+
+       DEBUGFUNC("e1000_release_swfw_sync_80003es2lan");
+
+       while (e1000_get_hw_semaphore_generic(hw) != E1000_SUCCESS)
+               ; /* Empty */
+
+       swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC);
+       swfw_sync &= ~mask;
+       E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync);
+
+       e1000_put_hw_semaphore_generic(hw);
+}
+
+/**
+ *  e1000_read_phy_reg_gg82563_80003es2lan - Read GG82563 PHY register
+ *  @hw: pointer to the HW structure
+ *  @offset: offset of the register to read
+ *  @data: pointer to the data returned from the operation
+ *
+ *  Read the GG82563 PHY register.
+ **/
+STATIC s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
+                                                 u32 offset, u16 *data)
+{
+       s32 ret_val;
+       u32 page_select;
+       u16 temp;
+
+       DEBUGFUNC("e1000_read_phy_reg_gg82563_80003es2lan");
+
+       ret_val = e1000_acquire_phy_80003es2lan(hw);
+       if (ret_val)
+               return ret_val;
+
+       /* Select Configuration Page */
+       if ((offset & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) {
+               page_select = GG82563_PHY_PAGE_SELECT;
+       } else {
+               /*
+                * Use Alternative Page Select register to access
+                * registers 30 and 31
+                */
+               page_select = GG82563_PHY_PAGE_SELECT_ALT;
+       }
+
+       temp = (u16)((u16)offset >> GG82563_PAGE_SHIFT);
+       ret_val = e1000_write_phy_reg_mdic(hw, page_select, temp);
+       if (ret_val) {
+               e1000_release_phy_80003es2lan(hw);
+               return ret_val;
+       }
+
+       if (hw->dev_spec._80003es2lan.mdic_wa_enable) {
+               /*
+                * The "ready" bit in the MDIC register may be incorrectly set
+                * before the device has completed the "Page Select" MDI
+                * transaction.  So we wait 200us after each MDI command...
+                */
+               usec_delay(200);
+
+               /* ...and verify the command was successful. */
+               ret_val = e1000_read_phy_reg_mdic(hw, page_select, &temp);
+
+               if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) {
+                       e1000_release_phy_80003es2lan(hw);
+                       return -E1000_ERR_PHY;
+               }
+
+               usec_delay(200);
+
+               ret_val = e1000_read_phy_reg_mdic(hw,
+                                                 MAX_PHY_REG_ADDRESS & offset,
+                                                 data);
+
+               usec_delay(200);
+       } else {
+               ret_val = e1000_read_phy_reg_mdic(hw,
+                                                 MAX_PHY_REG_ADDRESS & offset,
+                                                 data);
+       }
+
+       e1000_release_phy_80003es2lan(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_write_phy_reg_gg82563_80003es2lan - Write GG82563 PHY register
+ *  @hw: pointer to the HW structure
+ *  @offset: offset of the register to read
+ *  @data: value to write to the register
+ *
+ *  Write to the GG82563 PHY register.
+ **/
+STATIC s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
+                                                  u32 offset, u16 data)
+{
+       s32 ret_val;
+       u32 page_select;
+       u16 temp;
+
+       DEBUGFUNC("e1000_write_phy_reg_gg82563_80003es2lan");
+
+       ret_val = e1000_acquire_phy_80003es2lan(hw);
+       if (ret_val)
+               return ret_val;
+
+       /* Select Configuration Page */
+       if ((offset & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) {
+               page_select = GG82563_PHY_PAGE_SELECT;
+       } else {
+               /*
+                * Use Alternative Page Select register to access
+                * registers 30 and 31
+                */
+               page_select = GG82563_PHY_PAGE_SELECT_ALT;
+       }
+
+       temp = (u16)((u16)offset >> GG82563_PAGE_SHIFT);
+       ret_val = e1000_write_phy_reg_mdic(hw, page_select, temp);
+       if (ret_val) {
+               e1000_release_phy_80003es2lan(hw);
+               return ret_val;
+       }
+
+       if (hw->dev_spec._80003es2lan.mdic_wa_enable) {
+               /*
+                * The "ready" bit in the MDIC register may be incorrectly set
+                * before the device has completed the "Page Select" MDI
+                * transaction.  So we wait 200us after each MDI command...
+                */
+               usec_delay(200);
+
+               /* ...and verify the command was successful. */
+               ret_val = e1000_read_phy_reg_mdic(hw, page_select, &temp);
+
+               if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) {
+                       e1000_release_phy_80003es2lan(hw);
+                       return -E1000_ERR_PHY;
+               }
+
+               usec_delay(200);
+
+               ret_val = e1000_write_phy_reg_mdic(hw,
+                                                 MAX_PHY_REG_ADDRESS & offset,
+                                                 data);
+
+               usec_delay(200);
+       } else {
+               ret_val = e1000_write_phy_reg_mdic(hw,
+                                                 MAX_PHY_REG_ADDRESS & offset,
+                                                 data);
+       }
+
+       e1000_release_phy_80003es2lan(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_write_nvm_80003es2lan - Write to ESB2 NVM
+ *  @hw: pointer to the HW structure
+ *  @offset: offset of the register to read
+ *  @words: number of words to write
+ *  @data: buffer of data to write to the NVM
+ *
+ *  Write "words" of data to the ESB2 NVM.
+ **/
+STATIC s32 e1000_write_nvm_80003es2lan(struct e1000_hw *hw, u16 offset,
+                                      u16 words, u16 *data)
+{
+       DEBUGFUNC("e1000_write_nvm_80003es2lan");
+
+       return e1000_write_nvm_spi(hw, offset, words, data);
+}
+
+/**
+ *  e1000_get_cfg_done_80003es2lan - Wait for configuration to complete
+ *  @hw: pointer to the HW structure
+ *
+ *  Wait a specific amount of time for manageability processes to complete.
+ *  This is a function pointer entry point called by the phy module.
+ **/
+STATIC s32 e1000_get_cfg_done_80003es2lan(struct e1000_hw *hw)
+{
+       s32 timeout = PHY_CFG_TIMEOUT;
+       u32 mask = E1000_NVM_CFG_DONE_PORT_0;
+
+       DEBUGFUNC("e1000_get_cfg_done_80003es2lan");
+
+       if (hw->bus.func == 1)
+               mask = E1000_NVM_CFG_DONE_PORT_1;
+
+       while (timeout) {
+               if (E1000_READ_REG(hw, E1000_EEMNGCTL) & mask)
+                       break;
+               msec_delay(1);
+               timeout--;
+       }
+       if (!timeout) {
+               DEBUGOUT("MNG configuration cycle has not completed.\n");
+               return -E1000_ERR_RESET;
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_phy_force_speed_duplex_80003es2lan - Force PHY speed and duplex
+ *  @hw: pointer to the HW structure
+ *
+ *  Force the speed and duplex settings onto the PHY.  This is a
+ *  function pointer entry point called by the phy module.
+ **/
+STATIC s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw)
+{
+       s32 ret_val;
+       u16 phy_data;
+       bool link;
+
+       DEBUGFUNC("e1000_phy_force_speed_duplex_80003es2lan");
+
+       if (!(hw->phy.ops.read_reg))
+               return E1000_SUCCESS;
+
+       /*
+        * Clear Auto-Crossover to force MDI manually.  M88E1000 requires MDI
+        * forced whenever speed and duplex are forced.
+        */
+       ret_val = hw->phy.ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data);
+       if (ret_val)
+               return ret_val;
+
+       phy_data &= ~GG82563_PSCR_CROSSOVER_MODE_AUTO;
+       ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_SPEC_CTRL, phy_data);
+       if (ret_val)
+               return ret_val;
+
+       DEBUGOUT1("GG82563 PSCR: %X\n", phy_data);
+
+       ret_val = hw->phy.ops.read_reg(hw, PHY_CONTROL, &phy_data);
+       if (ret_val)
+               return ret_val;
+
+       e1000_phy_force_speed_duplex_setup(hw, &phy_data);
+
+       /* Reset the phy to commit changes. */
+       phy_data |= MII_CR_RESET;
+
+       ret_val = hw->phy.ops.write_reg(hw, PHY_CONTROL, phy_data);
+       if (ret_val)
+               return ret_val;
+
+       usec_delay(1);
+
+       if (hw->phy.autoneg_wait_to_complete) {
+               DEBUGOUT("Waiting for forced speed/duplex link on GG82563 phy.\n");
+
+               ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT,
+                                                    100000, &link);
+               if (ret_val)
+                       return ret_val;
+
+               if (!link) {
+                       /*
+                        * We didn't get link.
+                        * Reset the DSP and cross our fingers.
+                        */
+                       ret_val = e1000_phy_reset_dsp_generic(hw);
+                       if (ret_val)
+                               return ret_val;
+               }
+
+               /* Try once more */
+               ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT,
+                                                    100000, &link);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_MAC_SPEC_CTRL,
+                                      &phy_data);
+       if (ret_val)
+               return ret_val;
+
+       /*
+        * Resetting the phy means we need to verify the TX_CLK corresponds
+        * to the link speed.  10Mbps -> 2.5MHz, else 25MHz.
+        */
+       phy_data &= ~GG82563_MSCR_TX_CLK_MASK;
+       if (hw->mac.forced_speed_duplex & E1000_ALL_10_SPEED)
+               phy_data |= GG82563_MSCR_TX_CLK_10MBPS_2_5;
+       else
+               phy_data |= GG82563_MSCR_TX_CLK_100MBPS_25;
+
+       /*
+        * In addition, we must re-enable CRS on Tx for both half and full
+        * duplex.
+        */
+       phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
+       ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_MAC_SPEC_CTRL,
+                                       phy_data);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_get_cable_length_80003es2lan - Set approximate cable length
+ *  @hw: pointer to the HW structure
+ *
+ *  Find the approximate cable length as measured by the GG82563 PHY.
+ *  This is a function pointer entry point called by the phy module.
+ **/
+STATIC s32 e1000_get_cable_length_80003es2lan(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val = E1000_SUCCESS;
+       u16 phy_data, index;
+
+       DEBUGFUNC("e1000_get_cable_length_80003es2lan");
+
+       if (!(hw->phy.ops.read_reg))
+               return E1000_SUCCESS;
+
+       ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_DSP_DISTANCE, &phy_data);
+       if (ret_val)
+               return ret_val;
+
+       index = phy_data & GG82563_DSPD_CABLE_LENGTH;
+
+       if (index >= GG82563_CABLE_LENGTH_TABLE_SIZE - 5)
+               return -E1000_ERR_PHY;
+
+       phy->min_cable_length = e1000_gg82563_cable_length_table[index];
+       phy->max_cable_length = e1000_gg82563_cable_length_table[index + 5];
+
+       phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_get_link_up_info_80003es2lan - Report speed and duplex
+ *  @hw: pointer to the HW structure
+ *  @speed: pointer to speed buffer
+ *  @duplex: pointer to duplex buffer
+ *
+ *  Retrieve the current speed and duplex configuration.
+ **/
+STATIC s32 e1000_get_link_up_info_80003es2lan(struct e1000_hw *hw, u16 *speed,
+                                             u16 *duplex)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_get_link_up_info_80003es2lan");
+
+       if (hw->phy.media_type == e1000_media_type_copper) {
+               ret_val = e1000_get_speed_and_duplex_copper_generic(hw, speed,
+                                                                   duplex);
+               hw->phy.ops.cfg_on_link_up(hw);
+       } else {
+               ret_val = e1000_get_speed_and_duplex_fiber_serdes_generic(hw,
+                                                                 speed,
+                                                                 duplex);
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_reset_hw_80003es2lan - Reset the ESB2 controller
+ *  @hw: pointer to the HW structure
+ *
+ *  Perform a global reset to the ESB2 controller.
+ **/
+STATIC s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       s32 ret_val;
+       u16 kum_reg_data;
+
+       DEBUGFUNC("e1000_reset_hw_80003es2lan");
+
+       /*
+        * Prevent the PCI-E bus from sticking if there is no TLP connection
+        * on the last TLP read/write transaction when MAC is reset.
+        */
+       ret_val = e1000_disable_pcie_master_generic(hw);
+       if (ret_val)
+               DEBUGOUT("PCI-E Master disable polling has failed.\n");
+
+       DEBUGOUT("Masking off all interrupts\n");
+       E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+
+       E1000_WRITE_REG(hw, E1000_RCTL, 0);
+       E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP);
+       E1000_WRITE_FLUSH(hw);
+
+       msec_delay(10);
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       ret_val = e1000_acquire_phy_80003es2lan(hw);
+       DEBUGOUT("Issuing a global reset to MAC\n");
+       E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST);
+       e1000_release_phy_80003es2lan(hw);
+
+       /* Disable IBIST slave mode (far-end loopback) */
+       e1000_read_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
+                                       &kum_reg_data);
+       kum_reg_data |= E1000_KMRNCTRLSTA_IBIST_DISABLE;
+       e1000_write_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
+                                       kum_reg_data);
+
+       ret_val = e1000_get_auto_rd_done_generic(hw);
+       if (ret_val)
+               /* We don't want to continue accessing MAC registers. */
+               return ret_val;
+
+       /* Clear any pending interrupt events. */
+       E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+       E1000_READ_REG(hw, E1000_ICR);
+
+       return e1000_check_alt_mac_addr_generic(hw);
+}
+
+/**
+ *  e1000_init_hw_80003es2lan - Initialize the ESB2 controller
+ *  @hw: pointer to the HW structure
+ *
+ *  Initialize the hw bits, LED, VFTA, MTA, link and hw counters.
+ **/
+STATIC s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       u32 reg_data;
+       s32 ret_val;
+       u16 kum_reg_data;
+       u16 i;
+
+       DEBUGFUNC("e1000_init_hw_80003es2lan");
+
+       e1000_initialize_hw_bits_80003es2lan(hw);
+
+       /* Initialize identification LED */
+       ret_val = mac->ops.id_led_init(hw);
+       if (ret_val)
+               DEBUGOUT("Error initializing identification LED\n");
+               /* This is not fatal and we should not stop init due to this */
+
+       /* Disabling VLAN filtering */
+       DEBUGOUT("Initializing the IEEE VLAN\n");
+       mac->ops.clear_vfta(hw);
+
+       /* Setup the receive address. */
+       e1000_init_rx_addrs_generic(hw, mac->rar_entry_count);
+
+       /* Zero out the Multicast HASH table */
+       DEBUGOUT("Zeroing the MTA\n");
+       for (i = 0; i < mac->mta_reg_count; i++)
+               E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
+
+       /* Setup link and flow control */
+       ret_val = mac->ops.setup_link(hw);
+
+       /* Disable IBIST slave mode (far-end loopback) */
+       e1000_read_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
+                                       &kum_reg_data);
+       kum_reg_data |= E1000_KMRNCTRLSTA_IBIST_DISABLE;
+       e1000_write_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
+                                        kum_reg_data);
+
+       /* Set the transmit descriptor write-back policy */
+       reg_data = E1000_READ_REG(hw, E1000_TXDCTL(0));
+       reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) |
+                  E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC;
+       E1000_WRITE_REG(hw, E1000_TXDCTL(0), reg_data);
+
+       /* ...for both queues. */
+       reg_data = E1000_READ_REG(hw, E1000_TXDCTL(1));
+       reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) |
+                  E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC;
+       E1000_WRITE_REG(hw, E1000_TXDCTL(1), reg_data);
+
+       /* Enable retransmit on late collisions */
+       reg_data = E1000_READ_REG(hw, E1000_TCTL);
+       reg_data |= E1000_TCTL_RTLC;
+       E1000_WRITE_REG(hw, E1000_TCTL, reg_data);
+
+       /* Configure Gigabit Carry Extend Padding */
+       reg_data = E1000_READ_REG(hw, E1000_TCTL_EXT);
+       reg_data &= ~E1000_TCTL_EXT_GCEX_MASK;
+       reg_data |= DEFAULT_TCTL_EXT_GCEX_80003ES2LAN;
+       E1000_WRITE_REG(hw, E1000_TCTL_EXT, reg_data);
+
+       /* Configure Transmit Inter-Packet Gap */
+       reg_data = E1000_READ_REG(hw, E1000_TIPG);
+       reg_data &= ~E1000_TIPG_IPGT_MASK;
+       reg_data |= DEFAULT_TIPG_IPGT_1000_80003ES2LAN;
+       E1000_WRITE_REG(hw, E1000_TIPG, reg_data);
+
+       reg_data = E1000_READ_REG_ARRAY(hw, E1000_FFLT, 0x0001);
+       reg_data &= ~0x00100000;
+       E1000_WRITE_REG_ARRAY(hw, E1000_FFLT, 0x0001, reg_data);
+
+       /* default to true to enable the MDIC W/A */
+       hw->dev_spec._80003es2lan.mdic_wa_enable = true;
+
+       ret_val = e1000_read_kmrn_reg_80003es2lan(hw,
+                                                E1000_KMRNCTRLSTA_OFFSET >>
+                                                E1000_KMRNCTRLSTA_OFFSET_SHIFT,
+                                                &i);
+       if (!ret_val) {
+               if ((i & E1000_KMRNCTRLSTA_OPMODE_MASK) ==
+                    E1000_KMRNCTRLSTA_OPMODE_INBAND_MDIO)
+                       hw->dev_spec._80003es2lan.mdic_wa_enable = false;
+       }
+
+       /*
+        * Clear all of the statistics registers (clear on read).  It is
+        * important that we do this after we have tried to establish link
+        * because the symbol error count will increment wildly if there
+        * is no link.
+        */
+       e1000_clear_hw_cntrs_80003es2lan(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_initialize_hw_bits_80003es2lan - Init hw bits of ESB2
+ *  @hw: pointer to the HW structure
+ *
+ *  Initializes required hardware-dependent bits needed for normal operation.
+ **/
+static void e1000_initialize_hw_bits_80003es2lan(struct e1000_hw *hw)
+{
+       u32 reg;
+
+       DEBUGFUNC("e1000_initialize_hw_bits_80003es2lan");
+
+       /* Transmit Descriptor Control 0 */
+       reg = E1000_READ_REG(hw, E1000_TXDCTL(0));
+       reg |= (1 << 22);
+       E1000_WRITE_REG(hw, E1000_TXDCTL(0), reg);
+
+       /* Transmit Descriptor Control 1 */
+       reg = E1000_READ_REG(hw, E1000_TXDCTL(1));
+       reg |= (1 << 22);
+       E1000_WRITE_REG(hw, E1000_TXDCTL(1), reg);
+
+       /* Transmit Arbitration Control 0 */
+       reg = E1000_READ_REG(hw, E1000_TARC(0));
+       reg &= ~(0xF << 27); /* 30:27 */
+       if (hw->phy.media_type != e1000_media_type_copper)
+               reg &= ~(1 << 20);
+       E1000_WRITE_REG(hw, E1000_TARC(0), reg);
+
+       /* Transmit Arbitration Control 1 */
+       reg = E1000_READ_REG(hw, E1000_TARC(1));
+       if (E1000_READ_REG(hw, E1000_TCTL) & E1000_TCTL_MULR)
+               reg &= ~(1 << 28);
+       else
+               reg |= (1 << 28);
+       E1000_WRITE_REG(hw, E1000_TARC(1), reg);
+
+       /*
+        * Disable IPv6 extension header parsing because some malformed
+        * IPv6 headers can hang the Rx.
+        */
+       reg = E1000_READ_REG(hw, E1000_RFCTL);
+       reg |= (E1000_RFCTL_IPV6_EX_DIS | E1000_RFCTL_NEW_IPV6_EXT_DIS);
+       E1000_WRITE_REG(hw, E1000_RFCTL, reg);
+
+       return;
+}
+
+/**
+ *  e1000_copper_link_setup_gg82563_80003es2lan - Configure GG82563 Link
+ *  @hw: pointer to the HW structure
+ *
+ *  Setup some GG82563 PHY registers for obtaining link
+ **/
+static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val;
+       u32 ctrl_ext;
+       u16 data;
+
+       DEBUGFUNC("e1000_copper_link_setup_gg82563_80003es2lan");
+
+       ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, &data);
+       if (ret_val)
+               return ret_val;
+
+       data |= GG82563_MSCR_ASSERT_CRS_ON_TX;
+       /* Use 25MHz for both link down and 1000Base-T for Tx clock. */
+       data |= GG82563_MSCR_TX_CLK_1000MBPS_25;
+
+       ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, data);
+       if (ret_val)
+               return ret_val;
+
+       /*
+        * Options:
+        *   MDI/MDI-X = 0 (default)
+        *   0 - Auto for all speeds
+        *   1 - MDI mode
+        *   2 - MDI-X mode
+        *   3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
+        */
+       ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_SPEC_CTRL, &data);
+       if (ret_val)
+               return ret_val;
+
+       data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK;
+
+       switch (phy->mdix) {
+       case 1:
+               data |= GG82563_PSCR_CROSSOVER_MODE_MDI;
+               break;
+       case 2:
+               data |= GG82563_PSCR_CROSSOVER_MODE_MDIX;
+               break;
+       case 0:
+       default:
+               data |= GG82563_PSCR_CROSSOVER_MODE_AUTO;
+               break;
+       }
+
+       /*
+        * Options:
+        *   disable_polarity_correction = 0 (default)
+        *       Automatic Correction for Reversed Cable Polarity
+        *   0 - Disabled
+        *   1 - Enabled
+        */
+       data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE;
+       if (phy->disable_polarity_correction)
+               data |= GG82563_PSCR_POLARITY_REVERSAL_DISABLE;
+
+       ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_SPEC_CTRL, data);
+       if (ret_val)
+               return ret_val;
+
+       /* SW Reset the PHY so all changes take effect */
+       ret_val = hw->phy.ops.commit(hw);
+       if (ret_val) {
+               DEBUGOUT("Error Resetting the PHY\n");
+               return ret_val;
+       }
+
+       /* Bypass Rx and Tx FIFO's */
+       ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
+                                       E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL,
+                                       E1000_KMRNCTRLSTA_FIFO_CTRL_RX_BYPASS |
+                                       E1000_KMRNCTRLSTA_FIFO_CTRL_TX_BYPASS);
+       if (ret_val)
+               return ret_val;
+
+       ret_val = e1000_read_kmrn_reg_80003es2lan(hw,
+                               E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE, &data);
+       if (ret_val)
+               return ret_val;
+       data |= E1000_KMRNCTRLSTA_OPMODE_E_IDLE;
+       ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
+                               E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE, data);
+       if (ret_val)
+               return ret_val;
+
+       ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_SPEC_CTRL_2, &data);
+       if (ret_val)
+               return ret_val;
+
+       data &= ~GG82563_PSCR2_REVERSE_AUTO_NEG;
+       ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_SPEC_CTRL_2, data);
+       if (ret_val)
+               return ret_val;
+
+       ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
+       ctrl_ext &= ~(E1000_CTRL_EXT_LINK_MODE_MASK);
+       E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+
+       ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_PWR_MGMT_CTRL, &data);
+       if (ret_val)
+               return ret_val;
+
+       /*
+        * Do not init these registers when the HW is in IAMT mode, since the
+        * firmware will have already initialized them.  We only initialize
+        * them if the HW is not in IAMT mode.
+        */
+       if (!hw->mac.ops.check_mng_mode(hw)) {
+               /* Enable Electrical Idle on the PHY */
+               data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE;
+               ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_PWR_MGMT_CTRL,
+                                               data);
+               if (ret_val)
+                       return ret_val;
+
+               ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL,
+                                              &data);
+               if (ret_val)
+                       return ret_val;
+
+               data &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
+               ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_KMRN_MODE_CTRL,
+                                               data);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       /*
+        * Workaround: Disable padding in Kumeran interface in the MAC
+        * and in the PHY to avoid CRC errors.
+        */
+       ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_INBAND_CTRL, &data);
+       if (ret_val)
+               return ret_val;
+
+       data |= GG82563_ICR_DIS_PADDING;
+       ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_INBAND_CTRL, data);
+       if (ret_val)
+               return ret_val;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_setup_copper_link_80003es2lan - Setup Copper Link for ESB2
+ *  @hw: pointer to the HW structure
+ *
+ *  Essentially a wrapper for setting up all things "copper" related.
+ *  This is a function pointer entry point called by the mac module.
+ **/
+STATIC s32 e1000_setup_copper_link_80003es2lan(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       s32 ret_val;
+       u16 reg_data;
+
+       DEBUGFUNC("e1000_setup_copper_link_80003es2lan");
+
+       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);
+
+       /*
+        * Set the mac to wait the maximum time between each
+        * iteration and increase the max iterations when
+        * polling the phy; this fixes erroneous timeouts at 10Mbps.
+        */
+       ret_val = e1000_write_kmrn_reg_80003es2lan(hw, GG82563_REG(0x34, 4),
+                                                  0xFFFF);
+       if (ret_val)
+               return ret_val;
+       ret_val = e1000_read_kmrn_reg_80003es2lan(hw, GG82563_REG(0x34, 9),
+                                                 &reg_data);
+       if (ret_val)
+               return ret_val;
+       reg_data |= 0x3F;
+       ret_val = e1000_write_kmrn_reg_80003es2lan(hw, GG82563_REG(0x34, 9),
+                                                  reg_data);
+       if (ret_val)
+               return ret_val;
+       ret_val = e1000_read_kmrn_reg_80003es2lan(hw,
+                               E1000_KMRNCTRLSTA_OFFSET_INB_CTRL, &reg_data);
+       if (ret_val)
+               return ret_val;
+       reg_data |= E1000_KMRNCTRLSTA_INB_CTRL_DIS_PADDING;
+       ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
+                               E1000_KMRNCTRLSTA_OFFSET_INB_CTRL, reg_data);
+       if (ret_val)
+               return ret_val;
+
+       ret_val = e1000_copper_link_setup_gg82563_80003es2lan(hw);
+       if (ret_val)
+               return ret_val;
+
+       return e1000_setup_copper_link_generic(hw);
+}
+
+/**
+ *  e1000_cfg_on_link_up_80003es2lan - es2 link configuration after link-up
+ *  @hw: pointer to the HW structure
+ *  @duplex: current duplex setting
+ *
+ *  Configure the KMRN interface by applying last minute quirks for
+ *  10/100 operation.
+ **/
+static s32 e1000_cfg_on_link_up_80003es2lan(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u16 speed;
+       u16 duplex;
+
+       DEBUGFUNC("e1000_configure_on_link_up");
+
+       if (hw->phy.media_type == e1000_media_type_copper) {
+               ret_val = e1000_get_speed_and_duplex_copper_generic(hw, &speed,
+                                                                   &duplex);
+               if (ret_val)
+                       return ret_val;
+
+               if (speed == SPEED_1000)
+                       ret_val = e1000_cfg_kmrn_1000_80003es2lan(hw);
+               else
+                       ret_val = e1000_cfg_kmrn_10_100_80003es2lan(hw, duplex);
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_cfg_kmrn_10_100_80003es2lan - Apply "quirks" for 10/100 operation
+ *  @hw: pointer to the HW structure
+ *  @duplex: current duplex setting
+ *
+ *  Configure the KMRN interface by applying last minute quirks for
+ *  10/100 operation.
+ **/
+static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex)
+{
+       s32 ret_val;
+       u32 tipg;
+       u32 i = 0;
+       u16 reg_data, reg_data2;
+
+       DEBUGFUNC("e1000_configure_kmrn_for_10_100");
+
+       reg_data = E1000_KMRNCTRLSTA_HD_CTRL_10_100_DEFAULT;
+       ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
+                                      E1000_KMRNCTRLSTA_OFFSET_HD_CTRL,
+                                      reg_data);
+       if (ret_val)
+               return ret_val;
+
+       /* Configure Transmit Inter-Packet Gap */
+       tipg = E1000_READ_REG(hw, E1000_TIPG);
+       tipg &= ~E1000_TIPG_IPGT_MASK;
+       tipg |= DEFAULT_TIPG_IPGT_10_100_80003ES2LAN;
+       E1000_WRITE_REG(hw, E1000_TIPG, tipg);
+
+       do {
+               ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL,
+                                              &reg_data);
+               if (ret_val)
+                       return ret_val;
+
+               ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL,
+                                              &reg_data2);
+               if (ret_val)
+                       return ret_val;
+               i++;
+       } while ((reg_data != reg_data2) && (i < GG82563_MAX_KMRN_RETRY));
+
+       if (duplex == HALF_DUPLEX)
+               reg_data |= GG82563_KMCR_PASS_FALSE_CARRIER;
+       else
+               reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
+
+       return hw->phy.ops.write_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data);
+}
+
+/**
+ *  e1000_cfg_kmrn_1000_80003es2lan - Apply "quirks" for gigabit operation
+ *  @hw: pointer to the HW structure
+ *
+ *  Configure the KMRN interface by applying last minute quirks for
+ *  gigabit operation.
+ **/
+static s32 e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw)
+{
+       s32 ret_val;
+       u16 reg_data, reg_data2;
+       u32 tipg;
+       u32 i = 0;
+
+       DEBUGFUNC("e1000_configure_kmrn_for_1000");
+
+       reg_data = E1000_KMRNCTRLSTA_HD_CTRL_1000_DEFAULT;
+       ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
+                               E1000_KMRNCTRLSTA_OFFSET_HD_CTRL, reg_data);
+       if (ret_val)
+               return ret_val;
+
+       /* Configure Transmit Inter-Packet Gap */
+       tipg = E1000_READ_REG(hw, E1000_TIPG);
+       tipg &= ~E1000_TIPG_IPGT_MASK;
+       tipg |= DEFAULT_TIPG_IPGT_1000_80003ES2LAN;
+       E1000_WRITE_REG(hw, E1000_TIPG, tipg);
+
+       do {
+               ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL,
+                                              &reg_data);
+               if (ret_val)
+                       return ret_val;
+
+               ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL,
+                                              &reg_data2);
+               if (ret_val)
+                       return ret_val;
+               i++;
+       } while ((reg_data != reg_data2) && (i < GG82563_MAX_KMRN_RETRY));
+
+       reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
+
+       return hw->phy.ops.write_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data);
+}
+
+/**
+ *  e1000_read_kmrn_reg_80003es2lan - Read kumeran register
+ *  @hw: pointer to the HW structure
+ *  @offset: register offset to be read
+ *  @data: pointer to the read data
+ *
+ *  Acquire semaphore, then read the PHY register at offset
+ *  using the kumeran interface.  The information retrieved is stored in data.
+ *  Release the semaphore before exiting.
+ **/
+static s32 e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
+                                          u16 *data)
+{
+       u32 kmrnctrlsta;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_read_kmrn_reg_80003es2lan");
+
+       ret_val = e1000_acquire_mac_csr_80003es2lan(hw);
+       if (ret_val)
+               return ret_val;
+
+       kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
+                      E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
+       E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta);
+       E1000_WRITE_FLUSH(hw);
+
+       usec_delay(2);
+
+       kmrnctrlsta = E1000_READ_REG(hw, E1000_KMRNCTRLSTA);
+       *data = (u16)kmrnctrlsta;
+
+       e1000_release_mac_csr_80003es2lan(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_write_kmrn_reg_80003es2lan - Write kumeran register
+ *  @hw: pointer to the HW structure
+ *  @offset: register offset to write to
+ *  @data: data to write at register offset
+ *
+ *  Acquire semaphore, then write the data to PHY register
+ *  at the offset using the kumeran interface.  Release semaphore
+ *  before exiting.
+ **/
+static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
+                                           u16 data)
+{
+       u32 kmrnctrlsta;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_write_kmrn_reg_80003es2lan");
+
+       ret_val = e1000_acquire_mac_csr_80003es2lan(hw);
+       if (ret_val)
+               return ret_val;
+
+       kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
+                      E1000_KMRNCTRLSTA_OFFSET) | data;
+       E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta);
+       E1000_WRITE_FLUSH(hw);
+
+       usec_delay(2);
+
+       e1000_release_mac_csr_80003es2lan(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_read_mac_addr_80003es2lan - Read device MAC address
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_read_mac_addr_80003es2lan(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_read_mac_addr_80003es2lan");
+
+       /*
+        * If there's an alternate MAC address place it in RAR0
+        * so that it will override the Si installed default perm
+        * address.
+        */
+       ret_val = e1000_check_alt_mac_addr_generic(hw);
+       if (ret_val)
+               return ret_val;
+
+       return e1000_read_mac_addr_generic(hw);
+}
+
+/**
+ * e1000_power_down_phy_copper_80003es2lan - Remove link during PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, remove the link.
+ **/
+STATIC void e1000_power_down_phy_copper_80003es2lan(struct e1000_hw *hw)
+{
+       /* If the management interface is not enabled, then power down */
+       if (!(hw->mac.ops.check_mng_mode(hw) ||
+             hw->phy.ops.check_reset_block(hw)))
+               e1000_power_down_phy_copper(hw);
+
+       return;
+}
+
+/**
+ *  e1000_clear_hw_cntrs_80003es2lan - Clear device specific hardware counters
+ *  @hw: pointer to the HW structure
+ *
+ *  Clears the hardware counters by reading the counter registers.
+ **/
+STATIC void e1000_clear_hw_cntrs_80003es2lan(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_clear_hw_cntrs_80003es2lan");
+
+       e1000_clear_hw_cntrs_base_generic(hw);
+
+       E1000_READ_REG(hw, E1000_PRC64);
+       E1000_READ_REG(hw, E1000_PRC127);
+       E1000_READ_REG(hw, E1000_PRC255);
+       E1000_READ_REG(hw, E1000_PRC511);
+       E1000_READ_REG(hw, E1000_PRC1023);
+       E1000_READ_REG(hw, E1000_PRC1522);
+       E1000_READ_REG(hw, E1000_PTC64);
+       E1000_READ_REG(hw, E1000_PTC127);
+       E1000_READ_REG(hw, E1000_PTC255);
+       E1000_READ_REG(hw, E1000_PTC511);
+       E1000_READ_REG(hw, E1000_PTC1023);
+       E1000_READ_REG(hw, E1000_PTC1522);
+
+       E1000_READ_REG(hw, E1000_ALGNERRC);
+       E1000_READ_REG(hw, E1000_RXERRC);
+       E1000_READ_REG(hw, E1000_TNCRS);
+       E1000_READ_REG(hw, E1000_CEXTERR);
+       E1000_READ_REG(hw, E1000_TSCTC);
+       E1000_READ_REG(hw, E1000_TSCTFC);
+
+       E1000_READ_REG(hw, E1000_MGTPRC);
+       E1000_READ_REG(hw, E1000_MGTPDC);
+       E1000_READ_REG(hw, E1000_MGTPTC);
+
+       E1000_READ_REG(hw, E1000_IAC);
+       E1000_READ_REG(hw, E1000_ICRXOC);
+
+       E1000_READ_REG(hw, E1000_ICRXPTC);
+       E1000_READ_REG(hw, E1000_ICRXATC);
+       E1000_READ_REG(hw, E1000_ICTXPTC);
+       E1000_READ_REG(hw, E1000_ICTXATC);
+       E1000_READ_REG(hw, E1000_ICTXQEC);
+       E1000_READ_REG(hw, E1000_ICTXQMTC);
+       E1000_READ_REG(hw, E1000_ICRXDMTC);
+}
diff --git a/lib/librte_pmd_e1000/e1000/e1000_80003es2lan.h b/lib/librte_pmd_e1000/e1000/e1000_80003es2lan.h
new file mode 100644 (file)
index 0000000..9668b6a
--- /dev/null
@@ -0,0 +1,102 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#ifndef _E1000_80003ES2LAN_H_
+#define _E1000_80003ES2LAN_H_
+
+#define E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL     0x00
+#define E1000_KMRNCTRLSTA_OFFSET_INB_CTRL      0x02
+#define E1000_KMRNCTRLSTA_OFFSET_HD_CTRL       0x10
+#define E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE        0x1F
+
+#define E1000_KMRNCTRLSTA_FIFO_CTRL_RX_BYPASS  0x0008
+#define E1000_KMRNCTRLSTA_FIFO_CTRL_TX_BYPASS  0x0800
+#define E1000_KMRNCTRLSTA_INB_CTRL_DIS_PADDING 0x0010
+
+#define E1000_KMRNCTRLSTA_HD_CTRL_10_100_DEFAULT 0x0004
+#define E1000_KMRNCTRLSTA_HD_CTRL_1000_DEFAULT 0x0000
+#define E1000_KMRNCTRLSTA_OPMODE_E_IDLE                0x2000
+
+#define E1000_KMRNCTRLSTA_OPMODE_MASK          0x000C
+#define E1000_KMRNCTRLSTA_OPMODE_INBAND_MDIO   0x0004
+
+#define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gigabit Carry Extend Padding */
+#define DEFAULT_TCTL_EXT_GCEX_80003ES2LAN      0x00010000
+
+#define DEFAULT_TIPG_IPGT_1000_80003ES2LAN     0x8
+#define DEFAULT_TIPG_IPGT_10_100_80003ES2LAN   0x9
+
+/* GG82563 PHY Specific Status Register (Page 0, Register 16 */
+#define GG82563_PSCR_POLARITY_REVERSAL_DISABLE 0x0002 /* 1=Reversal Disabled */
+#define GG82563_PSCR_CROSSOVER_MODE_MASK       0x0060
+#define GG82563_PSCR_CROSSOVER_MODE_MDI                0x0000 /* 00=Manual MDI */
+#define GG82563_PSCR_CROSSOVER_MODE_MDIX       0x0020 /* 01=Manual MDIX */
+#define GG82563_PSCR_CROSSOVER_MODE_AUTO       0x0060 /* 11=Auto crossover */
+
+/* PHY Specific Control Register 2 (Page 0, Register 26) */
+#define GG82563_PSCR2_REVERSE_AUTO_NEG         0x2000 /* 1=Reverse Auto-Nego */
+
+/* MAC Specific Control Register (Page 2, Register 21) */
+/* Tx clock speed for Link Down and 1000BASE-T for the following speeds */
+#define GG82563_MSCR_TX_CLK_MASK               0x0007
+#define GG82563_MSCR_TX_CLK_10MBPS_2_5         0x0004
+#define GG82563_MSCR_TX_CLK_100MBPS_25         0x0005
+#define GG82563_MSCR_TX_CLK_1000MBPS_2_5       0x0006
+#define GG82563_MSCR_TX_CLK_1000MBPS_25                0x0007
+
+#define GG82563_MSCR_ASSERT_CRS_ON_TX          0x0010 /* 1=Assert */
+
+/* DSP Distance Register (Page 5, Register 26) */
+/*
+ * 0 = <50M
+ * 1 = 50-80M
+ * 2 = 80-100M
+ * 3 = 110-140M
+ * 4 = >140M
+ */
+#define GG82563_DSPD_CABLE_LENGTH              0x0007
+
+/* Kumeran Mode Control Register (Page 193, Register 16) */
+#define GG82563_KMCR_PASS_FALSE_CARRIER                0x0800
+
+/* Max number of times Kumeran read/write should be validated */
+#define GG82563_MAX_KMRN_RETRY                 0x5
+
+/* Power Management Control Register (Page 193, Register 20) */
+/* 1=Enable SERDES Electrical Idle */
+#define GG82563_PMCR_ENABLE_ELECTRICAL_IDLE    0x0001
+
+/* In-Band Control Register (Page 194, Register 18) */
+#define GG82563_ICR_DIS_PADDING                        0x0010 /* Disable Padding */
+
+#endif
diff --git a/lib/librte_pmd_e1000/e1000/e1000_82540.c b/lib/librte_pmd_e1000/e1000/e1000_82540.c
new file mode 100644 (file)
index 0000000..451928d
--- /dev/null
@@ -0,0 +1,717 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+/*
+ * 82540EM Gigabit Ethernet Controller
+ * 82540EP Gigabit Ethernet Controller
+ * 82545EM Gigabit Ethernet Controller (Copper)
+ * 82545EM Gigabit Ethernet Controller (Fiber)
+ * 82545GM Gigabit Ethernet Controller
+ * 82546EB Gigabit Ethernet Controller (Copper)
+ * 82546EB Gigabit Ethernet Controller (Fiber)
+ * 82546GB Gigabit Ethernet Controller
+ */
+
+#include "e1000_api.h"
+
+STATIC s32  e1000_init_phy_params_82540(struct e1000_hw *hw);
+STATIC s32  e1000_init_nvm_params_82540(struct e1000_hw *hw);
+STATIC s32  e1000_init_mac_params_82540(struct e1000_hw *hw);
+static s32  e1000_adjust_serdes_amplitude_82540(struct e1000_hw *hw);
+STATIC void e1000_clear_hw_cntrs_82540(struct e1000_hw *hw);
+STATIC s32  e1000_init_hw_82540(struct e1000_hw *hw);
+STATIC s32  e1000_reset_hw_82540(struct e1000_hw *hw);
+static s32  e1000_set_phy_mode_82540(struct e1000_hw *hw);
+static s32  e1000_set_vco_speed_82540(struct e1000_hw *hw);
+STATIC s32  e1000_setup_copper_link_82540(struct e1000_hw *hw);
+STATIC s32  e1000_setup_fiber_serdes_link_82540(struct e1000_hw *hw);
+STATIC void e1000_power_down_phy_copper_82540(struct e1000_hw *hw);
+STATIC s32  e1000_read_mac_addr_82540(struct e1000_hw *hw);
+
+/**
+ * e1000_init_phy_params_82540 - Init PHY func ptrs.
+ * @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_phy_params_82540(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val = E1000_SUCCESS;
+
+       phy->addr               = 1;
+       phy->autoneg_mask       = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+       phy->reset_delay_us     = 10000;
+       phy->type               = e1000_phy_m88;
+
+       /* Function Pointers */
+       phy->ops.check_polarity = e1000_check_polarity_m88;
+       phy->ops.commit         = e1000_phy_sw_reset_generic;
+       phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88;
+       phy->ops.get_cable_length = e1000_get_cable_length_m88;
+       phy->ops.get_cfg_done   = e1000_get_cfg_done_generic;
+       phy->ops.read_reg       = e1000_read_phy_reg_m88;
+       phy->ops.reset          = e1000_phy_hw_reset_generic;
+       phy->ops.write_reg      = e1000_write_phy_reg_m88;
+       phy->ops.get_info       = e1000_get_phy_info_m88;
+       phy->ops.power_up       = e1000_power_up_phy_copper;
+       phy->ops.power_down     = e1000_power_down_phy_copper_82540;
+
+       ret_val = e1000_get_phy_id(hw);
+       if (ret_val)
+               goto out;
+
+       /* Verify phy id */
+       switch (hw->mac.type) {
+       case e1000_82540:
+       case e1000_82545:
+       case e1000_82545_rev_3:
+       case e1000_82546:
+       case e1000_82546_rev_3:
+               if (phy->id == M88E1011_I_PHY_ID)
+                       break;
+               /* Fall Through */
+       default:
+               ret_val = -E1000_ERR_PHY;
+               goto out;
+               break;
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ * e1000_init_nvm_params_82540 - Init NVM func ptrs.
+ * @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_nvm_params_82540(struct e1000_hw *hw)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       u32 eecd = E1000_READ_REG(hw, E1000_EECD);
+
+       DEBUGFUNC("e1000_init_nvm_params_82540");
+
+       nvm->type = e1000_nvm_eeprom_microwire;
+       nvm->delay_usec = 50;
+       nvm->opcode_bits = 3;
+       switch (nvm->override) {
+       case e1000_nvm_override_microwire_large:
+               nvm->address_bits = 8;
+               nvm->word_size = 256;
+               break;
+       case e1000_nvm_override_microwire_small:
+               nvm->address_bits = 6;
+               nvm->word_size = 64;
+               break;
+       default:
+               nvm->address_bits = eecd & E1000_EECD_SIZE ? 8 : 6;
+               nvm->word_size = eecd & E1000_EECD_SIZE ? 256 : 64;
+               break;
+       }
+
+       /* Function Pointers */
+       nvm->ops.acquire        = e1000_acquire_nvm_generic;
+       nvm->ops.read           = e1000_read_nvm_microwire;
+       nvm->ops.release        = e1000_release_nvm_generic;
+       nvm->ops.update         = e1000_update_nvm_checksum_generic;
+       nvm->ops.valid_led_default = e1000_valid_led_default_generic;
+       nvm->ops.validate       = e1000_validate_nvm_checksum_generic;
+       nvm->ops.write          = e1000_write_nvm_microwire;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ * e1000_init_mac_params_82540 - Init MAC func ptrs.
+ * @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_mac_params_82540(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_init_mac_params_82540");
+
+       /* Set media type */
+       switch (hw->device_id) {
+       case E1000_DEV_ID_82545EM_FIBER:
+       case E1000_DEV_ID_82545GM_FIBER:
+       case E1000_DEV_ID_82546EB_FIBER:
+       case E1000_DEV_ID_82546GB_FIBER:
+               hw->phy.media_type = e1000_media_type_fiber;
+               break;
+       case E1000_DEV_ID_82545GM_SERDES:
+       case E1000_DEV_ID_82546GB_SERDES:
+               hw->phy.media_type = e1000_media_type_internal_serdes;
+               break;
+       default:
+               hw->phy.media_type = e1000_media_type_copper;
+               break;
+       }
+
+       /* Set mta register count */
+       mac->mta_reg_count = 128;
+       /* Set rar entry count */
+       mac->rar_entry_count = E1000_RAR_ENTRIES;
+
+       /* Function pointers */
+
+       /* bus type/speed/width */
+       mac->ops.get_bus_info = e1000_get_bus_info_pci_generic;
+       /* function id */
+       mac->ops.set_lan_id = e1000_set_lan_id_multi_port_pci;
+       /* reset */
+       mac->ops.reset_hw = e1000_reset_hw_82540;
+       /* hw initialization */
+       mac->ops.init_hw = e1000_init_hw_82540;
+       /* link setup */
+       mac->ops.setup_link = e1000_setup_link_generic;
+       /* physical interface setup */
+       mac->ops.setup_physical_interface =
+               (hw->phy.media_type == e1000_media_type_copper)
+                       ? e1000_setup_copper_link_82540
+                       : e1000_setup_fiber_serdes_link_82540;
+       /* check for link */
+       switch (hw->phy.media_type) {
+       case e1000_media_type_copper:
+               mac->ops.check_for_link = e1000_check_for_copper_link_generic;
+               break;
+       case e1000_media_type_fiber:
+               mac->ops.check_for_link = e1000_check_for_fiber_link_generic;
+               break;
+       case e1000_media_type_internal_serdes:
+               mac->ops.check_for_link = e1000_check_for_serdes_link_generic;
+               break;
+       default:
+               ret_val = -E1000_ERR_CONFIG;
+               goto out;
+               break;
+       }
+       /* link info */
+       mac->ops.get_link_up_info =
+               (hw->phy.media_type == e1000_media_type_copper)
+                       ? e1000_get_speed_and_duplex_copper_generic
+                       : e1000_get_speed_and_duplex_fiber_serdes_generic;
+       /* multicast address update */
+       mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
+       /* writing VFTA */
+       mac->ops.write_vfta = e1000_write_vfta_generic;
+       /* clearing VFTA */
+       mac->ops.clear_vfta = e1000_clear_vfta_generic;
+       /* read mac address */
+       mac->ops.read_mac_addr = e1000_read_mac_addr_82540;
+       /* ID LED init */
+       mac->ops.id_led_init = e1000_id_led_init_generic;
+       /* setup LED */
+       mac->ops.setup_led = e1000_setup_led_generic;
+       /* cleanup LED */
+       mac->ops.cleanup_led = e1000_cleanup_led_generic;
+       /* turn on/off LED */
+       mac->ops.led_on = e1000_led_on_generic;
+       mac->ops.led_off = e1000_led_off_generic;
+       /* clear hardware counters */
+       mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_82540;
+
+out:
+       return ret_val;
+}
+
+/**
+ * e1000_init_function_pointers_82540 - Init func ptrs.
+ * @hw: pointer to the HW structure
+ *
+ * Called to initialize all function pointers and parameters.
+ **/
+void e1000_init_function_pointers_82540(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_init_function_pointers_82540");
+
+       hw->mac.ops.init_params = e1000_init_mac_params_82540;
+       hw->nvm.ops.init_params = e1000_init_nvm_params_82540;
+       hw->phy.ops.init_params = e1000_init_phy_params_82540;
+}
+
+/**
+ *  e1000_reset_hw_82540 - Reset hardware
+ *  @hw: pointer to the HW structure
+ *
+ *  This resets the hardware into a known state.
+ **/
+STATIC s32 e1000_reset_hw_82540(struct e1000_hw *hw)
+{
+       u32 ctrl, manc;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_reset_hw_82540");
+
+       DEBUGOUT("Masking off all interrupts\n");
+       E1000_WRITE_REG(hw, E1000_IMC, 0xFFFFFFFF);
+
+       E1000_WRITE_REG(hw, E1000_RCTL, 0);
+       E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP);
+       E1000_WRITE_FLUSH(hw);
+
+       /*
+        * Delay to allow any outstanding PCI transactions to complete
+        * before resetting the device.
+        */
+       msec_delay(10);
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       DEBUGOUT("Issuing a global reset to 82540/82545/82546 MAC\n");
+       switch (hw->mac.type) {
+       case e1000_82545_rev_3:
+       case e1000_82546_rev_3:
+               E1000_WRITE_REG(hw, E1000_CTRL_DUP, ctrl | E1000_CTRL_RST);
+               break;
+       default:
+               /*
+                * These controllers can't ack the 64-bit write when
+                * issuing the reset, so we use IO-mapping as a
+                * workaround to issue the reset.
+                */
+               E1000_WRITE_REG_IO(hw, E1000_CTRL, ctrl | E1000_CTRL_RST);
+               break;
+       }
+
+       /* Wait for EEPROM reload */
+       msec_delay(5);
+
+       /* Disable HW ARPs on ASF enabled adapters */
+       manc = E1000_READ_REG(hw, E1000_MANC);
+       manc &= ~E1000_MANC_ARP_EN;
+       E1000_WRITE_REG(hw, E1000_MANC, manc);
+
+       E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+       E1000_READ_REG(hw, E1000_ICR);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_init_hw_82540 - Initialize hardware
+ *  @hw: pointer to the HW structure
+ *
+ *  This inits the hardware readying it for operation.
+ **/
+STATIC s32 e1000_init_hw_82540(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       u32 txdctl, ctrl_ext;
+       s32 ret_val = E1000_SUCCESS;
+       u16 i;
+
+       DEBUGFUNC("e1000_init_hw_82540");
+
+       /* Initialize identification LED */
+       ret_val = mac->ops.id_led_init(hw);
+       if (ret_val) {
+               DEBUGOUT("Error initializing identification LED\n");
+               /* This is not fatal and we should not stop init due to this */
+       }
+
+       /* Disabling VLAN filtering */
+       DEBUGOUT("Initializing the IEEE VLAN\n");
+       if (mac->type < e1000_82545_rev_3)
+               E1000_WRITE_REG(hw, E1000_VET, 0);
+
+       mac->ops.clear_vfta(hw);
+
+       /* Setup the receive address. */
+       e1000_init_rx_addrs_generic(hw, mac->rar_entry_count);
+
+       /* Zero out the Multicast HASH table */
+       DEBUGOUT("Zeroing the MTA\n");
+       for (i = 0; i < mac->mta_reg_count; i++) {
+               E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
+               /*
+                * Avoid back to back register writes by adding the register
+                * read (flush).  This is to protect against some strange
+                * bridge configurations that may issue Memory Write Block
+                * (MWB) to our register space.  The *_rev_3 hardware at
+                * least doesn't respond correctly to every other dword in an
+                * MWB to our register space.
+                */
+               E1000_WRITE_FLUSH(hw);
+       }
+
+       if (mac->type < e1000_82545_rev_3)
+               e1000_pcix_mmrbc_workaround_generic(hw);
+
+       /* Setup link and flow control */
+       ret_val = mac->ops.setup_link(hw);
+
+       txdctl = E1000_READ_REG(hw, E1000_TXDCTL(0));
+       txdctl = (txdctl & ~E1000_TXDCTL_WTHRESH) |
+                 E1000_TXDCTL_FULL_TX_DESC_WB;
+       E1000_WRITE_REG(hw, E1000_TXDCTL(0), txdctl);
+
+       /*
+        * Clear all of the statistics registers (clear on read).  It is
+        * important that we do this after we have tried to establish link
+        * because the symbol error count will increment wildly if there
+        * is no link.
+        */
+       e1000_clear_hw_cntrs_82540(hw);
+
+       if ((hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER) ||
+           (hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3)) {
+               ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
+               /*
+                * Relaxed ordering must be disabled to avoid a parity
+                * error crash in a PCI slot.
+                */
+               ctrl_ext |= E1000_CTRL_EXT_RO_DIS;
+               E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_setup_copper_link_82540 - Configure copper link settings
+ *  @hw: pointer to the HW structure
+ *
+ *  Calls the appropriate function to configure the link for auto-neg or forced
+ *  speed and duplex.  Then we check for link, once link is established calls
+ *  to configure collision distance and flow control are called.  If link is
+ *  not established, we return -E1000_ERR_PHY (-2).
+ **/
+STATIC s32 e1000_setup_copper_link_82540(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       s32 ret_val = E1000_SUCCESS;
+       u16 data;
+
+       DEBUGFUNC("e1000_setup_copper_link_82540");
+
+       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_set_phy_mode_82540(hw);
+       if (ret_val)
+               goto out;
+
+       if (hw->mac.type == e1000_82545_rev_3 ||
+           hw->mac.type == e1000_82546_rev_3) {
+               ret_val = hw->phy.ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                              &data);
+               if (ret_val)
+                       goto out;
+               data |= 0x00000008;
+               ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL,
+                                               data);
+               if (ret_val)
+                       goto out;
+       }
+
+       ret_val = e1000_copper_link_setup_m88(hw);
+       if (ret_val)
+               goto out;
+
+       ret_val = e1000_setup_copper_link_generic(hw);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_setup_fiber_serdes_link_82540 - Setup link for fiber/serdes
+ *  @hw: pointer to the HW structure
+ *
+ *  Set the output amplitude to the value in the EEPROM and adjust the VCO
+ *  speed to improve Bit Error Rate (BER) performance.  Configures collision
+ *  distance and flow control for fiber and serdes links.  Upon successful
+ *  setup, poll for link.
+ **/
+STATIC s32 e1000_setup_fiber_serdes_link_82540(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_setup_fiber_serdes_link_82540");
+
+       switch (mac->type) {
+       case e1000_82545_rev_3:
+       case e1000_82546_rev_3:
+               if (hw->phy.media_type == e1000_media_type_internal_serdes) {
+                       /*
+                        * If we're on serdes media, adjust the output
+                        * amplitude to value set in the EEPROM.
+                        */
+                       ret_val = e1000_adjust_serdes_amplitude_82540(hw);
+                       if (ret_val)
+                               goto out;
+               }
+               /* Adjust VCO speed to improve BER performance */
+               ret_val = e1000_set_vco_speed_82540(hw);
+               if (ret_val)
+                       goto out;
+       default:
+               break;
+       }
+
+       ret_val = e1000_setup_fiber_serdes_link_generic(hw);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_adjust_serdes_amplitude_82540 - Adjust amplitude based on EEPROM
+ *  @hw: pointer to the HW structure
+ *
+ *  Adjust the SERDES output amplitude based on the EEPROM settings.
+ **/
+static s32 e1000_adjust_serdes_amplitude_82540(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u16 nvm_data;
+
+       DEBUGFUNC("e1000_adjust_serdes_amplitude_82540");
+
+       ret_val = hw->nvm.ops.read(hw, NVM_SERDES_AMPLITUDE, 1, &nvm_data);
+       if (ret_val)
+               goto out;
+
+       if (nvm_data != NVM_RESERVED_WORD) {
+               /* Adjust serdes output amplitude only. */
+               nvm_data &= NVM_SERDES_AMPLITUDE_MASK;
+               ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_EXT_CTRL,
+                                               nvm_data);
+               if (ret_val)
+                       goto out;
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_set_vco_speed_82540 - Set VCO speed for better performance
+ *  @hw: pointer to the HW structure
+ *
+ *  Set the VCO speed to improve Bit Error Rate (BER) performance.
+ **/
+static s32 e1000_set_vco_speed_82540(struct e1000_hw *hw)
+{
+       s32  ret_val = E1000_SUCCESS;
+       u16 default_page = 0;
+       u16 phy_data;
+
+       DEBUGFUNC("e1000_set_vco_speed_82540");
+
+       /* Set PHY register 30, page 5, bit 8 to 0 */
+
+       ret_val = hw->phy.ops.read_reg(hw, M88E1000_PHY_PAGE_SELECT,
+                                      &default_page);
+       if (ret_val)
+               goto out;
+
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0005);
+       if (ret_val)
+               goto out;
+
+       ret_val = hw->phy.ops.read_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data);
+       if (ret_val)
+               goto out;
+
+       phy_data &= ~M88E1000_PHY_VCO_REG_BIT8;
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data);
+       if (ret_val)
+               goto out;
+
+       /* Set PHY register 30, page 4, bit 11 to 1 */
+
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0004);
+       if (ret_val)
+               goto out;
+
+       ret_val = hw->phy.ops.read_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data);
+       if (ret_val)
+               goto out;
+
+       phy_data |= M88E1000_PHY_VCO_REG_BIT11;
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data);
+       if (ret_val)
+               goto out;
+
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_PAGE_SELECT,
+                                       default_page);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_set_phy_mode_82540 - Set PHY to class A mode
+ *  @hw: pointer to the HW structure
+ *
+ *  Sets the PHY to class A mode and assumes the following operations will
+ *  follow to enable the new class mode:
+ *    1.  Do a PHY soft reset.
+ *    2.  Restart auto-negotiation or force link.
+ **/
+static s32 e1000_set_phy_mode_82540(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u16 nvm_data;
+
+       DEBUGFUNC("e1000_set_phy_mode_82540");
+
+       if (hw->mac.type != e1000_82545_rev_3)
+               goto out;
+
+       ret_val = hw->nvm.ops.read(hw, NVM_PHY_CLASS_WORD, 1, &nvm_data);
+       if (ret_val) {
+               ret_val = -E1000_ERR_PHY;
+               goto out;
+       }
+
+       if ((nvm_data != NVM_RESERVED_WORD) && (nvm_data & NVM_PHY_CLASS_A)) {
+               ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_PAGE_SELECT,
+                                               0x000B);
+               if (ret_val) {
+                       ret_val = -E1000_ERR_PHY;
+                       goto out;
+               }
+               ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL,
+                                               0x8104);
+               if (ret_val) {
+                       ret_val = -E1000_ERR_PHY;
+                       goto out;
+               }
+
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ * e1000_power_down_phy_copper_82540 - Remove link in case of PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, remove the link.
+ **/
+STATIC void e1000_power_down_phy_copper_82540(struct e1000_hw *hw)
+{
+       /* If the management interface is not enabled, then power down */
+       if (!(E1000_READ_REG(hw, E1000_MANC) & E1000_MANC_SMBUS_EN))
+               e1000_power_down_phy_copper(hw);
+
+       return;
+}
+
+/**
+ *  e1000_clear_hw_cntrs_82540 - Clear device specific hardware counters
+ *  @hw: pointer to the HW structure
+ *
+ *  Clears the hardware counters by reading the counter registers.
+ **/
+STATIC void e1000_clear_hw_cntrs_82540(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_clear_hw_cntrs_82540");
+
+       e1000_clear_hw_cntrs_base_generic(hw);
+
+       E1000_READ_REG(hw, E1000_PRC64);
+       E1000_READ_REG(hw, E1000_PRC127);
+       E1000_READ_REG(hw, E1000_PRC255);
+       E1000_READ_REG(hw, E1000_PRC511);
+       E1000_READ_REG(hw, E1000_PRC1023);
+       E1000_READ_REG(hw, E1000_PRC1522);
+       E1000_READ_REG(hw, E1000_PTC64);
+       E1000_READ_REG(hw, E1000_PTC127);
+       E1000_READ_REG(hw, E1000_PTC255);
+       E1000_READ_REG(hw, E1000_PTC511);
+       E1000_READ_REG(hw, E1000_PTC1023);
+       E1000_READ_REG(hw, E1000_PTC1522);
+
+       E1000_READ_REG(hw, E1000_ALGNERRC);
+       E1000_READ_REG(hw, E1000_RXERRC);
+       E1000_READ_REG(hw, E1000_TNCRS);
+       E1000_READ_REG(hw, E1000_CEXTERR);
+       E1000_READ_REG(hw, E1000_TSCTC);
+       E1000_READ_REG(hw, E1000_TSCTFC);
+
+       E1000_READ_REG(hw, E1000_MGTPRC);
+       E1000_READ_REG(hw, E1000_MGTPDC);
+       E1000_READ_REG(hw, E1000_MGTPTC);
+}
+
+/**
+ *  e1000_read_mac_addr_82540 - Read device MAC address
+ *  @hw: pointer to the HW structure
+ *
+ *  Reads the device MAC address from the EEPROM and stores the value.
+ *  Since devices with two ports use the same EEPROM, we increment the
+ *  last bit in the MAC address for the second port.
+ *
+ *  This version is being used over generic because of customer issues
+ *  with VmWare and Virtual Box when using generic. It seems in
+ *  the emulated 82545, RAR[0] does NOT have a valid address after a
+ *  reset, this older method works and using this breaks nothing for
+ *  these legacy adapters.
+ **/
+s32 e1000_read_mac_addr_82540(struct e1000_hw *hw)
+{
+       s32  ret_val = E1000_SUCCESS;
+       u16 offset, nvm_data, i;
+
+       DEBUGFUNC("e1000_read_mac_addr");
+
+       for (i = 0; i < ETH_ADDR_LEN; i += 2) {
+               offset = i >> 1;
+               ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data);
+               if (ret_val) {
+                       DEBUGOUT("NVM Read Error\n");
+                       goto out;
+               }
+               hw->mac.perm_addr[i] = (u8)(nvm_data & 0xFF);
+               hw->mac.perm_addr[i+1] = (u8)(nvm_data >> 8);
+       }
+
+       /* Flip last bit of mac address if we're on second port */
+       if (hw->bus.func == E1000_FUNC_1)
+               hw->mac.perm_addr[5] ^= 1;
+
+       for (i = 0; i < ETH_ADDR_LEN; i++)
+               hw->mac.addr[i] = hw->mac.perm_addr[i];
+
+out:
+       return ret_val;
+}
diff --git a/lib/librte_pmd_e1000/e1000/e1000_82541.c b/lib/librte_pmd_e1000/e1000/e1000_82541.c
new file mode 100644 (file)
index 0000000..6f47096
--- /dev/null
@@ -0,0 +1,1268 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+/*
+ * 82541EI Gigabit Ethernet Controller
+ * 82541ER Gigabit Ethernet Controller
+ * 82541GI Gigabit Ethernet Controller
+ * 82541PI Gigabit Ethernet Controller
+ * 82547EI Gigabit Ethernet Controller
+ * 82547GI Gigabit Ethernet Controller
+ */
+
+#include "e1000_api.h"
+
+STATIC s32  e1000_init_phy_params_82541(struct e1000_hw *hw);
+STATIC s32  e1000_init_nvm_params_82541(struct e1000_hw *hw);
+STATIC s32  e1000_init_mac_params_82541(struct e1000_hw *hw);
+STATIC s32  e1000_reset_hw_82541(struct e1000_hw *hw);
+STATIC s32  e1000_init_hw_82541(struct e1000_hw *hw);
+STATIC s32  e1000_get_link_up_info_82541(struct e1000_hw *hw, u16 *speed,
+                                        u16 *duplex);
+STATIC s32  e1000_phy_hw_reset_82541(struct e1000_hw *hw);
+STATIC s32  e1000_setup_copper_link_82541(struct e1000_hw *hw);
+STATIC s32  e1000_check_for_link_82541(struct e1000_hw *hw);
+STATIC s32  e1000_get_cable_length_igp_82541(struct e1000_hw *hw);
+STATIC s32  e1000_set_d3_lplu_state_82541(struct e1000_hw *hw,
+                                         bool active);
+STATIC s32  e1000_setup_led_82541(struct e1000_hw *hw);
+STATIC s32  e1000_cleanup_led_82541(struct e1000_hw *hw);
+STATIC void e1000_clear_hw_cntrs_82541(struct e1000_hw *hw);
+static s32  e1000_config_dsp_after_link_change_82541(struct e1000_hw *hw,
+                                                    bool link_up);
+static s32  e1000_phy_init_script_82541(struct e1000_hw *hw);
+STATIC void e1000_power_down_phy_copper_82541(struct e1000_hw *hw);
+
+static const u16 e1000_igp_cable_length_table[] = {
+       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10,
+       10, 10, 20, 20, 20, 20, 20, 25, 25, 25, 25, 25, 25, 25, 30, 30, 30, 30,
+       40, 40, 40, 40, 40, 40, 40, 40, 40, 50, 50, 50, 50, 50, 50, 50, 60, 60,
+       60, 60, 60, 60, 60, 60, 60, 70, 70, 70, 70, 70, 70, 80, 80, 80, 80, 80,
+       80, 90, 90, 90, 90, 90, 90, 90, 90, 90, 100, 100, 100, 100, 100, 100,
+       100, 100, 100, 100, 100, 100, 100, 100, 110, 110, 110, 110, 110, 110,
+       110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 120, 120,
+       120, 120, 120, 120, 120, 120, 120, 120};
+#define IGP01E1000_AGC_LENGTH_TABLE_SIZE \
+               (sizeof(e1000_igp_cable_length_table) / \
+                sizeof(e1000_igp_cable_length_table[0]))
+
+/**
+ *  e1000_init_phy_params_82541 - Init PHY func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_phy_params_82541(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_init_phy_params_82541");
+
+       phy->addr               = 1;
+       phy->autoneg_mask       = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+       phy->reset_delay_us     = 10000;
+       phy->type               = e1000_phy_igp;
+
+       /* Function Pointers */
+       phy->ops.check_polarity = e1000_check_polarity_igp;
+       phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_igp;
+       phy->ops.get_cable_length = e1000_get_cable_length_igp_82541;
+       phy->ops.get_cfg_done   = e1000_get_cfg_done_generic;
+       phy->ops.get_info       = e1000_get_phy_info_igp;
+       phy->ops.read_reg       = e1000_read_phy_reg_igp;
+       phy->ops.reset          = e1000_phy_hw_reset_82541;
+       phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_82541;
+       phy->ops.write_reg      = e1000_write_phy_reg_igp;
+       phy->ops.power_up       = e1000_power_up_phy_copper;
+       phy->ops.power_down     = e1000_power_down_phy_copper_82541;
+
+       ret_val = e1000_get_phy_id(hw);
+       if (ret_val)
+               goto out;
+
+       /* Verify phy id */
+       if (phy->id != IGP01E1000_I_PHY_ID) {
+               ret_val = -E1000_ERR_PHY;
+               goto out;
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_init_nvm_params_82541 - Init NVM func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_nvm_params_82541(struct e1000_hw *hw)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       s32 ret_val = E1000_SUCCESS;
+       u32 eecd = E1000_READ_REG(hw, E1000_EECD);
+       u16 size;
+
+       DEBUGFUNC("e1000_init_nvm_params_82541");
+
+       switch (nvm->override) {
+       case e1000_nvm_override_spi_large:
+               nvm->type = e1000_nvm_eeprom_spi;
+               eecd |= E1000_EECD_ADDR_BITS;
+               break;
+       case e1000_nvm_override_spi_small:
+               nvm->type = e1000_nvm_eeprom_spi;
+               eecd &= ~E1000_EECD_ADDR_BITS;
+               break;
+       case e1000_nvm_override_microwire_large:
+               nvm->type = e1000_nvm_eeprom_microwire;
+               eecd |= E1000_EECD_SIZE;
+               break;
+       case e1000_nvm_override_microwire_small:
+               nvm->type = e1000_nvm_eeprom_microwire;
+               eecd &= ~E1000_EECD_SIZE;
+               break;
+       default:
+               nvm->type = eecd & E1000_EECD_TYPE ? e1000_nvm_eeprom_spi
+                           : e1000_nvm_eeprom_microwire;
+               break;
+       }
+
+       if (nvm->type == e1000_nvm_eeprom_spi) {
+               nvm->address_bits = (eecd & E1000_EECD_ADDR_BITS) ? 16 : 8;
+               nvm->delay_usec = 1;
+               nvm->opcode_bits = 8;
+               nvm->page_size = (eecd & E1000_EECD_ADDR_BITS) ? 32 : 8;
+
+               /* Function Pointers */
+               nvm->ops.acquire        = e1000_acquire_nvm_generic;
+               nvm->ops.read           = e1000_read_nvm_spi;
+               nvm->ops.release        = e1000_release_nvm_generic;
+               nvm->ops.update         = e1000_update_nvm_checksum_generic;
+               nvm->ops.valid_led_default = e1000_valid_led_default_generic;
+               nvm->ops.validate       = e1000_validate_nvm_checksum_generic;
+               nvm->ops.write          = e1000_write_nvm_spi;
+
+               /*
+                * nvm->word_size must be discovered after the pointers
+                * are set so we can verify the size from the nvm image
+                * itself.  Temporarily set it to a dummy value so the
+                * read will work.
+                */
+               nvm->word_size = 64;
+               ret_val = nvm->ops.read(hw, NVM_CFG, 1, &size);
+               if (ret_val)
+                       goto out;
+               size = (size & NVM_SIZE_MASK) >> NVM_SIZE_SHIFT;
+               /*
+                * if size != 0, it can be added to a constant and become
+                * the left-shift value to set the word_size.  Otherwise,
+                * word_size stays at 64.
+                */
+               if (size) {
+                       size += NVM_WORD_SIZE_BASE_SHIFT_82541;
+                       nvm->word_size = 1 << size;
+               }
+       } else {
+               nvm->address_bits = (eecd & E1000_EECD_ADDR_BITS) ? 8 : 6;
+               nvm->delay_usec = 50;
+               nvm->opcode_bits = 3;
+               nvm->word_size = (eecd & E1000_EECD_ADDR_BITS) ? 256 : 64;
+
+               /* Function Pointers */
+               nvm->ops.acquire        = e1000_acquire_nvm_generic;
+               nvm->ops.read           = e1000_read_nvm_microwire;
+               nvm->ops.release        = e1000_release_nvm_generic;
+               nvm->ops.update         = e1000_update_nvm_checksum_generic;
+               nvm->ops.valid_led_default = e1000_valid_led_default_generic;
+               nvm->ops.validate       = e1000_validate_nvm_checksum_generic;
+               nvm->ops.write          = e1000_write_nvm_microwire;
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_init_mac_params_82541 - Init MAC func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_mac_params_82541(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+
+       DEBUGFUNC("e1000_init_mac_params_82541");
+
+       /* Set media type */
+       hw->phy.media_type = e1000_media_type_copper;
+       /* Set mta register count */
+       mac->mta_reg_count = 128;
+       /* Set rar entry count */
+       mac->rar_entry_count = E1000_RAR_ENTRIES;
+       /* Set if part includes ASF firmware */
+       mac->asf_firmware_present = true;
+
+       /* Function Pointers */
+
+       /* bus type/speed/width */
+       mac->ops.get_bus_info = e1000_get_bus_info_pci_generic;
+       /* function id */
+       mac->ops.set_lan_id = e1000_set_lan_id_single_port;
+       /* reset */
+       mac->ops.reset_hw = e1000_reset_hw_82541;
+       /* hw initialization */
+       mac->ops.init_hw = e1000_init_hw_82541;
+       /* link setup */
+       mac->ops.setup_link = e1000_setup_link_generic;
+       /* physical interface link setup */
+       mac->ops.setup_physical_interface = e1000_setup_copper_link_82541;
+       /* check for link */
+       mac->ops.check_for_link = e1000_check_for_link_82541;
+       /* link info */
+       mac->ops.get_link_up_info = e1000_get_link_up_info_82541;
+       /* multicast address update */
+       mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
+       /* writing VFTA */
+       mac->ops.write_vfta = e1000_write_vfta_generic;
+       /* clearing VFTA */
+       mac->ops.clear_vfta = e1000_clear_vfta_generic;
+       /* ID LED init */
+       mac->ops.id_led_init = e1000_id_led_init_generic;
+       /* setup LED */
+       mac->ops.setup_led = e1000_setup_led_82541;
+       /* cleanup LED */
+       mac->ops.cleanup_led = e1000_cleanup_led_82541;
+       /* turn on/off LED */
+       mac->ops.led_on = e1000_led_on_generic;
+       mac->ops.led_off = e1000_led_off_generic;
+       /* clear hardware counters */
+       mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_82541;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_function_pointers_82541 - Init func ptrs.
+ *  @hw: pointer to the HW structure
+ *
+ *  Called to initialize all function pointers and parameters.
+ **/
+void e1000_init_function_pointers_82541(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_init_function_pointers_82541");
+
+       hw->mac.ops.init_params = e1000_init_mac_params_82541;
+       hw->nvm.ops.init_params = e1000_init_nvm_params_82541;
+       hw->phy.ops.init_params = e1000_init_phy_params_82541;
+}
+
+/**
+ *  e1000_reset_hw_82541 - Reset hardware
+ *  @hw: pointer to the HW structure
+ *
+ *  This resets the hardware into a known state.
+ **/
+STATIC s32 e1000_reset_hw_82541(struct e1000_hw *hw)
+{
+       u32 ledctl, ctrl, manc;
+
+       DEBUGFUNC("e1000_reset_hw_82541");
+
+       DEBUGOUT("Masking off all interrupts\n");
+       E1000_WRITE_REG(hw, E1000_IMC, 0xFFFFFFFF);
+
+       E1000_WRITE_REG(hw, E1000_RCTL, 0);
+       E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP);
+       E1000_WRITE_FLUSH(hw);
+
+       /*
+        * Delay to allow any outstanding PCI transactions to complete
+        * before resetting the device.
+        */
+       msec_delay(10);
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       /* Must reset the Phy before resetting the MAC */
+       if ((hw->mac.type == e1000_82541) || (hw->mac.type == e1000_82547)) {
+               E1000_WRITE_REG(hw, E1000_CTRL, (ctrl | E1000_CTRL_PHY_RST));
+               E1000_WRITE_FLUSH(hw);
+               msec_delay(5);
+       }
+
+       DEBUGOUT("Issuing a global reset to 82541/82547 MAC\n");
+       switch (hw->mac.type) {
+       case e1000_82541:
+       case e1000_82541_rev_2:
+               /*
+                * These controllers can't ack the 64-bit write when
+                * issuing the reset, so we use IO-mapping as a
+                * workaround to issue the reset.
+                */
+               E1000_WRITE_REG_IO(hw, E1000_CTRL, ctrl | E1000_CTRL_RST);
+               break;
+       default:
+               E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST);
+               break;
+       }
+
+       /* Wait for NVM reload */
+       msec_delay(20);
+
+       /* Disable HW ARPs on ASF enabled adapters */
+       manc = E1000_READ_REG(hw, E1000_MANC);
+       manc &= ~E1000_MANC_ARP_EN;
+       E1000_WRITE_REG(hw, E1000_MANC, manc);
+
+       if ((hw->mac.type == e1000_82541) || (hw->mac.type == e1000_82547)) {
+               e1000_phy_init_script_82541(hw);
+
+               /* Configure activity LED after Phy reset */
+               ledctl = E1000_READ_REG(hw, E1000_LEDCTL);
+               ledctl &= IGP_ACTIVITY_LED_MASK;
+               ledctl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
+               E1000_WRITE_REG(hw, E1000_LEDCTL, ledctl);
+       }
+
+       /* Once again, mask the interrupts */
+       DEBUGOUT("Masking off all interrupts\n");
+       E1000_WRITE_REG(hw, E1000_IMC, 0xFFFFFFFF);
+
+       /* Clear any pending interrupt events. */
+       E1000_READ_REG(hw, E1000_ICR);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_hw_82541 - Initialize hardware
+ *  @hw: pointer to the HW structure
+ *
+ *  This inits the hardware readying it for operation.
+ **/
+STATIC s32 e1000_init_hw_82541(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
+       u32 i, txdctl;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_init_hw_82541");
+
+       /* Initialize identification LED */
+       ret_val = mac->ops.id_led_init(hw);
+       if (ret_val) {
+               DEBUGOUT("Error initializing identification LED\n");
+               /* This is not fatal and we should not stop init due to this */
+       }
+
+       /* Storing the Speed Power Down  value for later use */
+       ret_val = hw->phy.ops.read_reg(hw, IGP01E1000_GMII_FIFO,
+                                      &dev_spec->spd_default);
+       if (ret_val)
+               goto out;
+
+       /* Disabling VLAN filtering */
+       DEBUGOUT("Initializing the IEEE VLAN\n");
+       mac->ops.clear_vfta(hw);
+
+       /* Setup the receive address. */
+       e1000_init_rx_addrs_generic(hw, mac->rar_entry_count);
+
+       /* Zero out the Multicast HASH table */
+       DEBUGOUT("Zeroing the MTA\n");
+       for (i = 0; i < mac->mta_reg_count; i++) {
+               E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
+               /*
+                * Avoid back to back register writes by adding the register
+                * read (flush).  This is to protect against some strange
+                * bridge configurations that may issue Memory Write Block
+                * (MWB) to our register space.
+                */
+               E1000_WRITE_FLUSH(hw);
+       }
+
+       /* Setup link and flow control */
+       ret_val = mac->ops.setup_link(hw);
+
+       txdctl = E1000_READ_REG(hw, E1000_TXDCTL(0));
+       txdctl = (txdctl & ~E1000_TXDCTL_WTHRESH) |
+                 E1000_TXDCTL_FULL_TX_DESC_WB;
+       E1000_WRITE_REG(hw, E1000_TXDCTL(0), txdctl);
+
+       /*
+        * Clear all of the statistics registers (clear on read).  It is
+        * important that we do this after we have tried to establish link
+        * because the symbol error count will increment wildly if there
+        * is no link.
+        */
+       e1000_clear_hw_cntrs_82541(hw);
+
+out:
+       return ret_val;
+}
+
+/**
+ * e1000_get_link_up_info_82541 - Report speed and duplex
+ * @hw: pointer to the HW structure
+ * @speed: pointer to speed buffer
+ * @duplex: pointer to duplex buffer
+ *
+ * Retrieve the current speed and duplex configuration.
+ **/
+STATIC s32 e1000_get_link_up_info_82541(struct e1000_hw *hw, u16 *speed,
+                                       u16 *duplex)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val;
+       u16 data;
+
+       DEBUGFUNC("e1000_get_link_up_info_82541");
+
+       ret_val = e1000_get_speed_and_duplex_copper_generic(hw, speed, duplex);
+       if (ret_val)
+               goto out;
+
+       if (!phy->speed_downgraded)
+               goto out;
+
+       /*
+        * IGP01 PHY may advertise full duplex operation after speed
+        * downgrade even if it is operating at half duplex.
+        * Here we set the duplex settings to match the duplex in the
+        * link partner's capabilities.
+        */
+       ret_val = phy->ops.read_reg(hw, PHY_AUTONEG_EXP, &data);
+       if (ret_val)
+               goto out;
+
+       if (!(data & NWAY_ER_LP_NWAY_CAPS)) {
+               *duplex = HALF_DUPLEX;
+       } else {
+               ret_val = phy->ops.read_reg(hw, PHY_LP_ABILITY, &data);
+               if (ret_val)
+                       goto out;
+
+               if (*speed == SPEED_100) {
+                       if (!(data & NWAY_LPAR_100TX_FD_CAPS))
+                               *duplex = HALF_DUPLEX;
+               } else if (*speed == SPEED_10) {
+                       if (!(data & NWAY_LPAR_10T_FD_CAPS))
+                               *duplex = HALF_DUPLEX;
+               }
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_phy_hw_reset_82541 - PHY hardware reset
+ *  @hw: pointer to the HW structure
+ *
+ *  Verify the reset block is not blocking us from resetting.  Acquire
+ *  semaphore (if necessary) and read/set/write the device control reset
+ *  bit in the PHY.  Wait the appropriate delay time for the device to
+ *  reset and release the semaphore (if necessary).
+ **/
+STATIC s32 e1000_phy_hw_reset_82541(struct e1000_hw *hw)
+{
+       s32 ret_val;
+       u32 ledctl;
+
+       DEBUGFUNC("e1000_phy_hw_reset_82541");
+
+       ret_val = e1000_phy_hw_reset_generic(hw);
+       if (ret_val)
+               goto out;
+
+       e1000_phy_init_script_82541(hw);
+
+       if ((hw->mac.type == e1000_82541) || (hw->mac.type == e1000_82547)) {
+               /* Configure activity LED after PHY reset */
+               ledctl = E1000_READ_REG(hw, E1000_LEDCTL);
+               ledctl &= IGP_ACTIVITY_LED_MASK;
+               ledctl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
+               E1000_WRITE_REG(hw, E1000_LEDCTL, ledctl);
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_setup_copper_link_82541 - Configure copper link settings
+ *  @hw: pointer to the HW structure
+ *
+ *  Calls the appropriate function to configure the link for auto-neg or forced
+ *  speed and duplex.  Then we check for link, once link is established calls
+ *  to configure collision distance and flow control are called.  If link is
+ *  not established, we return -E1000_ERR_PHY (-2).
+ **/
+STATIC s32 e1000_setup_copper_link_82541(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
+       s32  ret_val;
+       u32 ctrl, ledctl;
+
+       DEBUGFUNC("e1000_setup_copper_link_82541");
+
+       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);
+
+
+       /* Earlier revs of the IGP phy require us to force MDI. */
+       if (hw->mac.type == e1000_82541 || hw->mac.type == e1000_82547) {
+               dev_spec->dsp_config = e1000_dsp_config_disabled;
+               phy->mdix = 1;
+       } else {
+               dev_spec->dsp_config = e1000_dsp_config_enabled;
+       }
+
+       ret_val = e1000_copper_link_setup_igp(hw);
+       if (ret_val)
+               goto out;
+
+       if (hw->mac.autoneg) {
+               if (dev_spec->ffe_config == e1000_ffe_config_active)
+                       dev_spec->ffe_config = e1000_ffe_config_enabled;
+       }
+
+       /* Configure activity LED after Phy reset */
+       ledctl = E1000_READ_REG(hw, E1000_LEDCTL);
+       ledctl &= IGP_ACTIVITY_LED_MASK;
+       ledctl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
+       E1000_WRITE_REG(hw, E1000_LEDCTL, ledctl);
+
+       ret_val = e1000_setup_copper_link_generic(hw);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_check_for_link_82541 - Check/Store link connection
+ *  @hw: pointer to the HW structure
+ *
+ *  This checks the link condition of the adapter and stores the
+ *  results in the hw->mac structure.
+ **/
+STATIC s32 e1000_check_for_link_82541(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       s32 ret_val;
+       bool link;
+
+       DEBUGFUNC("e1000_check_for_link_82541");
+
+       /*
+        * We only want to go out to the PHY registers to see if Auto-Neg
+        * has completed and/or if our link status has changed.  The
+        * get_link_status flag is set upon receiving a Link Status
+        * Change or Rx Sequence Error interrupt.
+        */
+       if (!mac->get_link_status) {
+               ret_val = E1000_SUCCESS;
+               goto out;
+       }
+
+       /*
+        * 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)
+               goto out;
+
+       if (!link) {
+               ret_val = e1000_config_dsp_after_link_change_82541(hw, false);
+               goto out; /* No link detected */
+       }
+
+       mac->get_link_status = false;
+
+       /*
+        * Check if there was DownShift, must be checked
+        * immediately after link-up
+        */
+       e1000_check_downshift_generic(hw);
+
+       /*
+        * If we are forcing speed/duplex, then we simply return since
+        * we have already determined whether we have link or not.
+        */
+       if (!mac->autoneg) {
+               ret_val = -E1000_ERR_CONFIG;
+               goto out;
+       }
+
+       ret_val = e1000_config_dsp_after_link_change_82541(hw, true);
+
+       /*
+        * Auto-Neg is enabled.  Auto Speed Detection takes care
+        * of MAC speed/duplex configuration.  So we only need to
+        * configure Collision Distance in the MAC.
+        */
+       mac->ops.config_collision_dist(hw);
+
+       /*
+        * Configure Flow Control now that Auto-Neg has completed.
+        * First, we need to restore the desired flow control
+        * settings because we may have had to re-autoneg with a
+        * different link partner.
+        */
+       ret_val = e1000_config_fc_after_link_up_generic(hw);
+       if (ret_val)
+               DEBUGOUT("Error configuring flow control\n");
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_config_dsp_after_link_change_82541 - Config DSP after link
+ *  @hw: pointer to the HW structure
+ *  @link_up: boolean flag for link up status
+ *
+ *  Return E1000_ERR_PHY when failing to read/write the PHY, else E1000_SUCCESS
+ *  at any other case.
+ *
+ *  82541_rev_2 & 82547_rev_2 have the capability to configure the DSP when a
+ *  gigabit link is achieved to improve link quality.
+ **/
+static s32 e1000_config_dsp_after_link_change_82541(struct e1000_hw *hw,
+                                                   bool link_up)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
+       s32 ret_val;
+       u32 idle_errs = 0;
+       u16 phy_data, phy_saved_data, speed, duplex, i;
+       u16 ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_20;
+       u16 dsp_reg_array[IGP01E1000_PHY_CHANNEL_NUM] = {
+                                               IGP01E1000_PHY_AGC_PARAM_A,
+                                               IGP01E1000_PHY_AGC_PARAM_B,
+                                               IGP01E1000_PHY_AGC_PARAM_C,
+                                               IGP01E1000_PHY_AGC_PARAM_D};
+
+       DEBUGFUNC("e1000_config_dsp_after_link_change_82541");
+
+       if (link_up) {
+               ret_val = hw->mac.ops.get_link_up_info(hw, &speed, &duplex);
+               if (ret_val) {
+                       DEBUGOUT("Error getting link speed and duplex\n");
+                       goto out;
+               }
+
+               if (speed != SPEED_1000) {
+                       ret_val = E1000_SUCCESS;
+                       goto out;
+               }
+
+               ret_val = phy->ops.get_cable_length(hw);
+               if (ret_val)
+                       goto out;
+
+               if ((dev_spec->dsp_config == e1000_dsp_config_enabled) &&
+                   phy->min_cable_length >= 50) {
+
+                       for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
+                               ret_val = phy->ops.read_reg(hw,
+                                                           dsp_reg_array[i],
+                                                           &phy_data);
+                               if (ret_val)
+                                       goto out;
+
+                               phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX;
+
+                               ret_val = phy->ops.write_reg(hw,
+                                                            dsp_reg_array[i],
+                                                            phy_data);
+                               if (ret_val)
+                                       goto out;
+                       }
+                       dev_spec->dsp_config = e1000_dsp_config_activated;
+               }
+
+               if ((dev_spec->ffe_config != e1000_ffe_config_enabled) ||
+                   (phy->min_cable_length >= 50)) {
+                       ret_val = E1000_SUCCESS;
+                       goto out;
+               }
+
+               /* clear previous idle error counts */
+               ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &phy_data);
+               if (ret_val)
+                       goto out;
+
+               for (i = 0; i < ffe_idle_err_timeout; i++) {
+                       usec_delay(1000);
+                       ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS,
+                                                   &phy_data);
+                       if (ret_val)
+                               goto out;
+
+                       idle_errs += (phy_data & SR_1000T_IDLE_ERROR_CNT);
+                       if (idle_errs > SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT) {
+                               dev_spec->ffe_config = e1000_ffe_config_active;
+
+                               ret_val = phy->ops.write_reg(hw,
+                                                 IGP01E1000_PHY_DSP_FFE,
+                                                 IGP01E1000_PHY_DSP_FFE_CM_CP);
+                               if (ret_val)
+                                       goto out;
+                               break;
+                       }
+
+                       if (idle_errs)
+                               ffe_idle_err_timeout =
+                                                FFE_IDLE_ERR_COUNT_TIMEOUT_100;
+               }
+       } else {
+               if (dev_spec->dsp_config == e1000_dsp_config_activated) {
+                       /*
+                        * Save off the current value of register 0x2F5B
+                        * to be restored at the end of the routines.
+                        */
+                       ret_val = phy->ops.read_reg(hw, 0x2F5B,
+                                                   &phy_saved_data);
+                       if (ret_val)
+                               goto out;
+
+                       /* Disable the PHY transmitter */
+                       ret_val = phy->ops.write_reg(hw, 0x2F5B, 0x0003);
+                       if (ret_val)
+                               goto out;
+
+                       msec_delay_irq(20);
+
+                       ret_val = phy->ops.write_reg(hw, 0x0000,
+                                                    IGP01E1000_IEEE_FORCE_GIG);
+                       if (ret_val)
+                               goto out;
+                       for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
+                               ret_val = phy->ops.read_reg(hw,
+                                                           dsp_reg_array[i],
+                                                           &phy_data);
+                               if (ret_val)
+                                       goto out;
+
+                               phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX;
+                               phy_data |= IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS;
+
+                               ret_val = phy->ops.write_reg(hw,
+                                                            dsp_reg_array[i],
+                                                            phy_data);
+                               if (ret_val)
+                                       goto out;
+                       }
+
+                       ret_val = phy->ops.write_reg(hw, 0x0000,
+                                              IGP01E1000_IEEE_RESTART_AUTONEG);
+                       if (ret_val)
+                               goto out;
+
+                       msec_delay_irq(20);
+
+                       /* Now enable the transmitter */
+                       ret_val = phy->ops.write_reg(hw, 0x2F5B,
+                                                    phy_saved_data);
+                       if (ret_val)
+                               goto out;
+
+                       dev_spec->dsp_config = e1000_dsp_config_enabled;
+               }
+
+               if (dev_spec->ffe_config != e1000_ffe_config_active) {
+                       ret_val = E1000_SUCCESS;
+                       goto out;
+               }
+
+               /*
+                * Save off the current value of register 0x2F5B
+                * to be restored at the end of the routines.
+                */
+               ret_val = phy->ops.read_reg(hw, 0x2F5B, &phy_saved_data);
+               if (ret_val)
+                       goto out;
+
+               /* Disable the PHY transmitter */
+               ret_val = phy->ops.write_reg(hw, 0x2F5B, 0x0003);
+               if (ret_val)
+                       goto out;
+
+               msec_delay_irq(20);
+
+               ret_val = phy->ops.write_reg(hw, 0x0000,
+                                            IGP01E1000_IEEE_FORCE_GIG);
+               if (ret_val)
+                       goto out;
+
+               ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_DSP_FFE,
+                                            IGP01E1000_PHY_DSP_FFE_DEFAULT);
+               if (ret_val)
+                       goto out;
+
+               ret_val = phy->ops.write_reg(hw, 0x0000,
+                                            IGP01E1000_IEEE_RESTART_AUTONEG);
+               if (ret_val)
+                       goto out;
+
+               msec_delay_irq(20);
+
+               /* Now enable the transmitter */
+               ret_val = phy->ops.write_reg(hw, 0x2F5B, phy_saved_data);
+
+               if (ret_val)
+                       goto out;
+
+               dev_spec->ffe_config = e1000_ffe_config_enabled;
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_get_cable_length_igp_82541 - Determine cable length for igp PHY
+ *  @hw: pointer to the HW structure
+ *
+ *  The automatic gain control (agc) normalizes the amplitude of the
+ *  received signal, adjusting for the attenuation produced by the
+ *  cable.  By reading the AGC registers, which represent the
+ *  combination of coarse and fine gain value, the value can be put
+ *  into a lookup table to obtain the approximate cable length
+ *  for each channel.
+ **/
+STATIC s32 e1000_get_cable_length_igp_82541(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val = E1000_SUCCESS;
+       u16 i, data;
+       u16 cur_agc_value, agc_value = 0;
+       u16 min_agc_value = IGP01E1000_AGC_LENGTH_TABLE_SIZE;
+       u16 agc_reg_array[IGP01E1000_PHY_CHANNEL_NUM] = {IGP01E1000_PHY_AGC_A,
+                                                        IGP01E1000_PHY_AGC_B,
+                                                        IGP01E1000_PHY_AGC_C,
+                                                        IGP01E1000_PHY_AGC_D};
+
+       DEBUGFUNC("e1000_get_cable_length_igp_82541");
+
+       /* Read the AGC registers for all channels */
+       for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
+               ret_val = phy->ops.read_reg(hw, agc_reg_array[i], &data);
+               if (ret_val)
+                       goto out;
+
+               cur_agc_value = data >> IGP01E1000_AGC_LENGTH_SHIFT;
+
+               /* Bounds checking */
+               if ((cur_agc_value >= IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) ||
+                   (cur_agc_value == 0)) {
+                       ret_val = -E1000_ERR_PHY;
+                       goto out;
+               }
+
+               agc_value += cur_agc_value;
+
+               if (min_agc_value > cur_agc_value)
+                       min_agc_value = cur_agc_value;
+       }
+
+       /* Remove the minimal AGC result for length < 50m */
+       if (agc_value < IGP01E1000_PHY_CHANNEL_NUM * 50) {
+               agc_value -= min_agc_value;
+               /* Average the three remaining channels for the length. */
+               agc_value /= (IGP01E1000_PHY_CHANNEL_NUM - 1);
+       } else {
+               /* Average the channels for the length. */
+               agc_value /= IGP01E1000_PHY_CHANNEL_NUM;
+       }
+
+       phy->min_cable_length = (e1000_igp_cable_length_table[agc_value] >
+                                IGP01E1000_AGC_RANGE)
+                               ? (e1000_igp_cable_length_table[agc_value] -
+                                  IGP01E1000_AGC_RANGE)
+                               : 0;
+       phy->max_cable_length = e1000_igp_cable_length_table[agc_value] +
+                               IGP01E1000_AGC_RANGE;
+
+       phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_set_d3_lplu_state_82541 - Sets low power link up state for D3
+ *  @hw: pointer to the HW structure
+ *  @active: boolean used to enable/disable lplu
+ *
+ *  Success returns 0, Failure returns 1
+ *
+ *  The low power link up (lplu) state is set to the power management level D3
+ *  and SmartSpeed is disabled when active is true, else clear lplu for D3
+ *  and enable Smartspeed.  LPLU and Smartspeed are mutually exclusive.  LPLU
+ *  is used during Dx states where the power conservation is most important.
+ *  During driver activity, SmartSpeed should be enabled so performance is
+ *  maintained.
+ **/
+STATIC s32 e1000_set_d3_lplu_state_82541(struct e1000_hw *hw, bool active)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val;
+       u16 data;
+
+       DEBUGFUNC("e1000_set_d3_lplu_state_82541");
+
+       switch (hw->mac.type) {
+       case e1000_82541_rev_2:
+       case e1000_82547_rev_2:
+               break;
+       default:
+               ret_val = e1000_set_d3_lplu_state_generic(hw, active);
+               goto out;
+               break;
+       }
+
+       ret_val = phy->ops.read_reg(hw, IGP01E1000_GMII_FIFO, &data);
+       if (ret_val)
+               goto out;
+
+       if (!active) {
+               data &= ~IGP01E1000_GMII_FLEX_SPD;
+               ret_val = phy->ops.write_reg(hw, IGP01E1000_GMII_FIFO, data);
+               if (ret_val)
+                       goto out;
+
+               /*
+                * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
+                * during Dx states where the power conservation is most
+                * important.  During driver activity we should enable
+                * SmartSpeed, so performance is maintained.
+                */
+               if (phy->smart_speed == e1000_smart_speed_on) {
+                       ret_val = phy->ops.read_reg(hw,
+                                                   IGP01E1000_PHY_PORT_CONFIG,
+                                                   &data);
+                       if (ret_val)
+                               goto out;
+
+                       data |= IGP01E1000_PSCFR_SMART_SPEED;
+                       ret_val = phy->ops.write_reg(hw,
+                                                    IGP01E1000_PHY_PORT_CONFIG,
+                                                    data);
+                       if (ret_val)
+                               goto out;
+               } else if (phy->smart_speed == e1000_smart_speed_off) {
+                       ret_val = phy->ops.read_reg(hw,
+                                                   IGP01E1000_PHY_PORT_CONFIG,
+                                                   &data);
+                       if (ret_val)
+                               goto out;
+
+                       data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+                       ret_val = phy->ops.write_reg(hw,
+                                                    IGP01E1000_PHY_PORT_CONFIG,
+                                                    data);
+                       if (ret_val)
+                               goto out;
+               }
+       } else if ((phy->autoneg_advertised == E1000_ALL_SPEED_DUPLEX) ||
+                  (phy->autoneg_advertised == E1000_ALL_NOT_GIG) ||
+                  (phy->autoneg_advertised == E1000_ALL_10_SPEED)) {
+               data |= IGP01E1000_GMII_FLEX_SPD;
+               ret_val = phy->ops.write_reg(hw, IGP01E1000_GMII_FIFO, data);
+               if (ret_val)
+                       goto out;
+
+               /* When LPLU is enabled, we should disable SmartSpeed */
+               ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+                                           &data);
+               if (ret_val)
+                       goto out;
+
+               data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+               ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+                                            data);
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_setup_led_82541 - Configures SW controllable LED
+ *  @hw: pointer to the HW structure
+ *
+ *  This prepares the SW controllable LED for use and saves the current state
+ *  of the LED so it can be later restored.
+ **/
+STATIC s32 e1000_setup_led_82541(struct e1000_hw *hw)
+{
+       struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_setup_led_82541");
+
+       ret_val = hw->phy.ops.read_reg(hw, IGP01E1000_GMII_FIFO,
+                                      &dev_spec->spd_default);
+       if (ret_val)
+               goto out;
+
+       ret_val = hw->phy.ops.write_reg(hw, IGP01E1000_GMII_FIFO,
+                                       (u16)(dev_spec->spd_default &
+                                       ~IGP01E1000_GMII_SPD));
+       if (ret_val)
+               goto out;
+
+       E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_cleanup_led_82541 - Set LED config to default operation
+ *  @hw: pointer to the HW structure
+ *
+ *  Remove the current LED configuration and set the LED configuration
+ *  to the default value, saved from the EEPROM.
+ **/
+STATIC s32 e1000_cleanup_led_82541(struct e1000_hw *hw)
+{
+       struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_cleanup_led_82541");
+
+       ret_val = hw->phy.ops.write_reg(hw, IGP01E1000_GMII_FIFO,
+                                       dev_spec->spd_default);
+       if (ret_val)
+               goto out;
+
+       E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_default);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_phy_init_script_82541 - Initialize GbE PHY
+ *  @hw: pointer to the HW structure
+ *
+ *  Initializes the IGP PHY.
+ **/
+static s32 e1000_phy_init_script_82541(struct e1000_hw *hw)
+{
+       struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
+       u32 ret_val;
+       u16 phy_saved_data;
+
+       DEBUGFUNC("e1000_phy_init_script_82541");
+
+       if (!dev_spec->phy_init_script) {
+               ret_val = E1000_SUCCESS;
+               goto out;
+       }
+
+       /* Delay after phy reset to enable NVM configuration to load */
+       msec_delay(20);
+
+       /*
+        * Save off the current value of register 0x2F5B to be restored at
+        * the end of this routine.
+        */
+       ret_val = hw->phy.ops.read_reg(hw, 0x2F5B, &phy_saved_data);
+
+       /* Disabled the PHY transmitter */
+       hw->phy.ops.write_reg(hw, 0x2F5B, 0x0003);
+
+       msec_delay(20);
+
+       hw->phy.ops.write_reg(hw, 0x0000, 0x0140);
+
+       msec_delay(5);
+
+       switch (hw->mac.type) {
+       case e1000_82541:
+       case e1000_82547:
+               hw->phy.ops.write_reg(hw, 0x1F95, 0x0001);
+
+               hw->phy.ops.write_reg(hw, 0x1F71, 0xBD21);
+
+               hw->phy.ops.write_reg(hw, 0x1F79, 0x0018);
+
+               hw->phy.ops.write_reg(hw, 0x1F30, 0x1600);
+
+               hw->phy.ops.write_reg(hw, 0x1F31, 0x0014);
+
+               hw->phy.ops.write_reg(hw, 0x1F32, 0x161C);
+
+               hw->phy.ops.write_reg(hw, 0x1F94, 0x0003);
+
+               hw->phy.ops.write_reg(hw, 0x1F96, 0x003F);
+
+               hw->phy.ops.write_reg(hw, 0x2010, 0x0008);
+               break;
+       case e1000_82541_rev_2:
+       case e1000_82547_rev_2:
+               hw->phy.ops.write_reg(hw, 0x1F73, 0x0099);
+               break;
+       default:
+               break;
+       }
+
+       hw->phy.ops.write_reg(hw, 0x0000, 0x3300);
+
+       msec_delay(20);
+
+       /* Now enable the transmitter */
+       hw->phy.ops.write_reg(hw, 0x2F5B, phy_saved_data);
+
+       if (hw->mac.type == e1000_82547) {
+               u16 fused, fine, coarse;
+
+               /* Move to analog registers page */
+               hw->phy.ops.read_reg(hw, IGP01E1000_ANALOG_SPARE_FUSE_STATUS,
+                                    &fused);
+
+               if (!(fused & IGP01E1000_ANALOG_SPARE_FUSE_ENABLED)) {
+                       hw->phy.ops.read_reg(hw, IGP01E1000_ANALOG_FUSE_STATUS,
+                                            &fused);
+
+                       fine = fused & IGP01E1000_ANALOG_FUSE_FINE_MASK;
+                       coarse = fused & IGP01E1000_ANALOG_FUSE_COARSE_MASK;
+
+                       if (coarse > IGP01E1000_ANALOG_FUSE_COARSE_THRESH) {
+                               coarse -= IGP01E1000_ANALOG_FUSE_COARSE_10;
+                               fine -= IGP01E1000_ANALOG_FUSE_FINE_1;
+                       } else if (coarse ==
+                                  IGP01E1000_ANALOG_FUSE_COARSE_THRESH)
+                               fine -= IGP01E1000_ANALOG_FUSE_FINE_10;
+
+                       fused = (fused & IGP01E1000_ANALOG_FUSE_POLY_MASK) |
+                               (fine & IGP01E1000_ANALOG_FUSE_FINE_MASK) |
+                               (coarse & IGP01E1000_ANALOG_FUSE_COARSE_MASK);
+
+                       hw->phy.ops.write_reg(hw,
+                                             IGP01E1000_ANALOG_FUSE_CONTROL,
+                                             fused);
+                       hw->phy.ops.write_reg(hw,
+                                     IGP01E1000_ANALOG_FUSE_BYPASS,
+                                     IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL);
+               }
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_init_script_state_82541 - Enable/Disable PHY init script
+ *  @hw: pointer to the HW structure
+ *  @state: boolean value used to enable/disable PHY init script
+ *
+ *  Allows the driver to enable/disable the PHY init script, if the PHY is an
+ *  IGP PHY.
+ **/
+void e1000_init_script_state_82541(struct e1000_hw *hw, bool state)
+{
+       struct e1000_dev_spec_82541 *dev_spec = &hw->dev_spec._82541;
+
+       DEBUGFUNC("e1000_init_script_state_82541");
+
+       if (hw->phy.type != e1000_phy_igp) {
+               DEBUGOUT("Initialization script not necessary.\n");
+               goto out;
+       }
+
+       dev_spec->phy_init_script = state;
+
+out:
+       return;
+}
+
+/**
+ * e1000_power_down_phy_copper_82541 - Remove link in case of PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, remove the link.
+ **/
+STATIC void e1000_power_down_phy_copper_82541(struct e1000_hw *hw)
+{
+       /* If the management interface is not enabled, then power down */
+       if (!(E1000_READ_REG(hw, E1000_MANC) & E1000_MANC_SMBUS_EN))
+               e1000_power_down_phy_copper(hw);
+
+       return;
+}
+
+/**
+ *  e1000_clear_hw_cntrs_82541 - Clear device specific hardware counters
+ *  @hw: pointer to the HW structure
+ *
+ *  Clears the hardware counters by reading the counter registers.
+ **/
+STATIC void e1000_clear_hw_cntrs_82541(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_clear_hw_cntrs_82541");
+
+       e1000_clear_hw_cntrs_base_generic(hw);
+
+       E1000_READ_REG(hw, E1000_PRC64);
+       E1000_READ_REG(hw, E1000_PRC127);
+       E1000_READ_REG(hw, E1000_PRC255);
+       E1000_READ_REG(hw, E1000_PRC511);
+       E1000_READ_REG(hw, E1000_PRC1023);
+       E1000_READ_REG(hw, E1000_PRC1522);
+       E1000_READ_REG(hw, E1000_PTC64);
+       E1000_READ_REG(hw, E1000_PTC127);
+       E1000_READ_REG(hw, E1000_PTC255);
+       E1000_READ_REG(hw, E1000_PTC511);
+       E1000_READ_REG(hw, E1000_PTC1023);
+       E1000_READ_REG(hw, E1000_PTC1522);
+
+       E1000_READ_REG(hw, E1000_ALGNERRC);
+       E1000_READ_REG(hw, E1000_RXERRC);
+       E1000_READ_REG(hw, E1000_TNCRS);
+       E1000_READ_REG(hw, E1000_CEXTERR);
+       E1000_READ_REG(hw, E1000_TSCTC);
+       E1000_READ_REG(hw, E1000_TSCTFC);
+
+       E1000_READ_REG(hw, E1000_MGTPRC);
+       E1000_READ_REG(hw, E1000_MGTPDC);
+       E1000_READ_REG(hw, E1000_MGTPTC);
+}
diff --git a/lib/librte_pmd_e1000/e1000/e1000_82541.h b/lib/librte_pmd_e1000/e1000/e1000_82541.h
new file mode 100644 (file)
index 0000000..c8b495b
--- /dev/null
@@ -0,0 +1,91 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#ifndef _E1000_82541_H_
+#define _E1000_82541_H_
+
+#define NVM_WORD_SIZE_BASE_SHIFT_82541 (NVM_WORD_SIZE_BASE_SHIFT + 1)
+
+#define IGP01E1000_PHY_CHANNEL_NUM             4
+
+#define IGP01E1000_PHY_AGC_A                   0x1172
+#define IGP01E1000_PHY_AGC_B                   0x1272
+#define IGP01E1000_PHY_AGC_C                   0x1472
+#define IGP01E1000_PHY_AGC_D                   0x1872
+
+#define IGP01E1000_PHY_AGC_PARAM_A             0x1171
+#define IGP01E1000_PHY_AGC_PARAM_B             0x1271
+#define IGP01E1000_PHY_AGC_PARAM_C             0x1471
+#define IGP01E1000_PHY_AGC_PARAM_D             0x1871
+
+#define IGP01E1000_PHY_EDAC_MU_INDEX           0xC000
+#define IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS    0x8000
+
+#define IGP01E1000_PHY_DSP_RESET               0x1F33
+
+#define IGP01E1000_PHY_DSP_FFE                 0x1F35
+#define IGP01E1000_PHY_DSP_FFE_CM_CP           0x0069
+#define IGP01E1000_PHY_DSP_FFE_DEFAULT         0x002A
+
+#define IGP01E1000_IEEE_FORCE_GIG              0x0140
+#define IGP01E1000_IEEE_RESTART_AUTONEG                0x3300
+
+#define IGP01E1000_AGC_LENGTH_SHIFT            7
+#define IGP01E1000_AGC_RANGE                   10
+
+#define FFE_IDLE_ERR_COUNT_TIMEOUT_20          20
+#define FFE_IDLE_ERR_COUNT_TIMEOUT_100         100
+
+#define IGP01E1000_ANALOG_FUSE_STATUS          0x20D0
+#define IGP01E1000_ANALOG_SPARE_FUSE_STATUS    0x20D1
+#define IGP01E1000_ANALOG_FUSE_CONTROL         0x20DC
+#define IGP01E1000_ANALOG_FUSE_BYPASS          0x20DE
+
+#define IGP01E1000_ANALOG_SPARE_FUSE_ENABLED   0x0100
+#define IGP01E1000_ANALOG_FUSE_FINE_MASK       0x0F80
+#define IGP01E1000_ANALOG_FUSE_COARSE_MASK     0x0070
+#define IGP01E1000_ANALOG_FUSE_COARSE_THRESH   0x0040
+#define IGP01E1000_ANALOG_FUSE_COARSE_10       0x0010
+#define IGP01E1000_ANALOG_FUSE_FINE_1          0x0080
+#define IGP01E1000_ANALOG_FUSE_FINE_10         0x0500
+#define IGP01E1000_ANALOG_FUSE_POLY_MASK       0xF000
+#define IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL 0x0002
+
+#define IGP01E1000_MSE_CHANNEL_D               0x000F
+#define IGP01E1000_MSE_CHANNEL_C               0x00F0
+#define IGP01E1000_MSE_CHANNEL_B               0x0F00
+#define IGP01E1000_MSE_CHANNEL_A               0xF000
+
+
+void e1000_init_script_state_82541(struct e1000_hw *hw, bool state);
+#endif
diff --git a/lib/librte_pmd_e1000/e1000/e1000_82542.c b/lib/librte_pmd_e1000/e1000/e1000_82542.c
new file mode 100644 (file)
index 0000000..0d5b536
--- /dev/null
@@ -0,0 +1,588 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+/*
+ * 82542 Gigabit Ethernet Controller
+ */
+
+#include "e1000_api.h"
+
+STATIC s32  e1000_init_phy_params_82542(struct e1000_hw *hw);
+STATIC s32  e1000_init_nvm_params_82542(struct e1000_hw *hw);
+STATIC s32  e1000_init_mac_params_82542(struct e1000_hw *hw);
+STATIC s32  e1000_get_bus_info_82542(struct e1000_hw *hw);
+STATIC s32  e1000_reset_hw_82542(struct e1000_hw *hw);
+STATIC s32  e1000_init_hw_82542(struct e1000_hw *hw);
+STATIC s32  e1000_setup_link_82542(struct e1000_hw *hw);
+STATIC s32  e1000_led_on_82542(struct e1000_hw *hw);
+STATIC s32  e1000_led_off_82542(struct e1000_hw *hw);
+STATIC void e1000_rar_set_82542(struct e1000_hw *hw, u8 *addr, u32 index);
+STATIC void e1000_clear_hw_cntrs_82542(struct e1000_hw *hw);
+STATIC s32  e1000_read_mac_addr_82542(struct e1000_hw *hw);
+
+/**
+ *  e1000_init_phy_params_82542 - Init PHY func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_phy_params_82542(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_init_phy_params_82542");
+
+       phy->type = e1000_phy_none;
+
+       return ret_val;
+}
+
+/**
+ *  e1000_init_nvm_params_82542 - Init NVM func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_nvm_params_82542(struct e1000_hw *hw)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+
+       DEBUGFUNC("e1000_init_nvm_params_82542");
+
+       nvm->address_bits       =  6;
+       nvm->delay_usec         = 50;
+       nvm->opcode_bits        =  3;
+       nvm->type               = e1000_nvm_eeprom_microwire;
+       nvm->word_size          = 64;
+
+       /* Function Pointers */
+       nvm->ops.read           = e1000_read_nvm_microwire;
+       nvm->ops.release        = e1000_stop_nvm;
+       nvm->ops.write          = e1000_write_nvm_microwire;
+       nvm->ops.update         = e1000_update_nvm_checksum_generic;
+       nvm->ops.validate       = e1000_validate_nvm_checksum_generic;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_mac_params_82542 - Init MAC func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_mac_params_82542(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+
+       DEBUGFUNC("e1000_init_mac_params_82542");
+
+       /* Set media type */
+       hw->phy.media_type = e1000_media_type_fiber;
+
+       /* Set mta register count */
+       mac->mta_reg_count = 128;
+       /* Set rar entry count */
+       mac->rar_entry_count = E1000_RAR_ENTRIES;
+
+       /* Function pointers */
+
+       /* bus type/speed/width */
+       mac->ops.get_bus_info = e1000_get_bus_info_82542;
+       /* function id */
+       mac->ops.set_lan_id = e1000_set_lan_id_multi_port_pci;
+       /* reset */
+       mac->ops.reset_hw = e1000_reset_hw_82542;
+       /* hw initialization */
+       mac->ops.init_hw = e1000_init_hw_82542;
+       /* link setup */
+       mac->ops.setup_link = e1000_setup_link_82542;
+       /* phy/fiber/serdes setup */
+       mac->ops.setup_physical_interface =
+                                       e1000_setup_fiber_serdes_link_generic;
+       /* check for link */
+       mac->ops.check_for_link = e1000_check_for_fiber_link_generic;
+       /* multicast address update */
+       mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
+       /* writing VFTA */
+       mac->ops.write_vfta = e1000_write_vfta_generic;
+       /* clearing VFTA */
+       mac->ops.clear_vfta = e1000_clear_vfta_generic;
+       /* read mac address */
+       mac->ops.read_mac_addr = e1000_read_mac_addr_82542;
+       /* set RAR */
+       mac->ops.rar_set = e1000_rar_set_82542;
+       /* turn on/off LED */
+       mac->ops.led_on = e1000_led_on_82542;
+       mac->ops.led_off = e1000_led_off_82542;
+       /* clear hardware counters */
+       mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_82542;
+       /* link info */
+       mac->ops.get_link_up_info =
+                               e1000_get_speed_and_duplex_fiber_serdes_generic;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_function_pointers_82542 - Init func ptrs.
+ *  @hw: pointer to the HW structure
+ *
+ *  Called to initialize all function pointers and parameters.
+ **/
+void e1000_init_function_pointers_82542(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_init_function_pointers_82542");
+
+       hw->mac.ops.init_params = e1000_init_mac_params_82542;
+       hw->nvm.ops.init_params = e1000_init_nvm_params_82542;
+       hw->phy.ops.init_params = e1000_init_phy_params_82542;
+}
+
+/**
+ *  e1000_get_bus_info_82542 - Obtain bus information for adapter
+ *  @hw: pointer to the HW structure
+ *
+ *  This will obtain information about the HW bus for which the
+ *  adapter is attached and stores it in the hw structure.
+ **/
+STATIC s32 e1000_get_bus_info_82542(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_get_bus_info_82542");
+
+       hw->bus.type = e1000_bus_type_pci;
+       hw->bus.speed = e1000_bus_speed_unknown;
+       hw->bus.width = e1000_bus_width_unknown;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_reset_hw_82542 - Reset hardware
+ *  @hw: pointer to the HW structure
+ *
+ *  This resets the hardware into a known state.
+ **/
+STATIC s32 e1000_reset_hw_82542(struct e1000_hw *hw)
+{
+       struct e1000_bus_info *bus = &hw->bus;
+       s32 ret_val = E1000_SUCCESS;
+       u32 ctrl;
+
+       DEBUGFUNC("e1000_reset_hw_82542");
+
+       if (hw->revision_id == E1000_REVISION_2) {
+               DEBUGOUT("Disabling MWI on 82542 rev 2\n");
+               e1000_pci_clear_mwi(hw);
+       }
+
+       DEBUGOUT("Masking off all interrupts\n");
+       E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+
+       E1000_WRITE_REG(hw, E1000_RCTL, 0);
+       E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP);
+       E1000_WRITE_FLUSH(hw);
+
+       /*
+        * Delay to allow any outstanding PCI transactions to complete before
+        * resetting the device
+        */
+       msec_delay(10);
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       DEBUGOUT("Issuing a global reset to 82542/82543 MAC\n");
+       E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST);
+
+       hw->nvm.ops.reload(hw);
+       msec_delay(2);
+
+       E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+       E1000_READ_REG(hw, E1000_ICR);
+
+       if (hw->revision_id == E1000_REVISION_2) {
+               if (bus->pci_cmd_word & CMD_MEM_WRT_INVALIDATE)
+                       e1000_pci_set_mwi(hw);
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_init_hw_82542 - Initialize hardware
+ *  @hw: pointer to the HW structure
+ *
+ *  This inits the hardware readying it for operation.
+ **/
+STATIC s32 e1000_init_hw_82542(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       struct e1000_dev_spec_82542 *dev_spec = &hw->dev_spec._82542;
+       s32 ret_val = E1000_SUCCESS;
+       u32 ctrl;
+       u16 i;
+
+       DEBUGFUNC("e1000_init_hw_82542");
+
+       /* Disabling VLAN filtering */
+       E1000_WRITE_REG(hw, E1000_VET, 0);
+       mac->ops.clear_vfta(hw);
+
+       /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */
+       if (hw->revision_id == E1000_REVISION_2) {
+               DEBUGOUT("Disabling MWI on 82542 rev 2.0\n");
+               e1000_pci_clear_mwi(hw);
+               E1000_WRITE_REG(hw, E1000_RCTL, E1000_RCTL_RST);
+               E1000_WRITE_FLUSH(hw);
+               msec_delay(5);
+       }
+
+       /* Setup the receive address. */
+       e1000_init_rx_addrs_generic(hw, mac->rar_entry_count);
+
+       /* For 82542 (rev 2.0), take the receiver out of reset and enable MWI */
+       if (hw->revision_id == E1000_REVISION_2) {
+               E1000_WRITE_REG(hw, E1000_RCTL, 0);
+               E1000_WRITE_FLUSH(hw);
+               msec_delay(1);
+               if (hw->bus.pci_cmd_word & CMD_MEM_WRT_INVALIDATE)
+                       e1000_pci_set_mwi(hw);
+       }
+
+       /* Zero out the Multicast HASH table */
+       DEBUGOUT("Zeroing the MTA\n");
+       for (i = 0; i < mac->mta_reg_count; i++)
+               E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
+
+       /*
+        * Set the PCI priority bit correctly in the CTRL register.  This
+        * determines if the adapter gives priority to receives, or if it
+        * gives equal priority to transmits and receives.
+        */
+       if (dev_spec->dma_fairness) {
+               ctrl = E1000_READ_REG(hw, E1000_CTRL);
+               E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_PRIOR);
+       }
+
+       /* Setup link and flow control */
+       ret_val = e1000_setup_link_82542(hw);
+
+       /*
+        * Clear all of the statistics registers (clear on read).  It is
+        * important that we do this after we have tried to establish link
+        * because the symbol error count will increment wildly if there
+        * is no link.
+        */
+       e1000_clear_hw_cntrs_82542(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_setup_link_82542 - Setup flow control and link settings
+ *  @hw: pointer to the HW structure
+ *
+ *  Determines which flow control settings to use, then configures flow
+ *  control.  Calls the appropriate media-specific link configuration
+ *  function.  Assuming the adapter has a valid link partner, a valid link
+ *  should be established.  Assumes the hardware has previously been reset
+ *  and the transmitter and receiver are not enabled.
+ **/
+STATIC s32 e1000_setup_link_82542(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_setup_link_82542");
+
+       ret_val = e1000_set_default_fc_generic(hw);
+       if (ret_val)
+               goto out;
+
+       hw->fc.requested_mode &= ~e1000_fc_tx_pause;
+
+       if (mac->report_tx_early)
+               hw->fc.requested_mode &= ~e1000_fc_rx_pause;
+
+       /*
+        * Save off the requested flow control mode for use later.  Depending
+        * on the link partner's capabilities, we may or may not use this mode.
+        */
+       hw->fc.current_mode = hw->fc.requested_mode;
+
+       DEBUGOUT1("After fix-ups FlowControl is now = %x\n",
+                 hw->fc.current_mode);
+
+       /* Call the necessary subroutine to configure the link. */
+       ret_val = mac->ops.setup_physical_interface(hw);
+       if (ret_val)
+               goto out;
+
+       /*
+        * Initialize the flow control address, type, and PAUSE timer
+        * registers to their default values.  This is done even if flow
+        * control is disabled, because it does not hurt anything to
+        * initialize these registers.
+        */
+       DEBUGOUT("Initializing Flow Control address, type and timer regs\n");
+
+       E1000_WRITE_REG(hw, E1000_FCAL, FLOW_CONTROL_ADDRESS_LOW);
+       E1000_WRITE_REG(hw, E1000_FCAH, FLOW_CONTROL_ADDRESS_HIGH);
+       E1000_WRITE_REG(hw, E1000_FCT, FLOW_CONTROL_TYPE);
+
+       E1000_WRITE_REG(hw, E1000_FCTTV, hw->fc.pause_time);
+
+       ret_val = e1000_set_fc_watermarks_generic(hw);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_led_on_82542 - Turn on SW controllable LED
+ *  @hw: pointer to the HW structure
+ *
+ *  Turns the SW defined LED on.
+ **/
+STATIC s32 e1000_led_on_82542(struct e1000_hw *hw)
+{
+       u32 ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       DEBUGFUNC("e1000_led_on_82542");
+
+       ctrl |= E1000_CTRL_SWDPIN0;
+       ctrl |= E1000_CTRL_SWDPIO0;
+       E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_led_off_82542 - Turn off SW controllable LED
+ *  @hw: pointer to the HW structure
+ *
+ *  Turns the SW defined LED off.
+ **/
+STATIC s32 e1000_led_off_82542(struct e1000_hw *hw)
+{
+       u32 ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       DEBUGFUNC("e1000_led_off_82542");
+
+       ctrl &= ~E1000_CTRL_SWDPIN0;
+       ctrl |= E1000_CTRL_SWDPIO0;
+       E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_rar_set_82542 - Set receive address register
+ *  @hw: pointer to the HW structure
+ *  @addr: pointer to the receive address
+ *  @index: receive address array register
+ *
+ *  Sets the receive address array register at index to the address passed
+ *  in by addr.
+ **/
+STATIC void e1000_rar_set_82542(struct e1000_hw *hw, u8 *addr, u32 index)
+{
+       u32 rar_low, rar_high;
+
+       DEBUGFUNC("e1000_rar_set_82542");
+
+       /*
+        * 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;
+
+       E1000_WRITE_REG_ARRAY(hw, E1000_RA, (index << 1), rar_low);
+       E1000_WRITE_REG_ARRAY(hw, E1000_RA, ((index << 1) + 1), rar_high);
+}
+
+/**
+ *  e1000_translate_register_82542 - Translate the proper register offset
+ *  @reg: e1000 register to be read
+ *
+ *  Registers in 82542 are located in different offsets than other adapters
+ *  even though they function in the same manner.  This function takes in
+ *  the name of the register to read and returns the correct offset for
+ *  82542 silicon.
+ **/
+u32 e1000_translate_register_82542(u32 reg)
+{
+       /*
+        * Some of the 82542 registers are located at different
+        * offsets than they are in newer adapters.
+        * Despite the difference in location, the registers
+        * function in the same manner.
+        */
+       switch (reg) {
+       case E1000_RA:
+               reg = 0x00040;
+               break;
+       case E1000_RDTR:
+               reg = 0x00108;
+               break;
+       case E1000_RDBAL(0):
+               reg = 0x00110;
+               break;
+       case E1000_RDBAH(0):
+               reg = 0x00114;
+               break;
+       case E1000_RDLEN(0):
+               reg = 0x00118;
+               break;
+       case E1000_RDH(0):
+               reg = 0x00120;
+               break;
+       case E1000_RDT(0):
+               reg = 0x00128;
+               break;
+       case E1000_RDBAL(1):
+               reg = 0x00138;
+               break;
+       case E1000_RDBAH(1):
+               reg = 0x0013C;
+               break;
+       case E1000_RDLEN(1):
+               reg = 0x00140;
+               break;
+       case E1000_RDH(1):
+               reg = 0x00148;
+               break;
+       case E1000_RDT(1):
+               reg = 0x00150;
+               break;
+       case E1000_FCRTH:
+               reg = 0x00160;
+               break;
+       case E1000_FCRTL:
+               reg = 0x00168;
+               break;
+       case E1000_MTA:
+               reg = 0x00200;
+               break;
+       case E1000_TDBAL(0):
+               reg = 0x00420;
+               break;
+       case E1000_TDBAH(0):
+               reg = 0x00424;
+               break;
+       case E1000_TDLEN(0):
+               reg = 0x00428;
+               break;
+       case E1000_TDH(0):
+               reg = 0x00430;
+               break;
+       case E1000_TDT(0):
+               reg = 0x00438;
+               break;
+       case E1000_TIDV:
+               reg = 0x00440;
+               break;
+       case E1000_VFTA:
+               reg = 0x00600;
+               break;
+       case E1000_TDFH:
+               reg = 0x08010;
+               break;
+       case E1000_TDFT:
+               reg = 0x08018;
+               break;
+       default:
+               break;
+       }
+
+       return reg;
+}
+
+/**
+ *  e1000_clear_hw_cntrs_82542 - Clear device specific hardware counters
+ *  @hw: pointer to the HW structure
+ *
+ *  Clears the hardware counters by reading the counter registers.
+ **/
+STATIC void e1000_clear_hw_cntrs_82542(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_clear_hw_cntrs_82542");
+
+       e1000_clear_hw_cntrs_base_generic(hw);
+
+       E1000_READ_REG(hw, E1000_PRC64);
+       E1000_READ_REG(hw, E1000_PRC127);
+       E1000_READ_REG(hw, E1000_PRC255);
+       E1000_READ_REG(hw, E1000_PRC511);
+       E1000_READ_REG(hw, E1000_PRC1023);
+       E1000_READ_REG(hw, E1000_PRC1522);
+       E1000_READ_REG(hw, E1000_PTC64);
+       E1000_READ_REG(hw, E1000_PTC127);
+       E1000_READ_REG(hw, E1000_PTC255);
+       E1000_READ_REG(hw, E1000_PTC511);
+       E1000_READ_REG(hw, E1000_PTC1023);
+       E1000_READ_REG(hw, E1000_PTC1522);
+}
+
+/**
+ *  e1000_read_mac_addr_82542 - Read device MAC address
+ *  @hw: pointer to the HW structure
+ *
+ *  Reads the device MAC address from the EEPROM and stores the value.
+ **/
+s32 e1000_read_mac_addr_82542(struct e1000_hw *hw)
+{
+       s32  ret_val = E1000_SUCCESS;
+       u16 offset, nvm_data, i;
+
+       DEBUGFUNC("e1000_read_mac_addr");
+
+       for (i = 0; i < ETH_ADDR_LEN; i += 2) {
+               offset = i >> 1;
+               ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data);
+               if (ret_val) {
+                       DEBUGOUT("NVM Read Error\n");
+                       goto out;
+               }
+               hw->mac.perm_addr[i] = (u8)(nvm_data & 0xFF);
+               hw->mac.perm_addr[i+1] = (u8)(nvm_data >> 8);
+       }
+
+       for (i = 0; i < ETH_ADDR_LEN; i++)
+               hw->mac.addr[i] = hw->mac.perm_addr[i];
+
+out:
+       return ret_val;
+}
diff --git a/lib/librte_pmd_e1000/e1000/e1000_82543.c b/lib/librte_pmd_e1000/e1000/e1000_82543.c
new file mode 100644 (file)
index 0000000..da76965
--- /dev/null
@@ -0,0 +1,1553 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+/*
+ * 82543GC Gigabit Ethernet Controller (Fiber)
+ * 82543GC Gigabit Ethernet Controller (Copper)
+ * 82544EI Gigabit Ethernet Controller (Copper)
+ * 82544EI Gigabit Ethernet Controller (Fiber)
+ * 82544GC Gigabit Ethernet Controller (Copper)
+ * 82544GC Gigabit Ethernet Controller (LOM)
+ */
+
+#include "e1000_api.h"
+
+STATIC s32  e1000_init_phy_params_82543(struct e1000_hw *hw);
+STATIC s32  e1000_init_nvm_params_82543(struct e1000_hw *hw);
+STATIC s32  e1000_init_mac_params_82543(struct e1000_hw *hw);
+STATIC s32  e1000_read_phy_reg_82543(struct e1000_hw *hw, u32 offset,
+                                    u16 *data);
+STATIC s32  e1000_write_phy_reg_82543(struct e1000_hw *hw, u32 offset,
+                                     u16 data);
+STATIC s32  e1000_phy_force_speed_duplex_82543(struct e1000_hw *hw);
+STATIC s32  e1000_phy_hw_reset_82543(struct e1000_hw *hw);
+STATIC s32  e1000_reset_hw_82543(struct e1000_hw *hw);
+STATIC s32  e1000_init_hw_82543(struct e1000_hw *hw);
+STATIC s32  e1000_setup_link_82543(struct e1000_hw *hw);
+STATIC s32  e1000_setup_copper_link_82543(struct e1000_hw *hw);
+STATIC s32  e1000_setup_fiber_link_82543(struct e1000_hw *hw);
+STATIC s32  e1000_check_for_copper_link_82543(struct e1000_hw *hw);
+STATIC s32  e1000_check_for_fiber_link_82543(struct e1000_hw *hw);
+STATIC s32  e1000_led_on_82543(struct e1000_hw *hw);
+STATIC s32  e1000_led_off_82543(struct e1000_hw *hw);
+STATIC void e1000_write_vfta_82543(struct e1000_hw *hw, u32 offset,
+                                  u32 value);
+STATIC void e1000_clear_hw_cntrs_82543(struct e1000_hw *hw);
+static s32  e1000_config_mac_to_phy_82543(struct e1000_hw *hw);
+static bool e1000_init_phy_disabled_82543(struct e1000_hw *hw);
+static void e1000_lower_mdi_clk_82543(struct e1000_hw *hw, u32 *ctrl);
+static s32  e1000_polarity_reversal_workaround_82543(struct e1000_hw *hw);
+static void e1000_raise_mdi_clk_82543(struct e1000_hw *hw, u32 *ctrl);
+static u16  e1000_shift_in_mdi_bits_82543(struct e1000_hw *hw);
+static void e1000_shift_out_mdi_bits_82543(struct e1000_hw *hw, u32 data,
+                                          u16 count);
+static bool e1000_tbi_compatibility_enabled_82543(struct e1000_hw *hw);
+static void e1000_set_tbi_sbp_82543(struct e1000_hw *hw, bool state);
+
+/**
+ *  e1000_init_phy_params_82543 - Init PHY func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_phy_params_82543(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_init_phy_params_82543");
+
+       if (hw->phy.media_type != e1000_media_type_copper) {
+               phy->type = e1000_phy_none;
+               goto out;
+       } else {
+               phy->ops.power_up = e1000_power_up_phy_copper;
+               phy->ops.power_down = e1000_power_down_phy_copper;
+       }
+
+       phy->addr               = 1;
+       phy->autoneg_mask       = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+       phy->reset_delay_us     = 10000;
+       phy->type               = e1000_phy_m88;
+
+       /* Function Pointers */
+       phy->ops.check_polarity = e1000_check_polarity_m88;
+       phy->ops.commit         = e1000_phy_sw_reset_generic;
+       phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_82543;
+       phy->ops.get_cable_length = e1000_get_cable_length_m88;
+       phy->ops.get_cfg_done   = e1000_get_cfg_done_generic;
+       phy->ops.read_reg       = (hw->mac.type == e1000_82543)
+                                 ? e1000_read_phy_reg_82543
+                                 : e1000_read_phy_reg_m88;
+       phy->ops.reset          = (hw->mac.type == e1000_82543)
+                                 ? e1000_phy_hw_reset_82543
+                                 : e1000_phy_hw_reset_generic;
+       phy->ops.write_reg      = (hw->mac.type == e1000_82543)
+                                 ? e1000_write_phy_reg_82543
+                                 : e1000_write_phy_reg_m88;
+       phy->ops.get_info       = e1000_get_phy_info_m88;
+
+       /*
+        * The external PHY of the 82543 can be in a funky state.
+        * Resetting helps us read the PHY registers for acquiring
+        * the PHY ID.
+        */
+       if (!e1000_init_phy_disabled_82543(hw)) {
+               ret_val = phy->ops.reset(hw);
+               if (ret_val) {
+                       DEBUGOUT("Resetting PHY during init failed.\n");
+                       goto out;
+               }
+               msec_delay(20);
+       }
+
+       ret_val = e1000_get_phy_id(hw);
+       if (ret_val)
+               goto out;
+
+       /* Verify phy id */
+       switch (hw->mac.type) {
+       case e1000_82543:
+               if (phy->id != M88E1000_E_PHY_ID) {
+                       ret_val = -E1000_ERR_PHY;
+                       goto out;
+               }
+               break;
+       case e1000_82544:
+               if (phy->id != M88E1000_I_PHY_ID) {
+                       ret_val = -E1000_ERR_PHY;
+                       goto out;
+               }
+               break;
+       default:
+               ret_val = -E1000_ERR_PHY;
+               goto out;
+               break;
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_init_nvm_params_82543 - Init NVM func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_nvm_params_82543(struct e1000_hw *hw)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+
+       DEBUGFUNC("e1000_init_nvm_params_82543");
+
+       nvm->type               = e1000_nvm_eeprom_microwire;
+       nvm->word_size          = 64;
+       nvm->delay_usec         = 50;
+       nvm->address_bits       =  6;
+       nvm->opcode_bits        =  3;
+
+       /* Function Pointers */
+       nvm->ops.read           = e1000_read_nvm_microwire;
+       nvm->ops.update         = e1000_update_nvm_checksum_generic;
+       nvm->ops.valid_led_default = e1000_valid_led_default_generic;
+       nvm->ops.validate       = e1000_validate_nvm_checksum_generic;
+       nvm->ops.write          = e1000_write_nvm_microwire;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_mac_params_82543 - Init MAC func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_mac_params_82543(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+
+       DEBUGFUNC("e1000_init_mac_params_82543");
+
+       /* Set media type */
+       switch (hw->device_id) {
+       case E1000_DEV_ID_82543GC_FIBER:
+       case E1000_DEV_ID_82544EI_FIBER:
+               hw->phy.media_type = e1000_media_type_fiber;
+               break;
+       default:
+               hw->phy.media_type = e1000_media_type_copper;
+               break;
+       }
+
+       /* Set mta register count */
+       mac->mta_reg_count = 128;
+       /* Set rar entry count */
+       mac->rar_entry_count = E1000_RAR_ENTRIES;
+
+       /* Function pointers */
+
+       /* bus type/speed/width */
+       mac->ops.get_bus_info = e1000_get_bus_info_pci_generic;
+       /* function id */
+       mac->ops.set_lan_id = e1000_set_lan_id_multi_port_pci;
+       /* reset */
+       mac->ops.reset_hw = e1000_reset_hw_82543;
+       /* hw initialization */
+       mac->ops.init_hw = e1000_init_hw_82543;
+       /* link setup */
+       mac->ops.setup_link = e1000_setup_link_82543;
+       /* physical interface setup */
+       mac->ops.setup_physical_interface =
+               (hw->phy.media_type == e1000_media_type_copper)
+                ? e1000_setup_copper_link_82543 : e1000_setup_fiber_link_82543;
+       /* check for link */
+       mac->ops.check_for_link =
+               (hw->phy.media_type == e1000_media_type_copper)
+                ? e1000_check_for_copper_link_82543
+                : e1000_check_for_fiber_link_82543;
+       /* link info */
+       mac->ops.get_link_up_info =
+               (hw->phy.media_type == e1000_media_type_copper)
+                ? e1000_get_speed_and_duplex_copper_generic
+                : e1000_get_speed_and_duplex_fiber_serdes_generic;
+       /* multicast address update */
+       mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
+       /* writing VFTA */
+       mac->ops.write_vfta = e1000_write_vfta_82543;
+       /* clearing VFTA */
+       mac->ops.clear_vfta = e1000_clear_vfta_generic;
+       /* turn on/off LED */
+       mac->ops.led_on = e1000_led_on_82543;
+       mac->ops.led_off = e1000_led_off_82543;
+       /* clear hardware counters */
+       mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_82543;
+
+       /* Set tbi compatibility */
+       if ((hw->mac.type != e1000_82543) ||
+           (hw->phy.media_type == e1000_media_type_fiber))
+               e1000_set_tbi_compatibility_82543(hw, false);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_function_pointers_82543 - Init func ptrs.
+ *  @hw: pointer to the HW structure
+ *
+ *  Called to initialize all function pointers and parameters.
+ **/
+void e1000_init_function_pointers_82543(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_init_function_pointers_82543");
+
+       hw->mac.ops.init_params = e1000_init_mac_params_82543;
+       hw->nvm.ops.init_params = e1000_init_nvm_params_82543;
+       hw->phy.ops.init_params = e1000_init_phy_params_82543;
+}
+
+/**
+ *  e1000_tbi_compatibility_enabled_82543 - Returns TBI compat status
+ *  @hw: pointer to the HW structure
+ *
+ *  Returns the current status of 10-bit Interface (TBI) compatibility
+ *  (enabled/disabled).
+ **/
+static bool e1000_tbi_compatibility_enabled_82543(struct e1000_hw *hw)
+{
+       struct e1000_dev_spec_82543 *dev_spec = &hw->dev_spec._82543;
+       bool state = false;
+
+       DEBUGFUNC("e1000_tbi_compatibility_enabled_82543");
+
+       if (hw->mac.type != e1000_82543) {
+               DEBUGOUT("TBI compatibility workaround for 82543 only.\n");
+               goto out;
+       }
+
+       state = !!(dev_spec->tbi_compatibility & TBI_COMPAT_ENABLED);
+
+out:
+       return state;
+}
+
+/**
+ *  e1000_set_tbi_compatibility_82543 - Set TBI compatibility
+ *  @hw: pointer to the HW structure
+ *  @state: enable/disable TBI compatibility
+ *
+ *  Enables or disabled 10-bit Interface (TBI) compatibility.
+ **/
+void e1000_set_tbi_compatibility_82543(struct e1000_hw *hw, bool state)
+{
+       struct e1000_dev_spec_82543 *dev_spec = &hw->dev_spec._82543;
+
+       DEBUGFUNC("e1000_set_tbi_compatibility_82543");
+
+       if (hw->mac.type != e1000_82543) {
+               DEBUGOUT("TBI compatibility workaround for 82543 only.\n");
+               goto out;
+       }
+
+       if (state)
+               dev_spec->tbi_compatibility |= TBI_COMPAT_ENABLED;
+       else
+               dev_spec->tbi_compatibility &= ~TBI_COMPAT_ENABLED;
+
+out:
+       return;
+}
+
+/**
+ *  e1000_tbi_sbp_enabled_82543 - Returns TBI SBP status
+ *  @hw: pointer to the HW structure
+ *
+ *  Returns the current status of 10-bit Interface (TBI) store bad packet (SBP)
+ *  (enabled/disabled).
+ **/
+bool e1000_tbi_sbp_enabled_82543(struct e1000_hw *hw)
+{
+       struct e1000_dev_spec_82543 *dev_spec = &hw->dev_spec._82543;
+       bool state = false;
+
+       DEBUGFUNC("e1000_tbi_sbp_enabled_82543");
+
+       if (hw->mac.type != e1000_82543) {
+               DEBUGOUT("TBI compatibility workaround for 82543 only.\n");
+               goto out;
+       }
+
+       state = !!(dev_spec->tbi_compatibility & TBI_SBP_ENABLED);
+
+out:
+       return state;
+}
+
+/**
+ *  e1000_set_tbi_sbp_82543 - Set TBI SBP
+ *  @hw: pointer to the HW structure
+ *  @state: enable/disable TBI store bad packet
+ *
+ *  Enables or disabled 10-bit Interface (TBI) store bad packet (SBP).
+ **/
+static void e1000_set_tbi_sbp_82543(struct e1000_hw *hw, bool state)
+{
+       struct e1000_dev_spec_82543 *dev_spec = &hw->dev_spec._82543;
+
+       DEBUGFUNC("e1000_set_tbi_sbp_82543");
+
+       if (state && e1000_tbi_compatibility_enabled_82543(hw))
+               dev_spec->tbi_compatibility |= TBI_SBP_ENABLED;
+       else
+               dev_spec->tbi_compatibility &= ~TBI_SBP_ENABLED;
+
+       return;
+}
+
+/**
+ *  e1000_init_phy_disabled_82543 - Returns init PHY status
+ *  @hw: pointer to the HW structure
+ *
+ *  Returns the current status of whether PHY initialization is disabled.
+ *  True if PHY initialization is disabled else false.
+ **/
+static bool e1000_init_phy_disabled_82543(struct e1000_hw *hw)
+{
+       struct e1000_dev_spec_82543 *dev_spec = &hw->dev_spec._82543;
+       bool ret_val;
+
+       DEBUGFUNC("e1000_init_phy_disabled_82543");
+
+       if (hw->mac.type != e1000_82543) {
+               ret_val = false;
+               goto out;
+       }
+
+       ret_val = dev_spec->init_phy_disabled;
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_tbi_adjust_stats_82543 - Adjust stats when TBI enabled
+ *  @hw: pointer to the HW structure
+ *  @stats: Struct containing statistic register values
+ *  @frame_len: The length of the frame in question
+ *  @mac_addr: The Ethernet destination address of the frame in question
+ *  @max_frame_size: The maximum frame size
+ *
+ *  Adjusts the statistic counters when a frame is accepted by TBI_ACCEPT
+ **/
+void e1000_tbi_adjust_stats_82543(struct e1000_hw *hw,
+                                 struct e1000_hw_stats *stats, u32 frame_len,
+                                 u8 *mac_addr, u32 max_frame_size)
+{
+       if (!(e1000_tbi_sbp_enabled_82543(hw)))
+               goto out;
+
+       /* First adjust the frame length. */
+       frame_len--;
+       /*
+        * We need to adjust the statistics counters, since the hardware
+        * counters overcount this packet as a CRC error and undercount
+        * the packet as a good packet
+        */
+       /* This packet should not be counted as a CRC error. */
+       stats->crcerrs--;
+       /* This packet does count as a Good Packet Received. */
+       stats->gprc++;
+
+       /* Adjust the Good Octets received counters */
+       stats->gorc += frame_len;
+
+       /*
+        * Is this a broadcast or multicast?  Check broadcast first,
+        * since the test for a multicast frame will test positive on
+        * a broadcast frame.
+        */
+       if ((mac_addr[0] == 0xff) && (mac_addr[1] == 0xff))
+               /* Broadcast packet */
+               stats->bprc++;
+       else if (*mac_addr & 0x01)
+               /* Multicast packet */
+               stats->mprc++;
+
+       /*
+        * In this case, the hardware has over counted the number of
+        * oversize frames.
+        */
+       if ((frame_len == max_frame_size) && (stats->roc > 0))
+               stats->roc--;
+
+       /*
+        * Adjust the bin counters when the extra byte put the frame in the
+        * wrong bin. Remember that the frame_len was adjusted above.
+        */
+       if (frame_len == 64) {
+               stats->prc64++;
+               stats->prc127--;
+       } else if (frame_len == 127) {
+               stats->prc127++;
+               stats->prc255--;
+       } else if (frame_len == 255) {
+               stats->prc255++;
+               stats->prc511--;
+       } else if (frame_len == 511) {
+               stats->prc511++;
+               stats->prc1023--;
+       } else if (frame_len == 1023) {
+               stats->prc1023++;
+               stats->prc1522--;
+       } else if (frame_len == 1522) {
+               stats->prc1522++;
+       }
+
+out:
+       return;
+}
+
+/**
+ *  e1000_read_phy_reg_82543 - Read PHY register
+ *  @hw: pointer to the HW structure
+ *  @offset: register offset to be read
+ *  @data: pointer to the read data
+ *
+ *  Reads the PHY at offset and stores the information read to data.
+ **/
+STATIC s32 e1000_read_phy_reg_82543(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+       u32 mdic;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_read_phy_reg_82543");
+
+       if (offset > MAX_PHY_REG_ADDRESS) {
+               DEBUGOUT1("PHY Address %d is out of range\n", offset);
+               ret_val = -E1000_ERR_PARAM;
+               goto out;
+       }
+
+       /*
+        * We must first send a preamble through the MDIO pin to signal the
+        * beginning of an MII instruction.  This is done by sending 32
+        * consecutive "1" bits.
+        */
+       e1000_shift_out_mdi_bits_82543(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE);
+
+       /*
+        * Now combine the next few fields that are required for a read
+        * operation.  We use this method instead of calling the
+        * e1000_shift_out_mdi_bits routine five different times.  The format
+        * of an MII read instruction consists of a shift out of 14 bits and
+        * is defined as follows:
+        *         <Preamble><SOF><Op Code><Phy Addr><Offset>
+        * followed by a shift in of 18 bits.  This first two bits shifted in
+        * are TurnAround bits used to avoid contention on the MDIO pin when a
+        * READ operation is performed.  These two bits are thrown away
+        * followed by a shift in of 16 bits which contains the desired data.
+        */
+       mdic = (offset | (hw->phy.addr << 5) |
+               (PHY_OP_READ << 10) | (PHY_SOF << 12));
+
+       e1000_shift_out_mdi_bits_82543(hw, mdic, 14);
+
+       /*
+        * Now that we've shifted out the read command to the MII, we need to
+        * "shift in" the 16-bit value (18 total bits) of the requested PHY
+        * register address.
+        */
+       *data = e1000_shift_in_mdi_bits_82543(hw);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_write_phy_reg_82543 - Write PHY register
+ *  @hw: pointer to the HW structure
+ *  @offset: register offset to be written
+ *  @data: pointer to the data to be written at offset
+ *
+ *  Writes data to the PHY at offset.
+ **/
+STATIC s32 e1000_write_phy_reg_82543(struct e1000_hw *hw, u32 offset, u16 data)
+{
+       u32 mdic;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_write_phy_reg_82543");
+
+       if (offset > MAX_PHY_REG_ADDRESS) {
+               DEBUGOUT1("PHY Address %d is out of range\n", offset);
+               ret_val = -E1000_ERR_PARAM;
+               goto out;
+       }
+
+       /*
+        * We'll need to use the SW defined pins to shift the write command
+        * out to the PHY. We first send a preamble to the PHY to signal the
+        * beginning of the MII instruction.  This is done by sending 32
+        * consecutive "1" bits.
+        */
+       e1000_shift_out_mdi_bits_82543(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE);
+
+       /*
+        * Now combine the remaining required fields that will indicate a
+        * write operation. We use this method instead of calling the
+        * e1000_shift_out_mdi_bits routine for each field in the command. The
+        * format of a MII write instruction is as follows:
+        * <Preamble><SOF><Op Code><Phy Addr><Reg Addr><Turnaround><Data>.
+        */
+       mdic = ((PHY_TURNAROUND) | (offset << 2) | (hw->phy.addr << 7) |
+               (PHY_OP_WRITE << 12) | (PHY_SOF << 14));
+       mdic <<= 16;
+       mdic |= (u32)data;
+
+       e1000_shift_out_mdi_bits_82543(hw, mdic, 32);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_raise_mdi_clk_82543 - Raise Management Data Input clock
+ *  @hw: pointer to the HW structure
+ *  @ctrl: pointer to the control register
+ *
+ *  Raise the management data input clock by setting the MDC bit in the control
+ *  register.
+ **/
+static void e1000_raise_mdi_clk_82543(struct e1000_hw *hw, u32 *ctrl)
+{
+       /*
+        * Raise the clock input to the Management Data Clock (by setting the
+        * MDC bit), and then delay a sufficient amount of time.
+        */
+       E1000_WRITE_REG(hw, E1000_CTRL, (*ctrl | E1000_CTRL_MDC));
+       E1000_WRITE_FLUSH(hw);
+       usec_delay(10);
+}
+
+/**
+ *  e1000_lower_mdi_clk_82543 - Lower Management Data Input clock
+ *  @hw: pointer to the HW structure
+ *  @ctrl: pointer to the control register
+ *
+ *  Lower the management data input clock by clearing the MDC bit in the
+ *  control register.
+ **/
+static void e1000_lower_mdi_clk_82543(struct e1000_hw *hw, u32 *ctrl)
+{
+       /*
+        * Lower the clock input to the Management Data Clock (by clearing the
+        * MDC bit), and then delay a sufficient amount of time.
+        */
+       E1000_WRITE_REG(hw, E1000_CTRL, (*ctrl & ~E1000_CTRL_MDC));
+       E1000_WRITE_FLUSH(hw);
+       usec_delay(10);
+}
+
+/**
+ *  e1000_shift_out_mdi_bits_82543 - Shift data bits our to the PHY
+ *  @hw: pointer to the HW structure
+ *  @data: data to send to the PHY
+ *  @count: number of bits to shift out
+ *
+ *  We need to shift 'count' bits out to the PHY.  So, the value in the
+ *  "data" parameter will be shifted out to the PHY one bit at a time.
+ *  In order to do this, "data" must be broken down into bits.
+ **/
+static void e1000_shift_out_mdi_bits_82543(struct e1000_hw *hw, u32 data,
+                                          u16 count)
+{
+       u32 ctrl, mask;
+
+       /*
+        * We need to shift "count" number of bits out to the PHY.  So, the
+        * value in the "data" parameter will be shifted out to the PHY one
+        * bit at a time.  In order to do this, "data" must be broken down
+        * into bits.
+        */
+       mask = 0x01;
+       mask <<= (count - 1);
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       /* Set MDIO_DIR and MDC_DIR direction bits to be used as output pins. */
+       ctrl |= (E1000_CTRL_MDIO_DIR | E1000_CTRL_MDC_DIR);
+
+       while (mask) {
+               /*
+                * A "1" is shifted out to the PHY by setting the MDIO bit to
+                * "1" and then raising and lowering the Management Data Clock.
+                * A "0" is shifted out to the PHY by setting the MDIO bit to
+                * "0" and then raising and lowering the clock.
+                */
+               if (data & mask)
+                       ctrl |= E1000_CTRL_MDIO;
+               else
+                       ctrl &= ~E1000_CTRL_MDIO;
+
+               E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+               E1000_WRITE_FLUSH(hw);
+
+               usec_delay(10);
+
+               e1000_raise_mdi_clk_82543(hw, &ctrl);
+               e1000_lower_mdi_clk_82543(hw, &ctrl);
+
+               mask >>= 1;
+       }
+}
+
+/**
+ *  e1000_shift_in_mdi_bits_82543 - Shift data bits in from the PHY
+ *  @hw: pointer to the HW structure
+ *
+ *  In order to read a register from the PHY, we need to shift 18 bits
+ *  in from the PHY.  Bits are "shifted in" by raising the clock input to
+ *  the PHY (setting the MDC bit), and then reading the value of the data out
+ *  MDIO bit.
+ **/
+static u16 e1000_shift_in_mdi_bits_82543(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       u16 data = 0;
+       u8 i;
+
+       /*
+        * In order to read a register from the PHY, we need to shift in a
+        * total of 18 bits from the PHY.  The first two bit (turnaround)
+        * times are used to avoid contention on the MDIO pin when a read
+        * operation is performed.  These two bits are ignored by us and
+        * thrown away.  Bits are "shifted in" by raising the input to the
+        * Management Data Clock (setting the MDC bit) and then reading the
+        * value of the MDIO bit.
+        */
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       /*
+        * Clear MDIO_DIR (SWDPIO1) to indicate this bit is to be used as
+        * input.
+        */
+       ctrl &= ~E1000_CTRL_MDIO_DIR;
+       ctrl &= ~E1000_CTRL_MDIO;
+
+       E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+       E1000_WRITE_FLUSH(hw);
+
+       /*
+        * Raise and lower the clock before reading in the data.  This accounts
+        * for the turnaround bits.  The first clock occurred when we clocked
+        * out the last bit of the Register Address.
+        */
+       e1000_raise_mdi_clk_82543(hw, &ctrl);
+       e1000_lower_mdi_clk_82543(hw, &ctrl);
+
+       for (data = 0, i = 0; i < 16; i++) {
+               data <<= 1;
+               e1000_raise_mdi_clk_82543(hw, &ctrl);
+               ctrl = E1000_READ_REG(hw, E1000_CTRL);
+               /* Check to see if we shifted in a "1". */
+               if (ctrl & E1000_CTRL_MDIO)
+                       data |= 1;
+               e1000_lower_mdi_clk_82543(hw, &ctrl);
+       }
+
+       e1000_raise_mdi_clk_82543(hw, &ctrl);
+       e1000_lower_mdi_clk_82543(hw, &ctrl);
+
+       return data;
+}
+
+/**
+ *  e1000_phy_force_speed_duplex_82543 - Force speed/duplex for PHY
+ *  @hw: pointer to the HW structure
+ *
+ *  Calls the function to force speed and duplex for the m88 PHY, and
+ *  if the PHY is not auto-negotiating and the speed is forced to 10Mbit,
+ *  then call the function for polarity reversal workaround.
+ **/
+STATIC s32 e1000_phy_force_speed_duplex_82543(struct e1000_hw *hw)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_phy_force_speed_duplex_82543");
+
+       ret_val = e1000_phy_force_speed_duplex_m88(hw);
+       if (ret_val)
+               goto out;
+
+       if (!hw->mac.autoneg && (hw->mac.forced_speed_duplex &
+           E1000_ALL_10_SPEED))
+               ret_val = e1000_polarity_reversal_workaround_82543(hw);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_polarity_reversal_workaround_82543 - Workaround polarity reversal
+ *  @hw: pointer to the HW structure
+ *
+ *  When forcing link to 10 Full or 10 Half, the PHY can reverse the polarity
+ *  inadvertently.  To workaround the issue, we disable the transmitter on
+ *  the PHY until we have established the link partner's link parameters.
+ **/
+static s32 e1000_polarity_reversal_workaround_82543(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u16 mii_status_reg;
+       u16 i;
+       bool link;
+
+       if (!(hw->phy.ops.write_reg))
+               goto out;
+
+       /* Polarity reversal workaround for forced 10F/10H links. */
+
+       /* Disable the transmitter on the PHY */
+
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019);
+       if (ret_val)
+               goto out;
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFFF);
+       if (ret_val)
+               goto out;
+
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000);
+       if (ret_val)
+               goto out;
+
+       /*
+        * This loop will early-out if the NO link condition has been met.
+        * In other words, DO NOT use e1000_phy_has_link_generic() here.
+        */
+       for (i = PHY_FORCE_TIME; i > 0; i--) {
+               /*
+                * Read the MII Status Register and wait for Link Status bit
+                * to be clear.
+                */
+
+               ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &mii_status_reg);
+               if (ret_val)
+                       goto out;
+
+               ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &mii_status_reg);
+               if (ret_val)
+                       goto out;
+
+               if (!(mii_status_reg & ~MII_SR_LINK_STATUS))
+                       break;
+               msec_delay_irq(100);
+       }
+
+       /* Recommended delay time after link has been lost */
+       msec_delay_irq(1000);
+
+       /* Now we will re-enable the transmitter on the PHY */
+
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019);
+       if (ret_val)
+               goto out;
+       msec_delay_irq(50);
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFF0);
+       if (ret_val)
+               goto out;
+       msec_delay_irq(50);
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFF00);
+       if (ret_val)
+               goto out;
+       msec_delay_irq(50);
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x0000);
+       if (ret_val)
+               goto out;
+
+       ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000);
+       if (ret_val)
+               goto out;
+
+       /*
+        * Read the MII Status Register and wait for Link Status bit
+        * to be set.
+        */
+       ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_TIME, 100000, &link);
+       if (ret_val)
+               goto out;
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_phy_hw_reset_82543 - PHY hardware reset
+ *  @hw: pointer to the HW structure
+ *
+ *  Sets the PHY_RESET_DIR bit in the extended device control register
+ *  to put the PHY into a reset and waits for completion.  Once the reset
+ *  has been accomplished, clear the PHY_RESET_DIR bit to take the PHY out
+ *  of reset.
+ **/
+STATIC s32 e1000_phy_hw_reset_82543(struct e1000_hw *hw)
+{
+       u32 ctrl_ext;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_phy_hw_reset_82543");
+
+       /*
+        * Read the Extended Device Control Register, assert the PHY_RESET_DIR
+        * bit to put the PHY into reset...
+        */
+       ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
+       ctrl_ext |= E1000_CTRL_EXT_SDP4_DIR;
+       ctrl_ext &= ~E1000_CTRL_EXT_SDP4_DATA;
+       E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+       E1000_WRITE_FLUSH(hw);
+
+       msec_delay(10);
+
+       /* ...then take it out of reset. */
+       ctrl_ext |= E1000_CTRL_EXT_SDP4_DATA;
+       E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+       E1000_WRITE_FLUSH(hw);
+
+       usec_delay(150);
+
+       if (!(hw->phy.ops.get_cfg_done))
+               return E1000_SUCCESS;
+
+       ret_val = hw->phy.ops.get_cfg_done(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_reset_hw_82543 - Reset hardware
+ *  @hw: pointer to the HW structure
+ *
+ *  This resets the hardware into a known state.
+ **/
+STATIC s32 e1000_reset_hw_82543(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_reset_hw_82543");
+
+       DEBUGOUT("Masking off all interrupts\n");
+       E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+
+       E1000_WRITE_REG(hw, E1000_RCTL, 0);
+       E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP);
+       E1000_WRITE_FLUSH(hw);
+
+       e1000_set_tbi_sbp_82543(hw, false);
+
+       /*
+        * Delay to allow any outstanding PCI transactions to complete before
+        * resetting the device
+        */
+       msec_delay(10);
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       DEBUGOUT("Issuing a global reset to 82543/82544 MAC\n");
+       if (hw->mac.type == e1000_82543) {
+               E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST);
+       } else {
+               /*
+                * The 82544 can't ACK the 64-bit write when issuing the
+                * reset, so use IO-mapping as a workaround.
+                */
+               E1000_WRITE_REG_IO(hw, E1000_CTRL, ctrl | E1000_CTRL_RST);
+       }
+
+       /*
+        * After MAC reset, force reload of NVM to restore power-on
+        * settings to device.
+        */
+       hw->nvm.ops.reload(hw);
+       msec_delay(2);
+
+       /* Masking off and clearing any pending interrupts */
+       E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+       E1000_READ_REG(hw, E1000_ICR);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_init_hw_82543 - Initialize hardware
+ *  @hw: pointer to the HW structure
+ *
+ *  This inits the hardware readying it for operation.
+ **/
+STATIC s32 e1000_init_hw_82543(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       struct e1000_dev_spec_82543 *dev_spec = &hw->dev_spec._82543;
+       u32 ctrl;
+       s32 ret_val;
+       u16 i;
+
+       DEBUGFUNC("e1000_init_hw_82543");
+
+       /* Disabling VLAN filtering */
+       E1000_WRITE_REG(hw, E1000_VET, 0);
+       mac->ops.clear_vfta(hw);
+
+       /* Setup the receive address. */
+       e1000_init_rx_addrs_generic(hw, mac->rar_entry_count);
+
+       /* Zero out the Multicast HASH table */
+       DEBUGOUT("Zeroing the MTA\n");
+       for (i = 0; i < mac->mta_reg_count; i++) {
+               E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
+               E1000_WRITE_FLUSH(hw);
+       }
+
+       /*
+        * Set the PCI priority bit correctly in the CTRL register.  This
+        * determines if the adapter gives priority to receives, or if it
+        * gives equal priority to transmits and receives.
+        */
+       if (hw->mac.type == e1000_82543 && dev_spec->dma_fairness) {
+               ctrl = E1000_READ_REG(hw, E1000_CTRL);
+               E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_PRIOR);
+       }
+
+       e1000_pcix_mmrbc_workaround_generic(hw);
+
+       /* Setup link and flow control */
+       ret_val = mac->ops.setup_link(hw);
+
+       /*
+        * Clear all of the statistics registers (clear on read).  It is
+        * important that we do this after we have tried to establish link
+        * because the symbol error count will increment wildly if there
+        * is no link.
+        */
+       e1000_clear_hw_cntrs_82543(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_setup_link_82543 - Setup flow control and link settings
+ *  @hw: pointer to the HW structure
+ *
+ *  Read the EEPROM to determine the initial polarity value and write the
+ *  extended device control register with the information before calling
+ *  the generic setup link function, which does the following:
+ *  Determines which flow control settings to use, then configures flow
+ *  control.  Calls the appropriate media-specific link configuration
+ *  function.  Assuming the adapter has a valid link partner, a valid link
+ *  should be established.  Assumes the hardware has previously been reset
+ *  and the transmitter and receiver are not enabled.
+ **/
+STATIC s32 e1000_setup_link_82543(struct e1000_hw *hw)
+{
+       u32 ctrl_ext;
+       s32  ret_val;
+       u16 data;
+
+       DEBUGFUNC("e1000_setup_link_82543");
+
+       /*
+        * Take the 4 bits from NVM word 0xF that determine the initial
+        * polarity value for the SW controlled pins, and setup the
+        * Extended Device Control reg with that info.
+        * This is needed because one of the SW controlled pins is used for
+        * signal detection.  So this should be done before phy setup.
+        */
+       if (hw->mac.type == e1000_82543) {
+               ret_val = hw->nvm.ops.read(hw, NVM_INIT_CONTROL2_REG, 1, &data);
+               if (ret_val) {
+                       DEBUGOUT("NVM Read Error\n");
+                       ret_val = -E1000_ERR_NVM;
+                       goto out;
+               }
+               ctrl_ext = ((data & NVM_WORD0F_SWPDIO_EXT_MASK) <<
+                           NVM_SWDPIO_EXT_SHIFT);
+               E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+       }
+
+       ret_val = e1000_setup_link_generic(hw);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_setup_copper_link_82543 - Configure copper link settings
+ *  @hw: pointer to the HW structure
+ *
+ *  Configures the link for auto-neg or forced speed and duplex.  Then we check
+ *  for link, once link is established calls to configure collision distance
+ *  and flow control are called.
+ **/
+STATIC s32 e1000_setup_copper_link_82543(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       s32 ret_val;
+       bool link;
+
+       DEBUGFUNC("e1000_setup_copper_link_82543");
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL) | E1000_CTRL_SLU;
+       /*
+        * With 82543, we need to force speed and duplex on the MAC
+        * equal to what the PHY speed and duplex configuration is.
+        * In addition, we need to perform a hardware reset on the
+        * PHY to take it out of reset.
+        */
+       if (hw->mac.type == e1000_82543) {
+               ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
+               E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+               ret_val = hw->phy.ops.reset(hw);
+               if (ret_val)
+                       goto out;
+       } else {
+               ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
+               E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+       }
+
+       /* Set MDI/MDI-X, Polarity Reversal, and downshift settings */
+       ret_val = e1000_copper_link_setup_m88(hw);
+       if (ret_val)
+               goto out;
+
+       if (hw->mac.autoneg) {
+               /*
+                * Setup autoneg and flow control advertisement and perform
+                * autonegotiation.
+                */
+               ret_val = e1000_copper_link_autoneg(hw);
+               if (ret_val)
+                       goto out;
+       } else {
+               /*
+                * PHY will be set to 10H, 10F, 100H or 100F
+                * depending on user settings.
+                */
+               DEBUGOUT("Forcing Speed and Duplex\n");
+               ret_val = e1000_phy_force_speed_duplex_82543(hw);
+               if (ret_val) {
+                       DEBUGOUT("Error Forcing Speed and Duplex\n");
+                       goto out;
+               }
+       }
+
+       /*
+        * Check link status. Wait up to 100 microseconds for link to become
+        * valid.
+        */
+       ret_val = e1000_phy_has_link_generic(hw, COPPER_LINK_UP_LIMIT, 10,
+                                            &link);
+       if (ret_val)
+               goto out;
+
+
+       if (link) {
+               DEBUGOUT("Valid link established!!!\n");
+               /* Config the MAC and PHY after link is up */
+               if (hw->mac.type == e1000_82544) {
+                       hw->mac.ops.config_collision_dist(hw);
+               } else {
+                       ret_val = e1000_config_mac_to_phy_82543(hw);
+                       if (ret_val)
+                               goto out;
+               }
+               ret_val = e1000_config_fc_after_link_up_generic(hw);
+       } else {
+               DEBUGOUT("Unable to establish link!!!\n");
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_setup_fiber_link_82543 - Setup link for fiber
+ *  @hw: pointer to the HW structure
+ *
+ *  Configures collision distance and flow control for fiber links.  Upon
+ *  successful setup, poll for link.
+ **/
+STATIC s32 e1000_setup_fiber_link_82543(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_setup_fiber_link_82543");
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       /* Take the link out of reset */
+       ctrl &= ~E1000_CTRL_LRST;
+
+       hw->mac.ops.config_collision_dist(hw);
+
+       ret_val = e1000_commit_fc_settings_generic(hw);
+       if (ret_val)
+               goto out;
+
+       DEBUGOUT("Auto-negotiation enabled\n");
+
+       E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+       E1000_WRITE_FLUSH(hw);
+       msec_delay(1);
+
+       /*
+        * For these adapters, the SW definable pin 1 is cleared when the
+        * optics detect a signal.  If we have a signal, then poll for a
+        * "Link-Up" indication.
+        */
+       if (!(E1000_READ_REG(hw, E1000_CTRL) & E1000_CTRL_SWDPIN1))
+               ret_val = e1000_poll_fiber_serdes_link_generic(hw);
+       else
+               DEBUGOUT("No signal detected\n");
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_check_for_copper_link_82543 - Check for link (Copper)
+ *  @hw: pointer to the HW structure
+ *
+ *  Checks the phy for link, if link exists, do the following:
+ *   - check for downshift
+ *   - do polarity workaround (if necessary)
+ *   - configure collision distance
+ *   - configure flow control after link up
+ *   - configure tbi compatibility
+ **/
+STATIC s32 e1000_check_for_copper_link_82543(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       u32 icr, rctl;
+       s32 ret_val;
+       u16 speed, duplex;
+       bool link;
+
+       DEBUGFUNC("e1000_check_for_copper_link_82543");
+
+       if (!mac->get_link_status) {
+               ret_val = E1000_SUCCESS;
+               goto out;
+       }
+
+       ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
+       if (ret_val)
+               goto out;
+
+       if (!link)
+               goto out; /* No link detected */
+
+       mac->get_link_status = false;
+
+       e1000_check_downshift_generic(hw);
+
+       /*
+        * If we are forcing speed/duplex, then we can return since
+        * we have already determined whether we have link or not.
+        */
+       if (!mac->autoneg) {
+               /*
+                * If speed and duplex are forced to 10H or 10F, then we will
+                * implement the polarity reversal workaround.  We disable
+                * interrupts first, and upon returning, place the devices
+                * interrupt state to its previous value except for the link
+                * status change interrupt which will happened due to the
+                * execution of this workaround.
+                */
+               if (mac->forced_speed_duplex & E1000_ALL_10_SPEED) {
+                       E1000_WRITE_REG(hw, E1000_IMC, 0xFFFFFFFF);
+                       ret_val = e1000_polarity_reversal_workaround_82543(hw);
+                       icr = E1000_READ_REG(hw, E1000_ICR);
+                       E1000_WRITE_REG(hw, E1000_ICS, (icr & ~E1000_ICS_LSC));
+                       E1000_WRITE_REG(hw, E1000_IMS, IMS_ENABLE_MASK);
+               }
+
+               ret_val = -E1000_ERR_CONFIG;
+               goto out;
+       }
+
+       /*
+        * We have a M88E1000 PHY and Auto-Neg is enabled.  If we
+        * have Si on board that is 82544 or newer, Auto
+        * Speed Detection takes care of MAC speed/duplex
+        * configuration.  So we only need to configure Collision
+        * Distance in the MAC.  Otherwise, we need to force
+        * speed/duplex on the MAC to the current PHY speed/duplex
+        * settings.
+        */
+       if (mac->type == e1000_82544)
+               hw->mac.ops.config_collision_dist(hw);
+       else {
+               ret_val = e1000_config_mac_to_phy_82543(hw);
+               if (ret_val) {
+                       DEBUGOUT("Error configuring MAC to PHY settings\n");
+                       goto out;
+               }
+       }
+
+       /*
+        * Configure Flow Control now that Auto-Neg has completed.
+        * First, we need to restore the desired flow control
+        * settings because we may have had to re-autoneg with a
+        * different link partner.
+        */
+       ret_val = e1000_config_fc_after_link_up_generic(hw);
+       if (ret_val)
+               DEBUGOUT("Error configuring flow control\n");
+
+       /*
+        * At this point we know that we are on copper and we have
+        * auto-negotiated link.  These are conditions for checking the link
+        * partner capability register.  We use the link speed to determine if
+        * TBI compatibility needs to be turned on or off.  If the link is not
+        * at gigabit speed, then TBI compatibility is not needed.  If we are
+        * at gigabit speed, we turn on TBI compatibility.
+        */
+       if (e1000_tbi_compatibility_enabled_82543(hw)) {
+               ret_val = mac->ops.get_link_up_info(hw, &speed, &duplex);
+               if (ret_val) {
+                       DEBUGOUT("Error getting link speed and duplex\n");
+                       return ret_val;
+               }
+               if (speed != SPEED_1000) {
+                       /*
+                        * If link speed is not set to gigabit speed,
+                        * we do not need to enable TBI compatibility.
+                        */
+                       if (e1000_tbi_sbp_enabled_82543(hw)) {
+                               /*
+                                * If we previously were in the mode,
+                                * turn it off.
+                                */
+                               e1000_set_tbi_sbp_82543(hw, false);
+                               rctl = E1000_READ_REG(hw, E1000_RCTL);
+                               rctl &= ~E1000_RCTL_SBP;
+                               E1000_WRITE_REG(hw, E1000_RCTL, rctl);
+                       }
+               } else {
+                       /*
+                        * If TBI compatibility is was previously off,
+                        * turn it on. For compatibility with a TBI link
+                        * partner, we will store bad packets. Some
+                        * frames have an additional byte on the end and
+                        * will look like CRC errors to to the hardware.
+                        */
+                       if (!e1000_tbi_sbp_enabled_82543(hw)) {
+                               e1000_set_tbi_sbp_82543(hw, true);
+                               rctl = E1000_READ_REG(hw, E1000_RCTL);
+                               rctl |= E1000_RCTL_SBP;
+                               E1000_WRITE_REG(hw, E1000_RCTL, rctl);
+                       }
+               }
+       }
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_check_for_fiber_link_82543 - Check for link (Fiber)
+ *  @hw: pointer to the HW structure
+ *
+ *  Checks for link up on the hardware.  If link is not up and we have
+ *  a signal, then we need to force link up.
+ **/
+STATIC s32 e1000_check_for_fiber_link_82543(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       u32 rxcw, ctrl, status;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_check_for_fiber_link_82543");
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+       status = E1000_READ_REG(hw, E1000_STATUS);
+       rxcw = E1000_READ_REG(hw, E1000_RXCW);
+
+       /*
+        * If we don't have link (auto-negotiation failed or link partner
+        * cannot auto-negotiate), the cable is plugged in (we have signal),
+        * and our link partner is not trying to auto-negotiate with us (we
+        * are receiving idles or data), we need to force link up. We also
+        * need to give auto-negotiation time to complete, in case the cable
+        * was just plugged in. The autoneg_failed flag does this.
+        */
+       /* (ctrl & E1000_CTRL_SWDPIN1) == 0 == have signal */
+       if ((!(ctrl & E1000_CTRL_SWDPIN1)) &&
+           (!(status & E1000_STATUS_LU)) &&
+           (!(rxcw & E1000_RXCW_C))) {
+               if (!mac->autoneg_failed) {
+                       mac->autoneg_failed = true;
+                       ret_val = 0;
+                       goto out;
+               }
+               DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\n");
+
+               /* Disable auto-negotiation in the TXCW register */
+               E1000_WRITE_REG(hw, E1000_TXCW, (mac->txcw & ~E1000_TXCW_ANE));
+
+               /* Force link-up and also force full-duplex. */
+               ctrl = E1000_READ_REG(hw, E1000_CTRL);
+               ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD);
+               E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+
+               /* Configure Flow Control after forcing link up. */
+               ret_val = e1000_config_fc_after_link_up_generic(hw);
+               if (ret_val) {
+                       DEBUGOUT("Error configuring flow control\n");
+                       goto out;
+               }
+       } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) {
+               /*
+                * If we are forcing link and we are receiving /C/ ordered
+                * sets, re-enable auto-negotiation in the TXCW register
+                * and disable forced link in the Device Control register
+                * in an attempt to auto-negotiate with our link partner.
+                */
+               DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\n");
+               E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw);
+               E1000_WRITE_REG(hw, E1000_CTRL, (ctrl & ~E1000_CTRL_SLU));
+
+               mac->serdes_has_link = true;
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_config_mac_to_phy_82543 - Configure MAC to PHY settings
+ *  @hw: pointer to the HW structure
+ *
+ *  For the 82543 silicon, we need to set the MAC to match the settings
+ *  of the PHY, even if the PHY is auto-negotiating.
+ **/
+static s32 e1000_config_mac_to_phy_82543(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       s32 ret_val = E1000_SUCCESS;
+       u16 phy_data;
+
+       DEBUGFUNC("e1000_config_mac_to_phy_82543");
+
+       if (!(hw->phy.ops.read_reg))
+               goto out;
+
+       /* Set the bits to force speed and duplex */
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+       ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
+       ctrl &= ~(E1000_CTRL_SPD_SEL | E1000_CTRL_ILOS);
+
+       /*
+        * Set up duplex in the Device Control and Transmit Control
+        * registers depending on negotiated values.
+        */
+       ret_val = hw->phy.ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data);
+       if (ret_val)
+               goto out;
+
+       ctrl &= ~E1000_CTRL_FD;
+       if (phy_data & M88E1000_PSSR_DPLX)
+               ctrl |= E1000_CTRL_FD;
+
+       hw->mac.ops.config_collision_dist(hw);
+
+       /*
+        * Set up speed in the Device Control register depending on
+        * negotiated values.
+        */
+       if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS)
+               ctrl |= E1000_CTRL_SPD_1000;
+       else if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS)
+               ctrl |= E1000_CTRL_SPD_100;
+
+       E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_write_vfta_82543 - Write value to VLAN filter table
+ *  @hw: pointer to the HW structure
+ *  @offset: the 32-bit offset in which to write the value to.
+ *  @value: the 32-bit value to write at location offset.
+ *
+ *  This writes a 32-bit value to a 32-bit offset in the VLAN filter
+ *  table.
+ **/
+STATIC void e1000_write_vfta_82543(struct e1000_hw *hw, u32 offset, u32 value)
+{
+       u32 temp;
+
+       DEBUGFUNC("e1000_write_vfta_82543");
+
+       if ((hw->mac.type == e1000_82544) && (offset & 1)) {
+               temp = E1000_READ_REG_ARRAY(hw, E1000_VFTA, offset - 1);
+               E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, value);
+               E1000_WRITE_FLUSH(hw);
+               E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset - 1, temp);
+               E1000_WRITE_FLUSH(hw);
+       } else {
+               e1000_write_vfta_generic(hw, offset, value);
+       }
+}
+
+/**
+ *  e1000_led_on_82543 - Turn on SW controllable LED
+ *  @hw: pointer to the HW structure
+ *
+ *  Turns the SW defined LED on.
+ **/
+STATIC s32 e1000_led_on_82543(struct e1000_hw *hw)
+{
+       u32 ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       DEBUGFUNC("e1000_led_on_82543");
+
+       if (hw->mac.type == e1000_82544 &&
+           hw->phy.media_type == e1000_media_type_copper) {
+               /* Clear SW-definable Pin 0 to turn on the LED */
+               ctrl &= ~E1000_CTRL_SWDPIN0;
+               ctrl |= E1000_CTRL_SWDPIO0;
+       } else {
+               /* Fiber 82544 and all 82543 use this method */
+               ctrl |= E1000_CTRL_SWDPIN0;
+               ctrl |= E1000_CTRL_SWDPIO0;
+       }
+       E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_led_off_82543 - Turn off SW controllable LED
+ *  @hw: pointer to the HW structure
+ *
+ *  Turns the SW defined LED off.
+ **/
+STATIC s32 e1000_led_off_82543(struct e1000_hw *hw)
+{
+       u32 ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       DEBUGFUNC("e1000_led_off_82543");
+
+       if (hw->mac.type == e1000_82544 &&
+           hw->phy.media_type == e1000_media_type_copper) {
+               /* Set SW-definable Pin 0 to turn off the LED */
+               ctrl |= E1000_CTRL_SWDPIN0;
+               ctrl |= E1000_CTRL_SWDPIO0;
+       } else {
+               ctrl &= ~E1000_CTRL_SWDPIN0;
+               ctrl |= E1000_CTRL_SWDPIO0;
+       }
+       E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_clear_hw_cntrs_82543 - Clear device specific hardware counters
+ *  @hw: pointer to the HW structure
+ *
+ *  Clears the hardware counters by reading the counter registers.
+ **/
+STATIC void e1000_clear_hw_cntrs_82543(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_clear_hw_cntrs_82543");
+
+       e1000_clear_hw_cntrs_base_generic(hw);
+
+       E1000_READ_REG(hw, E1000_PRC64);
+       E1000_READ_REG(hw, E1000_PRC127);
+       E1000_READ_REG(hw, E1000_PRC255);
+       E1000_READ_REG(hw, E1000_PRC511);
+       E1000_READ_REG(hw, E1000_PRC1023);
+       E1000_READ_REG(hw, E1000_PRC1522);
+       E1000_READ_REG(hw, E1000_PTC64);
+       E1000_READ_REG(hw, E1000_PTC127);
+       E1000_READ_REG(hw, E1000_PTC255);
+       E1000_READ_REG(hw, E1000_PTC511);
+       E1000_READ_REG(hw, E1000_PTC1023);
+       E1000_READ_REG(hw, E1000_PTC1522);
+
+       E1000_READ_REG(hw, E1000_ALGNERRC);
+       E1000_READ_REG(hw, E1000_RXERRC);
+       E1000_READ_REG(hw, E1000_TNCRS);
+       E1000_READ_REG(hw, E1000_CEXTERR);
+       E1000_READ_REG(hw, E1000_TSCTC);
+       E1000_READ_REG(hw, E1000_TSCTFC);
+}
diff --git a/lib/librte_pmd_e1000/e1000/e1000_82543.h b/lib/librte_pmd_e1000/e1000/e1000_82543.h
new file mode 100644 (file)
index 0000000..7c2494c
--- /dev/null
@@ -0,0 +1,56 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#ifndef _E1000_82543_H_
+#define _E1000_82543_H_
+
+#define PHY_PREAMBLE           0xFFFFFFFF
+#define PHY_PREAMBLE_SIZE      32
+#define PHY_SOF                        0x1
+#define PHY_OP_READ            0x2
+#define PHY_OP_WRITE           0x1
+#define PHY_TURNAROUND         0x2
+
+#define TBI_COMPAT_ENABLED     0x1 /* Global "knob" for the workaround */
+/* If TBI_COMPAT_ENABLED, then this is the current state (on/off) */
+#define TBI_SBP_ENABLED                0x2
+
+void e1000_tbi_adjust_stats_82543(struct e1000_hw *hw,
+                                 struct e1000_hw_stats *stats,
+                                 u32 frame_len, u8 *mac_addr,
+                                 u32 max_frame_size);
+void e1000_set_tbi_compatibility_82543(struct e1000_hw *hw,
+                                      bool state);
+bool e1000_tbi_sbp_enabled_82543(struct e1000_hw *hw);
+
+#endif
diff --git a/lib/librte_pmd_e1000/e1000/e1000_82571.c b/lib/librte_pmd_e1000/e1000/e1000_82571.c
new file mode 100644 (file)
index 0000000..bd95bc9
--- /dev/null
@@ -0,0 +1,2063 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+/*
+ * 82571EB Gigabit Ethernet Controller
+ * 82571EB Gigabit Ethernet Controller (Copper)
+ * 82571EB Gigabit Ethernet Controller (Fiber)
+ * 82571EB Dual Port Gigabit Mezzanine Adapter
+ * 82571EB Quad Port Gigabit Mezzanine Adapter
+ * 82571PT Gigabit PT Quad Port Server ExpressModule
+ * 82572EI Gigabit Ethernet Controller (Copper)
+ * 82572EI Gigabit Ethernet Controller (Fiber)
+ * 82572EI Gigabit Ethernet Controller
+ * 82573V Gigabit Ethernet Controller (Copper)
+ * 82573E Gigabit Ethernet Controller (Copper)
+ * 82573L Gigabit Ethernet Controller
+ * 82574L Gigabit Network Connection
+ * 82583V Gigabit Network Connection
+ */
+
+#include "e1000_api.h"
+
+STATIC s32  e1000_init_phy_params_82571(struct e1000_hw *hw);
+STATIC s32  e1000_init_nvm_params_82571(struct e1000_hw *hw);
+STATIC s32  e1000_init_mac_params_82571(struct e1000_hw *hw);
+STATIC s32  e1000_acquire_nvm_82571(struct e1000_hw *hw);
+STATIC void e1000_release_nvm_82571(struct e1000_hw *hw);
+STATIC s32  e1000_write_nvm_82571(struct e1000_hw *hw, u16 offset,
+                                 u16 words, u16 *data);
+STATIC s32  e1000_update_nvm_checksum_82571(struct e1000_hw *hw);
+STATIC s32  e1000_validate_nvm_checksum_82571(struct e1000_hw *hw);
+STATIC s32  e1000_get_cfg_done_82571(struct e1000_hw *hw);
+STATIC s32  e1000_set_d0_lplu_state_82571(struct e1000_hw *hw,
+                                         bool active);
+STATIC s32  e1000_reset_hw_82571(struct e1000_hw *hw);
+STATIC s32  e1000_init_hw_82571(struct e1000_hw *hw);
+STATIC void e1000_clear_vfta_82571(struct e1000_hw *hw);
+STATIC bool e1000_check_mng_mode_82574(struct e1000_hw *hw);
+STATIC s32 e1000_led_on_82574(struct e1000_hw *hw);
+STATIC s32  e1000_setup_link_82571(struct e1000_hw *hw);
+STATIC s32  e1000_setup_copper_link_82571(struct e1000_hw *hw);
+STATIC s32  e1000_check_for_serdes_link_82571(struct e1000_hw *hw);
+STATIC s32  e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw);
+STATIC s32  e1000_valid_led_default_82571(struct e1000_hw *hw, u16 *data);
+STATIC void e1000_clear_hw_cntrs_82571(struct e1000_hw *hw);
+static s32  e1000_get_hw_semaphore_82571(struct e1000_hw *hw);
+static s32  e1000_fix_nvm_checksum_82571(struct e1000_hw *hw);
+static s32  e1000_get_phy_id_82571(struct e1000_hw *hw);
+static void e1000_put_hw_semaphore_82571(struct e1000_hw *hw);
+static s32  e1000_get_hw_semaphore_82573(struct e1000_hw *hw);
+static void e1000_put_hw_semaphore_82573(struct e1000_hw *hw);
+static s32  e1000_get_hw_semaphore_82574(struct e1000_hw *hw);
+static void e1000_put_hw_semaphore_82574(struct e1000_hw *hw);
+STATIC s32  e1000_set_d0_lplu_state_82574(struct e1000_hw *hw,
+                                         bool active);
+STATIC s32  e1000_set_d3_lplu_state_82574(struct e1000_hw *hw,
+                                         bool active);
+static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw);
+static s32  e1000_write_nvm_eewr_82571(struct e1000_hw *hw, u16 offset,
+                                      u16 words, u16 *data);
+STATIC s32  e1000_read_mac_addr_82571(struct e1000_hw *hw);
+STATIC void e1000_power_down_phy_copper_82571(struct e1000_hw *hw);
+
+/**
+ *  e1000_init_phy_params_82571 - Init PHY func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_phy_params_82571(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_init_phy_params_82571");
+
+       if (hw->phy.media_type != e1000_media_type_copper) {
+               phy->type = e1000_phy_none;
+               return E1000_SUCCESS;
+       }
+
+       phy->addr                       = 1;
+       phy->autoneg_mask               = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+       phy->reset_delay_us             = 100;
+
+       phy->ops.check_reset_block      = e1000_check_reset_block_generic;
+       phy->ops.reset                  = e1000_phy_hw_reset_generic;
+       phy->ops.set_d0_lplu_state      = e1000_set_d0_lplu_state_82571;
+       phy->ops.set_d3_lplu_state      = e1000_set_d3_lplu_state_generic;
+       phy->ops.power_up               = e1000_power_up_phy_copper;
+       phy->ops.power_down             = e1000_power_down_phy_copper_82571;
+
+       switch (hw->mac.type) {
+       case e1000_82571:
+       case e1000_82572:
+               phy->type               = e1000_phy_igp_2;
+               phy->ops.get_cfg_done   = e1000_get_cfg_done_82571;
+               phy->ops.get_info       = e1000_get_phy_info_igp;
+               phy->ops.check_polarity = e1000_check_polarity_igp;
+               phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_igp;
+               phy->ops.get_cable_length = e1000_get_cable_length_igp_2;
+               phy->ops.read_reg       = e1000_read_phy_reg_igp;
+               phy->ops.write_reg      = e1000_write_phy_reg_igp;
+               phy->ops.acquire        = e1000_get_hw_semaphore_82571;
+               phy->ops.release        = e1000_put_hw_semaphore_82571;
+               break;
+       case e1000_82573:
+               phy->type               = e1000_phy_m88;
+               phy->ops.get_cfg_done   = e1000_get_cfg_done_generic;
+               phy->ops.get_info       = e1000_get_phy_info_m88;
+               phy->ops.check_polarity = e1000_check_polarity_m88;
+               phy->ops.commit         = e1000_phy_sw_reset_generic;
+               phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88;
+               phy->ops.get_cable_length = e1000_get_cable_length_m88;
+               phy->ops.read_reg       = e1000_read_phy_reg_m88;
+               phy->ops.write_reg      = e1000_write_phy_reg_m88;
+               phy->ops.acquire        = e1000_get_hw_semaphore_82571;
+               phy->ops.release        = e1000_put_hw_semaphore_82571;
+               break;
+       case e1000_82574:
+       case e1000_82583:
+               E1000_MUTEX_INIT(&hw->dev_spec._82571.swflag_mutex);
+
+               phy->type               = e1000_phy_bm;
+               phy->ops.get_cfg_done   = e1000_get_cfg_done_generic;
+               phy->ops.get_info       = e1000_get_phy_info_m88;
+               phy->ops.check_polarity = e1000_check_polarity_m88;
+               phy->ops.commit         = e1000_phy_sw_reset_generic;
+               phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88;
+               phy->ops.get_cable_length = e1000_get_cable_length_m88;
+               phy->ops.read_reg       = e1000_read_phy_reg_bm2;
+               phy->ops.write_reg      = e1000_write_phy_reg_bm2;
+               phy->ops.acquire        = e1000_get_hw_semaphore_82574;
+               phy->ops.release        = e1000_put_hw_semaphore_82574;
+               phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82574;
+               phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_82574;
+               break;
+       default:
+               return -E1000_ERR_PHY;
+               break;
+       }
+
+       /* This can only be done after all function pointers are setup. */
+       ret_val = e1000_get_phy_id_82571(hw);
+       if (ret_val) {
+               DEBUGOUT("Error getting PHY ID\n");
+               return ret_val;
+       }
+
+       /* Verify phy id */
+       switch (hw->mac.type) {
+       case e1000_82571:
+       case e1000_82572:
+               if (phy->id != IGP01E1000_I_PHY_ID)
+                       ret_val = -E1000_ERR_PHY;
+               break;
+       case e1000_82573:
+               if (phy->id != M88E1111_I_PHY_ID)
+                       ret_val = -E1000_ERR_PHY;
+               break;
+       case e1000_82574:
+       case e1000_82583:
+               if (phy->id != BME1000_E_PHY_ID_R2)
+                       ret_val = -E1000_ERR_PHY;
+               break;
+       default:
+               ret_val = -E1000_ERR_PHY;
+               break;
+       }
+
+       if (ret_val)
+               DEBUGOUT1("PHY ID unknown: type = 0x%08x\n", phy->id);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_init_nvm_params_82571 - Init NVM func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_nvm_params_82571(struct e1000_hw *hw)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       u32 eecd = E1000_READ_REG(hw, E1000_EECD);
+       u16 size;
+
+       DEBUGFUNC("e1000_init_nvm_params_82571");
+
+       nvm->opcode_bits = 8;
+       nvm->delay_usec = 1;
+       switch (nvm->override) {
+       case e1000_nvm_override_spi_large:
+               nvm->page_size = 32;
+               nvm->address_bits = 16;
+               break;
+       case e1000_nvm_override_spi_small:
+               nvm->page_size = 8;
+               nvm->address_bits = 8;
+               break;
+       default:
+               nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8;
+               nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ? 16 : 8;
+               break;
+       }
+
+       switch (hw->mac.type) {
+       case e1000_82573:
+       case e1000_82574:
+       case e1000_82583:
+               if (((eecd >> 15) & 0x3) == 0x3) {
+                       nvm->type = e1000_nvm_flash_hw;
+                       nvm->word_size = 2048;
+                       /*
+                        * Autonomous Flash update bit must be cleared due
+                        * to Flash update issue.
+                        */
+                       eecd &= ~E1000_EECD_AUPDEN;
+                       E1000_WRITE_REG(hw, E1000_EECD, eecd);
+                       break;
+               }
+               /* Fall Through */
+       default:
+               nvm->type = e1000_nvm_eeprom_spi;
+               size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >>
+                            E1000_EECD_SIZE_EX_SHIFT);
+               /*
+                * Added to a constant, "size" becomes the left-shift value
+                * for setting word_size.
+                */
+               size += NVM_WORD_SIZE_BASE_SHIFT;
+
+               /* EEPROM access above 16k is unsupported */
+               if (size > 14)
+                       size = 14;
+               nvm->word_size = 1 << size;
+               break;
+       }
+
+       /* Function Pointers */
+       switch (hw->mac.type) {
+       case e1000_82574:
+       case e1000_82583:
+               nvm->ops.acquire = e1000_get_hw_semaphore_82574;
+               nvm->ops.release = e1000_put_hw_semaphore_82574;
+               break;
+       default:
+               nvm->ops.acquire = e1000_acquire_nvm_82571;
+               nvm->ops.release = e1000_release_nvm_82571;
+               break;
+       }
+       nvm->ops.read = e1000_read_nvm_eerd;
+       nvm->ops.update = e1000_update_nvm_checksum_82571;
+       nvm->ops.validate = e1000_validate_nvm_checksum_82571;
+       nvm->ops.valid_led_default = e1000_valid_led_default_82571;
+       nvm->ops.write = e1000_write_nvm_82571;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_mac_params_82571 - Init MAC func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_mac_params_82571(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       u32 swsm = 0;
+       u32 swsm2 = 0;
+       bool force_clear_smbi = false;
+
+       DEBUGFUNC("e1000_init_mac_params_82571");
+
+       /* Set media type and media-dependent function pointers */
+       switch (hw->device_id) {
+       case E1000_DEV_ID_82571EB_FIBER:
+       case E1000_DEV_ID_82572EI_FIBER:
+       case E1000_DEV_ID_82571EB_QUAD_FIBER:
+               hw->phy.media_type = e1000_media_type_fiber;
+               mac->ops.setup_physical_interface =
+                       e1000_setup_fiber_serdes_link_82571;
+               mac->ops.check_for_link = e1000_check_for_fiber_link_generic;
+               mac->ops.get_link_up_info =
+                       e1000_get_speed_and_duplex_fiber_serdes_generic;
+               break;
+       case E1000_DEV_ID_82571EB_SERDES:
+       case E1000_DEV_ID_82571EB_SERDES_DUAL:
+       case E1000_DEV_ID_82571EB_SERDES_QUAD:
+       case E1000_DEV_ID_82572EI_SERDES:
+               hw->phy.media_type = e1000_media_type_internal_serdes;
+               mac->ops.setup_physical_interface =
+                       e1000_setup_fiber_serdes_link_82571;
+               mac->ops.check_for_link = e1000_check_for_serdes_link_82571;
+               mac->ops.get_link_up_info =
+                       e1000_get_speed_and_duplex_fiber_serdes_generic;
+               break;
+       default:
+               hw->phy.media_type = e1000_media_type_copper;
+               mac->ops.setup_physical_interface =
+                       e1000_setup_copper_link_82571;
+               mac->ops.check_for_link = e1000_check_for_copper_link_generic;
+               mac->ops.get_link_up_info =
+                       e1000_get_speed_and_duplex_copper_generic;
+               break;
+       }
+
+       /* Set mta register count */
+       mac->mta_reg_count = 128;
+       /* Set rar entry count */
+       mac->rar_entry_count = E1000_RAR_ENTRIES;
+       /* Set if part includes ASF firmware */
+       mac->asf_firmware_present = true;
+       /* Adaptive IFS supported */
+       mac->adaptive_ifs = true;
+
+       /* Function pointers */
+
+       /* bus type/speed/width */
+       mac->ops.get_bus_info = e1000_get_bus_info_pcie_generic;
+       /* reset */
+       mac->ops.reset_hw = e1000_reset_hw_82571;
+       /* hw initialization */
+       mac->ops.init_hw = e1000_init_hw_82571;
+       /* link setup */
+       mac->ops.setup_link = e1000_setup_link_82571;
+       /* multicast address update */
+       mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
+       /* writing VFTA */
+       mac->ops.write_vfta = e1000_write_vfta_generic;
+       /* clearing VFTA */
+       mac->ops.clear_vfta = e1000_clear_vfta_82571;
+       /* read mac address */
+       mac->ops.read_mac_addr = e1000_read_mac_addr_82571;
+       /* ID LED init */
+       mac->ops.id_led_init = e1000_id_led_init_generic;
+       /* setup LED */
+       mac->ops.setup_led = e1000_setup_led_generic;
+       /* cleanup LED */
+       mac->ops.cleanup_led = e1000_cleanup_led_generic;
+       /* turn off LED */
+       mac->ops.led_off = e1000_led_off_generic;
+       /* clear hardware counters */
+       mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_82571;
+
+       /* MAC-specific function pointers */
+       switch (hw->mac.type) {
+       case e1000_82573:
+               mac->ops.set_lan_id = e1000_set_lan_id_single_port;
+               mac->ops.check_mng_mode = e1000_check_mng_mode_generic;
+               mac->ops.led_on = e1000_led_on_generic;
+               mac->ops.blink_led = e1000_blink_led_generic;
+
+               /* FWSM register */
+               mac->has_fwsm = true;
+               /*
+                * ARC supported; valid only if manageability features are
+                * enabled.
+                */
+               mac->arc_subsystem_valid = !!(E1000_READ_REG(hw, E1000_FWSM) &
+                                             E1000_FWSM_MODE_MASK);
+               break;
+       case e1000_82574:
+       case e1000_82583:
+               mac->ops.set_lan_id = e1000_set_lan_id_single_port;
+               mac->ops.check_mng_mode = e1000_check_mng_mode_82574;
+               mac->ops.led_on = e1000_led_on_82574;
+               break;
+       default:
+               mac->ops.check_mng_mode = e1000_check_mng_mode_generic;
+               mac->ops.led_on = e1000_led_on_generic;
+               mac->ops.blink_led = e1000_blink_led_generic;
+
+               /* FWSM register */
+               mac->has_fwsm = true;
+               break;
+       }
+
+       /*
+        * Ensure that the inter-port SWSM.SMBI lock bit is clear before
+        * first NVM or PHY acess. This should be done for single-port
+        * devices, and for one port only on dual-port devices so that
+        * for those devices we can still use the SMBI lock to synchronize
+        * inter-port accesses to the PHY & NVM.
+        */
+       switch (hw->mac.type) {
+       case e1000_82571:
+       case e1000_82572:
+               swsm2 = E1000_READ_REG(hw, E1000_SWSM2);
+
+               if (!(swsm2 & E1000_SWSM2_LOCK)) {
+                       /* Only do this for the first interface on this card */
+                       E1000_WRITE_REG(hw, E1000_SWSM2, swsm2 |
+                                       E1000_SWSM2_LOCK);
+                       force_clear_smbi = true;
+               } else {
+                       force_clear_smbi = false;
+               }
+               break;
+       default:
+               force_clear_smbi = true;
+               break;
+       }
+
+       if (force_clear_smbi) {
+               /* Make sure SWSM.SMBI is clear */
+               swsm = E1000_READ_REG(hw, E1000_SWSM);
+               if (swsm & E1000_SWSM_SMBI) {
+                       /* This bit should not be set on a first interface, and
+                        * indicates that the bootagent or EFI code has
+                        * improperly left this bit enabled
+                        */
+                       DEBUGOUT("Please update your 82571 Bootagent\n");
+               }
+               E1000_WRITE_REG(hw, E1000_SWSM, swsm & ~E1000_SWSM_SMBI);
+       }
+
+       /*
+        * Initialze device specific counter of SMBI acquisition
+        * timeouts.
+        */
+        hw->dev_spec._82571.smb_counter = 0;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_function_pointers_82571 - Init func ptrs.
+ *  @hw: pointer to the HW structure
+ *
+ *  Called to initialize all function pointers and parameters.
+ **/
+void e1000_init_function_pointers_82571(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_init_function_pointers_82571");
+
+       hw->mac.ops.init_params = e1000_init_mac_params_82571;
+       hw->nvm.ops.init_params = e1000_init_nvm_params_82571;
+       hw->phy.ops.init_params = e1000_init_phy_params_82571;
+}
+
+/**
+ *  e1000_get_phy_id_82571 - Retrieve the PHY ID and revision
+ *  @hw: pointer to the HW structure
+ *
+ *  Reads the PHY registers and stores the PHY ID and possibly the PHY
+ *  revision in the hardware structure.
+ **/
+static s32 e1000_get_phy_id_82571(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val;
+       u16 phy_id = 0;
+
+       DEBUGFUNC("e1000_get_phy_id_82571");
+
+       switch (hw->mac.type) {
+       case e1000_82571:
+       case e1000_82572:
+               /*
+                * The 82571 firmware may still be configuring the PHY.
+                * In this case, we cannot access the PHY until the
+                * configuration is done.  So we explicitly set the
+                * PHY ID.
+                */
+               phy->id = IGP01E1000_I_PHY_ID;
+               break;
+       case e1000_82573:
+               return e1000_get_phy_id(hw);
+               break;
+       case e1000_82574:
+       case e1000_82583:
+               ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id);
+               if (ret_val)
+                       return ret_val;
+
+               phy->id = (u32)(phy_id << 16);
+               usec_delay(20);
+               ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id);
+               if (ret_val)
+                       return ret_val;
+
+               phy->id |= (u32)(phy_id);
+               phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK);
+               break;
+       default:
+               return -E1000_ERR_PHY;
+               break;
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_get_hw_semaphore_82571 - Acquire hardware semaphore
+ *  @hw: pointer to the HW structure
+ *
+ *  Acquire the HW semaphore to access the PHY or NVM
+ **/
+static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw)
+{
+       u32 swsm;
+       s32 sw_timeout = hw->nvm.word_size + 1;
+       s32 fw_timeout = hw->nvm.word_size + 1;
+       s32 i = 0;
+
+       DEBUGFUNC("e1000_get_hw_semaphore_82571");
+
+       /*
+        * If we have timedout 3 times on trying to acquire
+        * the inter-port SMBI semaphore, there is old code
+        * operating on the other port, and it is not
+        * releasing SMBI. Modify the number of times that
+        * we try for the semaphore to interwork with this
+        * older code.
+        */
+       if (hw->dev_spec._82571.smb_counter > 2)
+               sw_timeout = 1;
+
+       /* Get the SW semaphore */
+       while (i < sw_timeout) {
+               swsm = E1000_READ_REG(hw, E1000_SWSM);
+               if (!(swsm & E1000_SWSM_SMBI))
+                       break;
+
+               usec_delay(50);
+               i++;
+       }
+
+       if (i == sw_timeout) {
+               DEBUGOUT("Driver can't access device - SMBI bit is set.\n");
+               hw->dev_spec._82571.smb_counter++;
+       }
+       /* Get the FW semaphore. */
+       for (i = 0; i < fw_timeout; i++) {
+               swsm = E1000_READ_REG(hw, E1000_SWSM);
+               E1000_WRITE_REG(hw, E1000_SWSM, swsm | E1000_SWSM_SWESMBI);
+
+               /* Semaphore acquired if bit latched */
+               if (E1000_READ_REG(hw, E1000_SWSM) & E1000_SWSM_SWESMBI)
+                       break;
+
+               usec_delay(50);
+       }
+
+       if (i == fw_timeout) {
+               /* Release semaphores */
+               e1000_put_hw_semaphore_82571(hw);
+               DEBUGOUT("Driver can't access the NVM\n");
+               return -E1000_ERR_NVM;
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_put_hw_semaphore_82571 - Release hardware semaphore
+ *  @hw: pointer to the HW structure
+ *
+ *  Release hardware semaphore used to access the PHY or NVM
+ **/
+static void e1000_put_hw_semaphore_82571(struct e1000_hw *hw)
+{
+       u32 swsm;
+
+       DEBUGFUNC("e1000_put_hw_semaphore_generic");
+
+       swsm = E1000_READ_REG(hw, E1000_SWSM);
+
+       swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI);
+
+       E1000_WRITE_REG(hw, E1000_SWSM, swsm);
+}
+
+/**
+ *  e1000_get_hw_semaphore_82573 - Acquire hardware semaphore
+ *  @hw: pointer to the HW structure
+ *
+ *  Acquire the HW semaphore during reset.
+ *
+ **/
+static s32 e1000_get_hw_semaphore_82573(struct e1000_hw *hw)
+{
+       u32 extcnf_ctrl;
+       s32 i = 0;
+
+       DEBUGFUNC("e1000_get_hw_semaphore_82573");
+
+       extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+       extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP;
+       do {
+               E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
+               extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+
+               if (extcnf_ctrl & E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP)
+                       break;
+
+               extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP;
+
+               msec_delay(2);
+               i++;
+       } while (i < MDIO_OWNERSHIP_TIMEOUT);
+
+       if (i == MDIO_OWNERSHIP_TIMEOUT) {
+               /* Release semaphores */
+               e1000_put_hw_semaphore_82573(hw);
+               DEBUGOUT("Driver can't access the PHY\n");
+               return -E1000_ERR_PHY;
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_put_hw_semaphore_82573 - Release hardware semaphore
+ *  @hw: pointer to the HW structure
+ *
+ *  Release hardware semaphore used during reset.
+ *
+ **/
+static void e1000_put_hw_semaphore_82573(struct e1000_hw *hw)
+{
+       u32 extcnf_ctrl;
+
+       DEBUGFUNC("e1000_put_hw_semaphore_82573");
+
+       extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+       extcnf_ctrl &= ~E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP;
+       E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
+}
+
+/**
+ *  e1000_get_hw_semaphore_82574 - Acquire hardware semaphore
+ *  @hw: pointer to the HW structure
+ *
+ *  Acquire the HW semaphore to access the PHY or NVM.
+ *
+ **/
+static s32 e1000_get_hw_semaphore_82574(struct e1000_hw *hw)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_get_hw_semaphore_82574");
+
+       E1000_MUTEX_LOCK(&hw->dev_spec._82571.swflag_mutex);
+       ret_val = e1000_get_hw_semaphore_82573(hw);
+       if (ret_val)
+               E1000_MUTEX_UNLOCK(&hw->dev_spec._82571.swflag_mutex);
+       return ret_val;
+}
+
+/**
+ *  e1000_put_hw_semaphore_82574 - Release hardware semaphore
+ *  @hw: pointer to the HW structure
+ *
+ *  Release hardware semaphore used to access the PHY or NVM
+ *
+ **/
+static void e1000_put_hw_semaphore_82574(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_put_hw_semaphore_82574");
+
+       e1000_put_hw_semaphore_82573(hw);
+       E1000_MUTEX_UNLOCK(&hw->dev_spec._82571.swflag_mutex);
+}
+
+/**
+ *  e1000_set_d0_lplu_state_82574 - Set Low Power Linkup D0 state
+ *  @hw: pointer to the HW structure
+ *  @active: true to enable LPLU, false to disable
+ *
+ *  Sets the LPLU D0 state according to the active flag.
+ *  LPLU will not be activated unless the
+ *  device autonegotiation advertisement meets standards of
+ *  either 10 or 10/100 or 10/100/1000 at all duplexes.
+ *  This is a function pointer entry point only called by
+ *  PHY setup routines.
+ **/
+STATIC s32 e1000_set_d0_lplu_state_82574(struct e1000_hw *hw, bool active)
+{
+       u32 data = E1000_READ_REG(hw, E1000_POEMB);
+
+       DEBUGFUNC("e1000_set_d0_lplu_state_82574");
+
+       if (active)
+               data |= E1000_PHY_CTRL_D0A_LPLU;
+       else
+               data &= ~E1000_PHY_CTRL_D0A_LPLU;
+
+       E1000_WRITE_REG(hw, E1000_POEMB, data);
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_set_d3_lplu_state_82574 - Sets low power link up state for D3
+ *  @hw: pointer to the HW structure
+ *  @active: boolean used to enable/disable lplu
+ *
+ *  The low power link up (lplu) state is set to the power management level D3
+ *  when active is true, else clear lplu for D3. LPLU
+ *  is used during Dx states where the power conservation is most important.
+ *  During driver activity, SmartSpeed should be enabled so performance is
+ *  maintained.
+ **/
+STATIC s32 e1000_set_d3_lplu_state_82574(struct e1000_hw *hw, bool active)
+{
+       u32 data = E1000_READ_REG(hw, E1000_POEMB);
+
+       DEBUGFUNC("e1000_set_d3_lplu_state_82574");
+
+       if (!active) {
+               data &= ~E1000_PHY_CTRL_NOND0A_LPLU;
+       } else if ((hw->phy.autoneg_advertised == E1000_ALL_SPEED_DUPLEX) ||
+                  (hw->phy.autoneg_advertised == E1000_ALL_NOT_GIG) ||
+                  (hw->phy.autoneg_advertised == E1000_ALL_10_SPEED)) {
+               data |= E1000_PHY_CTRL_NOND0A_LPLU;
+       }
+
+       E1000_WRITE_REG(hw, E1000_POEMB, data);
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_acquire_nvm_82571 - Request for access to the EEPROM
+ *  @hw: pointer to the HW structure
+ *
+ *  To gain access to the EEPROM, first we must obtain a hardware semaphore.
+ *  Then for non-82573 hardware, set the EEPROM access request bit and wait
+ *  for EEPROM access grant bit.  If the access grant bit is not set, release
+ *  hardware semaphore.
+ **/
+STATIC s32 e1000_acquire_nvm_82571(struct e1000_hw *hw)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_acquire_nvm_82571");
+
+       ret_val = e1000_get_hw_semaphore_82571(hw);
+       if (ret_val)
+               return ret_val;
+
+       switch (hw->mac.type) {
+       case e1000_82573:
+               break;
+       default:
+               ret_val = e1000_acquire_nvm_generic(hw);
+               break;
+       }
+
+       if (ret_val)
+               e1000_put_hw_semaphore_82571(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_release_nvm_82571 - Release exclusive access to EEPROM
+ *  @hw: pointer to the HW structure
+ *
+ *  Stop any current commands to the EEPROM and clear the EEPROM request bit.
+ **/
+STATIC void e1000_release_nvm_82571(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_release_nvm_82571");
+
+       e1000_release_nvm_generic(hw);
+       e1000_put_hw_semaphore_82571(hw);
+}
+
+/**
+ *  e1000_write_nvm_82571 - Write to EEPROM using appropriate interface
+ *  @hw: pointer to the HW structure
+ *  @offset: offset within the EEPROM to be written to
+ *  @words: number of words to write
+ *  @data: 16 bit word(s) to be written to the EEPROM
+ *
+ *  For non-82573 silicon, write data to EEPROM at offset using SPI interface.
+ *
+ *  If e1000_update_nvm_checksum is not called after this function, the
+ *  EEPROM will most likely contain an invalid checksum.
+ **/
+STATIC s32 e1000_write_nvm_82571(struct e1000_hw *hw, u16 offset, u16 words,
+                                u16 *data)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_write_nvm_82571");
+
+       switch (hw->mac.type) {
+       case e1000_82573:
+       case e1000_82574:
+       case e1000_82583:
+               ret_val = e1000_write_nvm_eewr_82571(hw, offset, words, data);
+               break;
+       case e1000_82571:
+       case e1000_82572:
+               ret_val = e1000_write_nvm_spi(hw, offset, words, data);
+               break;
+       default:
+               ret_val = -E1000_ERR_NVM;
+               break;
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_update_nvm_checksum_82571 - Update EEPROM checksum
+ *  @hw: pointer to the HW structure
+ *
+ *  Updates the EEPROM checksum by reading/adding each word of the EEPROM
+ *  up to the checksum.  Then calculates the EEPROM checksum and writes the
+ *  value to the EEPROM.
+ **/
+STATIC s32 e1000_update_nvm_checksum_82571(struct e1000_hw *hw)
+{
+       u32 eecd;
+       s32 ret_val;
+       u16 i;
+
+       DEBUGFUNC("e1000_update_nvm_checksum_82571");
+
+       ret_val = e1000_update_nvm_checksum_generic(hw);
+       if (ret_val)
+               return ret_val;
+
+       /*
+        * If our nvm is an EEPROM, then we're done
+        * otherwise, commit the checksum to the flash NVM.
+        */
+       if (hw->nvm.type != e1000_nvm_flash_hw)
+               return E1000_SUCCESS;
+
+       /* Check for pending operations. */
+       for (i = 0; i < E1000_FLASH_UPDATES; i++) {
+               msec_delay(1);
+               if (!(E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_FLUPD))
+                       break;
+       }
+
+       if (i == E1000_FLASH_UPDATES)
+               return -E1000_ERR_NVM;
+
+       /* Reset the firmware if using STM opcode. */
+       if ((E1000_READ_REG(hw, E1000_FLOP) & 0xFF00) == E1000_STM_OPCODE) {
+               /*
+                * The enabling of and the actual reset must be done
+                * in two write cycles.
+                */
+               E1000_WRITE_REG(hw, E1000_HICR, E1000_HICR_FW_RESET_ENABLE);
+               E1000_WRITE_FLUSH(hw);
+               E1000_WRITE_REG(hw, E1000_HICR, E1000_HICR_FW_RESET);
+       }
+
+       /* Commit the write to flash */
+       eecd = E1000_READ_REG(hw, E1000_EECD) | E1000_EECD_FLUPD;
+       E1000_WRITE_REG(hw, E1000_EECD, eecd);
+
+       for (i = 0; i < E1000_FLASH_UPDATES; i++) {
+               msec_delay(1);
+               if (!(E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_FLUPD))
+                       break;
+       }
+
+       if (i == E1000_FLASH_UPDATES)
+               return -E1000_ERR_NVM;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_validate_nvm_checksum_82571 - Validate EEPROM checksum
+ *  @hw: pointer to the HW structure
+ *
+ *  Calculates the EEPROM checksum by reading/adding each word of the EEPROM
+ *  and then verifies that the sum of the EEPROM is equal to 0xBABA.
+ **/
+STATIC s32 e1000_validate_nvm_checksum_82571(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_validate_nvm_checksum_82571");
+
+       if (hw->nvm.type == e1000_nvm_flash_hw)
+               e1000_fix_nvm_checksum_82571(hw);
+
+       return e1000_validate_nvm_checksum_generic(hw);
+}
+
+/**
+ *  e1000_write_nvm_eewr_82571 - Write to EEPROM for 82573 silicon
+ *  @hw: pointer to the HW structure
+ *  @offset: offset within the EEPROM to be written to
+ *  @words: number of words to write
+ *  @data: 16 bit word(s) to be written to the EEPROM
+ *
+ *  After checking for invalid values, poll the EEPROM to ensure the previous
+ *  command has completed before trying to write the next word.  After write
+ *  poll for completion.
+ *
+ *  If e1000_update_nvm_checksum is not called after this function, the
+ *  EEPROM will most likely contain an invalid checksum.
+ **/
+static s32 e1000_write_nvm_eewr_82571(struct e1000_hw *hw, u16 offset,
+                                     u16 words, u16 *data)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       u32 i, eewr = 0;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_write_nvm_eewr_82571");
+
+       /*
+        * A check for invalid values:  offset too large, too many words,
+        * and not enough words.
+        */
+       if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
+           (words == 0)) {
+               DEBUGOUT("nvm parameter(s) out of bounds\n");
+               return -E1000_ERR_NVM;
+       }
+
+       for (i = 0; i < words; i++) {
+               eewr = (data[i] << E1000_NVM_RW_REG_DATA) |
+                      ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) |
+                      E1000_NVM_RW_REG_START;
+
+               ret_val = e1000_poll_eerd_eewr_done(hw, E1000_NVM_POLL_WRITE);
+               if (ret_val)
+                       break;
+
+               E1000_WRITE_REG(hw, E1000_EEWR, eewr);
+
+               ret_val = e1000_poll_eerd_eewr_done(hw, E1000_NVM_POLL_WRITE);
+               if (ret_val)
+                       break;
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_get_cfg_done_82571 - Poll for configuration done
+ *  @hw: pointer to the HW structure
+ *
+ *  Reads the management control register for the config done bit to be set.
+ **/
+STATIC s32 e1000_get_cfg_done_82571(struct e1000_hw *hw)
+{
+       s32 timeout = PHY_CFG_TIMEOUT;
+
+       DEBUGFUNC("e1000_get_cfg_done_82571");
+
+       while (timeout) {
+               if (E1000_READ_REG(hw, E1000_EEMNGCTL) &
+                   E1000_NVM_CFG_DONE_PORT_0)
+                       break;
+               msec_delay(1);
+               timeout--;
+       }
+       if (!timeout) {
+               DEBUGOUT("MNG configuration cycle has not completed.\n");
+               return -E1000_ERR_RESET;
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_set_d0_lplu_state_82571 - Set Low Power Linkup D0 state
+ *  @hw: pointer to the HW structure
+ *  @active: true to enable LPLU, false to disable
+ *
+ *  Sets the LPLU D0 state according to the active flag.  When activating LPLU
+ *  this function also disables smart speed and vice versa.  LPLU will not be
+ *  activated unless the device autonegotiation advertisement meets standards
+ *  of either 10 or 10/100 or 10/100/1000 at all duplexes.  This is a function
+ *  pointer entry point only called by PHY setup routines.
+ **/
+STATIC s32 e1000_set_d0_lplu_state_82571(struct e1000_hw *hw, bool active)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val;
+       u16 data;
+
+       DEBUGFUNC("e1000_set_d0_lplu_state_82571");
+
+       if (!(phy->ops.read_reg))
+               return E1000_SUCCESS;
+
+       ret_val = phy->ops.read_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data);
+       if (ret_val)
+               return ret_val;
+
+       if (active) {
+               data |= IGP02E1000_PM_D0_LPLU;
+               ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT,
+                                            data);
+               if (ret_val)
+                       return ret_val;
+
+               /* When LPLU is enabled, we should disable SmartSpeed */
+               ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+                                           &data);
+               data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+               ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
+                                            data);
+               if (ret_val)
+                       return ret_val;
+       } else {
+               data &= ~IGP02E1000_PM_D0_LPLU;
+               ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT,
+                                            data);
+               /*
+                * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
+                * during Dx states where the power conservation is most
+                * important.  During driver activity we should enable
+                * SmartSpeed, so performance is maintained.
+                */
+               if (phy->smart_speed == e1000_smart_speed_on) {
+                       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,
+                                                    data);
+                       if (ret_val)
+                               return ret_val;
+               } else if (phy->smart_speed == e1000_smart_speed_off) {
+                       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,
+                                                    data);
+                       if (ret_val)
+                               return ret_val;
+               }
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_reset_hw_82571 - Reset hardware
+ *  @hw: pointer to the HW structure
+ *
+ *  This resets the hardware into a known state.
+ **/
+STATIC s32 e1000_reset_hw_82571(struct e1000_hw *hw)
+{
+       u32 ctrl, ctrl_ext, eecd, tctl;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_reset_hw_82571");
+
+       /*
+        * Prevent the PCI-E bus from sticking if there is no TLP connection
+        * on the last TLP read/write transaction when MAC is reset.
+        */
+       ret_val = e1000_disable_pcie_master_generic(hw);
+       if (ret_val)
+               DEBUGOUT("PCI-E Master disable polling has failed.\n");
+
+       DEBUGOUT("Masking off all interrupts\n");
+       E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+
+       E1000_WRITE_REG(hw, E1000_RCTL, 0);
+       tctl = E1000_READ_REG(hw, E1000_TCTL);
+       tctl &= ~E1000_TCTL_EN;
+       E1000_WRITE_REG(hw, E1000_TCTL, tctl);
+       E1000_WRITE_FLUSH(hw);
+
+       msec_delay(10);
+
+       /*
+        * Must acquire the MDIO ownership before MAC reset.
+        * Ownership defaults to firmware after a reset.
+        */
+       switch (hw->mac.type) {
+       case e1000_82573:
+               ret_val = e1000_get_hw_semaphore_82573(hw);
+               break;
+       case e1000_82574:
+       case e1000_82583:
+               ret_val = e1000_get_hw_semaphore_82574(hw);
+               break;
+       default:
+               break;
+       }
+       if (ret_val)
+               DEBUGOUT("Cannot acquire MDIO ownership\n");
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       DEBUGOUT("Issuing a global reset to MAC\n");
+       E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST);
+
+       /* Must release MDIO ownership and mutex after MAC reset. */
+       switch (hw->mac.type) {
+       case e1000_82574:
+       case e1000_82583:
+               e1000_put_hw_semaphore_82574(hw);
+               break;
+       default:
+               break;
+       }
+
+       if (hw->nvm.type == e1000_nvm_flash_hw) {
+               usec_delay(10);
+               ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
+               ctrl_ext |= E1000_CTRL_EXT_EE_RST;
+               E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+               E1000_WRITE_FLUSH(hw);
+       }
+
+       ret_val = e1000_get_auto_rd_done_generic(hw);
+       if (ret_val)
+               /* We don't want to continue accessing MAC registers. */
+               return ret_val;
+
+       /*
+        * Phy configuration from NVM just starts after EECD_AUTO_RD is set.
+        * Need to wait for Phy configuration completion before accessing
+        * NVM and Phy.
+        */
+
+       switch (hw->mac.type) {
+       case e1000_82571:
+       case e1000_82572:
+               /*
+                * REQ and GNT bits need to be cleared when using AUTO_RD
+                * to access the EEPROM.
+                */
+               eecd = E1000_READ_REG(hw, E1000_EECD);
+               eecd &= ~(E1000_EECD_REQ | E1000_EECD_GNT);
+               E1000_WRITE_REG(hw, E1000_EECD, eecd);
+               break;
+       case e1000_82573:
+       case e1000_82574:
+       case e1000_82583:
+               msec_delay(25);
+               break;
+       default:
+               break;
+       }
+
+       /* Clear any pending interrupt events. */
+       E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+       E1000_READ_REG(hw, E1000_ICR);
+
+       if (hw->mac.type == e1000_82571) {
+               /* Install any alternate MAC address into RAR0 */
+               ret_val = e1000_check_alt_mac_addr_generic(hw);
+               if (ret_val)
+                       return ret_val;
+
+               e1000_set_laa_state_82571(hw, true);
+       }
+
+       /* Reinitialize the 82571 serdes link state machine */
+       if (hw->phy.media_type == e1000_media_type_internal_serdes)
+               hw->mac.serdes_link_state = e1000_serdes_link_down;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_hw_82571 - Initialize hardware
+ *  @hw: pointer to the HW structure
+ *
+ *  This inits the hardware readying it for operation.
+ **/
+STATIC s32 e1000_init_hw_82571(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       u32 reg_data;
+       s32 ret_val;
+       u16 i, rar_count = mac->rar_entry_count;
+
+       DEBUGFUNC("e1000_init_hw_82571");
+
+       e1000_initialize_hw_bits_82571(hw);
+
+       /* Initialize identification LED */
+       ret_val = mac->ops.id_led_init(hw);
+       if (ret_val)
+               DEBUGOUT("Error initializing identification LED\n");
+               /* This is not fatal and we should not stop init due to this */
+
+       /* Disabling VLAN filtering */
+       DEBUGOUT("Initializing the IEEE VLAN\n");
+       mac->ops.clear_vfta(hw);
+
+       /* Setup the receive address. */
+       /*
+        * If, however, a locally administered address was assigned to the
+        * 82571, we must reserve a RAR for it to work around an issue where
+        * resetting one port will reload the MAC on the other port.
+        */
+       if (e1000_get_laa_state_82571(hw))
+               rar_count--;
+       e1000_init_rx_addrs_generic(hw, rar_count);
+
+       /* Zero out the Multicast HASH table */
+       DEBUGOUT("Zeroing the MTA\n");
+       for (i = 0; i < mac->mta_reg_count; i++)
+               E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
+
+       /* Setup link and flow control */
+       ret_val = mac->ops.setup_link(hw);
+
+       /* Set the transmit descriptor write-back policy */
+       reg_data = E1000_READ_REG(hw, E1000_TXDCTL(0));
+       reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) |
+                  E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC;
+       E1000_WRITE_REG(hw, E1000_TXDCTL(0), reg_data);
+
+       /* ...for both queues. */
+       switch (mac->type) {
+       case e1000_82573:
+               e1000_enable_tx_pkt_filtering_generic(hw);
+               /* fall through */
+       case e1000_82574:
+       case e1000_82583:
+               reg_data = E1000_READ_REG(hw, E1000_GCR);
+               reg_data |= E1000_GCR_L1_ACT_WITHOUT_L0S_RX;
+               E1000_WRITE_REG(hw, E1000_GCR, reg_data);
+               break;
+       default:
+               reg_data = E1000_READ_REG(hw, E1000_TXDCTL(1));
+               reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) |
+                          E1000_TXDCTL_FULL_TX_DESC_WB |
+                          E1000_TXDCTL_COUNT_DESC;
+               E1000_WRITE_REG(hw, E1000_TXDCTL(1), reg_data);
+               break;
+       }
+
+       /*
+        * Clear all of the statistics registers (clear on read).  It is
+        * important that we do this after we have tried to establish link
+        * because the symbol error count will increment wildly if there
+        * is no link.
+        */
+       e1000_clear_hw_cntrs_82571(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_initialize_hw_bits_82571 - Initialize hardware-dependent bits
+ *  @hw: pointer to the HW structure
+ *
+ *  Initializes required hardware-dependent bits needed for normal operation.
+ **/
+static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw)
+{
+       u32 reg;
+
+       DEBUGFUNC("e1000_initialize_hw_bits_82571");
+
+       /* Transmit Descriptor Control 0 */
+       reg = E1000_READ_REG(hw, E1000_TXDCTL(0));
+       reg |= (1 << 22);
+       E1000_WRITE_REG(hw, E1000_TXDCTL(0), reg);
+
+       /* Transmit Descriptor Control 1 */
+       reg = E1000_READ_REG(hw, E1000_TXDCTL(1));
+       reg |= (1 << 22);
+       E1000_WRITE_REG(hw, E1000_TXDCTL(1), reg);
+
+       /* Transmit Arbitration Control 0 */
+       reg = E1000_READ_REG(hw, E1000_TARC(0));
+       reg &= ~(0xF << 27); /* 30:27 */
+       switch (hw->mac.type) {
+       case e1000_82571:
+       case e1000_82572:
+               reg |= (1 << 23) | (1 << 24) | (1 << 25) | (1 << 26);
+               break;
+       case e1000_82574:
+       case e1000_82583:
+               reg |= (1 << 26);
+               break;
+       default:
+               break;
+       }
+       E1000_WRITE_REG(hw, E1000_TARC(0), reg);
+
+       /* Transmit Arbitration Control 1 */
+       reg = E1000_READ_REG(hw, E1000_TARC(1));
+       switch (hw->mac.type) {
+       case e1000_82571:
+       case e1000_82572:
+               reg &= ~((1 << 29) | (1 << 30));
+               reg |= (1 << 22) | (1 << 24) | (1 << 25) | (1 << 26);
+               if (E1000_READ_REG(hw, E1000_TCTL) & E1000_TCTL_MULR)
+                       reg &= ~(1 << 28);
+               else
+                       reg |= (1 << 28);
+               E1000_WRITE_REG(hw, E1000_TARC(1), reg);
+               break;
+       default:
+               break;
+       }
+
+       /* Device Control */
+       switch (hw->mac.type) {
+       case e1000_82573:
+       case e1000_82574:
+       case e1000_82583:
+               reg = E1000_READ_REG(hw, E1000_CTRL);
+               reg &= ~(1 << 29);
+               E1000_WRITE_REG(hw, E1000_CTRL, reg);
+               break;
+       default:
+               break;
+       }
+
+       /* Extended Device Control */
+       switch (hw->mac.type) {
+       case e1000_82573:
+       case e1000_82574:
+       case e1000_82583:
+               reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
+               reg &= ~(1 << 23);
+               reg |= (1 << 22);
+               E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg);
+               break;
+       default:
+               break;
+       }
+
+       if (hw->mac.type == e1000_82571) {
+               reg = E1000_READ_REG(hw, E1000_PBA_ECC);
+               reg |= E1000_PBA_ECC_CORR_EN;
+               E1000_WRITE_REG(hw, E1000_PBA_ECC, reg);
+       }
+
+       /*
+        * Workaround for hardware errata.
+        * Ensure that DMA Dynamic Clock gating is disabled on 82571 and 82572
+        */
+       if ((hw->mac.type == e1000_82571) ||
+          (hw->mac.type == e1000_82572)) {
+               reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
+               reg &= ~E1000_CTRL_EXT_DMA_DYN_CLK_EN;
+               E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg);
+       }
+
+       /*
+        * Disable IPv6 extension header parsing because some malformed
+        * IPv6 headers can hang the Rx.
+        */
+       if (hw->mac.type <= e1000_82573) {
+               reg = E1000_READ_REG(hw, E1000_RFCTL);
+               reg |= (E1000_RFCTL_IPV6_EX_DIS | E1000_RFCTL_NEW_IPV6_EXT_DIS);
+               E1000_WRITE_REG(hw, E1000_RFCTL, reg);
+       }
+
+       /* PCI-Ex Control Registers */
+       switch (hw->mac.type) {
+       case e1000_82574:
+       case e1000_82583:
+               reg = E1000_READ_REG(hw, E1000_GCR);
+               reg |= (1 << 22);
+               E1000_WRITE_REG(hw, E1000_GCR, reg);
+
+               /*
+                * Workaround for hardware errata.
+                * apply workaround for hardware errata documented in errata
+                * docs Fixes issue where some error prone or unreliable PCIe
+                * completions are occurring, particularly with ASPM enabled.
+                * Without fix, issue can cause Tx timeouts.
+                */
+               reg = E1000_READ_REG(hw, E1000_GCR2);
+               reg |= 1;
+               E1000_WRITE_REG(hw, E1000_GCR2, reg);
+               break;
+       default:
+               break;
+       }
+
+       return;
+}
+
+/**
+ *  e1000_clear_vfta_82571 - Clear VLAN filter table
+ *  @hw: pointer to the HW structure
+ *
+ *  Clears the register array which contains the VLAN filter table by
+ *  setting all the values to 0.
+ **/
+STATIC void e1000_clear_vfta_82571(struct e1000_hw *hw)
+{
+       u32 offset;
+       u32 vfta_value = 0;
+       u32 vfta_offset = 0;
+       u32 vfta_bit_in_reg = 0;
+
+       DEBUGFUNC("e1000_clear_vfta_82571");
+
+       switch (hw->mac.type) {
+       case e1000_82573:
+       case e1000_82574:
+       case e1000_82583:
+               if (hw->mng_cookie.vlan_id != 0) {
+                       /*
+                        * The VFTA is a 4096b bit-field, each identifying
+                        * a single VLAN ID.  The following operations
+                        * determine which 32b entry (i.e. offset) into the
+                        * array we want to set the VLAN ID (i.e. bit) of
+                        * the manageability unit.
+                        */
+                       vfta_offset = (hw->mng_cookie.vlan_id >>
+                                      E1000_VFTA_ENTRY_SHIFT) &
+                           E1000_VFTA_ENTRY_MASK;
+                       vfta_bit_in_reg =
+                           1 << (hw->mng_cookie.vlan_id &
+                                 E1000_VFTA_ENTRY_BIT_SHIFT_MASK);
+               }
+               break;
+       default:
+               break;
+       }
+       for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) {
+               /*
+                * If the offset we want to clear is the same offset of the
+                * manageability VLAN ID, then clear all bits except that of
+                * the manageability unit.
+                */
+               vfta_value = (offset == vfta_offset) ? vfta_bit_in_reg : 0;
+               E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, vfta_value);
+               E1000_WRITE_FLUSH(hw);
+       }
+}
+
+/**
+ *  e1000_check_mng_mode_82574 - Check manageability is enabled
+ *  @hw: pointer to the HW structure
+ *
+ *  Reads the NVM Initialization Control Word 2 and returns true
+ *  (>0) if any manageability is enabled, else false (0).
+ **/
+STATIC bool e1000_check_mng_mode_82574(struct e1000_hw *hw)
+{
+       u16 data;
+
+       DEBUGFUNC("e1000_check_mng_mode_82574");
+
+       hw->nvm.ops.read(hw, NVM_INIT_CONTROL2_REG, 1, &data);
+       return (data & E1000_NVM_INIT_CTRL2_MNGM) != 0;
+}
+
+/**
+ *  e1000_led_on_82574 - Turn LED on
+ *  @hw: pointer to the HW structure
+ *
+ *  Turn LED on.
+ **/
+STATIC s32 e1000_led_on_82574(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       u32 i;
+
+       DEBUGFUNC("e1000_led_on_82574");
+
+       ctrl = hw->mac.ledctl_mode2;
+       if (!(E1000_STATUS_LU & E1000_READ_REG(hw, E1000_STATUS))) {
+               /*
+                * If no link, then turn LED on by setting the invert bit
+                * for each LED that's "on" (0x0E) in ledctl_mode2.
+                */
+               for (i = 0; i < 4; i++)
+                       if (((hw->mac.ledctl_mode2 >> (i * 8)) & 0xFF) ==
+                           E1000_LEDCTL_MODE_LED_ON)
+                               ctrl |= (E1000_LEDCTL_LED0_IVRT << (i * 8));
+       }
+       E1000_WRITE_REG(hw, E1000_LEDCTL, ctrl);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_check_phy_82574 - check 82574 phy hung state
+ *  @hw: pointer to the HW structure
+ *
+ *  Returns whether phy is hung or not
+ **/
+bool e1000_check_phy_82574(struct e1000_hw *hw)
+{
+       u16 status_1kbt = 0;
+       u16 receive_errors = 0;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_check_phy_82574");
+
+       /*
+        * Read PHY Receive Error counter first, if its is max - all F's then
+        * read the Base1000T status register If both are max then PHY is hung.
+        */
+       ret_val = hw->phy.ops.read_reg(hw, E1000_RECEIVE_ERROR_COUNTER,
+                                      &receive_errors);
+       if (ret_val)
+               return false;
+       if (receive_errors == E1000_RECEIVE_ERROR_MAX) {
+               ret_val = hw->phy.ops.read_reg(hw, E1000_BASE1000T_STATUS,
+                                              &status_1kbt);
+               if (ret_val)
+                       return false;
+               if ((status_1kbt & E1000_IDLE_ERROR_COUNT_MASK) ==
+                   E1000_IDLE_ERROR_COUNT_MASK)
+                       return true;
+       }
+
+       return false;
+}
+
+
+/**
+ *  e1000_setup_link_82571 - Setup flow control and link settings
+ *  @hw: pointer to the HW structure
+ *
+ *  Determines which flow control settings to use, then configures flow
+ *  control.  Calls the appropriate media-specific link configuration
+ *  function.  Assuming the adapter has a valid link partner, a valid link
+ *  should be established.  Assumes the hardware has previously been reset
+ *  and the transmitter and receiver are not enabled.
+ **/
+STATIC s32 e1000_setup_link_82571(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_setup_link_82571");
+
+       /*
+        * 82573 does not have a word in the NVM to determine
+        * the default flow control setting, so we explicitly
+        * set it to full.
+        */
+       switch (hw->mac.type) {
+       case e1000_82573:
+       case e1000_82574:
+       case e1000_82583:
+               if (hw->fc.requested_mode == e1000_fc_default)
+                       hw->fc.requested_mode = e1000_fc_full;
+               break;
+       default:
+               break;
+       }
+
+       return e1000_setup_link_generic(hw);
+}
+
+/**
+ *  e1000_setup_copper_link_82571 - Configure copper link settings
+ *  @hw: pointer to the HW structure
+ *
+ *  Configures the link for auto-neg or forced speed and duplex.  Then we check
+ *  for link, once link is established calls to configure collision distance
+ *  and flow control are called.
+ **/
+STATIC s32 e1000_setup_copper_link_82571(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_setup_copper_link_82571");
+
+       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);
+
+       switch (hw->phy.type) {
+       case e1000_phy_m88:
+       case e1000_phy_bm:
+               ret_val = e1000_copper_link_setup_m88(hw);
+               break;
+       case e1000_phy_igp_2:
+               ret_val = e1000_copper_link_setup_igp(hw);
+               break;
+       default:
+               return -E1000_ERR_PHY;
+               break;
+       }
+
+       if (ret_val)
+               return ret_val;
+
+       return e1000_setup_copper_link_generic(hw);
+}
+
+/**
+ *  e1000_setup_fiber_serdes_link_82571 - Setup link for fiber/serdes
+ *  @hw: pointer to the HW structure
+ *
+ *  Configures collision distance and flow control for fiber and serdes links.
+ *  Upon successful setup, poll for link.
+ **/
+STATIC s32 e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_setup_fiber_serdes_link_82571");
+
+       switch (hw->mac.type) {
+       case e1000_82571:
+       case e1000_82572:
+               /*
+                * If SerDes loopback mode is entered, there is no form
+                * of reset to take the adapter out of that mode.  So we
+                * have to explicitly take the adapter out of loopback
+                * mode.  This prevents drivers from twiddling their thumbs
+                * if another tool failed to take it out of loopback mode.
+                */
+               E1000_WRITE_REG(hw, E1000_SCTL,
+                               E1000_SCTL_DISABLE_SERDES_LOOPBACK);
+               break;
+       default:
+               break;
+       }
+
+       return e1000_setup_fiber_serdes_link_generic(hw);
+}
+
+/**
+ *  e1000_check_for_serdes_link_82571 - Check for link (Serdes)
+ *  @hw: pointer to the HW structure
+ *
+ *  Reports the link state as up or down.
+ *
+ *  If autonegotiation is supported by the link partner, the link state is
+ *  determined by the result of autonegotiation. This is the most likely case.
+ *  If autonegotiation is not supported by the link partner, and the link
+ *  has a valid signal, force the link up.
+ *
+ *  The link state is represented internally here by 4 states:
+ *
+ *  1) down
+ *  2) autoneg_progress
+ *  3) autoneg_complete (the link successfully autonegotiated)
+ *  4) forced_up (the link has been forced up, it did not autonegotiate)
+ *
+ **/
+STATIC s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       u32 rxcw;
+       u32 ctrl;
+       u32 status;
+       u32 txcw;
+       u32 i;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_check_for_serdes_link_82571");
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+       status = E1000_READ_REG(hw, E1000_STATUS);
+       rxcw = E1000_READ_REG(hw, E1000_RXCW);
+       /* SYNCH bit and IV bit are sticky */
+       usec_delay(10);
+       rxcw = E1000_READ_REG(hw, E1000_RXCW);
+
+       if ((rxcw & E1000_RXCW_SYNCH) && !(rxcw & E1000_RXCW_IV)) {
+
+               /* Receiver is synchronized with no invalid bits.  */
+               switch (mac->serdes_link_state) {
+               case e1000_serdes_link_autoneg_complete:
+                       if (!(status & E1000_STATUS_LU)) {
+                               /*
+                                * We have lost link, retry autoneg before
+                                * reporting link failure
+                                */
+                               mac->serdes_link_state =
+                                   e1000_serdes_link_autoneg_progress;
+                               mac->serdes_has_link = false;
+                               DEBUGOUT("AN_UP     -> AN_PROG\n");
+                       } else {
+                               mac->serdes_has_link = true;
+                       }
+                       break;
+
+               case e1000_serdes_link_forced_up:
+                       /*
+                        * If we are receiving /C/ ordered sets, re-enable
+                        * auto-negotiation in the TXCW register and disable
+                        * forced link in the Device Control register in an
+                        * attempt to auto-negotiate with our link partner.
+                        */
+                       if (rxcw & E1000_RXCW_C) {
+                               /* Enable autoneg, and unforce link up */
+                               E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw);
+                               E1000_WRITE_REG(hw, E1000_CTRL,
+                                   (ctrl & ~E1000_CTRL_SLU));
+                               mac->serdes_link_state =
+                                   e1000_serdes_link_autoneg_progress;
+                               mac->serdes_has_link = false;
+                               DEBUGOUT("FORCED_UP -> AN_PROG\n");
+                       } else {
+                               mac->serdes_has_link = true;
+                       }
+                       break;
+
+               case e1000_serdes_link_autoneg_progress:
+                       if (rxcw & E1000_RXCW_C) {
+                               /*
+                                * We received /C/ ordered sets, meaning the
+                                * link partner has autonegotiated, and we can
+                                * trust the Link Up (LU) status bit.
+                                */
+                               if (status & E1000_STATUS_LU) {
+                                       mac->serdes_link_state =
+                                           e1000_serdes_link_autoneg_complete;
+                                       DEBUGOUT("AN_PROG   -> AN_UP\n");
+                                       mac->serdes_has_link = true;
+                               } else {
+                                       /* Autoneg completed, but failed. */
+                                       mac->serdes_link_state =
+                                           e1000_serdes_link_down;
+                                       DEBUGOUT("AN_PROG   -> DOWN\n");
+                               }
+                       } else {
+                               /*
+                                * The link partner did not autoneg.
+                                * Force link up and full duplex, and change
+                                * state to forced.
+                                */
+                               E1000_WRITE_REG(hw, E1000_TXCW,
+                               (mac->txcw & ~E1000_TXCW_ANE));
+                               ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD);
+                               E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+
+                               /* Configure Flow Control after link up. */
+                               ret_val =
+                                   e1000_config_fc_after_link_up_generic(hw);
+                               if (ret_val) {
+                                       DEBUGOUT("Error config flow control\n");
+                                       break;
+                               }
+                               mac->serdes_link_state =
+                                               e1000_serdes_link_forced_up;
+                               mac->serdes_has_link = true;
+                               DEBUGOUT("AN_PROG   -> FORCED_UP\n");
+                       }
+                       break;
+
+               case e1000_serdes_link_down:
+               default:
+                       /*
+                        * The link was down but the receiver has now gained
+                        * valid sync, so lets see if we can bring the link
+                        * up.
+                        */
+                       E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw);
+                       E1000_WRITE_REG(hw, E1000_CTRL, (ctrl &
+                                       ~E1000_CTRL_SLU));
+                       mac->serdes_link_state =
+                                       e1000_serdes_link_autoneg_progress;
+                       mac->serdes_has_link = false;
+                       DEBUGOUT("DOWN      -> AN_PROG\n");
+                       break;
+               }
+       } else {
+               if (!(rxcw & E1000_RXCW_SYNCH)) {
+                       mac->serdes_has_link = false;
+                       mac->serdes_link_state = e1000_serdes_link_down;
+                       DEBUGOUT("ANYSTATE  -> DOWN\n");
+               } else {
+                       /*
+                        * Check several times, if SYNCH bit and CONFIG
+                        * bit both are consistently 1 then simply ignore
+                        * the IV bit and restart Autoneg
+                        */
+                       for (i = 0; i < AN_RETRY_COUNT; i++) {
+                               usec_delay(10);
+                               rxcw = E1000_READ_REG(hw, E1000_RXCW);
+                               if ((rxcw & E1000_RXCW_SYNCH) &&
+                                   (rxcw & E1000_RXCW_C))
+                                       continue;
+
+                               if (rxcw & E1000_RXCW_IV) {
+                                       mac->serdes_has_link = false;
+                                       mac->serdes_link_state =
+                                                       e1000_serdes_link_down;
+                                       DEBUGOUT("ANYSTATE  -> DOWN\n");
+                                       break;
+                               }
+                       }
+
+                       if (i == AN_RETRY_COUNT) {
+                               txcw = E1000_READ_REG(hw, E1000_TXCW);
+                               txcw |= E1000_TXCW_ANE;
+                               E1000_WRITE_REG(hw, E1000_TXCW, txcw);
+                               mac->serdes_link_state =
+                                       e1000_serdes_link_autoneg_progress;
+                               mac->serdes_has_link = false;
+                               DEBUGOUT("ANYSTATE  -> AN_PROG\n");
+                       }
+               }
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_valid_led_default_82571 - Verify a valid default LED config
+ *  @hw: pointer to the HW structure
+ *  @data: pointer to the NVM (EEPROM)
+ *
+ *  Read the EEPROM for the current default LED configuration.  If the
+ *  LED configuration is not valid, set to a valid LED configuration.
+ **/
+STATIC s32 e1000_valid_led_default_82571(struct e1000_hw *hw, u16 *data)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_valid_led_default_82571");
+
+       ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data);
+       if (ret_val) {
+               DEBUGOUT("NVM Read Error\n");
+               return ret_val;
+       }
+
+       switch (hw->mac.type) {
+       case e1000_82573:
+       case e1000_82574:
+       case e1000_82583:
+               if (*data == ID_LED_RESERVED_F746)
+                       *data = ID_LED_DEFAULT_82573;
+               break;
+       default:
+               if (*data == ID_LED_RESERVED_0000 ||
+                   *data == ID_LED_RESERVED_FFFF)
+                       *data = ID_LED_DEFAULT;
+               break;
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_get_laa_state_82571 - Get locally administered address state
+ *  @hw: pointer to the HW structure
+ *
+ *  Retrieve and return the current locally administered address state.
+ **/
+bool e1000_get_laa_state_82571(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_get_laa_state_82571");
+
+       if (hw->mac.type != e1000_82571)
+               return false;
+
+       return hw->dev_spec._82571.laa_is_present;
+}
+
+/**
+ *  e1000_set_laa_state_82571 - Set locally administered address state
+ *  @hw: pointer to the HW structure
+ *  @state: enable/disable locally administered address
+ *
+ *  Enable/Disable the current locally administered address state.
+ **/
+void e1000_set_laa_state_82571(struct e1000_hw *hw, bool state)
+{
+       DEBUGFUNC("e1000_set_laa_state_82571");
+
+       if (hw->mac.type != e1000_82571)
+               return;
+
+       hw->dev_spec._82571.laa_is_present = state;
+
+       /* If workaround is activated... */
+       if (state)
+               /*
+                * Hold a copy of the LAA in RAR[14] This is done so that
+                * between the time RAR[0] gets clobbered and the time it
+                * gets fixed, the actual LAA is in one of the RARs and no
+                * incoming packets directed to this port are dropped.
+                * Eventually the LAA will be in RAR[0] and RAR[14].
+                */
+               hw->mac.ops.rar_set(hw, hw->mac.addr,
+                                   hw->mac.rar_entry_count - 1);
+       return;
+}
+
+/**
+ *  e1000_fix_nvm_checksum_82571 - Fix EEPROM checksum
+ *  @hw: pointer to the HW structure
+ *
+ *  Verifies that the EEPROM has completed the update.  After updating the
+ *  EEPROM, we need to check bit 15 in work 0x23 for the checksum fix.  If
+ *  the checksum fix is not implemented, we need to set the bit and update
+ *  the checksum.  Otherwise, if bit 15 is set and the checksum is incorrect,
+ *  we need to return bad checksum.
+ **/
+static s32 e1000_fix_nvm_checksum_82571(struct e1000_hw *hw)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       s32 ret_val;
+       u16 data;
+
+       DEBUGFUNC("e1000_fix_nvm_checksum_82571");
+
+       if (nvm->type != e1000_nvm_flash_hw)
+               return E1000_SUCCESS;
+
+       /*
+        * Check bit 4 of word 10h.  If it is 0, firmware is done updating
+        * 10h-12h.  Checksum may need to be fixed.
+        */
+       ret_val = nvm->ops.read(hw, 0x10, 1, &data);
+       if (ret_val)
+               return ret_val;
+
+       if (!(data & 0x10)) {
+               /*
+                * Read 0x23 and check bit 15.  This bit is a 1
+                * when the checksum has already been fixed.  If
+                * the checksum is still wrong and this bit is a
+                * 1, we need to return bad checksum.  Otherwise,
+                * we need to set this bit to a 1 and update the
+                * checksum.
+                */
+               ret_val = nvm->ops.read(hw, 0x23, 1, &data);
+               if (ret_val)
+                       return ret_val;
+
+               if (!(data & 0x8000)) {
+                       data |= 0x8000;
+                       ret_val = nvm->ops.write(hw, 0x23, 1, &data);
+                       if (ret_val)
+                               return ret_val;
+                       ret_val = nvm->ops.update(hw);
+               }
+       }
+
+       return E1000_SUCCESS;
+}
+
+
+/**
+ *  e1000_read_mac_addr_82571 - Read device MAC address
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_read_mac_addr_82571(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_read_mac_addr_82571");
+
+       if (hw->mac.type == e1000_82571) {
+               s32 ret_val = E1000_SUCCESS;
+
+               /*
+                * If there's an alternate MAC address place it in RAR0
+                * so that it will override the Si installed default perm
+                * address.
+                */
+               ret_val = e1000_check_alt_mac_addr_generic(hw);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       return e1000_read_mac_addr_generic(hw);
+}
+
+/**
+ * e1000_power_down_phy_copper_82571 - Remove link during PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, remove the link.
+ **/
+STATIC void e1000_power_down_phy_copper_82571(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       struct e1000_mac_info *mac = &hw->mac;
+
+       if (!phy->ops.check_reset_block)
+               return;
+
+       /* If the management interface is not enabled, then power down */
+       if (!(mac->ops.check_mng_mode(hw) || phy->ops.check_reset_block(hw)))
+               e1000_power_down_phy_copper(hw);
+
+       return;
+}
+
+/**
+ *  e1000_clear_hw_cntrs_82571 - Clear device specific hardware counters
+ *  @hw: pointer to the HW structure
+ *
+ *  Clears the hardware counters by reading the counter registers.
+ **/
+STATIC void e1000_clear_hw_cntrs_82571(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_clear_hw_cntrs_82571");
+
+       e1000_clear_hw_cntrs_base_generic(hw);
+
+       E1000_READ_REG(hw, E1000_PRC64);
+       E1000_READ_REG(hw, E1000_PRC127);
+       E1000_READ_REG(hw, E1000_PRC255);
+       E1000_READ_REG(hw, E1000_PRC511);
+       E1000_READ_REG(hw, E1000_PRC1023);
+       E1000_READ_REG(hw, E1000_PRC1522);
+       E1000_READ_REG(hw, E1000_PTC64);
+       E1000_READ_REG(hw, E1000_PTC127);
+       E1000_READ_REG(hw, E1000_PTC255);
+       E1000_READ_REG(hw, E1000_PTC511);
+       E1000_READ_REG(hw, E1000_PTC1023);
+       E1000_READ_REG(hw, E1000_PTC1522);
+
+       E1000_READ_REG(hw, E1000_ALGNERRC);
+       E1000_READ_REG(hw, E1000_RXERRC);
+       E1000_READ_REG(hw, E1000_TNCRS);
+       E1000_READ_REG(hw, E1000_CEXTERR);
+       E1000_READ_REG(hw, E1000_TSCTC);
+       E1000_READ_REG(hw, E1000_TSCTFC);
+
+       E1000_READ_REG(hw, E1000_MGTPRC);
+       E1000_READ_REG(hw, E1000_MGTPDC);
+       E1000_READ_REG(hw, E1000_MGTPTC);
+
+       E1000_READ_REG(hw, E1000_IAC);
+       E1000_READ_REG(hw, E1000_ICRXOC);
+
+       E1000_READ_REG(hw, E1000_ICRXPTC);
+       E1000_READ_REG(hw, E1000_ICRXATC);
+       E1000_READ_REG(hw, E1000_ICTXPTC);
+       E1000_READ_REG(hw, E1000_ICTXATC);
+       E1000_READ_REG(hw, E1000_ICTXQEC);
+       E1000_READ_REG(hw, E1000_ICTXQMTC);
+       E1000_READ_REG(hw, E1000_ICRXDMTC);
+}
diff --git a/lib/librte_pmd_e1000/e1000/e1000_82571.h b/lib/librte_pmd_e1000/e1000/e1000_82571.h
new file mode 100644 (file)
index 0000000..c6bbb83
--- /dev/null
@@ -0,0 +1,65 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#ifndef _E1000_82571_H_
+#define _E1000_82571_H_
+
+#define ID_LED_RESERVED_F746   0xF746
+#define ID_LED_DEFAULT_82573   ((ID_LED_DEF1_DEF2 << 12) | \
+                                (ID_LED_OFF1_ON2  <<  8) | \
+                                (ID_LED_DEF1_DEF2 <<  4) | \
+                                (ID_LED_DEF1_DEF2))
+
+#define E1000_GCR_L1_ACT_WITHOUT_L0S_RX        0x08000000
+#define AN_RETRY_COUNT         5 /* Autoneg Retry Count value */
+
+/* Intr Throttling - RW */
+#define E1000_EITR_82574(_n)   (0x000E8 + (0x4 * (_n)))
+
+#define E1000_EIAC_82574       0x000DC /* Ext. Interrupt Auto Clear - RW */
+#define E1000_EIAC_MASK_82574  0x01F00000
+
+/* Manageability Operation Mode mask */
+#define E1000_NVM_INIT_CTRL2_MNGM      0x6000
+
+#define E1000_RXCFGL   0x0B634 /* TimeSync Rx EtherType & Msg Type Reg - RW */
+
+#define E1000_BASE1000T_STATUS         10
+#define E1000_IDLE_ERROR_COUNT_MASK    0xFF
+#define E1000_RECEIVE_ERROR_COUNTER    21
+#define E1000_RECEIVE_ERROR_MAX                0xFFFF
+bool e1000_check_phy_82574(struct e1000_hw *hw);
+bool e1000_get_laa_state_82571(struct e1000_hw *hw);
+void e1000_set_laa_state_82571(struct e1000_hw *hw, bool state);
+
+#endif
diff --git a/lib/librte_pmd_e1000/e1000/e1000_i210.c b/lib/librte_pmd_e1000/e1000/e1000_i210.c
new file mode 100644 (file)
index 0000000..d96d240
--- /dev/null
@@ -0,0 +1,840 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#include "e1000_api.h"
+
+
+STATIC s32 e1000_acquire_nvm_i210(struct e1000_hw *hw);
+STATIC void e1000_release_nvm_i210(struct e1000_hw *hw);
+STATIC s32 e1000_get_hw_semaphore_i210(struct e1000_hw *hw);
+STATIC void e1000_put_hw_semaphore_i210(struct e1000_hw *hw);
+STATIC s32 e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
+                               u16 *data);
+STATIC s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw);
+STATIC s32 e1000_valid_led_default_i210(struct e1000_hw *hw, u16 *data);
+STATIC s32 e1000_read_nvm_i211(struct e1000_hw *hw, u16 offset, u16 words,
+                              u16 *data);
+
+/**
+ *  e1000_acquire_nvm_i210 - Request for access to EEPROM
+ *  @hw: pointer to the HW structure
+ *
+ *  Acquire the necessary semaphores for exclusive access to the EEPROM.
+ *  Set the EEPROM access request bit and wait for EEPROM access grant bit.
+ *  Return successful if access grant bit set, else clear the request for
+ *  EEPROM access and return -E1000_ERR_NVM (-1).
+ **/
+STATIC s32 e1000_acquire_nvm_i210(struct e1000_hw *hw)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_acquire_nvm_i210");
+
+       ret_val = e1000_acquire_swfw_sync_i210(hw, E1000_SWFW_EEP_SM);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_release_nvm_i210 - Release exclusive access to EEPROM
+ *  @hw: pointer to the HW structure
+ *
+ *  Stop any current commands to the EEPROM and clear the EEPROM request bit,
+ *  then release the semaphores acquired.
+ **/
+STATIC void e1000_release_nvm_i210(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_release_nvm_i210");
+
+       e1000_release_swfw_sync_i210(hw, E1000_SWFW_EEP_SM);
+}
+
+/**
+ *  e1000_acquire_swfw_sync_i210 - Acquire SW/FW semaphore
+ *  @hw: pointer to the HW structure
+ *  @mask: specifies which semaphore to acquire
+ *
+ *  Acquire the SW/FW semaphore to access the PHY or NVM.  The mask
+ *  will also specify which port we're acquiring the lock for.
+ **/
+s32 e1000_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask)
+{
+       u32 swfw_sync;
+       u32 swmask = mask;
+       u32 fwmask = mask << 16;
+       s32 ret_val = E1000_SUCCESS;
+       s32 i = 0, timeout = 200; /* FIXME: find real value to use here */
+
+       DEBUGFUNC("e1000_acquire_swfw_sync_i210");
+
+       while (i < timeout) {
+               if (e1000_get_hw_semaphore_i210(hw)) {
+                       ret_val = -E1000_ERR_SWFW_SYNC;
+                       goto out;
+               }
+
+               swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC);
+               if (!(swfw_sync & fwmask))
+                       break;
+
+               /*
+                * Firmware currently using resource (fwmask)
+                */
+               e1000_put_hw_semaphore_i210(hw);
+               msec_delay_irq(5);
+               i++;
+       }
+
+       if (i == timeout) {
+               DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n");
+               ret_val = -E1000_ERR_SWFW_SYNC;
+               goto out;
+       }
+
+       swfw_sync |= swmask;
+       E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync);
+
+       e1000_put_hw_semaphore_i210(hw);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_release_swfw_sync_i210 - Release SW/FW semaphore
+ *  @hw: pointer to the HW structure
+ *  @mask: specifies which semaphore to acquire
+ *
+ *  Release the SW/FW semaphore used to access the PHY or NVM.  The mask
+ *  will also specify which port we're releasing the lock for.
+ **/
+void e1000_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask)
+{
+       u32 swfw_sync;
+
+       DEBUGFUNC("e1000_release_swfw_sync_i210");
+
+       while (e1000_get_hw_semaphore_i210(hw) != E1000_SUCCESS)
+               ; /* Empty */
+
+       swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC);
+       swfw_sync &= ~mask;
+       E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync);
+
+       e1000_put_hw_semaphore_i210(hw);
+}
+
+/**
+ *  e1000_get_hw_semaphore_i210 - Acquire hardware semaphore
+ *  @hw: pointer to the HW structure
+ *
+ *  Acquire the HW semaphore to access the PHY or NVM
+ **/
+STATIC s32 e1000_get_hw_semaphore_i210(struct e1000_hw *hw)
+{
+       u32 swsm;
+       s32 ret_val = E1000_SUCCESS;
+       s32 timeout = hw->nvm.word_size + 1;
+       s32 i = 0;
+
+       DEBUGFUNC("e1000_get_hw_semaphore_i210");
+
+       /* Get the FW semaphore. */
+       for (i = 0; i < timeout; i++) {
+               swsm = E1000_READ_REG(hw, E1000_SWSM);
+               E1000_WRITE_REG(hw, E1000_SWSM, swsm | E1000_SWSM_SWESMBI);
+
+               /* Semaphore acquired if bit latched */
+               if (E1000_READ_REG(hw, E1000_SWSM) & E1000_SWSM_SWESMBI)
+                       break;
+
+               usec_delay(50);
+       }
+
+       if (i == timeout) {
+               /* Release semaphores */
+               e1000_put_hw_semaphore_generic(hw);
+               DEBUGOUT("Driver can't access the NVM\n");
+               ret_val = -E1000_ERR_NVM;
+               goto out;
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_put_hw_semaphore_i210 - Release hardware semaphore
+ *  @hw: pointer to the HW structure
+ *
+ *  Release hardware semaphore used to access the PHY or NVM
+ **/
+STATIC void e1000_put_hw_semaphore_i210(struct e1000_hw *hw)
+{
+       u32 swsm;
+
+       DEBUGFUNC("e1000_put_hw_semaphore_i210");
+
+       swsm = E1000_READ_REG(hw, E1000_SWSM);
+
+       swsm &= ~E1000_SWSM_SWESMBI;
+
+       E1000_WRITE_REG(hw, E1000_SWSM, swsm);
+}
+
+/**
+ *  e1000_read_nvm_srrd_i210 - Reads Shadow Ram using EERD register
+ *  @hw: pointer to the HW structure
+ *  @offset: offset of word in the Shadow Ram to read
+ *  @words: number of words to read
+ *  @data: word read from the Shadow Ram
+ *
+ *  Reads a 16 bit word from the Shadow Ram using the EERD register.
+ *  Uses necessary synchronization semaphores.
+ **/
+s32 e1000_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
+                            u16 *data)
+{
+       s32 status = E1000_SUCCESS;
+       u16 i, count;
+
+       DEBUGFUNC("e1000_read_nvm_srrd_i210");
+
+       /* We cannot hold synchronization semaphores for too long,
+        * because of forceful takeover procedure. However it is more efficient
+        * to read in bursts than synchronizing access for each word. */
+       for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) {
+               count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ?
+                       E1000_EERD_EEWR_MAX_COUNT : (words - i);
+               if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+                       status = e1000_read_nvm_eerd(hw, offset, count,
+                                                    data + i);
+                       hw->nvm.ops.release(hw);
+               } else {
+                       status = E1000_ERR_SWFW_SYNC;
+               }
+
+               if (status != E1000_SUCCESS)
+                       break;
+       }
+
+       return status;
+}
+
+/**
+ *  e1000_write_nvm_srwr_i210 - Write to Shadow RAM using EEWR
+ *  @hw: pointer to the HW structure
+ *  @offset: offset within the Shadow RAM to be written to
+ *  @words: number of words to write
+ *  @data: 16 bit word(s) to be written to the Shadow RAM
+ *
+ *  Writes data to Shadow RAM at offset using EEWR register.
+ *
+ *  If e1000_update_nvm_checksum is not called after this function , the
+ *  data will not be committed to FLASH and also Shadow RAM will most likely
+ *  contain an invalid checksum.
+ *
+ *  If error code is returned, data and Shadow RAM may be inconsistent - buffer
+ *  partially written.
+ **/
+s32 e1000_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
+                             u16 *data)
+{
+       s32 status = E1000_SUCCESS;
+       u16 i, count;
+
+       DEBUGFUNC("e1000_write_nvm_srwr_i210");
+
+       /* We cannot hold synchronization semaphores for too long,
+        * because of forceful takeover procedure. However it is more efficient
+        * to write in bursts than synchronizing access for each word. */
+       for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) {
+               count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ?
+                       E1000_EERD_EEWR_MAX_COUNT : (words - i);
+               if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+                       status = e1000_write_nvm_srwr(hw, offset, count,
+                                                     data + i);
+                       hw->nvm.ops.release(hw);
+               } else {
+                       status = E1000_ERR_SWFW_SYNC;
+               }
+
+               if (status != E1000_SUCCESS)
+                       break;
+       }
+
+       return status;
+}
+
+/**
+ *  e1000_write_nvm_srwr - Write to Shadow Ram using EEWR
+ *  @hw: pointer to the HW structure
+ *  @offset: offset within the Shadow Ram to be written to
+ *  @words: number of words to write
+ *  @data: 16 bit word(s) to be written to the Shadow Ram
+ *
+ *  Writes data to Shadow Ram at offset using EEWR register.
+ *
+ *  If e1000_update_nvm_checksum is not called after this function , the
+ *  Shadow Ram will most likely contain an invalid checksum.
+ **/
+STATIC s32 e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
+                               u16 *data)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       u32 i, k, eewr = 0;
+       u32 attempts = 100000;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_write_nvm_srwr");
+
+       /*
+        * A check for invalid values:  offset too large, too many words,
+        * too many words for the offset, and not enough words.
+        */
+       if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
+           (words == 0)) {
+               DEBUGOUT("nvm parameter(s) out of bounds\n");
+               ret_val = -E1000_ERR_NVM;
+               goto out;
+       }
+
+       for (i = 0; i < words; i++) {
+               eewr = ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) |
+                       (data[i] << E1000_NVM_RW_REG_DATA) |
+                       E1000_NVM_RW_REG_START;
+
+               E1000_WRITE_REG(hw, E1000_SRWR, eewr);
+
+               for (k = 0; k < attempts; k++) {
+                       if (E1000_NVM_RW_REG_DONE &
+                           E1000_READ_REG(hw, E1000_SRWR)) {
+                               ret_val = E1000_SUCCESS;
+                               break;
+                       }
+                       usec_delay(5);
+               }
+
+               if (ret_val != E1000_SUCCESS) {
+                       DEBUGOUT("Shadow RAM write EEWR timed out\n");
+                       break;
+               }
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_read_nvm_i211 - Read NVM wrapper function for I211
+ *  @hw: pointer to the HW structure
+ *  @address: the word address (aka eeprom offset) to read
+ *  @data: pointer to the data read
+ *
+ *  Wrapper function to return data formerly found in the NVM.
+ **/
+STATIC s32 e1000_read_nvm_i211(struct e1000_hw *hw, u16 offset, u16 words,
+                              u16 *data)
+{
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_read_nvm_i211");
+
+       /* Only the MAC addr is required to be present in the iNVM */
+       switch (offset) {
+       case NVM_MAC_ADDR:
+               ret_val = e1000_read_invm_i211(hw, (u8)offset, &data[0]);
+               ret_val |= e1000_read_invm_i211(hw, (u8)offset+1, &data[1]);
+               ret_val |= e1000_read_invm_i211(hw, (u8)offset+2, &data[2]);
+               if (ret_val != E1000_SUCCESS)
+                       DEBUGOUT("MAC Addr not found in iNVM\n");
+               break;
+       case NVM_INIT_CTRL_2:
+               ret_val = e1000_read_invm_i211(hw, (u8)offset, data);
+               if (ret_val != E1000_SUCCESS) {
+                       *data = NVM_INIT_CTRL_2_DEFAULT_I211;
+                       ret_val = E1000_SUCCESS;
+               }
+               break;
+       case NVM_INIT_CTRL_4:
+               ret_val = e1000_read_invm_i211(hw, (u8)offset, data);
+               if (ret_val != E1000_SUCCESS) {
+                       *data = NVM_INIT_CTRL_4_DEFAULT_I211;
+                       ret_val = E1000_SUCCESS;
+               }
+               break;
+       case NVM_LED_1_CFG:
+               ret_val = e1000_read_invm_i211(hw, (u8)offset, data);
+               if (ret_val != E1000_SUCCESS) {
+                       *data = NVM_LED_1_CFG_DEFAULT_I211;
+                       ret_val = E1000_SUCCESS;
+               }
+               break;
+       case NVM_LED_0_2_CFG:
+               ret_val = e1000_read_invm_i211(hw, (u8)offset, data);
+               if (ret_val != E1000_SUCCESS) {
+                       *data = NVM_LED_0_2_CFG_DEFAULT_I211;
+                       ret_val = E1000_SUCCESS;
+               }
+               break;
+       case NVM_ID_LED_SETTINGS:
+               ret_val = e1000_read_invm_i211(hw, (u8)offset, data);
+               if (ret_val != E1000_SUCCESS) {
+                       *data = ID_LED_RESERVED_FFFF;
+                       ret_val = E1000_SUCCESS;
+               }
+               break;
+       case NVM_SUB_DEV_ID:
+               *data = hw->subsystem_device_id;
+               break;
+       case NVM_SUB_VEN_ID:
+               *data = hw->subsystem_vendor_id;
+               break;
+       case NVM_DEV_ID:
+               *data = hw->device_id;
+               break;
+       case NVM_VEN_ID:
+               *data = hw->vendor_id;
+               break;
+       default:
+               DEBUGOUT1("NVM word 0x%02x is not mapped.\n", offset);
+               *data = NVM_RESERVED_WORD;
+               break;
+       }
+       return ret_val;
+}
+
+/**
+ *  e1000_read_invm_i211 - Reads OTP
+ *  @hw: pointer to the HW structure
+ *  @address: the word address (aka eeprom offset) to read
+ *  @data: pointer to the data read
+ *
+ *  Reads 16-bit words from the OTP. Return error when the word is not
+ *  stored in OTP.
+ **/
+s32 e1000_read_invm_i211(struct e1000_hw *hw, u8 address, u16 *data)
+{
+       s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND;
+       u32 invm_dword;
+       u16 i;
+       u8 record_type, word_address;
+
+       DEBUGFUNC("e1000_read_invm_i211");
+
+       for (i = 0; i < E1000_INVM_SIZE; i++) {
+               invm_dword = E1000_READ_REG(hw, E1000_INVM_DATA_REG(i));
+               /* Get record type */
+               record_type = INVM_DWORD_TO_RECORD_TYPE(invm_dword);
+               if (record_type == E1000_INVM_UNINITIALIZED_STRUCTURE)
+                       break;
+               if (record_type == E1000_INVM_CSR_AUTOLOAD_STRUCTURE)
+                       i += E1000_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS;
+               if (record_type == E1000_INVM_RSA_KEY_SHA256_STRUCTURE)
+                       i += E1000_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS;
+               if (record_type == E1000_INVM_WORD_AUTOLOAD_STRUCTURE) {
+                       word_address = INVM_DWORD_TO_WORD_ADDRESS(invm_dword);
+                       if (word_address == address) {
+                               *data = INVM_DWORD_TO_WORD_DATA(invm_dword);
+                               DEBUGOUT2("Read INVM Word 0x%02x = %x",
+                                         address, *data);
+                               status = E1000_SUCCESS;
+                               break;
+                       }
+               }
+       }
+       if (status != E1000_SUCCESS)
+               DEBUGOUT1("Requested word 0x%02x not found in OTP\n", address);
+       return status;
+}
+
+/**
+ *  e1000_read_invm_version - Reads iNVM version and image type
+ *  @hw: pointer to the HW structure
+ *  @invm_ver: version structure for the version read
+ *
+ *  Reads iNVM version and image type.
+ **/
+s32 e1000_read_invm_version(struct e1000_hw *hw,
+                           struct e1000_fw_version *invm_ver)
+{
+       u32 *record = NULL;
+       u32 *next_record = NULL;
+       u32 i = 0;
+       u32 invm_dword = 0;
+       u32 invm_blocks = E1000_INVM_SIZE - (E1000_INVM_ULT_BYTES_SIZE /
+                                            E1000_INVM_RECORD_SIZE_IN_BYTES);
+       u32 buffer[E1000_INVM_SIZE];
+       s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND;
+       u16 version = 0;
+
+       DEBUGFUNC("e1000_read_invm_version");
+
+       /* Read iNVM memory */
+       for (i = 0; i < E1000_INVM_SIZE; i++) {
+               invm_dword = E1000_READ_REG(hw, E1000_INVM_DATA_REG(i));
+               buffer[i] = invm_dword;
+       }
+
+       /* Read version number */
+       for (i = 1; i < invm_blocks; i++) {
+               record = &buffer[invm_blocks - i];
+               next_record = &buffer[invm_blocks - i + 1];
+
+               /* Check if we have first version location used */
+               if ((i == 1) && ((*record & E1000_INVM_VER_FIELD_ONE) == 0)) {
+                       version = 0;
+                       status = E1000_SUCCESS;
+                       break;
+               }
+               /* Check if we have second version location used */
+               else if ((i == 1) &&
+                        ((*record & E1000_INVM_VER_FIELD_TWO) == 0)) {
+                       version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3;
+                       status = E1000_SUCCESS;
+                       break;
+               }
+               /*
+                * Check if we have odd version location
+                * used and it is the last one used
+                */
+               else if ((((*record & E1000_INVM_VER_FIELD_ONE) == 0) &&
+                        ((*record & 0x3) == 0)) || (((*record & 0x3) != 0) &&
+                        (i != 1))) {
+                       version = (*next_record & E1000_INVM_VER_FIELD_TWO)
+                                 >> 13;
+                       status = E1000_SUCCESS;
+                       break;
+               }
+               /*
+                * Check if we have even version location
+                * used and it is the last one used
+                */
+               else if (((*record & E1000_INVM_VER_FIELD_TWO) == 0) &&
+                        ((*record & 0x3) == 0)) {
+                       version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3;
+                       status = E1000_SUCCESS;
+                       break;
+               }
+       }
+
+       if (status == E1000_SUCCESS) {
+               invm_ver->invm_major = (version & E1000_INVM_MAJOR_MASK)
+                                       >> E1000_INVM_MAJOR_SHIFT;
+               invm_ver->invm_minor = version & E1000_INVM_MINOR_MASK;
+       }
+       /* Read Image Type */
+       for (i = 1; i < invm_blocks; i++) {
+               record = &buffer[invm_blocks - i];
+               next_record = &buffer[invm_blocks - i + 1];
+
+               /* Check if we have image type in first location used */
+               if ((i == 1) && ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) {
+                       invm_ver->invm_img_type = 0;
+                       status = E1000_SUCCESS;
+                       break;
+               }
+               /* Check if we have image type in first location used */
+               else if ((((*record & 0x3) == 0) &&
+                        ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) ||
+                        ((((*record & 0x3) != 0) && (i != 1)))) {
+                       invm_ver->invm_img_type =
+                               (*next_record & E1000_INVM_IMGTYPE_FIELD) >> 23;
+                       status = E1000_SUCCESS;
+                       break;
+               }
+       }
+       return status;
+}
+
+/**
+ *  e1000_validate_nvm_checksum_i210 - Validate EEPROM checksum
+ *  @hw: pointer to the HW structure
+ *
+ *  Calculates the EEPROM checksum by reading/adding each word of the EEPROM
+ *  and then verifies that the sum of the EEPROM is equal to 0xBABA.
+ **/
+s32 e1000_validate_nvm_checksum_i210(struct e1000_hw *hw)
+{
+       s32 status = E1000_SUCCESS;
+       s32 (*read_op_ptr)(struct e1000_hw *, u16, u16, u16 *);
+
+       DEBUGFUNC("e1000_validate_nvm_checksum_i210");
+
+       if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+
+               /*
+                * Replace the read function with semaphore grabbing with
+                * the one that skips this for a while.
+                * We have semaphore taken already here.
+                */
+               read_op_ptr = hw->nvm.ops.read;
+               hw->nvm.ops.read = e1000_read_nvm_eerd;
+
+               status = e1000_validate_nvm_checksum_generic(hw);
+
+               /* Revert original read operation. */
+               hw->nvm.ops.read = read_op_ptr;
+
+               hw->nvm.ops.release(hw);
+       } else {
+               status = E1000_ERR_SWFW_SYNC;
+       }
+
+       return status;
+}
+
+
+/**
+ *  e1000_update_nvm_checksum_i210 - Update EEPROM checksum
+ *  @hw: pointer to the HW structure
+ *
+ *  Updates the EEPROM checksum by reading/adding each word of the EEPROM
+ *  up to the checksum.  Then calculates the EEPROM checksum and writes the
+ *  value to the EEPROM. Next commit EEPROM data onto the Flash.
+ **/
+s32 e1000_update_nvm_checksum_i210(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u16 checksum = 0;
+       u16 i, nvm_data;
+
+       DEBUGFUNC("e1000_update_nvm_checksum_i210");
+
+       /*
+        * Read the first word from the EEPROM. If this times out or fails, do
+        * not continue or we could be in for a very long wait while every
+        * EEPROM read fails
+        */
+       ret_val = e1000_read_nvm_eerd(hw, 0, 1, &nvm_data);
+       if (ret_val != E1000_SUCCESS) {
+               DEBUGOUT("EEPROM read failed\n");
+               goto out;
+       }
+
+       if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+               /*
+                * Do not use hw->nvm.ops.write, hw->nvm.ops.read
+                * because we do not want to take the synchronization
+                * semaphores twice here.
+                */
+
+               for (i = 0; i < NVM_CHECKSUM_REG; i++) {
+                       ret_val = e1000_read_nvm_eerd(hw, i, 1, &nvm_data);
+                       if (ret_val) {
+                               hw->nvm.ops.release(hw);
+                               DEBUGOUT("NVM Read Error while updating checksum.\n");
+                               goto out;
+                       }
+                       checksum += nvm_data;
+               }
+               checksum = (u16) NVM_SUM - checksum;
+               ret_val = e1000_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1,
+                                               &checksum);
+               if (ret_val != E1000_SUCCESS) {
+                       hw->nvm.ops.release(hw);
+                       DEBUGOUT("NVM Write Error while updating checksum.\n");
+                       goto out;
+               }
+
+               hw->nvm.ops.release(hw);
+
+               ret_val = e1000_update_flash_i210(hw);
+       } else {
+               ret_val = E1000_ERR_SWFW_SYNC;
+       }
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_update_flash_i210 - Commit EEPROM to the flash
+ *  @hw: pointer to the HW structure
+ *
+ **/
+s32 e1000_update_flash_i210(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u32 flup;
+
+       DEBUGFUNC("e1000_update_flash_i210");
+
+       ret_val = e1000_pool_flash_update_done_i210(hw);
+       if (ret_val == -E1000_ERR_NVM) {
+               DEBUGOUT("Flash update time out\n");
+               goto out;
+       }
+
+       flup = E1000_READ_REG(hw, E1000_EECD) | E1000_EECD_FLUPD_I210;
+       E1000_WRITE_REG(hw, E1000_EECD, flup);
+
+       ret_val = e1000_pool_flash_update_done_i210(hw);
+       if (ret_val == E1000_SUCCESS)
+               DEBUGOUT("Flash update complete\n");
+       else
+               DEBUGOUT("Flash update time out\n");
+
+out:
+       return ret_val;
+}
+
+/**
+ *  e1000_pool_flash_update_done_i210 - Pool FLUDONE status.
+ *  @hw: pointer to the HW structure
+ *
+ **/
+s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw)
+{
+       s32 ret_val = -E1000_ERR_NVM;
+       u32 i, reg;
+
+       DEBUGFUNC("e1000_pool_flash_update_done_i210");
+
+       for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) {
+               reg = E1000_READ_REG(hw, E1000_EECD);
+               if (reg & E1000_EECD_FLUDONE_I210) {
+                       ret_val = E1000_SUCCESS;
+                       break;
+               }
+               usec_delay(5);
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_init_nvm_params_i210 - Initialize i210 NVM function pointers
+ *  @hw: pointer to the HW structure
+ *
+ *  Initialize the i210 NVM parameters and function pointers.
+ **/
+STATIC s32 e1000_init_nvm_params_i210(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+       struct e1000_nvm_info *nvm = &hw->nvm;
+
+       DEBUGFUNC("e1000_init_nvm_params_i210");
+
+       ret_val = e1000_init_nvm_params_82575(hw);
+
+       nvm->ops.acquire = e1000_acquire_nvm_i210;
+       nvm->ops.release = e1000_release_nvm_i210;
+       nvm->ops.read    = e1000_read_nvm_srrd_i210;
+       nvm->ops.write   = e1000_write_nvm_srwr_i210;
+       nvm->ops.valid_led_default = e1000_valid_led_default_i210;
+       nvm->ops.validate = e1000_validate_nvm_checksum_i210;
+       nvm->ops.update   = e1000_update_nvm_checksum_i210;
+
+       return ret_val;
+}
+
+/**
+ *  e1000_init_nvm_params_i211 - Initialize i211 NVM function pointers
+ *  @hw: pointer to the HW structure
+ *
+ *  Initialize the NVM parameters and function pointers for i211.
+ **/
+STATIC s32 e1000_init_nvm_params_i211(struct e1000_hw *hw)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+
+       DEBUGFUNC("e1000_init_nvm_params_i211");
+
+       nvm->ops.acquire  = e1000_acquire_nvm_i210;
+       nvm->ops.release  = e1000_release_nvm_i210;
+       nvm->ops.read     = e1000_read_nvm_i211;
+       nvm->ops.valid_led_default = e1000_valid_led_default_i210;
+       nvm->ops.write    = e1000_null_write_nvm;
+       nvm->ops.validate = e1000_null_ops_generic;
+       nvm->ops.update   = e1000_null_ops_generic;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_function_pointers_i210 - Init func ptrs.
+ *  @hw: pointer to the HW structure
+ *
+ *  Called to initialize all function pointers and parameters.
+ **/
+void e1000_init_function_pointers_i210(struct e1000_hw *hw)
+{
+       e1000_init_function_pointers_82575(hw);
+
+       switch (hw->mac.type) {
+       case e1000_i210:
+               hw->nvm.ops.init_params = e1000_init_nvm_params_i210;
+               break;
+       case e1000_i211:
+               hw->nvm.ops.init_params = e1000_init_nvm_params_i211;
+               break;
+       default:
+               break;
+       }
+       return;
+}
+
+/**
+ *  e1000_valid_led_default_i210 - Verify a valid default LED config
+ *  @hw: pointer to the HW structure
+ *  @data: pointer to the NVM (EEPROM)
+ *
+ *  Read the EEPROM for the current default LED configuration.  If the
+ *  LED configuration is not valid, set to a valid LED configuration.
+ **/
+STATIC s32 e1000_valid_led_default_i210(struct e1000_hw *hw, u16 *data)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_valid_led_default_i210");
+
+       ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data);
+       if (ret_val) {
+               DEBUGOUT("NVM Read Error\n");
+               goto out;
+       }
+
+       if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) {
+               switch (hw->phy.media_type) {
+               case e1000_media_type_internal_serdes:
+                       *data = ID_LED_DEFAULT_I210_SERDES;
+                       break;
+               case e1000_media_type_copper:
+               default:
+                       *data = ID_LED_DEFAULT_I210;
+                       break;
+               }
+       }
+out:
+       return ret_val;
+}
diff --git a/lib/librte_pmd_e1000/e1000/e1000_i210.h b/lib/librte_pmd_e1000/e1000/e1000_i210.h
new file mode 100644 (file)
index 0000000..ca1ab5c
--- /dev/null
@@ -0,0 +1,93 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#ifndef _E1000_I210_H_
+#define _E1000_I210_H_
+
+s32 e1000_update_flash_i210(struct e1000_hw *hw);
+s32 e1000_update_nvm_checksum_i210(struct e1000_hw *hw);
+s32 e1000_validate_nvm_checksum_i210(struct e1000_hw *hw);
+s32 e1000_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset,
+                             u16 words, u16 *data);
+s32 e1000_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset,
+                            u16 words, u16 *data);
+s32 e1000_read_invm_i211(struct e1000_hw *hw, u8 address, u16 *data);
+s32 e1000_read_invm_version(struct e1000_hw *hw,
+                           struct e1000_fw_version *invm_ver);
+s32 e1000_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask);
+void e1000_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask);
+
+#define E1000_STM_OPCODE               0xDB00
+#define E1000_EEPROM_FLASH_SIZE_WORD   0x11
+
+#define INVM_DWORD_TO_RECORD_TYPE(invm_dword) \
+       (u8)((invm_dword) & 0x7)
+#define INVM_DWORD_TO_WORD_ADDRESS(invm_dword) \
+       (u8)(((invm_dword) & 0x0000FE00) >> 9)
+#define INVM_DWORD_TO_WORD_DATA(invm_dword) \
+       (u16)(((invm_dword) & 0xFFFF0000) >> 16)
+
+enum E1000_INVM_STRUCTURE_TYPE {
+       E1000_INVM_UNINITIALIZED_STRUCTURE              = 0x00,
+       E1000_INVM_WORD_AUTOLOAD_STRUCTURE              = 0x01,
+       E1000_INVM_CSR_AUTOLOAD_STRUCTURE               = 0x02,
+       E1000_INVM_PHY_REGISTER_AUTOLOAD_STRUCTURE      = 0x03,
+       E1000_INVM_RSA_KEY_SHA256_STRUCTURE             = 0x04,
+       E1000_INVM_INVALIDATED_STRUCTURE                = 0x0F,
+};
+
+#define E1000_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS  8
+#define E1000_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS    1
+#define E1000_INVM_ULT_BYTES_SIZE      8
+#define E1000_INVM_RECORD_SIZE_IN_BYTES        4
+#define E1000_INVM_VER_FIELD_ONE       0x1FF8
+#define E1000_INVM_VER_FIELD_TWO       0x7FE000
+#define E1000_INVM_IMGTYPE_FIELD       0x1F800000
+
+#define E1000_INVM_MAJOR_MASK  0x3F0
+#define E1000_INVM_MINOR_MASK  0xF
+#define E1000_INVM_MAJOR_SHIFT 4
+
+#define ID_LED_DEFAULT_I210            ((ID_LED_OFF1_ON2  << 8) | \
+                                        (ID_LED_DEF1_DEF2 <<  4) | \
+                                        (ID_LED_OFF1_OFF2))
+#define ID_LED_DEFAULT_I210_SERDES     ((ID_LED_DEF1_DEF2 << 8) | \
+                                        (ID_LED_DEF1_DEF2 <<  4) | \
+                                        (ID_LED_DEF1_DEF2))
+
+/* NVM offset defaults for Pearsonville device */
+#define NVM_INIT_CTRL_2_DEFAULT_I211   0X7243
+#define NVM_INIT_CTRL_4_DEFAULT_I211   0x00C1
+#define NVM_LED_1_CFG_DEFAULT_I211     0x0184
+#define NVM_LED_0_2_CFG_DEFAULT_I211   0x200C
+#endif
diff --git a/lib/librte_pmd_e1000/e1000/e1000_ich8lan.c b/lib/librte_pmd_e1000/e1000/e1000_ich8lan.c
new file mode 100644 (file)
index 0000000..f27af88
--- /dev/null
@@ -0,0 +1,4524 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+/*
+ * 82562G 10/100 Network Connection
+ * 82562G-2 10/100 Network Connection
+ * 82562GT 10/100 Network Connection
+ * 82562GT-2 10/100 Network Connection
+ * 82562V 10/100 Network Connection
+ * 82562V-2 10/100 Network Connection
+ * 82566DC-2 Gigabit Network Connection
+ * 82566DC Gigabit Network Connection
+ * 82566DM-2 Gigabit Network Connection
+ * 82566DM Gigabit Network Connection
+ * 82566MC Gigabit Network Connection
+ * 82566MM Gigabit Network Connection
+ * 82567LM Gigabit Network Connection
+ * 82567LF Gigabit Network Connection
+ * 82567V Gigabit Network Connection
+ * 82567LM-2 Gigabit Network Connection
+ * 82567LF-2 Gigabit Network Connection
+ * 82567V-2 Gigabit Network Connection
+ * 82567LF-3 Gigabit Network Connection
+ * 82567LM-3 Gigabit Network Connection
+ * 82567LM-4 Gigabit Network Connection
+ * 82577LM Gigabit Network Connection
+ * 82577LC Gigabit Network Connection
+ * 82578DM Gigabit Network Connection
+ * 82578DC Gigabit Network Connection
+ * 82579LM Gigabit Network Connection
+ * 82579V Gigabit Network Connection
+ */
+
+#include "e1000_api.h"
+
+STATIC s32  e1000_init_phy_params_ich8lan(struct e1000_hw *hw);
+STATIC s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw);
+STATIC s32  e1000_init_nvm_params_ich8lan(struct e1000_hw *hw);
+STATIC s32  e1000_init_mac_params_ich8lan(struct e1000_hw *hw);
+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);
+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 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);
+STATIC s32  e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw,
+                                           bool active);
+STATIC s32  e1000_set_d3_lplu_state_ich8lan(struct e1000_hw *hw,
+                                           bool active);
+STATIC s32  e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset,
+                                  u16 words, u16 *data);
+STATIC s32  e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset,
+                                   u16 words, u16 *data);
+STATIC s32  e1000_validate_nvm_checksum_ich8lan(struct e1000_hw *hw);
+STATIC s32  e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw);
+STATIC s32  e1000_valid_led_default_ich8lan(struct e1000_hw *hw,
+                                           u16 *data);
+STATIC s32 e1000_id_led_init_pchlan(struct e1000_hw *hw);
+STATIC s32  e1000_get_bus_info_ich8lan(struct e1000_hw *hw);
+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_get_link_up_info_ich8lan(struct e1000_hw *hw,
+                                          u16 *speed, u16 *duplex);
+STATIC s32  e1000_cleanup_led_ich8lan(struct e1000_hw *hw);
+STATIC s32  e1000_led_on_ich8lan(struct e1000_hw *hw);
+STATIC s32  e1000_led_off_ich8lan(struct e1000_hw *hw);
+STATIC s32  e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link);
+STATIC s32  e1000_setup_led_pchlan(struct e1000_hw *hw);
+STATIC s32  e1000_cleanup_led_pchlan(struct e1000_hw *hw);
+STATIC s32  e1000_led_on_pchlan(struct e1000_hw *hw);
+STATIC s32  e1000_led_off_pchlan(struct e1000_hw *hw);
+STATIC void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw);
+STATIC s32  e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank);
+static s32  e1000_flash_cycle_ich8lan(struct e1000_hw *hw, u32 timeout);
+static s32  e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw);
+static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw);
+static s32  e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw);
+STATIC s32  e1000_read_flash_byte_ich8lan(struct e1000_hw *hw,
+                                         u32 offset, u8 *data);
+static s32  e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
+                                         u8 size, u16 *data);
+STATIC s32  e1000_read_flash_word_ich8lan(struct e1000_hw *hw,
+                                         u32 offset, u16 *data);
+static s32  e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw,
+                                                u32 offset, u8 byte);
+STATIC s32  e1000_write_flash_byte_ich8lan(struct e1000_hw *hw,
+                                          u32 offset, u8 data);
+static s32  e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
+                                          u8 size, u16 data);
+STATIC s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw);
+STATIC void e1000_power_down_phy_copper_ich8lan(struct e1000_hw *hw);
+static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw);
+STATIC void e1000_lan_init_done_ich8lan(struct e1000_hw *hw);
+STATIC s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw);
+STATIC s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw);
+STATIC s32 e1000_k1_workaround_lv(struct e1000_hw *hw);
+STATIC void e1000_gate_hw_phy_config_ich8lan(struct e1000_hw *hw, bool gate);
+
+/* ICH GbE Flash Hardware Sequencing Flash Status Register bit breakdown */
+/* Offset 04h HSFSTS */
+union ich8_hws_flash_status {
+       struct ich8_hsfsts {
+               u16 flcdone:1; /* bit 0 Flash Cycle Done */
+               u16 flcerr:1; /* bit 1 Flash Cycle Error */
+               u16 dael:1; /* bit 2 Direct Access error Log */
+               u16 berasesz:2; /* bit 4:3 Sector Erase Size */
+               u16 flcinprog:1; /* bit 5 flash cycle in Progress */
+               u16 reserved1:2; /* bit 13:6 Reserved */
+               u16 reserved2:6; /* bit 13:6 Reserved */
+               u16 fldesvalid:1; /* bit 14 Flash Descriptor Valid */
+               u16 flockdn:1; /* bit 15 Flash Config Lock-Down */
+       } hsf_status;
+       u16 regval;
+};
+
+/* ICH GbE Flash Hardware Sequencing Flash control Register bit breakdown */
+/* Offset 06h FLCTL */
+union ich8_hws_flash_ctrl {
+       struct ich8_hsflctl {
+               u16 flcgo:1;   /* 0 Flash Cycle Go */
+               u16 flcycle:2;   /* 2:1 Flash Cycle */
+               u16 reserved:5;   /* 7:3 Reserved  */
+               u16 fldbcount:2;   /* 9:8 Flash Data Byte Count */
+               u16 flockdn:6;   /* 15:10 Reserved */
+       } hsf_ctrl;
+       u16 regval;
+};
+
+/* ICH Flash Region Access Permissions */
+union ich8_hws_flash_regacc {
+       struct ich8_flracc {
+               u32 grra:8; /* 0:7 GbE region Read Access */
+               u32 grwa:8; /* 8:15 GbE region Write Access */
+               u32 gmrag:8; /* 23:16 GbE Master Read Access Grant */
+               u32 gmwag:8; /* 31:24 GbE Master Write Access Grant */
+       } hsf_flregacc;
+       u16 regval;
+};
+
+/**
+ *  e1000_phy_is_accessible_pchlan - Check if able to access PHY registers
+ *  @hw: pointer to the HW structure
+ *
+ *  Test access to the PHY registers by reading the PHY ID registers.  If
+ *  the PHY ID is already known (e.g. resume path) compare it with known ID,
+ *  otherwise assume the read PHY ID is correct if it is valid.
+ *
+ *  Assumes the sw/fw/hw semaphore is already acquired.
+ **/
+static bool e1000_phy_is_accessible_pchlan(struct e1000_hw *hw)
+{
+       u16 phy_reg = 0;
+       u32 phy_id = 0;
+       s32 ret_val;
+       u16 retry_count;
+
+       for (retry_count = 0; retry_count < 2; retry_count++) {
+               ret_val = hw->phy.ops.read_reg_locked(hw, PHY_ID1, &phy_reg);
+               if (ret_val || (phy_reg == 0xFFFF))
+                       continue;
+               phy_id = (u32)(phy_reg << 16);
+
+               ret_val = hw->phy.ops.read_reg_locked(hw, PHY_ID2, &phy_reg);
+               if (ret_val || (phy_reg == 0xFFFF)) {
+                       phy_id = 0;
+                       continue;
+               }
+               phy_id |= (u32)(phy_reg & PHY_REVISION_MASK);
+               break;
+       }
+
+       if (hw->phy.id) {
+               if  (hw->phy.id == phy_id)
+                       return true;
+       } else if (phy_id) {
+               hw->phy.id = phy_id;
+               hw->phy.revision = (u32)(phy_reg & ~PHY_REVISION_MASK);
+               return true;
+       }
+
+       /*
+        * 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);
+
+       return !ret_val;
+}
+
+/**
+ *  e1000_init_phy_workarounds_pchlan - PHY initialization workarounds
+ *  @hw: pointer to the HW structure
+ *
+ *  Workarounds/flow necessary for PHY initialization during driver load
+ *  and resume paths.
+ **/
+static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
+{
+       u32 mac_reg, fwsm = E1000_READ_REG(hw, E1000_FWSM);
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_init_phy_workarounds_pchlan");
+
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val) {
+               DEBUGOUT("Failed to initialize PHY flow\n");
+               return ret_val;
+       }
+
+       /*
+        * 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
+                */
+               if ((hw->mac.type == e1000_pch2lan) &&
+                   !(fwsm & E1000_ICH_FWSM_FW_VALID))
+                       e1000_gate_hw_phy_config_ich8lan(hw, true);
+
+               if (e1000_phy_is_accessible_pchlan(hw)) {
+                       break;
+               }
+
+               /* fall-through */
+       case e1000_pchlan:
+               if ((hw->mac.type == e1000_pchlan) &&
+                   (fwsm & E1000_ICH_FWSM_FW_VALID))
+                       break;
+
+               if (hw->phy.ops.check_reset_block(hw)) {
+                       DEBUGOUT("Required LANPHYPC toggle blocked by ME\n");
+                       break;
+               }
+
+               DEBUGOUT("Toggling LANPHYPC\n");
+
+               /* 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);
+               msec_delay(50);
+               break;
+       default:
+               break;
+       }
+
+       hw->phy.ops.release(hw);
+
+       /*
+        * 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);
+
+       /* Ungate automatic PHY configuration on non-managed 82579 */
+       if ((hw->mac.type == e1000_pch2lan) &&
+           !(fwsm & E1000_ICH_FWSM_FW_VALID)) {
+               msec_delay(10);
+               e1000_gate_hw_phy_config_ich8lan(hw, false);
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_init_phy_params_pchlan - Initialize PHY function pointers
+ *  @hw: pointer to the HW structure
+ *
+ *  Initialize family-specific PHY parameters and function pointers.
+ **/
+STATIC s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_init_phy_params_pchlan");
+
+       phy->addr               = 1;
+       phy->reset_delay_us     = 100;
+
+       phy->ops.acquire        = e1000_acquire_swflag_ich8lan;
+       phy->ops.check_reset_block = e1000_check_reset_block_ich8lan;
+       phy->ops.get_cfg_done   = e1000_get_cfg_done_ich8lan;
+       phy->ops.set_page       = e1000_set_page_igp;
+       phy->ops.read_reg       = e1000_read_phy_reg_hv;
+       phy->ops.read_reg_locked = e1000_read_phy_reg_hv_locked;
+       phy->ops.read_reg_page  = e1000_read_phy_reg_page_hv;
+       phy->ops.release        = e1000_release_swflag_ich8lan;
+       phy->ops.reset          = e1000_phy_hw_reset_ich8lan;
+       phy->ops.set_d0_lplu_state = e1000_set_lplu_state_pchlan;
+       phy->ops.set_d3_lplu_state = e1000_set_lplu_state_pchlan;
+       phy->ops.write_reg      = e1000_write_phy_reg_hv;
+       phy->ops.write_reg_locked = e1000_write_phy_reg_hv_locked;
+       phy->ops.write_reg_page = e1000_write_phy_reg_page_hv;
+       phy->ops.power_up       = e1000_power_up_phy_copper;
+       phy->ops.power_down     = e1000_power_down_phy_copper_ich8lan;
+       phy->autoneg_mask       = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+
+       phy->id = e1000_phy_unknown;
+
+       ret_val = e1000_init_phy_workarounds_pchlan(hw);
+       if (ret_val)
+               return ret_val;
+
+       if (phy->id == e1000_phy_unknown)
+               switch (hw->mac.type) {
+               default:
+                       ret_val = e1000_get_phy_id(hw);
+                       if (ret_val)
+                               return ret_val;
+                       if ((phy->id != 0) && (phy->id != PHY_REVISION_MASK))
+                               break;
+                       /* fall-through */
+               case e1000_pch2lan:
+                       /*
+                        * In case the PHY needs to be in mdio slow mode,
+                        * set slow mode and try to get the PHY id again.
+                        */
+                       ret_val = e1000_set_mdio_slow_mode_hv(hw);
+                       if (ret_val)
+                               return ret_val;
+                       ret_val = e1000_get_phy_id(hw);
+                       if (ret_val)
+                               return ret_val;
+                       break;
+               }
+       phy->type = e1000_get_phy_type_from_id(phy->id);
+
+       switch (phy->type) {
+       case e1000_phy_82577:
+       case e1000_phy_82579:
+       case e1000_phy_i217:
+               phy->ops.check_polarity = e1000_check_polarity_82577;
+               phy->ops.force_speed_duplex =
+                       e1000_phy_force_speed_duplex_82577;
+               phy->ops.get_cable_length = e1000_get_cable_length_82577;
+               phy->ops.get_info = e1000_get_phy_info_82577;
+               phy->ops.commit = e1000_phy_sw_reset_generic;
+               break;
+       case e1000_phy_82578:
+               phy->ops.check_polarity = e1000_check_polarity_m88;
+               phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88;
+               phy->ops.get_cable_length = e1000_get_cable_length_m88;
+               phy->ops.get_info = e1000_get_phy_info_m88;
+               break;
+       default:
+               ret_val = -E1000_ERR_PHY;
+               break;
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_init_phy_params_ich8lan - Initialize PHY function pointers
+ *  @hw: pointer to the HW structure
+ *
+ *  Initialize family-specific PHY parameters and function pointers.
+ **/
+STATIC s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       s32 ret_val;
+       u16 i = 0;
+
+       DEBUGFUNC("e1000_init_phy_params_ich8lan");
+
+       phy->addr               = 1;
+       phy->reset_delay_us     = 100;
+
+       phy->ops.acquire        = e1000_acquire_swflag_ich8lan;
+       phy->ops.check_reset_block = e1000_check_reset_block_ich8lan;
+       phy->ops.get_cable_length = e1000_get_cable_length_igp_2;
+       phy->ops.get_cfg_done   = e1000_get_cfg_done_ich8lan;
+       phy->ops.read_reg       = e1000_read_phy_reg_igp;
+       phy->ops.release        = e1000_release_swflag_ich8lan;
+       phy->ops.reset          = e1000_phy_hw_reset_ich8lan;
+       phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_ich8lan;
+       phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_ich8lan;
+       phy->ops.write_reg      = e1000_write_phy_reg_igp;
+       phy->ops.power_up       = e1000_power_up_phy_copper;
+       phy->ops.power_down     = e1000_power_down_phy_copper_ich8lan;
+
+       /*
+        * We may need to do this twice - once for IGP and if that fails,
+        * we'll set BM func pointers and try again
+        */
+       ret_val = e1000_determine_phy_address(hw);
+       if (ret_val) {
+               phy->ops.write_reg = e1000_write_phy_reg_bm;
+               phy->ops.read_reg  = e1000_read_phy_reg_bm;
+               ret_val = e1000_determine_phy_address(hw);
+               if (ret_val) {
+                       DEBUGOUT("Cannot determine PHY addr. Erroring out\n");
+                       return ret_val;
+               }
+       }
+
+       phy->id = 0;
+       while ((e1000_phy_unknown == e1000_get_phy_type_from_id(phy->id)) &&
+              (i++ < 100)) {
+               msec_delay(1);
+               ret_val = e1000_get_phy_id(hw);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       /* Verify phy id */
+       switch (phy->id) {
+       case IGP03E1000_E_PHY_ID:
+               phy->type = e1000_phy_igp_3;
+               phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+               phy->ops.read_reg_locked = e1000_read_phy_reg_igp_locked;
+               phy->ops.write_reg_locked = e1000_write_phy_reg_igp_locked;
+               phy->ops.get_info = e1000_get_phy_info_igp;
+               phy->ops.check_polarity = e1000_check_polarity_igp;
+               phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_igp;
+               break;
+       case IFE_E_PHY_ID:
+       case IFE_PLUS_E_PHY_ID:
+       case IFE_C_E_PHY_ID:
+               phy->type = e1000_phy_ife;
+               phy->autoneg_mask = E1000_ALL_NOT_GIG;
+               phy->ops.get_info = e1000_get_phy_info_ife;
+               phy->ops.check_polarity = e1000_check_polarity_ife;
+               phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_ife;
+               break;
+       case BME1000_E_PHY_ID:
+               phy->type = e1000_phy_bm;
+               phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+               phy->ops.read_reg = e1000_read_phy_reg_bm;
+               phy->ops.write_reg = e1000_write_phy_reg_bm;
+               phy->ops.commit = e1000_phy_sw_reset_generic;
+               phy->ops.get_info = e1000_get_phy_info_m88;
+               phy->ops.check_polarity = e1000_check_polarity_m88;
+               phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88;
+               break;
+       default:
+               return -E1000_ERR_PHY;
+               break;
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_nvm_params_ich8lan - Initialize NVM function pointers
+ *  @hw: pointer to the HW structure
+ *
+ *  Initialize family-specific NVM parameters and function
+ *  pointers.
+ **/
+STATIC s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
+       u32 gfpreg, sector_base_addr, sector_end_addr;
+       u16 i;
+
+       DEBUGFUNC("e1000_init_nvm_params_ich8lan");
+
+       /* Can't read flash registers if the register set isn't mapped. */
+       if (!hw->flash_address) {
+               DEBUGOUT("ERROR: Flash registers not mapped\n");
+               return -E1000_ERR_CONFIG;
+       }
+
+       nvm->type = e1000_nvm_flash_sw;
+
+       gfpreg = E1000_READ_FLASH_REG(hw, ICH_FLASH_GFPREG);
+
+       /*
+        * sector_X_addr is a "sector"-aligned address (4096 bytes)
+        * Add 1 to sector_end_addr since this sector is included in
+        * the overall size.
+        */
+       sector_base_addr = gfpreg & FLASH_GFPREG_BASE_MASK;
+       sector_end_addr = ((gfpreg >> 16) & FLASH_GFPREG_BASE_MASK) + 1;
+
+       /* flash_base_addr is byte-aligned */
+       nvm->flash_base_addr = sector_base_addr << FLASH_SECTOR_ADDR_SHIFT;
+
+       /*
+        * find total size of the NVM, then cut in half since the total
+        * size represents two separate NVM banks.
+        */
+       nvm->flash_bank_size = (sector_end_addr - sector_base_addr)
+                               << FLASH_SECTOR_ADDR_SHIFT;
+       nvm->flash_bank_size /= 2;
+       /* Adjust to word count */
+       nvm->flash_bank_size /= sizeof(u16);
+
+       nvm->word_size = E1000_SHADOW_RAM_WORDS;
+
+       /* Clear shadow ram */
+       for (i = 0; i < nvm->word_size; i++) {
+               dev_spec->shadow_ram[i].modified = false;
+               dev_spec->shadow_ram[i].value    = 0xFFFF;
+       }
+
+       E1000_MUTEX_INIT(&dev_spec->nvm_mutex);
+       E1000_MUTEX_INIT(&dev_spec->swflag_mutex);
+
+       /* Function Pointers */
+       nvm->ops.acquire        = e1000_acquire_nvm_ich8lan;
+       nvm->ops.release        = e1000_release_nvm_ich8lan;
+       nvm->ops.read           = e1000_read_nvm_ich8lan;
+       nvm->ops.update         = e1000_update_nvm_checksum_ich8lan;
+       nvm->ops.valid_led_default = e1000_valid_led_default_ich8lan;
+       nvm->ops.validate       = e1000_validate_nvm_checksum_ich8lan;
+       nvm->ops.write          = e1000_write_nvm_ich8lan;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_mac_params_ich8lan - Initialize MAC function pointers
+ *  @hw: pointer to the HW structure
+ *
+ *  Initialize family-specific MAC parameters and function
+ *  pointers.
+ **/
+STATIC s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+
+       DEBUGFUNC("e1000_init_mac_params_ich8lan");
+
+       /* Set media type function pointer */
+       hw->phy.media_type = e1000_media_type_copper;
+
+       /* Set mta register count */
+       mac->mta_reg_count = 32;
+       /* Set rar entry count */
+       mac->rar_entry_count = E1000_ICH_RAR_ENTRIES;
+       if (mac->type == e1000_ich8lan)
+               mac->rar_entry_count--;
+       /* Set if part includes ASF firmware */
+       mac->asf_firmware_present = true;
+       /* FWSM register */
+       mac->has_fwsm = true;
+       /* ARC subsystem not supported */
+       mac->arc_subsystem_valid = false;
+       /* Adaptive IFS supported */
+       mac->adaptive_ifs = true;
+
+       /* Function pointers */
+
+       /* bus type/speed/width */
+       mac->ops.get_bus_info = e1000_get_bus_info_ich8lan;
+       /* function id */
+       mac->ops.set_lan_id = e1000_set_lan_id_single_port;
+       /* reset */
+       mac->ops.reset_hw = e1000_reset_hw_ich8lan;
+       /* hw initialization */
+       mac->ops.init_hw = e1000_init_hw_ich8lan;
+       /* link setup */
+       mac->ops.setup_link = e1000_setup_link_ich8lan;
+       /* physical interface setup */
+       mac->ops.setup_physical_interface = e1000_setup_copper_link_ich8lan;
+       /* check for link */
+       mac->ops.check_for_link = e1000_check_for_copper_link_ich8lan;
+       /* link info */
+       mac->ops.get_link_up_info = e1000_get_link_up_info_ich8lan;
+       /* multicast address update */
+       mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
+       /* clear hardware counters */
+       mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_ich8lan;
+
+       /* LED and other operations */
+       switch (mac->type) {
+       case e1000_ich8lan:
+       case e1000_ich9lan:
+       case e1000_ich10lan:
+               /* check management mode */
+               mac->ops.check_mng_mode = e1000_check_mng_mode_ich8lan;
+               /* ID LED init */
+               mac->ops.id_led_init = e1000_id_led_init_generic;
+               /* blink LED */
+               mac->ops.blink_led = e1000_blink_led_generic;
+               /* setup LED */
+               mac->ops.setup_led = e1000_setup_led_generic;
+               /* cleanup LED */
+               mac->ops.cleanup_led = e1000_cleanup_led_ich8lan;
+               /* turn on/off LED */
+               mac->ops.led_on = e1000_led_on_ich8lan;
+               mac->ops.led_off = e1000_led_off_ich8lan;
+               break;
+       case e1000_pch2lan:
+               mac->rar_entry_count = E1000_PCH2_RAR_ENTRIES;
+               mac->ops.rar_set = e1000_rar_set_pch2lan;
+               /* fall-through */
+       case e1000_pchlan:
+               /* check management mode */
+               mac->ops.check_mng_mode = e1000_check_mng_mode_pchlan;
+               /* ID LED init */
+               mac->ops.id_led_init = e1000_id_led_init_pchlan;
+               /* setup LED */
+               mac->ops.setup_led = e1000_setup_led_pchlan;
+               /* cleanup LED */
+               mac->ops.cleanup_led = e1000_cleanup_led_pchlan;
+               /* turn on/off LED */
+               mac->ops.led_on = e1000_led_on_pchlan;
+               mac->ops.led_off = e1000_led_off_pchlan;
+               break;
+       default:
+               break;
+       }
+
+       /* 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;
+}
+
+/**
+ *  __e1000_access_emi_reg_locked - Read/write EMI register
+ *  @hw: pointer to the HW structure
+ *  @addr: EMI address to program
+ *  @data: pointer to value to read/write from/to the EMI address
+ *  @read: boolean flag to indicate read or write
+ *
+ *  This helper function assumes the SW/FW/HW Semaphore is already acquired.
+ **/
+STATIC s32 __e1000_access_emi_reg_locked(struct e1000_hw *hw, u16 address,
+                                        u16 *data, bool read)
+{
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("__e1000_access_emi_reg_locked");
+
+       ret_val = hw->phy.ops.write_reg_locked(hw, I82579_EMI_ADDR, address);
+       if (ret_val)
+               return ret_val;
+
+       if (read)
+               ret_val = hw->phy.ops.read_reg_locked(hw, I82579_EMI_DATA,
+                                                     data);
+       else
+               ret_val = hw->phy.ops.write_reg_locked(hw, I82579_EMI_DATA,
+                                                      *data);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_read_emi_reg_locked - Read Extended Management Interface register
+ *  @hw: pointer to the HW structure
+ *  @addr: EMI address to program
+ *  @data: value to be read from the EMI address
+ *
+ *  Assumes the SW/FW/HW Semaphore is already acquired.
+ **/
+s32 e1000_read_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 *data)
+{
+       DEBUGFUNC("e1000_read_emi_reg_locked");
+
+       return __e1000_access_emi_reg_locked(hw, addr, data, true);
+}
+
+/**
+ *  e1000_write_emi_reg_locked - Write Extended Management Interface register
+ *  @hw: pointer to the HW structure
+ *  @addr: EMI address to program
+ *  @data: value to be written to the EMI address
+ *
+ *  Assumes the SW/FW/HW Semaphore is already acquired.
+ **/
+STATIC s32 e1000_write_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 data)
+{
+       DEBUGFUNC("e1000_read_emi_reg_locked");
+
+       return __e1000_access_emi_reg_locked(hw, addr, &data, false);
+}
+
+/**
+ *  e1000_set_eee_pchlan - Enable/disable EEE support
+ *  @hw: pointer to the HW structure
+ *
+ *  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.
+ **/
+STATIC 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;
+
+       DEBUGFUNC("e1000_set_eee_pchlan");
+
+       if ((hw->phy.type != e1000_phy_82579) &&
+           (hw->phy.type != e1000_phy_i217))
+               return E1000_SUCCESS;
+
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val)
+               return ret_val;
+
+       ret_val = hw->phy.ops.read_reg_locked(hw, I82579_LPI_CTRL, &lpi_ctrl);
+       if (ret_val)
+               goto release;
+
+       /* Clear bits that enable EEE in various speeds */
+       lpi_ctrl &= ~I82579_LPI_CTRL_ENABLE_MASK;
+
+       /* 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.
+                */
+               if (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) {
+                       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
+                                * 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_check_for_copper_link_ich8lan - Check for link (Copper)
+ *  @hw: pointer to the HW structure
+ *
+ *  Checks to see of the link status of the hardware has changed.  If a
+ *  change in link status has been detected, then we read the PHY registers
+ *  to get the current speed/duplex if link exists.
+ **/
+static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       s32 ret_val;
+       bool link;
+       u16 phy_reg;
+
+       DEBUGFUNC("e1000_check_for_copper_link_ich8lan");
+
+       /*
+        * We only want to go out to the PHY registers to see if Auto-Neg
+        * has completed and/or if our link status has changed.  The
+        * get_link_status flag is set upon receiving a Link Status
+        * Change or Rx Sequence Error interrupt.
+        */
+       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 (hw->mac.type == e1000_pchlan) {
+               ret_val = e1000_k1_gig_workaround_hv(hw, link);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       /* Clear link partner's EEE ability */
+       hw->dev_spec.ich8lan.eee_lp_ability = 0;
+
+       if (!link)
+               return E1000_SUCCESS; /* No link detected */
+
+       mac->get_link_status = false;
+
+       switch (hw->mac.type) {
+       case e1000_pch2lan:
+               ret_val = e1000_k1_workaround_lv(hw);
+               if (ret_val)
+                       return ret_val;
+               /* fall-thru */
+       case e1000_pchlan:
+               if (hw->phy.type == e1000_phy_82578) {
+                       ret_val = e1000_link_stall_workaround_hv(hw);
+                       if (ret_val)
+                               return ret_val;
+               }
+
+               /*
+                * Workaround for PCHx parts in half-duplex:
+                * Set the number of preambles removed from the packet
+                * when it is passed from the PHY to the MAC to prevent
+                * the MAC from misinterpreting the packet type.
+                */
+               hw->phy.ops.read_reg(hw, HV_KMRN_FIFO_CTRLSTA, &phy_reg);
+               phy_reg &= ~HV_KMRN_FIFO_CTRLSTA_PREAMBLE_MASK;
+
+               if ((E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_FD) !=
+                   E1000_STATUS_FD)
+                       phy_reg |= (1 << HV_KMRN_FIFO_CTRLSTA_PREAMBLE_SHIFT);
+
+               hw->phy.ops.write_reg(hw, HV_KMRN_FIFO_CTRLSTA, phy_reg);
+               break;
+       default:
+               break;
+       }
+
+       /*
+        * Check if there was DownShift, must be checked
+        * immediately after link-up
+        */
+       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 we are forcing speed/duplex, then we simply return since
+        * we have already determined whether we have link or not.
+        */
+       if (!mac->autoneg)
+               return -E1000_ERR_CONFIG;
+
+       /*
+        * Auto-Neg is enabled.  Auto Speed Detection takes care
+        * of MAC speed/duplex configuration.  So we only need to
+        * configure Collision Distance in the MAC.
+        */
+       mac->ops.config_collision_dist(hw);
+
+       /*
+        * Configure Flow Control now that Auto-Neg has completed.
+        * First, we need to restore the desired flow control
+        * settings because we may have had to re-autoneg with a
+        * different link partner.
+        */
+       ret_val = e1000_config_fc_after_link_up_generic(hw);
+       if (ret_val)
+               DEBUGOUT("Error configuring flow control\n");
+
+       return ret_val;
+}
+
+/**
+ *  e1000_init_function_pointers_ich8lan - Initialize ICH8 function pointers
+ *  @hw: pointer to the HW structure
+ *
+ *  Initialize family-specific function pointers for PHY, MAC, and NVM.
+ **/
+void e1000_init_function_pointers_ich8lan(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_init_function_pointers_ich8lan");
+
+       hw->mac.ops.init_params = e1000_init_mac_params_ich8lan;
+       hw->nvm.ops.init_params = e1000_init_nvm_params_ich8lan;
+       switch (hw->mac.type) {
+       case e1000_ich8lan:
+       case e1000_ich9lan:
+       case e1000_ich10lan:
+               hw->phy.ops.init_params = e1000_init_phy_params_ich8lan;
+               break;
+       case e1000_pchlan:
+       case e1000_pch2lan:
+               hw->phy.ops.init_params = e1000_init_phy_params_pchlan;
+               break;
+       default:
+               break;
+       }
+}
+
+/**
+ *  e1000_acquire_nvm_ich8lan - Acquire NVM mutex
+ *  @hw: pointer to the HW structure
+ *
+ *  Acquires the mutex for performing NVM operations.
+ **/
+STATIC s32 e1000_acquire_nvm_ich8lan(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_acquire_nvm_ich8lan");
+
+       E1000_MUTEX_LOCK(&hw->dev_spec.ich8lan.nvm_mutex);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_release_nvm_ich8lan - Release NVM mutex
+ *  @hw: pointer to the HW structure
+ *
+ *  Releases the mutex used while performing NVM operations.
+ **/
+STATIC void e1000_release_nvm_ich8lan(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_release_nvm_ich8lan");
+
+       E1000_MUTEX_UNLOCK(&hw->dev_spec.ich8lan.nvm_mutex);
+
+       return;
+}
+
+/**
+ *  e1000_acquire_swflag_ich8lan - Acquire software control flag
+ *  @hw: pointer to the HW structure
+ *
+ *  Acquires the software control flag for performing PHY and select
+ *  MAC CSR accesses.
+ **/
+STATIC s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
+{
+       u32 extcnf_ctrl, timeout = PHY_CFG_TIMEOUT;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_acquire_swflag_ich8lan");
+
+       E1000_MUTEX_LOCK(&hw->dev_spec.ich8lan.swflag_mutex);
+
+       while (timeout) {
+               extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+               if (!(extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG))
+                       break;
+
+               msec_delay_irq(1);
+               timeout--;
+       }
+
+       if (!timeout) {
+               DEBUGOUT("SW has already locked the resource.\n");
+               ret_val = -E1000_ERR_CONFIG;
+               goto out;
+       }
+
+       timeout = SW_FLAG_TIMEOUT;
+
+       extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG;
+       E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
+
+       while (timeout) {
+               extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+               if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)
+                       break;
+
+               msec_delay_irq(1);
+               timeout--;
+       }
+
+       if (!timeout) {
+               DEBUGOUT2("Failed to acquire the semaphore, FW or HW has it: FWSM=0x%8.8x EXTCNF_CTRL=0x%8.8x)\n",
+                         E1000_READ_REG(hw, E1000_FWSM), extcnf_ctrl);
+               extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
+               E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
+               ret_val = -E1000_ERR_CONFIG;
+               goto out;
+       }
+
+out:
+       if (ret_val)
+               E1000_MUTEX_UNLOCK(&hw->dev_spec.ich8lan.swflag_mutex);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_release_swflag_ich8lan - Release software control flag
+ *  @hw: pointer to the HW structure
+ *
+ *  Releases the software control flag for performing PHY and select
+ *  MAC CSR accesses.
+ **/
+STATIC void e1000_release_swflag_ich8lan(struct e1000_hw *hw)
+{
+       u32 extcnf_ctrl;
+
+       DEBUGFUNC("e1000_release_swflag_ich8lan");
+
+       extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+
+       if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG) {
+               extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
+               E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
+       } else {
+               DEBUGOUT("Semaphore unexpectedly released by sw/fw/hw\n");
+       }
+
+       E1000_MUTEX_UNLOCK(&hw->dev_spec.ich8lan.swflag_mutex);
+
+       return;
+}
+
+/**
+ *  e1000_check_mng_mode_ich8lan - Checks management mode
+ *  @hw: pointer to the HW structure
+ *
+ *  This checks if the adapter has any manageability enabled.
+ *  This is a function pointer entry point only called by read/write
+ *  routines for the PHY and NVM parts.
+ **/
+STATIC bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw)
+{
+       u32 fwsm;
+
+       DEBUGFUNC("e1000_check_mng_mode_ich8lan");
+
+       fwsm = E1000_READ_REG(hw, E1000_FWSM);
+
+       return (fwsm & E1000_ICH_FWSM_FW_VALID) &&
+              ((fwsm & E1000_FWSM_MODE_MASK) ==
+               (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT));
+}
+
+/**
+ *  e1000_check_mng_mode_pchlan - Checks management mode
+ *  @hw: pointer to the HW structure
+ *
+ *  This checks if the adapter has iAMT enabled.
+ *  This is a function pointer entry point only called by read/write
+ *  routines for the PHY and NVM parts.
+ **/
+STATIC bool e1000_check_mng_mode_pchlan(struct e1000_hw *hw)
+{
+       u32 fwsm;
+
+       DEBUGFUNC("e1000_check_mng_mode_pchlan");
+
+       fwsm = E1000_READ_REG(hw, E1000_FWSM);
+
+       return (fwsm & E1000_ICH_FWSM_FW_VALID) &&
+              (fwsm & (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT));
+}
+
+/**
+ *  e1000_rar_set_pch2lan - Set receive address register
+ *  @hw: pointer to the HW structure
+ *  @addr: pointer to the receive address
+ *  @index: receive address array register
+ *
+ *  Sets the receive address array register at index to the address passed
+ *  in by addr.  For 82579, RAR[0] is the base address register that is to
+ *  contain the MAC address but RAR[1-6] are reserved for manageability (ME).
+ *  Use SHRA[0-3] in place of those reserved for ME.
+ **/
+STATIC void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
+{
+       u32 rar_low, rar_high;
+
+       DEBUGFUNC("e1000_rar_set_pch2lan");
+
+       /*
+        * 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;
+       }
+
+       if (index < hw->mac.rar_entry_count) {
+               s32 ret_val;
+
+               ret_val = e1000_acquire_swflag_ich8lan(hw);
+               if (ret_val)
+                       goto out;
+
+               E1000_WRITE_REG(hw, E1000_SHRAL(index - 1), rar_low);
+               E1000_WRITE_FLUSH(hw);
+               E1000_WRITE_REG(hw, E1000_SHRAH(index - 1), rar_high);
+               E1000_WRITE_FLUSH(hw);
+
+               e1000_release_swflag_ich8lan(hw);
+
+               /* verify the register updates */
+               if ((E1000_READ_REG(hw, E1000_SHRAL(index - 1)) == rar_low) &&
+                   (E1000_READ_REG(hw, E1000_SHRAH(index - 1)) == rar_high))
+                       return;
+
+               DEBUGOUT2("SHRA[%d] might be locked by ME - FWSM=0x%8.8x\n",
+                        (index - 1), E1000_READ_REG(hw, E1000_FWSM));
+       }
+
+out:
+       DEBUGOUT1("Failed to write receive address at index %d\n", index);
+}
+
+/**
+ *  e1000_check_reset_block_ich8lan - Check if PHY reset is blocked
+ *  @hw: pointer to the HW structure
+ *
+ *  Checks if firmware is blocking the reset of the PHY.
+ *  This is a function pointer entry point only called by
+ *  reset routines.
+ **/
+STATIC s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw)
+{
+       u32 fwsm;
+
+       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;
+}
+
+/**
+ *  e1000_write_smbus_addr - Write SMBus address to PHY needed during Sx states
+ *  @hw: pointer to the HW structure
+ *
+ *  Assumes semaphore already acquired.
+ *
+ **/
+STATIC s32 e1000_write_smbus_addr(struct e1000_hw *hw)
+{
+       u16 phy_data;
+       u32 strap = E1000_READ_REG(hw, E1000_STRAP);
+       u32 freq = (strap & E1000_STRAP_SMT_FREQ_MASK) >>
+               E1000_STRAP_SMT_FREQ_SHIFT;
+       s32 ret_val = E1000_SUCCESS;
+
+       strap &= E1000_STRAP_SMBUS_ADDRESS_MASK;
+
+       ret_val = e1000_read_phy_reg_hv_locked(hw, HV_SMB_ADDR, &phy_data);
+       if (ret_val)
+               return ret_val;
+
+       phy_data &= ~HV_SMB_ADDR_MASK;
+       phy_data |= (strap >> E1000_STRAP_SMBUS_ADDRESS_SHIFT);
+       phy_data |= HV_SMB_ADDR_PEC_EN | HV_SMB_ADDR_VALID;
+
+       if (hw->phy.type == e1000_phy_i217) {
+               /* Restore SMBus frequency */
+               if (freq--) {
+                       phy_data &= ~HV_SMB_ADDR_FREQ_MASK;
+                       phy_data |= (freq & (1 << 0)) <<
+                               HV_SMB_ADDR_FREQ_LOW_SHIFT;
+                       phy_data |= (freq & (1 << 1)) <<
+                               (HV_SMB_ADDR_FREQ_HIGH_SHIFT - 1);
+               } else {
+                       DEBUGOUT("Unsupported SMB frequency in PHY\n");
+               }
+       }
+
+       return e1000_write_phy_reg_hv_locked(hw, HV_SMB_ADDR, phy_data);
+}
+
+/**
+ *  e1000_sw_lcd_config_ich8lan - SW-based LCD Configuration
+ *  @hw:   pointer to the HW structure
+ *
+ *  SW should configure the LCD from the NVM extended configuration region
+ *  as a workaround for certain parts.
+ **/
+STATIC s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       u32 i, data, cnf_size, cnf_base_addr, sw_cfg_mask;
+       s32 ret_val = E1000_SUCCESS;
+       u16 word_addr, reg_data, reg_addr, phy_page = 0;
+
+       DEBUGFUNC("e1000_sw_lcd_config_ich8lan");
+
+       /*
+        * Initialize the PHY from the NVM on ICH platforms.  This
+        * is needed due to an issue where the NVM configuration is
+        * not properly autoloaded after power transitions.
+        * Therefore, after each PHY reset, we will load the
+        * configuration data out of the NVM manually.
+        */
+       switch (hw->mac.type) {
+       case e1000_ich8lan:
+               if (phy->type != e1000_phy_igp_3)
+                       return ret_val;
+
+               if ((hw->device_id == E1000_DEV_ID_ICH8_IGP_AMT) ||
+                   (hw->device_id == E1000_DEV_ID_ICH8_IGP_C)) {
+                       sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG;
+                       break;
+               }
+               /* Fall-thru */
+       case e1000_pchlan:
+       case e1000_pch2lan:
+               sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M;
+               break;
+       default:
+               return ret_val;
+       }
+
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val)
+               return ret_val;
+
+       data = E1000_READ_REG(hw, E1000_FEXTNVM);
+       if (!(data & sw_cfg_mask))
+               goto release;
+
+       /*
+        * Make sure HW does not configure LCD from PHY
+        * extended configuration before SW configuration
+        */
+       data = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+       if ((hw->mac.type < e1000_pch2lan) &&
+           (data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE))
+                       goto release;
+
+       cnf_size = E1000_READ_REG(hw, E1000_EXTCNF_SIZE);
+       cnf_size &= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK;
+       cnf_size >>= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT;
+       if (!cnf_size)
+               goto release;
+
+       cnf_base_addr = data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK;
+       cnf_base_addr >>= E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT;
+
+       if (((hw->mac.type == e1000_pchlan) &&
+            !(data & E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE)) ||
+           (hw->mac.type > e1000_pchlan)) {
+               /*
+                * HW configures the SMBus address and LEDs when the
+                * OEM and LCD Write Enable bits are set in the NVM.
+                * When both NVM bits are cleared, SW will configure
+                * them instead.
+                */
+               ret_val = e1000_write_smbus_addr(hw);
+               if (ret_val)
+                       goto release;
+
+               data = E1000_READ_REG(hw, E1000_LEDCTL);
+               ret_val = e1000_write_phy_reg_hv_locked(hw, HV_LED_CONFIG,
+                                                       (u16)data);
+               if (ret_val)
+                       goto release;
+       }
+
+       /* Configure LCD from extended configuration region. */
+
+       /* cnf_base_addr is in DWORD */
+       word_addr = (u16)(cnf_base_addr << 1);
+
+       for (i = 0; i < cnf_size; i++) {
+               ret_val = hw->nvm.ops.read(hw, (word_addr + i * 2), 1,
+                                          &reg_data);
+               if (ret_val)
+                       goto release;
+
+               ret_val = hw->nvm.ops.read(hw, (word_addr + i * 2 + 1),
+                                          1, &reg_addr);
+               if (ret_val)
+                       goto release;
+
+               /* Save off the PHY page for future writes. */
+               if (reg_addr == IGP01E1000_PHY_PAGE_SELECT) {
+                       phy_page = reg_data;
+                       continue;
+               }
+
+               reg_addr &= PHY_REG_MASK;
+               reg_addr |= phy_page;
+
+               ret_val = phy->ops.write_reg_locked(hw, (u32)reg_addr,
+                                                   reg_data);
+               if (ret_val)
+                       goto release;
+       }
+
+release:
+       hw->phy.ops.release(hw);
+       return ret_val;
+}
+
+/**
+ *  e1000_k1_gig_workaround_hv - K1 Si workaround
+ *  @hw:   pointer to the HW structure
+ *  @link: link up bool flag
+ *
+ *  If K1 is enabled for 1Gbps, the MAC might stall when transitioning
+ *  from a lower speed.  This workaround disables K1 whenever link is at 1Gig
+ *  If link is down, the function will restore the default K1 setting located
+ *  in the NVM.
+ **/
+STATIC s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u16 status_reg = 0;
+       bool k1_enable = hw->dev_spec.ich8lan.nvm_k1_enabled;
+
+       DEBUGFUNC("e1000_k1_gig_workaround_hv");
+
+       if (hw->mac.type != e1000_pchlan)
+               return E1000_SUCCESS;
+
+       /* Wrap the whole flow with the sw flag */
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val)
+               return ret_val;
+
+       /* Disable K1 when link is 1Gbps, otherwise use the NVM setting */
+       if (link) {
+               if (hw->phy.type == e1000_phy_82578) {
+                       ret_val = hw->phy.ops.read_reg_locked(hw, BM_CS_STATUS,
+                                                             &status_reg);
+                       if (ret_val)
+                               goto release;
+
+                       status_reg &= BM_CS_STATUS_LINK_UP |
+                                     BM_CS_STATUS_RESOLVED |
+                                     BM_CS_STATUS_SPEED_MASK;
+
+                       if (status_reg == (BM_CS_STATUS_LINK_UP |
+                                          BM_CS_STATUS_RESOLVED |
+                                          BM_CS_STATUS_SPEED_1000))
+                               k1_enable = false;
+               }
+
+               if (hw->phy.type == e1000_phy_82577) {
+                       ret_val = hw->phy.ops.read_reg_locked(hw, HV_M_STATUS,
+                                                             &status_reg);
+                       if (ret_val)
+                               goto release;
+
+                       status_reg &= HV_M_STATUS_LINK_UP |
+                                     HV_M_STATUS_AUTONEG_COMPLETE |
+                                     HV_M_STATUS_SPEED_MASK;
+
+                       if (status_reg == (HV_M_STATUS_LINK_UP |
+                                          HV_M_STATUS_AUTONEG_COMPLETE |
+                                          HV_M_STATUS_SPEED_1000))
+                               k1_enable = false;
+               }
+
+               /* Link stall fix for link up */
+               ret_val = hw->phy.ops.write_reg_locked(hw, PHY_REG(770, 19),
+                                                      0x0100);
+               if (ret_val)
+                       goto release;
+
+       } else {
+               /* Link stall fix for link down */
+               ret_val = hw->phy.ops.write_reg_locked(hw, PHY_REG(770, 19),
+                                                      0x4100);
+               if (ret_val)
+                       goto release;
+       }
+
+       ret_val = e1000_configure_k1_ich8lan(hw, k1_enable);
+
+release:
+       hw->phy.ops.release(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_configure_k1_ich8lan - Configure K1 power state
+ *  @hw: pointer to the HW structure
+ *  @enable: K1 state to configure
+ *
+ *  Configure the K1 power state based on the provided parameter.
+ *  Assumes semaphore already acquired.
+ *
+ *  Success returns 0, Failure returns -E1000_ERR_PHY (-2)
+ **/
+s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u32 ctrl_reg = 0;
+       u32 ctrl_ext = 0;
+       u32 reg = 0;
+       u16 kmrn_reg = 0;
+
+       DEBUGFUNC("e1000_configure_k1_ich8lan");
+
+       ret_val = e1000_read_kmrn_reg_locked(hw, E1000_KMRNCTRLSTA_K1_CONFIG,
+                                            &kmrn_reg);
+       if (ret_val)
+               return ret_val;
+
+       if (k1_enable)
+               kmrn_reg |= E1000_KMRNCTRLSTA_K1_ENABLE;
+       else
+               kmrn_reg &= ~E1000_KMRNCTRLSTA_K1_ENABLE;
+
+       ret_val = e1000_write_kmrn_reg_locked(hw, E1000_KMRNCTRLSTA_K1_CONFIG,
+                                             kmrn_reg);
+       if (ret_val)
+               return ret_val;
+
+       usec_delay(20);
+       ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
+       ctrl_reg = E1000_READ_REG(hw, E1000_CTRL);
+
+       reg = ctrl_reg & ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100);
+       reg |= E1000_CTRL_FRCSPD;
+       E1000_WRITE_REG(hw, E1000_CTRL, reg);
+
+       E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_SPD_BYPS);
+       E1000_WRITE_FLUSH(hw);
+       usec_delay(20);
+       E1000_WRITE_REG(hw, E1000_CTRL, ctrl_reg);
+       E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+       E1000_WRITE_FLUSH(hw);
+       usec_delay(20);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_oem_bits_config_ich8lan - SW-based LCD Configuration
+ *  @hw:       pointer to the HW structure
+ *  @d0_state: boolean if entering d0 or d3 device state
+ *
+ *  SW will configure Gbe Disable and LPLU based on the NVM. The four bits are
+ *  collectively called OEM bits.  The OEM Write Enable bit and SW Config bit
+ *  in NVM determines whether HW should configure LPLU and Gbe Disable.
+ **/
+static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state)
+{
+       s32 ret_val = 0;
+       u32 mac_reg;
+       u16 oem_reg;
+
+       DEBUGFUNC("e1000_oem_bits_config_ich8lan");
+
+       if (hw->mac.type < e1000_pchlan)
+               return ret_val;
+
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val)
+               return ret_val;
+
+       if (hw->mac.type == e1000_pchlan) {
+               mac_reg = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+               if (mac_reg & E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE)
+                       goto release;
+       }
+
+       mac_reg = E1000_READ_REG(hw, E1000_FEXTNVM);
+       if (!(mac_reg & E1000_FEXTNVM_SW_CONFIG_ICH8M))
+               goto release;
+
+       mac_reg = E1000_READ_REG(hw, E1000_PHY_CTRL);
+
+       ret_val = hw->phy.ops.read_reg_locked(hw, HV_OEM_BITS, &oem_reg);
+       if (ret_val)
+               goto release;
+
+       oem_reg &= ~(HV_OEM_BITS_GBE_DIS | HV_OEM_BITS_LPLU);
+
+       if (d0_state) {
+               if (mac_reg & E1000_PHY_CTRL_GBE_DISABLE)
+                       oem_reg |= HV_OEM_BITS_GBE_DIS;
+
+               if (mac_reg & E1000_PHY_CTRL_D0A_LPLU)
+                       oem_reg |= HV_OEM_BITS_LPLU;
+       } else {
+               if (mac_reg & (E1000_PHY_CTRL_GBE_DISABLE |
+                   E1000_PHY_CTRL_NOND0A_GBE_DISABLE))
+                       oem_reg |= HV_OEM_BITS_GBE_DIS;
+
+               if (mac_reg & (E1000_PHY_CTRL_D0A_LPLU |
+                   E1000_PHY_CTRL_NOND0A_LPLU))
+                       oem_reg |= HV_OEM_BITS_LPLU;
+       }
+
+       /* Set Restart auto-neg to activate the bits */
+       if ((d0_state || (hw->mac.type != e1000_pchlan)) &&
+           !hw->phy.ops.check_reset_block(hw))
+               oem_reg |= HV_OEM_BITS_RESTART_AN;
+
+       ret_val = hw->phy.ops.write_reg_locked(hw, HV_OEM_BITS, oem_reg);
+
+release:
+       hw->phy.ops.release(hw);
+
+       return ret_val;
+}
+
+
+/**
+ *  e1000_set_mdio_slow_mode_hv - Set slow MDIO access mode
+ *  @hw:   pointer to the HW structure
+ **/
+STATIC s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw)
+{
+       s32 ret_val;
+       u16 data;
+
+       DEBUGFUNC("e1000_set_mdio_slow_mode_hv");
+
+       ret_val = hw->phy.ops.read_reg(hw, HV_KMRN_MODE_CTRL, &data);
+       if (ret_val)
+               return ret_val;
+
+       data |= HV_KMRN_MDIO_SLOW;
+
+       ret_val = hw->phy.ops.write_reg(hw, HV_KMRN_MODE_CTRL, data);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_hv_phy_workarounds_ich8lan - A series of Phy workarounds to be
+ *  done after every PHY reset.
+ **/
+STATIC s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u16 phy_data;
+
+       DEBUGFUNC("e1000_hv_phy_workarounds_ich8lan");
+
+       if (hw->mac.type != e1000_pchlan)
+               return E1000_SUCCESS;
+
+       /* Set MDIO slow mode before any other MDIO access */
+       if (hw->phy.type == e1000_phy_82577) {
+               ret_val = e1000_set_mdio_slow_mode_hv(hw);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       if (((hw->phy.type == e1000_phy_82577) &&
+            ((hw->phy.revision == 1) || (hw->phy.revision == 2))) ||
+           ((hw->phy.type == e1000_phy_82578) && (hw->phy.revision == 1))) {
+               /* Disable generation of early preamble */
+               ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 25), 0x4431);
+               if (ret_val)
+                       return ret_val;
+
+               /* Preamble tuning for SSC */
+               ret_val = hw->phy.ops.write_reg(hw, HV_KMRN_FIFO_CTRLSTA,
+                                               0xA204);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       if (hw->phy.type == e1000_phy_82578) {
+               /*
+                * Return registers to default by doing a soft reset then
+                * writing 0x3140 to the control register.
+                */
+               if (hw->phy.revision < 2) {
+                       e1000_phy_sw_reset_generic(hw);
+                       ret_val = hw->phy.ops.write_reg(hw, PHY_CONTROL,
+                                                       0x3140);
+               }
+       }
+
+       /* Select page 0 */
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val)
+               return ret_val;
+
+       hw->phy.addr = 1;
+       ret_val = e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, 0);
+       hw->phy.ops.release(hw);
+       if (ret_val)
+               return ret_val;
+
+       /*
+        * Configure the K1 Si workaround during phy reset assuming there is
+        * link so that it disables K1 if link is in 1Gbps.
+        */
+       ret_val = e1000_k1_gig_workaround_hv(hw, true);
+       if (ret_val)
+               return ret_val;
+
+       /* Workaround for link disconnects on a busy hub in half duplex */
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val)
+               return ret_val;
+       ret_val = hw->phy.ops.read_reg_locked(hw, BM_PORT_GEN_CFG, &phy_data);
+       if (ret_val)
+               goto release;
+       ret_val = hw->phy.ops.write_reg_locked(hw, BM_PORT_GEN_CFG,
+                                              phy_data & 0x00FF);
+       if (ret_val)
+               goto release;
+
+       /* set MSE higher to enable link to stay up when noise is high */
+       ret_val = e1000_write_emi_reg_locked(hw, I82577_MSE_THRESHOLD, 0x0034);
+release:
+       hw->phy.ops.release(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_copy_rx_addrs_to_phy_ich8lan - Copy Rx addresses from MAC to PHY
+ *  @hw:   pointer to the HW structure
+ **/
+void e1000_copy_rx_addrs_to_phy_ich8lan(struct e1000_hw *hw)
+{
+       u32 mac_reg;
+       u16 i, phy_reg = 0;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_copy_rx_addrs_to_phy_ich8lan");
+
+       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;
+
+       /* Copy both RAL/H (rar_entry_count) and SHRAL/H (+4) to PHY */
+       for (i = 0; i < (hw->mac.rar_entry_count + 4); 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));
+               hw->phy.ops.write_reg_page(hw, BM_RAR_M(i),
+                                          (u16)((mac_reg >> 16) & 0xFFFF));
+
+               mac_reg = E1000_READ_REG(hw, E1000_RAH(i));
+               hw->phy.ops.write_reg_page(hw, BM_RAR_H(i),
+                                          (u16)(mac_reg & 0xFFFF));
+               hw->phy.ops.write_reg_page(hw, BM_RAR_CTRL(i),
+                                          (u16)((mac_reg & E1000_RAH_AV)
+                                                >> 16));
+       }
+
+       e1000_disable_phy_wakeup_reg_access_bm(hw, &phy_reg);
+
+release:
+       hw->phy.ops.release(hw);
+}
+
+static u32 e1000_calc_rx_da_crc(u8 mac[])
+{
+       u32 poly = 0xEDB88320;  /* Polynomial for 802.3 CRC calculation */
+       u32 i, j, mask, crc;
+
+       DEBUGFUNC("e1000_calc_rx_da_crc");
+
+       crc = 0xffffffff;
+       for (i = 0; i < 6; i++) {
+               crc = crc ^ mac[i];
+               for (j = 8; j > 0; j--) {
+                       mask = (crc & 1) * (-1);
+                       crc = (crc >> 1) ^ (poly & mask);
+               }
+       }
+       return ~crc;
+}
+
+/**
+ *  e1000_lv_jumbo_workaround_ich8lan - required for jumbo frame operation
+ *  with 82579 PHY
+ *  @hw: pointer to the HW structure
+ *  @enable: flag to enable/disable workaround when enabling/disabling jumbos
+ **/
+s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u16 phy_reg, data;
+       u32 mac_reg;
+       u16 i;
+
+       DEBUGFUNC("e1000_lv_jumbo_workaround_ich8lan");
+
+       if (hw->mac.type != e1000_pch2lan)
+               return E1000_SUCCESS;
+
+       /* disable Rx path while enabling/disabling workaround */
+       hw->phy.ops.read_reg(hw, PHY_REG(769, 20), &phy_reg);
+       ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 20),
+                                       phy_reg | (1 << 14));
+       if (ret_val)
+               return ret_val;
+
+       if (enable) {
+               /*
+                * Write Rx addresses (rar_entry_count for RAL/H, +4 for
+                * SHRAL/H) and initial CRC values to the MAC
+                */
+               for (i = 0; i < (hw->mac.rar_entry_count + 4); i++) {
+                       u8 mac_addr[ETH_ADDR_LEN] = {0};
+                       u32 addr_high, addr_low;
+
+                       addr_high = E1000_READ_REG(hw, E1000_RAH(i));
+                       if (!(addr_high & E1000_RAH_AV))
+                               continue;
+                       addr_low = E1000_READ_REG(hw, E1000_RAL(i));
+                       mac_addr[0] = (addr_low & 0xFF);
+                       mac_addr[1] = ((addr_low >> 8) & 0xFF);
+                       mac_addr[2] = ((addr_low >> 16) & 0xFF);
+                       mac_addr[3] = ((addr_low >> 24) & 0xFF);
+                       mac_addr[4] = (addr_high & 0xFF);
+                       mac_addr[5] = ((addr_high >> 8) & 0xFF);
+
+                       E1000_WRITE_REG(hw, E1000_PCH_RAICC(i),
+                                       e1000_calc_rx_da_crc(mac_addr));
+               }
+
+               /* Write Rx addresses to the PHY */
+               e1000_copy_rx_addrs_to_phy_ich8lan(hw);
+
+               /* Enable jumbo frame workaround in the MAC */
+               mac_reg = E1000_READ_REG(hw, E1000_FFLT_DBG);
+               mac_reg &= ~(1 << 14);
+               mac_reg |= (7 << 15);
+               E1000_WRITE_REG(hw, E1000_FFLT_DBG, mac_reg);
+
+               mac_reg = E1000_READ_REG(hw, E1000_RCTL);
+               mac_reg |= E1000_RCTL_SECRC;
+               E1000_WRITE_REG(hw, E1000_RCTL, mac_reg);
+
+               ret_val = e1000_read_kmrn_reg_generic(hw,
+                                               E1000_KMRNCTRLSTA_CTRL_OFFSET,
+                                               &data);
+               if (ret_val)
+                       return ret_val;
+               ret_val = e1000_write_kmrn_reg_generic(hw,
+                                               E1000_KMRNCTRLSTA_CTRL_OFFSET,
+                                               data | (1 << 0));
+               if (ret_val)
+                       return ret_val;
+               ret_val = e1000_read_kmrn_reg_generic(hw,
+                                               E1000_KMRNCTRLSTA_HD_CTRL,
+                                               &data);
+               if (ret_val)
+                       return ret_val;
+               data &= ~(0xF << 8);
+               data |= (0xB << 8);
+               ret_val = e1000_write_kmrn_reg_generic(hw,
+                                               E1000_KMRNCTRLSTA_HD_CTRL,
+                                               data);
+               if (ret_val)
+                       return ret_val;
+
+               /* Enable jumbo frame workaround in the PHY */
+               hw->phy.ops.read_reg(hw, PHY_REG(769, 23), &data);
+               data &= ~(0x7F << 5);
+               data |= (0x37 << 5);
+               ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 23), data);
+               if (ret_val)
+                       return ret_val;
+               hw->phy.ops.read_reg(hw, PHY_REG(769, 16), &data);
+               data &= ~(1 << 13);
+               ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 16), data);
+               if (ret_val)
+                       return ret_val;
+               hw->phy.ops.read_reg(hw, PHY_REG(776, 20), &data);
+               data &= ~(0x3FF << 2);
+               data |= (0x1A << 2);
+               ret_val = hw->phy.ops.write_reg(hw, PHY_REG(776, 20), data);
+               if (ret_val)
+                       return ret_val;
+               ret_val = hw->phy.ops.write_reg(hw, PHY_REG(776, 23), 0xF100);
+               if (ret_val)
+                       return ret_val;
+               hw->phy.ops.read_reg(hw, HV_PM_CTRL, &data);
+               ret_val = hw->phy.ops.write_reg(hw, HV_PM_CTRL, data |
+                                               (1 << 10));
+               if (ret_val)
+                       return ret_val;
+       } else {
+               /* Write MAC register values back to h/w defaults */
+               mac_reg = E1000_READ_REG(hw, E1000_FFLT_DBG);
+               mac_reg &= ~(0xF << 14);
+               E1000_WRITE_REG(hw, E1000_FFLT_DBG, mac_reg);
+
+               mac_reg = E1000_READ_REG(hw, E1000_RCTL);
+               mac_reg &= ~E1000_RCTL_SECRC;
+               E1000_WRITE_REG(hw, E1000_RCTL, mac_reg);
+
+               ret_val = e1000_read_kmrn_reg_generic(hw,
+                                               E1000_KMRNCTRLSTA_CTRL_OFFSET,
+                                               &data);
+               if (ret_val)
+                       return ret_val;
+               ret_val = e1000_write_kmrn_reg_generic(hw,
+                                               E1000_KMRNCTRLSTA_CTRL_OFFSET,
+                                               data & ~(1 << 0));
+               if (ret_val)
+                       return ret_val;
+               ret_val = e1000_read_kmrn_reg_generic(hw,
+                                               E1000_KMRNCTRLSTA_HD_CTRL,
+                                               &data);
+               if (ret_val)
+                       return ret_val;
+               data &= ~(0xF << 8);
+               data |= (0xB << 8);
+               ret_val = e1000_write_kmrn_reg_generic(hw,
+                                               E1000_KMRNCTRLSTA_HD_CTRL,
+                                               data);
+               if (ret_val)
+                       return ret_val;
+
+               /* Write PHY register values back to h/w defaults */
+               hw->phy.ops.read_reg(hw, PHY_REG(769, 23), &data);
+               data &= ~(0x7F << 5);
+               ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 23), data);
+               if (ret_val)
+                       return ret_val;
+               hw->phy.ops.read_reg(hw, PHY_REG(769, 16), &data);
+               data |= (1 << 13);
+               ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 16), data);
+               if (ret_val)
+                       return ret_val;
+               hw->phy.ops.read_reg(hw, PHY_REG(776, 20), &data);
+               data &= ~(0x3FF << 2);
+               data |= (0x8 << 2);
+               ret_val = hw->phy.ops.write_reg(hw, PHY_REG(776, 20), data);
+               if (ret_val)
+                       return ret_val;
+               ret_val = hw->phy.ops.write_reg(hw, PHY_REG(776, 23), 0x7E00);
+               if (ret_val)
+                       return ret_val;
+               hw->phy.ops.read_reg(hw, HV_PM_CTRL, &data);
+               ret_val = hw->phy.ops.write_reg(hw, HV_PM_CTRL, data &
+                                               ~(1 << 10));
+               if (ret_val)
+                       return ret_val;
+       }
+
+       /* re-enable Rx path after enabling/disabling workaround */
+       return hw->phy.ops.write_reg(hw, PHY_REG(769, 20), phy_reg &
+                                    ~(1 << 14));
+}
+
+/**
+ *  e1000_lv_phy_workarounds_ich8lan - A series of Phy workarounds to be
+ *  done after every PHY reset.
+ **/
+STATIC s32 e1000_lv_phy_workarounds_ich8lan(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_lv_phy_workarounds_ich8lan");
+
+       if (hw->mac.type != e1000_pch2lan)
+               return E1000_SUCCESS;
+
+       /* Set MDIO slow mode before any other MDIO access */
+       ret_val = e1000_set_mdio_slow_mode_hv(hw);
+       if (ret_val)
+               return ret_val;
+
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val)
+               return ret_val;
+       /* set MSE higher to enable link to stay up when noise is high */
+       ret_val = e1000_write_emi_reg_locked(hw, I82579_MSE_THRESHOLD, 0x0034);
+       if (ret_val)
+               goto release;
+       /* drop link after 5 times MSE threshold was reached */
+       ret_val = e1000_write_emi_reg_locked(hw, I82579_MSE_LINK_DOWN, 0x0005);
+release:
+       hw->phy.ops.release(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_k1_gig_workaround_lv - K1 Si workaround
+ *  @hw:   pointer to the HW structure
+ *
+ *  Workaround to set the K1 beacon duration for 82579 parts
+ **/
+STATIC s32 e1000_k1_workaround_lv(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u16 status_reg = 0;
+       u32 mac_reg;
+       u16 phy_reg;
+
+       DEBUGFUNC("e1000_k1_workaround_lv");
+
+       if (hw->mac.type != e1000_pch2lan)
+               return E1000_SUCCESS;
+
+       /* Set K1 beacon duration based on 1Gbps speed or otherwise */
+       ret_val = hw->phy.ops.read_reg(hw, HV_M_STATUS, &status_reg);
+       if (ret_val)
+               return ret_val;
+
+       if ((status_reg & (HV_M_STATUS_LINK_UP | HV_M_STATUS_AUTONEG_COMPLETE))
+           == (HV_M_STATUS_LINK_UP | HV_M_STATUS_AUTONEG_COMPLETE)) {
+               mac_reg = E1000_READ_REG(hw, E1000_FEXTNVM4);
+               mac_reg &= ~E1000_FEXTNVM4_BEACON_DURATION_MASK;
+
+               ret_val = hw->phy.ops.read_reg(hw, I82579_LPI_CTRL, &phy_reg);
+               if (ret_val)
+                       return ret_val;
+
+               if (status_reg & HV_M_STATUS_SPEED_1000) {
+                       u16 pm_phy_reg;
+
+                       mac_reg |= E1000_FEXTNVM4_BEACON_DURATION_8USEC;
+                       phy_reg &= ~I82579_LPI_CTRL_FORCE_PLL_LOCK_COUNT;
+                       /* LV 1G Packet drop issue wa  */
+                       ret_val = hw->phy.ops.read_reg(hw, HV_PM_CTRL,
+                                                      &pm_phy_reg);
+                       if (ret_val)
+                               return ret_val;
+                       pm_phy_reg &= ~HV_PM_CTRL_PLL_STOP_IN_K1_GIGA;
+                       ret_val = hw->phy.ops.write_reg(hw, HV_PM_CTRL,
+                                                       pm_phy_reg);
+                       if (ret_val)
+                               return ret_val;
+               } else {
+                       mac_reg |= E1000_FEXTNVM4_BEACON_DURATION_16USEC;
+                       phy_reg |= I82579_LPI_CTRL_FORCE_PLL_LOCK_COUNT;
+               }
+               E1000_WRITE_REG(hw, E1000_FEXTNVM4, mac_reg);
+               ret_val = hw->phy.ops.write_reg(hw, I82579_LPI_CTRL, phy_reg);
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_gate_hw_phy_config_ich8lan - disable PHY config via hardware
+ *  @hw:   pointer to the HW structure
+ *  @gate: boolean set to true to gate, false to ungate
+ *
+ *  Gate/ungate the automatic PHY configuration via hardware; perform
+ *  the configuration via software instead.
+ **/
+STATIC void e1000_gate_hw_phy_config_ich8lan(struct e1000_hw *hw, bool gate)
+{
+       u32 extcnf_ctrl;
+
+       DEBUGFUNC("e1000_gate_hw_phy_config_ich8lan");
+
+       if (hw->mac.type != e1000_pch2lan)
+               return;
+
+       extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+
+       if (gate)
+               extcnf_ctrl |= E1000_EXTCNF_CTRL_GATE_PHY_CFG;
+       else
+               extcnf_ctrl &= ~E1000_EXTCNF_CTRL_GATE_PHY_CFG;
+
+       E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
+}
+
+/**
+ *  e1000_lan_init_done_ich8lan - Check for PHY config completion
+ *  @hw: pointer to the HW structure
+ *
+ *  Check the appropriate indication the MAC has finished configuring the
+ *  PHY after a software reset.
+ **/
+STATIC void e1000_lan_init_done_ich8lan(struct e1000_hw *hw)
+{
+       u32 data, loop = E1000_ICH8_LAN_INIT_TIMEOUT;
+
+       DEBUGFUNC("e1000_lan_init_done_ich8lan");
+
+       /* Wait for basic configuration completes before proceeding */
+       do {
+               data = E1000_READ_REG(hw, E1000_STATUS);
+               data &= E1000_STATUS_LAN_INIT_DONE;
+               usec_delay(100);
+       } while ((!data) && --loop);
+
+       /*
+        * If basic configuration is incomplete before the above loop
+        * count reaches 0, loading the configuration from NVM will
+        * leave the PHY in a bad state possibly resulting in no link.
+        */
+       if (loop == 0)
+               DEBUGOUT("LAN_INIT_DONE not set, increase timeout\n");
+
+       /* Clear the Init Done bit for the next init event */
+       data = E1000_READ_REG(hw, E1000_STATUS);
+       data &= ~E1000_STATUS_LAN_INIT_DONE;
+       E1000_WRITE_REG(hw, E1000_STATUS, data);
+}
+
+/**
+ *  e1000_post_phy_reset_ich8lan - Perform steps required after a PHY reset
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_post_phy_reset_ich8lan(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u16 reg;
+
+       DEBUGFUNC("e1000_post_phy_reset_ich8lan");
+
+       if (hw->phy.ops.check_reset_block(hw))
+               return E1000_SUCCESS;
+
+       /* Allow time for h/w to get to quiescent state after reset */
+       msec_delay(10);
+
+       /* Perform any necessary post-reset workarounds */
+       switch (hw->mac.type) {
+       case e1000_pchlan:
+               ret_val = e1000_hv_phy_workarounds_ich8lan(hw);
+               if (ret_val)
+                       return ret_val;
+               break;
+       case e1000_pch2lan:
+               ret_val = e1000_lv_phy_workarounds_ich8lan(hw);
+               if (ret_val)
+                       return ret_val;
+               break;
+       default:
+               break;
+       }
+
+       /* Clear the host wakeup bit after lcd reset */
+       if (hw->mac.type >= e1000_pchlan) {
+               hw->phy.ops.read_reg(hw, BM_PORT_GEN_CFG, &reg);
+               reg &= ~BM_WUC_HOST_WU_BIT;
+               hw->phy.ops.write_reg(hw, BM_PORT_GEN_CFG, reg);
+       }
+
+       /* Configure the LCD with the extended configuration region in NVM */
+       ret_val = e1000_sw_lcd_config_ich8lan(hw);
+       if (ret_val)
+               return ret_val;
+
+       /* Configure the LCD with the OEM bits in NVM */
+       ret_val = e1000_oem_bits_config_ich8lan(hw, true);
+
+       if (hw->mac.type == e1000_pch2lan) {
+               /* Ungate automatic PHY configuration on non-managed 82579 */
+               if (!(E1000_READ_REG(hw, E1000_FWSM) &
+                   E1000_ICH_FWSM_FW_VALID)) {
+                       msec_delay(10);
+                       e1000_gate_hw_phy_config_ich8lan(hw, false);
+               }
+
+               /* Set EEE LPI Update Timer to 200usec */
+               ret_val = hw->phy.ops.acquire(hw);
+               if (ret_val)
+                       return ret_val;
+               ret_val = e1000_write_emi_reg_locked(hw,
+                                                    I82579_LPI_UPDATE_TIMER,
+                                                    0x1387);
+               hw->phy.ops.release(hw);
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_phy_hw_reset_ich8lan - Performs a PHY reset
+ *  @hw: pointer to the HW structure
+ *
+ *  Resets the PHY
+ *  This is a function pointer entry point called by drivers
+ *  or other shared routines.
+ **/
+STATIC s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_phy_hw_reset_ich8lan");
+
+       /* Gate automatic PHY configuration by hardware on non-managed 82579 */
+       if ((hw->mac.type == e1000_pch2lan) &&
+           !(E1000_READ_REG(hw, E1000_FWSM) & E1000_ICH_FWSM_FW_VALID))
+               e1000_gate_hw_phy_config_ich8lan(hw, true);
+
+       ret_val = e1000_phy_hw_reset_generic(hw);
+       if (ret_val)
+               return ret_val;
+
+       return e1000_post_phy_reset_ich8lan(hw);
+}
+
+/**
+ *  e1000_set_lplu_state_pchlan - Set Low Power Link Up state
+ *  @hw: pointer to the HW structure
+ *  @active: true to enable LPLU, false to disable
+ *
+ *  Sets the LPLU state according to the active flag.  For PCH, if OEM write
+ *  bit are disabled in the NVM, writing the LPLU bits in the MAC will not set
+ *  the phy speed. This function will manually set the LPLU bit and restart
+ *  auto-neg as hw would do. D3 and D0 LPLU will call the same function
+ *  since it configures the same bit.
+ **/
+STATIC s32 e1000_set_lplu_state_pchlan(struct e1000_hw *hw, bool active)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u16 oem_reg;
+
+       DEBUGFUNC("e1000_set_lplu_state_pchlan");
+
+       ret_val = hw->phy.ops.read_reg(hw, HV_OEM_BITS, &oem_reg);
+       if (ret_val)
+               return ret_val;
+
+       if (active)
+               oem_reg |= HV_OEM_BITS_LPLU;
+       else
+               oem_reg &= ~HV_OEM_BITS_LPLU;
+
+       if (!hw->phy.ops.check_reset_block(hw))
+               oem_reg |= HV_OEM_BITS_RESTART_AN;
+
+       return hw->phy.ops.write_reg(hw, HV_OEM_BITS, oem_reg);
+}
+
+/**
+ *  e1000_set_d0_lplu_state_ich8lan - Set Low Power Linkup D0 state
+ *  @hw: pointer to the HW structure
+ *  @active: true to enable LPLU, false to disable
+ *
+ *  Sets the LPLU D0 state according to the active flag.  When
+ *  activating LPLU this function also disables smart speed
+ *  and vice versa.  LPLU will not be activated unless the
+ *  device autonegotiation advertisement meets standards of
+ *  either 10 or 10/100 or 10/100/1000 at all duplexes.
+ *  This is a function pointer entry point only called by
+ *  PHY setup routines.
+ **/
+STATIC s32 e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw, bool active)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       u32 phy_ctrl;
+       s32 ret_val = E1000_SUCCESS;
+       u16 data;
+
+       DEBUGFUNC("e1000_set_d0_lplu_state_ich8lan");
+
+       if (phy->type == e1000_phy_ife)
+               return E1000_SUCCESS;
+
+       phy_ctrl = E1000_READ_REG(hw, E1000_PHY_CTRL);
+
+       if (active) {
+               phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU;
+               E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl);
+
+               if (phy->type != e1000_phy_igp_3)
+                       return E1000_SUCCESS;
+
+               /*
+                * Call gig speed drop workaround on LPLU before accessing
+                * any PHY registers
+                */
+               if (hw->mac.type == e1000_ich8lan)
+                       e1000_gig_downshift_workaround_ich8lan(hw);
+
+               /* When LPLU is enabled, we should disable SmartSpeed */
+               ret_val = phy->ops.read_reg(hw,
+                                           IGP01E1000_PHY_PORT_CONFIG,
+                                           &data);
+               data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+               ret_val = phy->ops.write_reg(hw,
+                                            IGP01E1000_PHY_PORT_CONFIG,
+                                            data);
+               if (ret_val)
+                       return ret_val;
+       } else {
+               phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU;
+               E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl);
+
+               if (phy->type != e1000_phy_igp_3)
+                       return E1000_SUCCESS;
+
+               /*
+                * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
+                * during Dx states where the power conservation is most
+                * important.  During driver activity we should enable
+                * SmartSpeed, so performance is maintained.
+                */
+               if (phy->smart_speed == e1000_smart_speed_on) {
+                       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,
+                                                    data);
+                       if (ret_val)
+                               return ret_val;
+               } else if (phy->smart_speed == e1000_smart_speed_off) {
+                       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,
+                                                    data);
+                       if (ret_val)
+                               return ret_val;
+               }
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_set_d3_lplu_state_ich8lan - Set Low Power Linkup D3 state
+ *  @hw: pointer to the HW structure
+ *  @active: true to enable LPLU, false to disable
+ *
+ *  Sets the LPLU D3 state according to the active flag.  When
+ *  activating LPLU this function also disables smart speed
+ *  and vice versa.  LPLU will not be activated unless the
+ *  device autonegotiation advertisement meets standards of
+ *  either 10 or 10/100 or 10/100/1000 at all duplexes.
+ *  This is a function pointer entry point only called by
+ *  PHY setup routines.
+ **/
+STATIC s32 e1000_set_d3_lplu_state_ich8lan(struct e1000_hw *hw, bool active)
+{
+       struct e1000_phy_info *phy = &hw->phy;
+       u32 phy_ctrl;
+       s32 ret_val = E1000_SUCCESS;
+       u16 data;
+
+       DEBUGFUNC("e1000_set_d3_lplu_state_ich8lan");
+
+       phy_ctrl = E1000_READ_REG(hw, E1000_PHY_CTRL);
+
+       if (!active) {
+               phy_ctrl &= ~E1000_PHY_CTRL_NOND0A_LPLU;
+               E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl);
+
+               if (phy->type != e1000_phy_igp_3)
+                       return E1000_SUCCESS;
+
+               /*
+                * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
+                * during Dx states where the power conservation is most
+                * important.  During driver activity we should enable
+                * SmartSpeed, so performance is maintained.
+                */
+               if (phy->smart_speed == e1000_smart_speed_on) {
+                       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,
+                                                    data);
+                       if (ret_val)
+                               return ret_val;
+               } else if (phy->smart_speed == e1000_smart_speed_off) {
+                       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,
+                                                    data);
+                       if (ret_val)
+                               return ret_val;
+               }
+       } else if ((phy->autoneg_advertised == E1000_ALL_SPEED_DUPLEX) ||
+                  (phy->autoneg_advertised == E1000_ALL_NOT_GIG) ||
+                  (phy->autoneg_advertised == E1000_ALL_10_SPEED)) {
+               phy_ctrl |= E1000_PHY_CTRL_NOND0A_LPLU;
+               E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl);
+
+               if (phy->type != e1000_phy_igp_3)
+                       return E1000_SUCCESS;
+
+               /*
+                * Call gig speed drop workaround on LPLU before accessing
+                * any PHY registers
+                */
+               if (hw->mac.type == e1000_ich8lan)
+                       e1000_gig_downshift_workaround_ich8lan(hw);
+
+               /* When LPLU is enabled, we should disable SmartSpeed */
+               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,
+                                            data);
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_valid_nvm_bank_detect_ich8lan - finds out the valid bank 0 or 1
+ *  @hw: pointer to the HW structure
+ *  @bank:  pointer to the variable that returns the active bank
+ *
+ *  Reads signature byte from the NVM using the flash access registers.
+ *  Word 0x13 bits 15:14 = 10b indicate a valid signature for that bank.
+ **/
+STATIC s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank)
+{
+       u32 eecd;
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       u32 bank1_offset = nvm->flash_bank_size * sizeof(u16);
+       u32 act_offset = E1000_ICH_NVM_SIG_WORD * 2 + 1;
+       u8 sig_byte = 0;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_valid_nvm_bank_detect_ich8lan");
+
+       switch (hw->mac.type) {
+       case e1000_ich8lan:
+       case e1000_ich9lan:
+               eecd = E1000_READ_REG(hw, E1000_EECD);
+               if ((eecd & E1000_EECD_SEC1VAL_VALID_MASK) ==
+                   E1000_EECD_SEC1VAL_VALID_MASK) {
+                       if (eecd & E1000_EECD_SEC1VAL)
+                               *bank = 1;
+                       else
+                               *bank = 0;
+
+                       return E1000_SUCCESS;
+               }
+               DEBUGOUT("Unable to determine valid NVM bank via EEC - reading flash signature\n");
+               /* fall-thru */
+       default:
+               /* set bank to 0 in case flash read fails */
+               *bank = 0;
+
+               /* Check bank 0 */
+               ret_val = e1000_read_flash_byte_ich8lan(hw, act_offset,
+                                                       &sig_byte);
+               if (ret_val)
+                       return ret_val;
+               if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
+                   E1000_ICH_NVM_SIG_VALUE) {
+                       *bank = 0;
+                       return E1000_SUCCESS;
+               }
+
+               /* Check bank 1 */
+               ret_val = e1000_read_flash_byte_ich8lan(hw, act_offset +
+                                                       bank1_offset,
+                                                       &sig_byte);
+               if (ret_val)
+                       return ret_val;
+               if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
+                   E1000_ICH_NVM_SIG_VALUE) {
+                       *bank = 1;
+                       return E1000_SUCCESS;
+               }
+
+               DEBUGOUT("ERROR: No valid NVM bank present\n");
+               return -E1000_ERR_NVM;
+       }
+}
+
+/**
+ *  e1000_read_nvm_ich8lan - Read word(s) from the NVM
+ *  @hw: pointer to the HW structure
+ *  @offset: The offset (in bytes) of the word(s) to read.
+ *  @words: Size of data to read in words
+ *  @data: Pointer to the word(s) to read at offset.
+ *
+ *  Reads a word(s) from the NVM using the flash access registers.
+ **/
+STATIC s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
+                                 u16 *data)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
+       u32 act_offset;
+       s32 ret_val = E1000_SUCCESS;
+       u32 bank = 0;
+       u16 i, word;
+
+       DEBUGFUNC("e1000_read_nvm_ich8lan");
+
+       if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) ||
+           (words == 0)) {
+               DEBUGOUT("nvm parameter(s) out of bounds\n");
+               ret_val = -E1000_ERR_NVM;
+               goto out;
+       }
+
+       nvm->ops.acquire(hw);
+
+       ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
+       if (ret_val != E1000_SUCCESS) {
+               DEBUGOUT("Could not detect valid bank, assuming bank 0\n");
+               bank = 0;
+       }
+
+       act_offset = (bank) ? nvm->flash_bank_size : 0;
+       act_offset += offset;
+
+       ret_val = E1000_SUCCESS;
+       for (i = 0; i < words; i++) {
+               if (dev_spec->shadow_ram[offset+i].modified) {
+                       data[i] = dev_spec->shadow_ram[offset+i].value;
+               } else {
+                       ret_val = e1000_read_flash_word_ich8lan(hw,
+                                                               act_offset + i,
+                                                               &word);
+                       if (ret_val)
+                               break;
+                       data[i] = word;
+               }
+       }
+
+       nvm->ops.release(hw);
+
+out:
+       if (ret_val)
+               DEBUGOUT1("NVM read error: %d\n", ret_val);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_flash_cycle_init_ich8lan - Initialize flash
+ *  @hw: pointer to the HW structure
+ *
+ *  This function does initial flash setup so that a new read/write/erase cycle
+ *  can be started.
+ **/
+static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw)
+{
+       union ich8_hws_flash_status hsfsts;
+       s32 ret_val = -E1000_ERR_NVM;
+
+       DEBUGFUNC("e1000_flash_cycle_init_ich8lan");
+
+       hsfsts.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFSTS);
+
+       /* Check if the flash descriptor is valid */
+       if (!hsfsts.hsf_status.fldesvalid) {
+               DEBUGOUT("Flash descriptor invalid.  SW Sequencing must be used.\n");
+               return -E1000_ERR_NVM;
+       }
+
+       /* Clear FCERR and DAEL in hw status by writing 1 */
+       hsfsts.hsf_status.flcerr = 1;
+       hsfsts.hsf_status.dael = 1;
+
+       E1000_WRITE_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval);
+
+       /*
+        * Either we should have a hardware SPI cycle in progress
+        * bit to check against, in order to start a new cycle or
+        * FDONE bit should be changed in the hardware so that it
+        * is 1 after hardware reset, which can then be used as an
+        * indication whether a cycle is in progress or has been
+        * completed.
+        */
+
+       if (!hsfsts.hsf_status.flcinprog) {
+               /*
+                * There is no cycle running at present,
+                * so we can start a cycle.
+                * Begin by setting Flash Cycle Done.
+                */
+               hsfsts.hsf_status.flcdone = 1;
+               E1000_WRITE_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval);
+               ret_val = E1000_SUCCESS;
+       } else {
+               s32 i;
+
+               /*
+                * Otherwise poll for sometime so the current
+                * cycle has a chance to end before giving up.
+                */
+               for (i = 0; i < ICH_FLASH_READ_COMMAND_TIMEOUT; i++) {
+                       hsfsts.regval = E1000_READ_FLASH_REG16(hw,
+                                                             ICH_FLASH_HSFSTS);
+                       if (!hsfsts.hsf_status.flcinprog) {
+                               ret_val = E1000_SUCCESS;
+                               break;
+                       }
+                       usec_delay(1);
+               }
+               if (ret_val == E1000_SUCCESS) {
+                       /*
+                        * Successful in waiting for previous cycle to timeout,
+                        * now set the Flash Cycle Done.
+                        */
+                       hsfsts.hsf_status.flcdone = 1;
+                       E1000_WRITE_FLASH_REG16(hw, ICH_FLASH_HSFSTS,
+                                               hsfsts.regval);
+               } else {
+                       DEBUGOUT("Flash controller busy, cannot get access\n");
+               }
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_flash_cycle_ich8lan - Starts flash cycle (read/write/erase)
+ *  @hw: pointer to the HW structure
+ *  @timeout: maximum time to wait for completion
+ *
+ *  This function starts a flash cycle and waits for its completion.
+ **/
+static s32 e1000_flash_cycle_ich8lan(struct e1000_hw *hw, u32 timeout)
+{
+       union ich8_hws_flash_ctrl hsflctl;
+       union ich8_hws_flash_status hsfsts;
+       u32 i = 0;
+
+       DEBUGFUNC("e1000_flash_cycle_ich8lan");
+
+       /* Start a cycle by writing 1 in Flash Cycle Go in Hw Flash Control */
+       hsflctl.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFCTL);
+       hsflctl.hsf_ctrl.flcgo = 1;
+       E1000_WRITE_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval);
+
+       /* wait till FDONE bit is set to 1 */
+       do {
+               hsfsts.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFSTS);
+               if (hsfsts.hsf_status.flcdone)
+                       break;
+               usec_delay(1);
+       } while (i++ < timeout);
+
+       if (hsfsts.hsf_status.flcdone && !hsfsts.hsf_status.flcerr)
+               return E1000_SUCCESS;
+
+       return -E1000_ERR_NVM;
+}
+
+/**
+ *  e1000_read_flash_word_ich8lan - Read word from flash
+ *  @hw: pointer to the HW structure
+ *  @offset: offset to data location
+ *  @data: pointer to the location for storing the data
+ *
+ *  Reads the flash word at offset into data.  Offset is converted
+ *  to bytes before read.
+ **/
+STATIC s32 e1000_read_flash_word_ich8lan(struct e1000_hw *hw, u32 offset,
+                                        u16 *data)
+{
+       DEBUGFUNC("e1000_read_flash_word_ich8lan");
+
+       if (!data)
+               return -E1000_ERR_NVM;
+
+       /* Must convert offset into bytes. */
+       offset <<= 1;
+
+       return e1000_read_flash_data_ich8lan(hw, offset, 2, data);
+}
+
+/**
+ *  e1000_read_flash_byte_ich8lan - Read byte from flash
+ *  @hw: pointer to the HW structure
+ *  @offset: The offset of the byte to read.
+ *  @data: Pointer to a byte to store the value read.
+ *
+ *  Reads a single byte from the NVM using the flash access registers.
+ **/
+STATIC s32 e1000_read_flash_byte_ich8lan(struct e1000_hw *hw, u32 offset,
+                                        u8 *data)
+{
+       s32 ret_val;
+       u16 word = 0;
+
+       ret_val = e1000_read_flash_data_ich8lan(hw, offset, 1, &word);
+       if (ret_val)
+               return ret_val;
+
+       *data = (u8)word;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_read_flash_data_ich8lan - Read byte or word from NVM
+ *  @hw: pointer to the HW structure
+ *  @offset: The offset (in bytes) of the byte or word to read.
+ *  @size: Size of data to read, 1=byte 2=word
+ *  @data: Pointer to the word to store the value read.
+ *
+ *  Reads a byte or word from the NVM using the flash access registers.
+ **/
+static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
+                                        u8 size, u16 *data)
+{
+       union ich8_hws_flash_status hsfsts;
+       union ich8_hws_flash_ctrl hsflctl;
+       u32 flash_linear_addr;
+       u32 flash_data = 0;
+       s32 ret_val = -E1000_ERR_NVM;
+       u8 count = 0;
+
+       DEBUGFUNC("e1000_read_flash_data_ich8lan");
+
+       if (size < 1  || size > 2 || offset > ICH_FLASH_LINEAR_ADDR_MASK)
+               return -E1000_ERR_NVM;
+
+       flash_linear_addr = (ICH_FLASH_LINEAR_ADDR_MASK & offset) +
+                           hw->nvm.flash_base_addr;
+
+       do {
+               usec_delay(1);
+               /* Steps */
+               ret_val = e1000_flash_cycle_init_ich8lan(hw);
+               if (ret_val != E1000_SUCCESS)
+                       break;
+
+               hsflctl.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFCTL);
+               /* 0b/1b corresponds to 1 or 2 byte size, respectively. */
+               hsflctl.hsf_ctrl.fldbcount = size - 1;
+               hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_READ;
+               E1000_WRITE_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval);
+
+               E1000_WRITE_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_addr);
+
+               ret_val = e1000_flash_cycle_ich8lan(hw,
+                                               ICH_FLASH_READ_COMMAND_TIMEOUT);
+
+               /*
+                * Check if FCERR is set to 1, if set to 1, clear it
+                * and try the whole sequence a few more times, else
+                * read in (shift in) the Flash Data0, the order is
+                * least significant byte first msb to lsb
+                */
+               if (ret_val == E1000_SUCCESS) {
+                       flash_data = E1000_READ_FLASH_REG(hw, ICH_FLASH_FDATA0);
+                       if (size == 1)
+                               *data = (u8)(flash_data & 0x000000FF);
+                       else if (size == 2)
+                               *data = (u16)(flash_data & 0x0000FFFF);
+                       break;
+               } else {
+                       /*
+                        * If we've gotten here, then things are probably
+                        * completely hosed, but if the error condition is
+                        * detected, it won't hurt to give it another try...
+                        * ICH_FLASH_CYCLE_REPEAT_COUNT times.
+                        */
+                       hsfsts.regval = E1000_READ_FLASH_REG16(hw,
+                                                             ICH_FLASH_HSFSTS);
+                       if (hsfsts.hsf_status.flcerr) {
+                               /* Repeat for some time before giving up. */
+                               continue;
+                       } else if (!hsfsts.hsf_status.flcdone) {
+                               DEBUGOUT("Timeout error - flash cycle did not complete.\n");
+                               break;
+                       }
+               }
+       } while (count++ < ICH_FLASH_CYCLE_REPEAT_COUNT);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_write_nvm_ich8lan - Write word(s) to the NVM
+ *  @hw: pointer to the HW structure
+ *  @offset: The offset (in bytes) of the word(s) to write.
+ *  @words: Size of data to write in words
+ *  @data: Pointer to the word(s) to write at offset.
+ *
+ *  Writes a byte or word to the NVM using the flash access registers.
+ **/
+STATIC s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
+                                  u16 *data)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
+       u16 i;
+
+       DEBUGFUNC("e1000_write_nvm_ich8lan");
+
+       if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) ||
+           (words == 0)) {
+               DEBUGOUT("nvm parameter(s) out of bounds\n");
+               return -E1000_ERR_NVM;
+       }
+
+       nvm->ops.acquire(hw);
+
+       for (i = 0; i < words; i++) {
+               dev_spec->shadow_ram[offset+i].modified = true;
+               dev_spec->shadow_ram[offset+i].value = data[i];
+       }
+
+       nvm->ops.release(hw);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_update_nvm_checksum_ich8lan - Update the checksum for NVM
+ *  @hw: pointer to the HW structure
+ *
+ *  The NVM checksum is updated by calling the generic update_nvm_checksum,
+ *  which writes the checksum to the shadow ram.  The changes in the shadow
+ *  ram are then committed to the EEPROM by processing each bank at a time
+ *  checking for the modified bit and writing only the pending changes.
+ *  After a successful commit, the shadow ram is cleared and is ready for
+ *  future writes.
+ **/
+STATIC s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
+       u32 i, act_offset, new_bank_offset, old_bank_offset, bank;
+       s32 ret_val;
+       u16 data;
+
+       DEBUGFUNC("e1000_update_nvm_checksum_ich8lan");
+
+       ret_val = e1000_update_nvm_checksum_generic(hw);
+       if (ret_val)
+               goto out;
+
+       if (nvm->type != e1000_nvm_flash_sw)
+               goto out;
+
+       nvm->ops.acquire(hw);
+
+       /*
+        * We're writing to the opposite bank so if we're on bank 1,
+        * write to bank 0 etc.  We also need to erase the segment that
+        * is going to be written
+        */
+       ret_val =  e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
+       if (ret_val != E1000_SUCCESS) {
+               DEBUGOUT("Could not detect valid bank, assuming bank 0\n");
+               bank = 0;
+       }
+
+       if (bank == 0) {
+               new_bank_offset = nvm->flash_bank_size;
+               old_bank_offset = 0;
+               ret_val = e1000_erase_flash_bank_ich8lan(hw, 1);
+               if (ret_val)
+                       goto release;
+       } else {
+               old_bank_offset = nvm->flash_bank_size;
+               new_bank_offset = 0;
+               ret_val = e1000_erase_flash_bank_ich8lan(hw, 0);
+               if (ret_val)
+                       goto release;
+       }
+
+       for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) {
+               /*
+                * Determine whether to write the value stored
+                * in the other NVM bank or a modified value stored
+                * in the shadow RAM
+                */
+               if (dev_spec->shadow_ram[i].modified) {
+                       data = dev_spec->shadow_ram[i].value;
+               } else {
+                       ret_val = e1000_read_flash_word_ich8lan(hw, i +
+                                                               old_bank_offset,
+                                                               &data);
+                       if (ret_val)
+                               break;
+               }
+
+               /*
+                * If the word is 0x13, then make sure the signature bits
+                * (15:14) are 11b until the commit has completed.
+                * This will allow us to write 10b which indicates the
+                * signature is valid.  We want to do this after the write
+                * has completed so that we don't mark the segment valid
+                * while the write is still in progress
+                */
+               if (i == E1000_ICH_NVM_SIG_WORD)
+                       data |= E1000_ICH_NVM_SIG_MASK;
+
+               /* Convert offset to bytes. */
+               act_offset = (i + new_bank_offset) << 1;
+
+               usec_delay(100);
+               /* Write the bytes to the new bank. */
+               ret_val = e1000_retry_write_flash_byte_ich8lan(hw,
+                                                              act_offset,
+                                                              (u8)data);
+               if (ret_val)
+                       break;
+
+               usec_delay(100);
+               ret_val = e1000_retry_write_flash_byte_ich8lan(hw,
+                                                         act_offset + 1,
+                                                         (u8)(data >> 8));
+               if (ret_val)
+                       break;
+       }
+
+       /*
+        * Don't bother writing the segment valid bits if sector
+        * programming failed.
+        */
+       if (ret_val) {
+               DEBUGOUT("Flash commit failed.\n");
+               goto release;
+       }
+
+       /*
+        * Finally validate the new segment by setting bit 15:14
+        * to 10b in word 0x13 , this can be done without an
+        * erase as well since these bits are 11 to start with
+        * and we need to change bit 14 to 0b
+        */
+       act_offset = new_bank_offset + E1000_ICH_NVM_SIG_WORD;
+       ret_val = e1000_read_flash_word_ich8lan(hw, act_offset, &data);
+       if (ret_val)
+               goto release;
+
+       data &= 0xBFFF;
+       ret_val = e1000_retry_write_flash_byte_ich8lan(hw,
+                                                      act_offset * 2 + 1,
+                                                      (u8)(data >> 8));
+       if (ret_val)
+               goto release;
+
+       /*
+        * And invalidate the previously valid segment by setting
+        * its signature word (0x13) high_byte to 0b. This can be
+        * done without an erase because flash erase sets all bits
+        * to 1's. We can write 1's to 0's without an erase
+        */
+       act_offset = (old_bank_offset + E1000_ICH_NVM_SIG_WORD) * 2 + 1;
+       ret_val = e1000_retry_write_flash_byte_ich8lan(hw, act_offset, 0);
+       if (ret_val)
+               goto release;
+
+       /* Great!  Everything worked, we can now clear the cached entries. */
+       for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) {
+               dev_spec->shadow_ram[i].modified = false;
+               dev_spec->shadow_ram[i].value = 0xFFFF;
+       }
+
+release:
+       nvm->ops.release(hw);
+
+       /*
+        * Reload the EEPROM, or else modifications will not appear
+        * until after the next adapter reset.
+        */
+       if (!ret_val) {
+               nvm->ops.reload(hw);
+               msec_delay(10);
+       }
+
+out:
+       if (ret_val)
+               DEBUGOUT1("NVM update error: %d\n", ret_val);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_validate_nvm_checksum_ich8lan - Validate EEPROM checksum
+ *  @hw: pointer to the HW structure
+ *
+ *  Check to see if checksum needs to be fixed by reading bit 6 in word 0x19.
+ *  If the bit is 0, that the EEPROM had been modified, but the checksum was not
+ *  calculated, in which case we need to calculate the checksum and set bit 6.
+ **/
+STATIC s32 e1000_validate_nvm_checksum_ich8lan(struct e1000_hw *hw)
+{
+       s32 ret_val;
+       u16 data;
+       u16 word;
+       u16 valid_csum_mask;
+
+       DEBUGFUNC("e1000_validate_nvm_checksum_ich8lan");
+
+       /*
+        * Read NVM and check Invalid Image CSUM bit.  If this bit is 0,
+        * the checksum needs to be fixed.  This bit is an indication that
+        * the NVM was prepared by OEM software and did not calculate
+        * the checksum...a likely scenario.
+        */
+       switch (hw->mac.type) {
+       default:
+               word = NVM_FUTURE_INIT_WORD1;
+               valid_csum_mask = NVM_FUTURE_INIT_WORD1_VALID_CSUM;
+               break;
+       }
+
+       ret_val = hw->nvm.ops.read(hw, word, 1, &data);
+       if (ret_val)
+               return ret_val;
+
+       if (!(data & valid_csum_mask)) {
+               data |= valid_csum_mask;
+               ret_val = hw->nvm.ops.write(hw, word, 1, &data);
+               if (ret_val)
+                       return ret_val;
+               ret_val = hw->nvm.ops.update(hw);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       return e1000_validate_nvm_checksum_generic(hw);
+}
+
+/**
+ *  e1000_write_flash_data_ich8lan - Writes bytes to the NVM
+ *  @hw: pointer to the HW structure
+ *  @offset: The offset (in bytes) of the byte/word to read.
+ *  @size: Size of data to read, 1=byte 2=word
+ *  @data: The byte(s) to write to the NVM.
+ *
+ *  Writes one/two bytes to the NVM using the flash access registers.
+ **/
+static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
+                                         u8 size, u16 data)
+{
+       union ich8_hws_flash_status hsfsts;
+       union ich8_hws_flash_ctrl hsflctl;
+       u32 flash_linear_addr;
+       u32 flash_data = 0;
+       s32 ret_val;
+       u8 count = 0;
+
+       DEBUGFUNC("e1000_write_ich8_data");
+
+       if (size < 1 || size > 2 || data > size * 0xff ||
+           offset > ICH_FLASH_LINEAR_ADDR_MASK)
+               return -E1000_ERR_NVM;
+
+       flash_linear_addr = (ICH_FLASH_LINEAR_ADDR_MASK & offset) +
+                           hw->nvm.flash_base_addr;
+
+       do {
+               usec_delay(1);
+               /* Steps */
+               ret_val = e1000_flash_cycle_init_ich8lan(hw);
+               if (ret_val != E1000_SUCCESS)
+                       break;
+
+               hsflctl.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFCTL);
+               /* 0b/1b corresponds to 1 or 2 byte size, respectively. */
+               hsflctl.hsf_ctrl.fldbcount = size - 1;
+               hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_WRITE;
+               E1000_WRITE_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval);
+
+               E1000_WRITE_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_addr);
+
+               if (size == 1)
+                       flash_data = (u32)data & 0x00FF;
+               else
+                       flash_data = (u32)data;
+
+               E1000_WRITE_FLASH_REG(hw, ICH_FLASH_FDATA0, flash_data);
+
+               /*
+                * check if FCERR is set to 1 , if set to 1, clear it
+                * and try the whole sequence a few more times else done
+                */
+               ret_val = e1000_flash_cycle_ich8lan(hw,
+                                              ICH_FLASH_WRITE_COMMAND_TIMEOUT);
+               if (ret_val == E1000_SUCCESS)
+                       break;
+
+               /*
+                * If we're here, then things are most likely
+                * completely hosed, but if the error condition
+                * is detected, it won't hurt to give it another
+                * try...ICH_FLASH_CYCLE_REPEAT_COUNT times.
+                */
+               hsfsts.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFSTS);
+               if (hsfsts.hsf_status.flcerr)
+                       /* Repeat for some time before giving up. */
+                       continue;
+               if (!hsfsts.hsf_status.flcdone) {
+                       DEBUGOUT("Timeout error - flash cycle did not complete.\n");
+                       break;
+               }
+       } while (count++ < ICH_FLASH_CYCLE_REPEAT_COUNT);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_write_flash_byte_ich8lan - Write a single byte to NVM
+ *  @hw: pointer to the HW structure
+ *  @offset: The index of the byte to read.
+ *  @data: The byte to write to the NVM.
+ *
+ *  Writes a single byte to the NVM using the flash access registers.
+ **/
+STATIC s32 e1000_write_flash_byte_ich8lan(struct e1000_hw *hw, u32 offset,
+                                         u8 data)
+{
+       u16 word = (u16)data;
+
+       DEBUGFUNC("e1000_write_flash_byte_ich8lan");
+
+       return e1000_write_flash_data_ich8lan(hw, offset, 1, word);
+}
+
+/**
+ *  e1000_retry_write_flash_byte_ich8lan - Writes a single byte to NVM
+ *  @hw: pointer to the HW structure
+ *  @offset: The offset of the byte to write.
+ *  @byte: The byte to write to the NVM.
+ *
+ *  Writes a single byte to the NVM using the flash access registers.
+ *  Goes through a retry algorithm before giving up.
+ **/
+static s32 e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw,
+                                               u32 offset, u8 byte)
+{
+       s32 ret_val;
+       u16 program_retries;
+
+       DEBUGFUNC("e1000_retry_write_flash_byte_ich8lan");
+
+       ret_val = e1000_write_flash_byte_ich8lan(hw, offset, byte);
+       if (!ret_val)
+               return ret_val;
+
+       for (program_retries = 0; program_retries < 100; program_retries++) {
+               DEBUGOUT2("Retrying Byte %2.2X at offset %u\n", byte, offset);
+               usec_delay(100);
+               ret_val = e1000_write_flash_byte_ich8lan(hw, offset, byte);
+               if (ret_val == E1000_SUCCESS)
+                       break;
+       }
+       if (program_retries == 100)
+               return -E1000_ERR_NVM;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_erase_flash_bank_ich8lan - Erase a bank (4k) from NVM
+ *  @hw: pointer to the HW structure
+ *  @bank: 0 for first bank, 1 for second bank, etc.
+ *
+ *  Erases the bank specified. Each bank is a 4k block. Banks are 0 based.
+ *  bank N is 4096 * N + flash_reg_addr.
+ **/
+STATIC s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       union ich8_hws_flash_status hsfsts;
+       union ich8_hws_flash_ctrl hsflctl;
+       u32 flash_linear_addr;
+       /* bank size is in 16bit words - adjust to bytes */
+       u32 flash_bank_size = nvm->flash_bank_size * 2;
+       s32 ret_val;
+       s32 count = 0;
+       s32 j, iteration, sector_size;
+
+       DEBUGFUNC("e1000_erase_flash_bank_ich8lan");
+
+       hsfsts.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFSTS);
+
+       /*
+        * Determine HW Sector size: Read BERASE bits of hw flash status
+        * register
+        * 00: The Hw sector is 256 bytes, hence we need to erase 16
+        *     consecutive sectors.  The start index for the nth Hw sector
+        *     can be calculated as = bank * 4096 + n * 256
+        * 01: The Hw sector is 4K bytes, hence we need to erase 1 sector.
+        *     The start index for the nth Hw sector can be calculated
+        *     as = bank * 4096
+        * 10: The Hw sector is 8K bytes, nth sector = bank * 8192
+        *     (ich9 only, otherwise error condition)
+        * 11: The Hw sector is 64K bytes, nth sector = bank * 65536
+        */
+       switch (hsfsts.hsf_status.berasesz) {
+       case 0:
+               /* Hw sector size 256 */
+               sector_size = ICH_FLASH_SEG_SIZE_256;
+               iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_256;
+               break;
+       case 1:
+               sector_size = ICH_FLASH_SEG_SIZE_4K;
+               iteration = 1;
+               break;
+       case 2:
+               sector_size = ICH_FLASH_SEG_SIZE_8K;
+               iteration = 1;
+               break;
+       case 3:
+               sector_size = ICH_FLASH_SEG_SIZE_64K;
+               iteration = 1;
+               break;
+       default:
+               return -E1000_ERR_NVM;
+       }
+
+       /* Start with the base address, then add the sector offset. */
+       flash_linear_addr = hw->nvm.flash_base_addr;
+       flash_linear_addr += (bank) ? flash_bank_size : 0;
+
+       for (j = 0; j < iteration ; j++) {
+               do {
+                       /* Steps */
+                       ret_val = e1000_flash_cycle_init_ich8lan(hw);
+                       if (ret_val)
+                               return ret_val;
+
+                       /*
+                        * Write a value 11 (block Erase) in Flash
+                        * Cycle field in hw flash control
+                        */
+                       hsflctl.regval = E1000_READ_FLASH_REG16(hw,
+                                                             ICH_FLASH_HSFCTL);
+                       hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_ERASE;
+                       E1000_WRITE_FLASH_REG16(hw, ICH_FLASH_HSFCTL,
+                                               hsflctl.regval);
+
+                       /*
+                        * Write the last 24 bits of an index within the
+                        * block into Flash Linear address field in Flash
+                        * Address.
+                        */
+                       flash_linear_addr += (j * sector_size);
+                       E1000_WRITE_FLASH_REG(hw, ICH_FLASH_FADDR,
+                                             flash_linear_addr);
+
+                       ret_val = e1000_flash_cycle_ich8lan(hw,
+                                              ICH_FLASH_ERASE_COMMAND_TIMEOUT);
+                       if (ret_val == E1000_SUCCESS)
+                               break;
+
+                       /*
+                        * Check if FCERR is set to 1.  If 1,
+                        * clear it and try the whole sequence
+                        * a few more times else Done
+                        */
+                       hsfsts.regval = E1000_READ_FLASH_REG16(hw,
+                                                     ICH_FLASH_HSFSTS);
+                       if (hsfsts.hsf_status.flcerr)
+                               /* repeat for some time before giving up */
+                               continue;
+                       else if (!hsfsts.hsf_status.flcdone)
+                               return ret_val;
+               } while (++count < ICH_FLASH_CYCLE_REPEAT_COUNT);
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_valid_led_default_ich8lan - Set the default LED settings
+ *  @hw: pointer to the HW structure
+ *  @data: Pointer to the LED settings
+ *
+ *  Reads the LED default settings from the NVM to data.  If the NVM LED
+ *  settings is all 0's or F's, set the LED default to a valid LED default
+ *  setting.
+ **/
+STATIC s32 e1000_valid_led_default_ich8lan(struct e1000_hw *hw, u16 *data)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_valid_led_default_ich8lan");
+
+       ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data);
+       if (ret_val) {
+               DEBUGOUT("NVM Read Error\n");
+               return ret_val;
+       }
+
+       if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF)
+               *data = ID_LED_DEFAULT_ICH8LAN;
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_id_led_init_pchlan - store LED configurations
+ *  @hw: pointer to the HW structure
+ *
+ *  PCH does not control LEDs via the LEDCTL register, rather it uses
+ *  the PHY LED configuration register.
+ *
+ *  PCH also does not have an "always on" or "always off" mode which
+ *  complicates the ID feature.  Instead of using the "on" mode to indicate
+ *  in ledctl_mode2 the LEDs to use for ID (see e1000_id_led_init_generic()),
+ *  use "link_up" mode.  The LEDs will still ID on request if there is no
+ *  link based on logic in e1000_led_[on|off]_pchlan().
+ **/
+STATIC s32 e1000_id_led_init_pchlan(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       s32 ret_val;
+       const u32 ledctl_on = E1000_LEDCTL_MODE_LINK_UP;
+       const u32 ledctl_off = E1000_LEDCTL_MODE_LINK_UP | E1000_PHY_LED0_IVRT;
+       u16 data, i, temp, shift;
+
+       DEBUGFUNC("e1000_id_led_init_pchlan");
+
+       /* Get default ID LED modes */
+       ret_val = hw->nvm.ops.valid_led_default(hw, &data);
+       if (ret_val)
+               return ret_val;
+
+       mac->ledctl_default = E1000_READ_REG(hw, E1000_LEDCTL);
+       mac->ledctl_mode1 = mac->ledctl_default;
+       mac->ledctl_mode2 = mac->ledctl_default;
+
+       for (i = 0; i < 4; i++) {
+               temp = (data >> (i << 2)) & E1000_LEDCTL_LED0_MODE_MASK;
+               shift = (i * 5);
+               switch (temp) {
+               case ID_LED_ON1_DEF2:
+               case ID_LED_ON1_ON2:
+               case ID_LED_ON1_OFF2:
+                       mac->ledctl_mode1 &= ~(E1000_PHY_LED0_MASK << shift);
+                       mac->ledctl_mode1 |= (ledctl_on << shift);
+                       break;
+               case ID_LED_OFF1_DEF2:
+               case ID_LED_OFF1_ON2:
+               case ID_LED_OFF1_OFF2:
+                       mac->ledctl_mode1 &= ~(E1000_PHY_LED0_MASK << shift);
+                       mac->ledctl_mode1 |= (ledctl_off << shift);
+                       break;
+               default:
+                       /* Do nothing */
+                       break;
+               }
+               switch (temp) {
+               case ID_LED_DEF1_ON2:
+               case ID_LED_ON1_ON2:
+               case ID_LED_OFF1_ON2:
+                       mac->ledctl_mode2 &= ~(E1000_PHY_LED0_MASK << shift);
+                       mac->ledctl_mode2 |= (ledctl_on << shift);
+                       break;
+               case ID_LED_DEF1_OFF2:
+               case ID_LED_ON1_OFF2:
+               case ID_LED_OFF1_OFF2:
+                       mac->ledctl_mode2 &= ~(E1000_PHY_LED0_MASK << shift);
+                       mac->ledctl_mode2 |= (ledctl_off << shift);
+                       break;
+               default:
+                       /* Do nothing */
+                       break;
+               }
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_get_bus_info_ich8lan - Get/Set the bus type and width
+ *  @hw: pointer to the HW structure
+ *
+ *  ICH8 use the PCI Express bus, but does not contain a PCI Express Capability
+ *  register, so the the bus width is hard coded.
+ **/
+STATIC s32 e1000_get_bus_info_ich8lan(struct e1000_hw *hw)
+{
+       struct e1000_bus_info *bus = &hw->bus;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_get_bus_info_ich8lan");
+
+       ret_val = e1000_get_bus_info_pcie_generic(hw);
+
+       /*
+        * ICH devices are "PCI Express"-ish.  They have
+        * a configuration space, but do not contain
+        * PCI Express Capability registers, so bus width
+        * must be hardcoded.
+        */
+       if (bus->width == e1000_bus_width_unknown)
+               bus->width = e1000_bus_width_pcie_x1;
+
+       return ret_val;
+}
+
+/**
+ *  e1000_reset_hw_ich8lan - Reset the hardware
+ *  @hw: pointer to the HW structure
+ *
+ *  Does a full reset of the hardware which includes a reset of the PHY and
+ *  MAC.
+ **/
+STATIC s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
+{
+       struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
+       u16 kum_cfg;
+       u32 ctrl, reg;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_reset_hw_ich8lan");
+
+       /*
+        * Prevent the PCI-E bus from sticking if there is no TLP connection
+        * on the last TLP read/write transaction when MAC is reset.
+        */
+       ret_val = e1000_disable_pcie_master_generic(hw);
+       if (ret_val)
+               DEBUGOUT("PCI-E Master disable polling has failed.\n");
+
+       DEBUGOUT("Masking off all interrupts\n");
+       E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+
+       /*
+        * Disable the Transmit and Receive units.  Then delay to allow
+        * any pending transactions to complete before we hit the MAC
+        * with the global reset.
+        */
+       E1000_WRITE_REG(hw, E1000_RCTL, 0);
+       E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP);
+       E1000_WRITE_FLUSH(hw);
+
+       msec_delay(10);
+
+       /* Workaround for ICH8 bit corruption issue in FIFO memory */
+       if (hw->mac.type == e1000_ich8lan) {
+               /* Set Tx and Rx buffer allocation to 8k apiece. */
+               E1000_WRITE_REG(hw, E1000_PBA, E1000_PBA_8K);
+               /* Set Packet Buffer Size to 16k. */
+               E1000_WRITE_REG(hw, E1000_PBS, E1000_PBS_16K);
+       }
+
+       if (hw->mac.type == e1000_pchlan) {
+               /* Save the NVM K1 bit setting*/
+               ret_val = e1000_read_nvm(hw, E1000_NVM_K1_CONFIG, 1, &kum_cfg);
+               if (ret_val)
+                       return ret_val;
+
+               if (kum_cfg & E1000_NVM_K1_ENABLE)
+                       dev_spec->nvm_k1_enabled = true;
+               else
+                       dev_spec->nvm_k1_enabled = false;
+       }
+
+       ctrl = E1000_READ_REG(hw, E1000_CTRL);
+
+       if (!hw->phy.ops.check_reset_block(hw)) {
+               /*
+                * Full-chip reset requires MAC and PHY reset at the same
+                * time to make sure the interface between MAC and the
+                * external PHY is reset.
+                */
+               ctrl |= E1000_CTRL_PHY_RST;
+
+               /*
+                * Gate automatic PHY configuration by hardware on
+                * non-managed 82579
+                */
+               if ((hw->mac.type == e1000_pch2lan) &&
+                   !(E1000_READ_REG(hw, E1000_FWSM) & E1000_ICH_FWSM_FW_VALID))
+                       e1000_gate_hw_phy_config_ich8lan(hw, true);
+       }
+       ret_val = e1000_acquire_swflag_ich8lan(hw);
+       DEBUGOUT("Issuing a global reset to ich8lan\n");
+       E1000_WRITE_REG(hw, E1000_CTRL, (ctrl | E1000_CTRL_RST));
+       /* cannot issue a flush here because it hangs the hardware */
+       msec_delay(20);
+
+       /* Set Phy Config Counter to 50msec */
+       if (hw->mac.type == e1000_pch2lan) {
+               reg = E1000_READ_REG(hw, E1000_FEXTNVM3);
+               reg &= ~E1000_FEXTNVM3_PHY_CFG_COUNTER_MASK;
+               reg |= E1000_FEXTNVM3_PHY_CFG_COUNTER_50MSEC;
+               E1000_WRITE_REG(hw, E1000_FEXTNVM3, reg);
+       }
+
+       if (!ret_val)
+               E1000_MUTEX_UNLOCK(&hw->dev_spec.ich8lan.swflag_mutex);
+
+       if (ctrl & E1000_CTRL_PHY_RST) {
+               ret_val = hw->phy.ops.get_cfg_done(hw);
+               if (ret_val)
+                       return ret_val;
+
+               ret_val = e1000_post_phy_reset_ich8lan(hw);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       /*
+        * For PCH, this write will make sure that any noise
+        * will be detected as a CRC error and be dropped rather than show up
+        * as a bad packet to the DMA engine.
+        */
+       if (hw->mac.type == e1000_pchlan)
+               E1000_WRITE_REG(hw, E1000_CRC_OFFSET, 0x65656565);
+
+       E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
+       E1000_READ_REG(hw, E1000_ICR);
+
+       reg = E1000_READ_REG(hw, E1000_KABGTXD);
+       reg |= E1000_KABGTXD_BGSQLBIAS;
+       E1000_WRITE_REG(hw, E1000_KABGTXD, reg);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_init_hw_ich8lan - Initialize the hardware
+ *  @hw: pointer to the HW structure
+ *
+ *  Prepares the hardware for transmit and receive by doing the following:
+ *   - initialize hardware bits
+ *   - initialize LED identification
+ *   - setup receive address registers
+ *   - setup flow control
+ *   - setup transmit descriptors
+ *   - clear statistics
+ **/
+STATIC s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
+{
+       struct e1000_mac_info *mac = &hw->mac;
+       u32 ctrl_ext, txdctl, snoop;
+       s32 ret_val;
+       u16 i;
+
+       DEBUGFUNC("e1000_init_hw_ich8lan");
+
+       e1000_initialize_hw_bits_ich8lan(hw);
+
+       /* Initialize identification LED */
+       ret_val = mac->ops.id_led_init(hw);
+       if (ret_val)
+               DEBUGOUT("Error initializing identification LED\n");
+               /* This is not fatal and we should not stop init due to this */
+
+       /* Setup the receive address. */
+       e1000_init_rx_addrs_generic(hw, mac->rar_entry_count);
+
+       /* Zero out the Multicast HASH table */
+       DEBUGOUT("Zeroing the MTA\n");
+       for (i = 0; i < mac->mta_reg_count; i++)
+               E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
+
+       /*
+        * The 82578 Rx buffer will stall if wakeup is enabled in host and
+        * the ME.  Disable wakeup by clearing the host wakeup bit.
+        * Reset the phy after disabling host wakeup to reset the Rx buffer.
+        */
+       if (hw->phy.type == e1000_phy_82578) {
+               hw->phy.ops.read_reg(hw, BM_PORT_GEN_CFG, &i);
+               i &= ~BM_WUC_HOST_WU_BIT;
+               hw->phy.ops.write_reg(hw, BM_PORT_GEN_CFG, i);
+               ret_val = e1000_phy_hw_reset_ich8lan(hw);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       /* Setup link and flow control */
+       ret_val = mac->ops.setup_link(hw);
+
+       /* Set the transmit descriptor write-back policy for both queues */
+       txdctl = E1000_READ_REG(hw, E1000_TXDCTL(0));
+       txdctl = (txdctl & ~E1000_TXDCTL_WTHRESH) |
+                E1000_TXDCTL_FULL_TX_DESC_WB;
+       txdctl = (txdctl & ~E1000_TXDCTL_PTHRESH) |
+                E1000_TXDCTL_MAX_TX_DESC_PREFETCH;
+       E1000_WRITE_REG(hw, E1000_TXDCTL(0), txdctl);
+       txdctl = E1000_READ_REG(hw, E1000_TXDCTL(1));
+       txdctl = (txdctl & ~E1000_TXDCTL_WTHRESH) |
+                E1000_TXDCTL_FULL_TX_DESC_WB;
+       txdctl = (txdctl & ~E1000_TXDCTL_PTHRESH) |
+                E1000_TXDCTL_MAX_TX_DESC_PREFETCH;
+       E1000_WRITE_REG(hw, E1000_TXDCTL(1), txdctl);
+
+       /*
+        * ICH8 has opposite polarity of no_snoop bits.
+        * By default, we should use snoop behavior.
+        */
+       if (mac->type == e1000_ich8lan)
+               snoop = PCIE_ICH8_SNOOP_ALL;
+       else
+               snoop = (u32) ~(PCIE_NO_SNOOP_ALL);
+       e1000_set_pcie_no_snoop_generic(hw, snoop);
+
+       ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
+       ctrl_ext |= E1000_CTRL_EXT_RO_DIS;
+       E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+
+       /*
+        * Clear all of the statistics registers (clear on read).  It is
+        * important that we do this after we have tried to establish link
+        * because the symbol error count will increment wildly if there
+        * is no link.
+        */
+       e1000_clear_hw_cntrs_ich8lan(hw);
+
+       return ret_val;
+}
+
+/**
+ *  e1000_initialize_hw_bits_ich8lan - Initialize required hardware bits
+ *  @hw: pointer to the HW structure
+ *
+ *  Sets/Clears required hardware bits necessary for correctly setting up the
+ *  hardware for transmit and receive.
+ **/
+static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw)
+{
+       u32 reg;
+
+       DEBUGFUNC("e1000_initialize_hw_bits_ich8lan");
+
+       /* Extended Device Control */
+       reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
+       reg |= (1 << 22);
+       /* Enable PHY low-power state when MAC is at D3 w/o WoL */
+       if (hw->mac.type >= e1000_pchlan)
+               reg |= E1000_CTRL_EXT_PHYPDEN;
+       E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg);
+
+       /* Transmit Descriptor Control 0 */
+       reg = E1000_READ_REG(hw, E1000_TXDCTL(0));
+       reg |= (1 << 22);
+       E1000_WRITE_REG(hw, E1000_TXDCTL(0), reg);
+
+       /* Transmit Descriptor Control 1 */
+       reg = E1000_READ_REG(hw, E1000_TXDCTL(1));
+       reg |= (1 << 22);
+       E1000_WRITE_REG(hw, E1000_TXDCTL(1), reg);
+
+       /* Transmit Arbitration Control 0 */
+       reg = E1000_READ_REG(hw, E1000_TARC(0));
+       if (hw->mac.type == e1000_ich8lan)
+               reg |= (1 << 28) | (1 << 29);
+       reg |= (1 << 23) | (1 << 24) | (1 << 26) | (1 << 27);
+       E1000_WRITE_REG(hw, E1000_TARC(0), reg);
+
+       /* Transmit Arbitration Control 1 */
+       reg = E1000_READ_REG(hw, E1000_TARC(1));
+       if (E1000_READ_REG(hw, E1000_TCTL) & E1000_TCTL_MULR)
+               reg &= ~(1 << 28);
+       else
+               reg |= (1 << 28);
+       reg |= (1 << 24) | (1 << 26) | (1 << 30);
+       E1000_WRITE_REG(hw, E1000_TARC(1), reg);
+
+       /* Device Status */
+       if (hw->mac.type == e1000_ich8lan) {
+               reg = E1000_READ_REG(hw, E1000_STATUS);
+               reg &= ~(1 << 31);
+               E1000_WRITE_REG(hw, E1000_STATUS, reg);
+       }
+
+       /*
+        * work-around descriptor data corruption issue during nfs v2 udp
+        * traffic, just disable the nfs filtering capability
+        */
+       reg = E1000_READ_REG(hw, E1000_RFCTL);
+       reg |= (E1000_RFCTL_NFSW_DIS | E1000_RFCTL_NFSR_DIS);
+       /*
+        * Disable IPv6 extension header parsing because some malformed
+        * IPv6 headers can hang the Rx.
+        */
+       if (hw->mac.type == e1000_ich8lan)
+               reg |= (E1000_RFCTL_IPV6_EX_DIS | E1000_RFCTL_NEW_IPV6_EXT_DIS);
+       E1000_WRITE_REG(hw, E1000_RFCTL, reg);
+
+       return;
+}
+
+/**
+ *  e1000_setup_link_ich8lan - Setup flow control and link settings
+ *  @hw: pointer to the HW structure
+ *
+ *  Determines which flow control settings to use, then configures flow
+ *  control.  Calls the appropriate media-specific link configuration
+ *  function.  Assuming the adapter has a valid link partner, a valid link
+ *  should be established.  Assumes the hardware has previously been reset
+ *  and the transmitter and receiver are not enabled.
+ **/
+STATIC s32 e1000_setup_link_ich8lan(struct e1000_hw *hw)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_setup_link_ich8lan");
+
+       if (hw->phy.ops.check_reset_block(hw))
+               return E1000_SUCCESS;
+
+       /*
+        * ICH parts do not have a word in the NVM to determine
+        * the default flow control setting, so we explicitly
+        * set it to full.
+        */
+       if (hw->fc.requested_mode == e1000_fc_default)
+               hw->fc.requested_mode = e1000_fc_full;
+
+       /*
+        * Save off the requested flow control mode for use later.  Depending
+        * on the link partner's capabilities, we may or may not use this mode.
+        */
+       hw->fc.current_mode = hw->fc.requested_mode;
+
+       DEBUGOUT1("After fix-ups FlowControl is now = %x\n",
+               hw->fc.current_mode);
+
+       /* Continue to configure the copper link. */
+       ret_val = hw->mac.ops.setup_physical_interface(hw);
+       if (ret_val)
+               return ret_val;
+
+       E1000_WRITE_REG(hw, E1000_FCTTV, hw->fc.pause_time);
+       if ((hw->phy.type == e1000_phy_82578) ||
+           (hw->phy.type == e1000_phy_82579) ||
+           (hw->phy.type == e1000_phy_i217) ||
+           (hw->phy.type == e1000_phy_82577)) {
+               E1000_WRITE_REG(hw, E1000_FCRTV_PCH, hw->fc.refresh_time);
+
+               ret_val = hw->phy.ops.write_reg(hw,
+                                            PHY_REG(BM_PORT_CTRL_PAGE, 27),
+                                            hw->fc.pause_time);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       return e1000_set_fc_watermarks_generic(hw);
+}
+
+/**
+ *  e1000_setup_copper_link_ich8lan - Configure MAC/PHY interface
+ *  @hw: pointer to the HW structure
+ *
+ *  Configures the kumeran interface to the PHY to wait the appropriate time
+ *  when polling the PHY, then call the generic setup_copper_link to finish
+ *  configuring the copper link.
+ **/
+STATIC s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw)
+{
+       u32 ctrl;
+       s32 ret_val;
+       u16 reg_data;
+
+       DEBUGFUNC("e1000_setup_copper_link_ich8lan");
+
+       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);
+
+       /*
+        * Set the mac to wait the maximum time between each iteration
+        * and increase the max iterations when polling the phy;
+        * this fixes erroneous timeouts at 10Mbps.
+        */
+       ret_val = e1000_write_kmrn_reg_generic(hw, E1000_KMRNCTRLSTA_TIMEOUTS,
+                                              0xFFFF);
+       if (ret_val)
+               return ret_val;
+       ret_val = e1000_read_kmrn_reg_generic(hw,
+                                             E1000_KMRNCTRLSTA_INBAND_PARAM,
+                                             &reg_data);
+       if (ret_val)
+               return ret_val;
+       reg_data |= 0x3F;
+       ret_val = e1000_write_kmrn_reg_generic(hw,
+                                              E1000_KMRNCTRLSTA_INBAND_PARAM,
+                                              reg_data);
+       if (ret_val)
+               return ret_val;
+
+       switch (hw->phy.type) {
+       case e1000_phy_igp_3:
+               ret_val = e1000_copper_link_setup_igp(hw);
+               if (ret_val)
+                       return ret_val;
+               break;
+       case e1000_phy_bm:
+       case e1000_phy_82578:
+               ret_val = e1000_copper_link_setup_m88(hw);
+               if (ret_val)
+                       return ret_val;
+               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;
+               break;
+       case e1000_phy_ife:
+               ret_val = hw->phy.ops.read_reg(hw, IFE_PHY_MDIX_CONTROL,
+                                              &reg_data);
+               if (ret_val)
+                       return ret_val;
+
+               reg_data &= ~IFE_PMC_AUTO_MDIX;
+
+               switch (hw->phy.mdix) {
+               case 1:
+                       reg_data &= ~IFE_PMC_FORCE_MDIX;
+                       break;
+               case 2:
+                       reg_data |= IFE_PMC_FORCE_MDIX;
+                       break;
+               case 0:
+               default:
+                       reg_data |= IFE_PMC_AUTO_MDIX;
+                       break;
+               }
+               ret_val = hw->phy.ops.write_reg(hw, IFE_PHY_MDIX_CONTROL,
+                                               reg_data);
+               if (ret_val)
+                       return ret_val;
+               break;
+       default:
+               break;
+       }
+
+       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
+ *  @speed: pointer to store current link speed
+ *  @duplex: pointer to store the current link duplex
+ *
+ *  Calls the generic get_speed_and_duplex to retrieve the current link
+ *  information and then calls the Kumeran lock loss workaround for links at
+ *  gigabit speeds.
+ **/
+STATIC s32 e1000_get_link_up_info_ich8lan(struct e1000_hw *hw, u16 *speed,
+                                         u16 *duplex)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_get_link_up_info_ich8lan");
+
+       ret_val = e1000_get_speed_and_duplex_copper_generic(hw, speed, duplex);
+       if (ret_val)
+               return ret_val;
+
+       if ((hw->mac.type == e1000_ich8lan) &&
+           (hw->phy.type == e1000_phy_igp_3) &&
+           (*speed == SPEED_1000)) {
+               ret_val = e1000_kmrn_lock_loss_workaround_ich8lan(hw);
+       }
+
+       return ret_val;
+}
+
+/**
+ *  e1000_kmrn_lock_loss_workaround_ich8lan - Kumeran workaround
+ *  @hw: pointer to the HW structure
+ *
+ *  Work-around for 82566 Kumeran PCS lock loss:
+ *  On link status change (i.e. PCI reset, speed change) and link is up and
+ *  speed is gigabit-
+ *    0) if workaround is optionally disabled do nothing
+ *    1) wait 1ms for Kumeran link to come up
+ *    2) check Kumeran Diagnostic register PCS lock loss bit
+ *    3) if not set the link is locked (all is good), otherwise...
+ *    4) reset the PHY
+ *    5) repeat up to 10 times
+ *  Note: this is only called for IGP3 copper when speed is 1gb.
+ **/
+static s32 e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw)
+{
+       struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
+       u32 phy_ctrl;
+       s32 ret_val;
+       u16 i, data;
+       bool link;
+
+       DEBUGFUNC("e1000_kmrn_lock_loss_workaround_ich8lan");
+
+       if (!dev_spec->kmrn_lock_loss_workaround_enabled)
+               return E1000_SUCCESS;
+
+       /*
+        * Make sure link is up before proceeding.  If not just return.
+        * Attempting this while link is negotiating fouled up link
+        * stability
+        */
+       ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
+       if (!link)
+               return E1000_SUCCESS;
+
+       for (i = 0; i < 10; i++) {
+               /* read once to clear */
+               ret_val = hw->phy.ops.read_reg(hw, IGP3_KMRN_DIAG, &data);
+               if (ret_val)
+                       return ret_val;
+               /* and again to get new status */
+               ret_val = hw->phy.ops.read_reg(hw, IGP3_KMRN_DIAG, &data);
+               if (ret_val)
+                       return ret_val;
+
+               /* check for PCS lock */
+               if (!(data & IGP3_KMRN_DIAG_PCS_LOCK_LOSS))
+                       return E1000_SUCCESS;
+
+               /* Issue PHY reset */
+               hw->phy.ops.reset(hw);
+               msec_delay_irq(5);
+       }
+       /* Disable GigE link negotiation */
+       phy_ctrl = E1000_READ_REG(hw, E1000_PHY_CTRL);
+       phy_ctrl |= (E1000_PHY_CTRL_GBE_DISABLE |
+                    E1000_PHY_CTRL_NOND0A_GBE_DISABLE);
+       E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl);
+
+       /*
+        * Call gig speed drop workaround on Gig disable before accessing
+        * any PHY registers
+        */
+       e1000_gig_downshift_workaround_ich8lan(hw);
+
+       /* unable to acquire PCS lock */
+       return -E1000_ERR_PHY;
+}
+
+/**
+ *  e1000_set_kmrn_lock_loss_workaround_ich8lan - Set Kumeran workaround state
+ *  @hw: pointer to the HW structure
+ *  @state: boolean value used to set the current Kumeran workaround state
+ *
+ *  If ICH8, set the current Kumeran workaround state (enabled - true
+ *  /disabled - false).
+ **/
+void e1000_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
+                                                bool state)
+{
+       struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
+
+       DEBUGFUNC("e1000_set_kmrn_lock_loss_workaround_ich8lan");
+
+       if (hw->mac.type != e1000_ich8lan) {
+               DEBUGOUT("Workaround applies to ICH8 only.\n");
+               return;
+       }
+
+       dev_spec->kmrn_lock_loss_workaround_enabled = state;
+
+       return;
+}
+
+/**
+ *  e1000_ipg3_phy_powerdown_workaround_ich8lan - Power down workaround on D3
+ *  @hw: pointer to the HW structure
+ *
+ *  Workaround for 82566 power-down on D3 entry:
+ *    1) disable gigabit link
+ *    2) write VR power-down enable
+ *    3) read it back
+ *  Continue if successful, else issue LCD reset and repeat
+ **/
+void e1000_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw)
+{
+       u32 reg;
+       u16 data;
+       u8  retry = 0;
+
+       DEBUGFUNC("e1000_igp3_phy_powerdown_workaround_ich8lan");
+
+       if (hw->phy.type != e1000_phy_igp_3)
+               return;
+
+       /* Try the workaround twice (if needed) */
+       do {
+               /* Disable link */
+               reg = E1000_READ_REG(hw, E1000_PHY_CTRL);
+               reg |= (E1000_PHY_CTRL_GBE_DISABLE |
+                       E1000_PHY_CTRL_NOND0A_GBE_DISABLE);
+               E1000_WRITE_REG(hw, E1000_PHY_CTRL, reg);
+
+               /*
+                * Call gig speed drop workaround on Gig disable before
+                * accessing any PHY registers
+                */
+               if (hw->mac.type == e1000_ich8lan)
+                       e1000_gig_downshift_workaround_ich8lan(hw);
+
+               /* Write VR power-down enable */
+               hw->phy.ops.read_reg(hw, IGP3_VR_CTRL, &data);
+               data &= ~IGP3_VR_CTRL_DEV_POWERDOWN_MODE_MASK;
+               hw->phy.ops.write_reg(hw, IGP3_VR_CTRL,
+                                     data | IGP3_VR_CTRL_MODE_SHUTDOWN);
+
+               /* Read it back and test */
+               hw->phy.ops.read_reg(hw, IGP3_VR_CTRL, &data);
+               data &= IGP3_VR_CTRL_DEV_POWERDOWN_MODE_MASK;
+               if ((data == IGP3_VR_CTRL_MODE_SHUTDOWN) || retry)
+                       break;
+
+               /* Issue PHY reset and repeat at most one more time */
+               reg = E1000_READ_REG(hw, E1000_CTRL);
+               E1000_WRITE_REG(hw, E1000_CTRL, reg | E1000_CTRL_PHY_RST);
+               retry++;
+       } while (retry);
+}
+
+/**
+ *  e1000_gig_downshift_workaround_ich8lan - WoL from S5 stops working
+ *  @hw: pointer to the HW structure
+ *
+ *  Steps to take when dropping from 1Gb/s (eg. link cable removal (LSC),
+ *  LPLU, Gig disable, MDIC PHY reset):
+ *    1) Set Kumeran Near-end loopback
+ *    2) Clear Kumeran Near-end loopback
+ *  Should only be called for ICH8[m] devices with any 1G Phy.
+ **/
+void e1000_gig_downshift_workaround_ich8lan(struct e1000_hw *hw)
+{
+       s32 ret_val;
+       u16 reg_data;
+
+       DEBUGFUNC("e1000_gig_downshift_workaround_ich8lan");
+
+       if ((hw->mac.type != e1000_ich8lan) ||
+           (hw->phy.type == e1000_phy_ife))
+               return;
+
+       ret_val = e1000_read_kmrn_reg_generic(hw, E1000_KMRNCTRLSTA_DIAG_OFFSET,
+                                             &reg_data);
+       if (ret_val)
+               return;
+       reg_data |= E1000_KMRNCTRLSTA_DIAG_NELPBK;
+       ret_val = e1000_write_kmrn_reg_generic(hw,
+                                              E1000_KMRNCTRLSTA_DIAG_OFFSET,
+                                              reg_data);
+       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_suspend_workarounds_ich8lan - workarounds needed during S0->Sx
+ *  @hw: pointer to the HW structure
+ *
+ *  During S0 to Sx transition, it is possible the link remains at gig
+ *  instead of negotiating to a lower speed.  Before going to Sx, set
+ *  'Gig Disable' to force link speed negotiation to a lower speed based on
+ *  the LPLU setting in the NVM or custom setting.  For PCH and newer parts,
+ *  the OEM bits PHY register (LED, GbE disable and LPLU configurations) also
+ *  needs to be written.
+ *  Parts that support (and are linked to a partner which support) EEE in
+ *  100Mbps should disable LPLU since 100Mbps w/ EEE requires less power
+ *  than 10Mbps w/o EEE.
+ **/
+void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw)
+{
+       struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
+       u32 phy_ctrl;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_suspend_workarounds_ich8lan");
+
+       phy_ctrl = E1000_READ_REG(hw, E1000_PHY_CTRL);
+       phy_ctrl |= E1000_PHY_CTRL_GBE_DISABLE;
+       if (hw->phy.type == e1000_phy_i217) {
+               u16 phy_reg;
+
+               ret_val = hw->phy.ops.acquire(hw);
+               if (ret_val)
+                       goto out;
+
+               if (!dev_spec->eee_disable) {
+                       u16 eee_advert;
+
+                       ret_val =
+                           e1000_read_emi_reg_locked(hw,
+                                                     I217_EEE_ADVERTISEMENT,
+                                                     &eee_advert);
+                       if (ret_val)
+                               goto release;
+
+                       /*
+                        * Disable LPLU if both link partners support 100BaseT
+                        * EEE and 100Full is advertised on both ends of the
+                        * link.
+                        */
+                       if ((eee_advert & I82579_EEE_100_SUPPORTED) &&
+                           (dev_spec->eee_lp_ability &
+                            I82579_EEE_100_SUPPORTED) &&
+                           (hw->phy.autoneg_advertised & ADVERTISE_100_FULL))
+                               phy_ctrl &= ~(E1000_PHY_CTRL_D0A_LPLU |
+                                             E1000_PHY_CTRL_NOND0A_LPLU);
+               }
+
+               /*
+                * For i217 Intel Rapid Start Technology support,
+                * when the system is going into Sx and no manageability engine
+                * is present, the driver must configure proxy to reset only on
+                * power good.  LPI (Low Power Idle) state must also reset only
+                * on power good, as well as the MTA (Multicast table array).
+                * The SMBus release must also be disabled on LCD reset.
+                */
+               if (!(E1000_READ_REG(hw, E1000_FWSM) &
+                       E1000_ICH_FWSM_FW_VALID)) {
+
+                       /* Enable proxy to reset only on power good. */
+                       hw->phy.ops.read_reg_locked(hw, I217_PROXY_CTRL,
+                                                   &phy_reg);
+                       phy_reg |= I217_PROXY_CTRL_AUTO_DISABLE;
+                       hw->phy.ops.write_reg_locked(hw, I217_PROXY_CTRL,
+                                                    phy_reg);
+
+                       /*
+                        * Set bit enable LPI (EEE) to reset only on
+                        * power good.
+                       */
+                       hw->phy.ops.read_reg_locked(hw, I217_SxCTRL, &phy_reg);
+                       phy_reg |= I217_SxCTRL_ENABLE_LPI_RESET;
+                       hw->phy.ops.write_reg_locked(hw, I217_SxCTRL, phy_reg);
+
+                       /* Disable the SMB release on LCD reset. */
+                       hw->phy.ops.read_reg_locked(hw, I217_MEMPWR, &phy_reg);
+                       phy_reg &= ~I217_MEMPWR_DISABLE_SMB_RELEASE;
+                       hw->phy.ops.write_reg_locked(hw, I217_MEMPWR, phy_reg);
+               }
+
+               /*
+                * Enable MTA to reset for Intel Rapid Start Technology
+                * Support
+                */
+               hw->phy.ops.read_reg_locked(hw, I217_CGFREG, &phy_reg);
+               phy_reg |= I217_CGFREG_ENABLE_MTA_RESET;
+               hw->phy.ops.write_reg_locked(hw, I217_CGFREG, phy_reg);
+
+release:
+               hw->phy.ops.release(hw);
+       }
+out:
+       E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl);
+
+       if (hw->mac.type == e1000_ich8lan)
+               e1000_gig_downshift_workaround_ich8lan(hw);
+
+       if (hw->mac.type >= e1000_pchlan) {
+               e1000_oem_bits_config_ich8lan(hw, false);
+
+               /* Reset PHY to activate OEM bits on 82577/8 */
+               if (hw->mac.type == e1000_pchlan)
+                       e1000_phy_hw_reset_generic(hw);
+
+               ret_val = hw->phy.ops.acquire(hw);
+               if (ret_val)
+                       return;
+               e1000_write_smbus_addr(hw);
+               hw->phy.ops.release(hw);
+       }
+
+       return;
+}
+
+/**
+ *  e1000_resume_workarounds_pchlan - workarounds needed during Sx->S0
+ *  @hw: pointer to the HW structure
+ *
+ *  During Sx to S0 transitions on non-managed devices or managed devices
+ *  on which PHY resets are not blocked, if the PHY registers cannot be
+ *  accessed properly by the s/w toggle the LANPHYPC value to power cycle
+ *  the PHY.
+ *  On i217, setup Intel Rapid Start Technology.
+ **/
+void e1000_resume_workarounds_pchlan(struct e1000_hw *hw)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_resume_workarounds_pchlan");
+
+       if (hw->mac.type < e1000_pch2lan)
+               return;
+
+       ret_val = e1000_init_phy_workarounds_pchlan(hw);
+       if (ret_val) {
+               DEBUGOUT1("Failed to init PHY flow ret_val=%d\n", ret_val);
+               return;
+       }
+
+       /*
+        * For i217 Intel Rapid Start Technology support when the system
+        * is transitioning from Sx and no manageability engine is present
+        * configure SMBus to restore on reset, disable proxy, and enable
+        * the reset on MTA (Multicast table array).
+        */
+       if (hw->phy.type == e1000_phy_i217) {
+               u16 phy_reg;
+
+               ret_val = hw->phy.ops.acquire(hw);
+               if (ret_val) {
+                       DEBUGOUT("Failed to setup iRST\n");
+                       return;
+               }
+
+               if (!(E1000_READ_REG(hw, E1000_FWSM) &
+                   E1000_ICH_FWSM_FW_VALID)) {
+                       /*
+                        * Restore clear on SMB if no manageability engine
+                        * is present
+                        */
+                       ret_val = hw->phy.ops.read_reg_locked(hw, I217_MEMPWR,
+                                                             &phy_reg);
+                       if (ret_val)
+                               goto release;
+                       phy_reg |= I217_MEMPWR_DISABLE_SMB_RELEASE;
+                       hw->phy.ops.write_reg_locked(hw, I217_MEMPWR, phy_reg);
+
+                       /* Disable Proxy */
+                       hw->phy.ops.write_reg_locked(hw, I217_PROXY_CTRL, 0);
+               }
+               /* Enable reset on MTA */
+               ret_val = hw->phy.ops.read_reg_locked(hw, I217_CGFREG,
+                                                     &phy_reg);
+               if (ret_val)
+                       goto release;
+               phy_reg &= ~I217_CGFREG_ENABLE_MTA_RESET;
+               hw->phy.ops.write_reg_locked(hw, I217_CGFREG, phy_reg);
+release:
+               if (ret_val)
+                       DEBUGOUT1("Error %d in resume workarounds\n", ret_val);
+               hw->phy.ops.release(hw);
+       }
+}
+
+/**
+ *  e1000_cleanup_led_ich8lan - Restore the default LED operation
+ *  @hw: pointer to the HW structure
+ *
+ *  Return the LED back to the default configuration.
+ **/
+STATIC s32 e1000_cleanup_led_ich8lan(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_cleanup_led_ich8lan");
+
+       if (hw->phy.type == e1000_phy_ife)
+               return hw->phy.ops.write_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED,
+                                            0);
+
+       E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_default);
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_led_on_ich8lan - Turn LEDs on
+ *  @hw: pointer to the HW structure
+ *
+ *  Turn on the LEDs.
+ **/
+STATIC s32 e1000_led_on_ich8lan(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_led_on_ich8lan");
+
+       if (hw->phy.type == e1000_phy_ife)
+               return hw->phy.ops.write_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED,
+                               (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_ON));
+
+       E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode2);
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_led_off_ich8lan - Turn LEDs off
+ *  @hw: pointer to the HW structure
+ *
+ *  Turn off the LEDs.
+ **/
+STATIC s32 e1000_led_off_ich8lan(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_led_off_ich8lan");
+
+       if (hw->phy.type == e1000_phy_ife)
+               return hw->phy.ops.write_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED,
+                              (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_OFF));
+
+       E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1);
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_setup_led_pchlan - Configures SW controllable LED
+ *  @hw: pointer to the HW structure
+ *
+ *  This prepares the SW controllable LED for use.
+ **/
+STATIC s32 e1000_setup_led_pchlan(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_setup_led_pchlan");
+
+       return hw->phy.ops.write_reg(hw, HV_LED_CONFIG,
+                                    (u16)hw->mac.ledctl_mode1);
+}
+
+/**
+ *  e1000_cleanup_led_pchlan - Restore the default LED operation
+ *  @hw: pointer to the HW structure
+ *
+ *  Return the LED back to the default configuration.
+ **/
+STATIC s32 e1000_cleanup_led_pchlan(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_cleanup_led_pchlan");
+
+       return hw->phy.ops.write_reg(hw, HV_LED_CONFIG,
+                                    (u16)hw->mac.ledctl_default);
+}
+
+/**
+ *  e1000_led_on_pchlan - Turn LEDs on
+ *  @hw: pointer to the HW structure
+ *
+ *  Turn on the LEDs.
+ **/
+STATIC s32 e1000_led_on_pchlan(struct e1000_hw *hw)
+{
+       u16 data = (u16)hw->mac.ledctl_mode2;
+       u32 i, led;
+
+       DEBUGFUNC("e1000_led_on_pchlan");
+
+       /*
+        * If no link, then turn LED on by setting the invert bit
+        * for each LED that's mode is "link_up" in ledctl_mode2.
+        */
+       if (!(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)) {
+               for (i = 0; i < 3; i++) {
+                       led = (data >> (i * 5)) & E1000_PHY_LED0_MASK;
+                       if ((led & E1000_PHY_LED0_MODE_MASK) !=
+                           E1000_LEDCTL_MODE_LINK_UP)
+                               continue;
+                       if (led & E1000_PHY_LED0_IVRT)
+                               data &= ~(E1000_PHY_LED0_IVRT << (i * 5));
+                       else
+                               data |= (E1000_PHY_LED0_IVRT << (i * 5));
+               }
+       }
+
+       return hw->phy.ops.write_reg(hw, HV_LED_CONFIG, data);
+}
+
+/**
+ *  e1000_led_off_pchlan - Turn LEDs off
+ *  @hw: pointer to the HW structure
+ *
+ *  Turn off the LEDs.
+ **/
+STATIC s32 e1000_led_off_pchlan(struct e1000_hw *hw)
+{
+       u16 data = (u16)hw->mac.ledctl_mode1;
+       u32 i, led;
+
+       DEBUGFUNC("e1000_led_off_pchlan");
+
+       /*
+        * If no link, then turn LED off by clearing the invert bit
+        * for each LED that's mode is "link_up" in ledctl_mode1.
+        */
+       if (!(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)) {
+               for (i = 0; i < 3; i++) {
+                       led = (data >> (i * 5)) & E1000_PHY_LED0_MASK;
+                       if ((led & E1000_PHY_LED0_MODE_MASK) !=
+                           E1000_LEDCTL_MODE_LINK_UP)
+                               continue;
+                       if (led & E1000_PHY_LED0_IVRT)
+                               data &= ~(E1000_PHY_LED0_IVRT << (i * 5));
+                       else
+                               data |= (E1000_PHY_LED0_IVRT << (i * 5));
+               }
+       }
+
+       return hw->phy.ops.write_reg(hw, HV_LED_CONFIG, data);
+}
+
+/**
+ *  e1000_get_cfg_done_ich8lan - Read config done bit after Full or PHY reset
+ *  @hw: pointer to the HW structure
+ *
+ *  Read appropriate register for the config done bit for completion status
+ *  and configure the PHY through s/w for EEPROM-less parts.
+ *
+ *  NOTE: some silicon which is EEPROM-less will fail trying to read the
+ *  config done bit, so only an error is logged and continues.  If we were
+ *  to return with error, EEPROM-less silicon would not be able to be reset
+ *  or change link.
+ **/
+STATIC s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_SUCCESS;
+       u32 bank = 0;
+       u32 status;
+
+       DEBUGFUNC("e1000_get_cfg_done_ich8lan");
+
+       e1000_get_cfg_done_generic(hw);
+
+       /* Wait for indication from h/w that it has completed basic config */
+       if (hw->mac.type >= e1000_ich10lan) {
+               e1000_lan_init_done_ich8lan(hw);
+       } else {
+               ret_val = e1000_get_auto_rd_done_generic(hw);
+               if (ret_val) {
+                       /*
+                        * When auto config read does not complete, do not
+                        * return with an error. This can happen in situations
+                        * where there is no eeprom and prevents getting link.
+                        */
+                       DEBUGOUT("Auto Read Done did not complete\n");
+                       ret_val = E1000_SUCCESS;
+               }
+       }
+
+       /* Clear PHY Reset Asserted bit */
+       status = E1000_READ_REG(hw, E1000_STATUS);
+       if (status & E1000_STATUS_PHYRA)
+               E1000_WRITE_REG(hw, E1000_STATUS, status & ~E1000_STATUS_PHYRA);
+       else
+               DEBUGOUT("PHY Reset Asserted not set - needs delay\n");
+
+       /* If EEPROM is not marked present, init the IGP 3 PHY manually */
+       if (hw->mac.type <= e1000_ich9lan) {
+               if (!(E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) &&
+                   (hw->phy.type == e1000_phy_igp_3)) {
+                       e1000_phy_init_script_igp3(hw);
+               }
+       } else {
+               if (e1000_valid_nvm_bank_detect_ich8lan(hw, &bank)) {
+                       /* Maybe we should do a basic PHY config */
+                       DEBUGOUT("EEPROM not present\n");
+                       ret_val = -E1000_ERR_CONFIG;
+               }
+       }
+
+       return ret_val;
+}
+
+/**
+ * e1000_power_down_phy_copper_ich8lan - Remove link during PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, remove the link.
+ **/
+STATIC void e1000_power_down_phy_copper_ich8lan(struct e1000_hw *hw)
+{
+       /* If the management interface is not enabled, then power down */
+       if (!(hw->mac.ops.check_mng_mode(hw) ||
+             hw->phy.ops.check_reset_block(hw)))
+               e1000_power_down_phy_copper(hw);
+
+       return;
+}
+
+/**
+ *  e1000_clear_hw_cntrs_ich8lan - Clear statistical counters
+ *  @hw: pointer to the HW structure
+ *
+ *  Clears hardware counters specific to the silicon family and calls
+ *  clear_hw_cntrs_generic to clear all general purpose counters.
+ **/
+STATIC void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw)
+{
+       u16 phy_data;
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_clear_hw_cntrs_ich8lan");
+
+       e1000_clear_hw_cntrs_base_generic(hw);
+
+       E1000_READ_REG(hw, E1000_ALGNERRC);
+       E1000_READ_REG(hw, E1000_RXERRC);
+       E1000_READ_REG(hw, E1000_TNCRS);
+       E1000_READ_REG(hw, E1000_CEXTERR);
+       E1000_READ_REG(hw, E1000_TSCTC);
+       E1000_READ_REG(hw, E1000_TSCTFC);
+
+       E1000_READ_REG(hw, E1000_MGTPRC);
+       E1000_READ_REG(hw, E1000_MGTPDC);
+       E1000_READ_REG(hw, E1000_MGTPTC);
+
+       E1000_READ_REG(hw, E1000_IAC);
+       E1000_READ_REG(hw, E1000_ICRXOC);
+
+       /* Clear PHY statistics registers */
+       if ((hw->phy.type == e1000_phy_82578) ||
+           (hw->phy.type == e1000_phy_82579) ||
+           (hw->phy.type == e1000_phy_i217) ||
+           (hw->phy.type == e1000_phy_82577)) {
+               ret_val = hw->phy.ops.acquire(hw);
+               if (ret_val)
+                       return;
+               ret_val = hw->phy.ops.set_page(hw,
+                                              HV_STATS_PAGE << IGP_PAGE_SHIFT);
+               if (ret_val)
+                       goto release;
+               hw->phy.ops.read_reg_page(hw, HV_SCC_UPPER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_SCC_LOWER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_ECOL_UPPER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_ECOL_LOWER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_MCC_UPPER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_MCC_LOWER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_LATECOL_UPPER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_LATECOL_LOWER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_COLC_UPPER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_COLC_LOWER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_DC_UPPER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_DC_LOWER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_TNCRS_UPPER, &phy_data);
+               hw->phy.ops.read_reg_page(hw, HV_TNCRS_LOWER, &phy_data);
+release:
+               hw->phy.ops.release(hw);
+       }
+}
+
diff --git a/lib/librte_pmd_e1000/e1000/e1000_ich8lan.h b/lib/librte_pmd_e1000/e1000/e1000_ich8lan.h
new file mode 100644 (file)
index 0000000..4abdfd6
--- /dev/null
@@ -0,0 +1,322 @@
+/*******************************************************************************
+
+Copyright (c) 2001-2012, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#ifndef _E1000_ICH8LAN_H_
+#define _E1000_ICH8LAN_H_
+
+#define ICH_FLASH_GFPREG               0x0000
+#define ICH_FLASH_HSFSTS               0x0004
+#define ICH_FLASH_HSFCTL               0x0006
+#define ICH_FLASH_FADDR                        0x0008
+#define ICH_FLASH_FDATA0               0x0010
+
+/* Requires up to 10 seconds when MNG might be accessing part. */
+#define ICH_FLASH_READ_COMMAND_TIMEOUT 10000000
+#define ICH_FLASH_WRITE_COMMAND_TIMEOUT        10000000
+#define ICH_FLASH_ERASE_COMMAND_TIMEOUT        10000000
+#define ICH_FLASH_LINEAR_ADDR_MASK     0x00FFFFFF
+#define ICH_FLASH_CYCLE_REPEAT_COUNT   10
+
+#define ICH_CYCLE_READ                 0
+#define ICH_CYCLE_WRITE                        2
+#define ICH_CYCLE_ERASE                        3
+
+#define FLASH_GFPREG_BASE_MASK         0x1FFF
+#define FLASH_SECTOR_ADDR_SHIFT                12
+
+#define ICH_FLASH_SEG_SIZE_256         256
+#define ICH_FLASH_SEG_SIZE_4K          4096
+#define ICH_FLASH_SEG_SIZE_8K          8192
+#define ICH_FLASH_SEG_SIZE_64K         65536
+#define ICH_FLASH_SECTOR_SIZE          4096
+
+#define ICH_FLASH_REG_MAPSIZE          0x00A0
+
+#define E1000_ICH_FWSM_RSPCIPHY                0x00000040 /* Reset PHY on PCI Reset */
+#define E1000_ICH_FWSM_DISSW           0x10000000 /* FW Disables SW Writes */
+/* FW established a valid mode */
+#define E1000_ICH_FWSM_FW_VALID                0x00008000
+#define E1000_ICH_FWSM_PCIM2PCI                0x01000000 /* ME PCIm-to-PCI active */
+#define E1000_ICH_FWSM_PCIM2PCI_COUNT  2000
+
+#define E1000_ICH_MNG_IAMT_MODE                0x2
+
+#define E1000_FWSM_PROXY_MODE          0x00000008 /* FW is in proxy mode */
+#define E1000_FWSM_MEMC                        0x00000010 /* ME Messaging capable */
+
+/* Shared Receive Address Registers */
+#define E1000_SHRAH_AV         0x80000000 /* Addr Valid bit */
+#define E1000_SHRAH_MAV                0x40000000 /* Multicast Addr Valid bit */
+
+#define E1000_H2ME             0x05B50    /* Host to ME */
+#define E1000_H2ME_LSECREQ     0x00000001 /* Linksec Request */
+#define E1000_H2ME_LSECA       0x00000002 /* Linksec Active */
+#define E1000_H2ME_LSECSF      0x00000004 /* Linksec Failed */
+#define E1000_H2ME_LSECD       0x00000008 /* Linksec Disabled */
+#define E1000_H2ME_SLCAPD      0x00000010 /* Start LCAPD */
+#define E1000_H2ME_IPV4_ARP_EN 0x00000020 /* Arp Offload enable bit */
+#define E1000_H2ME_IPV6_NS_EN  0x00000040 /* NS Offload enable bit */
+#define E1000_H2ME_ULP         0x00000800 /* ULP Indication Bit */
+
+#define ID_LED_DEFAULT_ICH8LAN ((ID_LED_DEF1_DEF2 << 12) | \
+                                (ID_LED_OFF1_OFF2 <<  8) | \
+                                (ID_LED_OFF1_ON2  <<  4) | \
+                                (ID_LED_DEF1_DEF2))
+
+#define E1000_ICH_NVM_SIG_WORD         0x13
+#define E1000_ICH_NVM_SIG_MASK         0xC000
+#define E1000_ICH_NVM_VALID_SIG_MASK   0xC0
+#define E1000_ICH_NVM_SIG_VALUE                0x80
+
+#define E1000_ICH8_LAN_INIT_TIMEOUT    1500
+
+/* FEXT register bit definition */
+#define E1000_FEXT_PHY_CABLE_DISCONNECTED      0x00000004
+
+#define E1000_FEXTNVM_SW_CONFIG                1
+#define E1000_FEXTNVM_SW_CONFIG_ICH8M  (1 << 27) /* Bit redefined for ICH8M */
+
+#define E1000_FEXTNVM3 0x0003C  /* Future Extended NVM 3 - RW */
+#define E1000_FEXTNVM3_PHY_CFG_COUNTER_MASK    0x0C000000
+#define E1000_FEXTNVM3_PHY_CFG_COUNTER_50MSEC  0x08000000
+
+#define E1000_FEXTNVM4_BEACON_DURATION_MASK    0x7
+#define E1000_FEXTNVM4_BEACON_DURATION_8USEC   0x7
+#define E1000_FEXTNVM4_BEACON_DURATION_16USEC  0x3
+
+#define PCIE_ICH8_SNOOP_ALL    PCIE_NO_SNOOP_ALL
+
+#define E1000_ICH_RAR_ENTRIES  7
+#define E1000_PCH2_RAR_ENTRIES 5 /* RAR[0], SHRA[0-3] */
+
+#define PHY_PAGE_SHIFT         5
+#define PHY_REG(page, reg)     (((page) << PHY_PAGE_SHIFT) | \
+                                ((reg) & MAX_PHY_REG_ADDRESS))
+#define IGP3_KMRN_DIAG         PHY_REG(770, 19) /* KMRN Diagnostic */
+#define IGP3_VR_CTRL           PHY_REG(776, 18) /* Voltage Regulator Control */
+#define IGP3_CAPABILITY                PHY_REG(776, 19) /* Capability */
+#define IGP3_PM_CTRL           PHY_REG(769, 20) /* Power Management Control */
+
+#define IGP3_KMRN_DIAG_PCS_LOCK_LOSS           0x0002
+#define IGP3_VR_CTRL_DEV_POWERDOWN_MODE_MASK   0x0300
+#define IGP3_VR_CTRL_MODE_SHUTDOWN             0x0200
+#define IGP3_PM_CTRL_FORCE_PWR_DOWN            0x0020
+
+/* PHY Wakeup Registers and defines */
+#define BM_PORT_GEN_CFG                PHY_REG(BM_PORT_CTRL_PAGE, 17)
+#define BM_RCTL                        PHY_REG(BM_WUC_PAGE, 0)
+#define BM_WUC                 PHY_REG(BM_WUC_PAGE, 1)
+#define BM_WUFC                        PHY_REG(BM_WUC_PAGE, 2)
+#define BM_WUS                 PHY_REG(BM_WUC_PAGE, 3)
+#define BM_RAR_L(_i)           (BM_PHY_REG(BM_WUC_PAGE, 16 + ((_i) << 2)))
+#define BM_RAR_M(_i)           (BM_PHY_REG(BM_WUC_PAGE, 17 + ((_i) << 2)))
+#define BM_RAR_H(_i)           (BM_PHY_REG(BM_WUC_PAGE, 18 + ((_i) << 2)))
+#define BM_RAR_CTRL(_i)                (BM_PHY_REG(BM_WUC_PAGE, 19 + ((_i) << 2)))
+#define BM_MTA(_i)             (BM_PHY_REG(BM_WUC_PAGE, 128 + ((_i) << 1)))
+#define BM_IPAV                        (BM_PHY_REG(BM_WUC_PAGE, 64))
+#define BM_IP4AT_L(_i)         (BM_PHY_REG(BM_WUC_PAGE, 82 + ((_i) * 2)))
+#define BM_IP4AT_H(_i)         (BM_PHY_REG(BM_WUC_PAGE, 83 + ((_i) * 2)))
+
+#define BM_SHRAL_LOWER(_i)     (BM_PHY_REG(BM_WUC_PAGE, 44 + ((_i) * 4)))
+#define BM_SHRAL_UPPER(_i)     (BM_PHY_REG(BM_WUC_PAGE, 45 + ((_i) * 4)))
+#define BM_SHRAH_LOWER(_i)     (BM_PHY_REG(BM_WUC_PAGE, 46 + ((_i) * 4)))
+#define BM_SHRAH_UPPER(_i)     (BM_PHY_REG(BM_WUC_PAGE, 47 + ((_i) * 4)))
+
+#define I217_SHRAL_LOWER(_i)   (BM_PHY_REG(BM_WUC_PAGE, 20 + ((_i) * 4)))
+#define I217_SHRAL_UPPER(_i)   (BM_PHY_REG(BM_WUC_PAGE, 21 + ((_i) * 4)))
+#define I217_SHRAH_LOWER(_i)   (BM_PHY_REG(BM_WUC_PAGE, 22 + ((_i) * 4)))
+#define I217_SHRAH_UPPER(_i)   (BM_PHY_REG(BM_WUC_PAGE, 23 + ((_i) * 4)))
+
+#define BM_RCTL_UPE            0x0001 /* Unicast Promiscuous Mode */
+#define BM_RCTL_MPE            0x0002 /* Multicast Promiscuous Mode */
+#define BM_RCTL_MO_SHIFT       3      /* Multicast Offset Shift */
+#define BM_RCTL_MO_MASK                (3 << 3) /* Multicast Offset Mask */
+#define BM_RCTL_BAM            0x0020 /* Broadcast Accept Mode */
+#define BM_RCTL_PMCF           0x0040 /* Pass MAC Control Frames */
+#define BM_RCTL_RFCE           0x0080 /* Rx Flow Control Enable */
+
+#define HV_LED_CONFIG          PHY_REG(768, 30) /* LED Configuration */
+#define HV_MUX_DATA_CTRL       PHY_REG(776, 16)
+#define HV_MUX_DATA_CTRL_GEN_TO_MAC    0x0400
+#define HV_MUX_DATA_CTRL_FORCE_SPEED   0x0004
+#define HV_STATS_PAGE  778
+#define HV_SCC_UPPER   PHY_REG(HV_STATS_PAGE, 16) /* Single Collision Count */
+#define HV_SCC_LOWER   PHY_REG(HV_STATS_PAGE, 17)
+#define HV_ECOL_UPPER  PHY_REG(HV_STATS_PAGE, 18) /* Excessive Coll. Count */
+#define HV_ECOL_LOWER  PHY_REG(HV_STATS_PAGE, 19)
+#define HV_MCC_UPPER   PHY_REG(HV_STATS_PAGE, 20) /* Multiple Coll. Count */
+#define HV_MCC_LOWER   PHY_REG(HV_STATS_PAGE, 21)
+#define HV_LATECOL_UPPER PHY_REG(HV_STATS_PAGE, 23) /* Late Collision Count */
+#define HV_LATECOL_LOWER PHY_REG(HV_STATS_PAGE, 24)
+#define HV_COLC_UPPER  PHY_REG(HV_STATS_PAGE, 25) /* Collision Count */
+#define HV_COLC_LOWER  PHY_REG(HV_STATS_PAGE, 26)
+#define HV_DC_UPPER    PHY_REG(HV_STATS_PAGE, 27) /* Defer Count */
+#define HV_DC_LOWER    PHY_REG(HV_STATS_PAGE, 28)
+#define HV_TNCRS_UPPER PHY_REG(HV_STATS_PAGE, 29) /* Transmit with no CRS */
+#define HV_TNCRS_LOWER PHY_REG(HV_STATS_PAGE, 30)
+
+#define E1000_FCRTV_PCH        0x05F40 /* PCH Flow Control Refresh Timer Value */
+
+/*
+ * For ICH, the name used for NVM word 17h is LED1 Config.
+ * For PCH, the word was re-named to OEM Config.
+ */
+#define E1000_NVM_LED1_CONFIG          0x17   /* NVM LED1/LPLU Config Word */
+#define E1000_NVM_LED1_CONFIG_LPLU_NONDOA 0x0400 /* NVM LPLU in non-D0a Bit */
+#define E1000_NVM_OEM_CONFIG           E1000_NVM_LED1_CONFIG
+#define E1000_NVM_OEM_CONFIG_LPLU_NONDOA E1000_NVM_LED1_CONFIG_LPLU_NONDOA
+
+#define E1000_NVM_K1_CONFIG    0x1B /* NVM K1 Config Word */
+#define E1000_NVM_K1_ENABLE    0x1  /* NVM Enable K1 bit */
+
+/* SMBus Control Phy Register */
+#define CV_SMB_CTRL            PHY_REG(769, 23)
+#define CV_SMB_CTRL_FORCE_SMBUS        0x0001
+
+/* SMBus Address Phy Register */
+#define HV_SMB_ADDR            PHY_REG(768, 26)
+#define HV_SMB_ADDR_MASK       0x007F
+#define HV_SMB_ADDR_PEC_EN     0x0200
+#define HV_SMB_ADDR_VALID      0x0080
+#define HV_SMB_ADDR_FREQ_MASK          0x1100
+#define HV_SMB_ADDR_FREQ_LOW_SHIFT     8
+#define HV_SMB_ADDR_FREQ_HIGH_SHIFT    12
+
+/* Strapping Option Register - RO */
+#define E1000_STRAP                    0x0000C
+#define E1000_STRAP_SMBUS_ADDRESS_MASK 0x00FE0000
+#define E1000_STRAP_SMBUS_ADDRESS_SHIFT        17
+#define E1000_STRAP_SMT_FREQ_MASK      0x00003000
+#define E1000_STRAP_SMT_FREQ_SHIFT     12
+
+/* OEM Bits Phy Register */
+#define HV_OEM_BITS            PHY_REG(768, 25)
+#define HV_OEM_BITS_LPLU       0x0004 /* Low Power Link Up */
+#define HV_OEM_BITS_GBE_DIS    0x0040 /* Gigabit Disable */
+#define HV_OEM_BITS_RESTART_AN 0x0400 /* Restart Auto-negotiation */
+
+#define LCD_CFG_PHY_ADDR_BIT   0x0020 /* Phy addr bit from LCD Config word */
+
+/* KMRN Mode Control */
+#define HV_KMRN_MODE_CTRL      PHY_REG(769, 16)
+#define HV_KMRN_MDIO_SLOW      0x0400
+
+/* KMRN FIFO Control and Status */
+#define HV_KMRN_FIFO_CTRLSTA                   PHY_REG(770, 16)
+#define HV_KMRN_FIFO_CTRLSTA_PREAMBLE_MASK     0x7000
+#define HV_KMRN_FIFO_CTRLSTA_PREAMBLE_SHIFT    12
+
+/* PHY Power Management Control */
+#define HV_PM_CTRL             PHY_REG(770, 17)
+#define HV_PM_CTRL_PLL_STOP_IN_K1_GIGA 0x100
+#define I217_MEM_PM_CFG                PHY_REG(772, 27) /* I217 PHY Mem PM Cfg Reg */
+#define I217_MEM_PM_CFG_TXF_SD 0x0020 /* Tx FIFO Memories Shutdown*/
+
+#define SW_FLAG_TIMEOUT                1000 /* SW Semaphore flag timeout in ms */
+
+/* PHY Low Power Idle Control */
+#define I82579_LPI_CTRL                                PHY_REG(772, 20)
+#define I82579_LPI_CTRL_100_ENABLE             0x2000
+#define I82579_LPI_CTRL_1000_ENABLE            0x4000
+#define I82579_LPI_CTRL_ENABLE_MASK            0x6000
+#define I82579_LPI_CTRL_FORCE_PLL_LOCK_COUNT   0x80
+
+/* Extended Management Interface (EMI) Registers */
+#define I82579_EMI_ADDR                0x10
+#define I82579_EMI_DATA                0x11
+#define I82579_LPI_UPDATE_TIMER        0x4805 /* in 40ns units + 40 ns base value */
+#define I82579_MSE_THRESHOLD   0x084F /* 82579 Mean Square Error Threshold */
+#define I82577_MSE_THRESHOLD   0x0887 /* 82577 Mean Square Error Threshold */
+#define I82579_MSE_LINK_DOWN   0x2411 /* MSE count before dropping link */
+#define I82579_EEE_PCS_STATUS          0x182D  /* IEEE MMD Register 3.1 >> 8 */
+#define I82579_EEE_CAPABILITY          0x0410 /* IEEE MMD Register 3.20 */
+#define I82579_EEE_ADVERTISEMENT       0x040E /* IEEE MMD Register 7.60 */
+#define I82579_EEE_LP_ABILITY          0x040F /* IEEE MMD Register 7.61 */
+#define I82579_EEE_100_SUPPORTED       (1 << 1) /* 100BaseTx EEE supported */
+#define I82579_EEE_1000_SUPPORTED      (1 << 2) /* 1000BaseTx EEE supported */
+#define I217_EEE_PCS_STATUS    0x9401   /* IEEE MMD Register 3.1 */
+#define I217_EEE_CAPABILITY    0x8000   /* IEEE MMD Register 3.20 */
+#define I217_EEE_ADVERTISEMENT 0x8001   /* IEEE MMD Register 7.60 */
+#define I217_EEE_LP_ABILITY    0x8002   /* IEEE MMD Register 7.61 */
+
+#define E1000_EEE_RX_LPI_RCVD  0x0400  /* Tx LP idle received */
+#define E1000_EEE_TX_LPI_RCVD  0x0800  /* Rx LP idle received */
+
+/* Intel Rapid Start Technology Support */
+#define I217_PROXY_CTRL                BM_PHY_REG(BM_WUC_PAGE, 70)
+#define I217_PROXY_CTRL_AUTO_DISABLE   0x0080
+#define I217_SxCTRL                    PHY_REG(BM_PORT_CTRL_PAGE, 28)
+#define I217_SxCTRL_ENABLE_LPI_RESET   0x1000
+#define I217_SxCTRL_ENABLE_SERDES      0x0020
+#define I217_CGFREG                    PHY_REG(772, 29)
+#define I217_CGFREG_ENABLE_MTA_RESET   0x0002
+#define I217_MEMPWR                    PHY_REG(772, 26)
+#define I217_MEMPWR_DISABLE_SMB_RELEASE        0x0010
+
+/*
+ * Additional interrupts need to be handled for ICH family:
+ *  DSW = The FW changed the status of the DISSW bit in FWSM
+ *  PHYINT = The LAN connected device generates an interrupt
+ *  EPRST = Manageability reset event
+ */
+#define IMS_ICH_ENABLE_MASK (\
+       E1000_IMS_DSW   | \
+       E1000_IMS_PHYINT | \
+       E1000_IMS_EPRST)
+
+/* Additional interrupt register bit definitions */
+#define E1000_ICR_LSECPNC      0x00004000  /* PN threshold - client */
+#define E1000_IMS_LSECPNC      E1000_ICR_LSECPNC   /* PN threshold - client */
+#define E1000_ICS_LSECPNC      E1000_ICR_LSECPNC   /* PN threshold - client */
+
+/* Security Processing bit Indication */
+#define E1000_RXDEXT_LINKSEC_STATUS_LSECH      0x01000000
+#define E1000_RXDEXT_LINKSEC_ERROR_BIT_MASK    0x60000000
+#define E1000_RXDEXT_LINKSEC_ERROR_NO_SA_MATCH 0x20000000
+#define E1000_RXDEXT_LINKSEC_ERROR_REPLAY_ERROR        0x40000000
+#define E1000_RXDEXT_LINKSEC_ERROR_BAD_SIG     0x60000000
+
+/* Receive Address Initial CRC Calculation */
+#define E1000_PCH_RAICC(_n)    (0x05F50 + ((_n) * 4))
+
+void e1000_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
+                                                bool state);
+void e1000_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw);
+void e1000_gig_downshift_workaround_ich8lan(struct e1000_hw *hw);
+void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw);
+void e1000_resume_workarounds_pchlan(struct e1000_hw *hw);
+s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable);
+void e1000_copy_rx_addrs_to_phy_ich8lan(struct e1000_hw *hw);
+s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable);
+s32 e1000_read_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 *data);
+#endif /* _E1000_ICH8LAN_H_ */