igb: Power down link when interface is down

This changes the behavior of the driver to power down the link
when the associated interface is down, unless management is enabled.

Signed-off-by: Nicholas Nunley <nicholasx.d.nunley@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Nick Nunley 2010-02-17 01:01:59 +00:00 committed by David S. Miller
parent 53c992fa84
commit 88a268c1a1
8 changed files with 129 additions and 11 deletions

View File

@ -726,6 +726,34 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw)
return ret_val;
}
/**
* igb_power_up_serdes_link_82575 - Power up the serdes link after shutdown
* @hw: pointer to the HW structure
**/
void igb_power_up_serdes_link_82575(struct e1000_hw *hw)
{
u32 reg;
if ((hw->phy.media_type != e1000_media_type_internal_serdes) &&
!igb_sgmii_active_82575(hw))
return;
/* Enable PCS to turn on link */
reg = rd32(E1000_PCS_CFG0);
reg |= E1000_PCS_CFG_PCS_EN;
wr32(E1000_PCS_CFG0, reg);
/* Power up the laser */
reg = rd32(E1000_CTRL_EXT);
reg &= ~E1000_CTRL_EXT_SDP3_DATA;
wr32(E1000_CTRL_EXT, reg);
/* flush the write to verify completion */
wrfl();
msleep(1);
}
/**
* igb_get_pcs_speed_and_duplex_82575 - Retrieve current speed/duplex
* @hw: pointer to the HW structure
@ -1165,6 +1193,22 @@ static s32 igb_read_mac_addr_82575(struct e1000_hw *hw)
return ret_val;
}
/**
* igb_power_down_phy_copper_82575 - 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.
**/
void igb_power_down_phy_copper_82575(struct e1000_hw *hw)
{
/* If the management interface is not enabled, then power down */
if (!(igb_enable_mng_pass_thru(hw) || igb_check_reset_block(hw)))
igb_power_down_phy_copper(hw);
return;
}
/**
* igb_clear_hw_cntrs_82575 - Clear device specific hardware counters
* @hw: pointer to the HW structure

View File

@ -29,6 +29,8 @@
#define _E1000_82575_H_
extern void igb_shutdown_serdes_link_82575(struct e1000_hw *hw);
extern void igb_power_up_serdes_link_82575(struct e1000_hw *hw);
extern void igb_power_down_phy_copper_82575(struct e1000_hw *hw);
extern void igb_rx_fifo_flush_82575(struct e1000_hw *hw);
#define ID_LED_DEFAULT_82575_SERDES ((ID_LED_DEF1_DEF2 << 12) | \

View File

@ -481,6 +481,7 @@
/* PHY Control Register */
#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
#define MII_CR_POWER_DOWN 0x0800 /* Power down */
#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */
#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */
#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */

View File

@ -1930,6 +1930,41 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw)
return 0;
}
/**
* igb_power_up_phy_copper - Restore copper link in case of PHY power down
* @hw: pointer to the HW structure
*
* In the case of a PHY power down to save power, or to turn off link during a
* driver unload, restore the link to previous settings.
**/
void igb_power_up_phy_copper(struct e1000_hw *hw)
{
u16 mii_reg = 0;
/* The PHY will retain its settings across a power down/up cycle */
hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
mii_reg &= ~MII_CR_POWER_DOWN;
hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
}
/**
* igb_power_down_phy_copper - Power down copper PHY
* @hw: pointer to the HW structure
*
* Power down PHY to save power when interface is down and wake on lan
* is not enabled.
**/
void igb_power_down_phy_copper(struct e1000_hw *hw)
{
u16 mii_reg = 0;
/* The PHY will retain its settings across a power down/up cycle */
hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
mii_reg |= MII_CR_POWER_DOWN;
hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
msleep(1);
}
/**
* igb_check_polarity_82580 - Checks the polarity.
* @hw: pointer to the HW structure

View File

@ -60,6 +60,8 @@ s32 igb_setup_copper_link(struct e1000_hw *hw);
s32 igb_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data);
s32 igb_phy_has_link(struct e1000_hw *hw, u32 iterations,
u32 usec_interval, bool *success);
void igb_power_up_phy_copper(struct e1000_hw *hw);
void igb_power_down_phy_copper(struct e1000_hw *hw);
s32 igb_phy_init_script_igp3(struct e1000_hw *hw);
s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);

View File

@ -358,6 +358,7 @@ extern void igb_alloc_rx_buffers_adv(struct igb_ring *, int);
extern void igb_update_stats(struct igb_adapter *);
extern bool igb_has_link(struct igb_adapter *adapter);
extern void igb_set_ethtool_ops(struct net_device *);
extern void igb_power_up_link(struct igb_adapter *);
static inline s32 igb_reset_phy(struct e1000_hw *hw)
{

View File

@ -1722,6 +1722,9 @@ static void igb_diag_test(struct net_device *netdev,
dev_info(&adapter->pdev->dev, "offline testing starting\n");
/* power up link for link test */
igb_power_up_link(adapter);
/* Link test performed before hardware reset so autoneg doesn't
* interfere with test result */
if (igb_link_test(adapter, &data[4]))
@ -1745,6 +1748,8 @@ static void igb_diag_test(struct net_device *netdev,
eth_test->flags |= ETH_TEST_FL_FAILED;
igb_reset(adapter);
/* power up link for loopback test */
igb_power_up_link(adapter);
if (igb_loopback_test(adapter, &data[3]))
eth_test->flags |= ETH_TEST_FL_FAILED;
@ -1763,9 +1768,14 @@ static void igb_diag_test(struct net_device *netdev,
dev_open(netdev);
} else {
dev_info(&adapter->pdev->dev, "online testing starting\n");
/* Online tests */
if (igb_link_test(adapter, &data[4]))
eth_test->flags |= ETH_TEST_FL_FAILED;
/* PHY is powered down when interface is down */
if (!netif_carrier_ok(netdev)) {
data[4] = 0;
} else {
if (igb_link_test(adapter, &data[4]))
eth_test->flags |= ETH_TEST_FL_FAILED;
}
/* Online tests aren't run; pass by default */
data[0] = 0;

View File

@ -1114,6 +1114,29 @@ static void igb_configure(struct igb_adapter *adapter)
adapter->tx_queue_len = netdev->tx_queue_len;
}
/**
* igb_power_up_link - Power up the phy/serdes link
* @adapter: address of board private structure
**/
void igb_power_up_link(struct igb_adapter *adapter)
{
if (adapter->hw.phy.media_type == e1000_media_type_copper)
igb_power_up_phy_copper(&adapter->hw);
else
igb_power_up_serdes_link_82575(&adapter->hw);
}
/**
* igb_power_down_link - Power down the phy/serdes link
* @adapter: address of board private structure
*/
static void igb_power_down_link(struct igb_adapter *adapter)
{
if (adapter->hw.phy.media_type == e1000_media_type_copper)
igb_power_down_phy_copper_82575(&adapter->hw);
else
igb_shutdown_serdes_link_82575(&adapter->hw);
}
/**
* igb_up - Open the interface and prepare it to handle traffic
@ -1335,6 +1358,9 @@ void igb_reset(struct igb_adapter *adapter)
wr32(E1000_PCIEMISC,
reg & ~E1000_PCIEMISC_LX_DECISION);
}
if (!netif_running(adapter->netdev))
igb_power_down_link(adapter);
igb_update_mng_vlan(adapter);
/* Enable h/w to recognize an 802.1Q VLAN Ethernet packet */
@ -1717,9 +1743,6 @@ static void __devexit igb_remove(struct pci_dev *pdev)
unregister_netdev(netdev);
if (!igb_check_reset_block(hw))
igb_reset_phy(hw);
igb_clear_interrupt_scheme(adapter);
#ifdef CONFIG_PCI_IOV
@ -1995,7 +2018,7 @@ static int igb_open(struct net_device *netdev)
if (err)
goto err_setup_rx;
/* e1000_power_up_phy(adapter); */
igb_power_up_link(adapter);
/* before we allocate an interrupt, we must be ready to handle it.
* Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt
@ -2037,7 +2060,7 @@ static int igb_open(struct net_device *netdev)
err_req_irq:
igb_release_hw_control(adapter);
/* e1000_power_down_phy(adapter); */
igb_power_down_link(adapter);
igb_free_all_rx_resources(adapter);
err_setup_rx:
igb_free_all_tx_resources(adapter);
@ -5820,7 +5843,9 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake)
*enable_wake = wufc || adapter->en_mng_pt;
if (!*enable_wake)
igb_shutdown_serdes_link_82575(hw);
igb_power_down_link(adapter);
else
igb_power_up_link(adapter);
/* Release control of h/w to f/w. If f/w is AMT enabled, this
* would have already happened in close and is redundant. */
@ -5877,8 +5902,6 @@ static int igb_resume(struct pci_dev *pdev)
return -ENOMEM;
}
/* e1000_power_up_phy(adapter); */
igb_reset(adapter);
/* let the f/w know that the h/w is now under the control of the