Commit 93ec439a authored by Sasha Neftin's avatar Sasha Neftin Committed by Jeff Kirsher
Browse files

igc: Add initial EEE support



IEEE802.3az-2010 Energy Efficient Ethernet has been
approved as standard (September 2010) and the driver
can enable and disable it via ethtool.
Disable the feature by default on parts which support it.
Add enable/disable eee options.
tx-lpi, tx-timer and advertise not supported yet.

Signed-off-by: default avatarSasha Neftin <sasha.neftin@intel.com>
Reviewed-by: default avatarAndre Guedes <andre.guedes@intel.com>
Tested-by: default avatarAaron Brown <aaron.f.brown@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent b8483eca
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -117,6 +117,9 @@ struct igc_ring {
struct igc_adapter {
	struct net_device *netdev;

	struct ethtool_eee eee;
	u16 eee_advert;

	unsigned long state;
	unsigned int flags;
	unsigned int num_q_vectors;
@@ -255,6 +258,7 @@ extern char igc_driver_name[];
#define IGC_FLAG_MEDIA_RESET		BIT(10)
#define IGC_FLAG_MAS_ENABLE		BIT(12)
#define IGC_FLAG_HAS_MSIX		BIT(13)
#define IGC_FLAG_EEE			BIT(14)
#define IGC_FLAG_VLAN_PROMISC		BIT(15)
#define IGC_FLAG_RX_LEGACY		BIT(16)
#define IGC_FLAG_TSN_QBV_ENABLED	BIT(17)
+10 −0
Original line number Diff line number Diff line
@@ -511,4 +511,14 @@
/* Maximum size of the MTA register table in all supported adapters */
#define MAX_MTA_REG			128

/* EEE defines */
#define IGC_IPCNFG_EEE_2_5G_AN		0x00000010 /* IPCNFG EEE Ena 2.5G AN */
#define IGC_IPCNFG_EEE_1G_AN		0x00000008 /* IPCNFG EEE Ena 1G AN */
#define IGC_IPCNFG_EEE_100M_AN		0x00000004 /* IPCNFG EEE Ena 100M AN */
#define IGC_EEER_EEE_NEG		0x20000000 /* EEE capability nego */
#define IGC_EEER_TX_LPI_EN		0x00010000 /* EEER Tx LPI Enable */
#define IGC_EEER_RX_LPI_EN		0x00020000 /* EEER Rx LPI Enable */
#define IGC_EEER_LPI_FC			0x00040000 /* EEER Ena on Flow Cntrl */
#define IGC_EEE_SU_LPI_CLK_STP		0x00800000 /* EEE LPI Clock Stop */

#endif /* _IGC_DEFINES_H_ */
+95 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
/* ethtool support for igc */
#include <linux/if_vlan.h>
#include <linux/pm_runtime.h>
#include <linux/mdio.h>

#include "igc.h"
#include "igc_diag.h"
@@ -1548,6 +1549,98 @@ static int igc_ethtool_set_priv_flags(struct net_device *netdev, u32 priv_flags)
	return 0;
}

static int igc_ethtool_get_eee(struct net_device *netdev,
			       struct ethtool_eee *edata)
{
	struct igc_adapter *adapter = netdev_priv(netdev);
	struct igc_hw *hw = &adapter->hw;
	u32 eeer;

	if (hw->dev_spec._base.eee_enable)
		edata->advertised =
			mmd_eee_adv_to_ethtool_adv_t(adapter->eee_advert);

	*edata = adapter->eee;
	edata->supported = SUPPORTED_Autoneg;

	eeer = rd32(IGC_EEER);

	/* EEE status on negotiated link */
	if (eeer & IGC_EEER_EEE_NEG)
		edata->eee_active = true;

	if (eeer & IGC_EEER_TX_LPI_EN)
		edata->tx_lpi_enabled = true;

	edata->eee_enabled = hw->dev_spec._base.eee_enable;

	edata->advertised = SUPPORTED_Autoneg;
	edata->lp_advertised = SUPPORTED_Autoneg;

	/* Report correct negotiated EEE status for devices that
	 * wrongly report EEE at half-duplex
	 */
	if (adapter->link_duplex == HALF_DUPLEX) {
		edata->eee_enabled = false;
		edata->eee_active = false;
		edata->tx_lpi_enabled = false;
		edata->advertised &= ~edata->advertised;
	}

	return 0;
}

static int igc_ethtool_set_eee(struct net_device *netdev,
			       struct ethtool_eee *edata)
{
	struct igc_adapter *adapter = netdev_priv(netdev);
	struct igc_hw *hw = &adapter->hw;
	struct ethtool_eee eee_curr;
	s32 ret_val;

	memset(&eee_curr, 0, sizeof(struct ethtool_eee));

	ret_val = igc_ethtool_get_eee(netdev, &eee_curr);
	if (ret_val) {
		netdev_err(netdev,
			   "Problem setting EEE advertisement options\n");
		return -EINVAL;
	}

	if (eee_curr.eee_enabled) {
		if (eee_curr.tx_lpi_enabled != edata->tx_lpi_enabled) {
			netdev_err(netdev,
				   "Setting EEE tx-lpi is not supported\n");
			return -EINVAL;
		}

		/* Tx LPI timer is not implemented currently */
		if (edata->tx_lpi_timer) {
			netdev_err(netdev,
				   "Setting EEE Tx LPI timer is not supported\n");
			return -EINVAL;
		}
	} else if (!edata->eee_enabled) {
		netdev_err(netdev,
			   "Setting EEE options are not supported with EEE disabled\n");
		return -EINVAL;
	}

	adapter->eee_advert = ethtool_adv_to_mmd_eee_adv_t(edata->advertised);
	if (hw->dev_spec._base.eee_enable != edata->eee_enabled) {
		hw->dev_spec._base.eee_enable = edata->eee_enabled;
		adapter->flags |= IGC_FLAG_EEE;

		/* reset link */
		if (netif_running(netdev))
			igc_reinit_locked(adapter);
		else
			igc_reset(adapter);
	}

	return 0;
}

static int igc_ethtool_begin(struct net_device *netdev)
{
	struct igc_adapter *adapter = netdev_priv(netdev);
@@ -1829,6 +1922,8 @@ static const struct ethtool_ops igc_ethtool_ops = {
	.set_channels		= igc_ethtool_set_channels,
	.get_priv_flags		= igc_ethtool_get_priv_flags,
	.set_priv_flags		= igc_ethtool_set_priv_flags,
	.get_eee		= igc_ethtool_get_eee,
	.set_eee		= igc_ethtool_set_eee,
	.begin			= igc_ethtool_begin,
	.complete		= igc_ethtool_complete,
	.get_link_ksettings	= igc_ethtool_get_link_ksettings,
+1 −0
Original line number Diff line number Diff line
@@ -191,6 +191,7 @@ struct igc_fc_info {

struct igc_dev_spec_base {
	bool clear_semaphore_once;
	bool eee_enable;
};

struct igc_hw {
+56 −0
Original line number Diff line number Diff line
@@ -488,3 +488,59 @@ s32 igc_init_nvm_params_i225(struct igc_hw *hw)
	}
	return 0;
}

/**
 *  igc_set_eee_i225 - Enable/disable EEE support
 *  @hw: pointer to the HW structure
 *  @adv2p5G: boolean flag enabling 2.5G EEE advertisement
 *  @adv1G: boolean flag enabling 1G EEE advertisement
 *  @adv100M: boolean flag enabling 100M EEE advertisement
 *
 *  Enable/disable EEE based on setting in dev_spec structure.
 **/
s32 igc_set_eee_i225(struct igc_hw *hw, bool adv2p5G, bool adv1G,
		     bool adv100M)
{
	u32 ipcnfg, eeer;

	ipcnfg = rd32(IGC_IPCNFG);
	eeer = rd32(IGC_EEER);

	/* enable or disable per user setting */
	if (hw->dev_spec._base.eee_enable) {
		u32 eee_su = rd32(IGC_EEE_SU);

		if (adv100M)
			ipcnfg |= IGC_IPCNFG_EEE_100M_AN;
		else
			ipcnfg &= ~IGC_IPCNFG_EEE_100M_AN;

		if (adv1G)
			ipcnfg |= IGC_IPCNFG_EEE_1G_AN;
		else
			ipcnfg &= ~IGC_IPCNFG_EEE_1G_AN;

		if (adv2p5G)
			ipcnfg |= IGC_IPCNFG_EEE_2_5G_AN;
		else
			ipcnfg &= ~IGC_IPCNFG_EEE_2_5G_AN;

		eeer |= (IGC_EEER_TX_LPI_EN | IGC_EEER_RX_LPI_EN |
			 IGC_EEER_LPI_FC);

		/* This bit should not be set in normal operation. */
		if (eee_su & IGC_EEE_SU_LPI_CLK_STP)
			hw_dbg("LPI Clock Stop Bit should not be set!\n");
	} else {
		ipcnfg &= ~(IGC_IPCNFG_EEE_2_5G_AN | IGC_IPCNFG_EEE_1G_AN |
			    IGC_IPCNFG_EEE_100M_AN);
		eeer &= ~(IGC_EEER_TX_LPI_EN | IGC_EEER_RX_LPI_EN |
			  IGC_EEER_LPI_FC);
	}
	wr32(IGC_IPCNFG, ipcnfg);
	wr32(IGC_EEER, eeer);
	rd32(IGC_IPCNFG);
	rd32(IGC_EEER);

	return IGC_SUCCESS;
}
Loading