aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/sfc/ethtool.c
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2009-11-28 22:42:41 -0500
committerDavid S. Miller <davem@davemloft.net>2009-11-29 19:46:28 -0500
commitd3245b28ef2a45ec4e115062a38100bd06229289 (patch)
tree036133fdf01a20f36086d5eb8606728859c056de /drivers/net/sfc/ethtool.c
parentef2b90ee4dba7a3d9001f1f0003b860b39a4aaae (diff)
sfc: Refactor link configuration
Refactor PHY, MAC and NIC configuration operations so that the existing link configuration can be re-pushed with: efx->phy_op->reconfigure(efx); efx->mac_op->reconfigure(efx); and a new configuration with: efx->nic_op->reconfigure_port(efx); (plus locking and error-checking). We have not held the link settings in software (aside from flow control), and have relied on asking the hardware what they are. This is a problem because in some cases the hardware may no longer be in a state to tell us. In particular, if an entire multi-port board is reset through one port, the driver bindings to other ports have no chance to save settings before recovering. We only actually need to keep track of the autonegotiation settings, so add an ethtool advertising mask to struct efx_nic, initialise it in PHY init and update it as necessary. Remove now-unneeded uses of efx_phy_op::{get,set}_settings() and struct ethtool_cmd. Much of this was done by Steve Hodgson <shodgson@solarflare.com>. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/sfc/ethtool.c')
-rw-r--r--drivers/net/sfc/ethtool.c61
1 files changed, 41 insertions, 20 deletions
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
index 49e0aed920d3..d95d0fa399ff 100644
--- a/drivers/net/sfc/ethtool.c
+++ b/drivers/net/sfc/ethtool.c
@@ -10,7 +10,6 @@
10 10
11#include <linux/netdevice.h> 11#include <linux/netdevice.h>
12#include <linux/ethtool.h> 12#include <linux/ethtool.h>
13#include <linux/mdio.h>
14#include <linux/rtnetlink.h> 13#include <linux/rtnetlink.h>
15#include "net_driver.h" 14#include "net_driver.h"
16#include "workarounds.h" 15#include "workarounds.h"
@@ -191,6 +190,7 @@ int efx_ethtool_get_settings(struct net_device *net_dev,
191 struct ethtool_cmd *ecmd) 190 struct ethtool_cmd *ecmd)
192{ 191{
193 struct efx_nic *efx = netdev_priv(net_dev); 192 struct efx_nic *efx = netdev_priv(net_dev);
193 struct efx_link_state *link_state = &efx->link_state;
194 194
195 mutex_lock(&efx->mac_lock); 195 mutex_lock(&efx->mac_lock);
196 efx->phy_op->get_settings(efx, ecmd); 196 efx->phy_op->get_settings(efx, ecmd);
@@ -198,6 +198,13 @@ int efx_ethtool_get_settings(struct net_device *net_dev,
198 198
199 /* Falcon GMAC does not support 1000Mbps HD */ 199 /* Falcon GMAC does not support 1000Mbps HD */
200 ecmd->supported &= ~SUPPORTED_1000baseT_Half; 200 ecmd->supported &= ~SUPPORTED_1000baseT_Half;
201 /* Both MACs support pause frames (bidirectional and respond-only) */
202 ecmd->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
203
204 if (LOOPBACK_INTERNAL(efx)) {
205 ecmd->speed = link_state->speed;
206 ecmd->duplex = link_state->fd ? DUPLEX_FULL : DUPLEX_HALF;
207 }
201 208
202 return 0; 209 return 0;
203} 210}
@@ -219,9 +226,6 @@ int efx_ethtool_set_settings(struct net_device *net_dev,
219 mutex_lock(&efx->mac_lock); 226 mutex_lock(&efx->mac_lock);
220 rc = efx->phy_op->set_settings(efx, ecmd); 227 rc = efx->phy_op->set_settings(efx, ecmd);
221 mutex_unlock(&efx->mac_lock); 228 mutex_unlock(&efx->mac_lock);
222 if (!rc)
223 efx_reconfigure_port(efx);
224
225 return rc; 229 return rc;
226} 230}
227 231
@@ -658,8 +662,12 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
658 struct ethtool_pauseparam *pause) 662 struct ethtool_pauseparam *pause)
659{ 663{
660 struct efx_nic *efx = netdev_priv(net_dev); 664 struct efx_nic *efx = netdev_priv(net_dev);
661 enum efx_fc_type wanted_fc; 665 enum efx_fc_type wanted_fc, old_fc;
666 u32 old_adv;
662 bool reset; 667 bool reset;
668 int rc = 0;
669
670 mutex_lock(&efx->mac_lock);
663 671
664 wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) | 672 wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) |
665 (pause->tx_pause ? EFX_FC_TX : 0) | 673 (pause->tx_pause ? EFX_FC_TX : 0) |
@@ -667,14 +675,14 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
667 675
668 if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) { 676 if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) {
669 EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n"); 677 EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n");
670 return -EINVAL; 678 rc = -EINVAL;
679 goto out;
671 } 680 }
672 681
673 if (!(efx->phy_op->mmds & MDIO_DEVS_AN) && 682 if ((wanted_fc & EFX_FC_AUTO) && !efx->link_advertising) {
674 (wanted_fc & EFX_FC_AUTO)) { 683 EFX_LOG(efx, "Autonegotiation is disabled\n");
675 EFX_LOG(efx, "PHY does not support flow control " 684 rc = -EINVAL;
676 "autonegotiation\n"); 685 goto out;
677 return -EINVAL;
678 } 686 }
679 687
680 /* TX flow control may automatically turn itself off if the 688 /* TX flow control may automatically turn itself off if the
@@ -686,25 +694,38 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
686 if (EFX_WORKAROUND_11482(efx) && reset) { 694 if (EFX_WORKAROUND_11482(efx) && reset) {
687 if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) { 695 if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) {
688 /* Recover by resetting the EM block */ 696 /* Recover by resetting the EM block */
689 if (efx->link_state.up) 697 falcon_stop_nic_stats(efx);
690 falcon_drain_tx_fifo(efx); 698 falcon_drain_tx_fifo(efx);
699 efx->mac_op->reconfigure(efx);
700 falcon_start_nic_stats(efx);
691 } else { 701 } else {
692 /* Schedule a reset to recover */ 702 /* Schedule a reset to recover */
693 efx_schedule_reset(efx, RESET_TYPE_INVISIBLE); 703 efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
694 } 704 }
695 } 705 }
696 706
697 /* Try to push the pause parameters */ 707 old_adv = efx->link_advertising;
698 mutex_lock(&efx->mac_lock); 708 old_fc = efx->wanted_fc;
709 efx_link_set_wanted_fc(efx, wanted_fc);
710 if (efx->link_advertising != old_adv ||
711 (efx->wanted_fc ^ old_fc) & EFX_FC_AUTO) {
712 rc = efx->phy_op->reconfigure(efx);
713 if (rc) {
714 EFX_ERR(efx, "Unable to advertise requested flow "
715 "control setting\n");
716 goto out;
717 }
718 }
699 719
700 efx->wanted_fc = wanted_fc; 720 /* Reconfigure the MAC. The PHY *may* generate a link state change event
701 if (efx->phy_op->mmds & MDIO_DEVS_AN) 721 * if the user just changed the advertised capabilities, but there's no
702 mdio45_ethtool_spauseparam_an(&efx->mdio, pause); 722 * harm doing this twice */
703 __efx_reconfigure_port(efx); 723 efx->mac_op->reconfigure(efx);
704 724
725out:
705 mutex_unlock(&efx->mac_lock); 726 mutex_unlock(&efx->mac_lock);
706 727
707 return 0; 728 return rc;
708} 729}
709 730
710static void efx_ethtool_get_pauseparam(struct net_device *net_dev, 731static void efx_ethtool_get_pauseparam(struct net_device *net_dev,