aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/sfc/efx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/sfc/efx.c')
-rw-r--r--drivers/net/sfc/efx.c793
1 files changed, 466 insertions, 327 deletions
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c
index cc4b2f99989..f983e3b507c 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/sfc/efx.c
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2005-2008 Solarflare Communications Inc.
+ * Copyright 2005-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -21,12 +21,73 @@
#include <linux/ethtool.h>
#include <linux/topology.h>
#include "net_driver.h"
-#include "ethtool.h"
-#include "tx.h"
-#include "rx.h"
#include "efx.h"
#include "mdio_10g.h"
-#include "falcon.h"
+#include "nic.h"
+
+#include "mcdi.h"
+
+/**************************************************************************
+ *
+ * Type name strings
+ *
+ **************************************************************************
+ */
+
+/* Loopback mode names (see LOOPBACK_MODE()) */
+const unsigned int efx_loopback_mode_max = LOOPBACK_MAX;
+const char *efx_loopback_mode_names[] = {
+ [LOOPBACK_NONE] = "NONE",
+ [LOOPBACK_DATA] = "DATAPATH",
+ [LOOPBACK_GMAC] = "GMAC",
+ [LOOPBACK_XGMII] = "XGMII",
+ [LOOPBACK_XGXS] = "XGXS",
+ [LOOPBACK_XAUI] = "XAUI",
+ [LOOPBACK_GMII] = "GMII",
+ [LOOPBACK_SGMII] = "SGMII",
+ [LOOPBACK_XGBR] = "XGBR",
+ [LOOPBACK_XFI] = "XFI",
+ [LOOPBACK_XAUI_FAR] = "XAUI_FAR",
+ [LOOPBACK_GMII_FAR] = "GMII_FAR",
+ [LOOPBACK_SGMII_FAR] = "SGMII_FAR",
+ [LOOPBACK_XFI_FAR] = "XFI_FAR",
+ [LOOPBACK_GPHY] = "GPHY",
+ [LOOPBACK_PHYXS] = "PHYXS",
+ [LOOPBACK_PCS] = "PCS",
+ [LOOPBACK_PMAPMD] = "PMA/PMD",
+ [LOOPBACK_XPORT] = "XPORT",
+ [LOOPBACK_XGMII_WS] = "XGMII_WS",
+ [LOOPBACK_XAUI_WS] = "XAUI_WS",
+ [LOOPBACK_XAUI_WS_FAR] = "XAUI_WS_FAR",
+ [LOOPBACK_XAUI_WS_NEAR] = "XAUI_WS_NEAR",
+ [LOOPBACK_GMII_WS] = "GMII_WS",
+ [LOOPBACK_XFI_WS] = "XFI_WS",
+ [LOOPBACK_XFI_WS_FAR] = "XFI_WS_FAR",
+ [LOOPBACK_PHYXS_WS] = "PHYXS_WS",
+};
+
+/* Interrupt mode names (see INT_MODE())) */
+const unsigned int efx_interrupt_mode_max = EFX_INT_MODE_MAX;
+const char *efx_interrupt_mode_names[] = {
+ [EFX_INT_MODE_MSIX] = "MSI-X",
+ [EFX_INT_MODE_MSI] = "MSI",
+ [EFX_INT_MODE_LEGACY] = "legacy",
+};
+
+const unsigned int efx_reset_type_max = RESET_TYPE_MAX;
+const char *efx_reset_type_names[] = {
+ [RESET_TYPE_INVISIBLE] = "INVISIBLE",
+ [RESET_TYPE_ALL] = "ALL",
+ [RESET_TYPE_WORLD] = "WORLD",
+ [RESET_TYPE_DISABLE] = "DISABLE",
+ [RESET_TYPE_TX_WATCHDOG] = "TX_WATCHDOG",
+ [RESET_TYPE_INT_ERROR] = "INT_ERROR",
+ [RESET_TYPE_RX_RECOVERY] = "RX_RECOVERY",
+ [RESET_TYPE_RX_DESC_FETCH] = "RX_DESC_FETCH",
+ [RESET_TYPE_TX_DESC_FETCH] = "TX_DESC_FETCH",
+ [RESET_TYPE_TX_SKIP] = "TX_SKIP",
+ [RESET_TYPE_MC_FAILURE] = "MC_FAILURE",
+};
#define EFX_MAX_MTU (9 * 1024)
@@ -145,7 +206,8 @@ static void efx_fini_channels(struct efx_nic *efx);
#define EFX_ASSERT_RESET_SERIALISED(efx) \
do { \
- if (efx->state == STATE_RUNNING) \
+ if ((efx->state == STATE_RUNNING) || \
+ (efx->state == STATE_DISABLED)) \
ASSERT_RTNL(); \
} while (0)
@@ -171,7 +233,7 @@ static int efx_process_channel(struct efx_channel *channel, int rx_quota)
!channel->enabled))
return 0;
- rx_packets = falcon_process_eventq(channel, rx_quota);
+ rx_packets = efx_nic_process_eventq(channel, rx_quota);
if (rx_packets == 0)
return 0;
@@ -203,7 +265,7 @@ static inline void efx_channel_processed(struct efx_channel *channel)
channel->work_pending = false;
smp_wmb();
- falcon_eventq_read_ack(channel);
+ efx_nic_eventq_read_ack(channel);
}
/* NAPI poll handler
@@ -228,26 +290,20 @@ static int efx_poll(struct napi_struct *napi, int budget)
if (channel->used_flags & EFX_USED_BY_RX &&
efx->irq_rx_adaptive &&
unlikely(++channel->irq_count == 1000)) {
- unsigned old_irq_moderation = channel->irq_moderation;
-
if (unlikely(channel->irq_mod_score <
irq_adapt_low_thresh)) {
- channel->irq_moderation =
- max_t(int,
- channel->irq_moderation -
- FALCON_IRQ_MOD_RESOLUTION,
- FALCON_IRQ_MOD_RESOLUTION);
+ if (channel->irq_moderation > 1) {
+ channel->irq_moderation -= 1;
+ efx->type->push_irq_moderation(channel);
+ }
} else if (unlikely(channel->irq_mod_score >
irq_adapt_high_thresh)) {
- channel->irq_moderation =
- min(channel->irq_moderation +
- FALCON_IRQ_MOD_RESOLUTION,
- efx->irq_rx_moderation);
+ if (channel->irq_moderation <
+ efx->irq_rx_moderation) {
+ channel->irq_moderation += 1;
+ efx->type->push_irq_moderation(channel);
+ }
}
-
- if (channel->irq_moderation != old_irq_moderation)
- falcon_set_int_moderation(channel);
-
channel->irq_count = 0;
channel->irq_mod_score = 0;
}
@@ -280,7 +336,7 @@ void efx_process_channel_now(struct efx_channel *channel)
BUG_ON(!channel->enabled);
/* Disable interrupts and wait for ISRs to complete */
- falcon_disable_interrupts(efx);
+ efx_nic_disable_interrupts(efx);
if (efx->legacy_irq)
synchronize_irq(efx->legacy_irq);
if (channel->irq)
@@ -290,14 +346,14 @@ void efx_process_channel_now(struct efx_channel *channel)
napi_disable(&channel->napi_str);
/* Poll the channel */
- efx_process_channel(channel, efx->type->evq_size);
+ efx_process_channel(channel, EFX_EVQ_SIZE);
/* Ack the eventq. This may cause an interrupt to be generated
* when they are reenabled */
efx_channel_processed(channel);
napi_enable(&channel->napi_str);
- falcon_enable_interrupts(efx);
+ efx_nic_enable_interrupts(efx);
}
/* Create event queue
@@ -309,7 +365,7 @@ static int efx_probe_eventq(struct efx_channel *channel)
{
EFX_LOG(channel->efx, "chan %d create event queue\n", channel->channel);
- return falcon_probe_eventq(channel);
+ return efx_nic_probe_eventq(channel);
}
/* Prepare channel's event queue */
@@ -319,21 +375,21 @@ static void efx_init_eventq(struct efx_channel *channel)
channel->eventq_read_ptr = 0;
- falcon_init_eventq(channel);
+ efx_nic_init_eventq(channel);
}
static void efx_fini_eventq(struct efx_channel *channel)
{
EFX_LOG(channel->efx, "chan %d fini event queue\n", channel->channel);
- falcon_fini_eventq(channel);
+ efx_nic_fini_eventq(channel);
}
static void efx_remove_eventq(struct efx_channel *channel)
{
EFX_LOG(channel->efx, "chan %d remove event queue\n", channel->channel);
- falcon_remove_eventq(channel);
+ efx_nic_remove_eventq(channel);
}
/**************************************************************************
@@ -499,7 +555,7 @@ static void efx_fini_channels(struct efx_nic *efx)
EFX_ASSERT_RESET_SERIALISED(efx);
BUG_ON(efx->port_enabled);
- rc = falcon_flush_queues(efx);
+ rc = efx_nic_flush_queues(efx);
if (rc)
EFX_ERR(efx, "failed to flush queues\n");
else
@@ -547,8 +603,10 @@ void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay)
* netif_carrier_on/off) of the link status, and also maintains the
* link status's stop on the port's TX queue.
*/
-static void efx_link_status_changed(struct efx_nic *efx)
+void efx_link_status_changed(struct efx_nic *efx)
{
+ struct efx_link_state *link_state = &efx->link_state;
+
/* SFC Bug 5356: A net_dev notifier is registered, so we must ensure
* that no events are triggered between unregister_netdev() and the
* driver unloading. A more general condition is that NETDEV_CHANGE
@@ -561,19 +619,19 @@ static void efx_link_status_changed(struct efx_nic *efx)
return;
}
- if (efx->link_up != netif_carrier_ok(efx->net_dev)) {
+ if (link_state->up != netif_carrier_ok(efx->net_dev)) {
efx->n_link_state_changes++;
- if (efx->link_up)
+ if (link_state->up)
netif_carrier_on(efx->net_dev);
else
netif_carrier_off(efx->net_dev);
}
/* Status message for kernel log */
- if (efx->link_up) {
+ if (link_state->up) {
EFX_INFO(efx, "link up at %uMbps %s-duplex (MTU %d)%s\n",
- efx->link_speed, efx->link_fd ? "full" : "half",
+ link_state->speed, link_state->fd ? "full" : "half",
efx->net_dev->mtu,
(efx->promiscuous ? " [PROMISC]" : ""));
} else {
@@ -582,16 +640,49 @@ static 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);
-/* This call reinitialises the MAC to pick up new PHY settings. The
- * caller must hold the mac_lock */
-void __efx_reconfigure_port(struct efx_nic *efx)
+/* Push loopback/power/transmit disable settings to the PHY, and reconfigure
+ * the MAC appropriately. All other PHY configuration changes are pushed
+ * 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",
- raw_smp_processor_id());
+ WARN_ON(!mutex_is_locked(&efx->mac_lock));
/* Serialise the promiscuous flag with efx_set_multicast_list. */
if (efx_dev_registered(efx)) {
@@ -599,61 +690,48 @@ void __efx_reconfigure_port(struct efx_nic *efx)
netif_addr_unlock_bh(efx->net_dev);
}
- falcon_deconfigure_mac_wrapper(efx);
-
- /* Reconfigure the PHY, disabling transmit in mac level loopback. */
+ /* Disable PHY transmit in mac level loopbacks */
+ phy_mode = efx->phy_mode;
if (LOOPBACK_INTERNAL(efx))
efx->phy_mode |= PHY_MODE_TX_DISABLED;
else
efx->phy_mode &= ~PHY_MODE_TX_DISABLED;
- efx->phy_op->reconfigure(efx);
- if (falcon_switch_mac(efx))
- goto fail;
+ rc = efx->type->reconfigure_port(efx);
- efx->mac_op->reconfigure(efx);
-
- /* Inform kernel of loss/gain of carrier */
- efx_link_status_changed(efx);
- return;
+ if (rc)
+ efx->phy_mode = phy_mode;
-fail:
- EFX_ERR(efx, "failed to reconfigure MAC\n");
- efx->port_enabled = false;
- efx_fini_port(efx);
+ return rc;
}
/* Reinitialise the MAC to pick up new PHY settings, even if the port is
* disabled. */
-void efx_reconfigure_port(struct efx_nic *efx)
+int efx_reconfigure_port(struct efx_nic *efx)
{
+ int rc;
+
EFX_ASSERT_RESET_SERIALISED(efx);
mutex_lock(&efx->mac_lock);
- __efx_reconfigure_port(efx);
+ rc = __efx_reconfigure_port(efx);
mutex_unlock(&efx->mac_lock);
-}
-
-/* Asynchronous efx_reconfigure_port work item. To speed up efx_flush_all()
- * we don't efx_reconfigure_port() if the port is disabled. Care is taken
- * in efx_stop_all() and efx_start_port() to prevent PHY events being lost */
-static void efx_phy_work(struct work_struct *data)
-{
- struct efx_nic *efx = container_of(data, struct efx_nic, phy_work);
- mutex_lock(&efx->mac_lock);
- if (efx->port_enabled)
- __efx_reconfigure_port(efx);
- mutex_unlock(&efx->mac_lock);
+ return rc;
}
+/* Asynchronous work item for changing MAC promiscuity and multicast
+ * hash. Avoid a drain/rx_ingress enable by reconfiguring the current
+ * MAC directly. */
static void efx_mac_work(struct work_struct *data)
{
struct efx_nic *efx = container_of(data, struct efx_nic, mac_work);
mutex_lock(&efx->mac_lock);
- if (efx->port_enabled)
- efx->mac_op->irq(efx);
+ if (efx->port_enabled) {
+ efx->type->push_multicast_hash(efx);
+ efx->mac_op->reconfigure(efx);
+ }
mutex_unlock(&efx->mac_lock);
}
@@ -663,8 +741,8 @@ static int efx_probe_port(struct efx_nic *efx)
EFX_LOG(efx, "create port\n");
- /* Connect up MAC/PHY operations table and read MAC address */
- rc = falcon_probe_port(efx);
+ /* Connect up MAC/PHY operations table */
+ rc = efx->type->probe_port(efx);
if (rc)
goto err;
@@ -699,29 +777,33 @@ static int efx_init_port(struct efx_nic *efx)
EFX_LOG(efx, "init port\n");
- rc = efx->phy_op->init(efx);
- if (rc)
- return rc;
mutex_lock(&efx->mac_lock);
- efx->phy_op->reconfigure(efx);
- rc = falcon_switch_mac(efx);
- mutex_unlock(&efx->mac_lock);
+
+ rc = efx->phy_op->init(efx);
if (rc)
- goto fail;
- efx->mac_op->reconfigure(efx);
+ goto fail1;
efx->port_initialized = true;
- efx_stats_enable(efx);
+
+ /* 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);
return 0;
-fail:
+fail2:
efx->phy_op->fini(efx);
+fail1:
+ mutex_unlock(&efx->mac_lock);
return rc;
}
-/* Allow efx_reconfigure_port() to be scheduled, and close the window
- * between efx_stop_port and efx_flush_all whereby a previously scheduled
- * efx_phy_work()/efx_mac_work() may have been cancelled */
static void efx_start_port(struct efx_nic *efx)
{
EFX_LOG(efx, "start port\n");
@@ -729,15 +811,16 @@ static void efx_start_port(struct efx_nic *efx)
mutex_lock(&efx->mac_lock);
efx->port_enabled = true;
- __efx_reconfigure_port(efx);
- efx->mac_op->irq(efx);
+
+ /* efx_mac_work() might have been scheduled after efx_stop_port(),
+ * and then cancelled by efx_flush_all() */
+ efx->type->push_multicast_hash(efx);
+ efx->mac_op->reconfigure(efx);
+
mutex_unlock(&efx->mac_lock);
}
-/* Prevent efx_phy_work, efx_mac_work, and efx_monitor() from executing,
- * and efx_set_multicast_list() from scheduling efx_phy_work. efx_phy_work
- * and efx_mac_work may still be scheduled via NAPI processing until
- * efx_flush_all() is called */
+/* Prevent efx_mac_work() and efx_monitor() from working */
static void efx_stop_port(struct efx_nic *efx)
{
EFX_LOG(efx, "stop port\n");
@@ -760,11 +843,10 @@ static void efx_fini_port(struct efx_nic *efx)
if (!efx->port_initialized)
return;
- efx_stats_disable(efx);
efx->phy_op->fini(efx);
efx->port_initialized = false;
- efx->link_up = false;
+ efx->link_state.up = false;
efx_link_status_changed(efx);
}
@@ -772,7 +854,7 @@ static void efx_remove_port(struct efx_nic *efx)
{
EFX_LOG(efx, "destroying port\n");
- falcon_remove_port(efx);
+ efx->type->remove_port(efx);
}
/**************************************************************************
@@ -824,9 +906,8 @@ static int efx_init_io(struct efx_nic *efx)
goto fail2;
}
- efx->membase_phys = pci_resource_start(efx->pci_dev,
- efx->type->mem_bar);
- rc = pci_request_region(pci_dev, efx->type->mem_bar, "sfc");
+ efx->membase_phys = pci_resource_start(efx->pci_dev, EFX_MEM_BAR);
+ rc = pci_request_region(pci_dev, EFX_MEM_BAR, "sfc");
if (rc) {
EFX_ERR(efx, "request for memory BAR failed\n");
rc = -EIO;
@@ -835,21 +916,20 @@ static int efx_init_io(struct efx_nic *efx)
efx->membase = ioremap_nocache(efx->membase_phys,
efx->type->mem_map_size);
if (!efx->membase) {
- EFX_ERR(efx, "could not map memory BAR %d at %llx+%x\n",
- efx->type->mem_bar,
+ EFX_ERR(efx, "could not map memory BAR at %llx+%x\n",
(unsigned long long)efx->membase_phys,
efx->type->mem_map_size);
rc = -ENOMEM;
goto fail4;
}
- EFX_LOG(efx, "memory BAR %u at %llx+%x (virtual %p)\n",
- efx->type->mem_bar, (unsigned long long)efx->membase_phys,
+ EFX_LOG(efx, "memory BAR at %llx+%x (virtual %p)\n",
+ (unsigned long long)efx->membase_phys,
efx->type->mem_map_size, efx->membase);
return 0;
fail4:
- pci_release_region(efx->pci_dev, efx->type->mem_bar);
+ pci_release_region(efx->pci_dev, EFX_MEM_BAR);
fail3:
efx->membase_phys = 0;
fail2:
@@ -868,7 +948,7 @@ static void efx_fini_io(struct efx_nic *efx)
}
if (efx->membase_phys) {
- pci_release_region(efx->pci_dev, efx->type->mem_bar);
+ pci_release_region(efx->pci_dev, EFX_MEM_BAR);
efx->membase_phys = 0;
}
@@ -1011,7 +1091,7 @@ static int efx_probe_nic(struct efx_nic *efx)
EFX_LOG(efx, "creating NIC\n");
/* Carry out hardware-type specific initialisation */
- rc = falcon_probe_nic(efx);
+ rc = efx->type->probe(efx);
if (rc)
return rc;
@@ -1032,7 +1112,7 @@ static void efx_remove_nic(struct efx_nic *efx)
EFX_LOG(efx, "destroying NIC\n");
efx_remove_interrupts(efx);
- falcon_remove_nic(efx);
+ efx->type->remove(efx);
}
/**************************************************************************
@@ -1112,12 +1192,31 @@ static void efx_start_all(struct efx_nic *efx)
efx_for_each_channel(channel, efx)
efx_start_channel(channel);
- falcon_enable_interrupts(efx);
-
- /* Start hardware monitor if we're in RUNNING */
- if (efx->state == STATE_RUNNING)
+ efx_nic_enable_interrupts(efx);
+
+ /* Switch to event based MCDI completions after enabling interrupts.
+ * If a reset has been scheduled, then we need to stay in polled mode.
+ * Rather than serialising efx_mcdi_mode_event() [which sleeps] and
+ * reset_pending [modified from an atomic context], we instead guarantee
+ * that efx_mcdi_mode_poll() isn't reverted erroneously */
+ efx_mcdi_mode_event(efx);
+ if (efx->reset_pending != RESET_TYPE_NONE)
+ efx_mcdi_mode_poll(efx);
+
+ /* Start the hardware monitor if there is one. Otherwise (we're link
+ * event driven), we have to poll the PHY because after an event queue
+ * flush, we could have a missed a link state change */
+ if (efx->type->monitor != NULL) {
queue_delayed_work(efx->workqueue, &efx->monitor_work,
efx_monitor_interval);
+ } else {
+ mutex_lock(&efx->mac_lock);
+ if (efx->phy_op->poll(efx))
+ efx_link_status_changed(efx);
+ mutex_unlock(&efx->mac_lock);
+ }
+
+ efx->type->start_stats(efx);
}
/* Flush all delayed work. Should only be called when no more delayed work
@@ -1136,8 +1235,6 @@ static void efx_flush_all(struct efx_nic *efx)
/* Stop scheduled port reconfigurations */
cancel_work_sync(&efx->mac_work);
- cancel_work_sync(&efx->phy_work);
-
}
/* Quiesce hardware and software without bringing the link down.
@@ -1155,8 +1252,13 @@ static void efx_stop_all(struct efx_nic *efx)
if (!efx->port_enabled)
return;
+ efx->type->stop_stats(efx);
+
+ /* Switch to MCDI polling on Siena before disabling interrupts */
+ efx_mcdi_mode_poll(efx);
+
/* Disable interrupts and wait for ISR to complete */
- falcon_disable_interrupts(efx);
+ efx_nic_disable_interrupts(efx);
if (efx->legacy_irq)
synchronize_irq(efx->legacy_irq);
efx_for_each_channel(channel, efx) {
@@ -1173,15 +1275,9 @@ static void efx_stop_all(struct efx_nic *efx)
* window to loose phy events */
efx_stop_port(efx);
- /* Flush efx_phy_work, efx_mac_work, refill_workqueue, monitor_work */
+ /* Flush efx_mac_work(), refill_workqueue, monitor_work */
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
* timer isn't ticking over the flush */
if (efx_dev_registered(efx)) {
@@ -1201,41 +1297,39 @@ static void efx_remove_all(struct efx_nic *efx)
efx_remove_nic(efx);
}
-/* A convinience function to safely flush all the queues */
-void efx_flush_queues(struct efx_nic *efx)
-{
- EFX_ASSERT_RESET_SERIALISED(efx);
-
- efx_stop_all(efx);
-
- efx_fini_channels(efx);
- efx_init_channels(efx);
-
- efx_start_all(efx);
-}
-
/**************************************************************************
*
* Interrupt moderation
*
**************************************************************************/
+static unsigned irq_mod_ticks(int usecs, int resolution)
+{
+ if (usecs <= 0)
+ return 0; /* cannot receive interrupts ahead of time :-) */
+ if (usecs < resolution)
+ return 1; /* never round down to 0 */
+ return usecs / resolution;
+}
+
/* Set interrupt moderation parameters */
void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs,
bool rx_adaptive)
{
struct efx_tx_queue *tx_queue;
struct efx_rx_queue *rx_queue;
+ unsigned tx_ticks = irq_mod_ticks(tx_usecs, EFX_IRQ_MOD_RESOLUTION);
+ unsigned rx_ticks = irq_mod_ticks(rx_usecs, EFX_IRQ_MOD_RESOLUTION);
EFX_ASSERT_RESET_SERIALISED(efx);
efx_for_each_tx_queue(tx_queue, efx)
- tx_queue->channel->irq_moderation = tx_usecs;
+ tx_queue->channel->irq_moderation = tx_ticks;
efx->irq_rx_adaptive = rx_adaptive;
- efx->irq_rx_moderation = rx_usecs;
+ efx->irq_rx_moderation = rx_ticks;
efx_for_each_rx_queue(rx_queue, efx)
- rx_queue->channel->irq_moderation = rx_usecs;
+ rx_queue->channel->irq_moderation = rx_ticks;
}
/**************************************************************************
@@ -1250,10 +1344,10 @@ static void efx_monitor(struct work_struct *data)
{
struct efx_nic *efx = container_of(data, struct efx_nic,
monitor_work.work);
- int rc;
EFX_TRACE(efx, "hardware monitor executing on CPU %d\n",
raw_smp_processor_id());
+ BUG_ON(efx->type->monitor == NULL);
/* If the mac_lock is already held then it is likely a port
* reconfiguration is already in place, which will likely do
@@ -1262,15 +1356,7 @@ static void efx_monitor(struct work_struct *data)
goto out_requeue;
if (!efx->port_enabled)
goto out_unlock;
- rc = efx->board_info.monitor(efx);
- if (rc) {
- EFX_ERR(efx, "Board sensor %s; shutting down PHY\n",
- (rc == -ERANGE) ? "reported fault" : "failed");
- efx->phy_mode |= PHY_MODE_LOW_POWER;
- falcon_sim_phy_event(efx);
- }
- efx->phy_op->poll(efx);
- efx->mac_op->poll(efx);
+ efx->type->monitor(efx);
out_unlock:
mutex_unlock(&efx->mac_lock);
@@ -1374,6 +1460,12 @@ static int efx_net_open(struct net_device *net_dev)
return -EIO;
if (efx->phy_mode & PHY_MODE_SPECIAL)
return -EBUSY;
+ if (efx_mcdi_poll_reboot(efx) && efx_reset(efx, RESET_TYPE_ALL))
+ return -EIO;
+
+ /* Notify the kernel of the link state polled during driver load,
+ * before the monitor starts running */
+ efx_link_status_changed(efx);
efx_start_all(efx);
return 0;
@@ -1400,20 +1492,6 @@ static int efx_net_stop(struct net_device *net_dev)
return 0;
}
-void efx_stats_disable(struct efx_nic *efx)
-{
- spin_lock(&efx->stats_lock);
- ++efx->stats_disable_count;
- spin_unlock(&efx->stats_lock);
-}
-
-void efx_stats_enable(struct efx_nic *efx)
-{
- spin_lock(&efx->stats_lock);
- --efx->stats_disable_count;
- spin_unlock(&efx->stats_lock);
-}
-
/* Context: process, dev_base_lock or RTNL held, non-blocking. */
static struct net_device_stats *efx_net_stats(struct net_device *net_dev)
{
@@ -1421,17 +1499,9 @@ static struct net_device_stats *efx_net_stats(struct net_device *net_dev)
struct efx_mac_stats *mac_stats = &efx->mac_stats;
struct net_device_stats *stats = &net_dev->stats;
- /* Update stats if possible, but do not wait if another thread
- * is updating them or if MAC stats fetches are temporarily
- * disabled; slightly stale stats are acceptable.
- */
- if (!spin_trylock(&efx->stats_lock))
- return stats;
- if (!efx->stats_disable_count) {
- efx->mac_op->update_stats(efx);
- falcon_update_nic_stats(efx);
- }
- spin_unlock(&efx->stats_lock);
+ spin_lock_bh(&efx->stats_lock);
+ efx->type->update_stats(efx);
+ spin_unlock_bh(&efx->stats_lock);
stats->rx_packets = mac_stats->rx_packets;
stats->tx_packets = mac_stats->tx_packets;
@@ -1490,7 +1560,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_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;
+ efx->mac_op->reconfigure(efx);
+ mutex_unlock(&efx->mac_lock);
+
efx_init_channels(efx);
efx_start_all(efx);
@@ -1514,7 +1591,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);
/* Reconfigure the MAC */
- efx_reconfigure_port(efx);
+ mutex_lock(&efx->mac_lock);
+ efx->mac_op->reconfigure(efx);
+ mutex_unlock(&efx->mac_lock);
return 0;
}
@@ -1525,16 +1604,14 @@ static void efx_set_multicast_list(struct net_device *net_dev)
struct efx_nic *efx = netdev_priv(net_dev);
struct dev_mc_list *mc_list = net_dev->mc_list;
union efx_multicast_hash *mc_hash = &efx->multicast_hash;
- bool promiscuous = !!(net_dev->flags & IFF_PROMISC);
- bool changed = (efx->promiscuous != promiscuous);
u32 crc;
int bit;
int i;
- efx->promiscuous = promiscuous;
+ efx->promiscuous = !!(net_dev->flags & IFF_PROMISC);
/* Build multicast hash table */
- if (promiscuous || (net_dev->flags & IFF_ALLMULTI)) {
+ if (efx->promiscuous || (net_dev->flags & IFF_ALLMULTI)) {
memset(mc_hash, 0xff, sizeof(*mc_hash));
} else {
memset(mc_hash, 0x00, sizeof(*mc_hash));
@@ -1544,17 +1621,17 @@ static void efx_set_multicast_list(struct net_device *net_dev)
set_bit_le(bit, mc_hash->byte);
mc_list = mc_list->next;
}
- }
-
- if (!efx->port_enabled)
- /* Delay pushing settings until efx_start_port() */
- return;
- if (changed)
- queue_work(efx->workqueue, &efx->phy_work);
+ /* Broadcast packets go through the multicast hash filter.
+ * ether_crc_le() of the broadcast address is 0xbe2612ff
+ * so we always add bit 0xff to the mask.
+ */
+ set_bit_le(0xff, mc_hash->byte);
+ }
- /* Create and activate new global multicast hash table */
- falcon_set_multicast_hash(efx);
+ if (efx->port_enabled)
+ queue_work(efx->workqueue, &efx->mac_work);
+ /* Otherwise efx_start_port() will do this */
}
static const struct net_device_ops efx_netdev_ops = {
@@ -1683,21 +1760,18 @@ static void efx_unregister_netdev(struct efx_nic *efx)
/* Tears down the entire software state and most of the hardware state
* before reset. */
-void efx_reset_down(struct efx_nic *efx, enum reset_type method,
- struct ethtool_cmd *ecmd)
+void efx_reset_down(struct efx_nic *efx, enum reset_type method)
{
EFX_ASSERT_RESET_SERIALISED(efx);
- efx_stats_disable(efx);
efx_stop_all(efx);
mutex_lock(&efx->mac_lock);
mutex_lock(&efx->spi_lock);
- efx->phy_op->get_settings(efx, ecmd);
-
efx_fini_channels(efx);
if (efx->port_initialized && method != RESET_TYPE_INVISIBLE)
efx->phy_op->fini(efx);
+ efx->type->fini(efx);
}
/* This function will always ensure that the locks acquired in
@@ -1705,79 +1779,67 @@ void efx_reset_down(struct efx_nic *efx, enum reset_type method,
* that we were unable to reinitialise the hardware, and the
* driver should be disabled. If ok is false, then the rx and tx
* engines are not restarted, pending a RESET_DISABLE. */
-int efx_reset_up(struct efx_nic *efx, enum reset_type method,
- struct ethtool_cmd *ecmd, bool ok)
+int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok)
{
int rc;
EFX_ASSERT_RESET_SERIALISED(efx);
- rc = falcon_init_nic(efx);
+ rc = efx->type->init(efx);
if (rc) {
EFX_ERR(efx, "failed to initialise NIC\n");
- ok = false;
+ goto fail;
}
+ if (!ok)
+ goto fail;
+
if (efx->port_initialized && method != RESET_TYPE_INVISIBLE) {
- if (ok) {
- rc = efx->phy_op->init(efx);
- if (rc)
- ok = false;
- }
- if (!ok)
- efx->port_initialized = false;
+ rc = efx->phy_op->init(efx);
+ if (rc)
+ goto fail;
+ if (efx->phy_op->reconfigure(efx))
+ EFX_ERR(efx, "could not restore PHY settings\n");
}
- if (ok) {
- efx_init_channels(efx);
+ efx->mac_op->reconfigure(efx);
- if (efx->phy_op->set_settings(efx, ecmd))
- EFX_ERR(efx, "could not restore PHY settings\n");
- }
+ efx_init_channels(efx);
+
+ mutex_unlock(&efx->spi_lock);
+ mutex_unlock(&efx->mac_lock);
+
+ efx_start_all(efx);
+
+ return 0;
+
+fail:
+ efx->port_initialized = false;
mutex_unlock(&efx->spi_lock);
mutex_unlock(&efx->mac_lock);
- if (ok) {
- efx_start_all(efx);
- efx_stats_enable(efx);
- }
return rc;
}
-/* Reset the NIC as transparently as possible. Do not reset the PHY
- * Note that the reset may fail, in which case the card will be left
- * in a most-probably-unusable state.
+/* Reset the NIC using the specified method. Note that the reset may
+ * fail, in which case the card will be left in an unusable state.
*
- * This function will sleep. You cannot reset from within an atomic
- * state; use efx_schedule_reset() instead.
- *
- * Grabs the rtnl_lock.
+ * Caller must hold the rtnl_lock.
*/
-static int efx_reset(struct efx_nic *efx)
+int efx_reset(struct efx_nic *efx, enum reset_type method)
{
- struct ethtool_cmd ecmd;
- enum reset_type method = efx->reset_pending;
- int rc = 0;
+ int rc, rc2;
+ bool disabled;
- /* Serialise with kernel interfaces */
- rtnl_lock();
+ EFX_INFO(efx, "resetting (%s)\n", RESET_TYPE(method));
- /* If we're not RUNNING then don't reset. Leave the reset_pending
- * flag set so that efx_pci_probe_main will be retried */
- if (efx->state != STATE_RUNNING) {
- EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n");
- goto out_unlock;
- }
+ efx_reset_down(efx, method);
- EFX_INFO(efx, "resetting (%d)\n", method);
-
- efx_reset_down(efx, method, &ecmd);
-
- rc = falcon_reset_hw(efx, method);
+ rc = efx->type->reset(efx, method);
if (rc) {
EFX_ERR(efx, "failed to reset hardware\n");
- goto out_disable;
+ goto out;
}
/* Allow resets to be rescheduled. */
@@ -1789,25 +1851,22 @@ static int efx_reset(struct efx_nic *efx)
* can respond to requests. */
pci_set_master(efx->pci_dev);
+out:
/* Leave device stopped if necessary */
- if (method == RESET_TYPE_DISABLE) {
- efx_reset_up(efx, method, &ecmd, false);
- rc = -EIO;
- } else {
- rc = efx_reset_up(efx, method, &ecmd, true);
+ disabled = rc || method == RESET_TYPE_DISABLE;
+ rc2 = efx_reset_up(efx, method, !disabled);
+ if (rc2) {
+ disabled = true;
+ if (!rc)
+ rc = rc2;
}
-out_disable:
- if (rc) {
+ if (disabled) {
EFX_ERR(efx, "has been disabled\n");
efx->state = STATE_DISABLED;
- dev_close(efx->net_dev);
} else {
EFX_LOG(efx, "reset complete\n");
}
-
-out_unlock:
- rtnl_unlock();
return rc;
}
@@ -1816,9 +1875,19 @@ out_unlock:
*/
static void efx_reset_work(struct work_struct *data)
{
- struct efx_nic *nic = container_of(data, struct efx_nic, reset_work);
+ struct efx_nic *efx = container_of(data, struct efx_nic, reset_work);
+
+ /* If we're not RUNNING then don't reset. Leave the reset_pending
+ * flag set so that efx_pci_probe_main will be retried */
+ if (efx->state != STATE_RUNNING) {
+ EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n");
+ return;
+ }
- efx_reset(nic);
+ rtnl_lock();
+ if (efx_reset(efx, efx->reset_pending))
+ dev_close(efx->net_dev);
+ rtnl_unlock();
}
void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
@@ -1843,18 +1912,24 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
case RESET_TYPE_TX_SKIP:
method = RESET_TYPE_INVISIBLE;
break;
+ case RESET_TYPE_MC_FAILURE:
default:
method = RESET_TYPE_ALL;
break;
}
if (method != type)
- EFX_LOG(efx, "scheduling reset (%d:%d)\n", type, method);
+ EFX_LOG(efx, "scheduling %s reset for %s\n",
+ RESET_TYPE(method), RESET_TYPE(type));
else
- EFX_LOG(efx, "scheduling reset (%d)\n", method);
+ EFX_LOG(efx, "scheduling %s reset\n", RESET_TYPE(method));
efx->reset_pending = method;
+ /* efx_process_channel() will no longer read events once a
+ * reset is scheduled. So switch back to poll'd MCDI completions. */
+ efx_mcdi_mode_poll(efx);
+
queue_work(reset_workqueue, &efx->reset_work);
}
@@ -1867,15 +1942,19 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
/* PCI device ID table */
static struct pci_device_id efx_pci_table[] __devinitdata = {
{PCI_DEVICE(EFX_VENDID_SFC, FALCON_A_P_DEVID),
- .driver_data = (unsigned long) &falcon_a_nic_type},
+ .driver_data = (unsigned long) &falcon_a1_nic_type},
{PCI_DEVICE(EFX_VENDID_SFC, FALCON_B_P_DEVID),
- .driver_data = (unsigned long) &falcon_b_nic_type},
+ .driver_data = (unsigned long) &falcon_b0_nic_type},
+ {PCI_DEVICE(EFX_VENDID_SFC, BETHPAGE_A_P_DEVID),
+ .driver_data = (unsigned long) &siena_a0_nic_type},
+ {PCI_DEVICE(EFX_VENDID_SFC, SIENA_A_P_DEVID),
+ .driver_data = (unsigned long) &siena_a0_nic_type},
{0} /* end of list */
};
/**************************************************************************
*
- * Dummy PHY/MAC/Board operations
+ * Dummy PHY/MAC operations
*
* Can be used for some unimplemented operations
* Needed so all function pointers are valid and do not have to be tested
@@ -1887,29 +1966,19 @@ int efx_port_dummy_op_int(struct efx_nic *efx)
return 0;
}
void efx_port_dummy_op_void(struct efx_nic *efx) {}
-void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink) {}
-
-static struct efx_mac_operations efx_dummy_mac_operations = {
- .reconfigure = efx_port_dummy_op_void,
- .poll = efx_port_dummy_op_void,
- .irq = efx_port_dummy_op_void,
-};
+void efx_port_dummy_op_set_id_led(struct efx_nic *efx, enum efx_led_mode mode)
+{
+}
+bool efx_port_dummy_op_poll(struct efx_nic *efx)
+{
+ return false;
+}
static struct efx_phy_operations efx_dummy_phy_operations = {
.init = efx_port_dummy_op_int,
- .reconfigure = efx_port_dummy_op_void,
- .poll = efx_port_dummy_op_void,
+ .reconfigure = efx_port_dummy_op_int,
+ .poll = efx_port_dummy_op_poll,
.fini = efx_port_dummy_op_void,
- .clear_interrupt = efx_port_dummy_op_void,
-};
-
-static struct efx_board efx_dummy_board_info = {
- .init = efx_port_dummy_op_int,
- .init_leds = efx_port_dummy_op_void,
- .set_id_led = efx_port_dummy_op_blink,
- .monitor = efx_port_dummy_op_int,
- .blink = efx_port_dummy_op_blink,
- .fini = efx_port_dummy_op_void,
};
/**************************************************************************
@@ -1932,26 +2001,26 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
/* Initialise common structures */
memset(efx, 0, sizeof(*efx));
spin_lock_init(&efx->biu_lock);
- spin_lock_init(&efx->phy_lock);
+ mutex_init(&efx->mdio_lock);
mutex_init(&efx->spi_lock);
+#ifdef CONFIG_SFC_MTD
+ INIT_LIST_HEAD(&efx->mtd_list);
+#endif
INIT_WORK(&efx->reset_work, efx_reset_work);
INIT_DELAYED_WORK(&efx->monitor_work, efx_monitor);
efx->pci_dev = pci_dev;
efx->state = STATE_INIT;
efx->reset_pending = RESET_TYPE_NONE;
strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name));
- efx->board_info = efx_dummy_board_info;
efx->net_dev = net_dev;
efx->rx_checksum_enabled = true;
spin_lock_init(&efx->netif_stop_lock);
spin_lock_init(&efx->stats_lock);
- efx->stats_disable_count = 1;
mutex_init(&efx->mac_lock);
- efx->mac_op = &efx_dummy_mac_operations;
+ efx->mac_op = type->default_mac_ops;
efx->phy_op = &efx_dummy_phy_operations;
efx->mdio.dev = net_dev;
- INIT_WORK(&efx->phy_work, efx_phy_work);
INIT_WORK(&efx->mac_work, efx_mac_work);
atomic_set(&efx->netif_stop_count, 1);
@@ -1981,17 +2050,9 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
efx->type = type;
- /* Sanity-check NIC type */
- EFX_BUG_ON_PARANOID(efx->type->txd_ring_mask &
- (efx->type->txd_ring_mask + 1));
- EFX_BUG_ON_PARANOID(efx->type->rxd_ring_mask &
- (efx->type->rxd_ring_mask + 1));
- EFX_BUG_ON_PARANOID(efx->type->evq_size &
- (efx->type->evq_size - 1));
/* As close as we can get to guaranteeing that we don't overflow */
- EFX_BUG_ON_PARANOID(efx->type->evq_size <
- (efx->type->txd_ring_mask + 1 +
- efx->type->rxd_ring_mask + 1));
+ BUILD_BUG_ON(EFX_EVQ_SIZE < EFX_TXQ_SIZE + EFX_RXQ_SIZE);
+
EFX_BUG_ON_PARANOID(efx->type->phys_addr_channels > EFX_MAX_CHANNELS);
/* Higher numbered interrupt modes are less capable! */
@@ -2027,19 +2088,10 @@ static void efx_fini_struct(struct efx_nic *efx)
*/
static void efx_pci_remove_main(struct efx_nic *efx)
{
- EFX_ASSERT_RESET_SERIALISED(efx);
-
- /* Skip everything if we never obtained a valid membase */
- if (!efx->membase)
- return;
-
+ efx_nic_fini_interrupt(efx);
efx_fini_channels(efx);
efx_fini_port(efx);
-
- /* Shutdown the board, then the NIC and board state */
- efx->board_info.fini(efx);
- falcon_fini_interrupt(efx);
-
+ efx->type->fini(efx);
efx_fini_napi(efx);
efx_remove_all(efx);
}
@@ -2063,9 +2115,6 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
/* Allow any queued efx_resets() to complete */
rtnl_unlock();
- if (efx->membase == NULL)
- goto out;
-
efx_unregister_netdev(efx);
efx_mtd_remove(efx);
@@ -2078,7 +2127,6 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
efx_pci_remove_main(efx);
-out:
efx_fini_io(efx);
EFX_LOG(efx, "shutdown successful\n");
@@ -2103,39 +2151,31 @@ static int efx_pci_probe_main(struct efx_nic *efx)
if (rc)
goto fail2;
- /* Initialise the board */
- rc = efx->board_info.init(efx);
- if (rc) {
- EFX_ERR(efx, "failed to initialise board\n");
- goto fail3;
- }
-
- rc = falcon_init_nic(efx);
+ rc = efx->type->init(efx);
if (rc) {
EFX_ERR(efx, "failed to initialise NIC\n");
- goto fail4;
+ goto fail3;
}
rc = efx_init_port(efx);
if (rc) {
EFX_ERR(efx, "failed to initialise port\n");
- goto fail5;
+ goto fail4;
}
efx_init_channels(efx);
- rc = falcon_init_interrupt(efx);
+ rc = efx_nic_init_interrupt(efx);
if (rc)
- goto fail6;
+ goto fail5;
return 0;
- fail6:
+ fail5:
efx_fini_channels(efx);
efx_fini_port(efx);
- fail5:
fail4:
- efx->board_info.fini(efx);
+ efx->type->fini(efx);
fail3:
efx_fini_napi(efx);
fail2:
@@ -2165,9 +2205,11 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
net_dev = alloc_etherdev(sizeof(*efx));
if (!net_dev)
return -ENOMEM;
- net_dev->features |= (NETIF_F_IP_CSUM | NETIF_F_SG |
+ net_dev->features |= (type->offload_features | NETIF_F_SG |
NETIF_F_HIGHDMA | NETIF_F_TSO |
NETIF_F_GRO);
+ if (type->offload_features & NETIF_F_V6_CSUM)
+ net_dev->features |= NETIF_F_TSO6;
/* Mask for features that also apply to VLAN devices */
net_dev->vlan_features |= (NETIF_F_ALL_CSUM | NETIF_F_SG |
NETIF_F_HIGHDMA | NETIF_F_TSO);
@@ -2219,18 +2261,19 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
goto fail4;
}
- /* Switch to the running state before we expose the device to
- * the OS. This is to ensure that the initial gathering of
- * MAC stats succeeds. */
+ /* Switch to the running state before we expose the device to the OS,
+ * so that dev_open()|efx_start_all() will actually start the device */
efx->state = STATE_RUNNING;
- efx_mtd_probe(efx); /* allowed to fail */
-
rc = efx_register_netdev(efx);
if (rc)
goto fail5;
EFX_LOG(efx, "initialisation successful\n");
+
+ rtnl_lock();
+ efx_mtd_probe(efx); /* allowed to fail */
+ rtnl_unlock();
return 0;
fail5:
@@ -2246,11 +2289,107 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
return rc;
}
+static int efx_pm_freeze(struct device *dev)
+{
+ struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+
+ efx->state = STATE_FINI;
+
+ netif_device_detach(efx->net_dev);
+
+ efx_stop_all(efx);
+ efx_fini_channels(efx);
+
+ return 0;
+}
+
+static int efx_pm_thaw(struct device *dev)
+{
+ struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+
+ efx->state = STATE_INIT;
+
+ efx_init_channels(efx);
+
+ mutex_lock(&efx->mac_lock);
+ efx->phy_op->reconfigure(efx);
+ mutex_unlock(&efx->mac_lock);
+
+ efx_start_all(efx);
+
+ netif_device_attach(efx->net_dev);
+
+ efx->state = STATE_RUNNING;
+
+ efx->type->resume_wol(efx);
+
+ return 0;
+}
+
+static int efx_pm_poweroff(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct efx_nic *efx = pci_get_drvdata(pci_dev);
+
+ efx->type->fini(efx);
+
+ efx->reset_pending = RESET_TYPE_NONE;
+
+ pci_save_state(pci_dev);
+ return pci_set_power_state(pci_dev, PCI_D3hot);
+}
+
+/* Used for both resume and restore */
+static int efx_pm_resume(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct efx_nic *efx = pci_get_drvdata(pci_dev);
+ int rc;
+
+ rc = pci_set_power_state(pci_dev, PCI_D0);
+ if (rc)
+ return rc;
+ pci_restore_state(pci_dev);
+ rc = pci_enable_device(pci_dev);
+ if (rc)
+ return rc;
+ pci_set_master(efx->pci_dev);
+ rc = efx->type->reset(efx, RESET_TYPE_ALL);
+ if (rc)
+ return rc;
+ rc = efx->type->init(efx);
+ if (rc)
+ return rc;
+ efx_pm_thaw(dev);
+ return 0;
+}
+
+static int efx_pm_suspend(struct device *dev)
+{
+ int rc;
+
+ efx_pm_freeze(dev);
+ rc = efx_pm_poweroff(dev);
+ if (rc)
+ efx_pm_resume(dev);
+ return rc;
+}
+
+static struct dev_pm_ops efx_pm_ops = {
+ .suspend = efx_pm_suspend,
+ .resume = efx_pm_resume,
+ .freeze = efx_pm_freeze,
+ .thaw = efx_pm_thaw,
+ .poweroff = efx_pm_poweroff,
+ .restore = efx_pm_resume,
+};
+
static struct pci_driver efx_pci_driver = {
.name = EFX_DRIVER_NAME,
.id_table = efx_pci_table,
.probe = efx_pci_probe,
.remove = efx_pci_remove,
+ .driver.pm = &efx_pm_ops,
};
/**************************************************************************
@@ -2314,8 +2453,8 @@ static void __exit efx_exit_module(void)
module_init(efx_init_module);
module_exit(efx_exit_module);
-MODULE_AUTHOR("Michael Brown <mbrown@fensystems.co.uk> and "
- "Solarflare Communications");
+MODULE_AUTHOR("Solarflare Communications and "
+ "Michael Brown <mbrown@fensystems.co.uk>");
MODULE_DESCRIPTION("Solarflare Communications network driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, efx_pci_table);