From 04cc8cacb01c09fba2297faf1477cd570ba43f0b Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 12 Dec 2008 21:50:46 -0800 Subject: sfc: Implement auto-negotiation Add infrastructure for auto-negotiation of speed, duplex and flow control. When using 10Xpress, auto-negotiate flow control. While we're at it, clean up the code to warn when partner is not 10GBASE-T capable. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/ethtool.c | 57 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 9 deletions(-) (limited to 'drivers/net/sfc/ethtool.c') diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 1b33f89df4d5..0e81af6d8348 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -12,11 +12,13 @@ #include #include #include "net_driver.h" +#include "workarounds.h" #include "selftest.h" #include "efx.h" #include "ethtool.h" #include "falcon.h" #include "spi.h" +#include "mdio_10g.h" const char *efx_loopback_mode_names[] = { [LOOPBACK_NONE] = "NONE", @@ -674,14 +676,51 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev, struct ethtool_pauseparam *pause) { struct efx_nic *efx = netdev_priv(net_dev); - enum efx_fc_type flow_control = efx->flow_control; + enum efx_fc_type wanted_fc; + bool reset; - flow_control &= ~(EFX_FC_RX | EFX_FC_TX | EFX_FC_AUTO); - flow_control |= pause->rx_pause ? EFX_FC_RX : 0; - flow_control |= pause->tx_pause ? EFX_FC_TX : 0; - flow_control |= pause->autoneg ? EFX_FC_AUTO : 0; + wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) | + (pause->tx_pause ? EFX_FC_TX : 0) | + (pause->autoneg ? EFX_FC_AUTO : 0)); + + if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) { + EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n"); + return -EINVAL; + } + + if (!(efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) && + (wanted_fc & EFX_FC_AUTO)) { + EFX_LOG(efx, "PHY does not support flow control " + "autonegotiation\n"); + return -EINVAL; + } + + /* TX flow control may automatically turn itself off if the + * link partner (intermittently) stops responding to pause + * frames. There isn't any indication that this has happened, + * so the best we do is leave it up to the user to spot this + * and fix it be cycling transmit flow control on this end. */ + reset = (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & EFX_FC_TX); + if (EFX_WORKAROUND_11482(efx) && reset) { + if (falcon_rev(efx) >= FALCON_REV_B0) { + /* Recover by resetting the EM block */ + if (efx->link_up) + falcon_drain_tx_fifo(efx); + } else { + /* Schedule a reset to recover */ + efx_schedule_reset(efx, RESET_TYPE_INVISIBLE); + } + } + + /* Try to push the pause parameters */ + mutex_lock(&efx->mac_lock); + + efx->wanted_fc = wanted_fc; + mdio_clause45_set_pause(efx); + __efx_reconfigure_port(efx); + + mutex_unlock(&efx->mac_lock); - efx_reconfigure_port(efx); return 0; } @@ -690,9 +729,9 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev, { struct efx_nic *efx = netdev_priv(net_dev); - pause->rx_pause = !!(efx->flow_control & EFX_FC_RX); - pause->tx_pause = !!(efx->flow_control & EFX_FC_TX); - pause->autoneg = !!(efx->flow_control & EFX_FC_AUTO); + pause->rx_pause = !!(efx->wanted_fc & EFX_FC_RX); + pause->tx_pause = !!(efx->wanted_fc & EFX_FC_TX); + pause->autoneg = !!(efx->wanted_fc & EFX_FC_AUTO); } -- cgit v1.2.3-55-g7522