Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / drivers / net / e1000e / ich8lan.c
index 523b971..54d03a0 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
 
   Intel PRO/1000 Linux driver
-  Copyright(c) 1999 - 2008 Intel Corporation.
+  Copyright(c) 1999 - 2009 Intel Corporation.
 
   This program is free software; you can redistribute it and/or modify it
   under the terms and conditions of the GNU General Public License,
@@ -27,6 +27,7 @@
 *******************************************************************************/
 
 /*
+ * 82562G 10/100 Network Connection
  * 82562G-2 10/100 Network Connection
  * 82562GT 10/100 Network Connection
  * 82562GT-2 10/100 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
  */
 
-#include <linux/netdevice.h>
-#include <linux/ethtool.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-
 #include "e1000.h"
 
 #define ICH_FLASH_GFPREG               0x0000
@@ -92,6 +93,8 @@
 
 #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
 
 #define IGP3_VR_CTRL_DEV_POWERDOWN_MODE_MASK 0x0300
 #define IGP3_VR_CTRL_MODE_SHUTDOWN     0x0200
 
+#define HV_LED_CONFIG          PHY_REG(768, 30) /* LED Configuration */
+
+#define SW_FLAG_TIMEOUT    1000 /* SW Semaphore flag timeout in milliseconds */
+
+/* SMBus Address Phy Register */
+#define HV_SMB_ADDR            PHY_REG(768, 26)
+#define HV_SMB_ADDR_PEC_EN     0x0200
+#define HV_SMB_ADDR_VALID      0x0080
+
+/* Strapping Option Register - RO */
+#define E1000_STRAP                     0x0000C
+#define E1000_STRAP_SMBUS_ADDRESS_MASK  0x00FE0000
+#define E1000_STRAP_SMBUS_ADDRESS_SHIFT 17
+
+/* 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 E1000_NVM_K1_CONFIG 0x1B /* NVM K1 Config Word */
+#define E1000_NVM_K1_ENABLE 0x1  /* NVM Enable K1 bit */
+
+/* KMRN Mode Control */
+#define HV_KMRN_MODE_CTRL      PHY_REG(769, 16)
+#define HV_KMRN_MDIO_SLOW      0x0400
+
 /* ICH GbE Flash Hardware Sequencing Flash Status Register bit breakdown */
 /* Offset 04h HSFSTS */
 union ich8_hws_flash_status {
@@ -169,7 +199,6 @@ union ich8_flash_protected_range {
 static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw);
 static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw);
 static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw);
-static s32 e1000_check_polarity_ife_ich8lan(struct e1000_hw *hw);
 static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank);
 static s32 e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw,
                                                u32 offset, u8 byte);
@@ -182,6 +211,19 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
 static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw);
 static s32 e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw);
 static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw);
+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_id_led_init_pchlan(struct e1000_hw *hw);
+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 s32 e1000_set_lplu_state_pchlan(struct e1000_hw *hw, bool active);
+static void e1000_power_down_phy_copper_ich8lan(struct e1000_hw *hw);
+static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw);
+static s32  e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link);
+static s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw);
 
 static inline u16 __er16flash(struct e1000_hw *hw, unsigned long reg)
 {
@@ -209,6 +251,71 @@ static inline void __ew32flash(struct e1000_hw *hw, unsigned long reg, u32 val)
 #define ew32flash(reg,val)     __ew32flash(hw, (reg), (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 = 0;
+
+       phy->addr                     = 1;
+       phy->reset_delay_us           = 100;
+
+       phy->ops.read_reg             = e1000_read_phy_reg_hv;
+       phy->ops.read_reg_locked      = e1000_read_phy_reg_hv_locked;
+       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.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 = e1000e_get_phy_id(hw);
+       if (ret_val)
+               goto out;
+       if ((phy->id == 0) || (phy->id == PHY_REVISION_MASK)) {
+               /*
+                * In case the PHY needs to be in mdio slow mode (eg. 82577),
+                * set slow mode and try to get the PHY id again.
+                */
+               ret_val = e1000_set_mdio_slow_mode_hv(hw);
+               if (ret_val)
+                       goto out;
+               ret_val = e1000e_get_phy_id(hw);
+               if (ret_val)
+                       goto out;
+       }
+       phy->type = e1000e_get_phy_type_from_id(phy->id);
+
+       switch (phy->type) {
+       case e1000_phy_82577:
+               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 = e1000e_phy_sw_reset;
+       case e1000_phy_82578:
+               phy->ops.check_polarity = e1000_check_polarity_m88;
+               phy->ops.force_speed_duplex = e1000e_phy_force_speed_duplex_m88;
+               phy->ops.get_cable_length = e1000e_get_cable_length_m88;
+               phy->ops.get_info = e1000e_get_phy_info_m88;
+               break;
+       default:
+               ret_val = -E1000_ERR_PHY;
+               break;
+       }
+
+out:
+       return ret_val;
+}
+
+/**
  *  e1000_init_phy_params_ich8lan - Initialize PHY function pointers
  *  @hw: pointer to the HW structure
  *
@@ -223,17 +330,22 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
        phy->addr                       = 1;
        phy->reset_delay_us             = 100;
 
+       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 = e1000e_determine_phy_address(hw);
        if (ret_val) {
-               hw->phy.ops.write_phy_reg = e1000e_write_phy_reg_bm;
-               hw->phy.ops.read_phy_reg  = e1000e_read_phy_reg_bm;
+               phy->ops.write_reg = e1000e_write_phy_reg_bm;
+               phy->ops.read_reg  = e1000e_read_phy_reg_bm;
                ret_val = e1000e_determine_phy_address(hw);
-               if (ret_val)
+               if (ret_val) {
+                       e_dbg("Cannot determine PHY addr. Erroring out\n");
                        return ret_val;
+               }
        }
 
        phy->id = 0;
@@ -250,19 +362,30 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
        case IGP03E1000_E_PHY_ID:
                phy->type = e1000_phy_igp_3;
                phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+               phy->ops.read_reg_locked = e1000e_read_phy_reg_igp_locked;
+               phy->ops.write_reg_locked = e1000e_write_phy_reg_igp_locked;
+               phy->ops.get_info = e1000e_get_phy_info_igp;
+               phy->ops.check_polarity = e1000_check_polarity_igp;
+               phy->ops.force_speed_duplex = e1000e_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;
-               hw->phy.ops.read_phy_reg = e1000e_read_phy_reg_bm;
-               hw->phy.ops.write_phy_reg = e1000e_write_phy_reg_bm;
-               hw->phy.ops.commit_phy = e1000e_phy_sw_reset;
+               phy->ops.read_reg = e1000e_read_phy_reg_bm;
+               phy->ops.write_reg = e1000e_write_phy_reg_bm;
+               phy->ops.commit = e1000e_phy_sw_reset;
+               phy->ops.get_info = e1000e_get_phy_info_m88;
+               phy->ops.check_polarity = e1000_check_polarity_m88;
+               phy->ops.force_speed_duplex = e1000e_phy_force_speed_duplex_m88;
                break;
        default:
                return -E1000_ERR_PHY;
@@ -283,14 +406,12 @@ 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;
-       u32 sector_base_addr;
-       u32 sector_end_addr;
+       u32 gfpreg, sector_base_addr, sector_end_addr;
        u16 i;
 
        /* Can't read flash registers if the register set isn't mapped. */
        if (!hw->flash_address) {
-               hw_dbg(hw, "ERROR: Flash registers not mapped\n");
+               e_dbg("ERROR: Flash registers not mapped\n");
                return -E1000_ERR_CONFIG;
        }
 
@@ -323,7 +444,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw)
 
        /* Clear shadow ram */
        for (i = 0; i < nvm->word_size; i++) {
-               dev_spec->shadow_ram[i].modified = 0;
+               dev_spec->shadow_ram[i].modified = false;
                dev_spec->shadow_ram[i].value    = 0xFFFF;
        }
 
@@ -352,15 +473,134 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_adapter *adapter)
        if (mac->type == e1000_ich8lan)
                mac->rar_entry_count--;
        /* Set if manageability features are enabled. */
-       mac->arc_subsystem_valid = 1;
+       mac->arc_subsystem_valid = true;
+       /* Adaptive IFS supported */
+       mac->adaptive_ifs = true;
+
+       /* LED operations */
+       switch (mac->type) {
+       case e1000_ich8lan:
+       case e1000_ich9lan:
+       case e1000_ich10lan:
+               /* ID LED init */
+               mac->ops.id_led_init = e1000e_id_led_init;
+               /* setup LED */
+               mac->ops.setup_led = e1000e_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_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)
-               e1000e_set_kmrn_lock_loss_workaround_ich8lan(hw, 1);
+               e1000e_set_kmrn_lock_loss_workaround_ich8lan(hw, true);
 
        return 0;
 }
 
+/**
+ *  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;
+
+       /*
+        * 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 = 0;
+               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 = e1000e_phy_has_link_generic(hw, 1, 0, &link);
+       if (ret_val)
+               goto out;
+
+       if (hw->mac.type == e1000_pchlan) {
+               ret_val = e1000_k1_gig_workaround_hv(hw, link);
+               if (ret_val)
+                       goto out;
+       }
+
+       if (!link)
+               goto out; /* No link detected */
+
+       mac->get_link_status = false;
+
+       if (hw->phy.type == e1000_phy_82578) {
+               ret_val = e1000_link_stall_workaround_hv(hw);
+               if (ret_val)
+                       goto out;
+       }
+
+       /*
+        * Check if there was DownShift, must be checked
+        * immediately after link-up
+        */
+       e1000e_check_downshift(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;
+       }
+
+       /*
+        * 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.
+        */
+       e1000e_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 = e1000e_config_fc_after_link_up(hw);
+       if (ret_val)
+               e_dbg("Error configuring flow control\n");
+
+out:
+       return ret_val;
+}
+
 static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter)
 {
        struct e1000_hw *hw = &adapter->hw;
@@ -374,10 +614,18 @@ static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter)
        if (rc)
                return rc;
 
-       rc = e1000_init_phy_params_ich8lan(hw);
+       if (hw->mac.type == e1000_pchlan)
+               rc = e1000_init_phy_params_pchlan(hw);
+       else
+               rc = e1000_init_phy_params_ich8lan(hw);
        if (rc)
                return rc;
 
+       if (adapter->hw.phy.type == e1000_phy_ife) {
+               adapter->flags &= ~FLAG_HAS_JUMBO_FRAMES;
+               adapter->max_hw_frame_size = ETH_FRAME_LEN + ETH_FCS_LEN;
+       }
+
        if ((adapter->hw.mac.type == e1000_ich8lan) &&
            (adapter->hw.phy.type == e1000_phy_igp_3))
                adapter->flags |= FLAG_LSC_GIG_SPEED_DROP;
@@ -386,61 +634,99 @@ static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter)
 }
 
 static DEFINE_MUTEX(nvm_mutex);
-static pid_t nvm_owner = -1;
+
+/**
+ *  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)
+{
+       mutex_lock(&nvm_mutex);
+
+       return 0;
+}
+
+/**
+ *  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)
+{
+       mutex_unlock(&nvm_mutex);
+
+       return;
+}
+
+static DEFINE_MUTEX(swflag_mutex);
 
 /**
  *  e1000_acquire_swflag_ich8lan - Acquire software control flag
  *  @hw: pointer to the HW structure
  *
- *  Acquires the software control flag for performing NVM and PHY
- *  operations.  This is a function pointer entry point only called by
- *  read/write routines for the PHY and NVM parts.
+ *  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;
-       u32 timeout = PHY_CFG_TIMEOUT;
-
-       might_sleep();
+       u32 extcnf_ctrl, timeout = PHY_CFG_TIMEOUT;
+       s32 ret_val = 0;
 
-       if (!mutex_trylock(&nvm_mutex)) {
-               WARN(1, KERN_ERR "e1000e mutex contention. Owned by pid %d\n",
-                    nvm_owner);
-               mutex_lock(&nvm_mutex);
-       }
-       nvm_owner = current->pid;
+       mutex_lock(&swflag_mutex);
 
        while (timeout) {
                extcnf_ctrl = er32(EXTCNF_CTRL);
-               extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG;
-               ew32(EXTCNF_CTRL, extcnf_ctrl);
+               if (!(extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG))
+                       break;
+
+               mdelay(1);
+               timeout--;
+       }
+
+       if (!timeout) {
+               e_dbg("SW/FW/HW has locked the resource for too long.\n");
+               ret_val = -E1000_ERR_CONFIG;
+               goto out;
+       }
+
+       timeout = SW_FLAG_TIMEOUT;
+
+       extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG;
+       ew32(EXTCNF_CTRL, extcnf_ctrl);
 
+       while (timeout) {
                extcnf_ctrl = er32(EXTCNF_CTRL);
                if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)
                        break;
+
                mdelay(1);
                timeout--;
        }
 
        if (!timeout) {
-               hw_dbg(hw, "FW or HW has locked the resource for too long.\n");
+               e_dbg("Failed to acquire the semaphore.\n");
                extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
                ew32(EXTCNF_CTRL, extcnf_ctrl);
-               nvm_owner = -1;
-               mutex_unlock(&nvm_mutex);
-               return -E1000_ERR_CONFIG;
+               ret_val = -E1000_ERR_CONFIG;
+               goto out;
        }
 
-       return 0;
+out:
+       if (ret_val)
+               mutex_unlock(&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 NVM and PHY operations.
- *  This is a function pointer entry point only called by read/write
- *  routines for the PHY and NVM parts.
+ *  Releases the software control flag for performing PHY and select
+ *  MAC CSR accesses.
  **/
 static void e1000_release_swflag_ich8lan(struct e1000_hw *hw)
 {
@@ -450,8 +736,9 @@ static void e1000_release_swflag_ich8lan(struct e1000_hw *hw)
        extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
        ew32(EXTCNF_CTRL, extcnf_ctrl);
 
-       nvm_owner = -1;
-       mutex_unlock(&nvm_mutex);
+       mutex_unlock(&swflag_mutex);
+
+       return;
 }
 
 /**
@@ -464,7 +751,9 @@ static void e1000_release_swflag_ich8lan(struct e1000_hw *hw)
  **/
 static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw)
 {
-       u32 fwsm = er32(FWSM);
+       u32 fwsm;
+
+       fwsm = er32(FWSM);
 
        return (fwsm & E1000_FWSM_MODE_MASK) ==
                (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT);
@@ -488,94 +777,20 @@ static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw)
 }
 
 /**
- *  e1000_phy_force_speed_duplex_ich8lan - Force PHY speed & duplex
- *  @hw: pointer to the HW structure
+ *  e1000_sw_lcd_config_ich8lan - SW-based LCD Configuration
+ *  @hw:   pointer to the HW structure
  *
- *  Forces the speed and duplex settings of the PHY.
- *  This is a function pointer entry point only called by
- *  PHY setup routines.
+ *  SW should configure the LCD from the NVM extended configuration region
+ *  as a workaround for certain parts.
  **/
-static s32 e1000_phy_force_speed_duplex_ich8lan(struct e1000_hw *hw)
+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;
-       u16 data;
-       bool link;
-
-       if (phy->type != e1000_phy_ife) {
-               ret_val = e1000e_phy_force_speed_duplex_igp(hw);
-               return ret_val;
-       }
-
-       ret_val = e1e_rphy(hw, PHY_CONTROL, &data);
-       if (ret_val)
-               return ret_val;
-
-       e1000e_phy_force_speed_duplex_setup(hw, &data);
-
-       ret_val = e1e_wphy(hw, PHY_CONTROL, data);
-       if (ret_val)
-               return ret_val;
-
-       /* Disable MDI-X support for 10/100 */
-       ret_val = e1e_rphy(hw, IFE_PHY_MDIX_CONTROL, &data);
-       if (ret_val)
-               return ret_val;
-
-       data &= ~IFE_PMC_AUTO_MDIX;
-       data &= ~IFE_PMC_FORCE_MDIX;
-
-       ret_val = e1e_wphy(hw, IFE_PHY_MDIX_CONTROL, data);
-       if (ret_val)
-               return ret_val;
-
-       hw_dbg(hw, "IFE PMC: %X\n", data);
-
-       udelay(1);
-
-       if (phy->autoneg_wait_to_complete) {
-               hw_dbg(hw, "Waiting for forced speed/duplex link on IFE phy.\n");
-
-               ret_val = e1000e_phy_has_link_generic(hw,
-                                                    PHY_FORCE_LIMIT,
-                                                    100000,
-                                                    &link);
-               if (ret_val)
-                       return ret_val;
-
-               if (!link)
-                       hw_dbg(hw, "Link taking longer than expected.\n");
-
-               /* Try once more */
-               ret_val = e1000e_phy_has_link_generic(hw,
-                                                    PHY_FORCE_LIMIT,
-                                                    100000,
-                                                    &link);
-               if (ret_val)
-                       return ret_val;
-       }
-
-       return 0;
-}
-
-/**
- *  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)
-{
-       struct e1000_phy_info *phy = &hw->phy;
-       u32 i;
-       u32 data, cnf_size, cnf_base_addr, sw_cfg_mask;
-       s32 ret_val;
-       u16 loop = E1000_ICH8_LAN_INIT_TIMEOUT;
        u16 word_addr, reg_data, reg_addr, phy_page = 0;
 
-       ret_val = e1000e_phy_hw_reset_generic(hw);
+       ret_val = hw->phy.ops.acquire(hw);
        if (ret_val)
                return ret_val;
 
@@ -586,40 +801,24 @@ static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
         * Therefore, after each PHY reset, we will load the
         * configuration data out of the NVM manually.
         */
-       if (hw->mac.type == e1000_ich8lan && phy->type == e1000_phy_igp_3) {
+       if ((hw->mac.type == e1000_ich8lan && phy->type == e1000_phy_igp_3) ||
+               (hw->mac.type == e1000_pchlan)) {
                struct e1000_adapter *adapter = hw->adapter;
 
-               /* Check if SW needs configure the PHY */
+               /* Check if SW needs to configure the PHY */
                if ((adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_M_AMT) ||
-                   (adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_M))
+                   (adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_M) ||
+                   (hw->mac.type == e1000_pchlan))
                        sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M;
                else
                        sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG;
 
                data = er32(FEXTNVM);
                if (!(data & sw_cfg_mask))
-                       return 0;
+                       goto out;
 
-               /* Wait for basic configuration completes before proceeding*/
-               do {
-                       data = er32(STATUS);
-                       data &= E1000_STATUS_LAN_INIT_DONE;
-                       udelay(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) {
-                       hw_dbg(hw, "LAN_INIT_DONE not set, increase timeout\n");
-               }
-
-               /* Clear the Init Done bit for the next init event */
-               data = er32(STATUS);
-               data &= ~E1000_STATUS_LAN_INIT_DONE;
-               ew32(STATUS, data);
+               /* Wait for basic configuration completes before proceeding */
+               e1000_lan_init_done_ich8lan(hw);
 
                /*
                 * Make sure HW does not configure LCD from PHY
@@ -627,36 +826,56 @@ static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
                 */
                data = er32(EXTCNF_CTRL);
                if (data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE)
-                       return 0;
+                       goto out;
 
                cnf_size = er32(EXTCNF_SIZE);
                cnf_size &= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK;
                cnf_size >>= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT;
                if (!cnf_size)
-                       return 0;
+                       goto out;
 
                cnf_base_addr = data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK;
                cnf_base_addr >>= E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT;
 
+               if (!(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.
+                        */
+                       data = er32(STRAP);
+                       data &= E1000_STRAP_SMBUS_ADDRESS_MASK;
+                       reg_data = data >> E1000_STRAP_SMBUS_ADDRESS_SHIFT;
+                       reg_data |= HV_SMB_ADDR_PEC_EN | HV_SMB_ADDR_VALID;
+                       ret_val = e1000_write_phy_reg_hv_locked(hw, HV_SMB_ADDR,
+                                                               reg_data);
+                       if (ret_val)
+                               goto out;
+
+                       data = er32(LEDCTL);
+                       ret_val = e1000_write_phy_reg_hv_locked(hw,
+                                                               HV_LED_CONFIG,
+                                                               (u16)data);
+                       if (ret_val)
+                               goto out;
+               }
                /* 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 = e1000_read_nvm(hw,
-                                               (word_addr + i * 2),
-                                               1,
-                                               &reg_data);
+                       ret_val = e1000_read_nvm(hw, (word_addr + i * 2), 1,
+                                                  &reg_data);
                        if (ret_val)
-                               return ret_val;
+                               goto out;
 
-                       ret_val = e1000_read_nvm(hw,
-                                               (word_addr + i * 2 + 1),
-                                               1,
-                                               &reg_addr);
+                       ret_val = e1000_read_nvm(hw, (word_addr + i * 2 + 1),
+                                                  1, &reg_addr);
                        if (ret_val)
-                               return ret_val;
+                               goto out;
 
                        /* Save off the PHY page for future writes. */
                        if (reg_addr == IGP01E1000_PHY_PAGE_SELECT) {
@@ -664,135 +883,431 @@ static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
                                continue;
                        }
 
+                       reg_addr &= PHY_REG_MASK;
                        reg_addr |= phy_page;
 
-                       ret_val = e1e_wphy(hw, (u32)reg_addr, reg_data);
+                       ret_val = phy->ops.write_reg_locked(hw,
+                                                           (u32)reg_addr,
+                                                           reg_data);
                        if (ret_val)
-                               return ret_val;
+                               goto out;
                }
        }
 
-       return 0;
+out:
+       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 = 0;
+       u16 status_reg = 0;
+       bool k1_enable = hw->dev_spec.ich8lan.nvm_k1_enabled;
+
+       if (hw->mac.type != e1000_pchlan)
+               goto out;
+
+       /* Wrap the whole flow with the sw flag */
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val)
+               goto out;
+
+       /* 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);
+out:
+       return ret_val;
 }
 
 /**
- *  e1000_get_phy_info_ife_ich8lan - Retrieves various IFE PHY states
+ *  e1000_configure_k1_ich8lan - Configure K1 power state
  *  @hw: pointer to the HW structure
+ *  @enable: K1 state to configure
  *
- *  Populates "phy" structure with various feature states.
- *  This function is only called by other family-specific
- *  routines.
+ *  Configure the K1 power state based on the provided parameter.
+ *  Assumes semaphore already acquired.
+ *
+ *  Success returns 0, Failure returns -E1000_ERR_PHY (-2)
  **/
-static s32 e1000_get_phy_info_ife_ich8lan(struct e1000_hw *hw)
+s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable)
 {
-       struct e1000_phy_info *phy = &hw->phy;
-       s32 ret_val;
-       u16 data;
-       bool link;
+       s32 ret_val = 0;
+       u32 ctrl_reg = 0;
+       u32 ctrl_ext = 0;
+       u32 reg = 0;
+       u16 kmrn_reg = 0;
+
+       ret_val = e1000e_read_kmrn_reg_locked(hw,
+                                            E1000_KMRNCTRLSTA_K1_CONFIG,
+                                            &kmrn_reg);
+       if (ret_val)
+               goto out;
 
-       ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link);
+       if (k1_enable)
+               kmrn_reg |= E1000_KMRNCTRLSTA_K1_ENABLE;
+       else
+               kmrn_reg &= ~E1000_KMRNCTRLSTA_K1_ENABLE;
+
+       ret_val = e1000e_write_kmrn_reg_locked(hw,
+                                             E1000_KMRNCTRLSTA_K1_CONFIG,
+                                             kmrn_reg);
+       if (ret_val)
+               goto out;
+
+       udelay(20);
+       ctrl_ext = er32(CTRL_EXT);
+       ctrl_reg = er32(CTRL);
+
+       reg = ctrl_reg & ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100);
+       reg |= E1000_CTRL_FRCSPD;
+       ew32(CTRL, reg);
+
+       ew32(CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_SPD_BYPS);
+       udelay(20);
+       ew32(CTRL, ctrl_reg);
+       ew32(CTRL_EXT, ctrl_ext);
+       udelay(20);
+
+out:
+       return ret_val;
+}
+
+/**
+ *  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;
+
+       if (hw->mac.type != e1000_pchlan)
+               return ret_val;
+
+       ret_val = hw->phy.ops.acquire(hw);
        if (ret_val)
                return ret_val;
 
-       if (!link) {
-               hw_dbg(hw, "Phy info is only valid if link is up\n");
-               return -E1000_ERR_CONFIG;
+       mac_reg = er32(EXTCNF_CTRL);
+       if (mac_reg & E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE)
+               goto out;
+
+       mac_reg = er32(FEXTNVM);
+       if (!(mac_reg & E1000_FEXTNVM_SW_CONFIG_ICH8M))
+               goto out;
+
+       mac_reg = er32(PHY_CTRL);
+
+       ret_val = hw->phy.ops.read_reg_locked(hw, HV_OEM_BITS, &oem_reg);
+       if (ret_val)
+               goto out;
+
+       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_NOND0A_GBE_DISABLE)
+                       oem_reg |= HV_OEM_BITS_GBE_DIS;
+
+               if (mac_reg & E1000_PHY_CTRL_NOND0A_LPLU)
+                       oem_reg |= HV_OEM_BITS_LPLU;
        }
+       /* Restart auto-neg to activate the bits */
+       if (!e1000_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);
+
+out:
+       hw->phy.ops.release(hw);
+
+       return ret_val;
+}
 
-       ret_val = e1e_rphy(hw, IFE_PHY_SPECIAL_CONTROL, &data);
+
+/**
+ *  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;
+
+       ret_val = e1e_rphy(hw, HV_KMRN_MODE_CTRL, &data);
        if (ret_val)
                return ret_val;
-       phy->polarity_correction = (!(data & IFE_PSC_AUTO_POLARITY_DISABLE));
 
-       if (phy->polarity_correction) {
-               ret_val = e1000_check_polarity_ife_ich8lan(hw);
+       data |= HV_KMRN_MDIO_SLOW;
+
+       ret_val = e1e_wphy(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 = 0;
+       u16 phy_data;
+
+       if (hw->mac.type != e1000_pchlan)
+               return ret_val;
+
+       /* 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)
+                       goto out;
+       }
+
+       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 = e1e_wphy(hw, PHY_REG(769, 25), 0x4431);
+               if (ret_val)
+                       return ret_val;
+
+               /* Preamble tuning for SSC */
+               ret_val = e1e_wphy(hw, PHY_REG(770, 16), 0xA204);
                if (ret_val)
                        return ret_val;
-       } else {
-               /* Polarity is forced */
-               phy->cable_polarity = (data & IFE_PSC_FORCE_POLARITY)
-                                     ? e1000_rev_polarity_reversed
-                                     : e1000_rev_polarity_normal;
        }
 
-       ret_val = e1e_rphy(hw, IFE_PHY_MDIX_CONTROL, &data);
+       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) {
+                       e1000e_phy_sw_reset(hw);
+                       ret_val = e1e_wphy(hw, PHY_CONTROL, 0x3140);
+               }
+       }
+
+       /* Select page 0 */
+       ret_val = hw->phy.ops.acquire(hw);
        if (ret_val)
                return ret_val;
 
-       phy->is_mdix = (data & IFE_PMC_MDIX_STATUS);
+       hw->phy.addr = 1;
+       ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, 0);
+       hw->phy.ops.release(hw);
+       if (ret_val)
+               goto out;
 
-       /* The following parameters are undefined for 10/100 operation. */
-       phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
-       phy->local_rx = e1000_1000t_rx_status_undefined;
-       phy->remote_rx = e1000_1000t_rx_status_undefined;
+       /*
+        * 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)
+               goto out;
 
-       return 0;
+       /* Workaround for link disconnects on a busy hub in half duplex */
+       ret_val = hw->phy.ops.acquire(hw);
+       if (ret_val)
+               goto out;
+       ret_val = hw->phy.ops.read_reg_locked(hw,
+                                             PHY_REG(BM_PORT_CTRL_PAGE, 17),
+                                             &phy_data);
+       if (ret_val)
+               goto release;
+       ret_val = hw->phy.ops.write_reg_locked(hw,
+                                              PHY_REG(BM_PORT_CTRL_PAGE, 17),
+                                              phy_data & 0x00FF);
+release:
+       hw->phy.ops.release(hw);
+out:
+       return ret_val;
+}
+
+/**
+ *  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;
+
+       /* Wait for basic configuration completes before proceeding */
+       do {
+               data = er32(STATUS);
+               data &= E1000_STATUS_LAN_INIT_DONE;
+               udelay(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)
+               e_dbg("LAN_INIT_DONE not set, increase timeout\n");
+
+       /* Clear the Init Done bit for the next init event */
+       data = er32(STATUS);
+       data &= ~E1000_STATUS_LAN_INIT_DONE;
+       ew32(STATUS, data);
 }
 
 /**
- *  e1000_get_phy_info_ich8lan - Calls appropriate PHY type get_phy_info
+ *  e1000_phy_hw_reset_ich8lan - Performs a PHY reset
  *  @hw: pointer to the HW structure
  *
- *  Wrapper for calling the get_phy_info routines for the appropriate phy type.
+ *  Resets the PHY
  *  This is a function pointer entry point called by drivers
  *  or other shared routines.
  **/
-static s32 e1000_get_phy_info_ich8lan(struct e1000_hw *hw)
+static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
 {
-       switch (hw->phy.type) {
-       case e1000_phy_ife:
-               return e1000_get_phy_info_ife_ich8lan(hw);
-               break;
-       case e1000_phy_igp_3:
-       case e1000_phy_bm:
-               return e1000e_get_phy_info_igp(hw);
-               break;
-       default:
-               break;
+       s32 ret_val = 0;
+       u16 reg;
+
+       ret_val = e1000e_phy_hw_reset_generic(hw);
+       if (ret_val)
+               return ret_val;
+
+       /* Allow time for h/w to get to a quiescent state after reset */
+       mdelay(10);
+
+       /* Perform any necessary post-reset workarounds */
+       if (hw->mac.type == e1000_pchlan) {
+               ret_val = e1000_hv_phy_workarounds_ich8lan(hw);
+               if (ret_val)
+                       return ret_val;
        }
 
-       return -E1000_ERR_PHY_TYPE;
+       /* Dummy read to clear the phy wakeup bit after lcd reset */
+       if (hw->mac.type == e1000_pchlan)
+               e1e_rphy(hw, BM_WUC, &reg);
+
+       /* Configure the LCD with the extended configuration region in NVM */
+       ret_val = e1000_sw_lcd_config_ich8lan(hw);
+       if (ret_val)
+               goto out;
+
+       /* Configure the LCD with the OEM bits in NVM */
+       if (hw->mac.type == e1000_pchlan)
+               ret_val = e1000_oem_bits_config_ich8lan(hw, true);
+
+out:
+       return 0;
 }
 
 /**
- *  e1000_check_polarity_ife_ich8lan - Check cable polarity for IFE PHY
+ *  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
  *
- *  Polarity is determined on the polarity reversal feature being enabled.
- *  This function is only called by other family-specific
- *  routines.
+ *  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_check_polarity_ife_ich8lan(struct e1000_hw *hw)
+static s32 e1000_set_lplu_state_pchlan(struct e1000_hw *hw, bool active)
 {
-       struct e1000_phy_info *phy = &hw->phy;
-       s32 ret_val;
-       u16 phy_data, offset, mask;
+       s32 ret_val = 0;
+       u16 oem_reg;
 
-       /*
-        * Polarity is determined based on the reversal feature being enabled.
-        */
-       if (phy->polarity_correction) {
-               offset  = IFE_PHY_EXTENDED_STATUS_CONTROL;
-               mask    = IFE_PESC_POLARITY_REVERSED;
-       } else {
-               offset  = IFE_PHY_SPECIAL_CONTROL;
-               mask    = IFE_PSC_FORCE_POLARITY;
-       }
+       ret_val = e1e_rphy(hw, HV_OEM_BITS, &oem_reg);
+       if (ret_val)
+               goto out;
 
-       ret_val = e1e_rphy(hw, offset, &phy_data);
+       if (active)
+               oem_reg |= HV_OEM_BITS_LPLU;
+       else
+               oem_reg &= ~HV_OEM_BITS_LPLU;
 
-       if (!ret_val)
-               phy->cable_polarity = (phy_data & mask)
-                                     ? e1000_rev_polarity_reversed
-                                     : e1000_rev_polarity_normal;
+       oem_reg |= HV_OEM_BITS_RESTART_AN;
+       ret_val = e1e_wphy(hw, HV_OEM_BITS, oem_reg);
 
+out:
        return ret_val;
 }
 
 /**
  *  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
+ *  @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
@@ -818,12 +1333,14 @@ static s32 e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw, bool active)
                phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU;
                ew32(PHY_CTRL, phy_ctrl);
 
+               if (phy->type != e1000_phy_igp_3)
+                       return 0;
+
                /*
                 * Call gig speed drop workaround on LPLU before accessing
                 * any PHY registers
                 */
-               if ((hw->mac.type == e1000_ich8lan) &&
-                   (hw->phy.type == e1000_phy_igp_3))
+               if (hw->mac.type == e1000_ich8lan)
                        e1000e_gig_downshift_workaround_ich8lan(hw);
 
                /* When LPLU is enabled, we should disable SmartSpeed */
@@ -836,6 +1353,9 @@ static s32 e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw, bool active)
                phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU;
                ew32(PHY_CTRL, phy_ctrl);
 
+               if (phy->type != e1000_phy_igp_3)
+                       return 0;
+
                /*
                 * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
                 * during Dx states where the power conservation is most
@@ -873,7 +1393,7 @@ static s32 e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw, bool active)
 /**
  *  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
+ *  @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
@@ -895,6 +1415,10 @@ static s32 e1000_set_d3_lplu_state_ich8lan(struct e1000_hw *hw, bool active)
        if (!active) {
                phy_ctrl &= ~E1000_PHY_CTRL_NOND0A_LPLU;
                ew32(PHY_CTRL, phy_ctrl);
+
+               if (phy->type != e1000_phy_igp_3)
+                       return 0;
+
                /*
                 * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
                 * during Dx states where the power conservation is most
@@ -930,12 +1454,14 @@ static s32 e1000_set_d3_lplu_state_ich8lan(struct e1000_hw *hw, bool active)
                phy_ctrl |= E1000_PHY_CTRL_NOND0A_LPLU;
                ew32(PHY_CTRL, phy_ctrl);
 
+               if (phy->type != e1000_phy_igp_3)
+                       return 0;
+
                /*
                 * Call gig speed drop workaround on LPLU before accessing
                 * any PHY registers
                 */
-               if ((hw->mac.type == e1000_ich8lan) &&
-                   (hw->phy.type == e1000_phy_igp_3))
+               if (hw->mac.type == e1000_ich8lan)
                        e1000e_gig_downshift_workaround_ich8lan(hw);
 
                /* When LPLU is enabled, we should disable SmartSpeed */
@@ -956,45 +1482,62 @@ static s32 e1000_set_d3_lplu_state_ich8lan(struct e1000_hw *hw, bool active)
  *  @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;
-       /* flash bank size is in words */
        u32 bank1_offset = nvm->flash_bank_size * sizeof(u16);
        u32 act_offset = E1000_ICH_NVM_SIG_WORD * 2 + 1;
-       u8 bank_high_byte = 0;
+       u8 sig_byte = 0;
+       s32 ret_val = 0;
 
-       if (hw->mac.type != e1000_ich10lan) {
-               if (er32(EECD) & E1000_EECD_SEC1VAL)
-                       *bank = 1;
-               else
-                       *bank = 0;
-       } else {
-               /*
-                * Make sure the signature for bank 0 is valid,
-                * if not check for bank1
-                */
-               e1000_read_flash_byte_ich8lan(hw, act_offset, &bank_high_byte);
-               if ((bank_high_byte & 0xC0) == 0x80) {
+       switch (hw->mac.type) {
+       case e1000_ich8lan:
+       case e1000_ich9lan:
+               eecd = er32(EECD);
+               if ((eecd & E1000_EECD_SEC1VAL_VALID_MASK) ==
+                   E1000_EECD_SEC1VAL_VALID_MASK) {
+                       if (eecd & E1000_EECD_SEC1VAL)
+                               *bank = 1;
+                       else
+                               *bank = 0;
+
+                       return 0;
+               }
+               e_dbg("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;
-               } else {
-                       /*
-                        * find if segment 1 is valid by verifying
-                        * bit 15:14 = 10b in word 0x13
-                        */
-                       e1000_read_flash_byte_ich8lan(hw,
-                                                     act_offset + bank1_offset,
-                                                     &bank_high_byte);
+                       return 0;
+               }
 
-                       /* bank1 has a valid signature equivalent to SEC1V */
-                       if ((bank_high_byte & 0xC0) == 0x80) {
-                               *bank = 1;
-                       } else {
-                               hw_dbg(hw, "ERROR: EEPROM not present\n");
-                               return -E1000_ERR_NVM;
-                       }
+               /* 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 0;
                }
+
+               e_dbg("ERROR: No valid NVM bank present\n");
+               return -E1000_ERR_NVM;
        }
 
        return 0;
@@ -1015,27 +1558,29 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
        struct e1000_nvm_info *nvm = &hw->nvm;
        struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
        u32 act_offset;
-       s32 ret_val;
+       s32 ret_val = 0;
        u32 bank = 0;
        u16 i, word;
 
        if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) ||
            (words == 0)) {
-               hw_dbg(hw, "nvm parameter(s) out of bounds\n");
-               return -E1000_ERR_NVM;
+               e_dbg("nvm parameter(s) out of bounds\n");
+               ret_val = -E1000_ERR_NVM;
+               goto out;
        }
 
-       ret_val = e1000_acquire_swflag_ich8lan(hw);
-       if (ret_val)
-               return ret_val;
+       nvm->ops.acquire(hw);
 
        ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
-       if (ret_val)
-               return ret_val;
+       if (ret_val) {
+               e_dbg("Could not detect valid bank, assuming bank 0\n");
+               bank = 0;
+       }
 
        act_offset = (bank) ? nvm->flash_bank_size : 0;
        act_offset += offset;
 
+       ret_val = 0;
        for (i = 0; i < words; i++) {
                if ((dev_spec->shadow_ram) &&
                    (dev_spec->shadow_ram[offset+i].modified)) {
@@ -1050,7 +1595,11 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
                }
        }
 
-       e1000_release_swflag_ich8lan(hw);
+       nvm->ops.release(hw);
+
+out:
+       if (ret_val)
+               e_dbg("NVM read error: %d\n", ret_val);
 
        return ret_val;
 }
@@ -1072,7 +1621,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw)
 
        /* Check if the flash descriptor is valid */
        if (hsfsts.hsf_status.fldesvalid == 0) {
-               hw_dbg(hw, "Flash descriptor invalid.  "
+               e_dbg("Flash descriptor invalid.  "
                         "SW Sequencing must be used.");
                return -E1000_ERR_NVM;
        }
@@ -1095,7 +1644,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw)
        if (hsfsts.hsf_status.flcinprog == 0) {
                /*
                 * There is no cycle running at present,
-                * so we can start a cycle
+                * so we can start a cycle.
                 * Begin by setting Flash Cycle Done.
                 */
                hsfsts.hsf_status.flcdone = 1;
@@ -1103,7 +1652,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw)
                ret_val = 0;
        } else {
                /*
-                * otherwise poll for sometime so the current
+                * 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++) {
@@ -1122,7 +1671,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw)
                        hsfsts.hsf_status.flcdone = 1;
                        ew16flash(ICH_FLASH_HSFSTS, hsfsts.regval);
                } else {
-                       hw_dbg(hw, "Flash controller busy, cannot get access");
+                       e_dbg("Flash controller busy, cannot get access");
                }
        }
 
@@ -1272,7 +1821,7 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
                                /* Repeat for some time before giving up. */
                                continue;
                        } else if (hsfsts.hsf_status.flcdone == 0) {
-                               hw_dbg(hw, "Timeout error - flash cycle "
+                               e_dbg("Timeout error - flash cycle "
                                         "did not complete.");
                                break;
                        }
@@ -1296,25 +1845,22 @@ static s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
 {
        struct e1000_nvm_info *nvm = &hw->nvm;
        struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
-       s32 ret_val;
        u16 i;
 
        if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) ||
            (words == 0)) {
-               hw_dbg(hw, "nvm parameter(s) out of bounds\n");
+               e_dbg("nvm parameter(s) out of bounds\n");
                return -E1000_ERR_NVM;
        }
 
-       ret_val = e1000_acquire_swflag_ich8lan(hw);
-       if (ret_val)
-               return ret_val;
+       nvm->ops.acquire(hw);
 
        for (i = 0; i < words; i++) {
-               dev_spec->shadow_ram[offset+i].modified = 1;
+               dev_spec->shadow_ram[offset+i].modified = true;
                dev_spec->shadow_ram[offset+i].value = data[i];
        }
 
-       e1000_release_swflag_ich8lan(hw);
+       nvm->ops.release(hw);
 
        return 0;
 }
@@ -1340,14 +1886,12 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
 
        ret_val = e1000e_update_nvm_checksum_generic(hw);
        if (ret_val)
-               return ret_val;
+               goto out;
 
        if (nvm->type != e1000_nvm_flash_sw)
-               return ret_val;
+               goto out;
 
-       ret_val = e1000_acquire_swflag_ich8lan(hw);
-       if (ret_val)
-               return ret_val;
+       nvm->ops.acquire(hw);
 
        /*
         * We're writing to the opposite bank so if we're on bank 1,
@@ -1355,17 +1899,27 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
         * is going to be written
         */
        ret_val =  e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
-       if (ret_val)
-               return ret_val;
+       if (ret_val) {
+               e_dbg("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;
-               e1000_erase_flash_bank_ich8lan(hw, 1);
+               ret_val = e1000_erase_flash_bank_ich8lan(hw, 1);
+               if (ret_val) {
+                       nvm->ops.release(hw);
+                       goto out;
+               }
        } else {
                old_bank_offset = nvm->flash_bank_size;
                new_bank_offset = 0;
-               e1000_erase_flash_bank_ich8lan(hw, 0);
+               ret_val = e1000_erase_flash_bank_ich8lan(hw, 0);
+               if (ret_val) {
+                       nvm->ops.release(hw);
+                       goto out;
+               }
        }
 
        for (i = 0; i < E1000_ICH8_SHADOW_RAM_WORDS; i++) {
@@ -1377,9 +1931,11 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
                if (dev_spec->shadow_ram[i].modified) {
                        data = dev_spec->shadow_ram[i].value;
                } else {
-                       e1000_read_flash_word_ich8lan(hw,
-                                                     i + old_bank_offset,
-                                                     &data);
+                       ret_val = e1000_read_flash_word_ich8lan(hw, i +
+                                                               old_bank_offset,
+                                                               &data);
+                       if (ret_val)
+                               break;
                }
 
                /*
@@ -1418,9 +1974,9 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
         */
        if (ret_val) {
                /* Possibly read-only, see e1000e_write_protect_nvm_ich8lan() */
-               hw_dbg(hw, "Flash commit failed.\n");
-               e1000_release_swflag_ich8lan(hw);
-               return ret_val;
+               e_dbg("Flash commit failed.\n");
+               nvm->ops.release(hw);
+               goto out;
        }
 
        /*
@@ -1430,14 +1986,18 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
         * and we need to change bit 14 to 0b
         */
        act_offset = new_bank_offset + E1000_ICH_NVM_SIG_WORD;
-       e1000_read_flash_word_ich8lan(hw, act_offset, &data);
+       ret_val = e1000_read_flash_word_ich8lan(hw, act_offset, &data);
+       if (ret_val) {
+               nvm->ops.release(hw);
+               goto out;
+       }
        data &= 0xBFFF;
        ret_val = e1000_retry_write_flash_byte_ich8lan(hw,
                                                       act_offset * 2 + 1,
                                                       (u8)(data >> 8));
        if (ret_val) {
-               e1000_release_swflag_ich8lan(hw);
-               return ret_val;
+               nvm->ops.release(hw);
+               goto out;
        }
 
        /*
@@ -1449,17 +2009,17 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
        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) {
-               e1000_release_swflag_ich8lan(hw);
-               return ret_val;
+               nvm->ops.release(hw);
+               goto out;
        }
 
        /* Great!  Everything worked, we can now clear the cached entries. */
        for (i = 0; i < E1000_ICH8_SHADOW_RAM_WORDS; i++) {
-               dev_spec->shadow_ram[i].modified = 0;
+               dev_spec->shadow_ram[i].modified = false;
                dev_spec->shadow_ram[i].value = 0xFFFF;
        }
 
-       e1000_release_swflag_ich8lan(hw);
+       nvm->ops.release(hw);
 
        /*
         * Reload the EEPROM, or else modifications will not appear
@@ -1468,6 +2028,10 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
        e1000e_reload_nvm(hw);
        msleep(10);
 
+out:
+       if (ret_val)
+               e_dbg("NVM update error: %d\n", ret_val);
+
        return ret_val;
 }
 
@@ -1519,14 +2083,12 @@ static s32 e1000_validate_nvm_checksum_ich8lan(struct e1000_hw *hw)
  **/
 void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw)
 {
+       struct e1000_nvm_info *nvm = &hw->nvm;
        union ich8_flash_protected_range pr0;
        union ich8_hws_flash_status hsfsts;
        u32 gfpreg;
-       s32 ret_val;
 
-       ret_val = e1000_acquire_swflag_ich8lan(hw);
-       if (ret_val)
-               return;
+       nvm->ops.acquire(hw);
 
        gfpreg = er32flash(ICH_FLASH_GFPREG);
 
@@ -1547,7 +2109,7 @@ void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw)
        hsfsts.hsf_status.flockdn = true;
        ew32flash(ICH_FLASH_HSFSTS, hsfsts.regval);
 
-       e1000_release_swflag_ich8lan(hw);
+       nvm->ops.release(hw);
 }
 
 /**
@@ -1618,7 +2180,7 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
                        /* Repeat for some time before giving up. */
                        continue;
                if (hsfsts.hsf_status.flcdone == 0) {
-                       hw_dbg(hw, "Timeout error - flash cycle "
+                       e_dbg("Timeout error - flash cycle "
                                 "did not complete.");
                        break;
                }
@@ -1663,7 +2225,7 @@ static s32 e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw,
                return ret_val;
 
        for (program_retries = 0; program_retries < 100; program_retries++) {
-               hw_dbg(hw, "Retrying Byte %2.2X at offset %u\n", byte, offset);
+               e_dbg("Retrying Byte %2.2X at offset %u\n", byte, offset);
                udelay(100);
                ret_val = e1000_write_flash_byte_ich8lan(hw, offset, byte);
                if (!ret_val)
@@ -1693,9 +2255,7 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank)
        u32 flash_bank_size = nvm->flash_bank_size * 2;
        s32 ret_val;
        s32 count = 0;
-       s32 iteration;
-       s32 sector_size;
-       s32 j;
+       s32 j, iteration, sector_size;
 
        hsfsts.regval = er16flash(ICH_FLASH_HSFSTS);
 
@@ -1720,19 +2280,15 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank)
                break;
        case 1:
                sector_size = ICH_FLASH_SEG_SIZE_4K;
-               iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_4K;
+               iteration = 1;
                break;
        case 2:
-               if (hw->mac.type == e1000_ich9lan) {
-                       sector_size = ICH_FLASH_SEG_SIZE_8K;
-                       iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_8K;
-               } else {
-                       return -E1000_ERR_NVM;
-               }
+               sector_size = ICH_FLASH_SEG_SIZE_8K;
+               iteration = 1;
                break;
        case 3:
                sector_size = ICH_FLASH_SEG_SIZE_64K;
-               iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_64K;
+               iteration = 1;
                break;
        default:
                return -E1000_ERR_NVM;
@@ -1740,7 +2296,7 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank)
 
        /* Start with the base address, then add the sector offset. */
        flash_linear_addr = hw->nvm.flash_base_addr;
-       flash_linear_addr += (bank) ? (sector_size * iteration) : 0;
+       flash_linear_addr += (bank) ? flash_bank_size : 0;
 
        for (j = 0; j < iteration ; j++) {
                do {
@@ -1802,7 +2358,7 @@ static s32 e1000_valid_led_default_ich8lan(struct e1000_hw *hw, u16 *data)
 
        ret_val = e1000_read_nvm(hw, NVM_ID_LED_SETTINGS, 1, data);
        if (ret_val) {
-               hw_dbg(hw, "NVM Read Error\n");
+               e_dbg("NVM Read Error\n");
                return ret_val;
        }
 
@@ -1814,6 +2370,79 @@ static s32 e1000_valid_led_default_ich8lan(struct e1000_hw *hw, u16 *data)
 }
 
 /**
+ *  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 e1000e_id_led_init()),
+ *  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;
+
+       /* Get default ID LED modes */
+       ret_val = hw->nvm.ops.valid_led_default(hw, &data);
+       if (ret_val)
+               goto out;
+
+       mac->ledctl_default = er32(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;
+               }
+       }
+
+out:
+       return ret_val;
+}
+
+/**
  *  e1000_get_bus_info_ich8lan - Get/Set the bus type and width
  *  @hw: pointer to the HW structure
  *
@@ -1848,6 +2477,8 @@ static s32 e1000_get_bus_info_ich8lan(struct e1000_hw *hw)
  **/
 static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
 {
+       struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
+       u16 reg;
        u32 ctrl, icr, kab;
        s32 ret_val;
 
@@ -1857,10 +2488,10 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
         */
        ret_val = e1000e_disable_pcie_master(hw);
        if (ret_val) {
-               hw_dbg(hw, "PCI-E Master disable polling has failed.\n");
+               e_dbg("PCI-E Master disable polling has failed.\n");
        }
 
-       hw_dbg(hw, "Masking off all interrupts\n");
+       e_dbg("Masking off all interrupts\n");
        ew32(IMC, 0xffffffff);
 
        /*
@@ -1882,9 +2513,27 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
                ew32(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, &reg);
+               if (ret_val)
+                       return ret_val;
+
+               if (reg & E1000_NVM_K1_ENABLE)
+                       dev_spec->nvm_k1_enabled = true;
+               else
+                       dev_spec->nvm_k1_enabled = false;
+       }
+
        ctrl = er32(CTRL);
 
        if (!e1000_check_reset_block(hw)) {
+               /* Clear PHY Reset Asserted bit */
+               if (hw->mac.type >= e1000_pchlan) {
+                       u32 status = er32(STATUS);
+                       ew32(STATUS, status & ~E1000_STATUS_PHYRA);
+               }
+
                /*
                 * PHY HW reset requires MAC CORE reset at the same
                 * time to make sure the interface between MAC and the
@@ -1893,22 +2542,53 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
                ctrl |= E1000_CTRL_PHY_RST;
        }
        ret_val = e1000_acquire_swflag_ich8lan(hw);
-       hw_dbg(hw, "Issuing a global reset to ich8lan");
+       e_dbg("Issuing a global reset to ich8lan\n");
        ew32(CTRL, (ctrl | E1000_CTRL_RST));
        msleep(20);
 
-       /* release the swflag because it is not reset by hardware reset */
-       e1000_release_swflag_ich8lan(hw);
+       if (!ret_val)
+               e1000_release_swflag_ich8lan(hw);
 
-       ret_val = e1000e_get_auto_rd_done(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.
-                */
-               hw_dbg(hw, "Auto Read Done did not complete\n");
+       /* Perform any necessary post-reset workarounds */
+       if (hw->mac.type == e1000_pchlan)
+               ret_val = e1000_hv_phy_workarounds_ich8lan(hw);
+
+       if (ctrl & E1000_CTRL_PHY_RST)
+               ret_val = hw->phy.ops.get_cfg_done(hw);
+
+       if (hw->mac.type >= e1000_ich10lan) {
+               e1000_lan_init_done_ich8lan(hw);
+       } else {
+               ret_val = e1000e_get_auto_rd_done(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.
+                        */
+                       e_dbg("Auto Read Done did not complete\n");
+               }
+       }
+       /* Dummy read to clear the phy wakeup bit after lcd reset */
+       if (hw->mac.type == e1000_pchlan)
+               e1e_rphy(hw, BM_WUC, &reg);
+
+       ret_val = e1000_sw_lcd_config_ich8lan(hw);
+       if (ret_val)
+               goto out;
+
+       if (hw->mac.type == e1000_pchlan) {
+               ret_val = e1000_oem_bits_config_ich8lan(hw, true);
+               if (ret_val)
+                       goto out;
        }
+       /*
+        * 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)
+               ew32(CRC_OFFSET, 0x65656565);
 
        ew32(IMC, 0xffffffff);
        icr = er32(ICR);
@@ -1917,6 +2597,7 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
        kab |= E1000_KABGTXD_BGSQLBIAS;
        ew32(KABGTXD, kab);
 
+out:
        return ret_val;
 }
 
@@ -1942,20 +2623,31 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
        e1000_initialize_hw_bits_ich8lan(hw);
 
        /* Initialize identification LED */
-       ret_val = e1000e_id_led_init(hw);
-       if (ret_val) {
-               hw_dbg(hw, "Error initializing identification LED\n");
-               return ret_val;
-       }
+       ret_val = mac->ops.id_led_init(hw);
+       if (ret_val)
+               e_dbg("Error initializing identification LED\n");
+               /* This is not fatal and we should not stop init due to this */
 
        /* Setup the receive address. */
        e1000e_init_rx_addrs(hw, mac->rar_entry_count);
 
        /* Zero out the Multicast HASH table */
-       hw_dbg(hw, "Zeroing the MTA\n");
+       e_dbg("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.  Reading the BM_WUC register will clear 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_WUC, &i);
+               ret_val = e1000_phy_hw_reset_ich8lan(hw);
+               if (ret_val)
+                       return ret_val;
+       }
+
        /* Setup link and flow control */
        ret_val = e1000_setup_link_ich8lan(hw);
 
@@ -2011,6 +2703,9 @@ static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw)
        /* Extended Device Control */
        reg = er32(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;
        ew32(CTRL_EXT, reg);
 
        /* Transmit Descriptor Control 0 */
@@ -2069,12 +2764,22 @@ static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw)
         * the default flow control setting, so we explicitly
         * set it to full.
         */
-       if (hw->fc.type == e1000_fc_default)
-               hw->fc.type = e1000_fc_full;
+       if (hw->fc.requested_mode == e1000_fc_default) {
+               /* Workaround h/w hang when Tx flow control enabled */
+               if (hw->mac.type == e1000_pchlan)
+                       hw->fc.requested_mode = e1000_fc_rx_pause;
+               else
+                       hw->fc.requested_mode = e1000_fc_full;
+       }
 
-       hw->fc.original_type = hw->fc.type;
+       /*
+        * 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;
 
-       hw_dbg(hw, "After fix-ups FlowControl is now = %x\n", hw->fc.type);
+       e_dbg("After fix-ups FlowControl is now = %x\n",
+               hw->fc.current_mode);
 
        /* Continue to configure the copper link. */
        ret_val = e1000_setup_copper_link_ich8lan(hw);
@@ -2082,6 +2787,14 @@ static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw)
                return ret_val;
 
        ew32(FCTTV, hw->fc.pause_time);
+       if ((hw->phy.type == e1000_phy_82578) ||
+           (hw->phy.type == e1000_phy_82577)) {
+               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 e1000e_set_fc_watermarks(hw);
 }
@@ -2110,29 +2823,39 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw)
         * and increase the max iterations when polling the phy;
         * this fixes erroneous timeouts at 10Mbps.
         */
-       ret_val = e1000e_write_kmrn_reg(hw, GG82563_REG(0x34, 4), 0xFFFF);
+       ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_TIMEOUTS, 0xFFFF);
        if (ret_val)
                return ret_val;
-       ret_val = e1000e_read_kmrn_reg(hw, GG82563_REG(0x34, 9), &reg_data);
+       ret_val = e1000e_read_kmrn_reg(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
+                                      &reg_data);
        if (ret_val)
                return ret_val;
        reg_data |= 0x3F;
-       ret_val = e1000e_write_kmrn_reg(hw, GG82563_REG(0x34, 9), reg_data);
+       ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
+                                       reg_data);
        if (ret_val)
                return ret_val;
 
-       if (hw->phy.type == e1000_phy_igp_3) {
+       switch (hw->phy.type) {
+       case e1000_phy_igp_3:
                ret_val = e1000e_copper_link_setup_igp(hw);
                if (ret_val)
                        return ret_val;
-       } else if (hw->phy.type == e1000_phy_bm) {
+               break;
+       case e1000_phy_bm:
+       case e1000_phy_82578:
                ret_val = e1000e_copper_link_setup_m88(hw);
                if (ret_val)
                        return ret_val;
-       }
-
-       if (hw->phy.type == e1000_phy_ife) {
-               ret_val = e1e_rphy(hw, IFE_PHY_MDIX_CONTROL, &reg_data);
+               break;
+       case e1000_phy_82577:
+               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;
 
@@ -2150,9 +2873,13 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw)
                        reg_data |= IFE_PMC_AUTO_MDIX;
                        break;
                }
-               ret_val = e1e_wphy(hw, IFE_PHY_MDIX_CONTROL, reg_data);
+               ret_val = hw->phy.ops.write_reg(hw, IFE_PHY_MDIX_CONTROL,
+                                               reg_data);
                if (ret_val)
                        return ret_val;
+               break;
+       default:
+               break;
        }
        return e1000e_setup_copper_link(hw);
 }
@@ -2259,8 +2986,8 @@ static s32 e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw)
  *  @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).
+ *  If ICH8, set the current Kumeran workaround state (enabled - true
+ *  /disabled - false).
  **/
 void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
                                                 bool state)
@@ -2268,7 +2995,7 @@ void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
        struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
 
        if (hw->mac.type != e1000_ich8lan) {
-               hw_dbg(hw, "Workaround applies to ICH8 only.\n");
+               e_dbg("Workaround applies to ICH8 only.\n");
                return;
        }
 
@@ -2369,18 +3096,26 @@ void e1000e_gig_downshift_workaround_ich8lan(struct e1000_hw *hw)
  *  'LPLU Enabled' and 'Gig Disable' to force link speed negotiation
  *  to a lower speed.
  *
- *  Should only be called for ICH9 and ICH10 devices.
+ *  Should only be called for applicable parts.
  **/
 void e1000e_disable_gig_wol_ich8lan(struct e1000_hw *hw)
 {
        u32 phy_ctrl;
 
-       if ((hw->mac.type == e1000_ich10lan) ||
-           (hw->mac.type == e1000_ich9lan)) {
+       switch (hw->mac.type) {
+       case e1000_ich8lan:
+       case e1000_ich9lan:
+       case e1000_ich10lan:
+       case e1000_pchlan:
                phy_ctrl = er32(PHY_CTRL);
                phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU |
                            E1000_PHY_CTRL_GBE_DISABLE;
                ew32(PHY_CTRL, phy_ctrl);
+
+               if (hw->mac.type == e1000_pchlan)
+                       e1000_phy_hw_reset_ich8lan(hw);
+       default:
+               break;
        }
 
        return;
@@ -2434,23 +3169,119 @@ static s32 e1000_led_off_ich8lan(struct e1000_hw *hw)
 }
 
 /**
+ *  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)
+{
+       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)
+{
+       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;
+
+       /*
+        * 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 (!(er32(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;
+
+       /*
+        * 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 (!(er32(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
  *  @hw: pointer to the HW structure
  *
  *  Read the management control register for the config done bit for
  *  completion status.  NOTE: silicon which is EEPROM-less will fail trying
  *  to read the config done bit, so an error is *ONLY* logged and returns
- *  E1000_SUCCESS.  If we were to return with error, EEPROM-less silicon
+ *  0.  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)
 {
        u32 bank = 0;
 
+       if (hw->mac.type >= e1000_pchlan) {
+               u32 status = er32(STATUS);
+
+               if (status & E1000_STATUS_PHYRA)
+                       ew32(STATUS, status & ~E1000_STATUS_PHYRA);
+               else
+                       e_dbg("PHY Reset Asserted not set - needs delay\n");
+       }
+
        e1000e_get_cfg_done(hw);
 
        /* If EEPROM is not marked present, init the IGP 3 PHY manually */
-       if (hw->mac.type != e1000_ich10lan) {
+       if ((hw->mac.type != e1000_ich10lan) &&
+           (hw->mac.type != e1000_pchlan)) {
                if (((er32(EECD) & E1000_EECD_PRES) == 0) &&
                    (hw->phy.type == e1000_phy_igp_3)) {
                        e1000e_phy_init_script_igp3(hw);
@@ -2458,7 +3289,7 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw)
        } else {
                if (e1000_valid_nvm_bank_detect_ich8lan(hw, &bank)) {
                        /* Maybe we should do a basic PHY config */
-                       hw_dbg(hw, "EEPROM not present\n");
+                       e_dbg("EEPROM not present\n");
                        return -E1000_ERR_CONFIG;
                }
        }
@@ -2467,6 +3298,23 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw)
 }
 
 /**
+ * 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
  *
@@ -2475,66 +3323,85 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw)
  **/
 static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw)
 {
-       u32 temp;
+       u16 phy_data;
 
        e1000e_clear_hw_cntrs_base(hw);
 
-       temp = er32(ALGNERRC);
-       temp = er32(RXERRC);
-       temp = er32(TNCRS);
-       temp = er32(CEXTERR);
-       temp = er32(TSCTC);
-       temp = er32(TSCTFC);
-
-       temp = er32(MGTPRC);
-       temp = er32(MGTPDC);
-       temp = er32(MGTPTC);
-
-       temp = er32(IAC);
-       temp = er32(ICRXOC);
-
+       er32(ALGNERRC);
+       er32(RXERRC);
+       er32(TNCRS);
+       er32(CEXTERR);
+       er32(TSCTC);
+       er32(TSCTFC);
+
+       er32(MGTPRC);
+       er32(MGTPDC);
+       er32(MGTPTC);
+
+       er32(IAC);
+       er32(ICRXOC);
+
+       /* Clear PHY statistics registers */
+       if ((hw->phy.type == e1000_phy_82578) ||
+           (hw->phy.type == e1000_phy_82577)) {
+               hw->phy.ops.read_reg(hw, HV_SCC_UPPER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_SCC_LOWER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_ECOL_UPPER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_ECOL_LOWER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_MCC_UPPER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_MCC_LOWER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_LATECOL_UPPER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_LATECOL_LOWER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_COLC_UPPER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_COLC_LOWER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_DC_UPPER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_DC_LOWER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_TNCRS_UPPER, &phy_data);
+               hw->phy.ops.read_reg(hw, HV_TNCRS_LOWER, &phy_data);
+       }
 }
 
 static struct e1000_mac_operations ich8_mac_ops = {
+       .id_led_init            = e1000e_id_led_init,
        .check_mng_mode         = e1000_check_mng_mode_ich8lan,
-       .check_for_link         = e1000e_check_for_copper_link,
-       .cleanup_led            = e1000_cleanup_led_ich8lan,
+       .check_for_link         = e1000_check_for_copper_link_ich8lan,
+       /* cleanup_led dependent on mac type */
        .clear_hw_cntrs         = e1000_clear_hw_cntrs_ich8lan,
        .get_bus_info           = e1000_get_bus_info_ich8lan,
+       .set_lan_id             = e1000_set_lan_id_single_port,
        .get_link_up_info       = e1000_get_link_up_info_ich8lan,
-       .led_on                 = e1000_led_on_ich8lan,
-       .led_off                = e1000_led_off_ich8lan,
+       /* led_on dependent on mac type */
+       /* led_off dependent on mac type */
        .update_mc_addr_list    = e1000e_update_mc_addr_list_generic,
        .reset_hw               = e1000_reset_hw_ich8lan,
        .init_hw                = e1000_init_hw_ich8lan,
        .setup_link             = e1000_setup_link_ich8lan,
        .setup_physical_interface= e1000_setup_copper_link_ich8lan,
+       /* id_led_init dependent on mac type */
 };
 
 static struct e1000_phy_operations ich8_phy_ops = {
-       .acquire_phy            = e1000_acquire_swflag_ich8lan,
+       .acquire                = e1000_acquire_swflag_ich8lan,
        .check_reset_block      = e1000_check_reset_block_ich8lan,
-       .commit_phy             = NULL,
-       .force_speed_duplex     = e1000_phy_force_speed_duplex_ich8lan,
+       .commit                 = NULL,
        .get_cfg_done           = e1000_get_cfg_done_ich8lan,
        .get_cable_length       = e1000e_get_cable_length_igp_2,
-       .get_phy_info           = e1000_get_phy_info_ich8lan,
-       .read_phy_reg           = e1000e_read_phy_reg_igp,
-       .release_phy            = e1000_release_swflag_ich8lan,
-       .reset_phy              = e1000_phy_hw_reset_ich8lan,
+       .read_reg               = e1000e_read_phy_reg_igp,
+       .release                = e1000_release_swflag_ich8lan,
+       .reset                  = e1000_phy_hw_reset_ich8lan,
        .set_d0_lplu_state      = e1000_set_d0_lplu_state_ich8lan,
        .set_d3_lplu_state      = e1000_set_d3_lplu_state_ich8lan,
-       .write_phy_reg          = e1000e_write_phy_reg_igp,
+       .write_reg              = e1000e_write_phy_reg_igp,
 };
 
 static struct e1000_nvm_operations ich8_nvm_ops = {
-       .acquire_nvm            = e1000_acquire_swflag_ich8lan,
-       .read_nvm               = e1000_read_nvm_ich8lan,
-       .release_nvm            = e1000_release_swflag_ich8lan,
-       .update_nvm             = e1000_update_nvm_checksum_ich8lan,
+       .acquire                = e1000_acquire_nvm_ich8lan,
+       .read                   = e1000_read_nvm_ich8lan,
+       .release                = e1000_release_nvm_ich8lan,
+       .update                 = e1000_update_nvm_checksum_ich8lan,
        .valid_led_default      = e1000_valid_led_default_ich8lan,
-       .validate_nvm           = e1000_validate_nvm_checksum_ich8lan,
-       .write_nvm              = e1000_write_nvm_ich8lan,
+       .validate               = e1000_validate_nvm_checksum_ich8lan,
+       .write                  = e1000_write_nvm_ich8lan,
 };
 
 struct e1000_info e1000_ich8_info = {
@@ -2547,6 +3414,7 @@ struct e1000_info e1000_ich8_info = {
                                  | FLAG_HAS_FLASH
                                  | FLAG_APME_IN_WUC,
        .pba                    = 8,
+       .max_hw_frame_size      = ETH_FRAME_LEN + ETH_FCS_LEN,
        .get_variants           = e1000_get_variants_ich8lan,
        .mac_ops                = &ich8_mac_ops,
        .phy_ops                = &ich8_phy_ops,
@@ -2565,6 +3433,7 @@ struct e1000_info e1000_ich9_info = {
                                  | FLAG_HAS_FLASH
                                  | FLAG_APME_IN_WUC,
        .pba                    = 10,
+       .max_hw_frame_size      = DEFAULT_JUMBO,
        .get_variants           = e1000_get_variants_ich8lan,
        .mac_ops                = &ich8_mac_ops,
        .phy_ops                = &ich8_phy_ops,
@@ -2583,6 +3452,26 @@ struct e1000_info e1000_ich10_info = {
                                  | FLAG_HAS_FLASH
                                  | FLAG_APME_IN_WUC,
        .pba                    = 10,
+       .max_hw_frame_size      = DEFAULT_JUMBO,
+       .get_variants           = e1000_get_variants_ich8lan,
+       .mac_ops                = &ich8_mac_ops,
+       .phy_ops                = &ich8_phy_ops,
+       .nvm_ops                = &ich8_nvm_ops,
+};
+
+struct e1000_info e1000_pch_info = {
+       .mac                    = e1000_pchlan,
+       .flags                  = FLAG_IS_ICH
+                                 | FLAG_HAS_WOL
+                                 | FLAG_RX_CSUM_ENABLED
+                                 | FLAG_HAS_CTRLEXT_ON_LOAD
+                                 | FLAG_HAS_AMT
+                                 | FLAG_HAS_FLASH
+                                 | FLAG_HAS_JUMBO_FRAMES
+                                 | FLAG_DISABLE_FC_PAUSE_TIME /* errata */
+                                 | FLAG_APME_IN_WUC,
+       .pba                    = 26,
+       .max_hw_frame_size      = 4096,
        .get_variants           = e1000_get_variants_ich8lan,
        .mac_ops                = &ich8_mac_ops,
        .phy_ops                = &ich8_phy_ops,