[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [linux-2.6.18-xen] Solarflare: Standard network driver.
# HG changeset patch # User Keir Fraser <keir.fraser@xxxxxxxxxx> # Date 1203330547 0 # Node ID fc90e9b2c12b316b5460ece28f013e6de881af1a # Parent 0034d9389130969f9452b76370f76394e10238b8 Solarflare: Standard network driver. Signed-off-by: Kieran Mansley <kmansley@xxxxxxxxxxxxxx> --- MAINTAINERS | 7 drivers/net/Kconfig | 2 drivers/net/Makefile | 2 drivers/net/sfc/Kconfig | 28 drivers/net/sfc/Makefile | 42 drivers/net/sfc/alaska.c | 159 + drivers/net/sfc/bitfield.h | 544 +++++ drivers/net/sfc/boards.c | 528 +++++ drivers/net/sfc/boards.h | 51 drivers/net/sfc/config.h | 1 drivers/net/sfc/debugfs.c | 924 +++++++++ drivers/net/sfc/debugfs.h | 172 + drivers/net/sfc/driverlink.c | 544 +++++ drivers/net/sfc/driverlink.h | 93 drivers/net/sfc/driverlink_api.h | 612 ++++++ drivers/net/sfc/efx.c | 2783 +++++++++++++++++++++++++++++ drivers/net/sfc/efx.h | 103 + drivers/net/sfc/enum.h | 117 + drivers/net/sfc/ethtool.c | 734 +++++++ drivers/net/sfc/ethtool.h | 44 drivers/net/sfc/extraversion.h | 4 drivers/net/sfc/falcon.c | 3708 +++++++++++++++++++++++++++++++++++++++ drivers/net/sfc/falcon.h | 177 + drivers/net/sfc/falcon_gmac.c | 320 +++ drivers/net/sfc/falcon_hwdefs.h | 1620 +++++++++++++++++ drivers/net/sfc/falcon_io.h | 259 ++ drivers/net/sfc/falcon_xmac.c | 691 +++++++ drivers/net/sfc/gmii.h | 212 ++ drivers/net/sfc/i2c-direct.c | 398 ++++ drivers/net/sfc/i2c-direct.h | 108 + drivers/net/sfc/kernel_compat.c | 654 ++++++ drivers/net/sfc/kernel_compat.h | 925 +++++++++ drivers/net/sfc/lm87_support.c | 295 +++ drivers/net/sfc/lm87_support.h | 58 drivers/net/sfc/mac.h | 38 drivers/net/sfc/mdio_10g.c | 441 ++++ drivers/net/sfc/mdio_10g.h | 295 +++ drivers/net/sfc/mentormac.c | 506 +++++ drivers/net/sfc/mtd.c | 602 ++++++ drivers/net/sfc/net_driver.h | 1096 +++++++++++ drivers/net/sfc/null_phy.c | 62 drivers/net/sfc/phy.c | 28 drivers/net/sfc/phy.h | 90 drivers/net/sfc/pm8358_phy.c | 206 ++ drivers/net/sfc/rx.c | 798 ++++++++ drivers/net/sfc/rx.h | 44 drivers/net/sfc/selftest.c | 815 ++++++++ drivers/net/sfc/selftest.h | 67 drivers/net/sfc/sfe4001.c | 315 +++ drivers/net/sfc/spi.h | 186 + drivers/net/sfc/tenxpress.c | 695 +++++++ drivers/net/sfc/tx.c | 522 +++++ drivers/net/sfc/tx.h | 41 drivers/net/sfc/txc43128_phy.c | 725 +++++++ drivers/net/sfc/workarounds.h | 97 + drivers/net/sfc/xenpack.h | 80 drivers/net/sfc/xfp_phy.c | 206 ++ 57 files changed, 24874 insertions(+) diff -r 0034d9389130 -r fc90e9b2c12b MAINTAINERS --- a/MAINTAINERS Fri Feb 15 10:01:06 2008 +0000 +++ b/MAINTAINERS Mon Feb 18 10:29:07 2008 +0000 @@ -2558,6 +2558,13 @@ L: linux-ia64@xxxxxxxxxxxxxxx L: linux-ia64@xxxxxxxxxxxxxxx S: Supported +SFC NETWORK DRIVER +P: Steve Hodgson +P: Ben Hutchings +P: Robert Stonehouse +M: linux-net-drivers@xxxxxxxxxxxxxx +S: Supported + SGI VISUAL WORKSTATION 320 AND 540 P: Andrey Panin M: pazke@xxxxxxxxx diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/Kconfig --- a/drivers/net/Kconfig Fri Feb 15 10:01:06 2008 +0000 +++ b/drivers/net/Kconfig Mon Feb 18 10:29:07 2008 +0000 @@ -2399,6 +2399,8 @@ config MYRI10GE <file:Documentation/networking/net-modules.txt>. The module will be called myri10ge. +source "drivers/net/sfc/Kconfig" + endmenu source "drivers/net/tokenring/Kconfig" diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/Makefile --- a/drivers/net/Makefile Fri Feb 15 10:01:06 2008 +0000 +++ b/drivers/net/Makefile Mon Feb 18 10:29:07 2008 +0000 @@ -219,3 +219,5 @@ obj-$(CONFIG_NETCONSOLE) += netconsole.o obj-$(CONFIG_FS_ENET) += fs_enet/ +obj-$(CONFIG_SFC) += sfc/ + diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/Kconfig --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/Kconfig Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,28 @@ +config SFC + tristate "Solarflare Solarstorm SFC4000 support" + depends on PCI && INET + select MII + help + This driver supports 10-gigabit Ethernet cards based on + the Solarflare Communications Solarstorm SFC4000 controller. + + 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. + + If unsure, say N. + +config SFC_MTD + depends on SFC && MTD && MTD_PARTITIONS + tristate "Solarflare Solarstorm SFC4000 flash/EEPROM support" + 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. diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/Makefile Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,42 @@ + +# Final objects +sfc_o = sfc.o +sfc_mtd_o = sfc_mtd.o + +# Constituent objects +sfc_elements_o := +sfc_elements_o += efx.o +sfc_elements_o += falcon.o +sfc_elements_o += tx.o +sfc_elements_o += rx.o +sfc_elements_o += mentormac.o +sfc_elements_o += falcon_gmac.o +sfc_elements_o += falcon_xmac.o +sfc_elements_o += alaska.o +sfc_elements_o += i2c-direct.o +sfc_elements_o += selftest.o +sfc_elements_o += driverlink.o +ifeq ($(CONFIG_SFC_DEBUGFS),y) +sfc_elements_o += debugfs.o +endif +sfc_elements_o += ethtool.o +sfc_elements_o += xfp_phy.o +sfc_elements_o += mdio_10g.o +sfc_elements_o += txc43128_phy.o +sfc_elements_o += tenxpress.o +sfc_elements_o += lm87_support.o +sfc_elements_o += boards.o +sfc_elements_o += sfe4001.o +sfc_elements_o += pm8358_phy.o +sfc_elements_o += null_phy.o +sfc_elements_o += phy.o +sfc_elements_o += kernel_compat.o + +sfc_mtd_elements_o := mtd.o + +obj-$(CONFIG_SFC) += $(sfc_o) +obj-$(CONFIG_SFC_MTD) += $(sfc_mtd_o) + +sfc-objs = $(sfc_elements_o) +sfc_mtd-objs = $(sfc_mtd_elements_o) + diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/alaska.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/alaska.c Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,159 @@ +/**************************************************************************** + * 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 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/bitfield.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/bitfield.h Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,544 @@ +/**************************************************************************** + * 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> + * + * 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 +#define EFX_BITFIELD_H + +/* + * Efx bitfield access + * + * Efx NICs make extensive use of bitfields up to 128 bits + * wide. Since there is no native 128-bit datatype on most systems, + * and since 64-bit datatypes are inefficient on 32-bit systems and + * vice versa, we wrap accesses in a way that uses the most efficient + * datatype. + * + * The NICs are PCI devices and therefore little-endian. Since most + * of the quantities that we deal with are DMAed to/from host memory, + * we define our datatypes (efx_oword_t, efx_qword_t and + * efx_dword_t) to be little-endian. + */ + +/* Lowest bit numbers and widths */ +#define EFX_DUMMY_FIELD_LBN 0 +#define EFX_DUMMY_FIELD_WIDTH 0 +#define EFX_DWORD_0_LBN 0 +#define EFX_DWORD_0_WIDTH 32 +#define EFX_DWORD_1_LBN 32 +#define EFX_DWORD_1_WIDTH 32 +#define EFX_DWORD_2_LBN 64 +#define EFX_DWORD_2_WIDTH 32 +#define EFX_DWORD_3_LBN 96 +#define EFX_DWORD_3_WIDTH 32 + +#define EFX_BYTE 1 +#define EFX_WORD 2 +#define EFX_DWORD 4 +#define EFX_OWORD 8 + +/* Specified attribute (e.g. LBN) of the specified field */ +#define EFX_VAL(field, attribute) field ## _ ## attribute +/* Low bit number of the specified field */ +#define EFX_LOW_BIT(field) EFX_VAL(field, LBN) +/* Bit width of the specified field */ +#define EFX_WIDTH(field) EFX_VAL(field, WIDTH) +/* High bit number of the specified field */ +#define EFX_HIGH_BIT(field) (EFX_LOW_BIT(field) + EFX_WIDTH(field) - 1) +/* Mask equal in width to the specified field. + * + * For example, a field with width 5 would have a mask of 0x1f. + * + * 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)) + +/* Mask equal in width to the specified field. + * + * For example, a field with width 5 would have a mask of 0x1f. + * + * 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)) + +/* A doubleword (i.e. 4 byte) datatype - little-endian in HW */ +typedef union efx_dword { + __le32 u32[1]; +} efx_dword_t; + +/* A quadword (i.e. 8 byte) datatype - little-endian in HW */ +typedef union efx_qword { + __le64 u64[1]; + __le32 u32[2]; + efx_dword_t dword[2]; +} efx_qword_t; + +/* An octword (eight-word, i.e. 16 byte) datatype - little-endian in HW */ +typedef union efx_oword { + __le64 u64[2]; + efx_qword_t qword[2]; + __le32 u32[4]; + efx_dword_t dword[4]; +} efx_oword_t; + +/* Format string and value expanders for printk */ +#define EFX_DWORD_FMT "%08x" +#define EFX_QWORD_FMT "%08x:%08x" +#define EFX_OWORD_FMT "%08x:%08x:%08x:%08x" +#define EFX_DWORD_VAL(dword) \ + ((unsigned int) le32_to_cpu((dword).u32[0])) +#define EFX_QWORD_VAL(qword) \ + ((unsigned int) le32_to_cpu((qword).u32[1])), \ + ((unsigned int) le32_to_cpu((qword).u32[0])) +#define EFX_OWORD_VAL(oword) \ + ((unsigned int) le32_to_cpu((oword).u32[3])), \ + ((unsigned int) le32_to_cpu((oword).u32[2])), \ + ((unsigned int) le32_to_cpu((oword).u32[1])), \ + ((unsigned int) le32_to_cpu((oword).u32[0])) + +/* + * Extract bit field portion [low,high) from the native-endian element + * which contains bits [min,max). + * + * For example, suppose "element" represents the high 32 bits of a + * 64-bit value, and we wish to extract the bits belonging to the bit + * field occupying bits 28-45 of this 64-bit value. + * + * Then EFX_EXTRACT ( element, 32, 63, 28, 45 ) would give + * + * ( element ) << 4 + * + * The result will contain the relevant bits filled in in the range + * [0,high-low), with garbage in bits [high-low+1,...). + */ +#define EFX_EXTRACT_NATIVE(native_element, min, max, low, high) \ + (((low > max) || (high < min)) ? 0 : \ + ((low > min) ? \ + ((native_element) >> (low - min)) : \ + ((native_element) << (min - low)))) + +/* + * Extract bit field portion [low,high) from the 64-bit little-endian + * element which contains bits [min,max) + */ +#define EFX_EXTRACT64(element, min, max, low, high) \ + EFX_EXTRACT_NATIVE(le64_to_cpu(element), min, max, low, high) + +/* + * Extract bit field portion [low,high) from the 32-bit little-endian + * element which contains bits [min,max) + */ +#define EFX_EXTRACT32(element, min, max, low, high) \ + 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)) + +#define EFX_EXTRACT_QWORD64(qword, low, high) \ + EFX_EXTRACT64((qword).u64[0], 0, 63, low, high) + +#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)) + +#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)) + +#define EFX_OWORD_IS_ZERO64(oword) \ + (((oword).u64[0] | (oword).u64[1]) == (__force __le64) 0) + +#define EFX_QWORD_IS_ZERO64(qword) \ + (((qword).u64[0]) == (__force __le64) 0) + +#define EFX_OWORD_IS_ZERO32(oword) \ + (((oword).u32[0] | (oword).u32[1] | (oword).u32[2] | (oword).u32[3]) \ + == (__force __le32) 0) + +#define EFX_QWORD_IS_ZERO32(qword) \ + (((qword).u32[0] | (qword).u32[1]) == (__force __le32) 0) + +#define EFX_DWORD_IS_ZERO(dword) \ + (((dword).u32[0]) == (__force __le32) 0) + +#define EFX_OWORD_IS_ALL_ONES64(oword) \ + (((oword).u64[0] & (oword).u64[1]) == ~((__force __le64) 0)) + +#define EFX_QWORD_IS_ALL_ONES64(qword) \ + ((qword).u64[0] == ~((__force __le64) 0)) + +#define EFX_OWORD_IS_ALL_ONES32(oword) \ + (((oword).u32[0] & (oword).u32[1] & (oword).u32[2] & (oword).u32[3]) \ + == ~((__force __le32) 0)) + +#define EFX_QWORD_IS_ALL_ONES32(qword) \ + (((qword).u32[0] & (qword).u32[1]) == ~((__force __le32) 0)) + +#define EFX_DWORD_IS_ALL_ONES(dword) \ + ((dword).u32[0] == ~((__force __le32) 0)) + +#if BITS_PER_LONG == 64 +#define EFX_OWORD_FIELD EFX_OWORD_FIELD64 +#define EFX_QWORD_FIELD EFX_QWORD_FIELD64 +#define EFX_OWORD_IS_ZERO EFX_OWORD_IS_ZERO64 +#define EFX_QWORD_IS_ZERO EFX_QWORD_IS_ZERO64 +#define EFX_OWORD_IS_ALL_ONES EFX_OWORD_IS_ALL_ONES64 +#define EFX_QWORD_IS_ALL_ONES EFX_QWORD_IS_ALL_ONES64 +#else +#define EFX_OWORD_FIELD EFX_OWORD_FIELD32 +#define EFX_QWORD_FIELD EFX_QWORD_FIELD32 +#define EFX_OWORD_IS_ZERO EFX_OWORD_IS_ZERO32 +#define EFX_QWORD_IS_ZERO EFX_QWORD_IS_ZERO32 +#define EFX_OWORD_IS_ALL_ONES EFX_OWORD_IS_ALL_ONES32 +#define EFX_QWORD_IS_ALL_ONES EFX_QWORD_IS_ALL_ONES32 +#endif + +/* + * Construct bit field portion + * + * Creates the portion of the bit field [low,high) that lies within + * the range [min,max). + */ +#define EFX_INSERT_NATIVE64(min, max, low, high, value) \ + (((low > max) || (high < min)) ? 0 : \ + ((low > min) ? \ + (((u64) (value)) << (low - min)) : \ + (((u64) (value)) >> (min - low)))) + +#define EFX_INSERT_NATIVE32(min, max, low, high, value) \ + (((low > max) || (high < min)) ? 0 : \ + ((low > min) ? \ + (((u32) (value)) << (low - min)) : \ + (((u32) (value)) >> (min - low)))) + +#define EFX_INSERT_NATIVE(min, max, low, high, value) \ + ((((max - min) >= 32) || ((high - low) >= 32)) ? \ + EFX_INSERT_NATIVE64(min, max, low, high, value) : \ + EFX_INSERT_NATIVE32(min, max, low, high, value)) + +/* + * Construct bit field portion + * + * Creates the portion of the named bit field that lies within the + * range [min,max). + */ +#define EFX_INSERT_FIELD_NATIVE(min, max, field, value) \ + EFX_INSERT_NATIVE(min, max, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +/* + * Construct bit field + * + * Creates the portion of the named bit fields that lie within the + * range [min,max). + */ +#define EFX_INSERT_FIELDS_NATIVE(min, max, \ + field1, value1, \ + field2, value2, \ + field3, value3, \ + field4, value4, \ + field5, value5, \ + field6, value6, \ + field7, value7, \ + field8, value8, \ + field9, value9, \ + field10, value10) \ + (EFX_INSERT_FIELD_NATIVE((min), (max), field1, (value1)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field2, (value2)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field3, (value3)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field4, (value4)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field5, (value5)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field6, (value6)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field7, (value7)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field8, (value8)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field9, (value9)) | \ + EFX_INSERT_FIELD_NATIVE((min), (max), field10, (value10))) + +#define EFX_INSERT_FIELDS64(...) \ + cpu_to_le64(EFX_INSERT_FIELDS_NATIVE(__VA_ARGS__)) + +#define EFX_INSERT_FIELDS32(...) \ + cpu_to_le32(EFX_INSERT_FIELDS_NATIVE(__VA_ARGS__)) + +#define EFX_POPULATE_OWORD64(oword, ...) do { \ + (oword).u64[0] = EFX_INSERT_FIELDS64(0, 63, __VA_ARGS__); \ + (oword).u64[1] = EFX_INSERT_FIELDS64(64, 127, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_QWORD64(qword, ...) do { \ + (qword).u64[0] = EFX_INSERT_FIELDS64(0, 63, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_OWORD32(oword, ...) do { \ + (oword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + (oword).u32[1] = EFX_INSERT_FIELDS32(32, 63, __VA_ARGS__); \ + (oword).u32[2] = EFX_INSERT_FIELDS32(64, 95, __VA_ARGS__); \ + (oword).u32[3] = EFX_INSERT_FIELDS32(96, 127, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_QWORD32(qword, ...) do { \ + (qword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + (qword).u32[1] = EFX_INSERT_FIELDS32(32, 63, __VA_ARGS__); \ + } while (0) + +#define EFX_POPULATE_DWORD(dword, ...) do { \ + (dword).u32[0] = EFX_INSERT_FIELDS32(0, 31, __VA_ARGS__); \ + } while (0) + +#if BITS_PER_LONG == 64 +#define EFX_POPULATE_OWORD EFX_POPULATE_OWORD64 +#define EFX_POPULATE_QWORD EFX_POPULATE_QWORD64 +#else +#define EFX_POPULATE_OWORD EFX_POPULATE_OWORD32 +#define EFX_POPULATE_QWORD EFX_POPULATE_QWORD32 +#endif + +/* Populate an octword field with various numbers of arguments */ +#define EFX_POPULATE_OWORD_10 EFX_POPULATE_OWORD +#define EFX_POPULATE_OWORD_9(oword, ...) \ + EFX_POPULATE_OWORD_10(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_8(oword, ...) \ + EFX_POPULATE_OWORD_9(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_7(oword, ...) \ + EFX_POPULATE_OWORD_8(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_6(oword, ...) \ + EFX_POPULATE_OWORD_7(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_5(oword, ...) \ + EFX_POPULATE_OWORD_6(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_4(oword, ...) \ + EFX_POPULATE_OWORD_5(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_3(oword, ...) \ + EFX_POPULATE_OWORD_4(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_2(oword, ...) \ + EFX_POPULATE_OWORD_3(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_OWORD_1(oword, ...) \ + EFX_POPULATE_OWORD_2(oword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_ZERO_OWORD(oword) \ + EFX_POPULATE_OWORD_1(oword, EFX_DUMMY_FIELD, 0) +#define EFX_SET_OWORD(oword) \ + EFX_POPULATE_OWORD_4(oword, \ + EFX_DWORD_0, 0xffffffff, \ + EFX_DWORD_1, 0xffffffff, \ + EFX_DWORD_2, 0xffffffff, \ + EFX_DWORD_3, 0xffffffff) + +/* Populate a quadword field with various numbers of arguments */ +#define EFX_POPULATE_QWORD_10 EFX_POPULATE_QWORD +#define EFX_POPULATE_QWORD_9(qword, ...) \ + EFX_POPULATE_QWORD_10(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_8(qword, ...) \ + EFX_POPULATE_QWORD_9(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_7(qword, ...) \ + EFX_POPULATE_QWORD_8(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_6(qword, ...) \ + EFX_POPULATE_QWORD_7(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_5(qword, ...) \ + EFX_POPULATE_QWORD_6(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_4(qword, ...) \ + EFX_POPULATE_QWORD_5(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_3(qword, ...) \ + EFX_POPULATE_QWORD_4(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_2(qword, ...) \ + EFX_POPULATE_QWORD_3(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_QWORD_1(qword, ...) \ + EFX_POPULATE_QWORD_2(qword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_ZERO_QWORD(qword) \ + EFX_POPULATE_QWORD_1(qword, EFX_DUMMY_FIELD, 0) +#define EFX_SET_QWORD(qword) \ + EFX_POPULATE_QWORD_2(qword, \ + EFX_DWORD_0, 0xffffffff, \ + EFX_DWORD_1, 0xffffffff) + +/* Populate a dword field with various numbers of arguments */ +#define EFX_POPULATE_DWORD_10 EFX_POPULATE_DWORD +#define EFX_POPULATE_DWORD_9(dword, ...) \ + EFX_POPULATE_DWORD_10(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_8(dword, ...) \ + EFX_POPULATE_DWORD_9(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_7(dword, ...) \ + EFX_POPULATE_DWORD_8(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_6(dword, ...) \ + EFX_POPULATE_DWORD_7(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_5(dword, ...) \ + EFX_POPULATE_DWORD_6(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_4(dword, ...) \ + EFX_POPULATE_DWORD_5(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_3(dword, ...) \ + EFX_POPULATE_DWORD_4(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_2(dword, ...) \ + EFX_POPULATE_DWORD_3(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_POPULATE_DWORD_1(dword, ...) \ + EFX_POPULATE_DWORD_2(dword, EFX_DUMMY_FIELD, 0, __VA_ARGS__) +#define EFX_ZERO_DWORD(dword) \ + EFX_POPULATE_DWORD_1(dword, EFX_DUMMY_FIELD, 0) +#define EFX_SET_DWORD(dword) \ + EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xffffffff) + +/* + * Modify a named field within an already-populated structure. Used + * 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 { \ + (oword).u64[0] = (((oword).u64[0] \ + & ~EFX_INPLACE_MASK64(0, 63, field)) \ + | EFX_INSERT_FIELD64(0, 63, field, 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 { \ + (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 { \ + (oword).u32[0] = (((oword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, field)) \ + | EFX_INSERT_FIELD32(0, 31, field, value)); \ + (oword).u32[1] = (((oword).u32[1] \ + & ~EFX_INPLACE_MASK32(32, 63, field)) \ + | EFX_INSERT_FIELD32(32, 63, field, value)); \ + (oword).u32[2] = (((oword).u32[2] \ + & ~EFX_INPLACE_MASK32(64, 95, field)) \ + | EFX_INSERT_FIELD32(64, 95, field, 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 { \ + (qword).u32[0] = (((qword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, field)) \ + | EFX_INSERT_FIELD32(0, 31, field, 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) + +#if BITS_PER_LONG == 64 +#define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD64 +#define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD64 +#else +#define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD32 +#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) + +/* + * Determine if a DMA address is over the 4GB threshold + * + * Defined in a slightly tortuous way to avoid compiler warnings. + */ +static inline int efx_is_over_4gb(dma_addr_t address) +{ + if (DMA_ADDR_T_WIDTH > 32) + return (((u64) address) >> 32) ? 1 : 0; + else + /* Can never be true */ + return 0; +} + +#endif /* EFX_BITFIELD_H */ diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/boards.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/boards.c Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,528 @@ +/**************************************************************************** + * 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 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/boards.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/boards.h Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,51 @@ +/**************************************************************************** + * 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 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/config.h Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,1 @@ +/* SFC config options can go here */ diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/debugfs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/debugfs.c Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,924 @@ +/**************************************************************************** + * 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> + * + * 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" + +/* 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 PRIu64 +# if (BITS_PER_LONG == 64) +# define PRIu64 "lu" +# else +# define PRIu64 "llu" +# endif +#endif + +#ifndef EFX_USE_DEBUGFS + +static void efx_debugfs_remove(struct proc_dir_entry *entry) +{ + if (entry) + remove_proc_entry(entry->name, entry->parent); +} +#define debugfs_remove efx_debugfs_remove + +#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 { + const struct efx_debugfs_parameter *param; + void *structure; +}; + + +/* Maximum length for a name component or symlink target */ +#define EFX_DEBUGFS_NAME_LEN 32 + + +/* Top-level debug directory ([/sys/kernel]/debug/sfc) */ +static struct dentry *efx_debug_root; + +/* "cards" directory ([/sys/kernel]/debug/sfc/cards) */ +static struct dentry *efx_debug_cards; + + +/* Sequential file interface to bound parameters */ + +#if defined(EFX_USE_DEBUGFS) + +static int efx_debugfs_seq_show(struct seq_file *file, void *v) +{ + struct efx_debugfs_bound_param *binding = + (struct efx_debugfs_bound_param *)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_NOT_UPSTREAM && !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; + void *structure = (void *)entry->read_proc; + + if (!structure) + return -EIO; + + return param->reader(file, structure + param->offset); +} + +static int efx_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, efx_debugfs_seq_show, PROC_I(inode)->pde); +} + +#endif /* !EFX_NOT_UPSTREAM || EFX_USE_DEBUGFS */ + + +static struct file_operations efx_debugfs_file_ops = { + .owner = THIS_MODULE, + .open = efx_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release +}; + + +#if 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(struct dentry *dir, const char *name) +{ + struct qstr child_name; + struct dentry *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_NOT_UPSTREAM && !EFX_USE_DEBUGFS */ + +void efx_fini_debugfs_child(struct proc_dir_entry *dir, const char *name) +{ + remove_proc_entry(name, dir); +} + +#endif /* !EFX_NOT_UPSTREAM || EFX_USE_DEBUGFS */ + +/* + * Remove a debugfs directory. + * + * This removes the named parameter-files and sym-links from the + * directory, and the directory itself. It does not do any recursion + * to subdirectories. + */ +static void efx_fini_debugfs_dir(struct dentry *dir, + struct efx_debugfs_parameter *params, + const char *const *symlink_names) +{ + if (!dir) + return; + + while (params->name) { + efx_fini_debugfs_child(dir, params->name); + params++; + } + while (symlink_names && *symlink_names) { + efx_fini_debugfs_child(dir, *symlink_names); + symlink_names++; + } + debugfs_remove(dir); +} + +/* Functions for printing various types of parameter. */ + +int efx_debugfs_read_uint(struct seq_file *file, void *data) +{ + return seq_printf(file, "%#x\n", *(unsigned int *)data); +} + +int efx_debugfs_read_int(struct seq_file *file, void *data) +{ + return seq_printf(file, "%d\n", *(int *)data); +} + +int efx_debugfs_read_atomic(struct seq_file *file, void *data) +{ + unsigned int value = atomic_read((atomic_t *) data); + + return seq_printf(file, "%#x\n", value); +} + +int efx_debugfs_read_dword(struct seq_file *file, void *data) +{ + unsigned int value = EFX_DWORD_FIELD(*(efx_dword_t *) data, + EFX_DWORD_0); + + return seq_printf(file, "%#x\n", value); +} + +static int efx_debugfs_read_int_mode(struct seq_file *file, void *data) +{ + unsigned int value = *(enum efx_int_mode *) data; + + return seq_printf(file, "%d => %s\n", value, + STRING_TABLE_LOOKUP(value, efx_interrupt_mode)); +} + +#define EFX_INT_MODE_PARAMETER(container_type, parameter) \ + EFX_PARAMETER(container_type, parameter, \ + enum efx_int_mode, efx_debugfs_read_int_mode) + +static int efx_debugfs_read_loop_mode(struct seq_file *file, void *data) +{ + unsigned int value = *(enum efx_loopback_mode *)data; + + return seq_printf(file, "%d => %s\n", value, + STRING_TABLE_LOOKUP(value, efx_loopback_mode)); +} + +#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) +{ + return seq_puts(file, (const char *)data); +} + + +/** + * efx_init_debugfs_files - create parameter-files in a debugfs directory + * @parent: Containing directory + * @params: Pointer to zero-terminated parameter definition array + * @structure: Structure containing parameters + * + * 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, + struct efx_debugfs_parameter *params, + void *structure) +{ + struct efx_debugfs_parameter *param = params; + + while (param->name) { + struct dentry *entry; +#if 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; + /* + * We have no good way to free a binding created here. + * However, once we install our file_operations the + * read_proc pointer becomes redundant and we can + * abuse it as a structure pointer. + */ + entry->data = param; + entry->read_proc = NULL; + smp_wmb(); + entry->proc_fops = &efx_debugfs_file_ops; + smp_wmb(); + entry->read_proc = (read_proc_t *) structure; +#endif + + param++; + } + + return 0; + + err: + while (param != params) { + param--; + efx_fini_debugfs_child(parent, param->name); + } + return -ENOMEM; +} + +/** + * efx_init_debugfs_netdev - create debugfs sym-links for net device + * @net_dev: Net device + * + * Create sym-links named after @net_dev to the debugfs directories for + * the corresponding NIC and port. Return a negative error code or 0 on + * success. The sym-links must be cleaned up using + * efx_fini_debugfs_netdev(). + */ +int efx_init_debugfs_netdev(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + char name[EFX_DEBUGFS_NAME_LEN]; + char target[EFX_DEBUGFS_NAME_LEN]; + size_t len; + + if (snprintf(name, sizeof(name), "nic_%s", net_dev->name) >= + sizeof(name)) + return -ENAMETOOLONG; + if (snprintf(target, sizeof(target), "cards/%s", pci_name(efx->pci_dev)) + >= sizeof(target)) + return -ENAMETOOLONG; + efx->debug_symlink = debugfs_create_symlink(name, + efx_debug_root, target); + if (!efx->debug_symlink) + return -ENOMEM; + + if (snprintf(name, sizeof(name), "if_%s", net_dev->name) >= + sizeof(name)) + return -ENAMETOOLONG; + len = snprintf(target, sizeof(target), + "cards/%s/port0", pci_name(efx->pci_dev)); + if (len >= sizeof(target)) + return -ENAMETOOLONG; + efx->debug_port_symlink = debugfs_create_symlink(name, + efx_debug_root, + target); + if (!efx->debug_port_symlink) + return -ENOMEM; + + return 0; +} + +/** + * efx_fini_debugfs_netdev - remove debugfs sym-links for net device + * @net_dev: Net device + * + * Remove sym-links created for @net_dev by efx_init_debugfs_netdev(). + */ +void efx_fini_debugfs_netdev(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + + debugfs_remove(efx->debug_port_symlink); + efx->debug_port_symlink = NULL; + debugfs_remove(efx->debug_symlink); + efx->debug_symlink = NULL; +} + +/* 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, net_dev_registered), + EFX_INT_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_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, + int, efx_debugfs_read_int), + EFX_UINT_PARAMETER(struct efx_nic, n_link_state_changes), + {NULL}, +}; + +/** + * efx_init_debugfs_port - create debugfs directory for port + * @efx: Efx NIC + * + * Create a debugfs directory containing parameter-files for @efx. + * Return a negative error code or 0 on success. The directory must be + * cleaned up using efx_fini_debugfs_port(). + */ +int efx_init_debugfs_port(struct efx_nic *efx) +{ + int rc; + + /* Create directory */ + efx->debug_port_dir = debugfs_create_dir("port0", efx->debug_dir); + if (!efx->debug_port_dir) + return -ENOMEM; + + /* Create files */ + rc = efx_init_debugfs_files(efx->debug_port_dir, + efx_debugfs_port_parameters, + (void *)efx); + if (rc) + efx_fini_debugfs_port(efx); + + return rc; +} + +/** + * efx_fini_debugfs_port - remove debugfs directory for port + * @efx: Efx NIC + * + * Remove directory created for @efx by efx_init_debugfs_port(). + */ +void efx_fini_debugfs_port(struct efx_nic *efx) +{ + efx_fini_debugfs_dir(efx->debug_port_dir, + efx_debugfs_port_parameters, NULL); + efx->debug_port_dir = NULL; +} + +/** + * efx_extend_debugfs_port - add parameter-files to directory for port + * @efx: Efx NIC + * @structure: Structure containing parameters + * @params: Pointer to zero-terminated parameter definition array + * + * Add parameter-files to the debugfs directory for @efx. Return + * a negative error code or 0 on success. This is intended for + * PHY-specific parameters. The files must be cleaned up using + * efx_trim_debugfs_port(). + */ +int efx_extend_debugfs_port(struct efx_nic *efx, + void *structure, + struct efx_debugfs_parameter *params) +{ + return efx_init_debugfs_files(efx->debug_port_dir, params, structure); +} + +/** + * efx_trim_debugfs_port - remove parameter-files from directory for port + * @efx: Efx NIC + * @params: Pointer to zero-terminated parameter definition array + * + * Remove parameter-files previously added to the debugfs directory + * for @efx using efx_extend_debugfs_port(). + */ +void efx_trim_debugfs_port(struct efx_nic *efx, + struct efx_debugfs_parameter *params) +{ + struct dentry *dir = efx->debug_port_dir; + + if (dir) { + struct efx_debugfs_parameter *field; + for (field = params; field->name; field++) + efx_fini_debugfs_child(dir, field->name); + } +} + +/* Per-TX-queue parameters */ +static struct efx_debugfs_parameter efx_debugfs_tx_queue_parameters[] = { + EFX_UINT_PARAMETER(struct efx_tx_queue, insert_count), + 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), + {NULL}, +}; + +static void efx_fini_debugfs_tx_queue(struct efx_tx_queue *tx_queue); + +/** + * efx_init_debugfs_tx_queue - create debugfs directory for TX queue + * @tx_queue: Efx TX queue + * + * Create a debugfs directory containing parameter-files for @tx_queue. + * Return a negative error code or 0 on success. The directory must be + * cleaned up using efx_fini_debugfs_tx_queue(). + */ +static int efx_init_debugfs_tx_queue(struct efx_tx_queue *tx_queue) +{ + char name[EFX_DEBUGFS_NAME_LEN]; + char target[EFX_DEBUGFS_NAME_LEN]; + int rc; + + /* Create directory */ + if (snprintf(name, sizeof(name), EFX_TX_QUEUE_NAME(tx_queue)) + >= sizeof(name)) + goto err_len; + tx_queue->debug_dir = debugfs_create_dir(name, + tx_queue->efx->debug_dir); + if (!tx_queue->debug_dir) + goto err_mem; + + /* Create files */ + rc = efx_init_debugfs_files(tx_queue->debug_dir, + efx_debugfs_tx_queue_parameters, + (void *)tx_queue); + if (rc) + goto err; + + /* Create symlink to channel */ + if (snprintf(target, sizeof(target), + "../" EFX_CHANNEL_NAME(tx_queue->channel)) >= + sizeof(target)) + goto err_len; + if (!debugfs_create_symlink("channel", tx_queue->debug_dir, target)) + goto err_mem; + + /* Create symlink to port */ + if (!debugfs_create_symlink("port", tx_queue->debug_dir, "../port0")) + goto err_mem; + + return 0; + + err_len: + rc = -ENAMETOOLONG; + goto err; + err_mem: + rc = -ENOMEM; + err: + efx_fini_debugfs_tx_queue(tx_queue); + return rc; +} + +/** + * efx_fini_debugfs_tx_queue - remove debugfs directory for TX queue + * @tx_queue: Efx TX queue + * + * Remove directory created for @tx_queue by efx_init_debugfs_tx_queue(). + */ +static void efx_fini_debugfs_tx_queue(struct efx_tx_queue *tx_queue) +{ + static const char *const symlink_names[] = { + "channel", "port", NULL + }; + + efx_fini_debugfs_dir(tx_queue->debug_dir, + efx_debugfs_tx_queue_parameters, symlink_names); + tx_queue->debug_dir = NULL; +} + +/* Per-RX-queue parameters */ +static struct efx_debugfs_parameter efx_debugfs_rx_queue_parameters[] = { + EFX_INT_PARAMETER(struct efx_rx_queue, added_count), + EFX_INT_PARAMETER(struct efx_rx_queue, removed_count), + EFX_UINT_PARAMETER(struct efx_rx_queue, max_fill), + EFX_UINT_PARAMETER(struct efx_rx_queue, fast_fill_trigger), + EFX_UINT_PARAMETER(struct efx_rx_queue, fast_fill_limit), + EFX_UINT_PARAMETER(struct efx_rx_queue, min_fill), + EFX_UINT_PARAMETER(struct efx_rx_queue, min_overfill), + EFX_UINT_PARAMETER(struct efx_rx_queue, alloc_page_count), + EFX_UINT_PARAMETER(struct efx_rx_queue, alloc_skb_count), + EFX_UINT_PARAMETER(struct efx_rx_queue, slow_fill_count), + {NULL}, +}; + +static void efx_fini_debugfs_rx_queue(struct efx_rx_queue *rx_queue); + +/** + * efx_init_debugfs_rx_queue - create debugfs directory for RX queue + * @rx_queue: Efx RX queue + * + * Create a debugfs directory containing parameter-files for @rx_queue. + * Return a negative error code or 0 on success. The directory must be + * cleaned up using efx_fini_debugfs_rx_queue(). + */ +static int efx_init_debugfs_rx_queue(struct efx_rx_queue *rx_queue) +{ + char name[EFX_DEBUGFS_NAME_LEN]; + char target[EFX_DEBUGFS_NAME_LEN]; + int rc; + + /* Create directory */ + if (snprintf(name, sizeof(name), EFX_RX_QUEUE_NAME(rx_queue)) + >= sizeof(name)) + goto err_len; + rx_queue->debug_dir = debugfs_create_dir(name, + rx_queue->efx->debug_dir); + if (!rx_queue->debug_dir) + goto err_mem; + + /* Create files */ + rc = efx_init_debugfs_files(rx_queue->debug_dir, + efx_debugfs_rx_queue_parameters, + (void *)rx_queue); + if (rc) + goto err; + + /* Create symlink to channel */ + if (snprintf(target, sizeof(target), + "../" EFX_CHANNEL_NAME(rx_queue->channel)) >= + sizeof(target)) + goto err_len; + if (!debugfs_create_symlink("channel", rx_queue->debug_dir, target)) + goto err_mem; + + return 0; + + err_len: + rc = -ENAMETOOLONG; + goto err; + err_mem: + rc = -ENOMEM; + err: + efx_fini_debugfs_rx_queue(rx_queue); + return rc; +} + +/** + * efx_fini_debugfs_rx_queue - remove debugfs directory for RX queue + * @rx_queue: Efx RX queue + * + * Remove directory created for @rx_queue by efx_init_debugfs_rx_queue(). + */ +static void efx_fini_debugfs_rx_queue(struct efx_rx_queue *rx_queue) +{ + const char *const symlink_names[] = { + "channel", NULL + }; + + efx_fini_debugfs_dir(rx_queue->debug_dir, + efx_debugfs_rx_queue_parameters, symlink_names); + rx_queue->debug_dir = NULL; +} + +/* Per-channel parameters */ +static struct efx_debugfs_parameter efx_debugfs_channel_parameters[] = { + EFX_INT_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_hdr_chksum_err), + EFX_UINT_PARAMETER(struct efx_channel, n_rx_tcp_udp_chksum_err), + 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), + {NULL}, +}; + +static void efx_fini_debugfs_channel(struct efx_channel *channel); + +/** + * efx_init_debugfs_channel - create debugfs directory for channel + * @channel: Efx channel + * + * Create a debugfs directory containing parameter-files for @channel. + * Return a negative error code or 0 on success. The directory must be + * cleaned up using efx_fini_debugfs_channel(). + */ +static int efx_init_debugfs_channel(struct efx_channel *channel) +{ + char name[EFX_DEBUGFS_NAME_LEN]; + int rc; + + /* Create directory */ + if (snprintf(name, sizeof(name), EFX_CHANNEL_NAME(channel)) + >= sizeof(name)) + goto err_len; + channel->debug_dir = debugfs_create_dir(name, channel->efx->debug_dir); + if (!channel->debug_dir) + goto err_mem; + + /* Create files */ + rc = efx_init_debugfs_files(channel->debug_dir, + efx_debugfs_channel_parameters, + (void *)channel); + if (rc) + goto err; + + return 0; + + err_len: + rc = -ENAMETOOLONG; + goto err; + err_mem: + rc = -ENOMEM; + err: + efx_fini_debugfs_channel(channel); + return rc; +} + +/** + * efx_fini_debugfs_channel - remove debugfs directory for channel + * @channel: Efx channel + * + * Remove directory created for @channel by efx_init_debugfs_channel(). + */ +static void efx_fini_debugfs_channel(struct efx_channel *channel) +{ + efx_fini_debugfs_dir(channel->debug_dir, + efx_debugfs_channel_parameters, NULL); + channel->debug_dir = NULL; +} + +/* 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_UINT_PARAMETER(struct efx_nic, rx_buffer_len), + EFX_INT_MODE_PARAMETER(struct efx_nic, interrupt_mode), + {.name = "hardware_desc", + .offset = 0, + .reader = falcon_debugfs_read_hardware_desc}, + {NULL}, +}; + +/* Per-NIC error counts */ +static struct efx_debugfs_parameter efx_debugfs_nic_error_parameters[] = { + EFX_ATOMIC_PARAMETER(struct efx_nic_errors, missing_event), + EFX_ATOMIC_PARAMETER(struct efx_nic_errors, rx_reset), + EFX_ATOMIC_PARAMETER(struct efx_nic_errors, rx_desc_fetch), + EFX_ATOMIC_PARAMETER(struct efx_nic_errors, tx_desc_fetch), + EFX_ATOMIC_PARAMETER(struct efx_nic_errors, spurious_tx), + {NULL}, +}; + +/** + * efx_init_debugfs_channels - create debugfs directories for NIC channels + * @efx: Efx NIC + * + * Create subdirectories of @efx's debugfs directory for all the + * channels, RX queues and TX queues used by this driver. Return a + * negative error code or 0 on success. The subdirectories must be + * cleaned up using efx_fini_debugfs_channels(). + */ +int efx_init_debugfs_channels(struct efx_nic *efx) +{ + struct efx_channel *channel; + struct efx_rx_queue *rx_queue; + struct efx_tx_queue *tx_queue; + int rc; + + efx_for_each_channel(channel, efx) { + rc = efx_init_debugfs_channel(channel); + if (rc) + goto err; + } + + efx_for_each_rx_queue(rx_queue, efx) { + rc = efx_init_debugfs_rx_queue(rx_queue); + if (rc) + goto err; + } + + efx_for_each_tx_queue(tx_queue, efx) { + rc = efx_init_debugfs_tx_queue(tx_queue); + if (rc) + goto err; + } + + return 0; + + err: + efx_fini_debugfs_channels(efx); + return rc; +} + +/** + * efx_fini_debugfs_channels - remove debugfs directories for NIC queues + * @efx: Efx NIC + * + * Remove subdirectories of @efx's debugfs directory created by + * efx_init_debugfs_channels(). + */ +void efx_fini_debugfs_channels(struct efx_nic *efx) +{ + struct efx_channel *channel; + struct efx_rx_queue *rx_queue; + struct efx_tx_queue *tx_queue; + + efx_for_each_tx_queue(tx_queue, efx) + efx_fini_debugfs_tx_queue(tx_queue); + + efx_for_each_rx_queue(rx_queue, efx) + efx_fini_debugfs_rx_queue(rx_queue); + + efx_for_each_channel(channel, efx) + efx_fini_debugfs_channel(channel); +} + +/** + * efx_init_debugfs_nic - create debugfs directory for NIC + * @efx: Efx NIC + * + * Create debugfs directory containing parameter-files for @efx, + * and a subdirectory "errors" containing per-NIC error counts. + * Return a negative error code or 0 on success. The directories + * must be cleaned up using efx_fini_debugfs_nic(). + */ +int efx_init_debugfs_nic(struct efx_nic *efx) +{ + int rc; + + /* Create directory */ + efx->debug_dir = debugfs_create_dir(pci_name(efx->pci_dev), + efx_debug_cards); + if (!efx->debug_dir) + goto err_mem; + + /* Create errors directory */ + efx->errors.debug_dir = debugfs_create_dir("errors", efx->debug_dir); + if (!efx->errors.debug_dir) + goto err_mem; + + /* Create files */ + rc = efx_init_debugfs_files(efx->debug_dir, + efx_debugfs_nic_parameters, (void *)efx); + if (rc) + goto err; + rc = efx_init_debugfs_files(efx->errors.debug_dir, + efx_debugfs_nic_error_parameters, + (void *)&efx->errors); + if (rc) + goto err; + + return 0; + + err_mem: + rc = -ENOMEM; + err: + efx_fini_debugfs_nic(efx); + return rc; +} + +/** + * efx_fini_debugfs_nic - remove debugfs directories for NIC + * @efx: Efx NIC + * + * Remove debugfs directories created for @efx by efx_init_debugfs_nic(). + */ +void efx_fini_debugfs_nic(struct efx_nic *efx) +{ + efx_fini_debugfs_dir(efx->errors.debug_dir, + efx_debugfs_nic_error_parameters, NULL); + efx->errors.debug_dir = NULL; + efx_fini_debugfs_dir(efx->debug_dir, efx_debugfs_nic_parameters, NULL); + efx->debug_dir = NULL; +} + +/** + * efx_init_debugfs - create debugfs directories for sfc driver + * + * Create debugfs directories "sfc" and "sfc/cards". This must be + * called before any of the other functions that create debugfs + * directories. Return a negative error code or 0 on success. The + * directories must be cleaned up using efx_fini_debugfs(). + */ +int efx_init_debugfs(void) +{ + /* Create top-level directory */ +#if defined(EFX_USE_DEBUGFS) + efx_debug_root = debugfs_create_dir("sfc", NULL); +#else + efx_debug_root = proc_mkdir("sfc", proc_root_driver); +#endif + if (!efx_debug_root) + goto err; + + /* Create "cards" directory */ + efx_debug_cards = debugfs_create_dir("cards", efx_debug_root); + if (!efx_debug_cards) + goto err; + +#if defined(EFX_USE_DEBUGFS) + /* Create compatibility sym-link */ + if (!proc_symlink("sfc", proc_root_driver, "/sys/kernel/debug/sfc")) + goto err; +#endif + return 0; + + err: + efx_fini_debugfs(); + return -ENOMEM; +} + +/** + * efx_fini_debugfs - remove debugfs directories for sfc driver + * + * Remove directories created by efx_init_debugfs(). + */ +void efx_fini_debugfs(void) +{ +#if defined(EFX_USE_DEBUGFS) + remove_proc_entry("sfc", proc_root_driver); +#endif + debugfs_remove(efx_debug_cards); + efx_debug_cards = NULL; + debugfs_remove(efx_debug_root); + efx_debug_root = NULL; +} diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/debugfs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/debugfs.h Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,172 @@ +/**************************************************************************** + * 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> + * + * 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) +{ + return 0; +} +static inline void efx_fini_debugfs_netdev(struct net_device *net_dev) {} +static inline int efx_init_debugfs_port(struct efx_nic *efx) +{ + return 0; +} +static inline void efx_fini_debugfs_port(struct efx_nic *efx) {} +static inline int efx_init_debugfs_nic(struct efx_nic *efx) +{ + return 0; +} +static inline void efx_fini_debugfs_nic(struct efx_nic *efx) {} +static inline int efx_init_debugfs_channels(struct efx_nic *efx) +{ + return 0; +} +static inline void efx_fini_debugfs_channels(struct efx_nic *efx) {} +static inline int efx_init_debugfs(void) +{ + return 0; +} +static inline void efx_fini_debugfs(void) {} + +#endif /* CONFIG_SFC_DEBUGFS */ + +#endif /* EFX_DEBUGFS_H */ diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/driverlink.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/driverlink.c Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,544 @@ +/**************************************************************************** + * 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> + * + * 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/list.h> +#include <linux/skbuff.h> +#include <linux/rtnetlink.h> +#include "net_driver.h" +#include "efx.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); + +/* List of all registered drivers */ +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 */ +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. + */ +static void efx_dl_del_device(struct efx_dl_device *efx_dev) +{ + struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); + + 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, + */ +static void efx_dl_try_add_device(struct efx_nic *efx, + struct efx_dl_driver *driver) +{ + struct efx_dl_handle *efx_handle; + struct efx_dl_device *efx_dev; + int rc; + + /* Allocate and initialise new efx_dl_device structure */ + efx_handle = kzalloc(sizeof(*efx_handle), GFP_KERNEL); + efx_dev = &efx_handle->efx_dev; + efx_handle->efx = efx; + efx_dev->driver = driver; + efx_dev->pci_dev = efx->pci_dev; + 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); + 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); + + EFX_INFO(efx, "%s driverlink client registered\n", driver->name); + return; + + 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. + */ +void efx_dl_unregister_driver(struct efx_dl_driver *driver) +{ + struct efx_dl_handle *efx_handle, *efx_handle_n; + + printk(KERN_INFO "Efx driverlink unregistering %s driver\n", + driver->name); + + /* Acquire lock. We can't return failure, so have to use + * down() instead of down_interruptible() + */ + mutex_lock(&efx_driverlink_lock); + + /* Remove all devices claimed by the driver */ + list_for_each_entry_safe(efx_handle, efx_handle_n, + &driver->device_list, driver_node) + efx_dl_del_device(&efx_handle->efx_dev); + + /* Remove driver from driver list */ + list_del(&driver->node); + + /* Release lock */ + mutex_unlock(&efx_driverlink_lock); +} +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. + */ +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 */ + 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); + + return 0; +} +EXPORT_SYMBOL(efx_dl_register_driver); + +void efx_dl_unregister_nic(struct efx_nic *efx) +{ + 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 */ + 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) +{ + 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 */ + 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. + * + * To avoid a branch point on the fast-path, the callbacks are always + * implemented - they are never NULL. + */ +#if defined(EFX_USE_FASTCALL) +static enum efx_veto fastcall +#else +static enum efx_veto +#endif +efx_dummy_tx_packet_callback(struct efx_dl_device *efx_dev, struct sk_buff *skb) +{ + /* Never veto the packet */ + return EFX_ALLOW_PACKET; +} + +#if defined(EFX_USE_FASTCALL) +static enum efx_veto fastcall +#else +static enum efx_veto +#endif +efx_dummy_rx_packet_callback(struct efx_dl_device *efx_dev, + const char *pkt_buf, int len) +{ + /* Never veto the packet */ + 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 */ + return 0; +} + +static void +efx_dummy_mtu_changed_callback(struct efx_dl_device *efx_dev, int mtu) +{ + return; +} + +static void efx_dummy_event_callback(struct efx_dl_device *efx_dev, void *event) +{ + return; +} + +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); + + 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); +} +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) +{ + struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); + struct efx_nic *efx = efx_handle->efx; + int rc = 0; + + /* Suspend net driver operations */ + efx_suspend(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)) { + rc = -EBUSY; + goto out; + } + + 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); + + out: + /* Resume net driver operations */ + efx_resume(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); + struct efx_nic *efx = efx_handle->efx; + + efx_schedule_reset(efx, RESET_TYPE_ALL); +} +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. + */ +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 */ + list_for_each_entry_reverse(efx_handle, + &efx->dl_device_list, + port_node) { + efx_dev = &efx_handle->efx_dev; + if (efx_dev->driver->reset_suspend) + efx_dev->driver->reset_suspend(efx_dev); + } +} + +/* + * 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. + */ +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 */ + list_for_each_entry(efx_handle, &efx->dl_device_list, + port_node) { + efx_dev = &efx_handle->efx_dev; + if (efx_dev->driver->reset_resume) + efx_dev->driver->reset_resume(efx_dev, ok); + } +} + +/** + * 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. + */ +struct efx_nic *efx_dl_get_nic(struct efx_dl_device *efx_dev) +{ + return efx_dl_handle(efx_dev)->efx; +} +EXPORT_SYMBOL(efx_dl_get_nic); diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/driverlink.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/driverlink.h Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,93 @@ +/**************************************************************************** + * 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_DRIVERLINK_H +#define EFX_DRIVERLINK_H + +/* Forward declarations */ +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_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_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 */ +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. */ +extern struct efx_nic *efx_dl_get_nic(struct efx_dl_device *efx_dev); + +#endif /* EFX_DRIVERLINK_H */ diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/driverlink_api.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/driverlink_api.h Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,612 @@ +/**************************************************************************** + * 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> + * + * 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 */ +#if !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 + +/** + * 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. + */ + +/* Forward declarations */ +struct pci_dev; +struct net_device; +struct sk_buff; +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. + */ +#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. + * + * @name: Name of the driver + * @probe: Called when device added + * @remove: Called when device removed + * @reset_suspend: Called before device is reset + * @reset_resume: Called after device is reset + */ +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: */ + struct list_head node; + struct list_head device_list; +}; + +/** + * 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. + * + * @EFX_DL_FALCON_RESOURCES: Information type is &struct efx_dl_falcon_resources + */ +enum efx_dl_device_info_type { + /** Falcon resources available for export */ + EFX_DL_FALCON_RESOURCES = 0, +}; + +/** + * 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; + enum efx_dl_device_info_type type; +}; + +/** + * enum efx_dl_falcon_resource_flags - Falcon resource information flags. + * + * Flags that describe hardware variations for the described Falcon based port. + * + * @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. + * @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. + */ +enum efx_dl_falcon_resource_flags { + EFX_DL_FALCON_DUAL_FUNC = 0x1, + EFX_DL_FALCON_USE_MSI = 0x2, +}; + +/** + * struct efx_dl_falcon_resources - Falcon resource information. + * + * This structure describes Falcon hardware resources available for + * use by a driverlink driver. + * + * @hdr: Resource linked list header + * @biu_lock: Register access lock. + * Some Falcon revisions require register access for configuration + * registers to be serialised between ports and PCI functions. + * The sfc driver will provide the appropriate lock semantics for + * the underlying hardware. + * @buffer_table_min: First available buffer table entry + * @buffer_table_max: Last available buffer table entry + 1 + * @evq_timer_min: First available event queue with timer + * @evq_timer_max: Last available event queue with timer + 1 + * @evq_int_min: First available event queue with interrupt + * @evq_int_max: Last available event queue with interrupt + 1 + * @rxq_min: First available RX queue + * @rxq_max: Last available RX queue + 1 + * @txq_min: First available TX queue + * @txq_max: Last available TX queue + 1 + * @flags: Hardware variation flags + */ +struct efx_dl_falcon_resources { + struct efx_dl_device_info hdr; + spinlock_t *biu_lock; + unsigned buffer_table_min, buffer_table_max; + unsigned evq_timer_min, evq_timer_max; + unsigned evq_int_min, evq_int_max; + unsigned rxq_min, rxq_max; + unsigned txq_min, txq_max; + 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. + * @priv: Driver private data + * Driverlink clients can use this to store a pointer to their + * internal per-device data structure. Each (driver, device) + * tuple has a separate &struct efx_dl_device, so clients can use + * this @priv field independently. + * @driver: Efx driverlink driver for this device + */ +struct efx_dl_device { + struct pci_dev *pci_dev; + void *priv; + struct efx_dl_driver *driver; +}; + +/** + * enum efx_veto - Packet veto request flag. + * + * This is the return type for the rx_packet() and tx_packet() methods + * in &struct efx_dl_callbacks. + * + * @EFX_ALLOW_PACKET: Packet may be transmitted/received + * @EFX_VETO_PACKET: Packet must not be transmitted/received + */ +enum efx_veto { + EFX_ALLOW_PACKET = 0, + EFX_VETO_PACKET = 1, +}; + +/** + * struct efx_dl_callbacks - Efx callbacks + * + * These methods can be hooked in to 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. + * + * @tx_packet: Called when packet is about to be transmitted + * @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 + */ +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_FASTCALL) + enum efx_veto fastcall (*tx_packet) (struct efx_dl_device *efx_dev, + struct sk_buff *skb); +#else + enum efx_veto (*tx_packet) (struct efx_dl_device *efx_dev, + struct sk_buff *skb); +#endif + + /* + * 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. + */ +#if defined(EFX_USE_FASTCALL) + enum efx_veto fastcall (*rx_packet) (struct efx_dl_device *efx_dev, + const char *pkt_hdr, int pkt_len); +#else + enum efx_veto (*rx_packet) (struct efx_dl_device *efx_dev, + const char *pkt_hdr, int pkt_len); +#endif + + /* + * 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. + */ + 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); +}; + +/* Include API version number in symbol used for efx_dl_register_driver */ +#define efx_dl_stringify_1(x, y) x ## y +#define efx_dl_stringify_2(x, y) efx_dl_stringify_1(x, y) +#define efx_dl_register_driver \ + efx_dl_stringify_2(efx_dl_register_driver_api_ver_, \ + EFX_DRIVERLINK_API_VERSION) + +extern int efx_dl_register_driver(struct efx_dl_driver *driver); + +extern void efx_dl_unregister_driver(struct efx_dl_driver *driver); + +extern int efx_dl_register_callbacks(struct efx_dl_device *efx_dev, + struct efx_dl_callbacks *callbacks); + +extern void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev, + struct efx_dl_callbacks *callbacks); + +extern void efx_dl_schedule_reset(struct efx_dl_device *efx_dev); + +/** + * efx_dl_for_each_device_info_matching - iterate an efx_dl_device_info list + * @_dev_info: Pointer to first &struct efx_dl_device_info + * @_type: Type code to look for + * @_info_type: Structure type corresponding to type code + * @_field: Name of &struct efx_dl_device_info field in the type + * @_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) { + * ..... + * } + * } + * } + */ +#define efx_dl_for_each_device_info_matching(_dev_info, _type, \ + _info_type, _field, _p) \ + for ((_p) = container_of((_dev_info), _info_type, _field); \ + (_p) != NULL; \ + (_p) = container_of((_p)->_field.next, _info_type, _field))\ + if ((_p)->_field.type != _type) \ + continue; \ + else + +/** + * efx_dl_search_device_info - search an efx_dl_device_info list + * @_dev_info: Pointer to first &struct efx_dl_device_info + * @_type: Type code to look for + * @_info_type: Structure type corresponding to type code + * @_field: Name of &struct efx_dl_device_info member in this type + * @_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) { + * .... + * } + * } + */ +#define efx_dl_search_device_info(_dev_info, _type, _info_type, \ + _field, _p) \ + efx_dl_for_each_device_info_matching((_dev_info), (_type), \ + _info_type, _field, (_p)) \ + break; + +#endif /* EFX_DRIVERLINK_API_H */ diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/efx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/efx.c Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,2783 @@ +/**************************************************************************** + * 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> + * + * 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> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/delay.h> +#include <linux/notifier.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <linux/in.h> +#include <linux/crc32.h> +#include <linux/ethtool.h> +#include <asm/uaccess.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" + +/************************************************************************** + * + * Type name strings + * + ************************************************************************** + */ + +/* Loopback mode names (see LOOPBACK_MODE()) */ +const unsigned int efx_loopback_mode_max = LOOPBACK_MAX; +const char *efx_loopback_mode_names[] = { + [LOOPBACK_NONE] = "NONE", + [LOOPBACK_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", +}; + +/* Interrupt mode names (see INT_MODE())) */ +const unsigned int efx_interrupt_mode_max = EFX_INT_MODE_MAX; +const char *efx_interrupt_mode_names[] = { + [EFX_INT_MODE_MSIX] = "MSI-X", + [EFX_INT_MODE_MSI] = "MSI", + [EFX_INT_MODE_LEGACY] = "legacy", +}; + +/* 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_INT_ERROR] = "INT_ERROR", + [RESET_TYPE_RX_RECOVERY] = "RX_RECOVERY", +}; + +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", +}; + +#define EFX_MAX_MTU (9 * 1024) + + +/************************************************************************** + * + * Configurable values + * + *************************************************************************/ + +/* + * 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; + +/* This is the weight assigned to each of the (per-channel) virtual + * NAPI devices. + */ +static int napi_weight = 64; + +/* This is the time (in jiffies) between invocations of the hardware + * monitor, which checks for known hardware bugs and resets the + * 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, + * such devices will be initialised with a random locally-generated + * MAC address. This allows for loading the efx_mtd driver to + * reprogram the flash, even if the flash contents (including the MAC + * address) have previously been erased. + */ +static unsigned int allow_bad_hwaddr; + +/* Initial interrupt moderation settings. They can be modified after + * module load with ethtool. + * + * The default for RX should strike a balance between increasing the + * round-trip latency and reducing overhead. + */ +static unsigned int rx_irq_mod_usec = 60; + +/* Initial interrupt moderation settings. They can be modified after + * module load with ethtool. + * + * This default is chosen to ensure that a 10G link does not go idle + * while a TX queue is stopped after it has become full. A queue is + * restarted when it drops below half full. The time this takes (assuming + * worst case 3 descriptors per packet and 1024 descriptors) is + * 512 / 3 * 1.2 = 205 usec. + */ +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; + +/* Set to 1 to enable the use of Message-Signalled Interrupts (MSI). + * MSI will not work on some motherboards due to limitations of the + * chipset, so the default is off. + * + * This is the highest capability interrupt mode to use + * 0 => MSI-X + * 1 => MSI + * 2 => legacy + */ +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 + * + * Default (0) means to use all CPUs in the system. This parameter + * can be set using "rss_cpus=xxx" when loading the module. + */ +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"); + +/************************************************************************** + * + * Utility functions and prototypes + * + *************************************************************************/ +static void efx_remove_channel(struct efx_channel *channel); +static void efx_remove_port(struct efx_nic *efx); +static void efx_fini_napi(struct efx_nic *efx); +static void efx_fini_channels(struct efx_nic *efx); + +/************************************************************************** + * + * Event queue processing + * + *************************************************************************/ + +/* Process channel's event queue + * + * This function is responsible for processing the event queue of a + * single channel. The caller must guarantee that this function will + * 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 || + !channel->enabled)) + return rx_quota; + + rxdmaqs = falcon_process_eventq(channel, &rx_quota); + + /* Deliver last RX packet. */ + if (channel->rx_pkt) { + __efx_rx_packet(channel, channel->rx_pkt, + channel->rx_pkt_csummed); + channel->rx_pkt = NULL; + } + + 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; +} + +/* Mark channel as finished processing + * + * Note that since we will not receive further interrupts for this + * channel before we finish processing and call the eventq_read_ack() + * method, there is no need to use the interrupt hold-off timers. + */ +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); +} + +/* NAPI poll handler + * + * NAPI guarantees serialisation of polls of the same device, which + * provides the guarantee required by efx_process_channel(). + */ +#if !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); + struct net_device *napi_dev = channel->napi_dev; +#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); +#endif + int unused; + 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); +#if defined(EFX_HAVE_OLD_NAPI) + napi_dev->quota -= rx_packets; + *budget_ret -= rx_packets; +#endif + + if (rx_packets < budget) { + /* There is no race here; although napi_disable() will + * only wait for netif_rx_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); + efx_channel_processed(channel); + } + +#if !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 + * + * Disable hardware generated interrupts, wait for any existing + * processing to finish, then directly poll (and ack ) the eventq. + * Finally reenable NAPI and interrupts. + * + * Since we are touching interrupts the caller should hold the suspend lock + */ +void efx_process_channel_now(struct efx_channel *channel) +{ + struct efx_nic *efx = channel->efx; + + BUG_ON(!channel->used_flags); + BUG_ON(!channel->enabled); + + /* Disable interrupts and wait for ISRs to complete */ + falcon_disable_interrupts(efx); + if (efx->legacy_irq) + synchronize_irq(efx->legacy_irq); + if (channel->has_interrupt && 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); + + /* 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); +} + +/* Create event queue + * Event queue memory allocations are done only once. If the channel + * is reset, the memory buffer will be reused; this guards against + * errors during channel reset and also simplifies interrupt handling. + */ +static int efx_probe_eventq(struct efx_channel *channel) +{ + EFX_LOG(channel->efx, "chan %d create event queue\n", channel->channel); + + return falcon_probe_eventq(channel); +} + +/* Prepare channel's event queue */ +static int efx_init_eventq(struct efx_channel *channel) +{ + EFX_LOG(channel->efx, "chan %d init event queue\n", channel->channel); + + ASSERT_RTNL(); + + /* Initialise fields */ + channel->eventq_read_ptr = 0; + + return falcon_init_eventq(channel); +} + +static void efx_fini_eventq(struct efx_channel *channel) +{ + EFX_LOG(channel->efx, "chan %d fini event queue\n", channel->channel); + + ASSERT_RTNL(); + + falcon_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); +} + +/************************************************************************** + * + * 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); + + /* Page-based allocation 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) +{ + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + int rc; + + EFX_LOG(channel->efx, "creating channel %d\n", channel->channel); + + rc = efx_probe_eventq(channel); + if (rc) + goto fail1; + + efx_for_each_channel_tx_queue(tx_queue, channel) { + rc = efx_probe_tx_queue(tx_queue); + if (rc) + goto fail2; + } + + efx_for_each_channel_rx_queue(rx_queue, channel) { + rc = efx_probe_rx_queue(rx_queue); + if (rc) + goto fail3; + } + + channel->n_rx_frm_trunc = 0; + + return 0; + + fail3: + efx_for_each_channel_rx_queue(rx_queue, channel) + efx_remove_rx_queue(rx_queue); + fail2: + efx_for_each_channel_tx_queue(tx_queue, channel) + efx_remove_tx_queue(tx_queue); + fail1: + return rc; +} + + +/* 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) +{ + 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); + + /* 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; + } + + /* 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; + } + + 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. + * + * Note that this function is not allowed to fail, since that would + * introduce too much complexity into the suspend/resume path. + */ +static void efx_start_channel(struct efx_channel *channel) +{ + struct efx_rx_queue *rx_queue; + + 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 */ + napi_enable(&channel->napi_str); + + /* Load up RX descriptors */ + efx_for_each_channel_rx_queue(rx_queue, channel) + efx_fast_push_rx_descriptors(rx_queue); +} + +/* This disables event queue processing and packet transmission. + * This function does not guarantee that all queue processing + * (e.g. RX refill) is complete. + */ +static void efx_stop_channel(struct efx_channel *channel) +{ + struct efx_rx_queue *rx_queue; + + if (!channel->enabled) + return; + + EFX_LOG(channel->efx, "stop chan %d\n", channel->channel); + + /* Mark channel as disabled */ + channel->enabled = 0; + + /* Wait for any NAPI processing to complete */ + napi_disable(&channel->napi_str); + + /* Ensure that any worker threads have exited or will be + * no-ops. + */ + efx_for_each_channel_rx_queue(rx_queue, channel) { + spin_lock_bh(&rx_queue->add_lock); + spin_unlock_bh(&rx_queue->add_lock); + } +} + +static void efx_fini_channels(struct efx_nic *efx) +{ + struct efx_channel *channel; + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + + ASSERT_RTNL(); + + efx_for_each_channel(channel, efx) { + EFX_LOG(channel->efx, "shut down chan %d\n", channel->channel); + + efx_for_each_channel_rx_queue(rx_queue, channel) + 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); + } +} + +static void efx_remove_channel(struct efx_channel *channel) +{ + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + + EFX_LOG(channel->efx, "destroy chan %d\n", channel->channel); + + efx_for_each_channel_rx_queue(rx_queue, channel) + efx_remove_rx_queue(rx_queue); + efx_for_each_channel_tx_queue(tx_queue, channel) + efx_remove_tx_queue(tx_queue); + efx_remove_eventq(channel); + + channel->used_flags = 0; +} + +/************************************************************************** + * + * Port handling + * + **************************************************************************/ + +/* This ensures that the kernel is kept informed (via + * 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) +{ + unsigned long flags __attribute__ ((unused)); + int carrier_ok; + + /* Ensure no link status notifications get sent to the OS after the net + * device has been unregistered. */ + if (!efx->net_dev_registered) + return; + + carrier_ok = netif_carrier_ok(efx->net_dev) ? 1 : 0; + if (efx->link_up != carrier_ok) { + efx->n_link_state_changes++; + + if (efx->link_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, + efx->net_dev->mtu, + (efx->loopback_mode ? " [" : ""), + (efx->loopback_mode ? LOOPBACK_MODE(efx) : ""), + (efx->loopback_mode ? " LOOPBACK]" : ""), + (efx->promiscuous ? " [PROMISC]" : "")); + } else { + EFX_INFO(efx, "link down\n"); + } + +} + +/* This call reinitialises the MAC to pick up new PHY settings + * To call from a context that cannot sleep use reconfigure_work work item + * For on_disabled=1 the caller must be serialised against efx_reset, + * ideally by holding the rtnl lock. + */ +void efx_reconfigure_port(struct efx_nic *efx, int on_disabled) +{ + mutex_lock(&efx->mac_lock); + + EFX_LOG(efx, "reconfiguring MAC from PHY settings\n"); + + if (on_disabled) + ASSERT_RTNL(); + else if (!efx->port_enabled) + goto out; + + efx->mac_op->reconfigure(efx); + +out: + /* Inform kernel of loss/gain of carrier */ + efx_link_status_changed(efx); + + mutex_unlock(&efx->mac_lock); +} + +static void efx_reconfigure_work(struct work_struct *data) +{ + struct efx_nic *efx = container_of(data, struct efx_nic, + reconfigure_work); + + EFX_LOG(efx, "MAC reconfigure executing on CPU %d\n", + raw_smp_processor_id()); + + /* Reinitialise MAC to activate new PHY parameters */ + efx_reconfigure_port(efx, 0); +} + +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 (rc) + goto err; + + /* Sanity check MAC address */ + dev_addr = efx->mac_address; + if (!is_valid_ether_addr(dev_addr)) { + DECLARE_MAC_BUF(mac); + + EFX_ERR(efx, "invalid MAC address %s\n", + print_mac(mac, dev_addr)); + if (!allow_bad_hwaddr) { + rc = -EINVAL; + goto err; + } + random_ether_addr(dev_addr); + EFX_INFO(efx, "using locally-generated MAC %s\n", + print_mac(mac, dev_addr)); + } + + /* Register debugfs entries */ + rc = efx_init_debugfs_port(efx); + if (rc) + goto err; + + return 0; + + err: + efx_remove_port(efx); + return rc; +} + +static int efx_init_port(struct efx_nic *efx) +{ + int rc; + + 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); + if (rc) + return rc; + + efx->port_initialized = 1; + + /* Reconfigure port to program MAC registers */ + efx->mac_op->reconfigure(efx); + + return 0; +} + +/* Allow efx_reconfigure_port() to run, and propagate delayed changes + * to the promiscuous flag to the MAC if needed */ +static void efx_start_port(struct efx_nic *efx) +{ + EFX_LOG(efx, "start port\n"); + ASSERT_RTNL(); + + BUG_ON(efx->port_enabled); + + mutex_lock(&efx->mac_lock); + efx->port_enabled = 1; + mutex_unlock(&efx->mac_lock); + + if (efx->net_dev_registered) { + int promiscuous; + + netif_tx_lock_bh(efx->net_dev); + promiscuous = (efx->net_dev->flags & IFF_PROMISC) ? 1 : 0; + if (efx->promiscuous != promiscuous) { + efx->promiscuous = promiscuous; + queue_work(efx->workqueue, &efx->reconfigure_work); + } + netif_tx_unlock_bh(efx->net_dev); + } +} + +/* Prevents efx_reconfigure_port() from executing, and prevents + * 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 */ +static void efx_stop_port(struct efx_nic *efx) +{ + EFX_LOG(efx, "stop port\n"); + ASSERT_RTNL(); + + mutex_lock(&efx->mac_lock); + efx->port_enabled = 0; + mutex_unlock(&efx->mac_lock); + + /* Serialise against efx_set_multicast_list() */ + if (efx->net_dev_registered) { + netif_tx_lock_bh(efx->net_dev); + netif_tx_unlock_bh(efx->net_dev); + } +} + +static void efx_fini_port(struct efx_nic *efx) +{ + EFX_LOG(efx, "shut down port\n"); + + if (!efx->port_initialized) + return; + + efx->mac_op->fini(efx); + efx->port_initialized = 0; + + /* Mark the link down */ + efx->link_up = 0; + efx_link_status_changed(efx); +} + +static void efx_remove_port(struct efx_nic *efx) +{ + EFX_LOG(efx, "destroying port\n"); + + efx_fini_debugfs_port(efx); + falcon_remove_port(efx); +} + +/************************************************************************** + * + * NIC handling + * + **************************************************************************/ + +/* This configures the PCI device to enable I/O and DMA. */ +static int efx_init_io(struct efx_nic *efx) +{ + struct pci_dev *pci_dev = efx->pci_dev; + int rc; + + 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"); + goto fail1; + } + + pci_set_master(pci_dev); + + /* Set the PCI DMA mask. Try all possibilities from our + * genuine mask down to 32 bits, because some architectures + * (e.g. x86_64 with iommu_sac_force set) will allow 40 bit + * masks event though they reject 46 bit masks. + */ + efx->dma_mask = efx->type->max_dma_mask; + while (efx->dma_mask > 0x7fffffffUL) { + if (pci_dma_supported(pci_dev, efx->dma_mask) && + ((rc = pci_set_dma_mask(pci_dev, efx->dma_mask)) == 0)) + break; + efx->dma_mask >>= 1; + } + if (rc) { + EFX_ERR(efx, "could not find a suitable DMA mask\n"); + goto fail2; + } + EFX_LOG(efx, "using DMA mask %llx\n", + (unsigned long long)efx->dma_mask); + rc = pci_set_consistent_dma_mask(pci_dev, efx->dma_mask); + if (rc) { + /* pci_set_consistent_dma_mask() is not *allowed* to + * fail with a mask that pci_set_dma_mask() accepted, + * but just in case... + */ + EFX_ERR(efx, "failed to set consistent DMA mask\n"); + goto fail2; + } + + /* Get memory base address */ + efx->membase_phys = pci_resource_start(efx->pci_dev, + efx->type->mem_bar); +#if !defined(EFX_HAVE_MSIX_TABLE_RESERVED) + rc = pci_request_region(pci_dev, efx->type->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; + goto fail3; + } + 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->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); + + return 0; + + fail4: + release_mem_region(efx->membase_phys, efx->type->mem_map_size); + fail3: + efx->membase_phys = 0UL; + /* fall-thru */ + fail2: + pci_disable_device(efx->pci_dev); + fail1: + return rc; +} + +static void efx_fini_io(struct efx_nic *efx) +{ + EFX_LOG(efx, "shutting down I/O\n"); + + if (efx->membase) { + iounmap(efx->membase); + efx->membase = NULL; + } + + if (efx->membase_phys) { +#if !defined(EFX_HAVE_MSIX_TABLE_RESERVED) + pci_release_region(efx->pci_dev, efx->type->mem_bar); +#else + release_mem_region(efx->membase_phys, efx->type->mem_map_size); +#endif + efx->membase_phys = 0UL; + } + + pci_disable_device(efx->pci_dev); +} + +/* Probe the number and type of interrupts we are able to obtain. */ +static int efx_probe_interrupts(struct efx_nic *efx) +{ + struct msix_entry xentries[EFX_MAX_CHANNELS]; + int rc, i; + + /* Select number of used RSS queues */ + /* TODO: Can we react to CPU hotplug? */ + if (rss_cpus == 0) + rss_cpus = num_online_cpus(); + + efx->rss_queues = 1; + if (efx->interrupt_mode == EFX_INT_MODE_MSIX) { + unsigned int max_channel = efx->type->phys_addr_channels - 1; + + BUG_ON(!pci_find_capability(efx->pci_dev, PCI_CAP_ID_MSIX)); + efx->rss_queues = min(max_channel + 1, rss_cpus); + efx->rss_queues = min(efx->rss_queues, EFX_MAX_CHANNELS); + } + + /* Determine how many RSS queues we can use, and mark channels + * with the appropriate interrupt state */ + if (efx->interrupt_mode == EFX_INT_MODE_MSIX) { + /* Build MSI request structure */ + for (i = 0; i < efx->rss_queues; i++) + xentries[i].entry = i; + + /* Request maximum number of MSI interrupts */ + 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->channel[i].irq = xentries[i].vector; + } + } else { + /* Fall back to single channel MSI */ + efx->interrupt_mode = EFX_INT_MODE_MSI; + EFX_ERR(efx, "could not enable MSI-X\n"); + } + } + + /* Try single interrupt MSI */ + if (efx->interrupt_mode == EFX_INT_MODE_MSI) { + efx->rss_queues = 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; + } + } + + /* Assume legacy interrupts */ + if (efx->interrupt_mode == EFX_INT_MODE_LEGACY) { + /* Every channel is interruptible */ + for (i = 0; i < EFX_MAX_CHANNELS; i++) + efx->channel[i].has_interrupt = 1; + efx->legacy_irq = efx->pci_dev->irq; + } + + return 0; +} + +static void efx_remove_interrupts(struct efx_nic *efx) +{ + struct efx_channel *channel; + + /* Remove MSI/MSI-X interrupts */ + efx_for_each_channel_with_interrupt(channel, efx) + channel->irq = 0; + pci_disable_msi(efx->pci_dev); + pci_disable_msix(efx->pci_dev); + + /* Remove legacy interrupt */ + efx->legacy_irq = 0; +} + +/* Select number of used resources + * Should be called after probe_interrupts() + */ +static int efx_select_used(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++; + } + } + return 0; +} + +static int efx_probe_nic(struct efx_nic *efx) +{ + int rc; + + EFX_LOG(efx, "creating NIC\n"); + + /* Carry out hardware-type specific initialisation */ + rc = falcon_probe_nic(efx); + if (rc) + goto fail1; + + /* Determine the number of channels and RX queues by trying to hook + * in MSI-X interrupts. */ + rc = efx_probe_interrupts(efx); + if (rc) + goto fail2; + + /* Determine number of RX queues and TX queues */ + rc = efx_select_used(efx); + if (rc) + goto fail3; + + /* Register debugfs entries */ + rc = efx_init_debugfs_nic(efx); + if (rc) + goto fail4; + /* Initialise the interrupt moderation settings */ + efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec); + + return 0; + + fail4: + /* fall-thru */ + fail3: + efx_remove_interrupts(efx); + fail2: + falcon_remove_nic(efx); + fail1: + return rc; +} + +static void efx_remove_nic(struct efx_nic *efx) +{ + EFX_LOG(efx, "destroying NIC\n"); + + efx_remove_interrupts(efx); + falcon_remove_nic(efx); + + efx_fini_debugfs_nic(efx); +} + +/************************************************************************** + * + * NIC startup/shutdown + * + *************************************************************************/ + +static int efx_probe_all(struct efx_nic *efx) +{ + struct efx_channel *channel; + int rc; + + /* Create NIC */ + rc = efx_probe_nic(efx); + if (rc) { + EFX_ERR(efx, "failed to create NIC\n"); + goto fail1; + } + + /* Create port */ + rc = efx_probe_port(efx); + if (rc) { + EFX_ERR(efx, "failed to create port\n"); + goto fail2; + } + + /* Create channels */ + efx_for_each_channel(channel, efx) { + rc = efx_probe_channel(channel); + if (rc) { + EFX_ERR(efx, "failed to create channel %d\n", + channel->channel); + goto fail3; + } + } + + return 0; + + fail3: + efx_for_each_channel(channel, efx) + efx_remove_channel(channel); + fail2: + efx_remove_port(efx); + fail1: + return rc; +} + +/* Called after previous invocation(s) of efx_stop_all, restarts the + * port, kernel transmit queue, NAPI processing and hardware interrupts. + * This function is safe to call multiple times when the NIC is in any + * state. */ +static void efx_start_all(struct efx_nic *efx) +{ + struct efx_channel *channel; + + ASSERT_RTNL(); + + /* Check that it is appropriate to restart the interface. All + * of these flags are safe to read under just the rtnl lock */ + if (efx->port_enabled) + return; + if ((efx->state != STATE_RUNNING) && (efx->state != STATE_INIT)) + return; + if (efx->net_dev_registered && !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); + + 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) + queue_delayed_work(efx->workqueue, &efx->monitor_work, + efx_monitor_interval); +} + +/* Flush all delayed work. Should only be called when no more delayed work + * will be scheduled. This doesn't flush pending online resets (efx_reset), + * since we're holding the rtnl_lock at this point. */ +static void efx_flush_all(struct efx_nic *efx) +{ +#if 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_CANCEL_WORK_SYNC) + /* Stop scheduled port reconfigurations */ + cancel_work_sync(&efx->reconfigure_work); +#endif + +#if !defined(EFX_USE_CANCEL_DELAYED_WORK_SYNC) + /* 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 (timer_pending(&efx->monitor_work.timer)) + cancel_delayed_work(&efx->monitor_work); + flush_workqueue(efx->workqueue); + if (timer_pending(&efx->monitor_work.timer)) + cancel_delayed_work(&efx->monitor_work); + flush_workqueue(efx->workqueue); + + /* efx_rx_work will disarm if !channel->enabled, so we can just + * flush the refill workqueue twice as well. */ + flush_workqueue(efx->refill_workqueue); + flush_workqueue(efx->refill_workqueue); +#endif +} + +/* Quiesce hardware and software without bringing the link down. + * Safe to call multiple times, when the nic and interface is in any + * 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) +{ + struct efx_channel *channel; + + ASSERT_RTNL(); + + /* port_enabled can be read safely under the rtnl lock */ + if (!efx->port_enabled) + return; + + /* Disable interrupts and wait for ISR to complete */ + falcon_disable_interrupts(efx); + if (efx->legacy_irq) + synchronize_irq(efx->legacy_irq); + efx_for_each_channel_with_interrupt(channel, efx) + if (channel->irq) + synchronize_irq(channel->irq); + + /* Stop all synchronous port reconfigurations. */ + efx_stop_port(efx); + + /* Stop all NAPI processing and synchronous rx refills */ + efx_for_each_channel(channel, efx) + efx_stop_channel(channel); + + /* Flush reconfigure_work, refill_workqueue, monitor_work */ + efx_flush_all(efx); + + /* Stop the kernel transmit interface late, so the watchdog + * timer isn't ticking over the flush */ + efx_stop_queue(efx); + if (efx->net_dev_registered) { + netif_tx_lock_bh(efx->net_dev); + netif_tx_unlock_bh(efx->net_dev); + } +} + +static void efx_remove_all(struct efx_nic *efx) +{ + struct efx_channel *channel; + + efx_for_each_channel(channel, efx) + efx_remove_channel(channel); + efx_remove_port(efx); + 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; +} + +int efx_flush_queues(struct efx_nic *efx) +{ + int rc; + + ASSERT_RTNL(); + + efx_stop_all(efx); + + /* We can't just flush the tx queues because the event queues + * may contain tx completions from that queue. Just flush everything */ + 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 + * + **************************************************************************/ + +/* Set interrupt moderation parameters */ +void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs) +{ + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + + ASSERT_RTNL(); + + efx_for_each_tx_queue(tx_queue, efx) + tx_queue->channel->irq_moderation = tx_usecs; + + efx_for_each_rx_queue(rx_queue, efx) + rx_queue->channel->irq_moderation = rx_usecs; +} + +/************************************************************************** + * + * Hardware monitor + * + **************************************************************************/ + +/* Run periodically off the general workqueue. Serialised against + * efx_reconfigure_port via the mac_lock */ +static void efx_monitor(struct work_struct *data) +{ +#if !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); +#endif + int rc = 0; + + EFX_TRACE(efx, "hardware monitor executing on CPU %d\n", + raw_smp_processor_id()); + + +#if !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 */ + 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)) { + queue_delayed_work(efx->workqueue, &efx->monitor_work, + efx_monitor_interval); + return; + } + + if (efx->port_enabled) + rc = efx->mac_op->check_hw(efx); +#endif + 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"); + } + } + + queue_delayed_work(efx->workqueue, &efx->monitor_work, + efx_monitor_interval); +} + +/************************************************************************** + * + * ioctls + * + *************************************************************************/ + +/* Net device ioctl + * Context: process, rtnl_lock() held. + */ +static int efx_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd) +{ + struct efx_nic *efx = net_dev->priv; + int rc; + + ASSERT_RTNL(); + + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + rc = generic_mii_ioctl(&efx->mii, if_mii(ifr), cmd, NULL); + break; + case SIOCSMIIREG: + rc = generic_mii_ioctl(&efx->mii, if_mii(ifr), cmd, NULL); + efx_reconfigure_port(efx, 0); + break; + default: + rc = -EOPNOTSUPP; + } + + return rc; +} + +/************************************************************************** + * + * NAPI interface + * + **************************************************************************/ + +/* 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; + + ASSERT_RTNL(); + + /* 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; + + /* Set features based on module parameters and DMA mask. + * Enable DMA to ZONE_HIGHMEM if the NIC can access all memory + * directly. This only has an effect on 32-bit systems and + * PAE on x86 limits memory to 64GB so 40 bits is plenty to + * address everything. If the device can't address 40 bits + * then it's safest to turn NETIF_F_HIGHDMA off because this + * might be a PAE system with more than 4G of RAM and a 32-bit + * NIC. The use of EFX_DMA_MASK is to eliminate compiler + * warnings on platforms where dma_addr_t is 32-bit. We + * assume that in those cases we can access all memory + * directly if our DMA mask is all ones. */ + efx->net_dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; + if (efx->dma_mask >= EFX_DMA_MASK(DMA_40BIT_MASK)) + efx->net_dev->features |= NETIF_F_HIGHDMA; + + /* 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_HAVE_OLD_NAPI) + channel->napi_dev = efx->net_dev; +#else + channel->napi_dev = alloc_etherdev(0); + if (!channel->napi_dev) { + rc = -ENOMEM; + goto err; + } + channel->napi_dev->priv = channel; + atomic_set(&channel->napi_dev->refcnt, 1); +#endif + } + + 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; + + ASSERT_RTNL(); + + efx_for_each_channel(channel, efx) { + /* Finish per channel NAPI */ +#if 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; + } +} + +/************************************************************************** + * + * Kernel netpoll interface + * + *************************************************************************/ + +#ifdef CONFIG_NET_POLL_CONTROLLER + +/* Although in the common case interrupts will be disabled, this is not + * guaranteed. However, all our work happens inside the NAPI callback, + * so no locking is required. + */ +static void efx_netpoll(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + struct efx_channel *channel; + + efx_for_each_channel_with_interrupt(channel, efx) + efx_schedule_channel(channel); +} + +#endif + +/************************************************************************** + * + * Kernel net device interface + * + *************************************************************************/ + +/* Context: process, rtnl_lock() held. */ +static int efx_net_open(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + ASSERT_RTNL(); + + EFX_LOG(efx, "opening device %s on CPU %d\n", net_dev->name, + raw_smp_processor_id()); + efx_start_all(efx); + return 0; +} + +/* Context: process, rtnl_lock() held. + * Note that the kernel will ignore our return code; this method + * should really be a void. + */ +static int efx_net_stop(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + int rc; + + EFX_LOG(efx, "closing %s on CPU %d\n", net_dev->name, + raw_smp_processor_id()); + + /* Stop 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); + + return 0; +} + +/* Context: process, dev_base_lock held, non-blocking. + * Statistics are taken directly from the MAC. + */ +static struct net_device_stats *efx_net_stats(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + struct efx_mac_stats *mac_stats = &efx->mac_stats; + 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); + spin_unlock(&efx->stats_lock); + + stats->rx_packets = mac_stats->rx_packets; + stats->tx_packets = mac_stats->tx_packets; + stats->rx_bytes = mac_stats->rx_bytes; + stats->tx_bytes = mac_stats->tx_bytes; + stats->tx_errors = mac_stats->tx_bad; + stats->multicast = mac_stats->rx_multicast; + stats->collisions = mac_stats->tx_collision; + stats->rx_length_errors = mac_stats->rx_gtjumbo; + stats->rx_over_errors = mac_stats->rx_overflow; + stats->rx_crc_errors = mac_stats->rx_bad; + stats->rx_frame_errors = mac_stats->rx_align_error; + stats->rx_fifo_errors = 0; + stats->rx_missed_errors = mac_stats->rx_missed; + stats->rx_errors = (stats->rx_length_errors + + stats->rx_over_errors + + stats->rx_crc_errors + + stats->rx_frame_errors + + stats->rx_fifo_errors + + stats->rx_missed_errors + + mac_stats->rx_symbol_error); + stats->tx_aborted_errors = 0; + stats->tx_carrier_errors = 0; + stats->tx_fifo_errors = 0; + stats->tx_heartbeat_errors = 0; + stats->tx_window_errors = 0; + + return stats; +} + +/* 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); +} + + +/* 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; + int rc = 0; + + ASSERT_RTNL(); + + if (new_mtu > EFX_MAX_MTU) + return -EINVAL; + + efx_stop_all(efx); + + /* Ask driverlink client if we can change MTU */ + rc = EFX_DL_CALLBACK(efx, request_mtu, new_mtu); + if (rc) { + EFX_ERR(efx, "MTU change vetoed by driverlink %s driver\n", + efx->dl_cb_dev.request_mtu->driver->name); + goto out; + } + + EFX_LOG(efx, "changing MTU to %d\n", new_mtu); + + efx_fini_channels(efx); + net_dev->mtu = new_mtu; + rc = efx_init_channels(efx); + if (rc) + goto fail; + + /* Reconfigure the MAC */ + efx_reconfigure_port(efx, 1); + + /* Notify driverlink client of new MTU */ + EFX_DL_CALLBACK(efx, mtu_changed, new_mtu); + + efx_start_all(efx); + + out: + 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 sockaddr *addr = data; + char *new_addr = addr->sa_data; + + ASSERT_RTNL(); + + if (!is_valid_ether_addr(new_addr)) { + DECLARE_MAC_BUF(mac); + EFX_ERR(efx, "invalid ethernet MAC address requested: %s\n", + print_mac(mac, new_addr)); + return -EINVAL; + } + + memcpy(net_dev->dev_addr, new_addr, net_dev->addr_len); + + /* Reconfigure the MAC */ + efx_reconfigure_port(efx, 1); + + return 0; +} + +/* Context: netif_tx_lock held, BHs disabled. */ +static void efx_set_multicast_list(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + struct dev_mc_list *mc_list = net_dev->mc_list; + union efx_multicast_hash *mc_hash = &efx->multicast_hash; + unsigned long flags __attribute__ ((unused)); + 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) { + if (efx->port_enabled) { + efx->promiscuous = promiscuous; + queue_work(efx->workqueue, &efx->reconfigure_work); + } + } + + /* Build multicast hash table */ + if (promiscuous || (net_dev->flags & IFF_ALLMULTI)) { + memset(mc_hash, 0xff, sizeof(*mc_hash)); + } else { + memset(mc_hash, 0x00, sizeof(*mc_hash)); + for (i = 0; i < net_dev->mc_count; i++) { + crc = ether_crc_le(ETH_ALEN, mc_list->dmi_addr); + bit = (crc & ((1 << EFX_MCAST_HASH_BITS) - 1)); + set_bit_le(bit, (void *)mc_hash); + mc_list = mc_list->next; + } + } + + /* Create and activate new global multicast hash table */ + falcon_set_multicast_hash(efx); +} + +/* Handle net device notifier events */ +static int efx_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *net_dev = (struct net_device *)ptr; + + 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); + } + + return NOTIFY_DONE; +} + +static struct notifier_block efx_netdev_notifier = { + .notifier_call = efx_netdev_event, +}; + +static int efx_register_netdev(struct efx_nic *efx) +{ + struct net_device *net_dev = efx->net_dev; + int rc; + + net_dev->watchdog_timeo = 5 * HZ; + net_dev->irq = efx->pci_dev->irq; + 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->hard_start_xmit = efx_hard_start_xmit; + net_dev->do_ioctl = efx_ioctl; + net_dev->change_mtu = efx_change_mtu; + net_dev->set_mac_address = efx_set_mac_address; + net_dev->set_multicast_list = efx_set_multicast_list; +#ifdef CONFIG_NET_POLL_CONTROLLER + net_dev->poll_controller = efx_netpoll; +#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); + + BUG_ON(efx->net_dev_registered); + + /* 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); + + /* 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; + } + + /* Allow link change notifications to be sent to the operating + * system. The must happen after register_netdev so that + * there are no outstanding link changes if that call fails. + * It must happen before efx_reconfigure_port so that the + * initial state of the link is reported. */ + mutex_lock(&efx->mac_lock); + efx->net_dev_registered = 1; + mutex_unlock(&efx->mac_lock); + + /* Safety net: in case we don't get a PHY event */ + rtnl_lock(); + efx_reconfigure_port(efx, 1); + rtnl_unlock(); + + EFX_LOG(efx, "registered\n"); + + return 0; +} + +static void efx_unregister_netdev(struct efx_nic *efx) +{ + int was_registered = efx->net_dev_registered; + struct efx_tx_queue *tx_queue; + + if (!efx->net_dev) + return; + + BUG_ON(efx->net_dev->priv != efx); + + /* SFC Bug 5356: Ensure that no more link status notifications get + * sent to the stack. Bad things happen if there's an + * outstanding notification after the net device is freed, but + * they only get flushed out by unregister_netdev, not by + * free_netdev. */ + mutex_lock(&efx->mac_lock); + efx->net_dev_registered = 0; + mutex_unlock(&efx->mac_lock); + + /* Free up any skbs still remaining. This has to happen before + * we try to unregister the netdev as running their destructors + * may be needed to get the device ref. count to 0. */ + efx_for_each_tx_queue(tx_queue, efx) + efx_release_tx_buffers(tx_queue); + + if (was_registered) { + strlcpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name)); + efx_fini_debugfs_netdev(efx->net_dev); + unregister_netdev(efx->net_dev); + } +} + +/************************************************************************** + * + * Device reset and suspend + * + **************************************************************************/ + +/* This suspends the device (and acquires the suspend lock) without + * flushing the descriptor queues. It is included for the convenience + * of the driverlink layer. + */ +void efx_suspend(struct efx_nic *efx) +{ + EFX_LOG(efx, "suspending operations\n"); + + down(&efx->suspend_lock); + + 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(); + + up(&efx->suspend_lock); +} + +/* 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; + + ASSERT_RTNL(); + + 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; + + /* In an INVISIBLE_RESET there might not be a link state transition, + * so we push the multicast list here. */ + falcon_set_multicast_hash(efx); + + /* 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. + */ +static int efx_reset(struct efx_nic *efx) +{ + struct ethtool_cmd ecmd; + unsigned long flags __attribute__ ((unused)); + enum reset_type method = efx->reset_pending; + int rc; + + efx_dl_reset_lock(); + + rc = down_interruptible(&efx->suspend_lock); + if (rc) { + EFX_ERR(efx, "reset aborted by signal\n"); + goto unlock_dl_lock; + } + + /* We've got suspend_lock, which means we can only be in + * STATE_RUNNING or STATE_FINI. Don't clear + * efx->reset_pending, since this flag indicates that we + * should retry device initialisation. + */ + if (efx->state != STATE_RUNNING) { + EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n"); + goto unlock_suspend_lock; + } + + /* Notify driverlink clients of imminent reset. */ + efx_dl_reset_suspend(efx); + rtnl_lock(); + + 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); + if (rc) { + EFX_ERR(efx, "failed to reset hardware\n"); + goto fail2; + } + + /* Allow resets to be rescheduled. */ + efx->reset_pending = RESET_TYPE_NONE; + + /* Reinitialise bus-mastering, which may have been turned off before + * the reset was scheduled. This is still appropriate, even in the + * RESET_TYPE_DISABLE since this driver generally assumes the hardware + * 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; + } + + /* 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_reconfigure_port(efx, 1); + EFX_LOG(efx, "reset complete\n"); + + efx->state = STATE_RUNNING; + efx_start_all(efx); + + rtnl_unlock(); + + goto notify; + + fail5: + fail4: + fail3: + fail2: + fail1: + EFX_ERR(efx, "has been disabled\n"); + efx->state = STATE_DISABLED; + + /* Remove the net_dev */ + mutex_unlock(&efx->mac_lock); + rtnl_unlock(); + efx_unregister_netdev(efx); + efx_fini_port(efx); + + notify: + /* Notify driverlink clients of completed reset */ + efx_dl_reset_resume(efx, (rc == 0)); + + unlock_suspend_lock: + up(&efx->suspend_lock); + + unlock_dl_lock: + efx_dl_reset_unlock(); + + return rc; +} + +/* The worker thread exists so that code that cannot sleep can + * schedule a reset for later. + */ +static void efx_reset_work(struct work_struct *data) +{ + struct efx_nic *nic = container_of(data, struct efx_nic, reset_work); + + efx_reset(nic); +} + +void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) +{ + enum reset_type method; + + if (efx->reset_pending != RESET_TYPE_NONE) { + EFX_INFO(efx, "quenching already scheduled reset\n"); + return; + } + + switch (type) { + case RESET_TYPE_INVISIBLE: + case RESET_TYPE_ALL: + case RESET_TYPE_WORLD: + case RESET_TYPE_DISABLE: + method = type; + break; + case RESET_TYPE_RX_RECOVERY: + case RESET_TYPE_RX_DESC_FETCH: + case RESET_TYPE_TX_DESC_FETCH: + method = RESET_TYPE_INVISIBLE; + break; + default: + method = RESET_TYPE_ALL; + break; + } + + if (method != type) + EFX_LOG(efx, "scheduling %s reset for %s\n", + RESET_TYPE(method), RESET_TYPE(type)); + else + EFX_LOG(efx, "scheduling %s reset\n", RESET_TYPE(method)); + + efx->reset_pending = method; + +#if !defined(EFX_USE_CANCEL_DELAYED_WORK_SYNC) + queue_work(efx->reset_workqueue, &efx->reset_work); +#else + queue_work(efx->workqueue, &efx->reset_work); +#endif +} + +/************************************************************************** + * + * List of NICs we support + * + **************************************************************************/ + +enum efx_type_index { + EFX_TYPE_FALCON_A = 0, + EFX_TYPE_FALCON_B = 1, +}; + +static struct efx_nic_type *efx_nic_types[] = { + [EFX_TYPE_FALCON_A] = &falcon_a_nic_type, + [EFX_TYPE_FALCON_B] = &falcon_b_nic_type, +}; + + +/* PCI device ID table */ +static struct pci_device_id efx_pci_table[] __devinitdata = { + {EFX_VENDID_SFC, FALCON_A_P_DEVID, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, EFX_TYPE_FALCON_A}, + {EFX_VENDID_SFC, FALCON_B_P_DEVID, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, EFX_TYPE_FALCON_B}, + {0} /* end of list */ +}; + +/************************************************************************** + * + * Dummy PHY/MAC/Board operations + * + * Can be used where the MAC does not implement this operation + * Needed so all function pointers are valid and do not have to be tested + * before use + * + **************************************************************************/ +int efx_port_dummy_op_int(struct efx_nic *efx) +{ + return 0; +} +void efx_port_dummy_op_void(struct efx_nic *efx) {} +void efx_port_dummy_op_blink(struct efx_nic *efx, 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, +}; + +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, + .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, +}; + +/************************************************************************** + * + * Data housekeeping + * + **************************************************************************/ + +/* This zeroes out and then fills in the invariants in a struct + * efx_nic (including all sub-structures). + */ +static int efx_init_struct(struct efx_nic *efx, enum efx_type_index type, + struct pci_dev *pci_dev) +{ + struct efx_channel *channel; + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + int i, rc; + + /* Initialise common structures */ + memset(efx, 0, sizeof(*efx)); + spin_lock_init(&efx->biu_lock); + spin_lock_init(&efx->phy_lock); + mutex_init(&efx->spi_lock); + sema_init(&efx->suspend_lock, 1); + 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; + 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->phy_op = &efx_dummy_phy_operations; + 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); + 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++) { + 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 */ + } + for (i = 0; i < EFX_MAX_RX_QUEUES; i++) { + rx_queue = &efx->rx_queue[i]; + rx_queue->efx = efx; + rx_queue->queue = i; + rx_queue->channel = &efx->channel[0]; /* for safety */ + rx_queue->buffer = NULL; + spin_lock_init(&rx_queue->add_lock); + INIT_DELAYED_WORK(&rx_queue->work, efx_rx_work); + } + + efx->type = efx_nic_types[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)); + + 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); +#if defined(EFX_NEED_DUMMY_MSIX) + if (efx->interrupt_mode == EFX_INT_MODE_MSIX) + efx->interrupt_mode = EFX_INT_MODE_MSI; +#endif + + /* Tasks that can fail are last */ + efx->refill_workqueue = create_workqueue("sfc_refill"); + if (!efx->refill_workqueue) { + rc = -ENOMEM; + goto fail1; + } + + efx->workqueue = create_singlethread_workqueue("sfc_work"); + if (!efx->workqueue) { + rc = -ENOMEM; + goto fail2; + } + +#if !defined(EFX_USE_CANCEL_DELAYED_WORK_SYNC) + efx->reset_workqueue = create_singlethread_workqueue("sfc_reset"); + if (!efx->reset_workqueue) { + rc = -ENOMEM; + goto fail3; + } +#endif + + return 0; + +#if !defined(EFX_USE_CANCEL_DELAYED_WORK_SYNC) + fail3: + destroy_workqueue(efx->workqueue); + efx->workqueue = NULL; +#endif + + fail2: + destroy_workqueue(efx->refill_workqueue); + efx->refill_workqueue = NULL; + fail1: + return rc; +} + +static void efx_fini_struct(struct efx_nic *efx) +{ +#if !defined(EFX_USE_CANCEL_DELAYED_WORK_SYNC) + if (efx->reset_workqueue) { + destroy_workqueue(efx->reset_workqueue); + efx->reset_workqueue = NULL; + } +#endif + if (efx->workqueue) { + destroy_workqueue(efx->workqueue); + efx->workqueue = NULL; + } + if (efx->refill_workqueue) { + destroy_workqueue(efx->refill_workqueue); + efx->refill_workqueue = NULL; + } +} + +/************************************************************************** + * + * PCI interface + * + **************************************************************************/ + +/* Main body of final NIC shutdown code + * This is called only at module unload (or hotplug removal). + */ +static void efx_pci_remove_main(struct efx_nic *efx) +{ + ASSERT_RTNL(); + + /* Skip everything if we never obtained a valid membase */ + if (!efx->membase) + return; + + 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_fini_napi(efx); + efx_remove_all(efx); +} + +/* Final NIC shutdown + * This is called only at module unload (or hotplug removal). + */ +static void efx_pci_remove(struct pci_dev *pci_dev) +{ + struct efx_nic *efx; + + efx = pci_get_drvdata(pci_dev); + if (!efx) + return; + + /* Unregister driver from driverlink layer */ + efx_dl_unregister_nic(efx); + + /* Mark the NIC as fini under both suspend_lock and + * rtnl_lock */ + down(&efx->suspend_lock); + rtnl_lock(); + efx->state = STATE_FINI; + up(&efx->suspend_lock); + + if (efx->membase) { + /* Stop the NIC. Since we're in STATE_FINI, this + * won't be reversed. */ + if (efx->net_dev_registered) + dev_close(efx->net_dev); + + /* Release the rtnl lock. Any queued efx_resets() + * can now return early [we're in STATE_FINI]. */ + rtnl_unlock(); + + efx_unregister_netdev(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. */ +#if !defined(EFX_USE_CANCEL_DELAYED_WORK_SYNC) + flush_workqueue(efx->reset_workqueue); +#else + flush_workqueue(efx->workqueue); +#endif + + /* Fini and remove all the software state */ + rtnl_lock(); + efx_pci_remove_main(efx); + } + + rtnl_unlock(); + + efx_fini_io(efx); + EFX_LOG(efx, "shutdown successful\n"); + + pci_set_drvdata(pci_dev, NULL); + efx_fini_struct(efx); + kfree(efx); +}; + +/* Main body of NIC initialisation + * This is called at module load (or hotplug insertion, theoretically). + */ +static int efx_pci_probe_main(struct efx_nic *efx) +{ + int rc; + + /* Do start-of-day initialisation */ + rc = efx_probe_all(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); + if (rc) { + EFX_ERR(efx, "failed to initialise NIC\n"); + goto fail4; + } + + /* Initialise port */ + rc = efx_init_port(efx); + if (rc) { + EFX_ERR(efx, "failed to initialise port\n"); + 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 */ + efx_stop_all(efx); + + return 0; + + fail8: + efx_stop_all(efx); + falcon_fini_interrupt(efx); + fail7: + efx_fini_channels(efx); + fail6: + efx_fini_port(efx); + fail5: + falcon_fini_nic(efx); + fail4: + efx->board_info.fini(efx); + fail3: + efx_fini_napi(efx); + fail2: + efx_remove_all(efx); + fail1: + return rc; +} + +/* NIC initialisation + * + * This is called at module load (or hotplug insertion, + * theoretically). It sets up PCI mappings, tests and resets the NIC, + * sets up and registers the network devices with the kernel and hooks + * the interrupt service routine. It does not prepare the device for + * transmission; this is left to the first time one of the network + * interfaces is brought up (i.e. efx_net_open). + */ +static int __devinit efx_pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *entry) +{ + struct efx_nic *efx; + enum efx_type_index type = entry->driver_data; + int i, rc; + + /* Allocate and initialise a struct efx_nic */ + efx = kmalloc(sizeof(*efx), GFP_KERNEL); + if (!efx) { + rc = -ENOMEM; + 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"); + + /* Set up basic I/O (BAR mappings etc) */ + rc = efx_init_io(efx); + if (rc) + goto fail3; + + /* From this point on we begin to expose the driver to the OS + * to varying degrees, so lets grab the suspend_lock and + * rtnl_lock to serialise against efx_reset() and + * friends. efx->state is not STATE_RUNNING yet, but we don't + * want these tasks to fail, just to block until we drop the + * lock + */ + rc = down_interruptible(&efx->suspend_lock); + if (rc) { + EFX_ERR(efx, "suspend interrupted - aborting\n"); + goto fail4; + } + + rtnl_lock(); + + /* Probe, initialise and start everything. Run self-test */ + for (i = 0; i < 5; i++) { + rc = efx_pci_probe_main(efx); + if (rc == 0) + break; + + /* Retry if a recoverably reset event has been scheduled */ + if ((efx->reset_pending != RESET_TYPE_INVISIBLE) && + (efx->reset_pending != RESET_TYPE_ALL)) + goto fail5; + + /* 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. */ + rtnl_unlock(); + up(&efx->suspend_lock); + +#if defined(EFX_USE_CANCEL_WORK_SYNC) + cancel_work_sync(&efx->reset_work); +#else + flush_workqueue(efx->reset_workqueue); +#endif + + down(&efx->suspend_lock); + rtnl_lock(); + + efx->reset_pending = RESET_TYPE_NONE; + }; + if (rc) { + EFX_ERR(efx, "Could not reset NIC\n"); + goto fail5; + } + + /* 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. */ + efx->state = STATE_RUNNING; + + rtnl_unlock(); + + rc = efx_register_netdev(efx); + if (rc) + goto fail7; + + up(&efx->suspend_lock); + + EFX_LOG(efx, "initialisation successful\n"); + + /* Register with driverlink layer */ + rc = efx_dl_register_nic(efx); + if (rc) + goto fail8; + + return 0; + + fail8: + down(&efx->suspend_lock); + efx_unregister_netdev(efx); + fail7: + /* Re-acquire the rtnl lock around pci_remove_main() */ + rtnl_lock(); + efx_fini_debugfs_channels(efx); + fail6: + efx_pci_remove_main(efx); + fail5: + /* Drop the locks before fini */ + rtnl_unlock(); + up(&efx->suspend_lock); + fail4: + efx_fini_io(efx); + fail3: + efx_fini_struct(efx); + fail2: + kfree(efx); + fail1: + EFX_LOG(efx, "initialisation failed. rc=%d\n", rc); + return rc; +} + +/* PCI driver definition */ +static struct pci_driver efx_pci_driver = { + .name = EFX_DRIVER_NAME, + .id_table = efx_pci_table, + .probe = efx_pci_probe, + .remove = efx_pci_remove, +}; + +/************************************************************************** + * + * Kernel module interface + * + *************************************************************************/ + +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) +{ + int rc; + + printk(KERN_INFO "Solarflare NET driver v" EFX_DRIVER_VERSION "\n"); + + rc = efx_init_debugfs(); + if (rc) + goto err_debugfs; + + rc = register_netdevice_notifier(&efx_netdev_notifier); + if (rc) + goto err_notifier; + + rc = pci_register_driver(&efx_pci_driver); + if (rc < 0) + goto err_pci; + + return 0; + + err_pci: + unregister_netdevice_notifier(&efx_netdev_notifier); + err_notifier: + efx_fini_debugfs(); + err_debugfs: + return rc; +} + +static void __exit efx_exit_module(void) +{ + printk(KERN_INFO "Solarflare NET driver unloading\n"); + + pci_unregister_driver(&efx_pci_driver); + unregister_netdevice_notifier(&efx_netdev_notifier); + efx_fini_debugfs(); + +} + +module_init(efx_init_module); +module_exit(efx_exit_module); + +MODULE_AUTHOR("Michael Brown <mbrown@xxxxxxxxxxxxxxxx> and " + "Solarflare Communications"); +MODULE_DESCRIPTION("Solarflare Communications network driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, efx_pci_table); diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/efx.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/efx.h Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,103 @@ +/**************************************************************************** + * 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> + * + * 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 +#define EFX_EFX_H + +#include "net_driver.h" + +/* PCI IDs */ +#define EFX_VENDID_SFC 0x1924 +#define FALCON_A_P_DEVID 0x0703 +#define FALCON_A_S_DEVID 0x6703 +#define FALCON_B_P_DEVID 0x0710 + +/* TX */ +extern int efx_xmit(struct efx_nic *efx, + struct efx_tx_queue *tx_queue, struct sk_buff *skb); +extern void efx_stop_queue(struct efx_nic *efx); +extern void efx_wake_queue(struct efx_nic *efx); + +/* RX */ +#if 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 +#if 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); +#else +extern void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, + unsigned int len, int checksummed, int discard); +#endif +extern void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *rx_buf); + +/* Channels */ +extern void efx_process_channel_now(struct efx_channel *channel); +extern int efx_flush_queues(struct efx_nic *efx); + +/* Ports */ +extern void efx_reconfigure_port(struct efx_nic *efx, + int on_disabled); + +/* 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_init_irq_moderation(struct efx_nic *efx, int tx_usecs, + int rx_usecs); +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 unsigned int efx_monitor_interval; + +static inline void efx_schedule_channel(struct efx_channel *channel) +{ + EFX_TRACE(channel->efx, "channel %d scheduling NAPI poll on CPU%d\n", + channel->channel, raw_smp_processor_id()); + channel->work_pending = 1; + +#if defined(EFX_HAVE_OLD_NAPI) + if (!test_and_set_bit(__LINK_STATE_RX_SCHED, &channel->napi_dev->state)) + __netif_rx_schedule(channel->napi_dev); +#else + netif_rx_schedule(channel->napi_dev, &channel->napi_str); +#endif +} + + +#endif /* EFX_EFX_H */ diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/enum.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/enum.h Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,117 @@ +/**************************************************************************** + * 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_ENUM_H +#define EFX_ENUM_H + +/** + * 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!) + */ +/* Please keep in order and up-to-date w.r.t the following two #defines */ +enum efx_loopback_mode { + LOOPBACK_NONE = 0, + LOOPBACK_NEAR = 1, + LOOPBACK_MAC = 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_MAX +}; +#define LOOPBACK_TEST_MAX LOOPBACK_FAR + +/* These loopbacks occur within the controller */ +#define LOOPBACKS_10G_INTERNAL ((1 << LOOPBACK_XGMII)| \ + (1 << LOOPBACK_XGXS) | \ + (1 << LOOPBACK_XAUI)) + +#define LOOPBACKS_1G_INTERNAL (1 << LOOPBACK_MAC) + +#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_CHANGED(_from, _to, _mask) \ + ((LOOPBACK_MASK(_from) ^ LOOPBACK_MASK(_to)) & \ + (_mask) ? 1 : 0) + +#define LOOPBACK_OUT_OF(_from, _to, _mask) \ + (((LOOPBACK_MASK(_from) & (_mask)) && \ + ((LOOPBACK_MASK(_to) & (_mask)) == 0)) ? 1 : 0) + +/*****************************************************************************/ + +/** + * enum reset_type - reset types + * + * %RESET_TYPE_INVSIBLE, %RESET_TYPE_ALL, %RESET_TYPE_WORLD and + * %RESET_TYPE_DISABLE specify the method/scope of the reset. The + * other valuesspecify reasons, which efx_schedule_reset() will choose + * a method for. + * + * @RESET_TYPE_INVISIBLE: don't reset the PHYs or interrupts + * @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_INT_ERROR: reset due to internal error + * @RESET_TYPE_RX_RECOVERY: reset to recover from RX datapath errors + */ +enum reset_type { + RESET_TYPE_NONE = -1, + RESET_TYPE_INVISIBLE = 0, + RESET_TYPE_ALL = 1, + RESET_TYPE_WORLD = 2, + RESET_TYPE_DISABLE = 3, + RESET_TYPE_MAX_METHOD, + RESET_TYPE_MONITOR, + RESET_TYPE_INT_ERROR, + RESET_TYPE_RX_RECOVERY, + RESET_TYPE_RX_DESC_FETCH, + RESET_TYPE_TX_DESC_FETCH, + RESET_TYPE_MAX, +}; + +#endif /* EFX_ENUM_H */ diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/ethtool.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/ethtool.c Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,734 @@ +/**************************************************************************** + * 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> + * + * 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 <asm/uaccess.h> +#include "net_driver.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); + +struct ethtool_string { + char name[ETH_GSTRING_LEN]; +}; + +struct efx_ethtool_stat { + const char *name; + enum { + EFX_ETHTOOL_STAT_SOURCE_mac_stats, + EFX_ETHTOOL_STAT_SOURCE_nic, + EFX_ETHTOOL_STAT_SOURCE_channel + } source; + unsigned offset; + u64(*get_stat) (void *field); /* Reader function */ +}; + +/* Initialiser for a struct #efx_ethtool_stat with type-checking */ +#define EFX_ETHTOOL_STAT(stat_name, source_name, field, field_type, \ + get_stat_function) { \ + .name = #stat_name, \ + .source = EFX_ETHTOOL_STAT_SOURCE_##source_name, \ + .offset = ((((field_type *) 0) == \ + &((struct efx_##source_name *)0)->field) ? \ + offsetof(struct efx_##source_name, field) : \ + offsetof(struct efx_##source_name, field)), \ + .get_stat = get_stat_function, \ +} + +static u64 efx_get_uint_stat(void *field) +{ + return *(unsigned int *)field; +} + +static u64 efx_get_ulong_stat(void *field) +{ + return *(unsigned long *)field; +} + +static u64 efx_get_u64_stat(void *field) +{ + return *(u64 *) field; +} + +static u64 efx_get_atomic_stat(void *field) +{ + return atomic_read((atomic_t *) field); +} + +#define EFX_ETHTOOL_ULONG_MAC_STAT(field) \ + EFX_ETHTOOL_STAT(field, mac_stats, field, \ + unsigned long, efx_get_ulong_stat) + +#define EFX_ETHTOOL_U64_MAC_STAT(field) \ + EFX_ETHTOOL_STAT(field, mac_stats, field, \ + u64, efx_get_u64_stat) + +#define EFX_ETHTOOL_UINT_NIC_STAT(name) \ + EFX_ETHTOOL_STAT(name, nic, n_##name, \ + unsigned int, efx_get_uint_stat) + +#define EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(field) \ + EFX_ETHTOOL_STAT(field, nic, errors.field, \ + atomic_t, efx_get_atomic_stat) + +#define EFX_ETHTOOL_UINT_CHANNEL_STAT(field) \ + EFX_ETHTOOL_STAT(field, channel, n_##field, \ + unsigned int, efx_get_uint_stat) + +static struct efx_ethtool_stat efx_ethtool_stats[] = { + EFX_ETHTOOL_U64_MAC_STAT(tx_bytes), + EFX_ETHTOOL_U64_MAC_STAT(tx_good_bytes), + EFX_ETHTOOL_U64_MAC_STAT(tx_bad_bytes), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_packets), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_bad), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_pause), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_control), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_unicast), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_multicast), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_broadcast), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_lt64), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_64), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_65_to_127), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_128_to_255), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_256_to_511), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_512_to_1023), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_1024_to_15xx), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_15xx_to_jumbo), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_gtjumbo), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_collision), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_single_collision), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_multiple_collision), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_excessive_collision), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_deferred), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_late_collision), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_excessive_deferred), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_non_tcpudp), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_mac_src_error), + EFX_ETHTOOL_ULONG_MAC_STAT(tx_ip_src_error), + EFX_ETHTOOL_U64_MAC_STAT(rx_bytes), + EFX_ETHTOOL_U64_MAC_STAT(rx_good_bytes), + EFX_ETHTOOL_U64_MAC_STAT(rx_bad_bytes), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_packets), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_good), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_pause), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_control), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_unicast), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_multicast), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_broadcast), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_lt64), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_64), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_65_to_127), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_128_to_255), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_256_to_511), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_512_to_1023), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_1024_to_15xx), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_15xx_to_jumbo), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_gtjumbo), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_lt64), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_64_to_15xx), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_15xx_to_jumbo), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_bad_gtjumbo), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_overflow), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_missed), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_false_carrier), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_symbol_error), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_align_error), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_length_error), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_internal_error), + EFX_ETHTOOL_UINT_NIC_STAT(rx_nodesc_drop_cnt), + EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(rx_reset), + 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_frm_trunc), +}; + +/* Number of ethtool statistics */ +#define EFX_ETHTOOL_NUM_STATS ARRAY_SIZE(efx_ethtool_stats) + +/************************************************************************** + * + * Ethtool operations + * + ************************************************************************** + */ + +/* 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); + return 0; +} + +/* This must be called with rtnl_lock held. */ +int efx_ethtool_get_settings(struct net_device *net_dev, + struct ethtool_cmd *ecmd) +{ + struct efx_nic *efx = net_dev->priv; + + return efx->mac_op->get_settings(efx, ecmd); +} + +/* 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; + int rc; + + rc = efx->mac_op->set_settings(efx, ecmd); + if (rc) + return rc; + + /* Push the settings to the MAC */ + efx_reconfigure_port(efx, 0); + + return 0; +} + +static void efx_ethtool_get_drvinfo(struct net_device *net_dev, + struct ethtool_drvinfo *info) +{ + struct efx_nic *efx = net_dev->priv; + + strlcpy(info->driver, EFX_DRIVER_NAME, sizeof(info->driver)); + strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); +} + +/** + * efx_fill_test - fill in an individual self-test entry + * @test_index: Index of the test + * @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") + * @test_format: Test name format (e.g. "loopback.\%s.tx.sent") + * @test_id: Test id (e.g. "PHY" for "loopback.PHY.tx_sent") + * + * Fill in an individual self-test entry. + */ +static void efx_fill_test(unsigned int test_index, + struct ethtool_string *strings, u64 *data, + int *test, const char *unit_format, int unit_id, + const char *test_format, const char *test_id) +{ + struct ethtool_string unit_str, test_str; + + /* Fill data value, if applicable */ + if (data) + data[test_index] = *test; + + /* Fill string, if applicable */ + if (strings) { + snprintf(unit_str.name, sizeof(unit_str.name), + unit_format, unit_id); + 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 + +/** + * efx_fill_loopback_test - fill in a block of loopback self-test entries + * @efx: Efx NIC + * @lb_tests: Efx loopback self-test results structure + * @mode: Loopback test mode + * @test_index: Starting index of the test + * @strings: Ethtool strings, or %NULL + * @data: Ethtool test results, or %NULL + * + * Fill in a block of loopback self-test entries. Return new test + * index. + */ +static int efx_fill_loopback_test(struct efx_nic *efx, + struct efx_loopback_self_tests *lb_tests, + enum efx_loopback_mode mode, + unsigned int test_index, + struct ethtool_string *strings, u64 *data) +{ + struct efx_tx_queue *tx_queue; + + efx_for_each_tx_queue(tx_queue, efx) { + 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_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_fill_test(test_index++, strings, data, + &lb_tests->rx_good, + EFX_PORT_NAME, + "loopback.%s.rx_good", + efx_loopback_mode_names[mode]); + efx_fill_test(test_index++, strings, data, + &lb_tests->rx_bad, + EFX_PORT_NAME, + "loopback.%s.rx_bad", + efx_loopback_mode_names[mode]); + + return test_index; +} + +/** + * efx_ethtool_fill_self_tests - get self-test details + * @efx: Efx NIC + * @tests: Efx self-test results structure, or %NULL + * @strings: Ethtool strings, or %NULL + * @data: Ethtool test results, or %NULL + * + * Get self-test number of strings, strings, and/or test results. + * Return number of strings (== number of test results). + * + * The reason for merging these three functions is to make sure that + * they can never be inconsistent. + */ +static int efx_ethtool_fill_self_tests(struct efx_nic *efx, + struct efx_self_tests *tests, + struct ethtool_string *strings, + u64 *data) +{ + struct efx_channel *channel; + unsigned int n = 0; + enum efx_loopback_mode mode; + + /* Interrupt */ + efx_fill_test(n++, strings, data, &tests->interrupt, + "core", 0, "interrupt", NULL); + + /* Event queues */ + efx_for_each_channel(channel, efx) { + efx_fill_test(n++, strings, data, + &tests->eventq_dma[channel->channel], + EFX_CHANNEL_NAME(channel), + "eventq.dma", NULL); + efx_fill_test(n++, strings, data, + &tests->eventq_int[channel->channel], + EFX_CHANNEL_NAME(channel), + "eventq.int", NULL); + efx_fill_test(n++, strings, data, + &tests->eventq_poll[channel->channel], + EFX_CHANNEL_NAME(channel), + "eventq.poll", NULL); + } + + /* PHY presence */ + efx_fill_test(n++, strings, data, &tests->phy_ok, + EFX_PORT_NAME, "phy_ok", 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++) { + if (!(efx->loopback_modes & (1 << mode))) + continue; + n = efx_fill_loopback_test(efx, + &tests->loopback[mode], mode, n, + strings, data); + } + + return n; +} + +static int efx_ethtool_get_stats_count(struct net_device *net_dev) +{ + return EFX_ETHTOOL_NUM_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); +} + +static void efx_ethtool_get_strings(struct net_device *net_dev, + u32 string_set, u8 *strings) +{ + struct efx_nic *efx = net_dev->priv; + struct ethtool_string *ethtool_strings = + (struct ethtool_string *)strings; + int i; + + switch (string_set) { + case ETH_SS_STATS: + for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) + strncpy(ethtool_strings[i].name, + efx_ethtool_stats[i].name, + sizeof(ethtool_strings[i].name)); + break; + case ETH_SS_TEST: + efx_ethtool_fill_self_tests(efx, NULL, + ethtool_strings, NULL); + break; + default: + /* No other string sets */ + break; + } +} + +static void efx_ethtool_get_stats(struct net_device *net_dev, + struct ethtool_stats *stats + __attribute__ ((unused)), u64 *data) +{ + unsigned long flags __attribute__ ((unused)); + struct efx_nic *efx = net_dev->priv; + struct efx_mac_stats *mac_stats = &efx->mac_stats; + struct efx_ethtool_stat *stat; + struct efx_channel *channel; + int i; + + EFX_BUG_ON_PARANOID(stats->n_stats != EFX_ETHTOOL_NUM_STATS); + + /* Update MAC and NIC statistics */ + net_dev->get_stats(net_dev); + falcon_update_nic_stats(efx); + + /* Fill detailed statistics buffer */ + for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) { + stat = &efx_ethtool_stats[i]; + switch (stat->source) { + case EFX_ETHTOOL_STAT_SOURCE_mac_stats: + data[i] = stat->get_stat((void *)mac_stats + + stat->offset); + break; + case EFX_ETHTOOL_STAT_SOURCE_nic: + data[i] = stat->get_stat((void *)efx + stat->offset); + break; + case EFX_ETHTOOL_STAT_SOURCE_channel: + data[i] = 0; + efx_for_each_channel(channel, efx) + data[i] += stat->get_stat((void *)channel + + stat->offset); + break; + } + } +} + +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); + + return 0; +} + +static int efx_ethtool_set_rx_csum(struct net_device *net_dev, u32 enable) +{ + struct efx_nic *efx = net_dev->priv; + + /* No way to stop the hardware doing the checks; we just + * ignore the result. + */ + efx->rx_checksum_enabled = (enable ? 1 : 0); + + return 0; +} + +static u32 efx_ethtool_get_rx_csum(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + + return efx->rx_checksum_enabled; +} + +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_self_tests efx_tests; + int offline, 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. */ + 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; + } + } + + 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: + 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_ethtool_fill_self_tests(efx, &efx_tests, NULL, data); + if (rc) + test->flags |= ETH_TEST_FL_FAILED; +} + +/* 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); +} + +static u32 efx_ethtool_get_link(struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + + return efx->link_up; +} + +static int efx_ethtool_get_coalesce(struct net_device *net_dev, + struct ethtool_coalesce *coalesce) +{ + struct efx_nic *efx = net_dev->priv; + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + struct efx_channel *channel; + + memset(coalesce, 0, sizeof(*coalesce)); + + /* Find lowest IRQ moderation across all used TX queues */ + coalesce->tx_coalesce_usecs_irq = ~((u32) 0); + efx_for_each_tx_queue(tx_queue, efx) { + channel = tx_queue->channel; + if (channel->irq_moderation < coalesce->tx_coalesce_usecs_irq) { + if (channel->used_flags != EFX_USED_BY_RX_TX) + coalesce->tx_coalesce_usecs_irq = + channel->irq_moderation; + else + coalesce->tx_coalesce_usecs_irq = 0; + } + } + + /* 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; + } + + return 0; +} + +/* Set coalescing parameters + * The difficulties occur for shared channels + */ +static int efx_ethtool_set_coalesce(struct net_device *net_dev, + struct ethtool_coalesce *coalesce) +{ + struct efx_nic *efx = net_dev->priv; + 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) + return -EOPNOTSUPP; + + if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) { + EFX_ERR(efx, "invalid coalescing setting. " + "Only rx/tx_coalesce_usecs_irq are supported\n"); + return -EOPNOTSUPP; + } + + rx_usecs = coalesce->rx_coalesce_usecs_irq; + tx_usecs = coalesce->tx_coalesce_usecs_irq; + + /* If the channel is shared only allow RX parameters to be set */ + efx_for_each_tx_queue(tx_queue, efx) { + if ((tx_queue->channel->used_flags == EFX_USED_BY_RX_TX) && + tx_usecs) { + EFX_ERR(efx, "Channel is shared. " + "Only RX coalescing may be set\n"); + return -EOPNOTSUPP; + } + } + + 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_for_each_channel(channel, efx) + falcon_set_int_moderation(channel); + + return 0; +} + +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 */ + rc = efx->mac_op->set_pause(efx, flow_control); + if (rc) + return rc; + + /* Push the settings to the MAC */ + efx_reconfigure_port(efx, 0); + + return 0; +} + +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; +} + + +#if 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; + + memcpy(data, efx->mac_address, ETH_ALEN); + + return 0; +} +#endif + +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_coalesce = efx_ethtool_get_coalesce, + .set_coalesce = efx_ethtool_set_coalesce, + .get_pauseparam = efx_ethtool_get_pauseparam, + .set_pauseparam = efx_ethtool_set_pauseparam, + .get_rx_csum = efx_ethtool_get_rx_csum, + .set_rx_csum = efx_ethtool_set_rx_csum, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = efx_ethtool_set_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, +#if defined(EFX_USE_ETHTOOL_FLAGS) + .get_flags = ethtool_op_get_flags, + .set_flags = ethtool_op_set_flags, +#endif + .self_test_count = efx_ethtool_self_test_count, + .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_ETHTOOL_GET_PERM_ADDR) + .get_perm_addr = efx_ethtool_op_get_perm_addr, +#endif +}; diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/ethtool.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/ethtool.h Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,44 @@ +/**************************************************************************** + * 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 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/extraversion.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/extraversion.h Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,4 @@ +/* + * If compiling on kernels with backported features you may need to + * define EFX_DIST_KVER_ symbols here + */ diff -r 0034d9389130 -r fc90e9b2c12b drivers/net/sfc/falcon.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/net/sfc/falcon.c Mon Feb 18 10:29:07 2008 +0000 @@ -0,0 +1,3708 @@ +/**************************************************************************** + * 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> + * + * 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 <asm/io.h> +#include <asm/bitops.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/seq_file.h> +#include "net_driver.h" +#include "bitfield.h" +#include "efx.h" +#include "mac.h" +#include "gmii.h" +#include "spi.h" +#include "falcon.h" +#include "falcon_hwdefs.h" +#include "falcon_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 { + /* Number of entries in each TX queue descriptor cache. */ + unsigned tx_dc_entries; + /* Number of entries in each RX queue descriptor cache. */ + unsigned rx_dc_entries; + /* Base address in SRAM of TX queue descriptor caches. */ + unsigned tx_dc_base; + /* Base address in SRAM of RX queue descriptor caches. */ + unsigned rx_dc_base; + + /* Previous loopback mode used in deconfigure_mac_wrapper */ + enum efx_loopback_mode old_loopback_mode; + + /* Driverlink parameters */ + 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; + +/* + * Override EEPROM/flash type from non-volatile configuration or GPIO; + * may need to be specified if bootstrapping from blank flash. + */ +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 (8192 - 1) + +/* Alignment of special buffers (4KB) */ +#define FALCON_BUF_ALIGN 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) +/* This field takes up bits 26 and 27. */ +#define PCI_EXP_DEVCAP_PWR_SCL_LBN (26) +#define PCI_EXP_LNKSTA_LNK_WID (0x3f0) +#define PCI_EXP_LNKSTA_LNK_WID_LBN (4) + + +/************************************************************************** + * + * 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]))); +} + +/* Read dword from a Falcon PCIE core register */ +static void falcon_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(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); + /* Extract PCIE_CORE_VALUE without byte-swapping */ + BUILD_BUG_ON(PCIE_CORE_VALUE_LBN != 32 || + PCIE_CORE_VALUE_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, + efx_dword_t value) +{ + efx_oword_t temp; + + BUG_ON(FALCON_REV(efx) < FALCON_REV_B0); + BUG_ON(address & 0x3 || address < 0); + + EFX_POPULATE_OWORD_2(temp, + PCIE_CORE_ADDR, address, + PCIE_CORE_RW, 1); + /* Fill PCIE_CORE_VALUE without byte-swapping */ + BUILD_BUG_ON(PCIE_CORE_VALUE_LBN != 32 || + PCIE_CORE_VALUE_WIDTH != 32); + temp.u32[1] = value.u32[0]; + falcon_write(efx, &temp, PCIE_CORE_INDIRECT_REG); +} + +/************************************************************************** + * + * I2C bus - this is a bit-bashing interface using GPIO pins + * Note that it uses the output enables to tristate the outputs + * SDA is the data pin and SCL is the clock + * + ************************************************************************** + */ +static void falcon_setsdascl(struct efx_i2c_interface *i2c) +{ + 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_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_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, + .getsda = falcon_getsda, + .getscl = falcon_getscl, + .udelay = 100, + .mdelay = 10, +}; + +/************************************************************************** + * + * Falcon special buffer handling + * Special buffers are used for event queues and the TX and RX + * descriptor rings. + * + *************************************************************************/ + +/* Adds the relevant entries to the full-mode buffer table. */ +static int +falcon_pin_special_buffer_full(struct efx_nic *efx, + struct efx_special_buffer *buffer) +{ + efx_qword_t buf_desc; + int index; + dma_addr_t dma_addr; + int i; + + /* 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; +} + +/* Clears the relevant entries from the buffer table */ +static void +falcon_clear_special_buffer_full(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); + + 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. It will always + * allocate an even number of 4kB buffers, since when we're in + * half-entry mode for the buffer table we can only deal with pairs of + * buffers. + */ +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; + + /* Round size up to an 8kB boundary (i.e. pairs of 4kB buffers) */ + len = (len + 8192 - 1) & ~(8192 - 1); + + /* 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 / 4096; + BUG_ON(buffer->dma_addr & (FALCON_BUF_ALIGN - 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; +} + +/* + * 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_BUG_ON_PARANOID(!buffer->addr); + + /* Write buffer descriptors to NIC */ + return falcon_pin_special_buffer_full(efx, buffer); +} + +/* 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) +{ + + if (!buffer->entries) + return; + + falcon_clear_special_buffer_full(efx, buffer); +} + +/* 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. + */ +#if defined(EFX_USE_FASTCALL) +void fastcall falcon_push_buffers(struct efx_tx_queue *tx_queue) +#else +void falcon_push_buffers(struct efx_tx_queue *tx_queue) +#endif +{ + + 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 the NIC is resetting then don't bother checking */ + if (EFX_WORKAROUND_7803(efx) || (efx->state == STATE_RESETTING)) + 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. + */ +#if defined(EFX_USE_FASTCALL) +void fastcall falcon_notify_rx_desc(struct efx_rx_queue *rx_queue) +#else +void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue) +#endif +{ + 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 the NIC is resetting then don't bother checking */ + if (EFX_WORKAROUND_7803(efx) || (efx->state == STATE_RESETTING)) + 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); + + /* 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. + */ +#if defined(EFX_USE_FASTCALL) +void fastcall falcon_eventq_read_ack(struct efx_channel *channel) +#else +void falcon_eventq_read_ack(struct efx_channel *channel) +#endif +{ + 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 (efx->net_dev_registered) + netif_tx_lock(efx->net_dev); + falcon_notify_tx_desc(tx_queue); + if (efx->net_dev_registered) + 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; + } +} + +#if defined(EFX_USE_FASTCALL) +int fastcall falcon_process_eventq(struct efx_channel *channel, int *rx_quota) +#else +int falcon_process_eventq(struct efx_channel *channel, int *rx_quota) +#endif +{ + 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) +{ + 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); + } 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); +} + +/* Acknowledge a legacy interrupt from Falcon + * + * This acknowledges a legacy (not MSI) interrupt via INT_ACK_KER_REG. + * + * Due to SFC bug 3706 (silicon revision <=A1) reads can be duplicated in the + * BIU. Interrupt acknowledge is read sensitive so must write instead + * (then read to ensure the BIU collector is flushed) + * + * NB most hardware supports MSI interrupts + */ +static 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) +{ + 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 (efx->type->is_dual_func) + pci_disable_device(efx->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(). + */ +#if !defined(EFX_HAVE_IRQ_HANDLER_REGS) +static irqreturn_t falcon_legacy_interrupt_b0(int irq, void *dev_id) +#else +static irqreturn_t falcon_legacy_interrupt_b0(int irq, void *dev_id, + struct pt_regs *regs + __attribute__ ((unused))) +#endif +{ + struct efx_nic *efx = (struct efx_nic *)dev_id; + efx_oword_t *int_ker = (efx_oword_t *) efx->irq_status.addr; + struct efx_channel *channel; + efx_dword_t reg; + u32 queues; + int syserr; + + /* Read the ISR which also ACKs the interrupts */ + falcon_readl(efx, ®, INT_ISR0_B0); + queues = EFX_EXTRACT_DWORD(reg, 0, 31); + + /* Check to see if we have a serious error condition */ + syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT); + if (unlikely(syserr)) + return falcon_fatal_interrupt(efx); + + if (queues == 0) + return IRQ_NONE; + + efx->last_irq_cpu = raw_smp_processor_id(); + EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_DWORD_FMT "\n", + irq, raw_smp_processor_id(), EFX_DWORD_VAL(reg)); + + /* Schedule processing of any interrupting queues */ + channel = &efx->channel[0]; + while (queues) { + if (queues & 0x01) + efx_schedule_channel(channel); + channel++; + queues >>= 1; + } + + return IRQ_HANDLED; +} + + +#if !defined(EFX_HAVE_IRQ_HANDLER_REGS) +static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) +#else +static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id, + struct pt_regs *regs + __attribute__ ((unused))) +#endif +{ + struct efx_nic *efx = (struct efx_nic *)dev_id; + efx_oword_t *int_ker = (efx_oword_t *) efx->irq_status.addr; + struct efx_channel *channel; + int syserr; + int queues; + + /* Check to see if this is our interrupt. If it isn't, we + * exit without having touched the hardware. + */ + if (unlikely(EFX_OWORD_IS_ZERO(*int_ker))) { + EFX_TRACE(efx, "IRQ %d on CPU %d not for me\n", irq, + raw_smp_processor_id()); + return IRQ_NONE; + } + efx->last_irq_cpu = raw_smp_processor_id(); + EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_OWORD_FMT "\n", + irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker)); + + /* Check to see if we have a serious error condition */ + syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT); + if (unlikely(syserr)) + return falcon_fatal_interrupt(efx); + + /* Determine interrupting queues, clear interrupt status + * register and acknowledge the device interrupt. + */ + BUILD_BUG_ON(INT_EVQS_WIDTH > EFX_MAX_CHANNELS); + queues = EFX_OWORD_FIELD(*int_ker, INT_EVQS); + EFX_ZERO_OWORD(*int_ker); + wmb(); /* Ensure the vector is cleared before interrupt ack */ + falcon_irq_ack_a1(efx); + + /* Schedule processing of any interrupting queues */ + channel = &efx->channel[0]; + while (queues) { + if (queues & 0x01) + efx_schedule_channel(channel); + channel++; + queues >>= 1; + } + + return IRQ_HANDLED; +} + +/* Handle an MSI interrupt from Falcon + * + * Handle an MSI hardware interrupt. This routine schedules event + * queue processing. No interrupt acknowledgement cycle is necessary. + * Also, we never need to check that the interrupt is for us, since + * MSI interrupts cannot be shared. + * + * This routine must guarantee not to touch the hardware when + * interrupts are disabled, to allow for correct semantics of + * efx_suspend() and efx_resume(). + */ +#if !defined(EFX_HAVE_IRQ_HANDLER_REGS) +static irqreturn_t falcon_msi_interrupt(int irq, void *dev_id) +#else +static irqreturn_t falcon_msi_interrupt(int irq, void *dev_id, + struct pt_regs *regs + __attribute__ ((unused))) +#endif +{ + struct efx_channel *channel = (struct efx_channel *)dev_id; + struct efx_nic *efx = channel->efx; + efx_oword_t *int_ker = (efx_oword_t *) efx->irq_status.addr; + int syserr; + + efx->last_irq_cpu = raw_smp_processor_id(); + EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_OWORD_FMT "\n", + irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker)); + + /* Check to see if we have a serious error condition */ + syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT); + if (unlikely(syserr)) + return falcon_fatal_interrupt(efx); + + /* Schedule processing of the channel */ + efx_schedule_channel(channel); + + return IRQ_HANDLED; +} + + +/* Setup RSS indirection table. + * This maps from the hash value of the packet to RXQ + */ +static void falcon_setup_rss_indir_table(struct efx_nic *efx) +{ + int i = 0; + unsigned long offset; + unsigned long flags __attribute__ ((unused)); + efx_dword_t dword; + + if (FALCON_REV(efx) < FALCON_REV_B0) + return; + + for (offset = RX_RSS_INDIR_TBL_B0; + offset < RX_RSS_INDIR_TBL_B0 + 0x800; + offset += 0x10) { + EFX_POPULATE_DWORD_1(dword, RX_RSS_INDIR_ENT_B0, + i % efx->rss_queues); + falcon_writel(efx, &dword, offset); + i++; + } +} + +/* Hook interrupt handler(s) + * Try MSI and then legacy interrupts. + */ +int falcon_init_interrupt(struct efx_nic *efx) +{ + struct efx_channel *channel; + int rc; + + if (!EFX_INT_MODE_USE_MSI(efx)) { + irq_handler_t handler; + if (FALCON_REV(efx) >= FALCON_REV_B0) + handler = falcon_legacy_interrupt_b0; + else + handler = falcon_legacy_interrupt_a1; + + rc = request_irq(efx->legacy_irq, handler, IRQF_SHARED, + efx->name, efx); + if (rc) { + EFX_ERR(efx, "failed to hook legacy IRQ %d\n", + efx->pci_dev->irq); + goto fail1; + } + return 0; + } + + /* Hook MSI or MSI-X interrupt */ + efx_for_each_channel_with_interrupt(channel, efx) { + rc = request_irq(channel->irq, falcon_msi_interrupt, + IRQF_PROBE_SHARED, /* Not shared */ + efx->name, channel); + if (rc) { + EFX_ERR(efx, "failed to hook IRQ %d\n", channel->irq); + goto fail2; + } + } + + return 0; + + fail2: + efx_for_each_channel_with_interrupt(channel, efx) + free_irq(channel->irq, channel); + fail1: + return rc; +} + +void falcon_fini_interrupt(struct efx_nic *efx) +{ + struct efx_channel *channel; + efx_oword_t reg; + + /* Disable MSI/MSI-X interrupts */ + efx_for_each_channel_with_interrupt(channel, efx) + if (channel->irq) + free_irq(channel->irq, channel); + + /* ACK legacy interrupt */ + if (FALCON_REV(efx) >= FALCON_REV_B0) + falcon_read(efx, ®, INT_ISR0_B0); + else + falcon_irq_ack_a1(efx); + + /* Disable legacy interrupt */ + if (efx->legacy_irq) + free_irq(efx->legacy_irq, efx); +} + +/************************************************************************** + * + * EEPROM/flash + * + ************************************************************************** + */ + +/* Wait for SPI command completion */ +static int falcon_spi_wait(struct efx_nic *efx) +{ + efx_oword_t reg; + int cmd_en, timer_active; + int count; + + count = 0; + do { + falcon_read(efx, ®, EE_SPI_HCMD_REG_KER); + cmd_en = EFX_OWORD_FIELD(reg, EE_SPI_HCMD_CMD_EN); + timer_active = EFX_OWORD_FIELD(reg, EE_WR_TIMER_ACTIVE); + if (!cmd_en && !timer_active) + return 0; + udelay(10); + } while (++count < 10000); /* wait upto 100msec */ + EFX_ERR(efx, "timed out waiting for SPI\n"); + return -ETIMEDOUT; +} + +static int +falcon_spi_read(const struct efx_spi_device *spi, struct efx_nic *efx, + unsigned int command, int address, void *data, unsigned int len) +{ + int addressed = (address >= 0); + efx_oword_t reg; + int rc; + + /* Input validation */ + if (len > FALCON_SPI_MAX_LEN) + return -EINVAL; + + /* Acquire SPI lock */ + mutex_lock(&efx->spi_lock); + + /* Check SPI not currently being accessed */ + rc = falcon_spi_wait(efx); + if (rc) + goto out; + + /* Program address register, if we have an address */ + if (addressed) { + EFX_POPULATE_OWORD_1(reg, EE_SPI_HADR_ADR, address); + falcon_write(efx, ®, EE_SPI_HADR_REG_KER); + } + + /* Issue read command */ + EFX_POPULATE_OWORD_7(reg, + EE_SPI_HCMD_CMD_EN, 1, + EE_SPI_HCMD_SF_SEL, spi->device_id, + EE_SPI_HCMD_DABCNT, len, + EE_SPI_HCMD_READ, EE_SPI_READ, + EE_SPI_HCMD_DUBCNT, 0, + EE_SPI_HCMD_ADBCNT, + (addressed ? spi->addr_len : 0), + EE_SPI_HCMD_ENC, command); + falcon_write(efx, ®, EE_SPI_HCMD_REG_KER); + + /* Wait for read to complete */ + rc = falcon_spi_wait(efx); + if (rc) + goto out; + + /* Read data */ + falcon_read(efx, ®, EE_SPI_HDATA_REG_KER); + memcpy(data, ®, len); + + out: + /* Release SPI lock */ + mutex_unlock(&efx->spi_lock); + + return rc; +} + +static int +falcon_spi_write(const struct efx_spi_device *spi, struct efx_nic *efx, + unsigned int command, int address, const void *data, + unsigned int len) +{ + int addressed = (address >= 0); + efx_oword_t reg; + int rc; + + /* Input validation */ + if (len > (addressed ? efx_spi_write_limit(spi, address) + : FALCON_SPI_MAX_LEN)) + return -EINVAL; + + /* Acquire SPI lock */ + mutex_lock(&efx->spi_lock); + + /* Check SPI not currently being accessed */ + rc = falcon_spi_wait(efx); + if (rc) + goto out; + + /* Program address register, if we have an address */ + if (addressed) { + EFX_POPULATE_OWORD_1(reg, EE_SPI_HADR_ADR, address); + falcon_write(efx, ®, EE_SPI_HADR_REG_KER); + } + + /* Program data register, if we have data */ + if (data) { + memcpy(®, data, len); + falcon_write(efx, ®, EE_SPI_HDATA_REG_KER); + } + + /* Issue write command */ + EFX_POPULATE_OWORD_7(reg, + EE_SPI_HCMD_CMD_EN, 1, + EE_SPI_HCMD_SF_SEL, spi->device_id, + EE_SPI_HCMD_DABCNT, len, + EE_SPI_HCMD_READ, EE_SPI_WRITE, + EE_SPI_HCMD_DUBCNT, 0, + EE_SPI_HCMD_ADBCNT, + (addressed ? spi->addr_len : 0), + EE_SPI_HCMD_ENC, command); + falcon_write(efx, ®, EE_SPI_HCMD_REG_KER); + + /* Wait for write to complete */ + rc = falcon_spi_wait(efx); + if (rc) + goto out; + + out: + /* Release SPI lock */ + mutex_unlock(&efx->spi_lock); + + return rc; +} + +/************************************************************************** + * + * MAC wrapper + * + ************************************************************************** + */ +void falcon_drain_tx_fifo(struct efx_nic *efx) +{ + efx_oword_t temp; + efx_oword_t mcast_reg0; + efx_oword_t mcast_reg1; + int count; + + if (FALCON_REV(efx) < FALCON_REV_B0) + return; + + falcon_read(efx, &temp, MAC0_CTRL_REG_KER); + /* There is no point in draining more than once */ + if (EFX_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0)) + return; + + /* MAC stats will fail whilst the TX fifo is draining. Serialise + * the drain sequence with the statistics fetch */ + spin_lock(&efx->stats_lock); + + EFX_SET_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0, 1); + falcon_write(efx, &temp, MAC0_CTRL_REG_KER); + + falcon_read(efx, &mcast_reg0, MAC_MCAST_HASH_REG0_KER); + falcon_read(efx, &mcast_reg1, MAC_MCAST_HASH_REG1_KER); + + /* Reset the MAC and EM block. */ + falcon_read(efx, &temp, GLB_CTL_REG_KER); + EFX_SET_OWORD_FIELD(temp, RST_XGTX, 1); + EFX_SET_OWORD_FIELD(temp, RST_XGRX, 1); + EFX_SET_OWORD_FIELD(temp, RST_EM, 1); + falcon_write(efx, &temp, GLB_CTL_REG_KER); + + count = 0; + while (1) { + falcon_read(efx, &temp, GLB_CTL_REG_KER); + if (!EFX_OWORD_FIELD(temp, RST_XGTX) && + !EFX_OWORD_FIELD(temp, RST_XGRX) && + !EFX_OWORD_FIELD(temp, RST_EM)) { + EFX_LOG(efx, "Completed MAC reset after %d loops\n", + count); + break; + } + if (count > 20) { + EFX_ERR(efx, "MAC reset failed\n"); + break; + } + count++; + udelay(10); + } + + spin_unlock(&efx->stats_lock); + + /* Restore the multicast hash registers. */ + falcon_write(efx, &mcast_reg0, MAC_MCAST_HASH_REG0_KER); + falcon_write(efx, &mcast_reg1, MAC_MCAST_HASH_REG1_KER); + + /* If we've reset the EM block and the link is up, then + * we'll have to kick the XAUI link so the PHY can recover */ + if (efx->link_up && EFX_IS10G(efx) && EFX_WORKAROUND_5147(efx)) + falcon_reset_xaui(efx); +} + +void falcon_deconfigure_mac_wrapper(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + efx_oword_t temp; + int changing_loopback; + + if (FALCON_REV(efx) < FALCON_REV_B0) + return; + + /* Isolate the MAC -> RX */ + falcon_read(efx, &temp, RX_CFG_REG_KER); + EFX_SET_OWORD_FIELD(temp, RX_INGR_EN_B0, 0); + falcon_write(efx, &temp, RX_CFG_REG_KER); + + /* Synchronise the EM block against any loopback mode changes by + * draining the TX fifo and resetting. */ + changing_loopback = (efx->loopback_mode != nic_data->old_loopback_mode); + nic_data->old_loopback_mode = efx->loopback_mode; + if (changing_loopback || !efx->link_up) + falcon_drain_tx_fifo(efx); +} + +void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) +{ + efx_oword_t reg; + int link_speed; + unsigned int tx_fc; + + if (efx->link_options & GM_LPA_10000) + link_speed = 0x3; + else if (efx->link_options & GM_LPA_1000) + link_speed = 0x2; + else if (efx->link_options & GM_LPA_100) + link_speed = 0x1; + else + link_speed = 0x0; + /* MAC_LINK_STATUS controls MAC backpressure but doesn't work + * as advertised. Disable to ensure packets are not + * indefinitely held and TX queue can be flushed at any point + * while the link is down. + */ + EFX_POPULATE_OWORD_5(reg, + MAC_XOFF_VAL, 0xffff /* max pause time */, + MAC_BCAD_ACPT, 1, + MAC_UC_PROM, efx->promiscuous, + MAC_LINK_STATUS, 1, /* always set */ + MAC_SPEED, link_speed); + /* On B0, MAC backpressure can be disabled and packets get + * discarded. */ + if (FALCON_REV(efx) >= FALCON_REV_B0) { + EFX_SET_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0, + !efx->link_up); + } + + falcon_write(efx, ®, MAC0_CTRL_REG_KER); + + /* + * Transmission of pause frames when RX crosses the threshold is + * covered by RX_XOFF_MAC_EN and XM_TX_CFG_REG:XM_FCNTL. + * + * Action on receipt of pause frames is controller by XM_DIS_FCNTL + */ + tx_fc = (efx->flow_control & EFX_FC_TX) ? 1 : 0; + falcon_read(efx, ®, RX_CFG_REG_KER); + EFX_SET_OWORD_FIELD_VER(efx, reg, RX_XOFF_MAC_EN, tx_fc); + + /* Unisolate the MAC -> RX */ + if (FALCON_REV(efx) >= FALCON_REV_B0) + EFX_SET_OWORD_FIELD(reg, RX_INGR_EN_B0, 1); + falcon_write(efx, ®, RX_CFG_REG_KER); +} + +int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset) +{ + efx_oword_t reg; + u32 *dma_done; + int i; + + if (disable_dma_stats) + return 0; + + /* Statistics fetch will fail if the MAC is in TX drain */ + if (FALCON_REV(efx) >= FALCON_REV_B0) { + efx_oword_t temp; + falcon_read(efx, &temp, MAC0_CTRL_REG_KER); + if (EFX_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0)) + return 0; + } + + /* Clear completion pointer */ + dma_done = (efx->stats_buffer.addr + done_offset); + *dma_done = FALCON_STATS_NOT_DONE; + wmb(); /* ensure done flag is clear */ + + /* Initiate DMA transfer of stats */ + EFX_POPULATE_OWORD_2(reg, + MAC_STAT_DMA_CMD, 1, + MAC_STAT_DMA_ADR, + efx->stats_buffer.dma_addr); + falcon_write(efx, ®, MAC0_STAT_DMA_REG_KER); + + /* Wait for transfer to complete */ + for (i = 0; i < 400; i++) { + if (*(volatile u32 *)dma_done == FALCON_STATS_DONE) + return 0; + udelay(10); + } + + if (EFX_WORKAROUND_8419(efx)) { + disable_dma_stats = 1; + EFX_INFO(efx, "MAC stats DMA disabled\n"); + } else { + EFX_ERR(efx, "timed out waiting for statistics\n"); + } + + return -ETIMEDOUT; +} + +/************************************************************************** + * + * PHY access via GMII + * + ************************************************************************** + */ + +/* Use the top bit of the MII PHY id to indicate the PHY type + * (1G/10G), with the remaining bits as the actual PHY id. + * + * This allows us to avoid leaking information from the mii_if_info + * structure into other data structures. + */ +#define FALCON_PHY_ID_ID_WIDTH EFX_WIDTH(MD_PRT_DEV_ADR) +#define FALCON_PHY_ID_ID_MASK ((1 << FALCON_PHY_ID_ID_WIDTH) - 1) +#define FALCON_PHY_ID_WIDTH (FALCON_PHY_ID_ID_WIDTH + 1) +#define FALCON_PHY_ID_MASK ((1 << FALCON_PHY_ID_WIDTH) - 1) +#define FALCON_PHY_ID_10G (1 << (FALCON_PHY_ID_WIDTH - 1)) + + +/* Packing the clause 45 port and device fields into a single value */ +#define MD_PRT_ADR_COMP_LBN (MD_PRT_ADR_LBN - MD_DEV_ADR_LBN) +#define MD_PRT_ADR_COMP_WIDTH MD_PRT_ADR_WIDTH +#define MD_DEV_ADR_COMP_LBN 0 +#define MD_DEV_ADR_COMP_WIDTH MD_DEV_ADR_WIDTH + + +/* Wait for GMII access to complete */ +static int falcon_gmii_wait(struct efx_nic *efx) +{ + efx_dword_t md_stat; + int count; + + for (count = 0; count < 1000; count++) { /* wait upto 10ms */ + falcon_readl(efx, &md_stat, MD_STAT_REG_KER); + if (EFX_DWORD_FIELD(md_stat, MD_BSY) == 0) { + if (EFX_DWORD_FIELD(md_stat, MD_LNFL) != 0 || + EFX_DWORD_FIELD(md_stat, MD_BSERR) != 0) { + EFX_ERR(efx, "error from GMII access " + EFX_DWORD_FMT"\n", + EFX_DWORD_VAL(md_stat)); + return -EIO; + } + return 0; + } + udelay(10); + } + EFX_ERR(efx, "timed out waiting for GMII\n"); + return -ETIMEDOUT; +} + +/* Writes a GMII register of a PHY connected to Falcon using MDIO. */ +static void falcon_mdio_write(struct net_device *net_dev, int phy_id, + int addr, int value) +{ + struct efx_nic *efx = (struct efx_nic *)net_dev->priv; + unsigned int phy_id2 = phy_id & FALCON_PHY_ID_ID_MASK; + unsigned int phy_10g = phy_id & FALCON_PHY_ID_10G; + efx_oword_t reg; + + /* The 'generic' prt/dev packing in mdio_10g.h is conveniently + * chosen so that the only current user, Falcon, can take the + * packed value and use them directly. + * Fail to build if this assumption is broken. + */ + BUILD_BUG_ON(FALCON_PHY_ID_10G != MDIO45_XPRT_ID_IS10G); + BUILD_BUG_ON(FALCON_PHY_ID_ID_WIDTH != MDIO45_PRT_DEV_WIDTH); + BUILD_BUG_ON(MD_PRT_ADR_COMP_LBN != MDIO45_PRT_ID_COMP_LBN); + BUILD_BUG_ON(MD_DEV_ADR_COMP_LBN != MDIO45_DEV_ID_COMP_LBN); + + if (phy_id2 == PHY_ADDR_INVALID) + return; + + /* See falcon_mdio_read for an explanation. */ + if (EFX_ISCLAUSE45(efx) && !phy_10g) { + int mmd = ffs(efx->phy_op->mmds) - 1; + EFX_TRACE(efx, "Fixing erroneous clause22 write\n"); + phy_id2 = mdio_clause45_pack(phy_id2, mmd) + & FALCON_PHY_ID_ID_MASK; + phy_10g = 1; + } + + EFX_REGDUMP(efx, "writing GMII %d register %02x with %04x\n", phy_id, + addr, value); + + /* Obtain PHY lock */ + spin_lock_bh(&efx->phy_lock); + + /* Check MII not currently being accessed */ + if (falcon_gmii_wait(efx) != 0) + goto out; + + /* Write the address/ID register */ + EFX_POPULATE_OWORD_1(reg, MD_PHY_ADR, addr); + falcon_write(efx, ®, MD_PHY_ADR_REG_KER); + + if (phy_10g) + EFX_POPULATE_OWORD_1(reg, MD_PRT_DEV_ADR, phy_id2); + else + /* MDIO clause 22 */ + EFX_POPULATE_OWORD_2(reg, + MD_PRT_ADR, phy_id2, + MD_DEV_ADR, addr); + falcon_write(efx, ®, MD_ID_REG_KER); + + /* Write data */ + EFX_POPULATE_OWORD_1(reg, MD_TXD, value); + falcon_write(efx, ®, MD_TXD_REG_KER); + + EFX_POPULATE_OWORD_2(reg, + MD_WRC, 1, + MD_GC, (phy_10g ? 0 : 1)); + falcon_write(efx, ®, MD_CS_REG_KER); + + /* Wait for data to be written */ + if (falcon_gmii_wait(efx) != 0) { + /* Abort the write operation */ + EFX_POPULATE_OWORD_2(reg, + MD_WRC, 0, + MD_GC, 1); + falcon_write(efx, ®, MD_CS_REG_KER); + udelay(10); + } + + out: + /* Release PHY lock */ + spin_unlock_bh(&efx->phy_lock); +} + +/* Reads a GMII register from a PHY connected to Falcon. If no value + * could be read, -1 will be returned. */ +static int falcon_mdio_read(struct net_device *net_dev, int phy_id, int addr) +{ + struct efx_nic *efx = (struct efx_nic *)net_dev->priv; + unsigned int phy_addr = phy_id & FALCON_PHY_ID_ID_MASK; + unsigned int phy_10g = phy_id & FALCON_PHY_ID_10G; + efx_oword_t reg; + int value = -1; + unsigned long flags __attribute__ ((unused)); + + if (phy_addr == PHY_ADDR_INVALID) + return -1; + + /* Our PHY code knows whether it needs to talk clause 22(1G) or 45(10G) + * but the generic Linux code does not make any distinction or have + * any state for this. + * We spot the case where someone tried to talk 22 to a 45 PHY and + * redirect the request to the lowest numbered MMD as a clause45 + * request. This is enough to allow simple queries like id and link + * state to succeed. TODO: We may need to do more in future. + */ + if (EFX_ISCLAUSE45(efx) && !phy_10g) { + int mmd = ffs(efx->phy_op->mmds) - 1; + EFX_TRACE(efx, "Fixing erroneous clause22 read\n"); + phy_addr = mdio_clause45_pack(phy_addr, mmd) + & FALCON_PHY_ID_ID_MASK; + phy_10g = 1; + } + + /* Obtain PHY lock */ + spin_lock_bh(&efx->phy_lock); + + /* Check MII not currently being accessed */ + if (falcon_gmii_wait(efx) != 0) + goto out; + + if (!phy_10g) { + /* Write the address registers */ + EFX_POPULATE_OWORD_2(reg, + MD_PRT_ADR, phy_addr, + MD_DEV_ADR, addr); + falcon_write(efx, ®, MD_ID_REG_KER); + /* Request data to be read */ + EFX_POPULATE_OWORD_2(reg, + MD_RIC, 1, _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |