diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2009-11-28 22:42:41 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-11-29 19:46:28 -0500 |
commit | d3245b28ef2a45ec4e115062a38100bd06229289 (patch) | |
tree | 036133fdf01a20f36086d5eb8606728859c056de /drivers/net/sfc/ethtool.c | |
parent | ef2b90ee4dba7a3d9001f1f0003b860b39a4aaae (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.c | 61 |
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 | ||
725 | out: | ||
705 | mutex_unlock(&efx->mac_lock); | 726 | mutex_unlock(&efx->mac_lock); |
706 | 727 | ||
707 | return 0; | 728 | return rc; |
708 | } | 729 | } |
709 | 730 | ||
710 | static void efx_ethtool_get_pauseparam(struct net_device *net_dev, | 731 | static void efx_ethtool_get_pauseparam(struct net_device *net_dev, |