sfc: Extend MTD driver for use with new NICs
[safe/jmp/linux-2.6] / drivers / net / sfc / ethtool.c
index eab3e5c..08a9db9 100644 (file)
 #include "workarounds.h"
 #include "selftest.h"
 #include "efx.h"
-#include "ethtool.h"
 #include "falcon.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];
 };
@@ -166,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),
 };
 
@@ -186,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;
 }
 
@@ -201,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);
@@ -208,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;
 }
@@ -219,9 +216,6 @@ int efx_ethtool_set_settings(struct net_device *net_dev,
        struct efx_nic *efx = netdev_priv(net_dev);
        int rc;
 
-       if (EFX_WORKAROUND_13963(efx) && !ecmd->autoneg)
-               return -EINVAL;
-
        /* Falcon GMAC does not support 1000Mbps HD */
        if (ecmd->speed == SPEED_1000 && ecmd->duplex != DUPLEX_FULL) {
                EFX_LOG(efx, "rejecting unsupported 1000Mbps HD"
@@ -232,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;
 }
 
@@ -291,7 +282,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
@@ -345,11 +336,11 @@ static int efx_ethtool_fill_self_tests(struct efx_nic *efx,
                                       u64 *data)
 {
        struct efx_channel *channel;
-       unsigned int n = 0;
+       unsigned int n = 0, i;
        enum efx_loopback_mode mode;
 
-       efx_fill_test(n++, strings, data, &tests->mii,
-                     "core", 0, "mii", NULL);
+       efx_fill_test(n++, strings, data, &tests->mdio,
+                     "core", 0, "mdio", NULL);
        efx_fill_test(n++, strings, data, &tests->nvram,
                      "core", 0, "nvram", NULL);
        efx_fill_test(n++, strings, data, &tests->interrupt,
@@ -373,8 +364,22 @@ static int efx_ethtool_fill_self_tests(struct efx_nic *efx,
 
        efx_fill_test(n++, strings, data, &tests->registers,
                      "core", 0, "registers", NULL);
-       efx_fill_test(n++, strings, data, &tests->phy,
-                     "phy", 0, "bist", 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++) {
@@ -487,7 +492,7 @@ static void efx_ethtool_self_test(struct net_device *net_dev,
 {
        struct efx_nic *efx = netdev_priv(net_dev);
        struct efx_self_tests efx_tests;
-       int offline, already_up;
+       int already_up;
        int rc;
 
        ASSERT_RTNL();
@@ -507,24 +512,15 @@ static void efx_ethtool_self_test(struct net_device *net_dev,
        }
 
        memset(&efx_tests, 0, sizeof(efx_tests));
-       offline = (test->flags & ETH_TEST_FL_OFFLINE);
 
-       /* Perform online self tests first */
-       rc = efx_online_test(efx, &efx_tests);
-       if (rc)
-               goto out;
+       rc = efx_selftest(efx, &efx_tests, test->flags);
 
-       /* Perform offline tests only if online tests passed */
-       if (offline)
-               rc = efx_offline_test(efx, &efx_tests,
-                                     efx->loopback_modes);
-
- out:
        if (!already_up)
                dev_close(efx->net_dev);
 
-       EFX_LOG(efx, "%s all %sline self-tests\n",
-               rc == 0 ? "passed" : "failed", offline ? "off" : "on");
+       EFX_LOG(efx, "%s %sline self-tests\n",
+               rc == 0 ? "passed" : "failed",
+               (test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on");
 
  fail2:
  fail1:
@@ -539,14 +535,14 @@ static int efx_ethtool_nway_reset(struct net_device *net_dev)
 {
        struct efx_nic *efx = netdev_priv(net_dev);
 
-       return mii_nway_restart(&efx->mii);
+       return mdio45_nway_restart(&efx->mdio);
 }
 
 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)
@@ -568,10 +564,14 @@ static int efx_ethtool_get_eeprom(struct net_device *net_dev,
        size_t len;
        int rc;
 
-       mutex_lock(&efx->spi_lock);
-       rc = falcon_spi_read(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
+       rc = mutex_lock_interruptible(&efx->spi_lock);
+       if (rc)
+               return rc;
+       rc = falcon_spi_read(efx, spi,
+                            eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
                             eeprom->len, &len, buf);
        mutex_unlock(&efx->spi_lock);
+
        eeprom->magic = EFX_ETHTOOL_EEPROM_MAGIC;
        eeprom->len = len;
        return rc;
@@ -588,10 +588,14 @@ static int efx_ethtool_set_eeprom(struct net_device *net_dev,
        if (eeprom->magic != EFX_ETHTOOL_EEPROM_MAGIC)
                return -EINVAL;
 
-       mutex_lock(&efx->spi_lock);
-       rc = falcon_spi_write(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
+       rc = mutex_lock_interruptible(&efx->spi_lock);
+       if (rc)
+               return rc;
+       rc = falcon_spi_write(efx, spi,
+                             eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
                              eeprom->len, &len, buf);
        mutex_unlock(&efx->spi_lock);
+
        eeprom->len = len;
        return rc;
 }
@@ -601,7 +605,6 @@ static int efx_ethtool_get_coalesce(struct net_device *net_dev,
 {
        struct efx_nic *efx = netdev_priv(net_dev);
        struct efx_tx_queue *tx_queue;
-       struct efx_rx_queue *rx_queue;
        struct efx_channel *channel;
 
        memset(coalesce, 0, sizeof(*coalesce));
@@ -619,14 +622,11 @@ static int efx_ethtool_get_coalesce(struct net_device *net_dev,
                }
        }
 
-       /* Find lowest IRQ moderation across all used RX queues */
-       coalesce->rx_coalesce_usecs_irq = ~((u32) 0);
-       efx_for_each_rx_queue(rx_queue, efx) {
-               channel = rx_queue->channel;
-               if (channel->irq_moderation < coalesce->rx_coalesce_usecs_irq)
-                       coalesce->rx_coalesce_usecs_irq =
-                               channel->irq_moderation;
-       }
+       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;
 }
@@ -640,10 +640,9 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev,
        struct efx_nic *efx = netdev_priv(net_dev);
        struct efx_channel *channel;
        struct efx_tx_queue *tx_queue;
-       unsigned tx_usecs, rx_usecs;
+       unsigned tx_usecs, rx_usecs, adaptive;
 
-       if (coalesce->use_adaptive_rx_coalesce ||
-           coalesce->use_adaptive_tx_coalesce)
+       if (coalesce->use_adaptive_tx_coalesce)
                return -EOPNOTSUPP;
 
        if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) {
@@ -654,6 +653,7 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev,
 
        rx_usecs = coalesce->rx_coalesce_usecs_irq;
        tx_usecs = coalesce->tx_coalesce_usecs_irq;
+       adaptive = coalesce->use_adaptive_rx_coalesce;
 
        /* If the channel is shared only allow RX parameters to be set */
        efx_for_each_tx_queue(tx_queue, efx) {
@@ -665,14 +665,9 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev,
                }
        }
 
-       efx_init_irq_moderation(efx, tx_usecs, rx_usecs);
-
-       /* 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_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive);
        efx_for_each_channel(channel, efx)
-               falcon_set_int_moderation(channel);
+               efx->type->push_irq_moderation(channel);
 
        return 0;
 }
@@ -681,8 +676,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) |
@@ -690,14 +689,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 & DEV_PRESENT_BIT(MDIO_MMD_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
@@ -707,26 +706,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;
-       mdio_clause45_set_pause(efx);
-       __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,
@@ -740,7 +753,51 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
 }
 
 
-struct ethtool_ops efx_ethtool_ops = {
+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,
        .get_drvinfo            = efx_ethtool_get_drvinfo,
@@ -768,4 +825,7 @@ 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,
 };