diff options
Diffstat (limited to 'drivers/net/sfc')
34 files changed, 8147 insertions, 2843 deletions
diff --git a/drivers/net/sfc/Kconfig b/drivers/net/sfc/Kconfig index 260aafa..a65c986 100644 --- a/drivers/net/sfc/Kconfig +++ b/drivers/net/sfc/Kconfig @@ -1,5 +1,5 @@ config SFC - tristate "Solarflare Solarstorm SFC4000 support" + tristate "Solarflare Solarstorm SFC4000/SFC9000-family support" depends on PCI && INET select MDIO select CRC32 @@ -7,15 +7,16 @@ config SFC select I2C_ALGOBIT help This driver supports 10-gigabit Ethernet cards based on - the Solarflare Communications Solarstorm SFC4000 controller. + the Solarflare Communications Solarstorm SFC4000 and + SFC9000-family controllers. To compile this driver as a module, choose M here. The module will be called sfc. config SFC_MTD - bool "Solarflare Solarstorm SFC4000 flash MTD support" + bool "Solarflare Solarstorm SFC4000/SFC9000-family MTD support" depends on SFC && MTD && !(SFC=y && MTD=m) default y help - This exposes the on-board flash memory as an MTD device (e.g. - /dev/mtd1). This makes it possible to upload new boot code - to the NIC. + This exposes the on-board flash memory as MTD devices (e.g. + /dev/mtd1). This makes it possible to upload new firmware + to the NIC. diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile index 7b52fe1..1047b19 100644 --- a/drivers/net/sfc/Makefile +++ b/drivers/net/sfc/Makefile @@ -1,6 +1,7 @@ -sfc-y += efx.o falcon.o tx.o rx.o falcon_gmac.o \ - falcon_xmac.o selftest.o ethtool.o qt202x_phy.o \ - mdio_10g.o tenxpress.o falcon_boards.o +sfc-y += efx.o nic.o falcon.o siena.o tx.o rx.o \ + falcon_gmac.o falcon_xmac.o mcdi_mac.o \ + selftest.o ethtool.o qt202x_phy.o mdio_10g.o \ + tenxpress.o falcon_boards.o mcdi.o mcdi_phy.o sfc-$(CONFIG_SFC_MTD) += mtd.o obj-$(CONFIG_SFC) += sfc.o diff --git a/drivers/net/sfc/bitfield.h b/drivers/net/sfc/bitfield.h index 6ad909b..098ac2a 100644 --- a/drivers/net/sfc/bitfield.h +++ b/drivers/net/sfc/bitfield.h @@ -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 @@ -37,6 +37,8 @@ #define EFX_DWORD_2_WIDTH 32 #define EFX_DWORD_3_LBN 96 #define EFX_DWORD_3_WIDTH 32 +#define EFX_QWORD_0_LBN 0 +#define EFX_QWORD_0_WIDTH 64 /* Specified attribute (e.g. LBN) of the specified field */ #define EFX_VAL(field, attribute) field ## _ ## attribute diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 4787faa..f983e3b 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 @@ -23,7 +23,9 @@ #include "net_driver.h" #include "efx.h" #include "mdio_10g.h" -#include "falcon.h" +#include "nic.h" + +#include "mcdi.h" /************************************************************************** * @@ -36,15 +38,32 @@ 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_NETWORK] = "NETWORK", + [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())) */ @@ -67,6 +86,7 @@ const char *efx_reset_type_names[] = { [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) @@ -186,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) @@ -212,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; @@ -244,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 @@ -273,14 +294,14 @@ static int efx_poll(struct napi_struct *napi, int budget) irq_adapt_low_thresh)) { if (channel->irq_moderation > 1) { channel->irq_moderation -= 1; - falcon_set_int_moderation(channel); + efx->type->push_irq_moderation(channel); } } else if (unlikely(channel->irq_mod_score > irq_adapt_high_thresh)) { if (channel->irq_moderation < efx->irq_rx_moderation) { channel->irq_moderation += 1; - falcon_set_int_moderation(channel); + efx->type->push_irq_moderation(channel); } } channel->irq_count = 0; @@ -315,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) @@ -332,7 +353,7 @@ void efx_process_channel_now(struct efx_channel *channel) efx_channel_processed(channel); napi_enable(&channel->napi_str); - falcon_enable_interrupts(efx); + efx_nic_enable_interrupts(efx); } /* Create event queue @@ -344,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 */ @@ -354,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); } /************************************************************************** @@ -534,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 @@ -582,7 +603,7 @@ 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; @@ -619,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)) { @@ -636,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; - - efx->mac_op->reconfigure(efx); + rc = efx->type->reconfigure_port(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); } @@ -700,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; @@ -736,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"); @@ -766,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"); @@ -797,7 +843,6 @@ 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; @@ -809,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); } /************************************************************************** @@ -1046,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; @@ -1067,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); } /************************************************************************** @@ -1147,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 @@ -1171,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. @@ -1190,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) { @@ -1208,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)) { @@ -1236,19 +1297,6 @@ 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 @@ -1270,8 +1318,8 @@ void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs, { struct efx_tx_queue *tx_queue; struct efx_rx_queue *rx_queue; - unsigned tx_ticks = irq_mod_ticks(tx_usecs, FALCON_IRQ_MOD_RESOLUTION); - unsigned rx_ticks = irq_mod_ticks(rx_usecs, FALCON_IRQ_MOD_RESOLUTION); + 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); @@ -1296,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 @@ -1308,15 +1356,7 @@ static void efx_monitor(struct work_struct *data) goto out_requeue; if (!efx->port_enabled) goto out_unlock; - rc = falcon_board(efx)->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); @@ -1420,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; @@ -1446,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) { @@ -1467,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; @@ -1536,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); @@ -1560,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; } @@ -1571,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)); @@ -1590,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 = { @@ -1729,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 @@ -1751,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; - - /* Serialise with kernel interfaces */ - rtnl_lock(); - - /* 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; - } + int rc, rc2; + bool disabled; EFX_INFO(efx, "resetting (%s)\n", RESET_TYPE(method)); - efx_reset_down(efx, method, &ecmd); + efx_reset_down(efx, method); - 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. */ @@ -1835,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; } @@ -1862,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); - efx_reset(nic); + /* 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; + } + + 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) @@ -1889,6 +1912,7 @@ 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; @@ -1902,6 +1926,10 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) 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); } @@ -1914,9 +1942,13 @@ 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 */ }; @@ -1937,19 +1969,16 @@ void efx_port_dummy_op_void(struct efx_nic *efx) {} void efx_port_dummy_op_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) { } - -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, -}; +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, }; /************************************************************************** @@ -1972,8 +2001,11 @@ 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; @@ -1985,12 +2017,10 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, 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); @@ -2058,9 +2088,10 @@ static void efx_fini_struct(struct efx_nic *efx) */ static void efx_pci_remove_main(struct efx_nic *efx) { - falcon_fini_interrupt(efx); + efx_nic_fini_interrupt(efx); efx_fini_channels(efx); efx_fini_port(efx); + efx->type->fini(efx); efx_fini_napi(efx); efx_remove_all(efx); } @@ -2120,7 +2151,7 @@ static int efx_pci_probe_main(struct efx_nic *efx) if (rc) goto fail2; - rc = falcon_init_nic(efx); + rc = efx->type->init(efx); if (rc) { EFX_ERR(efx, "failed to initialise NIC\n"); goto fail3; @@ -2134,7 +2165,7 @@ static int efx_pci_probe_main(struct efx_nic *efx) efx_init_channels(efx); - rc = falcon_init_interrupt(efx); + rc = efx_nic_init_interrupt(efx); if (rc) goto fail5; @@ -2144,6 +2175,7 @@ static int efx_pci_probe_main(struct efx_nic *efx) efx_fini_channels(efx); efx_fini_port(efx); fail4: + efx->type->fini(efx); fail3: efx_fini_napi(efx); fail2: @@ -2173,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); @@ -2227,9 +2261,8 @@ 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; rc = efx_register_netdev(efx); @@ -2256,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, }; /************************************************************************** @@ -2324,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); diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h index 3497b03..a615ac0 100644 --- a/drivers/net/sfc/efx.h +++ b/drivers/net/sfc/efx.h @@ -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 @@ -18,6 +18,8 @@ #define FALCON_A_P_DEVID 0x0703 #define FALCON_A_S_DEVID 0x6703 #define FALCON_B_P_DEVID 0x0710 +#define BETHPAGE_A_P_DEVID 0x0803 +#define SIENA_A_P_DEVID 0x0813 /* Solarstorm controllers use BAR 0 for I/O space and BAR 2(&3) for memory */ #define EFX_MEM_BAR 2 @@ -56,15 +58,12 @@ extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay); /* Channels */ extern void efx_process_channel_now(struct efx_channel *channel); -extern void efx_flush_queues(struct efx_nic *efx); #define EFX_EVQ_SIZE 4096 #define EFX_EVQ_MASK (EFX_EVQ_SIZE - 1) /* Ports */ -extern void efx_stats_disable(struct efx_nic *efx); -extern void efx_stats_enable(struct efx_nic *efx); -extern void 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); +extern int __efx_reconfigure_port(struct efx_nic *efx); /* Ethtool support */ extern int efx_ethtool_get_settings(struct net_device *net_dev, @@ -74,10 +73,9 @@ extern int efx_ethtool_set_settings(struct net_device *net_dev, extern const struct ethtool_ops efx_ethtool_ops; /* Reset handling */ -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, - struct ethtool_cmd *ecmd, bool ok); +extern int efx_reset(struct efx_nic *efx, enum reset_type method); +extern void efx_reset_down(struct efx_nic *efx, enum reset_type method); +extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok); /* Global */ extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); @@ -93,6 +91,7 @@ extern int efx_port_dummy_op_int(struct efx_nic *efx); extern void efx_port_dummy_op_void(struct efx_nic *efx); extern void efx_port_dummy_op_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); +extern bool efx_port_dummy_op_poll(struct efx_nic *efx); /* MTD */ #ifdef CONFIG_SFC_MTD @@ -116,4 +115,8 @@ static inline void efx_schedule_channel(struct efx_channel *channel) napi_schedule(&channel->napi_str); } +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 */ diff --git a/drivers/net/sfc/enum.h b/drivers/net/sfc/enum.h index fcd14b7..384cfe3 100644 --- a/drivers/net/sfc/enum.h +++ b/drivers/net/sfc/enum.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2008 Solarflare Communications Inc. + * Copyright 2007-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 @@ -13,38 +13,101 @@ /** * enum efx_loopback_mode - loopback modes * @LOOPBACK_NONE: no loopback - * @LOOPBACK_GMAC: loopback within GMAC at unspecified level - * @LOOPBACK_XGMII: loopback within XMAC at XGMII level - * @LOOPBACK_XGXS: loopback within XMAC at XGXS level - * @LOOPBACK_XAUI: loopback within XMAC at XAUI level + * @LOOPBACK_DATA: data path loopback + * @LOOPBACK_GMAC: loopback within GMAC + * @LOOPBACK_XGMII: loopback after XMAC + * @LOOPBACK_XGXS: loopback within BPX after XGXS + * @LOOPBACK_XAUI: loopback within BPX before XAUI serdes + * @LOOPBACK_GMII: loopback within BPX after GMAC + * @LOOPBACK_SGMII: loopback within BPX within SGMII + * @LOOPBACK_XGBR: loopback within BPX within XGBR + * @LOOPBACK_XFI: loopback within BPX before XFI serdes + * @LOOPBACK_XAUI_FAR: loopback within BPX after XAUI serdes + * @LOOPBACK_GMII_FAR: loopback within BPX before SGMII + * @LOOPBACK_SGMII_FAR: loopback within BPX after SGMII + * @LOOPBACK_XFI_FAR: loopback after XFI serdes * @LOOPBACK_GPHY: loopback within 1G PHY at unspecified level * @LOOPBACK_PHYXS: loopback within 10G PHY at PHYXS level * @LOOPBACK_PCS: loopback within 10G PHY at PCS level * @LOOPBACK_PMAPMD: loopback within 10G PHY at PMAPMD level - * @LOOPBACK_NETWORK: reflecting loopback (even further than furthest!) + * @LOOPBACK_XPORT: cross port loopback + * @LOOPBACK_XGMII_WS: wireside loopback excluding XMAC + * @LOOPBACK_XAUI_WS: wireside loopback within BPX within XAUI serdes + * @LOOPBACK_XAUI_WS_FAR: wireside loopback within BPX including XAUI serdes + * @LOOPBACK_XAUI_WS_NEAR: wireside loopback within BPX excluding XAUI serdes + * @LOOPBACK_GMII_WS: wireside loopback excluding GMAC + * @LOOPBACK_XFI_WS: wireside loopback excluding XFI serdes + * @LOOPBACK_XFI_WS_FAR: wireside loopback including XFI serdes + * @LOOPBACK_PHYXS_WS: wireside loopback within 10G PHY at PHYXS level */ -/* Please keep in order and up-to-date w.r.t the following two #defines */ +/* Please keep up-to-date w.r.t the following two #defines */ enum efx_loopback_mode { LOOPBACK_NONE = 0, - LOOPBACK_GMAC = 1, - LOOPBACK_XGMII = 2, - LOOPBACK_XGXS = 3, - LOOPBACK_XAUI = 4, - LOOPBACK_GPHY = 5, - LOOPBACK_PHYXS = 6, - LOOPBACK_PCS = 7, - LOOPBACK_PMAPMD = 8, - LOOPBACK_NETWORK = 9, + LOOPBACK_DATA = 1, + LOOPBACK_GMAC = 2, + LOOPBACK_XGMII = 3, + LOOPBACK_XGXS = 4, + LOOPBACK_XAUI = 5, + LOOPBACK_GMII = 6, + LOOPBACK_SGMII = 7, + LOOPBACK_XGBR = 8, + LOOPBACK_XFI = 9, + LOOPBACK_XAUI_FAR = 10, + LOOPBACK_GMII_FAR = 11, + LOOPBACK_SGMII_FAR = 12, + LOOPBACK_XFI_FAR = 13, + LOOPBACK_GPHY = 14, + LOOPBACK_PHYXS = 15, + LOOPBACK_PCS = 16, + LOOPBACK_PMAPMD = 17, + LOOPBACK_XPORT = 18, + LOOPBACK_XGMII_WS = 19, + LOOPBACK_XAUI_WS = 20, + LOOPBACK_XAUI_WS_FAR = 21, + LOOPBACK_XAUI_WS_NEAR = 22, + LOOPBACK_GMII_WS = 23, + LOOPBACK_XFI_WS = 24, + LOOPBACK_XFI_WS_FAR = 25, + LOOPBACK_PHYXS_WS = 26, LOOPBACK_MAX }; - #define LOOPBACK_TEST_MAX LOOPBACK_PMAPMD /* These loopbacks occur within the controller */ -#define LOOPBACKS_INTERNAL ((1 << LOOPBACK_GMAC) | \ - (1 << LOOPBACK_XGMII)| \ - (1 << LOOPBACK_XGXS) | \ - (1 << LOOPBACK_XAUI)) +#define LOOPBACKS_INTERNAL ((1 << LOOPBACK_DATA) | \ + (1 << LOOPBACK_GMAC) | \ + (1 << LOOPBACK_XGMII)| \ + (1 << LOOPBACK_XGXS) | \ + (1 << LOOPBACK_XAUI) | \ + (1 << LOOPBACK_GMII) | \ + (1 << LOOPBACK_SGMII) | \ + (1 << LOOPBACK_SGMII) | \ + (1 << LOOPBACK_XGBR) | \ + (1 << LOOPBACK_XFI) | \ + (1 << LOOPBACK_XAUI_FAR) | \ + (1 << LOOPBACK_GMII_FAR) | \ + (1 << LOOPBACK_SGMII_FAR) | \ + (1 << LOOPBACK_XFI_FAR) | \ + (1 << LOOPBACK_XGMII_WS) | \ + (1 << LOOPBACK_XAUI_WS) | \ + (1 << LOOPBACK_XAUI_WS_FAR) | \ + (1 << LOOPBACK_XAUI_WS_NEAR) | \ + (1 << LOOPBACK_GMII_WS) | \ + (1 << LOOPBACK_XFI_WS) | \ + (1 << LOOPBACK_XFI_WS_FAR)) + +#define LOOPBACKS_WS ((1 << LOOPBACK_XGMII_WS) | \ + (1 << LOOPBACK_XAUI_WS) | \ + (1 << LOOPBACK_XAUI_WS_FAR) | \ + (1 << LOOPBACK_XAUI_WS_NEAR) | \ + (1 << LOOPBACK_GMII_WS) | \ + (1 << LOOPBACK_XFI_WS) | \ + (1 << LOOPBACK_XFI_WS_FAR) | \ + (1 << LOOPBACK_PHYXS_WS)) + +#define LOOPBACKS_EXTERNAL(_efx) \ + ((_efx)->loopback_modes & ~LOOPBACKS_INTERNAL & \ + ~(1 << LOOPBACK_NONE)) #define LOOPBACK_MASK(_efx) \ (1 << (_efx)->loopback_mode) @@ -52,6 +115,9 @@ enum efx_loopback_mode { #define LOOPBACK_INTERNAL(_efx) \ (!!(LOOPBACKS_INTERNAL & LOOPBACK_MASK(_efx))) +#define LOOPBACK_EXTERNAL(_efx) \ + (!!(LOOPBACK_MASK(_efx) & LOOPBACKS_EXTERNAL(_efx))) + #define LOOPBACK_CHANGED(_from, _to, _mask) \ (!!((LOOPBACK_MASK(_from) ^ LOOPBACK_MASK(_to)) & (_mask))) @@ -78,6 +144,7 @@ enum efx_loopback_mode { * @RESET_TYPE_RX_DESC_FETCH: pcie error during rx descriptor fetch * @RESET_TYPE_TX_DESC_FETCH: pcie error during tx descriptor fetch * @RESET_TYPE_TX_SKIP: hardware completed empty tx descriptors + * @RESET_TYPE_MC_FAILURE: MC reboot/assertion */ enum reset_type { RESET_TYPE_NONE = -1, @@ -92,6 +159,7 @@ enum reset_type { RESET_TYPE_RX_DESC_FETCH, RESET_TYPE_TX_DESC_FETCH, RESET_TYPE_TX_SKIP, + RESET_TYPE_MC_FAILURE, RESET_TYPE_MAX, }; diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index d8915b9..6c0bbed 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,13 +10,12 @@ #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 "falcon.h" +#include "nic.h" #include "spi.h" #include "mdio_10g.h" @@ -153,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), }; @@ -174,14 +174,14 @@ static int efx_ethtool_phys_id(struct net_device *net_dev, u32 count) struct efx_nic *efx = netdev_priv(net_dev); do { - falcon_board(efx)->set_id_led(efx, EFX_LED_ON); + efx->type->set_id_led(efx, EFX_LED_ON); schedule_timeout_interruptible(HZ / 2); - falcon_board(efx)->set_id_led(efx, EFX_LED_OFF); + efx->type->set_id_led(efx, EFX_LED_OFF); schedule_timeout_interruptible(HZ / 2); } while (!signal_pending(current) && --count != 0); - falcon_board(efx)->set_id_led(efx, EFX_LED_DEFAULT); + efx->type->set_id_led(efx, EFX_LED_DEFAULT); return 0; } @@ -190,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); @@ -197,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; } @@ -218,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; } @@ -231,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)); } @@ -360,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++) { @@ -451,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); @@ -550,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); @@ -573,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); @@ -606,8 +658,8 @@ 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 *= FALCON_IRQ_MOD_RESOLUTION; - coalesce->rx_coalesce_usecs_irq *= FALCON_IRQ_MOD_RESOLUTION; + coalesce->tx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION; + coalesce->rx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION; return 0; } @@ -648,7 +700,7 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev, 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; } @@ -657,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) | @@ -666,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 @@ -683,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_state.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, @@ -717,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, @@ -733,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, @@ -745,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, }; diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index 41a321b..17afcd2 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.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 @@ -20,41 +20,14 @@ #include "efx.h" #include "mac.h" #include "spi.h" -#include "falcon.h" +#include "nic.h" #include "regs.h" #include "io.h" #include "mdio_10g.h" #include "phy.h" #include "workarounds.h" -/* Falcon hardware control. - * Falcon is the internal codename for the SFC4000 controller that is - * present in SFE400X evaluation boards - */ - -/************************************************************************** - * - * Configurable values - * - ************************************************************************** - */ - -static int disable_dma_stats; - -/* This is set to 16 for a good reason. In summary, if larger than - * 16, the descriptor cache holds more than a default socket - * buffer's worth of packets (for UDP we can only have at most one - * socket buffer's worth outstanding). This combined with the fact - * that we only get 1 TX event per descriptor cache means the NIC - * goes idle. - */ -#define TX_DC_ENTRIES 16 -#define TX_DC_ENTRIES_ORDER 0 -#define TX_DC_BASE 0x130000 - -#define RX_DC_ENTRIES 64 -#define RX_DC_ENTRIES_ORDER 2 -#define RX_DC_BASE 0x100000 +/* Hardware control for SFC4000 (aka Falcon). */ static const unsigned int /* "Large" EEPROM device: Atmel AT25640 or similar @@ -70,90 +43,6 @@ default_flash_type = ((17 << SPI_DEV_TYPE_SIZE_LBN) | (15 << SPI_DEV_TYPE_ERASE_SIZE_LBN) | (8 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)); -/* RX FIFO XOFF watermark - * - * When the amount of the RX FIFO increases used increases past this - * watermark send XOFF. Only used if RX flow control is enabled (ethtool -A) - * This also has an effect on RX/TX arbitration - */ -static int rx_xoff_thresh_bytes = -1; -module_param(rx_xoff_thresh_bytes, int, 0644); -MODULE_PARM_DESC(rx_xoff_thresh_bytes, "RX fifo XOFF threshold"); - -/* RX FIFO XON watermark - * - * When the amount of the RX FIFO used decreases below this - * watermark send XON. Only used if TX flow control is enabled (ethtool -A) - * This also has an effect on RX/TX arbitration - */ -static int rx_xon_thresh_bytes = -1; -module_param(rx_xon_thresh_bytes, int, 0644); -MODULE_PARM_DESC(rx_xon_thresh_bytes, "RX fifo XON threshold"); - -/* If FALCON_MAX_INT_ERRORS internal errors occur within - * FALCON_INT_ERROR_EXPIRE seconds, we consider the NIC broken and - * disable it. - */ -#define FALCON_INT_ERROR_EXPIRE 3600 -#define FALCON_MAX_INT_ERRORS 5 - -/* We poll for events every FLUSH_INTERVAL ms, and check FLUSH_POLL_COUNT times - */ -#define FALCON_FLUSH_INTERVAL 10 -#define FALCON_FLUSH_POLL_COUNT 100 - -/************************************************************************** - * - * Falcon constants - * - ************************************************************************** - */ - -/* Size and alignment of special buffers (4KB) */ -#define FALCON_BUF_SIZE 4096 - -/* Dummy SRAM size code */ -#define SRM_NB_BSZ_ONCHIP_ONLY (-1) - -#define FALCON_IS_DUAL_FUNC(efx) \ - (falcon_rev(efx) < FALCON_REV_B0) - -/************************************************************************** - * - * Falcon hardware access - * - **************************************************************************/ - -static inline void falcon_write_buf_tbl(struct efx_nic *efx, efx_qword_t *value, - unsigned int index) -{ - efx_sram_writeq(efx, efx->membase + efx->type->buf_tbl_base, - value, index); -} - -/* Read the current event from the event queue */ -static inline efx_qword_t *falcon_event(struct efx_channel *channel, - unsigned int index) -{ - return (((efx_qword_t *) (channel->eventq.addr)) + index); -} - -/* See if an event is present - * - * We check both the high and low dword of the event for all ones. We - * wrote all ones when we cleared the event, and no valid event can - * have all ones in either its high or low dwords. This approach is - * robust against reordering. - * - * Note that using a single 64-bit comparison is incorrect; even - * though the CPU read will be atomic, the DMA write may not be. - */ -static inline int falcon_event_present(efx_qword_t *event) -{ - return (!(EFX_DWORD_IS_ALL_ONES(event->dword[0]) | - EFX_DWORD_IS_ALL_ONES(event->dword[1]))); -} - /************************************************************************** * * I2C bus - this is a bit-bashing interface using GPIO pins @@ -210,843 +99,7 @@ static struct i2c_algo_bit_data falcon_i2c_bit_operations = { .timeout = DIV_ROUND_UP(HZ, 20), }; -/************************************************************************** - * - * Falcon special buffer handling - * Special buffers are used for event queues and the TX and RX - * descriptor rings. - * - *************************************************************************/ - -/* - * Initialise a Falcon special buffer - * - * This will define a buffer (previously allocated via - * falcon_alloc_special_buffer()) in Falcon's buffer table, allowing - * it to be used for event queues, descriptor rings etc. - */ -static void -falcon_init_special_buffer(struct efx_nic *efx, - struct efx_special_buffer *buffer) -{ - efx_qword_t buf_desc; - int index; - dma_addr_t dma_addr; - int i; - - EFX_BUG_ON_PARANOID(!buffer->addr); - - /* Write buffer descriptors to NIC */ - for (i = 0; i < buffer->entries; i++) { - index = buffer->index + i; - dma_addr = buffer->dma_addr + (i * 4096); - EFX_LOG(efx, "mapping special buffer %d at %llx\n", - index, (unsigned long long)dma_addr); - EFX_POPULATE_QWORD_3(buf_desc, - FRF_AZ_BUF_ADR_REGION, 0, - FRF_AZ_BUF_ADR_FBUF, dma_addr >> 12, - FRF_AZ_BUF_OWNER_ID_FBUF, 0); - falcon_write_buf_tbl(efx, &buf_desc, index); - } -} - -/* Unmaps a buffer from Falcon and clears the buffer table entries */ -static void -falcon_fini_special_buffer(struct efx_nic *efx, - struct efx_special_buffer *buffer) -{ - efx_oword_t buf_tbl_upd; - unsigned int start = buffer->index; - unsigned int end = (buffer->index + buffer->entries - 1); - - if (!buffer->entries) - return; - - EFX_LOG(efx, "unmapping special buffers %d-%d\n", - buffer->index, buffer->index + buffer->entries - 1); - - EFX_POPULATE_OWORD_4(buf_tbl_upd, - FRF_AZ_BUF_UPD_CMD, 0, - FRF_AZ_BUF_CLR_CMD, 1, - FRF_AZ_BUF_CLR_END_ID, end, - FRF_AZ_BUF_CLR_START_ID, start); - efx_writeo(efx, &buf_tbl_upd, FR_AZ_BUF_TBL_UPD); -} - -/* - * Allocate a new Falcon special buffer - * - * This allocates memory for a new buffer, clears it and allocates a - * new buffer ID range. It does not write into Falcon's buffer table. - * - * This call will allocate 4KB buffers, since Falcon can't use 8KB - * buffers for event queues and descriptor rings. - */ -static int falcon_alloc_special_buffer(struct efx_nic *efx, - struct efx_special_buffer *buffer, - unsigned int len) -{ - len = ALIGN(len, FALCON_BUF_SIZE); - - buffer->addr = pci_alloc_consistent(efx->pci_dev, len, - &buffer->dma_addr); - if (!buffer->addr) - return -ENOMEM; - buffer->len = len; - buffer->entries = len / FALCON_BUF_SIZE; - BUG_ON(buffer->dma_addr & (FALCON_BUF_SIZE - 1)); - - /* All zeros is a potentially valid event so memset to 0xff */ - memset(buffer->addr, 0xff, len); - - /* Select new buffer ID */ - buffer->index = efx->next_buffer_table; - efx->next_buffer_table += buffer->entries; - - EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x " - "(virt %p phys %llx)\n", buffer->index, - buffer->index + buffer->entries - 1, - (u64)buffer->dma_addr, len, - buffer->addr, (u64)virt_to_phys(buffer->addr)); - - return 0; -} - -static void falcon_free_special_buffer(struct efx_nic *efx, - struct efx_special_buffer *buffer) -{ - if (!buffer->addr) - return; - - EFX_LOG(efx, "deallocating special buffers %d-%d at %llx+%x " - "(virt %p phys %llx)\n", buffer->index, - buffer->index + buffer->entries - 1, - (u64)buffer->dma_addr, buffer->len, - buffer->addr, (u64)virt_to_phys(buffer->addr)); - - pci_free_consistent(efx->pci_dev, buffer->len, buffer->addr, - buffer->dma_addr); - buffer->addr = NULL; - buffer->entries = 0; -} - -/************************************************************************** - * - * Falcon generic buffer handling - * These buffers are used for interrupt status and MAC stats - * - **************************************************************************/ - -static int falcon_alloc_buffer(struct efx_nic *efx, - struct efx_buffer *buffer, unsigned int len) -{ - buffer->addr = pci_alloc_consistent(efx->pci_dev, len, - &buffer->dma_addr); - if (!buffer->addr) - return -ENOMEM; - buffer->len = len; - memset(buffer->addr, 0, len); - return 0; -} - -static void falcon_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer) -{ - if (buffer->addr) { - pci_free_consistent(efx->pci_dev, buffer->len, - buffer->addr, buffer->dma_addr); - buffer->addr = NULL; - } -} - -/************************************************************************** - * - * Falcon TX path - * - **************************************************************************/ - -/* Returns a pointer to the specified transmit descriptor in the TX - * descriptor queue belonging to the specified channel. - */ -static inline efx_qword_t *falcon_tx_desc(struct efx_tx_queue *tx_queue, - unsigned int index) -{ - return (((efx_qword_t *) (tx_queue->txd.addr)) + index); -} - -/* This writes to the TX_DESC_WPTR; write pointer for TX descriptor ring */ -static inline void falcon_notify_tx_desc(struct efx_tx_queue *tx_queue) -{ - unsigned write_ptr; - efx_dword_t reg; - - write_ptr = tx_queue->write_count & EFX_TXQ_MASK; - EFX_POPULATE_DWORD_1(reg, FRF_AZ_TX_DESC_WPTR_DWORD, write_ptr); - efx_writed_page(tx_queue->efx, ®, - FR_AZ_TX_DESC_UPD_DWORD_P0, tx_queue->queue); -} - - -/* For each entry inserted into the software descriptor ring, create a - * descriptor in the hardware TX descriptor ring (in host memory), and - * write a doorbell. - */ -void falcon_push_buffers(struct efx_tx_queue *tx_queue) -{ - - struct efx_tx_buffer *buffer; - efx_qword_t *txd; - unsigned write_ptr; - - BUG_ON(tx_queue->write_count == tx_queue->insert_count); - - do { - write_ptr = tx_queue->write_count & EFX_TXQ_MASK; - buffer = &tx_queue->buffer[write_ptr]; - txd = falcon_tx_desc(tx_queue, write_ptr); - ++tx_queue->write_count; - - /* Create TX descriptor ring entry */ - EFX_POPULATE_QWORD_4(*txd, - FSF_AZ_TX_KER_CONT, buffer->continuation, - FSF_AZ_TX_KER_BYTE_COUNT, buffer->len, - FSF_AZ_TX_KER_BUF_REGION, 0, - FSF_AZ_TX_KER_BUF_ADDR, buffer->dma_addr); - } while (tx_queue->write_count != tx_queue->insert_count); - - wmb(); /* Ensure descriptors are written before they are fetched */ - falcon_notify_tx_desc(tx_queue); -} - -/* Allocate hardware resources for a TX queue */ -int falcon_probe_tx(struct efx_tx_queue *tx_queue) -{ - struct efx_nic *efx = tx_queue->efx; - BUILD_BUG_ON(EFX_TXQ_SIZE < 512 || EFX_TXQ_SIZE > 4096 || - EFX_TXQ_SIZE & EFX_TXQ_MASK); - return falcon_alloc_special_buffer(efx, &tx_queue->txd, - EFX_TXQ_SIZE * sizeof(efx_qword_t)); -} - -void falcon_init_tx(struct efx_tx_queue *tx_queue) -{ - efx_oword_t tx_desc_ptr; - struct efx_nic *efx = tx_queue->efx; - - tx_queue->flushed = false; - - /* Pin TX descriptor ring */ - falcon_init_special_buffer(efx, &tx_queue->txd); - - /* Push TX descriptor ring to card */ - EFX_POPULATE_OWORD_10(tx_desc_ptr, - FRF_AZ_TX_DESCQ_EN, 1, - FRF_AZ_TX_ISCSI_DDIG_EN, 0, - FRF_AZ_TX_ISCSI_HDIG_EN, 0, - FRF_AZ_TX_DESCQ_BUF_BASE_ID, tx_queue->txd.index, - FRF_AZ_TX_DESCQ_EVQ_ID, - tx_queue->channel->channel, - FRF_AZ_TX_DESCQ_OWNER_ID, 0, - FRF_AZ_TX_DESCQ_LABEL, tx_queue->queue, - FRF_AZ_TX_DESCQ_SIZE, - __ffs(tx_queue->txd.entries), - FRF_AZ_TX_DESCQ_TYPE, 0, - FRF_BZ_TX_NON_IP_DROP_DIS, 1); - - if (falcon_rev(efx) >= FALCON_REV_B0) { - int csum = tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM; - EFX_SET_OWORD_FIELD(tx_desc_ptr, FRF_BZ_TX_IP_CHKSM_DIS, !csum); - EFX_SET_OWORD_FIELD(tx_desc_ptr, FRF_BZ_TX_TCP_CHKSM_DIS, - !csum); - } - - efx_writeo_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, - tx_queue->queue); - - if (falcon_rev(efx) < FALCON_REV_B0) { - efx_oword_t reg; - - /* Only 128 bits in this register */ - BUILD_BUG_ON(EFX_TX_QUEUE_COUNT >= 128); - - efx_reado(efx, ®, FR_AA_TX_CHKSM_CFG); - if (tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM) - clear_bit_le(tx_queue->queue, (void *)®); - else - set_bit_le(tx_queue->queue, (void *)®); - efx_writeo(efx, ®, FR_AA_TX_CHKSM_CFG); - } -} - -static void falcon_flush_tx_queue(struct efx_tx_queue *tx_queue) -{ - struct efx_nic *efx = tx_queue->efx; - efx_oword_t tx_flush_descq; - - /* Post a flush command */ - EFX_POPULATE_OWORD_2(tx_flush_descq, - FRF_AZ_TX_FLUSH_DESCQ_CMD, 1, - FRF_AZ_TX_FLUSH_DESCQ, tx_queue->queue); - efx_writeo(efx, &tx_flush_descq, FR_AZ_TX_FLUSH_DESCQ); -} - -void falcon_fini_tx(struct efx_tx_queue *tx_queue) -{ - struct efx_nic *efx = tx_queue->efx; - efx_oword_t tx_desc_ptr; - - /* The queue should have been flushed */ - WARN_ON(!tx_queue->flushed); - - /* Remove TX descriptor ring from card */ - EFX_ZERO_OWORD(tx_desc_ptr); - efx_writeo_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, - tx_queue->queue); - - /* Unpin TX descriptor ring */ - falcon_fini_special_buffer(efx, &tx_queue->txd); -} - -/* Free buffers backing TX queue */ -void falcon_remove_tx(struct efx_tx_queue *tx_queue) -{ - falcon_free_special_buffer(tx_queue->efx, &tx_queue->txd); -} - -/************************************************************************** - * - * Falcon RX path - * - **************************************************************************/ - -/* Returns a pointer to the specified descriptor in the RX descriptor queue */ -static inline efx_qword_t *falcon_rx_desc(struct efx_rx_queue *rx_queue, - unsigned int index) -{ - return (((efx_qword_t *) (rx_queue->rxd.addr)) + index); -} - -/* This creates an entry in the RX descriptor queue */ -static inline void falcon_build_rx_desc(struct efx_rx_queue *rx_queue, - unsigned index) -{ - struct efx_rx_buffer *rx_buf; - efx_qword_t *rxd; - - rxd = falcon_rx_desc(rx_queue, index); - rx_buf = efx_rx_buffer(rx_queue, index); - EFX_POPULATE_QWORD_3(*rxd, - FSF_AZ_RX_KER_BUF_SIZE, - rx_buf->len - - rx_queue->efx->type->rx_buffer_padding, - FSF_AZ_RX_KER_BUF_REGION, 0, - FSF_AZ_RX_KER_BUF_ADDR, rx_buf->dma_addr); -} - -/* This writes to the RX_DESC_WPTR register for the specified receive - * descriptor ring. - */ -void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue) -{ - efx_dword_t reg; - unsigned write_ptr; - - while (rx_queue->notified_count != rx_queue->added_count) { - falcon_build_rx_desc(rx_queue, - rx_queue->notified_count & - EFX_RXQ_MASK); - ++rx_queue->notified_count; - } - - wmb(); - write_ptr = rx_queue->added_count & EFX_RXQ_MASK; - EFX_POPULATE_DWORD_1(reg, FRF_AZ_RX_DESC_WPTR_DWORD, write_ptr); - efx_writed_page(rx_queue->efx, ®, - FR_AZ_RX_DESC_UPD_DWORD_P0, rx_queue->queue); -} - -int falcon_probe_rx(struct efx_rx_queue *rx_queue) -{ - struct efx_nic *efx = rx_queue->efx; - BUILD_BUG_ON(EFX_RXQ_SIZE < 512 || EFX_RXQ_SIZE > 4096 || - EFX_RXQ_SIZE & EFX_RXQ_MASK); - return falcon_alloc_special_buffer(efx, &rx_queue->rxd, - EFX_RXQ_SIZE * sizeof(efx_qword_t)); -} - -void falcon_init_rx(struct efx_rx_queue *rx_queue) -{ - efx_oword_t rx_desc_ptr; - struct efx_nic *efx = rx_queue->efx; - bool is_b0 = falcon_rev(efx) >= FALCON_REV_B0; - bool iscsi_digest_en = is_b0; - - EFX_LOG(efx, "RX queue %d ring in special buffers %d-%d\n", - rx_queue->queue, rx_queue->rxd.index, - rx_queue->rxd.index + rx_queue->rxd.entries - 1); - - rx_queue->flushed = false; - - /* Pin RX descriptor ring */ - falcon_init_special_buffer(efx, &rx_queue->rxd); - - /* Push RX descriptor ring to card */ - EFX_POPULATE_OWORD_10(rx_desc_ptr, - FRF_AZ_RX_ISCSI_DDIG_EN, iscsi_digest_en, - FRF_AZ_RX_ISCSI_HDIG_EN, iscsi_digest_en, - FRF_AZ_RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index, - FRF_AZ_RX_DESCQ_EVQ_ID, - rx_queue->channel->channel, - FRF_AZ_RX_DESCQ_OWNER_ID, 0, - FRF_AZ_RX_DESCQ_LABEL, rx_queue->queue, - FRF_AZ_RX_DESCQ_SIZE, - __ffs(rx_queue->rxd.entries), - FRF_AZ_RX_DESCQ_TYPE, 0 /* kernel queue */ , - /* For >=B0 this is scatter so disable */ - FRF_AZ_RX_DESCQ_JUMBO, !is_b0, - FRF_AZ_RX_DESCQ_EN, 1); - efx_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, - rx_queue->queue); -} - -static void falcon_flush_rx_queue(struct efx_rx_queue *rx_queue) -{ - struct efx_nic *efx = rx_queue->efx; - efx_oword_t rx_flush_descq; - - /* Post a flush command */ - EFX_POPULATE_OWORD_2(rx_flush_descq, - FRF_AZ_RX_FLUSH_DESCQ_CMD, 1, - FRF_AZ_RX_FLUSH_DESCQ, rx_queue->queue); - efx_writeo(efx, &rx_flush_descq, FR_AZ_RX_FLUSH_DESCQ); -} - -void falcon_fini_rx(struct efx_rx_queue *rx_queue) -{ - efx_oword_t rx_desc_ptr; - struct efx_nic *efx = rx_queue->efx; - - /* The queue should already have been flushed */ - WARN_ON(!rx_queue->flushed); - - /* Remove RX descriptor ring from card */ - EFX_ZERO_OWORD(rx_desc_ptr); - efx_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, - rx_queue->queue); - - /* Unpin RX descriptor ring */ - falcon_fini_special_buffer(efx, &rx_queue->rxd); -} - -/* Free buffers backing RX queue */ -void falcon_remove_rx(struct efx_rx_queue *rx_queue) -{ - falcon_free_special_buffer(rx_queue->efx, &rx_queue->rxd); -} - -/************************************************************************** - * - * Falcon event queue processing - * Event queues are processed by per-channel tasklets. - * - **************************************************************************/ - -/* Update a channel's event queue's read pointer (RPTR) register - * - * This writes the EVQ_RPTR_REG register for the specified channel's - * event queue. - * - * Note that EVQ_RPTR_REG contains the index of the "last read" event, - * whereas channel->eventq_read_ptr contains the index of the "next to - * read" event. - */ -void falcon_eventq_read_ack(struct efx_channel *channel) -{ - efx_dword_t reg; - struct efx_nic *efx = channel->efx; - - EFX_POPULATE_DWORD_1(reg, FRF_AZ_EVQ_RPTR, channel->eventq_read_ptr); - efx_writed_table(efx, ®, efx->type->evq_rptr_tbl_base, - channel->channel); -} - -/* Use HW to insert a SW defined event */ -void falcon_generate_event(struct efx_channel *channel, efx_qword_t *event) -{ - efx_oword_t drv_ev_reg; - - BUILD_BUG_ON(FRF_AZ_DRV_EV_DATA_LBN != 0 || - FRF_AZ_DRV_EV_DATA_WIDTH != 64); - drv_ev_reg.u32[0] = event->u32[0]; - drv_ev_reg.u32[1] = event->u32[1]; - drv_ev_reg.u32[2] = 0; - drv_ev_reg.u32[3] = 0; - EFX_SET_OWORD_FIELD(drv_ev_reg, FRF_AZ_DRV_EV_QID, channel->channel); - efx_writeo(channel->efx, &drv_ev_reg, FR_AZ_DRV_EV); -} - -/* Handle a transmit completion event - * - * Falcon batches TX completion events; the message we receive is of - * the form "complete all TX events up to this index". - */ -static void falcon_handle_tx_event(struct efx_channel *channel, - efx_qword_t *event) -{ - unsigned int tx_ev_desc_ptr; - unsigned int tx_ev_q_label; - struct efx_tx_queue *tx_queue; - struct efx_nic *efx = channel->efx; - - if (likely(EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_COMP))) { - /* Transmit completion */ - tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_DESC_PTR); - tx_ev_q_label = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_Q_LABEL); - tx_queue = &efx->tx_queue[tx_ev_q_label]; - channel->irq_mod_score += - (tx_ev_desc_ptr - tx_queue->read_count) & - EFX_TXQ_MASK; - efx_xmit_done(tx_queue, tx_ev_desc_ptr); - } else if (EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_WQ_FF_FULL)) { - /* Rewrite the FIFO write pointer */ - tx_ev_q_label = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_Q_LABEL); - tx_queue = &efx->tx_queue[tx_ev_q_label]; - - if (efx_dev_registered(efx)) - netif_tx_lock(efx->net_dev); - falcon_notify_tx_desc(tx_queue); - if (efx_dev_registered(efx)) - netif_tx_unlock(efx->net_dev); - } else if (EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_PKT_ERR) && - EFX_WORKAROUND_10727(efx)) { - efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); - } else { - EFX_ERR(efx, "channel %d unexpected TX event " - EFX_QWORD_FMT"\n", channel->channel, - EFX_QWORD_VAL(*event)); - } -} - -/* Detect errors included in the rx_evt_pkt_ok bit. */ -static void falcon_handle_rx_not_ok(struct efx_rx_queue *rx_queue, - const efx_qword_t *event, - bool *rx_ev_pkt_ok, - bool *discard) -{ - struct efx_nic *efx = rx_queue->efx; - bool rx_ev_buf_owner_id_err, rx_ev_ip_hdr_chksum_err; - bool rx_ev_tcp_udp_chksum_err, rx_ev_eth_crc_err; - bool rx_ev_frm_trunc, rx_ev_drib_nib, rx_ev_tobe_disc; - bool rx_ev_other_err, rx_ev_pause_frm; - bool rx_ev_ip_frag_err, rx_ev_hdr_type, rx_ev_mcast_pkt; - unsigned rx_ev_pkt_type; - - rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE); - rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_PKT); - rx_ev_tobe_disc = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_TOBE_DISC); - rx_ev_pkt_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_TYPE); - rx_ev_buf_owner_id_err = EFX_QWORD_FIELD(*event, - FSF_AZ_RX_EV_BUF_OWNER_ID_ERR); - rx_ev_ip_frag_err = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_IP_FRAG_ERR); - rx_ev_ip_hdr_chksum_err = EFX_QWORD_FIELD(*event, - FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR); - rx_ev_tcp_udp_chksum_err = EFX_QWORD_FIELD(*event, - FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR); - rx_ev_eth_crc_err = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_ETH_CRC_ERR); - rx_ev_frm_trunc = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_FRM_TRUNC); - rx_ev_drib_nib = ((falcon_rev(efx) >= FALCON_REV_B0) ? - 0 : EFX_QWORD_FIELD(*event, FSF_AA_RX_EV_DRIB_NIB)); - rx_ev_pause_frm = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PAUSE_FRM_ERR); - - /* Every error apart from tobe_disc and pause_frm */ - rx_ev_other_err = (rx_ev_drib_nib | rx_ev_tcp_udp_chksum_err | - rx_ev_buf_owner_id_err | rx_ev_eth_crc_err | - rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err); - - /* Count errors that are not in MAC stats. Ignore expected - * checksum errors during self-test. */ - if (rx_ev_frm_trunc) - ++rx_queue->channel->n_rx_frm_trunc; - else if (rx_ev_tobe_disc) - ++rx_queue->channel->n_rx_tobe_disc; - else if (!efx->loopback_selftest) { - if (rx_ev_ip_hdr_chksum_err) - ++rx_queue->channel->n_rx_ip_hdr_chksum_err; - else if (rx_ev_tcp_udp_chksum_err) - ++rx_queue->channel->n_rx_tcp_udp_chksum_err; - } - if (rx_ev_ip_frag_err) - ++rx_queue->channel->n_rx_ip_frag_err; - - /* The frame must be discarded if any of these are true. */ - *discard = (rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_drib_nib | - rx_ev_tobe_disc | rx_ev_pause_frm); - - /* TOBE_DISC is expected on unicast mismatches; don't print out an - * error message. FRM_TRUNC indicates RXDP dropped the packet due - * to a FIFO overflow. - */ -#ifdef EFX_ENABLE_DEBUG - if (rx_ev_other_err) { - EFX_INFO_RL(efx, " RX queue %d unexpected RX event " - EFX_QWORD_FMT "%s%s%s%s%s%s%s%s\n", - rx_queue->queue, EFX_QWORD_VAL(*event), - rx_ev_buf_owner_id_err ? " [OWNER_ID_ERR]" : "", - rx_ev_ip_hdr_chksum_err ? - " [IP_HDR_CHKSUM_ERR]" : "", - rx_ev_tcp_udp_chksum_err ? - " [TCP_UDP_CHKSUM_ERR]" : "", - rx_ev_eth_crc_err ? " [ETH_CRC_ERR]" : "", - rx_ev_frm_trunc ? " [FRM_TRUNC]" : "", - rx_ev_drib_nib ? " [DRIB_NIB]" : "", - rx_ev_tobe_disc ? " [TOBE_DISC]" : "", - rx_ev_pause_frm ? " [PAUSE]" : ""); - } -#endif -} - -/* Handle receive events that are not in-order. */ -static void falcon_handle_rx_bad_index(struct efx_rx_queue *rx_queue, - unsigned index) -{ - struct efx_nic *efx = rx_queue->efx; - unsigned expected, dropped; - - expected = rx_queue->removed_count & EFX_RXQ_MASK; - dropped = (index - expected) & EFX_RXQ_MASK; - EFX_INFO(efx, "dropped %d events (index=%d expected=%d)\n", - dropped, index, expected); - - efx_schedule_reset(efx, EFX_WORKAROUND_5676(efx) ? - RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); -} - -/* Handle a packet received event - * - * Falcon silicon gives a "discard" flag if it's a unicast packet with the - * wrong destination address - * Also "is multicast" and "matches multicast filter" flags can be used to - * discard non-matching multicast packets. - */ -static void falcon_handle_rx_event(struct efx_channel *channel, - const efx_qword_t *event) -{ - unsigned int rx_ev_desc_ptr, rx_ev_byte_cnt; - unsigned int rx_ev_hdr_type, rx_ev_mcast_pkt; - unsigned expected_ptr; - bool rx_ev_pkt_ok, discard = false, checksummed; - struct efx_rx_queue *rx_queue; - struct efx_nic *efx = channel->efx; - - /* Basic packet information */ - rx_ev_byte_cnt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_BYTE_CNT); - rx_ev_pkt_ok = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_OK); - rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE); - WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_JUMBO_CONT)); - WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_SOP) != 1); - WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_Q_LABEL) != - channel->channel); - - rx_queue = &efx->rx_queue[channel->channel]; - - rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_DESC_PTR); - expected_ptr = rx_queue->removed_count & EFX_RXQ_MASK; - if (unlikely(rx_ev_desc_ptr != expected_ptr)) - falcon_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr); - - if (likely(rx_ev_pkt_ok)) { - /* If packet is marked as OK and packet type is TCP/IPv4 or - * UDP/IPv4, then we can rely on the hardware checksum. - */ - checksummed = - efx->rx_checksum_enabled && - (rx_ev_hdr_type == FSE_AB_RX_EV_HDR_TYPE_IPV4_TCP || - rx_ev_hdr_type == FSE_AB_RX_EV_HDR_TYPE_IPV4_UDP); - } else { - falcon_handle_rx_not_ok(rx_queue, event, &rx_ev_pkt_ok, - &discard); - checksummed = false; - } - - /* Detect multicast packets that didn't match the filter */ - rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_PKT); - if (rx_ev_mcast_pkt) { - unsigned int rx_ev_mcast_hash_match = - EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_HASH_MATCH); - - if (unlikely(!rx_ev_mcast_hash_match)) - discard = true; - } - - channel->irq_mod_score += 2; - - /* Handle received packet */ - efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt, - checksummed, discard); -} - -/* Global events are basically PHY events */ -static void falcon_handle_global_event(struct efx_channel *channel, - efx_qword_t *event) -{ - struct efx_nic *efx = channel->efx; - bool handled = false; - - if (EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_G_PHY0_INTR) || - EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_XG_PHY0_INTR) || - EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_XFP_PHY0_INTR)) { - efx->phy_op->clear_interrupt(efx); - queue_work(efx->workqueue, &efx->phy_work); - handled = true; - } - - if ((falcon_rev(efx) >= FALCON_REV_B0) && - EFX_QWORD_FIELD(*event, FSF_BB_GLB_EV_XG_MGT_INTR)) { - queue_work(efx->workqueue, &efx->mac_work); - handled = true; - } - - if (falcon_rev(efx) <= FALCON_REV_A1 ? - EFX_QWORD_FIELD(*event, FSF_AA_GLB_EV_RX_RECOVERY) : - EFX_QWORD_FIELD(*event, FSF_BB_GLB_EV_RX_RECOVERY)) { - EFX_ERR(efx, "channel %d seen global RX_RESET " - "event. Resetting.\n", channel->channel); - - atomic_inc(&efx->rx_reset); - efx_schedule_reset(efx, EFX_WORKAROUND_6555(efx) ? - RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); - handled = true; - } - - if (!handled) - EFX_ERR(efx, "channel %d unknown global event " - EFX_QWORD_FMT "\n", channel->channel, - EFX_QWORD_VAL(*event)); -} - -static void falcon_handle_driver_event(struct efx_channel *channel, - efx_qword_t *event) -{ - struct efx_nic *efx = channel->efx; - unsigned int ev_sub_code; - unsigned int ev_sub_data; - - ev_sub_code = EFX_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBCODE); - ev_sub_data = EFX_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBDATA); - - switch (ev_sub_code) { - case FSE_AZ_TX_DESCQ_FLS_DONE_EV: - EFX_TRACE(efx, "channel %d TXQ %d flushed\n", - channel->channel, ev_sub_data); - break; - case FSE_AZ_RX_DESCQ_FLS_DONE_EV: - EFX_TRACE(efx, "channel %d RXQ %d flushed\n", - channel->channel, ev_sub_data); - break; - case FSE_AZ_EVQ_INIT_DONE_EV: - EFX_LOG(efx, "channel %d EVQ %d initialised\n", - channel->channel, ev_sub_data); - break; - case FSE_AZ_SRM_UPD_DONE_EV: - EFX_TRACE(efx, "channel %d SRAM update done\n", - channel->channel); - break; - case FSE_AZ_WAKE_UP_EV: - EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n", - channel->channel, ev_sub_data); - break; - case FSE_AZ_TIMER_EV: - EFX_TRACE(efx, "channel %d RX queue %d timer expired\n", - channel->channel, ev_sub_data); - break; - case FSE_AA_RX_RECOVER_EV: - EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. " - "Resetting.\n", channel->channel); - atomic_inc(&efx->rx_reset); - efx_schedule_reset(efx, - EFX_WORKAROUND_6555(efx) ? - RESET_TYPE_RX_RECOVERY : - RESET_TYPE_DISABLE); - break; - case FSE_BZ_RX_DSC_ERROR_EV: - EFX_ERR(efx, "RX DMA Q %d reports descriptor fetch error." - " RX Q %d is disabled.\n", ev_sub_data, ev_sub_data); - efx_schedule_reset(efx, RESET_TYPE_RX_DESC_FETCH); - break; - case FSE_BZ_TX_DSC_ERROR_EV: - EFX_ERR(efx, "TX DMA Q %d reports descriptor fetch error." - " TX Q %d is disabled.\n", ev_sub_data, ev_sub_data); - efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); - break; - default: - EFX_TRACE(efx, "channel %d unknown driver event code %d " - "data %04x\n", channel->channel, ev_sub_code, - ev_sub_data); - break; - } -} - -int falcon_process_eventq(struct efx_channel *channel, int rx_quota) -{ - unsigned int read_ptr; - efx_qword_t event, *p_event; - int ev_code; - int rx_packets = 0; - - read_ptr = channel->eventq_read_ptr; - - do { - p_event = falcon_event(channel, read_ptr); - event = *p_event; - - if (!falcon_event_present(&event)) - /* End of events */ - break; - - EFX_TRACE(channel->efx, "channel %d event is "EFX_QWORD_FMT"\n", - channel->channel, EFX_QWORD_VAL(event)); - - /* Clear this event by marking it all ones */ - EFX_SET_QWORD(*p_event); - - ev_code = EFX_QWORD_FIELD(event, FSF_AZ_EV_CODE); - - switch (ev_code) { - case FSE_AZ_EV_CODE_RX_EV: - falcon_handle_rx_event(channel, &event); - ++rx_packets; - break; - case FSE_AZ_EV_CODE_TX_EV: - falcon_handle_tx_event(channel, &event); - break; - case FSE_AZ_EV_CODE_DRV_GEN_EV: - channel->eventq_magic = EFX_QWORD_FIELD( - event, FSF_AZ_DRV_GEN_EV_MAGIC); - EFX_LOG(channel->efx, "channel %d received generated " - "event "EFX_QWORD_FMT"\n", channel->channel, - EFX_QWORD_VAL(event)); - break; - case FSE_AZ_EV_CODE_GLOBAL_EV: - falcon_handle_global_event(channel, &event); - break; - case FSE_AZ_EV_CODE_DRIVER_EV: - falcon_handle_driver_event(channel, &event); - break; - default: - EFX_ERR(channel->efx, "channel %d unknown event type %d" - " (data " EFX_QWORD_FMT ")\n", channel->channel, - ev_code, EFX_QWORD_VAL(event)); - } - - /* Increment read pointer */ - read_ptr = (read_ptr + 1) & EFX_EVQ_MASK; - - } while (rx_packets < rx_quota); - - channel->eventq_read_ptr = read_ptr; - return rx_packets; -} - -void falcon_set_int_moderation(struct efx_channel *channel) +static void falcon_push_irq_moderation(struct efx_channel *channel) { efx_dword_t timer_cmd; struct efx_nic *efx = channel->efx; @@ -1067,265 +120,18 @@ void falcon_set_int_moderation(struct efx_channel *channel) BUILD_BUG_ON(FR_AA_TIMER_COMMAND_KER != FR_BZ_TIMER_COMMAND_P0); efx_writed_page_locked(efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0, channel->channel); - -} - -/* Allocate buffer table entries for event queue */ -int falcon_probe_eventq(struct efx_channel *channel) -{ - struct efx_nic *efx = channel->efx; - BUILD_BUG_ON(EFX_EVQ_SIZE < 512 || EFX_EVQ_SIZE > 32768 || - EFX_EVQ_SIZE & EFX_EVQ_MASK); - return falcon_alloc_special_buffer(efx, &channel->eventq, - EFX_EVQ_SIZE * sizeof(efx_qword_t)); -} - -void falcon_init_eventq(struct efx_channel *channel) -{ - efx_oword_t evq_ptr; - struct efx_nic *efx = channel->efx; - - EFX_LOG(efx, "channel %d event queue in special buffers %d-%d\n", - channel->channel, channel->eventq.index, - channel->eventq.index + channel->eventq.entries - 1); - - /* Pin event queue buffer */ - falcon_init_special_buffer(efx, &channel->eventq); - - /* Fill event queue with all ones (i.e. empty events) */ - memset(channel->eventq.addr, 0xff, channel->eventq.len); - - /* Push event queue to card */ - EFX_POPULATE_OWORD_3(evq_ptr, - FRF_AZ_EVQ_EN, 1, - FRF_AZ_EVQ_SIZE, __ffs(channel->eventq.entries), - FRF_AZ_EVQ_BUF_BASE_ID, channel->eventq.index); - efx_writeo_table(efx, &evq_ptr, efx->type->evq_ptr_tbl_base, - channel->channel); - - falcon_set_int_moderation(channel); -} - -void falcon_fini_eventq(struct efx_channel *channel) -{ - efx_oword_t eventq_ptr; - struct efx_nic *efx = channel->efx; - - /* Remove event queue from card */ - EFX_ZERO_OWORD(eventq_ptr); - efx_writeo_table(efx, &eventq_ptr, efx->type->evq_ptr_tbl_base, - channel->channel); - - /* Unpin event queue */ - falcon_fini_special_buffer(efx, &channel->eventq); -} - -/* Free buffers backing event queue */ -void falcon_remove_eventq(struct efx_channel *channel) -{ - falcon_free_special_buffer(channel->efx, &channel->eventq); -} - - -/* Generates a test event on the event queue. A subsequent call to - * process_eventq() should pick up the event and place the value of - * "magic" into channel->eventq_magic; - */ -void falcon_generate_test_event(struct efx_channel *channel, unsigned int magic) -{ - efx_qword_t test_event; - - EFX_POPULATE_QWORD_2(test_event, FSF_AZ_EV_CODE, - FSE_AZ_EV_CODE_DRV_GEN_EV, - FSF_AZ_DRV_GEN_EV_MAGIC, magic); - falcon_generate_event(channel, &test_event); -} - -void falcon_sim_phy_event(struct efx_nic *efx) -{ - efx_qword_t phy_event; - - EFX_POPULATE_QWORD_1(phy_event, FSF_AZ_EV_CODE, - FSE_AZ_EV_CODE_GLOBAL_EV); - if (EFX_IS10G(efx)) - EFX_SET_QWORD_FIELD(phy_event, FSF_AB_GLB_EV_XG_PHY0_INTR, 1); - else - EFX_SET_QWORD_FIELD(phy_event, FSF_AB_GLB_EV_G_PHY0_INTR, 1); - - falcon_generate_event(&efx->channel[0], &phy_event); -} - -/************************************************************************** - * - * Flush handling - * - **************************************************************************/ - - -static void falcon_poll_flush_events(struct efx_nic *efx) -{ - struct efx_channel *channel = &efx->channel[0]; - struct efx_tx_queue *tx_queue; - struct efx_rx_queue *rx_queue; - unsigned int read_ptr = channel->eventq_read_ptr; - unsigned int end_ptr = (read_ptr - 1) & EFX_EVQ_MASK; - - do { - efx_qword_t *event = falcon_event(channel, read_ptr); - int ev_code, ev_sub_code, ev_queue; - bool ev_failed; - - if (!falcon_event_present(event)) - break; - - ev_code = EFX_QWORD_FIELD(*event, FSF_AZ_EV_CODE); - ev_sub_code = EFX_QWORD_FIELD(*event, - FSF_AZ_DRIVER_EV_SUBCODE); - if (ev_code == FSE_AZ_EV_CODE_DRIVER_EV && - ev_sub_code == FSE_AZ_TX_DESCQ_FLS_DONE_EV) { - ev_queue = EFX_QWORD_FIELD(*event, - FSF_AZ_DRIVER_EV_SUBDATA); - if (ev_queue < EFX_TX_QUEUE_COUNT) { - tx_queue = efx->tx_queue + ev_queue; - tx_queue->flushed = true; - } - } else if (ev_code == FSE_AZ_EV_CODE_DRIVER_EV && - ev_sub_code == FSE_AZ_RX_DESCQ_FLS_DONE_EV) { - ev_queue = EFX_QWORD_FIELD( - *event, FSF_AZ_DRIVER_EV_RX_DESCQ_ID); - ev_failed = EFX_QWORD_FIELD( - *event, FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL); - if (ev_queue < efx->n_rx_queues) { - rx_queue = efx->rx_queue + ev_queue; - - /* retry the rx flush */ - if (ev_failed) - falcon_flush_rx_queue(rx_queue); - else - rx_queue->flushed = true; - } - } - - read_ptr = (read_ptr + 1) & EFX_EVQ_MASK; - } while (read_ptr != end_ptr); -} - -/* Handle tx and rx flushes at the same time, since they run in - * parallel in the hardware and there's no reason for us to - * serialise them */ -int falcon_flush_queues(struct efx_nic *efx) -{ - struct efx_rx_queue *rx_queue; - struct efx_tx_queue *tx_queue; - int i; - bool outstanding; - - /* Issue flush requests */ - efx_for_each_tx_queue(tx_queue, efx) { - tx_queue->flushed = false; - falcon_flush_tx_queue(tx_queue); - } - efx_for_each_rx_queue(rx_queue, efx) { - rx_queue->flushed = false; - falcon_flush_rx_queue(rx_queue); - } - - /* Poll the evq looking for flush completions. Since we're not pushing - * any more rx or tx descriptors at this point, we're in no danger of - * overflowing the evq whilst we wait */ - for (i = 0; i < FALCON_FLUSH_POLL_COUNT; ++i) { - msleep(FALCON_FLUSH_INTERVAL); - falcon_poll_flush_events(efx); - - /* Check if every queue has been succesfully flushed */ - outstanding = false; - efx_for_each_tx_queue(tx_queue, efx) - outstanding |= !tx_queue->flushed; - efx_for_each_rx_queue(rx_queue, efx) - outstanding |= !rx_queue->flushed; - if (!outstanding) - return 0; - } - - /* Mark the queues as all flushed. We're going to return failure - * leading to a reset, or fake up success anyway. "flushed" now - * indicates that we tried to flush. */ - efx_for_each_tx_queue(tx_queue, efx) { - if (!tx_queue->flushed) - EFX_ERR(efx, "tx queue %d flush command timed out\n", - tx_queue->queue); - tx_queue->flushed = true; - } - efx_for_each_rx_queue(rx_queue, efx) { - if (!rx_queue->flushed) - EFX_ERR(efx, "rx queue %d flush command timed out\n", - rx_queue->queue); - rx_queue->flushed = true; - } - - if (EFX_WORKAROUND_7803(efx)) - return 0; - - return -ETIMEDOUT; } -/************************************************************************** - * - * Falcon hardware interrupts - * The hardware interrupt handler does very little work; all the event - * queue processing is carried out by per-channel tasklets. - * - **************************************************************************/ +static void falcon_deconfigure_mac_wrapper(struct efx_nic *efx); -/* Enable/disable/generate Falcon interrupts */ -static inline void falcon_interrupts(struct efx_nic *efx, int enabled, - int force) +static void falcon_prepare_flush(struct efx_nic *efx) { - efx_oword_t int_en_reg_ker; + falcon_deconfigure_mac_wrapper(efx); - EFX_POPULATE_OWORD_2(int_en_reg_ker, - FRF_AZ_KER_INT_KER, force, - FRF_AZ_DRV_INT_EN_KER, enabled); - efx_writeo(efx, &int_en_reg_ker, FR_AZ_INT_EN_KER); -} - -void falcon_enable_interrupts(struct efx_nic *efx) -{ - efx_oword_t int_adr_reg_ker; - struct efx_channel *channel; - - EFX_ZERO_OWORD(*((efx_oword_t *) efx->irq_status.addr)); - wmb(); /* Ensure interrupt vector is clear before interrupts enabled */ - - /* Program address */ - EFX_POPULATE_OWORD_2(int_adr_reg_ker, - FRF_AZ_NORM_INT_VEC_DIS_KER, - EFX_INT_MODE_USE_MSI(efx), - FRF_AZ_INT_ADR_KER, efx->irq_status.dma_addr); - efx_writeo(efx, &int_adr_reg_ker, FR_AZ_INT_ADR_KER); - - /* Enable interrupts */ - falcon_interrupts(efx, 1, 0); - - /* Force processing of all the channels to get the EVQ RPTRs up to - date */ - efx_for_each_channel(channel, efx) - efx_schedule_channel(channel); -} - -void falcon_disable_interrupts(struct efx_nic *efx) -{ - /* Disable interrupts */ - falcon_interrupts(efx, 0, 0); -} - -/* Generate a Falcon test interrupt - * Interrupt must already have been enabled, otherwise nasty things - * may happen. - */ -void falcon_generate_interrupt(struct efx_nic *efx) -{ - falcon_interrupts(efx, 1, 1); + /* Wait for the tx and rx fifo's to get to the next packet boundary + * (~1ms without back-pressure), then to drain the remainder of the + * fifo's at data path speeds (negligible), with a healthy margin. */ + msleep(10); } /* Acknowledge a legacy interrupt from Falcon @@ -1338,7 +144,7 @@ void falcon_generate_interrupt(struct efx_nic *efx) * * NB most hardware supports MSI interrupts */ -static inline void falcon_irq_ack_a1(struct efx_nic *efx) +inline void falcon_irq_ack_a1(struct efx_nic *efx) { efx_dword_t reg; @@ -1347,104 +153,8 @@ static inline void falcon_irq_ack_a1(struct efx_nic *efx) efx_readd(efx, ®, FR_AA_WORK_AROUND_BROKEN_PCI_READS); } -/* Process a fatal interrupt - * Disable bus mastering ASAP and schedule a reset - */ -static irqreturn_t falcon_fatal_interrupt(struct efx_nic *efx) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - efx_oword_t *int_ker = efx->irq_status.addr; - efx_oword_t fatal_intr; - int error, mem_perr; - - efx_reado(efx, &fatal_intr, FR_AZ_FATAL_INTR_KER); - error = EFX_OWORD_FIELD(fatal_intr, FRF_AZ_FATAL_INTR); - - EFX_ERR(efx, "SYSTEM ERROR " EFX_OWORD_FMT " status " - EFX_OWORD_FMT ": %s\n", EFX_OWORD_VAL(*int_ker), - EFX_OWORD_VAL(fatal_intr), - error ? "disabling bus mastering" : "no recognised error"); - if (error == 0) - goto out; - - /* If this is a memory parity error dump which blocks are offending */ - mem_perr = EFX_OWORD_FIELD(fatal_intr, FRF_AZ_MEM_PERR_INT_KER); - if (mem_perr) { - efx_oword_t reg; - efx_reado(efx, ®, FR_AZ_MEM_STAT); - EFX_ERR(efx, "SYSTEM ERROR: memory parity error " - EFX_OWORD_FMT "\n", EFX_OWORD_VAL(reg)); - } - - /* Disable both devices */ - pci_clear_master(efx->pci_dev); - if (FALCON_IS_DUAL_FUNC(efx)) - pci_clear_master(nic_data->pci_dev2); - falcon_disable_interrupts(efx); - - /* Count errors and reset or disable the NIC accordingly */ - if (efx->int_error_count == 0 || - time_after(jiffies, efx->int_error_expire)) { - efx->int_error_count = 0; - efx->int_error_expire = - jiffies + FALCON_INT_ERROR_EXPIRE * HZ; - } - if (++efx->int_error_count < FALCON_MAX_INT_ERRORS) { - EFX_ERR(efx, "SYSTEM ERROR - reset scheduled\n"); - efx_schedule_reset(efx, RESET_TYPE_INT_ERROR); - } else { - EFX_ERR(efx, "SYSTEM ERROR - max number of errors seen." - "NIC will be disabled\n"); - efx_schedule_reset(efx, RESET_TYPE_DISABLE); - } -out: - return IRQ_HANDLED; -} - -/* Handle a legacy interrupt from Falcon - * Acknowledges the interrupt and schedule event queue processing. - */ -static irqreturn_t falcon_legacy_interrupt_b0(int irq, void *dev_id) -{ - struct efx_nic *efx = dev_id; - efx_oword_t *int_ker = efx->irq_status.addr; - irqreturn_t result = IRQ_NONE; - struct efx_channel *channel; - efx_dword_t reg; - u32 queues; - int syserr; - - /* Read the ISR which also ACKs the interrupts */ - efx_readd(efx, ®, FR_BZ_INT_ISR0); - queues = EFX_EXTRACT_DWORD(reg, 0, 31); - - /* Check to see if we have a serious error condition */ - syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); - if (unlikely(syserr)) - return falcon_fatal_interrupt(efx); - - /* Schedule processing of any interrupting queues */ - efx_for_each_channel(channel, efx) { - if ((queues & 1) || - falcon_event_present( - falcon_event(channel, channel->eventq_read_ptr))) { - efx_schedule_channel(channel); - result = IRQ_HANDLED; - } - queues >>= 1; - } - - if (result == IRQ_HANDLED) { - efx->last_irq_cpu = raw_smp_processor_id(); - EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_DWORD_FMT "\n", - irq, raw_smp_processor_id(), EFX_DWORD_VAL(reg)); - } - - return result; -} - -static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) +irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) { struct efx_nic *efx = dev_id; efx_oword_t *int_ker = efx->irq_status.addr; @@ -1467,13 +177,13 @@ static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) /* Check to see if we have a serious error condition */ syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); if (unlikely(syserr)) - return falcon_fatal_interrupt(efx); + return efx_nic_fatal_interrupt(efx); /* Determine interrupting queues, clear interrupt status * register and acknowledge the device interrupt. */ - BUILD_BUG_ON(INT_EVQS_WIDTH > EFX_MAX_CHANNELS); - queues = EFX_OWORD_FIELD(*int_ker, INT_EVQS); + BUILD_BUG_ON(FSF_AZ_NET_IVEC_INT_Q_WIDTH > EFX_MAX_CHANNELS); + queues = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_INT_Q); EFX_ZERO_OWORD(*int_ker); wmb(); /* Ensure the vector is cleared before interrupt ack */ falcon_irq_ack_a1(efx); @@ -1489,126 +199,6 @@ static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) return IRQ_HANDLED; } - -/* Handle an MSI interrupt from Falcon - * - * Handle an MSI hardware interrupt. This routine schedules event - * queue processing. No interrupt acknowledgement cycle is necessary. - * Also, we never need to check that the interrupt is for us, since - * MSI interrupts cannot be shared. - */ -static irqreturn_t falcon_msi_interrupt(int irq, void *dev_id) -{ - struct efx_channel *channel = dev_id; - struct efx_nic *efx = channel->efx; - efx_oword_t *int_ker = efx->irq_status.addr; - int syserr; - - efx->last_irq_cpu = raw_smp_processor_id(); - EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_OWORD_FMT "\n", - irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker)); - - /* Check to see if we have a serious error condition */ - syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT); - if (unlikely(syserr)) - return falcon_fatal_interrupt(efx); - - /* Schedule processing of the channel */ - efx_schedule_channel(channel); - - return IRQ_HANDLED; -} - - -/* Setup RSS indirection table. - * This maps from the hash value of the packet to RXQ - */ -static void falcon_setup_rss_indir_table(struct efx_nic *efx) -{ - int i = 0; - unsigned long offset; - efx_dword_t dword; - - if (falcon_rev(efx) < FALCON_REV_B0) - return; - - for (offset = FR_BZ_RX_INDIRECTION_TBL; - offset < FR_BZ_RX_INDIRECTION_TBL + 0x800; - offset += 0x10) { - EFX_POPULATE_DWORD_1(dword, FRF_BZ_IT_QUEUE, - i % efx->n_rx_queues); - efx_writed(efx, &dword, offset); - i++; - } -} - -/* Hook interrupt handler(s) - * Try MSI and then legacy interrupts. - */ -int falcon_init_interrupt(struct efx_nic *efx) -{ - struct efx_channel *channel; - int rc; - - if (!EFX_INT_MODE_USE_MSI(efx)) { - irq_handler_t handler; - if (falcon_rev(efx) >= FALCON_REV_B0) - handler = falcon_legacy_interrupt_b0; - else - handler = falcon_legacy_interrupt_a1; - - rc = request_irq(efx->legacy_irq, handler, IRQF_SHARED, - efx->name, efx); - if (rc) { - EFX_ERR(efx, "failed to hook legacy IRQ %d\n", - efx->pci_dev->irq); - goto fail1; - } - return 0; - } - - /* Hook MSI or MSI-X interrupt */ - efx_for_each_channel(channel, efx) { - rc = request_irq(channel->irq, falcon_msi_interrupt, - IRQF_PROBE_SHARED, /* Not shared */ - channel->name, channel); - if (rc) { - EFX_ERR(efx, "failed to hook IRQ %d\n", channel->irq); - goto fail2; - } - } - - return 0; - - fail2: - efx_for_each_channel(channel, efx) - free_irq(channel->irq, channel); - fail1: - return rc; -} - -void falcon_fini_interrupt(struct efx_nic *efx) -{ - struct efx_channel *channel; - efx_oword_t reg; - - /* Disable MSI/MSI-X interrupts */ - efx_for_each_channel(channel, efx) { - if (channel->irq) - free_irq(channel->irq, channel); - } - - /* ACK legacy interrupt */ - if (falcon_rev(efx) >= FALCON_REV_B0) - efx_reado(efx, ®, FR_BZ_INT_ISR0); - else - falcon_irq_ack_a1(efx); - - /* Disable legacy interrupt */ - if (efx->legacy_irq) - free_irq(efx->legacy_irq, efx); -} - /************************************************************************** * * EEPROM/flash @@ -1652,11 +242,10 @@ static int falcon_spi_wait(struct efx_nic *efx) } } -int falcon_spi_cmd(const struct efx_spi_device *spi, +int falcon_spi_cmd(struct efx_nic *efx, const struct efx_spi_device *spi, unsigned int command, int address, const void *in, void *out, size_t len) { - struct efx_nic *efx = spi->efx; bool addressed = (address >= 0); bool reading = (out != NULL); efx_oword_t reg; @@ -1725,15 +314,15 @@ efx_spi_munge_command(const struct efx_spi_device *spi, } /* Wait up to 10 ms for buffered write completion */ -int falcon_spi_wait_write(const struct efx_spi_device *spi) +int +falcon_spi_wait_write(struct efx_nic *efx, const struct efx_spi_device *spi) { - struct efx_nic *efx = spi->efx; unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 100); u8 status; int rc; for (;;) { - rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, &status, sizeof(status)); if (rc) return rc; @@ -1749,8 +338,8 @@ int falcon_spi_wait_write(const struct efx_spi_device *spi) } } -int falcon_spi_read(const struct efx_spi_device *spi, loff_t start, - size_t len, size_t *retlen, u8 *buffer) +int falcon_spi_read(struct efx_nic *efx, const struct efx_spi_device *spi, + loff_t start, size_t len, size_t *retlen, u8 *buffer) { size_t block_len, pos = 0; unsigned int command; @@ -1760,7 +349,7 @@ int falcon_spi_read(const struct efx_spi_device *spi, loff_t start, block_len = min(len - pos, FALCON_SPI_MAX_LEN); command = efx_spi_munge_command(spi, SPI_READ, start + pos); - rc = falcon_spi_cmd(spi, command, start + pos, NULL, + rc = falcon_spi_cmd(efx, spi, command, start + pos, NULL, buffer + pos, block_len); if (rc) break; @@ -1779,8 +368,9 @@ int falcon_spi_read(const struct efx_spi_device *spi, loff_t start, return rc; } -int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, - size_t len, size_t *retlen, const u8 *buffer) +int +falcon_spi_write(struct efx_nic *efx, const struct efx_spi_device *spi, + loff_t start, size_t len, size_t *retlen, const u8 *buffer) { u8 verify_buffer[FALCON_SPI_MAX_LEN]; size_t block_len, pos = 0; @@ -1788,24 +378,24 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, int rc = 0; while (pos < len) { - rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); if (rc) break; block_len = min(len - pos, falcon_spi_write_limit(spi, start + pos)); command = efx_spi_munge_command(spi, SPI_WRITE, start + pos); - rc = falcon_spi_cmd(spi, command, start + pos, + rc = falcon_spi_cmd(efx, spi, command, start + pos, buffer + pos, NULL, block_len); if (rc) break; - rc = falcon_spi_wait_write(spi); + rc = falcon_spi_wait_write(efx, spi); if (rc) break; command = efx_spi_munge_command(spi, SPI_READ, start + pos); - rc = falcon_spi_cmd(spi, command, start + pos, + rc = falcon_spi_cmd(efx, spi, command, start + pos, NULL, verify_buffer, block_len); if (memcmp(verify_buffer, buffer + pos, block_len)) { rc = -EIO; @@ -1834,12 +424,23 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, ************************************************************************** */ -static int falcon_reset_macs(struct efx_nic *efx) +static void falcon_push_multicast_hash(struct efx_nic *efx) { - efx_oword_t reg; + union efx_multicast_hash *mc_hash = &efx->multicast_hash; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + + efx_writeo(efx, &mc_hash->oword[0], FR_AB_MAC_MC_HASH_REG0); + efx_writeo(efx, &mc_hash->oword[1], FR_AB_MAC_MC_HASH_REG1); +} + +static void falcon_reset_macs(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + efx_oword_t reg, mac_ctrl; int count; - if (falcon_rev(efx) < FALCON_REV_B0) { + if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) { /* It's not safe to use GLB_CTL_REG to reset the * macs, so instead use the internal MAC resets */ @@ -1851,7 +452,7 @@ static int falcon_reset_macs(struct efx_nic *efx) EFX_POPULATE_OWORD_1(reg, FRF_AB_GM_SW_RST, 0); efx_writeo(efx, ®, FR_AB_GM_CFG1); udelay(1000); - return 0; + return; } else { EFX_POPULATE_OWORD_1(reg, FRF_AB_XM_CORE_RST, 1); efx_writeo(efx, ®, FR_AB_XM_GLB_CFG); @@ -1860,22 +461,20 @@ static int falcon_reset_macs(struct efx_nic *efx) efx_reado(efx, ®, FR_AB_XM_GLB_CFG); if (EFX_OWORD_FIELD(reg, FRF_AB_XM_CORE_RST) == 0) - return 0; + return; udelay(10); } EFX_ERR(efx, "timed out waiting for XMAC core reset\n"); - return -ETIMEDOUT; } } - /* MAC stats will fail whilst the TX fifo is draining. Serialise - * the drain sequence with the statistics fetch */ - efx_stats_disable(efx); + /* Mac stats will fail whist the TX fifo is draining */ + WARN_ON(nic_data->stats_disable_count == 0); - efx_reado(efx, ®, FR_AB_MAC_CTRL); - EFX_SET_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN, 1); - efx_writeo(efx, ®, FR_AB_MAC_CTRL); + efx_reado(efx, &mac_ctrl, FR_AB_MAC_CTRL); + EFX_SET_OWORD_FIELD(mac_ctrl, FRF_BB_TXFIFO_DRAIN_EN, 1); + efx_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL); efx_reado(efx, ®, FR_AB_GLB_CTL); EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_XGTX, 1); @@ -1901,21 +500,16 @@ static int falcon_reset_macs(struct efx_nic *efx) udelay(10); } - efx_stats_enable(efx); - - /* If we've reset the EM block and the link is up, then - * we'll have to kick the XAUI link so the PHY can recover */ - if (efx->link_state.up && EFX_IS10G(efx) && EFX_WORKAROUND_5147(efx)) - falcon_reset_xaui(efx); - - return 0; + /* Ensure the correct MAC is selected before statistics + * are re-enabled by the caller */ + efx_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL); } void falcon_drain_tx_fifo(struct efx_nic *efx) { efx_oword_t reg; - if ((falcon_rev(efx) < FALCON_REV_B0) || + if ((efx_nic_rev(efx) < EFX_REV_FALCON_B0) || (efx->loopback_mode != LOOPBACK_NONE)) return; @@ -1927,11 +521,11 @@ void falcon_drain_tx_fifo(struct efx_nic *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; - if (falcon_rev(efx) < FALCON_REV_B0) + if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) return; /* Isolate the MAC -> RX */ @@ -1939,8 +533,8 @@ void falcon_deconfigure_mac_wrapper(struct efx_nic *efx) EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 0); efx_writeo(efx, ®, FR_AZ_RX_CFG); - if (!efx->link_state.up) - falcon_drain_tx_fifo(efx); + /* Isolate TX -> MAC */ + falcon_drain_tx_fifo(efx); } void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) @@ -1948,7 +542,6 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) struct efx_link_state *link_state = &efx->link_state; efx_oword_t reg; int link_speed; - bool tx_fc; switch (link_state->speed) { case 10000: link_speed = 3; break; @@ -1968,7 +561,7 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) FRF_AB_MAC_SPEED, link_speed); /* On B0, MAC backpressure can be disabled and packets get * discarded. */ - if (falcon_rev(efx) >= FALCON_REV_B0) { + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { EFX_SET_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN, !link_state->up); } @@ -1976,40 +569,31 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) efx_writeo(efx, ®, FR_AB_MAC_CTRL); /* Restore the multicast hash registers. */ - falcon_set_multicast_hash(efx); + falcon_push_multicast_hash(efx); - /* Transmission of pause frames when RX crosses the threshold is - * covered by RX_XOFF_MAC_EN and XM_TX_CFG_REG:XM_FCNTL. - * Action on receipt of pause frames is controller by XM_DIS_FCNTL */ - tx_fc = !!(efx->link_state.fc & EFX_FC_TX); efx_reado(efx, ®, FR_AZ_RX_CFG); - EFX_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, tx_fc); - + /* Enable XOFF signal from RX FIFO (we enabled it during NIC + * initialisation but it may read back as 0) */ + EFX_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, 1); /* Unisolate the MAC -> RX */ - if (falcon_rev(efx) >= FALCON_REV_B0) + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 1); efx_writeo(efx, ®, FR_AZ_RX_CFG); } -int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset) +static void falcon_stats_request(struct efx_nic *efx) { + struct falcon_nic_data *nic_data = efx->nic_data; efx_oword_t reg; - u32 *dma_done; - int i; - if (disable_dma_stats) - return 0; + WARN_ON(nic_data->stats_pending); + WARN_ON(nic_data->stats_disable_count); - /* Statistics fetch will fail if the MAC is in TX drain */ - if (falcon_rev(efx) >= FALCON_REV_B0) { - efx_oword_t temp; - efx_reado(efx, &temp, FR_AB_MAC_CTRL); - if (EFX_OWORD_FIELD(temp, FRF_BB_TXFIFO_DRAIN_EN)) - return 0; - } + if (nic_data->stats_dma_done == NULL) + return; /* no mac selected */ - dma_done = (efx->stats_buffer.addr + done_offset); - *dma_done = FALCON_STATS_NOT_DONE; + *nic_data->stats_dma_done = FALCON_STATS_NOT_DONE; + nic_data->stats_pending = true; wmb(); /* ensure done flag is clear */ /* Initiate DMA transfer of stats */ @@ -2019,17 +603,90 @@ int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset) efx->stats_buffer.dma_addr); efx_writeo(efx, ®, FR_AB_MAC_STAT_DMA); - /* Wait for transfer to complete */ - for (i = 0; i < 400; i++) { - if (*(volatile u32 *)dma_done == FALCON_STATS_DONE) { - rmb(); /* Ensure the stats are valid. */ - return 0; - } - udelay(10); + mod_timer(&nic_data->stats_timer, round_jiffies_up(jiffies + HZ / 2)); +} + +static void falcon_stats_complete(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + + if (!nic_data->stats_pending) + return; + + nic_data->stats_pending = 0; + if (*nic_data->stats_dma_done == FALCON_STATS_DONE) { + rmb(); /* read the done flag before the stats */ + efx->mac_op->update_stats(efx); + } else { + EFX_ERR(efx, "timed out waiting for statistics\n"); } +} - EFX_ERR(efx, "timed out waiting for statistics\n"); - return -ETIMEDOUT; +static void falcon_stats_timer_func(unsigned long context) +{ + struct efx_nic *efx = (struct efx_nic *)context; + struct falcon_nic_data *nic_data = efx->nic_data; + + spin_lock(&efx->stats_lock); + + falcon_stats_complete(efx); + if (nic_data->stats_disable_count == 0) + falcon_stats_request(efx); + + spin_unlock(&efx->stats_lock); +} + +static void falcon_switch_mac(struct efx_nic *efx); + +static bool falcon_loopback_link_poll(struct efx_nic *efx) +{ + struct efx_link_state old_state = efx->link_state; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + WARN_ON(!LOOPBACK_INTERNAL(efx)); + + efx->link_state.fd = true; + efx->link_state.fc = efx->wanted_fc; + efx->link_state.up = true; + + if (efx->loopback_mode == LOOPBACK_GMAC) + efx->link_state.speed = 1000; + else + efx->link_state.speed = 10000; + + 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; } /************************************************************************** @@ -2042,18 +699,18 @@ int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset) /* Wait for GMII access to complete */ static int falcon_gmii_wait(struct efx_nic *efx) { - efx_dword_t md_stat; + efx_oword_t md_stat; int count; /* wait upto 50ms - taken max from datasheet */ for (count = 0; count < 5000; count++) { - efx_readd(efx, &md_stat, FR_AB_MD_STAT); - if (EFX_DWORD_FIELD(md_stat, FRF_AB_MD_BSY) == 0) { - if (EFX_DWORD_FIELD(md_stat, FRF_AB_MD_LNFL) != 0 || - EFX_DWORD_FIELD(md_stat, FRF_AB_MD_BSERR) != 0) { + efx_reado(efx, &md_stat, FR_AB_MD_STAT); + if (EFX_OWORD_FIELD(md_stat, FRF_AB_MD_BSY) == 0) { + if (EFX_OWORD_FIELD(md_stat, FRF_AB_MD_LNFL) != 0 || + EFX_OWORD_FIELD(md_stat, FRF_AB_MD_BSERR) != 0) { EFX_ERR(efx, "error from GMII access " - EFX_DWORD_FMT"\n", - EFX_DWORD_VAL(md_stat)); + EFX_OWORD_FMT"\n", + EFX_OWORD_VAL(md_stat)); return -EIO; } return 0; @@ -2075,7 +732,7 @@ static int falcon_mdio_write(struct net_device *net_dev, EFX_REGDUMP(efx, "writing MDIO %d register %d.%d with 0x%04x\n", prtad, devad, addr, value); - spin_lock_bh(&efx->phy_lock); + mutex_lock(&efx->mdio_lock); /* Check MDIO not currently being accessed */ rc = falcon_gmii_wait(efx); @@ -2110,8 +767,8 @@ static int falcon_mdio_write(struct net_device *net_dev, udelay(10); } - out: - spin_unlock_bh(&efx->phy_lock); +out: + mutex_unlock(&efx->mdio_lock); return rc; } @@ -2123,7 +780,7 @@ static int falcon_mdio_read(struct net_device *net_dev, efx_oword_t reg; int rc; - spin_lock_bh(&efx->phy_lock); + mutex_lock(&efx->mdio_lock); /* Check MDIO not currently being accessed */ rc = falcon_gmii_wait(efx); @@ -2159,39 +816,20 @@ static int falcon_mdio_read(struct net_device *net_dev, prtad, devad, addr, rc); } - out: - spin_unlock_bh(&efx->phy_lock); +out: + mutex_unlock(&efx->mdio_lock); return rc; } -int falcon_switch_mac(struct efx_nic *efx) +static void falcon_clock_mac(struct efx_nic *efx) { - struct efx_mac_operations *old_mac_op = efx->mac_op; - efx_oword_t nic_stat; unsigned strap_val; - int rc = 0; - - /* Don't try to fetch MAC stats while we're switching MACs */ - efx_stats_disable(efx); - - /* Internal loopbacks override the phy speed setting */ - if (efx->loopback_mode == LOOPBACK_GMAC) { - efx->link_state.speed = 1000; - efx->link_state.fd = true; - } else if (LOOPBACK_INTERNAL(efx)) { - efx->link_state.speed = 10000; - efx->link_state.fd = true; - } - - WARN_ON(!mutex_is_locked(&efx->mac_lock)); - efx->mac_op = (EFX_IS10G(efx) ? - &falcon_xmac_operations : &falcon_gmac_operations); + efx_oword_t nic_stat; - /* Always push the NIC_STAT_REG setting even if the mac hasn't - * changed, because this function is run post online reset */ + /* Configure the NIC generated MAC clock correctly */ efx_reado(efx, &nic_stat, FR_AB_NIC_STAT); strap_val = EFX_IS10G(efx) ? 5 : 3; - if (falcon_rev(efx) >= FALCON_REV_B0) { + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { EFX_SET_OWORD_FIELD(nic_stat, FRF_BB_EE_STRAP_EN, 1); EFX_SET_OWORD_FIELD(nic_stat, FRF_BB_EE_STRAP, strap_val); efx_writeo(efx, &nic_stat, FR_AB_NIC_STAT); @@ -2201,22 +839,39 @@ int falcon_switch_mac(struct efx_nic *efx) BUG_ON(EFX_OWORD_FIELD(nic_stat, FRF_AB_STRAP_PINS) != strap_val); } +} + +static void falcon_switch_mac(struct efx_nic *efx) +{ + struct efx_mac_operations *old_mac_op = efx->mac_op; + struct falcon_nic_data *nic_data = efx->nic_data; + unsigned int stats_done_offset; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + WARN_ON(nic_data->stats_disable_count == 0); + + efx->mac_op = (EFX_IS10G(efx) ? + &falcon_xmac_operations : &falcon_gmac_operations); + + if (EFX_IS10G(efx)) + stats_done_offset = XgDmaDone_offset; + else + stats_done_offset = GDmaDone_offset; + nic_data->stats_dma_done = efx->stats_buffer.addr + stats_done_offset; if (old_mac_op == efx->mac_op) - goto out; + return; + + falcon_clock_mac(efx); EFX_LOG(efx, "selected %cMAC\n", EFX_IS10G(efx) ? 'X' : 'G'); /* Not all macs support a mac-level link state */ - efx->mac_up = true; - - rc = falcon_reset_macs(efx); -out: - efx_stats_enable(efx); - return rc; + efx->xmac_poll_required = false; + falcon_reset_macs(efx); } /* This call is responsible for hooking in the MAC and PHY operations */ -int falcon_probe_port(struct efx_nic *efx) +static int falcon_probe_port(struct efx_nic *efx) { int rc; @@ -2238,29 +893,26 @@ int falcon_probe_port(struct efx_nic *efx) return -ENODEV; } - if (efx->phy_op->macs & EFX_XMAC) - efx->loopback_modes |= ((1 << LOOPBACK_XGMII) | - (1 << LOOPBACK_XGXS) | - (1 << LOOPBACK_XAUI)); - if (efx->phy_op->macs & EFX_GMAC) - efx->loopback_modes |= (1 << LOOPBACK_GMAC); - efx->loopback_modes |= efx->phy_op->loopbacks; - - /* Set up MDIO structure for PHY */ - efx->mdio.mmds = efx->phy_op->mmds; - efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + /* Fill out MDIO structure and loopback modes */ efx->mdio.mdio_read = falcon_mdio_read; efx->mdio.mdio_write = falcon_mdio_write; + rc = efx->phy_op->probe(efx); + if (rc != 0) + return rc; + + /* Initial assumption */ + efx->link_state.speed = 10000; + efx->link_state.fd = true; /* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */ - if (falcon_rev(efx) >= FALCON_REV_B0) + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) efx->wanted_fc = EFX_FC_RX | EFX_FC_TX; else efx->wanted_fc = EFX_FC_RX; /* Allocate buffer for stats */ - rc = falcon_alloc_buffer(efx, &efx->stats_buffer, - FALCON_MAC_STATS_SIZE); + rc = efx_nic_alloc_buffer(efx, &efx->stats_buffer, + FALCON_MAC_STATS_SIZE); if (rc) return rc; EFX_LOG(efx, "stats buffer at %llx (virt %p phys %llx)\n", @@ -2271,40 +923,19 @@ int falcon_probe_port(struct efx_nic *efx) return 0; } -void falcon_remove_port(struct efx_nic *efx) +static void falcon_remove_port(struct efx_nic *efx) { - falcon_free_buffer(efx, &efx->stats_buffer); -} - -/************************************************************************** - * - * Multicast filtering - * - ************************************************************************** - */ - -void falcon_set_multicast_hash(struct efx_nic *efx) -{ - union efx_multicast_hash *mc_hash = &efx->multicast_hash; - - /* 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); - - efx_writeo(efx, &mc_hash->oword[0], FR_AB_MAC_MC_HASH_REG0); - efx_writeo(efx, &mc_hash->oword[1], FR_AB_MAC_MC_HASH_REG1); + efx_nic_free_buffer(efx, &efx->stats_buffer); } - /************************************************************************** * * Falcon test code * **************************************************************************/ -int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out) +static int +falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out) { struct falcon_nvconfig *nvconfig; struct efx_spi_device *spi; @@ -2323,7 +954,7 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out) nvconfig = region + FALCON_NVCONFIG_OFFSET; mutex_lock(&efx->spi_lock); - rc = falcon_spi_read(spi, 0, FALCON_NVCONFIG_END, NULL, region); + rc = falcon_spi_read(efx, spi, 0, FALCON_NVCONFIG_END, NULL, region); mutex_unlock(&efx->spi_lock); if (rc) { EFX_ERR(efx, "Failed to read %s\n", @@ -2367,11 +998,12 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out) return rc; } -/* Registers tested in the falcon register test */ -static struct { - unsigned address; - efx_oword_t mask; -} efx_test_registers[] = { +static int falcon_test_nvram(struct efx_nic *efx) +{ + return falcon_read_nvram(efx, NULL); +} + +static const struct efx_nic_register_test falcon_b0_register_tests[] = { { FR_AZ_ADR_REGION, EFX_OWORD32(0x0001FFFF, 0x0001FFFF, 0x0001FFFF, 0x0001FFFF) }, { FR_AZ_RX_CFG, @@ -2410,64 +1042,10 @@ static struct { EFX_OWORD32(0x0003FF0F, 0x00000000, 0x00000000, 0x00000000) }, }; -static bool efx_masked_compare_oword(const efx_oword_t *a, const efx_oword_t *b, - const efx_oword_t *mask) -{ - return ((a->u64[0] ^ b->u64[0]) & mask->u64[0]) || - ((a->u64[1] ^ b->u64[1]) & mask->u64[1]); -} - -int falcon_test_registers(struct efx_nic *efx) +static int falcon_b0_test_registers(struct efx_nic *efx) { - unsigned address = 0, i, j; - efx_oword_t mask, imask, original, reg, buf; - - /* Falcon should be in loopback to isolate the XMAC from the PHY */ - WARN_ON(!LOOPBACK_INTERNAL(efx)); - - for (i = 0; i < ARRAY_SIZE(efx_test_registers); ++i) { - address = efx_test_registers[i].address; - mask = imask = efx_test_registers[i].mask; - EFX_INVERT_OWORD(imask); - - efx_reado(efx, &original, address); - - /* bit sweep on and off */ - for (j = 0; j < 128; j++) { - if (!EFX_EXTRACT_OWORD32(mask, j, j)) - continue; - - /* Test this testable bit can be set in isolation */ - EFX_AND_OWORD(reg, original, mask); - EFX_SET_OWORD32(reg, j, j, 1); - - efx_writeo(efx, ®, address); - efx_reado(efx, &buf, address); - - if (efx_masked_compare_oword(®, &buf, &mask)) - goto fail; - - /* Test this testable bit can be cleared in isolation */ - EFX_OR_OWORD(reg, original, mask); - EFX_SET_OWORD32(reg, j, j, 0); - - efx_writeo(efx, ®, address); - efx_reado(efx, &buf, address); - - if (efx_masked_compare_oword(®, &buf, &mask)) - goto fail; - } - - efx_writeo(efx, &original, address); - } - - return 0; - -fail: - EFX_ERR(efx, "wrote "EFX_OWORD_FMT" read "EFX_OWORD_FMT - " at address 0x%x mask "EFX_OWORD_FMT"\n", EFX_OWORD_VAL(reg), - EFX_OWORD_VAL(buf), address, EFX_OWORD_VAL(mask)); - return -EIO; + return efx_nic_test_registers(efx, falcon_b0_register_tests, + ARRAY_SIZE(falcon_b0_register_tests)); } /************************************************************************** @@ -2479,7 +1057,7 @@ fail: /* Resets NIC to known state. This routine must be called in process * context and is allowed to sleep. */ -int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) +static int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) { struct falcon_nic_data *nic_data = efx->nic_data; efx_oword_t glb_ctl_reg_ker; @@ -2495,7 +1073,7 @@ int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) "function prior to hardware reset\n"); goto fail1; } - if (FALCON_IS_DUAL_FUNC(efx)) { + if (efx_nic_is_dual_func(efx)) { rc = pci_save_state(nic_data->pci_dev2); if (rc) { EFX_ERR(efx, "failed to backup PCI state of " @@ -2530,7 +1108,7 @@ int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) /* Restore PCI configuration if needed */ if (method == RESET_TYPE_WORLD) { - if (FALCON_IS_DUAL_FUNC(efx)) { + if (efx_nic_is_dual_func(efx)) { rc = pci_restore_state(nic_data->pci_dev2); if (rc) { EFX_ERR(efx, "failed to restore PCI config for " @@ -2568,6 +1146,44 @@ fail5: return rc; } +static void falcon_monitor(struct efx_nic *efx) +{ + bool link_changed; + int rc; + + BUG_ON(!mutex_is_locked(&efx->mac_lock)); + + rc = falcon_board(efx)->type->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; + rc = __efx_reconfigure_port(efx); + WARN_ON(rc); + } + + if (LOOPBACK_INTERNAL(efx)) + link_changed = falcon_loopback_link_poll(efx); + else + link_changed = efx->phy_op->poll(efx); + + if (link_changed) { + falcon_stop_nic_stats(efx); + falcon_deconfigure_mac_wrapper(efx); + + falcon_switch_mac(efx); + rc = efx->mac_op->reconfigure(efx); + BUG_ON(rc); + + falcon_start_nic_stats(efx); + + efx_link_status_changed(efx); + } + + if (EFX_IS10G(efx)) + falcon_poll_xmac(efx); +} + /* Zeroes out the SRAM contents. This routine must be called in * process context and is allowed to sleep. */ @@ -2634,8 +1250,6 @@ static int falcon_spi_device_init(struct efx_nic *efx, spi_device->block_size = 1 << SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_BLOCK_SIZE); - - spi_device->efx = efx; } else { spi_device = NULL; } @@ -2645,7 +1259,6 @@ static int falcon_spi_device_init(struct efx_nic *efx, return 0; } - static void falcon_remove_spi_devices(struct efx_nic *efx) { kfree(efx->spi_eeprom); @@ -2715,49 +1328,6 @@ static int falcon_probe_nvconfig(struct efx_nic *efx) return rc; } -/* Probe the NIC variant (revision, ASIC vs FPGA, function count, port - * count, port speed). Set workaround and feature flags accordingly. - */ -static int falcon_probe_nic_variant(struct efx_nic *efx) -{ - efx_oword_t altera_build; - efx_oword_t nic_stat; - - efx_reado(efx, &altera_build, FR_AZ_ALTERA_BUILD); - if (EFX_OWORD_FIELD(altera_build, FRF_AZ_ALTERA_BUILD_VER)) { - EFX_ERR(efx, "Falcon FPGA not supported\n"); - return -ENODEV; - } - - efx_reado(efx, &nic_stat, FR_AB_NIC_STAT); - - switch (falcon_rev(efx)) { - case FALCON_REV_A0: - case 0xff: - EFX_ERR(efx, "Falcon rev A0 not supported\n"); - return -ENODEV; - - case FALCON_REV_A1: - if (EFX_OWORD_FIELD(nic_stat, FRF_AA_STRAP_PCIE) == 0) { - EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n"); - return -ENODEV; - } - break; - - case FALCON_REV_B0: - break; - - default: - EFX_ERR(efx, "Unknown Falcon rev %d\n", falcon_rev(efx)); - return -ENODEV; - } - - /* Initial assumed speed */ - efx->link_state.speed = EFX_OWORD_FIELD(nic_stat, FRF_AB_STRAP_10G) ? 10000 : 1000; - - return 0; -} - /* Probe all SPI devices on the NIC */ static void falcon_probe_spi_devices(struct efx_nic *efx) { @@ -2797,7 +1367,7 @@ static void falcon_probe_spi_devices(struct efx_nic *efx) large_eeprom_type); } -int falcon_probe_nic(struct efx_nic *efx) +static int falcon_probe_nic(struct efx_nic *efx) { struct falcon_nic_data *nic_data; struct falcon_board *board; @@ -2809,15 +1379,33 @@ int falcon_probe_nic(struct efx_nic *efx) return -ENOMEM; efx->nic_data = nic_data; - /* Determine number of ports etc. */ - rc = falcon_probe_nic_variant(efx); - if (rc) + rc = -ENODEV; + + if (efx_nic_fpga_ver(efx) != 0) { + EFX_ERR(efx, "Falcon FPGA not supported\n"); goto fail1; + } - /* Probe secondary function if expected */ - if (FALCON_IS_DUAL_FUNC(efx)) { - struct pci_dev *dev = pci_dev_get(efx->pci_dev); + if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) { + efx_oword_t nic_stat; + struct pci_dev *dev; + u8 pci_rev = efx->pci_dev->revision; + if ((pci_rev == 0xff) || (pci_rev == 0)) { + EFX_ERR(efx, "Falcon rev A0 not supported\n"); + goto fail1; + } + efx_reado(efx, &nic_stat, FR_AB_NIC_STAT); + if (EFX_OWORD_FIELD(nic_stat, FRF_AB_STRAP_10G) == 0) { + EFX_ERR(efx, "Falcon rev A1 1G not supported\n"); + goto fail1; + } + if (EFX_OWORD_FIELD(nic_stat, FRF_AA_STRAP_PCIE) == 0) { + EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n"); + goto fail1; + } + + dev = pci_dev_get(efx->pci_dev); while ((dev = pci_get_device(EFX_VENDID_SFC, FALCON_A_S_DEVID, dev))) { if (dev->bus == efx->pci_dev->bus && @@ -2841,7 +1429,7 @@ int falcon_probe_nic(struct efx_nic *efx) } /* Allocate memory for INT_KER */ - rc = falcon_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t)); + rc = efx_nic_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t)); if (rc) goto fail4; BUG_ON(efx->irq_status.dma_addr & 0x0f); @@ -2870,12 +1458,16 @@ int falcon_probe_nic(struct efx_nic *efx) if (rc) goto fail5; - rc = falcon_board(efx)->init(efx); + rc = falcon_board(efx)->type->init(efx); if (rc) { EFX_ERR(efx, "failed to initialise board\n"); goto fail6; } + nic_data->stats_disable_count = 1; + setup_timer(&nic_data->stats_timer, &falcon_stats_timer_func, + (unsigned long)efx); + return 0; fail6: @@ -2883,7 +1475,7 @@ int falcon_probe_nic(struct efx_nic *efx) memset(&board->i2c_adap, 0, sizeof(board->i2c_adap)); fail5: falcon_remove_spi_devices(efx); - falcon_free_buffer(efx, &efx->irq_status); + efx_nic_free_buffer(efx, &efx->irq_status); fail4: fail3: if (nic_data->pci_dev2) { @@ -2906,12 +1498,12 @@ static void falcon_init_rx_cfg(struct efx_nic *efx) const unsigned ctrl_xon_thr = 20; const unsigned ctrl_xoff_thr = 25; /* RX data FIFO thresholds (256-byte units; size varies) */ - int data_xon_thr = rx_xon_thresh_bytes >> 8; - int data_xoff_thr = rx_xoff_thresh_bytes >> 8; + int data_xon_thr = efx_nic_rx_xon_thresh >> 8; + int data_xoff_thr = efx_nic_rx_xoff_thresh >> 8; efx_oword_t reg; efx_reado(efx, ®, FR_AZ_RX_CFG); - if (falcon_rev(efx) <= FALCON_REV_A1) { + if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) { /* Data FIFO size is 5.5K */ if (data_xon_thr < 0) data_xon_thr = 512 >> 8; @@ -2939,6 +1531,9 @@ static void falcon_init_rx_cfg(struct efx_nic *efx) EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_TX_TH, ctrl_xoff_thr); EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 1); } + /* Always enable XOFF signal from RX FIFO. We enable + * or disable transmission of pause frames at the MAC. */ + EFX_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, 1); efx_writeo(efx, ®, FR_AZ_RX_CFG); } @@ -2946,7 +1541,7 @@ static void falcon_init_rx_cfg(struct efx_nic *efx) * defining the descriptor cache sizes and number of RSS channels. * It does not set up any buffers, descriptor rings or event queues. */ -int falcon_init_nic(struct efx_nic *efx) +static int falcon_init_nic(struct efx_nic *efx) { efx_oword_t temp; int rc; @@ -2957,36 +1552,19 @@ int falcon_init_nic(struct efx_nic *efx) efx_writeo(efx, &temp, FR_AB_NIC_STAT); /* Set the source of the GMAC clock */ - if (falcon_rev(efx) == FALCON_REV_B0) { + if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) { efx_reado(efx, &temp, FR_AB_GPIO_CTL); EFX_SET_OWORD_FIELD(temp, FRF_AB_USE_NIC_CLK, true); efx_writeo(efx, &temp, FR_AB_GPIO_CTL); } + /* Select the correct MAC */ + falcon_clock_mac(efx); + rc = falcon_reset_sram(efx); if (rc) return rc; - /* Set positions of descriptor caches in SRAM. */ - EFX_POPULATE_OWORD_1(temp, FRF_AZ_SRM_TX_DC_BASE_ADR, TX_DC_BASE / 8); - efx_writeo(efx, &temp, FR_AZ_SRM_TX_DC_CFG); - EFX_POPULATE_OWORD_1(temp, FRF_AZ_SRM_RX_DC_BASE_ADR, RX_DC_BASE / 8); - efx_writeo(efx, &temp, FR_AZ_SRM_RX_DC_CFG); - - /* Set TX descriptor cache size. */ - BUILD_BUG_ON(TX_DC_ENTRIES != (16 << TX_DC_ENTRIES_ORDER)); - EFX_POPULATE_OWORD_1(temp, FRF_AZ_TX_DC_SIZE, TX_DC_ENTRIES_ORDER); - efx_writeo(efx, &temp, FR_AZ_TX_DC_CFG); - - /* Set RX descriptor cache size. Set low watermark to size-8, as - * this allows most efficient prefetching. - */ - BUILD_BUG_ON(RX_DC_ENTRIES != (16 << RX_DC_ENTRIES_ORDER)); - EFX_POPULATE_OWORD_1(temp, FRF_AZ_RX_DC_SIZE, RX_DC_ENTRIES_ORDER); - efx_writeo(efx, &temp, FR_AZ_RX_DC_CFG); - EFX_POPULATE_OWORD_1(temp, FRF_AZ_RX_DC_PF_LWM, RX_DC_ENTRIES - 8); - efx_writeo(efx, &temp, FR_AZ_RX_DC_PF_WM); - /* Clear the parity enables on the TX data fifos as * they produce false parity errors because of timing issues */ @@ -2996,19 +1574,6 @@ int falcon_init_nic(struct efx_nic *efx) efx_writeo(efx, &temp, FR_AZ_CSR_SPARE); } - /* Enable all the genuinely fatal interrupts. (They are still - * masked by the overall interrupt mask, controlled by - * falcon_interrupts()). - * - * Note: All other fatal interrupts are enabled - */ - EFX_POPULATE_OWORD_3(temp, - FRF_AZ_ILL_ADR_INT_KER_EN, 1, - FRF_AZ_RBUF_OWN_INT_KER_EN, 1, - FRF_AZ_TBUF_OWN_INT_KER_EN, 1); - EFX_INVERT_OWORD(temp); - efx_writeo(efx, &temp, FR_AZ_FATAL_INTR_KER); - if (EFX_WORKAROUND_7244(efx)) { efx_reado(efx, &temp, FR_BZ_RX_FILTER_CTL); EFX_SET_OWORD_FIELD(temp, FRF_BZ_UDP_FULL_SRCH_LIMIT, 8); @@ -3018,8 +1583,6 @@ int falcon_init_nic(struct efx_nic *efx) efx_writeo(efx, &temp, FR_BZ_RX_FILTER_CTL); } - falcon_setup_rss_indir_table(efx); - /* XXX This is documented only for Falcon A0/A1 */ /* Setup RX. Wait for descriptor is broken and must * be disabled. RXDP recovery shouldn't be needed, but is. @@ -3031,24 +1594,6 @@ int falcon_init_nic(struct efx_nic *efx) EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_ISCSI_DIS, 1); efx_writeo(efx, &temp, FR_AA_RX_SELF_RST); - /* Disable the ugly timer-based TX DMA backoff and allow TX DMA to be - * controlled by the RX FIFO fill level. Set arbitration to one pkt/Q. - */ - efx_reado(efx, &temp, FR_AZ_TX_RESERVED); - EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER, 0xfe); - EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER_EN, 1); - EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_ONE_PKT_PER_Q, 1); - EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PUSH_EN, 0); - EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_DIS_NON_IP_EV, 1); - /* Enable SW_EV to inherit in char driver - assume harmless here */ - EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_SOFT_EVT_EN, 1); - /* Prefetch threshold 2 => fetch when descriptor cache half empty */ - EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PREF_THRESHOLD, 2); - /* Squash TX of packets of 16 bytes or less */ - if (falcon_rev(efx) >= FALCON_REV_B0 && EFX_WORKAROUND_9141(efx)) - EFX_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1); - efx_writeo(efx, &temp, FR_AZ_TX_RESERVED); - /* Do not enable TX_NO_EOP_DISC_EN, since it limits packets to 16 * descriptors (which is bad). */ @@ -3059,21 +1604,23 @@ int falcon_init_nic(struct efx_nic *efx) falcon_init_rx_cfg(efx); /* Set destination of both TX and RX Flush events */ - if (falcon_rev(efx) >= FALCON_REV_B0) { + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { EFX_POPULATE_OWORD_1(temp, FRF_BZ_FLS_EVQ_ID, 0); efx_writeo(efx, &temp, FR_BZ_DP_CTRL); } + efx_nic_init_common(efx); + return 0; } -void falcon_remove_nic(struct efx_nic *efx) +static void falcon_remove_nic(struct efx_nic *efx) { struct falcon_nic_data *nic_data = efx->nic_data; struct falcon_board *board = falcon_board(efx); int rc; - falcon_board(efx)->fini(efx); + board->type->fini(efx); /* Remove I2C adapter and clear it in preparation for a retry */ rc = i2c_del_adapter(&board->i2c_adap); @@ -3081,7 +1628,7 @@ void falcon_remove_nic(struct efx_nic *efx) memset(&board->i2c_adap, 0, sizeof(board->i2c_adap)); falcon_remove_spi_devices(efx); - falcon_free_buffer(efx, &efx->irq_status); + efx_nic_free_buffer(efx, &efx->irq_status); falcon_reset_hw(efx, RESET_TYPE_ALL); @@ -3096,13 +1643,86 @@ void falcon_remove_nic(struct efx_nic *efx) efx->nic_data = NULL; } -void falcon_update_nic_stats(struct efx_nic *efx) +static void falcon_update_nic_stats(struct efx_nic *efx) { + struct falcon_nic_data *nic_data = efx->nic_data; efx_oword_t cnt; + if (nic_data->stats_disable_count) + return; + efx_reado(efx, &cnt, FR_AZ_RX_NODESC_DROP); efx->n_rx_nodesc_drop_cnt += EFX_OWORD_FIELD(cnt, FRF_AB_RX_NODESC_DROP_CNT); + + if (nic_data->stats_pending && + *nic_data->stats_dma_done == FALCON_STATS_DONE) { + nic_data->stats_pending = false; + rmb(); /* read the done flag before the stats */ + efx->mac_op->update_stats(efx); + } +} + +void falcon_start_nic_stats(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + + spin_lock_bh(&efx->stats_lock); + if (--nic_data->stats_disable_count == 0) + falcon_stats_request(efx); + spin_unlock_bh(&efx->stats_lock); +} + +void falcon_stop_nic_stats(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + int i; + + might_sleep(); + + spin_lock_bh(&efx->stats_lock); + ++nic_data->stats_disable_count; + spin_unlock_bh(&efx->stats_lock); + + del_timer_sync(&nic_data->stats_timer); + + /* Wait enough time for the most recent transfer to + * complete. */ + for (i = 0; i < 4 && nic_data->stats_pending; i++) { + if (*nic_data->stats_dma_done == FALCON_STATS_DONE) + break; + msleep(1); + } + + spin_lock_bh(&efx->stats_lock); + falcon_stats_complete(efx); + spin_unlock_bh(&efx->stats_lock); +} + +static void falcon_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ + falcon_board(efx)->type->set_id_led(efx, mode); +} + +/************************************************************************** + * + * Wake on LAN + * + ************************************************************************** + */ + +static void falcon_get_wol(struct efx_nic *efx, struct ethtool_wolinfo *wol) +{ + wol->supported = 0; + wol->wolopts = 0; + memset(&wol->sopass, 0, sizeof(wol->sopass)); +} + +static int falcon_set_wol(struct efx_nic *efx, u32 type) +{ + if (type != 0) + return -EINVAL; + return 0; } /************************************************************************** @@ -3112,7 +1732,30 @@ void falcon_update_nic_stats(struct efx_nic *efx) ************************************************************************** */ -struct efx_nic_type falcon_a_nic_type = { +struct efx_nic_type falcon_a1_nic_type = { + .probe = falcon_probe_nic, + .remove = falcon_remove_nic, + .init = falcon_init_nic, + .fini = efx_port_dummy_op_void, + .monitor = falcon_monitor, + .reset = falcon_reset_hw, + .probe_port = falcon_probe_port, + .remove_port = falcon_remove_port, + .prepare_flush = falcon_prepare_flush, + .update_stats = falcon_update_nic_stats, + .start_stats = falcon_start_nic_stats, + .stop_stats = falcon_stop_nic_stats, + .set_id_led = falcon_set_id_led, + .push_irq_moderation = falcon_push_irq_moderation, + .push_multicast_hash = falcon_push_multicast_hash, + .reconfigure_port = falcon_reconfigure_port, + .get_wol = falcon_get_wol, + .set_wol = falcon_set_wol, + .resume_wol = efx_port_dummy_op_void, + .test_nvram = falcon_test_nvram, + .default_mac_ops = &falcon_xmac_operations, + + .revision = EFX_REV_FALCON_A1, .mem_map_size = 0x20000, .txd_ptr_tbl_base = FR_AA_TX_DESC_PTR_TBL_KER, .rxd_ptr_tbl_base = FR_AA_RX_DESC_PTR_TBL_KER, @@ -3123,9 +1766,37 @@ struct efx_nic_type falcon_a_nic_type = { .rx_buffer_padding = 0x24, .max_interrupt_mode = EFX_INT_MODE_MSI, .phys_addr_channels = 4, + .tx_dc_base = 0x130000, + .rx_dc_base = 0x100000, + .offload_features = NETIF_F_IP_CSUM, + .reset_world_flags = ETH_RESET_IRQ, }; -struct efx_nic_type falcon_b_nic_type = { +struct efx_nic_type falcon_b0_nic_type = { + .probe = falcon_probe_nic, + .remove = falcon_remove_nic, + .init = falcon_init_nic, + .fini = efx_port_dummy_op_void, + .monitor = falcon_monitor, + .reset = falcon_reset_hw, + .probe_port = falcon_probe_port, + .remove_port = falcon_remove_port, + .prepare_flush = falcon_prepare_flush, + .update_stats = falcon_update_nic_stats, + .start_stats = falcon_start_nic_stats, + .stop_stats = falcon_stop_nic_stats, + .set_id_led = falcon_set_id_led, + .push_irq_moderation = falcon_push_irq_moderation, + .push_multicast_hash = falcon_push_multicast_hash, + .reconfigure_port = falcon_reconfigure_port, + .get_wol = falcon_get_wol, + .set_wol = falcon_set_wol, + .resume_wol = efx_port_dummy_op_void, + .test_registers = falcon_b0_test_registers, + .test_nvram = falcon_test_nvram, + .default_mac_ops = &falcon_xmac_operations, + + .revision = EFX_REV_FALCON_B0, /* Map everything up to and including the RSS indirection * table. Don't map MSI-X table, MSI-X PBA since Linux * requires that they not be mapped. */ @@ -3143,5 +1814,9 @@ struct efx_nic_type falcon_b_nic_type = { .phys_addr_channels = 32, /* Hardware limit is 64, but the legacy * interrupt handler only supports 32 * channels */ + .tx_dc_base = 0x130000, + .rx_dc_base = 0x100000, + .offload_features = NETIF_F_IP_CSUM, + .reset_world_flags = ETH_RESET_IRQ, }; diff --git a/drivers/net/sfc/falcon.h b/drivers/net/sfc/falcon.h deleted file mode 100644 index 0da5ea7..0000000 --- a/drivers/net/sfc/falcon.h +++ /dev/null @@ -1,191 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 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 - * by the Free Software Foundation, incorporated herein by reference. - */ - -#ifndef EFX_FALCON_H -#define EFX_FALCON_H - -#include <linux/i2c-algo-bit.h> -#include "net_driver.h" -#include "efx.h" - -/* - * Falcon hardware control - */ - -enum falcon_revision { - FALCON_REV_A0 = 0, - FALCON_REV_A1 = 1, - FALCON_REV_B0 = 2, -}; - -static inline int falcon_rev(struct efx_nic *efx) -{ - return efx->pci_dev->revision; -} - -/** - * struct falcon_board - board information - * @type: Board model type - * @major: Major rev. ('A', 'B' ...) - * @minor: Minor rev. (0, 1, ...) - * @init: Allocate resources and initialise peripheral hardware - * @init_phy: Do board-specific PHY initialisation - * @set_id_led: Set state of identifying LED or revert to automatic function - * @monitor: Board-specific health check function - * @fini: Shut down hardware and free resources - * @i2c_adap: I2C adapter for on-board peripherals - * @i2c_data: Data for bit-banging algorithm - * @hwmon_client: I2C client for hardware monitor - * @ioexp_client: I2C client for power/port control - */ -struct falcon_board { - int type; - int major; - int minor; - int (*init) (struct efx_nic *nic); - void (*init_phy) (struct efx_nic *efx); - void (*set_id_led) (struct efx_nic *efx, enum efx_led_mode mode); - int (*monitor) (struct efx_nic *nic); - void (*fini) (struct efx_nic *nic); - struct i2c_adapter i2c_adap; - struct i2c_algo_bit_data i2c_data; - struct i2c_client *hwmon_client, *ioexp_client; -}; - -/** - * struct falcon_nic_data - Falcon NIC state - * @pci_dev2: The secondary PCI device if present - * @board: Board state and functions - */ -struct falcon_nic_data { - struct pci_dev *pci_dev2; - struct falcon_board board; -}; - -static inline struct falcon_board *falcon_board(struct efx_nic *efx) -{ - struct falcon_nic_data *data = efx->nic_data; - return &data->board; -} - -extern struct efx_nic_type falcon_a_nic_type; -extern struct efx_nic_type falcon_b_nic_type; - -/************************************************************************** - * - * Externs - * - ************************************************************************** - */ - -extern void falcon_probe_board(struct efx_nic *efx, u16 revision_info); - -/* TX data path */ -extern int falcon_probe_tx(struct efx_tx_queue *tx_queue); -extern void falcon_init_tx(struct efx_tx_queue *tx_queue); -extern void falcon_fini_tx(struct efx_tx_queue *tx_queue); -extern void falcon_remove_tx(struct efx_tx_queue *tx_queue); -extern void falcon_push_buffers(struct efx_tx_queue *tx_queue); - -/* RX data path */ -extern int falcon_probe_rx(struct efx_rx_queue *rx_queue); -extern void falcon_init_rx(struct efx_rx_queue *rx_queue); -extern void falcon_fini_rx(struct efx_rx_queue *rx_queue); -extern void falcon_remove_rx(struct efx_rx_queue *rx_queue); -extern void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue); - -/* Event data path */ -extern int falcon_probe_eventq(struct efx_channel *channel); -extern void falcon_init_eventq(struct efx_channel *channel); -extern void falcon_fini_eventq(struct efx_channel *channel); -extern void falcon_remove_eventq(struct efx_channel *channel); -extern int falcon_process_eventq(struct efx_channel *channel, int rx_quota); -extern void falcon_eventq_read_ack(struct efx_channel *channel); - -/* Ports */ -extern int falcon_probe_port(struct efx_nic *efx); -extern void falcon_remove_port(struct efx_nic *efx); - -/* MAC/PHY */ -extern int falcon_switch_mac(struct efx_nic *efx); -extern bool falcon_xaui_link_ok(struct efx_nic *efx); -extern int falcon_dma_stats(struct efx_nic *efx, - unsigned int done_offset); -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); - -/* Interrupts and test events */ -extern int falcon_init_interrupt(struct efx_nic *efx); -extern void falcon_enable_interrupts(struct efx_nic *efx); -extern void falcon_generate_test_event(struct efx_channel *channel, - unsigned int magic); -extern void falcon_sim_phy_event(struct efx_nic *efx); -extern void falcon_generate_interrupt(struct efx_nic *efx); -extern void falcon_set_int_moderation(struct efx_channel *channel); -extern void falcon_disable_interrupts(struct efx_nic *efx); -extern void falcon_fini_interrupt(struct efx_nic *efx); - -#define FALCON_IRQ_MOD_RESOLUTION 5 - -/* Global Resources */ -extern int falcon_probe_nic(struct efx_nic *efx); -extern int falcon_init_nic(struct efx_nic *efx); -extern int falcon_flush_queues(struct efx_nic *efx); -extern int falcon_reset_hw(struct efx_nic *efx, enum reset_type method); -extern void falcon_remove_nic(struct efx_nic *efx); -extern void falcon_update_nic_stats(struct efx_nic *efx); -extern void falcon_set_multicast_hash(struct efx_nic *efx); -extern int falcon_reset_xaui(struct efx_nic *efx); - -/* Tests */ -struct falcon_nvconfig; -extern int falcon_read_nvram(struct efx_nic *efx, - struct falcon_nvconfig *nvconfig); -extern int falcon_test_registers(struct efx_nic *efx); - -/************************************************************************** - * - * Falcon MAC stats - * - ************************************************************************** - */ - -#define FALCON_STAT_OFFSET(falcon_stat) EFX_VAL(falcon_stat, offset) -#define FALCON_STAT_WIDTH(falcon_stat) EFX_VAL(falcon_stat, WIDTH) - -/* Retrieve statistic from statistics block */ -#define FALCON_STAT(efx, falcon_stat, efx_stat) do { \ - if (FALCON_STAT_WIDTH(falcon_stat) == 16) \ - (efx)->mac_stats.efx_stat += le16_to_cpu( \ - *((__force __le16 *) \ - (efx->stats_buffer.addr + \ - FALCON_STAT_OFFSET(falcon_stat)))); \ - else if (FALCON_STAT_WIDTH(falcon_stat) == 32) \ - (efx)->mac_stats.efx_stat += le32_to_cpu( \ - *((__force __le32 *) \ - (efx->stats_buffer.addr + \ - FALCON_STAT_OFFSET(falcon_stat)))); \ - else \ - (efx)->mac_stats.efx_stat += le64_to_cpu( \ - *((__force __le64 *) \ - (efx->stats_buffer.addr + \ - FALCON_STAT_OFFSET(falcon_stat)))); \ - } while (0) - -#define FALCON_MAC_STATS_SIZE 0x100 - -#define MAC_DATA_LBN 0 -#define MAC_DATA_WIDTH 32 - -extern void falcon_generate_event(struct efx_channel *channel, - efx_qword_t *event); - -#endif /* EFX_FALCON_H */ diff --git a/drivers/net/sfc/falcon_boards.c b/drivers/net/sfc/falcon_boards.c index 333ccc1..bf0b96a 100644 --- a/drivers/net/sfc/falcon_boards.c +++ b/drivers/net/sfc/falcon_boards.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2008 Solarflare Communications Inc. + * Copyright 2007-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 @@ -12,7 +12,7 @@ #include "net_driver.h" #include "phy.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #include "regs.h" #include "io.h" #include "workarounds.h" @@ -144,7 +144,7 @@ static inline int efx_check_lm87(struct efx_nic *efx, unsigned mask) */ /************************************************************************** - * Support for I2C IO Expander device on SFE40001 + * Support for I2C IO Expander device on SFE4001 */ #define PCA9539 0x74 @@ -347,14 +347,15 @@ static ssize_t set_phy_flash_cfg(struct device *dev, * MAC stats accordingly. */ efx->phy_mode = new_mode; if (new_mode & PHY_MODE_SPECIAL) - efx_stats_disable(efx); - if (falcon_board(efx)->type == FALCON_BOARD_SFE4001) + falcon_stop_nic_stats(efx); + if (falcon_board(efx)->type->id == FALCON_BOARD_SFE4001) err = sfe4001_poweron(efx); else err = sfn4111t_reset(efx); - efx_reconfigure_port(efx); + if (!err) + err = efx_reconfigure_port(efx); if (!(new_mode & PHY_MODE_SPECIAL)) - efx_stats_enable(efx); + falcon_start_nic_stats(efx); } rtnl_unlock(); @@ -380,7 +381,7 @@ static int sfe4001_check_hw(struct efx_nic *efx) s32 status; /* If XAUI link is up then do not monitor */ - if (EFX_WORKAROUND_7884(efx) && efx->mac_up) + if (EFX_WORKAROUND_7884(efx) && !efx->xmac_poll_required) return 0; /* Check the powered status of the PHY. Lack of power implies that @@ -438,17 +439,10 @@ static int sfe4001_init(struct efx_nic *efx) goto fail_hwmon; } - /* 10Xpress has fixed-function LED pins, so there is no board-specific - * blink code. */ - board->set_id_led = tenxpress_set_id_led; - - board->monitor = sfe4001_check_hw; - board->fini = sfe4001_fini; - if (efx->phy_mode & PHY_MODE_SPECIAL) { /* PHY won't generate a 156.25 MHz clock and MAC stats fetch * will fail. */ - efx_stats_disable(efx); + falcon_stop_nic_stats(efx); } rc = sfe4001_poweron(efx); if (rc) @@ -475,7 +469,7 @@ static int sfn4111t_check_hw(struct efx_nic *efx) s32 status; /* If XAUI link is up then do not monitor */ - if (EFX_WORKAROUND_7884(efx) && efx->mac_up) + if (EFX_WORKAROUND_7884(efx) && !efx->xmac_poll_required) return 0; /* Test LHIGH, RHIGH, FAULT, EOT and IOT alarms */ @@ -511,7 +505,7 @@ static void sfn4111t_init_phy(struct efx_nic *efx) return; efx->phy_mode = PHY_MODE_SPECIAL; - efx_stats_disable(efx); + falcon_stop_nic_stats(efx); } sfn4111t_reset(efx); @@ -531,11 +525,6 @@ static int sfn4111t_init(struct efx_nic *efx) if (!board->hwmon_client) return -EIO; - board->init_phy = sfn4111t_init_phy; - board->set_id_led = tenxpress_set_id_led; - board->monitor = sfn4111t_check_hw; - board->fini = sfn4111t_fini; - rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); if (rc) goto fail_hwmon; @@ -543,7 +532,7 @@ static int sfn4111t_init(struct efx_nic *efx) if (efx->phy_mode & PHY_MODE_SPECIAL) /* PHY may not generate a 156.25 MHz clock and MAC * stats fetch will fail. */ - efx_stats_disable(efx); + falcon_stop_nic_stats(efx); return 0; @@ -620,15 +609,7 @@ static int sfe4002_check_hw(struct efx_nic *efx) static int sfe4002_init(struct efx_nic *efx) { - struct falcon_board *board = falcon_board(efx); - int rc = efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs); - if (rc) - return rc; - board->monitor = sfe4002_check_hw; - board->init_phy = sfe4002_init_phy; - board->set_id_led = sfe4002_set_id_led; - board->fini = efx_fini_lm87; - return 0; + return efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs); } /***************************************************************************** @@ -692,67 +673,80 @@ static int sfn4112f_check_hw(struct efx_nic *efx) static int sfn4112f_init(struct efx_nic *efx) { - struct falcon_board *board = falcon_board(efx); - - int rc = efx_init_lm87(efx, &sfn4112f_hwmon_info, sfn4112f_lm87_regs); - if (rc) - return rc; - board->monitor = sfn4112f_check_hw; - board->init_phy = sfn4112f_init_phy; - board->set_id_led = sfn4112f_set_id_led; - board->fini = efx_fini_lm87; - return 0; + return efx_init_lm87(efx, &sfn4112f_hwmon_info, sfn4112f_lm87_regs); } -/* This will get expanded as board-specific details get moved out of the - * PHY drivers. */ -struct falcon_board_data { - u8 type; - const char *ref_model; - const char *gen_type; - int (*init) (struct efx_nic *nic); -}; - - -static struct falcon_board_data board_data[] = { - { FALCON_BOARD_SFE4001, "SFE4001", "10GBASE-T adapter", sfe4001_init }, - { FALCON_BOARD_SFE4002, "SFE4002", "XFP adapter", sfe4002_init }, - { FALCON_BOARD_SFN4111T, "SFN4111T", "100/1000/10GBASE-T adapter", - sfn4111t_init }, - { FALCON_BOARD_SFN4112F, "SFN4112F", "SFP+ adapter", - sfn4112f_init }, +static const struct falcon_board_type board_types[] = { + { + .id = FALCON_BOARD_SFE4001, + .ref_model = "SFE4001", + .gen_type = "10GBASE-T adapter", + .init = sfe4001_init, + .init_phy = efx_port_dummy_op_void, + .fini = sfe4001_fini, + .set_id_led = tenxpress_set_id_led, + .monitor = sfe4001_check_hw, + }, + { + .id = FALCON_BOARD_SFE4002, + .ref_model = "SFE4002", + .gen_type = "XFP adapter", + .init = sfe4002_init, + .init_phy = sfe4002_init_phy, + .fini = efx_fini_lm87, + .set_id_led = sfe4002_set_id_led, + .monitor = sfe4002_check_hw, + }, + { + .id = FALCON_BOARD_SFN4111T, + .ref_model = "SFN4111T", + .gen_type = "100/1000/10GBASE-T adapter", + .init = sfn4111t_init, + .init_phy = sfn4111t_init_phy, + .fini = sfn4111t_fini, + .set_id_led = tenxpress_set_id_led, + .monitor = sfn4111t_check_hw, + }, + { + .id = FALCON_BOARD_SFN4112F, + .ref_model = "SFN4112F", + .gen_type = "SFP+ adapter", + .init = sfn4112f_init, + .init_phy = sfn4112f_init_phy, + .fini = efx_fini_lm87, + .set_id_led = sfn4112f_set_id_led, + .monitor = sfn4112f_check_hw, + }, }; -static struct falcon_board falcon_dummy_board = { +static const struct falcon_board_type falcon_dummy_board = { .init = efx_port_dummy_op_int, .init_phy = efx_port_dummy_op_void, + .fini = efx_port_dummy_op_void, .set_id_led = efx_port_dummy_op_set_id_led, .monitor = efx_port_dummy_op_int, - .fini = efx_port_dummy_op_void, }; void falcon_probe_board(struct efx_nic *efx, u16 revision_info) { struct falcon_board *board = falcon_board(efx); - struct falcon_board_data *data = NULL; + u8 type_id = FALCON_BOARD_TYPE(revision_info); int i; - *board = falcon_dummy_board; - board->type = FALCON_BOARD_TYPE(revision_info); board->major = FALCON_BOARD_MAJOR(revision_info); board->minor = FALCON_BOARD_MINOR(revision_info); - for (i = 0; i < ARRAY_SIZE(board_data); i++) - if (board_data[i].type == board->type) - data = &board_data[i]; + for (i = 0; i < ARRAY_SIZE(board_types); i++) + if (board_types[i].id == type_id) + board->type = &board_types[i]; - if (data) { + if (board->type) { EFX_INFO(efx, "board is %s rev %c%d\n", (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC) - ? data->ref_model : data->gen_type, + ? board->type->ref_model : board->type->gen_type, 'A' + board->major, board->minor); - board->init = data->init; } else { - EFX_ERR(efx, "unknown board type %d\n", board->type); + EFX_ERR(efx, "unknown board type %d\n", type_id); + board->type = &falcon_dummy_board; } } diff --git a/drivers/net/sfc/falcon_gmac.c b/drivers/net/sfc/falcon_gmac.c index 967f3fb..7dadfcb 100644 --- a/drivers/net/sfc/falcon_gmac.c +++ b/drivers/net/sfc/falcon_gmac.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 @@ -11,7 +11,7 @@ #include <linux/delay.h> #include "net_driver.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #include "mac.h" #include "regs.h" #include "io.h" @@ -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; bool loopback, tx_fc, rx_fc, bytemode; @@ -123,6 +123,8 @@ static void falcon_reconfigure_gmac(struct efx_nic *efx) udelay(10); falcon_reconfigure_mac_wrapper(efx); + + return 0; } static void falcon_update_stats_gmac(struct efx_nic *efx) @@ -130,11 +132,6 @@ static void falcon_update_stats_gmac(struct efx_nic *efx) struct efx_mac_stats *mac_stats = &efx->mac_stats; unsigned long old_rx_pause, old_tx_pause; unsigned long new_rx_pause, new_tx_pause; - int rc; - - rc = falcon_dma_stats(efx, GDmaDone_offset); - if (rc) - return; /* Pause frames are erroneously counted as errors (SFC bug 3269) */ old_rx_pause = mac_stats->rx_pause; @@ -221,9 +218,13 @@ static void falcon_update_stats_gmac(struct efx_nic *efx) mac_stats->rx_lt64 = mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64; } +static bool falcon_gmac_check_fault(struct efx_nic *efx) +{ + return false; +} + struct efx_mac_operations falcon_gmac_operations = { .reconfigure = falcon_reconfigure_gmac, .update_stats = falcon_update_stats_gmac, - .irq = efx_port_dummy_op_void, - .poll = efx_port_dummy_op_void, + .check_fault = falcon_gmac_check_fault, }; diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c index 69cb55f..3da933f 100644 --- a/drivers/net/sfc/falcon_xmac.c +++ b/drivers/net/sfc/falcon_xmac.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 @@ -11,7 +11,7 @@ #include <linux/delay.h> #include "net_driver.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #include "regs.h" #include "io.h" #include "mac.h" @@ -60,11 +60,15 @@ static void falcon_setup_xaui(struct efx_nic *efx) int falcon_reset_xaui(struct efx_nic *efx) { + struct falcon_nic_data *nic_data = efx->nic_data; efx_oword_t reg; int count; + /* Don't fetch MAC statistics over an XMAC reset */ + WARN_ON(nic_data->stats_disable_count == 0); + /* Start reset sequence */ - EFX_POPULATE_DWORD_1(reg, FRF_AB_XX_RST_XX_EN, 1); + EFX_POPULATE_OWORD_1(reg, FRF_AB_XX_RST_XX_EN, 1); efx_writeo(efx, ®, FR_AB_XX_PWR_RST); /* Wait up to 10 ms for completion, then reinitialise */ @@ -85,7 +89,7 @@ static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) { efx_oword_t reg; - if ((falcon_rev(efx) != FALCON_REV_B0) || LOOPBACK_INTERNAL(efx)) + if ((efx_nic_rev(efx) != EFX_REV_FALCON_B0) || LOOPBACK_INTERNAL(efx)) return; /* We expect xgmii faults if the wireside link is up */ @@ -94,7 +98,7 @@ static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) /* We can only use this interrupt to signal the negative edge of * xaui_align [we have to poll the positive edge]. */ - if (!efx->mac_up) + if (efx->xmac_poll_required) return; /* Flush the ISR */ @@ -108,7 +112,7 @@ static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) } /* 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; bool align_done, link_ok = false; @@ -133,37 +137,38 @@ bool falcon_xaui_link_ok(struct efx_nic *efx) /* If the link is up, then check the phy side of the xaui link */ if (efx->link_state.up && link_ok) - if (efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS)) + if (efx->mdio.mmds & (1 << MDIO_MMD_PHYXS)) link_ok = efx_mdio_phyxgxs_lane_sync(efx); 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; efx_oword_t reg; bool rx_fc = !!(efx->link_state.fc & EFX_FC_RX); + bool tx_fc = !!(efx->link_state.fc & EFX_FC_TX); /* Configure MAC - cut-thru mode is hard wired on */ - EFX_POPULATE_DWORD_3(reg, + EFX_POPULATE_OWORD_3(reg, FRF_AB_XM_RX_JUMBO_MODE, 1, FRF_AB_XM_TX_STAT_EN, 1, FRF_AB_XM_RX_STAT_EN, 1); efx_writeo(efx, ®, FR_AB_XM_GLB_CFG); /* Configure TX */ - EFX_POPULATE_DWORD_6(reg, + EFX_POPULATE_OWORD_6(reg, FRF_AB_XM_TXEN, 1, FRF_AB_XM_TX_PRMBL, 1, FRF_AB_XM_AUTO_PAD, 1, FRF_AB_XM_TXCRC, 1, - FRF_AB_XM_FCNTL, 1, + FRF_AB_XM_FCNTL, tx_fc, FRF_AB_XM_IPG, 0x3); efx_writeo(efx, ®, FR_AB_XM_TX_CFG); /* Configure RX */ - EFX_POPULATE_DWORD_5(reg, + EFX_POPULATE_OWORD_5(reg, FRF_AB_XM_RXEN, 1, FRF_AB_XM_AUTO_DEPAD, 0, FRF_AB_XM_ACPT_ALL_MCAST, 1, @@ -173,14 +178,14 @@ static void falcon_reconfigure_xmac_core(struct efx_nic *efx) /* Set frame length */ max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu); - EFX_POPULATE_DWORD_1(reg, FRF_AB_XM_MAX_RX_FRM_SIZE, max_frame_len); + EFX_POPULATE_OWORD_1(reg, FRF_AB_XM_MAX_RX_FRM_SIZE, max_frame_len); efx_writeo(efx, ®, FR_AB_XM_RX_PARAM); - EFX_POPULATE_DWORD_2(reg, + EFX_POPULATE_OWORD_2(reg, FRF_AB_XM_MAX_TX_FRM_SIZE, max_frame_len, FRF_AB_XM_TX_JUMBO_MODE, 1); efx_writeo(efx, ®, FR_AB_XM_TX_PARAM); - EFX_POPULATE_DWORD_2(reg, + EFX_POPULATE_OWORD_2(reg, FRF_AB_XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */ FRF_AB_XM_DIS_FCNTL, !rx_fc); efx_writeo(efx, ®, FR_AB_XM_FC); @@ -239,28 +244,38 @@ static void falcon_reconfigure_xgxs_core(struct efx_nic *efx) } -/* Try and bring the Falcon side of the Falcon-Phy XAUI link fails - * to come back up. Bash it until it comes back up */ -static void falcon_check_xaui_link_up(struct efx_nic *efx, int tries) +/* Try to bring up the Falcon side of the Falcon-Phy XAUI link */ +static bool falcon_check_xaui_link_up(struct efx_nic *efx, int tries) { - efx->mac_up = falcon_xaui_link_ok(efx); + bool mac_up = falcon_xaui_link_ok(efx); - if ((efx->loopback_mode == LOOPBACK_NETWORK) || + if (LOOPBACK_MASK(efx) & LOOPBACKS_EXTERNAL(efx) & LOOPBACKS_WS || efx_phy_mode_disabled(efx->phy_mode)) /* XAUI link is expected to be down */ - return; + return mac_up; - while (!efx->mac_up && tries) { + falcon_stop_nic_stats(efx); + + while (!mac_up && tries) { EFX_LOG(efx, "bashing xaui\n"); falcon_reset_xaui(efx); udelay(200); - efx->mac_up = falcon_xaui_link_ok(efx); + mac_up = falcon_xaui_link_ok(efx); --tries; } + + falcon_start_nic_stats(efx); + + return mac_up; +} + +static bool falcon_xmac_check_fault(struct efx_nic *efx) +{ + 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); @@ -269,18 +284,15 @@ static void falcon_reconfigure_xmac(struct efx_nic *efx) falcon_reconfigure_mac_wrapper(efx); - falcon_check_xaui_link_up(efx, 5); + efx->xmac_poll_required = !falcon_check_xaui_link_up(efx, 5); falcon_mask_status_intr(efx, true); + + return 0; } static void falcon_update_stats_xmac(struct efx_nic *efx) { struct efx_mac_stats *mac_stats = &efx->mac_stats; - int rc; - - rc = falcon_dma_stats(efx, XgDmaDone_offset); - if (rc) - return; /* Update MAC stats from DMAed values */ FALCON_STAT(efx, XgRxOctets, rx_bytes); @@ -338,35 +350,19 @@ static void falcon_update_stats_xmac(struct efx_nic *efx) mac_stats->rx_control * 64); } -static void falcon_xmac_irq(struct efx_nic *efx) -{ - /* The XGMII link has a transient fault, which indicates either: - * - there's a transient xgmii fault - * - falcon's end of the xaui link may need a kick - * - the wire-side link may have gone down, but the lasi/poll() - * hasn't noticed yet. - * - * We only want to even bother polling XAUI if we're confident it's - * not (1) or (3). In both cases, the only reliable way to spot this - * is to wait a bit. We do this here by forcing the mac link state - * to down, and waiting for the mac poll to come round and check - */ - efx->mac_up = false; -} - -static void falcon_poll_xmac(struct efx_nic *efx) +void falcon_poll_xmac(struct efx_nic *efx) { - if (!EFX_WORKAROUND_5147(efx) || !efx->link_state.up || efx->mac_up) + if (!EFX_WORKAROUND_5147(efx) || !efx->link_state.up || + !efx->xmac_poll_required) return; falcon_mask_status_intr(efx, false); - falcon_check_xaui_link_up(efx, 1); + efx->xmac_poll_required = !falcon_check_xaui_link_up(efx, 1); falcon_mask_status_intr(efx, true); } struct efx_mac_operations falcon_xmac_operations = { .reconfigure = falcon_reconfigure_xmac, .update_stats = falcon_update_stats_xmac, - .irq = falcon_xmac_irq, - .poll = falcon_poll_xmac, + .check_fault = falcon_xmac_check_fault, }; diff --git a/drivers/net/sfc/mac.h b/drivers/net/sfc/mac.h index 4e70742..f1aa5f3 100644 --- a/drivers/net/sfc/mac.h +++ b/drivers/net/sfc/mac.h @@ -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 @@ -15,5 +15,9 @@ extern struct efx_mac_operations falcon_gmac_operations; extern struct efx_mac_operations falcon_xmac_operations; +extern struct efx_mac_operations efx_mcdi_mac_operations; +extern void falcon_reconfigure_xmac_core(struct efx_nic *efx); +extern int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr, + u32 dma_len, int enable, int clear); #endif diff --git a/drivers/net/sfc/mcdi.c b/drivers/net/sfc/mcdi.c new file mode 100644 index 0000000..683353b --- /dev/null +++ b/drivers/net/sfc/mcdi.c @@ -0,0 +1,1112 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2008-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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include <linux/delay.h> +#include "net_driver.h" +#include "nic.h" +#include "io.h" +#include "regs.h" +#include "mcdi_pcol.h" +#include "phy.h" + +/************************************************************************** + * + * Management-Controller-to-Driver Interface + * + ************************************************************************** + */ + +/* Software-defined structure to the shared-memory */ +#define CMD_NOTIFY_PORT0 0 +#define CMD_NOTIFY_PORT1 4 +#define CMD_PDU_PORT0 0x008 +#define CMD_PDU_PORT1 0x108 +#define REBOOT_FLAG_PORT0 0x3f8 +#define REBOOT_FLAG_PORT1 0x3fc + +#define MCDI_RPC_TIMEOUT 10 /*seconds */ + +#define MCDI_PDU(efx) \ + (efx_port_num(efx) ? CMD_PDU_PORT1 : CMD_PDU_PORT0) +#define MCDI_DOORBELL(efx) \ + (efx_port_num(efx) ? CMD_NOTIFY_PORT1 : CMD_NOTIFY_PORT0) +#define MCDI_REBOOT_FLAG(efx) \ + (efx_port_num(efx) ? REBOOT_FLAG_PORT1 : REBOOT_FLAG_PORT0) + +#define SEQ_MASK \ + EFX_MASK32(EFX_WIDTH(MCDI_HEADER_SEQ)) + +static inline struct efx_mcdi_iface *efx_mcdi(struct efx_nic *efx) +{ + struct siena_nic_data *nic_data; + EFX_BUG_ON_PARANOID(efx_nic_rev(efx) < EFX_REV_SIENA_A0); + nic_data = efx->nic_data; + return &nic_data->mcdi; +} + +void efx_mcdi_init(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi; + + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return; + + mcdi = efx_mcdi(efx); + init_waitqueue_head(&mcdi->wq); + spin_lock_init(&mcdi->iface_lock); + atomic_set(&mcdi->state, MCDI_STATE_QUIESCENT); + mcdi->mode = MCDI_MODE_POLL; + + (void) efx_mcdi_poll_reboot(efx); +} + +static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd, + const u8 *inbuf, size_t inlen) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); + unsigned doorbell = FR_CZ_MC_TREG_SMEM + MCDI_DOORBELL(efx); + unsigned int i; + efx_dword_t hdr; + u32 xflags, seqno; + + BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT); + BUG_ON(inlen & 3 || inlen >= 0x100); + + seqno = mcdi->seqno & SEQ_MASK; + xflags = 0; + if (mcdi->mode == MCDI_MODE_EVENTS) + xflags |= MCDI_HEADER_XFLAGS_EVREQ; + + EFX_POPULATE_DWORD_6(hdr, + MCDI_HEADER_RESPONSE, 0, + MCDI_HEADER_RESYNC, 1, + MCDI_HEADER_CODE, cmd, + MCDI_HEADER_DATALEN, inlen, + MCDI_HEADER_SEQ, seqno, + MCDI_HEADER_XFLAGS, xflags); + + efx_writed(efx, &hdr, pdu); + + for (i = 0; i < inlen; i += 4) + _efx_writed(efx, *((__le32 *)(inbuf + i)), pdu + 4 + i); + + /* Ensure the payload is written out before the header */ + wmb(); + + /* ring the doorbell with a distinctive value */ + _efx_writed(efx, (__force __le32) 0x45789abc, doorbell); +} + +static void efx_mcdi_copyout(struct efx_nic *efx, u8 *outbuf, size_t outlen) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); + int i; + + BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT); + BUG_ON(outlen & 3 || outlen >= 0x100); + + for (i = 0; i < outlen; i += 4) + *((__le32 *)(outbuf + i)) = _efx_readd(efx, pdu + 4 + i); +} + +static int efx_mcdi_poll(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + unsigned int time, finish; + unsigned int respseq, respcmd, error; + unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); + unsigned int rc, spins; + efx_dword_t reg; + + /* Check for a reboot atomically with respect to efx_mcdi_copyout() */ + rc = efx_mcdi_poll_reboot(efx); + if (rc) + goto out; + + /* Poll for completion. Poll quickly (once a us) for the 1st jiffy, + * because generally mcdi responses are fast. After that, back off + * and poll once a jiffy (approximately) + */ + spins = TICK_USEC; + finish = get_seconds() + MCDI_RPC_TIMEOUT; + + while (1) { + if (spins != 0) { + --spins; + udelay(1); + } else + schedule(); + + time = get_seconds(); + + rmb(); + efx_readd(efx, ®, pdu); + + /* All 1's indicates that shared memory is in reset (and is + * not a valid header). Wait for it to come out reset before + * completing the command */ + if (EFX_DWORD_FIELD(reg, EFX_DWORD_0) != 0xffffffff && + EFX_DWORD_FIELD(reg, MCDI_HEADER_RESPONSE)) + break; + + if (time >= finish) + return -ETIMEDOUT; + } + + mcdi->resplen = EFX_DWORD_FIELD(reg, MCDI_HEADER_DATALEN); + respseq = EFX_DWORD_FIELD(reg, MCDI_HEADER_SEQ); + respcmd = EFX_DWORD_FIELD(reg, MCDI_HEADER_CODE); + error = EFX_DWORD_FIELD(reg, MCDI_HEADER_ERROR); + + if (error && mcdi->resplen == 0) { + EFX_ERR(efx, "MC rebooted\n"); + rc = EIO; + } else if ((respseq ^ mcdi->seqno) & SEQ_MASK) { + EFX_ERR(efx, "MC response mismatch tx seq 0x%x rx seq 0x%x\n", + respseq, mcdi->seqno); + rc = EIO; + } else if (error) { + efx_readd(efx, ®, pdu + 4); + switch (EFX_DWORD_FIELD(reg, EFX_DWORD_0)) { +#define TRANSLATE_ERROR(name) \ + case MC_CMD_ERR_ ## name: \ + rc = name; \ + break + TRANSLATE_ERROR(ENOENT); + TRANSLATE_ERROR(EINTR); + TRANSLATE_ERROR(EACCES); + TRANSLATE_ERROR(EBUSY); + TRANSLATE_ERROR(EINVAL); + TRANSLATE_ERROR(EDEADLK); + TRANSLATE_ERROR(ENOSYS); + TRANSLATE_ERROR(ETIME); +#undef TRANSLATE_ERROR + default: + rc = EIO; + break; + } + } else + rc = 0; + +out: + mcdi->resprc = rc; + if (rc) + mcdi->resplen = 0; + + /* Return rc=0 like wait_event_timeout() */ + return 0; +} + +/* Test and clear MC-rebooted flag for this port/function */ +int efx_mcdi_poll_reboot(struct efx_nic *efx) +{ + unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_REBOOT_FLAG(efx); + efx_dword_t reg; + uint32_t value; + + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return false; + + efx_readd(efx, ®, addr); + value = EFX_DWORD_FIELD(reg, EFX_DWORD_0); + + if (value == 0) + return 0; + + EFX_ZERO_DWORD(reg); + efx_writed(efx, ®, addr); + + if (value == MC_STATUS_DWORD_ASSERT) + return -EINTR; + else + return -EIO; +} + +static void efx_mcdi_acquire(struct efx_mcdi_iface *mcdi) +{ + /* Wait until the interface becomes QUIESCENT and we win the race + * to mark it RUNNING. */ + wait_event(mcdi->wq, + atomic_cmpxchg(&mcdi->state, + MCDI_STATE_QUIESCENT, + MCDI_STATE_RUNNING) + == MCDI_STATE_QUIESCENT); +} + +static int efx_mcdi_await_completion(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + + if (wait_event_timeout( + mcdi->wq, + atomic_read(&mcdi->state) == MCDI_STATE_COMPLETED, + msecs_to_jiffies(MCDI_RPC_TIMEOUT * 1000)) == 0) + return -ETIMEDOUT; + + /* Check if efx_mcdi_set_mode() switched us back to polled completions. + * In which case, poll for completions directly. If efx_mcdi_ev_cpl() + * completed the request first, then we'll just end up completing the + * request again, which is safe. + * + * We need an smp_rmb() to synchronise with efx_mcdi_mode_poll(), which + * wait_event_timeout() implicitly provides. + */ + if (mcdi->mode == MCDI_MODE_POLL) + return efx_mcdi_poll(efx); + + return 0; +} + +static bool efx_mcdi_complete(struct efx_mcdi_iface *mcdi) +{ + /* If the interface is RUNNING, then move to COMPLETED and wake any + * waiters. If the interface isn't in RUNNING then we've received a + * duplicate completion after we've already transitioned back to + * QUIESCENT. [A subsequent invocation would increment seqno, so would + * have failed the seqno check]. + */ + if (atomic_cmpxchg(&mcdi->state, + MCDI_STATE_RUNNING, + MCDI_STATE_COMPLETED) == MCDI_STATE_RUNNING) { + wake_up(&mcdi->wq); + return true; + } + + return false; +} + +static void efx_mcdi_release(struct efx_mcdi_iface *mcdi) +{ + atomic_set(&mcdi->state, MCDI_STATE_QUIESCENT); + wake_up(&mcdi->wq); +} + +static void efx_mcdi_ev_cpl(struct efx_nic *efx, unsigned int seqno, + unsigned int datalen, unsigned int errno) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + bool wake = false; + + spin_lock(&mcdi->iface_lock); + + if ((seqno ^ mcdi->seqno) & SEQ_MASK) { + if (mcdi->credits) + /* The request has been cancelled */ + --mcdi->credits; + else + EFX_ERR(efx, "MC response mismatch tx seq 0x%x rx " + "seq 0x%x\n", seqno, mcdi->seqno); + } else { + mcdi->resprc = errno; + mcdi->resplen = datalen; + + wake = true; + } + + spin_unlock(&mcdi->iface_lock); + + if (wake) + efx_mcdi_complete(mcdi); +} + +/* Issue the given command by writing the data into the shared memory PDU, + * ring the doorbell and wait for completion. Copyout the result. */ +int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, + const u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen, + size_t *outlen_actual) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + int rc; + BUG_ON(efx_nic_rev(efx) < EFX_REV_SIENA_A0); + + efx_mcdi_acquire(mcdi); + + /* Serialise with efx_mcdi_ev_cpl() and efx_mcdi_ev_death() */ + spin_lock_bh(&mcdi->iface_lock); + ++mcdi->seqno; + spin_unlock_bh(&mcdi->iface_lock); + + efx_mcdi_copyin(efx, cmd, inbuf, inlen); + + if (mcdi->mode == MCDI_MODE_POLL) + rc = efx_mcdi_poll(efx); + else + rc = efx_mcdi_await_completion(efx); + + if (rc != 0) { + /* Close the race with efx_mcdi_ev_cpl() executing just too late + * and completing a request we've just cancelled, by ensuring + * that the seqno check therein fails. + */ + spin_lock_bh(&mcdi->iface_lock); + ++mcdi->seqno; + ++mcdi->credits; + spin_unlock_bh(&mcdi->iface_lock); + + EFX_ERR(efx, "MC command 0x%x inlen %d mode %d timed out\n", + cmd, (int)inlen, mcdi->mode); + } else { + size_t resplen; + + /* At the very least we need a memory barrier here to ensure + * we pick up changes from efx_mcdi_ev_cpl(). Protect against + * a spurious efx_mcdi_ev_cpl() running concurrently by + * acquiring the iface_lock. */ + spin_lock_bh(&mcdi->iface_lock); + rc = -mcdi->resprc; + resplen = mcdi->resplen; + spin_unlock_bh(&mcdi->iface_lock); + + if (rc == 0) { + efx_mcdi_copyout(efx, outbuf, + min(outlen, mcdi->resplen + 3) & ~0x3); + if (outlen_actual != NULL) + *outlen_actual = resplen; + } else if (cmd == MC_CMD_REBOOT && rc == -EIO) + ; /* Don't reset if MC_CMD_REBOOT returns EIO */ + else if (rc == -EIO || rc == -EINTR) { + EFX_ERR(efx, "MC fatal error %d\n", -rc); + efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE); + } else + EFX_ERR(efx, "MC command 0x%x inlen %d failed rc=%d\n", + cmd, (int)inlen, -rc); + } + + efx_mcdi_release(mcdi); + return rc; +} + +void efx_mcdi_mode_poll(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi; + + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return; + + mcdi = efx_mcdi(efx); + if (mcdi->mode == MCDI_MODE_POLL) + return; + + /* We can switch from event completion to polled completion, because + * mcdi requests are always completed in shared memory. We do this by + * switching the mode to POLL'd then completing the request. + * efx_mcdi_await_completion() will then call efx_mcdi_poll(). + * + * We need an smp_wmb() to synchronise with efx_mcdi_await_completion(), + * which efx_mcdi_complete() provides for us. + */ + mcdi->mode = MCDI_MODE_POLL; + + efx_mcdi_complete(mcdi); +} + +void efx_mcdi_mode_event(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi; + + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return; + + mcdi = efx_mcdi(efx); + + if (mcdi->mode == MCDI_MODE_EVENTS) + return; + + /* We can't switch from polled to event completion in the middle of a + * request, because the completion method is specified in the request. + * So acquire the interface to serialise the requestors. We don't need + * to acquire the iface_lock to change the mode here, but we do need a + * write memory barrier ensure that efx_mcdi_rpc() sees it, which + * efx_mcdi_acquire() provides. + */ + efx_mcdi_acquire(mcdi); + mcdi->mode = MCDI_MODE_EVENTS; + efx_mcdi_release(mcdi); +} + +static void efx_mcdi_ev_death(struct efx_nic *efx, int rc) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + + /* If there is an outstanding MCDI request, it has been terminated + * either by a BADASSERT or REBOOT event. If the mcdi interface is + * in polled mode, then do nothing because the MC reboot handler will + * set the header correctly. However, if the mcdi interface is waiting + * for a CMDDONE event it won't receive it [and since all MCDI events + * are sent to the same queue, we can't be racing with + * efx_mcdi_ev_cpl()] + * + * There's a race here with efx_mcdi_rpc(), because we might receive + * a REBOOT event *before* the request has been copied out. In polled + * mode (during startup) this is irrelevent, because efx_mcdi_complete() + * is ignored. In event mode, this condition is just an edge-case of + * receiving a REBOOT event after posting the MCDI request. Did the mc + * reboot before or after the copyout? The best we can do always is + * just return failure. + */ + spin_lock(&mcdi->iface_lock); + if (efx_mcdi_complete(mcdi)) { + if (mcdi->mode == MCDI_MODE_EVENTS) { + mcdi->resprc = rc; + mcdi->resplen = 0; + } + } else + /* Nobody was waiting for an MCDI request, so trigger a reset */ + efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE); + + spin_unlock(&mcdi->iface_lock); +} + +static unsigned int efx_mcdi_event_link_speed[] = { + [MCDI_EVENT_LINKCHANGE_SPEED_100M] = 100, + [MCDI_EVENT_LINKCHANGE_SPEED_1G] = 1000, + [MCDI_EVENT_LINKCHANGE_SPEED_10G] = 10000, +}; + + +static void efx_mcdi_process_link_change(struct efx_nic *efx, efx_qword_t *ev) +{ + u32 flags, fcntl, speed, lpa; + + speed = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_SPEED); + EFX_BUG_ON_PARANOID(speed >= ARRAY_SIZE(efx_mcdi_event_link_speed)); + speed = efx_mcdi_event_link_speed[speed]; + + flags = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LINK_FLAGS); + fcntl = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_FCNTL); + lpa = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LP_CAP); + + /* efx->link_state is only modified by efx_mcdi_phy_get_link(), + * which is only run after flushing the event queues. Therefore, it + * is safe to modify the link state outside of the mac_lock here. + */ + efx_mcdi_phy_decode_link(efx, &efx->link_state, speed, flags, fcntl); + + efx_mcdi_phy_check_fcntl(efx, lpa); + + efx_link_status_changed(efx); +} + +static const char *sensor_names[] = { + [MC_CMD_SENSOR_CONTROLLER_TEMP] = "Controller temp. sensor", + [MC_CMD_SENSOR_PHY_COMMON_TEMP] = "PHY shared temp. sensor", + [MC_CMD_SENSOR_CONTROLLER_COOLING] = "Controller cooling", + [MC_CMD_SENSOR_PHY0_TEMP] = "PHY 0 temp. sensor", + [MC_CMD_SENSOR_PHY0_COOLING] = "PHY 0 cooling", + [MC_CMD_SENSOR_PHY1_TEMP] = "PHY 1 temp. sensor", + [MC_CMD_SENSOR_PHY1_COOLING] = "PHY 1 cooling", + [MC_CMD_SENSOR_IN_1V0] = "1.0V supply sensor", + [MC_CMD_SENSOR_IN_1V2] = "1.2V supply sensor", + [MC_CMD_SENSOR_IN_1V8] = "1.8V supply sensor", + [MC_CMD_SENSOR_IN_2V5] = "2.5V supply sensor", + [MC_CMD_SENSOR_IN_3V3] = "3.3V supply sensor", + [MC_CMD_SENSOR_IN_12V0] = "12V supply sensor" +}; + +static const char *sensor_status_names[] = { + [MC_CMD_SENSOR_STATE_OK] = "OK", + [MC_CMD_SENSOR_STATE_WARNING] = "Warning", + [MC_CMD_SENSOR_STATE_FATAL] = "Fatal", + [MC_CMD_SENSOR_STATE_BROKEN] = "Device failure", +}; + +static void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev) +{ + unsigned int monitor, state, value; + const char *name, *state_txt; + monitor = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_MONITOR); + state = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_STATE); + value = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_VALUE); + /* Deal gracefully with the board having more drivers than we + * know about, but do not expect new sensor states. */ + name = (monitor >= ARRAY_SIZE(sensor_names)) + ? "No sensor name available" : + sensor_names[monitor]; + EFX_BUG_ON_PARANOID(state >= ARRAY_SIZE(sensor_status_names)); + state_txt = sensor_status_names[state]; + + EFX_ERR(efx, "Sensor %d (%s) reports condition '%s' for raw value %d\n", + monitor, name, state_txt, value); +} + +/* Called from falcon_process_eventq for MCDI events */ +void efx_mcdi_process_event(struct efx_channel *channel, + efx_qword_t *event) +{ + struct efx_nic *efx = channel->efx; + int code = EFX_QWORD_FIELD(*event, MCDI_EVENT_CODE); + u32 data = EFX_QWORD_FIELD(*event, MCDI_EVENT_DATA); + + switch (code) { + case MCDI_EVENT_CODE_BADSSERT: + EFX_ERR(efx, "MC watchdog or assertion failure at 0x%x\n", data); + efx_mcdi_ev_death(efx, EINTR); + break; + + case MCDI_EVENT_CODE_PMNOTICE: + EFX_INFO(efx, "MCDI PM event.\n"); + break; + + case MCDI_EVENT_CODE_CMDDONE: + efx_mcdi_ev_cpl(efx, + MCDI_EVENT_FIELD(*event, CMDDONE_SEQ), + MCDI_EVENT_FIELD(*event, CMDDONE_DATALEN), + MCDI_EVENT_FIELD(*event, CMDDONE_ERRNO)); + break; + + case MCDI_EVENT_CODE_LINKCHANGE: + efx_mcdi_process_link_change(efx, event); + break; + case MCDI_EVENT_CODE_SENSOREVT: + efx_mcdi_sensor_event(efx, event); + break; + case MCDI_EVENT_CODE_SCHEDERR: + EFX_INFO(efx, "MC Scheduler error address=0x%x\n", data); + break; + case MCDI_EVENT_CODE_REBOOT: + EFX_INFO(efx, "MC Reboot\n"); + efx_mcdi_ev_death(efx, EIO); + break; + case MCDI_EVENT_CODE_MAC_STATS_DMA: + /* MAC stats are gather lazily. We can ignore this. */ + break; + + default: + EFX_ERR(efx, "Unknown MCDI event 0x%x\n", code); + } +} + +/************************************************************************** + * + * Specific request functions + * + ************************************************************************** + */ + +int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build) +{ + u8 outbuf[ALIGN(MC_CMD_GET_VERSION_V1_OUT_LEN, 4)]; + size_t outlength; + const __le16 *ver_words; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_VERSION_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_VERSION, NULL, 0, + outbuf, sizeof(outbuf), &outlength); + if (rc) + goto fail; + + if (outlength == MC_CMD_GET_VERSION_V0_OUT_LEN) { + *version = 0; + *build = MCDI_DWORD(outbuf, GET_VERSION_OUT_FIRMWARE); + return 0; + } + + if (outlength < MC_CMD_GET_VERSION_V1_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + ver_words = (__le16 *)MCDI_PTR(outbuf, GET_VERSION_OUT_VERSION); + *version = (((u64)le16_to_cpu(ver_words[0]) << 48) | + ((u64)le16_to_cpu(ver_words[1]) << 32) | + ((u64)le16_to_cpu(ver_words[2]) << 16) | + le16_to_cpu(ver_words[3])); + *build = MCDI_DWORD(outbuf, GET_VERSION_OUT_FIRMWARE); + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, + bool *was_attached) +{ + u8 inbuf[MC_CMD_DRV_ATTACH_IN_LEN]; + u8 outbuf[MC_CMD_DRV_ATTACH_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_NEW_STATE, + driver_operating ? 1 : 0); + MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_UPDATE, 1); + + rc = efx_mcdi_rpc(efx, MC_CMD_DRV_ATTACH, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + if (outlen < MC_CMD_DRV_ATTACH_OUT_LEN) + goto fail; + + if (was_attached != NULL) + *was_attached = MCDI_DWORD(outbuf, DRV_ATTACH_OUT_OLD_STATE); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address, + u16 *fw_subtype_list) +{ + uint8_t outbuf[MC_CMD_GET_BOARD_CFG_OUT_LEN]; + size_t outlen; + int port_num = efx_port_num(efx); + int offset; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_BOARD_CFG, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_GET_BOARD_CFG_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + offset = (port_num) + ? MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_OFST + : MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_OFST; + if (mac_address) + memcpy(mac_address, outbuf + offset, ETH_ALEN); + if (fw_subtype_list) + memcpy(fw_subtype_list, + outbuf + MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST, + MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN); + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d len=%d\n", __func__, rc, (int)outlen); + + return rc; +} + +int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, u32 dest_evq) +{ + u8 inbuf[MC_CMD_LOG_CTRL_IN_LEN]; + u32 dest = 0; + int rc; + + if (uart) + dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_UART; + if (evq) + dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ; + + MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST, dest); + MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST_EVQ, dest_evq); + + BUILD_BUG_ON(MC_CMD_LOG_CTRL_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_LOG_CTRL, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out) +{ + u8 outbuf[MC_CMD_NVRAM_TYPES_OUT_LEN]; + size_t outlen; + int rc; + + BUILD_BUG_ON(MC_CMD_NVRAM_TYPES_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_TYPES, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + if (outlen < MC_CMD_NVRAM_TYPES_OUT_LEN) + goto fail; + + *nvram_types_out = MCDI_DWORD(outbuf, NVRAM_TYPES_OUT_TYPES); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", + __func__, rc); + return rc; +} + +int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type, + size_t *size_out, size_t *erase_size_out, + bool *protected_out) +{ + u8 inbuf[MC_CMD_NVRAM_INFO_IN_LEN]; + u8 outbuf[MC_CMD_NVRAM_INFO_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_INFO_IN_TYPE, type); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_INFO, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + if (outlen < MC_CMD_NVRAM_INFO_OUT_LEN) + goto fail; + + *size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_SIZE); + *erase_size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_ERASESIZE); + *protected_out = !!(MCDI_DWORD(outbuf, NVRAM_INFO_OUT_FLAGS) & + (1 << MC_CMD_NVRAM_PROTECTED_LBN)); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type) +{ + u8 inbuf[MC_CMD_NVRAM_UPDATE_START_IN_LEN]; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_START_IN_TYPE, type); + + BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_START_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_START, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type, + loff_t offset, u8 *buffer, size_t length) +{ + u8 inbuf[MC_CMD_NVRAM_READ_IN_LEN]; + u8 outbuf[MC_CMD_NVRAM_READ_OUT_LEN(length)]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_TYPE, type); + MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_OFFSET, offset); + MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_LENGTH, length); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_READ, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + memcpy(buffer, MCDI_PTR(outbuf, NVRAM_READ_OUT_READ_BUFFER), length); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type, + loff_t offset, const u8 *buffer, size_t length) +{ + u8 inbuf[MC_CMD_NVRAM_WRITE_IN_LEN(length)]; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type); + MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset); + MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length); + memcpy(MCDI_PTR(inbuf, NVRAM_WRITE_IN_WRITE_BUFFER), buffer, length); + + BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type, + loff_t offset, size_t length) +{ + u8 inbuf[MC_CMD_NVRAM_ERASE_IN_LEN]; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_TYPE, type); + MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_OFFSET, offset); + MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_LENGTH, length); + + BUILD_BUG_ON(MC_CMD_NVRAM_ERASE_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_ERASE, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type) +{ + u8 inbuf[MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN]; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type); + + BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_handle_assertion(struct efx_nic *efx) +{ + union { + u8 asserts[MC_CMD_GET_ASSERTS_IN_LEN]; + u8 reboot[MC_CMD_REBOOT_IN_LEN]; + } inbuf; + u8 assertion[MC_CMD_GET_ASSERTS_OUT_LEN]; + unsigned int flags, index, ofst; + const char *reason; + size_t outlen; + int retry; + int rc; + + /* Check if the MC is in the assertion handler, retrying twice. Once + * because a boot-time assertion might cause this command to fail + * with EINTR. And once again because GET_ASSERTS can race with + * MC_CMD_REBOOT running on the other port. */ + retry = 2; + do { + MCDI_SET_DWORD(inbuf.asserts, GET_ASSERTS_IN_CLEAR, 0); + rc = efx_mcdi_rpc(efx, MC_CMD_GET_ASSERTS, + inbuf.asserts, MC_CMD_GET_ASSERTS_IN_LEN, + assertion, sizeof(assertion), &outlen); + } while ((rc == -EINTR || rc == -EIO) && retry-- > 0); + + if (rc) + return rc; + if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN) + return -EINVAL; + + flags = MCDI_DWORD(assertion, GET_ASSERTS_OUT_GLOBAL_FLAGS); + if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS) + return 0; + + /* Reset the hardware atomically such that only one port with succeed. + * This command will succeed if a reboot is no longer required (because + * the other port did it first), but fail with EIO if it succeeds. + */ + BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0); + MCDI_SET_DWORD(inbuf.reboot, REBOOT_IN_FLAGS, + MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION); + efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf.reboot, MC_CMD_REBOOT_IN_LEN, + NULL, 0, NULL); + + /* Print out the assertion */ + reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL) + ? "system-level assertion" + : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL) + ? "thread-level assertion" + : (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED) + ? "watchdog reset" + : "unknown assertion"; + EFX_ERR(efx, "MCPU %s at PC = 0x%.8x in thread 0x%.8x\n", reason, + MCDI_DWORD(assertion, GET_ASSERTS_OUT_SAVED_PC_OFFS), + MCDI_DWORD(assertion, GET_ASSERTS_OUT_THREAD_OFFS)); + + /* Print out the registers */ + ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST; + for (index = 1; index < 32; index++) { + EFX_ERR(efx, "R%.2d (?): 0x%.8x\n", index, + MCDI_DWORD2(assertion, ofst)); + ofst += sizeof(efx_dword_t); + } + + return 0; +} + +void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ + u8 inbuf[MC_CMD_SET_ID_LED_IN_LEN]; + int rc; + + BUILD_BUG_ON(EFX_LED_OFF != MC_CMD_LED_OFF); + BUILD_BUG_ON(EFX_LED_ON != MC_CMD_LED_ON); + BUILD_BUG_ON(EFX_LED_DEFAULT != MC_CMD_LED_DEFAULT); + + BUILD_BUG_ON(MC_CMD_SET_ID_LED_OUT_LEN != 0); + + MCDI_SET_DWORD(inbuf, SET_ID_LED_IN_STATE, mode); + + rc = efx_mcdi_rpc(efx, MC_CMD_SET_ID_LED, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); +} + +int efx_mcdi_reset_port(struct efx_nic *efx) +{ + int rc = efx_mcdi_rpc(efx, MC_CMD_PORT_RESET, NULL, 0, NULL, 0, NULL); + if (rc) + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_reset_mc(struct efx_nic *efx) +{ + u8 inbuf[MC_CMD_REBOOT_IN_LEN]; + int rc; + + BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0); + MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS, 0); + rc = efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf, sizeof(inbuf), + NULL, 0, NULL); + /* White is black, and up is down */ + if (rc == -EIO) + return 0; + if (rc == 0) + rc = -EIO; + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type, + const u8 *mac, int *id_out) +{ + u8 inbuf[MC_CMD_WOL_FILTER_SET_IN_LEN]; + u8 outbuf[MC_CMD_WOL_FILTER_SET_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_WOL_TYPE, type); + MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_FILTER_MODE, + MC_CMD_FILTER_MODE_SIMPLE); + memcpy(MCDI_PTR(inbuf, WOL_FILTER_SET_IN_MAGIC_MAC), mac, ETH_ALEN); + + rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_SET, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_WOL_FILTER_SET_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + *id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_SET_OUT_FILTER_ID); + + return 0; + +fail: + *id_out = -1; + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; + +} + + +int +efx_mcdi_wol_filter_set_magic(struct efx_nic *efx, const u8 *mac, int *id_out) +{ + return efx_mcdi_wol_filter_set(efx, MC_CMD_WOL_TYPE_MAGIC, mac, id_out); +} + + +int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out) +{ + u8 outbuf[MC_CMD_WOL_FILTER_GET_OUT_LEN]; + size_t outlen; + int rc; + + rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_GET, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_WOL_FILTER_GET_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + *id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_GET_OUT_FILTER_ID); + + return 0; + +fail: + *id_out = -1; + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + + +int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id) +{ + u8 inbuf[MC_CMD_WOL_FILTER_REMOVE_IN_LEN]; + int rc; + + MCDI_SET_DWORD(inbuf, WOL_FILTER_REMOVE_IN_FILTER_ID, (u32)id); + + rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_REMOVE, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + + +int efx_mcdi_wol_filter_reset(struct efx_nic *efx) +{ + int rc; + + rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_RESET, NULL, 0, NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + diff --git a/drivers/net/sfc/mcdi.h b/drivers/net/sfc/mcdi.h new file mode 100644 index 0000000..de91672 --- /dev/null +++ b/drivers/net/sfc/mcdi.h @@ -0,0 +1,130 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2008-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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_MCDI_H +#define EFX_MCDI_H + +/** + * enum efx_mcdi_state + * @MCDI_STATE_QUIESCENT: No pending MCDI requests. If the caller holds the + * mcdi_lock then they are able to move to MCDI_STATE_RUNNING + * @MCDI_STATE_RUNNING: There is an MCDI request pending. Only the thread that + * moved into this state is allowed to move out of it. + * @MCDI_STATE_COMPLETED: An MCDI request has completed, but the owning thread + * has not yet consumed the result. For all other threads, equivalent to + * MCDI_STATE_RUNNING. + */ +enum efx_mcdi_state { + MCDI_STATE_QUIESCENT, + MCDI_STATE_RUNNING, + MCDI_STATE_COMPLETED, +}; + +enum efx_mcdi_mode { + MCDI_MODE_POLL, + MCDI_MODE_EVENTS, +}; + +/** + * struct efx_mcdi_iface + * @state: Interface state. Waited for by mcdi_wq. + * @wq: Wait queue for threads waiting for state != STATE_RUNNING + * @iface_lock: Protects @credits, @seqno, @resprc, @resplen + * @mode: Poll for mcdi completion, or wait for an mcdi_event. + * Serialised by @lock + * @seqno: The next sequence number to use for mcdi requests. + * Serialised by @lock + * @credits: Number of spurious MCDI completion events allowed before we + * trigger a fatal error. Protected by @lock + * @resprc: Returned MCDI completion + * @resplen: Returned payload length + */ +struct efx_mcdi_iface { + atomic_t state; + wait_queue_head_t wq; + spinlock_t iface_lock; + enum efx_mcdi_mode mode; + unsigned int credits; + unsigned int seqno; + unsigned int resprc; + size_t resplen; +}; + +extern void efx_mcdi_init(struct efx_nic *efx); + +extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const u8 *inbuf, + size_t inlen, u8 *outbuf, size_t outlen, + size_t *outlen_actual); + +extern int efx_mcdi_poll_reboot(struct efx_nic *efx); +extern void efx_mcdi_mode_poll(struct efx_nic *efx); +extern void efx_mcdi_mode_event(struct efx_nic *efx); + +extern void efx_mcdi_process_event(struct efx_channel *channel, + efx_qword_t *event); + +#define MCDI_PTR2(_buf, _ofst) \ + (((u8 *)_buf) + _ofst) +#define MCDI_SET_DWORD2(_buf, _ofst, _value) \ + EFX_POPULATE_DWORD_1(*((efx_dword_t *)MCDI_PTR2(_buf, _ofst)), \ + EFX_DWORD_0, _value) +#define MCDI_DWORD2(_buf, _ofst) \ + EFX_DWORD_FIELD(*((efx_dword_t *)MCDI_PTR2(_buf, _ofst)), \ + EFX_DWORD_0) +#define MCDI_QWORD2(_buf, _ofst) \ + EFX_QWORD_FIELD64(*((efx_qword_t *)MCDI_PTR2(_buf, _ofst)), \ + EFX_QWORD_0) + +#define MCDI_PTR(_buf, _ofst) \ + MCDI_PTR2(_buf, MC_CMD_ ## _ofst ## _OFST) +#define MCDI_SET_DWORD(_buf, _ofst, _value) \ + MCDI_SET_DWORD2(_buf, MC_CMD_ ## _ofst ## _OFST, _value) +#define MCDI_DWORD(_buf, _ofst) \ + MCDI_DWORD2(_buf, MC_CMD_ ## _ofst ## _OFST) +#define MCDI_QWORD(_buf, _ofst) \ + MCDI_QWORD2(_buf, MC_CMD_ ## _ofst ## _OFST) + +#define MCDI_EVENT_FIELD(_ev, _field) \ + EFX_QWORD_FIELD(_ev, MCDI_EVENT_ ## _field) + +extern int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build); +extern int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, + bool *was_attached_out); +extern int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address, + u16 *fw_subtype_list); +extern int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, + u32 dest_evq); +extern int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out); +extern int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type, + size_t *size_out, size_t *erase_size_out, + bool *protected_out); +extern int efx_mcdi_nvram_update_start(struct efx_nic *efx, + unsigned int type); +extern int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type, + loff_t offset, u8 *buffer, size_t length); +extern int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type, + loff_t offset, const u8 *buffer, + size_t length); +extern int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type, + loff_t offset, size_t length); +extern int efx_mcdi_nvram_update_finish(struct efx_nic *efx, + unsigned int type); +extern int efx_mcdi_handle_assertion(struct efx_nic *efx); +extern void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); +extern int efx_mcdi_reset_port(struct efx_nic *efx); +extern int efx_mcdi_reset_mc(struct efx_nic *efx); +extern int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type, + const u8 *mac, int *id_out); +extern int efx_mcdi_wol_filter_set_magic(struct efx_nic *efx, + const u8 *mac, int *id_out); +extern int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out); +extern int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id); +extern int efx_mcdi_wol_filter_reset(struct efx_nic *efx); + +#endif /* EFX_MCDI_H */ diff --git a/drivers/net/sfc/mcdi_mac.c b/drivers/net/sfc/mcdi_mac.c new file mode 100644 index 0000000..06d24a1 --- /dev/null +++ b/drivers/net/sfc/mcdi_mac.c @@ -0,0 +1,152 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "net_driver.h" +#include "efx.h" +#include "mac.h" +#include "mcdi.h" +#include "mcdi_pcol.h" + +static int efx_mcdi_set_mac(struct efx_nic *efx) +{ + u32 reject, fcntl; + u8 cmdbytes[MC_CMD_SET_MAC_IN_LEN]; + + memcpy(cmdbytes + MC_CMD_SET_MAC_IN_ADDR_OFST, + efx->net_dev->dev_addr, ETH_ALEN); + + MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_MTU, + EFX_MAX_FRAME_LEN(efx->net_dev->mtu)); + MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_DRAIN, 0); + + /* The MCDI command provides for controlling accept/reject + * of broadcast packets too, but the driver doesn't currently + * expose this. */ + reject = (efx->promiscuous) ? 0 : + (1 << MC_CMD_SET_MAC_IN_REJECT_UNCST_LBN); + MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_REJECT, reject); + + switch (efx->wanted_fc) { + case EFX_FC_RX | EFX_FC_TX: + fcntl = MC_CMD_FCNTL_BIDIR; + break; + case EFX_FC_RX: + fcntl = MC_CMD_FCNTL_RESPOND; + break; + default: + fcntl = MC_CMD_FCNTL_OFF; + break; + } + if (efx->wanted_fc & EFX_FC_AUTO) + fcntl = MC_CMD_FCNTL_AUTO; + + MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_FCNTL, fcntl); + + return efx_mcdi_rpc(efx, MC_CMD_SET_MAC, cmdbytes, sizeof(cmdbytes), + NULL, 0, NULL); +} + +static int efx_mcdi_get_mac_faults(struct efx_nic *efx, u32 *faults) +{ + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + size_t outlength; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), &outlength); + if (rc) + goto fail; + + *faults = MCDI_DWORD(outbuf, GET_LINK_OUT_MAC_FAULT); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", + __func__, rc); + return rc; +} + +int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr, + u32 dma_len, int enable, int clear) +{ + u8 inbuf[MC_CMD_MAC_STATS_IN_LEN]; + int rc; + efx_dword_t *cmd_ptr; + int period = 1000; + u32 addr_hi; + u32 addr_lo; + + BUILD_BUG_ON(MC_CMD_MAC_STATS_OUT_LEN != 0); + + addr_lo = ((u64)dma_addr) >> 0; + addr_hi = ((u64)dma_addr) >> 32; + + MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_ADDR_LO, addr_lo); + MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_ADDR_HI, addr_hi); + cmd_ptr = (efx_dword_t *)MCDI_PTR(inbuf, MAC_STATS_IN_CMD); + if (enable) + EFX_POPULATE_DWORD_6(*cmd_ptr, + MC_CMD_MAC_STATS_CMD_DMA, 1, + MC_CMD_MAC_STATS_CMD_CLEAR, clear, + MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE, 1, + MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE, 1, + MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR, 0, + MC_CMD_MAC_STATS_CMD_PERIOD_MS, period); + else + EFX_POPULATE_DWORD_5(*cmd_ptr, + MC_CMD_MAC_STATS_CMD_DMA, 0, + MC_CMD_MAC_STATS_CMD_CLEAR, clear, + MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE, 1, + MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE, 0, + MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR, 0); + MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_LEN, dma_len); + + rc = efx_mcdi_rpc(efx, MC_CMD_MAC_STATS, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: %s failed rc=%d\n", + __func__, enable ? "enable" : "disable", rc); + return rc; +} + +static int efx_mcdi_mac_reconfigure(struct efx_nic *efx) +{ + int rc; + + rc = efx_mcdi_set_mac(efx); + if (rc != 0) + return rc; + + /* Restore the multicast hash registers. */ + efx->type->push_multicast_hash(efx); + + return 0; +} + + +static bool efx_mcdi_mac_check_fault(struct efx_nic *efx) +{ + u32 faults; + int rc = efx_mcdi_get_mac_faults(efx, &faults); + return (rc != 0) || (faults != 0); +} + + +struct efx_mac_operations efx_mcdi_mac_operations = { + .reconfigure = efx_mcdi_mac_reconfigure, + .update_stats = efx_port_dummy_op_void, + .check_fault = efx_mcdi_mac_check_fault, +}; diff --git a/drivers/net/sfc/mcdi_pcol.h b/drivers/net/sfc/mcdi_pcol.h new file mode 100644 index 0000000..2a85360 --- /dev/null +++ b/drivers/net/sfc/mcdi_pcol.h @@ -0,0 +1,1578 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + + +#ifndef MCDI_PCOL_H +#define MCDI_PCOL_H + +/* Values to be written into FMCR_CZ_RESET_STATE_REG to control boot. */ +/* Power-on reset state */ +#define MC_FW_STATE_POR (1) +/* If this is set in MC_RESET_STATE_REG then it should be + * possible to jump into IMEM without loading code from flash. */ +#define MC_FW_WARM_BOOT_OK (2) +/* The MC main image has started to boot. */ +#define MC_FW_STATE_BOOTING (4) +/* The Scheduler has started. */ +#define MC_FW_STATE_SCHED (8) + +/* Values to be written to the per-port status dword in shared + * memory on reboot and assert */ +#define MC_STATUS_DWORD_REBOOT (0xb007b007) +#define MC_STATUS_DWORD_ASSERT (0xdeaddead) + +/* The current version of the MCDI protocol. + * + * Note that the ROM burnt into the card only talks V0, so at the very + * least every driver must support version 0 and MCDI_PCOL_VERSION + */ +#define MCDI_PCOL_VERSION 1 + +/** + * MCDI version 1 + * + * Each MCDI request starts with an MCDI_HEADER, which is a 32byte + * structure, filled in by the client. + * + * 0 7 8 16 20 22 23 24 31 + * | CODE | R | LEN | SEQ | Rsvd | E | R | XFLAGS | + * | | | + * | | \--- Response + * | \------- Error + * \------------------------------ Resync (always set) + * + * The client writes it's request into MC shared memory, and rings the + * doorbell. Each request is completed by either by the MC writting + * back into shared memory, or by writting out an event. + * + * All MCDI commands support completion by shared memory response. Each + * request may also contain additional data (accounted for by HEADER.LEN), + * and some response's may also contain additional data (again, accounted + * for by HEADER.LEN). + * + * Some MCDI commands support completion by event, in which any associated + * response data is included in the event. + * + * The protocol requires one response to be delivered for every request, a + * request should not be sent unless the response for the previous request + * has been received (either by polling shared memory, or by receiving + * an event). + */ + +/** Request/Response structure */ +#define MCDI_HEADER_OFST 0 +#define MCDI_HEADER_CODE_LBN 0 +#define MCDI_HEADER_CODE_WIDTH 7 +#define MCDI_HEADER_RESYNC_LBN 7 +#define MCDI_HEADER_RESYNC_WIDTH 1 +#define MCDI_HEADER_DATALEN_LBN 8 +#define MCDI_HEADER_DATALEN_WIDTH 8 +#define MCDI_HEADER_SEQ_LBN 16 +#define MCDI_HEADER_RSVD_LBN 20 +#define MCDI_HEADER_RSVD_WIDTH 2 +#define MCDI_HEADER_SEQ_WIDTH 4 +#define MCDI_HEADER_ERROR_LBN 22 +#define MCDI_HEADER_ERROR_WIDTH 1 +#define MCDI_HEADER_RESPONSE_LBN 23 +#define MCDI_HEADER_RESPONSE_WIDTH 1 +#define MCDI_HEADER_XFLAGS_LBN 24 +#define MCDI_HEADER_XFLAGS_WIDTH 8 +/* Request response using event */ +#define MCDI_HEADER_XFLAGS_EVREQ 0x01 + +/* Maximum number of payload bytes */ +#define MCDI_CTL_SDU_LEN_MAX 0xfc + +/* The MC can generate events for two reasons: + * - To complete a shared memory request if XFLAGS_EVREQ was set + * - As a notification (link state, i2c event), controlled + * via MC_CMD_LOG_CTRL + * + * Both events share a common structure: + * + * 0 32 33 36 44 52 60 + * | Data | Cont | Level | Src | Code | Rsvd | + * | + * \ There is another event pending in this notification + * + * If Code==CMDDONE, then the fields are further interpreted as: + * + * - LEVEL==INFO Command succeded + * - LEVEL==ERR Command failed + * + * 0 8 16 24 32 + * | Seq | Datalen | Errno | Rsvd | + * + * These fields are taken directly out of the standard MCDI header, i.e., + * LEVEL==ERR, Datalen == 0 => Reboot + * + * Events can be squirted out of the UART (using LOG_CTRL) without a + * MCDI header. An event can be distinguished from a MCDI response by + * examining the first byte which is 0xc0. This corresponds to the + * non-existent MCDI command MC_CMD_DEBUG_LOG. + * + * 0 7 8 + * | command | Resync | = 0xc0 + * + * Since the event is written in big-endian byte order, this works + * providing bits 56-63 of the event are 0xc0. + * + * 56 60 63 + * | Rsvd | Code | = 0xc0 + * + * Which means for convenience the event code is 0xc for all MC + * generated events. + */ +#define FSE_AZ_EV_CODE_MCDI_EVRESPONSE 0xc + +#define MCDI_EVENT_DATA_LBN 0 +#define MCDI_EVENT_DATA_WIDTH 32 +#define MCDI_EVENT_CONT_LBN 32 +#define MCDI_EVENT_CONT_WIDTH 1 +#define MCDI_EVENT_LEVEL_LBN 33 +#define MCDI_EVENT_LEVEL_WIDTH 3 +#define MCDI_EVENT_LEVEL_INFO (0) +#define MCDI_EVENT_LEVEL_WARN (1) +#define MCDI_EVENT_LEVEL_ERR (2) +#define MCDI_EVENT_LEVEL_FATAL (3) +#define MCDI_EVENT_SRC_LBN 36 +#define MCDI_EVENT_SRC_WIDTH 8 +#define MCDI_EVENT_CODE_LBN 44 +#define MCDI_EVENT_CODE_WIDTH 8 +#define MCDI_EVENT_CODE_BADSSERT (1) +#define MCDI_EVENT_CODE_PMNOTICE (2) +#define MCDI_EVENT_CODE_CMDDONE (3) +#define MCDI_EVENT_CMDDONE_SEQ_LBN 0 +#define MCDI_EVENT_CMDDONE_SEQ_WIDTH 8 +#define MCDI_EVENT_CMDDONE_DATALEN_LBN 8 +#define MCDI_EVENT_CMDDONE_DATALEN_WIDTH 8 +#define MCDI_EVENT_CMDDONE_ERRNO_LBN 16 +#define MCDI_EVENT_CMDDONE_ERRNO_WIDTH 8 +#define MCDI_EVENT_CODE_LINKCHANGE (4) +#define MCDI_EVENT_LINKCHANGE_LP_CAP_LBN 0 +#define MCDI_EVENT_LINKCHANGE_LP_CAP_WIDTH 16 +#define MCDI_EVENT_LINKCHANGE_SPEED_LBN 16 +#define MCDI_EVENT_LINKCHANGE_SPEED_WIDTH 4 +#define MCDI_EVENT_LINKCHANGE_SPEED_100M 1 +#define MCDI_EVENT_LINKCHANGE_SPEED_1G 2 +#define MCDI_EVENT_LINKCHANGE_SPEED_10G 3 +#define MCDI_EVENT_LINKCHANGE_FCNTL_LBN 20 +#define MCDI_EVENT_LINKCHANGE_FCNTL_WIDTH 4 +#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_LBN 24 +#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_WIDTH 8 +#define MCDI_EVENT_CODE_SENSOREVT (5) +#define MCDI_EVENT_SENSOREVT_MONITOR_LBN 0 +#define MCDI_EVENT_SENSOREVT_MONITOR_WIDTH 8 +#define MCDI_EVENT_SENSOREVT_STATE_LBN 8 +#define MCDI_EVENT_SENSOREVT_STATE_WIDTH 8 +#define MCDI_EVENT_SENSOREVT_VALUE_LBN 16 +#define MCDI_EVENT_SENSOREVT_VALUE_WIDTH 16 +#define MCDI_EVENT_CODE_SCHEDERR (6) +#define MCDI_EVENT_CODE_REBOOT (7) +#define MCDI_EVENT_CODE_MAC_STATS_DMA (8) +#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_LBN 0 +#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_WIDTH 32 + +/* Non-existent command target */ +#define MC_CMD_ERR_ENOENT 2 +/* assert() has killed the MC */ +#define MC_CMD_ERR_EINTR 4 +/* Caller does not hold required locks */ +#define MC_CMD_ERR_EACCES 13 +/* Resource is currently unavailable (e.g. lock contention) */ +#define MC_CMD_ERR_EBUSY 16 +/* Invalid argument to target */ +#define MC_CMD_ERR_EINVAL 22 +/* Non-recursive resource is already acquired */ +#define MC_CMD_ERR_EDEADLK 35 +/* Operation not implemented */ +#define MC_CMD_ERR_ENOSYS 38 +/* Operation timed out */ +#define MC_CMD_ERR_ETIME 62 + +#define MC_CMD_ERR_CODE_OFST 0 + + +/* MC_CMD_READ32: (debug, variadic out) + * Read multiple 32byte words from MC memory + */ +#define MC_CMD_READ32 0x01 +#define MC_CMD_READ32_IN_LEN 8 +#define MC_CMD_READ32_IN_ADDR_OFST 0 +#define MC_CMD_READ32_IN_NUMWORDS_OFST 4 +#define MC_CMD_READ32_OUT_LEN(_numwords) \ + (4 * (_numwords)) +#define MC_CMD_READ32_OUT_BUFFER_OFST 0 + +/* MC_CMD_WRITE32: (debug, variadic in) + * Write multiple 32byte words to MC memory + */ +#define MC_CMD_WRITE32 0x02 +#define MC_CMD_WRITE32_IN_LEN(_numwords) (((_numwords) * 4) + 4) +#define MC_CMD_WRITE32_IN_ADDR_OFST 0 +#define MC_CMD_WRITE32_IN_BUFFER_OFST 4 +#define MC_CMD_WRITE32_OUT_LEN 0 + +/* MC_CMD_COPYCODE: (debug) + * Copy MC code between two locations and jump + */ +#define MC_CMD_COPYCODE 0x03 +#define MC_CMD_COPYCODE_IN_LEN 16 +#define MC_CMD_COPYCODE_IN_SRC_ADDR_OFST 0 +#define MC_CMD_COPYCODE_IN_DEST_ADDR_OFST 4 +#define MC_CMD_COPYCODE_IN_NUMWORDS_OFST 8 +#define MC_CMD_COPYCODE_IN_JUMP_OFST 12 +/* Control should return to the caller rather than jumping */ +#define MC_CMD_COPYCODE_JUMP_NONE 1 +#define MC_CMD_COPYCODE_OUT_LEN 0 + +/* MC_CMD_SET_FUNC: (debug) + * Select function for function-specific commands. + */ +#define MC_CMD_SET_FUNC 0x04 +#define MC_CMD_SET_FUNC_IN_LEN 4 +#define MC_CMD_SET_FUNC_IN_FUNC_OFST 0 +#define MC_CMD_SET_FUNC_OUT_LEN 0 + +/* MC_CMD_GET_BOOT_STATUS: + * Get the instruction address from which the MC booted. + */ +#define MC_CMD_GET_BOOT_STATUS 0x05 +#define MC_CMD_GET_BOOT_STATUS_IN_LEN 0 +#define MC_CMD_GET_BOOT_STATUS_OUT_LEN 8 +#define MC_CMD_GET_BOOT_STATUS_OUT_BOOT_OFFSET_OFST 0 +#define MC_CMD_GET_BOOT_STATUS_OUT_FLAGS_OFST 4 +/* Reboot caused by watchdog */ +#define MC_CMD_GET_BOOT_STATUS_FLAGS_WATCHDOG_LBN (0) +#define MC_CMD_GET_BOOT_STATUS_FLAGS_WATCHDOG_WIDTH (1) +/* MC booted from primary flash partition */ +#define MC_CMD_GET_BOOT_STATUS_FLAGS_PRIMARY_LBN (1) +#define MC_CMD_GET_BOOT_STATUS_FLAGS_PRIMARY_WIDTH (1) +/* MC booted from backup flash partition */ +#define MC_CMD_GET_BOOT_STATUS_FLAGS_BACKUP_LBN (2) +#define MC_CMD_GET_BOOT_STATUS_FLAGS_BACKUP_WIDTH (1) + +/* MC_CMD_GET_ASSERTS: (debug, variadic out) + * Get (and optionally clear) the current assertion status. + * + * Only OUT.GLOBAL_FLAGS is guaranteed to exist in the completion + * payload. The other fields will only be present if + * OUT.GLOBAL_FLAGS != NO_FAILS + */ +#define MC_CMD_GET_ASSERTS 0x06 +#define MC_CMD_GET_ASSERTS_IN_LEN 4 +#define MC_CMD_GET_ASSERTS_IN_CLEAR_OFST 0 +#define MC_CMD_GET_ASSERTS_OUT_LEN 140 +/* Assertion status flag */ +#define MC_CMD_GET_ASSERTS_OUT_GLOBAL_FLAGS_OFST 0 +/*! No assertions have failed. */ +#define MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS 1 +/*! A system-level assertion has failed. */ +#define MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL 2 +/*! A thread-level assertion has failed. */ +#define MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL 3 +/*! The system was reset by the watchdog. */ +#define MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED 4 +/* Failing PC value */ +#define MC_CMD_GET_ASSERTS_OUT_SAVED_PC_OFFS_OFST 4 +/* Saved GP regs */ +#define MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST 8 +#define MC_CMD_GET_ASSERTS_OUT_GP_REGS_LEN 124 +/* Failing thread address */ +#define MC_CMD_GET_ASSERTS_OUT_THREAD_OFFS_OFST 132 + +/* MC_CMD_LOG_CTRL: + * Determine the output stream for various events and messages + */ +#define MC_CMD_LOG_CTRL 0x07 +#define MC_CMD_LOG_CTRL_IN_LEN 8 +#define MC_CMD_LOG_CTRL_IN_LOG_DEST_OFST 0 +#define MC_CMD_LOG_CTRL_IN_LOG_DEST_UART (1) +#define MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ (2) +#define MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ_OFST 4 +#define MC_CMD_LOG_CTRL_OUT_LEN 0 + +/* MC_CMD_GET_VERSION: + * Get version information about the MC firmware + */ +#define MC_CMD_GET_VERSION 0x08 +#define MC_CMD_GET_VERSION_IN_LEN 0 +#define MC_CMD_GET_VERSION_V0_OUT_LEN 4 +#define MC_CMD_GET_VERSION_V1_OUT_LEN 32 +#define MC_CMD_GET_VERSION_OUT_FIRMWARE_OFST 0 +/* Reserved version number to indicate "any" version. */ +#define MC_CMD_GET_VERSION_OUT_FIRMWARE_ANY 0xffffffff +/* The version response of a boot ROM awaiting rescue */ +#define MC_CMD_GET_VERSION_OUT_FIRMWARE_BOOTROM 0xb0070000 +#define MC_CMD_GET_VERSION_V1_OUT_PCOL_OFST 4 +/* 128bit mask of functions supported by the current firmware */ +#define MC_CMD_GET_VERSION_V1_OUT_SUPPORTED_FUNCS_OFST 8 +/* The command set exported by the boot ROM (MCDI v0) */ +#define MC_CMD_GET_VERSION_V0_SUPPORTED_FUNCS { \ + (1 << MC_CMD_READ32) | \ + (1 << MC_CMD_WRITE32) | \ + (1 << MC_CMD_COPYCODE) | \ + (1 << MC_CMD_GET_VERSION), \ + 0, 0, 0 } +#define MC_CMD_GET_VERSION_OUT_VERSION_OFST 24 + +/* Vectors in the boot ROM */ +/* Point to the copycode entry point. */ +#define MC_BOOTROM_COPYCODE_VEC (0x7f4) +/* Points to the recovery mode entry point. */ +#define MC_BOOTROM_NOFLASH_VEC (0x7f8) + +/* Test execution limits */ +#define MC_TESTEXEC_VARIANT_COUNT 16 +#define MC_TESTEXEC_RESULT_COUNT 7 + +/* MC_CMD_SET_TESTVARS: (debug, variadic in) + * Write variant words for test. + * + * The user supplies a bitmap of the variants they wish to set. + * They must ensure that IN.LEN >= 4 + 4 * ffs(BITMAP) + */ +#define MC_CMD_SET_TESTVARS 0x09 +#define MC_CMD_SET_TESTVARS_IN_LEN(_numwords) \ + (4 + 4*(_numwords)) +#define MC_CMD_SET_TESTVARS_IN_ARGS_BITMAP_OFST 0 +/* Up to MC_TESTEXEC_VARIANT_COUNT of 32byte words start here */ +#define MC_CMD_SET_TESTVARS_IN_ARGS_BUFFER_OFST 4 +#define MC_CMD_SET_TESTVARS_OUT_LEN 0 + +/* MC_CMD_GET_TESTRCS: (debug, variadic out) + * Return result words from test. + */ +#define MC_CMD_GET_TESTRCS 0x0a +#define MC_CMD_GET_TESTRCS_IN_LEN 4 +#define MC_CMD_GET_TESTRCS_IN_NUMWORDS_OFST 0 +#define MC_CMD_GET_TESTRCS_OUT_LEN(_numwords) \ + (4 * (_numwords)) +#define MC_CMD_GET_TESTRCS_OUT_BUFFER_OFST 0 + +/* MC_CMD_RUN_TEST: (debug) + * Run the test exported by this firmware image + */ +#define MC_CMD_RUN_TEST 0x0b +#define MC_CMD_RUN_TEST_IN_LEN 0 +#define MC_CMD_RUN_TEST_OUT_LEN 0 + +/* MC_CMD_CSR_READ32: (debug, variadic out) + * Read 32bit words from the indirect memory map + */ +#define MC_CMD_CSR_READ32 0x0c +#define MC_CMD_CSR_READ32_IN_LEN 12 +#define MC_CMD_CSR_READ32_IN_ADDR_OFST 0 +#define MC_CMD_CSR_READ32_IN_STEP_OFST 4 +#define MC_CMD_CSR_READ32_IN_NUMWORDS_OFST 8 +#define MC_CMD_CSR_READ32_OUT_LEN(_numwords) \ + (((_numwords) * 4) + 4) +/* IN.NUMWORDS of 32bit words start here */ +#define MC_CMD_CSR_READ32_OUT_BUFFER_OFST 0 +#define MC_CMD_CSR_READ32_OUT_IREG_STATUS_OFST(_numwords) \ + ((_numwords) * 4) + +/* MC_CMD_CSR_WRITE32: (debug, variadic in) + * Write 32bit dwords to the indirect memory map + */ +#define MC_CMD_CSR_WRITE32 0x0d +#define MC_CMD_CSR_WRITE32_IN_LEN(_numwords) \ + (((_numwords) * 4) + 8) +#define MC_CMD_CSR_WRITE32_IN_ADDR_OFST 0 +#define MC_CMD_CSR_WRITE32_IN_STEP_OFST 4 +/* Multiple 32bit words of data to write start here */ +#define MC_CMD_CSR_WRITE32_IN_BUFFER_OFST 8 +#define MC_CMD_CSR_WRITE32_OUT_LEN 4 +#define MC_CMD_CSR_WRITE32_OUT_STATUS_OFST 0 + +/* MC_CMD_JTAG_WORK: (debug, fpga only) + * Process JTAG work buffer for RBF acceleration. + * + * Host: bit count, (up to) 32 words of data to clock out to JTAG + * (bits 1,0=TMS,TDO for first bit; bits 3,2=TMS,TDO for second bit, etc.) + * MC: bit count, (up to) 32 words of data clocked in from JTAG + * (bit 0=TDI for first bit, bit 1=TDI for second bit, etc.; [31:16] unused) + */ +#define MC_CMD_JTAG_WORK 0x0e + +/* MC_CMD_STACKINFO: (debug, variadic out) + * Get stack information + * + * Host: nothing + * MC: (thread ptr, stack size, free space) for each thread in system + */ +#define MC_CMD_STACKINFO 0x0f + +/* MC_CMD_MDIO_READ: + * MDIO register read + */ +#define MC_CMD_MDIO_READ 0x10 +#define MC_CMD_MDIO_READ_IN_LEN 16 +#define MC_CMD_MDIO_READ_IN_BUS_OFST 0 +#define MC_CMD_MDIO_READ_IN_PRTAD_OFST 4 +#define MC_CMD_MDIO_READ_IN_DEVAD_OFST 8 +#define MC_CMD_MDIO_READ_IN_ADDR_OFST 12 +#define MC_CMD_MDIO_READ_OUT_LEN 8 +#define MC_CMD_MDIO_READ_OUT_VALUE_OFST 0 +#define MC_CMD_MDIO_READ_OUT_STATUS_OFST 4 + +/* MC_CMD_MDIO_WRITE: + * MDIO register write + */ +#define MC_CMD_MDIO_WRITE 0x11 +#define MC_CMD_MDIO_WRITE_IN_LEN 20 +#define MC_CMD_MDIO_WRITE_IN_BUS_OFST 0 +#define MC_CMD_MDIO_WRITE_IN_PRTAD_OFST 4 +#define MC_CMD_MDIO_WRITE_IN_DEVAD_OFST 8 +#define MC_CMD_MDIO_WRITE_IN_ADDR_OFST 12 +#define MC_CMD_MDIO_WRITE_IN_VALUE_OFST 16 +#define MC_CMD_MDIO_WRITE_OUT_LEN 4 +#define MC_CMD_MDIO_WRITE_OUT_STATUS_OFST 0 + +/* By default all the MCDI MDIO operations perform clause45 mode. + * If you want to use clause22 then set DEVAD = MC_CMD_MDIO_CLAUSE22. + */ +#define MC_CMD_MDIO_CLAUSE22 32 + +/* There are two MDIO buses: one for the internal PHY, and one for external + * devices. + */ +#define MC_CMD_MDIO_BUS_INTERNAL 0 +#define MC_CMD_MDIO_BUS_EXTERNAL 1 + +/* The MDIO commands return the raw status bits from the MDIO block. A "good" + * transaction should have the DONE bit set and all other bits clear. + */ +#define MC_CMD_MDIO_STATUS_GOOD 0x08 + + +/* MC_CMD_DBI_WRITE: (debug) + * Write DBI register(s) + * + * Host: address, byte-enables (and VF selection, and cs2 flag), + * value [,address ...] + * MC: nothing + */ +#define MC_CMD_DBI_WRITE 0x12 +#define MC_CMD_DBI_WRITE_IN_LEN(_numwords) \ + (12 * (_numwords)) +#define MC_CMD_DBI_WRITE_IN_ADDRESS_OFST(_word) \ + (((_word) * 12) + 0) +#define MC_CMD_DBI_WRITE_IN_BYTE_MASK_OFST(_word) \ + (((_word) * 12) + 4) +#define MC_CMD_DBI_WRITE_IN_VALUE_OFST(_word) \ + (((_word) * 12) + 8) +#define MC_CMD_DBI_WRITE_OUT_LEN 0 + +/* MC_CMD_DBI_READ: (debug) + * Read DBI register(s) + * + * Host: address, [,address ...] + * MC: value [,value ...] + * (note: this does not support reading from VFs, but is retained for backwards + * compatibility; see MC_CMD_DBI_READX below) + */ +#define MC_CMD_DBI_READ 0x13 +#define MC_CMD_DBI_READ_IN_LEN(_numwords) \ + (4 * (_numwords)) +#define MC_CMD_DBI_READ_OUT_LEN(_numwords) \ + (4 * (_numwords)) + +/* MC_CMD_PORT_READ32: (debug) + * Read a 32-bit register from the indirect port register map. + * + * The port to access is implied by the Shared memory channel used. + */ +#define MC_CMD_PORT_READ32 0x14 +#define MC_CMD_PORT_READ32_IN_LEN 4 +#define MC_CMD_PORT_READ32_IN_ADDR_OFST 0 +#define MC_CMD_PORT_READ32_OUT_LEN 8 +#define MC_CMD_PORT_READ32_OUT_VALUE_OFST 0 +#define MC_CMD_PORT_READ32_OUT_STATUS_OFST 4 + +/* MC_CMD_PORT_WRITE32: (debug) + * Write a 32-bit register to the indirect port register map. + * + * The port to access is implied by the Shared memory channel used. + */ +#define MC_CMD_PORT_WRITE32 0x15 +#define MC_CMD_PORT_WRITE32_IN_LEN 8 +#define MC_CMD_PORT_WRITE32_IN_ADDR_OFST 0 +#define MC_CMD_PORT_WRITE32_IN_VALUE_OFST 4 +#define MC_CMD_PORT_WRITE32_OUT_LEN 4 +#define MC_CMD_PORT_WRITE32_OUT_STATUS_OFST 0 + +/* MC_CMD_PORT_READ128: (debug) + * Read a 128-bit register from indirect port register map + * + * The port to access is implied by the Shared memory channel used. + */ +#define MC_CMD_PORT_READ128 0x16 +#define MC_CMD_PORT_READ128_IN_LEN 4 +#define MC_CMD_PORT_READ128_IN_ADDR_OFST 0 +#define MC_CMD_PORT_READ128_OUT_LEN 20 +#define MC_CMD_PORT_READ128_OUT_VALUE_OFST 0 +#define MC_CMD_PORT_READ128_OUT_STATUS_OFST 16 + +/* MC_CMD_PORT_WRITE128: (debug) + * Write a 128-bit register to indirect port register map. + * + * The port to access is implied by the Shared memory channel used. + */ +#define MC_CMD_PORT_WRITE128 0x17 +#define MC_CMD_PORT_WRITE128_IN_LEN 20 +#define MC_CMD_PORT_WRITE128_IN_ADDR_OFST 0 +#define MC_CMD_PORT_WRITE128_IN_VALUE_OFST 4 +#define MC_CMD_PORT_WRITE128_OUT_LEN 4 +#define MC_CMD_PORT_WRITE128_OUT_STATUS_OFST 0 + +/* MC_CMD_GET_BOARD_CFG: + * Returns the MC firmware configuration structure + * + * The FW_SUBTYPE_LIST contains a 16-bit value for each of the 12 types of + * NVRAM area. The values are defined in the firmware/mc/platform/<xxx>.c file + * for a specific board type, but otherwise have no meaning to the MC; they + * are used by the driver to manage selection of appropriate firmware updates. + */ +#define MC_CMD_GET_BOARD_CFG 0x18 +#define MC_CMD_GET_BOARD_CFG_IN_LEN 0 +#define MC_CMD_GET_BOARD_CFG_OUT_LEN 96 +#define MC_CMD_GET_BOARD_CFG_OUT_BOARD_TYPE_OFST 0 +#define MC_CMD_GET_BOARD_CFG_OUT_BOARD_NAME_OFST 4 +#define MC_CMD_GET_BOARD_CFG_OUT_BOARD_NAME_LEN 32 +#define MC_CMD_GET_BOARD_CFG_OUT_CAPABILITIES_PORT0_OFST 36 +#define MC_CMD_GET_BOARD_CFG_OUT_CAPABILITIES_PORT1_OFST 40 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_OFST 44 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_LEN 6 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_OFST 50 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_LEN 6 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_COUNT_PORT0_OFST 56 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_COUNT_PORT1_OFST 60 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_STRIDE_PORT0_OFST 64 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_STRIDE_PORT1_OFST 68 +#define MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST 72 +#define MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN 24 + +/* MC_CMD_DBI_READX: (debug) + * Read DBI register(s) -- extended functionality + * + * Host: vf selection, address, [,vf selection ...] + * MC: value [,value ...] + */ +#define MC_CMD_DBI_READX 0x19 +#define MC_CMD_DBI_READX_IN_LEN(_numwords) \ + (8*(_numwords)) +#define MC_CMD_DBI_READX_OUT_LEN(_numwords) \ + (4*(_numwords)) + +/* MC_CMD_SET_RAND_SEED: + * Set the 16byte seed for the MC psuedo-random generator + */ +#define MC_CMD_SET_RAND_SEED 0x1a +#define MC_CMD_SET_RAND_SEED_IN_LEN 16 +#define MC_CMD_SET_RAND_SEED_IN_SEED_OFST 0 +#define MC_CMD_SET_RAND_SEED_OUT_LEN 0 + +/* MC_CMD_LTSSM_HIST: (debug) + * Retrieve the history of the LTSSM, if the build supports it. + * + * Host: nothing + * MC: variable number of LTSSM values, as bytes + * The history is read-to-clear. + */ +#define MC_CMD_LTSSM_HIST 0x1b + +/* MC_CMD_DRV_ATTACH: + * Inform MCPU that this port is managed on the host (i.e. driver active) + */ +#define MC_CMD_DRV_ATTACH 0x1c +#define MC_CMD_DRV_ATTACH_IN_LEN 8 +#define MC_CMD_DRV_ATTACH_IN_NEW_STATE_OFST 0 +#define MC_CMD_DRV_ATTACH_IN_UPDATE_OFST 4 +#define MC_CMD_DRV_ATTACH_OUT_LEN 4 +#define MC_CMD_DRV_ATTACH_OUT_OLD_STATE_OFST 0 + +/* MC_CMD_NCSI_PROD: (debug) + * Trigger an NC-SI event (and possibly an AEN in response) + */ +#define MC_CMD_NCSI_PROD 0x1d +#define MC_CMD_NCSI_PROD_IN_LEN 4 +#define MC_CMD_NCSI_PROD_IN_EVENTS_OFST 0 +#define MC_CMD_NCSI_PROD_LINKCHANGE_LBN 0 +#define MC_CMD_NCSI_PROD_LINKCHANGE_WIDTH 1 +#define MC_CMD_NCSI_PROD_RESET_LBN 1 +#define MC_CMD_NCSI_PROD_RESET_WIDTH 1 +#define MC_CMD_NCSI_PROD_DRVATTACH_LBN 2 +#define MC_CMD_NCSI_PROD_DRVATTACH_WIDTH 1 +#define MC_CMD_NCSI_PROD_OUT_LEN 0 + +/* Enumeration */ +#define MC_CMD_NCSI_PROD_LINKCHANGE 0 +#define MC_CMD_NCSI_PROD_RESET 1 +#define MC_CMD_NCSI_PROD_DRVATTACH 2 + +/* MC_CMD_DEVEL: (debug) + * Reserved for development + */ +#define MC_CMD_DEVEL 0x1e + +/* MC_CMD_SHMUART: (debug) + * Route UART output to circular buffer in shared memory instead. + */ +#define MC_CMD_SHMUART 0x1f +#define MC_CMD_SHMUART_IN_FLAG_OFST 0 +#define MC_CMD_SHMUART_IN_LEN 4 +#define MC_CMD_SHMUART_OUT_LEN 0 + +/* MC_CMD_PORT_RESET: + * Generic per-port reset. There is no equivalent for per-board reset. + * + * Locks required: None + * Return code: 0, ETIME + */ +#define MC_CMD_PORT_RESET 0x20 +#define MC_CMD_PORT_RESET_IN_LEN 0 +#define MC_CMD_PORT_RESET_OUT_LEN 0 + +/* MC_CMD_RESOURCE_LOCK: + * Generic resource lock/unlock interface. + * + * Locks required: None + * Return code: 0, + * EBUSY (if trylock is contended by other port), + * EDEADLK (if trylock is already acquired by this port) + * EINVAL (if unlock doesn't own the lock) + */ +#define MC_CMD_RESOURCE_LOCK 0x21 +#define MC_CMD_RESOURCE_LOCK_IN_LEN 8 +#define MC_CMD_RESOURCE_LOCK_IN_ACTION_OFST 0 +#define MC_CMD_RESOURCE_LOCK_ACTION_TRYLOCK 1 +#define MC_CMD_RESOURCE_LOCK_ACTION_UNLOCK 0 +#define MC_CMD_RESOURCE_LOCK_IN_RESOURCE_OFST 4 +#define MC_CMD_RESOURCE_LOCK_I2C 2 +#define MC_CMD_RESOURCE_LOCK_PHY 3 +#define MC_CMD_RESOURCE_LOCK_OUT_LEN 0 + +/* MC_CMD_SPI_COMMAND: (variadic in, variadic out) + * Read/Write to/from the SPI device. + * + * Locks required: SPI_LOCK + * Return code: 0, ETIME, EINVAL, EACCES (if SPI_LOCK is not held) + */ +#define MC_CMD_SPI_COMMAND 0x22 +#define MC_CMD_SPI_COMMAND_IN_LEN(_write_bytes) (12 + (_write_bytes)) +#define MC_CMD_SPI_COMMAND_IN_ARGS_OFST 0 +#define MC_CMD_SPI_COMMAND_IN_ARGS_ADDRESS_OFST 0 +#define MC_CMD_SPI_COMMAND_IN_ARGS_READ_BYTES_OFST 4 +#define MC_CMD_SPI_COMMAND_IN_ARGS_CHIP_SELECT_OFST 8 +/* Data to write here */ +#define MC_CMD_SPI_COMMAND_IN_WRITE_BUFFER_OFST 12 +#define MC_CMD_SPI_COMMAND_OUT_LEN(_read_bytes) (_read_bytes) +/* Data read here */ +#define MC_CMD_SPI_COMMAND_OUT_READ_BUFFER_OFST 0 + +/* MC_CMD_I2C_READ_WRITE: (variadic in, variadic out) + * Read/Write to/from the I2C bus. + * + * Locks required: I2C_LOCK + * Return code: 0, ETIME, EINVAL, EACCES (if I2C_LOCK is not held) + */ +#define MC_CMD_I2C_RW 0x23 +#define MC_CMD_I2C_RW_IN_LEN(_write_bytes) (8 + (_write_bytes)) +#define MC_CMD_I2C_RW_IN_ARGS_OFST 0 +#define MC_CMD_I2C_RW_IN_ARGS_ADDR_OFST 0 +#define MC_CMD_I2C_RW_IN_ARGS_READ_BYTES_OFST 4 +/* Data to write here */ +#define MC_CMD_I2C_RW_IN_WRITE_BUFFER_OFSET 8 +#define MC_CMD_I2C_RW_OUT_LEN(_read_bytes) (_read_bytes) +/* Data read here */ +#define MC_CMD_I2C_RW_OUT_READ_BUFFER_OFST 0 + +/* Generic phy capability bitmask */ +#define MC_CMD_PHY_CAP_10HDX_LBN 1 +#define MC_CMD_PHY_CAP_10HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_10FDX_LBN 2 +#define MC_CMD_PHY_CAP_10FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_100HDX_LBN 3 +#define MC_CMD_PHY_CAP_100HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_100FDX_LBN 4 +#define MC_CMD_PHY_CAP_100FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_1000HDX_LBN 5 +#define MC_CMD_PHY_CAP_1000HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_1000FDX_LBN 6 +#define MC_CMD_PHY_CAP_1000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_10000FDX_LBN 7 +#define MC_CMD_PHY_CAP_10000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_PAUSE_LBN 8 +#define MC_CMD_PHY_CAP_PAUSE_WIDTH 1 +#define MC_CMD_PHY_CAP_ASYM_LBN 9 +#define MC_CMD_PHY_CAP_ASYM_WIDTH 1 +#define MC_CMD_PHY_CAP_AN_LBN 10 +#define MC_CMD_PHY_CAP_AN_WIDTH 1 + +/* Generic loopback enumeration */ +#define MC_CMD_LOOPBACK_NONE 0 +#define MC_CMD_LOOPBACK_DATA 1 +#define MC_CMD_LOOPBACK_GMAC 2 +#define MC_CMD_LOOPBACK_XGMII 3 +#define MC_CMD_LOOPBACK_XGXS 4 +#define MC_CMD_LOOPBACK_XAUI 5 +#define MC_CMD_LOOPBACK_GMII 6 +#define MC_CMD_LOOPBACK_SGMII 7 +#define MC_CMD_LOOPBACK_XGBR 8 +#define MC_CMD_LOOPBACK_XFI 9 +#define MC_CMD_LOOPBACK_XAUI_FAR 10 +#define MC_CMD_LOOPBACK_GMII_FAR 11 +#define MC_CMD_LOOPBACK_SGMII_FAR 12 +#define MC_CMD_LOOPBACK_XFI_FAR 13 +#define MC_CMD_LOOPBACK_GPHY 14 +#define MC_CMD_LOOPBACK_PHYXS 15 +#define MC_CMD_LOOPBACK_PCS 16 +#define MC_CMD_LOOPBACK_PMAPMD 17 +#define MC_CMD_LOOPBACK_XPORT 18 +#define MC_CMD_LOOPBACK_XGMII_WS 19 +#define MC_CMD_LOOPBACK_XAUI_WS 20 +#define MC_CMD_LOOPBACK_XAUI_WS_FAR 21 +#define MC_CMD_LOOPBACK_XAUI_WS_NEAR 22 +#define MC_CMD_LOOPBACK_GMII_WS 23 +#define MC_CMD_LOOPBACK_XFI_WS 24 +#define MC_CMD_LOOPBACK_XFI_WS_FAR 25 +#define MC_CMD_LOOPBACK_PHYXS_WS 26 + +/* Generic PHY statistics enumeration */ +#define MC_CMD_OUI 0 +#define MC_CMD_PMA_PMD_LINK_UP 1 +#define MC_CMD_PMA_PMD_RX_FAULT 2 +#define MC_CMD_PMA_PMD_TX_FAULT 3 +#define MC_CMD_PMA_PMD_SIGNAL 4 +#define MC_CMD_PMA_PMD_SNR_A 5 +#define MC_CMD_PMA_PMD_SNR_B 6 +#define MC_CMD_PMA_PMD_SNR_C 7 +#define MC_CMD_PMA_PMD_SNR_D 8 +#define MC_CMD_PCS_LINK_UP 9 +#define MC_CMD_PCS_RX_FAULT 10 +#define MC_CMD_PCS_TX_FAULT 11 +#define MC_CMD_PCS_BER 12 +#define MC_CMD_PCS_BLOCK_ERRORS 13 +#define MC_CMD_PHYXS_LINK_UP 14 +#define MC_CMD_PHYXS_RX_FAULT 15 +#define MC_CMD_PHYXS_TX_FAULT 16 +#define MC_CMD_PHYXS_ALIGN 17 +#define MC_CMD_PHYXS_SYNC 18 +#define MC_CMD_AN_LINK_UP 19 +#define MC_CMD_AN_COMPLETE 20 +#define MC_CMD_AN_10GBT_STATUS 21 +#define MC_CMD_CL22_LINK_UP 22 +#define MC_CMD_PHY_NSTATS 23 + +/* MC_CMD_GET_PHY_CFG: + * Report PHY configuration. This guarantees to succeed even if the PHY is in + * a "zombie" state. + * + * Locks required: None + * Return code: 0 + */ +#define MC_CMD_GET_PHY_CFG 0x24 + +#define MC_CMD_GET_PHY_CFG_IN_LEN 0 +#define MC_CMD_GET_PHY_CFG_OUT_LEN 72 + +#define MC_CMD_GET_PHY_CFG_OUT_FLAGS_OFST 0 +#define MC_CMD_GET_PHY_CFG_PRESENT_LBN 0 +#define MC_CMD_GET_PHY_CFG_PRESENT_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_SHORTBIST_LBN 1 +#define MC_CMD_GET_PHY_CFG_SHORTBIST_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_LONGBIST_LBN 2 +#define MC_CMD_GET_PHY_CFG_LONGBIST_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_LOWPOWER_LBN 3 +#define MC_CMD_GET_PHY_CFG_LOWPOWER_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_POWEROFF_LBN 4 +#define MC_CMD_GET_PHY_CFG_POWEROFF_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_TXDIS_LBN 5 +#define MC_CMD_GET_PHY_CFG_TXDIS_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_TYPE_OFST 4 +/* Bitmask of supported capabilities */ +#define MC_CMD_GET_PHY_CFG_OUT_SUPPORTED_CAP_OFST 8 +#define MC_CMD_GET_PHY_CFG_OUT_CHANNEL_OFST 12 +#define MC_CMD_GET_PHY_CFG_OUT_PRT_OFST 16 +/* PHY statistics bitmap */ +#define MC_CMD_GET_PHY_CFG_OUT_STATS_MASK_OFST 20 +/* PHY type/name string */ +#define MC_CMD_GET_PHY_CFG_OUT_NAME_OFST 24 +#define MC_CMD_GET_PHY_CFG_OUT_NAME_LEN 20 +#define MC_CMD_GET_PHY_CFG_OUT_MEDIA_TYPE_OFST 44 +#define MC_CMD_MEDIA_XAUI 1 +#define MC_CMD_MEDIA_CX4 2 +#define MC_CMD_MEDIA_KX4 3 +#define MC_CMD_MEDIA_XFP 4 +#define MC_CMD_MEDIA_SFP_PLUS 5 +#define MC_CMD_MEDIA_BASE_T 6 +/* MDIO "MMDS" supported */ +#define MC_CMD_GET_PHY_CFG_OUT_MMD_MASK_OFST 48 +/* Native clause 22 */ +#define MC_CMD_MMD_CLAUSE22 0 +#define MC_CMD_MMD_CLAUSE45_PMAPMD 1 +#define MC_CMD_MMD_CLAUSE45_WIS 2 +#define MC_CMD_MMD_CLAUSE45_PCS 3 +#define MC_CMD_MMD_CLAUSE45_PHYXS 4 +#define MC_CMD_MMD_CLAUSE45_DTEXS 5 +#define MC_CMD_MMD_CLAUSE45_TC 6 +#define MC_CMD_MMD_CLAUSE45_AN 7 +/* Clause22 proxied over clause45 by PHY */ +#define MC_CMD_MMD_CLAUSE45_C22EXT 29 +#define MC_CMD_MMD_CLAUSE45_VEND1 30 +#define MC_CMD_MMD_CLAUSE45_VEND2 31 +/* PHY stepping version */ +#define MC_CMD_GET_PHY_CFG_OUT_REVISION_OFST 52 +#define MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN 20 + +/* MC_CMD_START_PHY_BIST: + * Start a BIST test on the PHY. + * + * Locks required: PHY_LOCK if doing a PHY BIST + * Return code: 0, EINVAL, EACCES (if PHY_LOCK is not held) + */ +#define MC_CMD_START_BIST 0x25 +#define MC_CMD_START_BIST_IN_LEN 4 +#define MC_CMD_START_BIST_TYPE_OFST 0 + +/* Run the PHY's short BIST */ +#define MC_CMD_PHY_BIST_SHORT 1 +/* Run the PHY's long BIST */ +#define MC_CMD_PHY_BIST_LONG 2 +/* Run BIST on the currently selected BPX Serdes (XAUI or XFI) */ +#define MC_CMD_BPX_SERDES_BIST 3 + +/* MC_CMD_POLL_PHY_BIST: (variadic output) + * Poll for BIST completion + * + * Returns a single status code, and a binary blob of phy-specific + * bist output. If the driver can't succesfully parse the BIST output, + * it should still respect the Pass/Fail in OUT.RESULT. + * + * Locks required: PHY_LOCK if doing a PHY BIST + * Return code: 0, EACCES (if PHY_LOCK is not held) + */ +#define MC_CMD_POLL_BIST 0x26 +#define MC_CMD_POLL_BIST_IN_LEN 0 +#define MC_CMD_POLL_BIST_OUT_LEN UNKNOWN +#define MC_CMD_POLL_BIST_OUT_RESULT_OFST 0 +#define MC_CMD_POLL_BIST_RUNNING 1 +#define MC_CMD_POLL_BIST_PASSED 2 +#define MC_CMD_POLL_BIST_FAILED 3 +#define MC_CMD_POLL_BIST_TIMEOUT 4 +#define MC_CMD_POLL_BIST_OUT_PRIVATE_OFST 4 + +/* MC_CMD_PHY_SPI: (variadic in, variadic out) + * Read/Write/Erase the PHY SPI device + * + * Locks required: PHY_LOCK + * Return code: 0, ETIME, EINVAL, EACCES (if PHY_LOCK is not held) + */ +#define MC_CMD_PHY_SPI 0x27 +#define MC_CMD_PHY_SPI_IN_LEN(_write_bytes) (12 + (_write_bytes)) +#define MC_CMD_PHY_SPI_IN_ARGS_OFST 0 +#define MC_CMD_PHY_SPI_IN_ARGS_ADDR_OFST 0 +#define MC_CMD_PHY_SPI_IN_ARGS_READ_BYTES_OFST 4 +#define MC_CMD_PHY_SPI_IN_ARGS_ERASE_ALL_OFST 8 +/* Data to write here */ +#define MC_CMD_PHY_SPI_IN_WRITE_BUFFER_OFSET 12 +#define MC_CMD_PHY_SPI_OUT_LEN(_read_bytes) (_read_bytes) +/* Data read here */ +#define MC_CMD_PHY_SPI_OUT_READ_BUFFER_OFST 0 + + +/* MC_CMD_GET_LOOPBACK_MODES: + * Returns a bitmask of loopback modes evailable at each speed. + * + * Locks required: None + * Return code: 0 + */ +#define MC_CMD_GET_LOOPBACK_MODES 0x28 +#define MC_CMD_GET_LOOPBACK_MODES_IN_LEN 0 +#define MC_CMD_GET_LOOPBACK_MODES_OUT_LEN 32 +#define MC_CMD_GET_LOOPBACK_MODES_100M_OFST 0 +#define MC_CMD_GET_LOOPBACK_MODES_1G_OFST 8 +#define MC_CMD_GET_LOOPBACK_MODES_10G_OFST 16 +#define MC_CMD_GET_LOOPBACK_MODES_SUGGESTED_OFST 24 + +/* Flow control enumeration */ +#define MC_CMD_FCNTL_OFF 0 +#define MC_CMD_FCNTL_RESPOND 1 +#define MC_CMD_FCNTL_BIDIR 2 +/* Auto - Use what the link has autonegotiated + * - The driver should modify the advertised capabilities via SET_LINK.CAP + * to control the negotiated flow control mode. + * - Can only be set if the PHY supports PAUSE+ASYM capabilities + * - Never returned by GET_LINK as the value programmed into the MAC + */ +#define MC_CMD_FCNTL_AUTO 3 + +/* Generic mac fault bitmask */ +#define MC_CMD_MAC_FAULT_XGMII_LOCAL_LBN 0 +#define MC_CMD_MAC_FAULT_XGMII_LOCAL_WIDTH 1 +#define MC_CMD_MAC_FAULT_XGMII_REMOTE_LBN 1 +#define MC_CMD_MAC_FAULT_XGMII_REMOTE_WIDTH 1 +#define MC_CMD_MAC_FAULT_SGMII_REMOTE_LBN 2 +#define MC_CMD_MAC_FAULT_SGMII_REMOTE_WIDTH 1 + +/* MC_CMD_GET_LINK: + * Read the unified MAC/PHY link state + * + * Locks required: None + * Return code: 0, ETIME + */ +#define MC_CMD_GET_LINK 0x29 +#define MC_CMD_GET_LINK_IN_LEN 0 +#define MC_CMD_GET_LINK_OUT_LEN 28 +/* near-side and link-partner advertised capabilities */ +#define MC_CMD_GET_LINK_OUT_CAP_OFST 0 +#define MC_CMD_GET_LINK_OUT_LP_CAP_OFST 4 +/* Autonegotiated speed in mbit/s. The link may still be down + * even if this reads non-zero */ +#define MC_CMD_GET_LINK_OUT_LINK_SPEED_OFST 8 +#define MC_CMD_GET_LINK_OUT_LOOPBACK_MODE_OFST 12 +#define MC_CMD_GET_LINK_OUT_FLAGS_OFST 16 +/* Whether we have overall link up */ +#define MC_CMD_GET_LINK_LINK_UP_LBN 0 +#define MC_CMD_GET_LINK_LINK_UP_WIDTH 1 +#define MC_CMD_GET_LINK_FULL_DUPLEX_LBN 1 +#define MC_CMD_GET_LINK_FULL_DUPLEX_WIDTH 1 +/* Whether we have link at the layers provided by the BPX */ +#define MC_CMD_GET_LINK_BPX_LINK_LBN 2 +#define MC_CMD_GET_LINK_BPX_LINK_WIDTH 1 +/* Whether the PHY has external link */ +#define MC_CMD_GET_LINK_PHY_LINK_LBN 3 +#define MC_CMD_GET_LINK_PHY_LINK_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_FCNTL_OFST 20 +#define MC_CMD_GET_LINK_OUT_MAC_FAULT_OFST 24 + +/* MC_CMD_SET_LINK: + * Write the unified MAC/PHY link configuration + * + * A loopback speed of "0" is supported, and means + * (choose any available speed) + * + * Locks required: None + * Return code: 0, EINVAL, ETIME + */ +#define MC_CMD_SET_LINK 0x2a +#define MC_CMD_SET_LINK_IN_LEN 16 +#define MC_CMD_SET_LINK_IN_CAP_OFST 0 +#define MC_CMD_SET_LINK_IN_FLAGS_OFST 4 +#define MC_CMD_SET_LINK_LOWPOWER_LBN 0 +#define MC_CMD_SET_LINK_LOWPOWER_WIDTH 1 +#define MC_CMD_SET_LINK_POWEROFF_LBN 1 +#define MC_CMD_SET_LINK_POWEROFF_WIDTH 1 +#define MC_CMD_SET_LINK_TXDIS_LBN 2 +#define MC_CMD_SET_LINK_TXDIS_WIDTH 1 +#define MC_CMD_SET_LINK_IN_LOOPBACK_MODE_OFST 8 +#define MC_CMD_SET_LINK_IN_LOOPBACK_SPEED_OFST 12 +#define MC_CMD_SET_LINK_OUT_LEN 0 + +/* MC_CMD_SET_ID_LED: + * Set indentification LED state + * + * Locks required: None + * Return code: 0, EINVAL + */ +#define MC_CMD_SET_ID_LED 0x2b +#define MC_CMD_SET_ID_LED_IN_LEN 4 +#define MC_CMD_SET_ID_LED_IN_STATE_OFST 0 +#define MC_CMD_LED_OFF 0 +#define MC_CMD_LED_ON 1 +#define MC_CMD_LED_DEFAULT 2 +#define MC_CMD_SET_ID_LED_OUT_LEN 0 + +/* MC_CMD_SET_MAC: + * Set MAC configuration + * + * The MTU is the MTU programmed directly into the XMAC/GMAC + * (inclusive of EtherII, VLAN, bug16011 padding) + * + * Locks required: None + * Return code: 0, EINVAL + */ +#define MC_CMD_SET_MAC 0x2c +#define MC_CMD_SET_MAC_IN_LEN 24 +#define MC_CMD_SET_MAC_IN_MTU_OFST 0 +#define MC_CMD_SET_MAC_IN_DRAIN_OFST 4 +#define MC_CMD_SET_MAC_IN_ADDR_OFST 8 +#define MC_CMD_SET_MAC_IN_REJECT_OFST 16 +#define MC_CMD_SET_MAC_IN_REJECT_UNCST_LBN 0 +#define MC_CMD_SET_MAC_IN_REJECT_UNCST_WIDTH 1 +#define MC_CMD_SET_MAC_IN_REJECT_BRDCST_LBN 1 +#define MC_CMD_SET_MAC_IN_REJECT_BRDCST_WIDTH 1 +#define MC_CMD_SET_MAC_IN_FCNTL_OFST 20 +#define MC_CMD_SET_MAC_OUT_LEN 0 + +/* MC_CMD_PHY_STATS: + * Get generic PHY statistics + * + * This call returns the statistics for a generic PHY, by direct DMA + * into host memory, in a sparse array (indexed by the enumerate). + * Each value is represented by a 32bit number. + * + * Locks required: None + * Returns: 0, ETIME + * Response methods: shared memory, event + */ +#define MC_CMD_PHY_STATS 0x2d +#define MC_CMD_PHY_STATS_IN_LEN 8 +#define MC_CMD_PHY_STATS_IN_DMA_ADDR_LO_OFST 0 +#define MC_CMD_PHY_STATS_IN_DMA_ADDR_HI_OFST 4 +#define MC_CMD_PHY_STATS_OUT_LEN 0 + +/* Unified MAC statistics enumeration */ +#define MC_CMD_MAC_GENERATION_START 0 +#define MC_CMD_MAC_TX_PKTS 1 +#define MC_CMD_MAC_TX_PAUSE_PKTS 2 +#define MC_CMD_MAC_TX_CONTROL_PKTS 3 +#define MC_CMD_MAC_TX_UNICAST_PKTS 4 +#define MC_CMD_MAC_TX_MULTICAST_PKTS 5 +#define MC_CMD_MAC_TX_BROADCAST_PKTS 6 +#define MC_CMD_MAC_TX_BYTES 7 +#define MC_CMD_MAC_TX_BAD_BYTES 8 +#define MC_CMD_MAC_TX_LT64_PKTS 9 +#define MC_CMD_MAC_TX_64_PKTS 10 +#define MC_CMD_MAC_TX_65_TO_127_PKTS 11 +#define MC_CMD_MAC_TX_128_TO_255_PKTS 12 +#define MC_CMD_MAC_TX_256_TO_511_PKTS 13 +#define MC_CMD_MAC_TX_512_TO_1023_PKTS 14 +#define MC_CMD_MAC_TX_1024_TO_15XX_PKTS 15 +#define MC_CMD_MAC_TX_15XX_TO_JUMBO_PKTS 16 +#define MC_CMD_MAC_TX_GTJUMBO_PKTS 17 +#define MC_CMD_MAC_TX_BAD_FCS_PKTS 18 +#define MC_CMD_MAC_TX_SINGLE_COLLISION_PKTS 19 +#define MC_CMD_MAC_TX_MULTIPLE_COLLISION_PKTS 20 +#define MC_CMD_MAC_TX_EXCESSIVE_COLLISION_PKTS 21 +#define MC_CMD_MAC_TX_LATE_COLLISION_PKTS 22 +#define MC_CMD_MAC_TX_DEFERRED_PKTS 23 +#define MC_CMD_MAC_TX_EXCESSIVE_DEFERRED_PKTS 24 +#define MC_CMD_MAC_TX_NON_TCPUDP_PKTS 25 +#define MC_CMD_MAC_TX_MAC_SRC_ERR_PKTS 26 +#define MC_CMD_MAC_TX_IP_SRC_ERR_PKTS 27 +#define MC_CMD_MAC_RX_PKTS 28 +#define MC_CMD_MAC_RX_PAUSE_PKTS 29 +#define MC_CMD_MAC_RX_GOOD_PKTS 30 +#define MC_CMD_MAC_RX_CONTROL_PKTS 31 +#define MC_CMD_MAC_RX_UNICAST_PKTS 32 +#define MC_CMD_MAC_RX_MULTICAST_PKTS 33 +#define MC_CMD_MAC_RX_BROADCAST_PKTS 34 +#define MC_CMD_MAC_RX_BYTES 35 +#define MC_CMD_MAC_RX_BAD_BYTES 36 +#define MC_CMD_MAC_RX_64_PKTS 37 +#define MC_CMD_MAC_RX_65_TO_127_PKTS 38 +#define MC_CMD_MAC_RX_128_TO_255_PKTS 39 +#define MC_CMD_MAC_RX_256_TO_511_PKTS 40 +#define MC_CMD_MAC_RX_512_TO_1023_PKTS 41 +#define MC_CMD_MAC_RX_1024_TO_15XX_PKTS 42 +#define MC_CMD_MAC_RX_15XX_TO_JUMBO_PKTS 43 +#define MC_CMD_MAC_RX_GTJUMBO_PKTS 44 +#define MC_CMD_MAC_RX_UNDERSIZE_PKTS 45 +#define MC_CMD_MAC_RX_BAD_FCS_PKTS 46 +#define MC_CMD_MAC_RX_OVERFLOW_PKTS 47 +#define MC_CMD_MAC_RX_FALSE_CARRIER_PKTS 48 +#define MC_CMD_MAC_RX_SYMBOL_ERROR_PKTS 49 +#define MC_CMD_MAC_RX_ALIGN_ERROR_PKTS 50 +#define MC_CMD_MAC_RX_LENGTH_ERROR_PKTS 51 +#define MC_CMD_MAC_RX_INTERNAL_ERROR_PKTS 52 +#define MC_CMD_MAC_RX_JABBER_PKTS 53 +#define MC_CMD_MAC_RX_NODESC_DROPS 54 +#define MC_CMD_MAC_RX_LANES01_CHAR_ERR 55 +#define MC_CMD_MAC_RX_LANES23_CHAR_ERR 56 +#define MC_CMD_MAC_RX_LANES01_DISP_ERR 57 +#define MC_CMD_MAC_RX_LANES23_DISP_ERR 58 +#define MC_CMD_MAC_RX_MATCH_FAULT 59 +/* Insert new members here. */ +#define MC_CMD_MAC_GENERATION_END 60 +#define MC_CMD_MAC_NSTATS (MC_CMD_MAC_GENERATION_END+1) + +/* MC_CMD_MAC_STATS: + * Get unified GMAC/XMAC statistics + * + * This call returns unified statistics maintained by the MC as it + * switches between the GMAC and XMAC. The MC will write out all + * supported stats. The driver should zero initialise the buffer to + * guarantee consistent results. + * + * Locks required: None + * Returns: 0 + * Response methods: shared memory, event + */ +#define MC_CMD_MAC_STATS 0x2e +#define MC_CMD_MAC_STATS_IN_LEN 16 +#define MC_CMD_MAC_STATS_IN_DMA_ADDR_LO_OFST 0 +#define MC_CMD_MAC_STATS_IN_DMA_ADDR_HI_OFST 4 +#define MC_CMD_MAC_STATS_IN_CMD_OFST 8 +#define MC_CMD_MAC_STATS_CMD_DMA_LBN 0 +#define MC_CMD_MAC_STATS_CMD_DMA_WIDTH 1 +#define MC_CMD_MAC_STATS_CMD_CLEAR_LBN 1 +#define MC_CMD_MAC_STATS_CMD_CLEAR_WIDTH 1 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE_LBN 2 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE_WIDTH 1 +/* Fields only relevent when PERIODIC_CHANGE is set */ +#define MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE_LBN 3 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE_WIDTH 1 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR_LBN 4 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR_WIDTH 1 +#define MC_CMD_MAC_STATS_CMD_PERIOD_MS_LBN 16 +#define MC_CMD_MAC_STATS_CMD_PERIOD_MS_WIDTH 16 +#define MC_CMD_MAC_STATS_IN_DMA_LEN_OFST 12 + +#define MC_CMD_MAC_STATS_OUT_LEN 0 + +/* Callisto flags */ +#define MC_CMD_SFT9001_ROBUST_LBN 0 +#define MC_CMD_SFT9001_ROBUST_WIDTH 1 +#define MC_CMD_SFT9001_SHORT_REACH_LBN 1 +#define MC_CMD_SFT9001_SHORT_REACH_WIDTH 1 + +/* MC_CMD_SFT9001_GET: + * Read current callisto specific setting + * + * Locks required: None + * Returns: 0, ETIME + */ +#define MC_CMD_SFT9001_GET 0x30 +#define MC_CMD_SFT9001_GET_IN_LEN 0 +#define MC_CMD_SFT9001_GET_OUT_LEN 4 +#define MC_CMD_SFT9001_GET_OUT_FLAGS_OFST 0 + +/* MC_CMD_SFT9001_SET: + * Write current callisto specific setting + * + * Locks required: None + * Returns: 0, ETIME, EINVAL + */ +#define MC_CMD_SFT9001_SET 0x31 +#define MC_CMD_SFT9001_SET_IN_LEN 4 +#define MC_CMD_SFT9001_SET_IN_FLAGS_OFST 0 +#define MC_CMD_SFT9001_SET_OUT_LEN 0 + + +/* MC_CMD_WOL_FILTER_SET: + * Set a WoL filter + * + * Locks required: None + * Returns: 0, EBUSY, EINVAL, ENOSYS + */ +#define MC_CMD_WOL_FILTER_SET 0x32 +#define MC_CMD_WOL_FILTER_SET_IN_LEN 192 /* 190 rounded up to a word */ +#define MC_CMD_WOL_FILTER_SET_IN_FILTER_MODE_OFST 0 +#define MC_CMD_WOL_FILTER_SET_IN_WOL_TYPE_OFST 4 + +/* There is a union at offset 8, following defines overlap due to + * this */ +#define MC_CMD_WOL_FILTER_SET_IN_DATA_OFST 8 + +#define MC_CMD_WOL_FILTER_SET_IN_MAGIC_MAC_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + +#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_SRC_IP_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST +#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_DST_IP_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 4) +#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_SRC_PORT_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 8) +#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_DST_PORT_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 10) + +#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_SRC_IP_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST +#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_DST_IP_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 16) +#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_SRC_PORT_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 32) +#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_DST_PORT_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 34) + +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_MASK_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 48) +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_LEN_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 176) +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_LAYER3_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 177) +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_LAYER4_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 178) + +#define MC_CMD_WOL_FILTER_SET_OUT_LEN 4 +#define MC_CMD_WOL_FILTER_SET_OUT_FILTER_ID_OFST 0 + +/* WOL Filter types enumeration */ +#define MC_CMD_WOL_TYPE_MAGIC 0x0 + /* unused 0x1 */ +#define MC_CMD_WOL_TYPE_WIN_MAGIC 0x2 +#define MC_CMD_WOL_TYPE_IPV4_SYN 0x3 +#define MC_CMD_WOL_TYPE_IPV6_SYN 0x4 +#define MC_CMD_WOL_TYPE_BITMAP 0x5 +#define MC_CMD_WOL_TYPE_MAX 0x6 + +#define MC_CMD_FILTER_MODE_SIMPLE 0x0 +#define MC_CMD_FILTER_MODE_STRUCTURED 0xffffffff + +/* MC_CMD_WOL_FILTER_REMOVE: + * Remove a WoL filter + * + * Locks required: None + * Returns: 0, EINVAL, ENOSYS + */ +#define MC_CMD_WOL_FILTER_REMOVE 0x33 +#define MC_CMD_WOL_FILTER_REMOVE_IN_LEN 4 +#define MC_CMD_WOL_FILTER_REMOVE_IN_FILTER_ID_OFST 0 +#define MC_CMD_WOL_FILTER_REMOVE_OUT_LEN 0 + + +/* MC_CMD_WOL_FILTER_RESET: + * Reset (i.e. remove all) WoL filters + * + * Locks required: None + * Returns: 0, ENOSYS + */ +#define MC_CMD_WOL_FILTER_RESET 0x34 +#define MC_CMD_WOL_FILTER_RESET_IN_LEN 0 +#define MC_CMD_WOL_FILTER_RESET_OUT_LEN 0 + +/* MC_CMD_SET_MCAST_HASH: + * Set the MCASH hash value without otherwise + * reconfiguring the MAC + */ +#define MC_CMD_SET_MCAST_HASH 0x35 +#define MC_CMD_SET_MCAST_HASH_IN_LEN 32 +#define MC_CMD_SET_MCAST_HASH_IN_HASH0_OFST 0 +#define MC_CMD_SET_MCAST_HASH_IN_HASH1_OFST 16 +#define MC_CMD_SET_MCAST_HASH_OUT_LEN 0 + +/* MC_CMD_NVRAM_TYPES: + * Return bitfield indicating available types of virtual NVRAM partitions + * + * Locks required: none + * Returns: 0 + */ +#define MC_CMD_NVRAM_TYPES 0x36 +#define MC_CMD_NVRAM_TYPES_IN_LEN 0 +#define MC_CMD_NVRAM_TYPES_OUT_LEN 4 +#define MC_CMD_NVRAM_TYPES_OUT_TYPES_OFST 0 + +/* Supported NVRAM types */ +#define MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO 0 +#define MC_CMD_NVRAM_TYPE_MC_FW 1 +#define MC_CMD_NVRAM_TYPE_MC_FW_BACKUP 2 +#define MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0 3 +#define MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1 4 +#define MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 5 +#define MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1 6 +#define MC_CMD_NVRAM_TYPE_EXP_ROM 7 +#define MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0 8 +#define MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1 9 +#define MC_CMD_NVRAM_TYPE_PHY_PORT0 10 +#define MC_CMD_NVRAM_TYPE_PHY_PORT1 11 +#define MC_CMD_NVRAM_TYPE_LOG 12 + +/* MC_CMD_NVRAM_INFO: + * Read info about a virtual NVRAM partition + * + * Locks required: none + * Returns: 0, EINVAL (bad type) + */ +#define MC_CMD_NVRAM_INFO 0x37 +#define MC_CMD_NVRAM_INFO_IN_LEN 4 +#define MC_CMD_NVRAM_INFO_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_INFO_OUT_LEN 24 +#define MC_CMD_NVRAM_INFO_OUT_TYPE_OFST 0 +#define MC_CMD_NVRAM_INFO_OUT_SIZE_OFST 4 +#define MC_CMD_NVRAM_INFO_OUT_ERASESIZE_OFST 8 +#define MC_CMD_NVRAM_INFO_OUT_FLAGS_OFST 12 +#define MC_CMD_NVRAM_PROTECTED_LBN 0 +#define MC_CMD_NVRAM_PROTECTED_WIDTH 1 +#define MC_CMD_NVRAM_INFO_OUT_PHYSDEV_OFST 16 +#define MC_CMD_NVRAM_INFO_OUT_PHYSADDR_OFST 20 + +/* MC_CMD_NVRAM_UPDATE_START: + * Start a group of update operations on a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_UPDATE_START 0x38 +#define MC_CMD_NVRAM_UPDATE_START_IN_LEN 4 +#define MC_CMD_NVRAM_UPDATE_START_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_UPDATE_START_OUT_LEN 0 + +/* MC_CMD_NVRAM_READ: + * Read data from a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_READ 0x39 +#define MC_CMD_NVRAM_READ_IN_LEN 12 +#define MC_CMD_NVRAM_READ_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_READ_IN_OFFSET_OFST 4 +#define MC_CMD_NVRAM_READ_IN_LENGTH_OFST 8 +#define MC_CMD_NVRAM_READ_OUT_LEN(_read_bytes) (_read_bytes) +#define MC_CMD_NVRAM_READ_OUT_READ_BUFFER_OFST 0 + +/* MC_CMD_NVRAM_WRITE: + * Write data to a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_WRITE 0x3a +#define MC_CMD_NVRAM_WRITE_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_WRITE_IN_OFFSET_OFST 4 +#define MC_CMD_NVRAM_WRITE_IN_LENGTH_OFST 8 +#define MC_CMD_NVRAM_WRITE_IN_WRITE_BUFFER_OFST 12 +#define MC_CMD_NVRAM_WRITE_IN_LEN(_write_bytes) (12 + _write_bytes) +#define MC_CMD_NVRAM_WRITE_OUT_LEN 0 + +/* MC_CMD_NVRAM_ERASE: + * Erase sector(s) from a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_ERASE 0x3b +#define MC_CMD_NVRAM_ERASE_IN_LEN 12 +#define MC_CMD_NVRAM_ERASE_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_ERASE_IN_OFFSET_OFST 4 +#define MC_CMD_NVRAM_ERASE_IN_LENGTH_OFST 8 +#define MC_CMD_NVRAM_ERASE_OUT_LEN 0 + +/* MC_CMD_NVRAM_UPDATE_FINISH: + * Finish a group of update operations on a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_UPDATE_FINISH 0x3c +#define MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN 4 +#define MC_CMD_NVRAM_UPDATE_FINISH_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN 0 + +/* MC_CMD_REBOOT: + * Reboot the MC. The AFTER_ASSERTION flag is intended to be used + * when the driver notices an assertion failure, to allow two ports to + * both recover (semi-)gracefully. + * + * Locks required: NONE + * Returns: Nothing. You get back a response with ERR=1, DATALEN=0 + */ +#define MC_CMD_REBOOT 0x3d +#define MC_CMD_REBOOT_IN_LEN 4 +#define MC_CMD_REBOOT_IN_FLAGS_OFST 0 +#define MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION 1 +#define MC_CMD_REBOOT_OUT_LEN 0 + +/* MC_CMD_SCHEDINFO: + * Request scheduler info. from the MC. + * + * Locks required: NONE + * Returns: An array of (timeslice,maximum overrun), one for each thread, + * in ascending order of thread address.s + */ +#define MC_CMD_SCHEDINFO 0x3e +#define MC_CMD_SCHEDINFO_IN_LEN 0 + + +/* MC_CMD_SET_REBOOT_MODE: (debug) + * Set the mode for the next MC reboot. + * + * Locks required: NONE + * + * Sets the reboot mode to the specified value. Returns the old mode. + */ +#define MC_CMD_REBOOT_MODE 0x3f +#define MC_CMD_REBOOT_MODE_IN_LEN 4 +#define MC_CMD_REBOOT_MODE_IN_VALUE_OFST 0 +#define MC_CMD_REBOOT_MODE_OUT_LEN 4 +#define MC_CMD_REBOOT_MODE_OUT_VALUE_OFST 0 +#define MC_CMD_REBOOT_MODE_NORMAL 0 +#define MC_CMD_REBOOT_MODE_SNAPPER 3 + +/* MC_CMD_DEBUG_LOG: + * Null request/response command (debug) + * - sequence number is always zero + * - only supported on the UART interface + * (the same set of bytes is delivered as an + * event over PCI) + */ +#define MC_CMD_DEBUG_LOG 0x40 +#define MC_CMD_DEBUG_LOG_IN_LEN 0 +#define MC_CMD_DEBUG_LOG_OUT_LEN 0 + +/* Generic sensor enumeration. Note that a dual port NIC + * will EITHER expose PHY_COMMON_TEMP OR PHY0_TEMP and + * PHY1_TEMP depending on whether there is a single sensor + * in the vicinity of the two port, or one per port. + */ +#define MC_CMD_SENSOR_CONTROLLER_TEMP 0 /* degC */ +#define MC_CMD_SENSOR_PHY_COMMON_TEMP 1 /* degC */ +#define MC_CMD_SENSOR_CONTROLLER_COOLING 2 /* bool */ +#define MC_CMD_SENSOR_PHY0_TEMP 3 /* degC */ +#define MC_CMD_SENSOR_PHY0_COOLING 4 /* bool */ +#define MC_CMD_SENSOR_PHY1_TEMP 5 /* degC */ +#define MC_CMD_SENSOR_PHY1_COOLING 6 /* bool */ +#define MC_CMD_SENSOR_IN_1V0 7 /* mV */ +#define MC_CMD_SENSOR_IN_1V2 8 /* mV */ +#define MC_CMD_SENSOR_IN_1V8 9 /* mV */ +#define MC_CMD_SENSOR_IN_2V5 10 /* mV */ +#define MC_CMD_SENSOR_IN_3V3 11 /* mV */ +#define MC_CMD_SENSOR_IN_12V0 12 /* mV */ + + +/* Sensor state */ +#define MC_CMD_SENSOR_STATE_OK 0 +#define MC_CMD_SENSOR_STATE_WARNING 1 +#define MC_CMD_SENSOR_STATE_FATAL 2 +#define MC_CMD_SENSOR_STATE_BROKEN 3 + +/* MC_CMD_SENSOR_INFO: + * Returns information about every available sensor. + * + * Each sensor has a single (16bit) value, and a corresponding state. + * The mapping between value and sensor is nominally determined by the + * MC, but in practise is implemented as zero (BROKEN), one (TEMPERATURE), + * or two (VOLTAGE) ranges per sensor per state. + * + * This call returns a mask (32bit) of the sensors that are supported + * by this platform, then an array (indexed by MC_CMD_SENSOR) of byte + * offsets to the per-sensor arrays. Each sensor array has four 16bit + * numbers, min1, max1, min2, max2. + * + * Locks required: None + * Returns: 0 + */ +#define MC_CMD_SENSOR_INFO 0x41 +#define MC_CMD_SENSOR_INFO_IN_LEN 0 +#define MC_CMD_SENSOR_INFO_OUT_MASK_OFST 0 +#define MC_CMD_SENSOR_INFO_OUT_OFFSET_OFST(_x) \ + (4 + (_x)) +#define MC_CMD_SENSOR_INFO_OUT_MIN1_OFST(_ofst) \ + ((_ofst) + 0) +#define MC_CMD_SENSOR_INFO_OUT_MAX1_OFST(_ofst) \ + ((_ofst) + 2) +#define MC_CMD_SENSOR_INFO_OUT_MIN2_OFST(_ofst) \ + ((_ofst) + 4) +#define MC_CMD_SENSOR_INFO_OUT_MAX2_OFST(_ofst) \ + ((_ofst) + 6) + +/* MC_CMD_READ_SENSORS + * Returns the current (value, state) for each sensor + * + * Returns the current (value, state) [each 16bit] of each sensor supported by + * this board, by DMA'ing a sparse array (indexed by the sensor type) into host + * memory. + * + * The MC will send a SENSOREVT event every time any sensor changes state. The + * driver is responsible for ensuring that it doesn't miss any events. The board + * will function normally if all sensors are in STATE_OK or state_WARNING. + * Otherwise the board should not be expected to function. + */ +#define MC_CMD_READ_SENSORS 0x42 +#define MC_CMD_READ_SENSORS_IN_LEN 8 +#define MC_CMD_READ_SENSORS_IN_DMA_ADDR_LO_OFST 0 +#define MC_CMD_READ_SENSORS_IN_DMA_ADDR_HI_OFST 4 +#define MC_CMD_READ_SENSORS_OUT_LEN 0 + + +/* MC_CMD_GET_PHY_STATE: + * Report current state of PHY. A "zombie" PHY is a PHY that has failed to + * boot (e.g. due to missing or corrupted firmware). + * + * Locks required: None + * Return code: 0 + */ +#define MC_CMD_GET_PHY_STATE 0x43 + +#define MC_CMD_GET_PHY_STATE_IN_LEN 0 +#define MC_CMD_GET_PHY_STATE_OUT_LEN 4 +#define MC_CMD_GET_PHY_STATE_STATE_OFST 0 +/* PHY state enumeration: */ +#define MC_CMD_PHY_STATE_OK 1 +#define MC_CMD_PHY_STATE_ZOMBIE 2 + + +/* 802.1Qbb control. 8 Tx queues that map to priorities 0 - 7. Use all 1s to + * disable 802.Qbb for a given priority. */ +#define MC_CMD_SETUP_8021QBB 0x44 +#define MC_CMD_SETUP_8021QBB_IN_LEN 32 +#define MC_CMD_SETUP_8021QBB_OUT_LEN 0 +#define MC_CMD_SETUP_8021QBB_IN_TXQS_OFFST 0 + + +/* MC_CMD_WOL_FILTER_GET: + * Retrieve ID of any WoL filters + * + * Locks required: None + * Returns: 0, ENOSYS + */ +#define MC_CMD_WOL_FILTER_GET 0x45 +#define MC_CMD_WOL_FILTER_GET_IN_LEN 0 +#define MC_CMD_WOL_FILTER_GET_OUT_LEN 4 +#define MC_CMD_WOL_FILTER_GET_OUT_FILTER_ID_OFST 0 + + +/* MC_CMD_ADD_LIGHTSOUT_OFFLOAD: + * Offload a protocol to NIC for lights-out state + * + * Locks required: None + * Returns: 0, ENOSYS + */ +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD 0x46 + +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_LEN 16 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL_OFST 0 + +/* There is a union at offset 4, following defines overlap due to + * this */ +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_DATA_OFST 4 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARPMAC_OFST 4 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARPIP_OFST 10 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NSMAC_OFST 4 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NSSNIPV6_OFST 10 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NSIPV6_OFST 26 + +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN 4 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_FILTER_ID_OFST 0 + + +/* MC_CMD_REMOVE_LIGHTSOUT_PROTOCOL_OFFLOAD: + * Offload a protocol to NIC for lights-out state + * + * Locks required: None + * Returns: 0, ENOSYS + */ +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD 0x47 +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN 8 +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN 0 + +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL_OFST 0 +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_FILTER_ID_OFST 4 + +/* Lights-out offload protocols enumeration */ +#define MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP 0x1 +#define MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS 0x2 + + +/* MC_CMD_MAC_RESET_RESTORE: + * Restore MAC after block reset + * + * Locks required: None + * Returns: 0 + */ + +#define MC_CMD_MAC_RESET_RESTORE 0x48 +#define MC_CMD_MAC_RESET_RESTORE_IN_LEN 0 +#define MC_CMD_MAC_RESET_RESTORE_OUT_LEN 0 + +#endif /* MCDI_PCOL_H */ diff --git a/drivers/net/sfc/mcdi_phy.c b/drivers/net/sfc/mcdi_phy.c new file mode 100644 index 0000000..0e1bcc5 --- /dev/null +++ b/drivers/net/sfc/mcdi_phy.c @@ -0,0 +1,597 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +/* + * Driver for PHY related operations via MCDI. + */ + +#include "efx.h" +#include "phy.h" +#include "mcdi.h" +#include "mcdi_pcol.h" +#include "mdio_10g.h" + +struct efx_mcdi_phy_cfg { + u32 flags; + u32 type; + u32 supported_cap; + u32 channel; + u32 port; + u32 stats_mask; + u8 name[20]; + u32 media; + u32 mmd_mask; + u8 revision[20]; + u32 forced_cap; +}; + +static int +efx_mcdi_get_phy_cfg(struct efx_nic *efx, struct efx_mcdi_phy_cfg *cfg) +{ + u8 outbuf[MC_CMD_GET_PHY_CFG_OUT_LEN]; + size_t outlen; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_IN_LEN != 0); + BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_OUT_NAME_LEN != sizeof(cfg->name)); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_CFG, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_GET_PHY_CFG_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + cfg->flags = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_FLAGS); + cfg->type = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_TYPE); + cfg->supported_cap = + MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_SUPPORTED_CAP); + cfg->channel = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_CHANNEL); + cfg->port = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_PRT); + cfg->stats_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_STATS_MASK); + memcpy(cfg->name, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_NAME), + sizeof(cfg->name)); + cfg->media = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MEDIA_TYPE); + cfg->mmd_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MMD_MASK); + memcpy(cfg->revision, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_REVISION), + sizeof(cfg->revision)); + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +static int efx_mcdi_set_link(struct efx_nic *efx, u32 capabilities, + u32 flags, u32 loopback_mode, + u32 loopback_speed) +{ + u8 inbuf[MC_CMD_SET_LINK_IN_LEN]; + int rc; + + BUILD_BUG_ON(MC_CMD_SET_LINK_OUT_LEN != 0); + + MCDI_SET_DWORD(inbuf, SET_LINK_IN_CAP, capabilities); + MCDI_SET_DWORD(inbuf, SET_LINK_IN_FLAGS, flags); + MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_MODE, loopback_mode); + MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_SPEED, loopback_speed); + + rc = efx_mcdi_rpc(efx, MC_CMD_SET_LINK, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +static int efx_mcdi_loopback_modes(struct efx_nic *efx, u64 *loopback_modes) +{ + u8 outbuf[MC_CMD_GET_LOOPBACK_MODES_OUT_LEN]; + size_t outlen; + int rc; + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LOOPBACK_MODES, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_GET_LOOPBACK_MODES_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + *loopback_modes = MCDI_QWORD(outbuf, GET_LOOPBACK_MODES_SUGGESTED); + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_mdio_read(struct efx_nic *efx, unsigned int bus, + unsigned int prtad, unsigned int devad, u16 addr, + u16 *value_out, u32 *status_out) +{ + u8 inbuf[MC_CMD_MDIO_READ_IN_LEN]; + u8 outbuf[MC_CMD_MDIO_READ_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, MDIO_READ_IN_BUS, bus); + MCDI_SET_DWORD(inbuf, MDIO_READ_IN_PRTAD, prtad); + MCDI_SET_DWORD(inbuf, MDIO_READ_IN_DEVAD, devad); + MCDI_SET_DWORD(inbuf, MDIO_READ_IN_ADDR, addr); + + rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_READ, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + *value_out = (u16)MCDI_DWORD(outbuf, MDIO_READ_OUT_VALUE); + *status_out = MCDI_DWORD(outbuf, MDIO_READ_OUT_STATUS); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_mdio_write(struct efx_nic *efx, unsigned int bus, + unsigned int prtad, unsigned int devad, u16 addr, + u16 value, u32 *status_out) +{ + u8 inbuf[MC_CMD_MDIO_WRITE_IN_LEN]; + u8 outbuf[MC_CMD_MDIO_WRITE_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_BUS, bus); + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_PRTAD, prtad); + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_DEVAD, devad); + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_ADDR, addr); + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_VALUE, value); + + rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_WRITE, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + *status_out = MCDI_DWORD(outbuf, MDIO_WRITE_OUT_STATUS); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +static u32 mcdi_to_ethtool_cap(u32 media, u32 cap) +{ + u32 result = 0; + + switch (media) { + case MC_CMD_MEDIA_KX4: + result |= SUPPORTED_Backplane; + if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) + result |= SUPPORTED_1000baseKX_Full; + if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) + result |= SUPPORTED_10000baseKX4_Full; + break; + + case MC_CMD_MEDIA_XFP: + case MC_CMD_MEDIA_SFP_PLUS: + result |= SUPPORTED_FIBRE; + break; + + case MC_CMD_MEDIA_BASE_T: + result |= SUPPORTED_TP; + if (cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN)) + result |= SUPPORTED_10baseT_Half; + if (cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN)) + result |= SUPPORTED_10baseT_Full; + if (cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN)) + result |= SUPPORTED_100baseT_Half; + if (cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN)) + result |= SUPPORTED_100baseT_Full; + if (cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN)) + result |= SUPPORTED_1000baseT_Half; + if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) + result |= SUPPORTED_1000baseT_Full; + if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) + result |= SUPPORTED_10000baseT_Full; + break; + } + + if (cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN)) + result |= SUPPORTED_Pause; + if (cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) + result |= SUPPORTED_Asym_Pause; + if (cap & (1 << MC_CMD_PHY_CAP_AN_LBN)) + result |= SUPPORTED_Autoneg; + + return result; +} + +static u32 ethtool_to_mcdi_cap(u32 cap) +{ + u32 result = 0; + + if (cap & SUPPORTED_10baseT_Half) + result |= (1 << MC_CMD_PHY_CAP_10HDX_LBN); + if (cap & SUPPORTED_10baseT_Full) + result |= (1 << MC_CMD_PHY_CAP_10FDX_LBN); + if (cap & SUPPORTED_100baseT_Half) + result |= (1 << MC_CMD_PHY_CAP_100HDX_LBN); + if (cap & SUPPORTED_100baseT_Full) + result |= (1 << MC_CMD_PHY_CAP_100FDX_LBN); + if (cap & SUPPORTED_1000baseT_Half) + result |= (1 << MC_CMD_PHY_CAP_1000HDX_LBN); + if (cap & (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseKX_Full)) + result |= (1 << MC_CMD_PHY_CAP_1000FDX_LBN); + if (cap & (SUPPORTED_10000baseT_Full | SUPPORTED_10000baseKX4_Full)) + result |= (1 << MC_CMD_PHY_CAP_10000FDX_LBN); + if (cap & SUPPORTED_Pause) + result |= (1 << MC_CMD_PHY_CAP_PAUSE_LBN); + if (cap & SUPPORTED_Asym_Pause) + result |= (1 << MC_CMD_PHY_CAP_ASYM_LBN); + if (cap & SUPPORTED_Autoneg) + result |= (1 << MC_CMD_PHY_CAP_AN_LBN); + + return result; +} + +static u32 efx_get_mcdi_phy_flags(struct efx_nic *efx) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + enum efx_phy_mode mode, supported; + u32 flags; + + /* TODO: Advertise the capabilities supported by this PHY */ + supported = 0; + if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_TXDIS_LBN)) + supported |= PHY_MODE_TX_DISABLED; + if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_LOWPOWER_LBN)) + supported |= PHY_MODE_LOW_POWER; + if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_POWEROFF_LBN)) + supported |= PHY_MODE_OFF; + + mode = efx->phy_mode & supported; + + flags = 0; + if (mode & PHY_MODE_TX_DISABLED) + flags |= (1 << MC_CMD_SET_LINK_TXDIS_LBN); + if (mode & PHY_MODE_LOW_POWER) + flags |= (1 << MC_CMD_SET_LINK_LOWPOWER_LBN); + if (mode & PHY_MODE_OFF) + flags |= (1 << MC_CMD_SET_LINK_POWEROFF_LBN); + + return flags; +} + +static u32 mcdi_to_ethtool_media(u32 media) +{ + switch (media) { + case MC_CMD_MEDIA_XAUI: + case MC_CMD_MEDIA_CX4: + case MC_CMD_MEDIA_KX4: + return PORT_OTHER; + + case MC_CMD_MEDIA_XFP: + case MC_CMD_MEDIA_SFP_PLUS: + return PORT_FIBRE; + + case MC_CMD_MEDIA_BASE_T: + return PORT_TP; + + default: + return PORT_OTHER; + } +} + +static int efx_mcdi_phy_probe(struct efx_nic *efx) +{ + struct efx_mcdi_phy_cfg *phy_cfg; + int rc; + + /* TODO: Move phy_data initialisation to + * phy_op->probe/remove, rather than init/fini */ + phy_cfg = kzalloc(sizeof(*phy_cfg), GFP_KERNEL); + if (phy_cfg == NULL) { + rc = -ENOMEM; + goto fail_alloc; + } + rc = efx_mcdi_get_phy_cfg(efx, phy_cfg); + if (rc != 0) + goto fail; + + efx->phy_type = phy_cfg->type; + + efx->mdio_bus = phy_cfg->channel; + efx->mdio.prtad = phy_cfg->port; + efx->mdio.mmds = phy_cfg->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22); + efx->mdio.mode_support = 0; + if (phy_cfg->mmd_mask & (1 << MC_CMD_MMD_CLAUSE22)) + efx->mdio.mode_support |= MDIO_SUPPORTS_C22; + if (phy_cfg->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22)) + efx->mdio.mode_support |= MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + + /* Assert that we can map efx -> mcdi loopback modes */ + BUILD_BUG_ON(LOOPBACK_NONE != MC_CMD_LOOPBACK_NONE); + BUILD_BUG_ON(LOOPBACK_DATA != MC_CMD_LOOPBACK_DATA); + BUILD_BUG_ON(LOOPBACK_GMAC != MC_CMD_LOOPBACK_GMAC); + BUILD_BUG_ON(LOOPBACK_XGMII != MC_CMD_LOOPBACK_XGMII); + BUILD_BUG_ON(LOOPBACK_XGXS != MC_CMD_LOOPBACK_XGXS); + BUILD_BUG_ON(LOOPBACK_XAUI != MC_CMD_LOOPBACK_XAUI); + BUILD_BUG_ON(LOOPBACK_GMII != MC_CMD_LOOPBACK_GMII); + BUILD_BUG_ON(LOOPBACK_SGMII != MC_CMD_LOOPBACK_SGMII); + BUILD_BUG_ON(LOOPBACK_XGBR != MC_CMD_LOOPBACK_XGBR); + BUILD_BUG_ON(LOOPBACK_XFI != MC_CMD_LOOPBACK_XFI); + BUILD_BUG_ON(LOOPBACK_XAUI_FAR != MC_CMD_LOOPBACK_XAUI_FAR); + BUILD_BUG_ON(LOOPBACK_GMII_FAR != MC_CMD_LOOPBACK_GMII_FAR); + BUILD_BUG_ON(LOOPBACK_SGMII_FAR != MC_CMD_LOOPBACK_SGMII_FAR); + BUILD_BUG_ON(LOOPBACK_XFI_FAR != MC_CMD_LOOPBACK_XFI_FAR); + BUILD_BUG_ON(LOOPBACK_GPHY != MC_CMD_LOOPBACK_GPHY); + BUILD_BUG_ON(LOOPBACK_PHYXS != MC_CMD_LOOPBACK_PHYXS); + BUILD_BUG_ON(LOOPBACK_PCS != MC_CMD_LOOPBACK_PCS); + BUILD_BUG_ON(LOOPBACK_PMAPMD != MC_CMD_LOOPBACK_PMAPMD); + BUILD_BUG_ON(LOOPBACK_XPORT != MC_CMD_LOOPBACK_XPORT); + BUILD_BUG_ON(LOOPBACK_XGMII_WS != MC_CMD_LOOPBACK_XGMII_WS); + BUILD_BUG_ON(LOOPBACK_XAUI_WS != MC_CMD_LOOPBACK_XAUI_WS); + BUILD_BUG_ON(LOOPBACK_XAUI_WS_FAR != MC_CMD_LOOPBACK_XAUI_WS_FAR); + BUILD_BUG_ON(LOOPBACK_XAUI_WS_NEAR != MC_CMD_LOOPBACK_XAUI_WS_NEAR); + BUILD_BUG_ON(LOOPBACK_GMII_WS != MC_CMD_LOOPBACK_GMII_WS); + BUILD_BUG_ON(LOOPBACK_XFI_WS != MC_CMD_LOOPBACK_XFI_WS); + BUILD_BUG_ON(LOOPBACK_XFI_WS_FAR != MC_CMD_LOOPBACK_XFI_WS_FAR); + BUILD_BUG_ON(LOOPBACK_PHYXS_WS != MC_CMD_LOOPBACK_PHYXS_WS); + + rc = efx_mcdi_loopback_modes(efx, &efx->loopback_modes); + if (rc != 0) + goto fail; + /* The MC indicates that LOOPBACK_NONE is a valid loopback mode, + * but by convention we don't */ + efx->loopback_modes &= ~(1 << LOOPBACK_NONE); + + kfree(phy_cfg); + + return 0; + +fail: + kfree(phy_cfg); +fail_alloc: + return rc; +} + +static int efx_mcdi_phy_init(struct efx_nic *efx) +{ + struct efx_mcdi_phy_cfg *phy_data; + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + u32 caps; + int rc; + + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); + if (phy_data == NULL) + return -ENOMEM; + + rc = efx_mcdi_get_phy_cfg(efx, phy_data); + if (rc != 0) + goto fail; + + efx->phy_data = phy_data; + + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), NULL); + if (rc) + goto fail; + + caps = MCDI_DWORD(outbuf, GET_LINK_OUT_CAP); + if (caps & (1 << MC_CMD_PHY_CAP_AN_LBN)) + efx->link_advertising = + mcdi_to_ethtool_cap(phy_data->media, caps); + else + phy_data->forced_cap = caps; + + return 0; + +fail: + kfree(phy_data); + return rc; +} + +int efx_mcdi_phy_reconfigure(struct efx_nic *efx) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + u32 caps = (efx->link_advertising ? + ethtool_to_mcdi_cap(efx->link_advertising) : + phy_cfg->forced_cap); + + return efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx), + efx->loopback_mode, 0); +} + +void efx_mcdi_phy_decode_link(struct efx_nic *efx, + struct efx_link_state *link_state, + u32 speed, u32 flags, u32 fcntl) +{ + switch (fcntl) { + case MC_CMD_FCNTL_AUTO: + WARN_ON(1); /* This is not a link mode */ + link_state->fc = EFX_FC_AUTO | EFX_FC_TX | EFX_FC_RX; + break; + case MC_CMD_FCNTL_BIDIR: + link_state->fc = EFX_FC_TX | EFX_FC_RX; + break; + case MC_CMD_FCNTL_RESPOND: + link_state->fc = EFX_FC_RX; + break; + default: + WARN_ON(1); + case MC_CMD_FCNTL_OFF: + link_state->fc = 0; + break; + } + + link_state->up = !!(flags & (1 << MC_CMD_GET_LINK_LINK_UP_LBN)); + link_state->fd = !!(flags & (1 << MC_CMD_GET_LINK_FULL_DUPLEX_LBN)); + link_state->speed = speed; +} + +/* Verify that the forced flow control settings (!EFX_FC_AUTO) are + * supported by the link partner. Warn the user if this isn't the case + */ +void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + u32 rmtadv; + + /* The link partner capabilities are only relevent if the + * link supports flow control autonegotiation */ + if (~phy_cfg->supported_cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) + return; + + /* If flow control autoneg is supported and enabled, then fine */ + if (efx->wanted_fc & EFX_FC_AUTO) + return; + + rmtadv = 0; + if (lpa & (1 << MC_CMD_PHY_CAP_PAUSE_LBN)) + rmtadv |= ADVERTISED_Pause; + if (lpa & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) + rmtadv |= ADVERTISED_Asym_Pause; + + if ((efx->wanted_fc & EFX_FC_TX) && rmtadv == ADVERTISED_Asym_Pause) + EFX_ERR(efx, "warning: link partner doesn't support " + "pause frames"); +} + +static bool efx_mcdi_phy_poll(struct efx_nic *efx) +{ + struct efx_link_state old_state = efx->link_state; + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + int rc; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), NULL); + if (rc) { + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + efx->link_state.up = false; + } else { + efx_mcdi_phy_decode_link( + efx, &efx->link_state, + MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED), + MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS), + MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL)); + } + + return !efx_link_state_equal(&efx->link_state, &old_state); +} + +static void efx_mcdi_phy_fini(struct efx_nic *efx) +{ + struct efx_mcdi_phy_data *phy_data = efx->phy_data; + + efx->phy_data = NULL; + kfree(phy_data); +} + +static void efx_mcdi_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + int rc; + + ecmd->supported = + mcdi_to_ethtool_cap(phy_cfg->media, phy_cfg->supported_cap); + ecmd->advertising = efx->link_advertising; + ecmd->speed = efx->link_state.speed; + ecmd->duplex = efx->link_state.fd; + ecmd->port = mcdi_to_ethtool_media(phy_cfg->media); + ecmd->phy_address = phy_cfg->port; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->autoneg = !!(efx->link_advertising & ADVERTISED_Autoneg); + ecmd->mdio_support = (efx->mdio.mode_support & + (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22)); + + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), NULL); + if (rc) { + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return; + } + ecmd->lp_advertising = + mcdi_to_ethtool_cap(phy_cfg->media, + MCDI_DWORD(outbuf, GET_LINK_OUT_LP_CAP)); +} + +static int efx_mcdi_phy_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + u32 caps; + int rc; + + if (ecmd->autoneg) { + caps = (ethtool_to_mcdi_cap(ecmd->advertising) | + 1 << MC_CMD_PHY_CAP_AN_LBN); + } else if (ecmd->duplex) { + switch (ecmd->speed) { + case 10: caps = 1 << MC_CMD_PHY_CAP_10FDX_LBN; break; + case 100: caps = 1 << MC_CMD_PHY_CAP_100FDX_LBN; break; + case 1000: caps = 1 << MC_CMD_PHY_CAP_1000FDX_LBN; break; + case 10000: caps = 1 << MC_CMD_PHY_CAP_10000FDX_LBN; break; + default: return -EINVAL; + } + } else { + switch (ecmd->speed) { + case 10: caps = 1 << MC_CMD_PHY_CAP_10HDX_LBN; break; + case 100: caps = 1 << MC_CMD_PHY_CAP_100HDX_LBN; break; + case 1000: caps = 1 << MC_CMD_PHY_CAP_1000HDX_LBN; break; + default: return -EINVAL; + } + } + + rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx), + efx->loopback_mode, 0); + if (rc) + return rc; + + if (ecmd->autoneg) { + efx_link_set_advertising( + efx, ecmd->advertising | ADVERTISED_Autoneg); + phy_cfg->forced_cap = 0; + } else { + efx_link_set_advertising(efx, 0); + phy_cfg->forced_cap = caps; + } + return 0; +} + +struct efx_phy_operations efx_mcdi_phy_ops = { + .probe = efx_mcdi_phy_probe, + .init = efx_mcdi_phy_init, + .reconfigure = efx_mcdi_phy_reconfigure, + .poll = efx_mcdi_phy_poll, + .fini = efx_mcdi_phy_fini, + .get_settings = efx_mcdi_phy_get_settings, + .set_settings = efx_mcdi_phy_set_settings, + .run_tests = NULL, + .test_name = NULL, +}; diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c index 231e580..1574e52 100644 --- a/drivers/net/sfc/mdio_10g.c +++ b/drivers/net/sfc/mdio_10g.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * 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 @@ -15,6 +15,7 @@ #include "net_driver.h" #include "mdio_10g.h" #include "workarounds.h" +#include "nic.h" unsigned efx_mdio_id_oui(u32 id) { @@ -173,7 +174,7 @@ bool efx_mdio_links_ok(struct efx_nic *efx, unsigned int mmd_mask) * of mmd's */ if (LOOPBACK_INTERNAL(efx)) return true; - else if (efx->loopback_mode == LOOPBACK_NETWORK) + else if (LOOPBACK_MASK(efx) & LOOPBACKS_WS) return false; else if (efx_phy_mode_disabled(efx->phy_mode)) return false; @@ -210,7 +211,7 @@ void efx_mdio_phy_reconfigure(struct efx_nic *efx) efx->loopback_mode == LOOPBACK_PCS); efx_mdio_set_flag(efx, MDIO_MMD_PHYXS, MDIO_CTRL1, MDIO_PHYXS_CTRL1_LOOPBACK, - efx->loopback_mode == LOOPBACK_NETWORK); + efx->loopback_mode == LOOPBACK_PHYXS_WS); } static void efx_mdio_set_mmd_lpower(struct efx_nic *efx, @@ -248,8 +249,6 @@ void efx_mdio_set_mmds_lpower(struct efx_nic *efx, int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) { struct ethtool_cmd prev; - bool xnp; - int reg; efx->phy_op->get_settings(efx, &prev); @@ -269,44 +268,58 @@ int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) (ecmd->advertising | SUPPORTED_Autoneg) & ~prev.supported) return -EINVAL; - xnp = (ecmd->advertising & ADVERTISED_10000baseT_Full - || EFX_WORKAROUND_13204(efx)); + efx_link_set_advertising(efx, ecmd->advertising | ADVERTISED_Autoneg); + 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 */ reg = ADVERTISE_CSMA; - if (ecmd->advertising & ADVERTISED_10baseT_Half) + if (efx->link_advertising & ADVERTISED_10baseT_Half) reg |= ADVERTISE_10HALF; - if (ecmd->advertising & ADVERTISED_10baseT_Full) + if (efx->link_advertising & ADVERTISED_10baseT_Full) reg |= ADVERTISE_10FULL; - if (ecmd->advertising & ADVERTISED_100baseT_Half) + if (efx->link_advertising & ADVERTISED_100baseT_Half) reg |= ADVERTISE_100HALF; - if (ecmd->advertising & ADVERTISED_100baseT_Full) + if (efx->link_advertising & ADVERTISED_100baseT_Full) reg |= ADVERTISE_100FULL; if (xnp) reg |= ADVERTISE_RESV; - else if (ecmd->advertising & (ADVERTISED_1000baseT_Half | - ADVERTISED_1000baseT_Full)) + else if (efx->link_advertising & (ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full)) 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); /* Set up the (extended) next page if necessary */ 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 */ reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_CTRL1); reg |= MDIO_AN_CTRL1_ENABLE; - if (!(EFX_WORKAROUND_15195(efx) && - LOOPBACK_MASK(efx) & efx->phy_op->loopbacks)) + if (!(EFX_WORKAROUND_15195(efx) && LOOPBACK_EXTERNAL(efx))) reg |= MDIO_AN_CTRL1_RESTART; if (xnp) reg |= MDIO_AN_CTRL1_XNP; else reg &= ~MDIO_AN_CTRL1_XNP; efx_mdio_write(efx, MDIO_MMD_AN, MDIO_CTRL1, reg); - - return 0; } enum efx_fc_type efx_mdio_get_pause(struct efx_nic *efx) diff --git a/drivers/net/sfc/mdio_10g.h b/drivers/net/sfc/mdio_10g.h index 75b37f1..f6ac950 100644 --- a/drivers/net/sfc/mdio_10g.h +++ b/drivers/net/sfc/mdio_10g.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * 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 @@ -86,6 +86,9 @@ extern void efx_mdio_set_mmds_lpower(struct efx_nic *efx, /* Set (some of) the PHY settings over MDIO */ 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 * requested pause parameters) */ diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c index 820c233..3a46452 100644 --- a/drivers/net/sfc/mtd.c +++ b/drivers/net/sfc/mtd.c @@ -1,36 +1,80 @@ /**************************************************************************** * 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 * by the Free Software Foundation, incorporated herein by reference. */ +#include <linux/bitops.h> #include <linux/module.h> #include <linux/mtd/mtd.h> #include <linux/delay.h> +#include <linux/rtnetlink.h> #define EFX_DRIVER_NAME "sfc_mtd" #include "net_driver.h" #include "spi.h" #include "efx.h" +#include "nic.h" +#include "mcdi.h" +#include "mcdi_pcol.h" #define EFX_SPI_VERIFY_BUF_LEN 16 +#define EFX_MCDI_CHUNK_LEN 128 -struct efx_mtd { - const struct efx_spi_device *spi; +struct efx_mtd_partition { struct mtd_info mtd; + union { + struct { + bool updating; + u8 nvram_type; + u16 fw_subtype; + } mcdi; + size_t offset; + }; + const char *type_name; char name[IFNAMSIZ + 20]; }; +struct efx_mtd_ops { + int (*read)(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, u8 *buffer); + int (*erase)(struct mtd_info *mtd, loff_t start, size_t len); + int (*write)(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, const u8 *buffer); + int (*sync)(struct mtd_info *mtd); +}; + +struct efx_mtd { + struct list_head node; + struct efx_nic *efx; + const struct efx_spi_device *spi; + const char *name; + const struct efx_mtd_ops *ops; + size_t n_parts; + struct efx_mtd_partition part[0]; +}; + +#define efx_for_each_partition(part, efx_mtd) \ + for ((part) = &(efx_mtd)->part[0]; \ + (part) != &(efx_mtd)->part[(efx_mtd)->n_parts]; \ + (part)++) + +#define to_efx_mtd_partition(mtd) \ + container_of(mtd, struct efx_mtd_partition, mtd) + +static int falcon_mtd_probe(struct efx_nic *efx); +static int siena_mtd_probe(struct efx_nic *efx); + /* SPI utilities */ static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible) { const struct efx_spi_device *spi = efx_mtd->spi; - struct efx_nic *efx = spi->efx; + struct efx_nic *efx = efx_mtd->efx; u8 status; int rc, i; @@ -39,7 +83,7 @@ static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible) __set_current_state(uninterruptible ? TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE); schedule_timeout(HZ / 10); - rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, &status, sizeof(status)); if (rc) return rc; @@ -52,32 +96,35 @@ static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible) return -ETIMEDOUT; } -static int efx_spi_unlock(const struct efx_spi_device *spi) +static int +efx_spi_unlock(struct efx_nic *efx, const struct efx_spi_device *spi) { const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 | SPI_STATUS_BP0); u8 status; int rc; - rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, &status, sizeof(status)); + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, + &status, sizeof(status)); if (rc) return rc; if (!(status & unlock_mask)) return 0; /* already unlocked */ - rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); if (rc) return rc; - rc = falcon_spi_cmd(spi, SPI_SST_EWSR, -1, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, SPI_SST_EWSR, -1, NULL, NULL, 0); if (rc) return rc; status &= ~unlock_mask; - rc = falcon_spi_cmd(spi, SPI_WRSR, -1, &status, NULL, sizeof(status)); + rc = falcon_spi_cmd(efx, spi, SPI_WRSR, -1, &status, + NULL, sizeof(status)); if (rc) return rc; - rc = falcon_spi_wait_write(spi); + rc = falcon_spi_wait_write(efx, spi); if (rc) return rc; @@ -87,6 +134,7 @@ static int efx_spi_unlock(const struct efx_spi_device *spi) static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) { const struct efx_spi_device *spi = efx_mtd->spi; + struct efx_nic *efx = efx_mtd->efx; unsigned pos, block_len; u8 empty[EFX_SPI_VERIFY_BUF_LEN]; u8 buffer[EFX_SPI_VERIFY_BUF_LEN]; @@ -98,13 +146,14 @@ static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) if (spi->erase_command == 0) return -EOPNOTSUPP; - rc = efx_spi_unlock(spi); + rc = efx_spi_unlock(efx, spi); if (rc) return rc; - rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); if (rc) return rc; - rc = falcon_spi_cmd(spi, spi->erase_command, start, NULL, NULL, 0); + rc = falcon_spi_cmd(efx, spi, spi->erase_command, start, NULL, + NULL, 0); if (rc) return rc; rc = efx_spi_slow_wait(efx_mtd, false); @@ -113,7 +162,8 @@ static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) memset(empty, 0xff, sizeof(empty)); for (pos = 0; pos < len; pos += block_len) { block_len = min(len - pos, sizeof(buffer)); - rc = falcon_spi_read(spi, start + pos, block_len, NULL, buffer); + rc = falcon_spi_read(efx, spi, start + pos, block_len, + NULL, buffer); if (rc) return rc; if (memcmp(empty, buffer, block_len)) @@ -130,140 +180,473 @@ static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) /* MTD interface */ -static int efx_mtd_read(struct mtd_info *mtd, loff_t start, size_t len, - size_t *retlen, u8 *buffer) +static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) { struct efx_mtd *efx_mtd = mtd->priv; + int rc; + + rc = efx_mtd->ops->erase(mtd, erase->addr, erase->len); + if (rc == 0) { + erase->state = MTD_ERASE_DONE; + } else { + erase->state = MTD_ERASE_FAILED; + erase->fail_addr = 0xffffffff; + } + mtd_erase_callback(erase); + return rc; +} + +static void efx_mtd_sync(struct mtd_info *mtd) +{ + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + int rc; + + rc = efx_mtd->ops->sync(mtd); + if (rc) + EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc); +} + +static void efx_mtd_remove_partition(struct efx_mtd_partition *part) +{ + int rc; + + for (;;) { + rc = del_mtd_device(&part->mtd); + if (rc != -EBUSY) + break; + ssleep(1); + } + WARN_ON(rc); +} + +static void efx_mtd_remove_device(struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + + efx_for_each_partition(part, efx_mtd) + efx_mtd_remove_partition(part); + list_del(&efx_mtd->node); + kfree(efx_mtd); +} + +static void efx_mtd_rename_device(struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + + efx_for_each_partition(part, efx_mtd) + if (efx_nic_rev(efx_mtd->efx) >= EFX_REV_SIENA_A0) + snprintf(part->name, sizeof(part->name), + "%s %s:%02x", efx_mtd->efx->name, + part->type_name, part->mcdi.fw_subtype); + else + snprintf(part->name, sizeof(part->name), + "%s %s", efx_mtd->efx->name, + part->type_name); +} + +static int efx_mtd_probe_device(struct efx_nic *efx, struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + + efx_mtd->efx = efx; + + efx_mtd_rename_device(efx_mtd); + + efx_for_each_partition(part, efx_mtd) { + part->mtd.writesize = 1; + + part->mtd.owner = THIS_MODULE; + part->mtd.priv = efx_mtd; + part->mtd.name = part->name; + part->mtd.erase = efx_mtd_erase; + part->mtd.read = efx_mtd->ops->read; + part->mtd.write = efx_mtd->ops->write; + part->mtd.sync = efx_mtd_sync; + + if (add_mtd_device(&part->mtd)) + goto fail; + } + + list_add(&efx_mtd->node, &efx->mtd_list); + return 0; + +fail: + while (part != &efx_mtd->part[0]) { + --part; + efx_mtd_remove_partition(part); + } + /* add_mtd_device() returns 1 if the MTD table is full */ + return -ENOMEM; +} + +void efx_mtd_remove(struct efx_nic *efx) +{ + struct efx_mtd *efx_mtd, *next; + + WARN_ON(efx_dev_registered(efx)); + + list_for_each_entry_safe(efx_mtd, next, &efx->mtd_list, node) + efx_mtd_remove_device(efx_mtd); +} + +void efx_mtd_rename(struct efx_nic *efx) +{ + struct efx_mtd *efx_mtd; + + ASSERT_RTNL(); + + list_for_each_entry(efx_mtd, &efx->mtd_list, node) + efx_mtd_rename_device(efx_mtd); +} + +int efx_mtd_probe(struct efx_nic *efx) +{ + if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) + return siena_mtd_probe(efx); + else + return falcon_mtd_probe(efx); +} + +/* Implementation of MTD operations for Falcon */ + +static int falcon_mtd_read(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, u8 *buffer) +{ + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; const struct efx_spi_device *spi = efx_mtd->spi; - struct efx_nic *efx = spi->efx; + struct efx_nic *efx = efx_mtd->efx; int rc; rc = mutex_lock_interruptible(&efx->spi_lock); if (rc) return rc; - rc = falcon_spi_read(spi, FALCON_FLASH_BOOTCODE_START + start, - len, retlen, buffer); + rc = falcon_spi_read(efx, spi, part->offset + start, len, + retlen, buffer); mutex_unlock(&efx->spi_lock); return rc; } -static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) +static int falcon_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len) { + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); struct efx_mtd *efx_mtd = mtd->priv; - struct efx_nic *efx = efx_mtd->spi->efx; + struct efx_nic *efx = efx_mtd->efx; int rc; rc = mutex_lock_interruptible(&efx->spi_lock); if (rc) return rc; - rc = efx_spi_erase(efx_mtd, FALCON_FLASH_BOOTCODE_START + erase->addr, - erase->len); + rc = efx_spi_erase(efx_mtd, part->offset + start, len); mutex_unlock(&efx->spi_lock); - - if (rc == 0) { - erase->state = MTD_ERASE_DONE; - } else { - erase->state = MTD_ERASE_FAILED; - erase->fail_addr = 0xffffffff; - } - mtd_erase_callback(erase); return rc; } -static int efx_mtd_write(struct mtd_info *mtd, loff_t start, - size_t len, size_t *retlen, const u8 *buffer) +static int falcon_mtd_write(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, const u8 *buffer) { + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); struct efx_mtd *efx_mtd = mtd->priv; const struct efx_spi_device *spi = efx_mtd->spi; - struct efx_nic *efx = spi->efx; + struct efx_nic *efx = efx_mtd->efx; int rc; rc = mutex_lock_interruptible(&efx->spi_lock); if (rc) return rc; - rc = falcon_spi_write(spi, FALCON_FLASH_BOOTCODE_START + start, - len, retlen, buffer); + rc = falcon_spi_write(efx, spi, part->offset + start, len, + retlen, buffer); mutex_unlock(&efx->spi_lock); return rc; } -static void efx_mtd_sync(struct mtd_info *mtd) +static int falcon_mtd_sync(struct mtd_info *mtd) { struct efx_mtd *efx_mtd = mtd->priv; - struct efx_nic *efx = efx_mtd->spi->efx; + struct efx_nic *efx = efx_mtd->efx; int rc; mutex_lock(&efx->spi_lock); rc = efx_spi_slow_wait(efx_mtd, true); mutex_unlock(&efx->spi_lock); + return rc; +} + +static struct efx_mtd_ops falcon_mtd_ops = { + .read = falcon_mtd_read, + .erase = falcon_mtd_erase, + .write = falcon_mtd_write, + .sync = falcon_mtd_sync, +}; + +static int falcon_mtd_probe(struct efx_nic *efx) +{ + struct efx_spi_device *spi = efx->spi_flash; + struct efx_mtd *efx_mtd; + int rc; + + ASSERT_RTNL(); + if (!spi || spi->size <= FALCON_FLASH_BOOTCODE_START) + return -ENODEV; + + efx_mtd = kzalloc(sizeof(*efx_mtd) + sizeof(efx_mtd->part[0]), + GFP_KERNEL); + if (!efx_mtd) + return -ENOMEM; + + efx_mtd->spi = spi; + efx_mtd->name = "flash"; + efx_mtd->ops = &falcon_mtd_ops; + + efx_mtd->n_parts = 1; + efx_mtd->part[0].mtd.type = MTD_NORFLASH; + efx_mtd->part[0].mtd.flags = MTD_CAP_NORFLASH; + efx_mtd->part[0].mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START; + efx_mtd->part[0].mtd.erasesize = spi->erase_size; + efx_mtd->part[0].offset = FALCON_FLASH_BOOTCODE_START; + efx_mtd->part[0].type_name = "sfc_flash_bootrom"; + + rc = efx_mtd_probe_device(efx, efx_mtd); if (rc) - EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc); - return; + kfree(efx_mtd); + return rc; } -void efx_mtd_remove(struct efx_nic *efx) +/* Implementation of MTD operations for Siena */ + +static int siena_mtd_read(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, u8 *buffer) { - if (efx->spi_flash && efx->spi_flash->mtd) { - struct efx_mtd *efx_mtd = efx->spi_flash->mtd; - int rc; - - for (;;) { - rc = del_mtd_device(&efx_mtd->mtd); - if (rc != -EBUSY) - break; - ssleep(1); - } - WARN_ON(rc); - kfree(efx_mtd); + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + loff_t offset = start; + loff_t end = min_t(loff_t, start + len, mtd->size); + size_t chunk; + int rc = 0; + + while (offset < end) { + chunk = min_t(size_t, end - offset, EFX_MCDI_CHUNK_LEN); + rc = efx_mcdi_nvram_read(efx, part->mcdi.nvram_type, offset, + buffer, chunk); + if (rc) + goto out; + offset += chunk; + buffer += chunk; } +out: + *retlen = offset - start; + return rc; } -void efx_mtd_rename(struct efx_nic *efx) +static int siena_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len) { - if (efx->spi_flash && efx->spi_flash->mtd) { - struct efx_mtd *efx_mtd = efx->spi_flash->mtd; - snprintf(efx_mtd->name, sizeof(efx_mtd->name), - "%s sfc_flash_bootrom", efx->name); + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + loff_t offset = start & ~((loff_t)(mtd->erasesize - 1)); + loff_t end = min_t(loff_t, start + len, mtd->size); + size_t chunk = part->mtd.erasesize; + int rc = 0; + + if (!part->mcdi.updating) { + rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type); + if (rc) + goto out; + part->mcdi.updating = 1; + } + + /* The MCDI interface can in fact do multiple erase blocks at once; + * but erasing may be slow, so we make multiple calls here to avoid + * tripping the MCDI RPC timeout. */ + while (offset < end) { + rc = efx_mcdi_nvram_erase(efx, part->mcdi.nvram_type, offset, + chunk); + if (rc) + goto out; + offset += chunk; } +out: + return rc; } -int efx_mtd_probe(struct efx_nic *efx) +static int siena_mtd_write(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, const u8 *buffer) { - struct efx_spi_device *spi = efx->spi_flash; - struct efx_mtd *efx_mtd; + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + loff_t offset = start; + loff_t end = min_t(loff_t, start + len, mtd->size); + size_t chunk; + int rc = 0; + + if (!part->mcdi.updating) { + rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type); + if (rc) + goto out; + part->mcdi.updating = 1; + } - if (!spi || spi->size <= FALCON_FLASH_BOOTCODE_START) + while (offset < end) { + chunk = min_t(size_t, end - offset, EFX_MCDI_CHUNK_LEN); + rc = efx_mcdi_nvram_write(efx, part->mcdi.nvram_type, offset, + buffer, chunk); + if (rc) + goto out; + offset += chunk; + buffer += chunk; + } +out: + *retlen = offset - start; + return rc; +} + +static int siena_mtd_sync(struct mtd_info *mtd) +{ + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + int rc = 0; + + if (part->mcdi.updating) { + part->mcdi.updating = 0; + rc = efx_mcdi_nvram_update_finish(efx, part->mcdi.nvram_type); + } + + return rc; +} + +static struct efx_mtd_ops siena_mtd_ops = { + .read = siena_mtd_read, + .erase = siena_mtd_erase, + .write = siena_mtd_write, + .sync = siena_mtd_sync, +}; + +struct siena_nvram_type_info { + int port; + const char *name; +}; + +static struct siena_nvram_type_info siena_nvram_types[] = { + [MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO] = { 0, "sfc_dummy_phy" }, + [MC_CMD_NVRAM_TYPE_MC_FW] = { 0, "sfc_mcfw" }, + [MC_CMD_NVRAM_TYPE_MC_FW_BACKUP] = { 0, "sfc_mcfw_backup" }, + [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0] = { 0, "sfc_static_cfg" }, + [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1] = { 1, "sfc_static_cfg" }, + [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0] = { 0, "sfc_dynamic_cfg" }, + [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1] = { 1, "sfc_dynamic_cfg" }, + [MC_CMD_NVRAM_TYPE_EXP_ROM] = { 0, "sfc_exp_rom" }, + [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0] = { 0, "sfc_exp_rom_cfg" }, + [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1] = { 1, "sfc_exp_rom_cfg" }, + [MC_CMD_NVRAM_TYPE_PHY_PORT0] = { 0, "sfc_phy_fw" }, + [MC_CMD_NVRAM_TYPE_PHY_PORT1] = { 1, "sfc_phy_fw" }, +}; + +static int siena_mtd_probe_partition(struct efx_nic *efx, + struct efx_mtd *efx_mtd, + unsigned int part_id, + unsigned int type) +{ + struct efx_mtd_partition *part = &efx_mtd->part[part_id]; + struct siena_nvram_type_info *info; + size_t size, erase_size; + bool protected; + int rc; + + if (type >= ARRAY_SIZE(siena_nvram_types)) return -ENODEV; - efx_mtd = kzalloc(sizeof(*efx_mtd), GFP_KERNEL); + info = &siena_nvram_types[type]; + + if (info->port != efx_port_num(efx)) + return -ENODEV; + + rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &protected); + if (rc) + return rc; + if (protected) + return -ENODEV; /* hide it */ + + part->mcdi.nvram_type = type; + part->type_name = info->name; + + part->mtd.type = MTD_NORFLASH; + part->mtd.flags = MTD_CAP_NORFLASH; + part->mtd.size = size; + part->mtd.erasesize = erase_size; + + return 0; +} + +static int siena_mtd_get_fw_subtypes(struct efx_nic *efx, + struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + uint16_t fw_subtype_list[MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN / + sizeof(uint16_t)]; + int rc; + + rc = efx_mcdi_get_board_cfg(efx, NULL, fw_subtype_list); + if (rc) + return rc; + + efx_for_each_partition(part, efx_mtd) + part->mcdi.fw_subtype = fw_subtype_list[part->mcdi.nvram_type]; + + return 0; +} + +static int siena_mtd_probe(struct efx_nic *efx) +{ + struct efx_mtd *efx_mtd; + int rc = -ENODEV; + u32 nvram_types; + unsigned int type; + + ASSERT_RTNL(); + + rc = efx_mcdi_nvram_types(efx, &nvram_types); + if (rc) + return rc; + + efx_mtd = kzalloc(sizeof(*efx_mtd) + + hweight32(nvram_types) * sizeof(efx_mtd->part[0]), + GFP_KERNEL); if (!efx_mtd) return -ENOMEM; - efx_mtd->spi = spi; - spi->mtd = efx_mtd; - - efx_mtd->mtd.type = MTD_NORFLASH; - efx_mtd->mtd.flags = MTD_CAP_NORFLASH; - efx_mtd->mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START; - efx_mtd->mtd.erasesize = spi->erase_size; - efx_mtd->mtd.writesize = 1; - efx_mtd_rename(efx); - - efx_mtd->mtd.owner = THIS_MODULE; - efx_mtd->mtd.priv = efx_mtd; - efx_mtd->mtd.name = efx_mtd->name; - efx_mtd->mtd.erase = efx_mtd_erase; - efx_mtd->mtd.read = efx_mtd_read; - efx_mtd->mtd.write = efx_mtd_write; - efx_mtd->mtd.sync = efx_mtd_sync; - - if (add_mtd_device(&efx_mtd->mtd)) { - kfree(efx_mtd); - spi->mtd = NULL; - /* add_mtd_device() returns 1 if the MTD table is full */ - return -ENOMEM; + efx_mtd->name = "Siena NVRAM manager"; + + efx_mtd->ops = &siena_mtd_ops; + + type = 0; + efx_mtd->n_parts = 0; + + while (nvram_types != 0) { + if (nvram_types & 1) { + rc = siena_mtd_probe_partition(efx, efx_mtd, + efx_mtd->n_parts, type); + if (rc == 0) + efx_mtd->n_parts++; + else if (rc != -ENODEV) + goto fail; + } + type++; + nvram_types >>= 1; } - return 0; + rc = siena_mtd_get_fw_subtypes(efx, efx_mtd); + if (rc) + goto fail; + + rc = efx_mtd_probe_device(efx, efx_mtd); +fail: + if (rc) + kfree(efx_mtd); + return rc; } + diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index ac808d5..34c381f 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -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 @@ -38,7 +38,7 @@ #ifndef EFX_DRIVER_NAME #define EFX_DRIVER_NAME "sfc" #endif -#define EFX_DRIVER_VERSION "2.3" +#define EFX_DRIVER_VERSION "3.0" #ifdef EFX_ENABLE_DEBUG #define EFX_BUG_ON_PARANOID(x) BUG_ON(x) @@ -113,6 +113,13 @@ struct efx_special_buffer { int entries; }; +enum efx_flush_state { + FLUSH_NONE, + FLUSH_PENDING, + FLUSH_FAILED, + FLUSH_DONE, +}; + /** * struct efx_tx_buffer - An Efx TX buffer * @skb: The associated socket buffer. @@ -189,7 +196,7 @@ struct efx_tx_queue { struct efx_nic *nic; struct efx_tx_buffer *buffer; struct efx_special_buffer txd; - bool flushed; + enum efx_flush_state flushed; /* Members used mainly on the completion path */ unsigned int read_count ____cacheline_aligned_in_smp; @@ -284,7 +291,7 @@ struct efx_rx_queue { struct page *buf_page; dma_addr_t buf_dma_addr; char *buf_data; - bool flushed; + enum efx_flush_state flushed; }; /** @@ -343,9 +350,9 @@ enum efx_rx_alloc_method { * @rx_alloc_push_pages: RX allocation method currently in use for pushing * descriptors * @n_rx_tobe_disc: Count of RX_TOBE_DISC errors - * @n_rx_ip_frag_err: Count of RX IP fragment errors * @n_rx_ip_hdr_chksum_err: Count of RX IP header checksum errors * @n_rx_tcp_udp_chksum_err: Count of RX TCP and UDP checksum errors + * @n_rx_mcast_mismatch: Count of unmatched multicast frames * @n_rx_frm_trunc: Count of RX_FRM_TRUNC errors * @n_rx_overlength: Count of RX_OVERLENGTH errors * @n_skbuff_leaks: Count of skbuffs leaked due to RX overrun @@ -373,9 +380,9 @@ struct efx_channel { int rx_alloc_push_pages; unsigned n_rx_tobe_disc; - unsigned n_rx_ip_frag_err; unsigned n_rx_ip_hdr_chksum_err; unsigned n_rx_tcp_udp_chksum_err; + unsigned n_rx_mcast_mismatch; unsigned n_rx_frm_trunc; unsigned n_rx_overlength; unsigned n_skbuff_leaks; @@ -421,19 +428,6 @@ enum efx_int_mode { }; #define EFX_INT_MODE_USE_MSI(x) (((x)->interrupt_mode) <= EFX_INT_MODE_MSI) -enum phy_type { - PHY_TYPE_NONE = 0, - PHY_TYPE_TXC43128 = 1, - PHY_TYPE_88E1111 = 2, - PHY_TYPE_SFX7101 = 3, - PHY_TYPE_QT2022C2 = 4, - PHY_TYPE_PM8358 = 6, - PHY_TYPE_SFT9001A = 8, - PHY_TYPE_QT2025C = 9, - PHY_TYPE_SFT9001B = 10, - PHY_TYPE_MAX /* Insert any new items before this */ -}; - #define EFX_IS10G(efx) ((efx)->link_state.speed == 10000) enum nic_state { @@ -476,12 +470,6 @@ enum efx_fc_type { EFX_FC_AUTO = 4, }; -/* Supported MAC bit-mask */ -enum efx_mac_type { - EFX_GMAC = 1, - EFX_XMAC = 2, -}; - /** * struct efx_link_state - Current state of the link * @up: Link is up @@ -496,55 +484,55 @@ struct efx_link_state { unsigned int speed; }; +static inline bool efx_link_state_equal(const struct efx_link_state *left, + const struct efx_link_state *right) +{ + return left->up == right->up && left->fd == right->fd && + left->fc == right->fc && left->speed == right->speed; +} + /** * struct efx_mac_operations - Efx MAC operations table * @reconfigure: Reconfigure MAC. Serialised by the mac_lock * @update_stats: Update statistics - * @irq: Hardware MAC event callback. Serialised by the mac_lock - * @poll: Poll for hardware state. Serialised by the mac_lock + * @check_fault: Check fault state. True if fault present. */ struct efx_mac_operations { - void (*reconfigure) (struct efx_nic *efx); + int (*reconfigure) (struct efx_nic *efx); void (*update_stats) (struct efx_nic *efx); - void (*irq) (struct efx_nic *efx); - void (*poll) (struct efx_nic *efx); + bool (*check_fault)(struct efx_nic *efx); }; /** * struct efx_phy_operations - Efx PHY operations table + * @probe: Probe PHY and initialise efx->mdio.mode_support, efx->mdio.mmds, + * efx->loopback_modes. * @init: Initialise PHY * @fini: Shut down PHY * @reconfigure: Reconfigure PHY (e.g. for new link parameters) - * @clear_interrupt: Clear down interrupt - * @poll: Poll for hardware state. Serialised by the mac_lock. + * @poll: Update @link_state and report whether it changed. + * Serialised by the mac_lock. * @get_settings: Get ethtool settings. Serialised by the mac_lock. * @set_settings: Set ethtool settings. Serialised by the mac_lock. * @set_npage_adv: Set abilities advertised in (Extended) Next Page * (only needed where AN bit is set in mmds) - * @num_tests: Number of PHY-specific tests/results - * @test_names: Names of the tests/results + * @test_name: Get the name of a PHY-specific test/result * @run_tests: Run tests and record results as appropriate. * Flags are the ethtool tests flags. - * @mmds: MMD presence mask - * @loopbacks: Supported loopback modes mask */ struct efx_phy_operations { - enum efx_mac_type macs; + int (*probe) (struct efx_nic *efx); int (*init) (struct efx_nic *efx); void (*fini) (struct efx_nic *efx); - void (*reconfigure) (struct efx_nic *efx); - void (*clear_interrupt) (struct efx_nic *efx); - void (*poll) (struct efx_nic *efx); + int (*reconfigure) (struct efx_nic *efx); + bool (*poll) (struct efx_nic *efx); void (*get_settings) (struct efx_nic *efx, struct ethtool_cmd *ecmd); int (*set_settings) (struct efx_nic *efx, struct ethtool_cmd *ecmd); void (*set_npage_adv) (struct efx_nic *efx, u32); - u32 num_tests; - const char *const *test_names; + const char *(*test_name) (struct efx_nic *efx, unsigned int index); int (*run_tests) (struct efx_nic *efx, int *results, unsigned flags); - int mmds; - unsigned loopbacks; }; /** @@ -686,19 +674,20 @@ union efx_multicast_hash { * interrupt is handled. It is used by falcon_test_interrupt() * to verify that an interrupt has occurred. * @spi_flash: SPI flash device - * This field will be %NULL if no flash device is present. + * This field will be %NULL if no flash device is present (or for Siena). * @spi_eeprom: SPI EEPROM device - * This field will be %NULL if no EEPROM device is present. + * This field will be %NULL if no EEPROM device is present (or for Siena). * @spi_lock: SPI bus lock + * @mtd_list: List of MTDs attached to the NIC * @n_rx_nodesc_drop_cnt: RX no descriptor drop count * @nic_data: Hardware dependant state * @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode, * @port_inhibited, efx_monitor() and efx_reconfigure_port() * @port_enabled: Port enabled indicator. - * Serialises efx_stop_all(), efx_start_all(), efx_monitor(), - * efx_phy_work(), and efx_mac_work() with kernel interfaces. Safe to read - * under any one of the rtnl_lock, mac_lock, or netif_tx_lock, but all - * three must be held to modify it. + * Serialises efx_stop_all(), efx_start_all(), efx_monitor() and + * efx_mac_work() with kernel interfaces. Safe to read under any + * one of the rtnl_lock, mac_lock, or netif_tx_lock, but all three must + * be held to modify it. * @port_inhibited: If set, the netif_carrier is always off. Hold the mac_lock * @port_initialized: Port initialized? * @net_dev: Operating system network device. Consider holding the rtnl lock @@ -710,23 +699,23 @@ union efx_multicast_hash { * &struct net_device_stats. * @stats_buffer: DMA buffer for statistics * @stats_lock: Statistics update lock. Serialises statistics fetches - * @stats_disable_count: Nest count for disabling statistics fetches * @mac_op: MAC interface * @mac_address: Permanent MAC address * @phy_type: PHY type - * @phy_lock: PHY access lock + * @mdio_lock: MDIO lock * @phy_op: PHY interface * @phy_data: PHY private data (including PHY-specific stats) * @mdio: PHY MDIO interface + * @mdio_bus: PHY MDIO bus ID (only used by Siena) * @phy_mode: PHY operating mode. Serialised by @mac_lock. - * @mac_up: MAC link state + * @xmac_poll_required: XMAC link state needs polling + * @link_advertising: Autonegotiation advertising flags * @link_state: Current state of the link * @n_link_state_changes: Number of times the link has changed state * @promiscuous: Promiscuous flag. Protected by netif_tx_lock. * @multicast_hash: Multicast hash table * @wanted_fc: Wanted flow control flags - * @phy_work: work item for dealing with PHY events - * @mac_work: work item for dealing with MAC events + * @mac_work: Work item for changing MAC promiscuity and multicast hash * @loopback_mode: Loopback status * @loopback_modes: Supported loopback mode bitmask * @loopback_selftest: Offline self-test private state @@ -768,14 +757,18 @@ struct efx_nic { struct efx_buffer irq_status; volatile signed int last_irq_cpu; + unsigned long irq_zero_count; struct efx_spi_device *spi_flash; struct efx_spi_device *spi_eeprom; struct mutex spi_lock; +#ifdef CONFIG_SFC_MTD + struct list_head mtd_list; +#endif unsigned n_rx_nodesc_drop_cnt; - struct falcon_nic_data *nic_data; + void *nic_data; struct mutex mac_lock; struct work_struct mac_work; @@ -792,20 +785,20 @@ struct efx_nic { struct efx_mac_stats mac_stats; struct efx_buffer stats_buffer; spinlock_t stats_lock; - unsigned int stats_disable_count; struct efx_mac_operations *mac_op; unsigned char mac_address[ETH_ALEN]; - enum phy_type phy_type; - spinlock_t phy_lock; - struct work_struct phy_work; + unsigned int phy_type; + struct mutex mdio_lock; struct efx_phy_operations *phy_op; void *phy_data; struct mdio_if_info mdio; + unsigned int mdio_bus; enum efx_phy_mode phy_mode; - bool mac_up; + bool xmac_poll_required; + u32 link_advertising; struct efx_link_state link_state; unsigned int n_link_state_changes; @@ -815,7 +808,7 @@ struct efx_nic { atomic_t rx_reset; enum efx_loopback_mode loopback_mode; - unsigned int loopback_modes; + u64 loopback_modes; void *loopback_selftest; }; @@ -834,8 +827,37 @@ static inline const char *efx_dev_name(struct efx_nic *efx) return efx_dev_registered(efx) ? efx->name : ""; } +static inline unsigned int efx_port_num(struct efx_nic *efx) +{ + return PCI_FUNC(efx->pci_dev->devfn); +} + /** * struct efx_nic_type - Efx device type definition + * @probe: Probe the controller + * @remove: Free resources allocated by probe() + * @init: Initialise the controller + * @fini: Shut down the controller + * @monitor: Periodic function for polling link state and hardware monitor + * @reset: Reset the controller hardware and possibly the PHY. This will + * be called while the controller is uninitialised. + * @probe_port: Probe the MAC and PHY + * @remove_port: Free resources allocated by probe_port() + * @prepare_flush: Prepare the hardware for flushing the DMA queues + * @update_stats: Update statistics not provided by event handling + * @start_stats: Start the regular fetching of statistics + * @stop_stats: Stop the regular fetching of statistics + * @set_id_led: Set state of identifying LED or revert to automatic function + * @push_irq_moderation: Apply interrupt moderation value + * @push_multicast_hash: Apply multicast hash table + * @reconfigure_port: Push loopback/power/txdis changes to the MAC and PHY + * @get_wol: Get WoL configuration from driver state + * @set_wol: Push WoL configuration to the NIC + * @resume_wol: Synchronise WoL state between driver and MC (e.g. after resume) + * @test_registers: Test read/write functionality of control registers + * @test_nvram: Test validity of NVRAM contents + * @default_mac_ops: efx_mac_operations to set at startup + * @revision: Hardware architecture revision * @mem_map_size: Memory BAR mapped size * @txd_ptr_tbl_base: TX descriptor ring base address * @rxd_ptr_tbl_base: RX descriptor ring base address @@ -848,20 +870,52 @@ static inline const char *efx_dev_name(struct efx_nic *efx) * from &enum efx_init_mode. * @phys_addr_channels: Number of channels with physically addressed * descriptors + * @tx_dc_base: Base address in SRAM of TX queue descriptor caches + * @rx_dc_base: Base address in SRAM of RX queue descriptor caches + * @offload_features: net_device feature flags for protocol offload + * features implemented in hardware + * @reset_world_flags: Flags for additional components covered by + * reset method RESET_TYPE_WORLD */ struct efx_nic_type { + int (*probe)(struct efx_nic *efx); + void (*remove)(struct efx_nic *efx); + int (*init)(struct efx_nic *efx); + void (*fini)(struct efx_nic *efx); + void (*monitor)(struct efx_nic *efx); + int (*reset)(struct efx_nic *efx, enum reset_type method); + int (*probe_port)(struct efx_nic *efx); + void (*remove_port)(struct efx_nic *efx); + void (*prepare_flush)(struct efx_nic *efx); + void (*update_stats)(struct efx_nic *efx); + void (*start_stats)(struct efx_nic *efx); + void (*stop_stats)(struct efx_nic *efx); + void (*set_id_led)(struct efx_nic *efx, enum efx_led_mode mode); + void (*push_irq_moderation)(struct efx_channel *channel); + void (*push_multicast_hash)(struct efx_nic *efx); + int (*reconfigure_port)(struct efx_nic *efx); + void (*get_wol)(struct efx_nic *efx, struct ethtool_wolinfo *wol); + int (*set_wol)(struct efx_nic *efx, u32 type); + void (*resume_wol)(struct efx_nic *efx); + int (*test_registers)(struct efx_nic *efx); + int (*test_nvram)(struct efx_nic *efx); + struct efx_mac_operations *default_mac_ops; + + int revision; unsigned int mem_map_size; unsigned int txd_ptr_tbl_base; unsigned int rxd_ptr_tbl_base; unsigned int buf_tbl_base; unsigned int evq_ptr_tbl_base; unsigned int evq_rptr_tbl_base; - u64 max_dma_mask; - unsigned int rx_buffer_padding; unsigned int max_interrupt_mode; unsigned int phys_addr_channels; + unsigned int tx_dc_base; + unsigned int rx_dc_base; + unsigned long offload_features; + u32 reset_world_flags; }; /************************************************************************** diff --git a/drivers/net/sfc/nic.c b/drivers/net/sfc/nic.c new file mode 100644 index 0000000..a577be2 --- /dev/null +++ b/drivers/net/sfc/nic.c @@ -0,0 +1,1583 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/seq_file.h> +#include "net_driver.h" +#include "bitfield.h" +#include "efx.h" +#include "nic.h" +#include "regs.h" +#include "io.h" +#include "workarounds.h" + +/************************************************************************** + * + * Configurable values + * + ************************************************************************** + */ + +/* This is set to 16 for a good reason. In summary, if larger than + * 16, the descriptor cache holds more than a default socket + * buffer's worth of packets (for UDP we can only have at most one + * socket buffer's worth outstanding). This combined with the fact + * that we only get 1 TX event per descriptor cache means the NIC + * goes idle. + */ +#define TX_DC_ENTRIES 16 +#define TX_DC_ENTRIES_ORDER 1 + +#define RX_DC_ENTRIES 64 +#define RX_DC_ENTRIES_ORDER 3 + +/* RX FIFO XOFF watermark + * + * When the amount of the RX FIFO increases used increases past this + * watermark send XOFF. Only used if RX flow control is enabled (ethtool -A) + * This also has an effect on RX/TX arbitration + */ +int efx_nic_rx_xoff_thresh = -1; +module_param_named(rx_xoff_thresh_bytes, efx_nic_rx_xoff_thresh, int, 0644); +MODULE_PARM_DESC(rx_xoff_thresh_bytes, "RX fifo XOFF threshold"); + +/* RX FIFO XON watermark + * + * When the amount of the RX FIFO used decreases below this + * watermark send XON. Only used if TX flow control is enabled (ethtool -A) + * This also has an effect on RX/TX arbitration + */ +int efx_nic_rx_xon_thresh = -1; +module_param_named(rx_xon_thresh_bytes, efx_nic_rx_xon_thresh, int, 0644); +MODULE_PARM_DESC(rx_xon_thresh_bytes, "RX fifo XON threshold"); + +/* If EFX_MAX_INT_ERRORS internal errors occur within + * EFX_INT_ERROR_EXPIRE seconds, we consider the NIC broken and + * disable it. + */ +#define EFX_INT_ERROR_EXPIRE 3600 +#define EFX_MAX_INT_ERRORS 5 + +/* We poll for events every FLUSH_INTERVAL ms, and check FLUSH_POLL_COUNT times + */ +#define EFX_FLUSH_INTERVAL 10 +#define EFX_FLUSH_POLL_COUNT 100 + +/* Size and alignment of special buffers (4KB) */ +#define EFX_BUF_SIZE 4096 + +/* Depth of RX flush request fifo */ +#define EFX_RX_FLUSH_COUNT 4 + +/************************************************************************** + * + * Solarstorm hardware access + * + **************************************************************************/ + +static inline void efx_write_buf_tbl(struct efx_nic *efx, efx_qword_t *value, + unsigned int index) +{ + efx_sram_writeq(efx, efx->membase + efx->type->buf_tbl_base, + value, index); +} + +/* Read the current event from the event queue */ +static inline efx_qword_t *efx_event(struct efx_channel *channel, + unsigned int index) +{ + return (((efx_qword_t *) (channel->eventq.addr)) + index); +} + +/* See if an event is present + * + * We check both the high and low dword of the event for all ones. We + * wrote all ones when we cleared the event, and no valid event can + * have all ones in either its high or low dwords. This approach is + * robust against reordering. + * + * Note that using a single 64-bit comparison is incorrect; even + * though the CPU read will be atomic, the DMA write may not be. + */ +static inline int efx_event_present(efx_qword_t *event) +{ + return (!(EFX_DWORD_IS_ALL_ONES(event->dword[0]) | + EFX_DWORD_IS_ALL_ONES(event->dword[1]))); +} + +static bool efx_masked_compare_oword(const efx_oword_t *a, const efx_oword_t *b, + const efx_oword_t *mask) +{ + return ((a->u64[0] ^ b->u64[0]) & mask->u64[0]) || + ((a->u64[1] ^ b->u64[1]) & mask->u64[1]); +} + +int efx_nic_test_registers(struct efx_nic *efx, + const struct efx_nic_register_test *regs, + size_t n_regs) +{ + unsigned address = 0, i, j; + efx_oword_t mask, imask, original, reg, buf; + + /* Falcon should be in loopback to isolate the XMAC from the PHY */ + WARN_ON(!LOOPBACK_INTERNAL(efx)); + + for (i = 0; i < n_regs; ++i) { + address = regs[i].address; + mask = imask = regs[i].mask; + EFX_INVERT_OWORD(imask); + + efx_reado(efx, &original, address); + + /* bit sweep on and off */ + for (j = 0; j < 128; j++) { + if (!EFX_EXTRACT_OWORD32(mask, j, j)) + continue; + + /* Test this testable bit can be set in isolation */ + EFX_AND_OWORD(reg, original, mask); + EFX_SET_OWORD32(reg, j, j, 1); + + efx_writeo(efx, ®, address); + efx_reado(efx, &buf, address); + + if (efx_masked_compare_oword(®, &buf, &mask)) + goto fail; + + /* Test this testable bit can be cleared in isolation */ + EFX_OR_OWORD(reg, original, mask); + EFX_SET_OWORD32(reg, j, j, 0); + + efx_writeo(efx, ®, address); + efx_reado(efx, &buf, address); + + if (efx_masked_compare_oword(®, &buf, &mask)) + goto fail; + } + + efx_writeo(efx, &original, address); + } + + return 0; + +fail: + EFX_ERR(efx, "wrote "EFX_OWORD_FMT" read "EFX_OWORD_FMT + " at address 0x%x mask "EFX_OWORD_FMT"\n", EFX_OWORD_VAL(reg), + EFX_OWORD_VAL(buf), address, EFX_OWORD_VAL(mask)); + return -EIO; +} + +/************************************************************************** + * + * Special buffer handling + * Special buffers are used for event queues and the TX and RX + * descriptor rings. + * + *************************************************************************/ + +/* + * Initialise a special buffer + * + * This will define a buffer (previously allocated via + * efx_alloc_special_buffer()) in the buffer table, allowing + * it to be used for event queues, descriptor rings etc. + */ +static void +efx_init_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer) +{ + efx_qword_t buf_desc; + int index; + dma_addr_t dma_addr; + int i; + + EFX_BUG_ON_PARANOID(!buffer->addr); + + /* Write buffer descriptors to NIC */ + for (i = 0; i < buffer->entries; i++) { + index = buffer->index + i; + dma_addr = buffer->dma_addr + (i * 4096); + EFX_LOG(efx, "mapping special buffer %d at %llx\n", + index, (unsigned long long)dma_addr); + EFX_POPULATE_QWORD_3(buf_desc, + FRF_AZ_BUF_ADR_REGION, 0, + FRF_AZ_BUF_ADR_FBUF, dma_addr >> 12, + FRF_AZ_BUF_OWNER_ID_FBUF, 0); + efx_write_buf_tbl(efx, &buf_desc, index); + } +} + +/* Unmaps a buffer and clears the buffer table entries */ +static void +efx_fini_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer) +{ + efx_oword_t buf_tbl_upd; + unsigned int start = buffer->index; + unsigned int end = (buffer->index + buffer->entries - 1); + + if (!buffer->entries) + return; + + EFX_LOG(efx, "unmapping special buffers %d-%d\n", + buffer->index, buffer->index + buffer->entries - 1); + + EFX_POPULATE_OWORD_4(buf_tbl_upd, + FRF_AZ_BUF_UPD_CMD, 0, + FRF_AZ_BUF_CLR_CMD, 1, + FRF_AZ_BUF_CLR_END_ID, end, + FRF_AZ_BUF_CLR_START_ID, start); + efx_writeo(efx, &buf_tbl_upd, FR_AZ_BUF_TBL_UPD); +} + +/* + * Allocate a new special buffer + * + * This allocates memory for a new buffer, clears it and allocates a + * new buffer ID range. It does not write into the buffer table. + * + * This call will allocate 4KB buffers, since 8KB buffers can't be + * used for event queues and descriptor rings. + */ +static int efx_alloc_special_buffer(struct efx_nic *efx, + struct efx_special_buffer *buffer, + unsigned int len) +{ + len = ALIGN(len, EFX_BUF_SIZE); + + buffer->addr = pci_alloc_consistent(efx->pci_dev, len, + &buffer->dma_addr); + if (!buffer->addr) + return -ENOMEM; + buffer->len = len; + buffer->entries = len / EFX_BUF_SIZE; + BUG_ON(buffer->dma_addr & (EFX_BUF_SIZE - 1)); + + /* All zeros is a potentially valid event so memset to 0xff */ + memset(buffer->addr, 0xff, len); + + /* Select new buffer ID */ + buffer->index = efx->next_buffer_table; + efx->next_buffer_table += buffer->entries; + + EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x " + "(virt %p phys %llx)\n", buffer->index, + buffer->index + buffer->entries - 1, + (u64)buffer->dma_addr, len, + buffer->addr, (u64)virt_to_phys(buffer->addr)); + + return 0; +} + +static void +efx_free_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer) +{ + if (!buffer->addr) + return; + + EFX_LOG(efx, "deallocating special buffers %d-%d at %llx+%x " + "(virt %p phys %llx)\n", buffer->index, + buffer->index + buffer->entries - 1, + (u64)buffer->dma_addr, buffer->len, + buffer->addr, (u64)virt_to_phys(buffer->addr)); + + pci_free_consistent(efx->pci_dev, buffer->len, buffer->addr, + buffer->dma_addr); + buffer->addr = NULL; + buffer->entries = 0; +} + +/************************************************************************** + * + * Generic buffer handling + * These buffers are used for interrupt status and MAC stats + * + **************************************************************************/ + +int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer, + unsigned int len) +{ + buffer->addr = pci_alloc_consistent(efx->pci_dev, len, + &buffer->dma_addr); + if (!buffer->addr) + return -ENOMEM; + buffer->len = len; + memset(buffer->addr, 0, len); + return 0; +} + +void efx_nic_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer) +{ + if (buffer->addr) { + pci_free_consistent(efx->pci_dev, buffer->len, + buffer->addr, buffer->dma_addr); + buffer->addr = NULL; + } +} + +/************************************************************************** + * + * TX path + * + **************************************************************************/ + +/* Returns a pointer to the specified transmit descriptor in the TX + * descriptor queue belonging to the specified channel. + */ +static inline efx_qword_t * +efx_tx_desc(struct efx_tx_queue *tx_queue, unsigned int index) +{ + return (((efx_qword_t *) (tx_queue->txd.addr)) + index); +} + +/* This writes to the TX_DESC_WPTR; write pointer for TX descriptor ring */ +static inline void efx_notify_tx_desc(struct efx_tx_queue *tx_queue) +{ + unsigned write_ptr; + efx_dword_t reg; + + write_ptr = tx_queue->write_count & EFX_TXQ_MASK; + EFX_POPULATE_DWORD_1(reg, FRF_AZ_TX_DESC_WPTR_DWORD, write_ptr); + efx_writed_page(tx_queue->efx, ®, + FR_AZ_TX_DESC_UPD_DWORD_P0, tx_queue->queue); +} + + +/* For each entry inserted into the software descriptor ring, create a + * descriptor in the hardware TX descriptor ring (in host memory), and + * write a doorbell. + */ +void efx_nic_push_buffers(struct efx_tx_queue *tx_queue) +{ + + struct efx_tx_buffer *buffer; + efx_qword_t *txd; + unsigned write_ptr; + + BUG_ON(tx_queue->write_count == tx_queue->insert_count); + + do { + write_ptr = tx_queue->write_count & EFX_TXQ_MASK; + buffer = &tx_queue->buffer[write_ptr]; + txd = efx_tx_desc(tx_queue, write_ptr); + ++tx_queue->write_count; + + /* Create TX descriptor ring entry */ + EFX_POPULATE_QWORD_4(*txd, + FSF_AZ_TX_KER_CONT, buffer->continuation, + FSF_AZ_TX_KER_BYTE_COUNT, buffer->len, + FSF_AZ_TX_KER_BUF_REGION, 0, + FSF_AZ_TX_KER_BUF_ADDR, buffer->dma_addr); + } while (tx_queue->write_count != tx_queue->insert_count); + + wmb(); /* Ensure descriptors are written before they are fetched */ + efx_notify_tx_desc(tx_queue); +} + +/* Allocate hardware resources for a TX queue */ +int efx_nic_probe_tx(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + BUILD_BUG_ON(EFX_TXQ_SIZE < 512 || EFX_TXQ_SIZE > 4096 || + EFX_TXQ_SIZE & EFX_TXQ_MASK); + return efx_alloc_special_buffer(efx, &tx_queue->txd, + EFX_TXQ_SIZE * sizeof(efx_qword_t)); +} + +void efx_nic_init_tx(struct efx_tx_queue *tx_queue) +{ + efx_oword_t tx_desc_ptr; + struct efx_nic *efx = tx_queue->efx; + + tx_queue->flushed = FLUSH_NONE; + + /* Pin TX descriptor ring */ + efx_init_special_buffer(efx, &tx_queue->txd); + + /* Push TX descriptor ring to card */ + EFX_POPULATE_OWORD_10(tx_desc_ptr, + FRF_AZ_TX_DESCQ_EN, 1, + FRF_AZ_TX_ISCSI_DDIG_EN, 0, + FRF_AZ_TX_ISCSI_HDIG_EN, 0, + FRF_AZ_TX_DESCQ_BUF_BASE_ID, tx_queue->txd.index, + FRF_AZ_TX_DESCQ_EVQ_ID, + tx_queue->channel->channel, + FRF_AZ_TX_DESCQ_OWNER_ID, 0, + FRF_AZ_TX_DESCQ_LABEL, tx_queue->queue, + FRF_AZ_TX_DESCQ_SIZE, + __ffs(tx_queue->txd.entries), + FRF_AZ_TX_DESCQ_TYPE, 0, + FRF_BZ_TX_NON_IP_DROP_DIS, 1); + + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { + int csum = tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM; + EFX_SET_OWORD_FIELD(tx_desc_ptr, FRF_BZ_TX_IP_CHKSM_DIS, !csum); + EFX_SET_OWORD_FIELD(tx_desc_ptr, FRF_BZ_TX_TCP_CHKSM_DIS, + !csum); + } + + efx_writeo_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, + tx_queue->queue); + + if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) { + efx_oword_t reg; + + /* Only 128 bits in this register */ + BUILD_BUG_ON(EFX_TX_QUEUE_COUNT >= 128); + + efx_reado(efx, ®, FR_AA_TX_CHKSM_CFG); + if (tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM) + clear_bit_le(tx_queue->queue, (void *)®); + else + set_bit_le(tx_queue->queue, (void *)®); + efx_writeo(efx, ®, FR_AA_TX_CHKSM_CFG); + } +} + +static void efx_flush_tx_queue(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + efx_oword_t tx_flush_descq; + + tx_queue->flushed = FLUSH_PENDING; + + /* Post a flush command */ + EFX_POPULATE_OWORD_2(tx_flush_descq, + FRF_AZ_TX_FLUSH_DESCQ_CMD, 1, + FRF_AZ_TX_FLUSH_DESCQ, tx_queue->queue); + efx_writeo(efx, &tx_flush_descq, FR_AZ_TX_FLUSH_DESCQ); +} + +void efx_nic_fini_tx(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + efx_oword_t tx_desc_ptr; + + /* The queue should have been flushed */ + WARN_ON(tx_queue->flushed != FLUSH_DONE); + + /* Remove TX descriptor ring from card */ + EFX_ZERO_OWORD(tx_desc_ptr); + efx_writeo_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, + tx_queue->queue); + + /* Unpin TX descriptor ring */ + efx_fini_special_buffer(efx, &tx_queue->txd); +} + +/* Free buffers backing TX queue */ +void efx_nic_remove_tx(struct efx_tx_queue *tx_queue) +{ + efx_free_special_buffer(tx_queue->efx, &tx_queue->txd); +} + +/************************************************************************** + * + * RX path + * + **************************************************************************/ + +/* Returns a pointer to the specified descriptor in the RX descriptor queue */ +static inline efx_qword_t * +efx_rx_desc(struct efx_rx_queue *rx_queue, unsigned int index) +{ + return (((efx_qword_t *) (rx_queue->rxd.addr)) + index); +} + +/* This creates an entry in the RX descriptor queue */ +static inline void +efx_build_rx_desc(struct efx_rx_queue *rx_queue, unsigned index) +{ + struct efx_rx_buffer *rx_buf; + efx_qword_t *rxd; + + rxd = efx_rx_desc(rx_queue, index); + rx_buf = efx_rx_buffer(rx_queue, index); + EFX_POPULATE_QWORD_3(*rxd, + FSF_AZ_RX_KER_BUF_SIZE, + rx_buf->len - + rx_queue->efx->type->rx_buffer_padding, + FSF_AZ_RX_KER_BUF_REGION, 0, + FSF_AZ_RX_KER_BUF_ADDR, rx_buf->dma_addr); +} + +/* This writes to the RX_DESC_WPTR register for the specified receive + * descriptor ring. + */ +void efx_nic_notify_rx_desc(struct efx_rx_queue *rx_queue) +{ + efx_dword_t reg; + unsigned write_ptr; + + while (rx_queue->notified_count != rx_queue->added_count) { + efx_build_rx_desc(rx_queue, + rx_queue->notified_count & + EFX_RXQ_MASK); + ++rx_queue->notified_count; + } + + wmb(); + write_ptr = rx_queue->added_count & EFX_RXQ_MASK; + EFX_POPULATE_DWORD_1(reg, FRF_AZ_RX_DESC_WPTR_DWORD, write_ptr); + efx_writed_page(rx_queue->efx, ®, + FR_AZ_RX_DESC_UPD_DWORD_P0, rx_queue->queue); +} + +int efx_nic_probe_rx(struct efx_rx_queue *rx_queue) +{ + struct efx_nic *efx = rx_queue->efx; + BUILD_BUG_ON(EFX_RXQ_SIZE < 512 || EFX_RXQ_SIZE > 4096 || + EFX_RXQ_SIZE & EFX_RXQ_MASK); + return efx_alloc_special_buffer(efx, &rx_queue->rxd, + EFX_RXQ_SIZE * sizeof(efx_qword_t)); +} + +void efx_nic_init_rx(struct efx_rx_queue *rx_queue) +{ + efx_oword_t rx_desc_ptr; + struct efx_nic *efx = rx_queue->efx; + bool is_b0 = efx_nic_rev(efx) >= EFX_REV_FALCON_B0; + bool iscsi_digest_en = is_b0; + + EFX_LOG(efx, "RX queue %d ring in special buffers %d-%d\n", + rx_queue->queue, rx_queue->rxd.index, + rx_queue->rxd.index + rx_queue->rxd.entries - 1); + + rx_queue->flushed = FLUSH_NONE; + + /* Pin RX descriptor ring */ + efx_init_special_buffer(efx, &rx_queue->rxd); + + /* Push RX descriptor ring to card */ + EFX_POPULATE_OWORD_10(rx_desc_ptr, + FRF_AZ_RX_ISCSI_DDIG_EN, iscsi_digest_en, + FRF_AZ_RX_ISCSI_HDIG_EN, iscsi_digest_en, + FRF_AZ_RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index, + FRF_AZ_RX_DESCQ_EVQ_ID, + rx_queue->channel->channel, + FRF_AZ_RX_DESCQ_OWNER_ID, 0, + FRF_AZ_RX_DESCQ_LABEL, rx_queue->queue, + FRF_AZ_RX_DESCQ_SIZE, + __ffs(rx_queue->rxd.entries), + FRF_AZ_RX_DESCQ_TYPE, 0 /* kernel queue */ , + /* For >=B0 this is scatter so disable */ + FRF_AZ_RX_DESCQ_JUMBO, !is_b0, + FRF_AZ_RX_DESCQ_EN, 1); + efx_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, + rx_queue->queue); +} + +static void efx_flush_rx_queue(struct efx_rx_queue *rx_queue) +{ + struct efx_nic *efx = rx_queue->efx; + efx_oword_t rx_flush_descq; + + rx_queue->flushed = FLUSH_PENDING; + + /* Post a flush command */ + EFX_POPULATE_OWORD_2(rx_flush_descq, + FRF_AZ_RX_FLUSH_DESCQ_CMD, 1, + FRF_AZ_RX_FLUSH_DESCQ, rx_queue->queue); + efx_writeo(efx, &rx_flush_descq, FR_AZ_RX_FLUSH_DESCQ); +} + +void efx_nic_fini_rx(struct efx_rx_queue *rx_queue) +{ + efx_oword_t rx_desc_ptr; + struct efx_nic *efx = rx_queue->efx; + + /* The queue should already have been flushed */ + WARN_ON(rx_queue->flushed != FLUSH_DONE); + + /* Remove RX descriptor ring from card */ + EFX_ZERO_OWORD(rx_desc_ptr); + efx_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, + rx_queue->queue); + + /* Unpin RX descriptor ring */ + efx_fini_special_buffer(efx, &rx_queue->rxd); +} + +/* Free buffers backing RX queue */ +void efx_nic_remove_rx(struct efx_rx_queue *rx_queue) +{ + efx_free_special_buffer(rx_queue->efx, &rx_queue->rxd); +} + +/************************************************************************** + * + * Event queue processing + * Event queues are processed by per-channel tasklets. + * + **************************************************************************/ + +/* Update a channel's event queue's read pointer (RPTR) register + * + * This writes the EVQ_RPTR_REG register for the specified channel's + * event queue. + * + * Note that EVQ_RPTR_REG contains the index of the "last read" event, + * whereas channel->eventq_read_ptr contains the index of the "next to + * read" event. + */ +void efx_nic_eventq_read_ack(struct efx_channel *channel) +{ + efx_dword_t reg; + struct efx_nic *efx = channel->efx; + + EFX_POPULATE_DWORD_1(reg, FRF_AZ_EVQ_RPTR, channel->eventq_read_ptr); + efx_writed_table(efx, ®, efx->type->evq_rptr_tbl_base, + channel->channel); +} + +/* Use HW to insert a SW defined event */ +void efx_generate_event(struct efx_channel *channel, efx_qword_t *event) +{ + efx_oword_t drv_ev_reg; + + BUILD_BUG_ON(FRF_AZ_DRV_EV_DATA_LBN != 0 || + FRF_AZ_DRV_EV_DATA_WIDTH != 64); + drv_ev_reg.u32[0] = event->u32[0]; + drv_ev_reg.u32[1] = event->u32[1]; + drv_ev_reg.u32[2] = 0; + drv_ev_reg.u32[3] = 0; + EFX_SET_OWORD_FIELD(drv_ev_reg, FRF_AZ_DRV_EV_QID, channel->channel); + efx_writeo(channel->efx, &drv_ev_reg, FR_AZ_DRV_EV); +} + +/* Handle a transmit completion event + * + * The NIC batches TX completion events; the message we receive is of + * the form "complete all TX events up to this index". + */ +static void +efx_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) +{ + unsigned int tx_ev_desc_ptr; + unsigned int tx_ev_q_label; + struct efx_tx_queue *tx_queue; + struct efx_nic *efx = channel->efx; + + if (likely(EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_COMP))) { + /* Transmit completion */ + tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_DESC_PTR); + tx_ev_q_label = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_Q_LABEL); + tx_queue = &efx->tx_queue[tx_ev_q_label]; + channel->irq_mod_score += + (tx_ev_desc_ptr - tx_queue->read_count) & + EFX_TXQ_MASK; + efx_xmit_done(tx_queue, tx_ev_desc_ptr); + } else if (EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_WQ_FF_FULL)) { + /* Rewrite the FIFO write pointer */ + tx_ev_q_label = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_Q_LABEL); + tx_queue = &efx->tx_queue[tx_ev_q_label]; + + if (efx_dev_registered(efx)) + netif_tx_lock(efx->net_dev); + efx_notify_tx_desc(tx_queue); + if (efx_dev_registered(efx)) + netif_tx_unlock(efx->net_dev); + } else if (EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_PKT_ERR) && + EFX_WORKAROUND_10727(efx)) { + efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); + } else { + EFX_ERR(efx, "channel %d unexpected TX event " + EFX_QWORD_FMT"\n", channel->channel, + EFX_QWORD_VAL(*event)); + } +} + +/* Detect errors included in the rx_evt_pkt_ok bit. */ +static void efx_handle_rx_not_ok(struct efx_rx_queue *rx_queue, + const efx_qword_t *event, + bool *rx_ev_pkt_ok, + bool *discard) +{ + struct efx_nic *efx = rx_queue->efx; + bool rx_ev_buf_owner_id_err, rx_ev_ip_hdr_chksum_err; + bool rx_ev_tcp_udp_chksum_err, rx_ev_eth_crc_err; + bool rx_ev_frm_trunc, rx_ev_drib_nib, rx_ev_tobe_disc; + bool rx_ev_other_err, rx_ev_pause_frm; + bool rx_ev_hdr_type, rx_ev_mcast_pkt; + unsigned rx_ev_pkt_type; + + rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE); + rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_PKT); + rx_ev_tobe_disc = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_TOBE_DISC); + rx_ev_pkt_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_TYPE); + rx_ev_buf_owner_id_err = EFX_QWORD_FIELD(*event, + FSF_AZ_RX_EV_BUF_OWNER_ID_ERR); + rx_ev_ip_hdr_chksum_err = EFX_QWORD_FIELD(*event, + FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR); + rx_ev_tcp_udp_chksum_err = EFX_QWORD_FIELD(*event, + FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR); + rx_ev_eth_crc_err = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_ETH_CRC_ERR); + rx_ev_frm_trunc = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_FRM_TRUNC); + rx_ev_drib_nib = ((efx_nic_rev(efx) >= EFX_REV_FALCON_B0) ? + 0 : EFX_QWORD_FIELD(*event, FSF_AA_RX_EV_DRIB_NIB)); + rx_ev_pause_frm = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PAUSE_FRM_ERR); + + /* Every error apart from tobe_disc and pause_frm */ + rx_ev_other_err = (rx_ev_drib_nib | rx_ev_tcp_udp_chksum_err | + rx_ev_buf_owner_id_err | rx_ev_eth_crc_err | + rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err); + + /* Count errors that are not in MAC stats. Ignore expected + * checksum errors during self-test. */ + if (rx_ev_frm_trunc) + ++rx_queue->channel->n_rx_frm_trunc; + else if (rx_ev_tobe_disc) + ++rx_queue->channel->n_rx_tobe_disc; + else if (!efx->loopback_selftest) { + if (rx_ev_ip_hdr_chksum_err) + ++rx_queue->channel->n_rx_ip_hdr_chksum_err; + else if (rx_ev_tcp_udp_chksum_err) + ++rx_queue->channel->n_rx_tcp_udp_chksum_err; + } + + /* The frame must be discarded if any of these are true. */ + *discard = (rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_drib_nib | + rx_ev_tobe_disc | rx_ev_pause_frm); + + /* TOBE_DISC is expected on unicast mismatches; don't print out an + * error message. FRM_TRUNC indicates RXDP dropped the packet due + * to a FIFO overflow. + */ +#ifdef EFX_ENABLE_DEBUG + if (rx_ev_other_err) { + EFX_INFO_RL(efx, " RX queue %d unexpected RX event " + EFX_QWORD_FMT "%s%s%s%s%s%s%s%s\n", + rx_queue->queue, EFX_QWORD_VAL(*event), + rx_ev_buf_owner_id_err ? " [OWNER_ID_ERR]" : "", + rx_ev_ip_hdr_chksum_err ? + " [IP_HDR_CHKSUM_ERR]" : "", + rx_ev_tcp_udp_chksum_err ? + " [TCP_UDP_CHKSUM_ERR]" : "", + rx_ev_eth_crc_err ? " [ETH_CRC_ERR]" : "", + rx_ev_frm_trunc ? " [FRM_TRUNC]" : "", + rx_ev_drib_nib ? " [DRIB_NIB]" : "", + rx_ev_tobe_disc ? " [TOBE_DISC]" : "", + rx_ev_pause_frm ? " [PAUSE]" : ""); + } +#endif +} + +/* Handle receive events that are not in-order. */ +static void +efx_handle_rx_bad_index(struct efx_rx_queue *rx_queue, unsigned index) +{ + struct efx_nic *efx = rx_queue->efx; + unsigned expected, dropped; + + expected = rx_queue->removed_count & EFX_RXQ_MASK; + dropped = (index - expected) & EFX_RXQ_MASK; + EFX_INFO(efx, "dropped %d events (index=%d expected=%d)\n", + dropped, index, expected); + + efx_schedule_reset(efx, EFX_WORKAROUND_5676(efx) ? + RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); +} + +/* Handle a packet received event + * + * The NIC gives a "discard" flag if it's a unicast packet with the + * wrong destination address + * Also "is multicast" and "matches multicast filter" flags can be used to + * discard non-matching multicast packets. + */ +static void +efx_handle_rx_event(struct efx_channel *channel, const efx_qword_t *event) +{ + unsigned int rx_ev_desc_ptr, rx_ev_byte_cnt; + unsigned int rx_ev_hdr_type, rx_ev_mcast_pkt; + unsigned expected_ptr; + bool rx_ev_pkt_ok, discard = false, checksummed; + struct efx_rx_queue *rx_queue; + struct efx_nic *efx = channel->efx; + + /* Basic packet information */ + rx_ev_byte_cnt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_BYTE_CNT); + rx_ev_pkt_ok = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_OK); + rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE); + WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_JUMBO_CONT)); + WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_SOP) != 1); + WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_Q_LABEL) != + channel->channel); + + rx_queue = &efx->rx_queue[channel->channel]; + + rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_DESC_PTR); + expected_ptr = rx_queue->removed_count & EFX_RXQ_MASK; + if (unlikely(rx_ev_desc_ptr != expected_ptr)) + efx_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr); + + if (likely(rx_ev_pkt_ok)) { + /* If packet is marked as OK and packet type is TCP/IP or + * UDP/IP, then we can rely on the hardware checksum. + */ + checksummed = + likely(efx->rx_checksum_enabled) && + (rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP || + rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP); + } else { + efx_handle_rx_not_ok(rx_queue, event, &rx_ev_pkt_ok, &discard); + checksummed = false; + } + + /* Detect multicast packets that didn't match the filter */ + rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_PKT); + if (rx_ev_mcast_pkt) { + unsigned int rx_ev_mcast_hash_match = + EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_HASH_MATCH); + + if (unlikely(!rx_ev_mcast_hash_match)) { + ++channel->n_rx_mcast_mismatch; + discard = true; + } + } + + channel->irq_mod_score += 2; + + /* Handle received packet */ + efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt, + checksummed, discard); +} + +/* Global events are basically PHY events */ +static void +efx_handle_global_event(struct efx_channel *channel, efx_qword_t *event) +{ + struct efx_nic *efx = channel->efx; + bool handled = false; + + if (EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_G_PHY0_INTR) || + EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_XG_PHY0_INTR) || + EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_XFP_PHY0_INTR)) { + /* Ignored */ + handled = true; + } + + if ((efx_nic_rev(efx) >= EFX_REV_FALCON_B0) && + EFX_QWORD_FIELD(*event, FSF_BB_GLB_EV_XG_MGT_INTR)) { + efx->xmac_poll_required = true; + handled = true; + } + + if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1 ? + EFX_QWORD_FIELD(*event, FSF_AA_GLB_EV_RX_RECOVERY) : + EFX_QWORD_FIELD(*event, FSF_BB_GLB_EV_RX_RECOVERY)) { + EFX_ERR(efx, "channel %d seen global RX_RESET " + "event. Resetting.\n", channel->channel); + + atomic_inc(&efx->rx_reset); + efx_schedule_reset(efx, EFX_WORKAROUND_6555(efx) ? + RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); + handled = true; + } + + if (!handled) + EFX_ERR(efx, "channel %d unknown global event " + EFX_QWORD_FMT "\n", channel->channel, + EFX_QWORD_VAL(*event)); +} + +static void +efx_handle_driver_event(struct efx_channel *channel, efx_qword_t *event) +{ + struct efx_nic *efx = channel->efx; + unsigned int ev_sub_code; + unsigned int ev_sub_data; + + ev_sub_code = EFX_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBCODE); + ev_sub_data = EFX_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBDATA); + + switch (ev_sub_code) { + case FSE_AZ_TX_DESCQ_FLS_DONE_EV: + EFX_TRACE(efx, "channel %d TXQ %d flushed\n", + channel->channel, ev_sub_data); + break; + case FSE_AZ_RX_DESCQ_FLS_DONE_EV: + EFX_TRACE(efx, "channel %d RXQ %d flushed\n", + channel->channel, ev_sub_data); + break; + case FSE_AZ_EVQ_INIT_DONE_EV: + EFX_LOG(efx, "channel %d EVQ %d initialised\n", + channel->channel, ev_sub_data); + break; + case FSE_AZ_SRM_UPD_DONE_EV: + EFX_TRACE(efx, "channel %d SRAM update done\n", + channel->channel); + break; + case FSE_AZ_WAKE_UP_EV: + EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n", + channel->channel, ev_sub_data); + break; + case FSE_AZ_TIMER_EV: + EFX_TRACE(efx, "channel %d RX queue %d timer expired\n", + channel->channel, ev_sub_data); + break; + case FSE_AA_RX_RECOVER_EV: + EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. " + "Resetting.\n", channel->channel); + atomic_inc(&efx->rx_reset); + efx_schedule_reset(efx, + EFX_WORKAROUND_6555(efx) ? + RESET_TYPE_RX_RECOVERY : + RESET_TYPE_DISABLE); + break; + case FSE_BZ_RX_DSC_ERROR_EV: + EFX_ERR(efx, "RX DMA Q %d reports descriptor fetch error." + " RX Q %d is disabled.\n", ev_sub_data, ev_sub_data); + efx_schedule_reset(efx, RESET_TYPE_RX_DESC_FETCH); + break; + case FSE_BZ_TX_DSC_ERROR_EV: + EFX_ERR(efx, "TX DMA Q %d reports descriptor fetch error." + " TX Q %d is disabled.\n", ev_sub_data, ev_sub_data); + efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); + break; + default: + EFX_TRACE(efx, "channel %d unknown driver event code %d " + "data %04x\n", channel->channel, ev_sub_code, + ev_sub_data); + break; + } +} + +int efx_nic_process_eventq(struct efx_channel *channel, int rx_quota) +{ + unsigned int read_ptr; + efx_qword_t event, *p_event; + int ev_code; + int rx_packets = 0; + + read_ptr = channel->eventq_read_ptr; + + do { + p_event = efx_event(channel, read_ptr); + event = *p_event; + + if (!efx_event_present(&event)) + /* End of events */ + break; + + EFX_TRACE(channel->efx, "channel %d event is "EFX_QWORD_FMT"\n", + channel->channel, EFX_QWORD_VAL(event)); + + /* Clear this event by marking it all ones */ + EFX_SET_QWORD(*p_event); + + ev_code = EFX_QWORD_FIELD(event, FSF_AZ_EV_CODE); + + switch (ev_code) { + case FSE_AZ_EV_CODE_RX_EV: + efx_handle_rx_event(channel, &event); + ++rx_packets; + break; + case FSE_AZ_EV_CODE_TX_EV: + efx_handle_tx_event(channel, &event); + break; + case FSE_AZ_EV_CODE_DRV_GEN_EV: + channel->eventq_magic = EFX_QWORD_FIELD( + event, FSF_AZ_DRV_GEN_EV_MAGIC); + EFX_LOG(channel->efx, "channel %d received generated " + "event "EFX_QWORD_FMT"\n", channel->channel, + EFX_QWORD_VAL(event)); + break; + case FSE_AZ_EV_CODE_GLOBAL_EV: + efx_handle_global_event(channel, &event); + break; + case FSE_AZ_EV_CODE_DRIVER_EV: + efx_handle_driver_event(channel, &event); + break; + case FSE_CZ_EV_CODE_MCDI_EV: + efx_mcdi_process_event(channel, &event); + break; + default: + EFX_ERR(channel->efx, "channel %d unknown event type %d" + " (data " EFX_QWORD_FMT ")\n", channel->channel, + ev_code, EFX_QWORD_VAL(event)); + } + + /* Increment read pointer */ + read_ptr = (read_ptr + 1) & EFX_EVQ_MASK; + + } while (rx_packets < rx_quota); + + channel->eventq_read_ptr = read_ptr; + return rx_packets; +} + + +/* Allocate buffer table entries for event queue */ +int efx_nic_probe_eventq(struct efx_channel *channel) +{ + struct efx_nic *efx = channel->efx; + BUILD_BUG_ON(EFX_EVQ_SIZE < 512 || EFX_EVQ_SIZE > 32768 || + EFX_EVQ_SIZE & EFX_EVQ_MASK); + return efx_alloc_special_buffer(efx, &channel->eventq, + EFX_EVQ_SIZE * sizeof(efx_qword_t)); +} + +void efx_nic_init_eventq(struct efx_channel *channel) +{ + efx_oword_t reg; + struct efx_nic *efx = channel->efx; + + EFX_LOG(efx, "channel %d event queue in special buffers %d-%d\n", + channel->channel, channel->eventq.index, + channel->eventq.index + channel->eventq.entries - 1); + + if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) { + EFX_POPULATE_OWORD_3(reg, + FRF_CZ_TIMER_Q_EN, 1, + FRF_CZ_HOST_NOTIFY_MODE, 0, + FRF_CZ_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS); + efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, channel->channel); + } + + /* Pin event queue buffer */ + efx_init_special_buffer(efx, &channel->eventq); + + /* Fill event queue with all ones (i.e. empty events) */ + memset(channel->eventq.addr, 0xff, channel->eventq.len); + + /* Push event queue to card */ + EFX_POPULATE_OWORD_3(reg, + FRF_AZ_EVQ_EN, 1, + FRF_AZ_EVQ_SIZE, __ffs(channel->eventq.entries), + FRF_AZ_EVQ_BUF_BASE_ID, channel->eventq.index); + efx_writeo_table(efx, ®, efx->type->evq_ptr_tbl_base, + channel->channel); + + efx->type->push_irq_moderation(channel); +} + +void efx_nic_fini_eventq(struct efx_channel *channel) +{ + efx_oword_t reg; + struct efx_nic *efx = channel->efx; + + /* Remove event queue from card */ + EFX_ZERO_OWORD(reg); + efx_writeo_table(efx, ®, efx->type->evq_ptr_tbl_base, + channel->channel); + if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) + efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, channel->channel); + + /* Unpin event queue */ + efx_fini_special_buffer(efx, &channel->eventq); +} + +/* Free buffers backing event queue */ +void efx_nic_remove_eventq(struct efx_channel *channel) +{ + efx_free_special_buffer(channel->efx, &channel->eventq); +} + + +/* Generates a test event on the event queue. A subsequent call to + * process_eventq() should pick up the event and place the value of + * "magic" into channel->eventq_magic; + */ +void efx_nic_generate_test_event(struct efx_channel *channel, unsigned int magic) +{ + efx_qword_t test_event; + + EFX_POPULATE_QWORD_2(test_event, FSF_AZ_EV_CODE, + FSE_AZ_EV_CODE_DRV_GEN_EV, + FSF_AZ_DRV_GEN_EV_MAGIC, magic); + efx_generate_event(channel, &test_event); +} + +/************************************************************************** + * + * Flush handling + * + **************************************************************************/ + + +static void efx_poll_flush_events(struct efx_nic *efx) +{ + struct efx_channel *channel = &efx->channel[0]; + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + unsigned int read_ptr = channel->eventq_read_ptr; + unsigned int end_ptr = (read_ptr - 1) & EFX_EVQ_MASK; + + do { + efx_qword_t *event = efx_event(channel, read_ptr); + int ev_code, ev_sub_code, ev_queue; + bool ev_failed; + + if (!efx_event_present(event)) + break; + + ev_code = EFX_QWORD_FIELD(*event, FSF_AZ_EV_CODE); + ev_sub_code = EFX_QWORD_FIELD(*event, + FSF_AZ_DRIVER_EV_SUBCODE); + if (ev_code == FSE_AZ_EV_CODE_DRIVER_EV && + ev_sub_code == FSE_AZ_TX_DESCQ_FLS_DONE_EV) { + ev_queue = EFX_QWORD_FIELD(*event, + FSF_AZ_DRIVER_EV_SUBDATA); + if (ev_queue < EFX_TX_QUEUE_COUNT) { + tx_queue = efx->tx_queue + ev_queue; + tx_queue->flushed = FLUSH_DONE; + } + } else if (ev_code == FSE_AZ_EV_CODE_DRIVER_EV && + ev_sub_code == FSE_AZ_RX_DESCQ_FLS_DONE_EV) { + ev_queue = EFX_QWORD_FIELD( + *event, FSF_AZ_DRIVER_EV_RX_DESCQ_ID); + ev_failed = EFX_QWORD_FIELD( + *event, FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL); + if (ev_queue < efx->n_rx_queues) { + rx_queue = efx->rx_queue + ev_queue; + rx_queue->flushed = + ev_failed ? FLUSH_FAILED : FLUSH_DONE; + } + } + + /* We're about to destroy the queue anyway, so + * it's ok to throw away every non-flush event */ + EFX_SET_QWORD(*event); + + read_ptr = (read_ptr + 1) & EFX_EVQ_MASK; + } while (read_ptr != end_ptr); + + channel->eventq_read_ptr = read_ptr; +} + +/* Handle tx and rx flushes at the same time, since they run in + * parallel in the hardware and there's no reason for us to + * serialise them */ +int efx_nic_flush_queues(struct efx_nic *efx) +{ + struct efx_rx_queue *rx_queue; + struct efx_tx_queue *tx_queue; + int i, tx_pending, rx_pending; + + /* If necessary prepare the hardware for flushing */ + efx->type->prepare_flush(efx); + + /* Flush all tx queues in parallel */ + efx_for_each_tx_queue(tx_queue, efx) + efx_flush_tx_queue(tx_queue); + + /* The hardware supports four concurrent rx flushes, each of which may + * need to be retried if there is an outstanding descriptor fetch */ + for (i = 0; i < EFX_FLUSH_POLL_COUNT; ++i) { + rx_pending = tx_pending = 0; + efx_for_each_rx_queue(rx_queue, efx) { + if (rx_queue->flushed == FLUSH_PENDING) + ++rx_pending; + } + efx_for_each_rx_queue(rx_queue, efx) { + if (rx_pending == EFX_RX_FLUSH_COUNT) + break; + if (rx_queue->flushed == FLUSH_FAILED || + rx_queue->flushed == FLUSH_NONE) { + efx_flush_rx_queue(rx_queue); + ++rx_pending; + } + } + efx_for_each_tx_queue(tx_queue, efx) { + if (tx_queue->flushed != FLUSH_DONE) + ++tx_pending; + } + + if (rx_pending == 0 && tx_pending == 0) + return 0; + + msleep(EFX_FLUSH_INTERVAL); + efx_poll_flush_events(efx); + } + + /* Mark the queues as all flushed. We're going to return failure + * leading to a reset, or fake up success anyway */ + efx_for_each_tx_queue(tx_queue, efx) { + if (tx_queue->flushed != FLUSH_DONE) + EFX_ERR(efx, "tx queue %d flush command timed out\n", + tx_queue->queue); + tx_queue->flushed = FLUSH_DONE; + } + efx_for_each_rx_queue(rx_queue, efx) { + if (rx_queue->flushed != FLUSH_DONE) + EFX_ERR(efx, "rx queue %d flush command timed out\n", + rx_queue->queue); + rx_queue->flushed = FLUSH_DONE; + } + + if (EFX_WORKAROUND_7803(efx)) + return 0; + + return -ETIMEDOUT; +} + +/************************************************************************** + * + * Hardware interrupts + * The hardware interrupt handler does very little work; all the event + * queue processing is carried out by per-channel tasklets. + * + **************************************************************************/ + +/* Enable/disable/generate interrupts */ +static inline void efx_nic_interrupts(struct efx_nic *efx, + bool enabled, bool force) +{ + efx_oword_t int_en_reg_ker; + unsigned int level = 0; + + if (EFX_WORKAROUND_17213(efx) && !EFX_INT_MODE_USE_MSI(efx)) + /* Set the level always even if we're generating a test + * interrupt, because our legacy interrupt handler is safe */ + level = 0x1f; + + EFX_POPULATE_OWORD_3(int_en_reg_ker, + FRF_AZ_KER_INT_LEVE_SEL, level, + FRF_AZ_KER_INT_KER, force, + FRF_AZ_DRV_INT_EN_KER, enabled); + efx_writeo(efx, &int_en_reg_ker, FR_AZ_INT_EN_KER); +} + +void efx_nic_enable_interrupts(struct efx_nic *efx) +{ + struct efx_channel *channel; + + EFX_ZERO_OWORD(*((efx_oword_t *) efx->irq_status.addr)); + wmb(); /* Ensure interrupt vector is clear before interrupts enabled */ + + /* Enable interrupts */ + efx_nic_interrupts(efx, true, false); + + /* Force processing of all the channels to get the EVQ RPTRs up to + date */ + efx_for_each_channel(channel, efx) + efx_schedule_channel(channel); +} + +void efx_nic_disable_interrupts(struct efx_nic *efx) +{ + /* Disable interrupts */ + efx_nic_interrupts(efx, false, false); +} + +/* Generate a test interrupt + * Interrupt must already have been enabled, otherwise nasty things + * may happen. + */ +void efx_nic_generate_interrupt(struct efx_nic *efx) +{ + efx_nic_interrupts(efx, true, true); +} + +/* Process a fatal interrupt + * Disable bus mastering ASAP and schedule a reset + */ +irqreturn_t efx_nic_fatal_interrupt(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + efx_oword_t *int_ker = efx->irq_status.addr; + efx_oword_t fatal_intr; + int error, mem_perr; + + efx_reado(efx, &fatal_intr, FR_AZ_FATAL_INTR_KER); + error = EFX_OWORD_FIELD(fatal_intr, FRF_AZ_FATAL_INTR); + + EFX_ERR(efx, "SYSTEM ERROR " EFX_OWORD_FMT " status " + EFX_OWORD_FMT ": %s\n", EFX_OWORD_VAL(*int_ker), + EFX_OWORD_VAL(fatal_intr), + error ? "disabling bus mastering" : "no recognised error"); + if (error == 0) + goto out; + + /* If this is a memory parity error dump which blocks are offending */ + mem_perr = EFX_OWORD_FIELD(fatal_intr, FRF_AZ_MEM_PERR_INT_KER); + if (mem_perr) { + efx_oword_t reg; + efx_reado(efx, ®, FR_AZ_MEM_STAT); + EFX_ERR(efx, "SYSTEM ERROR: memory parity error " + EFX_OWORD_FMT "\n", EFX_OWORD_VAL(reg)); + } + + /* Disable both devices */ + pci_clear_master(efx->pci_dev); + if (efx_nic_is_dual_func(efx)) + pci_clear_master(nic_data->pci_dev2); + efx_nic_disable_interrupts(efx); + + /* Count errors and reset or disable the NIC accordingly */ + if (efx->int_error_count == 0 || + time_after(jiffies, efx->int_error_expire)) { + efx->int_error_count = 0; + efx->int_error_expire = + jiffies + EFX_INT_ERROR_EXPIRE * HZ; + } + if (++efx->int_error_count < EFX_MAX_INT_ERRORS) { + EFX_ERR(efx, "SYSTEM ERROR - reset scheduled\n"); + efx_schedule_reset(efx, RESET_TYPE_INT_ERROR); + } else { + EFX_ERR(efx, "SYSTEM ERROR - max number of errors seen." + "NIC will be disabled\n"); + efx_schedule_reset(efx, RESET_TYPE_DISABLE); + } +out: + return IRQ_HANDLED; +} + +/* Handle a legacy interrupt + * Acknowledges the interrupt and schedule event queue processing. + */ +static irqreturn_t efx_legacy_interrupt(int irq, void *dev_id) +{ + struct efx_nic *efx = dev_id; + efx_oword_t *int_ker = efx->irq_status.addr; + irqreturn_t result = IRQ_NONE; + struct efx_channel *channel; + efx_dword_t reg; + u32 queues; + int syserr; + + /* Read the ISR which also ACKs the interrupts */ + efx_readd(efx, ®, FR_BZ_INT_ISR0); + queues = EFX_EXTRACT_DWORD(reg, 0, 31); + + /* Check to see if we have a serious error condition */ + syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); + if (unlikely(syserr)) + return efx_nic_fatal_interrupt(efx); + + if (queues != 0) { + if (EFX_WORKAROUND_15783(efx)) + efx->irq_zero_count = 0; + + /* Schedule processing of any interrupting queues */ + efx_for_each_channel(channel, efx) { + if (queues & 1) + efx_schedule_channel(channel); + queues >>= 1; + } + result = IRQ_HANDLED; + + } else if (EFX_WORKAROUND_15783(efx) && + efx->irq_zero_count++ == 0) { + efx_qword_t *event; + + /* Ensure we rearm all event queues */ + efx_for_each_channel(channel, efx) { + event = efx_event(channel, channel->eventq_read_ptr); + if (efx_event_present(event)) + efx_schedule_channel(channel); + } + + result = IRQ_HANDLED; + } + + if (result == IRQ_HANDLED) { + efx->last_irq_cpu = raw_smp_processor_id(); + EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_DWORD_FMT "\n", + irq, raw_smp_processor_id(), EFX_DWORD_VAL(reg)); + } + + return result; +} + +/* Handle an MSI interrupt + * + * Handle an MSI hardware interrupt. This routine schedules event + * queue processing. No interrupt acknowledgement cycle is necessary. + * Also, we never need to check that the interrupt is for us, since + * MSI interrupts cannot be shared. + */ +static irqreturn_t efx_msi_interrupt(int irq, void *dev_id) +{ + struct efx_channel *channel = dev_id; + struct efx_nic *efx = channel->efx; + efx_oword_t *int_ker = efx->irq_status.addr; + int syserr; + + efx->last_irq_cpu = raw_smp_processor_id(); + EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_OWORD_FMT "\n", + irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker)); + + /* Check to see if we have a serious error condition */ + syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); + if (unlikely(syserr)) + return efx_nic_fatal_interrupt(efx); + + /* Schedule processing of the channel */ + efx_schedule_channel(channel); + + return IRQ_HANDLED; +} + + +/* Setup RSS indirection table. + * This maps from the hash value of the packet to RXQ + */ +static void efx_setup_rss_indir_table(struct efx_nic *efx) +{ + int i = 0; + unsigned long offset; + efx_dword_t dword; + + if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) + return; + + for (offset = FR_BZ_RX_INDIRECTION_TBL; + offset < FR_BZ_RX_INDIRECTION_TBL + 0x800; + offset += 0x10) { + EFX_POPULATE_DWORD_1(dword, FRF_BZ_IT_QUEUE, + i % efx->n_rx_queues); + efx_writed(efx, &dword, offset); + i++; + } +} + +/* Hook interrupt handler(s) + * Try MSI and then legacy interrupts. + */ +int efx_nic_init_interrupt(struct efx_nic *efx) +{ + struct efx_channel *channel; + int rc; + + if (!EFX_INT_MODE_USE_MSI(efx)) { + irq_handler_t handler; + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) + handler = efx_legacy_interrupt; + else + handler = falcon_legacy_interrupt_a1; + + rc = request_irq(efx->legacy_irq, handler, IRQF_SHARED, + efx->name, efx); + if (rc) { + EFX_ERR(efx, "failed to hook legacy IRQ %d\n", + efx->pci_dev->irq); + goto fail1; + } + return 0; + } + + /* Hook MSI or MSI-X interrupt */ + efx_for_each_channel(channel, efx) { + rc = request_irq(channel->irq, efx_msi_interrupt, + IRQF_PROBE_SHARED, /* Not shared */ + channel->name, channel); + if (rc) { + EFX_ERR(efx, "failed to hook IRQ %d\n", channel->irq); + goto fail2; + } + } + + return 0; + + fail2: + efx_for_each_channel(channel, efx) + free_irq(channel->irq, channel); + fail1: + return rc; +} + +void efx_nic_fini_interrupt(struct efx_nic *efx) +{ + struct efx_channel *channel; + efx_oword_t reg; + + /* Disable MSI/MSI-X interrupts */ + efx_for_each_channel(channel, efx) { + if (channel->irq) + free_irq(channel->irq, channel); + } + + /* ACK legacy interrupt */ + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) + efx_reado(efx, ®, FR_BZ_INT_ISR0); + else + falcon_irq_ack_a1(efx); + + /* Disable legacy interrupt */ + if (efx->legacy_irq) + free_irq(efx->legacy_irq, efx); +} + +u32 efx_nic_fpga_ver(struct efx_nic *efx) +{ + efx_oword_t altera_build; + efx_reado(efx, &altera_build, FR_AZ_ALTERA_BUILD); + return EFX_OWORD_FIELD(altera_build, FRF_AZ_ALTERA_BUILD_VER); +} + +void efx_nic_init_common(struct efx_nic *efx) +{ + efx_oword_t temp; + + /* Set positions of descriptor caches in SRAM. */ + EFX_POPULATE_OWORD_1(temp, FRF_AZ_SRM_TX_DC_BASE_ADR, + efx->type->tx_dc_base / 8); + efx_writeo(efx, &temp, FR_AZ_SRM_TX_DC_CFG); + EFX_POPULATE_OWORD_1(temp, FRF_AZ_SRM_RX_DC_BASE_ADR, + efx->type->rx_dc_base / 8); + efx_writeo(efx, &temp, FR_AZ_SRM_RX_DC_CFG); + + /* Set TX descriptor cache size. */ + BUILD_BUG_ON(TX_DC_ENTRIES != (8 << TX_DC_ENTRIES_ORDER)); + EFX_POPULATE_OWORD_1(temp, FRF_AZ_TX_DC_SIZE, TX_DC_ENTRIES_ORDER); + efx_writeo(efx, &temp, FR_AZ_TX_DC_CFG); + + /* Set RX descriptor cache size. Set low watermark to size-8, as + * this allows most efficient prefetching. + */ + BUILD_BUG_ON(RX_DC_ENTRIES != (8 << RX_DC_ENTRIES_ORDER)); + EFX_POPULATE_OWORD_1(temp, FRF_AZ_RX_DC_SIZE, RX_DC_ENTRIES_ORDER); + efx_writeo(efx, &temp, FR_AZ_RX_DC_CFG); + EFX_POPULATE_OWORD_1(temp, FRF_AZ_RX_DC_PF_LWM, RX_DC_ENTRIES - 8); + efx_writeo(efx, &temp, FR_AZ_RX_DC_PF_WM); + + /* Program INT_KER address */ + EFX_POPULATE_OWORD_2(temp, + FRF_AZ_NORM_INT_VEC_DIS_KER, + EFX_INT_MODE_USE_MSI(efx), + FRF_AZ_INT_ADR_KER, efx->irq_status.dma_addr); + efx_writeo(efx, &temp, FR_AZ_INT_ADR_KER); + + /* Enable all the genuinely fatal interrupts. (They are still + * masked by the overall interrupt mask, controlled by + * falcon_interrupts()). + * + * Note: All other fatal interrupts are enabled + */ + EFX_POPULATE_OWORD_3(temp, + FRF_AZ_ILL_ADR_INT_KER_EN, 1, + FRF_AZ_RBUF_OWN_INT_KER_EN, 1, + FRF_AZ_TBUF_OWN_INT_KER_EN, 1); + EFX_INVERT_OWORD(temp); + efx_writeo(efx, &temp, FR_AZ_FATAL_INTR_KER); + + efx_setup_rss_indir_table(efx); + + /* Disable the ugly timer-based TX DMA backoff and allow TX DMA to be + * controlled by the RX FIFO fill level. Set arbitration to one pkt/Q. + */ + efx_reado(efx, &temp, FR_AZ_TX_RESERVED); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER, 0xfe); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER_EN, 1); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_ONE_PKT_PER_Q, 1); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PUSH_EN, 0); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_DIS_NON_IP_EV, 1); + /* Enable SW_EV to inherit in char driver - assume harmless here */ + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_SOFT_EVT_EN, 1); + /* Prefetch threshold 2 => fetch when descriptor cache half empty */ + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PREF_THRESHOLD, 2); + /* Squash TX of packets of 16 bytes or less */ + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) + EFX_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1); + efx_writeo(efx, &temp, FR_AZ_TX_RESERVED); +} diff --git a/drivers/net/sfc/nic.h b/drivers/net/sfc/nic.h new file mode 100644 index 0000000..9351c03 --- /dev/null +++ b/drivers/net/sfc/nic.h @@ -0,0 +1,261 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_NIC_H +#define EFX_NIC_H + +#include <linux/i2c-algo-bit.h> +#include "net_driver.h" +#include "efx.h" +#include "mcdi.h" + +/* + * Falcon hardware control + */ + +enum { + EFX_REV_FALCON_A0 = 0, + EFX_REV_FALCON_A1 = 1, + EFX_REV_FALCON_B0 = 2, + EFX_REV_SIENA_A0 = 3, +}; + +static inline int efx_nic_rev(struct efx_nic *efx) +{ + return efx->type->revision; +} + +extern u32 efx_nic_fpga_ver(struct efx_nic *efx); + +static inline bool efx_nic_has_mc(struct efx_nic *efx) +{ + return efx_nic_rev(efx) >= EFX_REV_SIENA_A0; +} +/* NIC has two interlinked PCI functions for the same port. */ +static inline bool efx_nic_is_dual_func(struct efx_nic *efx) +{ + return efx_nic_rev(efx) < EFX_REV_FALCON_B0; +} + +enum { + PHY_TYPE_NONE = 0, + PHY_TYPE_TXC43128 = 1, + PHY_TYPE_88E1111 = 2, + PHY_TYPE_SFX7101 = 3, + PHY_TYPE_QT2022C2 = 4, + PHY_TYPE_PM8358 = 6, + PHY_TYPE_SFT9001A = 8, + PHY_TYPE_QT2025C = 9, + PHY_TYPE_SFT9001B = 10, +}; + +#define FALCON_XMAC_LOOPBACKS \ + ((1 << LOOPBACK_XGMII) | \ + (1 << LOOPBACK_XGXS) | \ + (1 << LOOPBACK_XAUI)) + +#define FALCON_GMAC_LOOPBACKS \ + (1 << LOOPBACK_GMAC) + +/** + * struct falcon_board_type - board operations and type information + * @id: Board type id, as found in NVRAM + * @ref_model: Model number of Solarflare reference design + * @gen_type: Generic board type description + * @init: Allocate resources and initialise peripheral hardware + * @init_phy: Do board-specific PHY initialisation + * @fini: Shut down hardware and free resources + * @set_id_led: Set state of identifying LED or revert to automatic function + * @monitor: Board-specific health check function + */ +struct falcon_board_type { + u8 id; + const char *ref_model; + const char *gen_type; + int (*init) (struct efx_nic *nic); + void (*init_phy) (struct efx_nic *efx); + void (*fini) (struct efx_nic *nic); + void (*set_id_led) (struct efx_nic *efx, enum efx_led_mode mode); + int (*monitor) (struct efx_nic *nic); +}; + +/** + * struct falcon_board - board information + * @type: Type of board + * @major: Major rev. ('A', 'B' ...) + * @minor: Minor rev. (0, 1, ...) + * @i2c_adap: I2C adapter for on-board peripherals + * @i2c_data: Data for bit-banging algorithm + * @hwmon_client: I2C client for hardware monitor + * @ioexp_client: I2C client for power/port control + */ +struct falcon_board { + const struct falcon_board_type *type; + int major; + int minor; + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_data; + struct i2c_client *hwmon_client, *ioexp_client; +}; + +/** + * struct falcon_nic_data - Falcon NIC state + * @pci_dev2: Secondary function of Falcon A + * @board: Board state and functions + * @stats_disable_count: Nest count for disabling statistics fetches + * @stats_pending: Is there a pending DMA of MAC statistics. + * @stats_timer: A timer for regularly fetching MAC statistics. + * @stats_dma_done: Pointer to the flag which indicates DMA completion. + */ +struct falcon_nic_data { + struct pci_dev *pci_dev2; + struct falcon_board board; + unsigned int stats_disable_count; + bool stats_pending; + struct timer_list stats_timer; + u32 *stats_dma_done; +}; + +static inline struct falcon_board *falcon_board(struct efx_nic *efx) +{ + struct falcon_nic_data *data = efx->nic_data; + return &data->board; +} + +/** + * struct siena_nic_data - Siena NIC state + * @fw_version: Management controller firmware version + * @fw_build: Firmware build number + * @mcdi: Management-Controller-to-Driver Interface + * @wol_filter_id: Wake-on-LAN packet filter id + */ +struct siena_nic_data { + u64 fw_version; + u32 fw_build; + struct efx_mcdi_iface mcdi; + int wol_filter_id; +}; + +extern void siena_print_fwver(struct efx_nic *efx, char *buf, size_t len); + +extern struct efx_nic_type falcon_a1_nic_type; +extern struct efx_nic_type falcon_b0_nic_type; +extern struct efx_nic_type siena_a0_nic_type; + +/************************************************************************** + * + * Externs + * + ************************************************************************** + */ + +extern void falcon_probe_board(struct efx_nic *efx, u16 revision_info); + +/* TX data path */ +extern int efx_nic_probe_tx(struct efx_tx_queue *tx_queue); +extern void efx_nic_init_tx(struct efx_tx_queue *tx_queue); +extern void efx_nic_fini_tx(struct efx_tx_queue *tx_queue); +extern void efx_nic_remove_tx(struct efx_tx_queue *tx_queue); +extern void efx_nic_push_buffers(struct efx_tx_queue *tx_queue); + +/* RX data path */ +extern int efx_nic_probe_rx(struct efx_rx_queue *rx_queue); +extern void efx_nic_init_rx(struct efx_rx_queue *rx_queue); +extern void efx_nic_fini_rx(struct efx_rx_queue *rx_queue); +extern void efx_nic_remove_rx(struct efx_rx_queue *rx_queue); +extern void efx_nic_notify_rx_desc(struct efx_rx_queue *rx_queue); + +/* Event data path */ +extern int efx_nic_probe_eventq(struct efx_channel *channel); +extern void efx_nic_init_eventq(struct efx_channel *channel); +extern void efx_nic_fini_eventq(struct efx_channel *channel); +extern void efx_nic_remove_eventq(struct efx_channel *channel); +extern int efx_nic_process_eventq(struct efx_channel *channel, int rx_quota); +extern void efx_nic_eventq_read_ack(struct efx_channel *channel); + +/* MAC/PHY */ +extern void falcon_drain_tx_fifo(struct efx_nic *efx); +extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx); +extern int efx_nic_rx_xoff_thresh, efx_nic_rx_xon_thresh; + +/* Interrupts and test events */ +extern int efx_nic_init_interrupt(struct efx_nic *efx); +extern void efx_nic_enable_interrupts(struct efx_nic *efx); +extern void efx_nic_generate_test_event(struct efx_channel *channel, + unsigned int magic); +extern void efx_nic_generate_interrupt(struct efx_nic *efx); +extern void efx_nic_disable_interrupts(struct efx_nic *efx); +extern void efx_nic_fini_interrupt(struct efx_nic *efx); +extern irqreturn_t efx_nic_fatal_interrupt(struct efx_nic *efx); +extern irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id); +extern void falcon_irq_ack_a1(struct efx_nic *efx); + +#define EFX_IRQ_MOD_RESOLUTION 5 + +/* Global Resources */ +extern int efx_nic_flush_queues(struct efx_nic *efx); +extern void falcon_start_nic_stats(struct efx_nic *efx); +extern void falcon_stop_nic_stats(struct efx_nic *efx); +extern int falcon_reset_xaui(struct efx_nic *efx); +extern void efx_nic_init_common(struct efx_nic *efx); + +int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer, + unsigned int len); +void efx_nic_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer); + +/* Tests */ +struct efx_nic_register_test { + unsigned address; + efx_oword_t mask; +}; +extern int efx_nic_test_registers(struct efx_nic *efx, + const struct efx_nic_register_test *regs, + size_t n_regs); + +/************************************************************************** + * + * Falcon MAC stats + * + ************************************************************************** + */ + +#define FALCON_STAT_OFFSET(falcon_stat) EFX_VAL(falcon_stat, offset) +#define FALCON_STAT_WIDTH(falcon_stat) EFX_VAL(falcon_stat, WIDTH) + +/* Retrieve statistic from statistics block */ +#define FALCON_STAT(efx, falcon_stat, efx_stat) do { \ + if (FALCON_STAT_WIDTH(falcon_stat) == 16) \ + (efx)->mac_stats.efx_stat += le16_to_cpu( \ + *((__force __le16 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + else if (FALCON_STAT_WIDTH(falcon_stat) == 32) \ + (efx)->mac_stats.efx_stat += le32_to_cpu( \ + *((__force __le32 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + else \ + (efx)->mac_stats.efx_stat += le64_to_cpu( \ + *((__force __le64 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + } while (0) + +#define FALCON_MAC_STATS_SIZE 0x100 + +#define MAC_DATA_LBN 0 +#define MAC_DATA_WIDTH 32 + +extern void efx_nic_generate_event(struct efx_channel *channel, + efx_qword_t *event); + +extern void falcon_poll_xmac(struct efx_nic *efx); + +#endif /* EFX_NIC_H */ diff --git a/drivers/net/sfc/phy.h b/drivers/net/sfc/phy.h index 2ad1cec..5bc2613 100644 --- a/drivers/net/sfc/phy.h +++ b/drivers/net/sfc/phy.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2008 Solarflare Communications Inc. + * Copyright 2007-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 @@ -41,4 +41,21 @@ extern struct efx_phy_operations falcon_qt202x_phy_ops; extern void falcon_qt202x_set_led(struct efx_nic *p, int led, int state); +/**************************************************************************** + * Siena managed PHYs + */ +extern struct efx_phy_operations efx_mcdi_phy_ops; + +extern int efx_mcdi_mdio_read(struct efx_nic *efx, unsigned int bus, + unsigned int prtad, unsigned int devad, + u16 addr, u16 *value_out, u32 *status_out); +extern int efx_mcdi_mdio_write(struct efx_nic *efx, unsigned int bus, + unsigned int prtad, unsigned int devad, + u16 addr, u16 value, u32 *status_out); +extern void efx_mcdi_phy_decode_link(struct efx_nic *efx, + struct efx_link_state *link_state, + u32 speed, u32 flags, u32 fcntl); +extern int efx_mcdi_phy_reconfigure(struct efx_nic *efx); +extern void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa); + #endif diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c index 8208ac0..3800fc7 100644 --- a/drivers/net/sfc/qt202x_phy.c +++ b/drivers/net/sfc/qt202x_phy.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * 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 @@ -15,7 +15,7 @@ #include "efx.h" #include "mdio_10g.h" #include "phy.h" -#include "falcon.h" +#include "nic.h" #define QT202X_REQUIRED_DEVS (MDIO_DEVS_PCS | \ MDIO_DEVS_PMAPMD | \ @@ -23,7 +23,7 @@ #define QT202X_LOOPBACKS ((1 << LOOPBACK_PCS) | \ (1 << LOOPBACK_PMAPMD) | \ - (1 << LOOPBACK_NETWORK)) + (1 << LOOPBACK_PHYXS_WS)) /****************************************************************************/ /* Quake-specific MDIO registers */ @@ -126,7 +126,7 @@ static int qt202x_reset_phy(struct efx_nic *efx) if (rc < 0) goto fail; - falcon_board(efx)->init_phy(efx); + falcon_board(efx)->type->init_phy(efx); return rc; @@ -135,42 +135,38 @@ static int qt202x_reset_phy(struct efx_nic *efx) return rc; } +static int qt202x_phy_probe(struct efx_nic *efx) +{ + efx->mdio.mmds = QT202X_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + efx->loopback_modes = QT202X_LOOPBACKS | FALCON_XMAC_LOOPBACKS; + return 0; +} + static int qt202x_phy_init(struct efx_nic *efx) { struct qt202x_phy_data *phy_data; - u32 devid = efx_mdio_read_id(efx, MDIO_MMD_PHYXS); + u32 devid; int rc; + rc = qt202x_reset_phy(efx); + if (rc) { + EFX_ERR(efx, "PHY init failed\n"); + return rc; + } + phy_data = kzalloc(sizeof(struct qt202x_phy_data), GFP_KERNEL); if (!phy_data) return -ENOMEM; efx->phy_data = phy_data; + devid = efx_mdio_read_id(efx, MDIO_MMD_PHYXS); EFX_INFO(efx, "PHY ID reg %x (OUI %06x model %02x revision %x)\n", devid, efx_mdio_id_oui(devid), efx_mdio_id_model(devid), efx_mdio_id_rev(devid)); phy_data->phy_mode = efx->phy_mode; - - rc = qt202x_reset_phy(efx); - - EFX_INFO(efx, "PHY init %s.\n", - rc ? "failed" : "successful"); - if (rc < 0) - goto fail; - return 0; - - fail: - kfree(efx->phy_data); - efx->phy_data = NULL; - return rc; -} - -static void qt202x_phy_clear_interrupt(struct efx_nic *efx) -{ - /* Read to clear link status alarm */ - efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT); } static int qt202x_link_ok(struct efx_nic *efx) @@ -178,18 +174,21 @@ static int qt202x_link_ok(struct efx_nic *efx) return efx_mdio_links_ok(efx, QT202X_REQUIRED_DEVS); } -static void qt202x_phy_poll(struct efx_nic *efx) +static bool qt202x_phy_poll(struct efx_nic *efx) { - int link_up = qt202x_link_ok(efx); - /* Simulate a PHY event if link state has changed */ - if (link_up != efx->link_state.up) - falcon_sim_phy_event(efx); + bool was_up = efx->link_state.up; + + efx->link_state.up = qt202x_link_ok(efx); + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->link_state.fc = efx->wanted_fc; + + 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 efx_link_state *link_state = &efx->link_state; if (efx->phy_type == PHY_TYPE_QT2025C) { /* There are several different register bits which can @@ -216,10 +215,8 @@ static void qt202x_phy_reconfigure(struct efx_nic *efx) efx_mdio_phy_reconfigure(efx); phy_data->phy_mode = efx->phy_mode; - link_state->up = qt202x_link_ok(efx); - link_state->speed = 10000; - link_state->fd = true; - link_state->fc = efx->wanted_fc; + + return 0; } static void qt202x_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) @@ -235,14 +232,11 @@ static void qt202x_phy_fini(struct efx_nic *efx) } struct efx_phy_operations falcon_qt202x_phy_ops = { - .macs = EFX_XMAC, + .probe = qt202x_phy_probe, .init = qt202x_phy_init, .reconfigure = qt202x_phy_reconfigure, .poll = qt202x_phy_poll, .fini = qt202x_phy_fini, - .clear_interrupt = qt202x_phy_clear_interrupt, .get_settings = qt202x_phy_get_settings, .set_settings = efx_mdio_set_settings, - .mmds = QT202X_REQUIRED_DEVS, - .loopbacks = QT202X_LOOPBACKS, }; diff --git a/drivers/net/sfc/regs.h b/drivers/net/sfc/regs.h index f336d83..89d606f 100644 --- a/drivers/net/sfc/regs.h +++ b/drivers/net/sfc/regs.h @@ -3106,18 +3106,6 @@ #define FALCON_STATS_NOT_DONE 0x00000000 #define FALCON_STATS_DONE 0xffffffff -/* Interrupt status register bits */ -#define FATAL_INT_LBN 64 -#define FATAL_INT_WIDTH 1 -#define INT_EVQS_LBN 40 -#define INT_EVQS_WIDTH 4 -#define INT_FLAG_LBN 32 -#define INT_FLAG_WIDTH 1 -#define EVQ_FIFO_HF_LBN 1 -#define EVQ_FIFO_HF_WIDTH 1 -#define EVQ_FIFO_AF_LBN 0 -#define EVQ_FIFO_AF_WIDTH 1 - /************************************************************************** * * Falcon non-volatile configuration diff --git a/drivers/net/sfc/rx.c b/drivers/net/sfc/rx.c index accf055..a97c923 100644 --- a/drivers/net/sfc/rx.c +++ b/drivers/net/sfc/rx.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 @@ -17,7 +17,7 @@ #include <net/checksum.h> #include "net_driver.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #include "selftest.h" #include "workarounds.h" @@ -341,7 +341,7 @@ static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue, out: /* Send write pointer to card. */ - falcon_notify_rx_desc(rx_queue); + efx_nic_notify_rx_desc(rx_queue); /* If the fast fill is running inside from the refill tasklet, then * for SMP systems it may be running on a different CPU to @@ -640,7 +640,7 @@ int efx_probe_rx_queue(struct efx_rx_queue *rx_queue) if (!rx_queue->buffer) return -ENOMEM; - rc = falcon_probe_rx(rx_queue); + rc = efx_nic_probe_rx(rx_queue); if (rc) { kfree(rx_queue->buffer); rx_queue->buffer = NULL; @@ -671,7 +671,7 @@ void efx_init_rx_queue(struct efx_rx_queue *rx_queue) rx_queue->fast_fill_limit = limit; /* Set up RX descriptor ring */ - falcon_init_rx(rx_queue); + efx_nic_init_rx(rx_queue); } void efx_fini_rx_queue(struct efx_rx_queue *rx_queue) @@ -681,7 +681,7 @@ void efx_fini_rx_queue(struct efx_rx_queue *rx_queue) EFX_LOG(rx_queue->efx, "shutting down RX queue %d\n", rx_queue->queue); - falcon_fini_rx(rx_queue); + efx_nic_fini_rx(rx_queue); /* Release RX buffers NB start at index 0 not current HW ptr */ if (rx_queue->buffer) { @@ -706,7 +706,7 @@ void efx_remove_rx_queue(struct efx_rx_queue *rx_queue) { EFX_LOG(rx_queue->efx, "destroying RX queue %d\n", rx_queue->queue); - falcon_remove_rx(rx_queue); + efx_nic_remove_rx(rx_queue); kfree(rx_queue->buffer); rx_queue->buffer = NULL; diff --git a/drivers/net/sfc/selftest.c b/drivers/net/sfc/selftest.c index 63ff295..14949bb 100644 --- a/drivers/net/sfc/selftest.c +++ b/drivers/net/sfc/selftest.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 @@ -21,7 +21,7 @@ #include <asm/io.h> #include "net_driver.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #include "selftest.h" #include "workarounds.h" #include "spi.h" @@ -55,6 +55,7 @@ static const char *payload_msg = * @flush: Drop all packets in efx_loopback_rx_packet * @packet_count: Number of packets being used in this test * @skbs: An array of skbs transmitted + * @offload_csum: Checksums are being offloaded * @rx_good: RX good packet count * @rx_bad: RX bad packet count * @payload: Payload used in tests @@ -63,10 +64,7 @@ struct efx_loopback_state { bool flush; int packet_count; struct sk_buff **skbs; - - /* Checksums are being offloaded */ bool offload_csum; - atomic_t rx_good; atomic_t rx_bad; struct efx_loopback_payload payload; @@ -102,7 +100,7 @@ static int efx_test_mdio(struct efx_nic *efx, struct efx_self_tests *tests) } if (EFX_IS10G(efx)) { - rc = efx_mdio_check_mmds(efx, efx->phy_op->mmds, 0); + rc = efx_mdio_check_mmds(efx, efx->mdio.mmds, 0); if (rc) goto out; } @@ -115,23 +113,26 @@ out: static int efx_test_nvram(struct efx_nic *efx, struct efx_self_tests *tests) { - int rc; + int rc = 0; + + if (efx->type->test_nvram) { + rc = efx->type->test_nvram(efx); + tests->nvram = rc ? -1 : 1; + } - rc = falcon_read_nvram(efx, NULL); - tests->nvram = rc ? -1 : 1; return rc; } static int efx_test_chip(struct efx_nic *efx, struct efx_self_tests *tests) { - int rc; + int rc = 0; - /* Not supported on A-series silicon */ - if (falcon_rev(efx) < FALCON_REV_B0) - return 0; + /* Test register access */ + if (efx->type->test_registers) { + rc = efx->type->test_registers(efx); + tests->registers = rc ? -1 : 1; + } - rc = falcon_test_registers(efx); - tests->registers = rc ? -1 : 1; return rc; } @@ -163,7 +164,7 @@ static int efx_test_interrupts(struct efx_nic *efx, goto success; } - falcon_generate_interrupt(efx); + efx_nic_generate_interrupt(efx); /* Wait for arrival of test interrupt. */ EFX_LOG(efx, "waiting for test interrupt\n"); @@ -201,7 +202,7 @@ static int efx_test_eventq_irq(struct efx_channel *channel, channel->eventq_magic = 0; smp_wmb(); - falcon_generate_test_event(channel, magic); + efx_nic_generate_test_event(channel, magic); /* Wait for arrival of interrupt */ count = 0; @@ -252,9 +253,6 @@ static int efx_test_phy(struct efx_nic *efx, struct efx_self_tests *tests, if (!efx->phy_op->run_tests) return 0; - EFX_BUG_ON_PARANOID(efx->phy_op->num_tests == 0 || - efx->phy_op->num_tests > EFX_MAX_PHY_TESTS); - mutex_lock(&efx->mac_lock); rc = efx->phy_op->run_tests(efx, tests->phy, flags); mutex_unlock(&efx->mac_lock); @@ -437,7 +435,6 @@ static int efx_begin_loopback(struct efx_tx_queue *tx_queue) kfree_skb(skb); return -EPIPE; } - efx->net_dev->trans_start = jiffies; } return 0; @@ -566,14 +563,49 @@ efx_test_loopback(struct efx_tx_queue *tx_queue, return 0; } +/* Wait for link up. On Falcon, we would prefer to rely on efx_monitor, but + * any contention on the mac lock (via e.g. efx_mac_mcast_work) causes it + * to delay and retry. Therefore, it's safer to just poll directly. Wait + * for link up and any faults to dissipate. */ +static int efx_wait_for_link(struct efx_nic *efx) +{ + struct efx_link_state *link_state = &efx->link_state; + int count; + bool link_up; + + for (count = 0; count < 40; count++) { + schedule_timeout_uninterruptible(HZ / 10); + + if (efx->type->monitor != NULL) { + mutex_lock(&efx->mac_lock); + efx->type->monitor(efx); + mutex_unlock(&efx->mac_lock); + } else { + struct efx_channel *channel = &efx->channel[0]; + if (channel->work_pending) + efx_process_channel_now(channel); + } + + mutex_lock(&efx->mac_lock); + link_up = link_state->up; + if (link_up) + link_up = !efx->mac_op->check_fault(efx); + mutex_unlock(&efx->mac_lock); + + if (link_up) + return 0; + } + + return -ETIMEDOUT; +} + static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests, unsigned int loopback_modes) { enum efx_loopback_mode mode; struct efx_loopback_state *state; struct efx_tx_queue *tx_queue; - bool link_up; - int count, rc = 0; + int rc = 0; /* Set the port loopback_selftest member. From this point on * all received packets will be dropped. Mark the state as @@ -592,46 +624,23 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests, /* Move the port into the specified loopback mode. */ state->flush = true; + mutex_lock(&efx->mac_lock); efx->loopback_mode = mode; - efx_reconfigure_port(efx); - - /* Wait for the PHY to signal the link is up. Interrupts - * are enabled for PHY's using LASI, otherwise we poll() - * quickly */ - count = 0; - do { - struct efx_channel *channel = &efx->channel[0]; + rc = __efx_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); + if (rc) { + EFX_ERR(efx, "unable to move into %s loopback\n", + LOOPBACK_MODE(efx)); + goto out; + } - efx->phy_op->poll(efx); - schedule_timeout_uninterruptible(HZ / 10); - if (channel->work_pending) - efx_process_channel_now(channel); - /* Wait for PHY events to be processed */ - flush_workqueue(efx->workqueue); - rmb(); - - /* We need both the phy and xaui links to be ok. - * rather than relying on the falcon_xmac irq/poll - * regime, just poll xaui directly */ - link_up = efx->link_state.up; - if (link_up && EFX_IS10G(efx) && - !falcon_xaui_link_ok(efx)) - link_up = false; - - } while ((++count < 20) && !link_up); - - /* The link should now be up. If it isn't, there is no point - * in attempting a loopback test */ - if (!link_up) { + rc = efx_wait_for_link(efx); + if (rc) { EFX_ERR(efx, "loopback %s never came up\n", LOOPBACK_MODE(efx)); - rc = -EIO; goto out; } - EFX_LOG(efx, "link came up in %s loopback in %d iterations\n", - LOOPBACK_MODE(efx), count); - /* Test every TX queue */ efx_for_each_tx_queue(tx_queue, efx) { state->offload_csum = (tx_queue->queue == @@ -665,7 +674,6 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, enum efx_loopback_mode loopback_mode = efx->loopback_mode; int phy_mode = efx->phy_mode; enum reset_type reset_method = RESET_TYPE_INVISIBLE; - struct ethtool_cmd ecmd; struct efx_channel *channel; int rc_test = 0, rc_reset = 0, rc; @@ -718,21 +726,21 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, mutex_unlock(&efx->mac_lock); /* 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); if (rc && !rc_test) rc_test = rc; /* reset the chip to recover from the register test */ - rc_reset = falcon_reset_hw(efx, reset_method); + rc_reset = efx->type->reset(efx, reset_method); /* Ensure that the phy is powered and out of loopback * for the bist and loopback tests */ efx->phy_mode &= ~PHY_MODE_LOW_POWER; 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) rc_reset = rc; @@ -751,10 +759,12 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, rc_test = rc; /* restore the PHY to the previous state */ - efx->loopback_mode = loopback_mode; + mutex_lock(&efx->mac_lock); efx->phy_mode = phy_mode; 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; } diff --git a/drivers/net/sfc/siena.c b/drivers/net/sfc/siena.c new file mode 100644 index 0000000..de07a4f --- /dev/null +++ b/drivers/net/sfc/siena.c @@ -0,0 +1,604 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * 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 + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/module.h> +#include "net_driver.h" +#include "bitfield.h" +#include "efx.h" +#include "nic.h" +#include "mac.h" +#include "spi.h" +#include "regs.h" +#include "io.h" +#include "phy.h" +#include "workarounds.h" +#include "mcdi.h" +#include "mcdi_pcol.h" + +/* Hardware control for SFC9000 family including SFL9021 (aka Siena). */ + +static void siena_init_wol(struct efx_nic *efx); + + +static void siena_push_irq_moderation(struct efx_channel *channel) +{ + efx_dword_t timer_cmd; + + if (channel->irq_moderation) + EFX_POPULATE_DWORD_2(timer_cmd, + FRF_CZ_TC_TIMER_MODE, + FFE_CZ_TIMER_MODE_INT_HLDOFF, + FRF_CZ_TC_TIMER_VAL, + channel->irq_moderation - 1); + else + EFX_POPULATE_DWORD_2(timer_cmd, + FRF_CZ_TC_TIMER_MODE, + FFE_CZ_TIMER_MODE_DIS, + FRF_CZ_TC_TIMER_VAL, 0); + efx_writed_page_locked(channel->efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0, + channel->channel); +} + +static void siena_push_multicast_hash(struct efx_nic *efx) +{ + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + + efx_mcdi_rpc(efx, MC_CMD_SET_MCAST_HASH, + efx->multicast_hash.byte, sizeof(efx->multicast_hash), + NULL, 0, NULL); +} + +static int siena_mdio_write(struct net_device *net_dev, + int prtad, int devad, u16 addr, u16 value) +{ + struct efx_nic *efx = netdev_priv(net_dev); + uint32_t status; + int rc; + + rc = efx_mcdi_mdio_write(efx, efx->mdio_bus, prtad, devad, + addr, value, &status); + if (rc) + return rc; + if (status != MC_CMD_MDIO_STATUS_GOOD) + return -EIO; + + return 0; +} + +static int siena_mdio_read(struct net_device *net_dev, + int prtad, int devad, u16 addr) +{ + struct efx_nic *efx = netdev_priv(net_dev); + uint16_t value; + uint32_t status; + int rc; + + rc = efx_mcdi_mdio_read(efx, efx->mdio_bus, prtad, devad, + addr, &value, &status); + if (rc) + return rc; + if (status != MC_CMD_MDIO_STATUS_GOOD) + return -EIO; + + return (int)value; +} + +/* This call is responsible for hooking in the MAC and PHY operations */ +static int siena_probe_port(struct efx_nic *efx) +{ + int rc; + + /* Hook in PHY operations table */ + efx->phy_op = &efx_mcdi_phy_ops; + + /* Set up MDIO structure for PHY */ + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + efx->mdio.mdio_read = siena_mdio_read; + efx->mdio.mdio_write = siena_mdio_write; + + /* Fill out MDIO structure and loopback modes */ + rc = efx->phy_op->probe(efx); + if (rc != 0) + return rc; + + /* Initial assumption */ + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->wanted_fc = EFX_FC_RX | EFX_FC_TX; + + /* Allocate buffer for stats */ + rc = efx_nic_alloc_buffer(efx, &efx->stats_buffer, + MC_CMD_MAC_NSTATS * sizeof(u64)); + if (rc) + return rc; + EFX_LOG(efx, "stats buffer at %llx (virt %p phys %llx)\n", + (u64)efx->stats_buffer.dma_addr, + efx->stats_buffer.addr, + (u64)virt_to_phys(efx->stats_buffer.addr)); + + efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 1); + + return 0; +} + +void siena_remove_port(struct efx_nic *efx) +{ + efx_nic_free_buffer(efx, &efx->stats_buffer); +} + +static const struct efx_nic_register_test siena_register_tests[] = { + { FR_AZ_ADR_REGION, + EFX_OWORD32(0x0001FFFF, 0x0001FFFF, 0x0001FFFF, 0x0001FFFF) }, + { FR_CZ_USR_EV_CFG, + EFX_OWORD32(0x000103FF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_CFG, + EFX_OWORD32(0xFFFFFFFE, 0xFFFFFFFF, 0x0003FFFF, 0x00000000) }, + { FR_AZ_TX_CFG, + EFX_OWORD32(0x7FFF0037, 0xFFFF8000, 0xFFFFFFFF, 0x03FFFFFF) }, + { FR_AZ_TX_RESERVED, + EFX_OWORD32(0xFFFEFE80, 0x1FFFFFFF, 0x020000FE, 0x007FFFFF) }, + { FR_AZ_SRM_TX_DC_CFG, + EFX_OWORD32(0x001FFFFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_DC_CFG, + EFX_OWORD32(0x00000003, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_DC_PF_WM, + EFX_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_BZ_DP_CTRL, + EFX_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_BZ_RX_RSS_TKEY, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) }, + { FR_CZ_RX_RSS_IPV6_REG1, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) }, + { FR_CZ_RX_RSS_IPV6_REG2, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) }, + { FR_CZ_RX_RSS_IPV6_REG3, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0x00000007, 0x00000000) }, +}; + +static int siena_test_registers(struct efx_nic *efx) +{ + return efx_nic_test_registers(efx, siena_register_tests, + ARRAY_SIZE(siena_register_tests)); +} + +/************************************************************************** + * + * Device reset + * + ************************************************************************** + */ + +static int siena_reset_hw(struct efx_nic *efx, enum reset_type method) +{ + + if (method == RESET_TYPE_WORLD) + return efx_mcdi_reset_mc(efx); + else + return efx_mcdi_reset_port(efx); +} + +static int siena_probe_nvconfig(struct efx_nic *efx) +{ + int rc; + + rc = efx_mcdi_get_board_cfg(efx, efx->mac_address, NULL); + if (rc) + return rc; + + return 0; +} + +static int siena_probe_nic(struct efx_nic *efx) +{ + struct siena_nic_data *nic_data; + bool already_attached = 0; + int rc; + + /* Allocate storage for hardware specific data */ + nic_data = kzalloc(sizeof(struct siena_nic_data), GFP_KERNEL); + if (!nic_data) + return -ENOMEM; + efx->nic_data = nic_data; + + if (efx_nic_fpga_ver(efx) != 0) { + EFX_ERR(efx, "Siena FPGA not supported\n"); + rc = -ENODEV; + goto fail1; + } + + efx_mcdi_init(efx); + + /* Recover from a failed assertion before probing */ + rc = efx_mcdi_handle_assertion(efx); + if (rc) + goto fail1; + + rc = efx_mcdi_fwver(efx, &nic_data->fw_version, &nic_data->fw_build); + if (rc) { + EFX_ERR(efx, "Failed to read MCPU firmware version - " + "rc %d\n", rc); + goto fail1; /* MCPU absent? */ + } + + /* Let the BMC know that the driver is now in charge of link and + * filter settings. We must do this before we reset the NIC */ + rc = efx_mcdi_drv_attach(efx, true, &already_attached); + if (rc) { + EFX_ERR(efx, "Unable to register driver with MCPU\n"); + goto fail2; + } + if (already_attached) + /* Not a fatal error */ + EFX_ERR(efx, "Host already registered with MCPU\n"); + + /* Now we can reset the NIC */ + rc = siena_reset_hw(efx, RESET_TYPE_ALL); + if (rc) { + EFX_ERR(efx, "failed to reset NIC\n"); + goto fail3; + } + + siena_init_wol(efx); + + /* Allocate memory for INT_KER */ + rc = efx_nic_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t)); + if (rc) + goto fail4; + BUG_ON(efx->irq_status.dma_addr & 0x0f); + + EFX_LOG(efx, "INT_KER at %llx (virt %p phys %llx)\n", + (unsigned long long)efx->irq_status.dma_addr, + efx->irq_status.addr, + (unsigned long long)virt_to_phys(efx->irq_status.addr)); + + /* Read in the non-volatile configuration */ + rc = siena_probe_nvconfig(efx); + if (rc == -EINVAL) { + EFX_ERR(efx, "NVRAM is invalid therefore using defaults\n"); + efx->phy_type = PHY_TYPE_NONE; + efx->mdio.prtad = MDIO_PRTAD_NONE; + } else if (rc) { + goto fail5; + } + + return 0; + +fail5: + efx_nic_free_buffer(efx, &efx->irq_status); +fail4: +fail3: + efx_mcdi_drv_attach(efx, false, NULL); +fail2: +fail1: + kfree(efx->nic_data); + return rc; +} + +/* This call performs hardware-specific global initialisation, such as + * defining the descriptor cache sizes and number of RSS channels. + * It does not set up any buffers, descriptor rings or event queues. + */ +static int siena_init_nic(struct efx_nic *efx) +{ + efx_oword_t temp; + int rc; + + /* Recover from a failed assertion post-reset */ + rc = efx_mcdi_handle_assertion(efx); + if (rc) + return rc; + + /* Squash TX of packets of 16 bytes or less */ + efx_reado(efx, &temp, FR_AZ_TX_RESERVED); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1); + efx_writeo(efx, &temp, FR_AZ_TX_RESERVED); + + /* Do not enable TX_NO_EOP_DISC_EN, since it limits packets to 16 + * descriptors (which is bad). + */ + efx_reado(efx, &temp, FR_AZ_TX_CFG); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_NO_EOP_DISC_EN, 0); + EFX_SET_OWORD_FIELD(temp, FRF_CZ_TX_FILTER_EN_BIT, 1); + efx_writeo(efx, &temp, FR_AZ_TX_CFG); + + efx_reado(efx, &temp, FR_AZ_RX_CFG); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_DESC_PUSH_EN, 0); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_INGR_EN, 1); + efx_writeo(efx, &temp, FR_AZ_RX_CFG); + + if (efx_nic_rx_xoff_thresh >= 0 || efx_nic_rx_xon_thresh >= 0) + /* No MCDI operation has been defined to set thresholds */ + EFX_ERR(efx, "ignoring RX flow control thresholds\n"); + + /* Enable event logging */ + rc = efx_mcdi_log_ctrl(efx, true, false, 0); + if (rc) + return rc; + + /* Set destination of both TX and RX Flush events */ + EFX_POPULATE_OWORD_1(temp, FRF_BZ_FLS_EVQ_ID, 0); + efx_writeo(efx, &temp, FR_BZ_DP_CTRL); + + EFX_POPULATE_OWORD_1(temp, FRF_CZ_USREV_DIS, 1); + efx_writeo(efx, &temp, FR_CZ_USR_EV_CFG); + + efx_nic_init_common(efx); + return 0; +} + +static void siena_remove_nic(struct efx_nic *efx) +{ + efx_nic_free_buffer(efx, &efx->irq_status); + + siena_reset_hw(efx, RESET_TYPE_ALL); + + /* Relinquish the device back to the BMC */ + if (efx_nic_has_mc(efx)) + efx_mcdi_drv_attach(efx, false, NULL); + + /* Tear down the private nic state */ + kfree(efx->nic_data); + efx->nic_data = NULL; +} + +#define STATS_GENERATION_INVALID ((u64)(-1)) + +static int siena_try_update_nic_stats(struct efx_nic *efx) +{ + u64 *dma_stats; + struct efx_mac_stats *mac_stats; + u64 generation_start; + u64 generation_end; + + mac_stats = &efx->mac_stats; + dma_stats = (u64 *)efx->stats_buffer.addr; + + generation_end = dma_stats[MC_CMD_MAC_GENERATION_END]; + if (generation_end == STATS_GENERATION_INVALID) + return 0; + rmb(); + +#define MAC_STAT(M, D) \ + mac_stats->M = dma_stats[MC_CMD_MAC_ ## D] + + MAC_STAT(tx_bytes, TX_BYTES); + MAC_STAT(tx_bad_bytes, TX_BAD_BYTES); + mac_stats->tx_good_bytes = (mac_stats->tx_bytes - + mac_stats->tx_bad_bytes); + MAC_STAT(tx_packets, TX_PKTS); + MAC_STAT(tx_bad, TX_BAD_FCS_PKTS); + MAC_STAT(tx_pause, TX_PAUSE_PKTS); + MAC_STAT(tx_control, TX_CONTROL_PKTS); + MAC_STAT(tx_unicast, TX_UNICAST_PKTS); + MAC_STAT(tx_multicast, TX_MULTICAST_PKTS); + MAC_STAT(tx_broadcast, TX_BROADCAST_PKTS); + MAC_STAT(tx_lt64, TX_LT64_PKTS); + MAC_STAT(tx_64, TX_64_PKTS); + MAC_STAT(tx_65_to_127, TX_65_TO_127_PKTS); + MAC_STAT(tx_128_to_255, TX_128_TO_255_PKTS); + MAC_STAT(tx_256_to_511, TX_256_TO_511_PKTS); + MAC_STAT(tx_512_to_1023, TX_512_TO_1023_PKTS); + MAC_STAT(tx_1024_to_15xx, TX_1024_TO_15XX_PKTS); + MAC_STAT(tx_15xx_to_jumbo, TX_15XX_TO_JUMBO_PKTS); + MAC_STAT(tx_gtjumbo, TX_GTJUMBO_PKTS); + mac_stats->tx_collision = 0; + MAC_STAT(tx_single_collision, TX_SINGLE_COLLISION_PKTS); + MAC_STAT(tx_multiple_collision, TX_MULTIPLE_COLLISION_PKTS); + MAC_STAT(tx_excessive_collision, TX_EXCESSIVE_COLLISION_PKTS); + MAC_STAT(tx_deferred, TX_DEFERRED_PKTS); + MAC_STAT(tx_late_collision, TX_LATE_COLLISION_PKTS); + mac_stats->tx_collision = (mac_stats->tx_single_collision + + mac_stats->tx_multiple_collision + + mac_stats->tx_excessive_collision + + mac_stats->tx_late_collision); + MAC_STAT(tx_excessive_deferred, TX_EXCESSIVE_DEFERRED_PKTS); + MAC_STAT(tx_non_tcpudp, TX_NON_TCPUDP_PKTS); + MAC_STAT(tx_mac_src_error, TX_MAC_SRC_ERR_PKTS); + MAC_STAT(tx_ip_src_error, TX_IP_SRC_ERR_PKTS); + MAC_STAT(rx_bytes, RX_BYTES); + MAC_STAT(rx_bad_bytes, RX_BAD_BYTES); + mac_stats->rx_good_bytes = (mac_stats->rx_bytes - + mac_stats->rx_bad_bytes); + MAC_STAT(rx_packets, RX_PKTS); + MAC_STAT(rx_good, RX_GOOD_PKTS); + mac_stats->rx_bad = mac_stats->rx_packets - mac_stats->rx_good; + MAC_STAT(rx_pause, RX_PAUSE_PKTS); + MAC_STAT(rx_control, RX_CONTROL_PKTS); + MAC_STAT(rx_unicast, RX_UNICAST_PKTS); + MAC_STAT(rx_multicast, RX_MULTICAST_PKTS); + MAC_STAT(rx_broadcast, RX_BROADCAST_PKTS); + MAC_STAT(rx_lt64, RX_UNDERSIZE_PKTS); + MAC_STAT(rx_64, RX_64_PKTS); + MAC_STAT(rx_65_to_127, RX_65_TO_127_PKTS); + MAC_STAT(rx_128_to_255, RX_128_TO_255_PKTS); + MAC_STAT(rx_256_to_511, RX_256_TO_511_PKTS); + MAC_STAT(rx_512_to_1023, RX_512_TO_1023_PKTS); + MAC_STAT(rx_1024_to_15xx, RX_1024_TO_15XX_PKTS); + MAC_STAT(rx_15xx_to_jumbo, RX_15XX_TO_JUMBO_PKTS); + MAC_STAT(rx_gtjumbo, RX_GTJUMBO_PKTS); + mac_stats->rx_bad_lt64 = 0; + mac_stats->rx_bad_64_to_15xx = 0; + mac_stats->rx_bad_15xx_to_jumbo = 0; + MAC_STAT(rx_bad_gtjumbo, RX_JABBER_PKTS); + MAC_STAT(rx_overflow, RX_OVERFLOW_PKTS); + mac_stats->rx_missed = 0; + MAC_STAT(rx_false_carrier, RX_FALSE_CARRIER_PKTS); + MAC_STAT(rx_symbol_error, RX_SYMBOL_ERROR_PKTS); + MAC_STAT(rx_align_error, RX_ALIGN_ERROR_PKTS); + MAC_STAT(rx_length_error, RX_LENGTH_ERROR_PKTS); + MAC_STAT(rx_internal_error, RX_INTERNAL_ERROR_PKTS); + mac_stats->rx_good_lt64 = 0; + + efx->n_rx_nodesc_drop_cnt = dma_stats[MC_CMD_MAC_RX_NODESC_DROPS]; + +#undef MAC_STAT + + rmb(); + generation_start = dma_stats[MC_CMD_MAC_GENERATION_START]; + if (generation_end != generation_start) + return -EAGAIN; + + return 0; +} + +static void siena_update_nic_stats(struct efx_nic *efx) +{ + while (siena_try_update_nic_stats(efx) == -EAGAIN) + cpu_relax(); +} + +static void siena_start_nic_stats(struct efx_nic *efx) +{ + u64 *dma_stats = (u64 *)efx->stats_buffer.addr; + + dma_stats[MC_CMD_MAC_GENERATION_END] = STATS_GENERATION_INVALID; + + efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, + MC_CMD_MAC_NSTATS * sizeof(u64), 1, 0); +} + +static void siena_stop_nic_stats(struct efx_nic *efx) +{ + efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 0); +} + +void siena_print_fwver(struct efx_nic *efx, char *buf, size_t len) +{ + struct siena_nic_data *nic_data = efx->nic_data; + snprintf(buf, len, "%u.%u.%u.%u", + (unsigned int)(nic_data->fw_version >> 48), + (unsigned int)(nic_data->fw_version >> 32 & 0xffff), + (unsigned int)(nic_data->fw_version >> 16 & 0xffff), + (unsigned int)(nic_data->fw_version & 0xffff)); +} + +/************************************************************************** + * + * Wake on LAN + * + ************************************************************************** + */ + +static void siena_get_wol(struct efx_nic *efx, struct ethtool_wolinfo *wol) +{ + struct siena_nic_data *nic_data = efx->nic_data; + + wol->supported = WAKE_MAGIC; + if (nic_data->wol_filter_id != -1) + wol->wolopts = WAKE_MAGIC; + else + wol->wolopts = 0; + memset(&wol->sopass, 0, sizeof(wol->sopass)); +} + + +static int siena_set_wol(struct efx_nic *efx, u32 type) +{ + struct siena_nic_data *nic_data = efx->nic_data; + int rc; + + if (type & ~WAKE_MAGIC) + return -EINVAL; + + if (type & WAKE_MAGIC) { + if (nic_data->wol_filter_id != -1) + efx_mcdi_wol_filter_remove(efx, + nic_data->wol_filter_id); + rc = efx_mcdi_wol_filter_set_magic(efx, efx->mac_address, + &nic_data->wol_filter_id); + if (rc) + goto fail; + + pci_wake_from_d3(efx->pci_dev, true); + } else { + rc = efx_mcdi_wol_filter_reset(efx); + nic_data->wol_filter_id = -1; + pci_wake_from_d3(efx->pci_dev, false); + if (rc) + goto fail; + } + + return 0; + fail: + EFX_ERR(efx, "%s failed: type=%d rc=%d\n", __func__, type, rc); + return rc; +} + + +static void siena_init_wol(struct efx_nic *efx) +{ + struct siena_nic_data *nic_data = efx->nic_data; + int rc; + + rc = efx_mcdi_wol_filter_get_magic(efx, &nic_data->wol_filter_id); + + if (rc != 0) { + /* If it failed, attempt to get into a synchronised + * state with MC by resetting any set WoL filters */ + efx_mcdi_wol_filter_reset(efx); + nic_data->wol_filter_id = -1; + } else if (nic_data->wol_filter_id != -1) { + pci_wake_from_d3(efx->pci_dev, true); + } +} + + +/************************************************************************** + * + * Revision-dependent attributes used by efx.c and nic.c + * + ************************************************************************** + */ + +struct efx_nic_type siena_a0_nic_type = { + .probe = siena_probe_nic, + .remove = siena_remove_nic, + .init = siena_init_nic, + .fini = efx_port_dummy_op_void, + .monitor = NULL, + .reset = siena_reset_hw, + .probe_port = siena_probe_port, + .remove_port = siena_remove_port, + .prepare_flush = efx_port_dummy_op_void, + .update_stats = siena_update_nic_stats, + .start_stats = siena_start_nic_stats, + .stop_stats = siena_stop_nic_stats, + .set_id_led = efx_mcdi_set_id_led, + .push_irq_moderation = siena_push_irq_moderation, + .push_multicast_hash = siena_push_multicast_hash, + .reconfigure_port = efx_mcdi_phy_reconfigure, + .get_wol = siena_get_wol, + .set_wol = siena_set_wol, + .resume_wol = siena_init_wol, + .test_registers = siena_test_registers, + .default_mac_ops = &efx_mcdi_mac_operations, + + .revision = EFX_REV_SIENA_A0, + .mem_map_size = (FR_CZ_MC_TREG_SMEM + + FR_CZ_MC_TREG_SMEM_STEP * FR_CZ_MC_TREG_SMEM_ROWS), + .txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL, + .rxd_ptr_tbl_base = FR_BZ_RX_DESC_PTR_TBL, + .buf_tbl_base = FR_BZ_BUF_FULL_TBL, + .evq_ptr_tbl_base = FR_BZ_EVQ_PTR_TBL, + .evq_rptr_tbl_base = FR_BZ_EVQ_RPTR, + .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH), + .rx_buffer_padding = 0, + .max_interrupt_mode = EFX_INT_MODE_MSIX, + .phys_addr_channels = 32, /* Hardware limit is 64, but the legacy + * interrupt handler only supports 32 + * channels */ + .tx_dc_base = 0x88000, + .rx_dc_base = 0x68000, + .offload_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM, + .reset_world_flags = ETH_RESET_MGMT << ETH_RESET_SHARED_SHIFT, +}; diff --git a/drivers/net/sfc/spi.h b/drivers/net/sfc/spi.h index 1b1ceb4..8bf4fce 100644 --- a/drivers/net/sfc/spi.h +++ b/drivers/net/sfc/spi.h @@ -36,8 +36,6 @@ /** * struct efx_spi_device - an Efx SPI (Serial Peripheral Interface) device - * @efx: The Efx controller that owns this device - * @mtd: MTD state * @device_id: Controller's id for the device * @size: Size (in bytes) * @addr_len: Number of address bytes in read/write commands @@ -54,10 +52,6 @@ * Write commands are limited to blocks with this size and alignment. */ struct efx_spi_device { - struct efx_nic *efx; -#ifdef CONFIG_SFC_MTD - void *mtd; -#endif int device_id; unsigned int size; unsigned int addr_len; @@ -67,12 +61,16 @@ struct efx_spi_device { unsigned int block_size; }; -int falcon_spi_cmd(const struct efx_spi_device *spi, unsigned int command, +int falcon_spi_cmd(struct efx_nic *efx, + const struct efx_spi_device *spi, unsigned int command, int address, const void* in, void *out, size_t len); -int falcon_spi_wait_write(const struct efx_spi_device *spi); -int falcon_spi_read(const struct efx_spi_device *spi, loff_t start, +int falcon_spi_wait_write(struct efx_nic *efx, + const struct efx_spi_device *spi); +int falcon_spi_read(struct efx_nic *efx, + const struct efx_spi_device *spi, loff_t start, size_t len, size_t *retlen, u8 *buffer); -int falcon_spi_write(const struct efx_spi_device *spi, loff_t start, +int falcon_spi_write(struct efx_nic *efx, + const struct efx_spi_device *spi, loff_t start, size_t len, size_t *retlen, const u8 *buffer); /* diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index b001f38..ca11572 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2008 Solarflare Communications Inc. + * Copyright 2007-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 @@ -12,7 +12,7 @@ #include <linux/seq_file.h> #include "efx.h" #include "mdio_10g.h" -#include "falcon.h" +#include "nic.h" #include "phy.h" #include "regs.h" #include "workarounds.h" @@ -30,13 +30,13 @@ #define SFX7101_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ (1 << LOOPBACK_PCS) | \ (1 << LOOPBACK_PMAPMD) | \ - (1 << LOOPBACK_NETWORK)) + (1 << LOOPBACK_PHYXS_WS)) #define SFT9001_LOOPBACKS ((1 << LOOPBACK_GPHY) | \ (1 << LOOPBACK_PHYXS) | \ (1 << LOOPBACK_PCS) | \ (1 << LOOPBACK_PMAPMD) | \ - (1 << LOOPBACK_NETWORK)) + (1 << LOOPBACK_PHYXS_WS)) /* We complain if we fail to see the link partner as 10G capable this many * times in a row (must be > 1 as sampling the autoneg. registers is racy) @@ -199,15 +199,16 @@ static ssize_t set_phy_short_reach(struct device *dev, const char *buf, size_t count) { struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + int rc; rtnl_lock(); efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TXPWR, MDIO_PMA_10GBT_TXPWR_SHORT, count != 0 && *buf != '0'); - efx_reconfigure_port(efx); + rc = efx_reconfigure_port(efx); rtnl_unlock(); - return count; + return rc < 0 ? rc : (ssize_t)count; } static DEVICE_ATTR(phy_short_reach, 0644, show_phy_short_reach, @@ -297,13 +298,29 @@ static int tenxpress_init(struct efx_nic *efx) return 0; } +static int sfx7101_phy_probe(struct efx_nic *efx) +{ + efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + efx->loopback_modes = SFX7101_LOOPBACKS | FALCON_XMAC_LOOPBACKS; + return 0; +} + +static int sft9001_phy_probe(struct efx_nic *efx) +{ + efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + efx->loopback_modes = (SFT9001_LOOPBACKS | FALCON_XMAC_LOOPBACKS | + FALCON_GMAC_LOOPBACKS); + return 0; +} + static int tenxpress_phy_init(struct efx_nic *efx) { struct tenxpress_phy_data *phy_data; - u16 old_adv, adv; int rc = 0; - falcon_board(efx)->init_phy(efx); + falcon_board(efx)->type->init_phy(efx); phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); if (!phy_data) @@ -335,14 +352,14 @@ static int tenxpress_phy_init(struct efx_nic *efx) if (rc < 0) goto fail; - /* Set pause advertising */ - old_adv = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE); - adv = ((old_adv & ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)) | - mii_advertise_flowctrl(efx->wanted_fc)); - if (adv != old_adv) { - efx_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, adv); - mdio45_nway_restart(&efx->mdio); - } + /* Initialise advertising flags */ + efx->link_advertising = (ADVERTISED_TP | ADVERTISED_Autoneg | + ADVERTISED_10000baseT_Full); + if (efx->phy_type != PHY_TYPE_SFX7101) + efx->link_advertising |= (ADVERTISED_1000baseT_Full | + ADVERTISED_100baseT_Full); + efx_link_set_wanted_fc(efx, efx->wanted_fc); + efx_mdio_an_reconfigure(efx); if (efx->phy_type == PHY_TYPE_SFT9001B) { rc = device_create_file(&efx->pci_dev->dev, @@ -374,7 +391,7 @@ static int tenxpress_special_reset(struct efx_nic *efx) /* The XGMAC clock is driven from the SFC7101/SFT9001 312MHz clock, so * a special software reset can glitch the XGMAC sufficiently for stats * requests to fail. */ - efx_stats_disable(efx); + falcon_stop_nic_stats(efx); /* Initiate reset */ reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG); @@ -396,7 +413,7 @@ static int tenxpress_special_reset(struct efx_nic *efx) /* Wait for the XGXS state machine to churn */ mdelay(10); out: - efx_stats_enable(efx); + falcon_start_nic_stats(efx); return rc; } @@ -500,97 +517,76 @@ static void tenxpress_low_power(struct efx_nic *efx) !!(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 efx_link_state *link_state = &efx->link_state; - struct ethtool_cmd ecmd; bool phy_mode_change, loop_reset; if (efx->phy_mode & (PHY_MODE_OFF | PHY_MODE_SPECIAL)) { phy_data->phy_mode = efx->phy_mode; - return; + return 0; } - tenxpress_low_power(efx); - phy_mode_change = (efx->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, LOOPBACKS_EXTERNAL(efx)) || LOOPBACK_CHANGED(phy_data, efx, 1 << LOOPBACK_GPHY)); 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) { - tenxpress_special_reset(efx); - - /* Reset XAUI if we were in 10G, and are staying - * in 10G. If we're moving into and out of 10G - * 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); + /* Reset XAUI if we were in 10G, and are staying + * in 10G. If we're moving into and out of 10G + * then xaui will be reset anyway */ + if (EFX_IS10G(efx)) + falcon_reset_xaui(efx); } + tenxpress_low_power(efx); efx_mdio_transmit_disable(efx); efx_mdio_phy_reconfigure(efx); tenxpress_ext_loopback(efx); + efx_mdio_an_reconfigure(efx); phy_data->loopback_mode = efx->loopback_mode; phy_data->phy_mode = efx->phy_mode; - if (efx->phy_type == PHY_TYPE_SFX7101) { - link_state->speed = 10000; - link_state->fd = true; - link_state->up = sfx7101_link_ok(efx); - } else { - efx->phy_op->get_settings(efx, &ecmd); - link_state->speed = ecmd.speed; - link_state->fd = ecmd.duplex == DUPLEX_FULL; - link_state->up = sft9001_link_ok(efx, &ecmd); - } - link_state->fc = efx_mdio_get_pause(efx); + return 0; } -/* Poll PHY for interrupt */ -static void tenxpress_phy_poll(struct efx_nic *efx) +static void +tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd); + +/* Poll for link state changes */ +static bool tenxpress_phy_poll(struct efx_nic *efx) { - struct tenxpress_phy_data *phy_data = efx->phy_data; - struct efx_link_state *link_state = &efx->link_state; - bool change = false; + struct efx_link_state old_state = efx->link_state; if (efx->phy_type == PHY_TYPE_SFX7101) { - bool link_ok = sfx7101_link_ok(efx); - if (link_ok != link_state->up) { - change = true; - } else { - unsigned int link_fc = efx_mdio_get_pause(efx); - if (link_fc != link_state->fc) - change = true; - } - sfx7101_check_bad_lp(efx, link_ok); - } else if (efx->loopback_mode) { - bool link_ok = sft9001_link_ok(efx, NULL); - if (link_ok != link_state->up) - change = true; + efx->link_state.up = sfx7101_link_ok(efx); + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->link_state.fc = efx_mdio_get_pause(efx); + + sfx7101_check_bad_lp(efx, efx->link_state.up); } else { - int status = efx_mdio_read(efx, MDIO_MMD_PMAPMD, - MDIO_PMA_LASI_STAT); - if (status & MDIO_PMA_LASI_LSALARM) - change = true; - } + struct ethtool_cmd ecmd; - if (change) - falcon_sim_phy_event(efx); + /* Check the LASI alarm first */ + if (efx->loopback_mode == LOOPBACK_NONE && + !(efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT) & + MDIO_PMA_LASI_LSALARM)) + return false; - if (phy_data->phy_mode != PHY_MODE_NORMAL) - return; + tenxpress_get_settings(efx, &ecmd); + + efx->link_state.up = sft9001_link_ok(efx, &ecmd); + efx->link_state.speed = ecmd.speed; + efx->link_state.fd = (ecmd.duplex == DUPLEX_FULL); + efx->link_state.fc = efx_mdio_get_pause(efx); + } + + return !efx_link_state_equal(&efx->link_state, &old_state); } static void tenxpress_phy_fini(struct efx_nic *efx) @@ -648,6 +644,13 @@ static const char *const sfx7101_test_names[] = { "bist" }; +static const char *sfx7101_test_name(struct efx_nic *efx, unsigned int index) +{ + if (index < ARRAY_SIZE(sfx7101_test_names)) + return sfx7101_test_names[index]; + return NULL; +} + static int sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags) { @@ -659,6 +662,9 @@ sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags) /* BIST is automatically run after a special software reset */ rc = tenxpress_special_reset(efx); results[0] = rc ? -1 : 1; + + efx_mdio_an_reconfigure(efx); + return rc; } @@ -674,14 +680,17 @@ static const char *const sft9001_test_names[] = { "cable.pairD.length", }; +static const char *sft9001_test_name(struct efx_nic *efx, unsigned int index) +{ + if (index < ARRAY_SIZE(sft9001_test_names)) + return sft9001_test_names[index]; + return NULL; +} + 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; - if (flags & ETH_TEST_FL_OFFLINE) - efx->phy_op->get_settings(efx, &ecmd); - /* Initialise cable diagnostic results to unknown failure */ for (i = 1; i < 9; ++i) results[i] = -1; @@ -733,9 +742,7 @@ out: if (!rc) rc = rc2; - rc2 = efx->phy_op->set_settings(efx, &ecmd); - if (!rc) - rc = rc2; + efx_mdio_an_reconfigure(efx); } return rc; @@ -766,7 +773,6 @@ tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) mdio45_ethtool_gset_npage(&efx->mdio, ecmd, adv, lpa); - ecmd->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; if (efx->phy_type != PHY_TYPE_SFX7101) { ecmd->supported |= (SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full); @@ -783,7 +789,7 @@ tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) * but doesn't advertise the correct speed. So override it */ if (efx->loopback_mode == LOOPBACK_GPHY) ecmd->speed = SPEED_1000; - else if (LOOPBACK_MASK(efx) & efx->phy_op->loopbacks) + else if (LOOPBACK_EXTERNAL(efx)) ecmd->speed = SPEED_10000; } @@ -813,35 +819,27 @@ static void sft9001_set_npage_adv(struct efx_nic *efx, u32 advertising) } struct efx_phy_operations falcon_sfx7101_phy_ops = { - .macs = EFX_XMAC, + .probe = sfx7101_phy_probe, .init = tenxpress_phy_init, .reconfigure = tenxpress_phy_reconfigure, .poll = tenxpress_phy_poll, .fini = tenxpress_phy_fini, - .clear_interrupt = efx_port_dummy_op_void, .get_settings = tenxpress_get_settings, .set_settings = tenxpress_set_settings, .set_npage_adv = sfx7101_set_npage_adv, - .num_tests = ARRAY_SIZE(sfx7101_test_names), - .test_names = sfx7101_test_names, + .test_name = sfx7101_test_name, .run_tests = sfx7101_run_tests, - .mmds = TENXPRESS_REQUIRED_DEVS, - .loopbacks = SFX7101_LOOPBACKS, }; struct efx_phy_operations falcon_sft9001_phy_ops = { - .macs = EFX_GMAC | EFX_XMAC, + .probe = sft9001_phy_probe, .init = tenxpress_phy_init, .reconfigure = tenxpress_phy_reconfigure, .poll = tenxpress_phy_poll, .fini = tenxpress_phy_fini, - .clear_interrupt = efx_port_dummy_op_void, .get_settings = tenxpress_get_settings, .set_settings = tenxpress_set_settings, .set_npage_adv = sft9001_set_npage_adv, - .num_tests = ARRAY_SIZE(sft9001_test_names), - .test_names = sft9001_test_names, + .test_name = sft9001_test_name, .run_tests = sft9001_run_tests, - .mmds = TENXPRESS_REQUIRED_DEVS, - .loopbacks = SFT9001_LOOPBACKS, }; diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c index c54fa30..e669f94 100644 --- a/drivers/net/sfc/tx.c +++ b/drivers/net/sfc/tx.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 @@ -12,11 +12,13 @@ #include <linux/tcp.h> #include <linux/ip.h> #include <linux/in.h> +#include <linux/ipv6.h> +#include <net/ipv6.h> #include <linux/if_ether.h> #include <linux/highmem.h> #include "net_driver.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #include "workarounds.h" /* @@ -278,7 +280,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) buffer->continuation = false; /* Pass off to hardware */ - falcon_push_buffers(tx_queue); + efx_nic_push_buffers(tx_queue); return NETDEV_TX_OK; @@ -426,7 +428,7 @@ int efx_probe_tx_queue(struct efx_tx_queue *tx_queue) tx_queue->buffer[i].continuation = true; /* Allocate hardware ring */ - rc = falcon_probe_tx(tx_queue); + rc = efx_nic_probe_tx(tx_queue); if (rc) goto fail; @@ -449,7 +451,7 @@ void efx_init_tx_queue(struct efx_tx_queue *tx_queue) BUG_ON(tx_queue->stopped); /* Set up TX descriptor ring */ - falcon_init_tx(tx_queue); + efx_nic_init_tx(tx_queue); } void efx_release_tx_buffers(struct efx_tx_queue *tx_queue) @@ -475,7 +477,7 @@ void efx_fini_tx_queue(struct efx_tx_queue *tx_queue) EFX_LOG(tx_queue->efx, "shutting down TX queue %d\n", tx_queue->queue); /* Flush TX queue, remove descriptor ring */ - falcon_fini_tx(tx_queue); + efx_nic_fini_tx(tx_queue); efx_release_tx_buffers(tx_queue); @@ -492,7 +494,7 @@ void efx_fini_tx_queue(struct efx_tx_queue *tx_queue) void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) { EFX_LOG(tx_queue->efx, "destroying TX queue %d\n", tx_queue->queue); - falcon_remove_tx(tx_queue); + efx_nic_remove_tx(tx_queue); kfree(tx_queue->buffer); tx_queue->buffer = NULL; @@ -531,6 +533,7 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) #define ETH_HDR_LEN(skb) (skb_network_header(skb) - (skb)->data) #define SKB_TCP_OFF(skb) PTR_DIFF(tcp_hdr(skb), (skb)->data) #define SKB_IPV4_OFF(skb) PTR_DIFF(ip_hdr(skb), (skb)->data) +#define SKB_IPV6_OFF(skb) PTR_DIFF(ipv6_hdr(skb), (skb)->data) /** * struct tso_state - TSO state for an SKB @@ -543,6 +546,7 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) * @unmap_len: Length of SKB fragment * @unmap_addr: DMA address of SKB fragment * @unmap_single: DMA single vs page mapping flag + * @protocol: Network protocol (after any VLAN header) * @header_len: Number of bytes of header * @full_packet_size: Number of bytes to put in each outgoing segment * @@ -563,6 +567,7 @@ struct tso_state { dma_addr_t unmap_addr; bool unmap_single; + __be16 protocol; unsigned header_len; int full_packet_size; }; @@ -570,9 +575,9 @@ struct tso_state { /* * Verify that our various assumptions about sk_buffs and the conditions - * under which TSO will be attempted hold true. + * under which TSO will be attempted hold true. Return the protocol number. */ -static void efx_tso_check_safe(struct sk_buff *skb) +static __be16 efx_tso_check_protocol(struct sk_buff *skb) { __be16 protocol = skb->protocol; @@ -587,13 +592,22 @@ static void efx_tso_check_safe(struct sk_buff *skb) if (protocol == htons(ETH_P_IP)) skb_set_transport_header(skb, sizeof(*veh) + 4 * ip_hdr(skb)->ihl); + else if (protocol == htons(ETH_P_IPV6)) + skb_set_transport_header(skb, sizeof(*veh) + + sizeof(struct ipv6hdr)); } - EFX_BUG_ON_PARANOID(protocol != htons(ETH_P_IP)); - EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP); + if (protocol == htons(ETH_P_IP)) { + EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP); + } else { + EFX_BUG_ON_PARANOID(protocol != htons(ETH_P_IPV6)); + EFX_BUG_ON_PARANOID(ipv6_hdr(skb)->nexthdr != NEXTHDR_TCP); + } EFX_BUG_ON_PARANOID((PTR_DIFF(tcp_hdr(skb), skb->data) + (tcp_hdr(skb)->doff << 2u)) > skb_headlen(skb)); + + return protocol; } @@ -836,7 +850,10 @@ static void tso_start(struct tso_state *st, const struct sk_buff *skb) + PTR_DIFF(tcp_hdr(skb), skb->data)); st->full_packet_size = st->header_len + skb_shinfo(skb)->gso_size; - st->ipv4_id = ntohs(ip_hdr(skb)->id); + if (st->protocol == htons(ETH_P_IP)) + st->ipv4_id = ntohs(ip_hdr(skb)->id); + else + st->ipv4_id = 0; st->seqnum = ntohl(tcp_hdr(skb)->seq); EFX_BUG_ON_PARANOID(tcp_hdr(skb)->urg); @@ -951,7 +968,6 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, struct tso_state *st) { struct efx_tso_header *tsoh; - struct iphdr *tsoh_iph; struct tcphdr *tsoh_th; unsigned ip_length; u8 *header; @@ -975,7 +991,6 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, header = TSOH_BUFFER(tsoh); tsoh_th = (struct tcphdr *)(header + SKB_TCP_OFF(skb)); - tsoh_iph = (struct iphdr *)(header + SKB_IPV4_OFF(skb)); /* Copy and update the headers. */ memcpy(header, skb->data, st->header_len); @@ -993,11 +1008,22 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, tsoh_th->fin = tcp_hdr(skb)->fin; tsoh_th->psh = tcp_hdr(skb)->psh; } - tsoh_iph->tot_len = htons(ip_length); - /* Linux leaves suitable gaps in the IP ID space for us to fill. */ - tsoh_iph->id = htons(st->ipv4_id); - st->ipv4_id++; + if (st->protocol == htons(ETH_P_IP)) { + struct iphdr *tsoh_iph = + (struct iphdr *)(header + SKB_IPV4_OFF(skb)); + + tsoh_iph->tot_len = htons(ip_length); + + /* Linux leaves suitable gaps in the IP ID space for us to fill. */ + tsoh_iph->id = htons(st->ipv4_id); + st->ipv4_id++; + } else { + struct ipv6hdr *tsoh_iph = + (struct ipv6hdr *)(header + SKB_IPV6_OFF(skb)); + + tsoh_iph->payload_len = htons(ip_length - sizeof(*tsoh_iph)); + } st->packet_space = skb_shinfo(skb)->gso_size; ++tx_queue->tso_packets; @@ -1027,8 +1053,8 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, int frag_i, rc, rc2 = NETDEV_TX_OK; struct tso_state state; - /* Verify TSO is safe - these checks should never fail. */ - efx_tso_check_safe(skb); + /* Find the packet protocol and sanity-check it */ + state.protocol = efx_tso_check_protocol(skb); EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); @@ -1078,7 +1104,7 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, } /* Pass off to hardware */ - falcon_push_buffers(tx_queue); + efx_nic_push_buffers(tx_queue); tx_queue->tso_bursts++; return NETDEV_TX_OK; diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h index 3250299..acd9c73 100644 --- a/drivers/net/sfc/workarounds.h +++ b/drivers/net/sfc/workarounds.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * 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 @@ -16,7 +16,9 @@ */ #define EFX_WORKAROUND_ALWAYS(efx) 1 -#define EFX_WORKAROUND_FALCON_A(efx) (falcon_rev(efx) <= FALCON_REV_A1) +#define EFX_WORKAROUND_FALCON_A(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) +#define EFX_WORKAROUND_FALCON_AB(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_B0) +#define EFX_WORKAROUND_SIENA(efx) (efx_nic_rev(efx) == EFX_REV_SIENA_A0) #define EFX_WORKAROUND_10G(efx) EFX_IS10G(efx) #define EFX_WORKAROUND_SFT9001(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A || \ (efx)->phy_type == PHY_TYPE_SFT9001B) @@ -27,17 +29,17 @@ #define EFX_WORKAROUND_7575 EFX_WORKAROUND_ALWAYS /* Bit-bashed I2C reads cause performance drop */ #define EFX_WORKAROUND_7884 EFX_WORKAROUND_10G -/* TX pkt parser problem with <= 16 byte TXes */ -#define EFX_WORKAROUND_9141 EFX_WORKAROUND_ALWAYS /* TX_EV_PKT_ERR can be caused by a dangling TX descriptor * or a PCIe error (bug 11028) */ #define EFX_WORKAROUND_10727 EFX_WORKAROUND_ALWAYS /* Transmit flow control may get disabled */ -#define EFX_WORKAROUND_11482 EFX_WORKAROUND_ALWAYS -/* Flush events can take a very long time to appear */ -#define EFX_WORKAROUND_11557 EFX_WORKAROUND_ALWAYS +#define EFX_WORKAROUND_11482 EFX_WORKAROUND_FALCON_AB /* Truncated IPv4 packets can confuse the TX packet parser */ -#define EFX_WORKAROUND_15592 EFX_WORKAROUND_ALWAYS +#define EFX_WORKAROUND_15592 EFX_WORKAROUND_FALCON_AB +/* Legacy ISR read can return zero once */ +#define EFX_WORKAROUND_15783 EFX_WORKAROUND_SIENA +/* Legacy interrupt storm when interrupt fifo fills */ +#define EFX_WORKAROUND_17213 EFX_WORKAROUND_SIENA /* Spurious parity errors in TSORT buffers */ #define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A |