diff options
author | Bruce Allan <bruce.w.allan@intel.com> | 2011-05-13 07:19:48 +0000 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2011-06-09 20:31:49 -0700 |
commit | 99730e4c13c8344b02dd96108945b48d28c14c25 (patch) | |
tree | 36ae5f5c6bf0804f45dfb387931f88d11f7bc400 /drivers/net/e1000e/ich8lan.c | |
parent | d9b24135b972ccdd5f5174fba06c730e895e6daf (diff) |
e1000e: 82579 intermittently disabled during S0->Sx
When repeatedly cycling Sx->S0 states with the network cable unplugged,
the 82579 PHY may not initialize as expected and may require a full power
cycle to recover functionality to the device. Workaround this by testing
access of the PHY registers after resuming; if that returns unexpected
results toggle the LANPHYPC signal to power cycle the PHY.
This is implemented in the new function e1000_resume_workarounds_pchlan()
which calls another new function, e1000_toggle_lanphypc_value_ich8lan(),
which has been created to reduce code duplication (same functionality
required by a previous workaround). Also, e1000e_disable_gig_wol_ich8lan
is now e1000_suspend_workarounds_ich8lan to better reflect what it does.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Tested-by: Aaron Brown <aaron.f.brown@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/e1000e/ich8lan.c')
-rw-r--r-- | drivers/net/e1000e/ich8lan.c | 86 |
1 files changed, 73 insertions, 13 deletions
diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c index 3369d1f6a39..dcd5db5e34e 100644 --- a/drivers/net/e1000e/ich8lan.c +++ b/drivers/net/e1000e/ich8lan.c @@ -275,6 +275,19 @@ 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)) +static void e1000_toggle_lanphypc_value_ich8lan(struct e1000_hw *hw) +{ + u32 ctrl; + + ctrl = er32(CTRL); + ctrl |= E1000_CTRL_LANPHYPC_OVERRIDE; + ctrl &= ~E1000_CTRL_LANPHYPC_VALUE; + ew32(CTRL, ctrl); + udelay(10); + ctrl &= ~E1000_CTRL_LANPHYPC_OVERRIDE; + ew32(CTRL, ctrl); +} + /** * e1000_init_phy_params_pchlan - Initialize PHY function pointers * @hw: pointer to the HW structure @@ -284,7 +297,7 @@ static inline void __ew32flash(struct e1000_hw *hw, unsigned long reg, u32 val) static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; - u32 ctrl, fwsm; + u32 fwsm; s32 ret_val = 0; phy->addr = 1; @@ -308,13 +321,7 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw) */ fwsm = er32(FWSM); if (!(fwsm & E1000_ICH_FWSM_FW_VALID) && !e1000_check_reset_block(hw)) { - ctrl = er32(CTRL); - ctrl |= E1000_CTRL_LANPHYPC_OVERRIDE; - ctrl &= ~E1000_CTRL_LANPHYPC_VALUE; - ew32(CTRL, ctrl); - udelay(10); - ctrl &= ~E1000_CTRL_LANPHYPC_OVERRIDE; - ew32(CTRL, ctrl); + e1000_toggle_lanphypc_value_ich8lan(hw); msleep(50); /* @@ -3586,17 +3593,16 @@ void e1000e_gig_downshift_workaround_ich8lan(struct e1000_hw *hw) } /** - * e1000e_disable_gig_wol_ich8lan - disable gig during WoL + * e1000_suspend_workarounds_ich8lan - workarounds needed during S0->Sx * @hw: pointer to the HW structure * * During S0 to Sx transition, it is possible the link remains at gig * instead of negotiating to a lower speed. Before going to Sx, set * 'LPLU Enabled' and 'Gig Disable' to force link speed negotiation - * to a lower speed. - * - * Should only be called for applicable parts. + * to a lower speed. For PCH and newer parts, the OEM bits PHY register + * (LED, GbE disable and LPLU configurations) also needs to be written. **/ -void e1000e_disable_gig_wol_ich8lan(struct e1000_hw *hw) +void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw) { u32 phy_ctrl; s32 ret_val; @@ -3616,6 +3622,60 @@ void e1000e_disable_gig_wol_ich8lan(struct e1000_hw *hw) } /** + * e1000_resume_workarounds_pchlan - workarounds needed during Sx->S0 + * @hw: pointer to the HW structure + * + * During Sx to S0 transitions on non-managed devices or managed devices + * on which PHY resets are not blocked, if the PHY registers cannot be + * accessed properly by the s/w toggle the LANPHYPC value to power cycle + * the PHY. + **/ +void e1000_resume_workarounds_pchlan(struct e1000_hw *hw) +{ + u32 fwsm; + + if (hw->mac.type != e1000_pch2lan) + return; + + fwsm = er32(FWSM); + if (!(fwsm & E1000_ICH_FWSM_FW_VALID) || !e1000_check_reset_block(hw)) { + u16 phy_id1, phy_id2; + s32 ret_val; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) { + e_dbg("Failed to acquire PHY semaphore in resume\n"); + return; + } + + /* Test access to the PHY registers by reading the ID regs */ + ret_val = hw->phy.ops.read_reg_locked(hw, PHY_ID1, &phy_id1); + if (ret_val) + goto release; + ret_val = hw->phy.ops.read_reg_locked(hw, PHY_ID2, &phy_id2); + if (ret_val) + goto release; + + if (hw->phy.id == ((u32)(phy_id1 << 16) | + (u32)(phy_id2 & PHY_REVISION_MASK))) + goto release; + + e1000_toggle_lanphypc_value_ich8lan(hw); + + hw->phy.ops.release(hw); + msleep(50); + e1000_phy_hw_reset(hw); + msleep(50); + return; + } + +release: + hw->phy.ops.release(hw); + + return; +} + +/** * e1000_cleanup_led_ich8lan - Restore the default LED operation * @hw: pointer to the HW structure * |