aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/sfc/ethtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/sfc/ethtool.c')
-rw-r--r--drivers/net/sfc/ethtool.c57
1 files changed, 48 insertions, 9 deletions
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
index 1b33f89df4d..0e81af6d834 100644
--- a/drivers/net/sfc/ethtool.c
+++ b/drivers/net/sfc/ethtool.c
@@ -12,11 +12,13 @@
12#include <linux/ethtool.h> 12#include <linux/ethtool.h>
13#include <linux/rtnetlink.h> 13#include <linux/rtnetlink.h>
14#include "net_driver.h" 14#include "net_driver.h"
15#include "workarounds.h"
15#include "selftest.h" 16#include "selftest.h"
16#include "efx.h" 17#include "efx.h"
17#include "ethtool.h" 18#include "ethtool.h"
18#include "falcon.h" 19#include "falcon.h"
19#include "spi.h" 20#include "spi.h"
21#include "mdio_10g.h"
20 22
21const char *efx_loopback_mode_names[] = { 23const char *efx_loopback_mode_names[] = {
22 [LOOPBACK_NONE] = "NONE", 24 [LOOPBACK_NONE] = "NONE",
@@ -674,14 +676,51 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
674 struct ethtool_pauseparam *pause) 676 struct ethtool_pauseparam *pause)
675{ 677{
676 struct efx_nic *efx = netdev_priv(net_dev); 678 struct efx_nic *efx = netdev_priv(net_dev);
677 enum efx_fc_type flow_control = efx->flow_control; 679 enum efx_fc_type wanted_fc;
680 bool reset;
678 681
679 flow_control &= ~(EFX_FC_RX | EFX_FC_TX | EFX_FC_AUTO); 682 wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) |
680 flow_control |= pause->rx_pause ? EFX_FC_RX : 0; 683 (pause->tx_pause ? EFX_FC_TX : 0) |
681 flow_control |= pause->tx_pause ? EFX_FC_TX : 0; 684 (pause->autoneg ? EFX_FC_AUTO : 0));
682 flow_control |= pause->autoneg ? EFX_FC_AUTO : 0; 685
686 if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) {
687 EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n");
688 return -EINVAL;
689 }
690
691 if (!(efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) &&
692 (wanted_fc & EFX_FC_AUTO)) {
693 EFX_LOG(efx, "PHY does not support flow control "
694 "autonegotiation\n");
695 return -EINVAL;
696 }
697
698 /* TX flow control may automatically turn itself off if the
699 * link partner (intermittently) stops responding to pause
700 * frames. There isn't any indication that this has happened,
701 * so the best we do is leave it up to the user to spot this
702 * and fix it be cycling transmit flow control on this end. */
703 reset = (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & EFX_FC_TX);
704 if (EFX_WORKAROUND_11482(efx) && reset) {
705 if (falcon_rev(efx) >= FALCON_REV_B0) {
706 /* Recover by resetting the EM block */
707 if (efx->link_up)
708 falcon_drain_tx_fifo(efx);
709 } else {
710 /* Schedule a reset to recover */
711 efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
712 }
713 }
714
715 /* Try to push the pause parameters */
716 mutex_lock(&efx->mac_lock);
717
718 efx->wanted_fc = wanted_fc;
719 mdio_clause45_set_pause(efx);
720 __efx_reconfigure_port(efx);
721
722 mutex_unlock(&efx->mac_lock);
683 723
684 efx_reconfigure_port(efx);
685 return 0; 724 return 0;
686} 725}
687 726
@@ -690,9 +729,9 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
690{ 729{
691 struct efx_nic *efx = netdev_priv(net_dev); 730 struct efx_nic *efx = netdev_priv(net_dev);
692 731
693 pause->rx_pause = !!(efx->flow_control & EFX_FC_RX); 732 pause->rx_pause = !!(efx->wanted_fc & EFX_FC_RX);
694 pause->tx_pause = !!(efx->flow_control & EFX_FC_TX); 733 pause->tx_pause = !!(efx->wanted_fc & EFX_FC_TX);
695 pause->autoneg = !!(efx->flow_control & EFX_FC_AUTO); 734 pause->autoneg = !!(efx->wanted_fc & EFX_FC_AUTO);
696} 735}
697 736
698 737