diff options
author | Bruce Allan <bruce.w.allan@intel.com> | 2012-12-05 12:40:59 +0400 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2013-01-18 16:54:49 +0400 |
commit | 203e41514a557b71c4c42b4bb2912b56fa0c2fdc (patch) | |
tree | 956d6ab42a9eee23a0e0f9ec391b836ba7aa2b4c /drivers/net/ethernet/intel/e1000e/ethtool.c | |
parent | 887c95cc1da53f66a5890fdeab13414613010097 (diff) | |
download | linux-203e41514a557b71c4c42b4bb2912b56fa0c2fdc.tar.xz |
e1000e: add ethtool .get_eee/.set_eee
Add the ability to query and set Energy Efficient Ethernet parameters via
ethtool for applicable devices.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Tested-by: Jeff Pieper <jeffrey.e.pieper@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/ethernet/intel/e1000e/ethtool.c')
-rw-r--r-- | drivers/net/ethernet/intel/e1000e/ethtool.c | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index 2225603a8a28..636ba09ca6fb 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -35,6 +35,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/vmalloc.h> +#include <linux/mdio.h> #include "e1000.h" @@ -2050,6 +2051,137 @@ static int e1000_get_rxnfc(struct net_device *netdev, } } +static int e1000e_get_eee(struct net_device *netdev, struct ethtool_eee *edata) +{ + struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + u16 cap_addr, adv_addr, lpa_addr, pcs_stat_addr, phy_data, lpi_ctrl; + u32 status, ret_val; + + if (!(adapter->flags & FLAG_IS_ICH) || + !(adapter->flags2 & FLAG2_HAS_EEE)) + return -EOPNOTSUPP; + + switch (hw->phy.type) { + case e1000_phy_82579: + cap_addr = I82579_EEE_CAPABILITY; + adv_addr = I82579_EEE_ADVERTISEMENT; + lpa_addr = I82579_EEE_LP_ABILITY; + pcs_stat_addr = I82579_EEE_PCS_STATUS; + break; + case e1000_phy_i217: + cap_addr = I217_EEE_CAPABILITY; + adv_addr = I217_EEE_ADVERTISEMENT; + lpa_addr = I217_EEE_LP_ABILITY; + pcs_stat_addr = I217_EEE_PCS_STATUS; + break; + default: + return -EOPNOTSUPP; + } + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return -EBUSY; + + /* EEE Capability */ + ret_val = e1000_read_emi_reg_locked(hw, cap_addr, &phy_data); + if (ret_val) + goto release; + edata->supported = mmd_eee_cap_to_ethtool_sup_t(phy_data); + + /* EEE Advertised */ + ret_val = e1000_read_emi_reg_locked(hw, adv_addr, &phy_data); + if (ret_val) + goto release; + edata->advertised = mmd_eee_adv_to_ethtool_adv_t(phy_data); + + /* EEE Link Partner Advertised */ + ret_val = e1000_read_emi_reg_locked(hw, lpa_addr, &phy_data); + if (ret_val) + goto release; + edata->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(phy_data); + + /* EEE PCS Status */ + ret_val = e1000_read_emi_reg_locked(hw, pcs_stat_addr, &phy_data); + if (hw->phy.type == e1000_phy_82579) + phy_data <<= 8; + +release: + hw->phy.ops.release(hw); + if (ret_val) + return -ENODATA; + + e1e_rphy(hw, I82579_LPI_CTRL, &lpi_ctrl); + status = er32(STATUS); + + /* Result of the EEE auto negotiation - there is no register that + * has the status of the EEE negotiation so do a best-guess based + * on whether both Tx and Rx LPI indications have been received or + * base it on the link speed, the EEE advertised speeds on both ends + * and the speeds on which EEE is enabled locally. + */ + if (((phy_data & E1000_EEE_TX_LPI_RCVD) && + (phy_data & E1000_EEE_RX_LPI_RCVD)) || + ((status & E1000_STATUS_SPEED_100) && + (edata->advertised & ADVERTISED_100baseT_Full) && + (edata->lp_advertised & ADVERTISED_100baseT_Full) && + (lpi_ctrl & I82579_LPI_CTRL_100_ENABLE)) || + ((status & E1000_STATUS_SPEED_1000) && + (edata->advertised & ADVERTISED_1000baseT_Full) && + (edata->lp_advertised & ADVERTISED_1000baseT_Full) && + (lpi_ctrl & I82579_LPI_CTRL_1000_ENABLE))) + edata->eee_active = true; + + edata->eee_enabled = !hw->dev_spec.ich8lan.eee_disable; + edata->tx_lpi_enabled = true; + edata->tx_lpi_timer = er32(LPIC) >> E1000_LPIC_LPIET_SHIFT; + + return 0; +} + +static int e1000e_set_eee(struct net_device *netdev, struct ethtool_eee *edata) +{ + struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + struct ethtool_eee eee_curr; + s32 ret_val; + + if (!(adapter->flags & FLAG_IS_ICH) || + !(adapter->flags2 & FLAG2_HAS_EEE)) + return -EOPNOTSUPP; + + ret_val = e1000e_get_eee(netdev, &eee_curr); + if (ret_val) + return ret_val; + + if (eee_curr.advertised != edata->advertised) { + e_err("Setting EEE advertisement is not supported\n"); + return -EINVAL; + } + + if (eee_curr.tx_lpi_enabled != edata->tx_lpi_enabled) { + e_err("Setting EEE tx-lpi is not supported\n"); + return -EINVAL; + } + + if (eee_curr.tx_lpi_timer != edata->tx_lpi_timer) { + e_err("Setting EEE Tx LPI timer is not supported\n"); + return -EINVAL; + } + + if (hw->dev_spec.ich8lan.eee_disable != !edata->eee_enabled) { + hw->dev_spec.ich8lan.eee_disable = !edata->eee_enabled; + + /* reset the link */ + if (netif_running(netdev)) + e1000e_reinit_locked(adapter); + else + e1000e_reset(adapter); + } + + return 0; +} + static const struct ethtool_ops e1000_ethtool_ops = { .get_settings = e1000_get_settings, .set_settings = e1000_set_settings, @@ -2078,6 +2210,8 @@ static const struct ethtool_ops e1000_ethtool_ops = { .set_coalesce = e1000_set_coalesce, .get_rxnfc = e1000_get_rxnfc, .get_ts_info = ethtool_op_get_ts_info, + .get_eee = e1000e_get_eee, + .set_eee = e1000e_set_eee, }; void e1000e_set_ethtool_ops(struct net_device *netdev) |