[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [linux-2.6.18-xen] Update Solarflare Communications net driver to version 3.0.2.2074
# HG changeset patch # User Keir Fraser <keir.fraser@xxxxxxxxxx> # Date 1262955949 0 # Node ID 0b5ca7cdbdfc8857229475519b23a78401829e91 # Parent 896902106793c0a7e8759cbddc97b9367746863e Update Solarflare Communications net driver to version 3.0.2.2074 Bring net driver in Xen tree in line with kernel.org tree Add support for new SFC9000 series NICs Signed-off-by: Kieran Mansley <kmansley@xxxxxxxxxxxxxx> --- drivers/net/sfc/alaska.c | 159 - drivers/net/sfc/boards.c | 528 ---- drivers/net/sfc/boards.h | 51 drivers/net/sfc/config.h | 1 drivers/net/sfc/ethtool.h | 44 drivers/net/sfc/extraversion.h | 4 drivers/net/sfc/falcon.h | 154 - drivers/net/sfc/falcon_hwdefs.h | 1638 ------------- drivers/net/sfc/falcon_io.h | 260 -- drivers/net/sfc/gmii.h | 212 - drivers/net/sfc/i2c-direct.c | 398 --- drivers/net/sfc/i2c-direct.h | 108 drivers/net/sfc/lm87_support.c | 295 -- drivers/net/sfc/lm87_support.h | 58 drivers/net/sfc/mentormac.c | 506 ---- drivers/net/sfc/phy.c | 28 drivers/net/sfc/pm8358_phy.c | 204 - drivers/net/sfc/rx.h | 61 drivers/net/sfc/sfe4001.c | 315 -- drivers/net/sfc/tx.h | 41 drivers/net/sfc/xenpack.h | 80 drivers/net/sfc/xfp_phy.c | 203 - MAINTAINERS | 11 drivers/net/sfc/Kconfig | 36 drivers/net/sfc/Makefile | 17 drivers/net/sfc/autocompat.h | 98 drivers/net/sfc/bitfield.h | 257 +- drivers/net/sfc/debugfs.c | 226 + drivers/net/sfc/debugfs.h | 138 - drivers/net/sfc/driverlink.c | 420 +-- drivers/net/sfc/driverlink.h | 59 drivers/net/sfc/driverlink_api.h | 507 +--- drivers/net/sfc/efx.c | 2393 +++++++++++--------- drivers/net/sfc/efx.h | 115 drivers/net/sfc/efx_ioctl.h | 71 drivers/net/sfc/enum.h | 153 - drivers/net/sfc/ethtool.c | 623 +++-- drivers/net/sfc/falcon.c | 4633 +++++++++++++-------------------------- drivers/net/sfc/falcon_boards.c | 739 ++++++ drivers/net/sfc/falcon_gmac.c | 314 -- drivers/net/sfc/falcon_xmac.c | 814 +----- drivers/net/sfc/io.h | 260 ++ drivers/net/sfc/ioctl.c | 70 drivers/net/sfc/kernel_compat.c | 479 +++- drivers/net/sfc/kernel_compat.h | 1217 +++++++++- drivers/net/sfc/linux_mdio.c | 430 +++ drivers/net/sfc/linux_mdio.h | 347 ++ drivers/net/sfc/lm87.c | 959 ++++++++ drivers/net/sfc/lm90.c | 829 ++++++ drivers/net/sfc/mac.h | 29 drivers/net/sfc/mcdi.c | 1178 +++++++++ drivers/net/sfc/mcdi.h | 131 + drivers/net/sfc/mcdi_mac.c | 152 + drivers/net/sfc/mcdi_pcol.h | 1729 ++++++++++++++ drivers/net/sfc/mcdi_phy.c | 616 +++++ drivers/net/sfc/mdio_10g.c | 514 +--- drivers/net/sfc/mdio_10g.h | 295 -- drivers/net/sfc/mtd.c | 1158 +++++---- drivers/net/sfc/net_driver.h | 763 +++--- drivers/net/sfc/nic.c | 1785 +++++++++++++++ drivers/net/sfc/nic.h | 307 ++ drivers/net/sfc/null_phy.c | 112 drivers/net/sfc/phy.h | 83 drivers/net/sfc/qt202x_phy.c | 478 ++++ drivers/net/sfc/regs.h | 3168 ++++++++++++++++++++++++++ drivers/net/sfc/rx.c | 828 +----- drivers/net/sfc/selftest.c | 1039 ++++---- drivers/net/sfc/selftest.h | 49 drivers/net/sfc/siena.c | 743 ++++++ drivers/net/sfc/spi.h | 178 - drivers/net/sfc/tenxpress.c | 1359 ++++++----- drivers/net/sfc/tx.c | 1005 +++++++- drivers/net/sfc/txc43128_phy.c | 446 +-- drivers/net/sfc/workarounds.h | 80 74 files changed, 24228 insertions(+), 15560 deletions(-) diff -r 896902106793 -r 0b5ca7cdbdfc MAINTAINERS --- a/MAINTAINERS Fri Jan 08 11:56:04 2010 +0000 +++ b/MAINTAINERS Fri Jan 08 13:05:49 2010 +0000 @@ -2559,11 +2559,12 @@ S: Supported S: Supported SFC NETWORK DRIVER -P: Steve Hodgson -P: Ben Hutchings -P: Robert Stonehouse -M: linux-net-drivers@xxxxxxxxxxxxxx -S: Supported +M: Solarflare linux maintainers <linux-net-drivers@xxxxxxxxxxxxxx> +M: Steve Hodgson <shodgson@xxxxxxxxxxxxxx> +M: Ben Hutchings <bhutchings@xxxxxxxxxxxxxx> +L: netdev@xxxxxxxxxxxxxxx +S: Supported +F: drivers/net/sfc/ SGI VISUAL WORKSTATION 320 AND 540 P: Andrey Panin diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/Kconfig --- a/drivers/net/sfc/Kconfig Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/Kconfig Fri Jan 08 13:05:49 2010 +0000 @@ -1,35 +1,39 @@ config SFC config SFC - tristate "Solarflare Solarstorm SFC4000 support" + tristate "Solarflare Solarstorm SFC4000/SFC9000-family support" depends on PCI && INET - select MII select CRC32 + select I2C + 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_DEBUGFS - bool "Solarflare Solarstorm SFC4000 debugging support" - depends on SFC && DEBUG_FS - default N - help - This option creates an "sfc" subdirectory of debugfs with - debugging information for the SFC4000 driver. + bool "Solarflare Solarstorm SFC4000 debugging support" + depends on SFC && DEBUG_FS + default N + help + This option creates an "sfc" subdirectory of debugfs with + debugging information for the SFC4000 driver. - If unsure, say N. + If unsure, say N. config SFC_MTD - depends on SFC && MTD && MTD_PARTITIONS - tristate "Solarflare Solarstorm SFC4000 flash/EEPROM support" + bool "Solarflare Solarstorm SFC4000/SFC9000-family MTD support" + depends on SFC && MTD && !(SFC=y && MTD=m) + default y help - This module exposes the on-board flash and/or EEPROM memory as - MTD devices (e.g. /dev/mtd1). This makes it possible to upload a - new boot ROM to the NIC. + This exposes the on-board flash and/or EEPROM memory as MTD + devices (e.g. /dev/mtd1). This makes it possible to upload + new boot code to the NIC. config SFC_RESOURCE depends on SFC && X86 - tristate "Solarflare Solarstorm SFC4000 resource driver" + tristate "Solarflare Solarstorm SFC4000/SFC9000 resource driver" help This module provides the SFC resource manager driver. + diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/Makefile --- a/drivers/net/sfc/Makefile Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/Makefile Fri Jan 08 13:05:49 2010 +0000 @@ -1,13 +1,14 @@ sfc-y += efx.o falcon.o tx.o rx.o ment -sfc-y += efx.o falcon.o tx.o rx.o mentormac.o falcon_gmac.o \ - falcon_xmac.o alaska.o i2c-direct.o selftest.o \ - driverlink.o ethtool.o xfp_phy.o mdio_10g.o \ - txc43128_phy.o tenxpress.o lm87_support.o boards.o \ - sfe4001.o pm8358_phy.o null_phy.o kernel_compat.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 \ + driverlink.o ethtool.o qt202x_phy.o mdio_10g.o \ + tenxpress.o falcon_boards.o mcdi.o linux_mdio.o \ + mcdi_phy.o ioctl.o kernel_compat.o lm87.o lm90.o +sfc-$(CONFIG_SFC_MTD) += mtd.o sfc-$(CONFIG_SFC_DEBUGFS) += debugfs.o + obj-$(CONFIG_SFC) += sfc.o - -sfc_mtd-y = mtd.o -obj-$(CONFIG_SFC_MTD) += sfc_mtd.o obj-$(CONFIG_SFC_RESOURCE) += sfc_resource/ +EXTRA_CFLAGS += -DEFX_USE_KCOMPAT=1 -Wno-unused-label diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/alaska.c --- a/drivers/net/sfc/alaska.c Fri Jan 08 11:56:04 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,159 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005: Fen Systems Ltd. - * Copyright 2006-2007: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> - * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** - */ - -#include "net_driver.h" -#include <linux/ethtool.h> -#include "gmii.h" -#include "phy.h" - -/* Marvell 88E1111 "Alaska" PHY control */ -#define ALASKA_PHY_SPECIFIC 16 -#define ALASKA_ALLOW_SLEEP 0x0200 - -#define ALASKA_EXTENDED_CONTROL 20 -#define EXTENDED_LINE_LOOPBACK 0x8000 - -#define ALASKA_LED_CONTROL 24 -#define LED_BLINK_MASK 0x0700 -#define LED_BLINK_FAST 0x0100 -#define LED_BLINK_SLOW 0x0300 -#define LED_TX_CTRL_MASK 0x0041 -#define LED_TX_CTRL_LINK_AND_ACTIVITY 0x0001 - -#define ALASKA_LED_OVERRIDE 25 -#define LED_LINK1000_MASK 0x0030 -#define LED_LINK1000_BLINK 0x0010 -#define LED_TX_MASK 0x0003 -#define LED_TX_BLINK 0x0001 - -static void alaska_reconfigure(struct efx_nic *efx) -{ - struct mii_if_info *gmii = &efx->mii; - u32 bmcr, phy_ext; - - /* Configure line loopback if requested */ - phy_ext = gmii->mdio_read(gmii->dev, gmii->phy_id, - ALASKA_EXTENDED_CONTROL); - if (efx->loopback_mode == LOOPBACK_NETWORK) - phy_ext |= EXTENDED_LINE_LOOPBACK; - else - phy_ext &= ~EXTENDED_LINE_LOOPBACK; - gmii->mdio_write(gmii->dev, gmii->phy_id, ALASKA_EXTENDED_CONTROL, - phy_ext); - - /* Configure PHY loopback if requested */ - bmcr = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_BMCR); - if (efx->loopback_mode == LOOPBACK_PHY) - bmcr |= BMCR_LOOPBACK; - else - bmcr &= ~BMCR_LOOPBACK; - gmii->mdio_write(gmii->dev, gmii->phy_id, MII_BMCR, bmcr); - - /* Read link up status */ - if (efx->loopback_mode == LOOPBACK_NONE) - efx->link_up = mii_link_ok(gmii); - else - efx->link_up = 1; - - /* Determine link options from PHY */ - if (gmii->force_media) { - efx->link_options = gmii_forced_result(bmcr); - } else { - int lpa = gmii_lpa(gmii); - int adv = gmii_advertised(gmii); - efx->link_options = gmii_nway_result(adv & lpa); - } -} - -static void alaska_clear_interrupt(struct efx_nic *efx) -{ - struct mii_if_info *gmii = &efx->mii; - - /* Read interrupt status register to clear */ - gmii->mdio_read(gmii->dev, gmii->phy_id, GMII_ISR); -} - -static int alaska_init(struct efx_nic *efx) -{ - struct mii_if_info *gmii = &efx->mii; - u32 ier, leds, ctrl_1g, phy_spec; - - /* Read ISR to clear any outstanding PHY interrupts */ - gmii->mdio_read(gmii->dev, gmii->phy_id, GMII_ISR); - - /* Enable PHY interrupts */ - ier = gmii->mdio_read(gmii->dev, gmii->phy_id, GMII_IER); - ier |= IER_LINK_CHG; - gmii->mdio_write(gmii->dev, gmii->phy_id, GMII_IER, ier); - - /* Remove 1G half-duplex as unsupported in Mentor MAC */ - ctrl_1g = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_CTRL1000); - ctrl_1g &= ~(ADVERTISE_1000HALF); - gmii->mdio_write(gmii->dev, gmii->phy_id, MII_CTRL1000, ctrl_1g); - - /* - * The PHY can save power when there is no external connection - * (sleep mode). However, this is incompatible with PHY - * loopback, and if enable and disable it quickly the PHY can - * go to sleep even when sleep mode is disabled. (SFC bug - * 9309.) Therefore we disable it all the time. - */ - phy_spec = gmii->mdio_read(gmii->dev, gmii->phy_id, - ALASKA_PHY_SPECIFIC); - phy_spec &= ~ALASKA_ALLOW_SLEEP; - gmii->mdio_write(gmii->dev, gmii->phy_id, ALASKA_PHY_SPECIFIC, - phy_spec); - - /* Configure LEDs */ - leds = gmii->mdio_read(gmii->dev, gmii->phy_id, ALASKA_LED_CONTROL); - leds &= ~(LED_BLINK_MASK | LED_TX_CTRL_MASK); - leds |= (LED_BLINK_FAST | LED_TX_CTRL_LINK_AND_ACTIVITY); - gmii->mdio_write(gmii->dev, gmii->phy_id, ALASKA_LED_CONTROL, leds); - - return 0; -} - -static void alaska_fini(struct efx_nic *efx) -{ - struct mii_if_info *gmii = &efx->mii; - u32 ier; - - /* Disable PHY interrupts */ - ier = gmii->mdio_read(gmii->dev, gmii->phy_id, GMII_IER); - ier &= ~IER_LINK_CHG; - gmii->mdio_write(gmii->dev, gmii->phy_id, GMII_IER, ier); -} - - -struct efx_phy_operations alaska_phy_operations = { - .init = alaska_init, - .fini = alaska_fini, - .reconfigure = alaska_reconfigure, - .clear_interrupt = alaska_clear_interrupt, - .loopbacks = (1 << LOOPBACK_PHY) | (1 << LOOPBACK_NETWORK), - .startup_loopback = LOOPBACK_PHY, -}; diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/autocompat.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/autocompat.h Fri Jan 08 13:05:49 2010 +0000 @@ -0,0 +1,98 @@ +#define EFX_HAVE_OLD_NAPI yes +#define EFX_HAVE_OLD_CSUM yes +#define EFX_HAVE_OLD_IP_FAST_CSUM yes +// #define EFX_NEED_BYTEORDER_TYPES +#define EFX_NEED_CSUM_UNFOLDED yes +// #define EFX_NEED_CSUM_TCPUDP_NOFOLD +// #define EFX_NEED_DEV_NOTICE +// #define EFX_NEED_DUMMY_PCI_DISABLE_MSI +// #define EFX_NEED_DUMMY_MSIX +// #define EFX_NEED_ENABLE_MSIX +// #define EFX_HAVE_GRO +// #define EFX_NEED_GFP_T +#define EFX_NEED_HEX_DUMP yes +// #define EFX_NEED_HEX_DUMP_CONST_FIX +// #define EFX_NEED_IF_MII +#define EFX_HAVE_IRQ_HANDLER_REGS yes +#define EFX_NEED_IRQ_HANDLER_T yes +// #define EFX_NEED_KZALLOC +// #define EFX_NEED_MII_CONSTANTS +#define EFX_NEED_MII_ADVERTISE_FLOWCTRL yes +#define EFX_NEED_MII_RESOLVE_FLOWCTRL_FDX yes +// #define EFX_HAVE_LINUX_MDIO_H +// #define EFX_NEED_MSECS_TO_JIFFIES +// #define EFX_NEED_MDELAY +// #define EFX_NEED_MSLEEP +// #define EFX_NEED_SSLEEP +// #define EFX_NEED_MTD_ERASE_CALLBACK +// #define EFX_NEED_MUTEX +// #define EFX_NEED_NETDEV_ALLOC_SKB +#define EFX_NEED_NETDEV_TX_T yes +#define EFX_NEED_NETIF_NAPI_DEL yes +// #define EFX_NEED_NETIF_TX_LOCK +#define EFX_NEED_NETIF_ADDR_LOCK yes +#define EFX_NEED_PCI_CLEAR_MASTER yes +// #define EFX_NEED_PCI_MATCH_ID +// #define EFX_NEED_PCI_SAVE_RESTORE_WRAPPERS +#define EFX_NEED_PRINT_MAC yes +// #define EFX_NEED_RANDOM_ETHER_ADDR +// #define EFX_NEED_RESOURCE_SIZE_T +// #define EFX_NEED_RTNL_TRYLOCK +// #define EFX_HAVE_ROUND_JIFFIES_UP +// #define EFX_NEED_SAFE_LISTS +// #define EFX_NEED_SCHEDULE_TIMEOUT_INTERRUPTIBLE +// #define EFX_NEED_SCHEDULE_TIMEOUT_UNINTERRUPTIBLE +// #define EFX_NEED_SETUP_TIMER +#define EFX_NEED_SKB_HEADER_MACROS yes +// #define EFX_NEED_ETH_HDR +#define EFX_NEED_TCP_HDR yes +#define EFX_NEED_IP_HDR yes +#define EFX_NEED_IPV6_HDR yes +#define EFX_NEED_WORK_API_WRAPPERS yes +// #define EFX_USE_CANCEL_DELAYED_WORK_SYNC +// #define EFX_USE_CANCEL_WORK_SYNC +// #define EFX_USE_ETHTOOL_ETH_TP_MDIX +#define EFX_USE_ETHTOOL_GET_PERM_ADDR yes +// #define EFX_USE_ETHTOOL_FLAGS +// #define EFX_USE_ETHTOOL_LP_ADVERTISING +// #define EFX_USE_ETHTOOL_MDIO_SUPPORT +#define EFX_USE_LINUX_IO_H yes +#define EFX_USE_LINUX_UACCESS_H yes +#define EFX_USE_MTD_ERASE_FAIL_ADDR yes +#define EFX_USE_MTD_WRITESIZE yes +// #define EFX_USE_NETDEV_STATS +// #define EFX_USE_PCI_DEV_REVISION +// #define EFX_USE_NETDEV_VLAN_FEATURES +#define EFX_USE_I2C_LEGACY yes +#define EFX_NEED_I2C_NEW_DUMMY yes +// #define EFX_HAVE_OLD_I2C_DRIVER_PROBE +// #define EFX_HAVE_OLD_I2C_NEW_DUMMY +// #define EFX_USE_I2C_DRIVER_NAME +#define EFX_HAVE_HWMON_H yes +// #define EFX_NEED_HWMON_VID +// #define EFX_HAVE_I2C_SENSOR_H +#define EFX_HAVE_HWMON_CLASS_DEVICE yes +// #define EFX_HAVE_OLD_DEVICE_ATTRIBUTE +#define EFX_NEED_BOOL yes +// #define EFX_USE_ETHTOOL_GET_SSET_COUNT +// #define EFX_HAVE_ETHTOOL_RESET +#define EFX_NEED_I2C_LOCK_ADAPTER yes +// #define EFX_USE_I2C_BUS_SEMAPHORE +#define EFX_HAVE_OLD_PCI_DMA_MAPPING_ERROR yes +// #define EFX_HAVE_LINUX_SEMAPHORE_H +#define EFX_NEED_DEV_GET_STATS yes +#define EFX_HAVE_OLD_CPUMASK_SCNPRINTF yes +#define EFX_USE_PM yes +// #define EFX_USE_PM_EXT_OPS +// #define EFX_USE_DEV_PM_OPS +#define EFX_NEED_ATOMIC_CMPXCHG yes +#define EFX_NEED_WARN_ON yes +// #define EFX_NEED_WAIT_EVENT_TIMEOUT +// #define EFX_NEED_ETHTOOL_CONSTANTS +#define EFX_NEED_PCI_WAKE_FROM_D3 yes +// #define EFX_HAVE_DEV_DISABLE_LRO +#define EFX_NEED_UNMASK_MSIX_VECTORS yes +// #define EFX_NEED_FOR_EACH_PCI_DEV +#define EFX_NEED_SCSI_SGLIST yes +#define EFX_NEED_SG_NEXT yes +// #define EFX_NEED_KFIFO diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/bitfield.h --- a/drivers/net/sfc/bitfield.h Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/bitfield.h Fri Jan 08 13:05:49 2010 +0000 @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2006-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> - * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> + * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_BITFIELD_H @@ -54,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 @@ -69,9 +54,9 @@ * * The maximum width mask that can be generated is 64 bits. */ -#define EFX_MASK64(field) \ - (EFX_WIDTH(field) == 64 ? ~((u64) 0) : \ - (((((u64) 1) << EFX_WIDTH(field))) - 1)) +#define EFX_MASK64(width) \ + ((width) == 64 ? ~((u64) 0) : \ + (((((u64) 1) << (width))) - 1)) /* Mask equal in width to the specified field. * @@ -80,9 +65,9 @@ * The maximum width mask that can be generated is 32 bits. Use * EFX_MASK64 for higher width fields. */ -#define EFX_MASK32(field) \ - (EFX_WIDTH(field) == 32 ? ~((u32) 0) : \ - (((((u32) 1) << EFX_WIDTH(field))) - 1)) +#define EFX_MASK32(width) \ + ((width) == 32 ? ~((u32) 0) : \ + (((((u32) 1) << (width))) - 1)) /* A doubleword (i.e. 4 byte) datatype - little-endian in HW */ typedef union efx_dword { @@ -155,44 +140,49 @@ typedef union efx_oword { EFX_EXTRACT_NATIVE(le32_to_cpu(element), min, max, low, high) #define EFX_EXTRACT_OWORD64(oword, low, high) \ - (EFX_EXTRACT64((oword).u64[0], 0, 63, low, high) | \ - EFX_EXTRACT64((oword).u64[1], 64, 127, low, high)) + ((EFX_EXTRACT64((oword).u64[0], 0, 63, low, high) | \ + EFX_EXTRACT64((oword).u64[1], 64, 127, low, high)) & \ + EFX_MASK64(high + 1 - low)) #define EFX_EXTRACT_QWORD64(qword, low, high) \ - EFX_EXTRACT64((qword).u64[0], 0, 63, low, high) + (EFX_EXTRACT64((qword).u64[0], 0, 63, low, high) & \ + EFX_MASK64(high + 1 - low)) #define EFX_EXTRACT_OWORD32(oword, low, high) \ - (EFX_EXTRACT32((oword).u32[0], 0, 31, low, high) | \ - EFX_EXTRACT32((oword).u32[1], 32, 63, low, high) | \ - EFX_EXTRACT32((oword).u32[2], 64, 95, low, high) | \ - EFX_EXTRACT32((oword).u32[3], 96, 127, low, high)) + ((EFX_EXTRACT32((oword).u32[0], 0, 31, low, high) | \ + EFX_EXTRACT32((oword).u32[1], 32, 63, low, high) | \ + EFX_EXTRACT32((oword).u32[2], 64, 95, low, high) | \ + EFX_EXTRACT32((oword).u32[3], 96, 127, low, high)) & \ + EFX_MASK32(high + 1 - low)) #define EFX_EXTRACT_QWORD32(qword, low, high) \ - (EFX_EXTRACT32((qword).u32[0], 0, 31, low, high) | \ - EFX_EXTRACT32((qword).u32[1], 32, 63, low, high)) - -#define EFX_EXTRACT_DWORD(dword, low, high) \ - EFX_EXTRACT32((dword).u32[0], 0, 31, low, high) - -#define EFX_OWORD_FIELD64(oword, field) \ - (EFX_EXTRACT_OWORD64(oword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ - & EFX_MASK64(field)) - -#define EFX_QWORD_FIELD64(qword, field) \ - (EFX_EXTRACT_QWORD64(qword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ - & EFX_MASK64(field)) - -#define EFX_OWORD_FIELD32(oword, field) \ - (EFX_EXTRACT_OWORD32(oword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ - & EFX_MASK32(field)) - -#define EFX_QWORD_FIELD32(qword, field) \ - (EFX_EXTRACT_QWORD32(qword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ - & EFX_MASK32(field)) - -#define EFX_DWORD_FIELD(dword, field) \ - (EFX_EXTRACT_DWORD(dword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ - & EFX_MASK32(field)) + ((EFX_EXTRACT32((qword).u32[0], 0, 31, low, high) | \ + EFX_EXTRACT32((qword).u32[1], 32, 63, low, high)) & \ + EFX_MASK32(high + 1 - low)) + +#define EFX_EXTRACT_DWORD(dword, low, high) \ + (EFX_EXTRACT32((dword).u32[0], 0, 31, low, high) & \ + EFX_MASK32(high + 1 - low)) + +#define EFX_OWORD_FIELD64(oword, field) \ + EFX_EXTRACT_OWORD64(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_QWORD_FIELD64(qword, field) \ + EFX_EXTRACT_QWORD64(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_OWORD_FIELD32(oword, field) \ + EFX_EXTRACT_OWORD32(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_QWORD_FIELD32(qword, field) \ + EFX_EXTRACT_QWORD32(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) + +#define EFX_DWORD_FIELD(dword, field) \ + EFX_EXTRACT_DWORD(dword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) #define EFX_OWORD_IS_ZERO64(oword) \ (((oword).u64[0] | (oword).u64[1]) == (__force __le64) 0) @@ -428,68 +418,101 @@ typedef union efx_oword { * for read-modify-write operations. * */ - #define EFX_INVERT_OWORD(oword) do { \ (oword).u64[0] = ~((oword).u64[0]); \ (oword).u64[1] = ~((oword).u64[1]); \ } while (0) -#define EFX_INSERT_FIELD64(...) \ - cpu_to_le64(EFX_INSERT_FIELD_NATIVE(__VA_ARGS__)) - -#define EFX_INSERT_FIELD32(...) \ - cpu_to_le32(EFX_INSERT_FIELD_NATIVE(__VA_ARGS__)) - -#define EFX_INPLACE_MASK64(min, max, field) \ - EFX_INSERT_FIELD64(min, max, field, EFX_MASK64(field)) - -#define EFX_INPLACE_MASK32(min, max, field) \ - EFX_INSERT_FIELD32(min, max, field, EFX_MASK32(field)) - -#define EFX_SET_OWORD_FIELD64(oword, field, value) do { \ +#define EFX_AND_OWORD(oword, from, mask) \ + do { \ + (oword).u64[0] = (from).u64[0] & (mask).u64[0]; \ + (oword).u64[1] = (from).u64[1] & (mask).u64[1]; \ + } while (0) + +#define EFX_OR_OWORD(oword, from, mask) \ + do { \ + (oword).u64[0] = (from).u64[0] | (mask).u64[0]; \ + (oword).u64[1] = (from).u64[1] | (mask).u64[1]; \ + } while (0) + +#define EFX_INSERT64(min, max, low, high, value) \ + cpu_to_le64(EFX_INSERT_NATIVE(min, max, low, high, value)) + +#define EFX_INSERT32(min, max, low, high, value) \ + cpu_to_le32(EFX_INSERT_NATIVE(min, max, low, high, value)) + +#define EFX_INPLACE_MASK64(min, max, low, high) \ + EFX_INSERT64(min, max, low, high, EFX_MASK64(high + 1 - low)) + +#define EFX_INPLACE_MASK32(min, max, low, high) \ + EFX_INSERT32(min, max, low, high, EFX_MASK32(high + 1 - low)) + +#define EFX_SET_OWORD64(oword, low, high, value) do { \ (oword).u64[0] = (((oword).u64[0] \ - & ~EFX_INPLACE_MASK64(0, 63, field)) \ - | EFX_INSERT_FIELD64(0, 63, field, value)); \ + & ~EFX_INPLACE_MASK64(0, 63, low, high)) \ + | EFX_INSERT64(0, 63, low, high, value)); \ (oword).u64[1] = (((oword).u64[1] \ - & ~EFX_INPLACE_MASK64(64, 127, field)) \ - | EFX_INSERT_FIELD64(64, 127, field, value)); \ - } while (0) - -#define EFX_SET_QWORD_FIELD64(qword, field, value) do { \ + & ~EFX_INPLACE_MASK64(64, 127, low, high)) \ + | EFX_INSERT64(64, 127, low, high, value)); \ + } while (0) + +#define EFX_SET_QWORD64(qword, low, high, value) do { \ (qword).u64[0] = (((qword).u64[0] \ - & ~EFX_INPLACE_MASK64(0, 63, field)) \ - | EFX_INSERT_FIELD64(0, 63, field, value)); \ - } while (0) - -#define EFX_SET_OWORD_FIELD32(oword, field, value) do { \ + & ~EFX_INPLACE_MASK64(0, 63, low, high)) \ + | EFX_INSERT64(0, 63, low, high, value)); \ + } while (0) + +#define EFX_SET_OWORD32(oword, low, high, value) do { \ (oword).u32[0] = (((oword).u32[0] \ - & ~EFX_INPLACE_MASK32(0, 31, field)) \ - | EFX_INSERT_FIELD32(0, 31, field, value)); \ + & ~EFX_INPLACE_MASK32(0, 31, low, high)) \ + | EFX_INSERT32(0, 31, low, high, value)); \ (oword).u32[1] = (((oword).u32[1] \ - & ~EFX_INPLACE_MASK32(32, 63, field)) \ - | EFX_INSERT_FIELD32(32, 63, field, value)); \ + & ~EFX_INPLACE_MASK32(32, 63, low, high)) \ + | EFX_INSERT32(32, 63, low, high, value)); \ (oword).u32[2] = (((oword).u32[2] \ - & ~EFX_INPLACE_MASK32(64, 95, field)) \ - | EFX_INSERT_FIELD32(64, 95, field, value)); \ + & ~EFX_INPLACE_MASK32(64, 95, low, high)) \ + | EFX_INSERT32(64, 95, low, high, value)); \ (oword).u32[3] = (((oword).u32[3] \ - & ~EFX_INPLACE_MASK32(96, 127, field)) \ - | EFX_INSERT_FIELD32(96, 127, field, value)); \ - } while (0) - -#define EFX_SET_QWORD_FIELD32(qword, field, value) do { \ + & ~EFX_INPLACE_MASK32(96, 127, low, high)) \ + | EFX_INSERT32(96, 127, low, high, value)); \ + } while (0) + +#define EFX_SET_QWORD32(qword, low, high, value) do { \ (qword).u32[0] = (((qword).u32[0] \ - & ~EFX_INPLACE_MASK32(0, 31, field)) \ - | EFX_INSERT_FIELD32(0, 31, field, value)); \ + & ~EFX_INPLACE_MASK32(0, 31, low, high)) \ + | EFX_INSERT32(0, 31, low, high, value)); \ (qword).u32[1] = (((qword).u32[1] \ - & ~EFX_INPLACE_MASK32(32, 63, field)) \ - | EFX_INSERT_FIELD32(32, 63, field, value)); \ - } while (0) - -#define EFX_SET_DWORD_FIELD(dword, field, value) do { \ - (dword).u32[0] = (((dword).u32[0] \ - & ~EFX_INPLACE_MASK32(0, 31, field)) \ - | EFX_INSERT_FIELD32(0, 31, field, value)); \ - } while (0) + & ~EFX_INPLACE_MASK32(32, 63, low, high)) \ + | EFX_INSERT32(32, 63, low, high, value)); \ + } while (0) + +#define EFX_SET_DWORD32(dword, low, high, value) do { \ + (dword).u32[0] = (((dword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, low, high)) \ + | EFX_INSERT32(0, 31, low, high, value)); \ + } while (0) + +#define EFX_SET_OWORD_FIELD64(oword, field, value) \ + EFX_SET_OWORD64(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_QWORD_FIELD64(qword, field, value) \ + EFX_SET_QWORD64(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_OWORD_FIELD32(oword, field, value) \ + EFX_SET_OWORD32(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_QWORD_FIELD32(qword, field, value) \ + EFX_SET_QWORD32(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_DWORD_FIELD(dword, field, value) \ + EFX_SET_DWORD32(dword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + + #if BITS_PER_LONG == 64 #define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD64 @@ -499,27 +522,17 @@ typedef union efx_oword { #define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD32 #endif -#define EFX_SET_OWORD_FIELD_VER(efx, oword, field, value) do { \ - if (FALCON_REV(efx) >= FALCON_REV_B0) { \ - EFX_SET_OWORD_FIELD((oword), field##_B0, (value)); \ - } else { \ - EFX_SET_OWORD_FIELD((oword), field##_A1, (value)); \ - } \ -} while (0) - -#define EFX_QWORD_FIELD_VER(efx, qword, field) \ - (FALCON_REV(efx) >= FALCON_REV_B0 ? \ - EFX_QWORD_FIELD((qword), field##_B0) : \ - EFX_QWORD_FIELD((qword), field##_A1)) - /* Used to avoid compiler warnings about shift range exceeding width * of the data types when dma_addr_t is only 32 bits wide. */ #define DMA_ADDR_T_WIDTH (8 * sizeof(dma_addr_t)) #define EFX_DMA_TYPE_WIDTH(width) \ (((width) < DMA_ADDR_T_WIDTH) ? (width) : DMA_ADDR_T_WIDTH) -#define EFX_DMA_MAX_MASK ((DMA_ADDR_T_WIDTH == 64) ? \ - ~((u64) 0) : ~((u32) 0)) -#define EFX_DMA_MASK(mask) ((mask) & EFX_DMA_MAX_MASK) + + +/* Static initialiser */ +#define EFX_OWORD32(a, b, c, d) \ + { .u32 = { __constant_cpu_to_le32(a), __constant_cpu_to_le32(b), \ + __constant_cpu_to_le32(c), __constant_cpu_to_le32(d) } } #endif /* EFX_BITFIELD_H */ diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/boards.c --- a/drivers/net/sfc/boards.c Fri Jan 08 11:56:04 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,528 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2007: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Developed by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** - */ - -#include "net_driver.h" -#include "phy.h" -#include "lm87_support.h" -#include "boards.h" -#include "efx.h" - -/* Macros for unpacking the board revision */ -/* The revision info is in host byte order. */ -#define BOARD_TYPE(_rev) (_rev >> 8) -#define BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf) -#define BOARD_MINOR(_rev) (_rev & 0xf) - -/* Blink support. If the PHY has no auto-blink mode so we hang it off a timer */ -#define BLINK_INTERVAL (HZ/2) - -static void blink_led_timer(unsigned long context) -{ - struct efx_nic *efx = (struct efx_nic *)context; - struct efx_blinker *bl = &efx->board_info.blinker; - efx->board_info.set_fault_led(efx, bl->state); - bl->state = !bl->state; - if (bl->resubmit) { - bl->timer.expires = jiffies + BLINK_INTERVAL; - add_timer(&bl->timer); - } -} - -static void board_blink(struct efx_nic *efx, int blink) -{ - struct efx_blinker *blinker = &efx->board_info.blinker; - - /* The rtnl mutex serialises all ethtool ioctls, so - * nothing special needs doing here. */ - if (blink) { - blinker->resubmit = 1; - blinker->state = 0; - setup_timer(&blinker->timer, blink_led_timer, - (unsigned long)efx); - blinker->timer.expires = jiffies + BLINK_INTERVAL; - add_timer(&blinker->timer); - } else { - blinker->resubmit = 0; - if (blinker->timer.function) - del_timer_sync(&blinker->timer); - efx->board_info.set_fault_led(efx, 0); - } -} - - -struct sensor_conf { - const char *name; - const unsigned high; - const unsigned low; -}; - -#define NO_LIMIT ((unsigned)-1) - -#define LM87_SENSOR_BYTES (18) - -static int sensor_limits_to_bytes(const struct sensor_conf *limits, - int nlimits, u8 *bytes, int maxbytes) -{ - int i, nbytes; - nbytes = 0; - for (i = 0; i < nlimits; i++) { - bytes[nbytes++] = limits[i].high; - if (limits[i].low != NO_LIMIT) - bytes[nbytes++] = limits[i].low; - /* We may have overrun by one at this point, but this test - * should only trigger in development drivers as the sizes - * are not dynamic. */ - if (nbytes > maxbytes) { - printk(KERN_ERR "%s: out of space!\n", __func__); - break; - } - } - return nbytes; -} - -/***************************************************************************** - * Support for the SFE4002 - * - */ -/* LM87 configuration data for the sensor on the SFE4002 board */ -static const struct sensor_conf sfe4002_lm87_limits[] = { - {"1.8V line", 0x91, 0x83}, /* 2.5V sensor, scaled for 1.8V */ - {"1.2V line", 0x5a, 0x51}, /* Vccp1 */ - {"3.3V line", 0xca, 0xb6}, - {"5V line", 0xc9, 0xb6}, - {"12V line", 0xe0, 0xb0}, - {"1V line", 0x4b, 0x44}, /* vccp2 */ - {"Ext. temp.", 0x46, 0x0a}, /* ASIC temp. */ - {"Int. temp.", 0x3c, 0x0a}, /* Board temp. */ - {"1.66V line", 0xb2, NO_LIMIT}, /* AIN1 only takes 1 value */ - {"1.5V line", 0xa1, NO_LIMIT} /* AIN2 only takes 1 value */ -}; - -static const int sfe4002_lm87_nlimits = ARRAY_SIZE(sfe4002_lm87_limits); - -static u16 sfe4002_lm87_irq_mask = EFX_LM87_NO_INTS; - -/* I2C ID of the onboard LM87 chip. This is board-specific as the bottom two - * bits are set by strap pins */ -#define SFE4002_LM87_I2C_ID (0x2e) - -/****************************************************************************/ -/* LED allocations. Note that on rev A0 boards the schematic and the reality - * differ: red and green are swapped. Below is the fixed (A1) layout (there - * are only 3 A0 boards in existence, so no real reason to make this - * conditional). - */ -#define SFE4002_FAULT_LED (2) /* Red */ -#define SFE4002_RX_LED (0) /* Green */ -#define SFE4002_TX_LED (1) /* Amber */ - -static int sfe4002_init_leds(struct efx_nic *efx) -{ - /* Set the TX and RX LEDs to reflect status and activity, and the - * fault LED off */ - xfp_set_led(efx, SFE4002_TX_LED, - QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT); - xfp_set_led(efx, SFE4002_RX_LED, - QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT); - xfp_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF); - efx->board_info.blinker.led_num = SFE4002_FAULT_LED; - return 0; -} - -static void sfe4002_fault_led(struct efx_nic *efx, int state) -{ - xfp_set_led(efx, SFE4002_FAULT_LED, state ? QUAKE_LED_ON : - QUAKE_LED_OFF); -} - -static int sfe4002_sensor_meaning(struct efx_nic *efx, int limit_num, - unsigned val) -{ - const struct sensor_conf *lim = &sfe4002_lm87_limits[limit_num]; - if (lim->low == NO_LIMIT) - EFX_ERR(efx, "%10s 0x%02x (nominal value 0x%02x)\n", lim->name, - val, lim->high); - else - EFX_ERR(efx, "%10s 0x%02x (nominal range 0x%02x - 0x%02x)\n", - lim->name, val, lim->high, lim->low); - return 1; -} - -static int sfe4002_check_hw(struct efx_nic *efx) -{ - int rc; - - /* A0 board rev. 4002s report a temperature fault the whole time - * (bad sensor) so we mask it out. */ - unsigned alarm_mask = (efx->board_info.minor > 0) ? - 0 : ~EFX_LM87_ETMP_INT; - - /* Check the sensor (NOP if not present). */ - rc = efx_check_lm87(efx, alarm_mask); - - /* We treat both lm87 interrupts and failure to talk to the lm87 - * as problems (since failure will only be reported if we did - * find the sensor at probe time. */ - if (rc) - EFX_ERR(efx, "sensor alert!\n"); - return rc; -} - -static int sfe4002_init(struct efx_nic *efx) -{ - u8 lm87_bytes[LM87_SENSOR_BYTES]; - int nbytes; - int rc; - - efx->board_info.monitor = sfe4002_check_hw; - efx->board_info.interpret_sensor = sfe4002_sensor_meaning; - efx->board_info.init_leds = sfe4002_init_leds; - efx->board_info.set_fault_led = sfe4002_fault_led; - efx->board_info.blink = board_blink; - /* To clean up shut down the lm87 (NOP if not present) */ - efx->board_info.fini = efx_remove_lm87; - - nbytes = sensor_limits_to_bytes(sfe4002_lm87_limits, - sfe4002_lm87_nlimits, lm87_bytes, - LM87_SENSOR_BYTES); - - /* Activate the lm87 sensor if present (succeeds if nothing there) */ - rc = efx_probe_lm87(efx, SFE4002_LM87_I2C_ID, - lm87_bytes, nbytes, sfe4002_lm87_irq_mask); - - return rc; -} - -/***************************************************************************** - * Support for the SFE4003 - * - */ -/* LM87 configuration data for the sensor on the SFE4003 board */ -static const struct sensor_conf sfe4003_lm87_limits[] = { - {"1.5V line", 0x78, 0x6d}, /* 2.5V input, values scaled for 1.5V */ - {"1.2V line", 0x5a, 0x51}, /* Vccp1 */ - {"3.3V line", 0xca, 0xb6}, - {"5V line", 0xc0, 0x00}, /* Sensor not connected. */ - {"12V line", 0xe0, 0xb0}, - {"1V line", 0x4b, 0x44}, /* Vccp2 */ - {"Ext. temp.", 0x46, 0x0a}, /* ASIC temp. */ - {"Int. temp.", 0x3c, 0x0a}, /* Board temp. */ - {"", 0xff, NO_LIMIT}, /* FAN1/AIN1 unused */ - {"", 0xff, NO_LIMIT} /* FAN2/AIN2 unused */ -}; - -static const int sfe4003_lm87_nlimits = ARRAY_SIZE(sfe4003_lm87_limits); - -static u16 sfe4003_lm87_irq_mask = EFX_LM87_NO_INTS; - - -static int sfe4003_sensor_meaning(struct efx_nic *efx, int limit_num, - unsigned val) -{ - const struct sensor_conf *lim = &sfe4003_lm87_limits[limit_num]; - if (lim->low == NO_LIMIT) - return 0; /* Neither AIN1 nor AIN2 mean anything to us */ - else - EFX_ERR(efx, "%10s 0x%02x (nominal range 0x%02x - 0x%02x)\n", - lim->name, val, lim->high, lim->low); - return 1; -} - -/* I2C ID of the onboard LM87 chip. This is board-specific as the bottom two - * bits are set by strap pins */ -#define SFE4003_LM87_I2C_ID (0x2e) - -/* Board-specific LED info. */ -#define SFE4003_RED_LED_GPIO (11) -#define SFE4003_LED_ON (1) -#define SFE4003_LED_OFF (0) - -static void sfe4003_fault_led(struct efx_nic *efx, int state) -{ - /* The LEDs were not wired to GPIOs before A3 */ - if (efx->board_info.minor < 3 && efx->board_info.major == 0) - return; - - txc_set_gpio_val(efx, SFE4003_RED_LED_GPIO, - state ? SFE4003_LED_ON : SFE4003_LED_OFF); -} - -static int sfe4003_init_leds(struct efx_nic *efx) -{ - /* The LEDs were not wired to GPIOs before A3 */ - if (efx->board_info.minor < 3 && efx->board_info.major == 0) - return 0; - - txc_set_gpio_dir(efx, SFE4003_RED_LED_GPIO, TXC_GPIO_DIR_OUTPUT); - txc_set_gpio_val(efx, SFE4003_RED_LED_GPIO, SFE4003_LED_OFF); - return 0; -} - -static int sfe4003_check_hw(struct efx_nic *efx) -{ - int rc; - /* A0/A1/A2 board rev. 4003s report a temperature fault the whole time - * (bad sensor) so we mask it out. */ - unsigned alarm_mask = - ~(EFX_LM87_ETMP_INT | EFX_LM87_FAN1_INT | EFX_LM87_FAN2_INT); - - /* Check the sensor (NOP if not present). */ - - rc = efx_check_lm87(efx, alarm_mask); - /* We treat both lm87 interrupts and failure to talk to the lm87 - * as problems (since failure will only be reported if we did - * find the sensor at probe time. */ - if (rc) - EFX_ERR(efx, "sensor alert!\n"); - - return rc; -} - -static int sfe4003_init(struct efx_nic *efx) -{ - u8 lm87_bytes[LM87_SENSOR_BYTES]; - int nbytes; - int rc; - efx->board_info.monitor = sfe4003_check_hw; - efx->board_info.interpret_sensor = sfe4003_sensor_meaning; - efx->board_info.init_leds = sfe4003_init_leds; - efx->board_info.set_fault_led = sfe4003_fault_led; - efx->board_info.blink = board_blink; - /* To clean up shut down the lm87 (NOP if not present) */ - efx->board_info.fini = efx_remove_lm87; - - nbytes = sensor_limits_to_bytes(sfe4003_lm87_limits, - sfe4003_lm87_nlimits, lm87_bytes, - LM87_SENSOR_BYTES); - - /* Activate the lm87 sensor if present (succeeds if nothing there) */ - rc = efx_probe_lm87(efx, SFE4003_LM87_I2C_ID, - lm87_bytes, nbytes, sfe4003_lm87_irq_mask); - - if (rc < 0) - EFX_ERR(efx, "Temperature sensor probe failure: " - "please check the jumper position\n"); - return rc; -} - -/***************************************************************************** - * Support for the SFE4005 - * - */ -/* LM87 configuration data for the sensor on the SFE4005 board */ -static const u8 sfe4005_lm87_limits[] = { - 0x51, /* 2.5V high lim. (actually monitor 1.0V line, so 1050mV) */ - 0x49, /* 2.5V low lim. (950mV) */ - 0xf6, /* Vccp1 high lim. (3.3V rail, 3465 mV) */ - 0xde, /* Vcpp1 low lim. (3.3V rail, 3135 mV) */ - 0xca, /* 3.3V AUX high lim. (3465 mV) */ - 0xb6, /* 3.3V AUX low lim. (3135mV) */ - 0xc0, /* 5V high lim. not connected) */ - 0x00, /* 5V low lim. (not connected) */ - 0xd0, /* 12V high lim. (13000mV) */ - 0xb0, /* 12V low lim. (11000mV) */ - 0xc0, /* Vccp2 high lim. (unused) */ - 0x00, /* Vccp2 low lim. (unused) */ - 0x46, /* Ext temp 1 (ASIC) high lim. */ - 0x0a, /* Ext temp 1 low lim. */ - 0x3c, /* Int temp (board) high lim. */ - 0x0a, /* Int temp 1 low lim. */ - 0xff, /* Fan 1 high (unused) */ - 0xff, /* Fan 2 high (unused) */ -}; - -#define SFE4005_LM87_I2C_ID (0x2e) - -/* Until the LM87 monitoring is interrupt driven. */ -#define SFE4005_LM87_IRQMASK EFX_LM87_NO_INTS - -#define SFE4005_PCF8575_I2C_ID (0x20) -/* Definitions for the I/O expander that controls the CX4 chip: - * which PCF8575 pin maps to which function */ -#define SFE4005_PORT0_EXTLOOP (1 << 0) -#define SFE4005_PORT1_EXTLOOP (1 << 1) -#define SFE4005_HOSTPROT_LOOP (1 << 2) -#define SFE4005_BCAST (1 << 3) /* TX on both ports */ -#define SFE4005_PORT0_EQ (1 << 4) -#define SFE4005_PORT1_EQ (1 << 5) -#define SFE4005_HOSTPORT_EQ (1 << 6) -#define SFE4005_PORTSEL (1 << 7) /* Which port (for RX in BCAST mode) */ -#define SFE4005_PORT0_PRE_LBN (8) /* Preemphasis on port 0 (2 bits)*/ -#define SFE4005_PORT1_PRE_LBN (10) /* Preemphasis on port 1 (2 bits)*/ -#define SFE4005_HOSTPORT_PRE_LBN (12) /* Preemphasis on host port (2 bits) */ -#define SFE4005_UNUSED (1 << 14) -#define SFE4005_CX4uC_nRESET (1 << 15) /* Reset the controller on CX4 chip */ - - -/* By default only turn on host port EQ. Can also OR in SFE4005_PORT0_EQ, - * SFE4005_PORT1_EQ but this hasn't been seen to make a difference. */ -#define SFE4005_CX4_DEFAULTS (SFE4005_CX4uC_nRESET | SFE4005_HOSTPORT_EQ) - -static int sfe4005_write_ioexpander(struct efx_nic *efx) -{ - unsigned long iobits = (unsigned long)efx->phy_data; - struct efx_i2c_interface *i2c = &efx->i2c; - u8 send[2], check[2]; - int rc; - /* Do not, EVER, deassert nRESET as that will reset Falcon too, - * and the driver won't know to repush the configuration, so - * nothing will work until the next power cycle. */ - BUG_ON(!(iobits & SFE4005_CX4uC_nRESET)); - send[0] = (iobits & 0xff); - send[1] = ((iobits >> 8) & 0xff); - rc = efx_i2c_send_bytes(i2c, SFE4005_PCF8575_I2C_ID, send, 2); - if (rc) { - EFX_ERR(efx, "failed to write to I/O expander: %d\n", rc); - return rc; - } - /* Paranoia: just check what the I/O expander reads back */ - rc = efx_i2c_recv_bytes(i2c, SFE4005_PCF8575_I2C_ID, check, 2); - if (rc) - EFX_ERR(efx, "failed to read back from I/O expander: %d\n", rc); - else if (check[0] != send[0] || check[1] != send[1]) - EFX_ERR(efx, "read back wrong value from I/O expander: " - "wanted %.2x%.2x, got %.2x%.2x\n", - send[1], send[0], check[1], check[0]); - return rc; -} - -static int sfe4005_init(struct efx_nic *efx) -{ - unsigned long iobits = SFE4005_CX4_DEFAULTS; - int rc; - - /* There is no PHY as such on the SFE4005 so phy_data is ours. */ - efx->phy_data = (void *)iobits; - - /* Push the values */ - rc = sfe4005_write_ioexpander(efx); - if (rc) - return rc; - - /* Activate the lm87 sensor if present (succeeds if nothing there) */ - rc = efx_probe_lm87(efx, SFE4005_LM87_I2C_ID, - sfe4005_lm87_limits, - sizeof(sfe4005_lm87_limits), SFE4005_LM87_IRQMASK); - - /* To clean up shut down the lm87 (NOP if not present) */ - efx->board_info.fini = efx_remove_lm87; - - return rc; -} - -/* This will get expanded as board-specific details get moved out of the - * PHY drivers. */ -struct efx_board_data { - const char *ref_model; - const char *gen_type; - int (*init) (struct efx_nic *nic); - unsigned mwatts; -}; - -static void dummy_fini(struct efx_nic *nic) -{ -} - -static int dummy_init(struct efx_nic *nic) -{ - nic->board_info.fini = dummy_fini; - return 0; -} - -/* Maximum board power (mW) - * Falcon controller ASIC accounts for 2.2W - * 10Xpress PHY accounts for 12W - * - */ -#define SFE4001_POWER 18000 -#define SFE4002_POWER 7500 -#define SFE4003_POWER 4500 -#define SFE4005_POWER 4500 - -static struct efx_board_data board_data[] = { - [EFX_BOARD_INVALID] = - {NULL, NULL, dummy_init, 0}, - [EFX_BOARD_SFE4001] = - {"SFE4001", "10GBASE-T adapter", sfe4001_poweron, SFE4001_POWER }, - [EFX_BOARD_SFE4002] = - {"SFE4002", "XFP adapter", sfe4002_init, SFE4002_POWER }, - [EFX_BOARD_SFE4003] = - {"SFE4003", "10GBASE-CX4 adapter", sfe4003_init, SFE4003_POWER }, - [EFX_BOARD_SFE4005] = - {"SFE4005", "10G blade adapter", sfe4005_init, SFE4005_POWER }, -}; - -int efx_set_board_info(struct efx_nic *efx, u16 revision_info) -{ - int rc = 0; - struct efx_board_data *data; - - if (BOARD_TYPE(revision_info) >= EFX_BOARD_MAX) { - EFX_ERR(efx, "squashing unknown board type %d\n", - BOARD_TYPE(revision_info)); - revision_info = 0; - } - - if (BOARD_TYPE(revision_info) == 0) { - efx->board_info.major = 0; - efx->board_info.minor = 0; - /* For early boards that don't have revision info. there is - * only 1 board for each PHY type, so we can work it out, with - * the exception of the PHY-less boards. */ - switch (efx->phy_type) { - case PHY_TYPE_10XPRESS: - efx->board_info.type = EFX_BOARD_SFE4001; - break; - case PHY_TYPE_XFP: - efx->board_info.type = EFX_BOARD_SFE4002; - break; - case PHY_TYPE_CX4_RTMR: - efx->board_info.type = EFX_BOARD_SFE4003; - break; - default: - efx->board_info.type = 0; - break; - } - } else { - efx->board_info.type = BOARD_TYPE(revision_info); - efx->board_info.major = BOARD_MAJOR(revision_info); - efx->board_info.minor = BOARD_MINOR(revision_info); - } - - data = &board_data[efx->board_info.type]; - - /* Report the board model number or generic type for recognisable - * boards. */ - if (efx->board_info.type != 0) - EFX_INFO(efx, "board is %s rev %c%d\n", - (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC) - ? data->ref_model : data->gen_type, - 'A' + efx->board_info.major, efx->board_info.minor); - - efx->board_info.init = data->init; - efx->board_info.mwatts = data->mwatts; - - return rc; -} diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/boards.h --- a/drivers/net/sfc/boards.h Fri Jan 08 11:56:04 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2007: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Developed by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** - */ - -#ifndef EFX_BOARDS_H -#define EFX_BOARDS_H - -/* Board IDs (must fit in 8 bits). Note that 0 must never be assigned because - * on early boards it means there is no revision info. Board types pre 400x - * are not covered here, but this is not a problem because: - * - the early Falcon boards (FPGA, 401, 403) don't have any extra H/W we - * need care about and aren't being updated. - */ -enum efx_board_type { - EFX_BOARD_INVALID = 0, /* Early boards do not have board rev. info. */ - EFX_BOARD_SFE4001 = 1, - EFX_BOARD_SFE4002 = 2, - EFX_BOARD_SFE4003 = 3, - EFX_BOARD_SFE4005 = 4, - /* Insert new types before here */ - EFX_BOARD_MAX -}; - -extern int efx_set_board_info(struct efx_nic *efx, u16 revision_info); - -/* SFE4001 (10GBASE-T) */ -extern int sfe4001_poweron(struct efx_nic *efx); -extern void sfe4001_poweroff(struct efx_nic *efx); - -#endif diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/config.h --- a/drivers/net/sfc/config.h Fri Jan 08 11:56:04 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -/* SFC config options can go here */ diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/debugfs.c --- a/drivers/net/sfc/debugfs.c Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/debugfs.c Fri Jan 08 13:05:49 2010 +0000 @@ -1,47 +1,34 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2006-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> - * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> + * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include <linux/module.h> #include <linux/pci.h> +/* For out-of-tree builds we always need procfs, if only for a compatibility + * symlink. + */ #include <linux/proc_fs.h> #include <linux/dcache.h> #include <linux/seq_file.h> #include "net_driver.h" #include "efx.h" #include "debugfs.h" -#include "falcon.h" - -#ifndef PRIu64 -# if (BITS_PER_LONG == 64) -# define PRIu64 "lu" -# else -# define PRIu64 "llu" -# endif +#include "nic.h" + +/* EFX_USE_DEBUGFS is defined by kernel_compat.h so we can't decide whether to + * include this earlier. + */ +#ifdef EFX_USE_DEBUGFS +#include <linux/debugfs.h> #endif + +#ifndef EFX_USE_DEBUGFS static void efx_debugfs_remove(struct proc_dir_entry *entry) { @@ -53,6 +40,7 @@ static void efx_debugfs_remove(struct pr #define debugfs_create_dir proc_mkdir #define debugfs_create_symlink proc_symlink +#endif /* !EFX_USE_DEBUGFS */ /* Parameter definition bound to a structure - each file has one of these */ struct efx_debugfs_bound_param { @@ -66,19 +54,36 @@ struct efx_debugfs_bound_param { /* Top-level debug directory ([/sys/kernel]/debug/sfc) */ -static struct dentry *efx_debug_root; +static efx_debugfs_entry *efx_debug_root; /* "cards" directory ([/sys/kernel]/debug/sfc/cards) */ -static struct dentry *efx_debug_cards; +static efx_debugfs_entry *efx_debug_cards; /* Sequential file interface to bound parameters */ +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_DEBUGFS) + static int efx_debugfs_seq_show(struct seq_file *file, void *v) { - struct proc_dir_entry *entry = (struct proc_dir_entry *)file->private; - struct efx_debugfs_parameter *param = - (struct efx_debugfs_parameter *)entry->data; + struct efx_debugfs_bound_param *binding = file->private; + + return binding->param->reader(file, + binding->structure + + binding->param->offset); +} + +static int efx_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, efx_debugfs_seq_show, inode->i_private); +} + +#else /* EFX_USE_KCOMPAT && !EFX_USE_DEBUGFS */ + +static int efx_debugfs_seq_show(struct seq_file *file, void *v) +{ + struct proc_dir_entry *entry = file->private; + struct efx_debugfs_parameter *param = entry->data; void *structure = (void *)entry->read_proc; if (!structure) @@ -91,6 +96,8 @@ static int efx_debugfs_open(struct inode { return single_open(file, efx_debugfs_seq_show, PROC_I(inode)->pde); } + +#endif /* !EFX_USE_KCOMPAT || EFX_USE_DEBUGFS */ static struct file_operations efx_debugfs_file_ops = { @@ -102,10 +109,41 @@ static struct file_operations efx_debugf }; +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_DEBUGFS) + +/** + * efx_fini_debugfs_child - remove a named child of a debugfs directory + * @dir: Directory + * @name: Name of child + * + * This removes the named child from the directory, if it exists. + */ +void efx_fini_debugfs_child(efx_debugfs_entry *dir, const char *name) +{ + struct qstr child_name; + efx_debugfs_entry *child; + + child_name.len = strlen(name); + child_name.name = name; + child_name.hash = full_name_hash(child_name.name, child_name.len); + child = d_lookup(dir, &child_name); + if (child) { + /* If it's a "regular" file, free its parameter binding */ + if (S_ISREG(child->d_inode->i_mode)) + kfree(child->d_inode->i_private); + debugfs_remove(child); + dput(child); + } +} + +#else /* EFX_USE_KCOMPAT && !EFX_USE_DEBUGFS */ + void efx_fini_debugfs_child(struct proc_dir_entry *dir, const char *name) { remove_proc_entry(name, dir); } + +#endif /* !EFX_USE_KCOMPAT || EFX_USE_DEBUGFS */ /* * Remove a debugfs directory. @@ -114,7 +152,7 @@ void efx_fini_debugfs_child(struct proc_ * directory, and the directory itself. It does not do any recursion * to subdirectories. */ -static void efx_fini_debugfs_dir(struct dentry *dir, +static void efx_fini_debugfs_dir(efx_debugfs_entry *dir, struct efx_debugfs_parameter *params, const char *const *symlink_names) { @@ -159,6 +197,11 @@ int efx_debugfs_read_dword(struct seq_fi return seq_printf(file, "%#x\n", value); } +int efx_debugfs_read_bool(struct seq_file *file, void *data) +{ + return seq_printf(file, "%d\n", *(bool *)data); +} + static int efx_debugfs_read_int_mode(struct seq_file *file, void *data) { unsigned int value = *(enum efx_int_mode *) data; @@ -182,18 +225,6 @@ static int efx_debugfs_read_loop_mode(st #define EFX_LOOPBACK_MODE_PARAMETER(container_type, parameter) \ EFX_PARAMETER(container_type, parameter, \ enum efx_loopback_mode, efx_debugfs_read_loop_mode) - -static int efx_debugfs_read_phy_type(struct seq_file *file, void *data) -{ - unsigned int value = *(enum phy_type *) data; - - return seq_printf(file, "%d => %s\n", value, - STRING_TABLE_LOOKUP(value, efx_phy_type)); -} - -#define EFX_PHY_TYPE_PARAMETER(container_type, parameter) \ - EFX_PARAMETER(container_type, parameter, \ - enum phy_type, efx_debugfs_read_phy_type) int efx_debugfs_read_string(struct seq_file *file, void *data) { @@ -210,14 +241,30 @@ int efx_debugfs_read_string(struct seq_f * Add parameter-files to the given debugfs directory. Return a * negative error code or 0 on success. */ -static int efx_init_debugfs_files(struct dentry *parent, +static int efx_init_debugfs_files(efx_debugfs_entry *parent, struct efx_debugfs_parameter *params, void *structure) { struct efx_debugfs_parameter *param = params; while (param->name) { - struct dentry *entry; + efx_debugfs_entry *entry; +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_DEBUGFS) + struct efx_debugfs_bound_param *binding; + + binding = kmalloc(sizeof(*binding), GFP_KERNEL); + if (!binding) + goto err; + binding->param = param; + binding->structure = structure; + + entry = debugfs_create_file(param->name, S_IRUGO, parent, + binding, &efx_debugfs_file_ops); + if (!entry) { + kfree(binding); + goto err; + } +#else entry = create_proc_entry(param->name, S_IRUGO, parent); if (!entry) goto err; @@ -233,6 +280,7 @@ static int efx_init_debugfs_files(struct entry->proc_fops = &efx_debugfs_file_ops; smp_wmb(); entry->read_proc = (read_proc_t *) structure; +#endif param++; } @@ -258,7 +306,7 @@ static int efx_init_debugfs_files(struct */ int efx_init_debugfs_netdev(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); char name[EFX_DEBUGFS_NAME_LEN]; char target[EFX_DEBUGFS_NAME_LEN]; size_t len; @@ -298,7 +346,7 @@ int efx_init_debugfs_netdev(struct net_d */ void efx_fini_debugfs_netdev(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); debugfs_remove(efx->debug_port_symlink); efx->debug_port_symlink = NULL; @@ -309,16 +357,25 @@ void efx_fini_debugfs_netdev(struct net_ /* Per-port parameters */ static struct efx_debugfs_parameter efx_debugfs_port_parameters[] = { EFX_NAMED_PARAMETER(enabled, struct efx_nic, port_enabled, - int, efx_debugfs_read_int), - EFX_INT_PARAMETER(struct efx_nic, rx_checksum_enabled), + bool, efx_debugfs_read_bool), +#if defined(EFX_USE_KCOMPAT) && !defined(NETIF_F_LRO) + EFX_BOOL_PARAMETER(struct efx_nic, lro_enabled), +#endif + EFX_BOOL_PARAMETER(struct efx_nic, rx_checksum_enabled), EFX_ATOMIC_PARAMETER(struct efx_nic, netif_stop_count), - EFX_INT_PARAMETER(struct efx_nic, link_up), - EFX_UINT_PARAMETER(struct efx_nic, link_options), - EFX_INT_PARAMETER(struct efx_nic, promiscuous), - EFX_UINT_PARAMETER(struct efx_nic, loopback_modes), + EFX_NAMED_PARAMETER(link_up, struct efx_nic, link_state.up, + bool, efx_debugfs_read_bool), + EFX_BOOL_PARAMETER(struct efx_nic, xmac_poll_required), + EFX_NAMED_PARAMETER(link_fd, struct efx_nic, link_state.fd, + bool, efx_debugfs_read_bool), + EFX_NAMED_PARAMETER(link_speed, struct efx_nic, link_state.speed, + unsigned int, efx_debugfs_read_uint), + EFX_BOOL_PARAMETER(struct efx_nic, promiscuous), + EFX_U64_PARAMETER(struct efx_nic, loopback_modes), EFX_LOOPBACK_MODE_PARAMETER(struct efx_nic, loopback_mode), - EFX_PHY_TYPE_PARAMETER(struct efx_nic, phy_type), - EFX_NAMED_PARAMETER(phy_id, struct efx_nic, mii.phy_id, + EFX_UINT_PARAMETER(struct efx_nic, phy_type), + EFX_STRING_PARAMETER(struct efx_nic, phy_name), + EFX_NAMED_PARAMETER(phy_id, struct efx_nic, mdio.prtad, int, efx_debugfs_read_int), EFX_UINT_PARAMETER(struct efx_nic, n_link_state_changes), {NULL}, @@ -344,7 +401,7 @@ int efx_init_debugfs_port(struct efx_nic /* Create files */ rc = efx_init_debugfs_files(efx->debug_port_dir, efx_debugfs_port_parameters, - (void *)efx); + efx); if (rc) efx_fini_debugfs_port(efx); @@ -393,7 +450,7 @@ void efx_trim_debugfs_port(struct efx_ni void efx_trim_debugfs_port(struct efx_nic *efx, struct efx_debugfs_parameter *params) { - struct dentry *dir = efx->debug_port_dir; + efx_debugfs_entry *dir = efx->debug_port_dir; if (dir) { struct efx_debugfs_parameter *field; @@ -408,6 +465,9 @@ static struct efx_debugfs_parameter efx_ EFX_UINT_PARAMETER(struct efx_tx_queue, write_count), EFX_UINT_PARAMETER(struct efx_tx_queue, read_count), EFX_INT_PARAMETER(struct efx_tx_queue, stopped), + EFX_UINT_PARAMETER(struct efx_tx_queue, tso_bursts), + EFX_UINT_PARAMETER(struct efx_tx_queue, tso_long_headers), + EFX_UINT_PARAMETER(struct efx_tx_queue, tso_packets), {NULL}, }; @@ -439,7 +499,7 @@ static int efx_init_debugfs_tx_queue(str /* Create files */ rc = efx_init_debugfs_files(tx_queue->debug_dir, efx_debugfs_tx_queue_parameters, - (void *)tx_queue); + tx_queue); if (rc) goto err; @@ -527,7 +587,7 @@ static int efx_init_debugfs_rx_queue(str /* Create files */ rc = efx_init_debugfs_files(rx_queue->debug_dir, efx_debugfs_rx_queue_parameters, - (void *)rx_queue); + rx_queue); if (rc) goto err; @@ -570,29 +630,21 @@ static void efx_fini_debugfs_rx_queue(st /* Per-channel parameters */ static struct efx_debugfs_parameter efx_debugfs_channel_parameters[] = { - EFX_INT_PARAMETER(struct efx_channel, enabled), + EFX_BOOL_PARAMETER(struct efx_channel, enabled), EFX_INT_PARAMETER(struct efx_channel, irq), - EFX_UINT_PARAMETER(struct efx_channel, has_interrupt), EFX_UINT_PARAMETER(struct efx_channel, irq_moderation), EFX_UINT_PARAMETER(struct efx_channel, eventq_read_ptr), EFX_UINT_PARAMETER(struct efx_channel, n_rx_tobe_disc), - EFX_UINT_PARAMETER(struct efx_channel, n_rx_ip_frag_err), + EFX_UINT_PARAMETER(struct efx_channel, n_rx_ip_frag), EFX_UINT_PARAMETER(struct efx_channel, n_rx_ip_hdr_chksum_err), EFX_UINT_PARAMETER(struct efx_channel, n_rx_tcp_udp_chksum_err), + EFX_UINT_PARAMETER(struct efx_channel, n_rx_eth_crc_err), + EFX_UINT_PARAMETER(struct efx_channel, n_rx_mcast_mismatch), EFX_UINT_PARAMETER(struct efx_channel, n_rx_frm_trunc), EFX_UINT_PARAMETER(struct efx_channel, n_rx_overlength), EFX_UINT_PARAMETER(struct efx_channel, n_skbuff_leaks), EFX_INT_PARAMETER(struct efx_channel, rx_alloc_level), EFX_INT_PARAMETER(struct efx_channel, rx_alloc_push_pages), - EFX_INT_PARAMETER(struct efx_channel, rx_alloc_pop_pages), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_merges), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_bursts), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_slow_start), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_misorder), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_too_many), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_new_stream), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_drop_idle), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_drop_closed), {NULL}, }; @@ -622,7 +674,7 @@ static int efx_init_debugfs_channel(stru /* Create files */ rc = efx_init_debugfs_files(channel->debug_dir, efx_debugfs_channel_parameters, - (void *)channel); + channel); if (rc) goto err; @@ -654,12 +706,13 @@ static void efx_fini_debugfs_channel(str /* Per-NIC parameters */ static struct efx_debugfs_parameter efx_debugfs_nic_parameters[] = { EFX_INT_PARAMETER(struct efx_nic, legacy_irq), - EFX_INT_PARAMETER(struct efx_nic, rss_queues), + EFX_INT_PARAMETER(struct efx_nic, n_rx_queues), EFX_UINT_PARAMETER(struct efx_nic, rx_buffer_len), EFX_INT_MODE_PARAMETER(struct efx_nic, interrupt_mode), + EFX_UINT_PARAMETER(struct efx_nic, state), {.name = "hardware_desc", .offset = 0, - .reader = falcon_debugfs_read_hardware_desc}, + .reader = efx_nic_debugfs_read_desc}, {NULL}, }; @@ -763,12 +816,12 @@ int efx_init_debugfs_nic(struct efx_nic /* Create files */ rc = efx_init_debugfs_files(efx->debug_dir, - efx_debugfs_nic_parameters, (void *)efx); + efx_debugfs_nic_parameters, efx); if (rc) goto err; rc = efx_init_debugfs_files(efx->errors.debug_dir, efx_debugfs_nic_error_parameters, - (void *)&efx->errors); + &efx->errors); if (rc) goto err; @@ -807,7 +860,11 @@ int efx_init_debugfs(void) int efx_init_debugfs(void) { /* Create top-level directory */ - efx_debug_root = proc_mkdir("sfc", proc_root_driver); +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_DEBUGFS) + efx_debug_root = debugfs_create_dir("sfc", NULL); +#else + efx_debug_root = proc_mkdir("driver/sfc", NULL); +#endif if (!efx_debug_root) goto err; @@ -816,6 +873,11 @@ int efx_init_debugfs(void) if (!efx_debug_cards) goto err; +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_DEBUGFS) + /* Create compatibility sym-link */ + if (!proc_symlink("driver/sfc", NULL, "/sys/kernel/debug/sfc")) + goto err; +#endif return 0; err: @@ -830,7 +892,9 @@ int efx_init_debugfs(void) */ void efx_fini_debugfs(void) { - remove_proc_entry("sfc", proc_root_driver); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_DEBUGFS) + remove_proc_entry("driver/sfc", NULL); +#endif debugfs_remove(efx_debug_cards); efx_debug_cards = NULL; debugfs_remove(efx_debug_root); diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/debugfs.h --- a/drivers/net/sfc/debugfs.h Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/debugfs.h Fri Jan 08 13:05:49 2010 +0000 @@ -1,145 +1,15 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2006-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> - * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> + * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_DEBUGFS_H #define EFX_DEBUGFS_H - -#ifdef CONFIG_SFC_DEBUGFS - -struct seq_file; - -struct efx_debugfs_parameter { - const char *name; - size_t offset; - int (*reader)(struct seq_file *, void *); -}; - -extern void efx_fini_debugfs_child(struct dentry *dir, const char *name); -extern int efx_init_debugfs_netdev(struct net_device *net_dev); -extern void efx_fini_debugfs_netdev(struct net_device *net_dev); -extern int efx_init_debugfs_port(struct efx_nic *efx); -extern void efx_fini_debugfs_port(struct efx_nic *efx); -extern int efx_init_debugfs_nic(struct efx_nic *efx); -extern void efx_fini_debugfs_nic(struct efx_nic *efx); -extern int efx_init_debugfs_channels(struct efx_nic *efx); -extern void efx_fini_debugfs_channels(struct efx_nic *efx); -extern int efx_init_debugfs(void); -extern void efx_fini_debugfs(void); -extern int efx_extend_debugfs_port(struct efx_nic *efx, - void *context, - struct efx_debugfs_parameter *params); -extern void efx_trim_debugfs_port(struct efx_nic *efx, - struct efx_debugfs_parameter *params); - -/* Helpers for handling debugfs entry reads */ -extern int efx_debugfs_read_uint(struct seq_file *, void *); -extern int efx_debugfs_read_string(struct seq_file *, void *); -extern int efx_debugfs_read_int(struct seq_file *, void *); -extern int efx_debugfs_read_atomic(struct seq_file *, void *); -extern int efx_debugfs_read_dword(struct seq_file *, void *); - -/* Handy macros for filling out parameters */ - -/* Initialiser for a struct efx_debugfs_parameter with type-checking */ -#define EFX_PARAMETER(container_type, parameter, field_type, \ - reader_function) { \ - .name = #parameter, \ - .offset = ((((field_type *) 0) == \ - &((container_type *) 0)->parameter) ? \ - offsetof(container_type, parameter) : \ - offsetof(container_type, parameter)), \ - .reader = reader_function, \ -} - -/* Likewise, but the file name is not taken from the field name */ -#define EFX_NAMED_PARAMETER(_name, container_type, parameter, field_type, \ - reader_function) { \ - .name = #_name, \ - .offset = ((((field_type *) 0) == \ - &((container_type *) 0)->parameter) ? \ - offsetof(container_type, parameter) : \ - offsetof(container_type, parameter)), \ - .reader = reader_function, \ -} - -/* Likewise, but with one file for each of 4 lanes */ -#define EFX_PER_LANE_PARAMETER(prefix, suffix, container_type, parameter, \ - field_type, reader_function) { \ - .name = prefix "0" suffix, \ - .offset = ((((field_type *) 0) == \ - ((container_type *) 0)->parameter) ? \ - offsetof(container_type, parameter[0]) : \ - offsetof(container_type, parameter[0])), \ - .reader = reader_function, \ -}, { \ - .name = prefix "1" suffix, \ - .offset = offsetof(container_type, parameter[1]), \ - .reader = reader_function, \ -}, { \ - .name = prefix "2" suffix, \ - .offset = offsetof(container_type, parameter[2]), \ - .reader = reader_function, \ -}, { \ - .name = prefix "3" suffix, \ - .offset = offsetof(container_type, parameter[3]), \ - .reader = reader_function, \ -} - -/* A string parameter (string embedded in the structure) */ -#define EFX_STRING_PARAMETER(container_type, parameter) { \ - .name = #parameter, \ - .offset = ((((char *) 0) == \ - ((container_type *) 0)->parameter) ? \ - offsetof(container_type, parameter) : \ - offsetof(container_type, parameter)), \ - .reader = efx_debugfs_read_string, \ -} - -/* An unsigned integer parameter */ -#define EFX_UINT_PARAMETER(container_type, parameter) \ - EFX_PARAMETER(container_type, parameter, \ - unsigned int, efx_debugfs_read_uint) - -/* A dword parameter */ -#define EFX_DWORD_PARAMETER(container_type, parameter) \ - EFX_PARAMETER(container_type, parameter, \ - efx_dword_t, efx_debugfs_read_dword) - -/* An atomic_t parameter */ -#define EFX_ATOMIC_PARAMETER(container_type, parameter) \ - EFX_PARAMETER(container_type, parameter, \ - atomic_t, efx_debugfs_read_atomic) - -/* An integer parameter */ -#define EFX_INT_PARAMETER(container_type, parameter) \ - EFX_PARAMETER(container_type, parameter, \ - int, efx_debugfs_read_int) - -#else /* !CONFIG_SFC_DEBUGFS */ static inline int efx_init_debugfs_netdev(struct net_device *net_dev) { @@ -167,6 +37,4 @@ static inline int efx_init_debugfs(void) } static inline void efx_fini_debugfs(void) {} -#endif /* CONFIG_SFC_DEBUGFS */ - #endif /* EFX_DEBUGFS_H */ diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/driverlink.c --- a/drivers/net/sfc/driverlink.c Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/driverlink.c Fri Jan 08 13:05:49 2010 +0000 @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005: Fen Systems Ltd. - * Copyright 2005-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> - * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005 Fen Systems Ltd. + * 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 * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include <linux/module.h> @@ -31,13 +14,10 @@ #include <linux/rtnetlink.h> #include "net_driver.h" #include "efx.h" +#include "driverlink_api.h" #include "driverlink.h" -/* Driverlink semaphore - * This semaphore must be held for any operation that modifies any of - * the driverlink lists. - */ -static DEFINE_MUTEX(efx_driverlink_lock); +/* Global lists are protected by rtnl_lock */ /* List of all registered drivers */ static LIST_HEAD(efx_driver_list); @@ -45,28 +25,27 @@ static LIST_HEAD(efx_driver_list); /* List of all registered Efx ports */ static LIST_HEAD(efx_port_list); -/* Driver link handle used internally to track devices */ +/** + * Driver link handle used internally to track devices + * @efx_dev: driverlink device handle exported to consumers + * @efx: efx_nic backing the driverlink device + * @port_node: per-device list head + * @driver_node: per-driver list head + */ struct efx_dl_handle { - /* The efx_dl_device consumers see */ struct efx_dl_device efx_dev; - /* The efx_nic providers provide */ struct efx_nic *efx; - /* Per-device list */ struct list_head port_node; - /* Per-driver list */ struct list_head driver_node; }; -/* Get the handle for an efx_dl_device */ static struct efx_dl_handle *efx_dl_handle(struct efx_dl_device *efx_dev) { return container_of(efx_dev, struct efx_dl_handle, efx_dev); } -/* Remove an Efx device - * You must hold the efx_driverlink_lock before calling this - * function. - */ +/* Remove an Efx device, and call the driver's remove() callback if + * present. The caller must hold rtnl_lock. */ static void efx_dl_del_device(struct efx_dl_device *efx_dev) { struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); @@ -74,23 +53,18 @@ static void efx_dl_del_device(struct efx EFX_INFO(efx_handle->efx, "%s driverlink client unregistering\n", efx_dev->driver->name); - /* Call driver's remove() routine */ if (efx_dev->driver->remove) efx_dev->driver->remove(efx_dev); - /* Remove handle from per-driver and per-NIC lists */ list_del(&efx_handle->driver_node); list_del(&efx_handle->port_node); - /* Free efx_handle structure */ kfree(efx_handle); } -/* Try to add an Efx device - * Attempt to probe the given device with the driver, creating a - * new efx_dl_device. If the probe routine fails, because the driver - * doesn't support this port, then the efx_dl_device is destroyed, - */ +/* Attempt to probe the given device with the driver, creating a + * new &struct efx_dl_device. If the probe routine returns an error, + * then the &struct efx_dl_device is destroyed */ static void efx_dl_try_add_device(struct efx_nic *efx, struct efx_dl_driver *driver) { @@ -98,8 +72,9 @@ static void efx_dl_try_add_device(struct struct efx_dl_device *efx_dev; int rc; - /* Allocate and initialise new efx_dl_device structure */ efx_handle = kzalloc(sizeof(*efx_handle), GFP_KERNEL); + if (!efx_handle) + goto fail; efx_dev = &efx_handle->efx_dev; efx_handle->efx = efx; efx_dev->driver = driver; @@ -107,13 +82,11 @@ static void efx_dl_try_add_device(struct INIT_LIST_HEAD(&efx_handle->port_node); INIT_LIST_HEAD(&efx_handle->driver_node); - /* Attempt driver probe */ rc = driver->probe(efx_dev, efx->net_dev, - efx->dl_info, efx->silicon_rev); + efx->dl_info, efx->type->dl_revision); if (rc) goto fail; - /* Add device to per-driver and per-NIC lists */ list_add_tail(&efx_handle->driver_node, &driver->device_list); list_add_tail(&efx_handle->port_node, &efx->dl_device_list); @@ -123,16 +96,11 @@ static void efx_dl_try_add_device(struct fail: EFX_INFO(efx, "%s driverlink client skipped\n", driver->name); - kfree(efx_dev); -} - -/** - * efx_dl_unregister_driver - unregister an Efx device driver - * @driver: Efx driverlink driver - * - * Unregisters an Efx driver. The driver's remove() method will be - * called for all Efx devices currently claimed by the driver. - */ + kfree(efx_handle); +} + +/* Unregister a driver from the driverlink layer, calling the + * driver's remove() callback for every attached device */ void efx_dl_unregister_driver(struct efx_dl_driver *driver) { struct efx_dl_handle *efx_handle, *efx_handle_n; @@ -140,8 +108,7 @@ void efx_dl_unregister_driver(struct efx printk(KERN_INFO "Efx driverlink unregistering %s driver\n", driver->name); - /* Acquire lock. We can't return failure */ - mutex_lock(&efx_driverlink_lock); + rtnl_lock(); list_for_each_entry_safe(efx_handle, efx_handle_n, &driver->device_list, driver_node) @@ -149,46 +116,29 @@ void efx_dl_unregister_driver(struct efx list_del(&driver->node); - mutex_unlock(&efx_driverlink_lock); + rtnl_unlock(); } EXPORT_SYMBOL(efx_dl_unregister_driver); -/** - * efx_dl_register_driver - register an Efx device driver - * @driver: Efx driverlink driver - * - * Registers a new Efx driver. The driver's probe() method will be - * called for all Efx NICs currently registered. - * - * Return a negative error code or 0 on success. - */ +/* Register a new driver with the driverlink layer. The driver's + * probe routine will be called for every attached nic. */ int efx_dl_register_driver(struct efx_dl_driver *driver) { struct efx_nic *efx; - int rc; printk(KERN_INFO "Efx driverlink registering %s driver\n", driver->name); - /* Initialise driver list structures */ INIT_LIST_HEAD(&driver->node); INIT_LIST_HEAD(&driver->device_list); - /* Acquire lock */ - rc = mutex_lock_interruptible(&efx_driverlink_lock); - if (rc) - return rc; - - /* Add driver to driver list */ + rtnl_lock(); + list_add_tail(&driver->node, &efx_driver_list); - - /* Feed all existing devices to driver */ list_for_each_entry(efx, &efx_port_list, dl_node) efx_dl_try_add_device(efx, driver); - /* Release locks */ - mutex_unlock(&efx_driverlink_lock); - + rtnl_unlock(); return 0; } EXPORT_SYMBOL(efx_dl_register_driver); @@ -197,85 +147,60 @@ void efx_dl_unregister_nic(struct efx_ni { struct efx_dl_handle *efx_handle, *efx_handle_n; - if (!efx) - return; - - /* Acquire lock. We can't return failure, so have to use - * down() instead of down_interruptible() - */ - mutex_lock(&efx_driverlink_lock); - - /* Remove all devices related to this NIC */ + ASSERT_RTNL(); + list_for_each_entry_safe_reverse(efx_handle, efx_handle_n, &efx->dl_device_list, port_node) efx_dl_del_device(&efx_handle->efx_dev); - /* Remove port from port list */ list_del(&efx->dl_node); - - /* Release lock */ - mutex_unlock(&efx_driverlink_lock); -} - -int efx_dl_register_nic(struct efx_nic *efx) +} + +void efx_dl_register_nic(struct efx_nic *efx) { struct efx_dl_driver *driver; - int rc; - - /* Acquire lock */ - rc = mutex_lock_interruptible(&efx_driverlink_lock); - if (rc) - return rc; - - /* Add port to port list */ + + ASSERT_RTNL(); + list_add_tail(&efx->dl_node, &efx_port_list); - - /* Feed port to all existing drivers */ list_for_each_entry(driver, &efx_driver_list, node) efx_dl_try_add_device(efx, driver); - - /* Release lock */ - mutex_unlock(&efx_driverlink_lock); - - return 0; -} - -/* - * Dummy callback implementations. - * +} + +/* Dummy callback implementations. * To avoid a branch point on the fast-path, the callbacks are always * implemented - they are never NULL. */ -static enum efx_veto fastcall -efx_dummy_tx_packet_callback(struct efx_dl_device *efx_dev, struct sk_buff *skb) -{ - /* Never veto the packet */ + +static enum efx_veto +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +fastcall +#endif +efx_dummy_tx_packet_callback(struct efx_dl_device *efx_dev, + struct sk_buff *skb) +{ return EFX_ALLOW_PACKET; } -static enum efx_veto fastcall +static enum efx_veto +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +fastcall +#endif efx_dummy_rx_packet_callback(struct efx_dl_device *efx_dev, - const char *pkt_buf, int len) -{ - /* Never veto the packet */ + const char *pkt_buf, int len) +{ return EFX_ALLOW_PACKET; } -static void -efx_dummy_link_change_callback(struct efx_dl_device *efx_dev, int link_up) -{ -} - -static int -efx_dummy_request_mtu_callback(struct efx_dl_device *efx_dev, int new_mtu) -{ - /* Always allow */ +static int efx_dummy_request_mtu_callback(struct efx_dl_device *efx_dev, + int new_mtu) +{ return 0; } -static void -efx_dummy_mtu_changed_callback(struct efx_dl_device *efx_dev, int mtu) +static void efx_dummy_mtu_changed_callback(struct efx_dl_device *efx_dev, + int mtu) { return; } @@ -288,106 +213,54 @@ struct efx_dl_callbacks efx_default_call struct efx_dl_callbacks efx_default_callbacks = { .tx_packet = efx_dummy_tx_packet_callback, .rx_packet = efx_dummy_rx_packet_callback, - .link_change = efx_dummy_link_change_callback, .request_mtu = efx_dummy_request_mtu_callback, .mtu_changed = efx_dummy_mtu_changed_callback, .event = efx_dummy_event_callback, }; -#define EFX_DL_UNREGISTER_CALLBACK(_port, _dev, _member) \ - do { \ - BUG_ON((_port)->dl_cb_dev._member != (_dev)); \ - (_port)->dl_cb._member = \ - efx_default_callbacks._member; \ - (_port)->dl_cb_dev._member = NULL; \ - } while (0) - - -#define EFX_DL_REGISTER_CALLBACK(_port, _dev, _from, _member) \ - if ((_from)->_member) { \ - BUG_ON((_port)->dl_cb_dev._member != NULL); \ - (_port)->dl_cb._member = (_from)->_member; \ - (_port)->dl_cb_dev._member = _dev; \ - } - -/** - * efx_dl_unregister_callbacks - unregister callbacks for an Efx NIC - * @efx_dev: Efx driverlink device - * @callbacks: Callback list - * - * This removes a set of callbacks registered with - * efx_dl_register_callbacks(). It should be called as part of the - * client's remove() method. - * - * The net driver will ensure that all callback functions have - * returned to the net driver before efx_dl_unregister_callbacks() - * returns. Note that the device itself may still be running when the - * client's remove() method is called. The client must therefore - * unhook its callbacks using efx_dl_unregister_callbacks() and only - * then ensure that any delayed tasks triggered by callback methods - * (e.g. scheduled tasklets) have completed. - */ void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev, struct efx_dl_callbacks *callbacks) { struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); struct efx_nic *efx = efx_handle->efx; - /* Suspend net driver operations */ - efx_suspend(efx); + ASSERT_RTNL(); + + efx_stop_all(efx); EFX_INFO(efx, "removing callback hooks into %s driver\n", efx_dev->driver->name); - if (callbacks->tx_packet) - EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, tx_packet); - - if (callbacks->rx_packet) - EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, rx_packet); - - if (callbacks->link_change) - EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, link_change); - - if (callbacks->request_mtu) - EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, request_mtu); - - if (callbacks->mtu_changed) - EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, mtu_changed); - - if (callbacks->event) - EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, event); - - /* Resume net driver operations */ - efx_resume(efx); + if (callbacks->tx_packet) { + BUG_ON(efx->dl_cb_dev.tx_packet != efx_dev); + efx->dl_cb.tx_packet = efx_default_callbacks.tx_packet; + efx->dl_cb_dev.tx_packet = NULL; + } + if (callbacks->rx_packet) { + BUG_ON(efx->dl_cb_dev.rx_packet != efx_dev); + efx->dl_cb.rx_packet = efx_default_callbacks.rx_packet; + efx->dl_cb_dev.rx_packet = NULL; + } + if (callbacks->request_mtu) { + BUG_ON(efx->dl_cb_dev.request_mtu != efx_dev); + efx->dl_cb.request_mtu = efx_default_callbacks.request_mtu; + efx->dl_cb_dev.request_mtu = NULL; + } + if (callbacks->mtu_changed) { + BUG_ON(efx->dl_cb_dev.mtu_changed != efx_dev); + efx->dl_cb.mtu_changed = efx_default_callbacks.mtu_changed; + efx->dl_cb_dev.mtu_changed = NULL; + } + if (callbacks->event) { + BUG_ON(efx->dl_cb_dev.event != efx_dev); + efx->dl_cb.event = efx_default_callbacks.event; + efx->dl_cb_dev.event = NULL; + } + + efx_start_all(efx); } EXPORT_SYMBOL(efx_dl_unregister_callbacks); -/** - * efx_dl_register_callbacks - register callbacks for an Efx NIC - * @efx_dev: Efx driverlink device - * @callbacks: Callback list - * - * This registers a set of callback functions with the net driver. - * These functions will be called at various key points to allow - * external code to monitor and/or modify the behaviour of the network - * driver. Any of the callback function pointers may be %NULL if a - * callback is not required. The intended user of this mechanism is - * the SFC char driver. - * - * This client should call efx_dl_register_callbacks() during its - * probe() method. The client must ensure that it also calls - * efx_dl_unregister_callbacks() as part of its remove() method. - * - * Only one function may be registered for each callback per NIC. - * If a requested callback is already registered for this NIC, this - * function will return -%EBUSY. - * - * The device may already be running, so the client must be prepared - * for callbacks to be triggered immediately after calling - * efx_dl_register_callbacks(). - * - * Return a negative error code or 0 on success. - */ int efx_dl_register_callbacks(struct efx_dl_device *efx_dev, struct efx_dl_callbacks *callbacks) { @@ -395,13 +268,13 @@ int efx_dl_register_callbacks(struct efx struct efx_nic *efx = efx_handle->efx; int rc = 0; - /* Suspend net driver operations */ - efx_suspend(efx); + ASSERT_RTNL(); + + efx_stop_all(efx); /* Check that the requested callbacks are not already hooked. */ if ((callbacks->tx_packet && efx->dl_cb_dev.tx_packet) || (callbacks->rx_packet && efx->dl_cb_dev.rx_packet) || - (callbacks->link_change && efx->dl_cb_dev.link_change) || (callbacks->request_mtu && efx->dl_cb_dev.request_mtu) || (callbacks->mtu_changed && efx->dl_cb_dev.mtu_changed) || (callbacks->event && efx->dl_cb_dev.event)) { @@ -412,35 +285,36 @@ int efx_dl_register_callbacks(struct efx EFX_INFO(efx, "adding callback hooks to %s driver\n", efx_dev->driver->name); - /* Hook in callbacks. For maximum speed, we never check to - * see whether these are NULL before calling; therefore we - * must ensure that they are never NULL. If the set we're - * being asked to hook in is sparse, we leave the default - * values in place for the empty hooks. - */ - EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, tx_packet); - EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, rx_packet); - EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, link_change); - EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, request_mtu); - EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, mtu_changed); - EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, event); + /* Hook in the requested callbacks, leaving any NULL members + * referencing the members of @efx_default_callbacks */ + if (callbacks->tx_packet) { + efx->dl_cb.tx_packet = callbacks->tx_packet; + efx->dl_cb_dev.tx_packet = efx_dev; + } + if (callbacks->rx_packet) { + efx->dl_cb.rx_packet = callbacks->rx_packet; + efx->dl_cb_dev.rx_packet = efx_dev; + } + if (callbacks->request_mtu) { + efx->dl_cb.request_mtu = callbacks->request_mtu; + efx->dl_cb_dev.request_mtu = efx_dev; + } + if (callbacks->mtu_changed) { + efx->dl_cb.mtu_changed = callbacks->mtu_changed; + efx->dl_cb_dev.mtu_changed = efx_dev; + } + if (callbacks->event) { + efx->dl_cb.event = callbacks->event; + efx->dl_cb_dev.event = efx_dev; + } out: - /* Resume net driver operations */ - efx_resume(efx); + efx_start_all(efx); return rc; } EXPORT_SYMBOL(efx_dl_register_callbacks); -/** - * efx_dl_schedule_reset - schedule an Efx NIC reset - * @efx_dev: Efx driverlink device - * - * This schedules a hardware reset for a short time in the future. It - * can be called from any context, and so can be used when - * efx_dl_reset() cannot be called. - */ void efx_dl_schedule_reset(struct efx_dl_device *efx_dev) { struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); @@ -450,41 +324,15 @@ void efx_dl_schedule_reset(struct efx_dl } EXPORT_SYMBOL(efx_dl_schedule_reset); -/* - * Lock the driverlink layer before a reset - * To avoid deadlock, efx_driverlink_lock needs to be acquired before - * efx->suspend_lock. - */ -void efx_dl_reset_lock(void) -{ - /* Acquire lock */ - mutex_lock(&efx_driverlink_lock); -} - -/* - * Unlock the driverlink layer after a reset - * This call must be matched against efx_dl_reset_lock. - */ -void efx_dl_reset_unlock(void) -{ - /* Acquire lock */ - mutex_unlock(&efx_driverlink_lock); -} - -/* - * Suspend ready for reset - * This calls the reset_suspend method of all drivers registered to - * the specified NIC. It must only be called between - * efx_dl_reset_lock and efx_dl_reset_unlock. - */ +/* Suspend ready for reset, calling the reset_suspend() callback of every + * registered driver */ void efx_dl_reset_suspend(struct efx_nic *efx) { struct efx_dl_handle *efx_handle; struct efx_dl_device *efx_dev; - BUG_ON(!mutex_is_locked(&efx_driverlink_lock)); - - /* Call suspend method of each driver in turn */ + ASSERT_RTNL(); + list_for_each_entry_reverse(efx_handle, &efx->dl_device_list, port_node) { @@ -494,20 +342,15 @@ void efx_dl_reset_suspend(struct efx_nic } } -/* - * Resume after a reset - * This calls the reset_resume method of all drivers registered to the - * specified NIC. It must only be called between efx_dl_reset_lock - * and efx_dl_reset_unlock. - */ +/* Resume after a reset, calling the resume() callback of every registered + * driver */ void efx_dl_reset_resume(struct efx_nic *efx, int ok) { struct efx_dl_handle *efx_handle; struct efx_dl_device *efx_dev; - BUG_ON(!mutex_is_locked(&efx_driverlink_lock)); - - /* Call resume method of each driver in turn */ + ASSERT_RTNL(); + list_for_each_entry(efx_handle, &efx->dl_device_list, port_node) { efx_dev = &efx_handle->efx_dev; @@ -516,14 +359,9 @@ void efx_dl_reset_resume(struct efx_nic } } -/** - * efx_dl_get_nic - obtain the Efx NIC for the given driverlink device - * @efx_dev: Efx driverlink device - * - * Get a pointer to the &struct efx_nic corresponding to - * @efx_dev. This can be used by driverlink clients built along with - * the sfc driver, which may have intimate knowledge of its internals. - */ +/* Obtain a pointer to the &struct efx_nic corresponding to @efx_dev. This + * can be used by driverlink clients built along with the sfc driver, which + * may have initimate knowledge of its internals. */ struct efx_nic *efx_dl_get_nic(struct efx_dl_device *efx_dev) { return efx_dl_handle(efx_dev)->efx; diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/driverlink.h --- a/drivers/net/sfc/driverlink.h Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/driverlink.h Fri Jan 08 13:05:49 2010 +0000 @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005: Fen Systems Ltd. - * Copyright 2006: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> - * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_DRIVERLINK_H @@ -32,59 +15,29 @@ struct efx_dl_device; struct efx_dl_device; struct efx_nic; -/* - * Efx driverlink - * - * This header file defines the portions of the Efx driverlink - * interface that are used only within the sfc module. It also - * declares efx_dl_get_nic(), which may be used by sfc_mtd - * and any other module built along with sfc. - */ - - /* Efx callback devices * * A list of the devices that own each callback. The partner to - * struct efx_dl_callbacks + * struct efx_dl_callbacks. */ struct efx_dl_cb_devices { - /* Device owning the tx_packet callback */ struct efx_dl_device *tx_packet; - /* Device owning the rx_packet callback */ struct efx_dl_device *rx_packet; - /* Device owning the link_change callback. */ - struct efx_dl_device *link_change; - /* Device owning the request_mtu callback. */ struct efx_dl_device *request_mtu; - /* Device owning the mtu_changed callback. */ struct efx_dl_device *mtu_changed; - /* Device owning the event callback. */ struct efx_dl_device *event; }; -/* No-op callbacks used for initialisation */ extern struct efx_dl_callbacks efx_default_callbacks; -/* Macro used to invoke callbacks */ #define EFX_DL_CALLBACK(_port, _name, ...) \ (_port)->dl_cb._name((_port)->dl_cb_dev._name, __VA_ARGS__) -/* Register an Efx NIC */ -extern int efx_dl_register_nic(struct efx_nic *efx); - -/* Unregister an Efx NIC */ +extern void efx_dl_register_nic(struct efx_nic *efx); extern void efx_dl_unregister_nic(struct efx_nic *efx); -/* Lock the driverlink layer prior to a reset */ -extern void efx_dl_reset_lock(void); - -/* Unlock the driverlink layer following a reset */ -extern void efx_dl_reset_unlock(void); - -/* Suspend all drivers prior to a hardware reset */ +/* Suspend and resume client drivers over a hardware reset */ extern void efx_dl_reset_suspend(struct efx_nic *efx); - -/* Resume all drivers after a hardware reset */ extern void efx_dl_reset_resume(struct efx_nic *efx, int ok); /* Obtain the Efx NIC for the given driverlink device. */ diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/driverlink_api.h --- a/drivers/net/sfc/driverlink_api.h Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/driverlink_api.h Fri Jan 08 13:05:49 2010 +0000 @@ -1,82 +1,24 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2005-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> - * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_DRIVERLINK_API_H #define EFX_DRIVERLINK_API_H -#include <linux/list.h> /* for struct list_head */ -#include <linux/linkage.h> -#define EFX_USE_FASTCALL yes - -/** - * DOC: Efx driverlink API - * - * This file must be included by any driver that wishes to attach to - * devices claimed by the Solarflare NIC driver (sfc). It allows separate - * kernel modules to expose other functionality offered by the NIC, with - * the sfc driver remaining in overall control. - * - * Overview: - * - * Driverlink clients define a &struct efx_dl_driver, and register - * this structure with the driverlink layer using - * efx_dl_register_driver(), which is exported by the sfc driver. - * - * The probe() routine of each driverlink client driver is called by - * the driverlink layer for each physical port in the system, after - * the sfc driver has performed start-of-day hardware initialisation - * and self-test. If ports are added or removed via pci hotplug then - * the &struct efx_dl_driver probe() or remove() routines are called - * as appropriate. - * - * If the port doesn't provide the necessary hardware resources for a - * client, then that client can return failure from its probe() - * routine. Information provided to the client driver at probe time - * includes - * - * Each probe() routine is given a unique &struct efx_dl_device per - * port, which means it can safely use the @priv member to store any - * useful state it needs. The probe routine also has the opportunity - * to provide a &struct efx_dl_callbacks via - * efx_dl_register_callbacks(), which allows the client to intercept - * the sfc driver's operations at strategic points. - * - * Occasionally, the underlying Efx device may need to be reset to - * recover from an error condition. The client's reset_suspend() and - * reset_resume() methods [if provided] will be called to enable the - * client to suspend operations and preserve any state before the - * reset. The client can itself request a reset using efx_dl_reset() - * or efx_dl_schedule_reset(), should it detect an error condition - * necessitating a reset. - * - * Example: - * - * The MTD driver (mtd.c) uses the driverlink layer. - */ +#include <linux/list.h> +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_FASTCALL) + #include <linux/version.h> + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + #define EFX_USE_FASTCALL yes + #include <linux/linkage.h> + #endif +#endif /* Forward declarations */ struct pci_dev; @@ -85,160 +27,45 @@ struct efx_dl_device; struct efx_dl_device; struct efx_dl_device_info; -/* - * This is used to guard against the registration of driverlink - * clients using an incorrect version of the API. - */ +/* An extra safeguard in addition to symbol versioning */ #define EFX_DRIVERLINK_API_VERSION 1 - /** * struct efx_dl_driver - An Efx driverlink device driver * - * This is the analogue of a struct pci_driver for a normal PCI - * driver. Driverlink clients should register themselves using - * efx_dl_register_driver() at module initialisation, and deregister - * themselves using efx_dl_unregister_driver() at module exit. - * - * All calls to members of efx_dl_driver are serialised by a single - * semaphore, so you are allowed to sleep in these functions. Take care - * to not call driverlink methods from within these callbacks, otherwise - * a deadlock is possible. + * A driverlink client defines and initializes as many instances of + * efx_dl_driver as required, registering each one with + * efx_dl_register_driver(). * * @name: Name of the driver * @probe: Called when device added + * The client should use the @def_info linked list and @silicon_rev + * to determine if they wish to attach to this device. + * Context: process, rtnl_lock or driverlink mutex held * @remove: Called when device removed + * The client must ensure the finish all operations with this + * device before returning from this method. + * Context: process, rtnl_lock or driverlink mutex held * @reset_suspend: Called before device is reset + * Called immediately before a hardware reset. The client must stop all + * hardware processing before returning from this method. Callbacks will + * be inactive when this method is called. + * Context: process, rtnl_lock and/or driverlink mutex held * @reset_resume: Called after device is reset + * Called after a hardware reset. If @ok is true, the client should + * state and resume normal operations. If @ok is false, the client should + * abandon use of the hardware resources. remove() will still be called. + * Context: process, rtnl_lock and/or driverlink mutex held */ struct efx_dl_driver { const char *name; - /* - * probe - Handle device addition. - * @efx_dev: Efx driverlink device - * @net_dev: The net_dev relevant to this port - * @dev_info: A linked list of device information. - * @silicon_rev: Silicon revision name. - * - * This will be called after driverlink client registration for - * every port on the system, and for every port that appears - * thereafter via hotplug. - * - * The client may use either @efx_dev->pci_dev, the dev_info linked - * list of available driver information, or the silicon revision - * name to determine if they can support this port. If they can, - * they should return 0 to indicate the probe was successful. Any - * other return code indicates that the probe failed, and the - * @efx_dl_dev will be invalidated. - * - * The client should perform whatever initialisation it - * requires, and store a pointer to its private data in - * @efx_dl_dev->priv (which is not shared between clients). - * It may also wish to hook in a callbacks table using - * efx_dl_register_callbacks(). - * - * Return a negative error code or 0 on success. - */ int (*probe) (struct efx_dl_device *efx_dl_dev, const struct net_device *net_dev, const struct efx_dl_device_info *dev_info, const char *silicon_rev); - - /* - * remove - Handle device removal. - * @efx_dev: Efx driverlink device - * - * This will be called at driver exit (or hotplug removal) for - * each registered driverlink client. - * - * The client must ensure that it has finished all operations - * using this device before returning from this method. If it - * has hooked in a callbacks table using - * efx_dl_register_callbacks(), it must unhook it using - * efx_dl_unregister_callbacks(), and then ensure that all - * callback-triggered operations (e.g. scheduled tasklets) - * have completed before returning. (It does not need to - * explicitly wait for callback methods to finish executing, - * since efx_dl_unregister_callbacks() will sleep until all - * callbacks have returned anyway.) - * - * Note that the device itself may not have been removed; it - * may be simply that the client is being unloaded - * via efx_dl_unregister_driver(). In this case other clients - * (and the sfc driver itself) will still be using the device, - * so the client cannot assume that the device itself is quiescent. - * In particular, callbacks may continue to be triggered at any - * point until efx_dl_unregister_callbacks() is called. - */ void (*remove) (struct efx_dl_device *efx_dev); - - /* - * reset_suspend - Suspend ready for reset. - * @efx_dev: Efx driverlink device - * - * This method will be called immediately before a hardware - * reset (which may or may not have been initiated by the - * driverlink client). This client must save any state that it - * will need to restore after the reset, and suspend all - * operations that might access the hardware. It must not - * return until the client can guarantee to have stopped - * touching the hardware. - * - * It is guaranteed that callbacks will be inactive by the - * time this method is called; the driverlink layer will - * already have prevented new callbacks being made and waited - * for all callbacks functions to return before calling - * reset_suspend(). However, any delayed work scheduled by - * the callback functions (e.g. tasklets) may not yet have - * completed. - * - * This method is allowed to sleep, so waiting on tasklets, - * work queues etc. is permitted. There will always be a - * corresponding call to the reset_resume() method, so it is - * safe to e.g. down a semaphore within reset_suspend() and up - * it within reset_resume(). (However, you obviously cannot - * do the same with a spinlock). - * - * Note that the reset operation may be being carried out in - * the context of scheduled work, so you cannot use - * flush_scheduled_work() to ensure that any work you may have - * scheduled has completed. - * - * During hardware reset, there is a chance of receiving - * spurious interrupts, so the client's ISR (if any) should be - * unhooked or otherwise disabled. - */ void (*reset_suspend) (struct efx_dl_device *efx_dev); - - /* - * reset_resume - Restore after a reset. - * @efx_dev: Efx driverlink device - * @ok: Reset success indicator - * - * This method will be called after a hardware reset. There - * will always have been a corresponding call to the - * reset_suspend() method beforehand. - * - * If @ok is non-zero, the client should restore the state - * that it saved during the call to reset_suspend() and resume - * normal operations. - * - * If @ok is zero, the reset operation has failed and the - * hardware is currently in an unusable state. In this case, - * the client should release any locks taken out by - * reset_suspend(), but should not take any other action; in - * particular, it must not access the hardware, nor resume - * normal operations. The hardware is effectively dead at - * this point, and our sole aim is to avoid deadlocking or - * crashing the host. - * - * The driverlink layer will still be locked when - * reset_resume() is called, so the client may not call - * driverlink functions. In particular, if the reset failed, - * the client must not call efx_dl_unregister_callbacks() at - * this point; it should wait until remove() is called. - */ void (*reset_resume) (struct efx_dl_device *efx_dev, int ok); /* private: */ @@ -247,23 +74,10 @@ struct efx_dl_driver { }; /** - * DOC: Efx driverlink device information - * - * Each &struct efx_dl_device makes certain hardware resources visible - * to driverlink clients, and they describe which resources are - * available by passing a linked list of &struct efx_dl_device_info - * into the probe() routine. - * - * The driverlink client's probe function can iterate through the linked list, - * and provided that it understands the resources that are exported, it can - * choose to make use of them through an external interface. - */ - -/** * enum efx_dl_device_info_type - Device information identifier. * - * Each distinct hardware resource API will have a member in this - * enumeration. + * Used to identify each item in the &struct efx_dl_device_info linked list + * provided to each driverlink client in the probe() @dev_info member. * * @EFX_DL_FALCON_RESOURCES: Information type is &struct efx_dl_falcon_resources */ @@ -274,14 +88,9 @@ enum efx_dl_device_info_type { /** * struct efx_dl_device_info - device information structure + * * @next: Link to next structure, if any * @type: Type code for this structure - * - * This structure is embedded in other structures provided by the - * driverlink device provider, and implements a linked list of - * resources pertinent to a driverlink client. - * - * Example: &struct efx_dl_falcon_resources */ struct efx_dl_device_info { struct efx_dl_device_info *next; @@ -291,18 +100,16 @@ struct efx_dl_device_info { /** * enum efx_dl_falcon_resource_flags - Falcon resource information flags. * - * Flags that describe hardware variations for the described Falcon based port. + * Flags that describe hardware variations for the current Falcon device. * * @EFX_DL_FALCON_DUAL_FUNC: Port is dual-function. * Certain silicon revisions have two pci functions, and require * certain hardware resources to be accessed via the secondary - * function. See the discussion of @pci_dev in &struct efx_dl_device - * below. + * function * @EFX_DL_FALCON_USE_MSI: Port is initialised to use MSI/MSI-X interrupts. * Falcon supports traditional legacy interrupts and MSI/MSI-X - * interrupts. Since the sfc driver supports either, as a run - * time configuration, driverlink drivers need to be aware of which - * one to use for their interrupting resources. + * interrupts. The choice is made at run time by the sfc driver, and + * notified to the clients by this enumeration */ enum efx_dl_falcon_resource_flags { EFX_DL_FALCON_DUAL_FUNC = 0x1, @@ -336,20 +143,23 @@ struct efx_dl_falcon_resources { struct efx_dl_falcon_resources { struct efx_dl_device_info hdr; spinlock_t *biu_lock; - unsigned buffer_table_min, buffer_table_lim; - unsigned evq_timer_min, evq_timer_lim; - unsigned evq_int_min, evq_int_lim; - unsigned rxq_min, rxq_lim; - unsigned txq_min, txq_lim; + unsigned buffer_table_min; + unsigned buffer_table_lim; + unsigned evq_timer_min; + unsigned evq_timer_lim; + unsigned evq_int_min; + unsigned evq_int_lim; + unsigned rxq_min; + unsigned rxq_lim; + unsigned txq_min; + unsigned txq_lim; enum efx_dl_falcon_resource_flags flags; }; /** * struct efx_dl_device - An Efx driverlink device. * - * @pci_dev: Underlying PCI device. - * This is the PCI device used by the sfc driver. It will - * already have been enabled for bus-mastering DMA etc. + * @pci_dev: PCI device used by the sfc driver. * @priv: Driver private data * Driverlink clients can use this to store a pointer to their * internal per-device data structure. Each (driver, device) @@ -380,139 +190,57 @@ enum efx_veto { /** * struct efx_dl_callbacks - Efx callbacks * - * These methods can be hooked in to the sfc driver via + * These methods can be hooked into the sfc driver via * efx_dl_register_callbacks(). They allow clients to intercept and/or * modify the behaviour of the sfc driver at predetermined points. * - * For efficiency, only one client can hook each callback. - * - * Since these callbacks are called on packet transmit and reception - * paths, clients should avoid acquiring locks or allocating memory. + * For efficiency, only one client can hook each callback. Since these + * callbacks are called on packet transmit and reception paths, and the + * sfc driver may have multiple tx and rx queues per port, clients should + * avoid acquiring locks or allocating memory. * * @tx_packet: Called when packet is about to be transmitted + * Called for every packet about to be transmitted, providing means + * for the client to snoop traffic, and veto transmission by returning + * %EFX_VETO_PACKET (the sfc driver will subsequently free the skb). + * Context: tasklet, netif_tx_lock held * @rx_packet: Called when packet is received - * @link_change: Called when link status has changed - * @request_mtu: Called to request MTU change - * @mtu_changed: Called when MTU has been changed - * @event: Called when NIC event is not handled by the sfc driver + * Called for every received packet (after LRO), allowing the client + * to snoop every received packet (on every rx queue), and veto + * reception by returning %EFX_VETO_PACKET. + * Context: tasklet + * @link_change: Never used + * @request_mtu: Called to request MTU change. + * Called whenever the user requests the net_dev mtu to be changed. + * If the client returns an error, the mtu change is aborted. The sfc + * driver guarantees that no other callbacks are running. + * Context: process, rtnl_lock held. + * @mtu_changed: Called when MTU has been changed. + * Called after the mtu has been successfully changed, always after + * a previous call to request_mtu(). The sfc driver guarantees that no + * other callbacks are running. + * Context: process, rtnl_lock held. + * @event: Called when a hardware NIC event is not understood by the sfc driver. + * Context: tasklet. */ struct efx_dl_callbacks { - /* - * tx_packet - Packet about to be transmitted. - * @efx_dev: Efx driverlink device - * @skb: Socket buffer containing the packet to be sent - * - * This method is called for every packet about to be - * transmitted. It allows the client to snoop on traffic sent - * via the kernel queues. - * - * The method may return %EFX_VETO_PACKET in order to prevent - * the sfc driver from transmitting the packet. The net - * driver will then discard the packet. If the client wishes - * to retain a reference to the packet data after returning - * %EFX_VETO_PACKET, it must obtain its own copy of the - * packet (e.g. by calling skb_get(), or by copying out the - * packet data to an external buffer). - * - * This method must return quickly, since it will have a - * direct performance impact upon the sfc driver. It will be - * called with interrupts disabled (and may be called in - * interrupt context), so may not sleep. Since the sfc driver - * may have multiple TX queues, running in parallel, please avoid - * the need for locking if it all possible. - */ +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) enum efx_veto fastcall (*tx_packet) (struct efx_dl_device *efx_dev, struct sk_buff *skb); - - /* - * rx_packet - Packet received. - * @efx_dev: Efx driverlink device - * @pkt_hdr: Pointer to received packet - * @pkt_len: Length of received packet - * - * This method is called for every received packet. It allows - * the client to snoop on traffic received by the kernel - * queues. - * - * The method may return %EFX_VETO_PACKET in order to prevent - * the sfc driver from passing the packet to the kernel. The net - * driver will then discard the packet. - * - * This method must return quickly, since it will have a - * direct performance impact upon the sfc driver. It is - * called in tasklet context, so may not sleep. Note that - * there are per-channel tasklets in the sfc driver, so - * rx_packet() may be called simultaneously on different CPUs - * and must lock appropriately. The design of the sfc driver - * allows for lockless operation between receive channels, so - * please avoid the need for locking if at all possible. - */ +#else + enum efx_veto (*tx_packet) (struct efx_dl_device *efx_dev, + struct sk_buff *skb); +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) enum efx_veto fastcall (*rx_packet) (struct efx_dl_device *efx_dev, const char *pkt_hdr, int pkt_len); - - /* - * link_change - Link status change. - * @efx_dev: Efx driverlink device - * @link_up: Link up indicator - * - * This method is called to inform the driverlink client - * whenever the PHY link status changes. By the time this - * function is called, the MAC has already been reconfigured - * with the new autonegotiation settings from the PHY. - * - * This method is called from tasklet context and may not - * sleep. - */ - void (*link_change) (struct efx_dl_device *efx_dev, int link_up); - - /* - * request_mtu: Request MTU change. - * @efx_dev: Efx driverlink device - * @new_mtu: Requested new MTU - * - * This method is called whenever the user requests an MTU - * change on an interface. The client may return an error, in - * which case the MTU change request will be denied. If the - * client returns success, the MAC will be reconfigured with a - * new maxmimum frame length equal to - * EFX_MAX_FRAME_LEN(new_mtu). The client will be notified - * via the mtu_changed() method once the MAC has been - * reconfigured. - * - * The current MTU for the port can be obtained via - * efx_dl_get_netdev(efx_dl_device)->mtu. - * - * The sfc driver guarantees that no other callback functions - * are in progress when this method is called. This function - * is called in process context and may sleep. - * - * Return a negative error code or 0 on success. - */ +#else + enum efx_veto (*rx_packet) (struct efx_dl_device *efx_dev, + const char *pkt_hdr, int pkt_len); +#endif + void (*link_change) (struct efx_dl_device *, int); int (*request_mtu) (struct efx_dl_device *efx_dev, int new_mtu); - - /* - * mtu_changed - MTU has been changed. - * @efx_dev: Efx driverlink device - * @mtu: The new MTU - * - * This method is called once the MAC has been reconfigured - * with a new MTU. There will have been a preceding call to - * request_mtu(). - * - * The sfc driver guarantees that no other callback functions - * are in progress when this method is called. This function - * is called in process context and may sleep. - */ void (*mtu_changed) (struct efx_dl_device *efx_dev, int mtu); - - /* - * event - Event callback. - * @efx_dev: Efx driverlink device - * @p_event: Pointer to event - * - * This method is called for each event that is not handled by the - * sfc driver. - */ void (*event) (struct efx_dl_device *efx_dev, void *p_event); }; @@ -523,16 +251,47 @@ struct efx_dl_callbacks { efx_dl_stringify_2(efx_dl_register_driver_api_ver_, \ EFX_DRIVERLINK_API_VERSION) +/** + * efx_dl_register_driver() - Register a client driver + * @driver: Driver operations structure + * + * This acquires the rtnl_lock and therefore must be called from + * process context. + */ extern int efx_dl_register_driver(struct efx_dl_driver *driver); +/** + * efx_dl_unregister_driver() - Unregister a client driver + * @driver: Driver operations structure + * + * This acquires the rtnl_lock and therefore must be called from + * process context. + */ extern void efx_dl_unregister_driver(struct efx_dl_driver *driver); +/** + * efx_dl_register_callbacks() - Set callback functions for a device + * @efx_dev: Device to be affected + * @callbacks: Callback functions structure + * + * This must be called in process context from the driver's probe() + * operation. + */ extern int efx_dl_register_callbacks(struct efx_dl_device *efx_dev, struct efx_dl_callbacks *callbacks); +/** + * efx_dl_unregister_callbacks() - Unset callback functions for a device + * @efx_dev: Device to be affected + * @callbacks: Callback functions structure + * + * This must be called in process context from the driver's remove() + * operation. + */ extern void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev, struct efx_dl_callbacks *callbacks); +/* Schedule a reset without grabbing any locks */ extern void efx_dl_schedule_reset(struct efx_dl_device *efx_dev); /** @@ -544,19 +303,13 @@ extern void efx_dl_schedule_reset(struct * @_p: Iterator variable * * Example: - * - * static int driver_dl_probe(... const struct efx_dl_device_info *dev_info ...) - * { - * struct efx_dl_falcon_resources *res; - * - * efx_dl_for_each_device_info_matching(dev_info,EFX_DL_FALCON_RESOURCES, - * struct efx_dl_falcon_resources, - * hdr, res) { - * if (res->flags & EFX_DL_FALCON_DUAL_FUNC) { - * ..... - * } - * } - * } + * struct efx_dl_falcon_resources *res; + * efx_dl_for_each_device_info_matching(dev_info, EFX_DL_FALCON_RESOURCES, + * struct efx_dl_falcon_resources, + * hdr, res) { + * if (res->flags & EFX_DL_FALCON_DUAL_FUNC) + * .... + * } */ #define efx_dl_for_each_device_info_matching(_dev_info, _type, \ _info_type, _field, _p) \ @@ -576,17 +329,11 @@ extern void efx_dl_schedule_reset(struct * @_p: Result variable * * Example: - * - * static int driver_dl_probe(... const struct efx_dl_device_info *dev_info ...) - * { - * struct efx_dl_falcon_resources *res; - * - * efx_dl_search_device_info(dev_info, EFX_DL_FALCON_RESOURCES, - * struct efx_dl_falcon_resources, hdr, res); - * if (res != NULL) { - * .... - * } - * } + * struct efx_dl_falcon_resources *res; + * efx_dl_search_device_info(dev_info, EFX_DL_FALCON_RESOURCES, + * struct efx_dl_falcon_resources, hdr, res); + * if (res) + * .... */ #define efx_dl_search_device_info(_dev_info, _type, _info_type, \ _field, _p) \ diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/efx.c --- a/drivers/net/sfc/efx.c Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/efx.c Fri Jan 08 13:05:49 2010 +0000 @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2005-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> - * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * 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 * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include <linux/module.h> @@ -36,18 +19,16 @@ #include <linux/in.h> #include <linux/crc32.h> #include <linux/ethtool.h> +#include <linux/topology.h> #include "net_driver.h" -#include "gmii.h" #include "driverlink.h" -#include "selftest.h" #include "debugfs.h" -#include "ethtool.h" -#include "tx.h" -#include "rx.h" #include "efx.h" #include "mdio_10g.h" -#include "falcon.h" -#include "workarounds.h" +#include "nic.h" +#include "efx_ioctl.h" + +#include "mcdi.h" /************************************************************************** * @@ -59,16 +40,33 @@ /* Loopback mode names (see LOOPBACK_MODE()) */ const unsigned int efx_loopback_mode_max = LOOPBACK_MAX; const char *efx_loopback_mode_names[] = { - [LOOPBACK_NONE] = "NONE", - [LOOPBACK_MAC] = "MAC", - [LOOPBACK_XGMII] = "XGMII", - [LOOPBACK_XGXS] = "XGXS", - [LOOPBACK_XAUI] = "XAUI", - [LOOPBACK_PHY] = "PHY", - [LOOPBACK_PHYXS] = "PHY(XS)", - [LOOPBACK_PCS] = "PHY(PCS)", - [LOOPBACK_PMAPMD] = "PHY(PMAPMD)", - [LOOPBACK_NETWORK] = "NETWORK", + [LOOPBACK_NONE] = "NONE", + [LOOPBACK_DATA] = "DATAPATH", + [LOOPBACK_GMAC] = "GMAC", + [LOOPBACK_XGMII] = "XGMII", + [LOOPBACK_XGXS] = "XGXS", + [LOOPBACK_XAUI] = "XAUI", + [LOOPBACK_GMII] = "GMII", + [LOOPBACK_SGMII] = "SGMII", + [LOOPBACK_XGBR] = "XGBR", + [LOOPBACK_XFI] = "XFI", + [LOOPBACK_XAUI_FAR] = "XAUI_FAR", + [LOOPBACK_GMII_FAR] = "GMII_FAR", + [LOOPBACK_SGMII_FAR] = "SGMII_FAR", + [LOOPBACK_XFI_FAR] = "XFI_FAR", + [LOOPBACK_GPHY] = "GPHY", + [LOOPBACK_PHYXS] = "PHYXS", + [LOOPBACK_PCS] = "PCS", + [LOOPBACK_PMAPMD] = "PMA/PMD", + [LOOPBACK_XPORT] = "XPORT", + [LOOPBACK_XGMII_WS] = "XGMII_WS", + [LOOPBACK_XAUI_WS] = "XAUI_WS", + [LOOPBACK_XAUI_WS_FAR] = "XAUI_WS_FAR", + [LOOPBACK_XAUI_WS_NEAR] = "XAUI_WS_NEAR", + [LOOPBACK_GMII_WS] = "GMII_WS", + [LOOPBACK_XFI_WS] = "XFI_WS", + [LOOPBACK_XFI_WS_FAR] = "XFI_WS_FAR", + [LOOPBACK_PHYXS_WS] = "PHYXS_WS", }; /* Interrupt mode names (see INT_MODE())) */ @@ -79,38 +77,19 @@ const char *efx_interrupt_mode_names[] = [EFX_INT_MODE_LEGACY] = "legacy", }; -/* PHY type names (see PHY_TYPE())) */ -const unsigned int efx_phy_type_max = PHY_TYPE_MAX; -const char *efx_phy_type_names[] = { - [PHY_TYPE_NONE] = "none", - [PHY_TYPE_CX4_RTMR] = "Mysticom CX4", - [PHY_TYPE_1G_ALASKA] = "1G Alaska", - [PHY_TYPE_10XPRESS] = "SFC 10Xpress", - [PHY_TYPE_XFP] = "Quake XFP", - [PHY_TYPE_PM8358] = "PM8358 XAUI", -}; - const unsigned int efx_reset_type_max = RESET_TYPE_MAX; const char *efx_reset_type_names[] = { [RESET_TYPE_INVISIBLE] = "INVISIBLE", [RESET_TYPE_ALL] = "ALL", [RESET_TYPE_WORLD] = "WORLD", [RESET_TYPE_DISABLE] = "DISABLE", - [RESET_TYPE_MONITOR] = "MONITOR", + [RESET_TYPE_TX_WATCHDOG] = "TX_WATCHDOG", [RESET_TYPE_INT_ERROR] = "INT_ERROR", [RESET_TYPE_RX_RECOVERY] = "RX_RECOVERY", [RESET_TYPE_RX_DESC_FETCH] = "RX_DESC_FETCH", [RESET_TYPE_TX_DESC_FETCH] = "TX_DESC_FETCH", [RESET_TYPE_TX_SKIP] = "TX_SKIP", -}; - -const unsigned int efx_nic_state_max = STATE_MAX; -const char *efx_nic_state_names[] = { - [STATE_INIT] = "INIT", - [STATE_RUNNING] = "RUNNING", - [STATE_FINI] = "FINI", - [STATE_RESETTING] = "RESETTING", - [STATE_DISABLED] = "DISABLED", + [RESET_TYPE_MC_FAILURE] = "MC_FAILURE", }; #define EFX_MAX_MTU (9 * 1024) @@ -122,32 +101,42 @@ const char *efx_nic_state_names[] = { */ static struct workqueue_struct *refill_workqueue; +/* Reset workqueue. If any NIC has a hardware failure then a reset will be + * queued onto this work queue. This is not a per-nic work queue, because + * efx_reset_work() acquires the rtnl lock, so resets are naturally serialised. + */ +static struct workqueue_struct *reset_workqueue; + /************************************************************************** * * Configurable values * *************************************************************************/ +#if defined(EFX_USE_KCOMPAT) && (defined(EFX_USE_GRO) || defined(EFX_USE_SFC_LRO)) /* * Enable large receive offload (LRO) aka soft segment reassembly (SSR) * * This sets the default for new devices. It can be controlled later * using ethtool. */ -static int lro = 1; +static int lro = true; module_param(lro, int, 0644); MODULE_PARM_DESC(lro, "Large receive offload acceleration"); +#endif /* * Use separate channels for TX and RX events * - * Set this to 1 to use separate channels for TX and RX. It allows us to - * apply a higher level of interrupt moderation to TX events. - * - * This is forced to 0 for MSI interrupt mode as the interrupt vector - * is not written - */ -static unsigned int separate_tx_and_rx_channels = 1; + * Set this to 1 to use separate channels for TX and RX. It allows us + * to control interrupt affinity separately for TX and RX. + * + * This is only used in MSI-X interrupt mode + */ +static unsigned int separate_tx_channels = false; +module_param(separate_tx_channels, uint, 0644); +MODULE_PARM_DESC(separate_tx_channels, + "Use separate channels for TX and RX"); /* This is the weight assigned to each of the (per-channel) virtual * NAPI devices. @@ -159,11 +148,6 @@ static int napi_weight = 64; * hardware and driver as necessary. */ unsigned int efx_monitor_interval = 1 * HZ; - -/* This controls whether or not the hardware monitor will trigger a - * reset when it detects an error condition. - */ -static unsigned int monitor_reset = 1; /* This controls whether or not the driver will initialise devices * with invalid MAC addresses stored in the EEPROM or flash. If true, @@ -193,13 +177,6 @@ static unsigned int rx_irq_mod_usec = 60 */ static unsigned int tx_irq_mod_usec = 150; -/* Ignore online self-test failures at load - * - * If set to 1, then the driver will not fail to load - * if the online self-test fails. Useful only during testing - */ -static unsigned int allow_load_on_failure; - /* This is the first interrupt mode to try out of: * 0 => MSI-X * 1 => MSI @@ -207,22 +184,50 @@ static unsigned int allow_load_on_failur */ static unsigned int interrupt_mode; -/* If set to 1, then the driver will perform an offline self test - * when each interface first comes up. This will appear like the - * interface bounces up and down - */ -static unsigned int onload_offline_selftest = 1; - -/* This is the requested number of CPUs to use for Receive-Side Scaling (RSS), - * i.e. the number of CPUs among which we may distribute simultaneous - * interrupt handling. - * - * Cards without MSI-X will only target one CPU via legacy or MSI interrupt. - * The default (0) means to assign an interrupt to each package (level II cache) - */ -static unsigned int rss_cpus; -module_param(rss_cpus, uint, 0444); -MODULE_PARM_DESC(rss_cpus, "Number of CPUs to use for Receive-Side Scaling"); +/* This is the requested number of CPUs to use for Receive-Side Scaling + * (RSS), i.e. the number of CPUs among which we may distribute + * simultaneous interrupt handling. Or alternatively it may be set to + * "packages", "cores" or "hyperthreads" to get one receive channel per + * package, core or hyperthread. + * + * Systems without MSI-X will only target one CPU via legacy or MSI + * interrupt. The default is "packages". + */ +static char *rss_cpus; +module_param(rss_cpus, charp, 0444); +MODULE_PARM_DESC(rss_cpus, "Number of CPUs to use for Receive-Side Scaling, " + "or 'packages', 'cores' or 'hyperthreads'"); + +enum rss_mode { + EFX_RSS_PACKAGES, + EFX_RSS_CORES, + EFX_RSS_HYPERTHREADS, + EFX_RSS_CUSTOM, +}; + +static int phy_flash_cfg; +module_param(phy_flash_cfg, int, 0644); +MODULE_PARM_DESC(phy_flash_cfg, "Set PHYs into reflash mode initially"); + +static unsigned irq_adapt_enable = 1; +module_param(irq_adapt_enable, uint, 0444); +MODULE_PARM_DESC(irq_adapt_enable, + "Enable adaptive interrupt moderation"); + +static unsigned irq_adapt_low_thresh = 10000; +module_param(irq_adapt_low_thresh, uint, 0644); +MODULE_PARM_DESC(irq_adapt_low_thresh, + "Threshold score for reducing IRQ moderation"); + +static unsigned irq_adapt_high_thresh = 20000; +module_param(irq_adapt_high_thresh, uint, 0644); +MODULE_PARM_DESC(irq_adapt_high_thresh, + "Threshold score for increasing IRQ moderation"); + +static unsigned irq_adapt_irqs = 1000; +module_param(irq_adapt_irqs, uint, 0644); +MODULE_PARM_DESC(irq_adapt_irqs, + "Number of IRQs per IRQ moderation adaptation"); /************************************************************************** * @@ -237,7 +242,7 @@ static void efx_fini_channels(struct efx #define EFX_ASSERT_RESET_SERIALISED(efx) \ do { \ if ((efx->state == STATE_RUNNING) || \ - (efx->state == STATE_RESETTING)) \ + (efx->state == STATE_DISABLED)) \ ASSERT_RTNL(); \ } while (0) @@ -254,16 +259,18 @@ static void efx_fini_channels(struct efx * never be concurrently called more than once on the same channel, * though different channels may be being processed concurrently. */ -static inline int efx_process_channel(struct efx_channel *channel, int rx_quota) -{ - int rxdmaqs; - struct efx_rx_queue *rx_queue; - - if (unlikely(channel->efx->reset_pending != RESET_TYPE_NONE || +static int efx_process_channel(struct efx_channel *channel, int rx_quota) +{ + struct efx_nic *efx = channel->efx; + int rx_packets; + + if (unlikely(efx->reset_pending != RESET_TYPE_NONE || !channel->enabled)) - return rx_quota; - - rxdmaqs = falcon_process_eventq(channel, &rx_quota); + return 0; + + rx_packets = efx_nic_process_eventq(channel, rx_quota); + if (rx_packets == 0) + return 0; /* Deliver last RX packet. */ if (channel->rx_pkt) { @@ -272,19 +279,11 @@ static inline int efx_process_channel(st channel->rx_pkt = NULL; } - efx_flush_lro(channel); efx_rx_strategy(channel); - /* Refill descriptor rings as necessary */ - rx_queue = &channel->efx->rx_queue[0]; - while (rxdmaqs) { - if (rxdmaqs & 0x01) - efx_fast_push_rx_descriptors(rx_queue); - rx_queue++; - rxdmaqs >>= 1; - } - - return rx_quota; + efx_fast_push_rx_descriptors(&efx->rx_queue[channel->channel]); + + return rx_packets; } /* Mark channel as finished processing @@ -295,12 +294,13 @@ static inline int efx_process_channel(st */ static inline void efx_channel_processed(struct efx_channel *channel) { - /* Write to EVQ_RPTR_REG. If a new event arrived in a race - * with finishing processing, a new interrupt will be raised. - */ - channel->work_pending = 0; - smp_wmb(); /* Ensure channel updated before any new interrupt. */ - falcon_eventq_read_ack(channel); + /* The interrupt handler for this channel may set work_pending + * as soon as we acknowledge the events we've seen. Make sure + * it's cleared before then. */ + channel->work_pending = false; + smp_wmb(); + + efx_nic_eventq_read_ack(channel); } /* NAPI poll handler @@ -308,33 +308,66 @@ static inline void efx_channel_processed * NAPI guarantees serialisation of polls of the same device, which * provides the guarantee required by efx_process_channel(). */ +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_OLD_NAPI) +static int efx_poll(struct napi_struct *napi, int budget) +{ + struct efx_channel *channel = + container_of(napi, struct efx_channel, napi_str); +#else static int efx_poll(struct net_device *napi, int *budget_ret) { - struct net_device *napi_dev = napi; - struct efx_channel *channel = napi_dev->priv; - int budget = min(napi_dev->quota, *budget_ret); - int unused; + struct efx_channel *channel = napi->priv; + int budget = min(napi->quota, *budget_ret); +#endif int rx_packets; EFX_TRACE(channel->efx, "channel %d NAPI poll executing on CPU %d\n", channel->channel, raw_smp_processor_id()); - unused = efx_process_channel(channel, budget); - rx_packets = (budget - unused); - napi_dev->quota -= rx_packets; + rx_packets = efx_process_channel(channel, budget); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_HAVE_OLD_NAPI) + napi->quota -= rx_packets; *budget_ret -= rx_packets; +#endif if (rx_packets < budget) { + struct efx_nic *efx = channel->efx; + + if (channel->used_flags & EFX_USED_BY_RX && + efx->irq_rx_adaptive && + unlikely(++channel->irq_count == irq_adapt_irqs)) { + if (unlikely(channel->irq_mod_score < + irq_adapt_low_thresh)) { + if (channel->irq_moderation > 1) { + channel->irq_moderation -= 1; + efx->type->push_irq_moderation(channel); + } + } else if (unlikely(channel->irq_mod_score > + irq_adapt_high_thresh)) { + if (channel->irq_moderation < + efx->irq_rx_moderation) { + channel->irq_moderation += 1; + efx->type->push_irq_moderation(channel); + } + } + channel->irq_count = 0; + channel->irq_mod_score = 0; + } + /* There is no race here; although napi_disable() will - * only wait for netif_rx_complete(), this isn't a problem + * only wait for napi_complete(), this isn't a problem * since efx_channel_processed() will have no effect if * interrupts have already been disabled. */ - netif_rx_complete(napi_dev, napi); + napi_complete(napi); efx_channel_processed(channel); } +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_OLD_NAPI) + return rx_packets; +#else return (rx_packets >= budget); +#endif } /* Process the eventq of the specified channel immediately on this CPU @@ -353,27 +386,24 @@ void efx_process_channel_now(struct efx_ 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->has_interrupt && channel->irq) + if (channel->irq) synchronize_irq(channel->irq); /* Wait for any NAPI processing to complete */ napi_disable(&channel->napi_str); /* Poll the channel */ - (void) efx_process_channel(channel, efx->type->evq_size); + efx_process_channel(channel, EFX_EVQ_SIZE); /* Ack the eventq. This may cause an interrupt to be generated * when they are reenabled */ efx_channel_processed(channel); - /* Reenable NAPI polling */ napi_enable(&channel->napi_str); - - /* Reenable interrupts */ - falcon_enable_interrupts(efx); + efx_nic_enable_interrupts(efx); } /* Create event queue @@ -385,32 +415,31 @@ static int efx_probe_eventq(struct efx_c { 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 */ -static int efx_init_eventq(struct efx_channel *channel) +static void efx_init_eventq(struct efx_channel *channel) { EFX_LOG(channel->efx, "chan %d init event queue\n", channel->channel); - /* Initialise fields */ channel->eventq_read_ptr = 0; - return 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); } /************************************************************************** @@ -418,26 +447,6 @@ static void efx_remove_eventq(struct efx * Channel handling * *************************************************************************/ - -/* Setup per-NIC RX buffer parameters. - * Calculate the rx buffer allocation parameters required to support - * the current MTU, including padding for header alignment and overruns. - */ -static void efx_calc_rx_buffer_params(struct efx_nic *efx) -{ - unsigned int order, len; - - len = (max(EFX_PAGE_IP_ALIGN, NET_IP_ALIGN) + - EFX_MAX_FRAME_LEN(efx->net_dev->mtu) + - efx->type->rx_buffer_padding); - - /* Calculate page-order */ - for (order = 0; ((1u << order) * PAGE_SIZE) < len; ++order) - ; - - efx->rx_buffer_len = len; - efx->rx_buffer_order = order; -} static int efx_probe_channel(struct efx_channel *channel) { @@ -478,54 +487,64 @@ static int efx_probe_channel(struct efx_ } +static void efx_set_channel_names(struct efx_nic *efx) +{ + struct efx_channel *channel; + const char *type = ""; + int number; + + efx_for_each_channel(channel, efx) { + number = channel->channel; + if (efx->n_channels > efx->n_rx_queues) { + if (channel->channel < efx->n_rx_queues) { + type = "-rx"; + } else { + type = "-tx"; + number -= efx->n_rx_queues; + } + } + snprintf(channel->name, sizeof(channel->name), + "%s%s-%d", efx->name, type, number); + } +} + /* Channels are shutdown and reinitialised whilst the NIC is running * to propagate configuration changes (mtu, checksum offload), or * to clear hardware error conditions */ -static int efx_init_channels(struct efx_nic *efx) +static void efx_init_channels(struct efx_nic *efx) { struct efx_tx_queue *tx_queue; struct efx_rx_queue *rx_queue; struct efx_channel *channel; - int rc = 0; - - /* Recalculate the rx buffer parameters */ - efx_calc_rx_buffer_params(efx); + + /* Calculate the rx buffer allocation parameters required to + * support the current MTU, including padding for header + * alignment and overruns. + */ + efx->rx_buffer_len = (max(EFX_PAGE_IP_ALIGN, NET_IP_ALIGN) + + EFX_MAX_FRAME_LEN(efx->net_dev->mtu) + + efx->type->rx_buffer_padding); + efx->rx_buffer_order = get_order(efx->rx_buffer_len); /* Initialise the channels */ efx_for_each_channel(channel, efx) { EFX_LOG(channel->efx, "init chan %d\n", channel->channel); - rc = efx_init_eventq(channel); - if (rc) - goto err; - - efx_for_each_channel_tx_queue(tx_queue, channel) { - rc = efx_init_tx_queue(tx_queue); - if (rc) - goto err; - } + efx_init_eventq(channel); + + efx_for_each_channel_tx_queue(tx_queue, channel) + efx_init_tx_queue(tx_queue); /* The rx buffer allocation strategy is MTU dependent */ efx_rx_strategy(channel); - efx_for_each_channel_rx_queue(rx_queue, channel) { - rc = efx_init_rx_queue(rx_queue); - if (rc) - goto err; - } + efx_for_each_channel_rx_queue(rx_queue, channel) + efx_init_rx_queue(rx_queue); WARN_ON(channel->rx_pkt != NULL); efx_rx_strategy(channel); } - - return 0; - - err: - EFX_ERR(efx, "failed to initialise channel %d\n", - channel ? channel->channel : -1); - efx_fini_channels(efx); - return rc; } /* This enables event queue processing and packet transmission. @@ -539,16 +558,13 @@ static void efx_start_channel(struct efx EFX_LOG(channel->efx, "starting chan %d\n", channel->channel); - if (!(channel->efx->net_dev->flags & IFF_UP)) - netif_napi_add(channel->napi_dev, &channel->napi_str, - efx_poll, napi_weight); - - /* Mark channel as enabled */ - channel->work_pending = 0; - channel->enabled = 1; - smp_wmb(); /* ensure channel updated before first interrupt */ - - /* Enable NAPI poll handler */ + /* The interrupt handler for this channel may set work_pending + * as soon as we enable it. Make sure it's cleared before + * then. Similarly, make sure it sees the enabled flag set. */ + channel->work_pending = false; + channel->enabled = true; + smp_wmb(); + napi_enable(&channel->napi_str); /* Load up RX descriptors */ @@ -569,10 +585,7 @@ static void efx_stop_channel(struct efx_ EFX_LOG(channel->efx, "stop chan %d\n", channel->channel); - /* Mark channel as disabled */ - channel->enabled = 0; - - /* Wait for any NAPI processing to complete */ + channel->enabled = false; napi_disable(&channel->napi_str); /* Ensure that any worker threads have exited or will be no-ops */ @@ -587,9 +600,16 @@ static void efx_fini_channels(struct efx struct efx_channel *channel; struct efx_tx_queue *tx_queue; struct efx_rx_queue *rx_queue; + int rc; EFX_ASSERT_RESET_SERIALISED(efx); BUG_ON(efx->port_enabled); + + rc = efx_nic_flush_queues(efx); + if (rc) + EFX_ERR(efx, "failed to flush queues\n"); + else + EFX_LOG(efx, "successfully flushed all queues\n"); efx_for_each_channel(channel, efx) { EFX_LOG(channel->efx, "shut down chan %d\n", channel->channel); @@ -598,13 +618,6 @@ static void efx_fini_channels(struct efx efx_fini_rx_queue(rx_queue); efx_for_each_channel_tx_queue(tx_queue, channel) efx_fini_tx_queue(tx_queue); - } - - /* Do the event queues last so that we can handle flush events - * for all DMA queues. */ - efx_for_each_channel(channel, efx) { - EFX_LOG(channel->efx, "shut down evq %d\n", channel->channel); - efx_fini_eventq(channel); } } @@ -640,9 +653,9 @@ void efx_schedule_slow_fill(struct efx_r * 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) -{ - int carrier_ok; +void efx_link_status_changed(struct efx_nic *efx) +{ + struct efx_link_state *link_state = &efx->link_state; /* SFC Bug 5356: A net_dev notifier is registered, so we must ensure * that no events are triggered between unregister_netdev() and the @@ -651,119 +664,169 @@ static void efx_link_status_changed(stru if (!netif_running(efx->net_dev)) return; - carrier_ok = netif_carrier_ok(efx->net_dev) ? 1 : 0; - if (efx->link_up != carrier_ok) { + if (efx->port_inhibited) { + netif_carrier_off(efx->net_dev); + return; + } + + if (link_state->up != netif_carrier_ok(efx->net_dev)) { efx->n_link_state_changes++; - if (efx->link_up) + if (link_state->up) netif_carrier_on(efx->net_dev); else netif_carrier_off(efx->net_dev); } - /* Inform driverlink client */ - EFX_DL_CALLBACK(efx, link_change, efx->link_up); - /* Status message for kernel log */ - if (efx->link_up) { - struct mii_if_info *gmii = &efx->mii; - unsigned adv, lpa; - /* NONE here means direct XAUI from the controller, with no - * MDIO-attached device we can query. */ - if (efx->phy_type != PHY_TYPE_NONE) { - adv = gmii_advertised(gmii); - lpa = gmii_lpa(gmii); - } else { - lpa = GM_LPA_10000 | LPA_DUPLEX; - adv = lpa; - } - EFX_INFO(efx, "link up at %dMbps %s-duplex " - "(adv %04x lpa %04x) (MTU %d)%s%s%s%s\n", - (efx->link_options & GM_LPA_10000 ? 10000 : - (efx->link_options & GM_LPA_1000 ? 1000 : - (efx->link_options & GM_LPA_100 ? 100 : - 10))), - (efx->link_options & GM_LPA_DUPLEX ? - "full" : "half"), - adv, lpa, + if (link_state->up) { + EFX_INFO(efx, "link up at %uMbps %s-duplex " + "(MTU %d)%s%s%s%s\n", + link_state->speed, link_state->fd ? "full" : "half", efx->net_dev->mtu, (efx->loopback_mode ? " [" : ""), (efx->loopback_mode ? LOOPBACK_MODE(efx) : ""), (efx->loopback_mode ? " LOOPBACK]" : ""), (efx->promiscuous ? " [PROMISC]" : "")); + + if ((efx->wanted_fc & EFX_FC_AUTO) && + (efx->wanted_fc & EFX_FC_TX) && + (~efx->link_state.fc & EFX_FC_TX)) + /* There is no way to report this state through ethtool, so + * print this information to the kernel log */ + EFX_ERR(efx, "Flow control autonegotiated " + "tx OFF (wanted ON)\n"); } else { EFX_INFO(efx, "link down%s\n", - efx->phy_powered ? "" : " [OFF]"); - } - -} - -/* This call reinitialises the MAC to pick up new PHY settings. The - * caller must hold the mac_lock */ -static void __efx_reconfigure_port(struct efx_nic *efx) -{ + (efx->phy_mode & PHY_MODE_LOW_POWER) ? " [OFF]" : ""); + } + +} + +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); + +/* 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) +{ + enum efx_phy_mode phy_mode; + int rc; + WARN_ON(!mutex_is_locked(&efx->mac_lock)); - EFX_LOG(efx, "reconfiguring MAC from PHY settings on CPU %d\n", - raw_smp_processor_id()); - - efx->mac_op->reconfigure(efx); - - /* Inform kernel of loss/gain of carrier */ - efx_link_status_changed(efx); + /* Serialise the promiscuous flag with efx_set_multicast_list. */ + if (efx_dev_registered(efx)) { + netif_addr_lock_bh(efx->net_dev); + netif_addr_unlock_bh(efx->net_dev); + } + + /* 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; + + rc = efx->type->reconfigure_port(efx); + + if (rc) + efx->phy_mode = phy_mode; + + 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_reconfigure_work(struct work_struct *data) -{ - struct efx_nic *efx = container_of(data, struct efx_nic, - reconfigure_work); + + 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_reconfigure_port(efx); + if (efx->port_enabled) { + efx->type->push_multicast_hash(efx); + efx->mac_op->reconfigure(efx); + } mutex_unlock(&efx->mac_lock); } static int efx_probe_port(struct efx_nic *efx) { - unsigned char *dev_addr; int rc; EFX_LOG(efx, "create port\n"); - /* Connect up MAC/PHY operations table and read MAC address */ - rc = falcon_probe_port(efx); + if (phy_flash_cfg) + efx->phy_mode = PHY_MODE_SPECIAL; + + /* Connect up MAC/PHY operations table */ + rc = efx->type->probe_port(efx); if (rc) goto err; /* Sanity check MAC address */ - dev_addr = efx->mac_address; - if (!is_valid_ether_addr(dev_addr)) { + if (is_valid_ether_addr(efx->mac_address)) { + memcpy(efx->net_dev->dev_addr, efx->mac_address, ETH_ALEN); + } else { DECLARE_MAC_BUF(mac); - EFX_ERR(efx, "invalid MAC address %s\n", - print_mac(mac, dev_addr)); if (!allow_bad_hwaddr) { + EFX_ERR(efx, "invalid MAC address %s\n", + print_mac(mac, efx->mac_address)); rc = -EINVAL; goto err; } - random_ether_addr(dev_addr); + random_ether_addr(efx->net_dev->dev_addr); EFX_INFO(efx, "using locally-generated MAC %s\n", - print_mac(mac, dev_addr)); + print_mac(mac, efx->net_dev->dev_addr)); } /* Register debugfs entries */ @@ -784,52 +847,61 @@ static int efx_init_port(struct efx_nic EFX_LOG(efx, "init port\n"); - /* The default power state is ON */ - efx->phy_powered = 1; - - /* Initialise the MAC and PHY */ - rc = efx->mac_op->init(efx); + mutex_lock(&efx->mac_lock); + + rc = efx->phy_op->init(efx); if (rc) - return rc; - - efx->port_initialized = 1; - - /* Reconfigure port to program MAC registers */ + goto fail1; + + efx->port_initialized = true; + + /* Reconfigure the MAC before creating dma queues (required for + * Falcon/A1 where RX_INGR_EN/TX_DRAIN_EN isn't supported) */ efx->mac_op->reconfigure(efx); + /* Ensure the PHY advertises the correct flow control settings */ + rc = efx->phy_op->reconfigure(efx); + if (rc) + goto fail2; + + mutex_unlock(&efx->mac_lock); return 0; -} - -/* Allow efx_reconfigure_port() to be scheduled, and close the window - * between efx_stop_port and efx_flush_all whereby a previously scheduled - * efx_reconfigure_port() may have been cancelled */ + +fail2: + efx->phy_op->fini(efx); +fail1: + mutex_unlock(&efx->mac_lock); + return rc; +} + static void efx_start_port(struct efx_nic *efx) { EFX_LOG(efx, "start port\n"); BUG_ON(efx->port_enabled); mutex_lock(&efx->mac_lock); - efx->port_enabled = 1; - __efx_reconfigure_port(efx); + efx->port_enabled = true; + + /* 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_reconfigure_work and efx_monitor() from executing, and - * efx_set_multicast_list() from scheduling efx_reconfigure_work. - * efx_reconfigure_work can 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"); mutex_lock(&efx->mac_lock); - efx->port_enabled = 0; + efx->port_enabled = false; mutex_unlock(&efx->mac_lock); - /* Serialise against efx_set_multicast_list() */ - if (NET_DEV_REGISTERED(efx)) { - netif_tx_lock_bh(efx->net_dev); - netif_tx_unlock_bh(efx->net_dev); + if (efx_dev_registered(efx)) { + netif_addr_lock_bh(efx->net_dev); + netif_addr_unlock_bh(efx->net_dev); } } @@ -840,11 +912,10 @@ static void efx_fini_port(struct efx_nic if (!efx->port_initialized) return; - efx->mac_op->fini(efx); - efx->port_initialized = 0; - - /* Mark the link down */ - efx->link_up = 0; + efx->phy_op->fini(efx); + efx->port_initialized = false; + + efx->link_state.up = false; efx_link_status_changed(efx); } @@ -853,7 +924,7 @@ static void efx_remove_port(struct efx_n EFX_LOG(efx, "destroying port\n"); efx_fini_debugfs_port(efx); - falcon_remove_port(efx); + efx->type->remove_port(efx); } /************************************************************************** @@ -871,7 +942,6 @@ static int efx_init_io(struct efx_nic *e EFX_LOG(efx, "initialising I/O\n"); - /* Generic device-enabling code */ rc = pci_enable_device(pci_dev); if (rc) { EFX_ERR(efx, "failed to enable PCI device\n"); @@ -906,10 +976,14 @@ static int efx_init_io(struct efx_nic *e goto fail2; } - /* Get memory base address */ - efx->membase_phys = pci_resource_start(efx->pci_dev, - efx->type->mem_bar); - rc = pci_request_region(pci_dev, efx->type->mem_bar, "sfc"); + efx->membase_phys = pci_resource_start(efx->pci_dev, EFX_MEM_BAR); +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_MSIX_TABLE_RESERVED) + rc = pci_request_region(pci_dev, EFX_MEM_BAR, "sfc"); +#else + if (!request_mem_region(efx->membase_phys, efx->type->mem_map_size, + "sfc")) + rc = -EIO; +#endif if (rc) { EFX_ERR(efx, "request for memory BAR failed\n"); rc = -EIO; @@ -918,23 +992,26 @@ static int efx_init_io(struct efx_nic *e efx->membase = ioremap_nocache(efx->membase_phys, efx->type->mem_map_size); if (!efx->membase) { - EFX_ERR(efx, "could not map memory BAR %d at %lx+%x\n", - efx->type->mem_bar, efx->membase_phys, + EFX_ERR(efx, "could not map memory BAR at %llx+%x\n", + (unsigned long long)efx->membase_phys, efx->type->mem_map_size); rc = -ENOMEM; goto fail4; } - EFX_LOG(efx, "memory BAR %u at %lx+%x (virtual %p)\n", - efx->type->mem_bar, efx->membase_phys, efx->type->mem_map_size, - efx->membase); + EFX_LOG(efx, "memory BAR at %llx+%x (virtual %p)\n", + (unsigned long long)efx->membase_phys, + efx->type->mem_map_size, efx->membase); return 0; fail4: +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_MSIX_TABLE_RESERVED) + pci_release_region(efx->pci_dev, EFX_MEM_BAR); +#else release_mem_region(efx->membase_phys, efx->type->mem_map_size); +#endif fail3: - efx->membase_phys = 0UL; - /* fall-thru */ + efx->membase_phys = 0; fail2: pci_disable_device(efx->pci_dev); fail1: @@ -951,65 +1028,176 @@ static void efx_fini_io(struct efx_nic * } if (efx->membase_phys) { - pci_release_region(efx->pci_dev, efx->type->mem_bar); - efx->membase_phys = 0UL; +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_MSIX_TABLE_RESERVED) + pci_release_region(efx->pci_dev, EFX_MEM_BAR); +#else + release_mem_region(efx->membase_phys, efx->type->mem_map_size); +#endif + efx->membase_phys = 0; } pci_disable_device(efx->pci_dev); } -/* Probe the number and type of interrupts we are able to obtain. */ +#if !defined(EFX_USE_KCOMPAT) || (defined(topology_core_siblings) && !defined(__VMKLNX__) && !defined(CONFIG_XEN)) +#define HAVE_EFX_NUM_PACKAGES +static int efx_num_packages(void) +{ + cpumask_t core_mask; + int count; + int cpu; + + cpus_clear(core_mask); + count = 0; + for_each_online_cpu(cpu) { + if (!cpu_isset(cpu, core_mask)) { + ++count; + cpus_or(core_mask, core_mask, + topology_core_siblings(cpu)); + } + } + + /* Create two RSS queues even on a single package host */ + if (count == 1) + count = 2; + + return count; +} +#endif + +#if !defined(EFX_USE_KCOMPAT) || (defined(topology_thread_siblings) && !defined(__VMKLNX__) && !defined(CONFIG_XEN)) +#define HAVE_EFX_NUM_CORES +static int efx_num_cores(void) +{ + cpumask_t core_mask; + int count; + int cpu; + + cpus_clear(core_mask); + count = 0; + for_each_online_cpu(cpu) { + if (!cpu_isset(cpu, core_mask)) { + ++count; + cpus_or(core_mask, core_mask, + topology_thread_siblings(cpu)); + } + } + + return count; +} +#endif + +/* Get number of RX queues wanted. */ +static int efx_wanted_rx_queues(struct efx_nic *efx) +{ + enum rss_mode rss_mode = EFX_RSS_PACKAGES; + bool selected = false; + int n_rxq = -1; + + if (rss_cpus == NULL) { + /* Leave at default. */ + } else if (strcmp(rss_cpus, "packages") == 0) { + rss_mode = EFX_RSS_PACKAGES; + selected = true; + } else if (strcmp(rss_cpus, "cores") == 0) { + rss_mode = EFX_RSS_CORES; + selected = true; + } else if (strcmp(rss_cpus, "hyperthreads") == 0) { + rss_mode = EFX_RSS_HYPERTHREADS; + selected = true; + } else if (sscanf(rss_cpus, "%d", &n_rxq) == 1 && n_rxq > 0) { + rss_mode = EFX_RSS_CUSTOM; + selected = true; + } else { + EFX_ERR(efx, "ERROR: Bad value for module parameter " + "rss_cpus='%s'\n", rss_cpus); + } + + switch (rss_mode) { +#if defined(HAVE_EFX_NUM_PACKAGES) + case EFX_RSS_PACKAGES: + n_rxq = efx_num_packages(); + break; +#elif defined(CONFIG_XEN) + case EFX_RSS_PACKAGES: + EFX_ERR(efx, "WARNING: Unable to determine CPU topology on Xen" + "reliably. Using 4 rss channels.\n" ); + n_rxq = 4; + break; +#endif +#if defined(HAVE_EFX_NUM_CORES) + case EFX_RSS_CORES: + n_rxq = efx_num_cores(); + break; +#elif defined(CONFIG_XEN) + case EFX_RSS_CORES: + EFX_ERR(efx, "WARNING: Unable to determine CPU topology on Xen" + "reliably. Assuming hyperthreading enabled.\n"); + n_rxq = max(1, num_online_cpus() / 2); + break; +#endif + case EFX_RSS_HYPERTHREADS: + n_rxq = num_online_cpus(); + break; + case EFX_RSS_CUSTOM: + break; + default: + if (selected) + EFX_ERR(efx, "ERROR: Selected rss mode '%s' not " + "available\n", rss_cpus); + rss_mode = EFX_RSS_HYPERTHREADS; + n_rxq = num_online_cpus(); + break; + } + + if (n_rxq > EFX_MAX_RX_QUEUES) { + EFX_ERR(efx, "WARNING: Reducing number of rss channels from " + "%d to %d.\n", n_rxq, EFX_MAX_RX_QUEUES); + n_rxq = EFX_MAX_RX_QUEUES; + } + + return n_rxq; +} + +/* Probe the number and type of interrupts we are able to obtain, and + * the resulting numbers of channels and RX queues. + */ static void efx_probe_interrupts(struct efx_nic *efx) { - int max_channel = efx->type->phys_addr_channels - 1; - struct msix_entry xentries[EFX_MAX_CHANNELS]; + int max_channels = + min_t(int, efx->type->phys_addr_channels, EFX_MAX_CHANNELS); int rc, i; if (efx->interrupt_mode == EFX_INT_MODE_MSIX) { - BUG_ON(!pci_find_capability(efx->pci_dev, PCI_CAP_ID_MSIX)); - - if (rss_cpus == 0) { -#ifdef topology_core_siblings - cpumask_t core_mask; - int cpu; - - cpus_clear(core_mask); - efx->rss_queues = 0; - for_each_online_cpu(cpu) { - if (!cpu_isset(cpu, core_mask)) { - ++efx->rss_queues; - cpus_or(core_mask, core_mask, - topology_core_siblings(cpu)); - } - } -#else - efx->rss_queues = num_online_cpus(); -#endif - } else { - efx->rss_queues = rss_cpus; + struct msix_entry xentries[EFX_MAX_CHANNELS]; + int wanted_ints; + int rx_queues; + + /* We will need one interrupt per channel. We need one + * channel per RX queue, and possibly one for TX queues. + */ + rx_queues = efx_wanted_rx_queues(efx); + wanted_ints = rx_queues + (separate_tx_channels ? 1 : 0); + wanted_ints = min(wanted_ints, max_channels); + + for (i = 0; i < wanted_ints; i++) + xentries[i].entry = i; + rc = pci_enable_msix(efx->pci_dev, xentries, wanted_ints); + if (rc > 0) { + EFX_ERR(efx, "WARNING: Insufficient MSI-X vectors" + " available (%d < %d).\n", rc, wanted_ints); + EFX_ERR(efx, "WARNING: Performance may be reduced.\n"); + EFX_BUG_ON_PARANOID(rc >= wanted_ints); + wanted_ints = rc; + rc = pci_enable_msix(efx->pci_dev, xentries, + wanted_ints); } - /* Limit the number of rss queues appropriately */ - efx->rss_queues = min(efx->rss_queues, max_channel + 1); - efx->rss_queues = min(efx->rss_queues, EFX_MAX_CHANNELS); - - /* Request maximum number of MSI interrupts, and fill out - * the channel interrupt information the allowed allocation */ - for (i = 0; i < efx->rss_queues; i++) - xentries[i].entry = i; - rc = pci_enable_msix(efx->pci_dev, xentries, efx->rss_queues); - if (rc > 0) { - EFX_BUG_ON_PARANOID(rc >= efx->rss_queues); - efx->rss_queues = rc; - rc = pci_enable_msix(efx->pci_dev, xentries, - efx->rss_queues); - } - if (rc == 0) { - for (i = 0; i < efx->rss_queues; i++) { - efx->channel[i].has_interrupt = 1; + efx->n_rx_queues = min(rx_queues, wanted_ints); + efx->n_channels = wanted_ints; + for (i = 0; i < wanted_ints; i++) efx->channel[i].irq = xentries[i].vector; - } } else { /* Fall back to single channel MSI */ efx->interrupt_mode = EFX_INT_MODE_MSI; @@ -1019,11 +1207,11 @@ static void efx_probe_interrupts(struct /* Try single interrupt MSI */ if (efx->interrupt_mode == EFX_INT_MODE_MSI) { - efx->rss_queues = 1; + efx->n_rx_queues = 1; + efx->n_channels = 1; rc = pci_enable_msi(efx->pci_dev); if (rc == 0) { efx->channel[0].irq = efx->pci_dev->irq; - efx->channel[0].has_interrupt = 1; } else { EFX_ERR(efx, "could not enable MSI\n"); efx->interrupt_mode = EFX_INT_MODE_LEGACY; @@ -1032,10 +1220,8 @@ static void efx_probe_interrupts(struct /* Assume legacy interrupts */ if (efx->interrupt_mode == EFX_INT_MODE_LEGACY) { - efx->rss_queues = 1; - /* Every channel is interruptible */ - for (i = 0; i < EFX_MAX_CHANNELS; i++) - efx->channel[i].has_interrupt = 1; + efx->n_rx_queues = 1; + efx->n_channels = 1 + (separate_tx_channels ? 1 : 0); efx->legacy_irq = efx->pci_dev->irq; } } @@ -1045,7 +1231,7 @@ static void efx_remove_interrupts(struct struct efx_channel *channel; /* Remove MSI/MSI-X interrupts */ - efx_for_each_channel_with_interrupt(channel, efx) + efx_for_each_channel(channel, efx) channel->irq = 0; pci_disable_msi(efx->pci_dev); pci_disable_msix(efx->pci_dev); @@ -1054,45 +1240,22 @@ static void efx_remove_interrupts(struct efx->legacy_irq = 0; } -/* Select number of used resources - * Should be called after probe_interrupts() - */ -static void efx_select_used(struct efx_nic *efx) +static void efx_set_channels(struct efx_nic *efx) { struct efx_tx_queue *tx_queue; struct efx_rx_queue *rx_queue; - int i; - - /* TX queues. One per port per channel with TX capability - * (more than one per port won't work on Linux, due to out - * of order issues... but will be fine on Solaris) - */ - tx_queue = &efx->tx_queue[0]; - - /* Perform this for each channel with TX capabilities. - * At the moment, we only support a single TX queue - */ - tx_queue->used = 1; - if ((!EFX_INT_MODE_USE_MSI(efx)) && separate_tx_and_rx_channels) - tx_queue->channel = &efx->channel[1]; - else - tx_queue->channel = &efx->channel[0]; - tx_queue->channel->used_flags |= EFX_USED_BY_TX; - tx_queue++; - - /* RX queues. Each has a dedicated channel. */ - for (i = 0; i < EFX_MAX_RX_QUEUES; i++) { - rx_queue = &efx->rx_queue[i]; - - if (i < efx->rss_queues) { - rx_queue->used = 1; - /* If we allow multiple RX queues per channel - * we need to decide that here - */ - rx_queue->channel = &efx->channel[rx_queue->queue]; - rx_queue->channel->used_flags |= EFX_USED_BY_RX; - rx_queue++; - } + + efx_for_each_tx_queue(tx_queue, efx) { + if (separate_tx_channels) + tx_queue->channel = &efx->channel[efx->n_channels-1]; + else + tx_queue->channel = &efx->channel[0]; + tx_queue->channel->used_flags |= EFX_USED_BY_TX; + } + + efx_for_each_rx_queue(rx_queue, efx) { + rx_queue->channel = &efx->channel[rx_queue->queue]; + rx_queue->channel->used_flags |= EFX_USED_BY_RX; } } @@ -1102,8 +1265,13 @@ static int efx_probe_nic(struct efx_nic EFX_LOG(efx, "creating NIC\n"); + /* Initialise NIC resource information */ + efx->resources = efx->type->resources; + efx->resources.biu_lock = &efx->biu_lock; + efx->dl_info = &efx->resources.hdr; + /* Carry out hardware-type specific initialisation */ - rc = falcon_probe_nic(efx); + rc = efx->type->probe(efx); if (rc) goto fail1; @@ -1111,22 +1279,26 @@ static int efx_probe_nic(struct efx_nic * in MSI-X interrupts. */ efx_probe_interrupts(efx); - /* Determine number of RX queues and TX queues */ - efx_select_used(efx); + if (EFX_INT_MODE_USE_MSI(efx)) + efx->resources.flags |= EFX_DL_FALCON_USE_MSI; + + efx_set_channels(efx); /* Register debugfs entries */ rc = efx_init_debugfs_nic(efx); if (rc) goto fail2; /* Initialise the interrupt moderation settings */ - efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec); + efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec, + irq_adapt_enable); return 0; fail2: efx_remove_interrupts(efx); - falcon_remove_nic(efx); + efx->type->remove(efx); fail1: + efx->dl_info = NULL; return rc; } @@ -1135,7 +1307,8 @@ static void efx_remove_nic(struct efx_ni EFX_LOG(efx, "destroying NIC\n"); efx_remove_interrupts(efx); - falcon_remove_nic(efx); + efx->type->remove(efx); + efx->dl_info = NULL; efx_fini_debugfs_nic(efx); } @@ -1174,6 +1347,7 @@ static int efx_probe_all(struct efx_nic goto fail3; } } + efx_set_channel_names(efx); return 0; @@ -1192,7 +1366,7 @@ static int efx_probe_all(struct efx_nic * and ensures that the port is scheduled to be reconfigured. * This function is safe to call multiple times when the NIC is in any * state. */ -static void efx_start_all(struct efx_nic *efx) +void efx_start_all(struct efx_nic *efx) { struct efx_channel *channel; @@ -1204,22 +1378,47 @@ static void efx_start_all(struct efx_nic return; if ((efx->state != STATE_RUNNING) && (efx->state != STATE_INIT)) return; - if (NET_DEV_REGISTERED(efx) && !netif_running(efx->net_dev)) + if (efx_dev_registered(efx) && !netif_running(efx->net_dev)) return; /* Mark the port as enabled so port reconfigurations can start, then * restart the transmit interface early so the watchdog timer stops */ efx_start_port(efx); - efx_wake_queue(efx); + if (efx_dev_registered(efx)) + efx_wake_queue(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) { +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_NEED_WORK_API_WRAPPERS) + queue_delayed_work(efx->workqueue, &efx->monitor_work, + efx_monitor_interval); +#else queue_work(efx->workqueue, &efx->monitor_work); +#endif + } 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 @@ -1227,13 +1426,28 @@ static void efx_start_all(struct efx_nic * since we're holding the rtnl_lock at this point. */ static void efx_flush_all(struct efx_nic *efx) { - /* Ensure that the hardware monitor and asynchronous port - * reconfigurations are complete, which are the only two consumers - * of efx->workqueue. Since the hardware monitor runs on a long period, - * we put in some effort to cancel the delayed work safely rather - * than just flushing the queue twice (which is guaranteed to flush - * all the work since both efx_monitor and efx_reconfigure_work disarm - * if !efx->port_enabled. */ +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_CANCEL_DELAYED_WORK_SYNC) + struct efx_rx_queue *rx_queue; + + /* Make sure the hardware monitor is stopped */ + cancel_delayed_work_sync(&efx->monitor_work); + + /* Ensure that all RX slow refills are complete. */ + efx_for_each_rx_queue(rx_queue, efx) + cancel_delayed_work_sync(&rx_queue->work); +#endif + +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_CANCEL_WORK_SYNC) + /* Stop scheduled port reconfigurations */ + cancel_work_sync(&efx->mac_work); +#endif +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_CANCEL_DELAYED_WORK_SYNC) + /* Ensure efx_monitor() and efx_mac_work() are complete, which + * are the only two consumers of efx->workqueue. Since the hardware + * monitor runs on a long period, we put in some effort to cancel + * the delayed work safely rather than just flushing the queue twice + * (which is guaranteed to flush all the work since efx_monitor(), + * and efx_mac_work() disarm if !efx->port_enabled). */ if (timer_pending(&efx->monitor_work.timer)) cancel_delayed_work(&efx->monitor_work); flush_workqueue(efx->workqueue); @@ -1245,6 +1459,7 @@ static void efx_flush_all(struct efx_nic * flush the refill workqueue twice as well. */ flush_workqueue(refill_workqueue); flush_workqueue(refill_workqueue); +#endif } /* Quiesce hardware and software without bringing the link down. @@ -1252,7 +1467,7 @@ static void efx_flush_all(struct efx_nic * state. The caller is guaranteed to subsequently be in a position * to modify any hardware and software state they see fit without * taking locks. */ -static void efx_stop_all(struct efx_nic *efx) +void efx_stop_all(struct efx_nic *efx) { struct efx_channel *channel; @@ -1262,13 +1477,19 @@ static void efx_stop_all(struct efx_nic 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_with_interrupt(channel, efx) + efx_for_each_channel(channel, efx) { if (channel->irq) synchronize_irq(channel->irq); + } /* Stop all NAPI processing and synchronous rx refills */ efx_for_each_channel(channel, efx) @@ -1279,18 +1500,13 @@ static void efx_stop_all(struct efx_nic * window to loose phy events */ efx_stop_port(efx); - /* Flush reconfigure_work, refill_workqueue, monitor_work */ + /* Flush efx_mac_work(), refill_workqueue and efx_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); - falcon_drain_tx_fifo(efx); /* Stop the kernel transmit interface late, so the watchdog * timer isn't ticking over the flush */ - efx_stop_queue(efx); - if (NET_DEV_REGISTERED(efx)) { + if (efx_dev_registered(efx)) { + efx_stop_queue(efx); netif_tx_lock_bh(efx->net_dev); netif_tx_unlock_bh(efx->net_dev); } @@ -1306,79 +1522,39 @@ static void efx_remove_all(struct efx_ni efx_remove_nic(efx); } -static int efx_run_selftests(struct efx_nic *efx) -{ - struct efx_self_tests tests; - unsigned modes = efx->startup_loopbacks & efx->loopback_modes; - int rc; - - rc = efx_online_test(efx, &tests); - if (rc) { - EFX_ERR(efx, "failed self-tests with interrupt_mode of %s\n", - INT_MODE(efx)); - goto fail; - } - - if (onload_offline_selftest && modes) { - /* Run offline self test */ - EFX_LOG(efx, "performing on-load offline self-tests\n"); - rc = efx_offline_test(efx, &tests, modes); - EFX_LOG(efx, "%s on-load offline self-tests\n", - rc ? "FAILED" : "PASSED"); - if (rc) - goto fail; - } - - return 0; - - fail: - EFX_ERR(efx, "self-tests failed. Given up!\n"); - if (allow_load_on_failure) - rc = 0; - - return rc; -} - -/* A convinience function to safely flush all the queues */ -int efx_flush_queues(struct efx_nic *efx) -{ - int rc; - - EFX_ASSERT_RESET_SERIALISED(efx); - - efx_stop_all(efx); - - efx_fini_channels(efx); - rc = efx_init_channels(efx); - if (rc) { - efx_schedule_reset(efx, RESET_TYPE_DISABLE); - return rc; - } - - efx_start_all(efx); - - return 0; -} - /************************************************************************** * * Interrupt moderation * **************************************************************************/ +static unsigned irq_mod_ticks(int usecs, int resolution) +{ + if (usecs <= 0) + return 0; /* cannot receive interrupts ahead of time :-) */ + if (usecs < resolution) + return 1; /* never round down to 0 */ + return usecs / resolution; +} + /* Set interrupt moderation parameters */ -void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs) +void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs, + bool rx_adaptive) { struct efx_tx_queue *tx_queue; struct efx_rx_queue *rx_queue; + unsigned tx_ticks = irq_mod_ticks(tx_usecs, EFX_IRQ_MOD_RESOLUTION); + unsigned rx_ticks = irq_mod_ticks(rx_usecs, EFX_IRQ_MOD_RESOLUTION); EFX_ASSERT_RESET_SERIALISED(efx); efx_for_each_tx_queue(tx_queue, efx) - tx_queue->channel->irq_moderation = tx_usecs; - + tx_queue->channel->irq_moderation = tx_ticks; + + efx->irq_rx_adaptive = rx_adaptive; + efx->irq_rx_moderation = rx_ticks; efx_for_each_rx_queue(rx_queue, efx) - rx_queue->channel->irq_moderation = rx_usecs; + rx_queue->channel->irq_moderation = rx_ticks; } /************************************************************************** @@ -1391,35 +1567,40 @@ void efx_init_irq_moderation(struct efx_ * efx_reconfigure_port via the mac_lock */ static void efx_monitor(struct work_struct *data) { +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_NEED_WORK_API_WRAPPERS) + struct efx_nic *efx = container_of(data, struct efx_nic, + monitor_work.work); +#else struct efx_nic *efx = container_of(data, struct efx_nic, monitor_work); - int rc = 0; +#endif EFX_TRACE(efx, "hardware monitor executing on CPU %d\n", raw_smp_processor_id()); - + BUG_ON(efx->type->monitor == NULL); + +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_CANCEL_DELAYED_WORK_SYNC) /* Without cancel_delayed_work_sync(), we have to make sure that - * we don't rearm when port_enabled == 0 */ + * we don't rearm when !port_enabled */ mutex_lock(&efx->mac_lock); if (!efx->port_enabled) { mutex_unlock(&efx->mac_lock); return; } - - rc = efx->mac_op->check_hw(efx); +#else + /* If the mac_lock is already held then it is likely a port + * reconfiguration is already in place, which will likely do + * most of the work of check_hw() anyway. */ + if (!mutex_trylock(&efx->mac_lock)) + goto out_requeue; + if (!efx->port_enabled) + goto out_unlock; +#endif + efx->type->monitor(efx); + +out_unlock: mutex_unlock(&efx->mac_lock); - - if (rc) { - if (monitor_reset) { - EFX_ERR(efx, "hardware monitor detected a fault: " - "triggering reset\n"); - efx_schedule_reset(efx, RESET_TYPE_MONITOR); - } else { - EFX_ERR(efx, "hardware monitor detected a fault, " - "skipping reset\n"); - } - } - +out_requeue: queue_delayed_work(efx->workqueue, &efx->monitor_work, efx_monitor_interval); } @@ -1435,12 +1616,36 @@ static void efx_monitor(struct work_stru */ static int efx_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd) { - struct efx_nic *efx = net_dev->priv; - - if (!in_interrupt()) - EFX_ASSERT_RESET_SERIALISED(efx); - - return generic_mii_ioctl(&efx->mii, if_mii(ifr), cmd, NULL); + struct efx_nic *efx = netdev_priv(net_dev); + struct mii_ioctl_data *data = if_mii(ifr); + +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_BONDING_HACKS) + if (in_interrupt()) + /* We can't execute mdio requests from an atomic context + * on Siena. Luckily, the bonding driver falls back to + * the ethtool API if this command fails. */ + return -ENOSYS; +#endif + EFX_ASSERT_RESET_SERIALISED(efx); + +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_HAVE_ETHTOOL_RESET) + if (cmd == SIOCDEVPRIVATE) { + struct efx_sock_ioctl __user *user_data = + (struct efx_sock_ioctl __user *)ifr->ifr_data; + u16 efx_cmd; + + if (copy_from_user(&efx_cmd, &user_data->cmd, sizeof(efx_cmd))) + return -EFAULT; + return efx_private_ioctl(efx, efx_cmd, &user_data->u); + } +#endif + + /* Convert phy_id from older PRTAD/DEVAD format */ + if ((cmd == SIOCGMIIREG || cmd == SIOCSMIIREG) && + (data->phy_id & 0xfc00) == 0x0400) + data->phy_id ^= MDIO_PHY_ID_C45 | 0x0400; + + return mdio_mii_ioctl(&efx->mdio, data, cmd); } /************************************************************************** @@ -1449,74 +1654,42 @@ static int efx_ioctl(struct net_device * * **************************************************************************/ -/* Allocate the NAPI dev's. - * Called after we know how many channels there are. - */ static int efx_init_napi(struct efx_nic *efx) { struct efx_channel *channel; - int rc; - - /* Allocate the NAPI dev for the port */ - efx->net_dev = alloc_etherdev(0); - if (!efx->net_dev) { - rc = -ENOMEM; - goto err; - } - efx->net_dev->priv = efx; - efx->mii.dev = efx->net_dev; - - efx->net_dev->features |= (NETIF_F_IP_CSUM | NETIF_F_SG | - NETIF_F_HIGHDMA); - efx->lro_enabled = lro; - - /* Copy MAC address */ - memcpy(&efx->net_dev->dev_addr, efx->mac_address, ETH_ALEN); - - /* Allocate the per channel devs */ + efx_for_each_channel(channel, efx) { +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_OLD_NAPI) + channel->napi_dev = efx->net_dev; +#else channel->napi_dev = alloc_etherdev(0); if (!channel->napi_dev) { - rc = -ENOMEM; - goto err; + efx_fini_napi(efx); + return -ENOMEM; } channel->napi_dev->priv = channel; atomic_set(&channel->napi_dev->refcnt, 1); - - /* Initialise LRO/SSR */ - rc = efx_ssr_init(&channel->ssr, efx); - if (rc) - goto err; - } - +#endif + netif_napi_add(channel->napi_dev, &channel->napi_str, + efx_poll, napi_weight); + } return 0; - err: - efx_fini_napi(efx); - return rc; -} - -/* Free the NAPI state for the port and channels */ +} + static void efx_fini_napi(struct efx_nic *efx) { struct efx_channel *channel; efx_for_each_channel(channel, efx) { - /* Fini LRO/SSR */ - efx_ssr_fini(&channel->ssr); - - /* Finish per channel NAPI */ + if (channel->napi_dev) + netif_napi_del(&channel->napi_str); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_HAVE_OLD_NAPI) if (channel->napi_dev) { channel->napi_dev->priv = NULL; free_netdev(channel->napi_dev); } +#endif channel->napi_dev = NULL; - } - - /* Finish port NAPI */ - if (efx->net_dev) { - efx->net_dev->priv = NULL; - free_netdev(efx->net_dev); - efx->net_dev = NULL; } } @@ -1534,10 +1707,10 @@ static void efx_fini_napi(struct efx_nic */ static void efx_netpoll(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_channel *channel; - efx_for_each_channel_with_interrupt(channel, efx) + efx_for_each_channel(channel, efx) efx_schedule_channel(channel); } @@ -1552,11 +1725,24 @@ static void efx_netpoll(struct net_devic /* Context: process, rtnl_lock() held. */ static int efx_net_open(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); EFX_ASSERT_RESET_SERIALISED(efx); EFX_LOG(efx, "opening device %s on CPU %d\n", net_dev->name, raw_smp_processor_id()); + + if (efx->state == STATE_DISABLED) { + EFX_ERR(efx, "Device is disabled.\n"); + 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; @@ -1568,36 +1754,35 @@ static int efx_net_open(struct net_devic */ static int efx_net_stop(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; - int rc; + struct efx_nic *efx = netdev_priv(net_dev); EFX_LOG(efx, "closing %s on CPU %d\n", net_dev->name, raw_smp_processor_id()); - /* Stop the device and flush all the channels */ - efx_stop_all(efx); - efx_fini_channels(efx); - rc = efx_init_channels(efx); - if (rc) - efx_schedule_reset(efx, RESET_TYPE_DISABLE); + if (efx->state != STATE_DISABLED) { + /* Stop the device and flush all the channels */ + efx_stop_all(efx); + efx_fini_channels(efx); + efx_init_channels(efx); + } return 0; } -/* Context: process, dev_base_lock held, non-blocking. */ +/* Context: process, dev_base_lock or RTNL held, non-blocking. */ static struct net_device_stats *efx_net_stats(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_mac_stats *mac_stats = &efx->mac_stats; +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_NETDEV_STATS) + struct net_device_stats *stats = &net_dev->stats; +#else struct net_device_stats *stats = &efx->stats; - - if (!spin_trylock(&efx->stats_lock)) - return stats; - if (efx->state == STATE_RUNNING) { - efx->mac_op->update_stats(efx); - falcon_update_nic_stats(efx); - } - spin_unlock(&efx->stats_lock); +#endif + + 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; @@ -1630,21 +1815,20 @@ static struct net_device_stats *efx_net_ /* Context: netif_tx_lock held, BHs disabled. */ static void efx_watchdog(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; - - EFX_ERR(efx, "TX stuck with stop_count=%d port_enabled=%d: %s\n", - atomic_read(&efx->netif_stop_count), efx->port_enabled, - monitor_reset ? "resetting channels" : "skipping reset"); - - if (monitor_reset) - efx_schedule_reset(efx, RESET_TYPE_MONITOR); + struct efx_nic *efx = netdev_priv(net_dev); + + EFX_ERR(efx, "TX stuck with stop_count=%d port_enabled=%d:" + " resetting channels\n", + atomic_read(&efx->netif_stop_count), efx->port_enabled); + + efx_schedule_reset(efx, RESET_TYPE_TX_WATCHDOG); } /* Context: process, rtnl_lock() held. */ static int efx_change_mtu(struct net_device *net_dev, int new_mtu) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); int rc = 0; EFX_ASSERT_RESET_SERIALISED(efx); @@ -1665,10 +1849,15 @@ static int efx_change_mtu(struct net_dev 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; - rc = efx_init_channels(efx); - if (rc) - goto fail; + efx->mac_op->reconfigure(efx); + mutex_unlock(&efx->mac_lock); + + efx_init_channels(efx); /* Notify driverlink client of new MTU */ EFX_DL_CALLBACK(efx, mtu_changed, new_mtu); @@ -1676,17 +1865,13 @@ static int efx_change_mtu(struct net_dev out: efx_start_all(efx); return rc; - - fail: - efx_schedule_reset(efx, RESET_TYPE_DISABLE); - return rc; } static int efx_set_mac_address(struct net_device *net_dev, void *data) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct sockaddr *addr = data; - char *new_addr = addr->sa_data; + u8 *new_addr = addr->sa_data; EFX_ASSERT_RESET_SERIALISED(efx); @@ -1700,34 +1885,27 @@ static int efx_set_mac_address(struct ne 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; } -/* Context: netif_tx_lock held, BHs disabled. */ +/* Context: netif_addr_lock held, BHs disabled. */ static void efx_set_multicast_list(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + 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; - int promiscuous; u32 crc; int bit; int i; - /* Set per-MAC promiscuity flag and reconfigure MAC if necessary */ - promiscuous = (net_dev->flags & IFF_PROMISC) ? 1 : 0; - if (efx->promiscuous != promiscuous) { - efx->promiscuous = promiscuous; - /* Close the window between efx_stop_port() and efx_flush_all() - * by only queuing work when the port is enabled. */ - if (efx->port_enabled) - queue_work(efx->workqueue, &efx->reconfigure_work); - } + 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)); @@ -1737,24 +1915,59 @@ static void efx_set_multicast_list(struc set_bit_le(bit, mc_hash->byte); mc_list = mc_list->next; } - } - - /* Create and activate new global multicast hash table */ - falcon_set_multicast_hash(efx); -} - -/* Handle net device notifier events */ + + /* 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); + } + + if (efx->port_enabled) + queue_work(efx->workqueue, &efx->mac_work); + /* Otherwise efx_start_port() will do this */ +} + +#if !defined(EFX_USE_KCOMPAT) || defined(HAVE_NET_DEVICE_OPS) +static const struct net_device_ops efx_netdev_ops = { + .ndo_open = efx_net_open, + .ndo_stop = efx_net_stop, + .ndo_get_stats = efx_net_stats, + .ndo_tx_timeout = efx_watchdog, + .ndo_start_xmit = efx_hard_start_xmit, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = efx_ioctl, + .ndo_change_mtu = efx_change_mtu, + .ndo_set_mac_address = efx_set_mac_address, + .ndo_set_multicast_list = efx_set_multicast_list, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = efx_netpoll, +#endif +}; +#endif + +static void efx_update_name(struct efx_nic *efx) +{ + strcpy(efx->name, efx->net_dev->name); + efx_mtd_rename(efx); + efx_set_channel_names(efx); +} + static int efx_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *net_dev = (struct net_device *)ptr; - + struct net_device *net_dev = ptr; + +#if !defined(EFX_USE_KCOMPAT) || defined(HAVE_NET_DEVICE_OPS) + if (net_dev->netdev_ops == &efx_netdev_ops && + event == NETDEV_CHANGENAME) { +#else if (net_dev->open == efx_net_open && event == NETDEV_CHANGENAME) { - struct efx_nic *efx = net_dev->priv; - - strcpy(efx->name, net_dev->name); - efx_fini_debugfs_netdev(net_dev); - efx_init_debugfs_netdev(net_dev); +#endif + struct efx_nic *efx = netdev_priv(net_dev); + + if (efx->state == STATE_RUNNING) + efx_update_name(efx); } return NOTIFY_DONE; @@ -1764,6 +1977,7 @@ static struct notifier_block efx_netdev_ .notifier_call = efx_netdev_event, }; +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_BONDING_HACKS) /* Prior to Linux 2.6.24, the bonding driver may call change_mtu() * without holding the RTNL, unlike all other callers. We try to * mitigate the risk of a race with other reconfiguration using @@ -1778,6 +1992,15 @@ static int efx_locked_change_mtu(struct return rc; } #define efx_change_mtu efx_locked_change_mtu +#endif + +static ssize_t +show_phy_type(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + return sprintf(buf, "%d\n", efx->phy_type); +} +static DEVICE_ATTR(phy_type, 0644, show_phy_type, NULL); static int efx_register_netdev(struct efx_nic *efx) { @@ -1786,10 +2009,13 @@ static int efx_register_netdev(struct ef net_dev->watchdog_timeo = 5 * HZ; net_dev->irq = efx->pci_dev->irq; +#if !defined(EFX_USE_KCOMPAT) || defined(HAVE_NET_DEVICE_OPS) + net_dev->netdev_ops = &efx_netdev_ops; +#else net_dev->open = efx_net_open; net_dev->stop = efx_net_stop; net_dev->get_stats = efx_net_stats; - net_dev->tx_timeout = &efx_watchdog; + net_dev->tx_timeout = efx_watchdog; net_dev->hard_start_xmit = efx_hard_start_xmit; net_dev->do_ioctl = efx_ioctl; net_dev->change_mtu = efx_change_mtu; @@ -1798,42 +2024,65 @@ static int efx_register_netdev(struct ef #ifdef CONFIG_NET_POLL_CONTROLLER net_dev->poll_controller = efx_netpoll; #endif +#endif SET_NETDEV_DEV(net_dev, &efx->pci_dev->dev); SET_ETHTOOL_OPS(net_dev, &efx_ethtool_ops); - - /* Always start with carrier off; PHY events will detect the link */ - netif_carrier_off(efx->net_dev); /* Clear MAC statistics */ efx->mac_op->update_stats(efx); memset(&efx->mac_stats, 0, sizeof(efx->mac_stats)); - rc = register_netdev(net_dev); - if (rc) { - EFX_ERR(efx, "could not register net dev\n"); - return rc; - } - strcpy(efx->name, net_dev->name); + rtnl_lock(); + + rc = dev_alloc_name(net_dev, net_dev->name); + if (rc < 0) + goto fail_locked; + efx_update_name(efx); + + rc = register_netdevice(net_dev); + if (rc) + goto fail_locked; + + /* Always start with carrier off; PHY events will detect the link */ + netif_carrier_off(net_dev); + + /* Register with driverlink layer */ + efx_dl_register_nic(efx); + + rtnl_unlock(); /* Create debugfs symlinks */ rc = efx_init_debugfs_netdev(net_dev); if (rc) { EFX_ERR(efx, "failed to init net dev debugfs\n"); - unregister_netdev(efx->net_dev); - return rc; + goto fail_registered; + } + + rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_type); + if (rc) { + EFX_ERR(efx, "failed to init net dev attributes\n"); + goto fail_debugfs; } return 0; + +fail_debugfs: + efx_fini_debugfs_netdev(net_dev); +fail_registered: + rtnl_lock(); + efx_dl_unregister_nic(efx); + unregister_netdevice(net_dev); +fail_locked: + rtnl_unlock(); + EFX_ERR(efx, "could not register net dev\n"); + return rc; } static void efx_unregister_netdev(struct efx_nic *efx) { struct efx_tx_queue *tx_queue; - if (!efx->net_dev) - return; - - BUG_ON(efx->net_dev->priv != efx); + BUG_ON(netdev_priv(efx->net_dev) != efx); /* Free up any skbs still remaining. This has to happen before * we try to unregister the netdev as running their destructors @@ -1841,8 +2090,9 @@ static void efx_unregister_netdev(struct efx_for_each_tx_queue(tx_queue, efx) efx_release_tx_buffers(tx_queue); - if (NET_DEV_REGISTERED(efx)) { + if (efx_dev_registered(efx)) { strlcpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name)); + device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_type); efx_fini_debugfs_netdev(efx->net_dev); unregister_netdev(efx->net_dev); } @@ -1854,122 +2104,93 @@ static void efx_unregister_netdev(struct * **************************************************************************/ -/* Serialise access to the driverlink callbacks, by quiescing event processing - * (without flushing the descriptor queues), and acquiring the rtnl_lock */ -void efx_suspend(struct efx_nic *efx) -{ - EFX_LOG(efx, "suspending operations\n"); - - rtnl_lock(); - efx_stop_all(efx); -} - -void efx_resume(struct efx_nic *efx) -{ - EFX_LOG(efx, "resuming operations\n"); - - efx_start_all(efx); - rtnl_unlock(); -} - -/* The final hardware and software finalisation before reset. - * This function does not handle serialisation with the kernel, it - * assumes the caller has done this */ -static int efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd) -{ - int rc; - +/* Tears down driverlink clients, the entire software state, + * and most of the hardware state before reset. */ +void efx_reset_down(struct efx_nic *efx, enum reset_type method) +{ EFX_ASSERT_RESET_SERIALISED(efx); - - rc = efx->mac_op->get_settings(efx, ecmd); - if (rc) { - EFX_ERR(efx, "could not back up PHY settings\n"); - goto fail; - } - - efx_fini_channels(efx); - return 0; - - fail: - return rc; -} - -/* The first part of software initialisation after a hardware reset - * This function does not handle serialisation with the kernel, it - * assumes the caller has done this */ -static int efx_reset_up(struct efx_nic *efx, struct ethtool_cmd *ecmd) -{ - int rc; - - rc = efx_init_channels(efx); - if (rc) - goto fail1; - - /* Restore MAC and PHY settings. */ - rc = efx->mac_op->set_settings(efx, ecmd); - if (rc) { - EFX_ERR(efx, "could not restore PHY settings\n"); - goto fail2; - } - - return 0; - - fail2: - efx_fini_channels(efx); - fail1: - 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. - * - * This function will sleep. You cannot reset from within an atomic - * state; use efx_schedule_reset() instead. - * - * Grabs the dl_reset_lock, and to serialise with kernel interfaces the - * rtnl_lock. - */ -static int efx_reset(struct efx_nic *efx) -{ - struct ethtool_cmd ecmd; - enum reset_type method = efx->reset_pending; - int rc; - - /* Notify driverlink clients of imminent reset. */ - efx_dl_reset_lock(); - efx_dl_reset_suspend(efx); - - /* 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 unlock_rtnl; - } - - efx->state = STATE_RESETTING; - EFX_INFO(efx, "resetting (%s)\n", RESET_TYPE(method)); - - /* The net_dev->get_stats handler is quite slow, and will fail - * if a fetch is pending over reset. Serialise against it. */ - spin_lock(&efx->stats_lock); - spin_unlock(&efx->stats_lock); efx_stop_all(efx); mutex_lock(&efx->mac_lock); - - rc = efx_reset_down(efx, &ecmd); - if (rc) - goto fail1; - falcon_fini_nic(efx); - - rc = falcon_reset_hw(efx, method); + mutex_lock(&efx->spi_lock); + + 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 + * efx_reset_down() are released. A failure return code indicates + * 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, bool ok) +{ + int rc; + + EFX_ASSERT_RESET_SERIALISED(efx); + + /* Ensure that SRAM is initialised even if we're disabling the device */ + rc = efx->type->init(efx); + if (rc) { + EFX_ERR(efx, "failed to initialise NIC\n"); + goto fail; + } + + if (!ok) + goto fail; + + if (efx->port_initialized && method != RESET_TYPE_INVISIBLE) { + 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"); + } + + efx->mac_op->reconfigure(efx); + + 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); + + return rc; +} + +/* 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. + * + * Caller must hold the rtnl_lock. + */ +int efx_reset(struct efx_nic *efx, enum reset_type method) +{ + int rc, rc2; + bool disabled; + + /* Notify driverlink clients of imminent reset then serialise + * against other driver operations */ + efx_dl_reset_suspend(efx); + + EFX_INFO(efx, "resetting (%s)\n", RESET_TYPE(method)); + + efx_reset_down(efx, method); + + rc = efx->type->reset(efx, method); if (rc) { EFX_ERR(efx, "failed to reset hardware\n"); - goto fail2; + goto out; } /* Allow resets to be rescheduled. */ @@ -1981,54 +2202,23 @@ static int efx_reset(struct efx_nic *efx * can respond to requests. */ pci_set_master(efx->pci_dev); - /* Reinitialise device. This is appropriate in the RESET_TYPE_DISABLE - * case so the driver can talk to external SRAM */ - rc = falcon_init_nic(efx); - if (rc) { - EFX_ERR(efx, "failed to initialise NIC\n"); - goto fail3; - } - +out: /* Leave device stopped if necessary */ - if (method == RESET_TYPE_DISABLE) { - /* Reinitialise the device anyway so the driver unload sequence - * can talk to the external SRAM */ - (void) falcon_init_nic(efx); - rc = -EIO; - goto fail4; - } - - rc = efx_reset_up(efx, &ecmd); - if (rc) - goto fail5; - - mutex_unlock(&efx->mac_lock); - EFX_LOG(efx, "reset complete\n"); - - efx->state = STATE_RUNNING; - efx_start_all(efx); - - unlock_rtnl: - rtnl_unlock(); - efx_dl_reset_resume(efx, 1); - efx_dl_reset_unlock(); - return 0; - - fail5: - fail4: - fail3: - fail2: - fail1: - EFX_ERR(efx, "has been disabled\n"); - efx->state = STATE_DISABLED; - - mutex_unlock(&efx->mac_lock); - rtnl_unlock(); - /* Remove the net_dev */ - efx_unregister_netdev(efx); - efx_fini_port(efx); - efx_dl_reset_resume(efx, 0); - efx_dl_reset_unlock(); + disabled = rc || method == RESET_TYPE_DISABLE; + rc2 = efx_reset_up(efx, method, !disabled); + if (rc2) { + disabled = true; + if (!rc) + rc = rc2; + } + + if (disabled) { + EFX_ERR(efx, "has been disabled\n"); + efx->state = STATE_DISABLED; + } else { + EFX_LOG(efx, "reset complete\n"); + } + efx_dl_reset_resume(efx, !disabled); return rc; } @@ -2037,9 +2227,19 @@ static int efx_reset(struct efx_nic *efx */ static void efx_reset_work(struct work_struct *data) { - struct efx_nic *nic = container_of(data, struct efx_nic, reset_work); - - efx_reset(nic); + struct efx_nic *efx = container_of(data, struct efx_nic, reset_work); + + /* If we're not RUNNING then don't reset. Leave the reset_pending + * flag set so that efx_pci_probe_main will be retried */ + if (efx->state != STATE_RUNNING) { + EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n"); + return; + } + + 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) @@ -2064,6 +2264,7 @@ void efx_schedule_reset(struct efx_nic * case RESET_TYPE_TX_SKIP: method = RESET_TYPE_INVISIBLE; break; + case RESET_TYPE_MC_FAILURE: default: method = RESET_TYPE_ALL; break; @@ -2077,7 +2278,11 @@ void efx_schedule_reset(struct efx_nic * efx->reset_pending = method; - queue_work(efx->reset_workqueue, &efx->reset_work); + /* 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); } /************************************************************************** @@ -2088,18 +2293,24 @@ void efx_schedule_reset(struct efx_nic * /* PCI device ID table */ static struct pci_device_id efx_pci_table[] __devinitdata = { +#ifndef __VMKERNEL_MODULE__ {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}, +#endif {PCI_DEVICE(EFX_VENDID_SFC, FALCON_B_P_DEVID), - .driver_data = (unsigned long) &falcon_b_nic_type}, + .driver_data = (unsigned long) &falcon_b0_nic_type}, + {PCI_DEVICE(EFX_VENDID_SFC, BETHPAGE_A_P_DEVID), + .driver_data = (unsigned long) &siena_a0_nic_type}, + {PCI_DEVICE(EFX_VENDID_SFC, SIENA_A_P_DEVID), + .driver_data = (unsigned long) &siena_a0_nic_type}, {0} /* end of list */ }; /************************************************************************** * - * Dummy PHY/MAC/Board operations - * - * Can be used where the MAC does not implement this operation + * Dummy PHY/MAC operations + * + * Can be used for some unimplemented operations * Needed so all function pointers are valid and do not have to be tested * before use * @@ -2109,38 +2320,19 @@ int efx_port_dummy_op_int(struct efx_nic return 0; } void efx_port_dummy_op_void(struct efx_nic *efx) {} -void efx_port_dummy_op_blink(struct efx_nic *efx, int blink) {} - -static struct efx_mac_operations efx_dummy_mac_operations = { - .init = efx_port_dummy_op_int, - .reconfigure = efx_port_dummy_op_void, - .fini = efx_port_dummy_op_void, -}; +void efx_port_dummy_op_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ +} +bool efx_port_dummy_op_poll(struct efx_nic *efx) +{ + return false; +} static struct efx_phy_operations efx_dummy_phy_operations = { .init = efx_port_dummy_op_int, - .reconfigure = efx_port_dummy_op_void, - .check_hw = efx_port_dummy_op_int, + .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, - .reset_xaui = efx_port_dummy_op_void, -}; - -/* Dummy board operations */ -static int efx_nic_dummy_op_int(struct efx_nic *nic) -{ - return 0; -} - -static void efx_nic_dummy_op_void(struct efx_nic *nic) {} - -static struct efx_board efx_dummy_board_info = { - .init = efx_nic_dummy_op_int, - .init_leds = efx_port_dummy_op_int, - .set_fault_led = efx_port_dummy_op_blink, - .monitor = efx_nic_dummy_op_int, - .blink = efx_port_dummy_op_blink, - .fini = efx_nic_dummy_op_void, }; /************************************************************************** @@ -2153,51 +2345,58 @@ static struct efx_board efx_dummy_board_ * efx_nic (including all sub-structures). */ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, - struct pci_dev *pci_dev) + struct pci_dev *pci_dev, struct net_device *net_dev) { struct efx_channel *channel; struct efx_tx_queue *tx_queue; struct efx_rx_queue *rx_queue; - int i, rc; + int i; /* Initialise common structures */ memset(efx, 0, sizeof(*efx)); spin_lock_init(&efx->biu_lock); - spin_lock_init(&efx->phy_lock); + mutex_init(&efx->mdio_lock); mutex_init(&efx->spi_lock); +#ifdef CONFIG_SFC_MTD + INIT_LIST_HEAD(&efx->mtd_list); +#endif INIT_WORK(&efx->reset_work, efx_reset_work); INIT_DELAYED_WORK(&efx->monitor_work, efx_monitor); efx->pci_dev = pci_dev; efx->state = STATE_INIT; efx->reset_pending = RESET_TYPE_NONE; strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); - efx->board_info = efx_dummy_board_info; - - efx->rx_checksum_enabled = 1; + + efx->net_dev = net_dev; + efx->rx_checksum_enabled = true; +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_SFC_LRO) && !defined(NETIF_F_LRO) + efx->lro_enabled = lro; +#endif spin_lock_init(&efx->netif_stop_lock); spin_lock_init(&efx->stats_lock); 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_LIST_HEAD(&efx->dl_node); INIT_LIST_HEAD(&efx->dl_device_list); efx->dl_cb = efx_default_callbacks; - INIT_WORK(&efx->reconfigure_work, efx_reconfigure_work); + INIT_WORK(&efx->mac_work, efx_mac_work); atomic_set(&efx->netif_stop_count, 1); for (i = 0; i < EFX_MAX_CHANNELS; i++) { channel = &efx->channel[i]; channel->efx = efx; channel->channel = i; - channel->evqnum = i; - channel->work_pending = 0; - } - for (i = 0; i < EFX_MAX_TX_QUEUES; i++) { + channel->work_pending = false; + } + for (i = 0; i < EFX_TX_QUEUE_COUNT; i++) { tx_queue = &efx->tx_queue[i]; tx_queue->efx = efx; tx_queue->queue = i; tx_queue->buffer = NULL; tx_queue->channel = &efx->channel[0]; /* for safety */ + tx_queue->tso_headers_free = NULL; } for (i = 0; i < EFX_MAX_RX_QUEUES; i++) { rx_queue = &efx->rx_queue[i]; @@ -2211,52 +2410,37 @@ static int efx_init_struct(struct efx_ni efx->type = type; - /* Sanity-check NIC type */ - EFX_BUG_ON_PARANOID(efx->type->txd_ring_mask & - (efx->type->txd_ring_mask + 1)); - EFX_BUG_ON_PARANOID(efx->type->rxd_ring_mask & - (efx->type->rxd_ring_mask + 1)); - EFX_BUG_ON_PARANOID(efx->type->evq_size & - (efx->type->evq_size - 1)); /* As close as we can get to guaranteeing that we don't overflow */ - EFX_BUG_ON_PARANOID(efx->type->evq_size < - (efx->type->txd_ring_mask + 1 + - efx->type->rxd_ring_mask + 1)); + BUILD_BUG_ON(EFX_EVQ_SIZE < EFX_TXQ_SIZE + EFX_RXQ_SIZE); EFX_BUG_ON_PARANOID(efx->type->phys_addr_channels > EFX_MAX_CHANNELS); /* Higher numbered interrupt modes are less capable! */ efx->interrupt_mode = max(efx->type->max_interrupt_mode, interrupt_mode); - - efx->workqueue = create_singlethread_workqueue("sfc_work"); - if (!efx->workqueue) { - rc = -ENOMEM; - goto fail1; - } - - efx->reset_workqueue = create_singlethread_workqueue("sfc_reset"); - if (!efx->reset_workqueue) { - rc = -ENOMEM; - goto fail2; - } +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_DUMMY_MSIX) + if (efx->interrupt_mode == EFX_INT_MODE_MSIX) + efx->interrupt_mode = EFX_INT_MODE_MSI; +#endif + + /* Would be good to use the net_dev name, but we're too early */ +#if defined(EFX_USE_KCOMPAT) && defined(EFX_HAVE_WORKQUEUE_NAME_LIMIT) + snprintf(efx->workqueue_name, 10 + 1, "sfc%02x:%02x.%d", + pci_dev->bus->number, PCI_SLOT(pci_dev->devfn), + PCI_FUNC(pci_dev->devfn)); +#else + snprintf(efx->workqueue_name, sizeof(efx->workqueue_name), "sfc%s", + pci_name(pci_dev)); +#endif + efx->workqueue = create_singlethread_workqueue(efx->workqueue_name); + if (!efx->workqueue) + return -ENOMEM; return 0; - - fail2: - destroy_workqueue(efx->workqueue); - efx->workqueue = NULL; - - fail1: - return rc; } static void efx_fini_struct(struct efx_nic *efx) { - if (efx->reset_workqueue) { - destroy_workqueue(efx->reset_workqueue); - efx->reset_workqueue = NULL; - } if (efx->workqueue) { destroy_workqueue(efx->workqueue); efx->workqueue = NULL; @@ -2274,22 +2458,10 @@ static void efx_fini_struct(struct efx_n */ static void efx_pci_remove_main(struct efx_nic *efx) { - EFX_ASSERT_RESET_SERIALISED(efx); - - /* Skip everything if we never obtained a valid membase */ - if (!efx->membase) - return; - + efx_nic_fini_interrupt(efx); efx_fini_channels(efx); efx_fini_port(efx); - - /* Shutdown the board, then the NIC and board state */ - efx->board_info.fini(efx); - falcon_fini_nic(efx); - falcon_fini_interrupt(efx); - efx->board_info.fini(efx); - - /* Tear down NAPI and LRO */ + efx->type->fini(efx); efx_fini_napi(efx); efx_remove_all(efx); } @@ -2305,38 +2477,38 @@ static void efx_pci_remove(struct pci_de if (!efx) return; - /* Unregister driver from driverlink layer */ - efx_dl_unregister_nic(efx); - /* Mark the NIC as fini, then stop the interface */ rtnl_lock(); + efx_dl_unregister_nic(efx); efx->state = STATE_FINI; dev_close(efx->net_dev); /* Allow any queued efx_resets() to complete */ rtnl_unlock(); - if (efx->membase == NULL) - goto out; - efx_unregister_netdev(efx); + + efx_mtd_remove(efx); efx_fini_debugfs_channels(efx); /* Wait for any scheduled resets to complete. No more will be * scheduled from this point because efx_stop_all() has been * called, we are no longer registered with driverlink, and * the net_device's have been removed. */ - flush_workqueue(efx->reset_workqueue); +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_CANCEL_WORK_SYNC) + cancel_work_sync(&efx->reset_work); +#else + flush_workqueue(reset_workqueue); +#endif efx_pci_remove_main(efx); -out: efx_fini_io(efx); EFX_LOG(efx, "shutdown successful\n"); pci_set_drvdata(pci_dev, NULL); efx_fini_struct(efx); - kfree(efx); + free_netdev(efx->net_dev); }; /* Main body of NIC initialisation @@ -2351,65 +2523,38 @@ static int efx_pci_probe_main(struct efx if (rc) goto fail1; - /* Initialise port/channel net_dev's */ rc = efx_init_napi(efx); if (rc) goto fail2; - /* Initialise the board */ - rc = efx->board_info.init(efx); - if (rc) { - EFX_ERR(efx, "failed to initialise board\n"); - goto fail3; - } - - /* Initialise device */ - rc = falcon_init_nic(efx); + rc = efx->type->init(efx); if (rc) { EFX_ERR(efx, "failed to initialise NIC\n"); - goto fail4; - } - - /* Initialise port */ + goto fail3; + } + rc = efx_init_port(efx); if (rc) { EFX_ERR(efx, "failed to initialise port\n"); + goto fail4; + } + + efx_init_channels(efx); + + rc = efx_nic_init_interrupt(efx); + if (rc) goto fail5; - } - - /* Initialise channels */ - rc = efx_init_channels(efx); - if (rc) - goto fail6; - - rc = falcon_init_interrupt(efx); - if (rc) - goto fail7; - - /* Start up device - interrupts can occur from this point */ - efx_start_all(efx); - - /* Check basic functionality and set interrupt mode */ - rc = efx_run_selftests(efx); - if (rc) - goto fail8; - - /* Stop the NIC */ + + return 0; + + fail6: efx_stop_all(efx); - - return 0; - - fail8: - efx_stop_all(efx); - falcon_fini_interrupt(efx); - fail7: + efx_nic_fini_interrupt(efx); + fail5: efx_fini_channels(efx); - fail6: efx_fini_port(efx); - fail5: - falcon_fini_nic(efx); fail4: - efx->board_info.fini(efx); + efx->type->fini(efx); fail3: efx_fini_napi(efx); fail2: @@ -2430,103 +2575,288 @@ static int __devinit efx_pci_probe(struc static int __devinit efx_pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *entry) { + struct efx_nic_type *type = (struct efx_nic_type *) entry->driver_data; + struct net_device *net_dev; struct efx_nic *efx; - struct efx_nic_type *type = (struct efx_nic_type *) entry->driver_data; int i, rc; - /* Allocate and initialise a struct efx_nic */ - efx = kmalloc(sizeof(*efx), GFP_KERNEL); - if (!efx) { - rc = -ENOMEM; + /* Allocate and initialise a struct net_device and struct efx_nic */ + net_dev = alloc_etherdev(sizeof(*efx)); + if (!net_dev) + return -ENOMEM; + net_dev->features |= (type->offload_features | NETIF_F_SG | + NETIF_F_HIGHDMA | NETIF_F_TSO); +#if !defined(EFX_USE_KCOMPAT) || defined(NETIF_F_TSO6) + if (type->offload_features & NETIF_F_V6_CSUM) + net_dev->features |= NETIF_F_TSO6; +#endif +#if defined(EFX_USE_GRO) + if (lro) + net_dev->features |= NETIF_F_GRO; +#endif +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_NETDEV_VLAN_FEATURES) + /* 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); +#endif + efx = netdev_priv(net_dev); + pci_set_drvdata(pci_dev, efx); + rc = efx_init_struct(efx, type, pci_dev, net_dev); + if (rc) goto fail1; - } - pci_set_drvdata(pci_dev, efx); - rc = efx_init_struct(efx, type, pci_dev); - if (rc) - goto fail2; - - EFX_INFO(efx, "Solarflare Communications NIC detected\n"); + + EFX_INFO(efx, "Solarflare Communications NIC detected PCI(%x:%x)\n", + pci_dev->vendor, pci_dev->device); /* Set up basic I/O (BAR mappings etc) */ rc = efx_init_io(efx); if (rc) - goto fail3; + goto fail2; /* No serialisation is required with the reset path because * we're in STATE_INIT. */ for (i = 0; i < 5; i++) { rc = efx_pci_probe_main(efx); - if (rc == 0) - break; /* Serialise against efx_reset(). No more resets will be * scheduled since efx_stop_all() has been called, and we * have not and never have been registered with either * the rtnetlink or driverlink layers. */ - flush_workqueue(efx->reset_workqueue); +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_CANCEL_WORK_SYNC) + cancel_work_sync(&efx->reset_work); +#else + flush_workqueue(reset_workqueue); +#endif + + if (rc == 0) { + if (efx->reset_pending != RESET_TYPE_NONE) { + /* If there was a scheduled reset during + * probe, the NIC is probably hosed anyway */ + efx_pci_remove_main(efx); + rc = -EIO; + } else { + break; + } + } /* Retry if a recoverably reset event has been scheduled */ if ((efx->reset_pending != RESET_TYPE_INVISIBLE) && - (efx->reset_pending != RESET_TYPE_ALL)) - goto fail4; + (efx->reset_pending != RESET_TYPE_ALL)) { + if (efx->reset_pending != RESET_TYPE_NONE) + EFX_ERR(efx, "Unrecoverable scheduled reset: " + "%s\n", RESET_TYPE(efx->reset_pending)); + goto fail3; + } efx->reset_pending = RESET_TYPE_NONE; } if (rc) { - EFX_ERR(efx, "Could not reset NIC\n"); - goto fail5; + EFX_ERR(efx, "Initialisation failed due to persistent reset\n"); + goto fail4; } /* Self-tests have all passed */ rc = efx_init_debugfs_channels(efx); if (rc) - goto fail6; - - /* 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. */ - rtnl_lock(); + goto fail5; + + /* 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; - rtnl_unlock(); rc = efx_register_netdev(efx); if (rc) - goto fail7; + goto fail6; EFX_LOG(efx, "initialisation successful\n"); - /* Register with driverlink layer */ - rc = efx_dl_register_nic(efx); - if (rc) - goto fail8; - + rtnl_lock(); + efx_mtd_probe(efx); /* allowed to fail */ + rtnl_unlock(); return 0; - fail8: - efx_unregister_netdev(efx); - fail7: + fail6: efx_fini_debugfs_channels(efx); - fail6: + fail5: efx_pci_remove_main(efx); - fail5: fail4: + fail3: efx_fini_io(efx); - fail3: + fail2: efx_fini_struct(efx); - fail2: - kfree(efx); fail1: EFX_LOG(efx, "initialisation failed. rc=%d\n", rc); + free_netdev(net_dev); return rc; } -/* PCI driver definition */ +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_PM) + +static int efx_pm_freeze(struct device *dev) +{ + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + + rtnl_lock(); + efx_dl_reset_suspend(efx); + rtnl_unlock(); + + 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; + + rtnl_lock(); + efx_dl_reset_resume(efx, true); + rtnl_unlock(); + + 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) + goto fail; + pci_restore_state(pci_dev); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_UNMASK_MSIX_VECTORS) + efx_unmask_msix_vectors(pci_dev); +#endif + rc = pci_enable_device(pci_dev); + if (rc) + goto fail; + pci_set_master(efx->pci_dev); + rc = efx->type->reset(efx, RESET_TYPE_ALL); + if (rc) + goto fail; + rc = efx->type->init(efx); + if (rc) + goto fail; + efx_pm_thaw(dev); + return 0; + +fail: + efx_dl_reset_resume(efx, false); + return rc; +} + +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; +} + +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_DEV_PM_OPS) + +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, +}; + +#elif defined(EFX_USE_PM_EXT_OPS) + +static struct pm_ext_ops efx_pm_ops = { + .base = { + .suspend = efx_pm_suspend, + .resume = efx_pm_resume, + .freeze = efx_pm_freeze, + .thaw = efx_pm_thaw, + .poweroff = efx_pm_poweroff, + .restore = efx_pm_resume, + } +}; + +#else /* !EFX_USE_DEV_PM_OPS && !EFX_USE_PM_EXT_OPS */ + +static int efx_pm_old_suspend(struct pci_dev *dev, pm_message_t state) +{ + switch (state.event) { + case PM_EVENT_FREEZE: +#if defined(PM_EVENT_QUIESCE) + case PM_EVENT_QUIESCE: +#elif defined(PM_EVENT_PRETHAW) + case PM_EVENT_PRETHAW: +#endif + return efx_pm_freeze(&dev->dev); + default: + return efx_pm_suspend(&dev->dev); + } +} + +static int efx_pm_old_resume(struct pci_dev *dev) +{ + return efx_pm_resume(&dev->dev); +} + +#endif /* EFX_USE_PM_EXT_OPS */ + +#endif /* EFX_USE_PM */ + static struct pci_driver efx_pci_driver = { .name = EFX_DRIVER_NAME, .id_table = efx_pci_table, .probe = efx_pci_probe, .remove = efx_pci_remove, +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_DEV_PM_OPS) + .driver.pm = &efx_pm_ops, +#elif defined(EFX_USE_PM_EXT_OPS) + .pm = &efx_pm_ops, +#elif defined(EFX_USE_PM) + .suspend = efx_pm_old_suspend, + .resume = efx_pm_old_resume, +#endif }; /************************************************************************** @@ -2538,9 +2868,6 @@ module_param(interrupt_mode, uint, 0444) module_param(interrupt_mode, uint, 0444); MODULE_PARM_DESC(interrupt_mode, "Interrupt mode (0=>MSIX 1=>MSI 2=>legacy)"); - -module_param(onload_offline_selftest, uint, 0444); -MODULE_PARM_DESC(onload_offline_selftest, "Perform offline selftest on load"); static int __init efx_init_module(void) { @@ -2561,6 +2888,27 @@ static int __init efx_init_module(void) rc = -ENOMEM; goto err_refill; } + reset_workqueue = create_singlethread_workqueue("sfc_reset"); + if (!reset_workqueue) { + rc = -ENOMEM; + goto err_reset; + } + +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_LM87_DRIVER) + rc = i2c_add_driver(&efx_lm87_driver); + if (rc < 0) + goto err_lm87; +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_LM90_DRIVER) + rc = i2c_add_driver(&efx_lm90_driver); + if (rc < 0) + goto err_lm90; +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_I2C_NEW_DUMMY) + rc = i2c_add_driver(&efx_i2c_dummy_driver); + if (rc < 0) + goto err_i2c_dummy; +#endif rc = pci_register_driver(&efx_pci_driver); if (rc < 0) @@ -2569,6 +2917,20 @@ static int __init efx_init_module(void) return 0; err_pci: +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_I2C_NEW_DUMMY) + i2c_del_driver(&efx_i2c_dummy_driver); + err_i2c_dummy: +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_LM90_DRIVER) + i2c_del_driver(&efx_lm90_driver); + err_lm90: +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_LM87_DRIVER) + i2c_del_driver(&efx_lm87_driver); + err_lm87: +#endif + destroy_workqueue(reset_workqueue); + err_reset: destroy_workqueue(refill_workqueue); err_refill: unregister_netdevice_notifier(&efx_netdev_notifier); @@ -2583,6 +2945,16 @@ static void __exit efx_exit_module(void) printk(KERN_INFO "Solarflare NET driver unloading\n"); pci_unregister_driver(&efx_pci_driver); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_I2C_NEW_DUMMY) + i2c_del_driver(&efx_i2c_dummy_driver); +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_LM90_DRIVER) + i2c_del_driver(&efx_lm90_driver); +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_LM87_DRIVER) + i2c_del_driver(&efx_lm87_driver); +#endif + destroy_workqueue(reset_workqueue); destroy_workqueue(refill_workqueue); unregister_netdevice_notifier(&efx_netdev_notifier); efx_fini_debugfs(); @@ -2592,8 +2964,9 @@ module_init(efx_init_module); module_init(efx_init_module); module_exit(efx_exit_module); -MODULE_AUTHOR("Michael Brown <mbrown@xxxxxxxxxxxxxxxx> and " - "Solarflare Communications"); +MODULE_AUTHOR("Solarflare Communications and " + "Michael Brown <mbrown@xxxxxxxxxxxxxxxx>"); MODULE_DESCRIPTION("Solarflare Communications network driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, efx_pci_table); +MODULE_VERSION(EFX_DRIVER_VERSION); diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/efx.h --- a/drivers/net/sfc/efx.h Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/efx.h Fri Jan 08 13:05:49 2010 +0000 @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2006-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> - * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> + * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_EFX_H @@ -35,42 +18,103 @@ #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 /* TX */ -extern int efx_xmit(struct efx_nic *efx, - struct efx_tx_queue *tx_queue, struct sk_buff *skb); +extern int efx_probe_tx_queue(struct efx_tx_queue *tx_queue); +extern void efx_remove_tx_queue(struct efx_tx_queue *tx_queue); +extern void efx_init_tx_queue(struct efx_tx_queue *tx_queue); +extern void efx_fini_tx_queue(struct efx_tx_queue *tx_queue); +extern void efx_release_tx_buffers(struct efx_tx_queue *tx_queue); +extern netdev_tx_t +efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); +extern netdev_tx_t +efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +extern void fastcall efx_xmit_done(struct efx_tx_queue *tx_queue, + unsigned int index); +#else +extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); +#endif extern void efx_stop_queue(struct efx_nic *efx); extern void efx_wake_queue(struct efx_nic *efx); +#define EFX_TXQ_SIZE 1024 +#define EFX_TXQ_MASK (EFX_TXQ_SIZE - 1) /* RX */ -extern void fastcall efx_xmit_done(struct efx_tx_queue *tx_queue, - unsigned int index); +extern int efx_probe_rx_queue(struct efx_rx_queue *rx_queue); +extern void efx_remove_rx_queue(struct efx_rx_queue *rx_queue); +extern void efx_init_rx_queue(struct efx_rx_queue *rx_queue); +extern void efx_fini_rx_queue(struct efx_rx_queue *rx_queue); +extern void efx_rx_strategy(struct efx_channel *channel); +extern void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue); +extern void efx_rx_work(struct work_struct *data); +extern void __efx_rx_packet(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, bool checksummed); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) extern void fastcall efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, unsigned int len, - int checksummed, int discard); + bool checksummed, bool discard); +#else +extern void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, + unsigned int len, bool checksummed, bool discard); +#endif extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay); +#define EFX_RXQ_SIZE 1024 +#define EFX_RXQ_MASK (EFX_RXQ_SIZE - 1) /* Channels */ extern void efx_process_channel_now(struct efx_channel *channel); -extern int efx_flush_queues(struct efx_nic *efx); +#define EFX_EVQ_SIZE 4096 +#define EFX_EVQ_MASK (EFX_EVQ_SIZE - 1) /* Ports */ -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, + struct ethtool_cmd *ecmd); +extern int efx_ethtool_set_settings(struct net_device *net_dev, + struct ethtool_cmd *ecmd); +extern int efx_ethtool_reset(struct net_device *net_dev, u32 *flags); +extern const struct ethtool_ops efx_ethtool_ops; + +/* Reset handling */ +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); -extern void efx_suspend(struct efx_nic *efx); -extern void efx_resume(struct efx_nic *efx); +extern void efx_start_all(struct efx_nic *efx); +extern void efx_stop_all(struct efx_nic *efx); extern void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, - int rx_usecs); + int rx_usecs, bool rx_adaptive); extern int efx_request_power(struct efx_nic *efx, int mw, const char *name); extern void efx_hex_dump(const u8 *, unsigned int, const char *); /* Dummy PHY ops for PHY drivers */ 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_blink(struct efx_nic *efx, int blink); +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 +extern int efx_mtd_probe(struct efx_nic *efx); +extern void efx_mtd_rename(struct efx_nic *efx); +extern void efx_mtd_remove(struct efx_nic *efx); +#else +static inline int efx_mtd_probe(struct efx_nic *efx) { return 0; } +static inline void efx_mtd_rename(struct efx_nic *efx) {} +static inline void efx_mtd_remove(struct efx_nic *efx) {} +#endif extern unsigned int efx_monitor_interval; @@ -78,10 +122,13 @@ static inline void efx_schedule_channel( { EFX_TRACE(channel->efx, "channel %d scheduling NAPI poll on CPU%d\n", channel->channel, raw_smp_processor_id()); - channel->work_pending = 1; + channel->work_pending = true; - if (!test_and_set_bit(__LINK_STATE_RX_SCHED, &channel->napi_dev->state)) - __netif_rx_schedule(channel->napi_dev); + 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 -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/efx_ioctl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/efx_ioctl.h Fri Jan 08 13:05:49 2010 +0000 @@ -0,0 +1,71 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2005-2006: Fen Systems Ltd. + * Copyright 2006-2009: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> + * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef EFX_IOCTL_H +#define EFX_IOCTL_H + +#if defined(__KERNEL__) +#include <linux/if.h> +#else +#include <net/if.h> +#ifndef _LINUX_IF_H +#define _LINUX_IF_H /* prevent <linux/if.h> from conflicting with <net/if.h> */ +#endif +#endif +#include <linux/sockios.h> +#include <linux/types.h> + +/* + * Efx private ioctls + */ + +/* Reset selected components, like ETHTOOL_RESET ****************************/ +#define EFX_RESET_FLAGS 0xef0d +struct efx_reset_flags { + __u32 flags; +}; + +/* Efx private ioctl command structures *************************************/ + +union efx_ioctl_data { + struct efx_reset_flags reset_flags; +}; + +struct efx_sock_ioctl { + /* Command to run */ + __u16 cmd; + __u16 reserved; + /* Parameters */ + union efx_ioctl_data u; +}; + +#ifdef __KERNEL__ +extern int efx_private_ioctl(struct efx_nic *efx, u16 cmd, + union efx_ioctl_data __user *data); +#endif + +#endif /* EFX_IOCTL_H */ diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/enum.h --- a/drivers/net/sfc/enum.h Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/enum.h Fri Jan 08 13:05:49 2010 +0000 @@ -1,26 +1,10 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2007: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Developed by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> + * Driver for Solarflare Solarstorm network controllers and boards + * 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 * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_ENUM_H @@ -29,57 +13,116 @@ /** * enum efx_loopback_mode - loopback modes * @LOOPBACK_NONE: no loopback - * @LOOPBACK_NEAR: loopback nearest to bus - * @LOOPBACK_MAC: loopback within MAC unspecified level - * @LOOPBACK_XGMII: loopback within MAC at XGMII level - * @LOOPBACK_XGXS: loopback within MAC at XGXS level - * @LOOPBACK_XAUI: loopback within MAC at XAUI level - * @LOOPBACK_PHY: loopback within PHY unspecified level - * @LOOPBACK_PHYXS: loopback within PHY at PHYXS level - * @LOOPBACK_PCS: loopback within PHY at PCS level - * @LOOPBACK_PMAPMD: loopback within PHY at PMAPMD level - * @LOOPBACK_FAR: loopback furthest from bus - * @LOOPBACK_NETWORK: reflecting loopback (even further than furthest!) + * @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_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_NEAR = 1, - LOOPBACK_MAC = 2, + LOOPBACK_DATA = 1, + LOOPBACK_GMAC = 2, LOOPBACK_XGMII = 3, LOOPBACK_XGXS = 4, LOOPBACK_XAUI = 5, - LOOPBACK_PHY = 6, - LOOPBACK_PHYXS = 7, - LOOPBACK_PCS = 8, - LOOPBACK_PMAPMD = 9, - LOOPBACK_FAR = 10, - LOOPBACK_NETWORK = 11, + 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_FAR +#define LOOPBACK_TEST_MAX LOOPBACK_PMAPMD /* These loopbacks occur within the controller */ -#define LOOPBACKS_10G_INTERNAL ((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_1G_INTERNAL (1 << LOOPBACK_MAC) +#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) -#define LOOPBACK_INTERNAL(_efx) \ - (((LOOPBACKS_10G_INTERNAL | LOOPBACKS_1G_INTERNAL) & \ - LOOPBACK_MASK(_efx)) ? 1 : 0) +#define LOOPBACK_INTERNAL(_efx) \ + (!!(LOOPBACKS_INTERNAL & LOOPBACK_MASK(_efx))) -#define LOOPBACK_CHANGED(_from, _to, _mask) \ - ((LOOPBACK_MASK(_from) ^ LOOPBACK_MASK(_to)) & \ - (_mask) ? 1 : 0) +#define LOOPBACK_EXTERNAL(_efx) \ + (!!(LOOPBACK_MASK(_efx) & LOOPBACKS_EXTERNAL(_efx))) -#define LOOPBACK_OUT_OF(_from, _to, _mask) \ - (((LOOPBACK_MASK(_from) & (_mask)) && \ - ((LOOPBACK_MASK(_to) & (_mask)) == 0)) ? 1 : 0) +#define LOOPBACK_CHANGED(_from, _to, _mask) \ + (!!((LOOPBACK_MASK(_from) ^ LOOPBACK_MASK(_to)) & (_mask))) + +#define LOOPBACK_OUT_OF(_from, _to, _mask) \ + ((LOOPBACK_MASK(_from) & (_mask)) && !(LOOPBACK_MASK(_to) & (_mask))) /*****************************************************************************/ @@ -95,12 +138,13 @@ enum efx_loopback_mode { * @RESET_TYPE_ALL: reset everything but PCI core blocks * @RESET_TYPE_WORLD: reset everything, save & restore PCI config * @RESET_TYPE_DISABLE: disable NIC - * @RESET_TYPE_MONITOR: reset due to hardware monitor + * @RESET_TYPE_TX_WATCHDOG: reset due to TX watchdog * @RESET_TYPE_INT_ERROR: reset due to internal error * @RESET_TYPE_RX_RECOVERY: reset to recover from RX datapath errors * @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, @@ -109,12 +153,13 @@ enum reset_type { RESET_TYPE_WORLD = 2, RESET_TYPE_DISABLE = 3, RESET_TYPE_MAX_METHOD, - RESET_TYPE_MONITOR, + RESET_TYPE_TX_WATCHDOG, RESET_TYPE_INT_ERROR, RESET_TYPE_RX_RECOVERY, RESET_TYPE_RX_DESC_FETCH, RESET_TYPE_TX_DESC_FETCH, RESET_TYPE_TX_SKIP, + RESET_TYPE_MC_FAILURE, RESET_TYPE_MAX, }; diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/ethtool.c --- a/drivers/net/sfc/ethtool.c Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/ethtool.c Fri Jan 08 13:05:49 2010 +0000 @@ -1,41 +1,23 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2006-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> - * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> + * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include <linux/netdevice.h> #include <linux/ethtool.h> #include <linux/rtnetlink.h> #include "net_driver.h" +#include "workarounds.h" #include "selftest.h" #include "efx.h" -#include "ethtool.h" -#include "falcon.h" -#include "gmii.h" - -static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable); +#include "nic.h" +#include "spi.h" +#include "mdio_10g.h" struct ethtool_string { char name[ETH_GSTRING_LEN]; @@ -170,11 +152,24 @@ static struct efx_ethtool_stat efx_ethto 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_eth_crc_err), + EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_mcast_mismatch), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_char_error_lane0), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_char_error_lane1), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_char_error_lane2), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_char_error_lane3), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_disp_error_lane0), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_disp_error_lane1), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_disp_error_lane2), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_disp_error_lane3), + EFX_ETHTOOL_U64_MAC_STAT(rx_match_fault), }; /* Number of ethtool statistics */ #define EFX_ETHTOOL_NUM_STATS ARRAY_SIZE(efx_ethtool_stats) + +#define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB /************************************************************************** * @@ -184,13 +179,19 @@ static struct efx_ethtool_stat efx_ethto */ /* Identify device by flashing LEDs */ -static int efx_ethtool_phys_id(struct net_device *net_dev, u32 seconds) -{ - struct efx_nic *efx = net_dev->priv; - - efx->board_info.blink(efx, 1); - schedule_timeout_interruptible(seconds * HZ); - efx->board_info.blink(efx, 0); +static int efx_ethtool_phys_id(struct net_device *net_dev, u32 count) +{ + struct efx_nic *efx = netdev_priv(net_dev); + + do { + efx->type->set_id_led(efx, EFX_LED_ON); + schedule_timeout_interruptible(HZ / 2); + + efx->type->set_id_led(efx, EFX_LED_OFF); + schedule_timeout_interruptible(HZ / 2); + } while (!signal_pending(current) && --count != 0); + + efx->type->set_id_led(efx, EFX_LED_DEFAULT); return 0; } @@ -198,41 +199,75 @@ int efx_ethtool_get_settings(struct net_ int efx_ethtool_get_settings(struct net_device *net_dev, struct ethtool_cmd *ecmd) { - struct efx_nic *efx = net_dev->priv; - int rc; - - if (!in_interrupt()) - mutex_lock(&efx->mac_lock); - rc = efx->mac_op->get_settings(efx, ecmd); - if (!in_interrupt()) - mutex_unlock(&efx->mac_lock); - - return rc; + struct efx_nic *efx = netdev_priv(net_dev); + struct efx_link_state *link_state = &efx->link_state; + +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_BONDING_HACKS) + if (in_interrupt()) { + memset(ecmd, 0, sizeof(*ecmd)); + ecmd->speed = link_state->speed; + ecmd->duplex = link_state->fd ? DUPLEX_FULL : DUPLEX_HALF; + return 0; + } +#endif + + mutex_lock(&efx->mac_lock); + efx->phy_op->get_settings(efx, ecmd); + mutex_unlock(&efx->mac_lock); + + /* 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; } /* This must be called with rtnl_lock held. */ int efx_ethtool_set_settings(struct net_device *net_dev, struct ethtool_cmd *ecmd) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); int rc; + /* Older versions of ethtool don't set all the right bits when + * turning autoneg on with no speed/duplex specified. But they + * always set more than one bit in this case, so test for that. + * Allow overriding this in ethtool 6 by setting + * ADVERTISED_Autoneg = 0x40. + */ + if (ecmd->advertising & (ecmd->advertising - 1) && + !(ecmd->advertising & ADVERTISED_Autoneg)) + ecmd->advertising = ecmd->supported; + + /* GMAC does not support 1000Mbps HD */ + if (ecmd->speed == SPEED_1000 && ecmd->duplex != DUPLEX_FULL) { + EFX_LOG(efx, "rejecting unsupported 1000Mbps HD" + " setting\n"); + return -EINVAL; + } + mutex_lock(&efx->mac_lock); - rc = efx->mac_op->set_settings(efx, ecmd); + rc = efx->phy_op->set_settings(efx, ecmd); mutex_unlock(&efx->mac_lock); - if (!rc) - efx_reconfigure_port(efx); - return rc; } static void efx_ethtool_get_drvinfo(struct net_device *net_dev, struct ethtool_drvinfo *info) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(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)); } @@ -242,10 +277,10 @@ static void efx_ethtool_get_drvinfo(stru * @strings: Ethtool strings, or %NULL * @data: Ethtool test results, or %NULL * @test: Pointer to test result (used only if data != %NULL) - * @unit_format: Unit name format (e.g. "channel\%d") - * @unit_id: Unit id (e.g. 0 for "channel0") + * @unit_format: Unit name format (e.g. "chan\%d") + * @unit_id: Unit id (e.g. 0 for "chan0") * @test_format: Test name format (e.g. "loopback.\%s.tx.sent") - * @test_id: Test id (e.g. "PHY" for "loopback.PHY.tx_sent") + * @test_id: Test id (e.g. "PHYXS" for "loopback.PHYXS.tx_sent") * * Fill in an individual self-test entry. */ @@ -262,17 +297,21 @@ static void efx_fill_test(unsigned int t /* Fill string, if applicable */ if (strings) { - snprintf(unit_str.name, sizeof(unit_str.name), - unit_format, unit_id); + if (strchr(unit_format, '%')) + snprintf(unit_str.name, sizeof(unit_str.name), + unit_format, unit_id); + else + strcpy(unit_str.name, unit_format); snprintf(test_str.name, sizeof(test_str.name), test_format, test_id); snprintf(strings[test_index].name, sizeof(strings[test_index].name), - "%-9s%-17s", unit_str.name, test_str.name); - } -} - -#define EFX_PORT_NAME "port%d", 0 + "%-6s %-24s", unit_str.name, test_str.name); + } +} + +#define EFX_LOOPBACK_NAME(_mode, _counter) \ + "loopback.%s." _counter, STRING_TABLE_LOOKUP(_mode, efx_loopback_mode) /** * efx_fill_loopback_test - fill in a block of loopback self-test entries @@ -298,24 +337,20 @@ static int efx_fill_loopback_test(struct efx_fill_test(test_index++, strings, data, &lb_tests->tx_sent[tx_queue->queue], EFX_TX_QUEUE_NAME(tx_queue), - "loopback.%s.tx_sent", - efx_loopback_mode_names[mode]); + EFX_LOOPBACK_NAME(mode, "tx_sent")); efx_fill_test(test_index++, strings, data, &lb_tests->tx_done[tx_queue->queue], EFX_TX_QUEUE_NAME(tx_queue), - "loopback.%s.tx_done", - efx_loopback_mode_names[mode]); + EFX_LOOPBACK_NAME(mode, "tx_done")); } efx_fill_test(test_index++, strings, data, &lb_tests->rx_good, - EFX_PORT_NAME, - "loopback.%s.rx_good", - efx_loopback_mode_names[mode]); + "rx", 0, + EFX_LOOPBACK_NAME(mode, "rx_good")); efx_fill_test(test_index++, strings, data, &lb_tests->rx_bad, - EFX_PORT_NAME, - "loopback.%s.rx_bad", - efx_loopback_mode_names[mode]); + "rx", 0, + EFX_LOOPBACK_NAME(mode, "rx_bad")); return test_index; } @@ -339,10 +374,13 @@ static int efx_ethtool_fill_self_tests(s u64 *data) { struct efx_channel *channel; - unsigned int n = 0; + unsigned int n = 0, i; enum efx_loopback_mode mode; - /* Interrupt */ + efx_fill_test(n++, strings, data, &tests->phy_alive, + "phy", 0, "alive", NULL); + efx_fill_test(n++, strings, data, &tests->nvram, + "core", 0, "nvram", NULL); efx_fill_test(n++, strings, data, &tests->interrupt, "core", 0, "interrupt", NULL); @@ -362,16 +400,29 @@ static int efx_ethtool_fill_self_tests(s "eventq.poll", NULL); } - /* PHY presence */ - efx_fill_test(n++, strings, data, &tests->phy_ok, - EFX_PORT_NAME, "phy_ok", NULL); + efx_fill_test(n++, strings, data, &tests->memory, + "core", 0, "memory", NULL); + efx_fill_test(n++, strings, data, &tests->registers, + "core", 0, "registers", 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_ext[i], + "phy", 0, name, NULL); + } + } /* Loopback tests */ - efx_fill_test(n++, strings, data, &tests->loopback_speed, - EFX_PORT_NAME, "loopback.speed", NULL); - efx_fill_test(n++, strings, data, &tests->loopback_full_duplex, - EFX_PORT_NAME, "loopback.full_duplex", NULL); - for (mode = LOOPBACK_NONE; mode < LOOPBACK_TEST_MAX; mode++) { + for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) { if (!(efx->loopback_modes & (1 << mode))) continue; n = efx_fill_loopback_test(efx, @@ -382,22 +433,35 @@ static int efx_ethtool_fill_self_tests(s return n; } +static int efx_ethtool_get_sset_count(struct net_device *net_dev, + int string_set) +{ + switch (string_set) { + case ETH_SS_STATS: + return EFX_ETHTOOL_NUM_STATS; + case ETH_SS_TEST: + return efx_ethtool_fill_self_tests(netdev_priv(net_dev), + NULL, NULL, NULL); + default: + return -EINVAL; + } +} + +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_ETHTOOL_GET_SSET_COUNT) static int efx_ethtool_get_stats_count(struct net_device *net_dev) { - return EFX_ETHTOOL_NUM_STATS; -} - + return efx_ethtool_get_sset_count(net_dev, ETH_SS_STATS); +} static int efx_ethtool_self_test_count(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; - - return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL); -} + return efx_ethtool_get_sset_count(net_dev, ETH_SS_TEST); +} +#endif static void efx_ethtool_get_strings(struct net_device *net_dev, u32 string_set, u8 *strings) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct ethtool_string *ethtool_strings = (struct ethtool_string *)strings; int i; @@ -423,7 +487,7 @@ static void efx_ethtool_get_stats(struct struct ethtool_stats *stats __attribute__ ((unused)), u64 *data) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_mac_stats *mac_stats = &efx->mac_stats; struct efx_ethtool_stat *stat; struct efx_channel *channel; @@ -432,7 +496,7 @@ static void efx_ethtool_get_stats(struct EFX_BUG_ON_PARANOID(stats->n_stats != EFX_ETHTOOL_NUM_STATS); /* Update MAC and NIC statistics */ - net_dev->get_stats(net_dev); + dev_get_stats(net_dev); /* Fill detailed statistics buffer */ for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) { @@ -455,35 +519,53 @@ static void efx_ethtool_get_stats(struct } } +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 !defined(EFX_USE_KCOMPAT) || defined(NETIF_F_TSO6) + if (efx->type->offload_features & NETIF_F_V6_CSUM) + features |= NETIF_F_TSO6; +#endif + + 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 = net_dev->priv; - int rc; - - rc = ethtool_op_set_tx_csum(net_dev, enable); - if (rc) - return rc; - - efx_flush_queues(efx); + 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 = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); /* No way to stop the hardware doing the checks; we just * ignore the result. */ - efx->rx_checksum_enabled = (enable ? 1 : 0); + efx->rx_checksum_enabled = !!enable; return 0; } static u32 efx_ethtool_get_rx_csum(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); return efx->rx_checksum_enabled; } @@ -491,71 +573,48 @@ static void efx_ethtool_self_test(struct static void efx_ethtool_self_test(struct net_device *net_dev, struct ethtool_test *test, u64 *data) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_self_tests efx_tests; - int offline, already_up; + bool already_up; int rc; - /* Make sure we've got rtnl lock since we're playing with - * interrupts, and calling efx_process_channel_now and others - */ ASSERT_RTNL(); - - /* If the NIC isn't in the RUNNING state then exit */ if (efx->state != STATE_RUNNING) { - rc = -EIO; - goto fail1; - } - - /* Make sure the interface is up. We need interrupts, NAPI - * and some RX buffers so this is helpful. NB. The caller has - * rtnl_lock so nobody else can call dev_open. */ + rc = -EBUSY; + goto out; + } + + if (test->flags & ETH_TEST_FL_OFFLINE) + efx_dl_reset_suspend(efx); + + /* We need rx buffers and interrupts. */ already_up = (efx->net_dev->flags & IFF_UP); if (!already_up) { rc = dev_open(efx->net_dev); if (rc) { EFX_ERR(efx, "failed opening device.\n"); - goto fail2; + goto out_resume; } } memset(&efx_tests, 0, sizeof(efx_tests)); - offline = (test->flags & ETH_TEST_FL_OFFLINE); - - /* Perform online self tests first */ - rc = efx_online_test(efx, &efx_tests); - if (rc) - goto out; - - /* Perform offline tests only if online tests passed */ - if (offline) { - /* Stop the kernel from sending packets during the test. The - * selftest will be consistently bringing the port up and down - * as it moves between loopback modes, so the watchdog timer - * probably won't run anyway */ - efx_stop_queue(efx); - - rc = efx_flush_queues(efx); - if (rc != 0) - goto out_offline; - - rc = efx_offline_test(efx, &efx_tests, - efx->loopback_modes); - out_offline: - efx_wake_queue(efx); - } - - /* fall-thru */ - out: + + rc = efx_selftest(efx, &efx_tests, test->flags); + if (!already_up) dev_close(efx->net_dev); - EFX_LOG(efx, "%s all %sline self-tests\n", - rc == 0 ? "passed" : "failed", offline ? "off" : "on"); - - fail2: - fail1: - /* Fill ethtool results structures */ + EFX_LOG(efx, "%s %sline self-tests\n", + rc == 0 ? "passed" : "failed", + (test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on"); + +out_resume: + if (test->flags & ETH_TEST_FL_OFFLINE) { + /* Return success because if efx_reset_up() failed, an + * EFX_RESET_DISABLE reset will have been scheduled */ + efx_dl_reset_resume(efx, true); + } +out: efx_ethtool_fill_self_tests(efx, &efx_tests, NULL, data); if (rc) test->flags |= ETH_TEST_FL_FAILED; @@ -564,24 +623,78 @@ static void efx_ethtool_self_test(struct /* Restart autonegotiation */ static int efx_ethtool_nway_reset(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; - - return mii_nway_restart(&efx->mii); + struct efx_nic *efx = netdev_priv(net_dev); + + return mdio45_nway_restart(&efx->mdio); } static u32 efx_ethtool_get_link(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; - - return efx->link_up; + struct efx_nic *efx = netdev_priv(net_dev); + + return efx->link_state.up; +} + +static int efx_ethtool_get_eeprom_len(struct net_device *net_dev) +{ + struct efx_nic *efx = netdev_priv(net_dev); + struct efx_spi_device *spi = efx->spi_eeprom; + + if (!spi) + return 0; + return min(spi->size, EFX_EEPROM_BOOTCONFIG_END) - + min(spi->size, EFX_EEPROM_BOOTCONFIG_START); +} + +static int efx_ethtool_get_eeprom(struct net_device *net_dev, + struct ethtool_eeprom *eeprom, u8 *buf) +{ + struct efx_nic *efx = netdev_priv(net_dev); + struct efx_spi_device *spi = efx->spi_eeprom; + size_t len; + int rc; + + rc = mutex_lock_interruptible(&efx->spi_lock); + if (rc) + return rc; + rc = falcon_spi_read(efx, spi, + eeprom->offset + EFX_EEPROM_BOOTCONFIG_START, + eeprom->len, &len, buf); + mutex_unlock(&efx->spi_lock); + + eeprom->magic = EFX_ETHTOOL_EEPROM_MAGIC; + eeprom->len = len; + return rc; +} + +static int efx_ethtool_set_eeprom(struct net_device *net_dev, + struct ethtool_eeprom *eeprom, u8 *buf) +{ + struct efx_nic *efx = netdev_priv(net_dev); + struct efx_spi_device *spi = efx->spi_eeprom; + size_t len; + int rc; + + if (eeprom->magic != EFX_ETHTOOL_EEPROM_MAGIC) + return -EINVAL; + + rc = mutex_lock_interruptible(&efx->spi_lock); + if (rc) + return rc; + rc = falcon_spi_write(efx, spi, + eeprom->offset + EFX_EEPROM_BOOTCONFIG_START, + eeprom->len, &len, buf); + mutex_unlock(&efx->spi_lock); + + eeprom->len = len; + return rc; } static int efx_ethtool_get_coalesce(struct net_device *net_dev, struct ethtool_coalesce *coalesce) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_tx_queue *tx_queue; - struct efx_rx_queue *rx_queue; struct efx_channel *channel; memset(coalesce, 0, sizeof(*coalesce)); @@ -599,14 +712,11 @@ static int efx_ethtool_get_coalesce(stru } } - /* Find lowest IRQ moderation across all used RX queues */ - coalesce->rx_coalesce_usecs_irq = ~((u32) 0); - efx_for_each_rx_queue(rx_queue, efx) { - channel = rx_queue->channel; - if (channel->irq_moderation < coalesce->rx_coalesce_usecs_irq) - coalesce->rx_coalesce_usecs_irq = - channel->irq_moderation; - } + coalesce->use_adaptive_rx_coalesce = efx->irq_rx_adaptive; + coalesce->rx_coalesce_usecs_irq = efx->irq_rx_moderation; + + coalesce->tx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION; + coalesce->rx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION; return 0; } @@ -617,13 +727,12 @@ static int efx_ethtool_set_coalesce(stru static int efx_ethtool_set_coalesce(struct net_device *net_dev, struct ethtool_coalesce *coalesce) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_channel *channel; struct efx_tx_queue *tx_queue; - unsigned tx_usecs, rx_usecs; - - if (coalesce->use_adaptive_rx_coalesce || - coalesce->use_adaptive_tx_coalesce) + unsigned tx_usecs, rx_usecs, adaptive; + + if (coalesce->use_adaptive_tx_coalesce) return -EOPNOTSUPP; if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) { @@ -634,6 +743,7 @@ static int efx_ethtool_set_coalesce(stru rx_usecs = coalesce->rx_coalesce_usecs_irq; tx_usecs = coalesce->tx_coalesce_usecs_irq; + adaptive = coalesce->use_adaptive_rx_coalesce; /* If the channel is shared only allow RX parameters to be set */ efx_for_each_tx_queue(tx_queue, efx) { @@ -645,14 +755,9 @@ static int efx_ethtool_set_coalesce(stru } } - efx_init_irq_moderation(efx, tx_usecs, rx_usecs); - - /* Reset channel to pick up new moderation value. Note that - * this may change the value of the irq_moderation field - * (e.g. to allow for hardware timer granularity). - */ + efx_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive); efx_for_each_channel(channel, efx) - falcon_set_int_moderation(channel); + efx->type->push_irq_moderation(channel); return 0; } @@ -660,22 +765,69 @@ static int efx_ethtool_set_pauseparam(st static int efx_ethtool_set_pauseparam(struct net_device *net_dev, struct ethtool_pauseparam *pause) { - struct efx_nic *efx = net_dev->priv; - enum efx_fc_type flow_control = efx->flow_control; - int rc; - - flow_control &= ~(EFX_FC_RX | EFX_FC_TX | EFX_FC_AUTO); - flow_control |= pause->rx_pause ? EFX_FC_RX : 0; - flow_control |= pause->tx_pause ? EFX_FC_TX : 0; - flow_control |= pause->autoneg ? EFX_FC_AUTO : 0; - - /* Try to push the pause parameters */ + struct efx_nic *efx = netdev_priv(net_dev); + enum efx_fc_type wanted_fc, old_fc; + u32 old_adv; + bool reset; + int rc = 0; + mutex_lock(&efx->mac_lock); - rc = efx->mac_op->set_pause(efx, flow_control); + + wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) | + (pause->tx_pause ? EFX_FC_TX : 0) | + (pause->autoneg ? EFX_FC_AUTO : 0)); + + if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) { + EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n"); + rc = -EINVAL; + goto out; + } + + 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 + * link partner (intermittently) stops responding to pause + * frames. There isn't any indication that this has happened, + * so the best we do is leave it up to the user to spot this + * 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 (efx_nic_rev(efx) == EFX_REV_FALCON_B0) { + /* Recover by resetting the EM block */ + 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); + } + } + + 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; + } + } + + /* 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); - - if (!rc) - efx_reconfigure_port(efx); return rc; } @@ -683,31 +835,81 @@ static void efx_ethtool_get_pauseparam(s static void efx_ethtool_get_pauseparam(struct net_device *net_dev, struct ethtool_pauseparam *pause) { - struct efx_nic *efx = net_dev->priv; - - pause->rx_pause = (efx->flow_control & EFX_FC_RX) ? 1 : 0; - pause->tx_pause = (efx->flow_control & EFX_FC_TX) ? 1 : 0; - pause->autoneg = (efx->flow_control & EFX_FC_AUTO) ? 1 : 0; -} - - + struct efx_nic *efx = netdev_priv(net_dev); + + pause->rx_pause = !!(efx->wanted_fc & EFX_FC_RX); + pause->tx_pause = !!(efx->wanted_fc & EFX_FC_TX); + pause->autoneg = !!(efx->wanted_fc & EFX_FC_AUTO); +} + + +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_ETHTOOL_GET_PERM_ADDR) static int efx_ethtool_op_get_perm_addr(struct net_device *net_dev, struct ethtool_perm_addr *addr, u8 *data) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); memcpy(data, efx->mac_address, ETH_ALEN); return 0; } - -struct ethtool_ops efx_ethtool_ops = { +#endif + + +static void efx_ethtool_get_wol(struct net_device *net_dev, + struct ethtool_wolinfo *wol) +{ + struct efx_nic *efx = netdev_priv(net_dev); + return efx->type->get_wol(efx, wol); +} + + +static int efx_ethtool_set_wol(struct net_device *net_dev, + struct ethtool_wolinfo *wol) +{ + struct efx_nic *efx = netdev_priv(net_dev); + return efx->type->set_wol(efx, wol->wolopts); +} + +extern int efx_ethtool_reset(struct net_device *net_dev, u32 *flags) +{ + struct efx_nic *efx = netdev_priv(net_dev); + enum reset_type method; + enum { + ETH_RESET_EFX_INVISIBLE = (ETH_RESET_DMA | ETH_RESET_FILTER | + ETH_RESET_OFFLOAD | ETH_RESET_MAC) + }; + + /* Check for minimal reset flags */ + if ((*flags & ETH_RESET_EFX_INVISIBLE) != ETH_RESET_EFX_INVISIBLE) + return -EINVAL; + *flags ^= ETH_RESET_EFX_INVISIBLE; + method = RESET_TYPE_INVISIBLE; + + if (*flags & ETH_RESET_PHY) { + *flags ^= ETH_RESET_PHY; + method = RESET_TYPE_ALL; + } + + if ((*flags & efx->type->reset_world_flags) == + efx->type->reset_world_flags) { + *flags ^= efx->type->reset_world_flags; + method = RESET_TYPE_WORLD; + } + + return efx_reset(efx, method); +} + +const struct ethtool_ops efx_ethtool_ops = { .get_settings = efx_ethtool_get_settings, .set_settings = efx_ethtool_set_settings, .get_drvinfo = efx_ethtool_get_drvinfo, .nway_reset = efx_ethtool_nway_reset, .get_link = efx_ethtool_get_link, + .get_eeprom_len = efx_ethtool_get_eeprom_len, + .get_eeprom = efx_ethtool_get_eeprom, + .set_eeprom = efx_ethtool_set_eeprom, .get_coalesce = efx_ethtool_get_coalesce, .set_coalesce = efx_ethtool_set_coalesce, .get_pauseparam = efx_ethtool_get_pauseparam, @@ -715,14 +917,33 @@ 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, + /* 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, + /* Need to enable/disable TSO-IPv6 too */ + .set_tso = efx_ethtool_set_tso, +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_ETHTOOL_FLAGS) + .get_flags = ethtool_op_get_flags, + .set_flags = ethtool_op_set_flags, +#endif +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_ETHTOOL_GET_SSET_COUNT) + .get_sset_count = efx_ethtool_get_sset_count, +#else .self_test_count = efx_ethtool_self_test_count, + .get_stats_count = efx_ethtool_get_stats_count, +#endif .self_test = efx_ethtool_self_test, .get_strings = efx_ethtool_get_strings, .phys_id = efx_ethtool_phys_id, - .get_stats_count = efx_ethtool_get_stats_count, .get_ethtool_stats = efx_ethtool_get_stats, +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_ETHTOOL_GET_PERM_ADDR) .get_perm_addr = efx_ethtool_op_get_perm_addr, +#endif + .get_wol = efx_ethtool_get_wol, + .set_wol = efx_ethtool_set_wol, +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_HAVE_ETHTOOL_RESET) + .reset = efx_ethtool_reset, +#endif }; diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/ethtool.h --- a/drivers/net/sfc/ethtool.h Fri Jan 08 11:56:04 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005: Fen Systems Ltd. - * Copyright 2006: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> - * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** - */ - -#ifndef EFX_ETHTOOL_H -#define EFX_ETHTOOL_H - -#include "net_driver.h" - -/* - * Ethtool support - */ - -extern int efx_ethtool_get_settings(struct net_device *net_dev, - struct ethtool_cmd *ecmd); -extern int efx_ethtool_set_settings(struct net_device *net_dev, - struct ethtool_cmd *ecmd); - -extern struct ethtool_ops efx_ethtool_ops; - -#endif /* EFX_ETHTOOL_H */ diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/extraversion.h --- a/drivers/net/sfc/extraversion.h Fri Jan 08 11:56:04 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -/* - * If compiling on kernels with backported features you may need to - * define EFX_DIST_KVER_ symbols here - */ diff -r 896902106793 -r 0b5ca7cdbdfc drivers/net/sfc/falcon.c --- a/drivers/net/sfc/falcon.c Fri Jan 08 11:56:04 2010 +0000 +++ b/drivers/net/sfc/falcon.c Fri Jan 08 13:05:49 2010 +0000 @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2006-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown <mbrown@xxxxxxxxxxxxxxxx> - * Maintained by Solarflare Communications <linux-net-drivers@xxxxxxxxxxxxxx> + * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include <linux/bitops.h> @@ -30,63 +13,22 @@ #include <linux/pci.h> #include <linux/module.h> #include <linux/seq_file.h> +#include <linux/i2c.h> +#include <linux/mii.h> #include "net_driver.h" #include "bitfield.h" #include "efx.h" #include "mac.h" -#include "gmii.h" #include "spi.h" -#include "falcon.h" -#include "falcon_hwdefs.h" -#include "falcon_io.h" +#include "nic.h" +#include "regs.h" +#include "io.h" #include "mdio_10g.h" #include "phy.h" -#include "boards.h" #include "driverlink.h" #include "workarounds.h" -/* Falcon hardware control. - * Falcon is the internal codename for the SFC4000 controller that is - * present in SFE400X evaluation boards - */ - -/** - * struct falcon_nic_data - Falcon NIC state - * @tx_dc_entries: Number of entries in each TX queue descriptor cache - * @rx_dc_entries: Number of entries in each RX queue descriptor cache - * @tx_dc_base: Base address in SRAM of TX queue descriptor caches - * @rx_dc_base: Base address in SRAM of RX queue descriptor caches - * @old_loopback_mode: Previous loopback mode used in deconfigure_mac_wrapper - * @external_sram_cfg: Size and number of banks of external SRAM - * @pci_dev2: The secondary PCI device if present - * @resources: Driverlink parameters - */ -struct falcon_nic_data { - unsigned tx_dc_entries; - unsigned rx_dc_entries; - unsigned tx_dc_base; - unsigned rx_dc_base; - - enum efx_loopback_mode old_loopback_mode; - - struct pci_dev *pci_dev2; - - int external_sram_cfg; - - struct efx_dl_falcon_resources resources; -}; - -/************************************************************************** - * - * Configurable values - * - ************************************************************************** - */ - -static int disable_dma_stats; - -/* Specify the size of the RX descriptor cache */ -static int descriptor_cache_size = 64; +/* Hardware control for SFC4000 (aka Falcon). */ /* * Override EEPROM/flash type from non-volatile configuration or GPIO; @@ -95,152 +37,64 @@ static unsigned int eeprom_type = -1; static unsigned int eeprom_type = -1; static unsigned int flash_type = -1; -/* 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"); - -/* TX descriptor ring size - min 512 max 4k */ -#define FALCON_TXD_RING_ORDER TX_DESCQ_SIZE_1K -#define FALCON_TXD_RING_SIZE 1024 -#define FALCON_TXD_RING_MASK (FALCON_TXD_RING_SIZE - 1) - -/* RX descriptor ring size - min 512 max 4k */ -#define FALCON_RXD_RING_ORDER RX_DESCQ_SIZE_1K -#define FALCON_RXD_RING_SIZE 1024 -#define FALCON_RXD_RING_MASK (FALCON_RXD_RING_SIZE - 1) - -/* Event queue size - max 32k */ -#define FALCON_EVQ_ORDER EVQ_SIZE_4K -#define FALCON_EVQ_SIZE 4096 -#define FALCON_EVQ_MASK (FALCON_EVQ_SIZE - 1) - -/* Max number of internal errors. After this resets will not be performed */ -#define FALCON_MAX_INT_ERRORS 4 - -/* Maximum period that we wait for flush events. If the flush event - * doesn't arrive in this period of time then we check if the queue - * was disabled anyway. */ -#define FALCON_FLUSH_TIMEOUT 10 /* 10ms */ - -/************************************************************************** - * - * Falcon constants - * - ************************************************************************** - */ - -/* DMA address mask (up to 46-bit, avoiding compiler warnings) - * - * Note that it is possible to have a platform with 64-bit longs and - * 32-bit DMA addresses, or vice versa. EFX_DMA_MASK takes care of the - * platform DMA mask. - */ -#if BITS_PER_LONG == 64 -#define FALCON_DMA_MASK EFX_DMA_MASK(0x00003fffffffffffUL) -#else -#define FALCON_DMA_MASK EFX_DMA_MASK(0x00003fffffffffffULL) -#endif - -/* TX DMA length mask (13-bit) */ -#define FALCON_TX_DMA_MASK (4096 - 1) - -/* Size and alignment of special buffers (4KB) */ -#define FALCON_BUF_SIZE 4096 - -/* Dummy SRAM size code */ -#define SRM_NB_BSZ_ONCHIP_ONLY (-1) - -/* Be nice if these (or equiv.) were in linux/pci_regs.h, but they're not. */ -#define PCI_EXP_DEVCAP_PWR_VAL_LBN 18 -#define PCI_EXP_DEVCAP_PWR_SCL_LBN 26 -#define PCI_EXP_DEVCTL_PAYLOAD_LBN 5 -#define PCI_EXP_LNKSTA_LNK_WID 0x3f0 -#define PCI_EXP_LNKSTA_LNK_WID_LBN 4 - -#define FALCON_IS_DUAL_FUNC(efx) \ - (FALCON_REV(efx) < FALCON_REV_B0) - -/************************************************************************** - * - * Falcon hardware access - * - **************************************************************************/ - -/* 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]))); -} +static const unsigned int +/* "Small" EEPROM device: Atmel AT25040 or similar + * 512 B, 9-bit address, 8 B write block */ +small_eeprom_type = ((9 << SPI_DEV_TYPE_SIZE_LBN) + | (1 << SPI_DEV_TYPE_ADDR_LEN_LBN) + | (3 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)), +/* "Large" EEPROM device: Atmel AT25640 or similar + * 8 KB, 16-bit address, 32 B write block */ +large_eeprom_type = ((13 << SPI_DEV_TYPE_SIZE_LBN) + | (2 << SPI_DEV_TYPE_ADDR_LEN_LBN) + | (5 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)), +/* Default flash device: Atmel AT25F1024 + * 128 KB, 24-bit address, 32 KB erase block, 256 B write block */ +default_flash_type = ((17 << SPI_DEV_TYPE_SIZE_LBN) + | (3 << SPI_DEV_TYPE_ADDR_LEN_LBN) + | (0x52 << SPI_DEV_TYPE_ERASE_CMD_LBN) + | (15 << SPI_DEV_TYPE_ERASE_SIZE_LBN) + | (8 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)); + +/* Dummy SRAM configuration code */ +#define SRAM_CONFIG_INTERNAL (-1) /* Read dword from a Falcon PCIE core register */ -static void falcon_pcie_core_read_reg(struct efx_nic *efx, int address, +static void falcon_b0_pcie_core_read_reg(struct efx_nic *efx, int address, efx_dword_t *result) { efx_oword_t temp; - BUG_ON(FALCON_REV(efx) < FALCON_REV_B0); + BUG_ON(efx_nic_rev(efx) != EFX_REV_FALCON_B0); BUG_ON(address & 3 || address < 0); - EFX_POPULATE_OWORD_1(temp, PCIE_CORE_ADDR, address); - - falcon_write(efx, &temp, PCIE_CORE_INDIRECT_REG); - falcon_read(efx, &temp, PCIE_CORE_INDIRECT_REG); + EFX_POPULATE_OWORD_1(temp, FRF_BB_PCIE_CORE_TARGET_REG_ADRS, address); + + efx_writeo(efx, &temp, FR_BB_PCIE_CORE_INDIRECT); + efx_reado(efx, &temp, FR_BB_PCIE_CORE_INDIRECT); /* Extract PCIE_CORE_VALUE without byte-swapping */ - BUILD_BUG_ON(PCIE_CORE_VALUE_LBN != 32 || - PCIE_CORE_VALUE_WIDTH != 32); + BUILD_BUG_ON(FRF_BB_PCIE_CORE_TARGET_DATA_LBN != 32 || + FRF_BB_PCIE_CORE_TARGET_DATA_WIDTH != 32); result->u32[0] = temp.u32[1]; } /* Write dword to a Falcon PCIE core register */ -static void falcon_pcie_core_write_reg(struct efx_nic *efx, int address, +static void falcon_b0_pcie_core_write_reg(struct efx_nic *efx, int address, efx_dword_t value) { efx_oword_t temp; - BUG_ON(FALCON_REV(efx) < FALCON_REV_B0); + BUG_ON(efx_nic_rev(efx) != EFX_REV_FALCON_B0); BUG_ON(address & 0x3 || address < 0); EFX_POPULATE_OWORD_2(temp, - PCIE_CORE_ADDR, address, - PCIE_CORE_RW, 1); + FRF_BB_PCIE_CORE_TARGET_REG_ADRS, address, + FRF_BB_PCIE_CORE_INDIRECT_ACCESS_DIR, 1); /* Fill PCIE_CORE_VALUE without byte-swapping */ - BUILD_BUG_ON(PCIE_CORE_VALUE_LBN != 32 || - PCIE_CORE_VALUE_WIDTH != 32); + BUILD_BUG_ON(FRF_BB_PCIE_CORE_TARGET_DATA_LBN != 32 || + FRF_BB_PCIE_CORE_TARGET_DATA_WIDTH != 32); temp.u32[1] = value.u32[0]; - falcon_write(efx, &temp, PCIE_CORE_INDIRECT_REG); + efx_writeo(efx, &temp, FR_BB_PCIE_CORE_INDIRECT); } /************************************************************************** @@ -251,1257 +105,87 @@ static void falcon_pcie_core_write_reg(s * ************************************************************************** */ -static void falcon_setsdascl(struct efx_i2c_interface *i2c) -{ +static void falcon_setsda(void *data, int state) +{ + struct efx_nic *efx = (struct efx_nic *)data; efx_oword_t reg; - falcon_read(i2c->efx, ®, GPIO_CTL_REG_KER); - EFX_SET_OWORD_FIELD(reg, GPIO0_OEN, (i2c->scl ? 0 : 1)); - EFX_SET_OWORD_FIELD(reg, GPIO3_OEN, (i2c->sda ? 0 : 1)); - falcon_write(i2c->efx, ®, GPIO_CTL_REG_KER); -} - -static int falcon_getsda(struct efx_i2c_interface *i2c) -{ + efx_reado(efx, ®, FR_AB_GPIO_CTL); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO3_OEN, !state); + efx_writeo(efx, ®, FR_AB_GPIO_CTL); +} + +static void falcon_setscl(void *data, int state) +{ + struct efx_nic *efx = (struct efx_nic *)data; efx_oword_t reg; - falcon_read(i2c->efx, ®, GPIO_CTL_REG_KER); - return EFX_OWORD_FIELD(reg, GPIO3_IN); -} - -static int falcon_getscl(struct efx_i2c_interface *i2c) -{ + efx_reado(efx, ®, FR_AB_GPIO_CTL); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO0_OEN, !state); + efx_writeo(efx, ®, FR_AB_GPIO_CTL); +} + +static int falcon_getsda(void *data) +{ + struct efx_nic *efx = (struct efx_nic *)data; efx_oword_t reg; - falcon_read(i2c->efx, ®, GPIO_CTL_REG_KER); - return EFX_DWORD_FIELD(reg, GPIO0_IN); -} - -static struct efx_i2c_bit_operations falcon_i2c_bit_operations = { - .setsda = falcon_setsdascl, - .setscl = falcon_setsdascl, + efx_reado(efx, ®, FR_AB_GPIO_CTL); + return EFX_OWORD_FIELD(reg, FRF_AB_GPIO3_IN); +} + +static int falcon_getscl(void *data) +{ + struct efx_nic *efx = (struct efx_nic *)data; + efx_oword_t reg; + + efx_reado(efx, ®, FR_AB_GPIO_CTL); + return EFX_OWORD_FIELD(reg, FRF_AB_GPIO0_IN); +} + +static struct i2c_algo_bit_data falcon_i2c_bit_operations = { + .setsda = falcon_setsda, + .setscl = falcon_setscl, .getsda = falcon_getsda, .getscl = falcon_getscl, - .udelay = 100, - .mdelay = 10, + .udelay = 5, + /* Wait up to 50 ms for slave to let us pull SCL high */ + .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 int -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_4(buf_desc, - IP_DAT_BUF_SIZE, IP_DAT_BUF_SIZE_4K, - BUF_ADR_REGION, 0, - BUF_ADR_FBUF, (dma_addr >> 12), - BUF_OWNER_ID_FBUF, 0); - falcon_write_sram(efx, &buf_desc, index); - } - - return 0; -} - -/* 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, - BUF_UPD_CMD, 0, - BUF_CLR_CMD, 1, - BUF_CLR_END_ID, end, - BUF_CLR_START_ID, start); - falcon_write(efx, &buf_tbl_upd, BUF_TBL_UPD_REG_KER); -} - -/* - * 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) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - - len = ALIGN(len, FALCON_BUF_SIZE); - - /* Allocate buffer as consistent PCI DMA space */ - 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 = nic_data->resources.buffer_table_min; - nic_data->resources.buffer_table_min += buffer->entries; - - EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x " - "(virt %p phys %lx)\n", buffer->index, - buffer->index + buffer->entries - 1, - (unsigned long long)buffer->dma_addr, len, - buffer->addr, virt_to_phys(buffer->addr)); - - return 0; -} - -/* Release the buffer memory. */ -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 %lx)\n", buffer->index, - buffer->index + buffer->entries - 1, - (unsigned long long)buffer->dma_addr, buffer->len, - buffer->addr, 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); -} - -/* Update TX descriptor write pointer - * This writes to the TX_DESC_WPTR register for the specified - * channel's transmit 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 & FALCON_TXD_RING_MASK; - EFX_POPULATE_DWORD_1(reg, TX_DESC_WPTR_DWORD, write_ptr); - falcon_writel_page(tx_queue->efx, ®, - TX_DESC_UPD_REG_KER_DWORD, 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 fastcall 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 & FALCON_TXD_RING_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_5(*txd, - TX_KER_PORT, 0, - TX_KER_CONT, buffer->continuation, - TX_KER_BYTE_CNT, buffer->len, - TX_KER_BUF_REGION, 0, - TX_KER_BUF_ADR, 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; - struct falcon_nic_data *nic_data = efx->nic_data; - int rc; - - rc = falcon_alloc_special_buffer(efx, &tx_queue->txd, - FALCON_TXD_RING_SIZE * - sizeof(efx_qword_t)); - if (rc) - return rc; - - nic_data->resources.txq_min = max(nic_data->resources.txq_min, - (unsigned)tx_queue->queue + 1); - - return 0; -} - -/* Prepare channel's TX datapath. */ -int falcon_init_tx(struct efx_tx_queue *tx_queue) -{ - efx_oword_t tx_desc_ptr; - struct efx_nic *efx = tx_queue->efx; - int rc; - - /* Pin TX descriptor ring */ - rc = falcon_init_special_buffer(efx, &tx_queue->txd); - if (rc) - return rc; - - /* Push TX descriptor ring to card */ - EFX_POPULATE_OWORD_10(tx_desc_ptr, - TX_DESCQ_EN, 1, - TX_ISCSI_DDIG_EN, 0, - TX_ISCSI_HDIG_EN, 0, - TX_DESCQ_BUF_BASE_ID, tx_queue->txd.index, - TX_DESCQ_EVQ_ID, tx_queue->channel->evqnum, - TX_DESCQ_OWNER_ID, 0, - TX_DESCQ_LABEL, tx_queue->queue, - TX_DESCQ_SIZE, FALCON_TXD_RING_ORDER, - TX_DESCQ_TYPE, 0, /* kernel queue */ - TX_NON_IP_DROP_DIS_B0, 1); - - if (FALCON_REV(efx) >= FALCON_REV_B0) { - int csum = !(efx->net_dev->features & NETIF_F_IP_CSUM); - EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_IP_CHKSM_DIS_B0, csum); - EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_TCP_CHKSM_DIS_B0, csum); - } - - falcon_write_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 */ - BUG_ON(tx_queue->queue >= 128); - - falcon_read(efx, ®, TX_CHKSM_CFG_REG_KER_A1); - if (efx->net_dev->features & NETIF_F_IP_CSUM) - clear_bit_le(tx_queue->queue, (void *)®); - else - set_bit_le(tx_queue->queue, (void *)®); - falcon_write(efx, ®, TX_CHKSM_CFG_REG_KER_A1); - } - - return 0; -} - -static int falcon_flush_tx_queue(struct efx_tx_queue *tx_queue) -{ - struct efx_nic *efx = tx_queue->efx; - struct efx_channel *channel = &efx->channel[0]; - efx_oword_t tx_flush_descq; - unsigned int read_ptr, i; - - /* Post a flush command */ - EFX_POPULATE_OWORD_2(tx_flush_descq, - TX_FLUSH_DESCQ_CMD, 1, - TX_FLUSH_DESCQ, tx_queue->queue); - falcon_write(efx, &tx_flush_descq, TX_FLUSH_DESCQ_REG_KER); - msleep(FALCON_FLUSH_TIMEOUT); - - if (EFX_WORKAROUND_7803(efx)) - return 0; - - /* Look for a flush completed event */ - read_ptr = channel->eventq_read_ptr; - for (i = 0; i < FALCON_EVQ_SIZE; ++i) { - efx_qword_t *event = falcon_event(channel, read_ptr); - int ev_code, ev_sub_code, ev_queue; - if (!falcon_event_present(event)) - break; - - ev_code = EFX_QWORD_FIELD(*event, EV_CODE); - ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE); - ev_queue = EFX_QWORD_FIELD(*event, DRIVER_EV_TX_DESCQ_ID); - if ((ev_sub_code == TX_DESCQ_FLS_DONE_EV_DECODE) && - (ev_queue == tx_queue->queue)) { - EFX_LOG(efx, "tx queue %d flush command succesful\n", - tx_queue->queue); - return 0; - } - - read_ptr = (read_ptr + 1) & FALCON_EVQ_MASK; - } - - if (EFX_WORKAROUND_11557(efx)) { - efx_oword_t reg; - int enabled; - - falcon_read_table(efx, ®, efx->type->txd_ptr_tbl_base, - tx_queue->queue); - enabled = EFX_OWORD_FIELD(reg, TX_DESCQ_EN); - if (!enabled) { - EFX_LOG(efx, "tx queue %d disabled without a " - "flush event seen\n", tx_queue->queue); - return 0; - } - } - - EFX_ERR(efx, "tx queue %d flush command timed out\n", tx_queue->queue); - return -ETIMEDOUT; -} - -void falcon_fini_tx(struct efx_tx_queue *tx_queue) -{ - struct efx_nic *efx = tx_queue->efx; - efx_oword_t tx_desc_ptr; - - /* Stop the hardware using the queue */ - if (falcon_flush_tx_queue(tx_queue)) - EFX_ERR(efx, "failed to flush tx queue %d\n", tx_queue->queue); - - /* Remove TX descriptor ring from card */ - EFX_ZERO_OWORD(tx_desc_ptr); - falcon_write_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 transmit 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 corresponding to - * the receive buffer. - */ -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, - RX_KER_BUF_SIZE, - rx_buf->len - - rx_queue->efx->type->rx_buffer_padding, - RX_KER_BUF_REGION, 0, - RX_KER_BUF_ADR, rx_buf->dma_addr); -} - -/* This writes to the RX_DESC_WPTR register for the specified receive - * descriptor ring. - */ -void fastcall 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 & - FALCON_RXD_RING_MASK); - ++rx_queue->notified_count; - } - - wmb(); - write_ptr = rx_queue->added_count & FALCON_RXD_RING_MASK; - EFX_POPULATE_DWORD_1(reg, RX_DESC_WPTR_DWORD, write_ptr); - falcon_writel_page(rx_queue->efx, ®, - RX_DESC_UPD_REG_KER_DWORD, rx_queue->queue); -} - -int falcon_probe_rx(struct efx_rx_queue *rx_queue) -{ - struct efx_nic *efx = rx_queue->efx; - struct falcon_nic_data *nic_data = efx->nic_data; - int rc; - - rc = falcon_alloc_special_buffer(efx, &rx_queue->rxd, - FALCON_RXD_RING_SIZE * - sizeof(efx_qword_t)); - if (rc) - return rc; - - /* Increment the rxq_min counter */ - nic_data->resources.rxq_min = max(nic_data->resources.rxq_min, - (unsigned)rx_queue->queue + 1); - - return 0; -} - -int falcon_init_rx(struct efx_rx_queue *rx_queue) -{ - efx_oword_t rx_desc_ptr; - struct efx_nic *efx = rx_queue->efx; - int rc; - int is_b0 = FALCON_REV(efx) >= FALCON_REV_B0; - int 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); - - /* Pin RX descriptor ring */ - rc = falcon_init_special_buffer(efx, &rx_queue->rxd); - if (rc) - return rc; - - /* Push RX descriptor ring to card */ - EFX_POPULATE_OWORD_10(rx_desc_ptr, - RX_ISCSI_DDIG_EN, iscsi_digest_en, - RX_ISCSI_HDIG_EN, iscsi_digest_en, - RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index, - RX_DESCQ_EVQ_ID, rx_queue->channel->evqnum, - RX_DESCQ_OWNER_ID, 0, - RX_DESCQ_LABEL, rx_queue->queue, - RX_DESCQ_SIZE, FALCON_RXD_RING_ORDER, - RX_DESCQ_TYPE, 0 /* kernel queue */ , - /* For >=B0 this is scatter so disable */ - RX_DESCQ_JUMBO, !is_b0, - RX_DESCQ_EN, 1); - falcon_write_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, - rx_queue->queue); - return 0; -} - -static int falcon_flush_rx_queue(struct efx_rx_queue *rx_queue) -{ - struct efx_nic *efx = rx_queue->efx; - struct efx_channel *channel = &efx->channel[0]; - unsigned int read_ptr, i; - efx_oword_t rx_flush_descq; - - /* Post a flush command */ - EFX_POPULATE_OWORD_2(rx_flush_descq, - RX_FLUSH_DESCQ_CMD, 1, - RX_FLUSH_DESCQ, rx_queue->queue); - - falcon_write(efx, &rx_flush_descq, RX_FLUSH_DESCQ_REG_KER); - msleep(FALCON_FLUSH_TIMEOUT); - - if (EFX_WORKAROUND_7803(efx)) - return 0; - - /* Look for a flush completed event */ - read_ptr = channel->eventq_read_ptr; - for (i = 0; i < FALCON_EVQ_SIZE; ++i) { - efx_qword_t *event = falcon_event(channel, read_ptr); - int ev_code, ev_sub_code, ev_queue, ev_failed; - if (!falcon_event_present(event)) - break; - - ev_code = EFX_QWORD_FIELD(*event, EV_CODE); - ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE); - ev_queue = EFX_QWORD_FIELD(*event, DRIVER_EV_RX_DESCQ_ID); - ev_failed = EFX_QWORD_FIELD(*event, DRIVER_EV_RX_FLUSH_FAIL); - - if ((ev_sub_code == RX_DESCQ_FLS_DONE_EV_DECODE) && - (ev_queue == rx_queue->queue)) { - if (ev_failed) { - EFX_INFO(efx, "rx queue %d flush command " - "failed\n", rx_queue->queue); - return -EAGAIN; - } else { - EFX_LOG(efx, "rx queue %d flush command " - "succesful\n", rx_queue->queue); - return 0; - } - } - - read_ptr = (read_ptr + 1) & FALCON_EVQ_MASK; - } - - if (EFX_WORKAROUND_11557(efx)) { - efx_oword_t reg; - int enabled; - - falcon_read_table(efx, ®, efx->type->rxd_ptr_tbl_base, - rx_queue->queue); - enabled = EFX_OWORD_FIELD(reg, RX_DESCQ_EN); - if (!enabled) { - EFX_LOG(efx, "rx queue %d disabled without a " - "flush event seen\n", rx_queue->queue); - return 0; - } - } - - EFX_ERR(efx, "rx queue %d flush command timed out\n", rx_queue->queue); - return -ETIMEDOUT; -} - -void falcon_fini_rx(struct efx_rx_queue *rx_queue) -{ - efx_oword_t rx_desc_ptr; - struct efx_nic *efx = rx_queue->efx; - int i, rc; - - /* Try and flush the rx queue. This may need to be repeated */ - for (i = 0; i < 5; i++) { - rc = falcon_flush_rx_queue(rx_queue); - if (rc == -EAGAIN) - continue; - break; - } - if (rc) { - EFX_ERR(efx, "failed to flush rx queue %d\n", rx_queue->queue); - efx_schedule_reset(efx, RESET_TYPE_INVISIBLE); - } - - /* Remove RX descriptor ring from card */ - EFX_ZERO_OWORD(rx_desc_ptr); - falcon_write_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 fastcall falcon_eventq_read_ack(struct efx_channel *channel) -{ - efx_dword_t reg; - struct efx_nic *efx = channel->efx; - - EFX_POPULATE_DWORD_1(reg, EVQ_RPTR_DWORD, channel->eventq_read_ptr); - falcon_writel_table(efx, ®, efx->type->evq_rptr_tbl_base, - channel->evqnum); -} - -/* 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; - - EFX_POPULATE_OWORD_2(drv_ev_reg, - DRV_EV_QID, channel->evqnum, - DRV_EV_DATA, - EFX_QWORD_FIELD64(*event, WHOLE_EVENT)); - falcon_write(channel->efx, &drv_ev_reg, DRV_EV_REG_KER); -} - -/* 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 inline 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, TX_EV_COMP))) { - /* Transmit completion */ - tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, TX_EV_DESC_PTR); - tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL); - tx_queue = &efx->tx_queue[tx_ev_q_label]; - efx_xmit_done(tx_queue, tx_ev_desc_ptr); - } else if (EFX_QWORD_FIELD(*event, TX_EV_WQ_FF_FULL)) { - /* Rewrite the FIFO write pointer */ - tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL); - tx_queue = &efx->tx_queue[tx_ev_q_label]; - - if (NET_DEV_REGISTERED(efx)) - netif_tx_lock(efx->net_dev); - falcon_notify_tx_desc(tx_queue); - if (NET_DEV_REGISTERED(efx)) - netif_tx_unlock(efx->net_dev); - } else if (EFX_QWORD_FIELD(*event, 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)); - } -} - -/* Check received packet's destination MAC address. */ -static int check_dest_mac(struct efx_rx_queue *rx_queue, - const efx_qword_t *event) -{ - struct efx_rx_buffer *rx_buf; - struct efx_nic *efx = rx_queue->efx; - int rx_ev_desc_ptr; - struct ethhdr *eh; - - if (efx->promiscuous) - return 1; - - rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, RX_EV_DESC_PTR); - rx_buf = efx_rx_buffer(rx_queue, rx_ev_desc_ptr); - eh = (struct ethhdr *)rx_buf->data; - if (memcmp(eh->h_dest, efx->net_dev->dev_addr, ETH_ALEN)) - return 0; - return 1; -} - -/* 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, - unsigned *rx_ev_pkt_ok, - int *discard, int byte_count) -{ - struct efx_nic *efx = rx_queue->efx; - unsigned rx_ev_buf_owner_id_err, rx_ev_ip_hdr_chksum_err; - unsigned rx_ev_tcp_udp_chksum_err, rx_ev_eth_crc_err; - unsigned rx_ev_frm_trunc, rx_ev_drib_nib, rx_ev_tobe_disc; - unsigned rx_ev_pkt_type, rx_ev_other_err, rx_ev_pause_frm; - unsigned rx_ev_ip_frag_err, rx_ev_hdr_type, rx_ev_mcast_pkt; - int snap, non_ip; - - rx_ev_hdr_type = EFX_QWORD_FIELD(*event, RX_EV_HDR_TYPE); - rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, RX_EV_MCAST_PKT); - rx_ev_tobe_disc = EFX_QWORD_FIELD(*event, RX_EV_TOBE_DISC); - rx_ev_pkt_type = EFX_QWORD_FIELD(*event, RX_EV_PKT_TYPE); - rx_ev_buf_owner_id_err = EFX_QWORD_FIELD(*event, - RX_EV_BUF_OWNER_ID_ERR); - rx_ev_ip_frag_err = EFX_QWORD_FIELD(*event, RX_EV_IF_FRAG_ERR); - rx_ev_ip_hdr_chksum_err = EFX_QWORD_FIELD(*event, - RX_EV_IP_HDR_CHKSUM_ERR); - rx_ev_tcp_udp_chksum_err = EFX_QWORD_FIELD(*event, - RX_EV_TCP_UDP_CHKSUM_ERR); - rx_ev_eth_crc_err = EFX_QWORD_FIELD(*event, RX_EV_ETH_CRC_ERR); - rx_ev_frm_trunc = EFX_QWORD_FIELD(*event, RX_EV_FRM_TRUNC); - rx_ev_drib_nib = ((FALCON_REV(efx) >= FALCON_REV_B0) ? - 0 : EFX_QWORD_FIELD(*event, RX_EV_DRIB_NIB)); - rx_ev_pause_frm = EFX_QWORD_FIELD(*event, 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); - - snap = (rx_ev_pkt_type == RX_EV_PKT_TYPE_LLC_DECODE) || - (rx_ev_pkt_type == RX_EV_PKT_TYPE_VLAN_LLC_DECODE); - non_ip = (rx_ev_hdr_type == RX_EV_HDR_TYPE_NON_IP_DECODE); - - /* SFC bug 5475/8970: The Falcon XMAC incorrectly calculates the - * length field of an LLC frame, which sets TOBE_DISC. We could set - * PASS_LEN_ERR, but we want the MAC to filter out short frames (to - * protect the RX block). - * - * bug5475 - LLC/SNAP: Falcon identifies SNAP packets. - * bug8970 - LLC/noSNAP: Falcon does not provide an LLC flag. - * LLC can't encapsulate IP, so by definition - * these packets are NON_IP. - * - * Unicast mismatch will also cause TOBE_DISC, so the driver needs - * to check this. - */ - if (EFX_WORKAROUND_5475(efx) && rx_ev_tobe_disc && (snap || non_ip)) { - /* If all the other flags are zero then we can state the - * entire packet is ok, which will flag to the kernel not - * to recalculate checksums. - */ - if (!(non_ip | rx_ev_other_err | rx_ev_pause_frm)) - *rx_ev_pkt_ok = 1; - - rx_ev_tobe_disc = 0; - - /* TOBE_DISC is set for unicast mismatch. But given that - * we can't trust TOBE_DISC here, we must validate the dest - * MAC address ourselves. - */ - if (!rx_ev_mcast_pkt && !check_dest_mac(rx_queue, event)) - rx_ev_tobe_disc = 1; - } - - /* Count errors that are not in MAC stats. */ - 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 (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%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]" : "", - snap ? " [SNAP/LLC]" : ""); - } -#endif - - if (unlikely(rx_ev_eth_crc_err && EFX_WORKAROUND_10750(efx) && - efx->phy_type == PHY_TYPE_10XPRESS)) - tenxpress_crc_err(efx); -} - - -/* 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 & FALCON_RXD_RING_MASK; - dropped = ((index + FALCON_RXD_RING_SIZE - expected) & - FALCON_RXD_RING_MASK); - EFX_INFO(efx, "dropped %d events (index=%d expected=%d)\n", - dropped, index, expected); - - atomic_inc(&efx->errors.missing_event); - 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 inline int falcon_handle_rx_event(struct efx_channel *channel, - const efx_qword_t *event) -{ - unsigned int rx_ev_q_label, rx_ev_desc_ptr, rx_ev_byte_cnt; - unsigned int rx_ev_pkt_ok, rx_ev_hdr_type, rx_ev_mcast_pkt; - unsigned expected_ptr; - int discard = 0, checksummed; - struct efx_rx_queue *rx_queue; - struct efx_nic *efx = channel->efx; - - /* Basic packet information */ - rx_ev_byte_cnt = EFX_QWORD_FIELD(*event, RX_EV_BYTE_CNT); - rx_ev_pkt_ok = EFX_QWORD_FIELD(*event, RX_EV_PKT_OK); - rx_ev_hdr_type = EFX_QWORD_FIELD(*event, RX_EV_HDR_TYPE); - WARN_ON(EFX_QWORD_FIELD(*event, RX_EV_JUMBO_CONT)); - WARN_ON(EFX_QWORD_FIELD(*event, RX_EV_SOP) != 1); - - rx_ev_q_label = EFX_QWORD_FIELD(*event, RX_EV_Q_LABEL); - rx_queue = &efx->rx_queue[rx_ev_q_label]; - - rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, RX_EV_DESC_PTR); - expected_ptr = rx_queue->removed_count & FALCON_RXD_RING_MASK; - if (unlikely(rx_ev_desc_ptr != expected_ptr)) { - falcon_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr); - return rx_ev_q_label; - } - - 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 = RX_EV_HDR_TYPE_HAS_CHECKSUMS(rx_ev_hdr_type); - } else { - falcon_handle_rx_not_ok(rx_queue, event, &rx_ev_pkt_ok, - &discard, rx_ev_byte_cnt); - checksummed = 0; - } - - /* Detect multicast packets that didn't match the filter */ - rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, RX_EV_MCAST_PKT); - if (rx_ev_mcast_pkt) { - unsigned int rx_ev_mcast_hash_match = - EFX_QWORD_FIELD(*event, RX_EV_MCAST_HASH_MATCH); - - if (unlikely(!rx_ev_mcast_hash_match)) - discard = 1; - } - - /* Handle received packet */ - efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt, - checksummed, discard); - - return rx_ev_q_label; -} - -/* 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; - int is_phy_event = 0, handled = 0; - - /* Check for interrupt on either port. Some boards have a - * single PHY wired to the interrupt line for port 1. */ - if (EFX_QWORD_FIELD(*event, G_PHY0_INTR) || - EFX_QWORD_FIELD(*event, G_PHY1_INTR) || - EFX_QWORD_FIELD(*event, XG_PHY_INTR)) - is_phy_event = 1; - - if ((FALCON_REV(efx) >= FALCON_REV_B0) && - EFX_OWORD_FIELD(*event, XG_MNT_INTR_B0)) - is_phy_event = 1; - - if (is_phy_event) { - efx->phy_op->clear_interrupt(efx); - queue_work(efx->workqueue, &efx->reconfigure_work); - handled = 1; - } - - if (EFX_QWORD_FIELD_VER(efx, *event, RX_RECOVERY)) { - EFX_ERR(efx, "channel %d seen global RX_RESET " - "event. Resetting.\n", channel->channel); - - atomic_inc(&efx->errors.rx_reset); - efx_schedule_reset(efx, EFX_WORKAROUND_6555(efx) ? - RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); - handled = 1; - } - - 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, DRIVER_EV_SUB_CODE); - ev_sub_data = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_DATA); - - switch (ev_sub_code) { - case TX_DESCQ_FLS_DONE_EV_DECODE: - EFX_TRACE(efx, "channel %d TXQ %d flushed\n", - channel->channel, ev_sub_data); - EFX_DL_CALLBACK(efx, event, event); - break; - case RX_DESCQ_FLS_DONE_EV_DECODE: - EFX_TRACE(efx, "channel %d RXQ %d flushed\n", - channel->channel, ev_sub_data); - EFX_DL_CALLBACK(efx, event, event); - break; - case EVQ_INIT_DONE_EV_DECODE: - EFX_LOG(efx, "channel %d EVQ %d initialised\n", - channel->channel, ev_sub_data); - break; - case SRM_UPD_DONE_EV_DECODE: - EFX_TRACE(efx, "channel %d SRAM update done\n", - channel->channel); - EFX_DL_CALLBACK(efx, event, event); - break; - case WAKE_UP_EV_DECODE: - EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n", - channel->channel, ev_sub_data); - EFX_DL_CALLBACK(efx, event, event); - break; - case TIMER_EV_DECODE: - EFX_TRACE(efx, "channel %d RX queue %d timer expired\n", - channel->channel, ev_sub_data); - EFX_DL_CALLBACK(efx, event, event); - break; - case RX_RECOVERY_EV_DECODE: - EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. " - "Resetting.\n", channel->channel); - - atomic_inc(&efx->errors.rx_reset); - efx_schedule_reset(efx, - EFX_WORKAROUND_6555(efx) ? - RESET_TYPE_RX_RECOVERY : - RESET_TYPE_DISABLE); - break; - case RX_DSC_ERROR_EV_DECODE: - EFX_ERR(efx, "RX DMA Q %d reports descriptor fetch error." - " RX Q %d is disabled.\n", ev_sub_data, ev_sub_data); - atomic_inc(&efx->errors.rx_desc_fetch); - efx_schedule_reset(efx, RESET_TYPE_RX_DESC_FETCH); - break; - case TX_DSC_ERROR_EV_DECODE: - EFX_ERR(efx, "TX DMA Q %d reports descriptor fetch error." - " TX Q %d is disabled.\n", ev_sub_data, ev_sub_data); - atomic_inc(&efx->errors.tx_desc_fetch); - 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); - EFX_DL_CALLBACK(efx, event, event); - break; - } -} - -int fastcall falcon_process_eventq(struct efx_channel *channel, int *rx_quota) -{ - unsigned int read_ptr; - efx_qword_t event, *p_event; - int ev_code; - int rxq; - int rxdmaqs = 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, EV_CODE); - - switch (ev_code) { - case RX_IP_EV_DECODE: - rxq = falcon_handle_rx_event(channel, &event); - rxdmaqs |= (1 << rxq); - (*rx_quota)--; - break; - case TX_IP_EV_DECODE: - falcon_handle_tx_event(channel, &event); - break; - case DRV_GEN_EV_DECODE: - channel->eventq_magic - = EFX_QWORD_FIELD(event, EVQ_MAGIC); - EFX_LOG(channel->efx, "channel %d received generated " - "event "EFX_QWORD_FMT"\n", channel->channel, - EFX_QWORD_VAL(event)); - break; - case GLOBAL_EV_DECODE: - falcon_handle_global_event(channel, &event); - break; - case DRIVER_EV_DECODE: - 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) & FALCON_EVQ_MASK; - - } while (*rx_quota); - - channel->eventq_read_ptr = read_ptr; - return rxdmaqs; -} - -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; /* Set timer register */ if (channel->irq_moderation) { - /* Round to resolution supported by hardware. The value we - * program is based at 0. So actual interrupt moderation - * achieved is ((x + 1) * res). - */ - unsigned int res = 5; - channel->irq_moderation -= (channel->irq_moderation % res); - if (channel->irq_moderation < res) - channel->irq_moderation = res; EFX_POPULATE_DWORD_2(timer_cmd, - TIMER_MODE, TIMER_MODE_INT_HLDOFF, - TIMER_VAL, - (channel->irq_moderation / res) - 1); + FRF_AB_TC_TIMER_MODE, + FFE_BB_TIMER_MODE_INT_HLDOFF, + FRF_AB_TC_TIMER_VAL, + channel->irq_moderation - 1); } else { EFX_POPULATE_DWORD_2(timer_cmd, - TIMER_MODE, TIMER_MODE_DIS, - TIMER_VAL, 0); - } - falcon_writel_page_locked(efx, &timer_cmd, TIMER_CMD_REG_KER, - channel->evqnum); - -} - -/* Allocate buffer table entries for event queue */ -int falcon_probe_eventq(struct efx_channel *channel) -{ - struct efx_nic *efx = channel->efx; - struct falcon_nic_data *nic_data = efx->nic_data; - unsigned int evq_size; - int rc; - - evq_size = FALCON_EVQ_SIZE * sizeof(efx_qword_t); - rc = falcon_alloc_special_buffer(efx, &channel->eventq, evq_size); - if (rc) - return rc; - - nic_data->resources.evq_int_min = max(nic_data->resources.evq_int_min, - (unsigned)channel->evqnum + 1); - - return 0; -} - -int falcon_init_eventq(struct efx_channel *channel) -{ - efx_oword_t evq_ptr; - struct efx_nic *efx = channel->efx; - int rc; - - 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 */ - rc = falcon_init_special_buffer(efx, &channel->eventq); - if (rc) - return rc; - - /* 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, - EVQ_EN, 1, - EVQ_SIZE, FALCON_EVQ_ORDER, - EVQ_BUF_BASE_ID, channel->eventq.index); - falcon_write_table(efx, &evq_ptr, efx->type->evq_ptr_tbl_base, - channel->evqnum); - - falcon_set_int_moderation(channel); - - return 0; -} - -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); - falcon_write_table(efx, &eventq_ptr, efx->type->evq_ptr_tbl_base, - channel->evqnum); - - /* 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, - EV_CODE, DRV_GEN_EV_DECODE, - EVQ_MAGIC, magic); - falcon_generate_event(channel, &test_event); -} - - -/************************************************************************** - * - * Falcon 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 Falcon interrupts */ -static inline void falcon_interrupts(struct efx_nic *efx, int enabled, - int force) -{ - efx_oword_t int_en_reg_ker; - - EFX_POPULATE_OWORD_2(int_en_reg_ker, - KER_INT_KER, force, - DRV_INT_EN_KER, enabled); - falcon_write(efx, &int_en_reg_ker, INT_EN_REG_KER); -} - -void falcon_enable_interrupts(struct efx_nic *efx) -{ - efx_oword_t int_adr_reg_ker; - struct efx_channel *channel; - - /* Zero INT_KER */ - EFX_ZERO_OWORD(*((efx_oword_t *) efx->irq_status.addr)); - wmb(); /* Ensure interrupt vector is clear before interrupts enabled */ - - /* Program INT_ADR_KER_REG */ - EFX_POPULATE_OWORD_2(int_adr_reg_ker, - NORM_INT_VEC_DIS_KER, EFX_INT_MODE_USE_MSI(efx), - INT_ADR_KER, efx->irq_status.dma_addr); - falcon_write(efx, &int_adr_reg_ker, INT_ADR_REG_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_with_interrupt(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); + FRF_AB_TC_TIMER_MODE, + FFE_BB_TIMER_MODE_DIS, + FRF_AB_TC_TIMER_VAL, 0); + } + 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); +} + +static void falcon_deconfigure_mac_wrapper(struct efx_nic *efx); + +static void falcon_prepare_flush(struct efx_nic *efx) +{ + falcon_deconfigure_mac_wrapper(efx); + + /* 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 @@ -1514,95 +198,54 @@ void falcon_generate_interrupt(struct ef * * 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; - EFX_POPULATE_DWORD_1(reg, INT_ACK_DUMMY_DATA, 0xb7eb7e); - falcon_writel(efx, ®, INT_ACK_REG_KER_A1); - falcon_readl(efx, ®, WORK_AROUND_BROKEN_PCI_READS_REG_KER_A1); -} - -/* 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_oword_t *) efx->irq_status.addr; - efx_oword_t fatal_intr; - int error, mem_perr; - static int n_int_errors; - - falcon_read(efx, &fatal_intr, FATAL_INTR_REG_KER); - error = EFX_OWORD_FIELD(fatal_intr, INT_KER_ERROR); - - 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, MEM_PERR_INT_KER); - if (mem_perr) { - efx_oword_t reg; - falcon_read(efx, ®, MEM_STAT_REG_KER); - EFX_ERR(efx, "SYSTEM ERROR: memory parity error " - EFX_OWORD_FMT "\n", EFX_OWORD_VAL(reg)); - } - - /* Disable DMA bus mastering on both devices */ - pci_disable_device(efx->pci_dev); - if (FALCON_IS_DUAL_FUNC(efx)) - pci_disable_device(nic_data->pci_dev2); - - if (++n_int_errors < 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. - * - * This routine must guarantee not to touch the hardware when - * interrupts are disabled, to allow for correct semantics of - * efx_suspend() and efx_resume(). - */ -static irqreturn_t falcon_legacy_interrupt_b0(int irq, void *dev_id, - struct pt_regs *regs - __attribute__ ((unused))) -{ - struct efx_nic *efx = (struct efx_nic *)dev_id; - efx_oword_t *int_ker = (efx_oword_t *) efx->irq_status.addr; + EFX_POPULATE_DWORD_1(reg, FRF_AA_INT_ACK_KER_FIELD, 0xb7eb7e); + efx_writed(efx, ®, FR_AA_INT_ACK_KER); + efx_readd(efx, ®, FR_AA_WORK_AROUND_BROKEN_PCI_READS); +} + +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_IRQ_HANDLER_REGS) +irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) +#else +irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id, + struct pt_regs *regs + __attribute__ ((unused))) +#endif +{ + struct efx_nic *efx = dev_id; + efx_oword_t *int_ker = efx->irq_status.addr; _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |