]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[mirror_ubuntu-artful-kernel.git] / drivers / net / ethernet / broadcom / bnxt / bnxt_ethtool.c
index 2e472f6dbf2d3df1ef3d1e084666ef713a187e65..b83e17403d6c6257b81a4d1b710175a13268d11c 100644 (file)
@@ -1,6 +1,6 @@
 /* Broadcom NetXtreme-C/E network driver.
  *
- * Copyright (c) 2014-2015 Broadcom Corporation
+ * Copyright (c) 2014-2016 Broadcom Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -56,6 +56,8 @@ static int bnxt_get_coalesce(struct net_device *dev,
        coal->tx_coalesce_usecs_irq = bp->tx_coal_ticks_irq;
        coal->tx_max_coalesced_frames_irq = bp->tx_coal_bufs_irq;
 
+       coal->stats_block_coalesce_usecs = bp->stats_coal_ticks;
+
        return 0;
 }
 
@@ -63,6 +65,7 @@ static int bnxt_set_coalesce(struct net_device *dev,
                             struct ethtool_coalesce *coal)
 {
        struct bnxt *bp = netdev_priv(dev);
+       bool update_stats = false;
        int rc = 0;
 
        bp->rx_coal_ticks = coal->rx_coalesce_usecs;
@@ -76,8 +79,26 @@ static int bnxt_set_coalesce(struct net_device *dev,
        bp->tx_coal_ticks_irq = coal->tx_coalesce_usecs_irq;
        bp->tx_coal_bufs_irq = coal->tx_max_coalesced_frames_irq;
 
-       if (netif_running(dev))
-               rc = bnxt_hwrm_set_coal(bp);
+       if (bp->stats_coal_ticks != coal->stats_block_coalesce_usecs) {
+               u32 stats_ticks = coal->stats_block_coalesce_usecs;
+
+               stats_ticks = clamp_t(u32, stats_ticks,
+                                     BNXT_MIN_STATS_COAL_TICKS,
+                                     BNXT_MAX_STATS_COAL_TICKS);
+               stats_ticks = rounddown(stats_ticks, BNXT_MIN_STATS_COAL_TICKS);
+               bp->stats_coal_ticks = stats_ticks;
+               update_stats = true;
+       }
+
+       if (netif_running(dev)) {
+               if (update_stats) {
+                       rc = bnxt_close_nic(bp, true, false);
+                       if (!rc)
+                               rc = bnxt_open_nic(bp, true, false);
+               } else {
+                       rc = bnxt_hwrm_set_coal(bp);
+               }
+       }
 
        return rc;
 }
@@ -327,7 +348,11 @@ static void bnxt_get_channels(struct net_device *dev,
        bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, true);
        channel->max_combined = max_rx_rings;
 
-       bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, false);
+       if (bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, false)) {
+               max_rx_rings = 0;
+               max_tx_rings = 0;
+       }
+
        tcs = netdev_get_num_tc(dev);
        if (tcs > 1)
                max_tx_rings /= tcs;
@@ -337,9 +362,13 @@ static void bnxt_get_channels(struct net_device *dev,
        channel->max_other = 0;
        if (bp->flags & BNXT_FLAG_SHARED_RINGS) {
                channel->combined_count = bp->rx_nr_rings;
+               if (BNXT_CHIP_TYPE_NITRO_A0(bp))
+                       channel->combined_count--;
        } else {
-               channel->rx_count = bp->rx_nr_rings;
-               channel->tx_count = bp->tx_nr_rings_per_tc;
+               if (!BNXT_CHIP_TYPE_NITRO_A0(bp)) {
+                       channel->rx_count = bp->rx_nr_rings;
+                       channel->tx_count = bp->tx_nr_rings_per_tc;
+               }
        }
 }
 
@@ -362,6 +391,10 @@ static int bnxt_set_channels(struct net_device *dev,
            (channel->rx_count || channel->tx_count))
                return -EINVAL;
 
+       if (BNXT_CHIP_TYPE_NITRO_A0(bp) && (channel->rx_count ||
+                                           channel->tx_count))
+               return -EINVAL;
+
        if (channel->combined_count)
                sh = true;
 
@@ -597,7 +630,7 @@ static void bnxt_get_drvinfo(struct net_device *dev,
        kfree(pkglog);
 }
 
-static u32 _bnxt_fw_to_ethtool_adv_spds(u16 fw_speeds, u8 fw_pause)
+u32 _bnxt_fw_to_ethtool_adv_spds(u16 fw_speeds, u8 fw_pause)
 {
        u32 speed_mask = 0;
 
@@ -624,7 +657,66 @@ static u32 _bnxt_fw_to_ethtool_adv_spds(u16 fw_speeds, u8 fw_pause)
        return speed_mask;
 }
 
-static u32 bnxt_fw_to_ethtool_advertised_spds(struct bnxt_link_info *link_info)
+#define BNXT_FW_TO_ETHTOOL_SPDS(fw_speeds, fw_pause, lk_ksettings, name)\
+{                                                                      \
+       if ((fw_speeds) & BNXT_LINK_SPEED_MSK_100MB)                    \
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, name,\
+                                                    100baseT_Full);    \
+       if ((fw_speeds) & BNXT_LINK_SPEED_MSK_1GB)                      \
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, name,\
+                                                    1000baseT_Full);   \
+       if ((fw_speeds) & BNXT_LINK_SPEED_MSK_10GB)                     \
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, name,\
+                                                    10000baseT_Full);  \
+       if ((fw_speeds) & BNXT_LINK_SPEED_MSK_25GB)                     \
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, name,\
+                                                    25000baseCR_Full); \
+       if ((fw_speeds) & BNXT_LINK_SPEED_MSK_40GB)                     \
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, name,\
+                                                    40000baseCR4_Full);\
+       if ((fw_speeds) & BNXT_LINK_SPEED_MSK_50GB)                     \
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, name,\
+                                                    50000baseCR2_Full);\
+       if ((fw_pause) & BNXT_LINK_PAUSE_RX) {                          \
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, name,\
+                                                    Pause);            \
+               if (!((fw_pause) & BNXT_LINK_PAUSE_TX))                 \
+                       ethtool_link_ksettings_add_link_mode(           \
+                                       lk_ksettings, name, Asym_Pause);\
+       } else if ((fw_pause) & BNXT_LINK_PAUSE_TX) {                   \
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, name,\
+                                                    Asym_Pause);       \
+       }                                                               \
+}
+
+#define BNXT_ETHTOOL_TO_FW_SPDS(fw_speeds, lk_ksettings, name)         \
+{                                                                      \
+       if (ethtool_link_ksettings_test_link_mode(lk_ksettings, name,   \
+                                                 100baseT_Full) ||     \
+           ethtool_link_ksettings_test_link_mode(lk_ksettings, name,   \
+                                                 100baseT_Half))       \
+               (fw_speeds) |= BNXT_LINK_SPEED_MSK_100MB;               \
+       if (ethtool_link_ksettings_test_link_mode(lk_ksettings, name,   \
+                                                 1000baseT_Full) ||    \
+           ethtool_link_ksettings_test_link_mode(lk_ksettings, name,   \
+                                                 1000baseT_Half))      \
+               (fw_speeds) |= BNXT_LINK_SPEED_MSK_1GB;                 \
+       if (ethtool_link_ksettings_test_link_mode(lk_ksettings, name,   \
+                                                 10000baseT_Full))     \
+               (fw_speeds) |= BNXT_LINK_SPEED_MSK_10GB;                \
+       if (ethtool_link_ksettings_test_link_mode(lk_ksettings, name,   \
+                                                 25000baseCR_Full))    \
+               (fw_speeds) |= BNXT_LINK_SPEED_MSK_25GB;                \
+       if (ethtool_link_ksettings_test_link_mode(lk_ksettings, name,   \
+                                                 40000baseCR4_Full))   \
+               (fw_speeds) |= BNXT_LINK_SPEED_MSK_40GB;                \
+       if (ethtool_link_ksettings_test_link_mode(lk_ksettings, name,   \
+                                                 50000baseCR2_Full))   \
+               (fw_speeds) |= BNXT_LINK_SPEED_MSK_50GB;                \
+}
+
+static void bnxt_fw_to_ethtool_advertised_spds(struct bnxt_link_info *link_info,
+                               struct ethtool_link_ksettings *lk_ksettings)
 {
        u16 fw_speeds = link_info->auto_link_speeds;
        u8 fw_pause = 0;
@@ -632,10 +724,11 @@ static u32 bnxt_fw_to_ethtool_advertised_spds(struct bnxt_link_info *link_info)
        if (link_info->autoneg & BNXT_AUTONEG_FLOW_CTRL)
                fw_pause = link_info->auto_pause_setting;
 
-       return _bnxt_fw_to_ethtool_adv_spds(fw_speeds, fw_pause);
+       BNXT_FW_TO_ETHTOOL_SPDS(fw_speeds, fw_pause, lk_ksettings, advertising);
 }
 
-static u32 bnxt_fw_to_ethtool_lp_adv(struct bnxt_link_info *link_info)
+static void bnxt_fw_to_ethtool_lp_adv(struct bnxt_link_info *link_info,
+                               struct ethtool_link_ksettings *lk_ksettings)
 {
        u16 fw_speeds = link_info->lp_auto_link_speeds;
        u8 fw_pause = 0;
@@ -643,16 +736,24 @@ static u32 bnxt_fw_to_ethtool_lp_adv(struct bnxt_link_info *link_info)
        if (link_info->autoneg & BNXT_AUTONEG_FLOW_CTRL)
                fw_pause = link_info->lp_pause;
 
-       return _bnxt_fw_to_ethtool_adv_spds(fw_speeds, fw_pause);
+       BNXT_FW_TO_ETHTOOL_SPDS(fw_speeds, fw_pause, lk_ksettings,
+                               lp_advertising);
 }
 
-static u32 bnxt_fw_to_ethtool_support_spds(struct bnxt_link_info *link_info)
+static void bnxt_fw_to_ethtool_support_spds(struct bnxt_link_info *link_info,
+                               struct ethtool_link_ksettings *lk_ksettings)
 {
        u16 fw_speeds = link_info->support_speeds;
-       u32 supported;
 
-       supported = _bnxt_fw_to_ethtool_adv_spds(fw_speeds, 0);
-       return supported | SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+       BNXT_FW_TO_ETHTOOL_SPDS(fw_speeds, 0, lk_ksettings, supported);
+
+       ethtool_link_ksettings_add_link_mode(lk_ksettings, supported, Pause);
+       ethtool_link_ksettings_add_link_mode(lk_ksettings, supported,
+                                            Asym_Pause);
+
+       if (link_info->support_auto_speeds)
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, supported,
+                                                    Autoneg);
 }
 
 u32 bnxt_fw_to_ethtool_speed(u16 fw_link_speed)
@@ -679,91 +780,114 @@ u32 bnxt_fw_to_ethtool_speed(u16 fw_link_speed)
        }
 }
 
-static int bnxt_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+static int bnxt_get_link_ksettings(struct net_device *dev,
+                                  struct ethtool_link_ksettings *lk_ksettings)
 {
        struct bnxt *bp = netdev_priv(dev);
        struct bnxt_link_info *link_info = &bp->link_info;
-       u16 ethtool_speed;
-
-       cmd->supported = bnxt_fw_to_ethtool_support_spds(link_info);
+       struct ethtool_link_settings *base = &lk_ksettings->base;
+       u32 ethtool_speed;
 
-       if (link_info->auto_link_speeds)
-               cmd->supported |= SUPPORTED_Autoneg;
+       ethtool_link_ksettings_zero_link_mode(lk_ksettings, supported);
+       bnxt_fw_to_ethtool_support_spds(link_info, lk_ksettings);
 
+       ethtool_link_ksettings_zero_link_mode(lk_ksettings, advertising);
        if (link_info->autoneg) {
-               cmd->advertising =
-                       bnxt_fw_to_ethtool_advertised_spds(link_info);
-               cmd->advertising |= ADVERTISED_Autoneg;
-               cmd->autoneg = AUTONEG_ENABLE;
+               bnxt_fw_to_ethtool_advertised_spds(link_info, lk_ksettings);
+               ethtool_link_ksettings_add_link_mode(lk_ksettings,
+                                                    advertising, Autoneg);
+               base->autoneg = AUTONEG_ENABLE;
                if (link_info->phy_link_status == BNXT_LINK_LINK)
-                       cmd->lp_advertising =
-                               bnxt_fw_to_ethtool_lp_adv(link_info);
+                       bnxt_fw_to_ethtool_lp_adv(link_info, lk_ksettings);
+               ethtool_speed = bnxt_fw_to_ethtool_speed(link_info->link_speed);
+               if (!netif_carrier_ok(dev))
+                       base->duplex = DUPLEX_UNKNOWN;
+               else if (link_info->duplex & BNXT_LINK_DUPLEX_FULL)
+                       base->duplex = DUPLEX_FULL;
+               else
+                       base->duplex = DUPLEX_HALF;
        } else {
-               cmd->autoneg = AUTONEG_DISABLE;
-               cmd->advertising = 0;
+               base->autoneg = AUTONEG_DISABLE;
+               ethtool_speed =
+                       bnxt_fw_to_ethtool_speed(link_info->req_link_speed);
+               base->duplex = DUPLEX_HALF;
+               if (link_info->req_duplex == BNXT_LINK_DUPLEX_FULL)
+                       base->duplex = DUPLEX_FULL;
        }
+       base->speed = ethtool_speed;
 
-       cmd->port = PORT_NONE;
+       base->port = PORT_NONE;
        if (link_info->media_type == PORT_PHY_QCFG_RESP_MEDIA_TYPE_TP) {
-               cmd->port = PORT_TP;
-               cmd->supported |= SUPPORTED_TP;
-               cmd->advertising |= ADVERTISED_TP;
+               base->port = PORT_TP;
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, supported,
+                                                    TP);
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, advertising,
+                                                    TP);
        } else {
-               cmd->supported |= SUPPORTED_FIBRE;
-               cmd->advertising |= ADVERTISED_FIBRE;
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, supported,
+                                                    FIBRE);
+               ethtool_link_ksettings_add_link_mode(lk_ksettings, advertising,
+                                                    FIBRE);
 
                if (link_info->media_type == PORT_PHY_QCFG_RESP_MEDIA_TYPE_DAC)
-                       cmd->port = PORT_DA;
+                       base->port = PORT_DA;
                else if (link_info->media_type ==
                         PORT_PHY_QCFG_RESP_MEDIA_TYPE_FIBRE)
-                       cmd->port = PORT_FIBRE;
+                       base->port = PORT_FIBRE;
        }
-
-       if (link_info->phy_link_status == BNXT_LINK_LINK) {
-               if (link_info->duplex & BNXT_LINK_DUPLEX_FULL)
-                       cmd->duplex = DUPLEX_FULL;
-       } else {
-               cmd->duplex = DUPLEX_UNKNOWN;
-       }
-       ethtool_speed = bnxt_fw_to_ethtool_speed(link_info->link_speed);
-       ethtool_cmd_speed_set(cmd, ethtool_speed);
-       if (link_info->transceiver ==
-               PORT_PHY_QCFG_RESP_TRANSCEIVER_TYPE_XCVR_INTERNAL)
-               cmd->transceiver = XCVR_INTERNAL;
-       else
-               cmd->transceiver = XCVR_EXTERNAL;
-       cmd->phy_address = link_info->phy_addr;
+       base->phy_address = link_info->phy_addr;
 
        return 0;
 }
 
 static u32 bnxt_get_fw_speed(struct net_device *dev, u16 ethtool_speed)
 {
+       struct bnxt *bp = netdev_priv(dev);
+       struct bnxt_link_info *link_info = &bp->link_info;
+       u16 support_spds = link_info->support_speeds;
+       u32 fw_speed = 0;
+
        switch (ethtool_speed) {
        case SPEED_100:
-               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_100MB;
+               if (support_spds & BNXT_LINK_SPEED_MSK_100MB)
+                       fw_speed = PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_100MB;
+               break;
        case SPEED_1000:
-               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_1GB;
+               if (support_spds & BNXT_LINK_SPEED_MSK_1GB)
+                       fw_speed = PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_1GB;
+               break;
        case SPEED_2500:
-               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_2_5GB;
+               if (support_spds & BNXT_LINK_SPEED_MSK_2_5GB)
+                       fw_speed = PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_2_5GB;
+               break;
        case SPEED_10000:
-               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_10GB;
+               if (support_spds & BNXT_LINK_SPEED_MSK_10GB)
+                       fw_speed = PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_10GB;
+               break;
        case SPEED_20000:
-               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_20GB;
+               if (support_spds & BNXT_LINK_SPEED_MSK_20GB)
+                       fw_speed = PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_20GB;
+               break;
        case SPEED_25000:
-               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_25GB;
+               if (support_spds & BNXT_LINK_SPEED_MSK_25GB)
+                       fw_speed = PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_25GB;
+               break;
        case SPEED_40000:
-               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_40GB;
+               if (support_spds & BNXT_LINK_SPEED_MSK_40GB)
+                       fw_speed = PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_40GB;
+               break;
        case SPEED_50000:
-               return PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_50GB;
+               if (support_spds & BNXT_LINK_SPEED_MSK_50GB)
+                       fw_speed = PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_50GB;
+               break;
        default:
                netdev_err(dev, "unsupported speed!\n");
                break;
        }
-       return 0;
+       return fw_speed;
 }
 
-static u16 bnxt_get_fw_auto_link_speeds(u32 advertising)
+u16 bnxt_get_fw_auto_link_speeds(u32 advertising)
 {
        u16 fw_speed_mask = 0;
 
@@ -785,37 +909,25 @@ static u16 bnxt_get_fw_auto_link_speeds(u32 advertising)
        return fw_speed_mask;
 }
 
-static int bnxt_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+static int bnxt_set_link_ksettings(struct net_device *dev,
+                          const struct ethtool_link_ksettings *lk_ksettings)
 {
-       int rc = 0;
        struct bnxt *bp = netdev_priv(dev);
        struct bnxt_link_info *link_info = &bp->link_info;
+       const struct ethtool_link_settings *base = &lk_ksettings->base;
        u32 speed, fw_advertising = 0;
        bool set_pause = false;
+       int rc = 0;
 
-       if (BNXT_VF(bp))
-               return rc;
-
-       if (cmd->autoneg == AUTONEG_ENABLE) {
-               u32 supported_spds = bnxt_fw_to_ethtool_support_spds(link_info);
+       if (!BNXT_SINGLE_PF(bp))
+               return -EOPNOTSUPP;
 
-               if (cmd->advertising & ~(supported_spds | ADVERTISED_Autoneg |
-                                        ADVERTISED_TP | ADVERTISED_FIBRE)) {
-                       netdev_err(dev, "Unsupported advertising mask (adv: 0x%x)\n",
-                                  cmd->advertising);
-                       rc = -EINVAL;
-                       goto set_setting_exit;
-               }
-               fw_advertising = bnxt_get_fw_auto_link_speeds(cmd->advertising);
-               if (fw_advertising & ~link_info->support_speeds) {
-                       netdev_err(dev, "Advertising parameters are not supported! (adv: 0x%x)\n",
-                                  cmd->advertising);
-                       rc = -EINVAL;
-                       goto set_setting_exit;
-               }
+       if (base->autoneg == AUTONEG_ENABLE) {
+               BNXT_ETHTOOL_TO_FW_SPDS(fw_advertising, lk_ksettings,
+                                       advertising);
                link_info->autoneg |= BNXT_AUTONEG_SPEED;
                if (!fw_advertising)
-                       link_info->advertising = link_info->support_speeds;
+                       link_info->advertising = link_info->support_auto_speeds;
                else
                        link_info->advertising = fw_advertising;
                /* any change to autoneg will cause link change, therefore the
@@ -823,24 +935,35 @@ static int bnxt_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                 */
                set_pause = true;
        } else {
-               /* TODO: currently don't support half duplex */
-               if (cmd->duplex == DUPLEX_HALF) {
+               u16 fw_speed;
+               u8 phy_type = link_info->phy_type;
+
+               if (phy_type == PORT_PHY_QCFG_RESP_PHY_TYPE_BASET  ||
+                   phy_type == PORT_PHY_QCFG_RESP_PHY_TYPE_BASETE ||
+                   link_info->media_type == PORT_PHY_QCFG_RESP_MEDIA_TYPE_TP) {
+                       netdev_err(dev, "10GBase-T devices must autoneg\n");
+                       rc = -EINVAL;
+                       goto set_setting_exit;
+               }
+               if (base->duplex == DUPLEX_HALF) {
                        netdev_err(dev, "HALF DUPLEX is not supported!\n");
                        rc = -EINVAL;
                        goto set_setting_exit;
                }
-               /* If received a request for an unknown duplex, assume full*/
-               if (cmd->duplex == DUPLEX_UNKNOWN)
-                       cmd->duplex = DUPLEX_FULL;
-               speed = ethtool_cmd_speed(cmd);
-               link_info->req_link_speed = bnxt_get_fw_speed(dev, speed);
+               speed = base->speed;
+               fw_speed = bnxt_get_fw_speed(dev, speed);
+               if (!fw_speed) {
+                       rc = -EINVAL;
+                       goto set_setting_exit;
+               }
+               link_info->req_link_speed = fw_speed;
                link_info->req_duplex = BNXT_LINK_DUPLEX_FULL;
                link_info->autoneg = 0;
                link_info->advertising = 0;
        }
 
        if (netif_running(dev))
-               rc = bnxt_hwrm_set_link_setting(bp, set_pause);
+               rc = bnxt_hwrm_set_link_setting(bp, set_pause, false);
 
 set_setting_exit:
        return rc;
@@ -866,15 +989,17 @@ static int bnxt_set_pauseparam(struct net_device *dev,
        struct bnxt *bp = netdev_priv(dev);
        struct bnxt_link_info *link_info = &bp->link_info;
 
-       if (BNXT_VF(bp))
-               return rc;
+       if (!BNXT_SINGLE_PF(bp))
+               return -EOPNOTSUPP;
 
        if (epause->autoneg) {
                if (!(link_info->autoneg & BNXT_AUTONEG_SPEED))
                        return -EINVAL;
 
                link_info->autoneg |= BNXT_AUTONEG_FLOW_CTRL;
-               link_info->req_flow_ctrl |= BNXT_LINK_PAUSE_BOTH;
+               if (bp->hwrm_spec_code >= 0x10201)
+                       link_info->req_flow_ctrl =
+                               PORT_PHY_CFG_REQ_AUTO_PAUSE_AUTONEG_PAUSE;
        } else {
                /* when transition from auto pause to force pause,
                 * force a link change
@@ -882,17 +1007,13 @@ static int bnxt_set_pauseparam(struct net_device *dev,
                if (link_info->autoneg & BNXT_AUTONEG_FLOW_CTRL)
                        link_info->force_link_chng = true;
                link_info->autoneg &= ~BNXT_AUTONEG_FLOW_CTRL;
-               link_info->req_flow_ctrl &= ~BNXT_LINK_PAUSE_BOTH;
+               link_info->req_flow_ctrl = 0;
        }
        if (epause->rx_pause)
                link_info->req_flow_ctrl |= BNXT_LINK_PAUSE_RX;
-       else
-               link_info->req_flow_ctrl &= ~BNXT_LINK_PAUSE_RX;
 
        if (epause->tx_pause)
                link_info->req_flow_ctrl |= BNXT_LINK_PAUSE_TX;
-       else
-               link_info->req_flow_ctrl &= ~BNXT_LINK_PAUSE_TX;
 
        if (netif_running(dev))
                rc = bnxt_hwrm_set_pause(bp);
@@ -967,6 +1088,8 @@ static int bnxt_firmware_reset(struct net_device *dev,
        case BNX_DIR_TYPE_APE_FW:
        case BNX_DIR_TYPE_APE_PATCH:
                req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT;
+               /* Self-reset APE upon next PCIe reset: */
+               req.selfrst_status = FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST;
                break;
        case BNX_DIR_TYPE_KONG_FW:
        case BNX_DIR_TYPE_KONG_PATCH:
@@ -1000,9 +1123,27 @@ static int bnxt_flash_firmware(struct net_device *dev,
        case BNX_DIR_TYPE_BOOTCODE_2:
                code_type = CODE_BOOT;
                break;
+       case BNX_DIR_TYPE_CHIMP_PATCH:
+               code_type = CODE_CHIMP_PATCH;
+               break;
        case BNX_DIR_TYPE_APE_FW:
                code_type = CODE_MCTP_PASSTHRU;
                break;
+       case BNX_DIR_TYPE_APE_PATCH:
+               code_type = CODE_APE_PATCH;
+               break;
+       case BNX_DIR_TYPE_KONG_FW:
+               code_type = CODE_KONG_FW;
+               break;
+       case BNX_DIR_TYPE_KONG_PATCH:
+               code_type = CODE_KONG_PATCH;
+               break;
+       case BNX_DIR_TYPE_BONO_FW:
+               code_type = CODE_BONO_FW;
+               break;
+       case BNX_DIR_TYPE_BONO_PATCH:
+               code_type = CODE_BONO_PATCH;
+               break;
        default:
                netdev_err(dev, "Unsupported directory entry type: %u\n",
                           dir_type);
@@ -1057,6 +1198,8 @@ static bool bnxt_dir_type_is_ape_bin_format(u16 dir_type)
        case BNX_DIR_TYPE_APE_PATCH:
        case BNX_DIR_TYPE_KONG_FW:
        case BNX_DIR_TYPE_KONG_PATCH:
+       case BNX_DIR_TYPE_BONO_FW:
+       case BNX_DIR_TYPE_BONO_PATCH:
                return true;
        }
 
@@ -1094,7 +1237,8 @@ static int bnxt_flash_firmware_from_file(struct net_device *dev,
        const struct firmware  *fw;
        int                     rc;
 
-       if (bnxt_dir_type_is_executable(dir_type) == false)
+       if (dir_type != BNX_DIR_TYPE_UPDATE &&
+           bnxt_dir_type_is_executable(dir_type) == false)
                return -EINVAL;
 
        rc = request_firmware(&fw, filename, &dev->dev);
@@ -1381,9 +1525,202 @@ static int bnxt_set_eeprom(struct net_device *dev,
                                eeprom->len);
 }
 
+static int bnxt_set_eee(struct net_device *dev, struct ethtool_eee *edata)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       struct ethtool_eee *eee = &bp->eee;
+       struct bnxt_link_info *link_info = &bp->link_info;
+       u32 advertising =
+                _bnxt_fw_to_ethtool_adv_spds(link_info->advertising, 0);
+       int rc = 0;
+
+       if (!BNXT_SINGLE_PF(bp))
+               return -EOPNOTSUPP;
+
+       if (!(bp->flags & BNXT_FLAG_EEE_CAP))
+               return -EOPNOTSUPP;
+
+       if (!edata->eee_enabled)
+               goto eee_ok;
+
+       if (!(link_info->autoneg & BNXT_AUTONEG_SPEED)) {
+               netdev_warn(dev, "EEE requires autoneg\n");
+               return -EINVAL;
+       }
+       if (edata->tx_lpi_enabled) {
+               if (bp->lpi_tmr_hi && (edata->tx_lpi_timer > bp->lpi_tmr_hi ||
+                                      edata->tx_lpi_timer < bp->lpi_tmr_lo)) {
+                       netdev_warn(dev, "Valid LPI timer range is %d and %d microsecs\n",
+                                   bp->lpi_tmr_lo, bp->lpi_tmr_hi);
+                       return -EINVAL;
+               } else if (!bp->lpi_tmr_hi) {
+                       edata->tx_lpi_timer = eee->tx_lpi_timer;
+               }
+       }
+       if (!edata->advertised) {
+               edata->advertised = advertising & eee->supported;
+       } else if (edata->advertised & ~advertising) {
+               netdev_warn(dev, "EEE advertised %x must be a subset of autoneg advertised speeds %x\n",
+                           edata->advertised, advertising);
+               return -EINVAL;
+       }
+
+       eee->advertised = edata->advertised;
+       eee->tx_lpi_enabled = edata->tx_lpi_enabled;
+       eee->tx_lpi_timer = edata->tx_lpi_timer;
+eee_ok:
+       eee->eee_enabled = edata->eee_enabled;
+
+       if (netif_running(dev))
+               rc = bnxt_hwrm_set_link_setting(bp, false, true);
+
+       return rc;
+}
+
+static int bnxt_get_eee(struct net_device *dev, struct ethtool_eee *edata)
+{
+       struct bnxt *bp = netdev_priv(dev);
+
+       if (!(bp->flags & BNXT_FLAG_EEE_CAP))
+               return -EOPNOTSUPP;
+
+       *edata = bp->eee;
+       if (!bp->eee.eee_enabled) {
+               /* Preserve tx_lpi_timer so that the last value will be used
+                * by default when it is re-enabled.
+                */
+               edata->advertised = 0;
+               edata->tx_lpi_enabled = 0;
+       }
+
+       if (!bp->eee.eee_active)
+               edata->lp_advertised = 0;
+
+       return 0;
+}
+
+static int bnxt_read_sfp_module_eeprom_info(struct bnxt *bp, u16 i2c_addr,
+                                           u16 page_number, u16 start_addr,
+                                           u16 data_length, u8 *buf)
+{
+       struct hwrm_port_phy_i2c_read_input req = {0};
+       struct hwrm_port_phy_i2c_read_output *output = bp->hwrm_cmd_resp_addr;
+       int rc, byte_offset = 0;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_I2C_READ, -1, -1);
+       req.i2c_slave_addr = i2c_addr;
+       req.page_number = cpu_to_le16(page_number);
+       req.port_id = cpu_to_le16(bp->pf.port_id);
+       do {
+               u16 xfer_size;
+
+               xfer_size = min_t(u16, data_length, BNXT_MAX_PHY_I2C_RESP_SIZE);
+               data_length -= xfer_size;
+               req.page_offset = cpu_to_le16(start_addr + byte_offset);
+               req.data_length = xfer_size;
+               req.enables = cpu_to_le32(start_addr + byte_offset ?
+                                PORT_PHY_I2C_READ_REQ_ENABLES_PAGE_OFFSET : 0);
+               mutex_lock(&bp->hwrm_cmd_lock);
+               rc = _hwrm_send_message(bp, &req, sizeof(req),
+                                       HWRM_CMD_TIMEOUT);
+               if (!rc)
+                       memcpy(buf + byte_offset, output->data, xfer_size);
+               mutex_unlock(&bp->hwrm_cmd_lock);
+               byte_offset += xfer_size;
+       } while (!rc && data_length > 0);
+
+       return rc;
+}
+
+static int bnxt_get_module_info(struct net_device *dev,
+                               struct ethtool_modinfo *modinfo)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       struct hwrm_port_phy_i2c_read_input req = {0};
+       struct hwrm_port_phy_i2c_read_output *output = bp->hwrm_cmd_resp_addr;
+       int rc;
+
+       /* No point in going further if phy status indicates
+        * module is not inserted or if it is powered down or
+        * if it is of type 10GBase-T
+        */
+       if (bp->link_info.module_status >
+               PORT_PHY_QCFG_RESP_MODULE_STATUS_WARNINGMSG)
+               return -EOPNOTSUPP;
+
+       /* This feature is not supported in older firmware versions */
+       if (bp->hwrm_spec_code < 0x10202)
+               return -EOPNOTSUPP;
+
+       bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_I2C_READ, -1, -1);
+       req.i2c_slave_addr = I2C_DEV_ADDR_A0;
+       req.page_number = 0;
+       req.page_offset = cpu_to_le16(SFP_EEPROM_SFF_8472_COMP_ADDR);
+       req.data_length = SFP_EEPROM_SFF_8472_COMP_SIZE;
+       req.port_id = cpu_to_le16(bp->pf.port_id);
+       mutex_lock(&bp->hwrm_cmd_lock);
+       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       if (!rc) {
+               u32 module_id = le32_to_cpu(output->data[0]);
+
+               switch (module_id) {
+               case SFF_MODULE_ID_SFP:
+                       modinfo->type = ETH_MODULE_SFF_8472;
+                       modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
+                       break;
+               case SFF_MODULE_ID_QSFP:
+               case SFF_MODULE_ID_QSFP_PLUS:
+                       modinfo->type = ETH_MODULE_SFF_8436;
+                       modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
+                       break;
+               case SFF_MODULE_ID_QSFP28:
+                       modinfo->type = ETH_MODULE_SFF_8636;
+                       modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
+                       break;
+               default:
+                       rc = -EOPNOTSUPP;
+                       break;
+               }
+       }
+       mutex_unlock(&bp->hwrm_cmd_lock);
+       return rc;
+}
+
+static int bnxt_get_module_eeprom(struct net_device *dev,
+                                 struct ethtool_eeprom *eeprom,
+                                 u8 *data)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       u16  start = eeprom->offset, length = eeprom->len;
+       int rc = 0;
+
+       memset(data, 0, eeprom->len);
+
+       /* Read A0 portion of the EEPROM */
+       if (start < ETH_MODULE_SFF_8436_LEN) {
+               if (start + eeprom->len > ETH_MODULE_SFF_8436_LEN)
+                       length = ETH_MODULE_SFF_8436_LEN - start;
+               rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0,
+                                                     start, length, data);
+               if (rc)
+                       return rc;
+               start += length;
+               data += length;
+               length = eeprom->len - length;
+       }
+
+       /* Read A2 portion of the EEPROM */
+       if (length) {
+               start -= ETH_MODULE_SFF_8436_LEN;
+               bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A2, 1, start,
+                                                length, data);
+       }
+       return rc;
+}
+
 const struct ethtool_ops bnxt_ethtool_ops = {
-       .get_settings           = bnxt_get_settings,
-       .set_settings           = bnxt_set_settings,
+       .get_link_ksettings     = bnxt_get_link_ksettings,
+       .set_link_ksettings     = bnxt_set_link_ksettings,
        .get_pauseparam         = bnxt_get_pauseparam,
        .set_pauseparam         = bnxt_set_pauseparam,
        .get_drvinfo            = bnxt_get_drvinfo,
@@ -1409,4 +1746,8 @@ const struct ethtool_ops bnxt_ethtool_ops = {
        .get_eeprom             = bnxt_get_eeprom,
        .set_eeprom             = bnxt_set_eeprom,
        .get_link               = bnxt_get_link,
+       .get_eee                = bnxt_get_eee,
+       .set_eee                = bnxt_set_eee,
+       .get_module_info        = bnxt_get_module_info,
+       .get_module_eeprom      = bnxt_get_module_eeprom,
 };