summaryrefslogtreecommitdiffstats
path: root/drivers/net/sfc/ethtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/sfc/ethtool.c')
-rw-r--r--drivers/net/sfc/ethtool.c222
1 files changed, 163 insertions, 59 deletions
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
index 45018f283ff..6c0bbed8c47 100644
--- a/drivers/net/sfc/ethtool.c
+++ b/drivers/net/sfc/ethtool.c
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-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
@@ -10,30 +10,15 @@
#include <linux/netdevice.h>
#include <linux/ethtool.h>
-#include <linux/mdio.h>
#include <linux/rtnetlink.h>
#include "net_driver.h"
#include "workarounds.h"
#include "selftest.h"
#include "efx.h"
-#include "ethtool.h"
-#include "falcon.h"
+#include "nic.h"
#include "spi.h"
#include "mdio_10g.h"
-const char *efx_loopback_mode_names[] = {
- [LOOPBACK_NONE] = "NONE",
- [LOOPBACK_GMAC] = "GMAC",
- [LOOPBACK_XGMII] = "XGMII",
- [LOOPBACK_XGXS] = "XGXS",
- [LOOPBACK_XAUI] = "XAUI",
- [LOOPBACK_GPHY] = "GPHY",
- [LOOPBACK_PHYXS] = "PHYXS",
- [LOOPBACK_PCS] = "PCS",
- [LOOPBACK_PMAPMD] = "PMA/PMD",
- [LOOPBACK_NETWORK] = "NETWORK",
-};
-
struct ethtool_string {
char name[ETH_GSTRING_LEN];
};
@@ -167,6 +152,7 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = {
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc),
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err),
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tcp_udp_chksum_err),
+ EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_mcast_mismatch),
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc),
};
@@ -187,13 +173,15 @@ static int efx_ethtool_phys_id(struct net_device *net_dev, u32 count)
{
struct efx_nic *efx = netdev_priv(net_dev);
- efx->board_info.blink(efx, 1);
- set_current_state(TASK_INTERRUPTIBLE);
- if (count)
- schedule_timeout(count * HZ);
- else
- schedule();
- efx->board_info.blink(efx, 0);
+ do {
+ efx->type->set_id_led(efx, EFX_LED_ON);
+ schedule_timeout_interruptible(HZ / 2);
+
+ efx->type->set_id_led(efx, EFX_LED_OFF);
+ schedule_timeout_interruptible(HZ / 2);
+ } while (!signal_pending(current) && --count != 0);
+
+ efx->type->set_id_led(efx, EFX_LED_DEFAULT);
return 0;
}
@@ -202,6 +190,7 @@ int efx_ethtool_get_settings(struct net_device *net_dev,
struct ethtool_cmd *ecmd)
{
struct efx_nic *efx = netdev_priv(net_dev);
+ struct efx_link_state *link_state = &efx->link_state;
mutex_lock(&efx->mac_lock);
efx->phy_op->get_settings(efx, ecmd);
@@ -209,6 +198,13 @@ int efx_ethtool_get_settings(struct net_device *net_dev,
/* Falcon GMAC does not support 1000Mbps HD */
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;
}
@@ -230,9 +226,6 @@ int efx_ethtool_set_settings(struct net_device *net_dev,
mutex_lock(&efx->mac_lock);
rc = efx->phy_op->set_settings(efx, ecmd);
mutex_unlock(&efx->mac_lock);
- if (!rc)
- efx_reconfigure_port(efx);
-
return rc;
}
@@ -243,6 +236,9 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev,
strlcpy(info->driver, EFX_DRIVER_NAME, sizeof(info->driver));
strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version));
+ if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0)
+ siena_print_fwver(efx, info->fw_version,
+ sizeof(info->fw_version));
strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info));
}
@@ -289,7 +285,7 @@ static void efx_fill_test(unsigned int test_index,
#define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->queue
#define EFX_RX_QUEUE_NAME(_rx_queue) "rxq%d", _rx_queue->queue
#define EFX_LOOPBACK_NAME(_mode, _counter) \
- "loopback.%s." _counter, LOOPBACK_MODE_NAME(mode)
+ "loopback.%s." _counter, STRING_TABLE_LOOKUP(_mode, efx_loopback_mode)
/**
* efx_fill_loopback_test - fill in a block of loopback self-test entries
@@ -372,9 +368,21 @@ static int efx_ethtool_fill_self_tests(struct efx_nic *efx,
efx_fill_test(n++, strings, data, &tests->registers,
"core", 0, "registers", NULL);
- for (i = 0; i < efx->phy_op->num_tests; i++)
- efx_fill_test(n++, strings, data, &tests->phy[i],
- "phy", 0, efx->phy_op->test_names[i], NULL);
+ if (efx->phy_op->run_tests != NULL) {
+ EFX_BUG_ON_PARANOID(efx->phy_op->test_name == NULL);
+
+ for (i = 0; true; ++i) {
+ const char *name;
+
+ EFX_BUG_ON_PARANOID(i >= EFX_MAX_PHY_TESTS);
+ name = efx->phy_op->test_name(efx, i);
+ if (name == NULL)
+ break;
+
+ efx_fill_test(n++, strings, data, &tests->phy[i],
+ "phy", 0, name, NULL);
+ }
+ }
/* Loopback tests */
for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) {
@@ -463,6 +471,36 @@ static void efx_ethtool_get_stats(struct net_device *net_dev,
}
}
+static int efx_ethtool_set_tso(struct net_device *net_dev, u32 enable)
+{
+ struct efx_nic *efx __attribute__ ((unused)) = netdev_priv(net_dev);
+ unsigned long features;
+
+ features = NETIF_F_TSO;
+ if (efx->type->offload_features & NETIF_F_V6_CSUM)
+ features |= NETIF_F_TSO6;
+
+ if (enable)
+ net_dev->features |= features;
+ else
+ net_dev->features &= ~features;
+
+ return 0;
+}
+
+static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ unsigned long features = efx->type->offload_features & NETIF_F_ALL_CSUM;
+
+ if (enable)
+ net_dev->features |= features;
+ else
+ net_dev->features &= ~features;
+
+ return 0;
+}
+
static int efx_ethtool_set_rx_csum(struct net_device *net_dev, u32 enable)
{
struct efx_nic *efx = netdev_priv(net_dev);
@@ -537,7 +575,7 @@ static u32 efx_ethtool_get_link(struct net_device *net_dev)
{
struct efx_nic *efx = netdev_priv(net_dev);
- return efx->link_up;
+ return efx->link_state.up;
}
static int efx_ethtool_get_eeprom_len(struct net_device *net_dev)
@@ -562,7 +600,8 @@ static int efx_ethtool_get_eeprom(struct net_device *net_dev,
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
- rc = falcon_spi_read(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
+ rc = falcon_spi_read(efx, spi,
+ eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
eeprom->len, &len, buf);
mutex_unlock(&efx->spi_lock);
@@ -585,7 +624,8 @@ static int efx_ethtool_set_eeprom(struct net_device *net_dev,
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
- rc = falcon_spi_write(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
+ rc = falcon_spi_write(efx, spi,
+ eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
eeprom->len, &len, buf);
mutex_unlock(&efx->spi_lock);
@@ -618,6 +658,9 @@ static int efx_ethtool_get_coalesce(struct net_device *net_dev,
coalesce->use_adaptive_rx_coalesce = efx->irq_rx_adaptive;
coalesce->rx_coalesce_usecs_irq = efx->irq_rx_moderation;
+ coalesce->tx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION;
+ coalesce->rx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION;
+
return 0;
}
@@ -656,13 +699,8 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev,
}
efx_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive);
-
- /* Reset channel to pick up new moderation value. Note that
- * this may change the value of the irq_moderation field
- * (e.g. to allow for hardware timer granularity).
- */
efx_for_each_channel(channel, efx)
- falcon_set_int_moderation(channel);
+ efx->type->push_irq_moderation(channel);
return 0;
}
@@ -671,8 +709,12 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
struct ethtool_pauseparam *pause)
{
struct efx_nic *efx = netdev_priv(net_dev);
- enum efx_fc_type wanted_fc;
+ enum efx_fc_type wanted_fc, old_fc;
+ u32 old_adv;
bool reset;
+ int rc = 0;
+
+ mutex_lock(&efx->mac_lock);
wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) |
(pause->tx_pause ? EFX_FC_TX : 0) |
@@ -680,14 +722,14 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) {
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) &&
- (wanted_fc & EFX_FC_AUTO)) {
- EFX_LOG(efx, "PHY does not support flow control "
- "autonegotiation\n");
- return -EINVAL;
+ if ((wanted_fc & EFX_FC_AUTO) && !efx->link_advertising) {
+ EFX_LOG(efx, "Autonegotiation is disabled\n");
+ rc = -EINVAL;
+ goto out;
}
/* TX flow control may automatically turn itself off if the
@@ -697,27 +739,40 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
* and fix it be cycling transmit flow control on this end. */
reset = (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & EFX_FC_TX);
if (EFX_WORKAROUND_11482(efx) && reset) {
- if (falcon_rev(efx) >= FALCON_REV_B0) {
+ if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) {
/* Recover by resetting the EM block */
- if (efx->link_up)
- falcon_drain_tx_fifo(efx);
+ falcon_stop_nic_stats(efx);
+ falcon_drain_tx_fifo(efx);
+ efx->mac_op->reconfigure(efx);
+ falcon_start_nic_stats(efx);
} else {
/* Schedule a reset to recover */
efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
}
}
- /* Try to push the pause parameters */
- mutex_lock(&efx->mac_lock);
+ old_adv = efx->link_advertising;
+ 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;
- if (efx->phy_op->mmds & MDIO_DEVS_AN)
- mdio45_ethtool_spauseparam_an(&efx->mdio, pause);
- __efx_reconfigure_port(efx);
+ /* Reconfigure the MAC. The PHY *may* generate a link state change event
+ * if the user just changed the advertised capabilities, but there's no
+ * harm doing this twice */
+ efx->mac_op->reconfigure(efx);
+out:
mutex_unlock(&efx->mac_lock);
- return 0;
+ return rc;
}
static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
@@ -731,6 +786,50 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
}
+static void efx_ethtool_get_wol(struct net_device *net_dev,
+ struct ethtool_wolinfo *wol)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ return efx->type->get_wol(efx, wol);
+}
+
+
+static int efx_ethtool_set_wol(struct net_device *net_dev,
+ struct ethtool_wolinfo *wol)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ return efx->type->set_wol(efx, wol->wolopts);
+}
+
+extern int efx_ethtool_reset(struct net_device *net_dev, u32 *flags)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ enum reset_type method;
+ enum {
+ ETH_RESET_EFX_INVISIBLE = (ETH_RESET_DMA | ETH_RESET_FILTER |
+ ETH_RESET_OFFLOAD | ETH_RESET_MAC)
+ };
+
+ /* Check for minimal reset flags */
+ if ((*flags & ETH_RESET_EFX_INVISIBLE) != ETH_RESET_EFX_INVISIBLE)
+ return -EINVAL;
+ *flags ^= ETH_RESET_EFX_INVISIBLE;
+ method = RESET_TYPE_INVISIBLE;
+
+ if (*flags & ETH_RESET_PHY) {
+ *flags ^= ETH_RESET_PHY;
+ method = RESET_TYPE_ALL;
+ }
+
+ if ((*flags & efx->type->reset_world_flags) ==
+ efx->type->reset_world_flags) {
+ *flags ^= efx->type->reset_world_flags;
+ method = RESET_TYPE_WORLD;
+ }
+
+ return efx_reset(efx, method);
+}
+
const struct ethtool_ops efx_ethtool_ops = {
.get_settings = efx_ethtool_get_settings,
.set_settings = efx_ethtool_set_settings,
@@ -747,11 +846,13 @@ const struct ethtool_ops efx_ethtool_ops = {
.get_rx_csum = efx_ethtool_get_rx_csum,
.set_rx_csum = efx_ethtool_set_rx_csum,
.get_tx_csum = ethtool_op_get_tx_csum,
- .set_tx_csum = ethtool_op_set_tx_csum,
+ /* Need to enable/disable IPv6 too */
+ .set_tx_csum = efx_ethtool_set_tx_csum,
.get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg,
.get_tso = ethtool_op_get_tso,
- .set_tso = ethtool_op_set_tso,
+ /* Need to enable/disable TSO-IPv6 too */
+ .set_tso = efx_ethtool_set_tso,
.get_flags = ethtool_op_get_flags,
.set_flags = ethtool_op_set_flags,
.get_sset_count = efx_ethtool_get_sset_count,
@@ -759,4 +860,7 @@ const struct ethtool_ops efx_ethtool_ops = {
.get_strings = efx_ethtool_get_strings,
.phys_id = efx_ethtool_phys_id,
.get_ethtool_stats = efx_ethtool_get_stats,
+ .get_wol = efx_ethtool_get_wol,
+ .set_wol = efx_ethtool_set_wol,
+ .reset = efx_ethtool_reset,
};