e1000e: add ethtool .get_eee/.set_eee
authorBruce Allan <bruce.w.allan@intel.com>
Wed, 5 Dec 2012 08:40:59 +0000 (08:40 +0000)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Fri, 18 Jan 2013 12:54:49 +0000 (04:54 -0800)
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>
drivers/net/ethernet/intel/e1000e/defines.h
drivers/net/ethernet/intel/e1000e/e1000.h
drivers/net/ethernet/intel/e1000e/ethtool.c
drivers/net/ethernet/intel/e1000e/hw.h
drivers/net/ethernet/intel/e1000e/ich8lan.c

index 7326ea2fef8fc5ae88e20d6e155a29aeaa6e4a65..3041a54e3a61b9679b8e8c3e36bb3cf0f13974f4 100644 (file)
 
 #define E1000_KABGTXD_BGSQLBIAS           0x00050000
 
+/* Low Power IDLE Control */
+#define E1000_LPIC_LPIET_SHIFT         24      /* Low Power Idle Entry Time */
+
 /* PBA constants */
 #define E1000_PBA_8K  0x0008    /* 8KB */
 #define E1000_PBA_16K 0x0010    /* 16KB */
 /* BME1000 PHY Specific Control Register */
 #define BME1000_PSCR_ENABLE_DOWNSHIFT   0x0800 /* 1 = enable downshift */
 
+/* PHY Low Power Idle Control */
+#define I82579_LPI_CTRL                                PHY_REG(772, 20)
+#define I82579_LPI_CTRL_100_ENABLE             0x2000
+#define I82579_LPI_CTRL_1000_ENABLE            0x4000
+#define I82579_LPI_CTRL_ENABLE_MASK            0x6000
+#define I82579_LPI_CTRL_FORCE_PLL_LOCK_COUNT   0x80
+
+/* Extended Management Interface (EMI) Registers */
+#define I82579_EMI_ADDR                0x10
+#define I82579_EMI_DATA                0x11
+#define I82579_LPI_UPDATE_TIMER        0x4805  /* in 40ns units + 40 ns base value */
+#define I82579_MSE_THRESHOLD   0x084F  /* 82579 Mean Square Error Threshold */
+#define I82577_MSE_THRESHOLD   0x0887  /* 82577 Mean Square Error Threshold */
+#define I82579_MSE_LINK_DOWN   0x2411  /* MSE count before dropping link */
+#define I82579_EEE_PCS_STATUS  0x182D  /* IEEE MMD Register 3.1 >> 8 */
+#define I82579_EEE_CAPABILITY  0x0410  /* IEEE MMD Register 3.20 */
+#define I82579_EEE_ADVERTISEMENT       0x040E  /* IEEE MMD Register 7.60 */
+#define I82579_EEE_LP_ABILITY          0x040F  /* IEEE MMD Register 7.61 */
+#define I82579_EEE_100_SUPPORTED       (1 << 1) /* 100BaseTx EEE supported */
+#define I82579_EEE_1000_SUPPORTED      (1 << 2) /* 1000BaseTx EEE supported */
+#define I217_EEE_PCS_STATUS    0x9401  /* IEEE MMD Register 3.1 */
+#define I217_EEE_CAPABILITY    0x8000  /* IEEE MMD Register 3.20 */
+#define I217_EEE_ADVERTISEMENT 0x8001  /* IEEE MMD Register 7.60 */
+#define I217_EEE_LP_ABILITY    0x8002  /* IEEE MMD Register 7.61 */
+
+#define E1000_EEE_RX_LPI_RCVD  0x0400  /* Tx LP idle received */
+#define E1000_EEE_TX_LPI_RCVD  0x0800  /* Rx LP idle received */
 
 #define PHY_PAGE_SHIFT 5
 #define PHY_REG(page, reg) (((page) << PHY_PAGE_SHIFT) | \
index 3f8bbc31795c40256a8dfcd7f052efaec7801e91..b89b181da7b18c1f2a827986dff0d3f664be0691 100644 (file)
@@ -659,6 +659,7 @@ extern s32 e1000_check_polarity_ife(struct e1000_hw *hw);
 extern s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw);
 extern s32 e1000_check_polarity_igp(struct e1000_hw *hw);
 extern bool e1000_check_phy_82574(struct e1000_hw *hw);
+extern s32 e1000_read_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 *data);
 
 static inline s32 e1000_phy_hw_reset(struct e1000_hw *hw)
 {
index 2225603a8a280fb4eb7b76e231368f98e64c207b..636ba09ca6fb1f491a6577734f63e02e05e6feb9 100644 (file)
@@ -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)
index 06239fe47db1949b5008863ca76c77626a8f4453..81afad5b80f28e956079e9ce2fc9e749919e2990 100644 (file)
@@ -62,6 +62,7 @@ enum e1e_registers {
        E1000_IVAR     = 0x000E4, /* Interrupt Vector Allocation - RW */
        E1000_EITR_82574_BASE = 0x000E8, /* Interrupt Throttling - RW */
 #define E1000_EITR_82574(_n) (E1000_EITR_82574_BASE + (_n << 2))
+       E1000_LPIC     = 0x000FC, /* Low Power Idle Control - RW */
        E1000_RCTL     = 0x00100, /* Rx Control - RW */
        E1000_FCTTV    = 0x00170, /* Flow Control Transmit Timer Value - RW */
        E1000_TXCW     = 0x00178, /* Tx Configuration Word - RW */
index 051dfda75fc02df651022fa750270a0e259b6bb2..3829f7fd1d971bf56382fb3b3de697572d779cda 100644 (file)
 #define HV_PM_CTRL             PHY_REG(770, 17)
 #define HV_PM_CTRL_PLL_STOP_IN_K1_GIGA 0x100
 
-/* PHY Low Power Idle Control */
-#define I82579_LPI_CTRL                                PHY_REG(772, 20)
-#define I82579_LPI_CTRL_100_ENABLE             0x2000
-#define I82579_LPI_CTRL_1000_ENABLE            0x4000
-#define I82579_LPI_CTRL_ENABLE_MASK            0x6000
-#define I82579_LPI_CTRL_FORCE_PLL_LOCK_COUNT   0x80
-
-/* Extended Management Interface (EMI) Registers */
-#define I82579_EMI_ADDR         0x10
-#define I82579_EMI_DATA         0x11
-#define I82579_LPI_UPDATE_TIMER 0x4805 /* in 40ns units + 40 ns base value */
-#define I82579_MSE_THRESHOLD    0x084F /* 82579 Mean Square Error Threshold */
-#define I82577_MSE_THRESHOLD    0x0887 /* 82577 Mean Square Error Threshold */
-#define I82579_MSE_LINK_DOWN    0x2411 /* MSE count before dropping link */
-#define I82579_EEE_PCS_STATUS  0x182D  /* IEEE MMD Register 3.1 >> 8 */
-#define I82579_EEE_LP_ABILITY  0x040F  /* IEEE MMD Register 7.61 */
-#define I82579_EEE_100_SUPPORTED       (1 << 1) /* 100BaseTx EEE supported */
-#define I82579_EEE_1000_SUPPORTED      (1 << 2) /* 1000BaseTx EEE supported */
-#define I217_EEE_PCS_STATUS    0x9401  /* IEEE MMD Register 3.1 */
-#define I217_EEE_ADVERTISEMENT  0x8001 /* IEEE MMD Register 7.60 */
-#define I217_EEE_LP_ABILITY     0x8002 /* IEEE MMD Register 7.61 */
-
 /* Intel Rapid Start Technology Support */
 #define I217_PROXY_CTRL                 BM_PHY_REG(BM_WUC_PAGE, 70)
 #define I217_PROXY_CTRL_AUTO_DISABLE    0x0080
@@ -829,7 +807,7 @@ static s32 __e1000_access_emi_reg_locked(struct e1000_hw *hw, u16 address,
  *
  *  Assumes the SW/FW/HW Semaphore is already acquired.
  **/
-static s32 e1000_read_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 *data)
+s32 e1000_read_emi_reg_locked(struct e1000_hw *hw, u16 addr, u16 *data)
 {
        return __e1000_access_emi_reg_locked(hw, addr, data, true);
 }