Commit 88a268c1 authored by Nick Nunley's avatar Nick Nunley Committed by David S. Miller

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: default avatarNicholas Nunley <nicholasx.d.nunley@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 53c992fa
...@@ -726,6 +726,34 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw) ...@@ -726,6 +726,34 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw)
return ret_val; 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 * igb_get_pcs_speed_and_duplex_82575 - Retrieve current speed/duplex
* @hw: pointer to the HW structure * @hw: pointer to the HW structure
...@@ -1165,6 +1193,22 @@ static s32 igb_read_mac_addr_82575(struct e1000_hw *hw) ...@@ -1165,6 +1193,22 @@ static s32 igb_read_mac_addr_82575(struct e1000_hw *hw)
return ret_val; 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 * igb_clear_hw_cntrs_82575 - Clear device specific hardware counters
* @hw: pointer to the HW structure * @hw: pointer to the HW structure
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#define _E1000_82575_H_ #define _E1000_82575_H_
extern void igb_shutdown_serdes_link_82575(struct e1000_hw *hw); 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); extern void igb_rx_fifo_flush_82575(struct e1000_hw *hw);
#define ID_LED_DEFAULT_82575_SERDES ((ID_LED_DEF1_DEF2 << 12) | \ #define ID_LED_DEFAULT_82575_SERDES ((ID_LED_DEF1_DEF2 << 12) | \
......
...@@ -481,6 +481,7 @@ ...@@ -481,6 +481,7 @@
/* PHY Control Register */ /* PHY Control Register */
#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ #define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ #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_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */
#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ #define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */
#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ #define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */
......
...@@ -1930,6 +1930,41 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw) ...@@ -1930,6 +1930,41 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw)
return 0; 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. * igb_check_polarity_82580 - Checks the polarity.
* @hw: pointer to the HW structure * @hw: pointer to the HW structure
......
...@@ -60,6 +60,8 @@ s32 igb_setup_copper_link(struct e1000_hw *hw); ...@@ -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_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data);
s32 igb_phy_has_link(struct e1000_hw *hw, u32 iterations, s32 igb_phy_has_link(struct e1000_hw *hw, u32 iterations,
u32 usec_interval, bool *success); 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_phy_init_script_igp3(struct e1000_hw *hw);
s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); 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); s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
......
...@@ -358,6 +358,7 @@ extern void igb_alloc_rx_buffers_adv(struct igb_ring *, int); ...@@ -358,6 +358,7 @@ extern void igb_alloc_rx_buffers_adv(struct igb_ring *, int);
extern void igb_update_stats(struct igb_adapter *); extern void igb_update_stats(struct igb_adapter *);
extern bool igb_has_link(struct igb_adapter *adapter); extern bool igb_has_link(struct igb_adapter *adapter);
extern void igb_set_ethtool_ops(struct net_device *); 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) static inline s32 igb_reset_phy(struct e1000_hw *hw)
{ {
......
...@@ -1722,6 +1722,9 @@ static void igb_diag_test(struct net_device *netdev, ...@@ -1722,6 +1722,9 @@ static void igb_diag_test(struct net_device *netdev,
dev_info(&adapter->pdev->dev, "offline testing starting\n"); 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 /* Link test performed before hardware reset so autoneg doesn't
* interfere with test result */ * interfere with test result */
if (igb_link_test(adapter, &data[4])) if (igb_link_test(adapter, &data[4]))
...@@ -1745,6 +1748,8 @@ static void igb_diag_test(struct net_device *netdev, ...@@ -1745,6 +1748,8 @@ static void igb_diag_test(struct net_device *netdev,
eth_test->flags |= ETH_TEST_FL_FAILED; eth_test->flags |= ETH_TEST_FL_FAILED;
igb_reset(adapter); igb_reset(adapter);
/* power up link for loopback test */
igb_power_up_link(adapter);
if (igb_loopback_test(adapter, &data[3])) if (igb_loopback_test(adapter, &data[3]))
eth_test->flags |= ETH_TEST_FL_FAILED; eth_test->flags |= ETH_TEST_FL_FAILED;
...@@ -1763,9 +1768,14 @@ static void igb_diag_test(struct net_device *netdev, ...@@ -1763,9 +1768,14 @@ static void igb_diag_test(struct net_device *netdev,
dev_open(netdev); dev_open(netdev);
} else { } else {
dev_info(&adapter->pdev->dev, "online testing starting\n"); dev_info(&adapter->pdev->dev, "online testing starting\n");
/* Online tests */
if (igb_link_test(adapter, &data[4])) /* PHY is powered down when interface is down */
eth_test->flags |= ETH_TEST_FL_FAILED; 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 */ /* Online tests aren't run; pass by default */
data[0] = 0; data[0] = 0;
......
...@@ -1114,6 +1114,29 @@ static void igb_configure(struct igb_adapter *adapter) ...@@ -1114,6 +1114,29 @@ static void igb_configure(struct igb_adapter *adapter)
adapter->tx_queue_len = netdev->tx_queue_len; 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 * igb_up - Open the interface and prepare it to handle traffic
...@@ -1335,6 +1358,9 @@ void igb_reset(struct igb_adapter *adapter) ...@@ -1335,6 +1358,9 @@ void igb_reset(struct igb_adapter *adapter)
wr32(E1000_PCIEMISC, wr32(E1000_PCIEMISC,
reg & ~E1000_PCIEMISC_LX_DECISION); reg & ~E1000_PCIEMISC_LX_DECISION);
} }
if (!netif_running(adapter->netdev))
igb_power_down_link(adapter);
igb_update_mng_vlan(adapter); igb_update_mng_vlan(adapter);
/* Enable h/w to recognize an 802.1Q VLAN Ethernet packet */ /* Enable h/w to recognize an 802.1Q VLAN Ethernet packet */
...@@ -1717,9 +1743,6 @@ static void __devexit igb_remove(struct pci_dev *pdev) ...@@ -1717,9 +1743,6 @@ static void __devexit igb_remove(struct pci_dev *pdev)
unregister_netdev(netdev); unregister_netdev(netdev);
if (!igb_check_reset_block(hw))
igb_reset_phy(hw);
igb_clear_interrupt_scheme(adapter); igb_clear_interrupt_scheme(adapter);
#ifdef CONFIG_PCI_IOV #ifdef CONFIG_PCI_IOV
...@@ -1995,7 +2018,7 @@ static int igb_open(struct net_device *netdev) ...@@ -1995,7 +2018,7 @@ static int igb_open(struct net_device *netdev)
if (err) if (err)
goto err_setup_rx; 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. /* before we allocate an interrupt, we must be ready to handle it.
* Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt * Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt
...@@ -2037,7 +2060,7 @@ static int igb_open(struct net_device *netdev) ...@@ -2037,7 +2060,7 @@ static int igb_open(struct net_device *netdev)
err_req_irq: err_req_irq:
igb_release_hw_control(adapter); igb_release_hw_control(adapter);
/* e1000_power_down_phy(adapter); */ igb_power_down_link(adapter);
igb_free_all_rx_resources(adapter); igb_free_all_rx_resources(adapter);
err_setup_rx: err_setup_rx:
igb_free_all_tx_resources(adapter); igb_free_all_tx_resources(adapter);
...@@ -5820,7 +5843,9 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake) ...@@ -5820,7 +5843,9 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake)
*enable_wake = wufc || adapter->en_mng_pt; *enable_wake = wufc || adapter->en_mng_pt;
if (!*enable_wake) 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 /* Release control of h/w to f/w. If f/w is AMT enabled, this
* would have already happened in close and is redundant. */ * would have already happened in close and is redundant. */
...@@ -5877,8 +5902,6 @@ static int igb_resume(struct pci_dev *pdev) ...@@ -5877,8 +5902,6 @@ static int igb_resume(struct pci_dev *pdev)
return -ENOMEM; return -ENOMEM;
} }
/* e1000_power_up_phy(adapter); */
igb_reset(adapter); igb_reset(adapter);
/* let the f/w know that the h/w is now under the control of the /* let the f/w know that the h/w is now under the control of the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment