Commit d3245b28 authored by Ben Hutchings's avatar Ben Hutchings Committed by David S. Miller

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: default avatarBen Hutchings <bhutchings@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ef2b90ee
...@@ -620,16 +620,49 @@ void efx_link_status_changed(struct efx_nic *efx) ...@@ -620,16 +620,49 @@ void efx_link_status_changed(struct efx_nic *efx)
} }
void efx_link_set_advertising(struct efx_nic *efx, u32 advertising)
{
efx->link_advertising = advertising;
if (advertising) {
if (advertising & ADVERTISED_Pause)
efx->wanted_fc |= (EFX_FC_TX | EFX_FC_RX);
else
efx->wanted_fc &= ~(EFX_FC_TX | EFX_FC_RX);
if (advertising & ADVERTISED_Asym_Pause)
efx->wanted_fc ^= EFX_FC_TX;
}
}
void efx_link_set_wanted_fc(struct efx_nic *efx, enum efx_fc_type wanted_fc)
{
efx->wanted_fc = wanted_fc;
if (efx->link_advertising) {
if (wanted_fc & EFX_FC_RX)
efx->link_advertising |= (ADVERTISED_Pause |
ADVERTISED_Asym_Pause);
else
efx->link_advertising &= ~(ADVERTISED_Pause |
ADVERTISED_Asym_Pause);
if (wanted_fc & EFX_FC_TX)
efx->link_advertising ^= ADVERTISED_Asym_Pause;
}
}
static void efx_fini_port(struct efx_nic *efx); static void efx_fini_port(struct efx_nic *efx);
/* This call reinitialises the MAC to pick up new PHY settings. The /* Push loopback/power/transmit disable settings to the PHY, and reconfigure
* caller must hold the mac_lock */ * the MAC appropriately. All other PHY configuration changes are pushed
void __efx_reconfigure_port(struct efx_nic *efx) * through phy_op->set_settings(), and pushed asynchronously to the MAC
* through efx_monitor().
*
* Callers must hold the mac_lock
*/
int __efx_reconfigure_port(struct efx_nic *efx)
{ {
WARN_ON(!mutex_is_locked(&efx->mac_lock)); enum efx_phy_mode phy_mode;
int rc;
EFX_LOG(efx, "reconfiguring MAC from PHY settings on CPU %d\n", WARN_ON(!mutex_is_locked(&efx->mac_lock));
raw_smp_processor_id());
/* Serialise the promiscuous flag with efx_set_multicast_list. */ /* Serialise the promiscuous flag with efx_set_multicast_list. */
if (efx_dev_registered(efx)) { if (efx_dev_registered(efx)) {
...@@ -637,42 +670,34 @@ void __efx_reconfigure_port(struct efx_nic *efx) ...@@ -637,42 +670,34 @@ void __efx_reconfigure_port(struct efx_nic *efx)
netif_addr_unlock_bh(efx->net_dev); netif_addr_unlock_bh(efx->net_dev);
} }
efx->type->stop_stats(efx); /* Disable PHY transmit in mac level loopbacks */
falcon_deconfigure_mac_wrapper(efx); phy_mode = efx->phy_mode;
/* Reconfigure the PHY, disabling transmit in mac level loopback. */
if (LOOPBACK_INTERNAL(efx)) if (LOOPBACK_INTERNAL(efx))
efx->phy_mode |= PHY_MODE_TX_DISABLED; efx->phy_mode |= PHY_MODE_TX_DISABLED;
else else
efx->phy_mode &= ~PHY_MODE_TX_DISABLED; efx->phy_mode &= ~PHY_MODE_TX_DISABLED;
efx->phy_op->reconfigure(efx);
if (falcon_switch_mac(efx))
goto fail;
efx->mac_op->reconfigure(efx); rc = efx->type->reconfigure_port(efx);
efx->type->start_stats(efx); if (rc)
efx->phy_mode = phy_mode;
/* Inform kernel of loss/gain of carrier */
efx_link_status_changed(efx);
return;
fail: return rc;
EFX_ERR(efx, "failed to reconfigure MAC\n");
efx->port_enabled = false;
efx_fini_port(efx);
} }
/* Reinitialise the MAC to pick up new PHY settings, even if the port is /* Reinitialise the MAC to pick up new PHY settings, even if the port is
* disabled. */ * disabled. */
void efx_reconfigure_port(struct efx_nic *efx) int efx_reconfigure_port(struct efx_nic *efx)
{ {
int rc;
EFX_ASSERT_RESET_SERIALISED(efx); EFX_ASSERT_RESET_SERIALISED(efx);
mutex_lock(&efx->mac_lock); mutex_lock(&efx->mac_lock);
__efx_reconfigure_port(efx); rc = __efx_reconfigure_port(efx);
mutex_unlock(&efx->mac_lock); mutex_unlock(&efx->mac_lock);
return rc;
} }
/* Asynchronous work item for changing MAC promiscuity and multicast /* Asynchronous work item for changing MAC promiscuity and multicast
...@@ -737,14 +762,18 @@ static int efx_init_port(struct efx_nic *efx) ...@@ -737,14 +762,18 @@ static int efx_init_port(struct efx_nic *efx)
rc = efx->phy_op->init(efx); rc = efx->phy_op->init(efx);
if (rc) if (rc)
goto fail1; goto fail1;
efx->phy_op->reconfigure(efx);
rc = falcon_switch_mac(efx);
if (rc)
goto fail2;
efx->mac_op->reconfigure(efx);
efx->port_initialized = true; efx->port_initialized = true;
/* Reconfigure the MAC before creating dma queues (required for
* Falcon/A1 where RX_INGR_EN/TX_DRAIN_EN isn't supported) */
efx->mac_op->reconfigure(efx);
/* Ensure the PHY advertises the correct flow control settings */
rc = efx->phy_op->reconfigure(efx);
if (rc)
goto fail2;
mutex_unlock(&efx->mac_lock); mutex_unlock(&efx->mac_lock);
return 0; return 0;
...@@ -1209,12 +1238,6 @@ static void efx_stop_all(struct efx_nic *efx) ...@@ -1209,12 +1238,6 @@ static void efx_stop_all(struct efx_nic *efx)
/* Flush efx_mac_work(), refill_workqueue, monitor_work */ /* Flush efx_mac_work(), refill_workqueue, monitor_work */
efx_flush_all(efx); efx_flush_all(efx);
/* Isolate the MAC from the TX and RX engines, so that queue
* flushes will complete in a timely fashion. */
falcon_deconfigure_mac_wrapper(efx);
msleep(10); /* Let the Rx FIFO drain */
falcon_drain_tx_fifo(efx);
/* Stop the kernel transmit interface late, so the watchdog /* Stop the kernel transmit interface late, so the watchdog
* timer isn't ticking over the flush */ * timer isn't ticking over the flush */
if (efx_dev_registered(efx)) { if (efx_dev_registered(efx)) {
...@@ -1491,7 +1514,14 @@ static int efx_change_mtu(struct net_device *net_dev, int new_mtu) ...@@ -1491,7 +1514,14 @@ static int efx_change_mtu(struct net_device *net_dev, int new_mtu)
EFX_LOG(efx, "changing MTU to %d\n", new_mtu); EFX_LOG(efx, "changing MTU to %d\n", new_mtu);
efx_fini_channels(efx); efx_fini_channels(efx);
mutex_lock(&efx->mac_lock);
/* Reconfigure the MAC before enabling the dma queues so that
* the RX buffers don't overflow */
net_dev->mtu = new_mtu; net_dev->mtu = new_mtu;
efx->mac_op->reconfigure(efx);
mutex_unlock(&efx->mac_lock);
efx_init_channels(efx); efx_init_channels(efx);
efx_start_all(efx); efx_start_all(efx);
...@@ -1515,7 +1545,9 @@ static int efx_set_mac_address(struct net_device *net_dev, void *data) ...@@ -1515,7 +1545,9 @@ static int efx_set_mac_address(struct net_device *net_dev, void *data)
memcpy(net_dev->dev_addr, new_addr, net_dev->addr_len); memcpy(net_dev->dev_addr, new_addr, net_dev->addr_len);
/* Reconfigure the MAC */ /* Reconfigure the MAC */
efx_reconfigure_port(efx); mutex_lock(&efx->mac_lock);
efx->mac_op->reconfigure(efx);
mutex_unlock(&efx->mac_lock);
return 0; return 0;
} }
...@@ -1682,8 +1714,7 @@ static void efx_unregister_netdev(struct efx_nic *efx) ...@@ -1682,8 +1714,7 @@ static void efx_unregister_netdev(struct efx_nic *efx)
/* Tears down the entire software state and most of the hardware state /* Tears down the entire software state and most of the hardware state
* before reset. */ * before reset. */
void efx_reset_down(struct efx_nic *efx, enum reset_type method, void efx_reset_down(struct efx_nic *efx, enum reset_type method)
struct ethtool_cmd *ecmd)
{ {
EFX_ASSERT_RESET_SERIALISED(efx); EFX_ASSERT_RESET_SERIALISED(efx);
...@@ -1691,8 +1722,6 @@ void efx_reset_down(struct efx_nic *efx, enum reset_type method, ...@@ -1691,8 +1722,6 @@ void efx_reset_down(struct efx_nic *efx, enum reset_type method,
mutex_lock(&efx->mac_lock); mutex_lock(&efx->mac_lock);
mutex_lock(&efx->spi_lock); mutex_lock(&efx->spi_lock);
efx->phy_op->get_settings(efx, ecmd);
efx_fini_channels(efx); efx_fini_channels(efx);
if (efx->port_initialized && method != RESET_TYPE_INVISIBLE) if (efx->port_initialized && method != RESET_TYPE_INVISIBLE)
efx->phy_op->fini(efx); efx->phy_op->fini(efx);
...@@ -1704,8 +1733,7 @@ void efx_reset_down(struct efx_nic *efx, enum reset_type method, ...@@ -1704,8 +1733,7 @@ void efx_reset_down(struct efx_nic *efx, enum reset_type method,
* that we were unable to reinitialise the hardware, and the * that we were unable to reinitialise the hardware, and the
* driver should be disabled. If ok is false, then the rx and tx * driver should be disabled. If ok is false, then the rx and tx
* engines are not restarted, pending a RESET_DISABLE. */ * engines are not restarted, pending a RESET_DISABLE. */
int efx_reset_up(struct efx_nic *efx, enum reset_type method, int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok)
struct ethtool_cmd *ecmd, bool ok)
{ {
int rc; int rc;
...@@ -1722,16 +1750,17 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method, ...@@ -1722,16 +1750,17 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method,
rc = efx->phy_op->init(efx); rc = efx->phy_op->init(efx);
if (rc) if (rc)
ok = false; ok = false;
if (efx->phy_op->reconfigure(efx))
EFX_ERR(efx, "could not restore PHY settings\n");
} }
if (!ok) if (!ok)
efx->port_initialized = false; efx->port_initialized = false;
} }
if (ok) { if (ok) {
efx_init_channels(efx); efx->mac_op->reconfigure(efx);
if (efx->phy_op->set_settings(efx, ecmd)) efx_init_channels(efx);
EFX_ERR(efx, "could not restore PHY settings\n");
} }
mutex_unlock(&efx->spi_lock); mutex_unlock(&efx->spi_lock);
...@@ -1753,7 +1782,6 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method, ...@@ -1753,7 +1782,6 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method,
*/ */
static int efx_reset(struct efx_nic *efx) static int efx_reset(struct efx_nic *efx)
{ {
struct ethtool_cmd ecmd;
enum reset_type method = efx->reset_pending; enum reset_type method = efx->reset_pending;
int rc = 0; int rc = 0;
...@@ -1769,7 +1797,7 @@ static int efx_reset(struct efx_nic *efx) ...@@ -1769,7 +1797,7 @@ static int efx_reset(struct efx_nic *efx)
EFX_INFO(efx, "resetting (%s)\n", RESET_TYPE(method)); EFX_INFO(efx, "resetting (%s)\n", RESET_TYPE(method));
efx_reset_down(efx, method, &ecmd); efx_reset_down(efx, method);
rc = efx->type->reset(efx, method); rc = efx->type->reset(efx, method);
if (rc) { if (rc) {
...@@ -1788,10 +1816,10 @@ static int efx_reset(struct efx_nic *efx) ...@@ -1788,10 +1816,10 @@ static int efx_reset(struct efx_nic *efx)
/* Leave device stopped if necessary */ /* Leave device stopped if necessary */
if (method == RESET_TYPE_DISABLE) { if (method == RESET_TYPE_DISABLE) {
efx_reset_up(efx, method, &ecmd, false); efx_reset_up(efx, method, false);
rc = -EIO; rc = -EIO;
} else { } else {
rc = efx_reset_up(efx, method, &ecmd, true); rc = efx_reset_up(efx, method, true);
} }
out_disable: out_disable:
...@@ -1895,7 +1923,7 @@ bool efx_port_dummy_op_poll(struct efx_nic *efx) ...@@ -1895,7 +1923,7 @@ bool efx_port_dummy_op_poll(struct efx_nic *efx)
static struct efx_phy_operations efx_dummy_phy_operations = { static struct efx_phy_operations efx_dummy_phy_operations = {
.init = efx_port_dummy_op_int, .init = efx_port_dummy_op_int,
.reconfigure = efx_port_dummy_op_void, .reconfigure = efx_port_dummy_op_int,
.poll = efx_port_dummy_op_poll, .poll = efx_port_dummy_op_poll,
.fini = efx_port_dummy_op_void, .fini = efx_port_dummy_op_void,
}; };
......
...@@ -60,8 +60,8 @@ extern void efx_process_channel_now(struct efx_channel *channel); ...@@ -60,8 +60,8 @@ extern void efx_process_channel_now(struct efx_channel *channel);
#define EFX_EVQ_MASK (EFX_EVQ_SIZE - 1) #define EFX_EVQ_MASK (EFX_EVQ_SIZE - 1)
/* Ports */ /* Ports */
extern void efx_reconfigure_port(struct efx_nic *efx); extern int efx_reconfigure_port(struct efx_nic *efx);
extern void __efx_reconfigure_port(struct efx_nic *efx); extern int __efx_reconfigure_port(struct efx_nic *efx);
/* Ethtool support */ /* Ethtool support */
extern int efx_ethtool_get_settings(struct net_device *net_dev, extern int efx_ethtool_get_settings(struct net_device *net_dev,
...@@ -71,10 +71,8 @@ extern int efx_ethtool_set_settings(struct net_device *net_dev, ...@@ -71,10 +71,8 @@ extern int efx_ethtool_set_settings(struct net_device *net_dev,
extern const struct ethtool_ops efx_ethtool_ops; extern const struct ethtool_ops efx_ethtool_ops;
/* Reset handling */ /* Reset handling */
extern void efx_reset_down(struct efx_nic *efx, enum reset_type method, extern void efx_reset_down(struct efx_nic *efx, enum reset_type method);
struct ethtool_cmd *ecmd); extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok);
extern int efx_reset_up(struct efx_nic *efx, enum reset_type method,
struct ethtool_cmd *ecmd, bool ok);
/* Global */ /* Global */
extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
...@@ -115,5 +113,7 @@ static inline void efx_schedule_channel(struct efx_channel *channel) ...@@ -115,5 +113,7 @@ static inline void efx_schedule_channel(struct efx_channel *channel)
} }
extern void efx_link_status_changed(struct efx_nic *efx); extern void efx_link_status_changed(struct efx_nic *efx);
extern void efx_link_set_advertising(struct efx_nic *efx, u32);
extern void efx_link_set_wanted_fc(struct efx_nic *efx, enum efx_fc_type);
#endif /* EFX_EFX_H */ #endif /* EFX_EFX_H */
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/mdio.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include "net_driver.h" #include "net_driver.h"
#include "workarounds.h" #include "workarounds.h"
...@@ -191,6 +190,7 @@ int efx_ethtool_get_settings(struct net_device *net_dev, ...@@ -191,6 +190,7 @@ int efx_ethtool_get_settings(struct net_device *net_dev,
struct ethtool_cmd *ecmd) struct ethtool_cmd *ecmd)
{ {
struct efx_nic *efx = netdev_priv(net_dev); struct efx_nic *efx = netdev_priv(net_dev);
struct efx_link_state *link_state = &efx->link_state;
mutex_lock(&efx->mac_lock); mutex_lock(&efx->mac_lock);
efx->phy_op->get_settings(efx, ecmd); efx->phy_op->get_settings(efx, ecmd);
...@@ -198,6 +198,13 @@ int efx_ethtool_get_settings(struct net_device *net_dev, ...@@ -198,6 +198,13 @@ int efx_ethtool_get_settings(struct net_device *net_dev,
/* Falcon GMAC does not support 1000Mbps HD */ /* Falcon GMAC does not support 1000Mbps HD */
ecmd->supported &= ~SUPPORTED_1000baseT_Half; ecmd->supported &= ~SUPPORTED_1000baseT_Half;
/* Both MACs support pause frames (bidirectional and respond-only) */
ecmd->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
if (LOOPBACK_INTERNAL(efx)) {
ecmd->speed = link_state->speed;
ecmd->duplex = link_state->fd ? DUPLEX_FULL : DUPLEX_HALF;
}
return 0; return 0;
} }
...@@ -219,9 +226,6 @@ int efx_ethtool_set_settings(struct net_device *net_dev, ...@@ -219,9 +226,6 @@ int efx_ethtool_set_settings(struct net_device *net_dev,
mutex_lock(&efx->mac_lock); mutex_lock(&efx->mac_lock);
rc = efx->phy_op->set_settings(efx, ecmd); rc = efx->phy_op->set_settings(efx, ecmd);
mutex_unlock(&efx->mac_lock); mutex_unlock(&efx->mac_lock);
if (!rc)
efx_reconfigure_port(efx);
return rc; return rc;
} }
...@@ -658,8 +662,12 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev, ...@@ -658,8 +662,12 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
struct ethtool_pauseparam *pause) struct ethtool_pauseparam *pause)
{ {
struct efx_nic *efx = netdev_priv(net_dev); struct efx_nic *efx = netdev_priv(net_dev);
enum efx_fc_type wanted_fc; enum efx_fc_type wanted_fc, old_fc;
u32 old_adv;
bool reset; bool reset;
int rc = 0;
mutex_lock(&efx->mac_lock);
wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) | wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) |
(pause->tx_pause ? EFX_FC_TX : 0) | (pause->tx_pause ? EFX_FC_TX : 0) |
...@@ -667,14 +675,14 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev, ...@@ -667,14 +675,14 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) { if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) {
EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n"); EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n");
return -EINVAL; rc = -EINVAL;
goto out;
} }
if (!(efx->phy_op->mmds & MDIO_DEVS_AN) && if ((wanted_fc & EFX_FC_AUTO) && !efx->link_advertising) {
(wanted_fc & EFX_FC_AUTO)) { EFX_LOG(efx, "Autonegotiation is disabled\n");
EFX_LOG(efx, "PHY does not support flow control " rc = -EINVAL;
"autonegotiation\n"); goto out;
return -EINVAL;
} }
/* TX flow control may automatically turn itself off if the /* 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,25 +694,38 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
if (EFX_WORKAROUND_11482(efx) && reset) { if (EFX_WORKAROUND_11482(efx) && reset) {
if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) { if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) {
/* Recover by resetting the EM block */ /* Recover by resetting the EM block */
if (efx->link_state.up) falcon_stop_nic_stats(efx);
falcon_drain_tx_fifo(efx); falcon_drain_tx_fifo(efx);
efx->mac_op->reconfigure(efx);
falcon_start_nic_stats(efx);
} else { } else {
/* Schedule a reset to recover */ /* Schedule a reset to recover */
efx_schedule_reset(efx, RESET_TYPE_INVISIBLE); efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
} }
} }
/* Try to push the pause parameters */ old_adv = efx->link_advertising;
mutex_lock(&efx->mac_lock); old_fc = efx->wanted_fc;
efx_link_set_wanted_fc(efx, wanted_fc);
if (efx->link_advertising != old_adv ||
(efx->wanted_fc ^ old_fc) & EFX_FC_AUTO) {
rc = efx->phy_op->reconfigure(efx);
if (rc) {
EFX_ERR(efx, "Unable to advertise requested flow "
"control setting\n");
goto out;
}
}
efx->wanted_fc = wanted_fc; /* Reconfigure the MAC. The PHY *may* generate a link state change event
if (efx->phy_op->mmds & MDIO_DEVS_AN) * if the user just changed the advertised capabilities, but there's no
mdio45_ethtool_spauseparam_an(&efx->mdio, pause); * harm doing this twice */
__efx_reconfigure_port(efx); efx->mac_op->reconfigure(efx);
out:
mutex_unlock(&efx->mac_lock); mutex_unlock(&efx->mac_lock);
return 0; return rc;
} }
static void efx_ethtool_get_pauseparam(struct net_device *net_dev, static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
......
...@@ -1193,6 +1193,8 @@ static void falcon_poll_flush_events(struct efx_nic *efx) ...@@ -1193,6 +1193,8 @@ static void falcon_poll_flush_events(struct efx_nic *efx)
channel->eventq_read_ptr = read_ptr; channel->eventq_read_ptr = read_ptr;
} }
static void falcon_deconfigure_mac_wrapper(struct efx_nic *efx);
static void falcon_prepare_flush(struct efx_nic *efx) static void falcon_prepare_flush(struct efx_nic *efx)
{ {
falcon_deconfigure_mac_wrapper(efx); falcon_deconfigure_mac_wrapper(efx);
...@@ -1836,9 +1838,10 @@ static void falcon_push_multicast_hash(struct efx_nic *efx) ...@@ -1836,9 +1838,10 @@ static void falcon_push_multicast_hash(struct efx_nic *efx)
efx_writeo(efx, &mc_hash->oword[1], FR_AB_MAC_MC_HASH_REG1); efx_writeo(efx, &mc_hash->oword[1], FR_AB_MAC_MC_HASH_REG1);
} }
static int falcon_reset_macs(struct efx_nic *efx) static void falcon_reset_macs(struct efx_nic *efx)
{ {
efx_oword_t reg; struct falcon_nic_data *nic_data = efx->nic_data;
efx_oword_t reg, mac_ctrl;
int count; int count;
if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) { if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) {
...@@ -1853,7 +1856,7 @@ static int falcon_reset_macs(struct efx_nic *efx) ...@@ -1853,7 +1856,7 @@ static int falcon_reset_macs(struct efx_nic *efx)
EFX_POPULATE_OWORD_1(reg, FRF_AB_GM_SW_RST, 0); EFX_POPULATE_OWORD_1(reg, FRF_AB_GM_SW_RST, 0);
efx_writeo(efx, &reg, FR_AB_GM_CFG1); efx_writeo(efx, &reg, FR_AB_GM_CFG1);
udelay(1000); udelay(1000);
return 0; return;
} else { } else {
EFX_POPULATE_OWORD_1(reg, FRF_AB_XM_CORE_RST, 1); EFX_POPULATE_OWORD_1(reg, FRF_AB_XM_CORE_RST, 1);
efx_writeo(efx, &reg, FR_AB_XM_GLB_CFG); efx_writeo(efx, &reg, FR_AB_XM_GLB_CFG);
...@@ -1862,22 +1865,20 @@ static int falcon_reset_macs(struct efx_nic *efx) ...@@ -1862,22 +1865,20 @@ static int falcon_reset_macs(struct efx_nic *efx)
efx_reado(efx, &reg, FR_AB_XM_GLB_CFG); efx_reado(efx, &reg, FR_AB_XM_GLB_CFG);
if (EFX_OWORD_FIELD(reg, FRF_AB_XM_CORE_RST) == if (EFX_OWORD_FIELD(reg, FRF_AB_XM_CORE_RST) ==
0) 0)
return 0; return;
udelay(10); udelay(10);
} }
EFX_ERR(efx, "timed out waiting for XMAC core reset\n"); EFX_ERR(efx, "timed out waiting for XMAC core reset\n");
return -ETIMEDOUT;
} }
} }
/* MAC stats will fail whilst the TX fifo is draining. Serialise /* Mac stats will fail whist the TX fifo is draining */
* the drain sequence with the statistics fetch */ WARN_ON(nic_data->stats_disable_count == 0);
falcon_stop_nic_stats(efx);
efx_reado(efx, &reg, FR_AB_MAC_CTRL); efx_reado(efx, &mac_ctrl, FR_AB_MAC_CTRL);
EFX_SET_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN, 1); EFX_SET_OWORD_FIELD(mac_ctrl, FRF_BB_TXFIFO_DRAIN_EN, 1);
efx_writeo(efx, &reg, FR_AB_MAC_CTRL); efx_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL);
efx_reado(efx, &reg, FR_AB_GLB_CTL); efx_reado(efx, &reg, FR_AB_GLB_CTL);
EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_XGTX, 1); EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_XGTX, 1);
...@@ -1903,14 +1904,9 @@ static int falcon_reset_macs(struct efx_nic *efx) ...@@ -1903,14 +1904,9 @@ static int falcon_reset_macs(struct efx_nic *efx)
udelay(10); udelay(10);
} }
/* If we've reset the EM block and the link is up, then /* Ensure the correct MAC is selected before statistics
* we'll have to kick the XAUI link so the PHY can recover */ * are re-enabled by the caller */
if (efx->link_state.up && EFX_IS10G(efx) && EFX_WORKAROUND_5147(efx)) efx_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL);
falcon_reset_xaui(efx);
falcon_start_nic_stats(efx);
return 0;
} }
void falcon_drain_tx_fifo(struct efx_nic *efx) void falcon_drain_tx_fifo(struct efx_nic *efx)
...@@ -1929,7 +1925,7 @@ void falcon_drain_tx_fifo(struct efx_nic *efx) ...@@ -1929,7 +1925,7 @@ void falcon_drain_tx_fifo(struct efx_nic *efx)
falcon_reset_macs(efx); falcon_reset_macs(efx);
} }
void falcon_deconfigure_mac_wrapper(struct efx_nic *efx) static void falcon_deconfigure_mac_wrapper(struct efx_nic *efx)
{ {
efx_oword_t reg; efx_oword_t reg;
...@@ -1941,8 +1937,8 @@ void falcon_deconfigure_mac_wrapper(struct efx_nic *efx) ...@@ -1941,8 +1937,8 @@ void falcon_deconfigure_mac_wrapper(struct efx_nic *efx)
EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 0); EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 0);
efx_writeo(efx, &reg, FR_AZ_RX_CFG); efx_writeo(efx, &reg, FR_AZ_RX_CFG);
if (!efx->link_state.up) /* Isolate TX -> MAC */
falcon_drain_tx_fifo(efx); falcon_drain_tx_fifo(efx);
} }
void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) void falcon_reconfigure_mac_wrapper(struct efx_nic *efx)
...@@ -2044,6 +2040,8 @@ static void falcon_stats_timer_func(unsigned long context) ...@@ -2044,6 +2040,8 @@ static void falcon_stats_timer_func(unsigned long context)
spin_unlock(&efx->stats_lock); spin_unlock(&efx->stats_lock);
} }
static void falcon_switch_mac(struct efx_nic *efx);
static bool falcon_loopback_link_poll(struct efx_nic *efx) static bool falcon_loopback_link_poll(struct efx_nic *efx)
{ {
struct efx_link_state old_state = efx->link_state; struct efx_link_state old_state = efx->link_state;
...@@ -2063,6 +2061,38 @@ static bool falcon_loopback_link_poll(struct efx_nic *efx) ...@@ -2063,6 +2061,38 @@ static bool falcon_loopback_link_poll(struct efx_nic *efx)
return !efx_link_state_equal(&efx->link_state, &old_state); return !efx_link_state_equal(&efx->link_state, &old_state);
} }
static int falcon_reconfigure_port(struct efx_nic *efx)
{
int rc;
WARN_ON(efx_nic_rev(efx) > EFX_REV_FALCON_B0);
/* Poll the PHY link state *before* reconfiguring it. This means we
* will pick up the correct speed (in loopback) to select the correct
* MAC.
*/
if (LOOPBACK_INTERNAL(efx))
falcon_loopback_link_poll(efx);
else
efx->phy_op->poll(efx);
falcon_stop_nic_stats(efx);
falcon_deconfigure_mac_wrapper(efx);
falcon_switch_mac(efx);
efx->phy_op->reconfigure(efx);
rc = efx->mac_op->reconfigure(efx);
BUG_ON(rc);
falcon_start_nic_stats(efx);
/* Synchronise efx->link_state with the kernel */
efx_link_status_changed(efx);
return 0;
}
/************************************************************************** /**************************************************************************
* *
* PHY access via GMII * PHY access via GMII
...@@ -2215,17 +2245,15 @@ static void falcon_clock_mac(struct efx_nic *efx) ...@@ -2215,17 +2245,15 @@ static void falcon_clock_mac(struct efx_nic *efx)
} }
} }
int falcon_switch_mac(struct efx_nic *efx) static void falcon_switch_mac(struct efx_nic *efx)
{ {
struct efx_mac_operations *old_mac_op = efx->mac_op; struct efx_mac_operations *old_mac_op = efx->mac_op;
struct falcon_nic_data *nic_data = efx->nic_data; struct falcon_nic_data *nic_data = efx->nic_data;
unsigned int stats_done_offset; unsigned int stats_done_offset;
int rc = 0;
/* Don't try to fetch MAC stats while we're switching MACs */
falcon_stop_nic_stats(efx);
WARN_ON(!mutex_is_locked(&efx->mac_lock)); WARN_ON(!mutex_is_locked(&efx->mac_lock));
WARN_ON(nic_data->stats_disable_count == 0);
efx->mac_op = (EFX_IS10G(efx) ? efx->mac_op = (EFX_IS10G(efx) ?
&falcon_xmac_operations : &falcon_gmac_operations); &falcon_xmac_operations : &falcon_gmac_operations);
...@@ -2236,18 +2264,14 @@ int falcon_switch_mac(struct efx_nic *efx) ...@@ -2236,18 +2264,14 @@ int falcon_switch_mac(struct efx_nic *efx)
nic_data->stats_dma_done = efx->stats_buffer.addr + stats_done_offset; nic_data->stats_dma_done = efx->stats_buffer.addr + stats_done_offset;
if (old_mac_op == efx->mac_op) if (old_mac_op == efx->mac_op)
goto out; return;
falcon_clock_mac(efx); falcon_clock_mac(efx);
EFX_LOG(efx, "selected %cMAC\n", EFX_IS10G(efx) ? 'X' : 'G'); EFX_LOG(efx, "selected %cMAC\n", EFX_IS10G(efx) ? 'X' : 'G');
/* Not all macs support a mac-level link state */ /* Not all macs support a mac-level link state */
efx->xmac_poll_required = false; efx->xmac_poll_required = false;
falcon_reset_macs(efx);
rc = falcon_reset_macs(efx);
out:
falcon_start_nic_stats(efx);
return rc;
} }
/* This call is responsible for hooking in the MAC and PHY operations */ /* This call is responsible for hooking in the MAC and PHY operations */
...@@ -2597,7 +2621,8 @@ static void falcon_monitor(struct efx_nic *efx) ...@@ -2597,7 +2621,8 @@ static void falcon_monitor(struct efx_nic *efx)
EFX_ERR(efx, "Board sensor %s; shutting down PHY\n", EFX_ERR(efx, "Board sensor %s; shutting down PHY\n",
(rc == -ERANGE) ? "reported fault" : "failed"); (rc == -ERANGE) ? "reported fault" : "failed");
efx->phy_mode |= PHY_MODE_LOW_POWER; efx->phy_mode |= PHY_MODE_LOW_POWER;
__efx_reconfigure_port(efx); rc = __efx_reconfigure_port(efx);
WARN_ON(rc);
} }
if (LOOPBACK_INTERNAL(efx)) if (LOOPBACK_INTERNAL(efx))
...@@ -2610,7 +2635,8 @@ static void falcon_monitor(struct efx_nic *efx) ...@@ -2610,7 +2635,8 @@ static void falcon_monitor(struct efx_nic *efx)
falcon_deconfigure_mac_wrapper(efx); falcon_deconfigure_mac_wrapper(efx);
falcon_switch_mac(efx); falcon_switch_mac(efx);
efx->mac_op->reconfigure(efx); rc = efx->mac_op->reconfigure(efx);
BUG_ON(rc);
falcon_start_nic_stats(efx); falcon_start_nic_stats(efx);
...@@ -3239,6 +3265,7 @@ struct efx_nic_type falcon_a1_nic_type = { ...@@ -3239,6 +3265,7 @@ struct efx_nic_type falcon_a1_nic_type = {
.stop_stats = falcon_stop_nic_stats, .stop_stats = falcon_stop_nic_stats,
.push_irq_moderation = falcon_push_irq_moderation, .push_irq_moderation = falcon_push_irq_moderation,
.push_multicast_hash = falcon_push_multicast_hash, .push_multicast_hash = falcon_push_multicast_hash,
.reconfigure_port = falcon_reconfigure_port,
.default_mac_ops = &falcon_xmac_operations, .default_mac_ops = &falcon_xmac_operations,
.revision = EFX_REV_FALCON_A1, .revision = EFX_REV_FALCON_A1,
...@@ -3271,6 +3298,7 @@ struct efx_nic_type falcon_b0_nic_type = { ...@@ -3271,6 +3298,7 @@ struct efx_nic_type falcon_b0_nic_type = {
.stop_stats = falcon_stop_nic_stats, .stop_stats = falcon_stop_nic_stats,
.push_irq_moderation = falcon_push_irq_moderation, .push_irq_moderation = falcon_push_irq_moderation,
.push_multicast_hash = falcon_push_multicast_hash, .push_multicast_hash = falcon_push_multicast_hash,
.reconfigure_port = falcon_reconfigure_port,
.default_mac_ops = &falcon_xmac_operations, .default_mac_ops = &falcon_xmac_operations,
.revision = EFX_REV_FALCON_B0, .revision = EFX_REV_FALCON_B0,
......
...@@ -130,10 +130,7 @@ extern int falcon_process_eventq(struct efx_channel *channel, int rx_quota); ...@@ -130,10 +130,7 @@ extern int falcon_process_eventq(struct efx_channel *channel, int rx_quota);
extern void falcon_eventq_read_ack(struct efx_channel *channel); extern void falcon_eventq_read_ack(struct efx_channel *channel);
/* MAC/PHY */ /* MAC/PHY */
extern int falcon_switch_mac(struct efx_nic *efx);
extern bool falcon_xaui_link_ok(struct efx_nic *efx);
extern void falcon_drain_tx_fifo(struct efx_nic *efx); extern void falcon_drain_tx_fifo(struct efx_nic *efx);
extern void falcon_deconfigure_mac_wrapper(struct efx_nic *efx);
extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx); extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx);
/* Interrupts and test events */ /* Interrupts and test events */
......
...@@ -352,7 +352,8 @@ static ssize_t set_phy_flash_cfg(struct device *dev, ...@@ -352,7 +352,8 @@ static ssize_t set_phy_flash_cfg(struct device *dev,
err = sfe4001_poweron(efx); err = sfe4001_poweron(efx);
else else
err = sfn4111t_reset(efx); err = sfn4111t_reset(efx);
efx_reconfigure_port(efx); if (!err)
err = efx_reconfigure_port(efx);
if (!(new_mode & PHY_MODE_SPECIAL)) if (!(new_mode & PHY_MODE_SPECIAL))
falcon_start_nic_stats(efx); falcon_start_nic_stats(efx);
} }
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
* *
*************************************************************************/ *************************************************************************/
static void falcon_reconfigure_gmac(struct efx_nic *efx) static int falcon_reconfigure_gmac(struct efx_nic *efx)
{ {
struct efx_link_state *link_state = &efx->link_state; struct efx_link_state *link_state = &efx->link_state;
bool loopback, tx_fc, rx_fc, bytemode; bool loopback, tx_fc, rx_fc, bytemode;
...@@ -123,6 +123,8 @@ static void falcon_reconfigure_gmac(struct efx_nic *efx) ...@@ -123,6 +123,8 @@ static void falcon_reconfigure_gmac(struct efx_nic *efx)
udelay(10); udelay(10);
falcon_reconfigure_mac_wrapper(efx); falcon_reconfigure_mac_wrapper(efx);
return 0;
} }
static void falcon_update_stats_gmac(struct efx_nic *efx) static void falcon_update_stats_gmac(struct efx_nic *efx)
......
...@@ -112,7 +112,7 @@ static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) ...@@ -112,7 +112,7 @@ static void falcon_mask_status_intr(struct efx_nic *efx, bool enable)
} }
/* Get status of XAUI link */ /* Get status of XAUI link */
bool falcon_xaui_link_ok(struct efx_nic *efx) static bool falcon_xaui_link_ok(struct efx_nic *efx)
{ {
efx_oword_t reg; efx_oword_t reg;
bool align_done, link_ok = false; bool align_done, link_ok = false;
...@@ -143,7 +143,7 @@ bool falcon_xaui_link_ok(struct efx_nic *efx) ...@@ -143,7 +143,7 @@ bool falcon_xaui_link_ok(struct efx_nic *efx)
return link_ok; return link_ok;
} }
static void falcon_reconfigure_xmac_core(struct efx_nic *efx) void falcon_reconfigure_xmac_core(struct efx_nic *efx)
{ {
unsigned int max_frame_len; unsigned int max_frame_len;
efx_oword_t reg; efx_oword_t reg;
...@@ -275,7 +275,7 @@ static bool falcon_xmac_check_fault(struct efx_nic *efx) ...@@ -275,7 +275,7 @@ static bool falcon_xmac_check_fault(struct efx_nic *efx)
return !falcon_check_xaui_link_up(efx, 5); return !falcon_check_xaui_link_up(efx, 5);
} }
static void falcon_reconfigure_xmac(struct efx_nic *efx) static int falcon_reconfigure_xmac(struct efx_nic *efx)
{ {
falcon_mask_status_intr(efx, false); falcon_mask_status_intr(efx, false);
...@@ -286,6 +286,8 @@ static void falcon_reconfigure_xmac(struct efx_nic *efx) ...@@ -286,6 +286,8 @@ static void falcon_reconfigure_xmac(struct efx_nic *efx)
efx->xmac_poll_required = !falcon_check_xaui_link_up(efx, 5); efx->xmac_poll_required = !falcon_check_xaui_link_up(efx, 5);
falcon_mask_status_intr(efx, true); falcon_mask_status_intr(efx, true);
return 0;
} }
static void falcon_update_stats_xmac(struct efx_nic *efx) static void falcon_update_stats_xmac(struct efx_nic *efx)
......
...@@ -15,5 +15,6 @@ ...@@ -15,5 +15,6 @@
extern struct efx_mac_operations falcon_gmac_operations; extern struct efx_mac_operations falcon_gmac_operations;
extern struct efx_mac_operations falcon_xmac_operations; extern struct efx_mac_operations falcon_xmac_operations;
extern void falcon_reconfigure_xmac_core(struct efx_nic *efx);
#endif #endif
...@@ -248,8 +248,6 @@ void efx_mdio_set_mmds_lpower(struct efx_nic *efx, ...@@ -248,8 +248,6 @@ void efx_mdio_set_mmds_lpower(struct efx_nic *efx,
int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
{ {
struct ethtool_cmd prev; struct ethtool_cmd prev;
bool xnp;
int reg;
efx->phy_op->get_settings(efx, &prev); efx->phy_op->get_settings(efx, &prev);
...@@ -269,30 +267,47 @@ int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) ...@@ -269,30 +267,47 @@ int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
(ecmd->advertising | SUPPORTED_Autoneg) & ~prev.supported) (ecmd->advertising | SUPPORTED_Autoneg) & ~prev.supported)
return -EINVAL; return -EINVAL;
xnp = (ecmd->advertising & ADVERTISED_10000baseT_Full efx_link_set_advertising(efx, ecmd->advertising | ADVERTISED_Autoneg);
|| EFX_WORKAROUND_13204(efx)); efx_mdio_an_reconfigure(efx);
return 0;
}
/**
* efx_mdio_an_reconfigure - Push advertising flags and restart autonegotiation
* @efx: Efx NIC
*/
void efx_mdio_an_reconfigure(struct efx_nic *efx)
{
bool xnp = (efx->link_advertising & ADVERTISED_10000baseT_Full
|| EFX_WORKAROUND_13204(efx));
int reg;
WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN));
/* Set up the base page */ /* Set up the base page */
reg = ADVERTISE_CSMA; reg = ADVERTISE_CSMA;
if (ecmd->advertising & ADVERTISED_10baseT_Half) if (efx->link_advertising & ADVERTISED_10baseT_Half)
reg |= ADVERTISE_10HALF; reg |= ADVERTISE_10HALF;
if (ecmd->advertising & ADVERTISED_10baseT_Full) if (efx->link_advertising & ADVERTISED_10baseT_Full)
reg |= ADVERTISE_10FULL; reg |= ADVERTISE_10FULL;
if (ecmd->advertising & ADVERTISED_100baseT_Half) if (efx->link_advertising & ADVERTISED_100baseT_Half)
reg |= ADVERTISE_100HALF; reg |= ADVERTISE_100HALF;
if (ecmd->advertising & ADVERTISED_100baseT_Full) if (efx->link_advertising & ADVERTISED_100baseT_Full)
reg |= ADVERTISE_100FULL; reg |= ADVERTISE_100FULL;
if (xnp) if (xnp)
reg |= ADVERTISE_RESV; reg |= ADVERTISE_RESV;
else if (ecmd->advertising & (ADVERTISED_1000baseT_Half | else if (efx->link_advertising & (ADVERTISED_1000baseT_Half |
ADVERTISED_1000baseT_Full)) ADVERTISED_1000baseT_Full))
reg |= ADVERTISE_NPAGE; reg |= ADVERTISE_NPAGE;
reg |= mii_advertise_flowctrl(efx->wanted_fc); if (efx->link_advertising & ADVERTISED_Pause)
reg |= ADVERTISE_PAUSE_CAP;
if (efx->link_advertising & ADVERTISED_Asym_Pause)
reg |= ADVERTISE_PAUSE_ASYM;
efx_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg); efx_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg);
/* Set up the (extended) next page if necessary */ /* Set up the (extended) next page if necessary */
if (efx->phy_op->set_npage_adv) if (efx->phy_op->set_npage_adv)
efx->phy_op->set_npage_adv(efx, ecmd->advertising); efx->phy_op->set_npage_adv(efx, efx->link_advertising);
/* Enable and restart AN */ /* Enable and restart AN */
reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_CTRL1); reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_CTRL1);
...@@ -305,8 +320,6 @@ int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) ...@@ -305,8 +320,6 @@ int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
else else
reg &= ~MDIO_AN_CTRL1_XNP; reg &= ~MDIO_AN_CTRL1_XNP;
efx_mdio_write(efx, MDIO_MMD_AN, MDIO_CTRL1, reg); efx_mdio_write(efx, MDIO_MMD_AN, MDIO_CTRL1, reg);
return 0;
} }
enum efx_fc_type efx_mdio_get_pause(struct efx_nic *efx) enum efx_fc_type efx_mdio_get_pause(struct efx_nic *efx)
......
...@@ -86,6 +86,9 @@ extern void efx_mdio_set_mmds_lpower(struct efx_nic *efx, ...@@ -86,6 +86,9 @@ extern void efx_mdio_set_mmds_lpower(struct efx_nic *efx,
/* Set (some of) the PHY settings over MDIO */ /* Set (some of) the PHY settings over MDIO */
extern int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd); extern int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd);
/* Push advertising flags and restart autonegotiation */
extern void efx_mdio_an_reconfigure(struct efx_nic *efx);
/* Get pause parameters from AN if available (otherwise return /* Get pause parameters from AN if available (otherwise return
* requested pause parameters) * requested pause parameters)
*/ */
......
...@@ -517,7 +517,7 @@ static inline bool efx_link_state_equal(const struct efx_link_state *left, ...@@ -517,7 +517,7 @@ static inline bool efx_link_state_equal(const struct efx_link_state *left,
* @check_fault: Check fault state. True if fault present. * @check_fault: Check fault state. True if fault present.
*/ */
struct efx_mac_operations { struct efx_mac_operations {
void (*reconfigure) (struct efx_nic *efx); int (*reconfigure) (struct efx_nic *efx);
void (*update_stats) (struct efx_nic *efx); void (*update_stats) (struct efx_nic *efx);
bool (*check_fault)(struct efx_nic *efx); bool (*check_fault)(struct efx_nic *efx);
}; };
...@@ -544,7 +544,7 @@ struct efx_phy_operations { ...@@ -544,7 +544,7 @@ struct efx_phy_operations {
enum efx_mac_type macs; enum efx_mac_type macs;
int (*init) (struct efx_nic *efx); int (*init) (struct efx_nic *efx);
void (*fini) (struct efx_nic *efx); void (*fini) (struct efx_nic *efx);
void (*reconfigure) (struct efx_nic *efx); int (*reconfigure) (struct efx_nic *efx);
bool (*poll) (struct efx_nic *efx); bool (*poll) (struct efx_nic *efx);
void (*get_settings) (struct efx_nic *efx, void (*get_settings) (struct efx_nic *efx,
struct ethtool_cmd *ecmd); struct ethtool_cmd *ecmd);
...@@ -730,6 +730,7 @@ union efx_multicast_hash { ...@@ -730,6 +730,7 @@ union efx_multicast_hash {
* @mdio: PHY MDIO interface * @mdio: PHY MDIO interface
* @phy_mode: PHY operating mode. Serialised by @mac_lock. * @phy_mode: PHY operating mode. Serialised by @mac_lock.
* @xmac_poll_required: XMAC link state needs polling * @xmac_poll_required: XMAC link state needs polling
* @link_advertising: Autonegotiation advertising flags
* @link_state: Current state of the link * @link_state: Current state of the link
* @n_link_state_changes: Number of times the link has changed state * @n_link_state_changes: Number of times the link has changed state
* @promiscuous: Promiscuous flag. Protected by netif_tx_lock. * @promiscuous: Promiscuous flag. Protected by netif_tx_lock.
...@@ -813,6 +814,7 @@ struct efx_nic { ...@@ -813,6 +814,7 @@ struct efx_nic {
enum efx_phy_mode phy_mode; enum efx_phy_mode phy_mode;
bool xmac_poll_required; bool xmac_poll_required;
u32 link_advertising;
struct efx_link_state link_state; struct efx_link_state link_state;
unsigned int n_link_state_changes; unsigned int n_link_state_changes;
...@@ -858,6 +860,7 @@ static inline const char *efx_dev_name(struct efx_nic *efx) ...@@ -858,6 +860,7 @@ static inline const char *efx_dev_name(struct efx_nic *efx)
* @stop_stats: Stop the regular fetching of statistics * @stop_stats: Stop the regular fetching of statistics
* @push_irq_moderation: Apply interrupt moderation value * @push_irq_moderation: Apply interrupt moderation value
* @push_multicast_hash: Apply multicast hash table * @push_multicast_hash: Apply multicast hash table
* @reconfigure_port: Push loopback/power/txdis changes to the MAC and PHY
* @default_mac_ops: efx_mac_operations to set at startup * @default_mac_ops: efx_mac_operations to set at startup
* @revision: Hardware architecture revision * @revision: Hardware architecture revision
* @mem_map_size: Memory BAR mapped size * @mem_map_size: Memory BAR mapped size
...@@ -890,6 +893,7 @@ struct efx_nic_type { ...@@ -890,6 +893,7 @@ struct efx_nic_type {
void (*stop_stats)(struct efx_nic *efx); void (*stop_stats)(struct efx_nic *efx);
void (*push_irq_moderation)(struct efx_channel *channel); void (*push_irq_moderation)(struct efx_channel *channel);
void (*push_multicast_hash)(struct efx_nic *efx); void (*push_multicast_hash)(struct efx_nic *efx);
int (*reconfigure_port)(struct efx_nic *efx);
struct efx_mac_operations *default_mac_ops; struct efx_mac_operations *default_mac_ops;
int revision; int revision;
......
...@@ -178,7 +178,7 @@ static bool qt202x_phy_poll(struct efx_nic *efx) ...@@ -178,7 +178,7 @@ static bool qt202x_phy_poll(struct efx_nic *efx)
return efx->link_state.up != was_up; return efx->link_state.up != was_up;
} }
static void qt202x_phy_reconfigure(struct efx_nic *efx) static int qt202x_phy_reconfigure(struct efx_nic *efx)
{ {
struct qt202x_phy_data *phy_data = efx->phy_data; struct qt202x_phy_data *phy_data = efx->phy_data;
...@@ -207,6 +207,8 @@ static void qt202x_phy_reconfigure(struct efx_nic *efx) ...@@ -207,6 +207,8 @@ static void qt202x_phy_reconfigure(struct efx_nic *efx)
efx_mdio_phy_reconfigure(efx); efx_mdio_phy_reconfigure(efx);
phy_data->phy_mode = efx->phy_mode; phy_data->phy_mode = efx->phy_mode;
return 0;
} }
static void qt202x_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) static void qt202x_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
......
...@@ -659,7 +659,6 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, ...@@ -659,7 +659,6 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests,
enum efx_loopback_mode loopback_mode = efx->loopback_mode; enum efx_loopback_mode loopback_mode = efx->loopback_mode;
int phy_mode = efx->phy_mode; int phy_mode = efx->phy_mode;
enum reset_type reset_method = RESET_TYPE_INVISIBLE; enum reset_type reset_method = RESET_TYPE_INVISIBLE;
struct ethtool_cmd ecmd;
struct efx_channel *channel; struct efx_channel *channel;
int rc_test = 0, rc_reset = 0, rc; int rc_test = 0, rc_reset = 0, rc;
...@@ -712,7 +711,7 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, ...@@ -712,7 +711,7 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests,
mutex_unlock(&efx->mac_lock); mutex_unlock(&efx->mac_lock);
/* free up all consumers of SRAM (including all the queues) */ /* free up all consumers of SRAM (including all the queues) */
efx_reset_down(efx, reset_method, &ecmd); efx_reset_down(efx, reset_method);
rc = efx_test_chip(efx, tests); rc = efx_test_chip(efx, tests);
if (rc && !rc_test) if (rc && !rc_test)
...@@ -726,7 +725,7 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, ...@@ -726,7 +725,7 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests,
efx->phy_mode &= ~PHY_MODE_LOW_POWER; efx->phy_mode &= ~PHY_MODE_LOW_POWER;
efx->loopback_mode = LOOPBACK_NONE; efx->loopback_mode = LOOPBACK_NONE;
rc = efx_reset_up(efx, reset_method, &ecmd, rc_reset == 0); rc = efx_reset_up(efx, reset_method, rc_reset == 0);
if (rc && !rc_reset) if (rc && !rc_reset)
rc_reset = rc; rc_reset = rc;
...@@ -745,10 +744,12 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, ...@@ -745,10 +744,12 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests,
rc_test = rc; rc_test = rc;
/* restore the PHY to the previous state */ /* restore the PHY to the previous state */
efx->loopback_mode = loopback_mode; mutex_lock(&efx->mac_lock);
efx->phy_mode = phy_mode; efx->phy_mode = phy_mode;
efx->port_inhibited = false; efx->port_inhibited = false;
efx_ethtool_set_settings(efx->net_dev, &ecmd); efx->loopback_mode = loopback_mode;
__efx_reconfigure_port(efx);
mutex_unlock(&efx->mac_lock);
return rc_test; return rc_test;
} }
......
...@@ -199,15 +199,16 @@ static ssize_t set_phy_short_reach(struct device *dev, ...@@ -199,15 +199,16 @@ static ssize_t set_phy_short_reach(struct device *dev,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
int rc;
rtnl_lock(); rtnl_lock();
efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TXPWR, efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TXPWR,
MDIO_PMA_10GBT_TXPWR_SHORT, MDIO_PMA_10GBT_TXPWR_SHORT,
count != 0 && *buf != '0'); count != 0 && *buf != '0');
efx_reconfigure_port(efx); rc = efx_reconfigure_port(efx);
rtnl_unlock(); rtnl_unlock();
return count; return rc < 0 ? rc : (ssize_t)count;
} }
static DEVICE_ATTR(phy_short_reach, 0644, show_phy_short_reach, static DEVICE_ATTR(phy_short_reach, 0644, show_phy_short_reach,
...@@ -300,7 +301,6 @@ static int tenxpress_init(struct efx_nic *efx) ...@@ -300,7 +301,6 @@ static int tenxpress_init(struct efx_nic *efx)
static int tenxpress_phy_init(struct efx_nic *efx) static int tenxpress_phy_init(struct efx_nic *efx)
{ {
struct tenxpress_phy_data *phy_data; struct tenxpress_phy_data *phy_data;
u16 old_adv, adv;
int rc = 0; int rc = 0;
falcon_board(efx)->type->init_phy(efx); falcon_board(efx)->type->init_phy(efx);
...@@ -335,14 +335,14 @@ static int tenxpress_phy_init(struct efx_nic *efx) ...@@ -335,14 +335,14 @@ static int tenxpress_phy_init(struct efx_nic *efx)
if (rc < 0) if (rc < 0)
goto fail; goto fail;
/* Set pause advertising */ /* Initialise advertising flags */
old_adv = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE); efx->link_advertising = (ADVERTISED_TP | ADVERTISED_Autoneg |
adv = ((old_adv & ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)) | ADVERTISED_10000baseT_Full);
mii_advertise_flowctrl(efx->wanted_fc)); if (efx->phy_type != PHY_TYPE_SFX7101)
if (adv != old_adv) { efx->link_advertising |= (ADVERTISED_1000baseT_Full |
efx_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, adv); ADVERTISED_100baseT_Full);
mdio45_nway_restart(&efx->mdio); efx_link_set_wanted_fc(efx, efx->wanted_fc);
} efx_mdio_an_reconfigure(efx);
if (efx->phy_type == PHY_TYPE_SFT9001B) { if (efx->phy_type == PHY_TYPE_SFT9001B) {
rc = device_create_file(&efx->pci_dev->dev, rc = device_create_file(&efx->pci_dev->dev,
...@@ -500,49 +500,41 @@ static void tenxpress_low_power(struct efx_nic *efx) ...@@ -500,49 +500,41 @@ static void tenxpress_low_power(struct efx_nic *efx)
!!(efx->phy_mode & PHY_MODE_LOW_POWER)); !!(efx->phy_mode & PHY_MODE_LOW_POWER));
} }
static void tenxpress_phy_reconfigure(struct efx_nic *efx) static int tenxpress_phy_reconfigure(struct efx_nic *efx)
{ {
struct tenxpress_phy_data *phy_data = efx->phy_data; struct tenxpress_phy_data *phy_data = efx->phy_data;
struct ethtool_cmd ecmd;
bool phy_mode_change, loop_reset; bool phy_mode_change, loop_reset;
if (efx->phy_mode & (PHY_MODE_OFF | PHY_MODE_SPECIAL)) { if (efx->phy_mode & (PHY_MODE_OFF | PHY_MODE_SPECIAL)) {
phy_data->phy_mode = efx->phy_mode; phy_data->phy_mode = efx->phy_mode;
return; return 0;
} }
tenxpress_low_power(efx);
phy_mode_change = (efx->phy_mode == PHY_MODE_NORMAL && phy_mode_change = (efx->phy_mode == PHY_MODE_NORMAL &&
phy_data->phy_mode != PHY_MODE_NORMAL); phy_data->phy_mode != PHY_MODE_NORMAL);
loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, efx->phy_op->loopbacks) || loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, efx->phy_op->loopbacks) ||
LOOPBACK_CHANGED(phy_data, efx, 1 << LOOPBACK_GPHY)); LOOPBACK_CHANGED(phy_data, efx, 1 << LOOPBACK_GPHY));
if (loop_reset || phy_mode_change) { if (loop_reset || phy_mode_change) {
int rc; tenxpress_special_reset(efx);
efx->phy_op->get_settings(efx, &ecmd);
if (loop_reset || phy_mode_change) { /* Reset XAUI if we were in 10G, and are staying
tenxpress_special_reset(efx); * in 10G. If we're moving into and out of 10G
* then xaui will be reset anyway */
/* Reset XAUI if we were in 10G, and are staying if (EFX_IS10G(efx))
* in 10G. If we're moving into and out of 10G falcon_reset_xaui(efx);
* then xaui will be reset anyway */
if (EFX_IS10G(efx))
falcon_reset_xaui(efx);
}
rc = efx->phy_op->set_settings(efx, &ecmd);
WARN_ON(rc);
} }
tenxpress_low_power(efx);
efx_mdio_transmit_disable(efx); efx_mdio_transmit_disable(efx);
efx_mdio_phy_reconfigure(efx); efx_mdio_phy_reconfigure(efx);
tenxpress_ext_loopback(efx); tenxpress_ext_loopback(efx);
efx_mdio_an_reconfigure(efx);
phy_data->loopback_mode = efx->loopback_mode; phy_data->loopback_mode = efx->loopback_mode;
phy_data->phy_mode = efx->phy_mode; phy_data->phy_mode = efx->phy_mode;
return 0;
} }
static void static void
...@@ -646,6 +638,9 @@ sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags) ...@@ -646,6 +638,9 @@ sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags)
/* BIST is automatically run after a special software reset */ /* BIST is automatically run after a special software reset */
rc = tenxpress_special_reset(efx); rc = tenxpress_special_reset(efx);
results[0] = rc ? -1 : 1; results[0] = rc ? -1 : 1;
efx_mdio_an_reconfigure(efx);
return rc; return rc;
} }
...@@ -663,12 +658,8 @@ static const char *const sft9001_test_names[] = { ...@@ -663,12 +658,8 @@ static const char *const sft9001_test_names[] = {
static int sft9001_run_tests(struct efx_nic *efx, int *results, unsigned flags) static int sft9001_run_tests(struct efx_nic *efx, int *results, unsigned flags)
{ {
struct ethtool_cmd ecmd;
int rc = 0, rc2, i, ctrl_reg, res_reg; int rc = 0, rc2, i, ctrl_reg, res_reg;
if (flags & ETH_TEST_FL_OFFLINE)
efx->phy_op->get_settings(efx, &ecmd);
/* Initialise cable diagnostic results to unknown failure */ /* Initialise cable diagnostic results to unknown failure */
for (i = 1; i < 9; ++i) for (i = 1; i < 9; ++i)
results[i] = -1; results[i] = -1;
...@@ -720,9 +711,7 @@ out: ...@@ -720,9 +711,7 @@ out:
if (!rc) if (!rc)
rc = rc2; rc = rc2;
rc2 = efx->phy_op->set_settings(efx, &ecmd); efx_mdio_an_reconfigure(efx);
if (!rc)
rc = rc2;
} }
return rc; return rc;
...@@ -753,7 +742,6 @@ tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) ...@@ -753,7 +742,6 @@ tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
mdio45_ethtool_gset_npage(&efx->mdio, ecmd, adv, lpa); mdio45_ethtool_gset_npage(&efx->mdio, ecmd, adv, lpa);
ecmd->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
if (efx->phy_type != PHY_TYPE_SFX7101) { if (efx->phy_type != PHY_TYPE_SFX7101) {
ecmd->supported |= (SUPPORTED_100baseT_Full | ecmd->supported |= (SUPPORTED_100baseT_Full |
SUPPORTED_1000baseT_Full); SUPPORTED_1000baseT_Full);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment