]> Pileus Git - ~andy/linux/blobdiff - drivers/net/e1000e/ich8lan.c
e1000e: do not write SmartSpeed register bits on parts without support
[~andy/linux] / drivers / net / e1000e / ich8lan.c
index b63d9878b0ac70b668f4279818d6c5bceba3361c..bf9b97db76b965ea05948c147c1345b1959df214 100644 (file)
  * 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>
 #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 */
+
 /* ICH GbE Flash Hardware Sequencing Flash Status Register bit breakdown */
 /* Offset 04h HSFSTS */
 union ich8_hws_flash_status {
@@ -186,6 +192,14 @@ 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 inline u16 __er16flash(struct e1000_hw *hw, unsigned long reg)
 {
@@ -212,6 +226,41 @@ static inline void __ew32flash(struct e1000_hw *hw, unsigned long reg, u32 val)
 #define ew16flash(reg,val)     __ew16flash(hw, (reg), (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.check_polarity       = e1000_check_polarity_ife_ich8lan;
+       phy->ops.read_phy_reg         = e1000_read_phy_reg_hv;
+       phy->ops.write_phy_reg        = e1000_write_phy_reg_hv;
+       phy->autoneg_mask             = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+
+       phy->id = e1000_phy_unknown;
+       e1000e_get_phy_id(hw);
+       phy->type = e1000e_get_phy_type_from_id(phy->id);
+
+       if (phy->type == 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_phy_info = e1000_get_phy_info_82577;
+               phy->ops.commit_phy = e1000e_phy_sw_reset;
+       }
+
+       return ret_val;
+}
+
 /**
  *  e1000_init_phy_params_ich8lan - Initialize PHY function pointers
  *  @hw: pointer to the HW structure
@@ -273,6 +322,8 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
                break;
        }
 
+       phy->ops.check_polarity = e1000_check_polarity_ife_ich8lan;
+
        return 0;
 }
 
@@ -358,6 +409,36 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_adapter *adapter)
        /* Set if manageability features are enabled. */
        mac->arc_subsystem_valid = 1;
 
+       /* 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);
@@ -378,7 +459,10 @@ 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;
 
@@ -415,12 +499,15 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
 
        while (timeout) {
                extcnf_ctrl = er32(EXTCNF_CTRL);
-               extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG;
-               ew32(EXTCNF_CTRL, extcnf_ctrl);
 
-               extcnf_ctrl = er32(EXTCNF_CTRL);
-               if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)
-                       break;
+               if (!(extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)) {
+                       extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG;
+                       ew32(EXTCNF_CTRL, extcnf_ctrl);
+
+                       extcnf_ctrl = er32(EXTCNF_CTRL);
+                       if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)
+                               break;
+               }
                mdelay(1);
                timeout--;
        }
@@ -559,6 +646,85 @@ static s32 e1000_phy_force_speed_duplex_ich8lan(struct e1000_hw *hw)
        return 0;
 }
 
+/**
+ *  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;
+
+       if (hw->mac.type != e1000_pchlan)
+               return ret_val;
+
+       if (((hw->phy.type == e1000_phy_82577) &&
+            ((hw->phy.revision == 1) || (hw->phy.revision == 2))) ||
+           ((hw->phy.type == e1000_phy_82578) && (hw->phy.revision == 1))) {
+               /* Disable generation of early preamble */
+               ret_val = 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;
+       }
+
+       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_phy(hw);
+       if (ret_val)
+               return ret_val;
+       hw->phy.addr = 1;
+       e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, 0);
+       hw->phy.ops.release_phy(hw);
+
+       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)
+               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);
+}
+
 /**
  *  e1000_phy_hw_reset_ich8lan - Performs a PHY reset
  *  @hw: pointer to the HW structure
@@ -573,13 +739,21 @@ static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
        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);
        if (ret_val)
                return ret_val;
 
+       /* Allow time for h/w to get to a quiescent state after reset */
+       mdelay(10);
+
+       if (hw->mac.type == e1000_pchlan) {
+               ret_val = e1000_hv_phy_workarounds_ich8lan(hw);
+               if (ret_val)
+                       return ret_val;
+       }
+
        /*
         * Initialize the PHY from the NVM on ICH platforms.  This
         * is needed due to an issue where the NVM configuration is
@@ -601,26 +775,8 @@ static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
                if (!(data & sw_cfg_mask))
                        return 0;
 
-               /* 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
@@ -706,7 +862,7 @@ static s32 e1000_get_phy_info_ife_ich8lan(struct e1000_hw *hw)
        phy->polarity_correction = (!(data & IFE_PSC_AUTO_POLARITY_DISABLE));
 
        if (phy->polarity_correction) {
-               ret_val = e1000_check_polarity_ife_ich8lan(hw);
+               ret_val = phy->ops.check_polarity(hw);
                if (ret_val)
                        return ret_val;
        } else {
@@ -746,6 +902,8 @@ static s32 e1000_get_phy_info_ich8lan(struct e1000_hw *hw)
                break;
        case e1000_phy_igp_3:
        case e1000_phy_bm:
+       case e1000_phy_82578:
+       case e1000_phy_82577:
                return e1000e_get_phy_info_igp(hw);
                break;
        default:
@@ -819,12 +977,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 */
@@ -837,6 +997,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
@@ -896,6 +1059,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
@@ -931,12 +1098,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 */
@@ -1856,6 +2025,79 @@ static s32 e1000_valid_led_default_ich8lan(struct e1000_hw *hw, u16 *data)
        return 0;
 }
 
+/**
+ *  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
@@ -1928,6 +2170,12 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
        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
@@ -1941,21 +2189,24 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
        ew32(CTRL, (ctrl | E1000_CTRL_RST));
        msleep(20);
 
-       if (!ret_val) {
-               /* release the swflag because it is not reset by
-                * hardware reset
-                */
+       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");
+       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.
+                        */
+                       hw_dbg(hw, "Auto Read Done did not complete\n");
+               }
        }
 
        ew32(IMC, 0xffffffff);
@@ -1965,6 +2216,9 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
        kab |= E1000_KABGTXD_BGSQLBIAS;
        ew32(KABGTXD, kab);
 
+       if (hw->mac.type == e1000_pchlan)
+               ret_val = e1000_hv_phy_workarounds_ich8lan(hw);
+
        return ret_val;
 }
 
@@ -1990,7 +2244,7 @@ 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);
+       ret_val = mac->ops.id_led_init(hw);
        if (ret_val) {
                hw_dbg(hw, "Error initializing identification LED\n");
                return ret_val;
@@ -2004,6 +2258,18 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
        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_phy_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);
 
@@ -2059,6 +2325,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 */
@@ -2117,8 +2386,13 @@ 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.requested_mode == e1000_fc_default)
-               hw->fc.requested_mode = 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;
+       }
 
        /*
         * Save off the requested flow control mode for use later.  Depending
@@ -2135,6 +2409,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_phy_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);
 }
@@ -2174,18 +2456,26 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw)
        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_phy_reg(hw, IFE_PHY_MDIX_CONTROL,
+                                              &reg_data);
                if (ret_val)
                        return ret_val;
 
@@ -2203,9 +2493,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_phy_reg(hw, IFE_PHY_MDIX_CONTROL,
+                                               reg_data);
                if (ret_val)
                        return ret_val;
+               break;
+       default:
+               break;
        }
        return e1000e_setup_copper_link(hw);
 }
@@ -2422,18 +2716,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_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);
+
+               /* Workaround SWFLAG unexpectedly set during S0->Sx */
+               if (hw->mac.type == e1000_pchlan)
+                       udelay(500);
+       default:
+               break;
        }
 
        return;
@@ -2486,6 +2788,92 @@ static s32 e1000_led_off_ich8lan(struct e1000_hw *hw)
        return 0;
 }
 
+/**
+ *  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_phy_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_phy_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_phy_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_phy_reg(hw, HV_LED_CONFIG, data);
+}
+
 /**
  *  e1000_get_cfg_done_ich8lan - Read config done bit
  *  @hw: pointer to the HW structure
@@ -2493,17 +2881,28 @@ static s32 e1000_led_off_ich8lan(struct e1000_hw *hw)
  *  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
+                       hw_dbg(hw,
+                              "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);
@@ -2529,6 +2928,7 @@ 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);
 
@@ -2546,22 +2946,42 @@ static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw)
        temp = er32(IAC);
        temp = er32(ICRXOC);
 
+       /* Clear PHY statistics registers */
+       if ((hw->phy.type == e1000_phy_82578) ||
+           (hw->phy.type == e1000_phy_82577)) {
+               hw->phy.ops.read_phy_reg(hw, HV_SCC_UPPER, &phy_data);
+               hw->phy.ops.read_phy_reg(hw, HV_SCC_LOWER, &phy_data);
+               hw->phy.ops.read_phy_reg(hw, HV_ECOL_UPPER, &phy_data);
+               hw->phy.ops.read_phy_reg(hw, HV_ECOL_LOWER, &phy_data);
+               hw->phy.ops.read_phy_reg(hw, HV_MCC_UPPER, &phy_data);
+               hw->phy.ops.read_phy_reg(hw, HV_MCC_LOWER, &phy_data);
+               hw->phy.ops.read_phy_reg(hw, HV_LATECOL_UPPER, &phy_data);
+               hw->phy.ops.read_phy_reg(hw, HV_LATECOL_LOWER, &phy_data);
+               hw->phy.ops.read_phy_reg(hw, HV_COLC_UPPER, &phy_data);
+               hw->phy.ops.read_phy_reg(hw, HV_COLC_LOWER, &phy_data);
+               hw->phy.ops.read_phy_reg(hw, HV_DC_UPPER, &phy_data);
+               hw->phy.ops.read_phy_reg(hw, HV_DC_LOWER, &phy_data);
+               hw->phy.ops.read_phy_reg(hw, HV_TNCRS_UPPER, &phy_data);
+               hw->phy.ops.read_phy_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,
+       /* cleanup_led dependent on mac type */
        .clear_hw_cntrs         = e1000_clear_hw_cntrs_ich8lan,
        .get_bus_info           = e1000_get_bus_info_ich8lan,
        .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 = {
@@ -2644,3 +3064,21 @@ struct e1000_info e1000_ich10_info = {
        .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_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,
+       .nvm_ops                = &ich8_nvm_ops,
+};