Skip to content

Commit

Permalink
igb: Power down link when interface is down
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Nick Nunley authored and David S. Miller committed Feb 17, 2010
1 parent 53c992f commit 88a268c
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 11 deletions.
44 changes: 44 additions & 0 deletions drivers/net/igb/e1000_82575.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/igb/e1000_82575.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) | \
Expand Down
1 change: 1 addition & 0 deletions drivers/net/igb/e1000_defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
35 changes: 35 additions & 0 deletions drivers/net/igb/e1000_phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/igb/e1000_phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions drivers/net/igb/igb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
16 changes: 13 additions & 3 deletions drivers/net/igb/igb_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]))
Expand All @@ -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;

Expand All @@ -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;
Expand Down
39 changes: 31 additions & 8 deletions drivers/net/igb/igb_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 88a268c

Please sign in to comment.