igc: Add initial EEE support
authorSasha Neftin <sasha.neftin@intel.com>
Wed, 27 May 2020 20:51:32 +0000 (13:51 -0700)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Tue, 30 Jun 2020 00:43:38 +0000 (17:43 -0700)
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: Sasha Neftin <sasha.neftin@intel.com>
Reviewed-by: Andre Guedes <andre.guedes@intel.com>
Tested-by: Aaron Brown <aaron.f.brown@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/igc/igc.h
drivers/net/ethernet/intel/igc/igc_defines.h
drivers/net/ethernet/intel/igc/igc_ethtool.c
drivers/net/ethernet/intel/igc/igc_hw.h
drivers/net/ethernet/intel/igc/igc_i225.c
drivers/net/ethernet/intel/igc/igc_i225.h
drivers/net/ethernet/intel/igc/igc_main.c
drivers/net/ethernet/intel/igc/igc_regs.h

index a2d260165df34a500d305ffb688f17df8d0da0c9..9c57afad6afec0d19079f9123f2978dcd7a19492 100644 (file)
@@ -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)
index 186deb1d9375c4520340bf0ee41663ed43ccf0ae..ee7fa1c062a0ed77d9ab156988cf76f005c7968a 100644 (file)
 /* 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_ */
index 735f3fb47dcab55b2b47fe64b7b18d785d0d70cb..ac331116ea084d8c7d3d80a423cb6bc332b2a170 100644 (file)
@@ -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,
index af34ae3103273c3d0e9410ec98be872163a64bf7..2ab7d9fab6af8ca2cd1904f54decb030553669dc 100644 (file)
@@ -191,6 +191,7 @@ struct igc_fc_info {
 
 struct igc_dev_spec_base {
        bool clear_semaphore_once;
+       bool eee_enable;
 };
 
 struct igc_hw {
index c25f555aaf82299f74cabb31a7a52fdc846312a3..3a4e982edb67255988ce810e8a41cfac44801525 100644 (file)
@@ -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;
+}
index 7b66e1f9c0e637da5b56c2849e7441e3af1aa20e..04759e076a9ea5ff096a97d10ab4376ecf9d87eb 100644 (file)
@@ -9,5 +9,7 @@ void igc_release_swfw_sync_i225(struct igc_hw *hw, u16 mask);
 
 s32 igc_init_nvm_params_i225(struct igc_hw *hw);
 bool igc_get_flash_presence_i225(struct igc_hw *hw);
+s32 igc_set_eee_i225(struct igc_hw *hw, bool adv2p5G, bool adv1G,
+                    bool adv100M);
 
 #endif
index c2f41a558fd6fd060ddc15df367c94d8901e5e75..7e4d56c7b4c44d96444df4264ae2bd47585e1216 100644 (file)
@@ -102,6 +102,9 @@ void igc_reset(struct igc_adapter *adapter)
        if (hw->mac.ops.init_hw(hw))
                netdev_err(dev, "Error on hardware initialization\n");
 
+       /* Re-establish EEE setting */
+       igc_set_eee_i225(hw, true, true, true);
+
        if (!netif_running(adapter->netdev))
                igc_power_down_link(adapter);
 
@@ -4252,6 +4255,15 @@ static void igc_watchdog_task(struct work_struct *work)
                                    (ctrl & IGC_CTRL_RFCE) ?  "RX" :
                                    (ctrl & IGC_CTRL_TFCE) ?  "TX" : "None");
 
+                       /* disable EEE if enabled */
+                       if ((adapter->flags & IGC_FLAG_EEE) &&
+                           adapter->link_duplex == HALF_DUPLEX) {
+                               netdev_info(netdev,
+                                           "EEE Disabled: unsupported at half duplex. Re-enable using ethtool when at full duplex\n");
+                               adapter->hw.dev_spec._base.eee_enable = false;
+                               adapter->flags &= ~IGC_FLAG_EEE;
+                       }
+
                        /* check if SmartSpeed worked */
                        igc_check_downshift(hw);
                        if (phy->speed_downgraded)
@@ -5182,6 +5194,10 @@ static int igc_probe(struct pci_dev *pdev,
        netdev_info(netdev, "MAC: %pM\n", netdev->dev_addr);
 
        dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE);
+       /* Disable EEE for internal PHY devices */
+       hw->dev_spec._base.eee_enable = false;
+       adapter->flags &= ~IGC_FLAG_EEE;
+       igc_set_eee_i225(hw, false, false, false);
 
        pm_runtime_put_noidle(&pdev->dev);
 
index 232e82dec62e39c5f8b0d4d0bbfb0afeee146f4e..75e040a5d46f76cdcecf9a8487f7e94313ac22cc 100644 (file)
 /* Wake Up packet memory */
 #define IGC_WUPM_REG(_i)       (0x05A00 + ((_i) * 4))
 
+/* Energy Efficient Ethernet "EEE" registers */
+#define IGC_EEER       0x0E30 /* Energy Efficient Ethernet "EEE"*/
+#define IGC_IPCNFG     0x0E38 /* Internal PHY Configuration */
+#define IGC_EEE_SU     0x0E34 /* EEE Setup */
+
 /* forward declaration */
 struct igc_hw;
 u32 igc_rd32(struct igc_hw *hw, u32 reg);