Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 203e4151 authored by Bruce Allan's avatar Bruce Allan Committed by Jeff Kirsher
Browse files

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: default avatarBruce Allan <bruce.w.allan@intel.com>
Tested-by: default avatarJeff Pieper <jeffrey.e.pieper@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 887c95cc
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -383,6 +383,9 @@

#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 */
@@ -799,6 +802,33 @@
/* 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) | \
+1 −0
Original line number Diff line number Diff line
@@ -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)
{
+134 −0
Original line number Diff line number Diff line
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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 */
+1 −23
Original line number Diff line number Diff line
@@ -148,28 +148,6 @@
#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);
}