From: https://support.solarflare.com/netdev/6/netdev-2.6-sfc-2.2.0056.patch # replaces http://xenbits.xensource.com/linux-2.6.18-xen.hg c/s 420: # HG changeset patch # User Keir Fraser # Date 1203330547 0 # Node ID fc90e9b2c12b316b5460ece28f013e6de881af1a # Parent 0034d9389130969f9452b76370f76394e10238b8 Subject: Solarflare: Standard network driver (FATE#303479). Signed-off-by: Kieran Mansley Not-yet: Acked-by: jbeulich@novell.com Index: head-2008-03-17/MAINTAINERS =================================================================== --- head-2008-03-17.orig/MAINTAINERS 2008-03-17 10:27:17.000000000 +0100 +++ head-2008-03-17/MAINTAINERS 2008-03-17 14:26:32.000000000 +0100 @@ -3503,6 +3503,13 @@ M: pfg@sgi.com L: linux-ia64@vger.kernel.org S: Supported +SFC NETWORK DRIVER +P: Steve Hodgson +P: Ben Hutchings +P: Robert Stonehouse +M: linux-net-drivers@solarflare.com +S: Supported + SGI VISUAL WORKSTATION 320 AND 540 P: Andrey Panin M: pazke@donpac.ru Index: head-2008-03-17/drivers/net/Kconfig =================================================================== --- head-2008-03-17.orig/drivers/net/Kconfig 2008-03-06 10:28:04.000000000 +0100 +++ head-2008-03-17/drivers/net/Kconfig 2008-03-17 14:27:16.000000000 +0100 @@ -2672,6 +2672,7 @@ config BNX2X To compile this driver as a module, choose M here: the module will be called bnx2x. This is recommended. +source "drivers/net/sfc/Kconfig" endif # NETDEV_10000 Index: head-2008-03-17/drivers/net/Makefile =================================================================== --- head-2008-03-17.orig/drivers/net/Makefile 2008-03-06 10:28:04.000000000 +0100 +++ head-2008-03-17/drivers/net/Makefile 2008-03-17 14:27:44.000000000 +0100 @@ -252,3 +252,4 @@ obj-$(CONFIG_FS_ENET) += fs_enet/ obj-$(CONFIG_NETXEN_NIC) += netxen/ obj-$(CONFIG_NIU) += niu.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o +obj-$(CONFIG_SFC) += sfc/ Index: head-2008-03-17/drivers/net/sfc/Kconfig =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/Kconfig 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,29 @@ +config SFC + tristate "Solarflare Solarstorm SFC4000 support" + depends on PCI && INET + select MII + select INET_LRO + 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. Index: head-2008-03-17/drivers/net/sfc/Makefile =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/Makefile 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,41 @@ + +# 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_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) + Index: head-2008-03-17/drivers/net/sfc/alaska.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/alaska.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,180 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#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); +} + +void alaska_blink(struct efx_nic *efx, int blink) +{ + struct mii_if_info *gmii = &efx->mii; + u32 leds, ovr; + + leds = gmii->mdio_read(gmii->dev, gmii->phy_id, ALASKA_LED_CONTROL); + ovr = gmii->mdio_read(gmii->dev, gmii->phy_id, ALASKA_LED_OVERRIDE); + + leds &= ~LED_BLINK_MASK; + ovr &= ~(LED_LINK1000_MASK | LED_TX_MASK); + if (blink) { + leds |= LED_BLINK_SLOW; + ovr |= (LED_LINK1000_BLINK | LED_TX_BLINK); + } else { + leds |= LED_BLINK_FAST; + } + + gmii->mdio_write(gmii->dev, gmii->phy_id, ALASKA_LED_CONTROL, leds); + gmii->mdio_write(gmii->dev, gmii->phy_id, ALASKA_LED_OVERRIDE, ovr); +} + +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, +}; Index: head-2008-03-17/drivers/net/sfc/bitfield.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/bitfield.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,543 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 { + u32 u32[1]; +} efx_dword_t; + +/* A quadword (i.e. 8 byte) datatype - little-endian in HW */ +typedef union efx_qword { + u64 u64[1]; + u32 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 { + u64 u64[2]; + efx_qword_t qword[2]; + u32 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])) + +#define EFX_QWORD_IS_ZERO64(qword) \ + (!((qword).u64[0])) + +#define EFX_OWORD_IS_ZERO32(oword) \ + (!((oword).u32[0] | (oword).u32[1] | (oword).u32[2] | (oword).u32[3])) + +#define EFX_QWORD_IS_ZERO32(qword) \ + (!((qword).u32[0] | (qword).u32[1])) + +#define EFX_DWORD_IS_ZERO(dword) \ + (!((dword).u32[0])) + +#define EFX_OWORD_IS_ALL_ONES64(oword) \ + (((oword).u64[0] & (oword).u64[1]) == ~((u64) 0)) + +#define EFX_QWORD_IS_ALL_ONES64(qword) \ + ((qword).u64[0] == ~((u64) 0)) + +#define EFX_OWORD_IS_ALL_ONES32(oword) \ + (((oword).u32[0] & (oword).u32[1] & (oword).u32[2] & (oword).u32[3]) \ + == ~((u32) 0)) + +#define EFX_QWORD_IS_ALL_ONES32(qword) \ + (((qword).u32[0] & (qword).u32[1]) == ~((u32) 0)) + +#define EFX_DWORD_IS_ALL_ONES(dword) \ + ((dword).u32[0] == ~((u32) 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 */ Index: head-2008-03-17/drivers/net/sfc/boards.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/boards.c 2008-03-17 14:26:32.000000000 +0100 @@ -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 + * + * 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", __FUNCTION__); + 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; +} Index: head-2008-03-17/drivers/net/sfc/boards.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/boards.h 2008-03-17 14:26:32.000000000 +0100 @@ -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 + * + * 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 Index: head-2008-03-17/drivers/net/sfc/debugfs.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/debugfs.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,826 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#include +#include +#include +#include +#include "net_driver.h" +#include "efx.h" +#include "debugfs.h" +#include "falcon.h" + +/* 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 */ + +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); +} + +static struct file_operations efx_debugfs_file_ops = { + .owner = THIS_MODULE, + .open = efx_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release +}; + + +/** + * 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); + } +} + +/* + * 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; + 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; + } + + 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 */ + efx_debug_root = debugfs_create_dir("sfc", NULL); + 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; + + 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) +{ + debugfs_remove(efx_debug_cards); + efx_debug_cards = NULL; + debugfs_remove(efx_debug_root); + efx_debug_root = NULL; +} Index: head-2008-03-17/drivers/net/sfc/debugfs.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/debugfs.h 2008-03-17 14:26:32.000000000 +0100 @@ -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 + * Maintained by Solarflare Communications + * + * 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 */ Index: head-2008-03-17/drivers/net/sfc/driverlink.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/driverlink.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,536 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#include +#include +#include +#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. + */ +static enum efx_veto +efx_dummy_tx_packet_callback(struct efx_dl_device *efx_dev, struct sk_buff *skb) +{ + /* Never veto the packet */ + return EFX_ALLOW_PACKET; +} + +static enum efx_veto +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); Index: head-2008-03-17/drivers/net/sfc/driverlink.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/driverlink.h 2008-03-17 14:26:32.000000000 +0100 @@ -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 + * Maintained by Solarflare Communications + * + * 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 */ Index: head-2008-03-17/drivers/net/sfc/driverlink_api.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/driverlink_api.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,594 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 /* for struct list_head */ + +/** + * 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. + */ + enum efx_veto (*tx_packet) (struct efx_dl_device *efx_dev, + struct sk_buff *skb); + + /* + * rx_packet - Packet received. + * @efx_dev: Efx driverlink device + * @pkt_hdr: Pointer to received packet + * @pkt_len: Length of received packet + * + * This method is called for every received packet. It allows + * the client to snoop on traffic received by the kernel + * queues. + * + * The method may return %EFX_VETO_PACKET in order to prevent + * the sfc driver from passing the packet to the kernel. The net + * driver will then discard the packet. + * + * This method must return quickly, since it will have a + * direct performance impact upon the sfc driver. It is + * called in tasklet context, so may not sleep. Note that + * there are per-channel tasklets in the sfc driver, so + * rx_packet() may be called simultaneously on different CPUs + * and must lock appropriately. The design of the sfc driver + * allows for lockless operation between receive channels, so + * please avoid the need for locking if at all possible. + */ + enum efx_veto (*rx_packet) (struct efx_dl_device *efx_dev, + const char *pkt_hdr, int pkt_len); + + /* + * link_change - Link status change. + * @efx_dev: Efx driverlink device + * @link_up: Link up indicator + * + * This method is called to inform the driverlink client + * whenever the PHY link status changes. By the time this + * function is called, the MAC has already been reconfigured + * with the new autonegotiation settings from the PHY. + * + * This method is called from tasklet context and may not + * sleep. + */ + void (*link_change) (struct efx_dl_device *efx_dev, int link_up); + + /* + * request_mtu: Request MTU change. + * @efx_dev: Efx driverlink device + * @new_mtu: Requested new MTU + * + * This method is called whenever the user requests an MTU + * change on an interface. The client may return an error, in + * which case the MTU change request will be denied. If the + * client returns success, the MAC will be reconfigured with a + * new maxmimum frame length equal to + * EFX_MAX_FRAME_LEN(new_mtu). The client will be notified + * via the mtu_changed() method once the MAC has been + * reconfigured. + * + * The current MTU for the port can be obtained via + * efx_dl_get_netdev(efx_dl_device)->mtu. + * + * The sfc driver guarantees that no other callback functions + * are in progress when this method is called. This function + * is called in process context and may sleep. + * + * Return a negative error code or 0 on success. + */ + 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 */ Index: head-2008-03-17/drivers/net/sfc/efx.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/efx.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,2746 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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", +}; + + +/* + * RX maximum head room required. + * + * This must be at least 1 to prevent overflow and at least 2 to allow + * pipelined receives. + */ +#define EFX_RXD_HEAD_ROOM 2 + +#define EFX_MAX_MTU (9 * 1024) + + +/************************************************************************** + * + * Configurable values + * + *************************************************************************/ + +/* + * Enable large receive offload (LRO) aka soft segment reassembly (SSR) + * + * This sets the default for new devices. It can be controlled later + * using ethtool. + */ +static int lro = 1; +module_param(lro, int, 0644); +MODULE_PARM_DESC(lro, "Large receive offload acceleration"); + +/* + * 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 percentage fill level below which new RX descriptors + * will be added to the RX descriptor ring. + */ +static unsigned int rx_refill_threshold = 90; + +/* This is the percentage fill level to which an RX queue will be refilled + * when the "RX refill threshold" is reached. + */ +static unsigned int rx_refill_limit = 95; + +/* 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_flush_lro(channel); + efx_rx_strategy(channel); + + /* Refill descriptor rings as necessary */ + rx_queue = &channel->efx->rx_queue[0]; + while (rxdmaqs) { + if (rxdmaqs & 0x01) + efx_fast_push_rx_descriptors(rx_queue); + rx_queue++; + rxdmaqs >>= 1; + } + + return rx_quota; +} + +/* 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(). + */ +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; + 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 (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); + } + + return rx_packets; +} + +/* 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 + * + *************************************************************************/ + +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 may be shut down and reinitialised while the NIC is + * running, in order 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; + + efx_for_each_channel(channel, efx) { + EFX_LOG(channel->efx, "init chan %d\n", channel->channel); + + rc = efx_init_eventq(channel); + if (rc) + goto err; + + efx_for_each_channel_tx_queue(tx_queue, channel) { + rc = efx_init_tx_queue(tx_queue); + if (rc) + goto err; + } + + efx_for_each_channel_rx_queue(rx_queue, channel) { + rc = efx_init_rx_queue(rx_queue); + if (rc) + goto err; + } + + 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(&rx_queue->add_lock); + spin_unlock(&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 + * + **************************************************************************/ + +/* + * Setup per-NIC RX buffer parameters. + * + * This calculates the receive buffer length required to support a + * given MTU. It includes padding for header alignment and for + * possible overrun (SFC bug 8071). + */ +static void efx_calc_rx_buffer_params(struct efx_nic *efx, unsigned int mtu) +{ + unsigned int order, len; + + len = (max(EFX_PAGE_IP_ALIGN, NET_IP_ALIGN) + EFX_MAX_FRAME_LEN(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; +} + +/* 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); +} + +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; +} + +/* 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_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); + + /* Start TX queue */ + efx_wake_queue(efx); +} + +static void efx_stop_port(struct efx_nic *efx) +{ + EFX_LOG(efx, "stop port\n"); + ASSERT_RTNL(); + + if (!efx->port_enabled) + return; + + /* Stop TX queue */ + efx_stop_queue(efx); + + /* Ensure the TX engine is stopped */ + if (efx->net_dev_registered) { + netif_tx_lock(efx->net_dev); + netif_tx_unlock(efx->net_dev); + } + + /* Mark port as disabled */ + mutex_lock(&efx->mac_lock); + efx->port_enabled = 0; + mutex_unlock(&efx->mac_lock); +} + +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"); + + /* Paranoia. efx_reconfigure_work() should have been flushed + * be efx_stop_all (which is always called after efx_stop_port). */ + flush_workqueue(efx->workqueue); + + 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); + rc = pci_request_region(pci_dev, efx->type->mem_bar, "sfc"); + 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) { + pci_release_region(efx->pci_dev, efx->type->mem_bar); + 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; +} + +/* Start NIC, channels and port and the hardware monitor + * 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_all(struct efx_nic *efx) +{ + struct efx_channel *channel; + + efx_for_each_channel(channel, efx) + efx_start_channel(channel); + + efx_start_port(efx); + + 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); +} + +static void efx_stop_all(struct efx_nic *efx) +{ + struct efx_channel *channel; + struct efx_rx_queue *rx_queue; + + ASSERT_RTNL(); + + /* We must also ensure that all callback functions have returned. + * efx_stop_channel() and will sync with the rx refill tasklet + * tx_packet() callback is called under the TX lock, so + * efx_stop_channel() ensures that it has returned. + */ + + /* 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 port and channels. Port must be stopped first, to + * ensure that the TX path is disabled. + */ + efx_stop_port(efx); + efx_for_each_channel(channel, efx) + efx_stop_channel(channel); + + /* Flush asynchronous port reconfigurations */ + cancel_work_sync(&efx->reconfigure_work); + + /* 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); + } +} + +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); +} + +/* Push RX descriptors on to all the queues */ +static void efx_fill_queues(struct efx_nic *efx) +{ + struct efx_rx_queue *rx_queue; + unsigned int max_fill = + efx->type->rxd_ring_mask + 1 - EFX_RXD_HEAD_ROOM; + unsigned int fast_fill_trigger = + max_fill * min(rx_refill_threshold, 100U) / 100U; + unsigned int fast_fill_limit = + max_fill * min(rx_refill_limit, 100U) / 100U; + + /* Load RX descriptors */ + efx_for_each_rx_queue(rx_queue, efx) { + /* Set active RX descriptor fill levels */ + rx_queue->max_fill = max_fill; + rx_queue->fast_fill_trigger = fast_fill_trigger; + rx_queue->fast_fill_limit = fast_fill_limit; + efx_fast_push_rx_descriptors(rx_queue); + } +} + +/* Flush RX descriptors from all the queues + * + * Warning: It is the caller's responsibility to make + * sure this does not run concurrently with the RX data + * path. + */ +static void efx_flush_rx_queues(struct efx_nic *efx) +{ + struct efx_rx_queue *rx_queue; + + /* Flush RX descriptors */ + efx_for_each_rx_queue(rx_queue, efx) { + /* Zero descriptor fill levels */ + rx_queue->fast_fill_trigger = 0; + rx_queue->fast_fill_limit = 0; + rx_queue->max_fill = 0; + + /* Reset RX queue to flush descriptors */ + efx_fini_rx_queue(rx_queue); + + if (efx->state == STATE_DISABLED) + continue; + + /* Reinitialise the RX queue but leave without descriptors */ + if (efx_init_rx_queue(rx_queue) != 0) + EFX_ERR(efx, "failed to restart RX queue %d\n", + rx_queue->queue); + } +} + +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) { + /* net_dev is not currently created yet so use dummy mtu */ + efx_calc_rx_buffer_params(efx, 1500); + + /* Load RX descriptors */ + efx_fill_queues(efx); + + /* 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_tx_queues(struct efx_nic *efx) +{ + struct efx_tx_queue *tx_queue; + int rc; + + ASSERT_RTNL(); + + /* Stop the port, so TX is quiescent. We want any ISR's to + * finish, so that means disabling interrupts. + */ + efx_stop_all(efx); + + /* Tear down and re-create each TX queue */ + efx_for_each_tx_queue(tx_queue, efx) { + efx_fini_tx_queue(tx_queue); + rc = efx_init_tx_queue(tx_queue); + if (rc != 0) { + efx_schedule_reset(efx, RESET_TYPE_DISABLE); + return rc; + } + } + + /* Start TX again */ + if ((efx->state == STATE_RUNNING) && (efx->net_dev->flags & IFF_UP)) + 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 + * + **************************************************************************/ + +static void efx_monitor(struct work_struct *data) +{ + struct efx_nic *efx = container_of(data, struct efx_nic, + monitor_work.work); + int rc = 0; + + EFX_TRACE(efx, "hardware monitor executing on CPU %d\n", + raw_smp_processor_id()); + + if (!mutex_trylock(&efx->mac_lock)) + goto reschedule; + if (efx->port_enabled) + rc = efx->mac_op->check_hw(efx); + 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"); + } + } + + reschedule: + 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; + if (lro) + efx->net_dev->features |= NETIF_F_LRO; + + /* Copy MAC address */ + memcpy(&efx->net_dev->dev_addr, efx->mac_address, ETH_ALEN); + + /* Allocate the per channel devs */ + efx_for_each_channel(channel, efx) { + channel->napi_dev = efx->net_dev; + + /* Initialise LRO/SSR */ + rc = efx_lro_init(&channel->lro_mgr, efx); + if (rc) + goto err; + } + + 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) { + /* Fini LRO/SSR */ + efx_lro_fini(&channel->lro_mgr); + + /* Finish per channel NAPI */ + 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; + int rc = 0; + + EFX_LOG(efx, "attempting to open %s\n", net_dev->name); + + ASSERT_RTNL(); + + EFX_LOG(efx, "opening device on CPU %d\n", raw_smp_processor_id()); + + efx_calc_rx_buffer_params(efx, efx->net_dev->mtu); + + if (efx->state == STATE_RUNNING) + efx_start_all(efx); + + /* Give device some RX descriptors */ + efx_fill_queues(efx); + + /* Start interface */ + efx_wake_queue(efx); + + return rc; +} + +/* 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; + + EFX_LOG(efx, "closing %s on CPU %d\n", net_dev->name, + raw_smp_processor_id()); + + /* Stop device */ + efx_stop_all(efx); + + /* Decrement the port usage count by 1 again */ + efx_stop_queue(efx); + + /* Flush RX descriptors */ + efx_flush_rx_queues(efx); + + 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: %s\n", + atomic_read(&efx->netif_stop_count), + 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; + struct efx_channel *channel; + 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); + + /* Set new maximum frame length for both port's own MAC and + * the per-NIC RX buffer allocator. + */ + net_dev->mtu = new_mtu; + efx_calc_rx_buffer_params(efx, new_mtu); + efx_for_each_channel(channel, efx) { + efx_rx_strategy(channel); + } + EFX_LOG(efx, "new RX buffer length is %d\n", efx->rx_buffer_len); + + /* Reconfigure the MAC */ + efx_reconfigure_port(efx, 1); + + rc = efx_init_channels(efx); + if (rc) + goto fail; + + /* Notify driverlink client of new MTU */ + EFX_DL_CALLBACK(efx, mtu_changed, new_mtu); + + /* Restart device if needed. This function can be called in + * STATE_FINI between the call to efx_stop_all and + * efx_unregister_netdev. In that case, it's safe to call + * efx_stop_all again (above), but we mustn't call + * efx_start_all. */ + if ((efx->state == STATE_RUNNING) && (efx->net_dev->flags & IFF_UP)) + 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 *new_address = data; + + ASSERT_RTNL(); + + memcpy(net_dev->dev_addr, new_address->sa_data, 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) { + 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); + + /* 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); + + /* Create debugfs symlinks */ + efx_init_debugfs_netdev(net_dev); + + /* 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(); + + if (efx->state == STATE_RUNNING) { + /* Actually suspend the device */ + efx_stop_all(efx); + } +} + +void efx_resume(struct efx_nic *efx) +{ + EFX_LOG(efx, "resuming operations\n"); + + /* Resume the device */ + if ((efx->state == STATE_RUNNING) && (efx->net_dev->flags & IFF_UP)) + efx_start_all(efx); + + /* Release rtnl_lock */ + 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; + if (efx->net_dev->flags & IFF_UP) + 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; + + queue_work(efx->workqueue, &efx->reset_work); +} + +/************************************************************************** + * + * 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); + + /* Doubly stopped at start of day: interface down, + * and efx_start_port not yet called. + */ + atomic_set(&efx->netif_stop_count, 2); + + 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_RXD_HEAD_ROOM)); + 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); + + /* 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; + } + + return 0; + + fail2: + destroy_workqueue(efx->refill_workqueue); + efx->refill_workqueue = NULL; + fail1: + return rc; +} + +static void efx_fini_struct(struct efx_nic *efx) +{ + 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. */ + efx_stop_all(efx); + + /* Release the rtnl lock. Any queued efx_resets() + * can now return early [we're in STATE_FINI]. */ + rtnl_unlock(); + + /* Unregister net device. This will trigger + * shutdown of the interface, which will + * flush any outstanding descriptors. */ + 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. */ + flush_workqueue(efx->workqueue); + + /* 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; + + /* If there was an RX_RESET during driver load, then + * repeat the load. */ + if (efx->reset_pending == RESET_TYPE_NONE) + 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); + + cancel_work_sync(&efx->reset_work); + + 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 and " + "Solarflare Communications"); +MODULE_DESCRIPTION("Solarflare Communications network driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, efx_pci_table); Index: head-2008-03-17/drivers/net/sfc/efx.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/efx.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,87 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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); +extern int efx_flush_tx_queues(struct efx_nic *efx); + +/* RX */ +extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); +extern void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, + unsigned int len, int checksummed, int discard); +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); + +/* 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; + + netif_rx_schedule(channel->napi_dev, &channel->napi_str); +} + + +#endif /* EFX_EFX_H */ Index: head-2008-03-17/drivers/net/sfc/enum.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/enum.h 2008-03-17 14:26:32.000000000 +0100 @@ -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 + * + * 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 */ Index: head-2008-03-17/drivers/net/sfc/ethtool.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/ethtool.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,714 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#include +#include +#include +#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 { + u8 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_tx_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 + * kernel wathdog may fire if the test take too long, but + * since we're holding the rtnl lock nothing any rx resets + * will be deferred until after the test. */ + efx_stop_queue(efx); + rc = efx_flush_tx_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; +} + + +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, + .get_flags = ethtool_op_get_flags, + .set_flags = ethtool_op_set_flags, + .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, +}; Index: head-2008-03-17/drivers/net/sfc/ethtool.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/ethtool.h 2008-03-17 14:26:32.000000000 +0100 @@ -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 + * Maintained by Solarflare Communications + * + * 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 */ Index: head-2008-03-17/drivers/net/sfc/falcon.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/falcon.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,3598 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#include +#include +#include +#include +#include +#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 + */ + +static int falcon_process_eventq_flush(struct efx_channel *channel, + int is_rx, int queue); + +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 + +/* Max number of internal errors. After this resets will not be performed */ +#define FALCON_MAX_INT_ERRORS 4 + + +/************************************************************************** + * + * 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 dword from a Falcon PCIE core register */ +static void falcon_pcie_core_read_reg(struct efx_nic *efx, int address, + u32 *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); + *result = EFX_OWORD_FIELD(temp, PCIE_CORE_VALUE); + +} + +/* Write dword to a Falcon PCIE core register */ +static void falcon_pcie_core_write_reg(struct efx_nic *efx, int address, + u32 value) +{ + efx_oword_t temp; + + BUG_ON(FALCON_REV(efx) < FALCON_REV_B0); + BUG_ON(address & 0x3 || address < 0); + + EFX_POPULATE_OWORD_3(temp, + PCIE_CORE_ADDR, address, + PCIE_CORE_VALUE, value, + PCIE_CORE_RW, 1); + 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. + */ +void falcon_push_buffers(struct efx_tx_queue *tx_queue) +{ + + struct efx_tx_buffer *buffer; + efx_qword_t *txd; + unsigned write_ptr; + + BUG_ON(tx_queue->write_count == tx_queue->insert_count); + + do { + write_ptr = tx_queue->write_count & 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; +} + +void falcon_fini_tx(struct efx_tx_queue *tx_queue) +{ + efx_oword_t tx_flush_descq; + efx_oword_t tx_desc_ptr; + struct efx_nic *efx = tx_queue->efx; + + /* Flush TX descriptor queue */ + 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); + + /* Give time for flush event to appear */ + schedule_timeout_uninterruptible(HZ / 100); + if (!EFX_WORKAROUND_7803(efx)) + falcon_process_eventq_flush(&efx->channel[0], 0, + tx_queue->queue); + + /* Remove TX descriptor ring from card */ + EFX_ZERO_OWORD(tx_desc_ptr); + falcon_write_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, + tx_queue->queue); + + /* Unpin TX descriptor ring */ + falcon_fini_special_buffer(efx, &tx_queue->txd); +} + +/* Free buffers backing TX queue */ +void falcon_remove_tx(struct efx_tx_queue *tx_queue) +{ + falcon_free_special_buffer(tx_queue->efx, &tx_queue->txd); +} + +/************************************************************************** + * + * Falcon RX path + * + **************************************************************************/ + +/* Returns a pointer to the specified transmit descriptor in the RX + * descriptor queue. + */ +static inline efx_qword_t *falcon_rx_desc(struct efx_rx_queue *rx_queue, + unsigned int index) +{ + return (((efx_qword_t *) (rx_queue->rxd.addr)) + index); +} + +/* This creates an entry in the RX descriptor queue corresponding to + * the receive buffer. + */ +static inline void falcon_build_rx_desc(struct efx_rx_queue *rx_queue, + unsigned index) +{ + struct efx_rx_buffer *rx_buf; + efx_qword_t *rxd; + + rxd = falcon_rx_desc(rx_queue, index); + rx_buf = efx_rx_buffer(rx_queue, index); + EFX_POPULATE_QWORD_3(*rxd, + RX_KER_BUF_SIZE, + rx_buf->len - + rx_queue->efx->type->rx_buffer_padding, + RX_KER_BUF_REGION, 0, + RX_KER_BUF_ADR, rx_buf->dma_addr); +} + +/* This writes to the RX_DESC_WPTR register for the specified receive + * descriptor ring. + */ +void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue) +{ + efx_dword_t reg; + unsigned write_ptr; + + while (rx_queue->notified_count != rx_queue->added_count) { + falcon_build_rx_desc(rx_queue, + rx_queue->notified_count & + FALCON_RXD_RING_MASK); + ++rx_queue->notified_count; + } + + wmb(); + write_ptr = rx_queue->added_count & FALCON_RXD_RING_MASK; + EFX_POPULATE_DWORD_1(reg, RX_DESC_WPTR_DWORD, write_ptr); + falcon_writel_page(rx_queue->efx, ®, + RX_DESC_UPD_REG_KER_DWORD, rx_queue->queue); +} + +int falcon_probe_rx(struct efx_rx_queue *rx_queue) +{ + struct efx_nic *efx = rx_queue->efx; + struct falcon_nic_data *nic_data = efx->nic_data; + int rc; + + rc = falcon_alloc_special_buffer(efx, &rx_queue->rxd, + FALCON_RXD_RING_SIZE * + sizeof(efx_qword_t)); + if (rc) + return rc; + + /* Increment the rxq_min counter */ + nic_data->resources.rxq_min = max(nic_data->resources.rxq_min, + (unsigned)rx_queue->queue + 1); + + return 0; +} + +int falcon_init_rx(struct efx_rx_queue *rx_queue) +{ + efx_oword_t rx_desc_ptr; + struct efx_nic *efx = rx_queue->efx; + int rc; + int is_b0 = FALCON_REV(efx) >= FALCON_REV_B0; + int iscsi_digest_en = is_b0; + + EFX_LOG(efx, "RX queue %d ring in special buffers %d-%d\n", + rx_queue->queue, rx_queue->rxd.index, + rx_queue->rxd.index + rx_queue->rxd.entries - 1); + + /* Pin RX descriptor ring */ + rc = falcon_init_special_buffer(efx, &rx_queue->rxd); + if (rc) + return rc; + + /* Push RX descriptor ring to card */ + EFX_POPULATE_OWORD_10(rx_desc_ptr, + RX_ISCSI_DDIG_EN, iscsi_digest_en, + RX_ISCSI_HDIG_EN, iscsi_digest_en, + RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index, + RX_DESCQ_EVQ_ID, rx_queue->channel->evqnum, + RX_DESCQ_OWNER_ID, 0, + RX_DESCQ_LABEL, rx_queue->queue, + RX_DESCQ_SIZE, FALCON_RXD_RING_ORDER, + RX_DESCQ_TYPE, 0 /* kernel queue */ , + /* For >=B0 this is scatter so disable */ + RX_DESCQ_JUMBO, !is_b0, + RX_DESCQ_EN, 1); + falcon_write_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, + rx_queue->queue); + return 0; +} + +static void falcon_flush_rx_queue(struct efx_rx_queue *rx_queue) +{ + efx_oword_t rx_flush_descq; + struct efx_nic *efx = rx_queue->efx; + int i, queue = rx_queue->queue; + + EFX_POPULATE_OWORD_2(rx_flush_descq, + RX_FLUSH_DESCQ_CMD, 1, + RX_FLUSH_DESCQ, queue); + + for (i = 0; i < 5; i++) { + falcon_write(efx, &rx_flush_descq, RX_FLUSH_DESCQ_REG_KER); + schedule_timeout_uninterruptible(HZ / 100); + if (!falcon_process_eventq_flush(&efx->channel[0], 1, queue)) + return; + } + EFX_ERR(efx, "RX flush not seen\n"); +} + +void falcon_fini_rx(struct efx_rx_queue *rx_queue) +{ + efx_oword_t rx_desc_ptr; + struct efx_nic *efx = rx_queue->efx; + + falcon_flush_rx_queue(rx_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. + * + **************************************************************************/ + +/* 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]))); +} + +/* Update a channel's event queue's read pointer (RPTR) register + * + * This writes the EVQ_RPTR_REG register for the specified channel's + * event queue. + * + * Note that EVQ_RPTR_REG contains the index of the "last read" event, + * whereas channel->eventq_read_ptr contains the index of the "next to + * read" event. + */ +void falcon_eventq_read_ack(struct efx_channel *channel) +{ + efx_dword_t reg; + struct efx_nic *efx = channel->efx; + + EFX_POPULATE_DWORD_1(reg, 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 { + 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; + } +} + +/* Process the eventQ to find flush done events + * + * It is expected that this is run when the RXQs and TXQs are stopped + * To ensure safety only process the flush events. They are not + * removed and will be seen again by falcon_process_eventq() if + * interrupts are re-enabled + */ +static int falcon_process_eventq_flush(struct efx_channel *channel, + int is_rx, int queue) +{ + unsigned int read_ptr; + int i, ev_code, ev_sub_code; + + EFX_BUG_ON_PARANOID(!channel->eventq.addr); + + if (channel->efx->reset_pending != RESET_TYPE_NONE) + return 0; + + read_ptr = channel->eventq_read_ptr; + + /* Protect against eventQ overflowing (e.g. driver/global events) */ + for (i = 0; i < FALCON_EVQ_SIZE; ++i) { + efx_qword_t *event = falcon_event(channel, read_ptr); + + if (!falcon_event_present(event)) + break; /* End of events */ + + ev_code = EFX_QWORD_FIELD(*event, EV_CODE); + if (ev_code != DRIVER_EV_DECODE) + continue; + + ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE); + + if (is_rx && (ev_sub_code == RX_DESCQ_FLS_DONE_EV_DECODE) && + (EFX_QWORD_FIELD(*event, DRIVER_EV_RX_DESCQ_ID) == queue)) { + if (EFX_QWORD_FIELD(*event, DRIVER_EV_RX_FLUSH_FAIL)) + return -1; /* retry */ + else + return 0; /* Found it! */ + } + + if (!is_rx && (ev_sub_code == TX_DESCQ_FLS_DONE_EV_DECODE) && + (EFX_QWORD_FIELD(*event, DRIVER_EV_TX_DESCQ_ID) == queue)) + return 0; /* Found it! */ + + read_ptr = (read_ptr + 1) & (FALCON_EVQ_SIZE - 1); + } + + EFX_ERR(channel->efx, "%s_queue %d flush not seen on channel %d\n", + is_rx ? "rx" : "tx", queue, channel->channel); + return -1; +} + +int falcon_process_eventq(struct efx_channel *channel, int *rx_quota) +{ + unsigned int read_ptr; + efx_qword_t event, *p_event; + int ev_code; + int 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_SIZE - 1); + + } 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(). + */ +static irqreturn_t falcon_legacy_interrupt_b0(int irq, void *dev_id) +{ + 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 = reg.u32[0]; + + /* 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; +} + + +static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) +{ + 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(). + */ +static irqreturn_t falcon_msi_interrupt(int irq, void *dev_id) +{ + 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; + + 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); + + /* MAC stats will fail whilst the EM block is in reset */ + spin_lock(&efx->stats_lock); + + /* 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; + + /* 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, + MD_GC, 1); + } else { + EFX_POPULATE_OWORD_1(reg, MD_PHY_ADR, addr); + falcon_write(efx, ®, MD_PHY_ADR_REG_KER); + + EFX_POPULATE_OWORD_1(reg, MD_PRT_DEV_ADR, phy_addr); + falcon_write(efx, ®, MD_ID_REG_KER); + + /* Request data to be read */ + EFX_POPULATE_OWORD_2(reg, + MD_RDC, 1, + MD_GC, 0); + } + falcon_write(efx, ®, MD_CS_REG_KER); + + /* Wait for data to become available */ + value = falcon_gmii_wait(efx); + if (value == 0) { + falcon_read(efx, ®, MD_RXD_REG_KER); + value = EFX_OWORD_FIELD(reg, MD_RXD); + EFX_REGDUMP(efx, "read from GMII %d register %02x, got %04x\n", + phy_id, addr, value); + } else { + /* Abort the read operation */ + EFX_POPULATE_OWORD_2(reg, + MD_RIC, 0, + MD_GC, 1); + falcon_write(efx, ®, MD_CS_REG_KER); + + EFX_LOG(efx, "read from GMII 0x%x register %02x, got " + "error %d\n", phy_id, addr, value); + } + + out: + /* Release PHY lock */ + spin_unlock_bh(&efx->phy_lock); + + return value; +} + +static void falcon_init_mdio(struct mii_if_info *gmii) +{ + gmii->mdio_read = falcon_mdio_read; + gmii->mdio_write = falcon_mdio_write; + gmii->phy_id_mask = FALCON_PHY_ID_MASK; + gmii->reg_num_mask = ((1 << EFX_WIDTH(MD_DEV_ADR)) - 1); +} + +static int falcon_probe_gmac_port(struct efx_nic *efx) +{ + struct efx_phy_operations *phy_op = efx->phy_op; + + efx->mac_op = &falcon_gmac_operations; + efx->loopback_modes = LOOPBACKS_1G_INTERNAL | phy_op->loopbacks; + efx->startup_loopbacks = ((1 << LOOPBACK_MAC) | + (1 << phy_op->startup_loopback)); + return 0; +} + +static int falcon_probe_xmac_port(struct efx_nic *efx) +{ + struct efx_phy_operations *phy_op = efx->phy_op; + + efx->mac_op = &falcon_xmac_operations; + + /* The Falcon B0 FPGA only supports XGMII loopback */ + if (FALCON_REV(efx) >= FALCON_REV_B0 && !efx->is_asic) + efx->loopback_modes = (1 << LOOPBACK_XGMII); + else + efx->loopback_modes = LOOPBACKS_10G_INTERNAL; + efx->loopback_modes |= phy_op->loopbacks; + + efx->startup_loopbacks = ((1 << LOOPBACK_XGMII) | + (1 << phy_op->startup_loopback)); + return 0; +} + +static int falcon_probe_phy(struct efx_nic *efx) +{ + switch (efx->phy_type) { + case PHY_TYPE_1G_ALASKA: + efx->phy_op = &alaska_phy_operations; + break; + case PHY_TYPE_10XPRESS: + efx->phy_op = &falcon_tenxpress_phy_ops; + break; + case PHY_TYPE_NONE: + efx->phy_op = &falcon_null_phy_ops; + break; + case PHY_TYPE_XFP: + efx->phy_op = &falcon_xfp_phy_ops; + break; + case PHY_TYPE_CX4_RTMR: + efx->phy_op = &falcon_txc_phy_ops; + break; + case PHY_TYPE_PM8358: + efx->phy_op = &falcon_pm8358_phy_ops; + break; + default: + EFX_ERR(efx, "Unknown PHY type %d\n", + efx->phy_type); + return -1; + } + return 0; +} + +/* This call is responsible for hooking in the MAC and PHY operations */ +int falcon_probe_port(struct efx_nic *efx) +{ + int rc; + + /* Hook in PHY operations table */ + rc = falcon_probe_phy(efx); + if (rc) + return rc; + + /* Hook in MAC operations table */ + if (EFX_IS10G(efx)) + rc = falcon_probe_xmac_port(efx); + else + rc = falcon_probe_gmac_port(efx); + if (rc) + return rc; + + EFX_LOG(efx, "created port using %cMAC\n", + EFX_IS10G(efx) ? 'X' : 'G'); + + /* Set up GMII structure for PHY */ + efx->mii.supports_gmii = 1; + falcon_init_mdio(&efx->mii); + + /* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */ + if (FALCON_REV(efx) >= FALCON_REV_B0) + efx->flow_control = EFX_FC_RX | EFX_FC_TX; + else + efx->flow_control = EFX_FC_RX; + + /* Allocate buffer for stats */ + rc = falcon_alloc_buffer(efx, &efx->stats_buffer, + FALCON_MAC_STATS_SIZE); + if (rc) + return rc; + EFX_LOG(efx, "stats buffer at %llx (virt %p phys %lx)\n", + (unsigned long long)efx->stats_buffer.dma_addr, + efx->stats_buffer.addr, + virt_to_phys(efx->stats_buffer.addr)); + + return 0; +} + +void falcon_remove_port(struct efx_nic *efx) +{ + /* Free stats buffer */ + falcon_free_buffer(efx, &efx->stats_buffer); +} + +/************************************************************************** + * + * Multicast filtering + * + ************************************************************************** + */ + +void falcon_set_multicast_hash(struct efx_nic *efx) +{ + union efx_multicast_hash falcon_mc_hash; + + /* Broadcast packets go through the multicast hash filter. + * ether_crc_le() of the broadcast address is 0xbe2612ff + * so we always add bit 0xff to the mask we are given. + */ + memcpy(&falcon_mc_hash, &efx->multicast_hash, sizeof(falcon_mc_hash)); + set_bit_le(0xff, (void *)&falcon_mc_hash); + + falcon_write(efx, &falcon_mc_hash.oword[0], MAC_MCAST_HASH_REG0_KER); + falcon_write(efx, &falcon_mc_hash.oword[1], MAC_MCAST_HASH_REG1_KER); +} + +/************************************************************************** + * + * Device reset + * + ************************************************************************** + */ + +static int falcon_clear_b0_memories(struct efx_nic *efx) +{ + /* Need to clear memories after a reset. On B0 we can do this + * via the net function. + */ + int rc = 0, offset; + efx_oword_t blanko; + efx_dword_t blankd; + unsigned long membase_phys, membase_len; + void __iomem *membase_orig; + unsigned long flags __attribute__ ((unused)); + + EFX_ZERO_OWORD(blanko); + EFX_ZERO_DWORD(blankd); + membase_orig = efx->membase; + membase_phys = pci_resource_start(efx->pci_dev, efx->type->mem_bar); + + for (offset = RX_FILTER_TBL0; + offset < RX_RSS_INDIR_TBL_B0; + offset += 0x10) + falcon_write(efx, &blanko, offset); + + /* Clear RSS indirection table */ + for (offset = RX_RSS_INDIR_TBL_B0; + offset < RX_RSS_INDIR_TBL_B0 + 0x800; + offset += 0x10) + /* Clear 6 bits every 16 bytes */ + falcon_writel(efx, &blankd, offset); + + /* Need to split this into several mappings so MSI-X table and PBA + * never get mapped + */ + membase_phys = membase_phys + 0x2800000; + membase_len = 0x3000000 - 0x2800000; + + efx->membase = ioremap_nocache(membase_phys, membase_len); + if (efx->membase == NULL) { + EFX_ERR(efx, "could not map memory BAR %d at %lx+%lx\n", + efx->type->mem_bar, membase_phys, membase_len); + rc = -ENOMEM; + goto out; + } + /* Clear the buffer table. The first 7/8 of it is a duplicate + * of the mapping at 0x800000 and must be accessed 2 DWORDs at + * a time. The final 1/8 must be accessed 4 DWORDs at a time. + * We make sure to obey both rules at the same time. + */ + for (offset = 0; offset < membase_len; offset += 0x10) { + spin_lock_irqsave(&efx->biu_lock, flags); + _falcon_writel(efx, 0, offset + 0x0); + wmb(); + _falcon_writel(efx, 0, offset + 0x4); + wmb(); + _falcon_writel(efx, 0, offset + 0x8); + wmb(); + _falcon_writel(efx, 0, offset + 0xc); + mmiowb(); + spin_unlock_irqrestore(&efx->biu_lock, flags); + } + + iounmap(efx->membase); + +out: + /* Restore */ + efx->membase = membase_orig; + + return rc; +} + + +/* Resets NIC to known state. This routine must be called in process + * context and is allowed to sleep. */ +int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) +{ + efx_oword_t glb_ctl_reg_ker; + int rc; + + EFX_LOG(efx, "performing %s hardware reset\n", RESET_TYPE(method)); + + /* Initiate device reset */ + if (method == RESET_TYPE_WORLD) { + /* Save PCI config space */ + rc = pci_save_state(efx->pci_dev); + if (rc) { + EFX_ERR(efx, "failed to backup PCI state of primary " + "function prior to hardware reset\n"); + goto fail1; + } + if (efx->type->is_dual_func) { + rc = pci_save_state(efx->pci_dev2); + if (rc) { + EFX_ERR(efx, "failed to backup PCI state of " + "secondary function prior to " + "hardware reset\n"); + goto fail2; + } + } + + EFX_POPULATE_OWORD_2(glb_ctl_reg_ker, + EXT_PHY_RST_DUR, 0x7, + SWRST, 1); + } else { + int reset_phy = (method == RESET_TYPE_INVISIBLE ? + EXCLUDE_FROM_RESET : 0); + + EFX_POPULATE_OWORD_7(glb_ctl_reg_ker, + EXT_PHY_RST_CTL, reset_phy, + PCIE_CORE_RST_CTL, EXCLUDE_FROM_RESET, + PCIE_NSTCK_RST_CTL, EXCLUDE_FROM_RESET, + PCIE_SD_RST_CTL, EXCLUDE_FROM_RESET, + EE_RST_CTL, EXCLUDE_FROM_RESET, + EXT_PHY_RST_DUR, 0x7 /* 10ms */, + SWRST, 1); + } + falcon_write(efx, &glb_ctl_reg_ker, GLB_CTL_REG_KER); + + /* Wait for 50ms for the chip to come out of reset */ + EFX_LOG(efx, "waiting for hardware reset\n"); + schedule_timeout_uninterruptible(HZ / 20); + + /* Restore PCI configuration if needed */ + if (method == RESET_TYPE_WORLD) { + if (efx->type->is_dual_func) { + rc = pci_restore_state(efx->pci_dev2); + if (rc) { + EFX_ERR(efx, "failed to restore PCI config for " + "the secondary function\n"); + goto fail3; + } + } + rc = pci_restore_state(efx->pci_dev); + if (rc) { + EFX_ERR(efx, "failed to restore PCI config for the " + "primary function\n"); + goto fail4; + } + EFX_LOG(efx, "successfully restored PCI config\n"); + } + + /* Assert that reset complete */ + falcon_read(efx, &glb_ctl_reg_ker, GLB_CTL_REG_KER); + if (EFX_OWORD_FIELD(glb_ctl_reg_ker, SWRST) != 0) { + rc = -ETIMEDOUT; + EFX_ERR(efx, "timed out waiting for hardware reset\n"); + goto fail5; + } + EFX_LOG(efx, "hardware reset complete\n"); + + if (EFX_WORKAROUND_8202(efx)) { + rc = falcon_clear_b0_memories(efx); + if (rc) + goto fail6; + } + + return 0; + + /* pci_save_state() and pci_restore_state() MUST be called in pairs */ +fail2: +fail3: + pci_restore_state(efx->pci_dev); + /* fall-thru */ +fail1: +fail4: +fail5: +fail6: + return rc; +} + +/* Zeroes out the SRAM contents. This routine must be called in + * process context and is allowed to sleep. + */ +static int falcon_reset_sram(struct efx_nic *efx) +{ + efx_oword_t srm_cfg_reg_ker, gpio_cfg_reg_ker; + int count, onchip, sram_cfg_val; + + /* Set the SRAM wake/sleep GPIO appropriately. */ + onchip = (efx->external_sram_cfg == SRM_NB_BSZ_ONCHIP_ONLY); + falcon_read(efx, &gpio_cfg_reg_ker, GPIO_CTL_REG_KER); + EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, GPIO1_OEN, 1); + EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, GPIO1_OUT, onchip ? 1 : 0); + falcon_write(efx, &gpio_cfg_reg_ker, GPIO_CTL_REG_KER); + + /* Initiate SRAM reset */ + sram_cfg_val = (efx->external_sram_cfg == SRM_NB_BSZ_ONCHIP_ONLY) ? + 0 : efx->external_sram_cfg; + + EFX_POPULATE_OWORD_2(srm_cfg_reg_ker, + SRAM_OOB_BT_INIT_EN, 1, + SRM_NUM_BANKS_AND_BANK_SIZE, sram_cfg_val); + falcon_write(efx, &srm_cfg_reg_ker, SRM_CFG_REG_KER); + + /* Wait for SRAM reset to complete */ + count = 0; + do { + EFX_LOG(efx, "waiting for SRAM reset (attempt %d)...\n", count); + + /* SRAM reset is slow; expect around 16ms */ + schedule_timeout_uninterruptible(HZ / 50); + + /* Check for reset complete */ + falcon_read(efx, &srm_cfg_reg_ker, SRM_CFG_REG_KER); + if (!EFX_OWORD_FIELD(srm_cfg_reg_ker, SRAM_OOB_BT_INIT_EN)) { + EFX_LOG(efx, "SRAM reset complete\n"); + + return 0; + } + } while (++count < 20); /* wait upto 0.4 sec */ + + EFX_ERR(efx, "timed out waiting for SRAM reset\n"); + return -ETIMEDOUT; +} + +static void falcon_spi_device_init(struct efx_spi_device **spi_device_ret, + unsigned int device_id, u32 device_type) +{ + struct efx_spi_device *spi_device; + + if (device_type != 0) { + spi_device = kmalloc(sizeof(*spi_device), GFP_KERNEL); + spi_device->device_id = device_id; + spi_device->size = + 1 << SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_SIZE); + spi_device->addr_len = + SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_ADDR_LEN); + spi_device->munge_address = (spi_device->size == 1 << 9 && + spi_device->addr_len == 1); + spi_device->erase_command = + SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_ERASE_CMD); + spi_device->erase_size = + 1 << SPI_DEV_TYPE_FIELD(device_type, + SPI_DEV_TYPE_ERASE_SIZE); + spi_device->block_size = + 1 << SPI_DEV_TYPE_FIELD(device_type, + SPI_DEV_TYPE_BLOCK_SIZE); + spi_device->read = falcon_spi_read; + spi_device->write = falcon_spi_write; + } else { + spi_device = NULL; + } + + kfree(*spi_device_ret); + *spi_device_ret = spi_device; +} + +/* Extract non-volatile configuration */ +static int falcon_probe_nvconfig(struct efx_nic *efx) +{ + int rc; + struct falcon_nvconfig *nvconfig; + struct efx_spi_device *spi; + size_t offset, len; + int magic_num, struct_ver, board_rev, onchip_sram; + + nvconfig = kmalloc(sizeof(*nvconfig), GFP_KERNEL); + + /* Read the whole configuration structure into memory. It's + * in Falcon's boot device, which may be either flash or + * EEPROM, but if both are present Falcon prefers flash. The + * boot device is always too large for 9-bit addressing, so we + * don't have to munge commands. + */ + spi = efx->spi_flash ? efx->spi_flash : efx->spi_eeprom; + for (offset = 0; offset < sizeof(*nvconfig); offset += len) { + len = min(sizeof(*nvconfig) - offset, + (size_t) FALCON_SPI_MAX_LEN); + rc = falcon_spi_read(spi, efx, SPI_READ, + NVCONFIG_BASE + offset, + (char *)nvconfig + offset, len); + if (rc) + goto out; + } + + /* Read the MAC addresses */ + memcpy(efx->mac_address, nvconfig->mac_address[0], ETH_ALEN); + + /* Read the board configuration. */ + magic_num = le16_to_cpu(nvconfig->board_magic_num); + struct_ver = le16_to_cpu(nvconfig->board_struct_ver); + + if (magic_num != NVCONFIG_BOARD_MAGIC_NUM || struct_ver < 2) { + EFX_ERR(efx, "Non volatile memory bad magic=%x ver=%x " + "therefore using defaults\n", magic_num, struct_ver); + efx->phy_type = PHY_TYPE_NONE; + efx->mii.phy_id = PHY_ADDR_INVALID; + board_rev = 0; + onchip_sram = 1; + + } else { + struct falcon_nvconfig_board_v2 *v2 = &nvconfig->board_v2; + struct falcon_nvconfig_board_v3 *v3 = &nvconfig->board_v3; + + efx->phy_type = le16_to_cpu(v2->port0_phy_type); + efx->mii.phy_id = le16_to_cpu(v2->port0_phy_addr); + board_rev = le16_to_cpu(v2->board_revision); + onchip_sram = EFX_OWORD_FIELD(nvconfig->nic_stat_reg, + ONCHIP_SRAM); + + if (struct_ver >= 3) { + u32 fl = v3->spi_device_type[EE_SPI_FLASH]; + u32 ee = v3->spi_device_type[EE_SPI_EEPROM]; + falcon_spi_device_init(&efx->spi_flash, EE_SPI_FLASH, + le32_to_cpu(fl)); + falcon_spi_device_init(&efx->spi_eeprom, EE_SPI_EEPROM, + le32_to_cpu(ee)); + } + } + + EFX_LOG(efx, "PHY is %s(%d) phy_id %d\n", + PHY_TYPE(efx), efx->phy_type, + efx->mii.phy_id); + + efx_set_board_info(efx, board_rev); + + /* Read the SRAM configuration. The register is initialised + * automatically but might may been reset since boot. + */ + if (onchip_sram) { + efx->external_sram_cfg = SRM_NB_BSZ_ONCHIP_ONLY; + } else { + efx->external_sram_cfg = + EFX_OWORD_FIELD(nvconfig->srm_cfg_reg, + SRM_NUM_BANKS_AND_BANK_SIZE); + WARN_ON(efx->external_sram_cfg == SRM_NB_BSZ_RESERVED); + /* Replace invalid setting with the smallest defaults */ + if (efx->external_sram_cfg == SRM_NB_BSZ_DEFAULT) + efx->external_sram_cfg = SRM_NB_BSZ_1BANKS_2M; + } + EFX_LOG(efx, "external_sram_cfg=%d (>=0 is external)\n", + efx->external_sram_cfg); + + out: + kfree(nvconfig); + return rc; +} + +/* Looks at available SRAM resources and silicon revision, and works out + * how many queues we can support, and where things like descriptor caches + * should live. */ +static int falcon_dimension_resources(struct efx_nic *efx) +{ + unsigned buffer_entry_bytes, internal_dcs_entries, dcs; + struct falcon_nic_data *nic_data = efx->nic_data; + struct efx_dl_falcon_resources *res = &nic_data->resources; + + /* Fill out the driverlink resource list */ + res->hdr.type = EFX_DL_FALCON_RESOURCES; + res->biu_lock = &efx->biu_lock; + efx->dl_info = &res->hdr; + + /* This is set to 16 for a good reason. In summary, if larger than + * 16, the descriptor cache holds more than a default socket + * buffer's worth of packets (for UDP we can only have at most one + * socket buffer's worth outstanding). This combined with the fact + * that we only get 1 TX event per descriptor cache means the NIC + * goes idle. + * 16 gives us up to 256 TXQs on Falcon B in internal-SRAM mode, + * and up to 512 on Falcon A. + */ + nic_data->tx_dc_entries = 16; + + /* Set the RX descriptor cache size. Values 16, 32 and 64 are + * supported (8 won't work). Bigger is better, especially on B + * silicon. + */ + nic_data->rx_dc_entries = descriptor_cache_size; + dcs = ffs(nic_data->rx_dc_entries); + if ((dcs < 5) || (dcs > 7) || + ((1 << (dcs - 1)) != nic_data->rx_dc_entries)) { + EFX_ERR(efx, "bad descriptor_cache_size=%d (dcs=%d)\n", + nic_data->rx_dc_entries, dcs); + return -EINVAL; + } + + /* NB. The minimum values get increased as this driver initialises + * its resources, so this should prevent any overlap. + */ + switch (FALCON_REV(efx)) { + case FALCON_REV_A1: + res->rxq_min = res->txq_min = 16; + res->evq_int_min = res->evq_int_max = 4; + res->evq_timer_min = 5; + res->evq_timer_max = 4096; + internal_dcs_entries = 8192; + break; + case FALCON_REV_B0: + default: + res->rxq_min = res->txq_min = res->evq_int_min = 0; + res->evq_int_max = 64; + res->evq_timer_min = 64; + res->evq_timer_max = 4096; + internal_dcs_entries = 4096; + break; + } + + buffer_entry_bytes = 8; + + if (efx->external_sram_cfg == SRM_NB_BSZ_ONCHIP_ONLY) { + res->rxq_max = internal_dcs_entries / nic_data->rx_dc_entries; + res->txq_max = internal_dcs_entries / nic_data->tx_dc_entries; + /* Prog model says 8K entries for buffer table in internal + * mode. But does this not depend on full/half mode? + */ + res->buffer_table_max = 8192; + nic_data->tx_dc_base = 0x130000; + nic_data->rx_dc_base = 0x100000; + } else { + unsigned sram_bytes, vnic_bytes, max_vnics, n_vnics; + + /* Determine how much SRAM we have to play with. We have + * to fit buffer table and descriptor caches in. + */ + switch (efx->external_sram_cfg) { + case SRM_NB_BSZ_1BANKS_2M: + default: + sram_bytes = 2 * 1024 * 1024; + break; + case SRM_NB_BSZ_1BANKS_4M: + case SRM_NB_BSZ_2BANKS_4M: + sram_bytes = 4 * 1024 * 1024; + break; + case SRM_NB_BSZ_1BANKS_8M: + case SRM_NB_BSZ_2BANKS_8M: + sram_bytes = 8 * 1024 * 1024; + break; + case SRM_NB_BSZ_2BANKS_16M: + sram_bytes = 16 * 1024 * 1024; + break; + } + /* For each VNIC allow at least 512 buffer table entries + * and descriptor cache for an rxq and txq. Buffer table + * space for evqs and dmaqs is relatively trivial, so not + * considered in this calculation. + */ + vnic_bytes = (512 * buffer_entry_bytes + + nic_data->rx_dc_entries * 8 + + nic_data->tx_dc_entries * 8); + max_vnics = sram_bytes / vnic_bytes; + for (n_vnics = 1; n_vnics < res->evq_timer_min + max_vnics;) + n_vnics *= 2; + res->rxq_max = n_vnics; + res->txq_max = n_vnics; + + dcs = n_vnics * nic_data->tx_dc_entries * 8; + nic_data->tx_dc_base = sram_bytes - dcs; + dcs = n_vnics * nic_data->rx_dc_entries * 8; + nic_data->rx_dc_base = nic_data->tx_dc_base - dcs; + res->buffer_table_max = nic_data->rx_dc_base / 8; + } + + if (efx->type->is_dual_func) + res->flags |= EFX_DL_FALCON_DUAL_FUNC; + + if (EFX_INT_MODE_USE_MSI(efx)) + res->flags |= EFX_DL_FALCON_USE_MSI; + + return 0; +} + +/* Probe the NIC variant (revision, ASIC vs FPGA, function count, port + * count, port speed). Set workaround and feature flags accordingly. + */ +static int falcon_probe_nic_variant(struct efx_nic *efx) +{ + efx_oword_t altera_build; + + falcon_read(efx, &altera_build, ALTERA_BUILD_REG_KER); + efx->is_asic = EFX_OWORD_FIELD(altera_build, VER_ALL) == 0; + + switch (FALCON_REV(efx)) { + case FALCON_REV_A0: + case 0xff: + EFX_ERR(efx, "Falcon rev A0 not supported\n"); + return -ENODEV; + + case FALCON_REV_A1:{ + efx_oword_t nic_stat; + + falcon_read(efx, &nic_stat, NIC_STAT_REG); + + if (!efx->is_asic) { + EFX_ERR(efx, "Falcon rev A1 FPGA not supported\n"); + return -ENODEV; + } + if (EFX_OWORD_FIELD(nic_stat, STRAP_PCIE) == 0) { + EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n"); + return -ENODEV; + } + efx->is_10g = EFX_OWORD_FIELD(nic_stat, STRAP_10G); + efx->silicon_rev = "falcon/a1"; + break; + } + + case FALCON_REV_B0:{ + efx->is_10g = 1; + efx->silicon_rev = "falcon/b0"; + break; + } + + default: + EFX_ERR(efx, "Unknown Falcon rev %d\n", FALCON_REV(efx)); + return -ENODEV; + } + + return 0; +} + +/* Probe all SPI devices on the NIC */ +static void falcon_probe_spi_devices(struct efx_nic *efx) +{ + efx_oword_t nic_stat, gpio_ctl, ee_vpd_cfg; + unsigned int has_flash, has_eeprom, boot_is_external; + + falcon_read(efx, &gpio_ctl, GPIO_CTL_REG_KER); + falcon_read(efx, &nic_stat, NIC_STAT_REG); + falcon_read(efx, &ee_vpd_cfg, EE_VPD_CFG_REG_KER); + + has_flash = EFX_OWORD_FIELD(nic_stat, SF_PRST); + has_eeprom = EFX_OWORD_FIELD(nic_stat, EE_PRST); + boot_is_external = EFX_OWORD_FIELD(gpio_ctl, BOOTED_USING_NVDEVICE); + + if (has_flash) { + u32 flash_device_type; + + if (flash_type == -1) { + /* Default flash SPI device: Atmel AT25F1024 + * 128 KB, 24-bit address, 32 KB erase block, + * 256 B write block + */ + flash_device_type = + (17 << SPI_DEV_TYPE_SIZE_LBN) + | (3 << SPI_DEV_TYPE_ADDR_LEN_LBN) + | (0x52 << SPI_DEV_TYPE_ERASE_CMD_LBN) + | (15 << SPI_DEV_TYPE_ERASE_SIZE_LBN) + | (8 << SPI_DEV_TYPE_BLOCK_SIZE_LBN); + } else { + flash_device_type = flash_type; + } + + falcon_spi_device_init(&efx->spi_flash, EE_SPI_FLASH, + flash_device_type); + + if (!boot_is_external) { + /* Disable VPD and set clock dividers to safe + * values for initial programming. + */ + EFX_LOG(efx, "Booted from internal ASIC settings;" + " setting SPI config\n"); + EFX_POPULATE_OWORD_3(ee_vpd_cfg, EE_VPD_EN, 0, + /* 125 MHz / 7 ~= 20 MHz */ + EE_SF_CLOCK_DIV, 7, + /* 125 MHz / 63 ~= 2 MHz */ + EE_EE_CLOCK_DIV, 63); + falcon_write(efx, &ee_vpd_cfg, EE_VPD_CFG_REG_KER); + } + } + + if (has_eeprom) { + u32 eeprom_device_type; + + /* eeprom_type may be -1 (default) for automatic detection, + * 0 or 1 to select the default or large EEPROM, or + * some larger number to specify the precise configuration + */ + if (eeprom_type == -1 || eeprom_type <= 1) { + /* If it has no flash, it must have a large EEPROM + * for chip config; otherwise check whether 9-bit + * addressing is used for VPD configuration + */ + if (eeprom_type == 0 || + (eeprom_type == -1 && has_flash && + (!boot_is_external || + EFX_OWORD_FIELD(ee_vpd_cfg, + EE_VPD_EN_AD9_MODE)))) { + /* Default SPI device: Atmel AT25040 or similar + * 512 B, 9-bit address, 8 B write block + */ + eeprom_device_type = + (9 << SPI_DEV_TYPE_SIZE_LBN) + | (1 << SPI_DEV_TYPE_ADDR_LEN_LBN) + | (3 << SPI_DEV_TYPE_BLOCK_SIZE_LBN); + } else { + /* "Large" SPI device: Atmel AT25640 or similar + * 8 KB, 16-bit address, 32 B write block + */ + eeprom_device_type = + (13 << SPI_DEV_TYPE_SIZE_LBN) + | (2 << SPI_DEV_TYPE_ADDR_LEN_LBN) + | (5 << SPI_DEV_TYPE_BLOCK_SIZE_LBN); + } + } else { + eeprom_device_type = eeprom_type; + } + + falcon_spi_device_init(&efx->spi_eeprom, EE_SPI_EEPROM, + eeprom_device_type); + } + + EFX_LOG(efx, "flash is %s, EEPROM is %s\n", + (has_flash ? "present" : "absent"), + (has_eeprom ? "present" : "absent")); +} + +static void falcon_remove_spi_devices(struct efx_nic *efx) +{ + kfree(efx->spi_eeprom); + efx->spi_eeprom = NULL; + kfree(efx->spi_flash); + efx->spi_flash = NULL; +} + +#ifdef CONFIG_SFC_DEBUGFS + +/* Generate a hardware revision string */ +int falcon_debugfs_read_hardware_desc(struct seq_file *file, void *data) +{ + struct efx_nic *efx = data; + efx_oword_t altera_build; + int major, minor, build; + int rc, len; + + if (efx->is_asic) { + rc = seq_puts(file, "Falcon ASIC"); + } else { + falcon_read(efx, &altera_build, ALTERA_BUILD_REG_KER); + + major = EFX_OWORD_FIELD(altera_build, VER_MAJOR); + minor = EFX_OWORD_FIELD(altera_build, VER_MINOR); + build = EFX_OWORD_FIELD(altera_build, VER_BUILD); + rc = seq_printf(file, "Falcon FPGA v%x.%x.%x", + major, minor, build); + } + len = rc; + + switch (FALCON_REV(efx)) { + case FALCON_REV_A1: + rc = seq_puts(file, " rev A1 "); + break; + case FALCON_REV_B0: + rc = seq_puts(file, " rev B0 "); + break; + default: + rc = seq_puts(file, " rev ?? "); + break; + } + len += rc; + + rc = seq_printf(file, "%s %s\n", + efx->is_10g ? "10G" : "1G", PHY_TYPE(efx)); + len += rc; + + return rc < 0 ? rc : len; +} + +#endif /* CONFIG_SFC_DEBUGFS */ + +int falcon_probe_nic(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data; + int rc; + + /* Initialise I2C interface state */ + efx->i2c.efx = efx; + efx->i2c.op = &falcon_i2c_bit_operations; + efx->i2c.sda = 1; + efx->i2c.scl = 1; + + /* Determine number of ports etc. */ + rc = falcon_probe_nic_variant(efx); + if (rc) + goto fail1; + + /* Probe secondary function if expected */ + if (efx->type->is_dual_func) { + struct pci_dev *dev = pci_dev_get(efx->pci_dev); + + while ((dev = pci_get_device(EFX_VENDID_SFC, FALCON_A_S_DEVID, + dev))) { + if (dev->bus == efx->pci_dev->bus && + dev->devfn == efx->pci_dev->devfn + 1) { + efx->pci_dev2 = dev; + break; + } + } + if (!efx->pci_dev2) { + EFX_ERR(efx, "failed to find secondary function\n"); + rc = -ENODEV; + goto fail2; + } + } + + /* Now we can reset the NIC */ + rc = falcon_reset_hw(efx, RESET_TYPE_ALL); + if (rc) { + EFX_ERR(efx, "failed to reset NIC\n"); + goto fail3; + } + + /* Allocate memory for INT_KER */ + rc = falcon_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t)); + if (rc) + goto fail4; + BUG_ON(efx->irq_status.dma_addr & 0x0f); + + EFX_LOG(efx, "INT_KER at %llx (virt %p phys %lx)\n", + (unsigned long long)efx->irq_status.dma_addr, + efx->irq_status.addr, virt_to_phys(efx->irq_status.addr)); + + /* Determine attached SPI devices */ + falcon_probe_spi_devices(efx); + + /* Read in the non-volatile configuration */ + rc = falcon_probe_nvconfig(efx); + if (rc) + goto fail5; + + if (!efx->is_10g && efx->phy_type != PHY_TYPE_1G_ALASKA) { + /* Actually using 1G port, not 10G port */ + efx->phy_type = PHY_TYPE_1G_ALASKA; + efx->mii.phy_id = 2; + } + + /* Decide how many resources we can allocate, to ourselves + * and to driverlink clients */ + nic_data = kzalloc(sizeof(*nic_data), GFP_KERNEL); + efx->nic_data = (void *) nic_data; + + rc = falcon_dimension_resources(efx); + if (rc) + goto fail6; + + return 0; + + fail6: + kfree(nic_data); + efx->nic_data = efx->dl_info = NULL; + fail5: + falcon_remove_spi_devices(efx); + falcon_free_buffer(efx, &efx->irq_status); + fail4: + /* fall-thru */ + fail3: + if (efx->pci_dev2) { + pci_dev_put(efx->pci_dev2); + efx->pci_dev2 = NULL; + } + fail2: + /* fall-thru */ + fail1: + return rc; +} + +static int falcon_check_power_limit(struct efx_nic *efx) +{ + int pciecap_offset = pci_find_capability(efx->pci_dev, PCI_CAP_ID_EXP); + u32 pcie_devcap; + unsigned val, scale; + int rc; + + if (!pciecap_offset) + return -EIO; + rc = pci_read_config_dword(efx->pci_dev, + (pciecap_offset + PCI_EXP_DEVCAP), + &pcie_devcap); + if (rc) + return rc; + + val = ((pcie_devcap & PCI_EXP_DEVCAP_PWR_VAL) >> + PCI_EXP_DEVCAP_PWR_VAL_LBN); + scale = ((pcie_devcap & PCI_EXP_DEVCAP_PWR_SCL) >> + PCI_EXP_DEVCAP_PWR_SCL_LBN); + + /* Re-scale to milliwatts if necessary */ + while (scale != 3) { + val *= 10; + scale++; + } + + if (val != 0 && efx->board_info.mwatts > val) { + EFX_ERR(efx, "board needs %d mW but only %d mW available\n", + efx->board_info.mwatts, val); + return -EIO; + } + + return 0; +} + +static void falcon_init_ack_repl_timer(struct efx_nic *efx, int num_lanes) +{ + unsigned tlp_size; + efx_dword_t pcie_ack_rpl_reg; + efx_dword_t pcie_ack_freq_reg; + u16 pcie_devicectrl; + u32 temp; + int lut_index, tlp_size_decoded; + int current_replay, expected_replay; + int current_ack_timer, current_ack_freq; + + static struct efx_tlp_ack_factor { + int tlp; + int replay[4]; /* 0=1x, 1=2x, 3=4x, 4=8x (see pcie docs) */ + } tlp_ack_factor_lut[4] = { + { 128, { 421, 257, 174, 166 } }, + { 256, { 689, 391, 241, 225 } }, + { 512, { 903, 498, 295, 193 } }, + { 1024, { 1670, 881, 487, 290 } } + }; + struct efx_tlp_ack_factor *tlp_ack_factor; + + /* Get TLP size */ + falcon_pcie_core_read_reg(efx, PCIE_CORE_ADDR_PCIE_DEVICE_CTRL_STAT, + &temp); + pcie_devicectrl = (u16) (temp && 0xffff); + tlp_size = ((PCI_EXP_DEVCTL_PAYLOAD & pcie_devicectrl) >> + ffs(PCI_EXP_DEVCTL_PAYLOAD)); + EFX_WARN_ON_PARANOID(tlp_size > 3); /* => 1024 bytes */ + tlp_ack_factor = &tlp_ack_factor_lut[tlp_size & 0x3]; + tlp_size_decoded = tlp_ack_factor->tlp; + + /* Get actual ack & actual and expected replay settings */ + falcon_pcie_core_read_reg(efx, PCIE_CORE_ADDR_ACK_RPL_TIMER, + pcie_ack_rpl_reg.u32); + current_replay = EFX_DWORD_FIELD(pcie_ack_rpl_reg, PCIE_CORE_RPL_TL); + current_ack_timer = EFX_DWORD_FIELD(pcie_ack_rpl_reg, + PCIE_CORE_ACK_TL); + + lut_index = ffs(num_lanes) - 1; + expected_replay = tlp_ack_factor->replay[lut_index & 0x3]; + + falcon_pcie_core_read_reg(efx, PCIE_CORE_ADDR_ACK_FREQ, + pcie_ack_freq_reg.u32); + current_ack_freq = EFX_DWORD_FIELD(pcie_ack_freq_reg, + PCIE_CORE_ACK_FREQ); + + EFX_LOG(efx, "pcie x%d tlp=%d replay_reg=" EFX_DWORD_FMT " { ack=%d " + "current_replay=%d expected_replay=%d } ack_reg=" + EFX_DWORD_FMT " { current_freq=%d expected_freq=%d }\n", + num_lanes, tlp_size_decoded, + EFX_DWORD_VAL(pcie_ack_rpl_reg), current_ack_timer, + current_replay, expected_replay, + EFX_DWORD_VAL(pcie_ack_rpl_reg), current_ack_freq, 0); + + /* If expected replay setting needs to be bigger then set it */ + if (expected_replay > current_replay) { + EFX_SET_DWORD_FIELD(pcie_ack_rpl_reg, PCIE_CORE_RPL_TL, + expected_replay); + + falcon_pcie_core_write_reg(efx, PCIE_CORE_ADDR_ACK_RPL_TIMER, + pcie_ack_rpl_reg.u32[0]); + } +} + +static int falcon_init_pcie_core(struct efx_nic *efx) +{ + int pciecap_offset; + unsigned num_lanes = 0; + + /* Get num lanes */ + pciecap_offset = pci_find_capability(efx->pci_dev, PCI_CAP_ID_EXP); + if (pciecap_offset) { + u16 pcie_linkstat; + int rc, link_sta; + + link_sta = pciecap_offset + PCI_EXP_LNKSTA; + rc = pci_read_config_word(efx->pci_dev, link_sta, + &pcie_linkstat); + if (rc) + return rc; + + num_lanes = ((pcie_linkstat & PCI_EXP_LNKSTA_LNK_WID) + >> PCI_EXP_LNKSTA_LNK_WID_LBN); + EFX_BUG_ON_PARANOID(num_lanes <= 0 || num_lanes > 8); + + if (num_lanes < 8) + EFX_ERR(efx, "WARNING: the Solarflare Network Adapter " + "has been plugged into a PCI-Express slot with " + "less than 8 lanes (%d detected). This will " + "limit the maximum achievable bandwidth! " + "Consult your motherboard documentation to " + "find a slot that is 8 lanes electrically and " + "physically\n", num_lanes); + } + + if (FALCON_REV(efx) <= FALCON_REV_A1) + return 0; + + if (EFX_WORKAROUND_6943(efx) && num_lanes > 0) + falcon_init_ack_repl_timer(efx, num_lanes); + + if (EFX_WORKAROUND_9096(efx)) { + efx_dword_t pcie_ack_freq_reg; + + /* ensure ack freq timer is 0 = always ack after timeout */ + falcon_pcie_core_read_reg(efx, PCIE_CORE_ADDR_ACK_FREQ, + pcie_ack_freq_reg.u32); + EFX_SET_DWORD_FIELD(pcie_ack_freq_reg, PCIE_CORE_ACK_FREQ, 0); + falcon_pcie_core_write_reg(efx, PCIE_CORE_ADDR_ACK_FREQ, + pcie_ack_freq_reg.u32[0]); + } + + return 0; +} + +static void falcon_fini_pcie_core(struct efx_nic *efx) +{ + efx_dword_t pcie_ack_freq_reg; + + if (FALCON_REV(efx) <= FALCON_REV_A1) + return; + + if (EFX_WORKAROUND_9096(efx)) { + /* Set the ACK frequency timer to 1, so TLP's are acked in + * a timely fashion. + */ + falcon_pcie_core_read_reg(efx, PCIE_CORE_ADDR_ACK_FREQ, + pcie_ack_freq_reg.u32); + EFX_SET_DWORD_FIELD(pcie_ack_freq_reg, PCIE_CORE_ACK_FREQ, 1); + falcon_pcie_core_write_reg(efx, PCIE_CORE_ADDR_ACK_FREQ, + pcie_ack_freq_reg.u32[0]); + } +} + +/* This call performs hardware-specific global initialisation, such as + * defining the descriptor cache sizes and number of RSS channels. + * It does not set up any buffers, descriptor rings or event queues. + */ +int falcon_init_nic(struct efx_nic *efx) +{ + struct falcon_nic_data *data; + efx_oword_t temp; + unsigned thresh; + int rc; + + data = (struct falcon_nic_data *)efx->nic_data; + + /* Set up the address region register. This is only needed + * for the B0 FPGA, but since we are just pushing in the + * reset defaults this may as well be unconditional. */ + EFX_POPULATE_OWORD_4(temp, ADR_REGION0, 0, + ADR_REGION1, (1 << 16), + ADR_REGION2, (2 << 16), + ADR_REGION3, (3 << 16)); + falcon_write(efx, &temp, ADR_REGION_REG_KER); + + /* Use on-chip SRAM if needed. + */ + falcon_read(efx, &temp, NIC_STAT_REG); + if (efx->external_sram_cfg == SRM_NB_BSZ_ONCHIP_ONLY) + EFX_SET_OWORD_FIELD(temp, ONCHIP_SRAM, 1); + else + EFX_SET_OWORD_FIELD(temp, ONCHIP_SRAM, 0); + falcon_write(efx, &temp, NIC_STAT_REG); + + /* Check power requirements against PCIe power budgeting */ + rc = falcon_check_power_limit(efx); + if (rc) + return rc; + + /* Warn if <8 lanes of PCIe detected & set pcie timers */ + rc = falcon_init_pcie_core(efx); + if (rc) + return rc; + + /* Set buffer table mode */ + EFX_POPULATE_OWORD_1(temp, BUF_TBL_MODE, BUF_TBL_MODE_FULL); + falcon_write(efx, &temp, BUF_TBL_CFG_REG_KER); + + rc = falcon_reset_sram(efx); + if (rc) + return rc; + + /* Set positions of descriptor caches in SRAM. */ + EFX_POPULATE_OWORD_1(temp, SRM_TX_DC_BASE_ADR, data->tx_dc_base / 8); + falcon_write(efx, &temp, SRM_TX_DC_CFG_REG_KER); + EFX_POPULATE_OWORD_1(temp, SRM_RX_DC_BASE_ADR, data->rx_dc_base / 8); + falcon_write(efx, &temp, SRM_RX_DC_CFG_REG_KER); + + /* Set TX descriptor cache size. */ + EFX_POPULATE_OWORD_1(temp, TX_DC_SIZE, ffs(data->tx_dc_entries) - 4); + falcon_write(efx, &temp, TX_DC_CFG_REG_KER); + + /* Set RX descriptor cache size. Set low watermark to size-8, as + * this allows most efficient prefetching. + */ + EFX_POPULATE_OWORD_1(temp, RX_DC_SIZE, ffs(data->rx_dc_entries) - 4); + falcon_write(efx, &temp, RX_DC_CFG_REG_KER); + EFX_POPULATE_OWORD_1(temp, RX_DC_PF_LWM, data->rx_dc_entries - 8); + falcon_write(efx, &temp, RX_DC_PF_WM_REG_KER); + + /* Clear the parity enables on the TX data fifos as + * they produce false parity errors because of timing issues + */ + if (EFX_WORKAROUND_5129(efx)) { + falcon_read(efx, &temp, SPARE_REG_KER); + EFX_SET_OWORD_FIELD(temp, MEM_PERR_EN_TX_DATA, 0); + falcon_write(efx, &temp, SPARE_REG_KER); + } + + /* Enable all the genuinely fatal interrupts. (They are still + * masked by the overall interrupt mask, controlled by + * falcon_interrupts()). + * + * Note: All other fatal interrupts are enabled + */ + EFX_POPULATE_OWORD_3(temp, + ILL_ADR_INT_KER_EN, 1, + RBUF_OWN_INT_KER_EN, 1, + TBUF_OWN_INT_KER_EN, 1); + EFX_INVERT_OWORD(temp); + falcon_write(efx, &temp, FATAL_INTR_REG_KER); + + /* Set number of RSS queues for receive path. */ + falcon_read(efx, &temp, RX_FILTER_CTL_REG); + if (FALCON_REV(efx) >= FALCON_REV_B0) + EFX_SET_OWORD_FIELD(temp, NUM_KER, 0); + else + EFX_SET_OWORD_FIELD(temp, NUM_KER, efx->rss_queues - 1); + if (EFX_WORKAROUND_7244(efx)) { + EFX_SET_OWORD_FIELD(temp, UDP_FULL_SRCH_LIMIT, 8); + EFX_SET_OWORD_FIELD(temp, UDP_WILD_SRCH_LIMIT, 8); + EFX_SET_OWORD_FIELD(temp, TCP_FULL_SRCH_LIMIT, 8); + EFX_SET_OWORD_FIELD(temp, TCP_WILD_SRCH_LIMIT, 8); + } + falcon_write(efx, &temp, RX_FILTER_CTL_REG); + + falcon_setup_rss_indir_table(efx); + + /* Setup RX. Wait for descriptor is broken and must + * be disabled. RXDP recovery shouldn't be needed, but is. + */ + falcon_read(efx, &temp, RX_SELF_RST_REG_KER); + EFX_SET_OWORD_FIELD(temp, RX_NODESC_WAIT_DIS, 1); + EFX_SET_OWORD_FIELD(temp, RX_RECOVERY_EN, 1); + if (EFX_WORKAROUND_5583(efx)) + EFX_SET_OWORD_FIELD(temp, RX_ISCSI_DIS, 1); + falcon_write(efx, &temp, RX_SELF_RST_REG_KER); + + /* Disable the ugly timer-based TX DMA backoff and allow TX DMA to be + * controlled by the RX FIFO fill level. Set arbitration to one pkt/Q. + */ + falcon_read(efx, &temp, TX_CFG2_REG_KER); + EFX_SET_OWORD_FIELD(temp, TX_RX_SPACER, 0xfe); + EFX_SET_OWORD_FIELD(temp, TX_RX_SPACER_EN, 1); + EFX_SET_OWORD_FIELD(temp, TX_ONE_PKT_PER_Q, 1); + EFX_SET_OWORD_FIELD(temp, TX_CSR_PUSH_EN, 0); + EFX_SET_OWORD_FIELD(temp, TX_DIS_NON_IP_EV, 1); + /* Enable SW_EV to inherit in char driver - assume harmless here */ + EFX_SET_OWORD_FIELD(temp, TX_SW_EV_EN, 1); + /* Prefetch threshold 2 => fetch when descriptor cache half empty */ + EFX_SET_OWORD_FIELD(temp, TX_PREF_THRESHOLD, 2); + if (EFX_WORKAROUND_9008(efx)) + EFX_SET_OWORD_FIELD(temp, TX_PREF_WD_TMR, (unsigned)0x3fffff); + /* Squash TX of packets of 16 bytes or less */ + if (FALCON_REV(efx) >= FALCON_REV_B0 && EFX_WORKAROUND_9141(efx)) + EFX_SET_OWORD_FIELD(temp, TX_FLUSH_MIN_LEN_EN_B0, 1); + falcon_write(efx, &temp, TX_CFG2_REG_KER); + + /* Do not enable TX_NO_EOP_DISC_EN, since it limits packets to 16 + * descriptors (which is bad). + */ + falcon_read(efx, &temp, TX_CFG_REG_KER); + EFX_SET_OWORD_FIELD(temp, TX_NO_EOP_DISC_EN, 0); + falcon_write(efx, &temp, TX_CFG_REG_KER); + + /* RX config */ + falcon_read(efx, &temp, RX_CFG_REG_KER); + EFX_SET_OWORD_FIELD_VER(efx, temp, RX_DESC_PUSH_EN, 0); + if (EFX_WORKAROUND_7575(efx)) + EFX_SET_OWORD_FIELD_VER(efx, temp, RX_USR_BUF_SIZE, + (3 * 4096) / 32); + if (FALCON_REV(efx) >= FALCON_REV_B0) + EFX_SET_OWORD_FIELD(temp, RX_INGR_EN_B0, 1); + + /* RX FIFO flow control thresholds */ + thresh = ((rx_xon_thresh_bytes >= 0) ? + rx_xon_thresh_bytes : efx->type->rx_xon_thresh); + EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XON_MAC_TH, thresh / 256); + thresh = ((rx_xoff_thresh_bytes >= 0) ? + rx_xoff_thresh_bytes : efx->type->rx_xoff_thresh); + EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XOFF_MAC_TH, thresh / 256); + /* RX control FIFO thresholds [32 entries] */ + EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XON_TX_TH, 25); + EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XOFF_TX_TH, 20); + falcon_write(efx, &temp, RX_CFG_REG_KER); + + /* Set destination of both TX and RX Flush events */ + if (FALCON_REV(efx) >= FALCON_REV_B0) { + EFX_POPULATE_OWORD_1(temp, FLS_EVQ_ID, 0); + falcon_write(efx, &temp, DP_CTRL_REG); + } + + return 0; +} + +void falcon_fini_nic(struct efx_nic *efx) +{ + falcon_fini_pcie_core(efx); +} + +void falcon_remove_nic(struct efx_nic *efx) +{ + /* Tear down the private nic state, and the driverlink nic params */ + kfree(efx->nic_data); + efx->nic_data = efx->dl_info = NULL; + + falcon_remove_spi_devices(efx); + falcon_free_buffer(efx, &efx->irq_status); + + /* Reset the NIC finally */ + (void) falcon_reset_hw(efx, RESET_TYPE_ALL); + + /* Release the second function after the reset */ + if (efx->pci_dev2) { + pci_dev_put(efx->pci_dev2); + efx->pci_dev2 = NULL; + } +} + +void falcon_update_nic_stats(struct efx_nic *efx) +{ + efx_oword_t cnt; + + /* Read the RX drop counter */ + falcon_read(efx, &cnt, RX_NODESC_DROP_REG_KER); + efx->n_rx_nodesc_drop_cnt += EFX_OWORD_FIELD(cnt, RX_NODESC_DROP_CNT); +} + +/************************************************************************** + * + * Revision-dependent attributes used by efx.c + * + ************************************************************************** + */ + +struct efx_nic_type falcon_a_nic_type = { + .is_dual_func = 1, + .mem_bar = 2, + .mem_map_size = 0x20000, + .txd_ptr_tbl_base = TX_DESC_PTR_TBL_KER_A1, + .rxd_ptr_tbl_base = RX_DESC_PTR_TBL_KER_A1, + .buf_tbl_base = BUF_TBL_KER_A1, + .evq_ptr_tbl_base = EVQ_PTR_TBL_KER_A1, + .evq_rptr_tbl_base = EVQ_RPTR_REG_KER_A1, + .txd_ring_mask = FALCON_TXD_RING_MASK, + .rxd_ring_mask = FALCON_RXD_RING_MASK, + .evq_size = FALCON_EVQ_SIZE, + .max_dma_mask = FALCON_DMA_MASK, + .tx_dma_mask = FALCON_TX_DMA_MASK, + .bug5391_mask = 0xf, + .rx_xoff_thresh = 2048, + .rx_xon_thresh = 512, + .rx_buffer_padding = 0x24, + .max_interrupt_mode = EFX_INT_MODE_MSI, + .phys_addr_channels = 4, +}; + +struct efx_nic_type falcon_b_nic_type = { + .is_dual_func = 0, + .mem_bar = 2, + /* Map everything up to and including the RSS indirection + * table. Don't map MSI-X table, MSI-X PBA since Linux + * requires that they not be mapped. */ + .mem_map_size = RX_RSS_INDIR_TBL_B0 + 0x800, + .txd_ptr_tbl_base = TX_DESC_PTR_TBL_KER_B0, + .rxd_ptr_tbl_base = RX_DESC_PTR_TBL_KER_B0, + .buf_tbl_base = BUF_TBL_KER_B0, + .evq_ptr_tbl_base = EVQ_PTR_TBL_KER_B0, + .evq_rptr_tbl_base = EVQ_RPTR_REG_KER_B0, + .txd_ring_mask = FALCON_TXD_RING_MASK, + .rxd_ring_mask = FALCON_RXD_RING_MASK, + .evq_size = FALCON_EVQ_SIZE, + .max_dma_mask = FALCON_DMA_MASK, + .tx_dma_mask = FALCON_TX_DMA_MASK, + .bug5391_mask = 0, + .rx_xoff_thresh = 54272, /* ~80Kb - 3*max MTU */ + .rx_xon_thresh = 27648, /* ~3*max MTU */ + .rx_buffer_padding = 0, + .max_interrupt_mode = EFX_INT_MODE_MSIX, + .phys_addr_channels = 32, /* Hardware limit is 64, but the legacy + * interrupt handler only supports 32 + * channels */ + +}; + Index: head-2008-03-17/drivers/net/sfc/falcon.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/falcon.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,156 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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_FALCON_H +#define EFX_FALCON_H + +#include +#include +#include "net_driver.h" + +/* + * Falcon hardware control + */ + +enum falcon_revision { + FALCON_REV_A0 = 0, + FALCON_REV_A1 = 1, + FALCON_REV_B0 = 2, +}; + +#define FALCON_REV(efx) ((efx)->pci_dev->revision) + +extern struct efx_nic_type falcon_a_nic_type; +extern struct efx_nic_type falcon_b_nic_type; + +/************************************************************************** + * + * Externs + * + ************************************************************************** + */ + +/* TX data path */ +extern int falcon_probe_tx(struct efx_tx_queue *tx_queue); +extern int falcon_init_tx(struct efx_tx_queue *tx_queue); +extern void falcon_fini_tx(struct efx_tx_queue *tx_queue); +extern void falcon_remove_tx(struct efx_tx_queue *tx_queue); +extern void falcon_push_buffers(struct efx_tx_queue *tx_queue); + +/* RX data path */ +extern int falcon_probe_rx(struct efx_rx_queue *rx_queue); +extern int falcon_init_rx(struct efx_rx_queue *rx_queue); +extern void falcon_fini_rx(struct efx_rx_queue *rx_queue); +extern void falcon_remove_rx(struct efx_rx_queue *rx_queue); +extern void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue); + +/* Event data path */ +extern int falcon_probe_eventq(struct efx_channel *channel); +extern int falcon_init_eventq(struct efx_channel *channel); +extern void falcon_fini_eventq(struct efx_channel *channel); +extern void falcon_remove_eventq(struct efx_channel *channel); +extern int falcon_process_eventq(struct efx_channel *channel, int *rx_quota); +extern void falcon_eventq_read_ack(struct efx_channel *channel); + +/* Ports */ +extern int falcon_probe_port(struct efx_nic *efx); +extern void falcon_remove_port(struct efx_nic *efx); + +/* MAC/PHY */ +extern void falcon_check_xaui_link_up(struct efx_nic *efx); +extern int falcon_xaui_link_ok(struct efx_nic *efx); +extern int falcon_dma_stats(struct efx_nic *efx, + unsigned int done_offset); +extern void falcon_drain_tx_fifo(struct efx_nic *efx); +extern void falcon_deconfigure_mac_wrapper(struct efx_nic *efx); +extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx); + +/* Interrupts and test events */ +extern int falcon_init_interrupt(struct efx_nic *efx); +extern void falcon_enable_interrupts(struct efx_nic *efx); +extern void falcon_generate_test_event(struct efx_channel *channel, + unsigned int magic); +extern void falcon_generate_interrupt(struct efx_nic *efx); +extern void falcon_set_int_moderation(struct efx_channel *channel); +extern void falcon_disable_interrupts(struct efx_nic *efx); +extern void falcon_fini_interrupt(struct efx_nic *efx); + +/* Global Resources */ +extern int falcon_probe_nic(struct efx_nic *efx); +extern int falcon_probe_resources(struct efx_nic *efx); +extern int falcon_init_nic(struct efx_nic *efx); +extern int falcon_reset_hw(struct efx_nic *efx, enum reset_type method); +extern void falcon_fini_nic(struct efx_nic *efx); +extern void falcon_remove_resources(struct efx_nic *efx); +extern void falcon_remove_nic(struct efx_nic *efx); +extern void falcon_update_nic_stats(struct efx_nic *efx); +extern void falcon_set_multicast_hash(struct efx_nic *efx); +extern int falcon_reset_xaui(struct efx_nic *efx); + +/************************************************************************** + * + * Falcon MAC stats + * + ************************************************************************** + */ + +#define FALCON_STAT_OFFSET(falcon_stat) EFX_VAL(falcon_stat, offset) +#define FALCON_STAT_WIDTH(falcon_stat) EFX_VAL(falcon_stat, WIDTH) + +/* Retrieve statistic from statistics block */ +#define FALCON_STAT(efx, falcon_stat, efx_stat) do { \ + if (FALCON_STAT_WIDTH(falcon_stat) == 16) \ + (efx)->mac_stats.efx_stat += le16_to_cpu( \ + *((u16 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + else if (FALCON_STAT_WIDTH(falcon_stat) == 32) \ + (efx)->mac_stats.efx_stat += le32_to_cpu( \ + *((u32 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + else \ + (efx)->mac_stats.efx_stat += le64_to_cpu( \ + *((u64 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + } while (0) + +#define FALCON_MAC_STATS_SIZE 0x100 + +#define MAC_DATA_LBN 0 +#define MAC_DATA_WIDTH 32 + +extern void falcon_generate_event(struct efx_channel *channel, + efx_qword_t *event); + +#ifdef CONFIG_SFC_DEBUGFS +struct seq_file; +extern int falcon_debugfs_read_hardware_desc(struct seq_file *file, void *data); +#endif + +#endif /* EFX_FALCON_H */ Index: head-2008-03-17/drivers/net/sfc/falcon_gmac.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/falcon_gmac.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,320 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#include "net_driver.h" +#include "efx.h" +#include "falcon.h" +#include "mac.h" +#include "falcon_hwdefs.h" +#include "falcon_io.h" +#include "gmii.h" + +/************************************************************************** + * + * MAC register access + * + **************************************************************************/ + +/* Offset of a GMAC register within Falcon */ +#define FALCON_GMAC_REG(mac_reg) \ + (FALCON_GMAC_REGBANK + ((mac_reg) * FALCON_GMAC_REG_SIZE)) + +static void falcon_gmac_writel(struct efx_nic *efx, + efx_dword_t *value, unsigned int mac_reg) +{ + efx_oword_t temp; + + EFX_POPULATE_OWORD_1(temp, MAC_DATA, EFX_DWORD_FIELD(*value, MAC_DATA)); + falcon_write(efx, &temp, FALCON_GMAC_REG(mac_reg)); +} + +static void falcon_gmac_readl(struct efx_nic *efx, + efx_dword_t *value, unsigned int mac_reg) +{ + efx_oword_t temp; + + falcon_read(efx, &temp, FALCON_GMAC_REG(mac_reg)); + EFX_POPULATE_DWORD_1(*value, MAC_DATA, EFX_OWORD_FIELD(temp, MAC_DATA)); +} + +/************************************************************************** + * + * MAC operations + * + *************************************************************************/ + +static int falcon_init_gmac(struct efx_nic *efx) +{ + int rc; + + /* Reset the MAC */ + mentormac_reset(efx); + + /* Initialise PHY */ + rc = efx->phy_op->init(efx); + if (rc) + return rc; + + return 0; +} + +static void falcon_reconfigure_gmac(struct efx_nic *efx) +{ + /* Reconfigure PHY and pick up PHY parameters. This updates + * the link status. */ + efx->phy_op->reconfigure(efx); + + /* Isolate the MAC. */ + falcon_deconfigure_mac_wrapper(efx); + + /* Reconfigure MAC */ + mentormac_reconfigure(efx); + + /* Reconfigure MAC wrapper */ + falcon_reconfigure_mac_wrapper(efx); +} + +static void falcon_fini_gmac(struct efx_nic *efx) +{ + /* Isolate the MAC - PHY */ + falcon_deconfigure_mac_wrapper(efx); + + /* Shut down PHY */ + efx->phy_op->fini(efx); + + /* Reset MAC */ + mentormac_reset(efx); +} + +static void falcon_update_stats_gmac(struct efx_nic *efx) +{ + struct efx_mac_stats *mac_stats = &efx->mac_stats; + unsigned long old_rx_pause, old_tx_pause; + unsigned long new_rx_pause, new_tx_pause; + int rc; + + rc = falcon_dma_stats(efx, GDmaDone_offset); + if (rc) + return; + + /* Pause frames are erroneously counted as errors (SFC bug 3269) */ + old_rx_pause = mac_stats->rx_pause; + old_tx_pause = mac_stats->tx_pause; + + /* Update MAC stats from DMAed values */ + FALCON_STAT(efx, GRxGoodOct, rx_good_bytes); + FALCON_STAT(efx, GRxBadOct, rx_bad_bytes); + FALCON_STAT(efx, GRxMissPkt, rx_missed); + FALCON_STAT(efx, GRxFalseCRS, rx_false_carrier); + FALCON_STAT(efx, GRxPausePkt, rx_pause); + FALCON_STAT(efx, GRxBadPkt, rx_bad); + FALCON_STAT(efx, GRxUcastPkt, rx_unicast); + FALCON_STAT(efx, GRxMcastPkt, rx_multicast); + FALCON_STAT(efx, GRxBcastPkt, rx_broadcast); + FALCON_STAT(efx, GRxGoodLt64Pkt, rx_good_lt64); + FALCON_STAT(efx, GRxBadLt64Pkt, rx_bad_lt64); + FALCON_STAT(efx, GRx64Pkt, rx_64); + FALCON_STAT(efx, GRx65to127Pkt, rx_65_to_127); + FALCON_STAT(efx, GRx128to255Pkt, rx_128_to_255); + FALCON_STAT(efx, GRx256to511Pkt, rx_256_to_511); + FALCON_STAT(efx, GRx512to1023Pkt, rx_512_to_1023); + FALCON_STAT(efx, GRx1024to15xxPkt, rx_1024_to_15xx); + FALCON_STAT(efx, GRx15xxtoJumboPkt, rx_15xx_to_jumbo); + FALCON_STAT(efx, GRxGtJumboPkt, rx_gtjumbo); + FALCON_STAT(efx, GRxFcsErr64to15xxPkt, rx_bad_64_to_15xx); + FALCON_STAT(efx, GRxFcsErr15xxtoJumboPkt, rx_bad_15xx_to_jumbo); + FALCON_STAT(efx, GRxFcsErrGtJumboPkt, rx_bad_gtjumbo); + FALCON_STAT(efx, GTxGoodBadOct, tx_bytes); + FALCON_STAT(efx, GTxGoodOct, tx_good_bytes); + FALCON_STAT(efx, GTxSglColPkt, tx_single_collision); + FALCON_STAT(efx, GTxMultColPkt, tx_multiple_collision); + FALCON_STAT(efx, GTxExColPkt, tx_excessive_collision); + FALCON_STAT(efx, GTxDefPkt, tx_deferred); + FALCON_STAT(efx, GTxLateCol, tx_late_collision); + FALCON_STAT(efx, GTxExDefPkt, tx_excessive_deferred); + FALCON_STAT(efx, GTxPausePkt, tx_pause); + FALCON_STAT(efx, GTxBadPkt, tx_bad); + FALCON_STAT(efx, GTxUcastPkt, tx_unicast); + FALCON_STAT(efx, GTxMcastPkt, tx_multicast); + FALCON_STAT(efx, GTxBcastPkt, tx_broadcast); + FALCON_STAT(efx, GTxLt64Pkt, tx_lt64); + FALCON_STAT(efx, GTx64Pkt, tx_64); + FALCON_STAT(efx, GTx65to127Pkt, tx_65_to_127); + FALCON_STAT(efx, GTx128to255Pkt, tx_128_to_255); + FALCON_STAT(efx, GTx256to511Pkt, tx_256_to_511); + FALCON_STAT(efx, GTx512to1023Pkt, tx_512_to_1023); + FALCON_STAT(efx, GTx1024to15xxPkt, tx_1024_to_15xx); + FALCON_STAT(efx, GTx15xxtoJumboPkt, tx_15xx_to_jumbo); + FALCON_STAT(efx, GTxGtJumboPkt, tx_gtjumbo); + FALCON_STAT(efx, GTxNonTcpUdpPkt, tx_non_tcpudp); + FALCON_STAT(efx, GTxMacSrcErrPkt, tx_mac_src_error); + FALCON_STAT(efx, GTxIpSrcErrPkt, tx_ip_src_error); + + /* Pause frames are erroneously counted as errors (SFC bug 3269) */ + new_rx_pause = mac_stats->rx_pause; + new_tx_pause = mac_stats->tx_pause; + mac_stats->rx_bad -= (new_rx_pause - old_rx_pause); + mac_stats->tx_bad -= (new_tx_pause - old_tx_pause); + + /* Derive stats that the MAC doesn't provide directly */ + mac_stats->tx_bad_bytes = + mac_stats->tx_bytes - mac_stats->tx_good_bytes; + mac_stats->tx_packets = + mac_stats->tx_lt64 + mac_stats->tx_64 + + mac_stats->tx_65_to_127 + mac_stats->tx_128_to_255 + + mac_stats->tx_256_to_511 + mac_stats->tx_512_to_1023 + + mac_stats->tx_1024_to_15xx + mac_stats->tx_15xx_to_jumbo + + mac_stats->tx_gtjumbo; + mac_stats->tx_collision = + mac_stats->tx_single_collision + + mac_stats->tx_multiple_collision + + mac_stats->tx_excessive_collision + + mac_stats->tx_late_collision; + mac_stats->rx_bytes = + mac_stats->rx_good_bytes + mac_stats->rx_bad_bytes; + mac_stats->rx_packets = + mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64 + + mac_stats->rx_64 + mac_stats->rx_65_to_127 + + mac_stats->rx_128_to_255 + mac_stats->rx_256_to_511 + + mac_stats->rx_512_to_1023 + mac_stats->rx_1024_to_15xx + + mac_stats->rx_15xx_to_jumbo + mac_stats->rx_gtjumbo; + mac_stats->rx_good = mac_stats->rx_packets - mac_stats->rx_bad; + mac_stats->rx_lt64 = mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64; +} + +static int falcon_check_gmac(struct efx_nic *efx) +{ + /* Nothing to do */ + return 0; +} + +static void falcon_gmac_sim_phy_event(struct efx_nic *efx) +{ + efx_qword_t phy_event; + + EFX_POPULATE_QWORD_2(phy_event, + EV_CODE, GLOBAL_EV_DECODE, + G_PHY0_INTR, 1); + falcon_generate_event(&efx->channel[0], &phy_event); +} + +static void falcon_gmac_reset_phy(struct efx_nic *efx) +{ + struct mii_if_info *gmii = &efx->mii; + int bmcr, i; + + /* Perform software reset to make new settings take effect */ + bmcr = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_BMCR); + bmcr |= BMCR_RESET; + gmii->mdio_write(gmii->dev, gmii->phy_id, MII_BMCR, bmcr); + + /* Wait for the reset to deassert */ + for (i = 20; i; --i) { + udelay(10); + if ((gmii->mdio_read(gmii->dev, gmii->phy_id, MII_BMCR) & + BMCR_RESET) == 0) + return; + } + + EFX_ERR(efx, "wait for PHY reset timed out\n"); +} + + +static int falcon_gmac_get_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd) +{ + struct mii_if_info *gmii = &efx->mii; + int rc; + + rc = mii_ethtool_gset(gmii, ecmd); + ecmd->supported &= ~(SUPPORTED_1000baseT_Half); + return rc; +} + +static int falcon_gmac_set_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd) +{ + struct mii_if_info *gmii = &efx->mii; + int rc; + + /* 1000Mbps half-duplex is technically legal, but none of our + * current hardware supports it, so just disallow it. */ + if (ecmd->speed == SPEED_1000 && ecmd->duplex != DUPLEX_FULL) { + EFX_LOG(efx, "rejecting unsupported 1000Mbps HD" + " setting\n"); + return -EINVAL; + } + + /* Use MII to set all other settings */ + rc = mii_ethtool_sset(gmii, ecmd); + if (rc) + return rc; + + /* Reset the PHY */ + falcon_gmac_reset_phy(efx); + + return 0; +} + +static int falcon_gmac_set_pause(struct efx_nic *efx, + enum efx_fc_type flow_control) +{ + struct mii_if_info *gmii = &efx->mii; + int adv; + + /* GMAC has tiny MAC FIFO, so TX flow control won't work */ + if (flow_control & EFX_FC_TX) + return -EINVAL; + + efx->flow_control = flow_control; + + /* Push autonegotiation to PHY */ + adv = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_ADVERTISE); + adv &= ~GM_ADVERTISE_PAUSE_CAP; + adv |= (flow_control & EFX_FC_AUTO) ? GM_ADVERTISE_PAUSE_CAP : 0; + gmii->mdio_write(gmii->dev, gmii->phy_id, MII_ADVERTISE, adv); + + falcon_gmac_reset_phy(efx); + + return 0; +} + + +struct efx_mac_operations falcon_gmac_operations = { + .mac_writel = falcon_gmac_writel, + .mac_readl = falcon_gmac_readl, + .init = falcon_init_gmac, + .reconfigure = falcon_reconfigure_gmac, + .update_stats = falcon_update_stats_gmac, + .fini = falcon_fini_gmac, + .check_hw = falcon_check_gmac, + .fake_phy_event = falcon_gmac_sim_phy_event, + .get_settings = falcon_gmac_get_settings, + .set_settings = falcon_gmac_set_settings, + .set_pause = falcon_gmac_set_pause, +}; Index: head-2008-03-17/drivers/net/sfc/falcon_hwdefs.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/falcon_hwdefs.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,1618 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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_FALCON_HWDEFS_H +#define EFX_FALCON_HWDEFS_H + +/* + * Falcon hardware value definitions. + * Falcon is the internal codename for the SFC4000 controller that is + * present in SFE400X evaluation boards + */ + +/************************************************************************** + * + * Falcon registers + * + ************************************************************************** + */ + +/* Address region register */ +#define ADR_REGION_REG_KER 0x00 +#define ADR_REGION0_LBN 0 +#define ADR_REGION0_WIDTH 18 +#define ADR_REGION1_LBN 32 +#define ADR_REGION1_WIDTH 18 +#define ADR_REGION2_LBN 64 +#define ADR_REGION2_WIDTH 18 +#define ADR_REGION3_LBN 96 +#define ADR_REGION3_WIDTH 18 + +/* Interrupt enable register */ +#define INT_EN_REG_KER 0x0010 +#define INT_LEVEL_SEL_LBN 8 +#define INT_LEVEL_SEL_WIDTH 6 +#define MEM_PERR_INT_EN_KER_LBN 5 +#define MEM_PERR_INT_EN_KER_WIDTH 1 +#define KER_INT_CHAR_LBN 4 +#define KER_INT_CHAR_WIDTH 1 +#define KER_INT_KER_LBN 3 +#define KER_INT_KER_WIDTH 1 +#define ILL_ADR_ERR_INT_EN_KER_LBN 2 +#define ILL_ADR_ERR_INT_EN_KER_WIDTH 1 +#define SRM_PERR_INT_EN_KER_LBN 1 +#define SRM_PERR_INT_EN_KER_WIDTH 1 +#define DRV_INT_EN_KER_LBN 0 +#define DRV_INT_EN_KER_WIDTH 1 + +/* Interrupt status address register */ +#define INT_ADR_REG_KER 0x0030 +#define NORM_INT_VEC_DIS_KER_LBN 64 +#define NORM_INT_VEC_DIS_KER_WIDTH 1 +#define INT_ADR_KER_LBN 0 +#define INT_ADR_KER_WIDTH EFX_DMA_TYPE_WIDTH(64) /* not 46 for this one */ + +/* Interrupt status register (B0 only) */ +#define INT_ISR0_B0 0x90 +#define INT_ISR1_B0 0xA0 + +/* Interrupt acknowledge register (A0/A1 only) */ +#define INT_ACK_REG_KER_A1 0x0050 +#define INT_ACK_DUMMY_DATA_LBN 0 +#define INT_ACK_DUMMY_DATA_WIDTH 32 + +/* Interrupt acknowledge work-around register (A0/A1 only )*/ +#define WORK_AROUND_BROKEN_PCI_READS_REG_KER_A1 0x0070 + +/* Hardware initialisation register */ +#define HW_INIT_REG_KER 0x00c0 +#define BCSR_TARGET_MASK_LBN 101 +#define BCSR_TARGET_MASK_WIDTH 4 +#define PCIE_TIMEOUT_DIS_LBN 123 +#define PCIE_TIMEOUT_DIS_WIDTH 1 +#define B2B_REQ_EN_B0_LBN 45 +#define B2B_REQ_EN_B0_WIDTH 1 +#define FC_BLOCKING_EN_B0_LBN 44 +#define FC_BLOCKING_EN_B0_WIDTH 1 + +/* SPI host command register */ +#define EE_SPI_HCMD_REG_KER 0x0100 +#define EE_SPI_HCMD_CMD_EN_LBN 31 +#define EE_SPI_HCMD_CMD_EN_WIDTH 1 +#define EE_WR_TIMER_ACTIVE_LBN 28 +#define EE_WR_TIMER_ACTIVE_WIDTH 1 +#define EE_SPI_HCMD_SF_SEL_LBN 24 +#define EE_SPI_HCMD_SF_SEL_WIDTH 1 +#define EE_SPI_EEPROM 0 +#define EE_SPI_FLASH 1 +#define EE_SPI_HCMD_DABCNT_LBN 16 +#define EE_SPI_HCMD_DABCNT_WIDTH 5 +#define EE_SPI_HCMD_READ_LBN 15 +#define EE_SPI_HCMD_READ_WIDTH 1 +#define EE_SPI_READ 1 +#define EE_SPI_WRITE 0 +#define EE_SPI_HCMD_DUBCNT_LBN 12 +#define EE_SPI_HCMD_DUBCNT_WIDTH 2 +#define EE_SPI_HCMD_ADBCNT_LBN 8 +#define EE_SPI_HCMD_ADBCNT_WIDTH 2 +#define EE_SPI_HCMD_ENC_LBN 0 +#define EE_SPI_HCMD_ENC_WIDTH 8 + +/* SPI host address register */ +#define EE_SPI_HADR_REG_KER 0x0110 +#define EE_SPI_HADR_DUBYTE_LBN 24 +#define EE_SPI_HADR_DUBYTE_WIDTH 8 +#define EE_SPI_HADR_ADR_LBN 0 +#define EE_SPI_HADR_ADR_WIDTH 24 + +/* SPI host data register */ +#define EE_SPI_HDATA_REG_KER 0x0120 +#define EE_SPI_HDATA3_LBN 96 +#define EE_SPI_HDATA3_WIDTH 32 +#define EE_SPI_HDATA2_LBN 64 +#define EE_SPI_HDATA2_WIDTH 32 +#define EE_SPI_HDATA1_LBN 32 +#define EE_SPI_HDATA1_WIDTH 32 +#define EE_SPI_HDATA0_LBN 0 +#define EE_SPI_HDATA0_WIDTH 32 + +/* SPI/VPD config register */ +#define EE_VPD_CFG_REG_KER 0x0140 +#define EE_VPD_EN_LBN 0 +#define EE_VPD_EN_WIDTH 1 +#define EE_VPD_EN_AD9_MODE_LBN 1 +#define EE_VPD_EN_AD9_MODE_WIDTH 1 +#define EE_EE_CLOCK_DIV_LBN 112 +#define EE_EE_CLOCK_DIV_WIDTH 7 +#define EE_SF_CLOCK_DIV_LBN 120 +#define EE_SF_CLOCK_DIV_WIDTH 7 + +/* PCIE CORE ACCESS REG */ +#define PCIE_CORE_INDIRECT_REG 0x01f0 +#define PCIE_CORE_ADDR_LBN 0 +#define PCIE_CORE_ADDR_WIDTH 12 +#define PCIE_CORE_RW_LBN 15 +#define PCIE_CORE_RW_WIDTH 1 +#define PCIE_CORE_VALUE_LBN 32 +#define PCIE_CORE_VALUE_WIDTH 32 + +#define PCIE_CORE_ADDR_PCIE_DEVICE_CTRL_STAT 0x68 +#define PCIE_CORE_ADDR_PCIE_LINK_CTRL_STAT 0x70 +#define PCIE_CORE_ADDR_ACK_RPL_TIMER 0x700 +#define PCIE_CORE_ACK_TL_LBN 0 +#define PCIE_CORE_ACK_TL_WIDTH 16 +#define PCIE_CORE_RPL_TL_LBN 16 +#define PCIE_CORE_RPL_TL_WIDTH 16 + +#define PCIE_CORE_ADDR_ACK_FREQ 0x70C +#define PCIE_CORE_ACK_FREQ_LBN 0 +#define PCIE_CORE_ACK_FREQ_WIDTH 7 + + +/* NIC status register */ +#define NIC_STAT_REG 0x0200 +#define EE_STRAP_EN_LBN 31 +#define EE_STRAP_EN_WIDTH 1 +#define EE_STRAP_OVR_LBN 24 +#define EE_STRAP_OVR_WIDTH 4 +#define ONCHIP_SRAM_LBN 16 +#define ONCHIP_SRAM_WIDTH 1 +#define SF_PRST_LBN 9 +#define SF_PRST_WIDTH 1 +#define EE_PRST_LBN 8 +#define EE_PRST_WIDTH 1 +#define EE_STRAP_LBN 7 +#define EE_STRAP_WIDTH 1 +/* See pic_mode_t for decoding of this field */ +#define STRAP_ISCSI_EN_LBN 3 +#define STRAP_ISCSI_EN_WIDTH 1 +#define STRAP_PINS_LBN 0 +#define STRAP_PINS_WIDTH 3 +/* These bit definitions are extrapolated from the list of numerical + * values for STRAP_PINS. + */ +#define STRAP_10G_LBN 2 +#define STRAP_10G_WIDTH 1 +#define STRAP_DUAL_PORT_LBN 1 +#define STRAP_DUAL_PORT_WIDTH 1 +#define STRAP_PCIE_LBN 0 +#define STRAP_PCIE_WIDTH 1 + +#define FLASH_PRESENT_LBN 7 +#define FLASH_PRESENT_WIDTH 1 +#define EEPROM_PRESENT_LBN 6 +#define EEPROM_PRESENT_WIDTH 1 +#define BOOTED_USING_NVDEVICE_LBN 3 +#define BOOTED_USING_NVDEVICE_WIDTH 1 + +/* GPIO control register */ + +#define GPIO_CTL_REG_KER 0x0210 + +#define GPIO_USE_NIC_CLK_LBN (30) +#define GPIO_USE_NIC_CLK_WIDTH (1) + +#define GPIO_OUTPUTS_LBN (16) +#define GPIO_OUTPUTS_WIDTH (4) + +#define GPIO_INPUTS_LBN (8) +#define GPIO_INPUT_WIDTH (4) + +#define GPIO_DIRECTION_LBN (24) +#define GPIO_DIRECTION_WIDTH (4) +#define GPIO_DIRECTION_OUT (1) +#define GPIO_SRAM_SLEEP (1 << 1) + +#define GPIO3_OEN_LBN (GPIO_DIRECTION_LBN + 3) +#define GPIO3_OEN_WIDTH 1 +#define GPIO2_OEN_LBN (GPIO_DIRECTION_LBN + 2) +#define GPIO2_OEN_WIDTH 1 +#define GPIO1_OEN_LBN (GPIO_DIRECTION_LBN + 1) +#define GPIO1_OEN_WIDTH 1 +#define GPIO0_OEN_LBN (GPIO_DIRECTION_LBN + 0) +#define GPIO0_OEN_WIDTH 1 + +#define GPIO3_OUT_LBN (GPIO_OUTPUTS_LBN + 3) +#define GPIO3_OUT_WIDTH 1 +#define GPIO2_OUT_LBN (GPIO_OUTPUTS_LBN + 2) +#define GPIO2_OUT_WIDTH 1 +#define GPIO1_OUT_LBN (GPIO_OUTPUTS_LBN + 1) +#define GPIO1_OUT_WIDTH 1 +#define GPIO0_OUT_LBN (GPIO_OUTPUTS_LBN + 0) +#define GPIO0_OUT_WIDTH 1 + +#define GPIO3_IN_LBN (GPIO_INPUTS_LBN + 3) +#define GPIO3_IN_WIDTH 1 +#define GPIO2_IN_LBN (GPIO_INPUTS_LBN + 2) +#define GPIO2_IN_WIDTH 1 +#define GPIO1_IN_LBN (GPIO_INPUTS_LBN + 1) +#define GPIO1_IN_WIDTH 1 +#define GPIO0_IN_LBN (GPIO_INPUTS_LBN + 0) +#define GPIO0_IN_WIDTH 1 + +/* Global control register */ +#define GLB_CTL_REG_KER 0x0220 +#define EXT_PHY_RST_CTL_LBN 63 +#define EXT_PHY_RST_CTL_WIDTH 1 +#define PCIE_SD_RST_CTL_LBN 61 +#define PCIE_SD_RST_CTL_WIDTH 1 +#define PCIX_RST_CTL_LBN 60 +#define PCIX_RST_CTL_WIDTH 1 +#define PCIE_STCK_RST_CTL_LBN 59 + +#define PCIE_STCK_RST_CTL_WIDTH 1 +#define PCIE_NSTCK_RST_CTL_LBN 58 +#define PCIE_NSTCK_RST_CTL_WIDTH 1 +#define PCIE_CORE_RST_CTL_LBN 57 +#define PCIE_CORE_RST_CTL_WIDTH 1 +#define EE_RST_CTL_LBN 49 +#define EE_RST_CTL_WIDTH 1 +#define CS_RST_CTL_LBN 48 +#define CS_RST_CTL_WIDTH 1 +#define RST_EXT_PHY_LBN 31 +#define RST_EXT_PHY_WIDTH 1 +#define RST_XGRX_LBN 24 +#define RST_XGRX_WIDTH 1 +#define RST_XGTX_LBN 23 +#define RST_XGTX_WIDTH 1 +#define RST_EM_LBN 22 +#define RST_EM_WIDTH 1 +#define INT_RST_DUR_LBN 4 +#define INT_RST_DUR_WIDTH 3 +#define EXT_PHY_RST_DUR_LBN 1 +#define EXT_PHY_RST_DUR_WIDTH 3 +#define SWRST_LBN 0 +#define SWRST_WIDTH 1 +#define INCLUDE_IN_RESET 0 +#define EXCLUDE_FROM_RESET 1 + +/* Fatal interrupt register */ +#define FATAL_INTR_REG_KER 0x0230 +#define PCI_BUSERR_INT_KER_EN_LBN 43 +#define PCI_BUSERR_INT_KER_EN_WIDTH 1 +#define SRAM_OOB_INT_KER_EN_LBN 42 +#define SRAM_OOB_INT_KER_EN_WIDTH 1 +#define BUFID_OOB_INT_KER_EN_LBN 41 +#define BUFID_OOB_INT_KER_EN_WIDTH 1 +#define MEM_PERR_INT_KER_EN_LBN 40 +#define MEM_PERR_INT_KER_EN_WIDTH 1 +#define RBUF_OWN_INT_KER_EN_LBN 39 +#define RBUF_OWN_INT_KER_EN_WIDTH 1 +#define TBUF_OWN_INT_KER_EN_LBN 38 +#define TBUF_OWN_INT_KER_EN_WIDTH 1 +#define RDESCQ_OWN_INT_KER_EN_LBN 37 +#define RDESCQ_OWN_INT_KER_EN_WIDTH 1 +#define TDESCQ_OWN_INT_KER_EN_LBN 36 +#define TDESCQ_OWN_INT_KER_EN_WIDTH 1 +#define EVQ_OWN_INT_KER_EN_LBN 35 +#define EVQ_OWN_INT_KER_EN_WIDTH 1 +#define EVFF_OFLO_INT_KER_EN_LBN 34 +#define EVFF_OFLO_INT_KER_EN_WIDTH 1 +#define ILL_ADR_INT_KER_EN_LBN 33 +#define ILL_ADR_INT_KER_EN_WIDTH 1 +#define SRM_PERR_INT_KER_EN_LBN 32 +#define SRM_PERR_INT_KER_EN_WIDTH 1 +#define MEM_PERR_INT_KER_LBN 8 +#define MEM_PERR_INT_KER_WIDTH 1 +#define INT_KER_ERROR_LBN 0 +#define INT_KER_ERROR_WIDTH 12 + +#define DP_CTRL_REG 0x250 +#define FLS_EVQ_ID_LBN 0 +#define FLS_EVQ_ID_WIDTH 11 + +#define MEM_STAT_REG_KER 0x260 + +/* Debug probe register */ +#define DEBUG_REG_KER 0x0270 +#define DEBUG_BLK_SEL2_LBN 47 +#define DEBUG_BLK_SEL2_WIDTH 3 +#define DEBUG_BLK_SEL1_LBN 44 +#define DEBUG_BLK_SEL1_WIDTH 3 +#define DEBUG_BLK_SEL0_LBN 41 +#define DEBUG_BLK_SEL0_WIDTH 3 +#define DEBUG_BLK_SEL_MISC 7 +#define DEBUG_BLK_SEL_SERDES 6 +#define DEBUG_BLK_SEL_EM 5 +#define DEBUG_BLK_SEL_SR 4 +#define DEBUG_BLK_SEL_EV 3 +#define DEBUG_BLK_SEL_RX 2 +#define DEBUG_BLK_SEL_TX 1 +#define DEBUG_BLK_SEL_BIU 0 +#define MISC_DEBUG_ADDR_LBN 36 +#define MISC_DEBUG_ADDR_WIDTH 5 +#define SERDES_DEBUG_ADDR_LBN 31 +#define SERDES_DEBUG_ADDR_WIDTH 5 +#define EM_DEBUG_ADDR_LBN 26 +#define EM_DEBUG_ADDR_WIDTH 5 +#define SR_DEBUG_ADDR_LBN 21 +#define SR_DEBUG_ADDR_WIDTH 5 +#define EV_DEBUG_ADDR_LBN 16 +#define EV_DEBUG_ADDR_WIDTH 5 +#define RX_DEBUG_ADDR_LBN 11 +#define RX_DEBUG_ADDR_WIDTH 5 +#define TX_DEBUG_ADDR_LBN 5 +#define TX_DEBUG_ADDR_WIDTH 5 +#define BIU_DEBUG_ADDR_LBN 1 +#define BIU_DEBUG_ADDR_WIDTH 5 +#define DEBUG_DIS_LBN 0 +#define DEBUG_DIS_WIDTH 1 + +/* Scratch register 0 */ +#define DRIVER_REG0_KER 0x0280 +#define DRIVER_DW0_LBN 0 +#define DRIVER_DW0_WIDTH 32 + +/* Scratch register 1 */ +#define DRIVER_REG1_KER 0x0290 +#define DRIVER_DW1_LBN 0 +#define DRIVER_DW1_WIDTH 32 + +/* Scratch register 2 */ +#define DRIVER_REG2_KER 0x02A0 +#define DRIVER_DW2_LBN 0 +#define DRIVER_DW2_WIDTH 32 + +/* Scratch register 3 */ +#define DRIVER_REG3_KER 0x02B0 +#define DRIVER_DW3_LBN 0 +#define DRIVER_DW3_WIDTH 32 + +/* Scratch register 4 */ +#define DRIVER_REG4_KER 0x02C0 +#define DRIVER_DW3_LBN 0 +#define DRIVER_DW3_WIDTH 32 + +/* Scratch register 5 */ +#define DRIVER_REG5_KER 0x02D0 +#define DRIVER_DW3_LBN 0 +#define DRIVER_DW3_WIDTH 32 + +/* Scratch register 6 */ +#define DRIVER_REG6_KER 0x02E0 +#define DRIVER_DW3_LBN 0 +#define DRIVER_DW3_WIDTH 32 + +/* Scratch register 7 */ +#define DRIVER_REG7_KER 0x02F0 +#define DRIVER_DW3_LBN 0 +#define DRIVER_DW3_WIDTH 32 + +/* FPGA build version */ +#define ALTERA_BUILD_REG_KER 0x0300 +#define VER_MAJOR_LBN 24 +#define VER_MAJOR_WIDTH 8 +#define VER_MINOR_LBN 16 +#define VER_MINOR_WIDTH 8 +#define VER_BUILD_LBN 0 +#define VER_BUILD_WIDTH 16 +#define VER_ALL_LBN 0 +#define VER_ALL_WIDTH 32 + +/* Spare EEPROM bits register (flash 0x390) */ +#define SPARE_REG_KER 0x310 +#define MEM_PERR_EN_LBN 64 +#define MEM_PERR_EN_WIDTH 38 +#define MEM_PERR_EN_TX_DATA_LBN 72 +#define MEM_PERR_EN_TX_DATA_WIDTH 2 +#define SPARE_EE_BITS_LBN 1 +#define SPARE_EE_BITS_WIDTH 31 +#define PCIE_LEGACY_ENDPOINT_LBN 0 +#define PCIE_LEGACY_ENDPOINT_WIDTH 1 + +/* Page mapped view of bottom 1024 EVQ RPTRS */ +#define EVQ_RPTR_REG_P0 0x400 +/* Bit definitions are as for the densely mapped + * RPTR registers. */ + +/* Timer table for kernel access */ +#define TIMER_CMD_REG_KER 0x420 +#define TIMER_MODE_LBN 12 +#define TIMER_MODE_WIDTH 2 +#define TIMER_MODE_DIS 0 +#define TIMER_MODE_INT_HLDOFF 2 +#define TIMER_VAL_LBN 0 +#define TIMER_VAL_WIDTH 12 + +/* Driver generated event register */ +#define DRV_EV_REG_KER 0x440 +#define DRV_EV_QID_LBN 64 +#define DRV_EV_QID_WIDTH 12 +#define DRV_EV_DATA_LBN 0 +#define DRV_EV_DATA_WIDTH 64 + +/* Event Queue control register */ +#define EVQ_CTL_REG_KER 0x450 +#define EVQ_FIFO_NOTAF_TH_LBN 0 +#define EVQ_FIFO_NOTAF_TH_WIDTH 6 +#define EVQ_FIFO_AF_TH_LBN 8 +#define EVQ_FIFO_AF_TH_WIDTH 6 + +/* Buffer table configuration register */ +#define BUF_TBL_CFG_REG_KER 0x600 +#define BUF_TBL_MODE_LBN 3 +#define BUF_TBL_MODE_WIDTH 1 +#define BUF_TBL_MODE_HALF 0 +#define BUF_TBL_MODE_FULL 1 + +/* SRAM receive descriptor cache configuration register */ +#define SRM_RX_DC_CFG_REG_KER 0x610 +#define SRM_RX_DC_BASE_ADR_LBN 0 +#define SRM_RX_DC_BASE_ADR_WIDTH 21 + +/* SRAM transmit descriptor cache configuration register */ +#define SRM_TX_DC_CFG_REG_KER 0x620 +#define SRM_TX_DC_BASE_ADR_LBN 0 +#define SRM_TX_DC_BASE_ADR_WIDTH 21 + +/* SRAM configuration register */ +#define SRM_CFG_REG_KER 0x630 +#define SRAM_OOB_ADR_INTEN_LBN 5 +#define SRAM_OOB_ADR_INTEN_WIDTH 1 +#define SRAM_OOB_BUF_INTEN_LBN 4 +#define SRAM_OOB_BUF_INTEN_WIDTH 1 +#define SRAM_OOB_BT_INIT_EN_LBN 3 +#define SRAM_OOB_BT_INIT_EN_WIDTH 1 +#define SRM_NUM_BANK_LBN 2 +#define SRM_NUM_BANK_WIDTH 1 +#define SRM_BANK_SIZE_LBN 0 +#define SRM_BANK_SIZE_WIDTH 2 +#define SRM_NUM_BANKS_AND_BANK_SIZE_LBN 0 +#define SRM_NUM_BANKS_AND_BANK_SIZE_WIDTH 3 +#define SRM_NB_BSZ_1BANKS_2M 0 +#define SRM_NB_BSZ_1BANKS_4M 1 +#define SRM_NB_BSZ_1BANKS_8M 2 +#define SRM_NB_BSZ_DEFAULT 3 /* char driver will set the default */ +#define SRM_NB_BSZ_2BANKS_4M 4 +#define SRM_NB_BSZ_2BANKS_8M 5 +#define SRM_NB_BSZ_2BANKS_16M 6 +#define SRM_NB_BSZ_RESERVED 7 + +/* Special buffer table update register */ +#define BUF_TBL_UPD_REG_KER 0x0650 +#define BUF_UPD_CMD_LBN 63 +#define BUF_UPD_CMD_WIDTH 1 +#define BUF_CLR_CMD_LBN 62 +#define BUF_CLR_CMD_WIDTH 1 +#define BUF_CLR_END_ID_LBN 32 +#define BUF_CLR_END_ID_WIDTH 20 +#define BUF_CLR_START_ID_LBN 0 +#define BUF_CLR_START_ID_WIDTH 20 + +/* Receive configuration register */ +#define RX_CFG_REG_KER 0x800 + +/* B0 */ +#define RX_TOEP_TCP_SUPPRESS_B0_LBN 48 +#define RX_TOEP_TCP_SUPPRESS_B0_WIDTH 1 +#define RX_INGR_EN_B0_LBN 47 +#define RX_INGR_EN_B0_WIDTH 1 +#define RX_TOEP_IPV4_B0_LBN 46 +#define RX_TOEP_IPV4_B0_WIDTH 1 +#define RX_HASH_ALG_B0_LBN 45 +#define RX_HASH_ALG_B0_WIDTH 1 +#define RX_HASH_INSERT_HDR_B0_LBN 44 +#define RX_HASH_INSERT_HDR_B0_WIDTH 1 +#define RX_DESC_PUSH_EN_B0_LBN 43 +#define RX_DESC_PUSH_EN_B0_WIDTH 1 +#define RX_RDW_PATCH_EN_LBN 42 /* Non head of line blocking */ +#define RX_RDW_PATCH_EN_WIDTH 1 +#define RX_PCI_BURST_SIZE_B0_LBN 39 +#define RX_PCI_BURST_SIZE_B0_WIDTH 3 +#define RX_OWNERR_CTL_B0_LBN 38 +#define RX_OWNERR_CTL_B0_WIDTH 1 +#define RX_XON_TX_TH_B0_LBN 33 +#define RX_XON_TX_TH_B0_WIDTH 5 +#define RX_XOFF_TX_TH_B0_LBN 28 +#define RX_XOFF_TX_TH_B0_WIDTH 5 +#define RX_USR_BUF_SIZE_B0_LBN 19 +#define RX_USR_BUF_SIZE_B0_WIDTH 9 +#define RX_XON_MAC_TH_B0_LBN 10 +#define RX_XON_MAC_TH_B0_WIDTH 9 +#define RX_XOFF_MAC_TH_B0_LBN 1 +#define RX_XOFF_MAC_TH_B0_WIDTH 9 +#define RX_XOFF_MAC_EN_B0_LBN 0 +#define RX_XOFF_MAC_EN_B0_WIDTH 1 + +/* A1 */ +#define RX_DESC_PUSH_EN_A1_LBN 35 +#define RX_DESC_PUSH_EN_A1_WIDTH 1 +#define RX_PCI_BURST_SIZE_A1_LBN 31 +#define RX_PCI_BURST_SIZE_A1_WIDTH 3 +#define RX_OWNERR_CTL_A1_LBN 30 +#define RX_OWNERR_CTL_A1_WIDTH 1 +#define RX_XON_TX_TH_A1_LBN 25 +#define RX_XON_TX_TH_A1_WIDTH 5 +#define RX_XOFF_TX_TH_A1_LBN 20 +#define RX_XOFF_TX_TH_A1_WIDTH 5 +#define RX_USR_BUF_SIZE_A1_LBN 11 +#define RX_USR_BUF_SIZE_A1_WIDTH 9 +#define RX_XON_MAC_TH_A1_LBN 6 +#define RX_XON_MAC_TH_A1_WIDTH 5 +#define RX_XOFF_MAC_TH_A1_LBN 1 +#define RX_XOFF_MAC_TH_A1_WIDTH 5 +#define RX_XOFF_MAC_EN_A1_LBN 0 +#define RX_XOFF_MAC_EN_A1_WIDTH 1 + +/* Receive filter control register */ +#define RX_FILTER_CTL_REG 0x810 +#define SCATTER_ENBL_NO_MATCH_Q_B0_LBN 40 +#define SCATTER_ENBL_NO_MATCH_Q_B0_WIDTH 1 +#define UDP_FULL_SRCH_LIMIT_LBN 32 +#define UDP_FULL_SRCH_LIMIT_WIDTH 8 +#define NUM_KER_LBN 24 +#define NUM_KER_WIDTH 2 +#define UDP_WILD_SRCH_LIMIT_LBN 16 +#define UDP_WILD_SRCH_LIMIT_WIDTH 8 +#define TCP_WILD_SRCH_LIMIT_LBN 8 +#define TCP_WILD_SRCH_LIMIT_WIDTH 8 +#define TCP_FULL_SRCH_LIMIT_LBN 0 +#define TCP_FULL_SRCH_LIMIT_WIDTH 8 + +/* RX queue flush register */ +#define RX_FLUSH_DESCQ_REG_KER 0x0820 +#define RX_FLUSH_DESCQ_CMD_LBN 24 +#define RX_FLUSH_DESCQ_CMD_WIDTH 1 +#define RX_FLUSH_DESCQ_LBN 0 +#define RX_FLUSH_DESCQ_WIDTH 12 + +/* Receive descriptor update register */ +#define RX_DESC_UPD_REG_KER 0x0830 +#define RX_DESC_WPTR_LBN 96 +#define RX_DESC_WPTR_WIDTH 12 +#define RX_DESC_UPD_REG_KER_DWORD (RX_DESC_UPD_REG_KER + 12) +#define RX_DESC_WPTR_DWORD_LBN 0 +#define RX_DESC_WPTR_DWORD_WIDTH 12 + +/* Receive descriptor cache configuration register */ +#define RX_DC_CFG_REG_KER 0x840 +#define RX_DC_SIZE_LBN 0 +#define RX_DC_SIZE_WIDTH 2 + +#define RX_DC_PF_WM_REG_KER 0x850 +#define RX_DC_PF_LWM_LBN 0 +#define RX_DC_PF_LWM_WIDTH 6 + +/* RX no descriptor drop counter */ +#define RX_NODESC_DROP_REG_KER 0x880 +#define RX_NODESC_DROP_CNT_LBN 0 +#define RX_NODESC_DROP_CNT_WIDTH 16 + +/* RX black magic register */ +#define RX_SELF_RST_REG_KER 0x890 +#define RX_ISCSI_DIS_LBN 17 +#define RX_ISCSI_DIS_WIDTH 1 +#define RX_PREFETCH_TIMEOUT_EN_LBN 10 +#define RX_PREFETCH_TIMEOUT_EN_WIDTH 1 +#define RX_NODESC_WAIT_DIS_LBN 9 +#define RX_NODESC_WAIT_DIS_WIDTH 1 +#define RX_RECOVERY_EN_LBN 8 +#define RX_RECOVERY_EN_WIDTH 1 +#define RX_SHUTDOWN_REASON_LBN 0 +#define RX_SHUTDOWN_REASON_WIDTH (3) + +/* TX queue flush register */ +#define TX_FLUSH_DESCQ_REG_KER 0x0a00 +#define TX_FLUSH_DESCQ_CMD_LBN 12 +#define TX_FLUSH_DESCQ_CMD_WIDTH 1 +#define TX_FLUSH_DESCQ_LBN 0 +#define TX_FLUSH_DESCQ_WIDTH 12 + +/* Transmit descriptor update register */ +#define TX_DESC_UPD_REG_KER 0x0a10 +#define TX_DESC_WPTR_LBN 96 +#define TX_DESC_WPTR_WIDTH 12 +#define TX_DESC_UPD_REG_KER_DWORD (TX_DESC_UPD_REG_KER + 12) +#define TX_DESC_WPTR_DWORD_LBN 0 +#define TX_DESC_WPTR_DWORD_WIDTH 12 + +/* Transmit descriptor cache configuration register */ +#define TX_DC_CFG_REG_KER 0xa20 +#define TX_DC_SIZE_LBN 0 +#define TX_DC_SIZE_WIDTH 2 + +/* Transmit checksum configuration register (A0/A1 only) */ +#define TX_CHKSM_CFG_REG_KER_A1 0xa30 + +/* Transmit configuration register */ +#define TX_CFG_REG_KER 0xa50 +#define TX_NO_EOP_DISC_EN_LBN 5 +#define TX_NO_EOP_DISC_EN_WIDTH 1 + +/* Transmit configuration register 2 */ +#define TX_CFG2_REG_KER 0xa80 +#define TX_CSR_PUSH_EN_LBN 89 +#define TX_CSR_PUSH_EN_WIDTH 1 +#define TX_RX_SPACER_LBN 64 +#define TX_RX_SPACER_WIDTH 8 +#define TX_SW_EV_EN_LBN 59 +#define TX_SW_EV_EN_WIDTH 1 +#define TX_RX_SPACER_EN_LBN 57 +#define TX_RX_SPACER_EN_WIDTH 1 +#define TX_PREF_WD_TMR_LBN 22 +#define TX_PREF_WD_TMR_WIDTH 22 +#define TX_PREF_THRESHOLD_LBN 19 +#define TX_PREF_THRESHOLD_WIDTH 2 +#define TX_ONE_PKT_PER_Q_LBN 18 +#define TX_ONE_PKT_PER_Q_WIDTH 1 +#define TX_DIS_NON_IP_EV_LBN 17 +#define TX_DIS_NON_IP_EV_WIDTH 1 +#define TX_DMA_SPACER_LBN 8 +#define TX_DMA_SPACER_WIDTH 8 +#define TX_FLUSH_MIN_LEN_EN_B0_LBN 7 +#define TX_FLUSH_MIN_LEN_EN_B0_WIDTH 1 +#define TX_TCP_DIS_A1_LBN 7 +#define TX_TCP_DIS_A1_WIDTH 1 +#define TX_IP_DIS_A1_LBN 6 +#define TX_IP_DIS_A1_WIDTH 1 +#define TX_MAX_CPL_LBN 2 +#define TX_MAX_CPL_WIDTH 2 +#define TX_MAX_PREF_LBN 0 +#define TX_MAX_PREF_WIDTH 2 + +/* Transmit VLAN filter control register */ +#define TX_VLAN_REG 0xae0 + +/* PHY management transmit data register */ +#define MD_TXD_REG_KER 0xc00 +#define MD_TXD_LBN 0 +#define MD_TXD_WIDTH 16 + +/* PHY management receive data register */ +#define MD_RXD_REG_KER 0xc10 +#define MD_RXD_LBN 0 +#define MD_RXD_WIDTH 16 + +/* PHY management configuration & status register */ +#define MD_CS_REG_KER 0xc20 +#define MD_PT_LBN 7 +#define MD_PT_WIDTH 3 +#define MD_PL_LBN 6 +#define MD_PL_WIDTH 1 +#define MD_INT_CLR_LBN 5 +#define MD_INT_CLR_WIDTH 1 +#define MD_GC_LBN 4 +#define MD_GC_WIDTH 1 +#define MD_PRSP_LBN 3 +#define MD_PRSP_WIDTH 1 +#define MD_RIC_LBN 2 +#define MD_RIC_WIDTH 1 +#define MD_RDC_LBN 1 +#define MD_RDC_WIDTH 1 +#define MD_WRC_LBN 0 +#define MD_WRC_WIDTH 1 + +/* PHY management PHY address register */ +#define MD_PHY_ADR_REG_KER 0xc30 +#define MD_PHY_ADR_LBN 0 +#define MD_PHY_ADR_WIDTH 16 + +/* PHY management ID register */ +#define MD_ID_REG_KER 0xc40 +#define MD_PRT_ADR_LBN 11 +#define MD_PRT_ADR_WIDTH 5 +#define MD_DEV_ADR_LBN 6 +#define MD_DEV_ADR_WIDTH 5 +/* Used for writing both at once */ +#define MD_PRT_DEV_ADR_LBN 6 +#define MD_PRT_DEV_ADR_WIDTH 10 + +/* PHY management status & mask register (DWORD read only) */ +#define MD_STAT_REG_KER 0xc50 +#define MD_PINT_LBN 4 +#define MD_PINT_WIDTH 1 +#define MD_DONE_LBN 3 +#define MD_DONE_WIDTH 1 +#define MD_BSERR_LBN 2 +#define MD_BSERR_WIDTH 1 +#define MD_LNFL_LBN 1 +#define MD_LNFL_WIDTH 1 +#define MD_BSY_LBN 0 +#define MD_BSY_WIDTH 1 + +/* Port 0 and 1 MAC stats registers */ +#define MAC0_STAT_DMA_REG_KER 0xc60 +#define MAC1_STAT_DMA_REG_KER 0xc70 +#define MAC_STAT_DMA_CMD_LBN 48 +#define MAC_STAT_DMA_CMD_WIDTH 1 +#define MAC_STAT_DMA_REGION_LBN 46 +#define MAC_STAT_DMA_REGION_WIDTH 2 +#define MAC_STAT_DMA_ADR_LBN 0 +#define MAC_STAT_DMA_ADR_WIDTH EFX_DMA_TYPE_WIDTH(46) + +/* Port 0 and 1 MAC control registers */ +#define MAC0_CTRL_REG_KER 0xc80 +#define MAC1_CTRL_REG_KER 0xc90 +#define MAC_XOFF_VAL_LBN 16 +#define MAC_XOFF_VAL_WIDTH 16 +#define TXFIFO_DRAIN_EN_B0_LBN 7 +#define TXFIFO_DRAIN_EN_B0_WIDTH 1 +#define MAC_XG_DISTXCRC_LBN 5 +#define MAC_XG_DISTXCRC_WIDTH 1 +#define MAC_BCAD_ACPT_LBN 4 +#define MAC_BCAD_ACPT_WIDTH 1 +#define MAC_UC_PROM_LBN 3 +#define MAC_UC_PROM_WIDTH 1 +#define MAC_LINK_STATUS_LBN 2 +#define MAC_LINK_STATUS_WIDTH 1 +#define MAC_SPEED_LBN 0 +#define MAC_SPEED_WIDTH 2 + +/* External interrupt control (replaces MAC1_CTRL in B0) */ +#define GEN_MODE_REG_KER 0xc90 +#define XFP_PHY_INT_POL_SEL_LBN 3 +#define XFP_PHY_INT_POL_SEL_WIDTH 1 +#define XG_PHY_INT_POL_SEL_LBN 2 +#define XG_PHY_INT_POL_SEL_WIDTH 1 +#define XFP_PHY_INT_MASK_LBN 1 +#define XFP_PHY_INT_MASK_WIDTH 1 +#define XG_PHY_INT_MASK_LBN 0 +#define XG_PHY_INT_MASK_WIDTH 1 + + +/* 10G XAUI XGXS default values */ +#define XX_TXDRV_DEQ_DEFAULT 0xe /* deq=.6 */ +#define XX_TXDRV_DTX_DEFAULT 0x5 /* 1.25 */ +#define XX_SD_CTL_DRV_DEFAULT 0 /* 20mA */ + +/* Multicast address hash table */ +#define MAC_MCAST_HASH_REG0_KER 0xca0 +#define MAC_MCAST_HASH_REG1_KER 0xcb0 + +/* MAC test register. */ +#define MAC_TEST_REG_KER 0xcc0 +#define MAC_PTLOOP_EN_LBN 0 +#define MAC_PTLOOP_EN_WIDTH 1 + +/* GMAC registers */ +#define FALCON_GMAC_REGBANK 0xe00 +#define FALCON_GMAC_REGBANK_SIZE 0x200 +#define FALCON_GMAC_REG_SIZE 0x10 + +/* Source MAC filter control register */ +#define TX_SRC_MAC_CTRL_REG 0x1100 + +/* XMAC registers */ +#define FALCON_XMAC_REGBANK 0x1200 +#define FALCON_XMAC_REGBANK_SIZE 0x200 +#define FALCON_XMAC_REG_SIZE 0x10 + +/* XGMAC address register low */ +#define XM_ADR_LO_REG_MAC 0x00 +#define XM_ADR_3_LBN 24 +#define XM_ADR_3_WIDTH 8 +#define XM_ADR_2_LBN 16 +#define XM_ADR_2_WIDTH 8 +#define XM_ADR_1_LBN 8 +#define XM_ADR_1_WIDTH 8 +#define XM_ADR_0_LBN 0 +#define XM_ADR_0_WIDTH 8 + +/* XGMAC address register high */ +#define XM_ADR_HI_REG_MAC 0x01 +#define XM_ADR_5_LBN 8 +#define XM_ADR_5_WIDTH 8 +#define XM_ADR_4_LBN 0 +#define XM_ADR_4_WIDTH 8 + +/* XGMAC global configuration */ +#define XM_GLB_CFG_REG_MAC 0x02 +#define XM_LINE_LB_DEEP_RSVD_LBN 28 +#define XM_LINE_LB_DEEP_RSVD_WIDTH 1 +#define XM_RMTFLT_GEN_LBN 17 +#define XM_RMTFLT_GEN_WIDTH 1 +#define XM_DEBUG_MODE_LBN 16 +#define XM_DEBUG_MODE_WIDTH 1 +#define XM_RX_STAT_EN_LBN 11 +#define XM_RX_STAT_EN_WIDTH 1 +#define XM_TX_STAT_EN_LBN 10 +#define XM_TX_STAT_EN_WIDTH 1 +#define XM_RX_JUMBO_MODE_LBN 6 +#define XM_RX_JUMBO_MODE_WIDTH 1 +#define XM_WAN_MODE_LBN 5 +#define XM_WAN_MODE_WIDTH 1 +#define XM_AUTOCLR_MODE_LBN 4 +#define XM_AUTOCLR_MODE_WIDTH 1 +#define XM_INTCLR_MODE_LBN 3 +#define XM_INTCLR_MODE_WIDTH 1 +#define XM_CORE_RST_LBN 0 +#define XM_CORE_RST_WIDTH 1 + +/* XGMAC transmit configuration */ +#define XM_TX_CFG_REG_MAC 0x03 +#define XM_TX_PROG_LBN 24 +#define XM_TX_PROG_WIDTH 1 +#define XM_IPG_LBN 16 +#define XM_IPG_WIDTH 4 +#define XM_FCNTL_LBN 10 +#define XM_FCNTL_WIDTH 1 +#define XM_TXCRC_LBN 8 +#define XM_TXCRC_WIDTH 1 +#define XM_EDRC_LBN 6 +#define XM_EDRC_WIDTH 1 +#define XM_AUTO_PAD_LBN 5 +#define XM_AUTO_PAD_WIDTH 1 +#define XM_TX_PRMBL_LBN 2 +#define XM_TX_PRMBL_WIDTH 1 +#define XM_TXEN_LBN 1 +#define XM_TXEN_WIDTH 1 +#define XM_TX_RST_LBN 0 +#define XM_TX_RST_WIDTH 1 + +/* XGMAC receive configuration */ +#define XM_RX_CFG_REG_MAC 0x04 +#define XM_PASS_LENERR_LBN 26 +#define XM_PASS_LENERR_WIDTH 1 +#define XM_PASS_CRC_ERR_LBN 25 +#define XM_PASS_CRC_ERR_WIDTH 1 +#define XM_PASS_PRMBLE_ERR_LBN 24 +#define XM_PASS_PRMBLE_ERR_WIDTH 1 +#define XM_ACPT_ALL_MCAST_LBN 11 +#define XM_ACPT_ALL_MCAST_WIDTH 1 +#define XM_ACPT_ALL_UCAST_LBN 9 +#define XM_ACPT_ALL_UCAST_WIDTH 1 +#define XM_AUTO_DEPAD_LBN 8 +#define XM_AUTO_DEPAD_WIDTH 1 +#define XM_RXCRC_LBN 3 +#define XM_RXCRC_WIDTH 1 +#define XM_RX_PRMBL_LBN 2 +#define XM_RX_PRMBL_WIDTH 1 +#define XM_RXEN_LBN 1 +#define XM_RXEN_WIDTH 1 +#define XM_RX_RST_LBN 0 +#define XM_RX_RST_WIDTH 1 + +/* XGMAC flow control register */ +#define XM_FC_REG_MAC 0x7 +#define XM_PAUSE_TIME_LBN 16 +#define XM_PAUSE_TIME_WIDTH 16 +#define XM_DIS_FCNTL_LBN 0 +#define XM_DIS_FCNTL_WIDTH 1 + +/* XGMAC pause time count register */ +#define XM_PAUSE_TIME_REG_MAC 0x9 +#define XM_TX_PAUSE_CNT_LBN 16 +#define XM_TX_PAUSE_CNT_WIDTH 16 +#define XM_RX_PAUSE_CNT_LBN 0 +#define XM_RX_PAUSE_CNT_WIDTH 16 + +/* XGMAC transmit parameter register */ +#define XM_TX_PARAM_REG_MAC 0x0d +#define XM_TX_JUMBO_MODE_LBN 31 +#define XM_TX_JUMBO_MODE_WIDTH 1 +#define XM_MAX_TX_FRM_SIZE_LBN 16 +#define XM_MAX_TX_FRM_SIZE_WIDTH 14 +#define XM_PAD_CHAR_LBN 0 +#define XM_PAD_CHAR_WIDTH 8 + +/* XGMAC receive parameter register */ +#define XM_RX_PARAM_REG_MAC 0x0e +#define XM_MAX_RX_FRM_SIZE_LBN 0 +#define XM_MAX_RX_FRM_SIZE_WIDTH 14 + +/* XGXS/XAUI powerdown/reset register */ +#define XX_PWR_RST_REG_MAC 0x10 + +#define XX_PWRDND_EN_LBN 15 +#define XX_PWRDND_EN_WIDTH 1 +#define XX_PWRDNC_EN_LBN 14 +#define XX_PWRDNC_EN_WIDTH 1 +#define XX_PWRDNB_EN_LBN 13 +#define XX_PWRDNB_EN_WIDTH 1 +#define XX_PWRDNA_EN_LBN 12 +#define XX_PWRDNA_EN_WIDTH 1 +#define XX_RSTPLLCD_EN_LBN 9 +#define XX_RSTPLLCD_EN_WIDTH 1 +#define XX_RSTPLLAB_EN_LBN 8 +#define XX_RSTPLLAB_EN_WIDTH 1 +#define XX_RESETD_EN_LBN 7 +#define XX_RESETD_EN_WIDTH 1 +#define XX_RESETC_EN_LBN 6 +#define XX_RESETC_EN_WIDTH 1 +#define XX_RESETB_EN_LBN 5 +#define XX_RESETB_EN_WIDTH 1 +#define XX_RESETA_EN_LBN 4 +#define XX_RESETA_EN_WIDTH 1 +#define XX_RSTXGXSRX_EN_LBN 2 +#define XX_RSTXGXSRX_EN_WIDTH 1 +#define XX_RSTXGXSTX_EN_LBN 1 +#define XX_RSTXGXSTX_EN_WIDTH 1 +#define XX_RST_XX_EN_LBN 0 +#define XX_RST_XX_EN_WIDTH 1 + +/* XGXS/XAUI powerdown/reset control register */ +#define XX_SD_CTL_REG_MAC 0x11 +#define XX_TERMADJ1_LBN 17 +#define XX_TERMADJ1_WIDTH 1 +#define XX_TERMADJ0_LBN 16 +#define XX_TERMADJ0_WIDTH 1 +#define XX_HIDRVD_LBN 15 +#define XX_HIDRVD_WIDTH 1 +#define XX_LODRVD_LBN 14 +#define XX_LODRVD_WIDTH 1 +#define XX_HIDRVC_LBN 13 +#define XX_HIDRVC_WIDTH 1 +#define XX_LODRVC_LBN 12 +#define XX_LODRVC_WIDTH 1 +#define XX_HIDRVB_LBN 11 +#define XX_HIDRVB_WIDTH 1 +#define XX_LODRVB_LBN 10 +#define XX_LODRVB_WIDTH 1 +#define XX_HIDRVA_LBN 9 +#define XX_HIDRVA_WIDTH 1 +#define XX_LODRVA_LBN 8 +#define XX_LODRVA_WIDTH 1 +#define XX_LPBKD_LBN 3 +#define XX_LPBKD_WIDTH 1 +#define XX_LPBKC_LBN 2 +#define XX_LPBKC_WIDTH 1 +#define XX_LPBKB_LBN 1 +#define XX_LPBKB_WIDTH 1 +#define XX_LPBKA_LBN 0 +#define XX_LPBKA_WIDTH 1 + +#define XX_TXDRV_CTL_REG_MAC 0x12 +#define XX_DEQD_LBN 28 +#define XX_DEQD_WIDTH 4 +#define XX_DEQC_LBN 24 +#define XX_DEQC_WIDTH 4 +#define XX_DEQB_LBN 20 +#define XX_DEQB_WIDTH 4 +#define XX_DEQA_LBN 16 +#define XX_DEQA_WIDTH 4 +#define XX_DTXD_LBN 12 +#define XX_DTXD_WIDTH 4 +#define XX_DTXC_LBN 8 +#define XX_DTXC_WIDTH 4 +#define XX_DTXB_LBN 4 +#define XX_DTXB_WIDTH 4 +#define XX_DTXA_LBN 0 +#define XX_DTXA_WIDTH 4 + +/* XAUI XGXS core status register */ +#define XX_FORCE_SIG_LBN 24 +#define XX_FORCE_SIG_WIDTH 8 +#define XX_FORCE_SIG_DECODE_FORCED 0xff +#define XX_XGXS_LB_EN_LBN 23 +#define XX_XGXS_LB_EN_WIDTH 1 +#define XX_XGMII_LB_EN_LBN 22 +#define XX_XGMII_LB_EN_WIDTH 1 +#define XX_CORE_STAT_REG_MAC 0x16 +#define XX_ALIGN_DONE_LBN 20 +#define XX_ALIGN_DONE_WIDTH 1 +#define XX_SYNC_STAT_LBN 16 +#define XX_SYNC_STAT_WIDTH 4 +#define XX_SYNC_STAT_DECODE_SYNCED 0xf +#define XX_COMMA_DET_LBN 12 +#define XX_COMMA_DET_WIDTH 4 +#define XX_COMMA_DET_DECODE_DETECTED 0xf +#define XX_COMMA_DET_RESET 0xf +#define XX_CHARERR_LBN 4 +#define XX_CHARERR_WIDTH 4 +#define XX_CHARERR_RESET 0xf +#define XX_DISPERR_LBN 0 +#define XX_DISPERR_WIDTH 4 +#define XX_DISPERR_RESET 0xf + +/* Receive filter table */ +#define RX_FILTER_TBL0 0xF00000 + +/* Receive descriptor pointer table */ +#define RX_DESC_PTR_TBL_KER_A1 0x11800 +#define RX_DESC_PTR_TBL_KER_B0 0xF40000 +#define RX_DESC_PTR_TBL_KER_P0 0x900 +#define RX_ISCSI_DDIG_EN_LBN 88 +#define RX_ISCSI_DDIG_EN_WIDTH 1 +#define RX_ISCSI_HDIG_EN_LBN 87 +#define RX_ISCSI_HDIG_EN_WIDTH 1 +#define RX_DESC_PREF_ACT_LBN 86 +#define RX_DESC_PREF_ACT_WIDTH 1 +#define RX_DC_HW_RPTR_LBN 80 +#define RX_DC_HW_RPTR_WIDTH 6 +#define RX_DESCQ_HW_RPTR_LBN 68 +#define RX_DESCQ_HW_RPTR_WIDTH 12 +#define RX_DESCQ_SW_WPTR_LBN 56 +#define RX_DESCQ_SW_WPTR_WIDTH 12 +#define RX_DESCQ_BUF_BASE_ID_LBN 36 +#define RX_DESCQ_BUF_BASE_ID_WIDTH 20 +#define RX_DESCQ_EVQ_ID_LBN 24 +#define RX_DESCQ_EVQ_ID_WIDTH 12 +#define RX_DESCQ_OWNER_ID_LBN 10 +#define RX_DESCQ_OWNER_ID_WIDTH 14 +#define RX_DESCQ_LABEL_LBN 5 +#define RX_DESCQ_LABEL_WIDTH 5 +#define RX_DESCQ_SIZE_LBN 3 +#define RX_DESCQ_SIZE_WIDTH 2 +#define RX_DESCQ_SIZE_4K 3 +#define RX_DESCQ_SIZE_2K 2 +#define RX_DESCQ_SIZE_1K 1 +#define RX_DESCQ_SIZE_512 0 +#define RX_DESCQ_TYPE_LBN 2 +#define RX_DESCQ_TYPE_WIDTH 1 +#define RX_DESCQ_JUMBO_LBN 1 +#define RX_DESCQ_JUMBO_WIDTH 1 +#define RX_DESCQ_EN_LBN 0 +#define RX_DESCQ_EN_WIDTH 1 + +/* Transmit descriptor pointer table */ +#define TX_DESC_PTR_TBL_KER_A1 0x11900 +#define TX_DESC_PTR_TBL_KER_B0 0xF50000 +#define TX_DESC_PTR_TBL_KER_P0 0xa40 +#define TX_NON_IP_DROP_DIS_B0_LBN 91 +#define TX_NON_IP_DROP_DIS_B0_WIDTH 1 +#define TX_IP_CHKSM_DIS_B0_LBN 90 +#define TX_IP_CHKSM_DIS_B0_WIDTH 1 +#define TX_TCP_CHKSM_DIS_B0_LBN 89 +#define TX_TCP_CHKSM_DIS_B0_WIDTH 1 +#define TX_DESCQ_EN_LBN 88 +#define TX_DESCQ_EN_WIDTH 1 +#define TX_ISCSI_DDIG_EN_LBN 87 +#define TX_ISCSI_DDIG_EN_WIDTH 1 +#define TX_ISCSI_HDIG_EN_LBN 86 +#define TX_ISCSI_HDIG_EN_WIDTH 1 +#define TX_DC_HW_RPTR_LBN 80 +#define TX_DC_HW_RPTR_WIDTH 6 +#define TX_DESCQ_HW_RPTR_LBN 68 +#define TX_DESCQ_HW_RPTR_WIDTH 12 +#define TX_DESCQ_SW_WPTR_LBN 56 +#define TX_DESCQ_SW_WPTR_WIDTH 12 +#define TX_DESCQ_BUF_BASE_ID_LBN 36 +#define TX_DESCQ_BUF_BASE_ID_WIDTH 20 +#define TX_DESCQ_EVQ_ID_LBN 24 +#define TX_DESCQ_EVQ_ID_WIDTH 12 +#define TX_DESCQ_OWNER_ID_LBN 10 +#define TX_DESCQ_OWNER_ID_WIDTH 14 +#define TX_DESCQ_LABEL_LBN 5 +#define TX_DESCQ_LABEL_WIDTH 5 +#define TX_DESCQ_SIZE_LBN 3 +#define TX_DESCQ_SIZE_WIDTH 2 +#define TX_DESCQ_SIZE_4K 3 +#define TX_DESCQ_SIZE_2K 2 +#define TX_DESCQ_SIZE_1K 1 +#define TX_DESCQ_SIZE_512 0 +#define TX_DESCQ_TYPE_LBN 1 +#define TX_DESCQ_TYPE_WIDTH 2 +#define TX_DESCQ_FLUSH_LBN 0 +#define TX_DESCQ_FLUSH_WIDTH 1 + +/* Event queue pointer */ +#define EVQ_PTR_TBL_KER_A1 0x11a00 +#define EVQ_PTR_TBL_KER_B0 0xf60000 +#define EVQ_PTR_TBL_KER_P0 0x500 +#define EVQ_WKUP_OR_INT_EN_LBN 39 +#define EVQ_WKUP_OR_INT_EN_WIDTH 1 +#define EVQ_NXT_WPTR_LBN 24 +#define EVQ_NXT_WPTR_WIDTH 15 +#define EVQ_EN_LBN 23 +#define EVQ_EN_WIDTH 1 +#define EVQ_SIZE_LBN 20 +#define EVQ_SIZE_WIDTH 3 +#define EVQ_SIZE_32K 6 +#define EVQ_SIZE_16K 5 +#define EVQ_SIZE_8K 4 +#define EVQ_SIZE_4K 3 +#define EVQ_SIZE_2K 2 +#define EVQ_SIZE_1K 1 +#define EVQ_SIZE_512 0 +#define EVQ_BUF_BASE_ID_LBN 0 +#define EVQ_BUF_BASE_ID_WIDTH 20 + +/* Event queue read pointer */ +#define EVQ_RPTR_REG_KER_A1 0x11b00 +#define EVQ_RPTR_REG_KER_B0 0xfa0000 +#define EVQ_RPTR_LBN 0 +#define EVQ_RPTR_WIDTH 14 +#define EVQ_RPTR_REG_KER_DWORD (EVQ_RPTR_REG_KER + 0) +#define EVQ_RPTR_DWORD_LBN 0 +#define EVQ_RPTR_DWORD_WIDTH 14 + +/* RSS indirection table */ +#define RX_RSS_INDIR_TBL_B0 0xFB0000 +#define RX_RSS_INDIR_ENT_B0_LBN 0 +#define RX_RSS_INDIR_ENT_B0_WIDTH 6 + +/* Special buffer descriptors (full-mode) */ +#define BUF_FULL_TBL_KER_A1 0x8000 +#define BUF_FULL_TBL_KER_B0 0x800000 +#define IP_DAT_BUF_SIZE_LBN 50 +#define IP_DAT_BUF_SIZE_WIDTH 1 +#define IP_DAT_BUF_SIZE_8K 1 +#define IP_DAT_BUF_SIZE_4K 0 +#define BUF_ADR_REGION_LBN 48 +#define BUF_ADR_REGION_WIDTH 2 +#define BUF_ADR_FBUF_LBN 14 +#define BUF_ADR_FBUF_WIDTH 34 +#define BUF_OWNER_ID_FBUF_LBN 0 +#define BUF_OWNER_ID_FBUF_WIDTH 14 + +/* Special buffer descriptors (half-mode) */ +#define BUF_HALF_TBL_KER_A1 0x8000 +#define BUF_HALF_TBL_KER_B0 0x800000 +#define BUF_ADR_HBUF_ODD_LBN 44 +#define BUF_ADR_HBUF_ODD_WIDTH 20 +#define BUF_OWNER_ID_HBUF_ODD_LBN 32 +#define BUF_OWNER_ID_HBUF_ODD_WIDTH 12 +#define BUF_ADR_HBUF_EVEN_LBN 12 +#define BUF_ADR_HBUF_EVEN_WIDTH 20 +#define BUF_OWNER_ID_HBUF_EVEN_LBN 0 +#define BUF_OWNER_ID_HBUF_EVEN_WIDTH 12 + +#define SRM_DBG_REG_B0 0x3000000 + +/* Transmit descriptor */ +#define TX_KER_PORT_LBN 63 +#define TX_KER_PORT_WIDTH 1 +#define TX_KER_CONT_LBN 62 +#define TX_KER_CONT_WIDTH 1 +#define TX_KER_BYTE_CNT_LBN 48 +#define TX_KER_BYTE_CNT_WIDTH 14 +#define TX_KER_BUF_REGION_LBN 46 +#define TX_KER_BUF_REGION_WIDTH 2 +#define TX_KER_BUF_REGION0_DECODE 0 +#define TX_KER_BUF_REGION1_DECODE 1 +#define TX_KER_BUF_REGION2_DECODE 2 +#define TX_KER_BUF_REGION3_DECODE 3 +#define TX_KER_BUF_ADR_LBN 0 +#define TX_KER_BUF_ADR_WIDTH EFX_DMA_TYPE_WIDTH(46) + +/* Receive descriptor */ +#define RX_KER_BUF_SIZE_LBN 48 +#define RX_KER_BUF_SIZE_WIDTH 14 +#define RX_KER_BUF_REGION_LBN 46 +#define RX_KER_BUF_REGION_WIDTH 2 +#define RX_KER_BUF_REGION0_DECODE 0 +#define RX_KER_BUF_REGION1_DECODE 1 +#define RX_KER_BUF_REGION2_DECODE 2 +#define RX_KER_BUF_REGION3_DECODE 3 +#define RX_KER_BUF_ADR_LBN 0 +#define RX_KER_BUF_ADR_WIDTH EFX_DMA_TYPE_WIDTH(46) + +/************************************************************************** + * + * Falcon events + * + ************************************************************************** + */ + +/* Event queue entries */ +#define EV_CODE_LBN 60 +#define EV_CODE_WIDTH 4 +#define RX_IP_EV_DECODE 0 +#define TX_IP_EV_DECODE 2 +#define DRIVER_EV_DECODE 5 +#define GLOBAL_EV_DECODE 6 +#define DRV_GEN_EV_DECODE 7 +#define WHOLE_EVENT_LBN 0 +#define WHOLE_EVENT_WIDTH 64 + +/* Receive events */ +#define RX_EV_PKT_OK_LBN 56 +#define RX_EV_PKT_OK_WIDTH 1 +#define RX_EV_PAUSE_FRM_ERR_LBN 55 +#define RX_EV_PAUSE_FRM_ERR_WIDTH 1 +#define RX_EV_BUF_OWNER_ID_ERR_LBN 54 +#define RX_EV_BUF_OWNER_ID_ERR_WIDTH 1 +#define RX_EV_IF_FRAG_ERR_LBN 53 +#define RX_EV_IF_FRAG_ERR_WIDTH 1 +#define RX_EV_IP_HDR_CHKSUM_ERR_LBN 52 +#define RX_EV_IP_HDR_CHKSUM_ERR_WIDTH 1 +#define RX_EV_TCP_UDP_CHKSUM_ERR_LBN 51 +#define RX_EV_TCP_UDP_CHKSUM_ERR_WIDTH 1 +#define RX_EV_ETH_CRC_ERR_LBN 50 +#define RX_EV_ETH_CRC_ERR_WIDTH 1 +#define RX_EV_FRM_TRUNC_LBN 49 +#define RX_EV_FRM_TRUNC_WIDTH 1 +#define RX_EV_DRIB_NIB_LBN 48 +#define RX_EV_DRIB_NIB_WIDTH 1 +#define RX_EV_TOBE_DISC_LBN 47 +#define RX_EV_TOBE_DISC_WIDTH 1 +#define RX_EV_PKT_TYPE_LBN 44 +#define RX_EV_PKT_TYPE_WIDTH 3 +#define RX_EV_PKT_TYPE_ETH_DECODE 0 +#define RX_EV_PKT_TYPE_LLC_DECODE 1 +#define RX_EV_PKT_TYPE_JUMBO_DECODE 2 +#define RX_EV_PKT_TYPE_VLAN_DECODE 3 +#define RX_EV_PKT_TYPE_VLAN_LLC_DECODE 4 +#define RX_EV_PKT_TYPE_VLAN_JUMBO_DECODE 5 +#define RX_EV_HDR_TYPE_LBN 42 +#define RX_EV_HDR_TYPE_WIDTH 2 +#define RX_EV_HDR_TYPE_TCP_IPV4_DECODE 0 +#define RX_EV_HDR_TYPE_UDP_IPV4_DECODE 1 +#define RX_EV_HDR_TYPE_OTHER_IP_DECODE 2 +#define RX_EV_HDR_TYPE_NON_IP_DECODE 3 +#define RX_EV_HDR_TYPE_HAS_CHECKSUMS(hdr_type) \ + ((hdr_type) <= RX_EV_HDR_TYPE_UDP_IPV4_DECODE) +#define RX_EV_DESC_Q_EMPTY_LBN 41 +#define RX_EV_DESC_Q_EMPTY_WIDTH 1 +#define RX_EV_MCAST_HASH_MATCH_LBN 40 +#define RX_EV_MCAST_HASH_MATCH_WIDTH 1 +#define RX_EV_MCAST_PKT_LBN 39 +#define RX_EV_MCAST_PKT_WIDTH 1 +#define RX_EV_RECOVERY_FLAG_LBN 37 +#define RX_EV_RECOVERY_FLAG_WIDTH 1 +#define RX_EV_Q_LABEL_LBN 32 +#define RX_EV_Q_LABEL_WIDTH 5 +#define RX_EV_JUMBO_CONT_LBN 31 +#define RX_EV_JUMBO_CONT_WIDTH 1 +#define RX_EV_PORT_LBN 30 +#define RX_EV_PORT_WIDTH 1 +#define RX_EV_BYTE_CNT_LBN 16 +#define RX_EV_BYTE_CNT_WIDTH 14 +#define RX_EV_SOP_LBN 15 +#define RX_EV_SOP_WIDTH 1 +#define RX_ISCSI_DDIG_ERR_LBN 13 +#define RX_ISCSI_DDIG_ERR_WIDTH 1 +#define RX_ISCSI_HDIG_ERR_LBN 12 +#define RX_ISCSI_HDIG_ERR_WIDTH 1 +#define RX_EV_DESC_PTR_LBN 0 +#define RX_EV_DESC_PTR_WIDTH 12 + +/* Transmit events */ +#define TX_EV_PKT_TOO_BIG_LBN 37 +#define TX_EV_PKT_TOO_BIG_WIDTH 1 +#define TX_EV_Q_LABEL_LBN 32 +#define TX_EV_Q_LABEL_WIDTH 5 +#define TX_EV_PORT_LBN 16 +#define TX_EV_PORT_WIDTH 1 +#define TX_EV_WQ_FF_FULL_LBN 15 +#define TX_EV_WQ_FF_FULL_WIDTH 1 +#define TX_EV_BUF_OWNER_ID_ERR_LBN 14 +#define TX_EV_BUF_OWNER_ID_ERR_WIDTH 1 +#define TX_EV_COMP_LBN 12 +#define TX_EV_COMP_WIDTH 1 +#define TX_EV_DESC_PTR_LBN 0 +#define TX_EV_DESC_PTR_WIDTH 12 + +/* Driver events */ +#define DRIVER_EV_SUB_CODE_LBN 56 +#define DRIVER_EV_SUB_CODE_WIDTH 4 +#define DRIVER_EV_SUB_DATA_LBN 0 +#define DRIVER_EV_SUB_DATA_WIDTH 14 +#define TX_DESCQ_FLS_DONE_EV_DECODE 0 +#define RX_DESCQ_FLS_DONE_EV_DECODE 1 +#define EVQ_INIT_DONE_EV_DECODE 2 +#define EVQ_NOT_EN_EV_DECODE 3 +#define RX_DESCQ_FLSFF_OVFL_EV_DECODE 4 +#define SRM_UPD_DONE_EV_DECODE 5 +#define WAKE_UP_EV_DECODE 6 +#define TX_PKT_NON_TCP_UDP_DECODE 9 +#define TIMER_EV_DECODE 10 +#define RX_RECOVERY_EV_DECODE 11 +#define RX_DSC_ERROR_EV_DECODE 14 +#define TX_DSC_ERROR_EV_DECODE 15 +#define DRIVER_EV_TX_DESCQ_ID_LBN 0 +#define DRIVER_EV_TX_DESCQ_ID_WIDTH 12 +#define DRIVER_EV_RX_FLUSH_FAIL_LBN 12 +#define DRIVER_EV_RX_FLUSH_FAIL_WIDTH 1 +#define DRIVER_EV_RX_DESCQ_ID_LBN 0 +#define DRIVER_EV_RX_DESCQ_ID_WIDTH 12 +#define DRIVER_EV_EVQ_ID_LBN 0 +#define DRIVER_EV_EVQ_ID_WIDTH 12 +#define DRIVER_EV_SRM_UPD_LBN 0 +#define DRIVER_EV_SRM_UPD_WIDTH 2 +#define SRM_CLR_EV_DECODE 0 +#define SRM_UPD_EV_DECODE 1 +#define SRM_ILLCLR_EV_DECODE 2 + +/* Global events */ +#define RX_RECOVERY_B0_LBN 12 +#define RX_RECOVERY_B0_WIDTH 1 +#define XG_MNT_INTR_B0_LBN 11 +#define XG_MNT_INTR_B0_WIDTH 1 + +#define RX_RECOVERY_A1_LBN 11 +#define RX_RECOVERY_A1_WIDTH 1 + +#define XFP_PHY_INTR_LBN 10 +#define XFP_PHY_INTR_WIDTH 1 +#define XG_PHY_INTR_LBN 9 +#define XG_PHY_INTR_WIDTH 1 +#define G_PHY1_INTR_LBN 8 +#define G_PHY1_INTR_WIDTH 1 +#define G_PHY0_INTR_LBN 7 +#define G_PHY0_INTR_WIDTH 1 + +/* Driver-generated test events */ +#define DRV_GEN_EV_CODE_LBN 60 +#define DRV_GEN_EV_CODE_WIDTH 4 +#define DRV_GEN_EV_DATA_LBN 0 +#define DRV_GEN_EV_DATA_WIDTH 60 +#define EVQ_MAGIC_LBN 0 +#define EVQ_MAGIC_WIDTH 32 + +/************************************************************************** + * + * Falcon MAC stats + * + ************************************************************************** + * + */ + +#define GRxGoodOct_offset 0x0 +#define GRxGoodOct_WIDTH 48 +#define GRxBadOct_offset 0x8 +#define GRxBadOct_WIDTH 48 +#define GRxMissPkt_offset 0x10 +#define GRxMissPkt_WIDTH 32 +#define GRxFalseCRS_offset 0x14 +#define GRxFalseCRS_WIDTH 32 +#define GRxPausePkt_offset 0x18 +#define GRxPausePkt_WIDTH 32 +#define GRxBadPkt_offset 0x1C +#define GRxBadPkt_WIDTH 32 +#define GRxUcastPkt_offset 0x20 +#define GRxUcastPkt_WIDTH 32 +#define GRxMcastPkt_offset 0x24 +#define GRxMcastPkt_WIDTH 32 +#define GRxBcastPkt_offset 0x28 +#define GRxBcastPkt_WIDTH 32 +#define GRxGoodLt64Pkt_offset 0x2C +#define GRxGoodLt64Pkt_WIDTH 32 +#define GRxBadLt64Pkt_offset 0x30 +#define GRxBadLt64Pkt_WIDTH 32 +#define GRx64Pkt_offset 0x34 +#define GRx64Pkt_WIDTH 32 +#define GRx65to127Pkt_offset 0x38 +#define GRx65to127Pkt_WIDTH 32 +#define GRx128to255Pkt_offset 0x3C +#define GRx128to255Pkt_WIDTH 32 +#define GRx256to511Pkt_offset 0x40 +#define GRx256to511Pkt_WIDTH 32 +#define GRx512to1023Pkt_offset 0x44 +#define GRx512to1023Pkt_WIDTH 32 +#define GRx1024to15xxPkt_offset 0x48 +#define GRx1024to15xxPkt_WIDTH 32 +#define GRx15xxtoJumboPkt_offset 0x4C +#define GRx15xxtoJumboPkt_WIDTH 32 +#define GRxGtJumboPkt_offset 0x50 +#define GRxGtJumboPkt_WIDTH 32 +#define GRxFcsErr64to15xxPkt_offset 0x54 +#define GRxFcsErr64to15xxPkt_WIDTH 32 +#define GRxFcsErr15xxtoJumboPkt_offset 0x58 +#define GRxFcsErr15xxtoJumboPkt_WIDTH 32 +#define GRxFcsErrGtJumboPkt_offset 0x5C +#define GRxFcsErrGtJumboPkt_WIDTH 32 +#define GTxGoodBadOct_offset 0x80 +#define GTxGoodBadOct_WIDTH 48 +#define GTxGoodOct_offset 0x88 +#define GTxGoodOct_WIDTH 48 +#define GTxSglColPkt_offset 0x90 +#define GTxSglColPkt_WIDTH 32 +#define GTxMultColPkt_offset 0x94 +#define GTxMultColPkt_WIDTH 32 +#define GTxExColPkt_offset 0x98 +#define GTxExColPkt_WIDTH 32 +#define GTxDefPkt_offset 0x9C +#define GTxDefPkt_WIDTH 32 +#define GTxLateCol_offset 0xA0 +#define GTxLateCol_WIDTH 32 +#define GTxExDefPkt_offset 0xA4 +#define GTxExDefPkt_WIDTH 32 +#define GTxPausePkt_offset 0xA8 +#define GTxPausePkt_WIDTH 32 +#define GTxBadPkt_offset 0xAC +#define GTxBadPkt_WIDTH 32 +#define GTxUcastPkt_offset 0xB0 +#define GTxUcastPkt_WIDTH 32 +#define GTxMcastPkt_offset 0xB4 +#define GTxMcastPkt_WIDTH 32 +#define GTxBcastPkt_offset 0xB8 +#define GTxBcastPkt_WIDTH 32 +#define GTxLt64Pkt_offset 0xBC +#define GTxLt64Pkt_WIDTH 32 +#define GTx64Pkt_offset 0xC0 +#define GTx64Pkt_WIDTH 32 +#define GTx65to127Pkt_offset 0xC4 +#define GTx65to127Pkt_WIDTH 32 +#define GTx128to255Pkt_offset 0xC8 +#define GTx128to255Pkt_WIDTH 32 +#define GTx256to511Pkt_offset 0xCC +#define GTx256to511Pkt_WIDTH 32 +#define GTx512to1023Pkt_offset 0xD0 +#define GTx512to1023Pkt_WIDTH 32 +#define GTx1024to15xxPkt_offset 0xD4 +#define GTx1024to15xxPkt_WIDTH 32 +#define GTx15xxtoJumboPkt_offset 0xD8 +#define GTx15xxtoJumboPkt_WIDTH 32 +#define GTxGtJumboPkt_offset 0xDC +#define GTxGtJumboPkt_WIDTH 32 +#define GTxNonTcpUdpPkt_offset 0xE0 +#define GTxNonTcpUdpPkt_WIDTH 16 +#define GTxMacSrcErrPkt_offset 0xE4 +#define GTxMacSrcErrPkt_WIDTH 16 +#define GTxIpSrcErrPkt_offset 0xE8 +#define GTxIpSrcErrPkt_WIDTH 16 +#define GDmaDone_offset 0xEC +#define GDmaDone_WIDTH 32 + +#define XgRxOctets_offset 0x0 +#define XgRxOctets_WIDTH 48 +#define XgRxOctetsOK_offset 0x8 +#define XgRxOctetsOK_WIDTH 48 +#define XgRxPkts_offset 0x10 +#define XgRxPkts_WIDTH 32 +#define XgRxPktsOK_offset 0x14 +#define XgRxPktsOK_WIDTH 32 +#define XgRxBroadcastPkts_offset 0x18 +#define XgRxBroadcastPkts_WIDTH 32 +#define XgRxMulticastPkts_offset 0x1C +#define XgRxMulticastPkts_WIDTH 32 +#define XgRxUnicastPkts_offset 0x20 +#define XgRxUnicastPkts_WIDTH 32 +#define XgRxUndersizePkts_offset 0x24 +#define XgRxUndersizePkts_WIDTH 32 +#define XgRxOversizePkts_offset 0x28 +#define XgRxOversizePkts_WIDTH 32 +#define XgRxJabberPkts_offset 0x2C +#define XgRxJabberPkts_WIDTH 32 +#define XgRxUndersizeFCSerrorPkts_offset 0x30 +#define XgRxUndersizeFCSerrorPkts_WIDTH 32 +#define XgRxDropEvents_offset 0x34 +#define XgRxDropEvents_WIDTH 32 +#define XgRxFCSerrorPkts_offset 0x38 +#define XgRxFCSerrorPkts_WIDTH 32 +#define XgRxAlignError_offset 0x3C +#define XgRxAlignError_WIDTH 32 +#define XgRxSymbolError_offset 0x40 +#define XgRxSymbolError_WIDTH 32 +#define XgRxInternalMACError_offset 0x44 +#define XgRxInternalMACError_WIDTH 32 +#define XgRxControlPkts_offset 0x48 +#define XgRxControlPkts_WIDTH 32 +#define XgRxPausePkts_offset 0x4C +#define XgRxPausePkts_WIDTH 32 +#define XgRxPkts64Octets_offset 0x50 +#define XgRxPkts64Octets_WIDTH 32 +#define XgRxPkts65to127Octets_offset 0x54 +#define XgRxPkts65to127Octets_WIDTH 32 +#define XgRxPkts128to255Octets_offset 0x58 +#define XgRxPkts128to255Octets_WIDTH 32 +#define XgRxPkts256to511Octets_offset 0x5C +#define XgRxPkts256to511Octets_WIDTH 32 +#define XgRxPkts512to1023Octets_offset 0x60 +#define XgRxPkts512to1023Octets_WIDTH 32 +#define XgRxPkts1024to15xxOctets_offset 0x64 +#define XgRxPkts1024to15xxOctets_WIDTH 32 +#define XgRxPkts15xxtoMaxOctets_offset 0x68 +#define XgRxPkts15xxtoMaxOctets_WIDTH 32 +#define XgRxLengthError_offset 0x6C +#define XgRxLengthError_WIDTH 32 +#define XgTxPkts_offset 0x80 +#define XgTxPkts_WIDTH 32 +#define XgTxOctets_offset 0x88 +#define XgTxOctets_WIDTH 48 +#define XgTxMulticastPkts_offset 0x90 +#define XgTxMulticastPkts_WIDTH 32 +#define XgTxBroadcastPkts_offset 0x94 +#define XgTxBroadcastPkts_WIDTH 32 +#define XgTxUnicastPkts_offset 0x98 +#define XgTxUnicastPkts_WIDTH 32 +#define XgTxControlPkts_offset 0x9C +#define XgTxControlPkts_WIDTH 32 +#define XgTxPausePkts_offset 0xA0 +#define XgTxPausePkts_WIDTH 32 +#define XgTxPkts64Octets_offset 0xA4 +#define XgTxPkts64Octets_WIDTH 32 +#define XgTxPkts65to127Octets_offset 0xA8 +#define XgTxPkts65to127Octets_WIDTH 32 +#define XgTxPkts128to255Octets_offset 0xAC +#define XgTxPkts128to255Octets_WIDTH 32 +#define XgTxPkts256to511Octets_offset 0xB0 +#define XgTxPkts256to511Octets_WIDTH 32 +#define XgTxPkts512to1023Octets_offset 0xB4 +#define XgTxPkts512to1023Octets_WIDTH 32 +#define XgTxPkts1024to15xxOctets_offset 0xB8 +#define XgTxPkts1024to15xxOctets_WIDTH 32 +#define XgTxPkts1519toMaxOctets_offset 0xBC +#define XgTxPkts1519toMaxOctets_WIDTH 32 +#define XgTxUndersizePkts_offset 0xC0 +#define XgTxUndersizePkts_WIDTH 32 +#define XgTxOversizePkts_offset 0xC4 +#define XgTxOversizePkts_WIDTH 32 +#define XgTxNonTcpUdpPkt_offset 0xC8 +#define XgTxNonTcpUdpPkt_WIDTH 16 +#define XgTxMacSrcErrPkt_offset 0xCC +#define XgTxMacSrcErrPkt_WIDTH 16 +#define XgTxIpSrcErrPkt_offset 0xD0 +#define XgTxIpSrcErrPkt_WIDTH 16 +#define XgDmaDone_offset 0xD4 +#define XgDmaDone_WIDTH 32 + +#define FALCON_STATS_NOT_DONE 0x00000000 +#define FALCON_STATS_DONE 0xffffffff + +/* Interrupt status register bits */ +#define FATAL_INT_LBN 64 +#define FATAL_INT_WIDTH 1 +#define INT_EVQS_LBN 40 +#define INT_EVQS_WIDTH 4 +#define INT_FLAG_LBN 32 +#define INT_FLAG_WIDTH 1 +#define EVQ_FIFO_HF_LBN 1 +#define EVQ_FIFO_HF_WIDTH 1 +#define EVQ_FIFO_AF_LBN 0 +#define EVQ_FIFO_AF_WIDTH 1 + +/************************************************************************** + * + * Falcon non-volatile configuration + * + ************************************************************************** + */ + +/* Board configuration v2 (v1 is obsolete; later versions are compatible) */ +struct falcon_nvconfig_board_v2 { + u16 nports; + u8 port0_phy_addr; + u8 port0_phy_type; + u8 port1_phy_addr; + u8 port1_phy_type; + u16 asic_sub_revision; + u16 board_revision; +} __attribute__ ((packed)); + +/* Board configuration v3 extra information */ +struct falcon_nvconfig_board_v3 { + u32 spi_device_type[2]; +} __attribute__ ((packed)); + +/* Bit numbers for spi_device_type */ +#define SPI_DEV_TYPE_SIZE_LBN 0 +#define SPI_DEV_TYPE_SIZE_WIDTH 5 +#define SPI_DEV_TYPE_ADDR_LEN_LBN 6 +#define SPI_DEV_TYPE_ADDR_LEN_WIDTH 2 +#define SPI_DEV_TYPE_ERASE_CMD_LBN 8 +#define SPI_DEV_TYPE_ERASE_CMD_WIDTH 8 +#define SPI_DEV_TYPE_ERASE_SIZE_LBN 16 +#define SPI_DEV_TYPE_ERASE_SIZE_WIDTH 5 +#define SPI_DEV_TYPE_BLOCK_SIZE_LBN 24 +#define SPI_DEV_TYPE_BLOCK_SIZE_WIDTH 5 +#define SPI_DEV_TYPE_FIELD(type, field) \ + (((type) >> EFX_LOW_BIT(field)) & EFX_MASK32(field)) + +#define NVCONFIG_BASE 0x300 +#define NVCONFIG_BOARD_MAGIC_NUM 0xFA1C +struct falcon_nvconfig { + efx_oword_t ee_vpd_cfg_reg; /* 0x300 */ + u8 mac_address[2][8]; /* 0x310 */ + efx_oword_t pcie_sd_ctl0123_reg; /* 0x320 */ + efx_oword_t pcie_sd_ctl45_reg; /* 0x330 */ + efx_oword_t pcie_pcs_ctl_stat_reg; /* 0x340 */ + efx_oword_t hw_init_reg; /* 0x350 */ + efx_oword_t nic_stat_reg; /* 0x360 */ + efx_oword_t glb_ctl_reg; /* 0x370 */ + efx_oword_t srm_cfg_reg; /* 0x380 */ + efx_oword_t spare_reg; /* 0x390 */ + u16 board_magic_num; /* 0x3A0 */ + u16 board_struct_ver; + u16 board_checksum; + struct falcon_nvconfig_board_v2 board_v2; + efx_oword_t ee_base_page_reg; /* 0x3B0 */ + struct falcon_nvconfig_board_v3 board_v3; /* 0x3C0 */ +} __attribute__ ((packed)); + +#endif /* EFX_FALCON_HWDEFS_H */ Index: head-2008-03-17/drivers/net/sfc/falcon_io.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/falcon_io.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,250 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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_FALCON_IO_H +#define EFX_FALCON_IO_H + +#include "net_driver.h" +#include "falcon.h" + +/************************************************************************** + * + * Falcon hardware access + * + ************************************************************************** + * + * Notes on locking strategy: + * + * Most Falcon registers require 16-byte (or 8-byte, for SRAM + * registers) atomic writes which necessitates locking. + * Under normal operation few writes to the Falcon BAR are made and these + * registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and TX_DESC_UPD_REG) are special + * cased to allow 4-byte (hence lockless) accesses. + * + * It *is* safe to write to these 4-byte registers in the middle of an + * access to an 8-byte or 16-byte register. We therefore use a + * spinlock to protect accesses to the larger registers, but no locks + * for the 4-byte registers. + * + * A write barrier is needed to ensure that DW3 is written after DW0/1/2 + * due to the way the 16byte registers are "collected" in the Falcon BIU + * + * We also lock when carrying out reads, to ensure consistency of the + * data (made possible since the BIU reads all 128 bits into a cache). + * Reads are very rare, so this isn't a significant performance + * impact. (Most data transferred from NIC to host is DMAed directly + * into host memory). + * + * I/O BAR access uses locks for both reads and writes (but is only provided + * for testing purposes). + */ + +/* Special buffer descriptors (Falcon SRAM) */ +#define BUF_TBL_KER_A1 0x18000 +#define BUF_TBL_KER_B0 0x800000 + + +#if BITS_PER_LONG == 64 +#define FALCON_USE_QWORD_IO 1 +#endif + +#define _falcon_writeq(efx, value, reg) \ + __raw_writeq((value), (efx)->membase + (reg)) +#define _falcon_writel(efx, value, reg) \ + __raw_writel((value), (efx)->membase + (reg)) +#define _falcon_readq(efx, reg) __raw_readq((efx)->membase + (reg)) +#define _falcon_readl(efx, reg) __raw_readl((efx)->membase + (reg)) + +/* Writes to a normal 16-byte Falcon register, locking as appropriate. */ +static inline void falcon_write(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg) +{ + unsigned long flags __attribute__ ((unused)); + + EFX_REGDUMP(efx, "writing register %x with " EFX_OWORD_FMT "\n", reg, + EFX_OWORD_VAL(*value)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef FALCON_USE_QWORD_IO + _falcon_writeq(efx, value->u64[0], reg + 0); + wmb(); + _falcon_writeq(efx, value->u64[1], reg + 8); +#else + _falcon_writel(efx, value->u32[0], reg + 0); + _falcon_writel(efx, value->u32[1], reg + 4); + _falcon_writel(efx, value->u32[2], reg + 8); + wmb(); + _falcon_writel(efx, value->u32[3], reg + 12); +#endif + mmiowb(); + spin_unlock_irqrestore(&efx->biu_lock, flags); +} + +/* Writes to an 8-byte Falcon SRAM register, locking as appropriate. */ +static inline void falcon_write_sram(struct efx_nic *efx, efx_qword_t *value, + unsigned int index) +{ + unsigned int reg = efx->type->buf_tbl_base + (index * sizeof(*value)); + unsigned long flags __attribute__ ((unused)); + + EFX_REGDUMP(efx, "writing SRAM register %x with " EFX_QWORD_FMT "\n", + reg, EFX_QWORD_VAL(*value)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef FALCON_USE_QWORD_IO + _falcon_writeq(efx, value->u64[0], reg + 0); +#else + _falcon_writel(efx, value->u32[0], reg + 0); + wmb(); + _falcon_writel(efx, value->u32[1], reg + 4); +#endif + mmiowb(); + spin_unlock_irqrestore(&efx->biu_lock, flags); +} + +/* Write dword to Falcon register that allows partial writes + * + * Some Falcon registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and + * TX_DESC_UPD_REG) can be written to as a single dword. This allows + * for lockless writes. + */ +static inline void falcon_writel(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg) +{ + EFX_REGDUMP(efx, "writing partial register %x with "EFX_DWORD_FMT"\n", + reg, EFX_DWORD_VAL(*value)); + + /* No lock required */ + _falcon_writel(efx, value->u32[0], reg); +} + +/* Read from a Falcon register + * + * This reads an entire 16-byte Falcon register in one go, locking as + * appropriate. It is essential to read the first dword first, as this + * prompts Falcon to load the current value into the shadow register. + */ +static inline void falcon_read(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg) +{ + unsigned long flags __attribute__ ((unused)); + + spin_lock_irqsave(&efx->biu_lock, flags); + value->u32[0] = _falcon_readl(efx, reg + 0); + rmb(); + value->u32[1] = _falcon_readl(efx, reg + 4); + value->u32[2] = _falcon_readl(efx, reg + 8); + value->u32[3] = _falcon_readl(efx, reg + 12); + spin_unlock_irqrestore(&efx->biu_lock, flags); + + EFX_REGDUMP(efx, "read from register %x, got " EFX_OWORD_FMT "\n", reg, + EFX_OWORD_VAL(*value)); +} + +/* This reads an 8-byte Falcon SRAM entry in one go. */ +static inline void falcon_read_sram(struct efx_nic *efx, efx_qword_t *value, + unsigned int index) +{ + unsigned int reg = efx->type->buf_tbl_base + (index * sizeof(*value)); + unsigned long flags __attribute__ ((unused)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef FALCON_USE_QWORD_IO + value->u64[0] = _falcon_readq(efx, reg + 0); +#else + value->u32[0] = _falcon_readl(efx, reg + 0); + rmb(); + value->u32[1] = _falcon_readl(efx, reg + 4); +#endif + spin_unlock_irqrestore(&efx->biu_lock, flags); + + EFX_REGDUMP(efx, "read from SRAM register %x, got "EFX_QWORD_FMT"\n", + reg, EFX_QWORD_VAL(*value)); +} + +/* Read dword from Falcon register that allows partial writes (sic) */ +static inline void falcon_readl(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg) +{ + value->u32[0] = _falcon_readl(efx, reg); + EFX_REGDUMP(efx, "read from register %x, got "EFX_DWORD_FMT"\n", + reg, EFX_DWORD_VAL(*value)); +} + +/* Write to a register forming part of a table */ +static inline void falcon_write_table(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg, unsigned int index) +{ + falcon_write(efx, value, reg + index * sizeof(efx_oword_t)); +} + +/* Write to a dword register forming part of a table */ +static inline void falcon_writel_table(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg, unsigned int index) +{ + falcon_writel(efx, value, reg + index * sizeof(efx_oword_t)); +} + +/* Page-mapped register block size */ +#define FALCON_PAGE_BLOCK_SIZE 0x2000 + +/* Calculate offset to page-mapped register block */ +#define FALCON_PAGED_REG(page, reg) \ + ((page) * FALCON_PAGE_BLOCK_SIZE + (reg)) + +/* As for falcon_write(), but for a page-mapped register. */ +static inline void falcon_write_page(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg, unsigned int page) +{ + falcon_write(efx, value, FALCON_PAGED_REG(page, reg)); +} + +/* As for falcon_writel(), but for a page-mapped register. */ +static inline void falcon_writel_page(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg, unsigned int page) +{ + falcon_writel(efx, value, FALCON_PAGED_REG(page, reg)); +} + +/* Write dword to Falcon page-mapped register with an extra lock. + * + * As for falcon_writel_page(), but for a register that suffers from + * SFC bug 3181. Take out a lock so the BIU collector cannot be + * confused. */ +static inline void falcon_writel_page_locked(struct efx_nic *efx, + efx_dword_t *value, + unsigned int reg, + unsigned int page) +{ + unsigned long flags __attribute__ ((unused)); + + spin_lock_irqsave(&efx->biu_lock, flags); + falcon_writel(efx, value, FALCON_PAGED_REG(page, reg)); + spin_unlock_irqrestore(&efx->biu_lock, flags); +} + +#endif /* EFX_FALCON_IO_H */ Index: head-2008-03-17/drivers/net/sfc/falcon_xmac.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/falcon_xmac.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,691 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#include "net_driver.h" +#include "efx.h" +#include "falcon.h" +#include "falcon_hwdefs.h" +#include "falcon_io.h" +#include "mac.h" +#include "gmii.h" +#include "mdio_10g.h" +#include "phy.h" +#include "boards.h" +#include "workarounds.h" + +/************************************************************************** + * + * MAC register access + * + **************************************************************************/ + +/* Offset of an XMAC register within Falcon */ +#define FALCON_XMAC_REG(mac_reg) \ + (FALCON_XMAC_REGBANK + ((mac_reg) * FALCON_XMAC_REG_SIZE)) + +static void falcon_xmac_writel(struct efx_nic *efx, + efx_dword_t *value, unsigned int mac_reg) +{ + efx_oword_t temp; + + EFX_POPULATE_OWORD_1(temp, MAC_DATA, EFX_DWORD_FIELD(*value, MAC_DATA)); + falcon_write(efx, &temp, FALCON_XMAC_REG(mac_reg)); +} + +static void falcon_xmac_readl(struct efx_nic *efx, + efx_dword_t *value, unsigned int mac_reg) +{ + efx_oword_t temp; + + falcon_read(efx, &temp, FALCON_XMAC_REG(mac_reg)); + EFX_POPULATE_DWORD_1(*value, MAC_DATA, EFX_OWORD_FIELD(temp, MAC_DATA)); +} + +/************************************************************************** + * + * MAC operations + * + *************************************************************************/ +static int falcon_reset_xmac(struct efx_nic *efx) +{ + efx_dword_t reg; + int count; + + EFX_POPULATE_DWORD_1(reg, XM_CORE_RST, 1); + efx->mac_op->mac_writel(efx, ®, XM_GLB_CFG_REG_MAC); + + for (count = 0; count < 10000; count++) { /* wait upto 100ms */ + efx->mac_op->mac_readl(efx, ®, XM_GLB_CFG_REG_MAC); + if (EFX_DWORD_FIELD(reg, XM_CORE_RST) == 0) + return 0; + udelay(10); + } + + EFX_ERR(efx, "timed out waiting for XMAC core reset\n"); + return -ETIMEDOUT; +} + +/* Configure the XAUI driver that is an output from Falcon */ +static void falcon_setup_xaui(struct efx_nic *efx) +{ + efx_dword_t sdctl, txdrv; + + /* Move the XAUI into low power, unless there is no PHY, in + * which case the XAUI will have to drive a cable. */ + if (efx->phy_type == PHY_TYPE_NONE) + return; + + efx->mac_op->mac_readl(efx, &sdctl, XX_SD_CTL_REG_MAC); + EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVD, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_LODRVD, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVC, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_LODRVC, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVB, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_LODRVB, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_HIDRVA, XX_SD_CTL_DRV_DEFAULT); + EFX_SET_DWORD_FIELD(sdctl, XX_LODRVA, XX_SD_CTL_DRV_DEFAULT); + efx->mac_op->mac_writel(efx, &sdctl, XX_SD_CTL_REG_MAC); + + EFX_POPULATE_DWORD_8(txdrv, + XX_DEQD, XX_TXDRV_DEQ_DEFAULT, + XX_DEQC, XX_TXDRV_DEQ_DEFAULT, + XX_DEQB, XX_TXDRV_DEQ_DEFAULT, + XX_DEQA, XX_TXDRV_DEQ_DEFAULT, + XX_DTXD, XX_TXDRV_DTX_DEFAULT, + XX_DTXC, XX_TXDRV_DTX_DEFAULT, + XX_DTXB, XX_TXDRV_DTX_DEFAULT, + XX_DTXA, XX_TXDRV_DTX_DEFAULT); + efx->mac_op->mac_writel(efx, &txdrv, XX_TXDRV_CTL_REG_MAC); +} + +static void falcon_hold_xaui_in_rst(struct efx_nic *efx) +{ + efx_dword_t reg; + + EFX_ZERO_DWORD(reg); + EFX_SET_DWORD_FIELD(reg, XX_PWRDNA_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_PWRDNB_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_PWRDNC_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_PWRDND_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RSTPLLAB_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RSTPLLCD_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RESETA_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RESETB_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RESETC_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RESETD_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSRX_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSTX_EN, 1); + efx->mac_op->mac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); +} + +static int _falcon_reset_xaui_a(struct efx_nic *efx) +{ + efx_dword_t reg; + + if (!efx->is_asic) + return 0; + + falcon_hold_xaui_in_rst(efx); + efx->mac_op->mac_readl(efx, ®, XX_PWR_RST_REG_MAC); + + /* Follow the RAMBUS XAUI data reset sequencing + * Channels A and B first: power down, reset PLL, reset, clear + */ + EFX_SET_DWORD_FIELD(reg, XX_PWRDNA_EN, 0); + EFX_SET_DWORD_FIELD(reg, XX_PWRDNB_EN, 0); + efx->mac_op->mac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + EFX_SET_DWORD_FIELD(reg, XX_RSTPLLAB_EN, 0); + efx->mac_op->mac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + EFX_SET_DWORD_FIELD(reg, XX_RESETA_EN, 0); + EFX_SET_DWORD_FIELD(reg, XX_RESETB_EN, 0); + efx->mac_op->mac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + /* Channels C and D: power down, reset PLL, reset, clear */ + EFX_SET_DWORD_FIELD(reg, XX_PWRDNC_EN, 0); + EFX_SET_DWORD_FIELD(reg, XX_PWRDND_EN, 0); + efx->mac_op->mac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + EFX_SET_DWORD_FIELD(reg, XX_RSTPLLCD_EN, 0); + efx->mac_op->mac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + EFX_SET_DWORD_FIELD(reg, XX_RESETC_EN, 0); + EFX_SET_DWORD_FIELD(reg, XX_RESETD_EN, 0); + efx->mac_op->mac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + /* Setup XAUI */ + falcon_setup_xaui(efx); + udelay(10); + + /* Take XGXS out of reset */ + EFX_ZERO_DWORD(reg); + efx->mac_op->mac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + return 0; +} + +static int _falcon_reset_xaui_b(struct efx_nic *efx) +{ + efx_dword_t reg; + int count; + + if (!efx->is_asic) + return 0; + + EFX_POPULATE_DWORD_1(reg, XX_RST_XX_EN, 1); + efx->mac_op->mac_writel(efx, ®, XX_PWR_RST_REG_MAC); + + /* Give some time for the link to establish */ + for (count = 0; count < 1000; count++) { /* wait upto 10ms */ + efx->mac_op->mac_readl(efx, ®, XX_PWR_RST_REG_MAC); + if (EFX_DWORD_FIELD(reg, XX_RST_XX_EN) == 0) { + falcon_setup_xaui(efx); + return 0; + } + udelay(10); + } + EFX_ERR(efx, "timed out waiting for XAUI/XGXS reset\n"); + return -ETIMEDOUT; +} + +int falcon_reset_xaui(struct efx_nic *efx) +{ + int rc; + + if (EFX_WORKAROUND_9388(efx)) { + falcon_hold_xaui_in_rst(efx); + efx->phy_op->reset_xaui(efx); + rc = _falcon_reset_xaui_a(efx); + } else { + rc = _falcon_reset_xaui_b(efx); + } + return rc; +} + +static int falcon_init_xmac(struct efx_nic *efx) +{ + int rc; + + /* Initialize the PHY first so the clock is around */ + rc = efx->phy_op->init(efx); + if (rc) + goto fail1; + + rc = falcon_reset_xaui(efx); + if (rc) + goto fail2; + + /* Wait again. Give the PHY and MAC time to faff */ + schedule_timeout_uninterruptible(HZ / 10); + + /* Reset the MAC */ + rc = falcon_reset_xmac(efx); + if (rc) + goto fail2; + + return 0; + + fail2: + efx->phy_op->fini(efx); + fail1: + return rc; +} + +/* Get status of XAUI link */ +int falcon_xaui_link_ok(struct efx_nic *efx) +{ + efx_dword_t reg; + int align_done; + int sync_status; + int link_ok = 0; + + /* If we're in internal loopback, then the link is up. + * The A1 FPGA/4G has RX and TX XAUI wired together, so the link is up. + * The B0 FPGA has XAUI offchip, so it is always up. + */ + if (!efx->is_asic || LOOPBACK_INTERNAL(efx)) + return 1; + + /* Read link status */ + efx->mac_op->mac_readl(efx, ®, XX_CORE_STAT_REG_MAC); + + align_done = EFX_DWORD_FIELD(reg, XX_ALIGN_DONE); + sync_status = EFX_DWORD_FIELD(reg, XX_SYNC_STAT); + if (align_done && (sync_status == XX_SYNC_STAT_DECODE_SYNCED)) + link_ok = 1; + + /* Clear link status ready for next read */ + EFX_SET_DWORD_FIELD(reg, XX_COMMA_DET, XX_COMMA_DET_RESET); + EFX_SET_DWORD_FIELD(reg, XX_CHARERR, XX_CHARERR_RESET); + EFX_SET_DWORD_FIELD(reg, XX_DISPERR, XX_DISPERR_RESET); + + efx->mac_op->mac_writel(efx, ®, XX_CORE_STAT_REG_MAC); + + return link_ok; +} + +/* Do most of the heavy lifting of falcon_reconfigure_xmac */ +static void falcon_reconfigure_xmac_core(struct efx_nic *efx) +{ + unsigned int max_frame_len; + efx_dword_t reg; + efx_oword_t mac_test_reg; + int rx_fc = (efx->flow_control & EFX_FC_RX) ? 1 : 0; + + if (FALCON_REV(efx) <= FALCON_REV_A1 && !efx->is_asic) { + /* 10G FPGA's have the XAUI TX and RX wired together. Fake + * the link status and configure the link options before + * the MAC wrapper is configured */ + efx->link_options = GM_LPA_10000FULL; + efx->link_up = 1; + } + + /* Configure MAC - cut-thru mode is hard wired on */ + EFX_POPULATE_DWORD_3(reg, + XM_RX_JUMBO_MODE, 1, + XM_TX_STAT_EN, 1, + XM_RX_STAT_EN, 1); + efx->mac_op->mac_writel(efx, ®, XM_GLB_CFG_REG_MAC); + + /* Configure TX */ + EFX_POPULATE_DWORD_6(reg, + XM_TXEN, 1, + XM_TX_PRMBL, 1, + XM_AUTO_PAD, 1, + XM_TXCRC, 1, + XM_FCNTL, 1, + XM_IPG, 0x3); + efx->mac_op->mac_writel(efx, ®, XM_TX_CFG_REG_MAC); + + /* Configure RX */ + EFX_POPULATE_DWORD_5(reg, + XM_RXEN, 1, + XM_AUTO_DEPAD, 0, + XM_ACPT_ALL_MCAST, 1, + XM_ACPT_ALL_UCAST, efx->promiscuous, + XM_PASS_CRC_ERR, 1); + efx->mac_op->mac_writel(efx, ®, XM_RX_CFG_REG_MAC); + + /* Set frame length */ + max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu); + EFX_POPULATE_DWORD_1(reg, XM_MAX_RX_FRM_SIZE, max_frame_len); + efx->mac_op->mac_writel(efx, ®, XM_RX_PARAM_REG_MAC); + EFX_POPULATE_DWORD_2(reg, + XM_MAX_TX_FRM_SIZE, max_frame_len, + XM_TX_JUMBO_MODE, 1); + efx->mac_op->mac_writel(efx, ®, XM_TX_PARAM_REG_MAC); + + EFX_POPULATE_DWORD_2(reg, + XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */ + XM_DIS_FCNTL, rx_fc ? 0 : 1); + efx->mac_op->mac_writel(efx, ®, XM_FC_REG_MAC); + + /* Set MAC address */ + EFX_POPULATE_DWORD_4(reg, + XM_ADR_0, efx->net_dev->dev_addr[0], + XM_ADR_1, efx->net_dev->dev_addr[1], + XM_ADR_2, efx->net_dev->dev_addr[2], + XM_ADR_3, efx->net_dev->dev_addr[3]); + efx->mac_op->mac_writel(efx, ®, XM_ADR_LO_REG_MAC); + EFX_POPULATE_DWORD_2(reg, + XM_ADR_4, efx->net_dev->dev_addr[4], + XM_ADR_5, efx->net_dev->dev_addr[5]); + efx->mac_op->mac_writel(efx, ®, XM_ADR_HI_REG_MAC); + + /* Handle B0 FPGA loopback where RAMBUS XGXS block not present */ + if (FALCON_REV(efx) == FALCON_REV_B0 && !efx->is_asic) { + int xgmii_loopback = + (efx->loopback_mode == LOOPBACK_XGMII) ? 1 : 0; + + /* Set the MAC loopback bit. */ + EFX_POPULATE_OWORD_1(mac_test_reg, + MAC_PTLOOP_EN, xgmii_loopback); + falcon_write(efx, &mac_test_reg, MAC_TEST_REG_KER); + } +} + +/* Do most of the heavy lifting of falcon_reconfigure_xmac */ +static void falcon_reconfigure_xgxs_core(struct efx_nic *efx) +{ + efx_dword_t reg; + int xgxs_loopback = (efx->loopback_mode == LOOPBACK_XGXS) ? 1 : 0; + int xaui_loopback = (efx->loopback_mode == LOOPBACK_XAUI) ? 1 : 0; + int xgmii_loopback = + (efx->loopback_mode == LOOPBACK_XGMII) ? 1 : 0; + + if (FALCON_REV(efx) == FALCON_REV_B0 && !efx->is_asic) + /* RAMBUS XGXS block is not present */ + return; + + /* XGXS block is flaky and will need to be reset if moving + * into our out of XGMII, XGXS or XAUI loopbacks. */ + if (EFX_WORKAROUND_5147(efx)) { + int old_xgmii_loopback, old_xgxs_loopback, old_xaui_loopback; + int reset_xgxs; + + efx->mac_op->mac_readl(efx, ®, + XX_CORE_STAT_REG_MAC); + old_xgxs_loopback = EFX_DWORD_FIELD(reg, XX_XGXS_LB_EN); + old_xgmii_loopback = EFX_DWORD_FIELD(reg, XX_XGMII_LB_EN); + + efx->mac_op->mac_readl(efx, ®, XX_SD_CTL_REG_MAC); + old_xaui_loopback = EFX_DWORD_FIELD(reg, XX_LPBKA); + + /* The PHY driver may have turned XAUI off */ + reset_xgxs = ((xgxs_loopback != old_xgxs_loopback) || + (xaui_loopback != old_xaui_loopback) || + (xgmii_loopback != old_xgmii_loopback)); + if (reset_xgxs) { + efx->mac_op->mac_readl(efx, ®, + XX_PWR_RST_REG_MAC); + EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSTX_EN, 1); + EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSRX_EN, 1); + efx->mac_op->mac_writel(efx, ®, + XX_PWR_RST_REG_MAC); + udelay(1); + EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSTX_EN, 0); + EFX_SET_DWORD_FIELD(reg, XX_RSTXGXSRX_EN, 0); + efx->mac_op->mac_writel(efx, ®, + XX_PWR_RST_REG_MAC); + udelay(1); + } + } + + efx->mac_op->mac_readl(efx, ®, XX_CORE_STAT_REG_MAC); + EFX_SET_DWORD_FIELD(reg, XX_FORCE_SIG, + (xgxs_loopback || xaui_loopback) ? + XX_FORCE_SIG_DECODE_FORCED : 0); + EFX_SET_DWORD_FIELD(reg, XX_XGXS_LB_EN, xgxs_loopback); + EFX_SET_DWORD_FIELD(reg, XX_XGMII_LB_EN, xgmii_loopback); + efx->mac_op->mac_writel(efx, ®, XX_CORE_STAT_REG_MAC); + + efx->mac_op->mac_readl(efx, ®, XX_SD_CTL_REG_MAC); + EFX_SET_DWORD_FIELD(reg, XX_LPBKD, xaui_loopback); + EFX_SET_DWORD_FIELD(reg, XX_LPBKC, xaui_loopback); + EFX_SET_DWORD_FIELD(reg, XX_LPBKB, xaui_loopback); + EFX_SET_DWORD_FIELD(reg, XX_LPBKA, xaui_loopback); + efx->mac_op->mac_writel(efx, ®, XX_SD_CTL_REG_MAC); +} + + +/* Sometimes the XAUI link between Falcon and XFP fails to come up. The state + * of the link is checked during phy_reconfigure(). After XAIU is reset then + * the MAC must be reconfigured. + */ +#define MAX_XAUI_TRIES (5) /* It's never been seen to take more than 2 */ + +void falcon_check_xaui_link_up(struct efx_nic *efx) +{ + int max_tries, tries; + tries = EFX_WORKAROUND_5147(efx) ? MAX_XAUI_TRIES : 1; + max_tries = tries; + + if ((efx->loopback_mode == LOOPBACK_NETWORK) || + (efx->phy_type == PHY_TYPE_NONE) || + !efx->phy_powered) + return; + + while (tries) { + if (falcon_xaui_link_ok(efx)) + return; + + EFX_LOG(efx, "%s Clobbering XAUI (%d tries left).\n", + __FUNCTION__, tries); + (void) falcon_reset_xaui(efx); + /* Cannot use full reconfigure. Need to avoid recursion */ + + /* Give the poor thing time to sort itself out: if we retry + * too fast it will never train. */ + udelay(200); + + falcon_reconfigure_xgxs_core(efx); + + tries--; + } + + EFX_ERR(efx, "Failed to bring XAUI link back up in %d tries!" + "\n", max_tries); +} + +static void falcon_reconfigure_xmac(struct efx_nic *efx) +{ + falcon_deconfigure_mac_wrapper(efx); + + /* In internal loopback modes disable transmit */ + efx->tx_disabled = LOOPBACK_INTERNAL(efx); + + efx->phy_op->reconfigure(efx); + + falcon_reconfigure_xgxs_core(efx); + falcon_reconfigure_xmac_core(efx); + + /* Reconfigure MAC wrapper */ + falcon_reconfigure_mac_wrapper(efx); + + /* Ensure XAUI link is up - might repeat reconfigure_xmac_core */ + falcon_check_xaui_link_up(efx); +} + +static void falcon_fini_xmac(struct efx_nic *efx) +{ + /* Isolate the MAC - PHY */ + falcon_deconfigure_mac_wrapper(efx); + + /* Potentially power down the PHY */ + efx->phy_op->fini(efx); +} + +static void falcon_update_stats_xmac(struct efx_nic *efx) +{ + struct efx_mac_stats *mac_stats = &efx->mac_stats; + int rc; + + rc = falcon_dma_stats(efx, XgDmaDone_offset); + if (rc) + return; + + /* Update MAC stats from DMAed values */ + FALCON_STAT(efx, XgRxOctets, rx_bytes); + FALCON_STAT(efx, XgRxOctetsOK, rx_good_bytes); + FALCON_STAT(efx, XgRxPkts, rx_packets); + FALCON_STAT(efx, XgRxPktsOK, rx_good); + FALCON_STAT(efx, XgRxBroadcastPkts, rx_broadcast); + FALCON_STAT(efx, XgRxMulticastPkts, rx_multicast); + FALCON_STAT(efx, XgRxUnicastPkts, rx_unicast); + FALCON_STAT(efx, XgRxUndersizePkts, rx_lt64); + FALCON_STAT(efx, XgRxOversizePkts, rx_gtjumbo); + FALCON_STAT(efx, XgRxJabberPkts, rx_bad_gtjumbo); + FALCON_STAT(efx, XgRxUndersizeFCSerrorPkts, rx_bad_lt64); + FALCON_STAT(efx, XgRxDropEvents, rx_overflow); + FALCON_STAT(efx, XgRxFCSerrorPkts, rx_bad); + FALCON_STAT(efx, XgRxAlignError, rx_align_error); + FALCON_STAT(efx, XgRxSymbolError, rx_symbol_error); + FALCON_STAT(efx, XgRxInternalMACError, rx_internal_error); + FALCON_STAT(efx, XgRxControlPkts, rx_control); + FALCON_STAT(efx, XgRxPausePkts, rx_pause); + FALCON_STAT(efx, XgRxPkts64Octets, rx_64); + FALCON_STAT(efx, XgRxPkts65to127Octets, rx_65_to_127); + FALCON_STAT(efx, XgRxPkts128to255Octets, rx_128_to_255); + FALCON_STAT(efx, XgRxPkts256to511Octets, rx_256_to_511); + FALCON_STAT(efx, XgRxPkts512to1023Octets, rx_512_to_1023); + FALCON_STAT(efx, XgRxPkts1024to15xxOctets, rx_1024_to_15xx); + FALCON_STAT(efx, XgRxPkts15xxtoMaxOctets, rx_15xx_to_jumbo); + FALCON_STAT(efx, XgRxLengthError, rx_length_error); + FALCON_STAT(efx, XgTxPkts, tx_packets); + FALCON_STAT(efx, XgTxOctets, tx_bytes); + FALCON_STAT(efx, XgTxMulticastPkts, tx_multicast); + FALCON_STAT(efx, XgTxBroadcastPkts, tx_broadcast); + FALCON_STAT(efx, XgTxUnicastPkts, tx_unicast); + FALCON_STAT(efx, XgTxControlPkts, tx_control); + FALCON_STAT(efx, XgTxPausePkts, tx_pause); + FALCON_STAT(efx, XgTxPkts64Octets, tx_64); + FALCON_STAT(efx, XgTxPkts65to127Octets, tx_65_to_127); + FALCON_STAT(efx, XgTxPkts128to255Octets, tx_128_to_255); + FALCON_STAT(efx, XgTxPkts256to511Octets, tx_256_to_511); + FALCON_STAT(efx, XgTxPkts512to1023Octets, tx_512_to_1023); + FALCON_STAT(efx, XgTxPkts1024to15xxOctets, tx_1024_to_15xx); + FALCON_STAT(efx, XgTxPkts1519toMaxOctets, tx_15xx_to_jumbo); + FALCON_STAT(efx, XgTxUndersizePkts, tx_lt64); + FALCON_STAT(efx, XgTxOversizePkts, tx_gtjumbo); + FALCON_STAT(efx, XgTxNonTcpUdpPkt, tx_non_tcpudp); + FALCON_STAT(efx, XgTxMacSrcErrPkt, tx_mac_src_error); + FALCON_STAT(efx, XgTxIpSrcErrPkt, tx_ip_src_error); + + /* Update derived statistics */ + mac_stats->tx_good_bytes = + (mac_stats->tx_bytes - mac_stats->tx_bad_bytes); + mac_stats->rx_bad_bytes = + (mac_stats->rx_bytes - mac_stats->rx_good_bytes); +} + +#define EFX_XAUI_RETRAIN_MAX 8 + +static int falcon_check_xmac(struct efx_nic *efx) +{ + unsigned link_ok, phyxs_ok = 1; + unsigned has_phyxs = efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS); + + /* Check the remote XAUI link status */ + link_ok = falcon_xaui_link_ok(efx); + + if ((efx->loopback_mode == LOOPBACK_NETWORK) || + !efx->phy_powered) + return 0; + + if (link_ok && has_phyxs && !LOOPBACK_INTERNAL(efx)) { + /* Does the PHYXS think we have lane sync? */ + phyxs_ok = mdio_clause45_phyxgxs_lane_sync(efx); + } + + if (EFX_WORKAROUND_5147(efx) && (!link_ok || !phyxs_ok)) { + (void) falcon_reset_xaui(efx); + falcon_reconfigure_xgxs_core(efx); + } + + /* Call the PHY check_hw routine */ + efx->phy_op->check_hw(efx); + return 0; +} + +/* Simulate a PHY event */ +static void falcon_xmac_sim_phy_event(struct efx_nic *efx) +{ + efx_qword_t phy_event; + + EFX_POPULATE_QWORD_2(phy_event, + EV_CODE, GLOBAL_EV_DECODE, + XG_PHY_INTR, 1); + falcon_generate_event(&efx->channel[0], &phy_event); +} + +static int falcon_xmac_get_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd) +{ + mdio_clause45_get_settings(efx, ecmd); + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = efx->mii.phy_id; + ecmd->autoneg = AUTONEG_DISABLE; + ecmd->duplex = DUPLEX_FULL; + return 0; +} + +static int falcon_xmac_set_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd) +{ + if (ecmd->transceiver != XCVR_INTERNAL) + return -EINVAL; + if (ecmd->autoneg != AUTONEG_DISABLE) + return -EINVAL; + if (ecmd->duplex != DUPLEX_FULL) + return -EINVAL; + + return mdio_clause45_set_settings(efx, ecmd); +} + + +static int falcon_xmac_set_pause(struct efx_nic *efx, + enum efx_fc_type flow_control) +{ + int reset; + + if (flow_control & EFX_FC_AUTO) { + EFX_LOG(efx, "10G does not support flow control " + "autonegotiation\n"); + return -EINVAL; + } + + if ((flow_control & EFX_FC_TX) && !(flow_control & EFX_FC_RX)) + return -EINVAL; + + /* TX flow control may automatically turn itself off if the + * link partner (intermittently) stops responding to pause + * frames. There isn't any indication that this has happened, + * so the best we do is leave it up to the user to spot this + * and fix it be cycling transmit flow control on this end. */ + reset = ((flow_control & EFX_FC_TX) && + !(efx->flow_control & EFX_FC_TX)); + if (EFX_WORKAROUND_11482(efx) && reset) { + if (FALCON_REV(efx) == FALCON_REV_B0) { + /* Recover by resetting the EM block */ + mutex_lock(&efx->mac_lock); + if (efx->link_up) + falcon_drain_tx_fifo(efx); + mutex_unlock(&efx->mac_lock); + } else { + /* Schedule a reset to recover */ + efx_schedule_reset(efx, RESET_TYPE_INVISIBLE); + } + } + + efx->flow_control = flow_control; + + return 0; +} + +struct efx_mac_operations falcon_xmac_operations = { + .mac_writel = falcon_xmac_writel, + .mac_readl = falcon_xmac_readl, + .init = falcon_init_xmac, + .reconfigure = falcon_reconfigure_xmac, + .update_stats = falcon_update_stats_xmac, + .fini = falcon_fini_xmac, + .check_hw = falcon_check_xmac, + .fake_phy_event = falcon_xmac_sim_phy_event, + .get_settings = falcon_xmac_get_settings, + .set_settings = falcon_xmac_set_settings, + .set_pause = falcon_xmac_set_pause, +}; Index: head-2008-03-17/drivers/net/sfc/gmii.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/gmii.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,212 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2005-2006: Fen Systems Ltd. + * Copyright 2006: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Initially developed by Michael Brown + * Maintained by Solarflare Communications + * + * 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_GMII_H +#define EFX_GMII_H + +/* + * GMII interface + */ + +#include + +/* GMII registers, excluding registers already defined as MII + * registers in mii.h + */ +#define GMII_IER 0x12 /* Interrupt enable register */ +#define GMII_ISR 0x13 /* Interrupt status register */ + +/* Interrupt enable register */ +#define IER_ANEG_ERR 0x8000 /* Bit 15 - autonegotiation error */ +#define IER_SPEED_CHG 0x4000 /* Bit 14 - speed changed */ +#define IER_DUPLEX_CHG 0x2000 /* Bit 13 - duplex changed */ +#define IER_PAGE_RCVD 0x1000 /* Bit 12 - page received */ +#define IER_ANEG_DONE 0x0800 /* Bit 11 - autonegotiation complete */ +#define IER_LINK_CHG 0x0400 /* Bit 10 - link status changed */ +#define IER_SYM_ERR 0x0200 /* Bit 9 - symbol error */ +#define IER_FALSE_CARRIER 0x0100 /* Bit 8 - false carrier */ +#define IER_FIFO_ERR 0x0080 /* Bit 7 - FIFO over/underflow */ +#define IER_MDIX_CHG 0x0040 /* Bit 6 - MDI crossover changed */ +#define IER_DOWNSHIFT 0x0020 /* Bit 5 - downshift */ +#define IER_ENERGY 0x0010 /* Bit 4 - energy detect */ +#define IER_DTE_POWER 0x0004 /* Bit 2 - DTE power detect */ +#define IER_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */ +#define IER_JABBER 0x0001 /* Bit 0 - jabber */ + +/* Interrupt status register */ +#define ISR_ANEG_ERR 0x8000 /* Bit 15 - autonegotiation error */ +#define ISR_SPEED_CHG 0x4000 /* Bit 14 - speed changed */ +#define ISR_DUPLEX_CHG 0x2000 /* Bit 13 - duplex changed */ +#define ISR_PAGE_RCVD 0x1000 /* Bit 12 - page received */ +#define ISR_ANEG_DONE 0x0800 /* Bit 11 - autonegotiation complete */ +#define ISR_LINK_CHG 0x0400 /* Bit 10 - link status changed */ +#define ISR_SYM_ERR 0x0200 /* Bit 9 - symbol error */ +#define ISR_FALSE_CARRIER 0x0100 /* Bit 8 - false carrier */ +#define ISR_FIFO_ERR 0x0080 /* Bit 7 - FIFO over/underflow */ +#define ISR_MDIX_CHG 0x0040 /* Bit 6 - MDI crossover changed */ +#define ISR_DOWNSHIFT 0x0020 /* Bit 5 - downshift */ +#define ISR_ENERGY 0x0010 /* Bit 4 - energy detect */ +#define ISR_DTE_POWER 0x0004 /* Bit 2 - DTE power detect */ +#define ISR_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */ +#define ISR_JABBER 0x0001 /* Bit 0 - jabber */ + +/* Logically extended advertisement register */ +#define GM_ADVERTISE_SLCT ADVERTISE_SLCT +#define GM_ADVERTISE_CSMA ADVERTISE_CSMA +#define GM_ADVERTISE_10HALF ADVERTISE_10HALF +#define GM_ADVERTISE_1000XFULL ADVERTISE_1000XFULL +#define GM_ADVERTISE_10FULL ADVERTISE_10FULL +#define GM_ADVERTISE_1000XHALF ADVERTISE_1000XHALF +#define GM_ADVERTISE_100HALF ADVERTISE_100HALF +#define GM_ADVERTISE_1000XPAUSE ADVERTISE_1000XPAUSE +#define GM_ADVERTISE_100FULL ADVERTISE_100FULL +#define GM_ADVERTISE_1000XPSE_ASYM ADVERTISE_1000XPSE_ASYM +#define GM_ADVERTISE_100BASE4 ADVERTISE_100BASE4 +#define GM_ADVERTISE_PAUSE_CAP ADVERTISE_PAUSE_CAP +#define GM_ADVERTISE_PAUSE_ASYM ADVERTISE_PAUSE_ASYM +#define GM_ADVERTISE_RESV ADVERTISE_RESV +#define GM_ADVERTISE_RFAULT ADVERTISE_RFAULT +#define GM_ADVERTISE_LPACK ADVERTISE_LPACK +#define GM_ADVERTISE_NPAGE ADVERTISE_NPAGE +#define GM_ADVERTISE_1000FULL (ADVERTISE_1000FULL << 8) +#define GM_ADVERTISE_1000HALF (ADVERTISE_1000HALF << 8) +#define GM_ADVERTISE_1000 (GM_ADVERTISE_1000FULL | \ + GM_ADVERTISE_1000HALF) +#define GM_ADVERTISE_FULL (GM_ADVERTISE_1000FULL | \ + ADVERTISE_FULL) +#define GM_ADVERTISE_ALL (GM_ADVERTISE_1000FULL | \ + GM_ADVERTISE_1000HALF | \ + ADVERTISE_ALL) + +/* Logically extended link partner ability register */ +#define GM_LPA_SLCT LPA_SLCT +#define GM_LPA_10HALF LPA_10HALF +#define GM_LPA_1000XFULL LPA_1000XFULL +#define GM_LPA_10FULL LPA_10FULL +#define GM_LPA_1000XHALF LPA_1000XHALF +#define GM_LPA_100HALF LPA_100HALF +#define GM_LPA_1000XPAUSE LPA_1000XPAUSE +#define GM_LPA_100FULL LPA_100FULL +#define GM_LPA_1000XPAUSE_ASYM LPA_1000XPAUSE_ASYM +#define GM_LPA_100BASE4 LPA_100BASE4 +#define GM_LPA_PAUSE_CAP LPA_PAUSE_CAP +#define GM_LPA_PAUSE_ASYM LPA_PAUSE_ASYM +#define GM_LPA_RESV LPA_RESV +#define GM_LPA_RFAULT LPA_RFAULT +#define GM_LPA_LPACK LPA_LPACK +#define GM_LPA_NPAGE LPA_NPAGE +#define GM_LPA_1000FULL (LPA_1000FULL << 6) +#define GM_LPA_1000HALF (LPA_1000HALF << 6) +#define GM_LPA_10000FULL 0x00040000 +#define GM_LPA_10000HALF 0x00080000 +#define GM_LPA_DUPLEX (GM_LPA_1000FULL | GM_LPA_10000FULL \ + | LPA_DUPLEX) +#define GM_LPA_10 (LPA_10FULL | LPA_10HALF) +#define GM_LPA_100 LPA_100 +#define GM_LPA_1000 (GM_LPA_1000FULL | GM_LPA_1000HALF) +#define GM_LPA_10000 (GM_LPA_10000FULL | GM_LPA_10000HALF) + +/* Retrieve GMII autonegotiation advertised abilities + * + * The MII advertisment register (MII_ADVERTISE) is logically extended + * to include advertisement bits ADVERTISE_1000FULL and + * ADVERTISE_1000HALF from MII_CTRL1000. The result can be tested + * against the GM_ADVERTISE_xxx constants. + */ +static inline unsigned int gmii_advertised(struct mii_if_info *gmii) +{ + unsigned int advertise; + unsigned int ctrl1000; + + advertise = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_ADVERTISE); + ctrl1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_CTRL1000); + return (((ctrl1000 << 8) & GM_ADVERTISE_1000) | advertise); +} + +/* Retrieve GMII autonegotiation link partner abilities + * + * The MII link partner ability register (MII_LPA) is logically + * extended by adding bits LPA_1000HALF and LPA_1000FULL from + * MII_STAT1000. The result can be tested against the GM_LPA_xxx + * constants. + */ +static inline unsigned int gmii_lpa(struct mii_if_info *gmii) +{ + unsigned int lpa; + unsigned int stat1000; + + lpa = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_LPA); + stat1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_STAT1000); + return (((stat1000 << 6) & GM_LPA_1000) | lpa); +} + +/* Calculate GMII autonegotiated link technology + * + * "negotiated" should be the result of gmii_advertised() logically + * ANDed with the result of gmii_lpa(). + * + * "tech" will be negotiated with the unused bits masked out. For + * example, if both ends of the link are capable of both + * GM_LPA_1000FULL and GM_LPA_100FULL, GM_LPA_100FULL will be masked + * out. + */ +static inline unsigned int gmii_nway_result(unsigned int negotiated) +{ + unsigned int other_bits; + + /* Mask out the speed and duplexity bits */ + other_bits = negotiated & ~(GM_LPA_10 | GM_LPA_100 | GM_LPA_1000); + + if (negotiated & GM_LPA_1000FULL) + return (other_bits | GM_LPA_1000FULL); + else if (negotiated & GM_LPA_1000HALF) + return (other_bits | GM_LPA_1000HALF); + else + return (other_bits | mii_nway_result(negotiated)); +} + +/* Calculate GMII non-autonegotiated link technology + * + * This provides an equivalent to gmii_nway_result for the case when + * autonegotiation is disabled. + */ +static inline unsigned int gmii_forced_result(unsigned int bmcr) +{ + unsigned int result; + int full_duplex; + + full_duplex = bmcr & BMCR_FULLDPLX; + if (bmcr & BMCR_SPEED1000) + result = full_duplex ? GM_LPA_1000FULL : GM_LPA_1000HALF; + else if (bmcr & BMCR_SPEED100) + result = full_duplex ? GM_LPA_100FULL : GM_LPA_100HALF; + else + result = full_duplex ? GM_LPA_10FULL : GM_LPA_10HALF; + return result; +} + +#endif /* EFX_GMII_H */ Index: head-2008-03-17/drivers/net/sfc/i2c-direct.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/i2c-direct.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,398 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2005: Fen Systems Ltd. + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Initially developed by Michael Brown + * Maintained by Solarflare Communications + * + * 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 +#include +#include "net_driver.h" +#include "i2c-direct.h" + +/* EEPROM access via I2C + * data (SDA) and clock (SCL) line read/writes + */ + +static inline void setsda(struct efx_i2c_interface *i2c, int state) +{ + udelay(i2c->op->udelay); + i2c->sda = state; + i2c->op->setsda(i2c); + udelay(i2c->op->udelay); +} + +static inline void setscl(struct efx_i2c_interface *i2c, int state) +{ + udelay(i2c->op->udelay); + i2c->scl = state; + i2c->op->setscl(i2c); + udelay(i2c->op->udelay); +} + +static inline int getsda(struct efx_i2c_interface *i2c) +{ + int sda; + + udelay(i2c->op->udelay); + sda = i2c->op->getsda(i2c); + udelay(i2c->op->udelay); + return sda; +} + +static inline int getscl(struct efx_i2c_interface *i2c) +{ + int scl; + + udelay(i2c->op->udelay); + scl = i2c->op->getscl(i2c); + udelay(i2c->op->udelay); + return scl; +} + +/* + * I2C low-level protocol operations + * + */ + +static inline void i2c_release(struct efx_i2c_interface *i2c) +{ + EFX_WARN_ON_PARANOID(!i2c->scl); + EFX_WARN_ON_PARANOID(!i2c->sda); + /* Just in case */ + setscl(i2c, 1); + setsda(i2c, 1); + EFX_BUG_ON_PARANOID(getsda(i2c) != 1); + EFX_BUG_ON_PARANOID(getscl(i2c) != 1); +} + +static inline void i2c_start(struct efx_i2c_interface *i2c) +{ + /* We may be restarting immediately after a {send,recv}_bit, + * so SCL will not necessarily already be high. + */ + EFX_WARN_ON_PARANOID(!i2c->sda); + setscl(i2c, 1); + setsda(i2c, 0); + setscl(i2c, 0); + setsda(i2c, 1); +} + +static inline void i2c_send_bit(struct efx_i2c_interface *i2c, int bit) +{ + EFX_WARN_ON_PARANOID(i2c->scl != 0); + setsda(i2c, bit); + setscl(i2c, 1); + setscl(i2c, 0); + setsda(i2c, 1); +} + +static inline int i2c_recv_bit(struct efx_i2c_interface *i2c) +{ + int bit; + + EFX_WARN_ON_PARANOID(i2c->scl != 0); + EFX_WARN_ON_PARANOID(!i2c->sda); + setscl(i2c, 1); + bit = getsda(i2c); + setscl(i2c, 0); + return bit; +} + +static inline void i2c_stop(struct efx_i2c_interface *i2c) +{ + EFX_WARN_ON_PARANOID(i2c->scl != 0); + setsda(i2c, 0); + setscl(i2c, 1); + setsda(i2c, 1); +} + +/* + * I2C mid-level protocol operations + * + */ + +/* Sends a byte via the I2C bus and checks for an acknowledgement from + * the slave device. + */ +static int i2c_send_byte(struct efx_i2c_interface *i2c, u8 byte) +{ + int i; + + /* Send byte */ + for (i = 0; i < 8; i++) { + i2c_send_bit(i2c, !!(byte & 0x80)); + byte <<= 1; + } + + /* Check for acknowledgement from slave */ + return (i2c_recv_bit(i2c) == 0 ? 0 : -EIO); +} + +/* Receives a byte via the I2C bus and sends ACK/NACK to the slave device. */ +static u8 i2c_recv_byte(struct efx_i2c_interface *i2c, int ack) +{ + u8 value = 0; + int i; + + /* Receive byte */ + for (i = 0; i < 8; i++) + value = (value << 1) | i2c_recv_bit(i2c); + + /* Send ACK/NACK */ + i2c_send_bit(i2c, (ack ? 0 : 1)); + + return value; +} + +/* Calculate command byte for a read operation */ +static inline u8 i2c_read_cmd(u8 device_id) +{ + return ((device_id << 1) | 1); +} + +/* Calculate command byte for a write operation */ +static inline u8 i2c_write_cmd(u8 device_id) +{ + return ((device_id << 1) | 0); +} + +int efx_i2c_check_presence(struct efx_i2c_interface *i2c, u8 device_id) +{ + int rc; + + /* If someone is driving the bus low we just give up. */ + if (getsda(i2c) == 0 || getscl(i2c) == 0) { + EFX_ERR(i2c->efx, "%s someone is holding the I2C bus low." + " Giving up.\n", __FUNCTION__); + return -EFAULT; + } + + /* Pretend to initiate a device write */ + i2c_start(i2c); + rc = i2c_send_byte(i2c, i2c_write_cmd(device_id)); + if (rc) + goto out; + + out: + i2c_stop(i2c); + i2c_release(i2c); + + return rc; +} + +/* This performs a fast read of one or more consecutive bytes from an + * I2C device. Not all devices support consecutive reads of more than + * one byte; for these devices use efx_i2c_read() instead. + */ +int efx_i2c_fast_read(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, u8 *data, unsigned int len) +{ + int i; + int rc; + + EFX_WARN_ON_PARANOID(getsda(i2c) != 1); + EFX_WARN_ON_PARANOID(getscl(i2c) != 1); + EFX_WARN_ON_PARANOID(data == NULL); + EFX_WARN_ON_PARANOID(len < 1); + + /* Select device and starting offset */ + i2c_start(i2c); + rc = i2c_send_byte(i2c, i2c_write_cmd(device_id)); + if (rc) + goto out; + rc = i2c_send_byte(i2c, offset); + if (rc) + goto out; + + /* Read data from device */ + i2c_start(i2c); + rc = i2c_send_byte(i2c, i2c_read_cmd(device_id)); + if (rc) + goto out; + for (i = 0; i < (len - 1); i++) + /* Read and acknowledge all but the last byte */ + data[i] = i2c_recv_byte(i2c, 1); + /* Read last byte with no acknowledgement */ + data[i] = i2c_recv_byte(i2c, 0); + + out: + i2c_stop(i2c); + i2c_release(i2c); + + return rc; +} + +/* This performs a fast write of one or more consecutive bytes to an + * I2C device. Not all devices support consecutive writes of more + * than one byte; for these devices use efx_i2c_write() instead. + */ +int efx_i2c_fast_write(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, + const u8 *data, unsigned int len) +{ + int i; + int rc; + + EFX_WARN_ON_PARANOID(getsda(i2c) != 1); + EFX_WARN_ON_PARANOID(getscl(i2c) != 1); + EFX_WARN_ON_PARANOID(len < 1); + + /* Select device and starting offset */ + i2c_start(i2c); + rc = i2c_send_byte(i2c, i2c_write_cmd(device_id)); + if (rc) + goto out; + rc = i2c_send_byte(i2c, offset); + if (rc) + goto out; + + /* Write data to device */ + for (i = 0; i < len; i++) { + rc = i2c_send_byte(i2c, data[i]); + if (rc) + goto out; + } + + out: + i2c_stop(i2c); + i2c_release(i2c); + + return rc; +} + +/* I2C byte-by-byte read */ +int efx_i2c_read(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, u8 *data, unsigned int len) +{ + int rc; + + /* i2c_fast_read with length 1 is a single byte read */ + for (; len > 0; offset++, data++, len--) { + rc = efx_i2c_fast_read(i2c, device_id, offset, data, 1); + if (rc) + return rc; + } + + return 0; +} + +/* I2C byte-by-byte write */ +int efx_i2c_write(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, const u8 *data, unsigned int len) +{ + int rc; + + /* i2c_fast_write with length 1 is a single byte write */ + for (; len > 0; offset++, data++, len--) { + rc = efx_i2c_fast_write(i2c, device_id, offset, data, 1); + if (rc) + return rc; + mdelay(i2c->op->mdelay); + } + + return 0; +} + + +/* This is just a slightly neater wrapper round efx_i2c_fast_write + * in the case where the target doesn't take an offset + */ +int efx_i2c_send_bytes(struct efx_i2c_interface *i2c, + u8 device_id, const u8 *data, unsigned int len) +{ + return efx_i2c_fast_write(i2c, device_id, data[0], data + 1, len - 1); +} + +/* I2C receiving of bytes - does not send an offset byte */ +int efx_i2c_recv_bytes(struct efx_i2c_interface *i2c, u8 device_id, + u8 *bytes, unsigned int len) +{ + int i; + int rc; + + EFX_WARN_ON_PARANOID(getsda(i2c) != 1); + EFX_WARN_ON_PARANOID(getscl(i2c) != 1); + EFX_WARN_ON_PARANOID(len < 1); + + /* Select device */ + i2c_start(i2c); + + /* Read data from device */ + rc = i2c_send_byte(i2c, i2c_read_cmd(device_id)); + if (rc) + goto out; + + for (i = 0; i < (len - 1); i++) + /* Read and acknowledge all but the last byte */ + bytes[i] = i2c_recv_byte(i2c, 1); + /* Read last byte with no acknowledgement */ + bytes[i] = i2c_recv_byte(i2c, 0); + + out: + i2c_stop(i2c); + i2c_release(i2c); + + return rc; +} + +/* SMBus and some I2C devices will time out if the I2C clock is + * held low for too long. This is most likely to happen in virtualised + * systems (when the entire domain is descheduled) but could in + * principle happen due to preemption on any busy system (and given the + * potential length of an I2C operation turning preemption off is not + * a sensible option). The following functions deal with the failure by + * retrying up to a fixed number of times. + */ + +#define I2C_MAX_RETRIES (10) + +/* The timeout problem will result in -EIO. If the wrapped function + * returns any other error, pass this up and do not retry. */ +#define RETRY_WRAPPER(_f) \ + int retries = I2C_MAX_RETRIES; \ + int rc; \ + while (retries) { \ + rc = _f; \ + if (rc != -EIO) \ + return rc; \ + retries--; \ + } \ + return rc; \ + +int efx_i2c_check_presence_retry(struct efx_i2c_interface *i2c, u8 device_id) +{ + RETRY_WRAPPER(efx_i2c_check_presence(i2c, device_id)) +} + +int efx_i2c_read_retry(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, u8 *data, unsigned int len) +{ + RETRY_WRAPPER(efx_i2c_read(i2c, device_id, offset, data, len)) +} + +int efx_i2c_write_retry(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, const u8 *data, unsigned int len) +{ + RETRY_WRAPPER(efx_i2c_write(i2c, device_id, offset, data, len)) +} Index: head-2008-03-17/drivers/net/sfc/i2c-direct.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/i2c-direct.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,108 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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_I2C_DIRECT_H +#define EFX_I2C_DIRECT_H + +#include "net_driver.h" + +/* + * Direct control of an I2C bus + */ + +struct efx_i2c_interface; + +/** + * struct efx_i2c_bit_operations - I2C bus direct control methods + * + * I2C bus direct control methods. + * + * @setsda: Set state of SDA line + * @setscl: Set state of SCL line + * @getsda: Get state of SDA line + * @getscl: Get state of SCL line + * @udelay: Delay between each bit operation + * @mdelay: Delay between each byte write + */ +struct efx_i2c_bit_operations { + void (*setsda) (struct efx_i2c_interface *i2c); + void (*setscl) (struct efx_i2c_interface *i2c); + int (*getsda) (struct efx_i2c_interface *i2c); + int (*getscl) (struct efx_i2c_interface *i2c); + unsigned int udelay; + unsigned int mdelay; +}; + +/** + * struct efx_i2c_interface - an I2C interface + * + * An I2C interface. + * + * @efx: Attached Efx NIC + * @op: I2C bus control methods + * @sda: Current output state of SDA line + * @scl: Current output state of SCL line + */ +struct efx_i2c_interface { + struct efx_nic *efx; + struct efx_i2c_bit_operations *op; + unsigned int sda:1; + unsigned int scl:1; +}; + +extern int efx_i2c_check_presence(struct efx_i2c_interface *i2c, u8 device_id); +extern int efx_i2c_fast_read(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, + u8 *data, unsigned int len); +extern int efx_i2c_fast_write(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, + const u8 *data, unsigned int len); +extern int efx_i2c_read(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, u8 *data, unsigned int len); +extern int efx_i2c_write(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, + const u8 *data, unsigned int len); + +extern int efx_i2c_send_bytes(struct efx_i2c_interface *i2c, u8 device_id, + const u8 *bytes, unsigned int len); + +extern int efx_i2c_recv_bytes(struct efx_i2c_interface *i2c, u8 device_id, + u8 *bytes, unsigned int len); + + +/* Versions of the API that retry on failure. */ +extern int efx_i2c_check_presence_retry(struct efx_i2c_interface *i2c, + u8 device_id); + +extern int efx_i2c_read_retry(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, u8 *data, unsigned int len); + +extern int efx_i2c_write_retry(struct efx_i2c_interface *i2c, + u8 device_id, u8 offset, + const u8 *data, unsigned int len); + +#endif /* EFX_I2C_DIRECT_H */ Index: head-2008-03-17/drivers/net/sfc/lm87_support.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/lm87_support.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,295 @@ +/**************************************************************************** + * 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 + * + * 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 "lm87_support.h" +#include "workarounds.h" + +/* Setting this to 1 will cause efx_check_lm87 to dump the status when it + * detects an alarm. This will result in the canonical name (i.e. that in + * the LM87 data book) being printed for each set status bit, along with + * the reading for that sensor value, if applicable. If set to 0 only the + * raw status1 and status2 register values are printed. */ +#define LM87_VERBOSE_ALARMS 1 + +/************************************************************************** + * + * Onboard LM87 temperature and voltage monitor + * + ************************************************************************** + */ + +/* LM87 channel mode: all current boards either do not use AIN1/FAN1 and 2 + * or use them as AIN. */ +#define LM87_CHANNEL_MODE 0x16 +#define LM87_CHANNEL_AIN1 1 +#define LM87_CHANNEL_AIN2 2 +#define LM87_CHANNEL_INIT (LM87_CHANNEL_AIN2 | LM87_CHANNEL_AIN1) + +/* LM87 configuration register 1 */ +#define LM87_CONFIG_1 0x40 +#define LM87_START 0x01 +#define LM87_INTEN 0x02 +#define LM87_INITIALIZATION 0x80 + +/* LM87 interrupt status register 1 */ +#define LM87_INT_STATUS_1 0x41 + +/* LM87 interrupt status register 2 */ +#define LM87_INT_STATUS_2 0x42 + +/* LM87 interrupt mask register 1 */ +#define LM87_INT_MASK_1 0x43 + +/* LM87 interrupt mask register 2 */ +#define LM87_INT_MASK_2 0x44 + +/* LM87 monitoring limits */ +#define LM87_LIMITS 0x2b + + +int efx_probe_lm87(struct efx_nic *efx, int addr, + const u8 *limits, int nlimits, const u16 irqmask) +{ + struct efx_i2c_interface *i2c = &efx->i2c; + u8 byte; + int rc; + + /* Check for onboard LM87 */ + rc = efx_i2c_check_presence_retry(i2c, addr); + if (rc) { + /* Not an error to lack an LM87, but failure to probe the + * bus is worrying. */ + if (rc == -EFAULT) { + EFX_ERR(efx, "Failed to probe I2C bus for LM87!\n"); + return rc; + } else { + EFX_LOG(efx, "has no onboard LM87 chip\n"); + return 0; + } + } + efx->board_info.lm87_addr = addr; + EFX_LOG(efx, "detected onboard LM87 chip at 0x%2x\n", addr); + + /* Reset chip */ + byte = LM87_INITIALIZATION; + rc = efx_i2c_write_retry(i2c, addr, LM87_CONFIG_1, &byte, 1); + if (rc) { + EFX_ERR(efx, "could not reset LM87\n"); + return rc; + } + + /* Configure channel mode: currently hardwire to make pins 5 and 6 + * AIN1 and AIN2 rather than FAN1, FAN2. */ + byte = LM87_CHANNEL_INIT; + rc = efx_i2c_write_retry(i2c, addr, LM87_CHANNEL_MODE, &byte, 1); + if (rc) { + EFX_ERR(efx, "could not program LM87 chan. mode\n"); + return rc; + } + + /* Configure limits */ + rc = efx_i2c_write_retry(i2c, addr, LM87_LIMITS, limits, nlimits); + if (rc) { + EFX_ERR(efx, "could not program LM87 limits\n"); + return rc; + } + + /* Mask off unwanted interrupts */ + byte = (irqmask & 0xff); + rc = efx_i2c_write_retry(i2c, addr, LM87_INT_MASK_1, &byte, 1); + if (rc) { + EFX_ERR(efx, "could not mask LM87 interrupts\n"); + return rc; + } + + byte = (irqmask >> 8); + rc = efx_i2c_write_retry(i2c, addr, LM87_INT_MASK_2, &byte, 1); + if (rc) { + EFX_ERR(efx, "could not mask LM87 interrupts\n"); + return rc; + } + + /* Start monitoring */ + byte = LM87_START; + if (irqmask != EFX_LM87_NO_INTS) + byte |= LM87_INTEN; + + rc = efx_i2c_write_retry(i2c, addr, LM87_CONFIG_1, &byte, 1); + if (rc) { + EFX_ERR(efx, "could not start LM87\n"); + return rc; + } + + return rc; +} + +void efx_remove_lm87(struct efx_nic *efx) +{ + struct efx_i2c_interface *i2c = &efx->i2c; + u8 byte; + + if (!efx->board_info.lm87_addr) + return; + + /* Reset chip */ + byte = LM87_INITIALIZATION; + if (efx_i2c_write_retry(i2c, efx->board_info.lm87_addr, + LM87_CONFIG_1, &byte, 1) != 0) + EFX_ERR(efx, "could not reset LM87 on exit\n"); +} + +#if LM87_VERBOSE_ALARMS +/* Bit number to name mapping for status1 */ +static const char *lm_stat_names[] = { +/* Status 1 contents */ + "+2.5Vin", + "Vccp1", + "Vcc", + "+5Vin", + "Int. Temp.", + "Ext. Temp.", + "FAN1/AIN1", + "FAN2/AIN2", +/* Status 2 contents */ + "+12Vin", + "Vccp2", + "Reserved", + "Reserved", + "CI", + "THERM#", + "D1 Fault", + "D2 Fault" +}; + +/* Where to read the value corresponding to an alarm bit. */ +static const int lm_stat_regs[] = { + 0x20, 0x21, 0x22, 0x23, 0x27, 0x26, 0x28, 0x29, + 0x24, 0x25, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +/* The positions of the alarm bits do not correspond exactly to the + * order of the limit values. Convert so the user only needs to maintain + * one array */ +static int lm_bit_to_lim[] = { + 0, /* 2.5V */ + 1, /* Vccp1 */ + 2, /* Vcc */ + 3, /* 5V */ + 7, /* Int temp. */ + 6, /* Ext temp. */ + 8, /* AIN1 */ + 9, /* AIN2 */ + 4, /* 12V */ + 5 /* Vccp2 */ +}; + +/* These are bit numbers. I feel justified in hardwiring the max. */ +static const int lm_stat_max = 16; + +static void lm87_show_alarm(struct efx_nic *efx, int bit) +{ + char valbuf[8]; + u8 val; + + if (lm_stat_regs[bit] != 0xff) { + efx_i2c_read_retry(&efx->i2c, efx->board_info.lm87_addr, + lm_stat_regs[bit], &val, 1); + sprintf(valbuf, "0x%02x ", val); + } else { + strcpy(valbuf, "---- "); + } + /* If the board code knows what this sensor is wired to, let it tell + * us, else just print the LM87 datasheet name of the input, and the + * value. */ + if (efx->board_info.interpret_sensor == NULL || + (bit < ARRAY_SIZE(lm_bit_to_lim) && + efx->board_info.interpret_sensor(efx, lm_bit_to_lim[bit], val) + == 0)) + EFX_ERR(efx, ": %10s %4s\n", + STRING_TABLE_LOOKUP(bit, lm_stat), valbuf); +} + +static void lm87_dump_alarms(struct efx_nic *efx, int stat1, int stat2) +{ + int i; + EFX_ERR(efx, " NAME value\n"); + for (i = 0; i < 8; i++) { + if (stat1 & (1 << i)) + lm87_show_alarm(efx, i); + if (stat2 & (1 << i)) + lm87_show_alarm(efx, i + 8); + } +} + +#else +#define lm87_dump_alarms(_name, _stat1, _stat2) do {} while (0) +#endif + +/* Read onboard LM87 (if present) + * Return error code if lm87 could not be read (-EIO) + * _or_ is raising an alarm (-ERANGE). 0 if AOK. + */ +int efx_check_lm87(struct efx_nic *efx, unsigned mask) +{ + struct efx_i2c_interface *i2c = &efx->i2c; + u8 int_status_1, int_status_2; + unsigned ints; + int rc = 0; + + /* If link is up then do not monitor temperature */ + if (EFX_WORKAROUND_7884(efx) && efx->link_up) + return 0; + + if (!efx->board_info.lm87_addr) + return 0; + + /* Read interrupt status registers */ + rc = efx_i2c_read_retry(i2c, efx->board_info.lm87_addr, + LM87_INT_STATUS_1, &int_status_1, 1); + if (rc) { + EFX_ERR(efx, "could not read LM87 INT status 1\n"); + return rc; + } + rc = efx_i2c_read_retry(i2c, efx->board_info.lm87_addr, + LM87_INT_STATUS_2, &int_status_2, 1); + if (rc) { + EFX_ERR(efx, "could not read LM87 INT status 2\n"); + return rc; + } + + int_status_1 &= mask; + int_status_2 &= (mask >> 8); + ints = ((int_status_2 << 8) | int_status_1); + + /* Check interrupt status */ + if (ints == 0) + return 0; + + EFX_ERR(efx, "LM87 detected a hardware failure (status %02x:%02x)\n", + int_status_1, int_status_2); + lm87_dump_alarms(efx, int_status_1, int_status_2); + + return -ERANGE; +} Index: head-2008-03-17/drivers/net/sfc/lm87_support.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/lm87_support.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,58 @@ +/**************************************************************************** + * 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 + * + * 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_LM87_SUPPORT_H +#define EFX_LM87_SUPPORT_H + +/* The interrupt bit masks. These are the same in the interrupt status and + * interrupt mask registers. */ +/* Register 1 bits */ +#define EFX_LM87_2_5V_INT (1) +#define EFX_LM87_VCCP1_INT (2) +#define EFX_LM87_VCC_INT (4) +#define EFX_LM87_5_V_INT (8) +#define EFX_LM87_ITMP_INT (0x10) +#define EFX_LM87_ETMP_INT (0x20) +#define EFX_LM87_FAN1_INT (0x40) +#define EFX_LM87_FAN2_INT (0x80) +/* Register 2 bits */ +#define EFX_LM87_12V_INT (0x100) +#define EFX_LM87_VCCP2_INT (0x200) +/* Bits 2 and 3 are reserved. */ +#define EFX_LM87_CI_INT (0x1000) +#define EFX_LM87_THERM_INT (0x2000) +#define EFX_LM87_D1_INT (0x4000) +#define EFX_LM87_D2_INT (0x8000) + +#define EFX_LM87_NO_INTS ((u16)-1) + +extern +int efx_probe_lm87(struct efx_nic *efx, int addr, const u8 *limits, + int nlimits, const u16 irqmask); + +extern void efx_remove_lm87(struct efx_nic *efx); + +extern int efx_check_lm87(struct efx_nic *efx, unsigned mask); + +#endif /* EFX_LM87_SUPPORT_H */ Index: head-2008-03-17/drivers/net/sfc/mac.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/mac.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,38 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2005-2006: Fen Systems Ltd. + * Copyright 2006-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Initially developed by Michael Brown + * Maintained by Solarflare Communications + * + * 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_MAC_H +#define EFX_MAC_H + +#include "net_driver.h" + +extern void mentormac_reset(struct efx_nic *efx); +extern void mentormac_reconfigure(struct efx_nic *efx); +extern struct efx_mac_operations falcon_gmac_operations; +extern struct efx_mac_operations falcon_xmac_operations; + +#endif Index: head-2008-03-17/drivers/net/sfc/mdio_10g.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/mdio_10g.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,441 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed by Solarflare Communications + * + * 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 + **************************************************************************** + */ +/* + * Useful functions for working with MDIO clause 45 PHYs + */ +#include +#include +#include +#include "net_driver.h" +#include "mdio_10g.h" +#include "boards.h" + +static const char *mmd_block_names[] = { + [0] = "(illegal)", + [MDIO_MMD_PMAPMD] = "PMA/PMD", + [MDIO_MMD_WIS] = "WIS", + [MDIO_MMD_PCS] = "PCS", + [MDIO_MMD_PHYXS] = "PHY XS", + [MDIO_MMD_DTEXS] = "DTE XS", + [MDIO_MMD_TC] = "TC", + [MDIO_MMD_AN] = "AN", +}; +static const int mmd_block_max = ARRAY_SIZE(mmd_block_names); + +const char *mdio_clause45_mmd_name(int mmd) +{ + return STRING_TABLE_LOOKUP(mmd, mmd_block); +} + +int mdio_clause45_reset_mmd(struct efx_nic *port, int mmd, + int spins, int spintime) +{ + u32 ctrl; + int phy_id = port->mii.phy_id; + + /* Catch callers passing values in the wrong units (or just silly) */ + EFX_BUG_ON_PARANOID(spins * spintime >= 5000); + + mdio_clause45_write(port, phy_id, mmd, MDIO_MMDREG_CTRL1, + (1 << MDIO_MMDREG_CTRL1_RESET_LBN)); + /* Wait for the reset bit to clear. */ + do { + msleep(spintime); + ctrl = mdio_clause45_read(port, phy_id, mmd, MDIO_MMDREG_CTRL1); + spins--; + + } while (spins && (ctrl & (1 << MDIO_MMDREG_CTRL1_RESET_LBN))); + + return spins ? spins : -ETIMEDOUT; +} + +static int mdio_clause45_check_mmd(struct efx_nic *efx, int mmd, + int fault_fatal) +{ + int status; + int phy_id = efx->mii.phy_id; + const char *mmdname = STRING_TABLE_LOOKUP(mmd, mmd_block); + + if (LOOPBACK_INTERNAL(efx)) + return 0; + + /* Read MMD STATUS2 to check it is responding. */ + status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT2); + if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) & + ((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) != + MDIO_MMDREG_STAT2_PRESENT_VAL) { + EFX_ERR(efx, "PHY MMD %s not responding.\n", mmdname); + return -EIO; + } + + /* Read MMD STATUS 1 to check for fault. */ + status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT1); + if ((status & (1 << MDIO_MMDREG_STAT1_FAULT_LBN)) != 0) { + if (fault_fatal) { + EFX_ERR(efx, "PHY MMD %s reporting fatal" + " fault: status %x\n", mmdname, status); + return -EIO; + } else { + EFX_LOG(efx, "PHY MMD %s reporting status" + " %x (expected)\n", mmdname, status); + } + } + return 0; +} + +/* This ought to be ridiculous overkill. We expect it to fail rarely */ +#define MDIO45_RESET_TIME HZ +#define MDIO45_RESET_ITERS (100) + +int mdio_clause45_wait_reset_mmds(struct efx_nic *efx, + unsigned int mmd_mask) +{ + const int spintime = MDIO45_RESET_TIME / MDIO45_RESET_ITERS; + int tries = MDIO45_RESET_ITERS; + int rc = 0; + int in_reset; + + while (tries) { + int mask = mmd_mask; + int mmd = 0; + int stat; + in_reset = 0; + while (mask) { + if (mask & 1) { + stat = mdio_clause45_read(efx, + efx->mii.phy_id, + mmd, + MDIO_MMDREG_CTRL1); + if (stat < 0) { + EFX_ERR(efx, "failed to read" + " status of MMD %s\n", + STRING_TABLE_LOOKUP(mmd, + mmd_block)); + return -EIO; + } + if (stat & (1 << MDIO_MMDREG_CTRL1_RESET_LBN)) + in_reset |= (1 << mmd); + } + mask = mask >> 1; + mmd++; + } + if (!in_reset) + break; + tries--; + msleep(spintime); + } + if (in_reset != 0) { + EFX_ERR(efx, "not all MMDs came out of reset in time." + " MMDs still in reset: %x\n", in_reset); + rc = -ETIMEDOUT; + } + return rc; +} + +int mdio_clause45_check_mmds(struct efx_nic *efx, + unsigned int mmd_mask, unsigned int fatal_mask) +{ + int devices, mmd = 0; + int probe_mmd; + + /* Historically we have probed the PHYXS to find out what devices are + * present,but that doesn't work so well if the PHYXS isn't expected + * to exist, if so just find the first item in the list supplied. */ + probe_mmd = (mmd_mask & MDIO_MMDREG_DEVS0_PHYXS) ? MDIO_MMD_PHYXS : + __ffs(mmd_mask); + devices = mdio_clause45_read(efx, efx->mii.phy_id, + probe_mmd, MDIO_MMDREG_DEVS0); + + /* Check all the expected MMDs are present */ + if (devices < 0) { + EFX_ERR(efx, "failed to read devices present\n"); + return -EIO; + } + if ((devices & mmd_mask) != mmd_mask) { + EFX_ERR(efx, "required MMDs not present: got %x, " + "wanted %x\n", devices, mmd_mask); + return -ENODEV; + } + EFX_TRACE(efx, "Devices present: %x\n", devices); + + /* Check all required MMDs are responding and happy. */ + while (mmd_mask) { + if (mmd_mask & 1) { + int fault_fatal = fatal_mask & 1; + if (mdio_clause45_check_mmd(efx, mmd, fault_fatal)) + return -EIO; + } + mmd_mask = mmd_mask >> 1; + fatal_mask = fatal_mask >> 1; + mmd++; + } + + return 0; +} + +int mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask) +{ + int phy_id = efx->mii.phy_id; + int status; + int ok = 1; + int mmd = 0; + int good; + + /* If the port is in loopback, then we should only consider a subset + * of mmd's */ + if (LOOPBACK_INTERNAL(efx)) + return 1; + else if (efx->loopback_mode == LOOPBACK_NETWORK) + return 0; + else if (!efx->phy_powered) + return 0; + else if (efx->loopback_mode == LOOPBACK_PHYXS) + mmd_mask &= ~(MDIO_MMDREG_DEVS0_PHYXS | + MDIO_MMDREG_DEVS0_PCS | + MDIO_MMDREG_DEVS0_PMAPMD); + else if (efx->loopback_mode == LOOPBACK_PCS) + mmd_mask &= ~(MDIO_MMDREG_DEVS0_PCS | + MDIO_MMDREG_DEVS0_PMAPMD); + else if (efx->loopback_mode == LOOPBACK_PMAPMD) + mmd_mask &= ~MDIO_MMDREG_DEVS0_PMAPMD; + + while (mmd_mask) { + if (mmd_mask & 1) { + /* Double reads because link state is latched, and a + * read moves the current state into the register */ + status = mdio_clause45_read(efx, phy_id, + mmd, MDIO_MMDREG_STAT1); + status = mdio_clause45_read(efx, phy_id, + mmd, MDIO_MMDREG_STAT1); + + good = status & (1 << MDIO_MMDREG_STAT1_LINK_LBN); + ok = ok && good; + } + mmd_mask = (mmd_mask >> 1); + mmd++; + } + return ok; +} + +void mdio_clause45_transmit_disable(struct efx_nic *efx, int disable) +{ + int phy_id = efx->mii.phy_id; + int ctrl1, ctrl2; + + ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_MMDREG_TXDIS); + if (disable) + ctrl2 |= (1 << MDIO_MMDREG_TXDIS_GLOBAL_LBN); + else + ctrl1 &= ~(1 << MDIO_MMDREG_TXDIS_GLOBAL_LBN); + if (ctrl1 != ctrl2) + mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_MMDREG_TXDIS, ctrl2); +} + +void mdio_clause45_phy_reconfigure(struct efx_nic *efx) +{ + int phy_id = efx->mii.phy_id; + int ctrl1, ctrl2; + + /* Handle (with debouncing) PMA/PMD loopback */ + ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_MMDREG_CTRL1); + + if (efx->loopback_mode == LOOPBACK_PMAPMD) + ctrl2 |= (1 << MDIO_PMAPMD_CTRL1_LBACK_LBN); + else + ctrl2 &= ~(1 << MDIO_PMAPMD_CTRL1_LBACK_LBN); + + if (ctrl1 != ctrl2) + mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_MMDREG_CTRL1, ctrl2); + + /* Handle (with debouncing) PCS loopback */ + ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PCS, + MDIO_MMDREG_CTRL1); + if (efx->loopback_mode == LOOPBACK_PCS) + ctrl2 |= (1 << MDIO_MMDREG_CTRL1_LBACK_LBN); + else + ctrl2 &= ~(1 << MDIO_MMDREG_CTRL1_LBACK_LBN); + + if (ctrl1 != ctrl2) + mdio_clause45_write(efx, phy_id, MDIO_MMD_PCS, + MDIO_MMDREG_CTRL1, ctrl2); + + /* Handle (with debouncing) PHYXS network loopback */ + ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS, + MDIO_MMDREG_CTRL1); + if (efx->loopback_mode == LOOPBACK_NETWORK) + ctrl2 |= (1 << MDIO_MMDREG_CTRL1_LBACK_LBN); + else + ctrl2 &= ~(1 << MDIO_MMDREG_CTRL1_LBACK_LBN); + + if (ctrl1 != ctrl2) + mdio_clause45_write(efx, phy_id, MDIO_MMD_PHYXS, + MDIO_MMDREG_CTRL1, ctrl2); +} + +static void mdio_clause45_set_mmd_lpower(struct efx_nic *efx, + int lpower, int mmd) +{ + const char *mmdname = STRING_TABLE_LOOKUP(mmd, mmd_block); + int phy = efx->mii.phy_id; + int stat = mdio_clause45_read(efx, phy, mmd, MDIO_MMDREG_STAT1); + int ctrl1, ctrl2; + + EFX_TRACE(efx, "Setting low power mode for MMD %s to %d\n", + mmdname, lpower); + + if (stat & (1 << MDIO_MMDREG_STAT1_LPABLE_LBN)) { + ctrl1 = ctrl2 = mdio_clause45_read(efx, phy, + mmd, MDIO_MMDREG_CTRL1); + if (lpower) + ctrl2 |= (1 << MDIO_MMDREG_CTRL1_LPOWER_LBN); + else + ctrl2 &= ~(1 << MDIO_MMDREG_CTRL1_LPOWER_LBN); + if (ctrl1 != ctrl2) + mdio_clause45_write(efx, phy, mmd, + MDIO_MMDREG_CTRL1, ctrl2); + } else { + /* If we ever want a completely generic PHY driver + * that which just does clause 45, we may consider not + * complaining, but for now expect the driver to know + * which MMDs to apply this to. */ + EFX_ERR(efx, "Attempt change power setting of MMD %s" + " which doesn't support it.\n", mmdname); + } +} + +void mdio_clause45_set_mmds_lpower(struct efx_nic *efx, + int low_power, unsigned int mmd_mask) +{ + int mmd = 0; + while (mmd_mask) { + if (mmd_mask & 1) + mdio_clause45_set_mmd_lpower(efx, low_power, mmd); + mmd_mask = (mmd_mask >> 1); + mmd++; + } +} + +/** + * mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO. + * @efx: Efx NIC + * @ecmd: Buffer for settings + * + * On return the 'port', 'speed', 'supported' and 'advertising' fields of + * ecmd have been filled out based on the PMA type. + */ +void mdio_clause45_get_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd) +{ + int pma_type; + + /* If no PMA is present we are presumably talking something XAUI-ish + * like CX4. Which we report as FIBRE (see below) */ + if ((efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)) == 0) { + ecmd->speed = SPEED_10000; + ecmd->port = PORT_FIBRE; + ecmd->supported = SUPPORTED_FIBRE; + ecmd->advertising = ADVERTISED_FIBRE; + return; + } + + pma_type = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL2); + pma_type &= MDIO_PMAPMD_CTRL2_TYPE_MASK; + + switch (pma_type) { + /* We represent CX4 as fibre in the absence of anything + better. */ + case MDIO_PMAPMD_CTRL2_10G_CX4: + ecmd->speed = SPEED_10000; + ecmd->port = PORT_FIBRE; + ecmd->supported = SUPPORTED_FIBRE; + ecmd->advertising = ADVERTISED_FIBRE; + break; + /* 10G Base-T */ + case MDIO_PMAPMD_CTRL2_10G_BT: + ecmd->speed = SPEED_10000; + ecmd->port = PORT_TP; + ecmd->supported = SUPPORTED_TP | SUPPORTED_10000baseT_Full; + ecmd->advertising = (ADVERTISED_FIBRE + | ADVERTISED_10000baseT_Full); + break; + case MDIO_PMAPMD_CTRL2_1G_BT: + ecmd->speed = SPEED_1000; + ecmd->port = PORT_TP; + ecmd->supported = SUPPORTED_TP | SUPPORTED_1000baseT_Full; + ecmd->advertising = (ADVERTISED_FIBRE + | ADVERTISED_1000baseT_Full); + break; + case MDIO_PMAPMD_CTRL2_100_BT: + ecmd->speed = SPEED_100; + ecmd->port = PORT_TP; + ecmd->supported = SUPPORTED_TP | SUPPORTED_100baseT_Full; + ecmd->advertising = (ADVERTISED_FIBRE + | ADVERTISED_100baseT_Full); + break; + case MDIO_PMAPMD_CTRL2_10_BT: + ecmd->speed = SPEED_10; + ecmd->port = PORT_TP; + ecmd->supported = SUPPORTED_TP | SUPPORTED_10baseT_Full; + ecmd->advertising = ADVERTISED_FIBRE | ADVERTISED_10baseT_Full; + break; + /* All the other defined modes are flavours of + * 10G optical */ + default: + ecmd->speed = SPEED_10000; + ecmd->port = PORT_FIBRE; + ecmd->supported = SUPPORTED_FIBRE; + ecmd->advertising = ADVERTISED_FIBRE; + break; + } +} + +/** + * mdio_clause45_set_settings - Set (some of) the PHY settings over MDIO. + * @efx: Efx NIC + * @ecmd: New settings + * + * Currently this just enforces that we are _not_ changing the + * 'port', 'speed', 'supported' or 'advertising' settings as these + * cannot be changed on any currently supported PHY. + */ +int mdio_clause45_set_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd) +{ + struct ethtool_cmd tmpcmd; + mdio_clause45_get_settings(efx, &tmpcmd); + /* None of the current PHYs support more than one mode + * of operation (and only 10GBT ever will), so keep things + * simple for now */ + if ((ecmd->speed == tmpcmd.speed) && (ecmd->port == tmpcmd.port) && + (ecmd->supported == tmpcmd.supported) && + (ecmd->advertising == tmpcmd.advertising)) + return 0; + return -EOPNOTSUPP; +} Index: head-2008-03-17/drivers/net/sfc/mdio_10g.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/mdio_10g.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,295 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed by Solarflare Communications + * + * 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_MDIO_10G_H +#define EFX_MDIO_10G_H + +/* + * Definitions needed for doing 10G MDIO as specified in clause 45 + * MDIO, which do not appear in Linux yet. Also some helper functions. + */ + +#include "efx.h" +#include "boards.h" + +/* Numbering of the MDIO Manageable Devices (MMDs) */ +/* Physical Medium Attachment/ Physical Medium Dependent sublayer */ +#define MDIO_MMD_PMAPMD (1) +/* WAN Interface Sublayer */ +#define MDIO_MMD_WIS (2) +/* Physical Coding Sublayer */ +#define MDIO_MMD_PCS (3) +/* PHY Extender Sublayer */ +#define MDIO_MMD_PHYXS (4) +/* Extender Sublayer */ +#define MDIO_MMD_DTEXS (5) +/* Transmission convergence */ +#define MDIO_MMD_TC (6) +/* Auto negotiation */ +#define MDIO_MMD_AN (7) + +/* Generic register locations */ +#define MDIO_MMDREG_CTRL1 (0) +#define MDIO_MMDREG_STAT1 (1) +#define MDIO_MMDREG_IDHI (2) +#define MDIO_MMDREG_IDLOW (3) +#define MDIO_MMDREG_SPEED (4) +#define MDIO_MMDREG_DEVS0 (5) +#define MDIO_MMDREG_DEVS1 (6) +#define MDIO_MMDREG_CTRL2 (7) +#define MDIO_MMDREG_STAT2 (8) +#define MDIO_MMDREG_TXDIS (9) + +/* Bits in MMDREG_CTRL1 */ +/* Reset */ +#define MDIO_MMDREG_CTRL1_RESET_LBN (15) +#define MDIO_MMDREG_CTRL1_RESET_WIDTH (1) +/* Loopback */ +/* Note that while WIS, PCS, PHYXS and DTEXS have the loopback bit here, + * the PMA/PMD does not. */ +#define MDIO_MMDREG_CTRL1_LBACK_LBN (14) +#define MDIO_MMDREG_CTRL1_LBACK_WIDTH (1) +/* Low power */ +#define MDIO_MMDREG_CTRL1_LPOWER_LBN (11) +#define MDIO_MMDREG_CTRL1_LPOWER_WIDTH (1) + +/* Bits in MMDREG_STAT1 */ +#define MDIO_MMDREG_STAT1_FAULT_LBN (7) +#define MDIO_MMDREG_STAT1_FAULT_WIDTH (1) +/* Link state */ +#define MDIO_MMDREG_STAT1_LINK_LBN (2) +#define MDIO_MMDREG_STAT1_LINK_WIDTH (1) +/* Low power ability */ +#define MDIO_MMDREG_STAT1_LPABLE_LBN (1) +#define MDIO_MMDREG_STAT1_LPABLE_WIDTH (1) + +/* Bits in ID reg */ +#define MDIO_ID_REV(_id32) (_id32 & 0xf) +#define MDIO_ID_MODEL(_id32) ((_id32 >> 4) & 0x3f) +#define MDIO_ID_OUI(_id32) (_id32 >> 10) + +/* Bits in MMDREG_DEVS0. Someone thoughtfully layed things out + * so the 'bit present' bit number of an MMD is the number of + * that MMD */ +#define DEV_PRESENT_BIT(_b) (1 << _b) + +#define MDIO_MMDREG_DEVS0_DTEXS DEV_PRESENT_BIT(MDIO_MMD_DTEXS) +#define MDIO_MMDREG_DEVS0_PHYXS DEV_PRESENT_BIT(MDIO_MMD_PHYXS) +#define MDIO_MMDREG_DEVS0_PCS DEV_PRESENT_BIT(MDIO_MMD_PCS) +#define MDIO_MMDREG_DEVS0_WIS DEV_PRESENT_BIT(MDIO_MMD_WIS) +#define MDIO_MMDREG_DEVS0_PMAPMD DEV_PRESENT_BIT(MDIO_MMD_PMAPMD) +#define MDIO_MMDREG_DEVS0_AN DEV_PRESENT_BIT(MDIO_MMD_AN) + + +/* Bits in MMDREG_STAT2 */ +#define MDIO_MMDREG_STAT2_PRESENT_VAL (2) +#define MDIO_MMDREG_STAT2_PRESENT_LBN (14) +#define MDIO_MMDREG_STAT2_PRESENT_WIDTH (2) + +/* Bits in MMDREG_TXDIS */ +#define MDIO_MMDREG_TXDIS_GLOBAL_LBN (0) +#define MDIO_MMDREG_TXDIS_GLOBAL_WIDTH (1) + +/* MMD-specific bits, ordered by MMD, then register */ +#define MDIO_PMAPMD_CTRL1_LBACK_LBN (0) +#define MDIO_PMAPMD_CTRL1_LBACK_WIDTH (1) + +/* PMA type (4 bits) */ +#define MDIO_PMAPMD_CTRL2_10G_CX4 (0x0) +#define MDIO_PMAPMD_CTRL2_10G_EW (0x1) +#define MDIO_PMAPMD_CTRL2_10G_LW (0x2) +#define MDIO_PMAPMD_CTRL2_10G_SW (0x3) +#define MDIO_PMAPMD_CTRL2_10G_LX4 (0x4) +#define MDIO_PMAPMD_CTRL2_10G_ER (0x5) +#define MDIO_PMAPMD_CTRL2_10G_LR (0x6) +#define MDIO_PMAPMD_CTRL2_10G_SR (0x7) +/* Reserved */ +#define MDIO_PMAPMD_CTRL2_10G_BT (0x9) +/* Reserved */ +/* Reserved */ +#define MDIO_PMAPMD_CTRL2_1G_BT (0xc) +/* Reserved */ +#define MDIO_PMAPMD_CTRL2_100_BT (0xe) +#define MDIO_PMAPMD_CTRL2_10_BT (0xf) +#define MDIO_PMAPMD_CTRL2_TYPE_MASK (0xf) + +/* PCS 10GBT registers */ +#define MDIO_PCS_10GBT_STATUS (32) +#define MDIO_PCS_10GBT_STATUS2 (33) +#define MDIO_PCS_10GBT_STATUS2_BER_LBN (8) +#define MDIO_PCS_10GBT_STATUS2_BER_WIDTH (6) +#define MDIO_PCS_10GBT_STATUS2_ERR_LBN (0) +#define MDIO_PCS_10GBT_STATUS2_ERR_WIDTH (8) + +/* PHY XGXS lane state */ +#define MDIO_PHYXS_LANE_STATE (0x18) +#define MDIO_PHYXS_LANE_ALIGNED_LBN (12) +#define MDIO_PHYXS_LANE_SYNC0_LBN (0) +#define MDIO_PHYXS_LANE_SYNC1_LBN (1) +#define MDIO_PHYXS_LANE_SYNC2_LBN (2) +#define MDIO_PHYXS_LANE_SYNC3_LBN (3) + +/* AN registers */ +#define MDIO_AN_STATUS (1) +#define MDIO_AN_STATUS_XNP_LBN (7) +#define MDIO_AN_STATUS_PAGE_LBN (6) +#define MDIO_AN_STATUS_AN_DONE_LBN (5) +#define MDIO_AN_STATUS_LP_AN_CAP_LBN (0) + +#define MDIO_AN_10GBT_STATUS (33) +#define MDIO_AN_10GBT_STATUS_MS_FLT_LBN (15) /* MASTER/SLAVE config fault */ +#define MDIO_AN_10GBT_STATUS_MS_LBN (14) /* MASTER/SLAVE config */ +#define MDIO_AN_10GBT_STATUS_LOC_OK_LBN (13) /* Local OK */ +#define MDIO_AN_10GBT_STATUS_REM_OK_LBN (12) /* Remote OK */ +#define MDIO_AN_10GBT_STATUS_LP_10G_LBN (11) /* Link partner is 10GBT capable */ +#define MDIO_AN_10GBT_STATUS_LP_LTA_LBN (10) /* LP loop timing ability */ +#define MDIO_AN_10GBT_STATUS_LP_TRR_LBN (9) /* LP Training Reset Request */ + + +/* Packing of the prt and dev arguments of clause 45 style MDIO into a + * single int so they can be passed into the mdio_read/write functions + * that currently exist. Note that as Falcon is the only current user, + * the packed form is chosen to match what Falcon needs to write into + * a register. This is checked at compile-time so do not change it. If + * your target chip needs things layed out differently you will need + * to unpack the arguments in your chip-specific mdio functions. + */ + /* These are defined by the standard. */ +#define MDIO45_PRT_ID_WIDTH (5) +#define MDIO45_DEV_ID_WIDTH (5) + +/* The prt ID is just packed in immediately to the left of the dev ID */ +#define MDIO45_PRT_DEV_WIDTH (MDIO45_PRT_ID_WIDTH + MDIO45_DEV_ID_WIDTH) + +#define MDIO45_PRT_ID_MASK ((1 << MDIO45_PRT_DEV_WIDTH) - 1) +/* This is the prt + dev extended by 1 bit to hold the 'is clause 45' flag. */ +#define MDIO45_XPRT_ID_WIDTH (MDIO45_PRT_DEV_WIDTH + 1) +#define MDIO45_XPRT_ID_MASK ((1 << MDIO45_XPRT_ID_WIDTH) - 1) +#define MDIO45_XPRT_ID_IS10G (1 << (MDIO45_XPRT_ID_WIDTH - 1)) + + +#define MDIO45_PRT_ID_COMP_LBN MDIO45_DEV_ID_WIDTH +#define MDIO45_PRT_ID_COMP_WIDTH MDIO45_PRT_ID_WIDTH +#define MDIO45_DEV_ID_COMP_LBN 0 +#define MDIO45_DEV_ID_COMP_WIDTH MDIO45_DEV_ID_WIDTH + +/* Compose port and device into a phy_id */ +static inline int mdio_clause45_pack(u8 prt, u8 dev) +{ + efx_dword_t phy_id; + EFX_POPULATE_DWORD_2(phy_id, MDIO45_PRT_ID_COMP, prt, + MDIO45_DEV_ID_COMP, dev); + return MDIO45_XPRT_ID_IS10G | EFX_DWORD_VAL(phy_id); +} + +static inline void mdio_clause45_unpack(u32 val, u8 *prt, u8 *dev) +{ + efx_dword_t phy_id; + EFX_POPULATE_DWORD_1(phy_id, EFX_DWORD_0, val); + *prt = EFX_DWORD_FIELD(phy_id, MDIO45_PRT_ID_COMP); + *dev = EFX_DWORD_FIELD(phy_id, MDIO45_DEV_ID_COMP); +} + +static inline int mdio_clause45_read(struct efx_nic *efx, + u8 prt, u8 dev, u16 addr) +{ + return efx->mii.mdio_read(efx->net_dev, + mdio_clause45_pack(prt, dev), addr); +} + +static inline void mdio_clause45_write(struct efx_nic *efx, + u8 prt, u8 dev, u16 addr, int value) +{ + efx->mii.mdio_write(efx->net_dev, + mdio_clause45_pack(prt, dev), addr, value); +} + + +static inline u32 mdio_clause45_read_id(struct efx_nic *efx, int mmd) +{ + int phy_id = efx->mii.phy_id; + u16 id_low = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_IDLOW); + u16 id_hi = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_IDHI); + return (id_hi << 16) | (id_low); +} + +static inline int mdio_clause45_phyxgxs_lane_sync(struct efx_nic *efx) +{ + int i, sync, lane_status; + + for (i = 0; i < 2; ++i) + lane_status = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PHYXS, + MDIO_PHYXS_LANE_STATE); + + sync = (lane_status & (1 << MDIO_PHYXS_LANE_ALIGNED_LBN)) != 0; + if (!sync) + EFX_INFO(efx, "XGXS lane status: %x\n", lane_status); + return sync; +} + +extern const char *mdio_clause45_mmd_name(int mmd); + +/* + * Reset a specific MMD and wait for reset to clear. + * Return number of spins left (>0) on success, -%ETIMEDOUT on failure. + * + * This function will sleep + */ +extern int mdio_clause45_reset_mmd(struct efx_nic *efx, int mmd, + int spins, int spintime); + +/* As mdio_clause45_check_mmd but for multiple MMDs */ +int mdio_clause45_check_mmds(struct efx_nic *efx, + unsigned int mmd_mask, unsigned int fatal_mask); + +/* Check the link status of specified mmds in bit mask */ +extern int mdio_clause45_links_ok(struct efx_nic *efx, + unsigned int mmd_mask); + +/* Generic transmit disable support though PMAPMD */ +extern void mdio_clause45_transmit_disable(struct efx_nic *efx, + int disable); + +/* Generic part of reconfigure: set/clear loopback bits */ +extern void mdio_clause45_phy_reconfigure(struct efx_nic *efx); + +/* Set the power state of the specified MMDs */ +extern void mdio_clause45_set_mmds_lpower(struct efx_nic *efx, + int low_power, unsigned int mmd_mask); + +/* Read (some of) the PHY settings over MDIO */ +extern void mdio_clause45_get_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd); + +/* Set (some of) the PHY settings over MDIO */ +extern int mdio_clause45_set_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd); + +/* Wait for specified MMDs to exit reset within a timeout */ +extern int mdio_clause45_wait_reset_mmds(struct efx_nic *efx, + unsigned int mmd_mask); + +#endif /* EFX_MDIO_10G_H */ Index: head-2008-03-17/drivers/net/sfc/mentormac.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/mentormac.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,506 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#include "net_driver.h" +#include "gmii.h" +#include "mac.h" + +/* + * Mentor MAC control + */ + +/************************************************************************** + * + * Mentor MAC registers + * + ************************************************************************** + * + * Register addresses are Mentor MAC register numbers. Falcon maps these + * registers in at 16-byte intervals. The mac_writel() and mac_readl() + * methods take care of abstracting away this difference. + */ + +/* GMAC configuration register 1 */ +#define GM_CFG1_REG_MAC 0x00 +#define GM_SW_RST_LBN 31 +#define GM_SW_RST_WIDTH 1 +#define GM_SIM_RST_LBN 30 +#define GM_SIM_RST_WIDTH 1 +#define GM_RST_RX_MAC_CTL_LBN 19 +#define GM_RST_RX_MAC_CTL_WIDTH 1 +#define GM_RST_TX_MAC_CTL_LBN 18 +#define GM_RST_TX_MAC_CTL_WIDTH 1 +#define GM_RST_RX_FUNC_LBN 17 +#define GM_RST_RX_FUNC_WIDTH 1 +#define GM_RST_TX_FUNC_LBN 16 +#define GM_RST_TX_FUNC_WIDTH 1 +#define GM_LOOP_LBN 8 +#define GM_LOOP_WIDTH 1 +#define GM_RX_FC_EN_LBN 5 +#define GM_RX_FC_EN_WIDTH 1 +#define GM_TX_FC_EN_LBN 4 +#define GM_TX_FC_EN_WIDTH 1 +#define GM_SYNC_RXEN_LBN 3 +#define GM_SYNC_RXEN_WIDTH 1 +#define GM_RX_EN_LBN 2 +#define GM_RX_EN_WIDTH 1 +#define GM_SYNC_TXEN_LBN 1 +#define GM_SYNC_TXEN_WIDTH 1 +#define GM_TX_EN_LBN 0 +#define GM_TX_EN_WIDTH 1 + +/* GMAC configuration register 2 */ +#define GM_CFG2_REG_MAC 0x01 +#define GM_PAMBL_LEN_LBN 12 +#define GM_PAMBL_LEN_WIDTH 4 +#define GM_IF_MODE_LBN 8 +#define GM_IF_MODE_WIDTH 2 +#define GM_HUGE_FRM_EN_LBN 5 +#define GM_HUGE_FRM_EN_WIDTH 1 +#define GM_LEN_CHK_LBN 4 +#define GM_LEN_CHK_WIDTH 1 +#define GM_PAD_CRC_EN_LBN 2 +#define GM_PAD_CRC_EN_WIDTH 1 +#define GM_CRC_EN_LBN 1 +#define GM_CRC_EN_WIDTH 1 +#define GM_FD_LBN 0 +#define GM_FD_WIDTH 1 + +/* GMAC maximum frame length register */ +#define GM_MAX_FLEN_REG_MAC 0x04 +#define GM_MAX_FLEN_LBN 0 +#define GM_MAX_FLEN_WIDTH 16 + +/* GMAC MII management configuration register */ +#define GM_MII_MGMT_CFG_REG_MAC 0x08 +#define GM_RST_MII_MGMT_LBN 31 +#define GM_RST_MII_MGMT_WIDTH 1 +#define GM_MGMT_SCAN_AUTO_INC_LBN 5 +#define GM_MGMT_SCAN_AUTO_INC_WIDTH 1 +#define GM_MGMT_PREM_SUPR_LBN 4 +#define GM_MGMT_PREM_SUPR_WIDTH 1 +#define GM_MGMT_CLK_SEL_LBN 0 +#define GM_MGMT_CLK_SEL_WIDTH 3 + +/* GMAC MII management command register */ +#define GM_MII_MGMT_CMD_REG_MAC 0x09 +#define GM_MGMT_SCAN_CYC_LBN 1 +#define GM_MGMT_SCAN_CYC_WIDTH 1 +#define GM_MGMT_RD_CYC_LBN 0 +#define GM_MGMT_RD_CYC_WIDTH 1 + +/* GMAC MII management address register */ +#define GM_MII_MGMT_ADR_REG_MAC 0x0a +#define GM_MGMT_PHY_ADDR_LBN 8 +#define GM_MGMT_PHY_ADDR_WIDTH 5 +#define GM_MGMT_REG_ADDR_LBN 0 +#define GM_MGMT_REG_ADDR_WIDTH 5 + +/* GMAC MII management control register */ +#define GM_MII_MGMT_CTL_REG_MAC 0x0b +#define GM_MGMT_CTL_LBN 0 +#define GM_MGMT_CTL_WIDTH 16 + +/* GMAC MII management status register */ +#define GM_MII_MGMT_STAT_REG_MAC 0x0c +#define GM_MGMT_STAT_LBN 0 +#define GM_MGMT_STAT_WIDTH 16 + +/* GMAC MII management indicators register */ +#define GM_MII_MGMT_IND_REG_MAC 0x0d +#define GM_MGMT_NOT_VLD_LBN 2 +#define GM_MGMT_NOT_VLD_WIDTH 1 +#define GM_MGMT_SCANNING_LBN 1 +#define GM_MGMT_SCANNING_WIDTH 1 +#define GM_MGMT_BUSY_LBN 0 +#define GM_MGMT_BUSY_WIDTH 1 + +/* GMAC station address register 1 */ +#define GM_ADR1_REG_MAC 0x10 +#define GM_HWADDR_5_LBN 24 +#define GM_HWADDR_5_WIDTH 8 +#define GM_HWADDR_4_LBN 16 +#define GM_HWADDR_4_WIDTH 8 +#define GM_HWADDR_3_LBN 8 +#define GM_HWADDR_3_WIDTH 8 +#define GM_HWADDR_2_LBN 0 +#define GM_HWADDR_2_WIDTH 8 + +/* GMAC station address register 2 */ +#define GM_ADR2_REG_MAC 0x11 +#define GM_HWADDR_1_LBN 24 +#define GM_HWADDR_1_WIDTH 8 +#define GM_HWADDR_0_LBN 16 +#define GM_HWADDR_0_WIDTH 8 + +/* GMAC FIFO configuration register 0 */ +#define GMF_CFG0_REG_MAC 0x12 +#define GMF_FTFENRPLY_LBN 20 +#define GMF_FTFENRPLY_WIDTH 1 +#define GMF_STFENRPLY_LBN 19 +#define GMF_STFENRPLY_WIDTH 1 +#define GMF_FRFENRPLY_LBN 18 +#define GMF_FRFENRPLY_WIDTH 1 +#define GMF_SRFENRPLY_LBN 17 +#define GMF_SRFENRPLY_WIDTH 1 +#define GMF_WTMENRPLY_LBN 16 +#define GMF_WTMENRPLY_WIDTH 1 +#define GMF_FTFENREQ_LBN 12 +#define GMF_FTFENREQ_WIDTH 1 +#define GMF_STFENREQ_LBN 11 +#define GMF_STFENREQ_WIDTH 1 +#define GMF_FRFENREQ_LBN 10 +#define GMF_FRFENREQ_WIDTH 1 +#define GMF_SRFENREQ_LBN 9 +#define GMF_SRFENREQ_WIDTH 1 +#define GMF_WTMENREQ_LBN 8 +#define GMF_WTMENREQ_WIDTH 1 +#define GMF_HSTRSTFT_LBN 4 +#define GMF_HSTRSTFT_WIDTH 1 +#define GMF_HSTRSTST_LBN 3 +#define GMF_HSTRSTST_WIDTH 1 +#define GMF_HSTRSTFR_LBN 2 +#define GMF_HSTRSTFR_WIDTH 1 +#define GMF_HSTRSTSR_LBN 1 +#define GMF_HSTRSTSR_WIDTH 1 +#define GMF_HSTRSTWT_LBN 0 +#define GMF_HSTRSTWT_WIDTH 1 + +/* GMAC FIFO configuration register 1 */ +#define GMF_CFG1_REG_MAC 0x13 +#define GMF_CFGFRTH_LBN 16 +#define GMF_CFGFRTH_WIDTH 5 +#define GMF_CFGXOFFRTX_LBN 0 +#define GMF_CFGXOFFRTX_WIDTH 16 + +/* GMAC FIFO configuration register 2 */ +#define GMF_CFG2_REG_MAC 0x14 +#define GMF_CFGHWM_LBN 16 +#define GMF_CFGHWM_WIDTH 6 +#define GMF_CFGLWM_LBN 0 +#define GMF_CFGLWM_WIDTH 6 + +/* GMAC FIFO configuration register 3 */ +#define GMF_CFG3_REG_MAC 0x15 +#define GMF_CFGHWMFT_LBN 16 +#define GMF_CFGHWMFT_WIDTH 6 +#define GMF_CFGFTTH_LBN 0 +#define GMF_CFGFTTH_WIDTH 6 + +/* GMAC FIFO configuration register 4 */ +#define GMF_CFG4_REG_MAC 0x16 +#define GMF_HSTFLTRFRM_LBN 0 +#define GMF_HSTFLTRFRM_WIDTH 18 +#define GMF_HSTFLTRFRM_PAUSE_LBN 12 +#define GMF_HSTFLTRFRM_PAUSE_WIDTH 12 + +/* GMAC FIFO configuration register 5 */ +#define GMF_CFG5_REG_MAC 0x17 +#define GMF_CFGHDPLX_LBN 22 +#define GMF_CFGHDPLX_WIDTH 1 +#define GMF_SRFULL_LBN 21 +#define GMF_SRFULL_WIDTH 1 +#define GMF_HSTSRFULLCLR_LBN 20 +#define GMF_HSTSRFULLCLR_WIDTH 1 +#define GMF_CFGBYTMODE_LBN 19 +#define GMF_CFGBYTMODE_WIDTH 1 +#define GMF_HSTDRPLT64_LBN 18 +#define GMF_HSTDRPLT64_WIDTH 1 +#define GMF_HSTFLTRFRMDC_LBN 0 +#define GMF_HSTFLTRFRMDC_WIDTH 18 +#define GMF_HSTFLTRFRMDC_PAUSE_LBN 12 +#define GMF_HSTFLTRFRMDC_PAUSE_WIDTH 1 + +/* TX total octet count */ +#define GM_TX_OCT_CNT_REG_MAC 0x40 +#define GM_STAT_LBN 0 +#define GM_STAT_WIDTH 32 + +/* TX good octet count */ +#define GM_TX_GOOD_OCT_CNT_REG_MAC 0x41 + +/* TX single collision packet count */ +#define GM_TX_SGLCOL_PKT_CNT_REG_MAC 0x42 + +/* TX multiple collision packet count */ +#define GM_TX_MULTCOL_PKT_CNT_REG_MAC 0x43 + +/* TX excessive collision packet count */ +#define GM_TX_EXCOL_PKT_CNT_REG_MAC 0x44 + +/* TX deferred packet count */ +#define GM_TX_DEF_PKT_CNT_REG_MAC 0x45 + +/* TX late packet count */ +#define GM_TX_LATECOL_PKT_CNT_REG_MAC 0x46 + +/* TX excessive deferral packet count */ +#define GM_TX_EXDEF_PKT_CNT_REG_MAC 0x47 + +/* TX pause packet count */ +#define GM_TX_PAUSE_PKT_CNT_REG_MAC 0x48 + +/* TX bad packet count */ +#define GM_TX_BAD_PKT_CNT_REG_MAC 0x49 + +/* TX unicast packet count */ +#define GM_TX_UCAST_PKT_CNT_REG_MAC 0x4a + +/* TX multicast packet count */ +#define GM_TX_MCAST_PKT_CNT_REG_MAC 0x4b + +/* TX broadcast packet count */ +#define GM_TX_BCAST_PKT_CNT_REG_MAC 0x4c + +/* TX <64-byte packet count */ +#define GM_TX_LT64_PKT_CNT_REG_MAC 0x4d + +/* TX 64-byte packet count */ +#define GM_TX_64_PKT_CNT_REG_MAC 0x4e + +/* TX 65-byte to 127-byte packet count */ +#define GM_TX_65_TO_127_PKT_CNT_REG_MAC 0x4f + +/* TX 128-byte to 255-byte packet count */ +#define GM_TX_128_TO_255_PKT_CNT_REG_MAC 0x50 + +/* TX 256-byte to 511-byte packet count */ +#define GM_TX_256_TO_511_PKT_CNT_REG_MAC 0x51 + +/* TX 512-byte to 1023-byte packet count */ +#define GM_TX_512_TO_1023_PKT_CNT_REG_MAC 0x52 + +/* TX 1024-byte to 15xx-byte packet count */ +#define GM_TX_1024_TO_15XX_PKT_CNT_REG_MAC 0x53 + +/* TX 15xx-byte to jumbo packet count */ +#define GM_TX_15XX_TO_JUMBO_PKT_CNT_REG_MAC 0x54 + +/* TX >jumbo packet count */ +#define GM_TX_GTJUMBO_PKT_CNT_REG_MAC 0x55 + +/* RX good octet count */ +#define GM_RX_GOOD_OCT_CNT_REG_MAC 0x60 + +/* RX bad octet count */ +#define GM_RX_BAD_OCT_CNT_REG_MAC 0x61 + +/* RX missed packet count */ +#define GM_RX_MISS_PKT_CNT_REG_MAC 0x62 + +/* RX false carrier count */ +#define GM_RX_FALSE_CRS_CNT_REG_MAC 0x63 + +/* RX pause packet count */ +#define GM_RX_PAUSE_PKT_CNT_REG_MAC 0x64 + +/* RX bad packet count */ +#define GM_RX_BAD_PKT_CNT_REG_MAC 0x65 + +/* RX unicast packet count */ +#define GM_RX_UCAST_PKT_CNT_REG_MAC 0x66 + +/* RX multicast packet count */ +#define GM_RX_MCAST_PKT_CNT_REG_MAC 0x67 + +/* RX broadcast packet count */ +#define GM_RX_BCAST_PKT_CNT_REG_MAC 0x68 + +/* RX <64-byte good packet count */ +#define GM_RX_GOOD_LT64_PKT_CNT_REG_MAC 0x69 + +/* RX <64-byte bad packet count */ +#define GM_RX_BAD_LT64_PKT_CNT_REG_MAC 0x6a + +/* RX 64-byte packet count */ +#define GM_RX_64_PKT_CNT_REG_MAC 0x6b + +/* RX 65-byte to 127-byte packet count */ +#define GM_RX_65_TO_127_PKT_CNT_REG_MAC 0x6c + +/* RX 128-byte to 255-byte packet count*/ +#define GM_RX_128_TO_255_PKT_CNT_REG_MAC 0x6d + +/* RX 256-byte to 511-byte packet count */ +#define GM_RX_256_TO_511_PKT_CNT_REG_MAC 0x6e + +/* RX 512-byte to 1023-byte packet count */ +#define GM_RX_512_TO_1023_PKT_CNT_REG_MAC 0x6f + +/* RX 1024-byte to 15xx-byte packet count */ +#define GM_RX_1024_TO_15XX_PKT_CNT_REG_MAC 0x70 + +/* RX 15xx-byte to jumbo packet count */ +#define GM_RX_15XX_TO_JUMBO_PKT_CNT_REG_MAC 0x71 + +/* RX >jumbo packet count */ +#define GM_RX_GTJUMBO_PKT_CNT_REG_MAC 0x72 + +/* RX 64-byte to 15xx-byte bad crc packet count */ +#define GM_RX_BAD_64_TO_15XX_PKT_CNT_REG_MAC 0x73 + +/* RX 15xx-byte to jumbo bad crc packet count */ +#define GM_RX_BAD_15XX_TO_JUMBO_PKT_CNT_REG_MAC 0x74 + +/* RX >jumbo bad crc packet count */ +#define GM_RX_BAD_GTJUMBO_PKT_CNT_REG_MAC 0x75 + +/************************************************************************** + * + * GMII access to PHY + * + ************************************************************************** + */ + +/* This does not reset the PHY, only the MAC. However, TX and RX will + * both be disabled on the MAC after this, so the state of the PHY is + * somewhat irrelevant until the MAC is reinitialised. + */ +void mentormac_reset(struct efx_nic *efx) +{ + efx_dword_t reg; + + EFX_POPULATE_DWORD_1(reg, GM_SW_RST, 1); + efx->mac_op->mac_writel(efx, ®, GM_CFG1_REG_MAC); + udelay(1000); + + EFX_POPULATE_DWORD_1(reg, GM_SW_RST, 0); + efx->mac_op->mac_writel(efx, ®, GM_CFG1_REG_MAC); + udelay(1000); + + /* Configure GMII interface so PHY is accessible */ + EFX_POPULATE_DWORD_1(reg, GM_MGMT_CLK_SEL, 0x4); + efx->mac_op->mac_writel(efx, ®, + GM_MII_MGMT_CFG_REG_MAC); + udelay(10); +} + +void mentormac_reconfigure(struct efx_nic *efx) +{ + int loopback, tx_fc, rx_fc, if_mode, full_duplex, bytemode, half_duplex; + unsigned int max_frame_len; + efx_dword_t reg; + + /* Configuration register 1 */ + tx_fc = (efx->flow_control & EFX_FC_TX) ? 1 : 0; + rx_fc = (efx->flow_control & EFX_FC_RX) ? 1 : 0; + loopback = (efx->loopback_mode == LOOPBACK_MAC) ? 1 : 0; + bytemode = (efx->link_options & GM_LPA_1000) ? 1 : 0; + + if (efx->loopback_mode != LOOPBACK_NONE) + bytemode = 1; + if (!(efx->link_options & GM_LPA_DUPLEX)) + /* Half-duplex operation requires TX flow control */ + tx_fc = 1; + EFX_POPULATE_DWORD_5(reg, + GM_LOOP, loopback, + GM_TX_EN, 1, + GM_TX_FC_EN, tx_fc, + GM_RX_EN, 1, + GM_RX_FC_EN, rx_fc); + efx->mac_op->mac_writel(efx, ®, GM_CFG1_REG_MAC); + udelay(10); + + /* Configuration register 2 */ + if_mode = (bytemode) ? 2 : 1; + full_duplex = (efx->link_options & GM_LPA_DUPLEX) ? 1 : 0; + EFX_POPULATE_DWORD_4(reg, + GM_IF_MODE, if_mode, + GM_PAD_CRC_EN, 1, + GM_FD, full_duplex, + GM_PAMBL_LEN, 0x7/*datasheet recommended */); + + efx->mac_op->mac_writel(efx, ®, GM_CFG2_REG_MAC); + udelay(10); + + /* Max frame len register */ + max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu); + EFX_POPULATE_DWORD_1(reg, GM_MAX_FLEN, max_frame_len); + efx->mac_op->mac_writel(efx, ®, GM_MAX_FLEN_REG_MAC); + udelay(10); + + /* FIFO configuration register 0 */ + EFX_POPULATE_DWORD_5(reg, + GMF_FTFENREQ, 1, + GMF_STFENREQ, 1, + GMF_FRFENREQ, 1, + GMF_SRFENREQ, 1, + GMF_WTMENREQ, 1); + efx->mac_op->mac_writel(efx, ®, GMF_CFG0_REG_MAC); + udelay(10); + + /* FIFO configuration register 1 */ + EFX_POPULATE_DWORD_2(reg, + GMF_CFGFRTH, 0x12, + GMF_CFGXOFFRTX, 0xffff); + efx->mac_op->mac_writel(efx, ®, GMF_CFG1_REG_MAC); + udelay(10); + + /* FIFO configuration register 2 */ + EFX_POPULATE_DWORD_2(reg, + GMF_CFGHWM, 0x3f, + GMF_CFGLWM, 0xa); + efx->mac_op->mac_writel(efx, ®, GMF_CFG2_REG_MAC); + udelay(10); + + /* FIFO configuration register 3 */ + EFX_POPULATE_DWORD_2(reg, + GMF_CFGHWMFT, 0x1c, + GMF_CFGFTTH, 0x08); + efx->mac_op->mac_writel(efx, ®, GMF_CFG3_REG_MAC); + udelay(10); + + /* FIFO configuration register 4 */ + EFX_POPULATE_DWORD_1(reg, GMF_HSTFLTRFRM_PAUSE, 1); + efx->mac_op->mac_writel(efx, ®, GMF_CFG4_REG_MAC); + udelay(10); + + /* FIFO configuration register 5 */ + half_duplex = (efx->link_options & GM_LPA_DUPLEX) ? 0 : 1; + efx->mac_op->mac_readl(efx, ®, GMF_CFG5_REG_MAC); + EFX_SET_DWORD_FIELD(reg, GMF_CFGBYTMODE, bytemode); + EFX_SET_DWORD_FIELD(reg, GMF_CFGHDPLX, half_duplex); + EFX_SET_DWORD_FIELD(reg, GMF_HSTDRPLT64, half_duplex); + EFX_SET_DWORD_FIELD(reg, GMF_HSTFLTRFRMDC_PAUSE, 0); + efx->mac_op->mac_writel(efx, ®, GMF_CFG5_REG_MAC); + udelay(10); + + /* MAC address */ + EFX_POPULATE_DWORD_4(reg, + GM_HWADDR_5, efx->net_dev->dev_addr[5], + GM_HWADDR_4, efx->net_dev->dev_addr[4], + GM_HWADDR_3, efx->net_dev->dev_addr[3], + GM_HWADDR_2, efx->net_dev->dev_addr[2]); + efx->mac_op->mac_writel(efx, ®, GM_ADR1_REG_MAC); + udelay(10); + EFX_POPULATE_DWORD_2(reg, + GM_HWADDR_1, efx->net_dev->dev_addr[1], + GM_HWADDR_0, efx->net_dev->dev_addr[0]); + efx->mac_op->mac_writel(efx, ®, GM_ADR2_REG_MAC); + udelay(10); +} Index: head-2008-03-17/drivers/net/sfc/mtd.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/mtd.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,598 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#include +#include +#include + +#define EFX_DRIVER_NAME "sfc_mtd" +#include "driverlink_api.h" +#include "net_driver.h" +#include "spi.h" + +/* + * Flash and EEPROM (MTD) device driver + * + * This file provides a separate kernel module (sfc_mtd) which + * exposes the flash and EEPROM devices present on Solarflare NICs as + * MTD devices, enabling you to reflash the boot ROM code (or use the + * remaining space on the flash as a jffs2 filesystem, should you want + * to do so). + */ + +#define EFX_MTD_VERIFY_BUF_LEN 16 +#define EFX_MAX_PARTITIONS 2 +#define EFX_FLASH_BOOTROM_OFFSET 0x8000U + +/* Write enable for EEPROM/flash configuration area + * + * Normally, writes to parts of non-volatile storage which contain + * critical configuration are disabled to prevent accidents. This + * parameter allows enabling of such writes. + */ +static unsigned int efx_allow_nvconfig_writes; + +struct efx_mtd { + struct mtd_info mtd; + struct mtd_partition part[EFX_MAX_PARTITIONS]; + char part_name[EFX_MAX_PARTITIONS][32]; + char name[32]; + struct efx_dl_device *efx_dev; + struct efx_nic *efx; + /* This must be held when using *spi; it guards against races + * with device reset and between sequences of dependent + * commands. */ + struct semaphore access_lock; + struct efx_spi_device *spi; +}; + +/* SPI utilities */ + +static int efx_spi_fast_wait(struct efx_mtd *efx_mtd) +{ + struct efx_spi_device *spi = efx_mtd->spi; + u8 status; + int i, rc; + + /* Wait up to 1000us for flash/EEPROM to finish a fast operation. */ + for (i = 0; i < 50; i++) { + udelay(20); + + rc = spi->read(spi, efx_mtd->efx, SPI_RDSR, -1, + &status, sizeof(status)); + if (rc) + return rc; + if (!(status & SPI_STATUS_NRDY)) + return 0; + } + EFX_ERR(efx_mtd->efx, "timed out waiting for %s last status=0x%02x\n", + efx_mtd->name, status); + return -ETIMEDOUT; +} + +static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, int uninterruptible) +{ + struct efx_spi_device *spi = efx_mtd->spi; + u8 status; + int rc, i; + + /* Wait up to 4s for flash/EEPROM to finish a slow operation. */ + for (i = 0; i < 40; i++) { + __set_current_state(uninterruptible ? + TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + rc = spi->read(spi, efx_mtd->efx, SPI_RDSR, -1, + &status, sizeof(status)); + if (rc) + return rc; + if (!(status & SPI_STATUS_NRDY)) + return 0; + if (signal_pending(current)) + return -EINTR; + } + EFX_ERR(efx_mtd->efx, "timed out waiting for %s\n", efx_mtd->name); + return -ETIMEDOUT; +} + +static int +efx_spi_write_enable(struct efx_mtd *efx_mtd) +{ + struct efx_spi_device *spi = efx_mtd->spi; + + return spi->write(spi, efx_mtd->efx, SPI_WREN, -1, NULL, 0); +} + +static int efx_spi_unlock(struct efx_mtd *efx_mtd) +{ + struct efx_spi_device *spi = efx_mtd->spi; + const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 | + SPI_STATUS_BP0); + u8 status; + int rc; + + rc = spi->read(spi, efx_mtd->efx, SPI_RDSR, -1, &status, + sizeof(status)); + if (rc) + return rc; + + if (!(status & unlock_mask)) + return 0; /* already unlocked */ + + rc = efx_spi_write_enable(efx_mtd); + if (rc) + return rc; + rc = spi->write(spi, efx_mtd->efx, SPI_SST_EWSR, -1, NULL, 0); + if (rc) + return rc; + + status &= ~unlock_mask; + rc = spi->write(spi, efx_mtd->efx, SPI_WRSR, -1, &status, + sizeof(status)); + if (rc) + return rc; + rc = efx_spi_fast_wait(efx_mtd); + if (rc) + return rc; + + return 0; +} + +/* Dummy device used in case of a failed reset */ + +static int efx_spi_dummy_read(const struct efx_spi_device *spi, + struct efx_nic *efx, unsigned int command, + int address, void *data, unsigned int len) +{ + return -EIO; +} + +static int efx_spi_dummy_write(const struct efx_spi_device *spi, + struct efx_nic *efx, unsigned int command, + int address, const void *data, unsigned int len) +{ + return -EIO; +} + +static struct efx_spi_device efx_spi_dummy_device = { + .block_size = 1, + .erase_command = 0xff, + .read = efx_spi_dummy_read, + .write = efx_spi_dummy_write, +}; + +/* MTD interface */ + +static int efx_mtd_read(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, u8 *buffer) +{ + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_spi_device *spi; + unsigned int command; + unsigned int block_len; + unsigned int pos = 0; + int rc; + + rc = down_interruptible(&efx_mtd->access_lock); + if (rc) + goto out; + spi = efx_mtd->spi; + + while (pos < len) { + block_len = min((unsigned int)len - pos, + efx_spi_read_limit(spi, start + pos)); + command = efx_spi_munge_command(spi, SPI_READ, start + pos); + rc = spi->read(spi, efx_mtd->efx, command, start + pos, + buffer + pos, block_len); + if (rc) + break; + pos += block_len; + + /* Avoid locking up the system */ + cond_resched(); + if (signal_pending(current)) { + rc = -EINTR; + break; + } + } + + up(&efx_mtd->access_lock); +out: + *retlen = pos; + return rc; +} + +/* Check that device contents match buffer. If repeat is true, buffer + * contains a pattern of length EFX_MTD_VERIFY_BUF_LEN which the + * device contents should match repeatedly. + */ +static int efx_mtd_verify(struct mtd_info *mtd, loff_t start, + size_t len, const u8 *buffer, int repeat) +{ + u8 verify_buffer[EFX_MTD_VERIFY_BUF_LEN]; + unsigned int block_len; + size_t read_len; + unsigned int pos = 0; + int rc = 0; + + while (pos < len) { + block_len = min(len - pos, sizeof(verify_buffer)); + rc = efx_mtd_read(mtd, start + pos, block_len, &read_len, + verify_buffer); + if (rc) + return rc; + if (memcmp(repeat ? buffer : buffer + pos, verify_buffer, + block_len)) + return -EIO; + pos += block_len; + } + + return 0; +} + +static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_spi_device *spi; + u8 empty[EFX_MTD_VERIFY_BUF_LEN]; + int rc; + + if (erase->len != mtd->erasesize) { + rc = -EINVAL; + goto out; + } + + rc = down_interruptible(&efx_mtd->access_lock); + if (rc) + goto out; + spi = efx_mtd->spi; + if (spi->erase_command == 0) { + rc = -EOPNOTSUPP; + goto out_up; + } + + rc = efx_spi_unlock(efx_mtd); + if (rc) + goto out_up; + rc = efx_spi_write_enable(efx_mtd); + if (rc) + goto out_up; + rc = spi->write(spi, efx_mtd->efx, spi->erase_command, erase->addr, + NULL, 0); + if (rc) + goto out_up; + rc = efx_spi_slow_wait(efx_mtd, 0); + +out_up: + up(&efx_mtd->access_lock); + if (rc) + goto out; + + memset(empty, 0xff, sizeof(empty)); + rc = efx_mtd_verify(mtd, erase->addr, erase->len, empty, 1); + +out: + if (rc == 0) { + erase->state = MTD_ERASE_DONE; + } else { + erase->state = MTD_ERASE_FAILED; + erase->fail_addr = 0xffffffff; + } + mtd_erase_callback(erase); + return rc; +} + +static int efx_mtd_write(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, const u8 *buffer) +{ + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_spi_device *spi; + unsigned int command; + unsigned int block_len; + unsigned int pos = 0; + int rc; + + rc = down_interruptible(&efx_mtd->access_lock); + if (rc) + goto out; + spi = efx_mtd->spi; + + rc = efx_spi_unlock(efx_mtd); + if (rc) + goto out_up; + + while (pos < len) { + rc = efx_spi_write_enable(efx_mtd); + if (rc) + break; + + block_len = min((unsigned int)len - pos, + efx_spi_write_limit(spi, start + pos)); + command = efx_spi_munge_command(spi, SPI_WRITE, start + pos); + rc = spi->write(spi, efx_mtd->efx, command, start + pos, + buffer + pos, block_len); + if (rc) + break; + pos += block_len; + + rc = efx_spi_fast_wait(efx_mtd); + if (rc) + break; + + /* Avoid locking up the system */ + cond_resched(); + if (signal_pending(current)) { + rc = -EINTR; + break; + } + } + +out_up: + up(&efx_mtd->access_lock); + if (rc == 0) + rc = efx_mtd_verify(mtd, start, len, buffer, 0); +out: + *retlen = pos; + return rc; +} + +static void efx_mtd_sync(struct mtd_info *mtd) +{ + struct efx_mtd *efx_mtd = mtd->priv; + int rc; + + down(&efx_mtd->access_lock); + rc = efx_spi_slow_wait(efx_mtd, 1); + if (rc) + EFX_ERR(efx_mtd->efx, "%s sync failed (%d)\n", + efx_mtd->name, rc); + up(&efx_mtd->access_lock); +} + +/* Driverlink interface */ + +static void efx_mtd_reset_suspend(struct efx_dl_device *efx_dev) +{ + struct efx_mtd *efx_mtd = efx_dev->priv; + + if (!efx_mtd) + return; + + /* Acquire lock to ensure that any in-progress operations have + * completed, and no new ones can start. + */ + down(&efx_mtd->access_lock); +} + +static void efx_mtd_reset_resume(struct efx_dl_device *efx_dev, int ok) +{ + struct efx_mtd *efx_mtd = efx_dev->priv; + + if (!efx_mtd) + return; + + /* If device reset failed already, or SPI device doesn't + * become ready, disable device. + */ + if (!ok || efx_spi_slow_wait(efx_mtd, 1) != 0) { + efx_mtd->spi = &efx_spi_dummy_device; + EFX_ERR(efx_mtd->efx, "%s disabled after failed reset\n", + efx_mtd->name); + } + + up(&efx_mtd->access_lock); +} + +static void efx_mtd_remove(struct efx_dl_device *efx_dev) +{ + struct efx_mtd *efx_mtd = efx_dev->priv; + + del_mtd_partitions(&efx_mtd->mtd); + kfree(efx_mtd); + efx_dev->priv = NULL; +} + +static __devinit int efx_mtd_register(struct efx_mtd *efx_mtd, + struct efx_dl_device *efx_dev, + struct efx_nic *efx, + struct efx_spi_device *spi, + const char *type_name, + const char **part_type_name, + unsigned int num_parts) +{ + int i; + + efx_dev->priv = efx_mtd; + + efx_mtd->efx_dev = efx_dev; + efx_mtd->efx = efx; + efx_mtd->spi = spi; + sema_init(&efx_mtd->access_lock, 1); + + efx_mtd->mtd.size = spi->size; + efx_mtd->mtd.erasesize = spi->erase_size; + efx_mtd->mtd.writesize = 1; + if (snprintf(efx_mtd->name, sizeof(efx_mtd->name), + "%s %s", efx->name, type_name) >= + sizeof(efx_mtd->name)) + return -ENAMETOOLONG; + + efx_mtd->mtd.priv = efx_mtd; + efx_mtd->mtd.name = efx_mtd->name; + efx_mtd->mtd.erase = efx_mtd_erase; + efx_mtd->mtd.read = efx_mtd_read; + efx_mtd->mtd.write = efx_mtd_write; + efx_mtd->mtd.sync = efx_mtd_sync; + + for (i = 0; i < num_parts; i++) { + efx_mtd->part[i].name = efx_mtd->part_name[i]; + if (snprintf(efx_mtd->part_name[i], + sizeof(efx_mtd->part_name[i]), + "%s %s", efx->name, part_type_name[i]) >= + sizeof(efx_mtd->part_name[i])) + return -ENAMETOOLONG; + + if (efx_allow_nvconfig_writes) + efx_mtd->part[i].mask_flags &= ~MTD_WRITEABLE; + } + + return add_mtd_partitions(&efx_mtd->mtd, efx_mtd->part, num_parts); +} + +static int __devinit +efx_flash_probe(struct efx_dl_device *efx_dev, + const struct net_device *net_dev, + const struct efx_dl_device_info *dev_info, + const char *silicon_rev) +{ + struct efx_nic *efx = efx_dl_get_nic(efx_dev); + struct efx_mtd *efx_mtd; + const char *part_type_name[2]; + unsigned int num_parts; + int rc; + + if (!efx->spi_flash) + return -ENODEV; + + efx_mtd = kzalloc(sizeof(*efx_mtd), GFP_KERNEL); + if (!efx_mtd) + return -ENOMEM; + + efx_mtd->mtd.type = MTD_NORFLASH; + efx_mtd->mtd.flags = MTD_CAP_NORFLASH; + + part_type_name[0] = "sfc_flash_config"; + efx_mtd->part[0].offset = 0; + efx_mtd->part[0].size = min(efx->spi_flash->size, + EFX_FLASH_BOOTROM_OFFSET); + efx_mtd->part[0].mask_flags = MTD_WRITEABLE; + + if (efx->spi_flash->size <= EFX_FLASH_BOOTROM_OFFSET) { + num_parts = 1; + } else { + part_type_name[1] = "sfc_flash_bootrom"; + efx_mtd->part[1].offset = EFX_FLASH_BOOTROM_OFFSET; + efx_mtd->part[1].size = (efx->spi_flash->size + - EFX_FLASH_BOOTROM_OFFSET); + num_parts = 2; + } + + rc = efx_mtd_register(efx_mtd, efx_dev, efx, efx->spi_flash, + "sfc_flash", part_type_name, num_parts); + if (rc) + kfree(efx_mtd); + return rc; +} + +static struct efx_dl_driver efx_flash_driver = { + .name = "sfc_flash", + .probe = efx_flash_probe, + .remove = efx_mtd_remove, + .reset_suspend = efx_mtd_reset_suspend, + .reset_resume = efx_mtd_reset_resume, +}; + +static int __devinit +efx_eeprom_probe(struct efx_dl_device *efx_dev, + const struct net_device *net_dev, + const struct efx_dl_device_info *dev_info, + const char *silicon_rev) +{ + struct efx_nic *efx = efx_dl_get_nic(efx_dev); + struct efx_mtd *efx_mtd; + const char *type_name; + const char *part_type_name[1]; + int rc; + + if (!efx->spi_eeprom) + return -ENODEV; + + efx_mtd = kzalloc(sizeof(*efx_mtd), GFP_KERNEL); + if (!efx_mtd) + return -ENOMEM; + + efx_mtd->mtd.type = MTD_RAM; + efx_mtd->mtd.flags = MTD_CAP_RAM; + + efx_mtd->part[0].offset = 0; + efx_mtd->part[0].size = efx->spi_eeprom->size; + efx_mtd->part[0].mask_flags = MTD_WRITEABLE; + + if (efx->spi_eeprom->size <= 0x200) { + type_name = "sfc_small_eeprom"; + part_type_name[0] = "sfc_small_config"; + } else { + type_name = "sfc_large_eeprom"; + part_type_name[0] = "sfc_large_config"; + } + + rc = efx_mtd_register(efx_mtd, efx_dev, efx, efx->spi_eeprom, + type_name, part_type_name, 1); + if (rc) + kfree(efx_mtd); + return rc; +} + +static struct efx_dl_driver efx_eeprom_driver = { + .name = "sfc_eeprom", + .probe = efx_eeprom_probe, + .remove = efx_mtd_remove, + .reset_suspend = efx_mtd_reset_suspend, + .reset_resume = efx_mtd_reset_resume, +}; + +/* Kernel module interface */ + +static int __init efx_mtd_init_module(void) +{ + int rc; + + rc = efx_dl_register_driver(&efx_flash_driver); + if (rc) + return rc; + rc = efx_dl_register_driver(&efx_eeprom_driver); + if (rc) { + efx_dl_unregister_driver(&efx_flash_driver); + return rc; + } + + return 0; +} + +static void __exit efx_mtd_exit_module(void) +{ + efx_dl_unregister_driver(&efx_eeprom_driver); + efx_dl_unregister_driver(&efx_flash_driver); +} + +module_init(efx_mtd_init_module); +module_exit(efx_mtd_exit_module); + +MODULE_AUTHOR("Michael Brown and " + "Solarflare Communications"); +MODULE_DESCRIPTION("SFC MTD driver"); +MODULE_LICENSE("GPL"); Index: head-2008-03-17/drivers/net/sfc/net_driver.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/net_driver.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,1079 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 + **************************************************************************** + */ + +/* Common definitions for all Efx net driver code */ + +#ifndef EFX_NET_DRIVER_H +#define EFX_NET_DRIVER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "enum.h" +#include "bitfield.h" +#include "driverlink_api.h" +#include "driverlink.h" +#include "i2c-direct.h" + +#define EFX_MAX_LRO_DESCRIPTORS 8 +#define EFX_MAX_LRO_AGGR MAX_SKB_FRAGS + +/************************************************************************** + * + * Build definitions + * + **************************************************************************/ +#ifndef EFX_DRIVER_NAME +#define EFX_DRIVER_NAME "sfc" +#endif +#define EFX_DRIVER_VERSION "2.2.0056" + +#ifdef EFX_ENABLE_DEBUG +#define EFX_BUG_ON_PARANOID(x) BUG_ON(x) +#define EFX_WARN_ON_PARANOID(x) WARN_ON(x) +#else +#define EFX_BUG_ON_PARANOID(x) do {} while (0) +#define EFX_WARN_ON_PARANOID(x) do {} while (0) +#endif + +/* Include net device name in log messages if it has been registered. + * Use efx->name not efx->net_dev->name so that races with (un)registration + * are harmless. + */ +#define NET_DEV_NAME(efx) ((efx)->net_dev_registered ? (efx)->name : "") + +/* Un-rate-limited logging */ +#define EFX_ERR(efx, fmt, args...) \ +dev_err(&((efx)->pci_dev->dev), "ERR: %s " fmt, NET_DEV_NAME(efx), ##args) + +#define EFX_INFO(efx, fmt, args...) \ +dev_info(&((efx)->pci_dev->dev), "INFO: %s " fmt, NET_DEV_NAME(efx), ##args) + +#ifdef EFX_ENABLE_DEBUG +#define EFX_LOG(efx, fmt, args...) \ +dev_info(&((efx)->pci_dev->dev), "DBG: %s " fmt, NET_DEV_NAME(efx), ##args) +#else +#define EFX_LOG(efx, fmt, args...) \ +dev_dbg(&((efx)->pci_dev->dev), "DBG: %s " fmt, NET_DEV_NAME(efx), ##args) +#endif + +#define EFX_TRACE(efx, fmt, args...) do {} while (0) + +#define EFX_REGDUMP(efx, fmt, args...) do {} while (0) + +/* Rate-limited logging */ +#define EFX_ERR_RL(efx, fmt, args...) \ +do {if (net_ratelimit()) EFX_ERR(efx, fmt, ##args); } while (0) + +#define EFX_INFO_RL(efx, fmt, args...) \ +do {if (net_ratelimit()) EFX_INFO(efx, fmt, ##args); } while (0) + +#define EFX_LOG_RL(efx, fmt, args...) \ +do {if (net_ratelimit()) EFX_LOG(efx, fmt, ##args); } while (0) + +/* Kernel headers may redefine inline anyway */ +#ifndef inline +#define inline inline __attribute__ ((always_inline)) +#endif + +/************************************************************************** + * + * Efx data structures + * + **************************************************************************/ + +#define EFX_MAX_CHANNELS 32 +#define EFX_MAX_TX_QUEUES 1 +#define EFX_MAX_RX_QUEUES EFX_MAX_CHANNELS + +/** + * struct efx_special_buffer - An Efx special buffer + * @addr: CPU base address of the buffer + * @dma_addr: DMA base address of the buffer + * @len: Buffer length, in bytes + * @index: Buffer index within controller;s buffer table + * @entries: Number of buffer table entries + * + * Special buffers are used for the event queues and the TX and RX + * descriptor queues for each channel. They are *not* used for the + * actual transmit and receive buffers. + * + * Note that for Falcon, TX and RX descriptor queues live in host memory. + * Allocation and freeing procedures must take this into account. + */ +struct efx_special_buffer { + void *addr; + dma_addr_t dma_addr; + unsigned int len; + int index; + int entries; +}; + +/** + * struct efx_tx_buffer - An Efx TX buffer + * @skb: The associated socket buffer. + * Set only on the final fragment of a packet; %NULL for all other + * fragments. When this fragment completes, then we can free this + * skb. + * @dma_addr: DMA address of the fragment. + * @len: Length of this fragment. + * This field is zero when the queue slot is empty. + * @continuation: True if this fragment is not the end of a packet. + * @unmap_single: True if pci_unmap_single should be used. + * @unmap_addr: DMA address to unmap + * @unmap_len: Length of this fragment to unmap + */ +struct efx_tx_buffer { + const struct sk_buff *skb; + dma_addr_t dma_addr; + unsigned short len; + unsigned char continuation; + unsigned char unmap_single; + dma_addr_t unmap_addr; + unsigned short unmap_len; +}; + +/** + * struct efx_tx_queue - An Efx TX queue + * + * This is a ring buffer of TX fragments. + * Since the TX completion path always executes on the same + * CPU and the xmit path can operate on different CPUs, + * performance is increased by ensuring that the completion + * path and the xmit path operate on different cache lines. + * This is particularly important if the xmit path is always + * executing on one CPU which is different from the completion + * path. There is also a cache line for members which are + * read but not written on the fast path. + * + * @efx: The associated Efx NIC + * @queue: DMA queue number + * @used: Queue is used by net driver + * @channel: The associated channel + * @buffer: The software buffer ring + * @txd: The hardware descriptor ring + * @debug_dir: debugfs directory + * @read_count: Current read pointer. + * This is the number of buffers that have been removed from both rings. + * @stopped: Stopped flag. + * Set if this TX queue is currently stopping its port. + * @insert_count: Current insert pointer + * This is the number of buffers that have been added to the + * software ring. + * @write_count: Current write pointer + * This is the number of buffers that have been added to the + * hardware ring. + * @old_read_count: The value of read_count when last checked. + * This is here for performance reasons. The xmit path will + * only get the up-to-date value of read_count if this + * variable indicates that the queue is full. This is to + * avoid cache-line ping-pong between the xmit path and the + * completion path. + */ +struct efx_tx_queue { + /* Members which don't change on the fast path */ + struct efx_nic *efx ____cacheline_aligned_in_smp; + int queue; + int used; + struct efx_channel *channel; + struct efx_nic *nic; + struct efx_tx_buffer *buffer; + struct efx_special_buffer txd; +#ifdef CONFIG_SFC_DEBUGFS + struct dentry *debug_dir; +#endif + + /* Members used mainly on the completion path */ + unsigned int read_count ____cacheline_aligned_in_smp; + int stopped; + + /* Members used only on the xmit path */ + unsigned int insert_count ____cacheline_aligned_in_smp; + unsigned int write_count; + unsigned int old_read_count; +}; + +/** + * struct efx_rx_buffer - An Efx RX data buffer + * @dma_addr: DMA base address of the buffer + * @skb: The associated socket buffer, if any. + * If both this and page are %NULL, the buffer slot is currently free. + * @page: The associated page buffer, if any. + * If both this and skb are %NULL, the buffer slot is currently free. + * @data: Pointer to ethernet header + * @len: Buffer length, in bytes. + * @unmap_addr: DMA address to unmap + */ +struct efx_rx_buffer { + dma_addr_t dma_addr; + struct sk_buff *skb; + struct page *page; + char *data; + unsigned int len; + dma_addr_t unmap_addr; +}; + +/** + * struct efx_rx_queue - An Efx RX queue + * @efx: The associated Efx NIC + * @queue: DMA queue number + * @used: Queue is used by net driver + * @channel: The associated channel + * @buffer: The software buffer ring + * @rxd: The hardware descriptor ring + * @added_count: Number of buffers added to the receive queue. + * @notified_count: Number of buffers given to NIC (<= @added_count). + * @removed_count: Number of buffers removed from the receive queue. + * @add_lock: Receive queue descriptor add spin lock. + * This lock must be held in order to add buffers to the RX + * descriptor ring (rxd and buffer) and to update added_count (but + * not removed_count). + * @max_fill: RX descriptor maximum fill level (<= ring size) + * @fast_fill_trigger: RX descriptor fill level that will trigger a fast fill + * (<= @max_fill) + * @fast_fill_limit: The level to which a fast fill will fill + * (@fast_fill_trigger <= @fast_fill_limit <= @max_fill) + * @min_fill: RX descriptor minimum non-zero fill level. + * This records the minimum fill level observed when a ring + * refill was triggered. + * @min_overfill: RX descriptor minimum overflow fill level. + * This records the minimum fill level at which RX queue + * overflow was observed. It should never be set. + * @alloc_page_count: RX allocation strategy counter. + * @alloc_skb_count: RX allocation strategy counter. + * @work: Descriptor push work thread + * @buf_page: Page for next RX buffer. + * We can use a single page for multiple RX buffers. This tracks + * the remaining space in the allocation. + * @buf_dma_addr: Page's DMA address. + * @buf_data: Page's host address. + * @debug_dir: debugfs directory + */ +struct efx_rx_queue { + struct efx_nic *efx; + int queue; + int used; + struct efx_channel *channel; + struct efx_rx_buffer *buffer; + struct efx_special_buffer rxd; + + int added_count; + int notified_count; + int removed_count; + spinlock_t add_lock; + unsigned int max_fill; + unsigned int fast_fill_trigger; + unsigned int fast_fill_limit; + unsigned int min_fill; + unsigned int min_overfill; + unsigned int alloc_page_count; + unsigned int alloc_skb_count; + struct delayed_work work; + unsigned int slow_fill_count; + + struct page *buf_page; + dma_addr_t buf_dma_addr; + char *buf_data; + +#ifdef CONFIG_SFC_DEBUGFS + struct dentry *debug_dir; +#endif +}; + +/** + * struct efx_buffer - An Efx general-purpose buffer + * @addr: host base address of the buffer + * @dma_addr: DMA base address of the buffer + * @len: Buffer length, in bytes + * + * Falcon uses these buffers for its interrupt status registers and + * MAC stats dumps. + */ +struct efx_buffer { + void *addr; + dma_addr_t dma_addr; + unsigned int len; +}; + + +/* Flags for channel->used_flags */ +#define EFX_USED_BY_RX 1 +#define EFX_USED_BY_TX 2 +#define EFX_USED_BY_RX_TX (EFX_USED_BY_RX | EFX_USED_BY_TX) + +enum efx_rx_alloc_method { + RX_ALLOC_METHOD_AUTO = 0, + RX_ALLOC_METHOD_SKB = 1, + RX_ALLOC_METHOD_PAGE = 2, +}; + +/** + * struct efx_channel - An Efx channel + * + * A channel comprises an event queue, at least one TX queue, at least + * one RX queue, and an associated tasklet for processing the event + * queue. + * + * @efx: Associated Efx NIC + * @evqnum: Event queue number + * @channel: Channel instance number + * @used_flags: Channel is used by net driver + * @enabled: Channel enabled indicator + * @irq: IRQ number (MSI and MSI-X only) + * @has_interrupt: Channel has an interrupt + * @irq_moderation: IRQ moderation value (in us) + * @napi_dev: Net device used with NAPI + * @napi_str: NAPI control structure + * @reset_work: Scheduled reset work thread + * @work_pending: Is work pending via NAPI? + * @eventq: Event queue buffer + * @eventq_read_ptr: Event queue read pointer + * @last_eventq_read_ptr: Last event queue read pointer value. + * @eventq_magic: Event queue magic value for driver-generated test events + * @debug_dir: debugfs directory + * @lro_mgr: LRO state + * @rx_alloc_level: Watermark based heuristic counter for pushing descriptors + * and diagnostic counters + * @rx_alloc_push_pages: RX allocation method currently in use for pushing + * descriptors + * @rx_alloc_pop_pages: RX allocation method currently in use for popping + * descriptors + * @n_rx_tobe_disc: Count of RX_TOBE_DISC errors + * @n_rx_ip_frag_err: Count of RX IP fragment errors + * @n_rx_ip_hdr_chksum_err: Count of RX IP header checksum errors + * @n_rx_tcp_udp_chksum_err: Count of RX TCP and UDP checksum errors + * @n_rx_frm_trunc: Count of RX_FRM_TRUNC errors + * @n_rx_overlength: Count of RX_OVERLENGTH errors + * @n_skbuff_leaks: Count of skbuffs leaked due to RX overrun + */ +struct efx_channel { + struct efx_nic *efx; + int evqnum; + int channel; + int used_flags; + int enabled; + int irq; + unsigned int has_interrupt; + unsigned int irq_moderation; + struct net_device *napi_dev; + struct napi_struct napi_str; + struct work_struct reset_work; + int work_pending; + struct efx_special_buffer eventq; + unsigned int eventq_read_ptr; + unsigned int last_eventq_read_ptr; + unsigned int eventq_magic; + +#ifdef CONFIG_SFC_DEBUGFS + struct dentry *debug_dir; +#endif + + struct net_lro_mgr lro_mgr; + int rx_alloc_level; + int rx_alloc_push_pages; + int rx_alloc_pop_pages; + + unsigned n_rx_tobe_disc; + unsigned n_rx_ip_frag_err; + unsigned n_rx_ip_hdr_chksum_err; + unsigned n_rx_tcp_udp_chksum_err; + unsigned n_rx_frm_trunc; + unsigned n_rx_overlength; + unsigned n_skbuff_leaks; + + /* Used to pipeline received packets in order to optimise memory + * access with prefetches. + */ + struct efx_rx_buffer *rx_pkt; + int rx_pkt_csummed; + +}; + +/** + * struct efx_blinker - S/W LED blinking context + * @led_num: LED ID (board-specific meaning) + * @state: Current state - on or off + * @resubmit: Timer resubmission flag + * @timer: Control timer for blinking + */ +struct efx_blinker { + int led_num; + int state; + int resubmit; + struct timer_list timer; +}; + + +/** + * struct efx_board - board information + * @type: Board model type + * @major: Major rev. ('A', 'B' ...) + * @minor: Minor rev. (0, 1, ...) + * @init: Initialisation function + * @init_leds: Sets up board LEDs + * @set_fault_led: Turns the fault LED on or off + * @blink: Starts/stops blinking + * @monitor: Board-specific health check function + * @fini: Cleanup function + * @interpret_sensor: Function to interpret LM87 sensor meanings. + * Returns %FALSE if no board-specific meaning. + * @mwatts: Power requirements (mW) + * @blinker: used to blink LEDs in software + * @lm87_addr: I2C address of LM87 monitoring chip (0 if absent) + */ +struct efx_board { + int type; + int major; + int minor; + int (*init) (struct efx_nic *nic); + /* As the LEDs are typically attached to the PHY, LEDs + * have a separate init callback that happens later than + * board init. */ + int (*init_leds)(struct efx_nic *efx); + int (*monitor) (struct efx_nic *nic); + void (*set_fault_led) (struct efx_nic *efx, int state); + void (*blink) (struct efx_nic *efx, int start); + void (*fini) (struct efx_nic *nic); + int (*interpret_sensor) (struct efx_nic *nic, int num, unsigned val); + unsigned mwatts; + struct efx_blinker blinker; + unsigned int lm87_addr; +}; + +#define STRING_TABLE_LOOKUP(val, member) \ + ((val) < member ## _max) ? member ## _names[val] : "(invalid)" + +extern const char *efx_loopback_mode_names[]; +extern const unsigned int efx_loopback_mode_max; +#define LOOPBACK_MODE(efx) \ + STRING_TABLE_LOOKUP((efx)->loopback_mode, efx_loopback_mode) + +extern const char *efx_interrupt_mode_names[]; +extern const unsigned int efx_interrupt_mode_max; +#define INT_MODE(efx) \ + STRING_TABLE_LOOKUP(efx->interrupt_mode, efx_interrupt_mode) + +extern const char *efx_reset_type_names[]; +extern const unsigned int efx_reset_type_max; +#define RESET_TYPE(type) \ + STRING_TABLE_LOOKUP(type, efx_reset_type) + +extern const char *efx_phy_type_names[]; +extern const unsigned int efx_phy_type_max; +#define PHY_TYPE(efx) \ + STRING_TABLE_LOOKUP((efx)->phy_type, efx_phy_type) + +extern const char *efx_nic_state_names[]; +extern const unsigned int efx_nic_state_max; +#define STATE_NAME(efx) \ + STRING_TABLE_LOOKUP((efx)->state, efx_nic_state) + +enum efx_int_mode { + /* Be careful if altering to correct macro below */ + EFX_INT_MODE_MSIX = 0, + EFX_INT_MODE_MSI = 1, + EFX_INT_MODE_LEGACY = 2, + EFX_INT_MODE_MAX /* Insert any new items before this */ +}; +#define EFX_INT_MODE_USE_MSI(x) (((x)->interrupt_mode) <= EFX_INT_MODE_MSI) + +enum phy_type { + PHY_TYPE_NONE = 0, + PHY_TYPE_CX4_RTMR = 1, + PHY_TYPE_1G_ALASKA = 2, + PHY_TYPE_10XPRESS = 3, + PHY_TYPE_XFP = 4, + PHY_TYPE_PM8358 = 6, + PHY_TYPE_MAX /* Insert any new items before this */ +}; + +#define PHY_ADDR_INVALID 0xff + +#define EFX_IS10G(efx) ((efx)->is_10g) +#define EFX_ISCLAUSE45(efx) ((efx)->phy_type != PHY_TYPE_1G_ALASKA) + +enum nic_state { + STATE_INIT = 0, /* suspend_lock always held */ + STATE_RUNNING = 1, + STATE_FINI = 2, + STATE_RESETTING = 3, /* suspend_lock always held */ + STATE_DISABLED = 4, + STATE_MAX, +}; + +/* + * Alignment of page-allocated RX buffers + * + * Controls the number of bytes inserted at the start of an RX buffer. + * This is the equivalent of NET_IP_ALIGN [which controls the alignment + * of the skb->head for hardware DMA]. + */ +#ifdef __ia64__ +#define EFX_PAGE_IP_ALIGN 2 +#else +#define EFX_PAGE_IP_ALIGN 0 +#endif + +/* + * Alignment of the skb->head which wraps a page-allocated RX buffer + * + * The skb allocated to wrap an rx_buffer can have this alignment. Since + * the data is memcpy'd from the rx_buf, it does not need to be equal to + * EFX_PAGE_IP_ALIGN. + */ +#define EFX_PAGE_SKB_ALIGN 2 + +/* Forward declaration */ +struct efx_nic; + +/* Pseudo bit-mask flow control field */ +enum efx_fc_type { + EFX_FC_RX = 1, + EFX_FC_TX = 2, + EFX_FC_AUTO = 4, +}; + +/** + * struct efx_mac_operations - Efx MAC operations table + * @mac_writel: Write dword to MAC register + * @mac_readl: Read dword from a MAC register + * @init: Initialise MAC and PHY + * @reconfigure: Reconfigure MAC and PHY (e.g. for new link parameters) + * @update_stats: Update statistics + * @fini: Shut down MAC and PHY + * @check_hw: Check hardware + * @fake_phy_event: Simulate a PHY event on a port + * @get_settings: Get ethtool settings + * @set_settings: Set ethtool settings + * @set_pause: Set pause parameters + */ +struct efx_mac_operations { + void (*mac_writel) (struct efx_nic *efx, + efx_dword_t *value, unsigned int mac_reg); + void (*mac_readl) (struct efx_nic *efx, + efx_dword_t *value, unsigned int mac_reg); + int (*init) (struct efx_nic *efx); + void (*reconfigure) (struct efx_nic *efx); + void (*update_stats) (struct efx_nic *efx); + void (*fini) (struct efx_nic *efx); + int (*check_hw) (struct efx_nic *efx); + void (*fake_phy_event) (struct efx_nic *efx); + + int (*get_settings) (struct efx_nic *efx, + struct ethtool_cmd *ecmd); + int (*set_settings) (struct efx_nic *efx, + struct ethtool_cmd *ecmd); + int (*set_pause) (struct efx_nic *efx, + enum efx_fc_type pause_params); +}; + +/** + * struct efx_phy_operations - Efx PHY operations table + * @init: Initialise PHY + * @fini: Shut down PHY + * @reconfigure: Reconfigure PHY (e.g. for new link parameters) + * @clear_interrupt: Clear down interrupt + * @blink: Blink LEDs + * @check_hw: Check hardware + * @reset_xaui: Reset XAUI side of PHY for (software sequenced reset) + * @mmds: MMD presence mask + * @loopbacks: Supported loopback modes mask + * @startup_loopback: Loopback mode for start-of-day self-test + */ +struct efx_phy_operations { + int (*init) (struct efx_nic *efx); + void (*fini) (struct efx_nic *efx); + void (*reconfigure) (struct efx_nic *efx); + void (*clear_interrupt) (struct efx_nic *efx); + int (*check_hw) (struct efx_nic *efx); + void (*reset_xaui) (struct efx_nic *efx); + int mmds; + unsigned loopbacks; + unsigned startup_loopback; +}; + +/* + * Efx extended statistics + * + * Not all statistics are provided by all supported MACs. The purpose + * is this structure is to contain the raw statistics provided by each + * MAC. + */ +struct efx_mac_stats { + u64 tx_bytes; + u64 tx_good_bytes; + u64 tx_bad_bytes; + unsigned long tx_packets; + unsigned long tx_bad; + unsigned long tx_pause; + unsigned long tx_control; + unsigned long tx_unicast; + unsigned long tx_multicast; + unsigned long tx_broadcast; + unsigned long tx_lt64; + unsigned long tx_64; + unsigned long tx_65_to_127; + unsigned long tx_128_to_255; + unsigned long tx_256_to_511; + unsigned long tx_512_to_1023; + unsigned long tx_1024_to_15xx; + unsigned long tx_15xx_to_jumbo; + unsigned long tx_gtjumbo; + unsigned long tx_collision; + unsigned long tx_single_collision; + unsigned long tx_multiple_collision; + unsigned long tx_excessive_collision; + unsigned long tx_deferred; + unsigned long tx_late_collision; + unsigned long tx_excessive_deferred; + unsigned long tx_non_tcpudp; + unsigned long tx_mac_src_error; + unsigned long tx_ip_src_error; + u64 rx_bytes; + u64 rx_good_bytes; + u64 rx_bad_bytes; + unsigned long rx_packets; + unsigned long rx_good; + unsigned long rx_bad; + unsigned long rx_pause; + unsigned long rx_control; + unsigned long rx_unicast; + unsigned long rx_multicast; + unsigned long rx_broadcast; + unsigned long rx_lt64; + unsigned long rx_64; + unsigned long rx_65_to_127; + unsigned long rx_128_to_255; + unsigned long rx_256_to_511; + unsigned long rx_512_to_1023; + unsigned long rx_1024_to_15xx; + unsigned long rx_15xx_to_jumbo; + unsigned long rx_gtjumbo; + unsigned long rx_bad_lt64; + unsigned long rx_bad_64_to_15xx; + unsigned long rx_bad_15xx_to_jumbo; + unsigned long rx_bad_gtjumbo; + unsigned long rx_overflow; + unsigned long rx_missed; + unsigned long rx_false_carrier; + unsigned long rx_symbol_error; + unsigned long rx_align_error; + unsigned long rx_length_error; + unsigned long rx_internal_error; + unsigned long rx_good_lt64; +}; + +/* Number of bits used in a multicast filter hash address */ +#define EFX_MCAST_HASH_BITS 8 + +/* Number of (single-bit) entries in a multicast filter hash */ +#define EFX_MCAST_HASH_ENTRIES (1 << EFX_MCAST_HASH_BITS) + +/* An Efx multicast filter hash */ +union efx_multicast_hash { + u8 byte[EFX_MCAST_HASH_ENTRIES / sizeof(u8)]; + efx_oword_t oword[EFX_MCAST_HASH_ENTRIES / sizeof(efx_oword_t)]; +}; + +/* Efx Error condition statistics */ +struct efx_nic_errors { + atomic_t missing_event; + atomic_t rx_reset; + atomic_t rx_desc_fetch; + atomic_t tx_desc_fetch; + atomic_t spurious_tx; + +#ifdef CONFIG_SFC_DEBUGFS + struct dentry *debug_dir; +#endif +}; + +/** + * struct efx_nic - an Efx NIC + * @name: Device name (net device name or bus id before net device registered) + * @pci_dev: The PCI device + * @pci_dev2: The secondary PCI device if present + * @type: Controller type attributes + * @dma_mask: DMA mask + * @legacy_irq: IRQ number + * @workqueue: Workqueue for resets, port reconfigures and the HW monitor + * @refill_workqueue: RX refill workqueue + * @reset_work: Scheduled reset workitem + * @monitor_work: Hardware monitor workitem + * @membase_phys: Memory BAR value as physical address + * @membase: Memory BAR value + * @biu_lock: BIU (bus interface unit) lock + * @interrupt_mode: Interrupt mode + * @is_asic: Is ASIC (else FPGA) + * @is_10g: Is set to 10G (else 1G) + * @external_sram_cfg: Size and number of banks of external SRAM + * @i2c: I2C interface + * @board_info: Board-level information + * @state: Device state flag. Can only be manipulated when both + * suspend_lock and rtnl_lock are held. Can be read when + * either is held. + * @reset_pending: Pending reset method (normally RESET_TYPE_NONE) + * @suspend_lock: Device suspend lock. This must not be acquired with + * rtnl_lock held. + * @tx_queue: TX DMA queues + * @rx_queue: RX DMA queues + * @channel: Channels + * @rss_queues: Number of RSS queues + * @rx_buffer_len: RX buffer length + * @rx_buffer_order: Order (log2) of number of pages for each RX buffer + * @errors: Error condition stats + * @irq_status: Interrupt status buffer + * @last_irq_cpu: Last CPU to handle interrupt. + * This register is written with the SMP processor ID whenever an + * interrupt is handled. It is used by falcon_test_interrupt() + * to verify that an interrupt has occurred. + * @spi_flash: SPI flash device + * This field will be %NULL if no flash device is present. + * @spi_eeprom: SPI EEPROM device + * This field will be %NULL if no EEPROM device is present. + * @spi_lock: SPI bus lock + * @n_rx_nodesc_drop_cnt: RX no descriptor drop count + * @nic_data: Hardware dependant state + * @mac_lock: MAC access lock. Protects efx->enabled/net_dev_registered + * and efx_reconfigure_port() + * @port_enabled: Port enabled indicator + * @net_dev_registered: Port is registered with operating system. + * @port_initialized: Port initialized? + * @net_dev: Operating system network device. Consider holding the rtnl lock + * @rx_checksum_enabled: RX checksumming enabled + * @netif_stop_count: Port stop count + * @netif_stop_lock: Port stop lock + * @mac_stats: MAC statistics + * @stats: Net device statistics. + * Hardware-specific code fills in @mac_stats, which provides a + * detailed breakdown. Generic code aggregates these statistics + * into a standard &struct net_device_stats. + * @stats_buffer: DMA buffer for statistics + * @stats_lock: Statistics update lock + * @mac_op: MAC interface + * @mac_address: Permanent MAC address + * @phy_type: PHY type + * @phy_lock: PHY access lock + * @phy_op: PHY interface + * @phy_data: PHY private data (including PHY-specific stats) + * @mii: PHY interface + * @phy_powered: PHY power state + * @tx_disabled: PHY transmitter turned off + * @link_up: Link status + * @link_options: Link options (MII/GMII format) + * @n_link_state_changes: Number of times the link has changed state + * @promiscuous: Promiscuous flag + * @multicast_hash: Multicast hash table + * @flow_control: Flow control flags - separate RX/TX so can't use link_options + * @reconfigure_work: work item for dealing with PHY events + * @loopback_mode: Loopback status + * @loopback_modes: Supported loopback mode bitmask + * @loopback_selftest: Offline self-test private state + * @silicon_rev: Silicon revision description for driverlink + * @dl_info: Linked list of hardware parameters exposed through driverlink + * @dl_node: Driverlink port list + * @dl_device_list: Driverlink device list + * @dl_cb: Driverlink callbacks table + * @dl_cb_dev: Driverlink callback owner devices + * @debug_dir: NIC debugfs directory + * @debug_symlink: NIC debugfs sym-link (nic_eth\%d) + * @debug_port_dir: Port debugfs directory + * @debug_port_symlink: Port debugfs sym-link (if_eth\%d) + * + * The @priv field of the corresponding &struct net_device points to + * this. + */ +struct efx_nic { + char name[IFNAMSIZ]; + struct pci_dev *pci_dev; + struct pci_dev *pci_dev2; + const struct efx_nic_type *type; + dma_addr_t dma_mask; + int legacy_irq; + struct workqueue_struct *workqueue; + struct workqueue_struct *refill_workqueue; + struct work_struct reset_work; + struct delayed_work monitor_work; + unsigned long membase_phys; + void __iomem *membase; + spinlock_t biu_lock; + enum efx_int_mode interrupt_mode; + unsigned int is_asic:1; + unsigned int is_10g:1; + int external_sram_cfg; + + struct efx_i2c_interface i2c; + struct efx_board board_info; + + enum nic_state state; + enum reset_type reset_pending; + + struct semaphore suspend_lock; + + struct efx_tx_queue tx_queue[EFX_MAX_TX_QUEUES]; + struct efx_rx_queue rx_queue[EFX_MAX_RX_QUEUES]; + struct efx_channel channel[EFX_MAX_CHANNELS]; + + int rss_queues; + unsigned int rx_buffer_len; + unsigned int rx_buffer_order; + + struct efx_nic_errors errors; + + struct efx_buffer irq_status; + volatile signed int last_irq_cpu; + + struct efx_spi_device *spi_flash; + struct efx_spi_device *spi_eeprom; + struct mutex spi_lock; + + unsigned n_rx_nodesc_drop_cnt; + + void *nic_data; + + struct mutex mac_lock; + int port_enabled; + int net_dev_registered; + + int port_initialized; + struct net_device *net_dev; + int rx_checksum_enabled; + + atomic_t netif_stop_count; + spinlock_t netif_stop_lock; + + struct efx_mac_stats mac_stats; + struct net_device_stats stats; + struct efx_buffer stats_buffer; + spinlock_t stats_lock; + + struct efx_mac_operations *mac_op; + unsigned char mac_address[ETH_ALEN]; + + enum phy_type phy_type; + spinlock_t phy_lock; + struct efx_phy_operations *phy_op; + void *phy_data; + struct mii_if_info mii; + unsigned phy_powered; + unsigned tx_disabled; + + int link_up; + unsigned int link_options; + unsigned int n_link_state_changes; + + int promiscuous; + union efx_multicast_hash multicast_hash; + enum efx_fc_type flow_control; + struct work_struct reconfigure_work; + + enum efx_loopback_mode loopback_mode; + unsigned int loopback_modes; + unsigned int startup_loopbacks; + + void *loopback_selftest; + + const char *silicon_rev; + struct efx_dl_device_info *dl_info; + struct list_head dl_node; + struct list_head dl_device_list; + struct efx_dl_callbacks dl_cb; + struct efx_dl_cb_devices dl_cb_dev; + +#ifdef CONFIG_SFC_DEBUGFS + struct dentry *debug_dir; + struct dentry *debug_symlink; + struct dentry *debug_port_dir; + struct dentry *debug_port_symlink; +#endif +}; + +/** + * struct efx_nic_type - Efx device type definition + * @is_dual_func: Is dual-function (else single-function) + * @mem_bar: Memory BAR number + * @mem_map_size: Memory BAR mapped size + * @txd_ptr_tbl_base: TX descriptor ring base address + * @rxd_ptr_tbl_base: RX descriptor ring base address + * @buf_tbl_base: Buffer table base address + * @evq_ptr_tbl_base: Event queue pointer table base address + * @evq_rptr_tbl_base: Event queue read-pointer table base address + * @txd_ring_mask: TX descriptor ring size - 1 (must be a power of two - 1) + * @rxd_ring_mask: RX descriptor ring size - 1 (must be a power of two - 1) + * @evq_size: Event queue size (must be a power of two) + * @max_dma_mask: Maximum possible DMA mask + * @tx_dma_mask: TX DMA mask + * @bug5391_mask: Address mask for bug 5391 workaround + * @rx_xoff_thresh: RX FIFO XOFF watermark (bytes) + * @rx_xon_thresh: RX FIFO XON watermark (bytes) + * @rx_buffer_padding: Padding added to each RX buffer + * @max_interrupt_mode: Highest capability interrupt mode supported + * from &enum efx_init_mode. + * @phys_addr_channels: Number of channels with physically addressed + * descriptors + */ +struct efx_nic_type { + unsigned int is_dual_func; + unsigned int mem_bar; + unsigned int mem_map_size; + unsigned int txd_ptr_tbl_base; + unsigned int rxd_ptr_tbl_base; + unsigned int buf_tbl_base; + unsigned int evq_ptr_tbl_base; + unsigned int evq_rptr_tbl_base; + + unsigned int txd_ring_mask; + unsigned int rxd_ring_mask; + unsigned int evq_size; + dma_addr_t max_dma_mask; + unsigned int tx_dma_mask; + unsigned bug5391_mask; + + int rx_xoff_thresh; + int rx_xon_thresh; + unsigned int rx_buffer_padding; + unsigned int max_interrupt_mode; + unsigned int phys_addr_channels; +}; + +/************************************************************************** + * + * Prototypes and inline functions + * + *************************************************************************/ + +/* Iterate over all used channels */ +#define efx_for_each_channel(_channel, _efx) \ + for (_channel = &_efx->channel[0]; \ + _channel < &_efx->channel[EFX_MAX_CHANNELS]; \ + _channel++) \ + if (!_channel->used_flags) \ + continue; \ + else + +/* Iterate over all used channels with interrupts */ +#define efx_for_each_channel_with_interrupt(_channel, _efx) \ + for (_channel = &_efx->channel[0]; \ + _channel < &_efx->channel[EFX_MAX_CHANNELS]; \ + _channel++) \ + if (!(_channel->used_flags && _channel->has_interrupt)) \ + continue; \ + else + +/* Iterate over all used TX queues */ +#define efx_for_each_tx_queue(_tx_queue, _efx) \ + for (_tx_queue = &_efx->tx_queue[0]; \ + _tx_queue < &_efx->tx_queue[EFX_MAX_TX_QUEUES]; \ + _tx_queue++) \ + if (!_tx_queue->used) \ + continue; \ + else + +/* Iterate over all TX queues belonging to a channel */ +#define efx_for_each_channel_tx_queue(_tx_queue, _channel) \ + for (_tx_queue = &_channel->efx->tx_queue[0]; \ + _tx_queue < &_channel->efx->tx_queue[EFX_MAX_TX_QUEUES]; \ + _tx_queue++) \ + if ((!_tx_queue->used) || \ + (_tx_queue->channel != _channel)) \ + continue; \ + else + +/* Iterate over all used RX queues */ +#define efx_for_each_rx_queue(_rx_queue, _efx) \ + for (_rx_queue = &_efx->rx_queue[0]; \ + _rx_queue < &_efx->rx_queue[EFX_MAX_RX_QUEUES]; \ + _rx_queue++) \ + if (!_rx_queue->used) \ + continue; \ + else + +/* Iterate over all RX queues belonging to a channel */ +#define efx_for_each_channel_rx_queue(_rx_queue, _channel) \ + for (_rx_queue = &_channel->efx->rx_queue[0]; \ + _rx_queue < &_channel->efx->rx_queue[EFX_MAX_RX_QUEUES]; \ + _rx_queue++) \ + if ((!_rx_queue->used) || \ + (_rx_queue->channel != _channel)) \ + continue; \ + else + +/* Name formats */ +#define EFX_CHANNEL_NAME(_channel) "channel%d", _channel->channel +#define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->queue +#define EFX_RX_QUEUE_NAME(_rx_queue) "rxq%d", _rx_queue->queue + +/* Returns a pointer to the specified receive buffer in the RX + * descriptor queue. + */ +static inline struct efx_rx_buffer *efx_rx_buffer(struct efx_rx_queue *rx_queue, + unsigned int index) +{ + return (&rx_queue->buffer[index]); +} + +/* Set bit in a little-endian bitfield */ +static inline void set_bit_le(int nr, unsigned char *addr) +{ + addr[nr / 8] |= (1 << (nr % 8)); +} + +/* Clear bit in a little-endian bitfield */ +static inline void clear_bit_le(int nr, unsigned char *addr) +{ + addr[nr / 8] &= ~(1 << (nr % 8)); +} + + +/** + * EFX_MAX_FRAME_LEN - calculate maximum frame length + * + * This calculates the maximum frame length that will be used for a + * given MTU. The frame length will be equal to the MTU plus a + * constant amount of header space and padding. This is the quantity + * that the net driver will program into the MAC as the maximum frame + * length. + * + * The 10G MAC used in Falcon requires 8-byte alignment on the frame + * length, so we round up to the nearest 8. + */ +#define EFX_MAX_FRAME_LEN(mtu) \ + ((((mtu) + ETH_HLEN + VLAN_HLEN + 4/* FCS */) + 7) & ~7) + + +#endif /* EFX_NET_DRIVER_H */ Index: head-2008-03-17/drivers/net/sfc/null_phy.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/null_phy.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,62 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed by Solarflare Communications + * + * 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 "efx.h" +#include "falcon.h" +#include "gmii.h" +#include "phy.h" + +static int falcon_null_phy_check_hw(struct efx_nic *efx) +{ + int link_ok = falcon_xaui_link_ok(efx); + + /* Generate PHY event that a PHY would have generated */ + if (link_ok != efx->link_up) { + efx->link_up = link_ok; + efx->mac_op->fake_phy_event(efx); + } + + return 0; +} + +static void falcon_null_phy_reconfigure(struct efx_nic *efx) +{ + /* CX4 is always 10000FD only */ + efx->link_options = GM_LPA_10000FULL; + + falcon_null_phy_check_hw(efx); +} + +struct efx_phy_operations falcon_null_phy_ops = { + .reconfigure = falcon_null_phy_reconfigure, + .check_hw = falcon_null_phy_check_hw, + .fini = efx_port_dummy_op_void, + .clear_interrupt = efx_port_dummy_op_void, + .init = efx_port_dummy_op_int, + .reset_xaui = efx_port_dummy_op_void, + .mmds = 0, + .loopbacks = 0, + .startup_loopback = 0, +}; Index: head-2008-03-17/drivers/net/sfc/phy.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/phy.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,28 @@ +/**************************************************************************** + * 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 + * + * 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" + Index: head-2008-03-17/drivers/net/sfc/phy.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/phy.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,90 @@ +/**************************************************************************** + * 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 + * + * 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_PHY_H +#define EFX_PHY_H + +/**************************************************************************** + * 10Xpress (SFX7101) PHY + */ +extern struct efx_phy_operations falcon_tenxpress_phy_ops; + +enum tenxpress_state { + TENXPRESS_STATUS_OFF = 0, + TENXPRESS_STATUS_OTEMP = 1, + TENXPRESS_STATUS_NORMAL = 2, +}; + +extern void tenxpress_set_state(struct efx_nic *efx, + enum tenxpress_state state); +extern void tenxpress_phy_blink(struct efx_nic *efx, int blink); +extern void tenxpress_crc_err(struct efx_nic *efx); + +/**************************************************************************** + * Marvell 88E1111 "Alaska" PHY control + */ +extern struct efx_phy_operations alaska_phy_operations; + +/**************************************************************************** +* Exported functions from the driver for Transwitch CX4 retimer +*/ +extern struct efx_phy_operations falcon_txc_phy_ops; + +#define TXC_GPIO_DIR_INPUT (0) +#define TXC_GPIO_DIR_OUTPUT (1) + +extern void txc_set_gpio_dir(struct efx_nic *p, int pin, int dir); +extern void txc_set_gpio_val(struct efx_nic *p, int pin, int val); + +/**************************************************************************** + * Exported functions from the driver for PMC PM8358 PHY + */ +extern struct efx_phy_operations falcon_pm8358_phy_ops; + +/**************************************************************************** + * Exported functions from the driver for XFP optical PHYs + */ +extern struct efx_phy_operations falcon_xfp_phy_ops; + +/* The QUAKE XFP PHY provides various H/W control states for LEDs */ +#define QUAKE_LED_LINK_INVAL (0) +#define QUAKE_LED_LINK_STAT (1) +#define QUAKE_LED_LINK_ACT (2) +#define QUAKE_LED_LINK_ACTSTAT (3) +#define QUAKE_LED_OFF (4) +#define QUAKE_LED_ON (5) +#define QUAKE_LED_LINK_INPUT (6) /* Pin is an input. */ +/* What link the LED tracks */ +#define QUAKE_LED_TXLINK (0) +#define QUAKE_LED_RXLINK (8) + +extern void xfp_set_led(struct efx_nic *p, int led, int state); + +/**************************************************************************** + * NULL PHY ops + */ +extern struct efx_phy_operations falcon_null_phy_ops; + +#endif Index: head-2008-03-17/drivers/net/sfc/pm8358_phy.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/pm8358_phy.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,206 @@ +/* Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed by Solarflare Communications + * + * 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 + **************************************************************************** + */ +/* + * Driver for PMC-Sierra PM8358 XAUI PHY + */ + +#include +#include "efx.h" +#include "gmii.h" +#include "mdio_10g.h" +#include "phy.h" + +#define PM8358_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_DTEXS) +#define PM8358_LOOPBACKS (1 << LOOPBACK_PHY) + +/* PHY-specific definitions */ +/* Master ID and Global Performance Monitor Update */ +#define PMC_MASTER_REG (0xd000) +/* Analog TX/RX settings under software control */ +#define PMC_MASTER_ANLG_CTRL (1 << 11) + +#define PMC_MCONF2_REG (0xd002) +/* Drive TX off centre of data eye (1) vs. clock edge (0) */ +#define PMC_MCONF2_TEDGE (1 << 2) +/* Drive RX off centre of data eye (1) vs. clock edge (0) */ +#define PMC_MCONF2_REDGE (1 << 3) + +/* Analog RX settings */ +#define PMC_ANALOG_RX_CFG0 (0xd025) +#define PMC_ANALOG_RX_CFG1 (0xd02d) +#define PMC_ANALOG_RX_CFG2 (0xd035) +#define PMC_ANALOG_RX_CFG3 (0xd03d) +#define PMC_ANALOG_RX_TERM (1 << 15) /* Bit 15 of RX CFG: 0 for 100 ohms + float, 1 for 50 to 1.2V */ +#define PMC_ANALOG_RX_EQ_MASK (3 << 8) +#define PMC_ANALOG_RX_EQ_NONE (0 << 8) +#define PMC_ANALOG_RX_EQ_HALF (1 << 8) +#define PMC_ANALOG_RX_EQ_FULL (2 << 8) +#define PMC_ANALOG_RX_EQ_RSVD (3 << 8) + +/* Reset the DTE XS MMD. */ +#define PMC_MAX_RESET_TIME 500 +#define PMC_RESET_WAIT 10 + +static int pmc_reset_phy(struct efx_nic *efx) +{ + int rc = mdio_clause45_reset_mmd(efx, MDIO_MMD_DTEXS, + PMC_MAX_RESET_TIME / PMC_RESET_WAIT, + PMC_RESET_WAIT); + if (rc >= 0) { + EFX_TRACE(efx, "PMC8358: came out of reset with " + "%d0 ms left\n", rc); + rc = 0; + } else { + EFX_ERR(efx, "PMC8358: reset timed out!\n"); + } + return rc; +} + + +static void pmc_full_rx_eq(struct efx_nic *efx) +{ + int i, reg; + + /* Enable software control of analog settings */ + reg = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_DTEXS, PMC_MASTER_REG); + reg |= PMC_MASTER_ANLG_CTRL; + + mdio_clause45_write(efx, efx->mii.phy_id, + MDIO_MMD_DTEXS, PMC_MASTER_REG, reg); + + /* Turn RX eq on full for all channels. */ + for (i = 0; i < 3; i++) { + /* The analog CFG registers are evenly spaced 8 apart */ + u16 addr = PMC_ANALOG_RX_CFG0 + 8 * i; + + reg = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_DTEXS, addr); + reg = (reg & ~PMC_ANALOG_RX_EQ_MASK) | PMC_ANALOG_RX_EQ_FULL; + mdio_clause45_write(efx, efx->mii.phy_id, + MDIO_MMD_DTEXS, addr, reg); + } +} + +static void pmc_set_data_edges(struct efx_nic *efx) +{ + int reg; + /* Set TEDGE, clear REDGE */ + reg = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_DTEXS, PMC_MCONF2_REG); + reg &= ~PMC_MCONF2_REDGE; + reg |= PMC_MCONF2_TEDGE; + + mdio_clause45_write(efx, efx->mii.phy_id, + MDIO_MMD_DTEXS, PMC_MCONF2_REG, reg); +} + +static int pm8358_phy_init(struct efx_nic *efx) +{ + u32 devid; + int rc; + + /* The GLB_CTL reset line has been pulled before this is called, + * and it may take up to 5ms for the PLL's to synchronise after + * this is done. Best to wait 10ms here */ + schedule_timeout_uninterruptible(HZ / 100); + + rc = pmc_reset_phy(efx); + if (rc < 0) + return rc; + + /* Check that all the MMDs we expect are present and responding. We + * expect faults on some if the link is down, but not on the PHY XS */ + rc = mdio_clause45_check_mmds(efx, PM8358_REQUIRED_DEVS, 0); + if (rc < 0) + return rc; + + devid = mdio_clause45_read_id(efx, MDIO_MMD_DTEXS); + EFX_LOG(efx, "PM8358: PHY ID reg %x (OUI %x model %x revision" + " %x)\n", devid, MDIO_ID_OUI(devid), MDIO_ID_MODEL(devid), + MDIO_ID_REV(devid)); + + /* Turn on full RX equalisation */ + pmc_full_rx_eq(efx); + + /* Adjust RX and TX data edge position */ + pmc_set_data_edges(efx); + + EFX_LOG(efx, "PM8358: PHY init successful.\n"); + return rc; +} + +static int pm8358_link_ok(struct efx_nic *efx) +{ + return mdio_clause45_links_ok(efx, PM8358_REQUIRED_DEVS); +} + +static int pm8358_phy_check_hw(struct efx_nic *efx) +{ + int rc = 0; + int link_up = pm8358_link_ok(efx); + /* Simulate a PHY event if link state has changed */ + if (link_up != efx->link_up) { + efx->link_up = link_up; + efx->mac_op->fake_phy_event(efx); + } + + return rc; +} + +static void pm8358_phy_reconfigure(struct efx_nic *efx) +{ + int phy_id = efx->mii.phy_id; + int ctrl; + /* Handle DTE loopback */ + ctrl = mdio_clause45_read(efx, phy_id, MDIO_MMD_DTEXS, + MDIO_MMDREG_CTRL1); + if (efx->loopback_mode == LOOPBACK_PHY) { + EFX_TRACE(efx, "PM8358: setting DTE loopback\n"); + ctrl |= (1 << MDIO_MMDREG_CTRL1_LBACK_LBN); + } else { + if (ctrl & (1 << MDIO_MMDREG_CTRL1_LBACK_LBN)) + EFX_TRACE(efx, + "PM8358: clearing DTE loopback\n"); + ctrl &= ~(1 << MDIO_MMDREG_CTRL1_LBACK_LBN); + } + mdio_clause45_write(efx, phy_id, MDIO_MMD_DTEXS, + MDIO_MMDREG_CTRL1, ctrl); + + efx->link_up = pm8358_link_ok(efx); + efx->link_options = GM_LPA_10000FULL; +} + +struct efx_phy_operations falcon_pm8358_phy_ops = { + .init = pm8358_phy_init, + .reconfigure = pm8358_phy_reconfigure, + .check_hw = pm8358_phy_check_hw, + .fini = efx_port_dummy_op_void, + .clear_interrupt = efx_port_dummy_op_void, + .reset_xaui = efx_port_dummy_op_void, + .mmds = PM8358_REQUIRED_DEVS, + .loopbacks = PM8358_LOOPBACKS, + .startup_loopback = LOOPBACK_PHY, +}; Index: head-2008-03-17/drivers/net/sfc/rx.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/rx.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,916 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#include +#include +#include +#include +#include +#include +#include "net_driver.h" +#include "rx.h" +#include "efx.h" +#include "falcon.h" +#include "selftest.h" +#include "workarounds.h" + + +/* Number of RX descriptors pushed at once. */ +#define EFX_RX_BATCH 8 + +/* Size of buffer allocated for skb header area. */ +#define EFX_SKB_HEADERS 64u + +/* + * rx_alloc_method - RX buffer allocation method + * + * This driver supports two methods for allocating and using RX buffers: + * each RX buffer may be backed by an skb or by an order-n page. + * + * When LRO is in use then the second method has a lower overhead, + * since we don't have to allocate then free skbs on reassembled frames. + * + * Values: + * - RX_ALLOC_METHOD_AUTO = 0 + * - RX_ALLOC_METHOD_SKB = 1 + * - RX_ALLOC_METHOD_PAGE = 2 + * + * The heuristic for %RX_ALLOC_METHOD_AUTO is a simple hysteresis count + * controlled by the parameters below. + * + * - Since pushing and popping descriptors are separated by the rx_queue + * size, so the watermarks should be ~rxd_size. + * - The performance win by using page-based allocation for LRO is less + * than the performance hit of using page-based allocation of non-LRO, + * so the watermarks should reflect this. + * + * Per channel we maintain a single variable, updated by each channel: + * + * rx_alloc_level += (lro_performed ? RX_ALLOC_FACTOR_LRO : + * RX_ALLOC_FACTOR_SKB) + * Per NAPI poll interval, we constrain rx_alloc_level to 0..MAX (which + * limits the hysteresis), and update the allocation strategy: + * + * rx_alloc_method = (rx_alloc_level > RX_ALLOC_LEVEL_LRO ? + * RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB) + */ +static int rx_alloc_method = RX_ALLOC_METHOD_PAGE; + + +#define RX_ALLOC_LEVEL_LRO 0x2000 +#define RX_ALLOC_LEVEL_MAX 0x3000 +#define RX_ALLOC_FACTOR_LRO 1 +#define RX_ALLOC_FACTOR_SKB -2 + +/* Macros for zero-order pages (potentially) containing multiple RX buffers */ +#define RX_DATA_OFFSET(_data) \ + (((unsigned long) (_data)) & (PAGE_SIZE-1)) +#define RX_BUF_OFFSET(_rx_buf) \ + RX_DATA_OFFSET((_rx_buf)->data) + +#define RX_PAGE_SIZE(_efx) \ + (PAGE_SIZE * (1u << (_efx)->rx_buffer_order)) + +/************************************************************************** + * + * Linux generic LRO handling + * + ************************************************************************** + */ + +static int efx_lro_get_skb_hdr(struct sk_buff *skb, void **ip_hdr, + void **tcpudp_hdr, u64 *hdr_flags, void *priv) +{ + struct efx_channel *channel = (struct efx_channel *)priv; + struct iphdr *iph; + struct tcphdr *th; + + iph = (struct iphdr *)skb->data; + if (skb->protocol != htons(ETH_P_IP) || iph->protocol != IPPROTO_TCP) + goto fail; + + th = (struct tcphdr *)(skb->data + iph->ihl * 4); + + *tcpudp_hdr = th; + *ip_hdr = iph; + *hdr_flags = LRO_IPV4 | LRO_TCP; + + channel->rx_alloc_level += RX_ALLOC_FACTOR_LRO; + return 0; +fail: + channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB; + return -1; +} + +static int efx_get_frag_hdr(struct skb_frag_struct *frag, void **mac_hdr, + void **ip_hdr, void **tcpudp_hdr, u64 *hdr_flags, + void *priv) +{ + struct efx_channel *channel = (struct efx_channel *)priv; + struct ethhdr *eh; + struct iphdr *iph; + + /* We support EtherII and VLAN encapsulated IPv4 */ + eh = (struct ethhdr *)(page_address(frag->page) + frag->page_offset); + *mac_hdr = eh; + + if (eh->h_proto == htons(ETH_P_IP)) { + iph = (struct iphdr *)(eh + 1); + } else { + struct vlan_ethhdr *veh = (struct vlan_ethhdr *)eh; + if (veh->h_vlan_encapsulated_proto != htons(ETH_P_IP)) + goto fail; + + iph = (struct iphdr *)(veh + 1); + } + *ip_hdr = iph; + + /* We can only do LRO over TCP */ + if (iph->protocol != IPPROTO_TCP) + goto fail; + + *hdr_flags = LRO_IPV4 | LRO_TCP; + *tcpudp_hdr = (struct tcphdr *)((u8 *) iph + iph->ihl * 4); + + channel->rx_alloc_level += RX_ALLOC_FACTOR_LRO; + return 0; + fail: + channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB; + return -1; +} + +int efx_lro_init(struct net_lro_mgr *lro_mgr, struct efx_nic *efx) +{ + size_t s = sizeof(struct net_lro_desc) * EFX_MAX_LRO_DESCRIPTORS; + struct net_lro_desc *lro_arr; + + /* Allocate the LRO descriptors structure */ + lro_arr = kzalloc(s, GFP_KERNEL); + if (lro_arr == NULL) + return -ENOMEM; + + lro_mgr->lro_arr = lro_arr; + lro_mgr->max_desc = EFX_MAX_LRO_DESCRIPTORS; + lro_mgr->max_aggr = EFX_MAX_LRO_AGGR; + lro_mgr->frag_align_pad = EFX_PAGE_SKB_ALIGN; + + lro_mgr->get_skb_header = efx_lro_get_skb_hdr; + lro_mgr->get_frag_header = efx_get_frag_hdr; + lro_mgr->dev = efx->net_dev; + + lro_mgr->features = LRO_F_NAPI; + + /* We can pass packets up with the checksum intact */ + lro_mgr->ip_summed = CHECKSUM_UNNECESSARY; + + lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY; + + return 0; +} + +void efx_lro_fini(struct net_lro_mgr *lro_mgr) +{ + kfree(lro_mgr->lro_arr); + lro_mgr->lro_arr = NULL; +} + +/** + * efx_init_rx_buffer_skb - create new RX buffer using skb-based allocation + * + * @rx_queue: Efx RX queue + * @rx_buf: RX buffer structure to populate + * + * This allocates memory for a new receive buffer, maps it for DMA, + * and populates a struct efx_rx_buffer with the relevant + * information. Return a negative error code or 0 on success. + */ +static inline int efx_init_rx_buffer_skb(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *rx_buf) +{ + struct efx_nic *efx = rx_queue->efx; + struct net_device *net_dev = efx->net_dev; + int skb_len = efx->rx_buffer_len; + + rx_buf->skb = netdev_alloc_skb(net_dev, skb_len); + if (unlikely(!rx_buf->skb)) + return -ENOMEM; + + /* Adjust the SKB for padding and checksum */ + skb_reserve(rx_buf->skb, NET_IP_ALIGN); + rx_buf->len = skb_len - NET_IP_ALIGN; + rx_buf->data = rx_buf->skb->data; + + rx_buf->skb->ip_summed = CHECKSUM_UNNECESSARY; + + /* Map for DMA */ + rx_buf->dma_addr = pci_map_single(efx->pci_dev, + rx_buf->data, rx_buf->len, + PCI_DMA_FROMDEVICE); + + if (unlikely(pci_dma_mapping_error(rx_buf->dma_addr))) { + /* Free the SKB */ + dev_kfree_skb_any(rx_buf->skb); + rx_buf->skb = NULL; + return -EIO; + } + + return 0; +} + +/** + * efx_init_rx_buffer_page - create new RX buffer using page-based allocation + * + * @rx_queue: Efx RX queue + * @rx_buf: RX buffer structure to populate + * + * This allocates memory for a new receive buffer, maps it for DMA, + * and populates a struct efx_rx_buffer with the relevant + * information. Return a negative error code or 0 on success. + */ +static inline int efx_init_rx_buffer_page(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *rx_buf) +{ + struct efx_nic *efx = rx_queue->efx; + int bytes, space, offset; + + bytes = efx->rx_buffer_len - EFX_PAGE_IP_ALIGN; + + /* If there is space left in the previously allocated page, + * then use it. Otherwise allocate a new one */ + rx_buf->page = rx_queue->buf_page; + if (rx_buf->page == NULL) { + dma_addr_t dma_addr; + + rx_buf->page = alloc_pages(__GFP_COLD | __GFP_COMP | GFP_ATOMIC, + efx->rx_buffer_order); + if (unlikely(rx_buf->page == NULL)) + return -ENOMEM; + + /* Map the entire page for DMA */ + dma_addr = pci_map_page(efx->pci_dev, rx_buf->page, + 0, RX_PAGE_SIZE(efx), + PCI_DMA_FROMDEVICE); + + if (unlikely(pci_dma_mapping_error(dma_addr))) { + __free_pages(rx_buf->page, efx->rx_buffer_order); + rx_buf->page = NULL; + return -EIO; + } + + rx_queue->buf_page = rx_buf->page; + rx_queue->buf_dma_addr = dma_addr; + rx_queue->buf_data = ((char *) page_address(rx_buf->page) + + EFX_PAGE_IP_ALIGN); + } + + offset = RX_DATA_OFFSET(rx_queue->buf_data); + rx_buf->len = bytes; + rx_buf->dma_addr = rx_queue->buf_dma_addr + offset; + rx_buf->data = rx_queue->buf_data; + + /* Try to pack multiple buffers per page */ + if (efx->rx_buffer_order == 0) { + /* The next buffer starts on the next 512 byte boundary */ + rx_queue->buf_data += ((bytes + 0x1ff) & ~0x1ff); + offset += ((bytes + 0x1ff) & ~0x1ff); + + space = RX_PAGE_SIZE(efx) - offset; + if (space >= bytes) { + /* Refs dropped on kernel releasing each skb */ + get_page(rx_queue->buf_page); + goto out; + } + } + + /* This is the final RX buffer for this page, so mark it for + * unmapping */ + rx_queue->buf_page = NULL; + rx_buf->unmap_addr = rx_queue->buf_dma_addr; + + out: + return 0; +} + +/* This allocates memory for a new receive buffer, maps it for DMA, + * and populates a struct efx_rx_buffer with the relevant + * information. + */ +static inline int efx_init_rx_buffer(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *new_rx_buf) +{ + int rc = 0; + + if (rx_queue->channel->rx_alloc_push_pages) { + new_rx_buf->skb = NULL; + rc = efx_init_rx_buffer_page(rx_queue, new_rx_buf); + rx_queue->alloc_page_count++; + } else { + new_rx_buf->page = NULL; + rc = efx_init_rx_buffer_skb(rx_queue, new_rx_buf); + rx_queue->alloc_skb_count++; + } + + if (unlikely(rc < 0)) + EFX_LOG_RL(rx_queue->efx, "%s RXQ[%d] =%d\n", __FUNCTION__, + rx_queue->queue, rc); + return rc; +} + +static inline void efx_unmap_rx_buffer(struct efx_nic *efx, + struct efx_rx_buffer *rx_buf) +{ + if (rx_buf->page) { + EFX_BUG_ON_PARANOID(rx_buf->skb); + if (rx_buf->unmap_addr) { + pci_unmap_page(efx->pci_dev, rx_buf->unmap_addr, + RX_PAGE_SIZE(efx), PCI_DMA_FROMDEVICE); + rx_buf->unmap_addr = 0; + } + } else if (likely(rx_buf->skb)) { + pci_unmap_single(efx->pci_dev, rx_buf->dma_addr, + rx_buf->len, PCI_DMA_FROMDEVICE); + } +} + +static inline void efx_free_rx_buffer(struct efx_nic *efx, + struct efx_rx_buffer *rx_buf) +{ + if (rx_buf->page) { + __free_pages(rx_buf->page, efx->rx_buffer_order); + rx_buf->page = NULL; + } else if (likely(rx_buf->skb)) { + dev_kfree_skb_any(rx_buf->skb); + rx_buf->skb = NULL; + } +} + +inline void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *rx_buf) +{ + /* Unmap for DMA */ + efx_unmap_rx_buffer(rx_queue->efx, rx_buf); + + /* Free the skb/page */ + efx_free_rx_buffer(rx_queue->efx, rx_buf); +} + +/** + * efx_fast_push_rx_descriptors - push new RX descriptors quickly + * @rx_queue: RX descriptor queue + * @retry: Recheck the fill level + * This will aim to fill the RX descriptor queue up to + * @rx_queue->@fast_fill_limit. If there is insufficient atomic + * memory to do so, the caller should retry. + */ +static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue, + int retry) +{ + struct efx_rx_buffer *rx_buf; + unsigned fill_level, index; + int i, space, rc = 0; + + /* Calculate current fill level. Do this outside the lock, + * because most of the time we'll end up not wanting to do the + * fill anyway. + */ + fill_level = (rx_queue->added_count - rx_queue->removed_count); + EFX_BUG_ON_PARANOID(fill_level > + rx_queue->efx->type->rxd_ring_mask + 1); + + /* Don't fill if we don't need to */ + if (fill_level >= rx_queue->fast_fill_trigger) + return 0; + + /* Record minimum fill level */ + if (unlikely(fill_level < rx_queue->min_fill)) + if (fill_level) + rx_queue->min_fill = fill_level; + + /* Acquire RX add lock. If this lock is contended, then a fast + * fill must already be in progress (e.g. in the refill + * tasklet), so we don't need to do anything + */ + if (!spin_trylock_bh(&rx_queue->add_lock)) + return -1; + + retry: + /* Recalculate current fill level now that we have the lock */ + fill_level = (rx_queue->added_count - rx_queue->removed_count); + EFX_BUG_ON_PARANOID(fill_level > + rx_queue->efx->type->rxd_ring_mask + 1); + space = rx_queue->fast_fill_limit - fill_level; + if (space < EFX_RX_BATCH) + goto out_unlock; + + EFX_TRACE(rx_queue->efx, "RX queue %d fast-filling descriptor ring from" + " level %d to level %d using %s allocation\n", + rx_queue->queue, fill_level, rx_queue->fast_fill_limit, + rx_queue->channel->rx_alloc_push_pages ? "page" : "skb"); + + do { + for (i = 0; i < EFX_RX_BATCH; ++i) { + index = (rx_queue->added_count & + rx_queue->efx->type->rxd_ring_mask); + rx_buf = efx_rx_buffer(rx_queue, index); + rc = efx_init_rx_buffer(rx_queue, rx_buf); + if (unlikely(rc)) + goto out; + ++rx_queue->added_count; + } + } while ((space -= EFX_RX_BATCH) >= EFX_RX_BATCH); + + EFX_TRACE(rx_queue->efx, "RX queue %d fast-filled descriptor ring " + "to level %d\n", rx_queue->queue, + rx_queue->added_count - rx_queue->removed_count); + + out: + /* Send write pointer to card. */ + falcon_notify_rx_desc(rx_queue); + + /* If the fast fill is running inside from the refill tasklet, then + * for SMP systems it may be running on a different CPU to + * RX event processing, which means that the fill level may now be + * out of date. */ + if (unlikely(retry && (rc == 0))) + goto retry; + + out_unlock: + /* Release RX add lock */ + spin_unlock_bh(&rx_queue->add_lock); + + return rc; +} + +/** + * efx_fast_push_rx_descriptors - push new RX descriptors quickly + * @rx_queue: RX descriptor queue + * + * This will aim to fill the RX descriptor queue up to + * @rx_queue->@fast_fill_limit. If there is insufficient memory to do so, + * it will schedule a work item to immediately continue the fast fill + */ +void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue) +{ + int rc; + + rc = __efx_fast_push_rx_descriptors(rx_queue, 0); + if (unlikely(rc)) { + /* Schedule the work item to run immediately. The hope is + * that work is immediately pending to free some memory + * (e.g. an RX event or TX completion) + */ + queue_delayed_work(rx_queue->efx->refill_workqueue, + &rx_queue->work, 0); + } +} + +void efx_rx_work(struct work_struct *data) +{ + struct efx_rx_queue *rx_queue; + int rc; + + rx_queue = container_of(data, struct efx_rx_queue, work.work); + + if (unlikely(!rx_queue->channel->enabled)) + return; + + EFX_TRACE(rx_queue->efx, "RX queue %d worker thread executing on CPU " + "%d\n", rx_queue->queue, raw_smp_processor_id()); + + ++rx_queue->slow_fill_count; + /* Push new RX descriptors, allowing at least 1 jiffy for + * the kernel to free some more memory. */ + rc = __efx_fast_push_rx_descriptors(rx_queue, 1); + if (rc) { + queue_delayed_work(rx_queue->efx->refill_workqueue, + &rx_queue->work, 1); + } +} + +static inline void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *rx_buf, + int len, int *discard, + int *leak_packet) +{ + struct efx_nic *efx = rx_queue->efx; + unsigned max_len = rx_buf->len - efx->type->rx_buffer_padding; + + if (likely(len <= max_len)) + return; + + /* The packet must be discarded, but this is only a fatal error + * if the caller indicated it was + */ + *discard = 1; + + if ((len > rx_buf->len) && EFX_WORKAROUND_8071(efx)) { + EFX_ERR_RL(efx, " RX queue %d seriously overlength " + "RX event (0x%x > 0x%x+0x%x). Leaking\n", + rx_queue->queue, len, max_len, + efx->type->rx_buffer_padding); + /* If this buffer was skb-allocated, then the meta + * data at the end of the skb will be trashed. So + * we have no choice but to leak the fragment. + */ + *leak_packet = (rx_buf->skb != NULL); + efx_schedule_reset(efx, RESET_TYPE_RX_RECOVERY); + } else { + EFX_ERR_RL(efx, " RX queue %d overlength RX event " + "(0x%x > 0x%x)\n", rx_queue->queue, len, max_len); + } + + rx_queue->channel->n_rx_overlength++; +} + +/* Pass a received packet up through the generic LRO stack + * + * Handles driverlink veto, and passes the fragment up via + * the appropriate LRO method + */ +static inline void efx_rx_packet_lro(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf) +{ + struct efx_nic *efx = channel->efx; + struct net_lro_mgr *lro_mgr = &channel->lro_mgr; + void *priv = channel; + int veto; + + /* It would be faster if we had access to packets at the + * other side of generic LRO. Unfortunately, there isn't + * an obvious interface to this, so veto packets before LRO */ + veto = EFX_DL_CALLBACK(efx, rx_packet, rx_buf->data, rx_buf->len); + if (unlikely(veto)) { + EFX_LOG(efx, "LRO RX vetoed by driverlink %s driver\n", + efx->dl_cb_dev.rx_packet->driver->name); + /* Free the buffer now */ + efx_free_rx_buffer(efx, rx_buf); + return; + } + + /* Pass the skb/page into the LRO engine */ + if (rx_buf->page) { + struct skb_frag_struct frags; + + frags.page = rx_buf->page; + frags.page_offset = RX_BUF_OFFSET(rx_buf); + frags.size = rx_buf->len; + + lro_receive_frags(lro_mgr, &frags, rx_buf->len, + rx_buf->len, priv, 0); + + EFX_BUG_ON_PARANOID(rx_buf->skb); + rx_buf->page = NULL; + } else { + EFX_BUG_ON_PARANOID(!rx_buf->skb); + + lro_receive_skb(lro_mgr, rx_buf->skb, priv); + rx_buf->skb = NULL; + } +} + +/* Allocate and construct an SKB around a struct page.*/ +static inline struct sk_buff *efx_rx_mk_skb(struct efx_rx_buffer *rx_buf, + struct efx_nic *efx, + int hdr_len) +{ + struct sk_buff *skb; + + /* Allocate an SKB to store the headers */ + skb = netdev_alloc_skb(efx->net_dev, hdr_len + EFX_PAGE_SKB_ALIGN); + if (unlikely(skb == NULL)) { + EFX_ERR_RL(efx, "RX out of memory for skb\n"); + return NULL; + } + + EFX_BUG_ON_PARANOID(skb_shinfo(skb)->nr_frags); + EFX_BUG_ON_PARANOID(rx_buf->len < hdr_len); + + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb_reserve(skb, EFX_PAGE_SKB_ALIGN); + + skb->len = rx_buf->len; + skb->truesize = rx_buf->len + sizeof(struct sk_buff); + memcpy(skb->data, rx_buf->data, hdr_len); + skb->tail += hdr_len; + + /* Append the remaining page onto the frag list */ + if (unlikely(rx_buf->len > hdr_len)) { + struct skb_frag_struct *frag = skb_shinfo(skb)->frags; + frag->page = rx_buf->page; + frag->page_offset = RX_BUF_OFFSET(rx_buf) + hdr_len; + frag->size = skb->len - hdr_len; + skb_shinfo(skb)->nr_frags = 1; + skb->data_len = frag->size; + } else { + __free_pages(rx_buf->page, efx->rx_buffer_order); + skb->data_len = 0; + } + + /* Ownership has transferred from the rx_buf to skb */ + rx_buf->page = NULL; + + /* Move past the ethernet header */ + skb->protocol = eth_type_trans(skb, efx->net_dev); + + return skb; +} + +void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, + unsigned int len, int checksummed, int discard) +{ + struct efx_nic *efx = rx_queue->efx; + struct efx_rx_buffer *rx_buf; + int leak_packet = 0; + + rx_buf = efx_rx_buffer(rx_queue, index); + EFX_BUG_ON_PARANOID(!rx_buf->data); + EFX_BUG_ON_PARANOID(rx_buf->skb && rx_buf->page); + EFX_BUG_ON_PARANOID(!(rx_buf->skb || rx_buf->page)); + + /* This allows the refill path to post another buffer. + * EFX_RXD_HEAD_ROOM ensures that the slot we are using + * isn't overwritten yet. + */ + rx_queue->removed_count++; + + /* Validate the length encoded in the event vs the descriptor pushed */ + efx_rx_packet__check_len(rx_queue, rx_buf, len, + &discard, &leak_packet); + + EFX_TRACE(efx, "RX queue %d received id %x at %llx+%x %s%s\n", + rx_queue->queue, index, + (unsigned long long)rx_buf->dma_addr, len, + (checksummed ? " [SUMMED]" : ""), + (discard ? " [DISCARD]" : "")); + + /* Discard packet, if instructed to do so */ + if (unlikely(discard)) { + if (unlikely(leak_packet)) + rx_queue->channel->n_skbuff_leaks++; + else + /* We haven't called efx_unmap_rx_buffer yet, + * so fini the entire rx_buffer here */ + efx_fini_rx_buffer(rx_queue, rx_buf); + return; + } + + /* Release card resources - assumes all RX buffers consumed in-order + * per RX queue + */ + efx_unmap_rx_buffer(efx, rx_buf); + + /* Prefetch nice and early so data will (hopefully) be in cache by + * the time we look at it. + */ + prefetch(rx_buf->data); + + /* Pipeline receives so that we give time for packet headers to be + * prefetched into cache. + */ + rx_buf->len = len; + if (rx_queue->channel->rx_pkt) + __efx_rx_packet(rx_queue->channel, + rx_queue->channel->rx_pkt, + rx_queue->channel->rx_pkt_csummed); + rx_queue->channel->rx_pkt = rx_buf; + rx_queue->channel->rx_pkt_csummed = checksummed; +} + +/* Handle a received packet. Second half: Touches packet payload. */ +void __efx_rx_packet(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, int checksummed) +{ + struct efx_nic *efx = channel->efx; + enum efx_veto veto; + struct sk_buff *skb; + int lro = efx->net_dev->features & NETIF_F_LRO; + + /* If we're in loopback test, then pass the packet directly to the + * loopback layer, and free the rx_buf here + */ + if (unlikely(efx->loopback_selftest)) { + efx_loopback_rx_packet(efx, rx_buf->data, rx_buf->len); + efx_free_rx_buffer(efx, rx_buf); + goto done; + } + + if (rx_buf->skb) { + /* Prefetch more information */ + prefetch(skb_shinfo(rx_buf->skb)); + + /* Reserve space for the data */ + skb_put(rx_buf->skb, rx_buf->len); + + /* Move past the ethernet header. rx_buf->data still points + * at the ethernet header */ + rx_buf->skb->protocol = eth_type_trans(rx_buf->skb, + efx->net_dev); + } + + /* Both our generic-LRO and SFC-SSR support skb and page based + * allocation, but neither support switching from one to the + * other on the fly. If we spot that the allocation mode has + * changed, then flush the LRO state. + */ + if (unlikely(channel->rx_alloc_pop_pages != (rx_buf->page != NULL))) { + efx_flush_lro(channel); + channel->rx_alloc_pop_pages = (rx_buf->page != NULL); + } + if (likely(checksummed && lro)) { + efx_rx_packet_lro(channel, rx_buf); + goto done; + } + + /* Allow callback to veto the packet */ + veto = EFX_DL_CALLBACK(efx, rx_packet, rx_buf->data, rx_buf->len); + if (unlikely(veto)) { + EFX_LOG(efx, "RX vetoed by driverlink %s driver\n", + efx->dl_cb_dev.rx_packet->driver->name); + /* Free the buffer now */ + efx_free_rx_buffer(efx, rx_buf); + goto done; + } + + /* Form an skb if required */ + if (rx_buf->page) { + int hdr_len = min(rx_buf->len, EFX_SKB_HEADERS); + skb = efx_rx_mk_skb(rx_buf, efx, hdr_len); + if (unlikely(skb == NULL)) { + efx_free_rx_buffer(efx, rx_buf); + goto done; + } + } else { + /* We now own the SKB */ + skb = rx_buf->skb; + rx_buf->skb = NULL; + } + + EFX_BUG_ON_PARANOID(rx_buf->page); + EFX_BUG_ON_PARANOID(rx_buf->skb); + EFX_BUG_ON_PARANOID(!skb); + + /* Set the SKB flags */ + if (unlikely(!checksummed || !efx->rx_checksum_enabled)) + skb->ip_summed = CHECKSUM_NONE; + + /* Pass the packet up */ + netif_receive_skb(skb); + + /* Update allocation strategy method */ + channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB; + + /* fall-thru */ +done: + /* Update statistics */ + efx->net_dev->last_rx = jiffies; +} + +void efx_rx_strategy(struct efx_channel *channel) +{ + enum efx_rx_alloc_method method = rx_alloc_method; + + /* Only makes sense to use page based allocation if LRO is enabled */ + if (!(channel->efx->net_dev->features & NETIF_F_LRO)) { + method = RX_ALLOC_METHOD_SKB; + } else if (method == RX_ALLOC_METHOD_AUTO) { + /* Constrain the rx_alloc_level */ + if (channel->rx_alloc_level < 0) + channel->rx_alloc_level = 0; + else if (channel->rx_alloc_level > RX_ALLOC_LEVEL_MAX) + channel->rx_alloc_level = RX_ALLOC_LEVEL_MAX; + + /* Decide on the allocation method */ + method = ((channel->rx_alloc_level > RX_ALLOC_LEVEL_LRO) ? + RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB); + } + + /* Push the option */ + channel->rx_alloc_push_pages = (method == RX_ALLOC_METHOD_PAGE); +} + +int efx_probe_rx_queue(struct efx_rx_queue *rx_queue) +{ + struct efx_nic *efx = rx_queue->efx; + unsigned int rxq_size; + int rc; + + EFX_LOG(efx, "creating RX queue %d\n", rx_queue->queue); + + /* Allocate RX buffers */ + rxq_size = (efx->type->rxd_ring_mask + 1) * sizeof(*rx_queue->buffer); + rx_queue->buffer = kzalloc(rxq_size, GFP_KERNEL); + if (!rx_queue->buffer) { + rc = -ENOMEM; + goto fail1; + } + + rc = falcon_probe_rx(rx_queue); + if (rc) + goto fail2; + + return 0; + + fail2: + kfree(rx_queue->buffer); + rx_queue->buffer = NULL; + fail1: + /* Mark queue as unused */ + rx_queue->used = 0; + + return rc; +} + +int efx_init_rx_queue(struct efx_rx_queue *rx_queue) +{ + EFX_LOG(rx_queue->efx, "initialising RX queue %d\n", rx_queue->queue); + + ASSERT_RTNL(); + + /* Initialise other fields */ + rx_queue->added_count = 0; + rx_queue->notified_count = 0; + rx_queue->removed_count = 0; + rx_queue->min_fill = -1U; + rx_queue->min_overfill = -1U; + + /* Set up RX descriptor ring */ + return falcon_init_rx(rx_queue); +} + +void efx_fini_rx_queue(struct efx_rx_queue *rx_queue) +{ + int i; + struct efx_rx_buffer *rx_buf; + + EFX_LOG(rx_queue->efx, "shutting down RX queue %d\n", rx_queue->queue); + + ASSERT_RTNL(); + + /* Flush RX queue and remove descriptor ring */ + falcon_fini_rx(rx_queue); + + /* Release RX buffers NB start at index 0 not current HW ptr */ + if (rx_queue->buffer) { + for (i = 0; i <= rx_queue->efx->type->rxd_ring_mask; i++) { + rx_buf = efx_rx_buffer(rx_queue, i); + efx_fini_rx_buffer(rx_queue, rx_buf); + } + } + + /* For a page that is part-way through splitting into RX buffers */ + if (rx_queue->buf_page != NULL) { + pci_unmap_page(rx_queue->efx->pci_dev, rx_queue->buf_dma_addr, + RX_PAGE_SIZE(rx_queue->efx), PCI_DMA_FROMDEVICE); + __free_pages(rx_queue->buf_page, + rx_queue->efx->rx_buffer_order); + rx_queue->buf_page = NULL; + } +} + +void efx_remove_rx_queue(struct efx_rx_queue *rx_queue) +{ + EFX_LOG(rx_queue->efx, "destroying RX queue %d\n", rx_queue->queue); + + falcon_remove_rx(rx_queue); + + kfree(rx_queue->buffer); + rx_queue->buffer = NULL; + rx_queue->used = 0; +} + +/* Flush LRO/SSR state for the given channel */ +void efx_flush_lro(struct efx_channel *channel) +{ + lro_flush_all(&channel->lro_mgr); +} + + +module_param(rx_alloc_method, int, 0644); +MODULE_PARM_DESC(rx_alloc_method, "Allocation method used for RX buffers"); + + Index: head-2008-03-17/drivers/net/sfc/rx.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/rx.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,47 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2006: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed by Solarflare Communications + * + * 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_RX_H +#define EFX_RX_H + +#include "net_driver.h" + + +int efx_probe_rx_queue(struct efx_rx_queue *rx_queue); +void efx_remove_rx_queue(struct efx_rx_queue *rx_queue); +int efx_init_rx_queue(struct efx_rx_queue *rx_queue); +void efx_fini_rx_queue(struct efx_rx_queue *rx_queue); + +int efx_lro_init(struct net_lro_mgr *lro_mgr, struct efx_nic *efx); +void efx_lro_fini(struct net_lro_mgr *lro_mgr); +void efx_flush_lro(struct efx_channel *channel); +void efx_rx_strategy(struct efx_channel *channel); +void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue); +void efx_rx_work(struct work_struct *data); +void __efx_rx_packet(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, int checksummed); + + +#endif /* EFX_RX_H */ Index: head-2008-03-17/drivers/net/sfc/selftest.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/selftest.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,811 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net_driver.h" +#include "ethtool.h" +#include "efx.h" +#include "falcon.h" +#include "selftest.h" +#include "boards.h" +#include "workarounds.h" + +/* Self tests */ + +/* + * Loopback test packet structure + * + * The self-test should stress every RSS vector, and unfortunately + * Falcon only performs RSS on TCP/UDP packets. + */ +struct efx_loopback_payload { + struct ethhdr header; + struct iphdr ip; + struct udphdr udp; + u16 iteration; + const char msg[64]; +} __attribute__ ((packed)); + +/* Loopback test source MAC address */ +static const unsigned char payload_source[ETH_ALEN] = { + 0x00, 0x0f, 0x53, 0x1b, 0x1b, 0x1b, +}; + +static const char *payload_msg = + "Hello world! This is an Efx loopback test in progress!"; + +struct efx_selftest_state { + /* Drop all packets in efx_loopback_rx_packet */ + int flush; + + /* Number of packets being used in this test */ + int packet_count; + + /* RX good packet count */ + atomic_t rx_good; + + /* RX bad packet count */ + atomic_t rx_bad; + + /* Payload used in tests */ + struct efx_loopback_payload payload; +}; + +/************************************************************************** + * + * Configurable values + * + **************************************************************************/ + +/* Level of loopback testing + * + * The maximum packet burst length is 16**(n-1), i.e. + * + * - Level 0 : no packets + * - Level 1 : 1 packet + * - Level 2 : 17 packets (1 * 1 packet, 1 * 16 packets) + * - Level 3 : 273 packets (1 * 1 packet, 1 * 16 packet, 1 * 256 packets) + * + */ +static unsigned int loopback_test_level = 3; + +/************************************************************************** + * + * Interrupt and event queue testing + * + **************************************************************************/ + +/* + * Test interrupts + * + * This generates a test interrupt and waits for it to be received by + * a CPU. This routine must be called from process context and will + * sleep. + */ +static int efx_test_interrupts(struct efx_nic *efx, + struct efx_self_tests *tests) +{ + struct efx_channel *channel; + unsigned long j_start; + + EFX_LOG(efx, "testing interrupts\n"); + tests->interrupt = -1; + + /* Reset interrupt flag */ + efx->last_irq_cpu = -1; + smp_wmb(); + + /* To guarantee that an interrupt is received make sure that the + * channels are ack'd at least once, reenabling interrupts. + * We will then receive an interrupt, either by explicitly + * requesting one, or receiving traffic. + */ + efx_for_each_channel_with_interrupt(channel, efx) { + /* Process the eventq synchronously */ + if (channel->work_pending) + efx_process_channel_now(channel); + /* Check if we haven't already received an interrupt */ + if (efx->last_irq_cpu >= 0) + goto success; + } + + j_start = jiffies; + + /* Generate test interrupt. */ + falcon_generate_interrupt(efx); + + /* Wait for arrival of test interrupt. */ + EFX_LOG(efx, "waiting for test interrupt\n"); + schedule_timeout_uninterruptible(HZ / 10); + if (efx->last_irq_cpu >= 0) + goto success; + + EFX_ERR(efx, "timed out in %ld jiffies waiting for interrupt\n", + jiffies - j_start); + return -ETIMEDOUT; + + success: + EFX_LOG(efx, "%s test interrupt seen on CPU%d\n", INT_MODE(efx), + efx->last_irq_cpu); + tests->interrupt = 1; + return 0; +} + +/* + * Test capability of events to receive events + * This generates a test event and waits for it to be processed + * This routine must be called from process context and will sleep. + */ +static int efx_test_eventq(struct efx_channel *channel, + struct efx_self_tests *tests) +{ + unsigned int magic; + + /* Channel specific code, limited to 20 bits */ + magic = (0x00010150 + channel->channel); + EFX_LOG(channel->efx, "channel %d testing event queue with code %x\n", + channel->channel, magic); + + tests->eventq_dma[channel->channel] = -1; + tests->eventq_int[channel->channel] = 1; /* fake pass */ + tests->eventq_poll[channel->channel] = 1; /* fake pass */ + + /* Reset flag and zero magic word */ + channel->efx->last_irq_cpu = -1; + channel->eventq_magic = 0; + smp_wmb(); + + /* Generate test event */ + falcon_generate_test_event(channel, magic); + udelay(1); + + efx_process_channel_now(channel); + if (channel->eventq_magic != magic) { + EFX_ERR(channel->efx, "channel %d failed to see test event\n", + channel->channel); + return -ETIMEDOUT; + } else { + tests->eventq_dma[channel->channel] = 1; + } + + return 0; +} + + +/* + * Test capability of events to generate interrupts + * This generates a test event and waits for it to be processed by an + * ISR. This routine must be called from process context and will + * sleep. + */ +static int efx_test_eventq_irq(struct efx_channel *channel, + struct efx_self_tests *tests) +{ + unsigned int magic, count; + unsigned long j_start = jiffies; + + /* Channel specific code, limited to 20 bits */ + magic = (0x00010150 + channel->channel); + EFX_LOG(channel->efx, "channel %d testing event queue with code %x\n", + channel->channel, magic); + + tests->eventq_dma[channel->channel] = -1; + tests->eventq_int[channel->channel] = -1; + tests->eventq_poll[channel->channel] = -1; + + /* Reset flag and zero magic word */ + channel->efx->last_irq_cpu = -1; + channel->eventq_magic = 0; + smp_wmb(); + + /* Generate test event */ + falcon_generate_test_event(channel, magic); + + /* Wait for arrival of interrupt */ + count = 0; + do { + schedule_timeout_uninterruptible(HZ / 100); + + if (channel->work_pending) + efx_process_channel_now(channel); + + if (channel->eventq_magic == magic) + goto eventq_ok; + } + while (++count < 2); + + EFX_ERR(channel->efx, "channel %d timed out in %ld jiffies waiting for" + " event queue\n", channel->channel, jiffies - j_start); + + /* See if interrupt arrived */ + if (channel->efx->last_irq_cpu >= 0) { + EFX_ERR(channel->efx, "channel %d saw interrupt on CPU%d " + "during event queue test\n", channel->channel, + raw_smp_processor_id()); + tests->eventq_int[channel->channel] = 1; + } + + /* Check to see if event was received even if interrupt wasn't */ + efx_process_channel_now(channel); + if (channel->eventq_magic == magic) { + EFX_ERR(channel->efx, "channel %d event was generated, but " + "failed to trigger an interrupt\n", channel->channel); + tests->eventq_dma[channel->channel] = 1; + } + + return -ETIMEDOUT; + eventq_ok: + EFX_LOG(channel->efx, "channel %d event queue passed\n", + channel->channel); + tests->eventq_dma[channel->channel] = 1; + tests->eventq_int[channel->channel] = 1; + tests->eventq_poll[channel->channel] = 1; + return 0; +} + +/************************************************************************** + * + * PHY testing + * + **************************************************************************/ + +/* + * Check PHY presence + * This reads the PHY ID registers via GMII and checks that neither + * are all-zeroes or all-ones (indicating a nonexistent or + * uncommunicative PHY). + */ +static int efx_test_phy(struct efx_nic *efx, + struct efx_self_tests *tests) +{ + u16 physid1, physid2; + struct mii_if_info *mii = &efx->mii; + struct net_device *net_dev = efx->net_dev; + + if (efx->phy_type == PHY_TYPE_NONE) + return 0; + + EFX_LOG(efx, "testing PHY presence\n"); + tests->phy_ok = -1; + + physid1 = mii->mdio_read(net_dev, mii->phy_id, MII_PHYSID1); + physid2 = mii->mdio_read(net_dev, mii->phy_id, MII_PHYSID2); + + if ((physid1 != 0x0000) && (physid1 != 0xffff) && + (physid2 != 0x0000) && (physid2 != 0xffff)) { + EFX_LOG(efx, "found MII PHY %d ID 0x%x:%x\n", + mii->phy_id, physid1, physid2); + tests->phy_ok = 1; + return 0; + } + + EFX_ERR(efx, "no MII PHY present with ID %d\n", mii->phy_id); + return -ENODEV; +} + +/************************************************************************** + * + * Loopback testing + * NB Only one loopback test can be executing concurrently. + * + **************************************************************************/ + +/* Loopback test RX callback + * This is called for each received packet during loopback testing. + */ +void efx_loopback_rx_packet(struct efx_nic *efx, + const char *buf_ptr, int pkt_len) +{ + struct efx_selftest_state *state = efx->loopback_selftest; + struct efx_loopback_payload *received; + struct efx_loopback_payload *payload; + + BUG_ON(!buf_ptr); + + /* If we are just flushing, then drop the packet */ + if ((state == NULL) || state->flush) + return; + + payload = &state->payload; + + /* The packet should have been passed up to us before any LRO/SSR, so + * we should be able to compare the data directly. Since the packet + * is going to be thrown away by the caller, modify it in place. + * efx_test_loopback guarantees to not touch state->payload during + * the test */ + received = (struct efx_loopback_payload *)(char *)buf_ptr; + received->ip.saddr = payload->ip.saddr; + received->ip.check = payload->ip.check; + + /* Check that header exists */ + if (pkt_len < sizeof(received->header)) { + EFX_ERR(efx, "saw runt RX packet (length %d) " + "in %s loopback test\n", pkt_len, + LOOPBACK_MODE(efx)); + goto err; + } + + /* Check that header matches */ + if (memcmp(&received->header, &payload->header, ETH_HLEN) != 0) { + EFX_ERR(efx, "saw non-loopback RX packet in" + " %s loopback test\n", + LOOPBACK_MODE(efx)); + goto err; + } + + /* Check packet length */ + if (pkt_len != sizeof(*payload)) { + EFX_ERR(efx, "saw incorrect RX packet length" + " %d (wanted %d) in %s loopback test\n", + pkt_len, (int)sizeof(*payload), + LOOPBACK_MODE(efx)); + goto err; + } + + /* Check that IP header matches */ + if (memcmp(&received->ip, &payload->ip, sizeof(payload->ip)) != 0) { + EFX_ERR(efx, "saw corrupted IP header in %s " + "loopback test\n", + LOOPBACK_MODE(efx)); + goto err; + } + + /* Check that msg and padding matches */ + if (memcmp(&received->msg, &payload->msg, sizeof(received->msg)) != 0) { + EFX_ERR(efx, "saw corrupted RX packet in %s " + "loopback test\n", + LOOPBACK_MODE(efx)); + goto err; + } + + /* Check that iteration matches */ + if (received->iteration != payload->iteration) { + EFX_ERR(efx, "saw RX packet from iteration %d" + " (wanted %d) in %s loopback test\n", + ntohs(received->iteration), ntohs(payload->iteration), + LOOPBACK_MODE(efx)); + goto err; + } + + /* Increase correct RX count */ + EFX_TRACE(efx, "got loopback RX in %s loopback test\n", + LOOPBACK_MODE(efx)); + + atomic_inc(&state->rx_good); + return; + + err: +#ifdef EFX_ENABLE_DEBUG + if (atomic_read(&state->rx_bad) == 0) { + EFX_ERR(efx, "received packet:\n"); + print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1, + buf_ptr, pkt_len, 0); + EFX_ERR(efx, "expected packet:\n"); + print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1, + &state->payload, sizeof(state->payload), 0); + } +#endif + atomic_inc(&state->rx_bad); +} + +/* Initialise an efx_selftest_state for a new iteration */ +static void efx_iterate_state(struct efx_nic *efx) +{ + struct efx_selftest_state *state = efx->loopback_selftest; + struct net_device *net_dev = efx->net_dev; + struct efx_loopback_payload *payload = &state->payload; + + /* Initialise the layerII header */ + memcpy(&payload->header.h_dest, net_dev->dev_addr, ETH_ALEN); + memcpy(&payload->header.h_source, &payload_source, ETH_ALEN); + payload->header.h_proto = htons(ETH_P_IP); + + /* saddr set later and used as incrementing count */ + payload->ip.daddr = ntohl(INADDR_LOOPBACK); + payload->ip.ihl = 5; + payload->ip.check = 0; /* offloaded */ + payload->ip.tot_len = htons(sizeof(*payload) - sizeof(struct ethhdr)); + payload->ip.version = IPVERSION; + payload->ip.protocol = IPPROTO_UDP; + + /* Initialise udp header */ + payload->udp.source = 0; + payload->udp.len = ntohs(sizeof(*payload) - sizeof(struct ethhdr) - + sizeof(struct iphdr)); + payload->udp.check = 0; /* checksum ignored */ + + /* Fill out payload */ + payload->iteration = htons(ntohs(payload->iteration) + 1); + memcpy(&payload->msg, payload_msg, sizeof(payload_msg)); + + /* Fill out remaining state members */ + atomic_set(&state->rx_good, 0); + atomic_set(&state->rx_bad, 0); + smp_wmb(); +} + +/* + * Perform loopback test with N packets + * + * This will transmit "num_packets" copies of a test packet, and check + * that they were both transmitted (i.e. a TX completion event was + * received) and received (i.e. the data arrived intact via loopback). + * The port must have already been placed into the desired loopback + * mode. + */ +static int efx_test_loopback(struct efx_nic *efx, + struct efx_tx_queue *tx_queue, + struct efx_loopback_self_tests *lb_tests) +{ + struct efx_channel *channel; + struct efx_selftest_state *state = efx->loopback_selftest; + struct efx_loopback_payload *payload; + struct sk_buff *skb; + int rc = 0, i, tx_done, rx_good, rx_bad; + + /* Fill out the packet contents */ + efx_iterate_state(efx); + + /* Create and fill skb */ + skb = alloc_skb(sizeof(state->payload), GFP_KERNEL); + if (!skb) { + rc = -ENOMEM; + goto out1; + } + payload = ((struct efx_loopback_payload *) + skb_put(skb, sizeof(state->payload))); + memcpy(payload, &state->payload, sizeof(state->payload)); + + /* Transmit N copies of buffer */ + for (i = 0; i < state->packet_count; i++) { + /* Set the source address in the copy of the packet. + * Incrementing the source address on a per-packet basis + * should ensure that we stress all RSS vectors */ + payload->ip.saddr = ntohl(INADDR_LOOPBACK | (i << 2)); + skb_get(skb); + + if (efx_xmit(efx, tx_queue, skb) != NETDEV_TX_OK) { + EFX_ERR(efx, "TX queue %d could not transmit " + "packet %d of %d in %s loopback test\n", + tx_queue->queue, i + 1, + state->packet_count, LOOPBACK_MODE(efx)); + rc = -EPIPE; + goto out2; + } + + /* Avoid hogging the PCI bus */ + udelay(10); + } + + /* NAPI polling is not enabled, so process channels synchronously */ + schedule_timeout_uninterruptible(HZ / 50); + efx_for_each_channel_with_interrupt(channel, efx) { + if (channel->work_pending) + efx_process_channel_now(channel); + } + + if (state->flush) + goto out3; + + /* Check TX completion and received packet counts */ + tx_done = state->packet_count - (atomic_read(&skb->users) - 1); + rx_good = atomic_read(&state->rx_good); + rx_bad = atomic_read(&state->rx_bad); + if (tx_done != state->packet_count) { + /* Don't free the skbs; they will be picked up on TX + * overflow or channel teardown. + */ + EFX_ERR(efx, "TX queue %d saw only %d out of an " + "expected %d TX completion events in %s loopback " + "test\n", tx_queue->queue, tx_done, + state->packet_count, LOOPBACK_MODE(efx)); + rc = -ETIMEDOUT; + /* Allow to fall through so we see the RX errors as well */ + } + + /* We may always be up to a flush away from our desired packet total */ + if (rx_good != state->packet_count) { + EFX_LOG(efx, "TX queue %d saw only %d out of an " + "expected %d received packets in %s loopback " + "test\n", tx_queue->queue, rx_good, + state->packet_count, LOOPBACK_MODE(efx)); + rc = -ETIMEDOUT; + /* Fall through */ + } + + /* Update loopback test structure */ + lb_tests->tx_sent[tx_queue->queue] += state->packet_count; + lb_tests->tx_done[tx_queue->queue] += tx_done; + lb_tests->rx_good += rx_good; + lb_tests->rx_bad += rx_bad; + + out3: + out2: + /* Free skb */ + if (skb) { + WARN_ON(skb_shared(skb)); + dev_kfree_skb_any(skb); + } + + out1: + return rc; +} + +/* Perform loopback test safely + * + * This performs a safe loopback test by starting with a single packet + * and only increasing the number of packets while the tests are + * passing. This avoids flooding a network with garbage packets if + * e.g. setting the loopback mode fails. + */ +static int +efx_test_loopback_safely(struct efx_nic *efx, + struct efx_tx_queue *tx_queue, + struct efx_loopback_self_tests *lb_tests) +{ + struct efx_selftest_state *state = efx->loopback_selftest; + int i, rc = 0; + + for (i = 0; i < loopback_test_level; i++) { + /* Determine how many packets to send */ + state->packet_count = (efx->type->txd_ring_mask + 1) / 3; + state->packet_count = min(1 << (i << 2), state->packet_count); + state->flush = 0; + + EFX_LOG(efx, "TX queue %d testing %s loopback" + " with %d packets\n", tx_queue->queue, + LOOPBACK_MODE(efx), state->packet_count); + + rc = efx_test_loopback(efx, tx_queue, lb_tests); + if (rc) { + /* Wait a while to ensure there are no packets + * floating around after a failure. + */ + schedule_timeout_uninterruptible(HZ / 5); + return rc; + } + } + + EFX_LOG(efx, "TX queue %d passed %s loopback test " + "with a burst length of %d packets\n", + tx_queue->queue, LOOPBACK_MODE(efx), state->packet_count); + + return rc; +} + +static int efx_test_loopbacks(struct efx_nic *efx, + struct efx_self_tests *tests, + unsigned int loopback_modes) +{ + struct efx_selftest_state *state = efx->loopback_selftest; + struct ethtool_cmd ecmd, ecmd_loopback; + struct efx_tx_queue *tx_queue; + enum efx_loopback_mode old_mode, mode; + int old_powered, count, rc = 0; + int retry = EFX_WORKAROUND_8909(efx); + + /* Get current PHY settings */ + rc = efx_ethtool_get_settings(efx->net_dev, &ecmd); + if (rc) { + EFX_ERR(efx, "could not get GMII settings\n"); + return rc; + } + old_mode = efx->loopback_mode; + old_powered = efx->phy_powered; + + /* Disable autonegotiation for the purposes of loopback */ + memcpy(&ecmd_loopback, &ecmd, sizeof(ecmd_loopback)); + if (ecmd_loopback.autoneg == AUTONEG_ENABLE) { + ecmd_loopback.autoneg = AUTONEG_DISABLE; + ecmd_loopback.duplex = DUPLEX_FULL; + ecmd_loopback.speed = EFX_IS10G(efx) ? + SPEED_10000 : SPEED_1000; + } + + rc = efx_ethtool_set_settings(efx->net_dev, &ecmd_loopback); + if (rc) { + EFX_ERR(efx, "could not disable autonegotiation\n"); + goto out; + } + tests->loopback_speed = ecmd_loopback.speed; + tests->loopback_full_duplex = ecmd_loopback.duplex; + + /* Test all supported loopback modes */ + for (mode = LOOPBACK_NONE; mode < LOOPBACK_TEST_MAX; mode++) { + if (!(loopback_modes & (1 << mode))) + continue; + + /* Move the port into the specified loopback mode. */ + state->flush = 1; + efx->phy_powered = 1; + efx->loopback_mode = mode; + efx_reconfigure_port(efx, 0); + + /* Wait for the PHY to signal the link is up */ + count = 0; + do { + (void) efx->mac_op->check_hw(efx); + schedule_timeout_uninterruptible(HZ / 10); + efx_process_channel_now(&efx->channel[0]); + + /* Wait for PHY events to be processed */ + flush_workqueue(efx->workqueue); + rmb(); + } while ((++count < 20) && !efx->link_up); + + /* The link should now be up. If it isn't, there is no point + * in attempting a loopback test */ + if (!efx->link_up) { + EFX_ERR(efx, "loopback %s never came up\n", + LOOPBACK_MODE(efx)); + rc = -EIO; + goto out; + } + + EFX_LOG(efx, "link came up in %s loopback in %d " + "iterations\n", LOOPBACK_MODE(efx), + count); + + /* Test every TX queue */ + efx_for_each_tx_queue(tx_queue, efx) { + rc |= efx_test_loopback_safely(efx, tx_queue, + &tests->loopback[mode]); + if (rc) + goto fail; + } + + continue; + +fail: + if (retry) { + /* Give the PHY a kick by pretending to move into + * a Falcon internal loopback mode, then back out */ + int first = ffs(efx->loopback_modes) - 1; + + EFX_INFO(efx, "retrying %s loopback\n", + LOOPBACK_MODE(efx)); + + state->flush = 1; + efx->loopback_mode = first; + efx_reconfigure_port(efx, 0); + + retry = rc = 0; + --mode; + continue; + } + break; + } + + out: + /* Take out of loopback and restore PHY settings */ + state->flush = 1; + efx->loopback_mode = old_mode; + efx->phy_powered = old_powered; + /* Push the loopback change, and restore any other + * settings we may have trodden on */ + (void) efx_ethtool_set_settings(efx->net_dev, &ecmd); + + return rc; +} + +/************************************************************************** + * + * Entry points + * + *************************************************************************/ + +/* Online (i.e. non-disruptive) testing + * + * This checks interrupt generation, event delivery and PHY presence. + * The caller should hold the suspend lock + * + */ +int efx_online_test(struct efx_nic *efx, struct efx_self_tests *tests) +{ + struct efx_channel *channel; + int rc = 0; + + ASSERT_RTNL(); + + EFX_LOG(efx, "performing online self-tests\n"); + + rc |= efx_test_interrupts(efx, tests); + efx_for_each_channel(channel, efx) { + if (channel->has_interrupt) + rc |= efx_test_eventq_irq(channel, tests); + else + rc |= efx_test_eventq(channel, tests); + } + rc |= efx_test_phy(efx, tests); + + if (rc) + EFX_ERR(efx, "failed online self-tests\n"); + + return rc; +} + +/* Offline (i.e. disruptive) testing + * This checks MAC and PHY loopback on the specified port. The caller + * should hold the rtnl lock + */ +int efx_offline_test(struct efx_nic *efx, + struct efx_self_tests *tests, unsigned int loopback_modes) +{ + struct efx_selftest_state *state; + int rc = 0; + + ASSERT_RTNL(); + + EFX_LOG(efx, "performing offline self-tests\n"); + + /* Create a selftest_state structure to hold state for the test */ + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state == NULL) { + rc = -ENOMEM; + goto out; + } + + /* Stop the kernel queue. The net_dev watchdog won't barf here + * because we hold rtnl_lock() */ + efx_stop_queue(efx); + + /* Set the port loopback_selftest member. From this point on + * all received packets will be dropped. Mark the state as + * "flushing" so all inflight packets are dropped */ + BUG_ON(efx->loopback_selftest); + state->flush = 1; + efx->loopback_selftest = (void *)state; + wmb(); + + /* Test all loopback modes */ + rc = efx_test_loopbacks(efx, tests, loopback_modes); + + /* Tidy up the port test state */ + efx->loopback_selftest = NULL; + wmb(); + kfree(state); + + /* Start the kernel queue */ + efx_wake_queue(efx); + + out: + if (rc) + EFX_ERR(efx, "failed offline self-tests\n"); + + return rc; +} + Index: head-2008-03-17/drivers/net/sfc/selftest.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/selftest.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,67 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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_SELFTEST_H +#define EFX_SELFTEST_H + +#include "net_driver.h" + +/* + * Self tests + */ + +struct efx_loopback_self_tests { + int tx_sent[EFX_MAX_TX_QUEUES]; + int tx_done[EFX_MAX_TX_QUEUES]; + int rx_good; + int rx_bad; +}; + +/* Efx self test results + * For fields which are not counters, 1 indicates success and -1 + * indicates failure. + */ +struct efx_self_tests { + int interrupt; + int eventq_dma[EFX_MAX_CHANNELS]; + int eventq_int[EFX_MAX_CHANNELS]; + int eventq_poll[EFX_MAX_CHANNELS]; + int phy_ok; + int loopback_speed; + int loopback_full_duplex; + struct efx_loopback_self_tests loopback[LOOPBACK_TEST_MAX]; +}; + +extern void efx_loopback_rx_packet(struct efx_nic *efx, + const char *buf_ptr, int pkt_len); +extern int efx_online_test(struct efx_nic *efx, + struct efx_self_tests *tests); +extern int efx_offline_test(struct efx_nic *efx, + struct efx_self_tests *tests, + unsigned int loopback_modes); + +#endif /* EFX_SELFTEST_H */ Index: head-2008-03-17/drivers/net/sfc/sfe4001.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/sfe4001.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,315 @@ +/**************************************************************************** + * 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 + * + * 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 + ****************************************************************************/ + +/***************************************************************************** + * Support for the SFE4001 NIC: driver code for the PCA9539 I/O expander that + * controls the PHY power rails, and for the MAX6647 temp. sensor used to check + * the PHY + */ +#include +#include "efx.h" +#include "phy.h" +#include "boards.h" +#include "falcon.h" +#include "falcon_hwdefs.h" + +/************************************************************************** + * + * I2C IO Expander device + * + **************************************************************************/ +#define PCA9539 0x74 + +#define P0_IN 0x00 +#define P0_OUT 0x02 +#define P0_INVERT 0x04 +#define P0_CONFIG 0x06 + +#define P0_EN_1V0X_LBN 0 +#define P0_EN_1V0X_WIDTH 1 +#define P0_EN_1V2_LBN 1 +#define P0_EN_1V2_WIDTH 1 +#define P0_EN_2V5_LBN 2 +#define P0_EN_2V5_WIDTH 1 +#define P0_EN_3V3X_LBN 3 +#define P0_EN_3V3X_WIDTH 1 +#define P0_EN_5V_LBN 4 +#define P0_EN_5V_WIDTH 1 +#define P0_SHORTEN_JTAG_LBN 5 +#define P0_SHORTEN_JTAG_WIDTH 1 +#define P0_X_TRST_LBN 6 +#define P0_X_TRST_WIDTH 1 +#define P0_DSP_RESET_LBN 7 +#define P0_DSP_RESET_WIDTH 1 + +#define P1_IN 0x01 +#define P1_OUT 0x03 +#define P1_INVERT 0x05 +#define P1_CONFIG 0x07 + +#define P1_AFE_PWD_LBN 0 +#define P1_AFE_PWD_WIDTH 1 +#define P1_DSP_PWD25_LBN 1 +#define P1_DSP_PWD25_WIDTH 1 +#define P1_RESERVED_LBN 2 +#define P1_RESERVED_WIDTH 2 +#define P1_SPARE_LBN 4 +#define P1_SPARE_WIDTH 4 + + +/************************************************************************** + * + * Temperature Sensor + * + **************************************************************************/ +#define MAX6647 0x4e + +#define RLTS 0x00 +#define RLTE 0x01 +#define RSL 0x02 +#define RCL 0x03 +#define RCRA 0x04 +#define RLHN 0x05 +#define RLLI 0x06 +#define RRHI 0x07 +#define RRLS 0x08 +#define WCRW 0x0a +#define WLHO 0x0b +#define WRHA 0x0c +#define WRLN 0x0e +#define OSHT 0x0f +#define REET 0x10 +#define RIET 0x11 +#define RWOE 0x19 +#define RWOI 0x20 +#define HYS 0x21 +#define QUEUE 0x22 +#define MFID 0xfe +#define REVID 0xff + +/* Status bits */ +#define MAX6647_BUSY (1 << 7) /* ADC is converting */ +#define MAX6647_LHIGH (1 << 6) /* Local high temp. alarm */ +#define MAX6647_LLOW (1 << 5) /* Local low temp. alarm */ +#define MAX6647_RHIGH (1 << 4) /* Remote high temp. alarm */ +#define MAX6647_RLOW (1 << 3) /* Remote low temp. alarm */ +#define MAX6647_FAULT (1 << 2) /* DXN/DXP short/open circuit */ +#define MAX6647_EOT (1 << 1) /* Remote junction overtemp. */ +#define MAX6647_IOT (1 << 0) /* Local junction overtemp. */ + +static const u8 xgphy_max_temperature = 90; + +void sfe4001_poweroff(struct efx_nic *efx) +{ + struct efx_i2c_interface *i2c = &efx->i2c; + + u8 cfg, out, in; + + EFX_INFO(efx, "%s\n", __FUNCTION__); + + /* Turn off all power rails */ + out = 0xff; + (void) efx_i2c_write(i2c, PCA9539, P0_OUT, &out, EFX_BYTE); + + /* Disable port 1 outputs on IO expander */ + cfg = 0xff; + (void) efx_i2c_write(i2c, PCA9539, P1_CONFIG, &cfg, EFX_BYTE); + + /* Disable port 0 outputs on IO expander */ + cfg = 0xff; + (void) efx_i2c_write(i2c, PCA9539, P0_CONFIG, &cfg, EFX_BYTE); + + /* Clear any over-temperature alert */ + (void) efx_i2c_read(i2c, MAX6647, RSL, &in, EFX_BYTE); +} + +static int sfe4001_check_hw(struct efx_nic *efx) +{ + struct efx_i2c_interface *i2c = &efx->i2c; + int rc; + u8 status; + + /* Check the powered status of the PHY. Lack of power implies that + * the MAX6647 has shut down power to it, probably due to a temp. + * alarm. Reading the power status rather than the MAX6647 status + * directly because the later is read-to-clear and would thus + * start to power up the PHY again when polled, causing us to blip + * the power undesirably */ + + /* If XAUI link is down, check power status. Reading + * power requires a I2C byte read, which is too slow + * to poll (see SFC bug 7884). */ + if (falcon_xaui_link_ok(efx)) + return 0; + + rc = efx_i2c_read(i2c, PCA9539, P1_IN, &status, EFX_BYTE); + status &= ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN)); + + /* We know we can read from the IO expander because we did + * it during power-on. Assume failure now is bad news. */ + if (rc != 0 || status == 0) { + sfe4001_poweroff(efx); + + /* Note that the PHY is pining for the cooling fans */ + tenxpress_set_state(efx, TENXPRESS_STATUS_OTEMP); + + /* Log the info */ + if (status == 0) { + EFX_ERR(efx, "%s: Temperature sensor reports " + "alarm! (0x%x) Shutting down PHY.\n", + __FUNCTION__, status); + rc = -EIO; + } else { + EFX_ERR(efx, "%s: Failed to read PHY status!" + " Shutting down PHY.\n", + __FUNCTION__); + } + } + + return rc; +} + +/* This board uses an I2C expander to provider power to the PHY, which needs to + * be turned on before the PHY can be used. + * Context: Process context, rtnl lock held + */ +int sfe4001_poweron(struct efx_nic *efx) +{ + struct efx_i2c_interface *i2c = &efx->i2c; + unsigned int count; + int rc; + u8 out, in, cfg; + efx_dword_t reg; + + /* 10Xpress has fixed-function LED pins, so there is no board-specific + * blink code. */ + efx->board_info.blink = tenxpress_phy_blink; + + /* Ensure that XGXS and XAUI SerDes are held in reset */ + EFX_POPULATE_DWORD_7(reg, XX_PWRDNA_EN, 1, + XX_PWRDNB_EN, 1, + XX_RSTPLLAB_EN, 1, + XX_RESETA_EN, 1, + XX_RESETB_EN, 1, + XX_RSTXGXSRX_EN, 1, + XX_RSTXGXSTX_EN, 1); + efx->mac_op->mac_writel(efx, ®, XX_PWR_RST_REG_MAC); + udelay(10); + + efx->board_info.monitor = sfe4001_check_hw; + efx->board_info.fini = sfe4001_poweroff; + + /* Set DSP over-temperature alert threshold */ + EFX_INFO(efx, "DSP cut-out at %dC\n", xgphy_max_temperature); + rc = efx_i2c_write(i2c, MAX6647, WLHO, + &xgphy_max_temperature, EFX_BYTE); + if (rc) + goto fail1; + + /* Read it back and verify */ + rc = efx_i2c_read(i2c, MAX6647, RLHN, &in, EFX_BYTE); + if (rc) + goto fail1; + if (in != xgphy_max_temperature) { + rc = -EFAULT; + goto fail1; + } + + /* Clear any previous over-temperature alert */ + rc = efx_i2c_read(i2c, MAX6647, RSL, &in, EFX_BYTE); + if (rc) + goto fail1; + + /* Enable port 0 and port 1 outputs on IO expander */ + cfg = 0x00; + rc = efx_i2c_write(i2c, PCA9539, P0_CONFIG, &cfg, EFX_BYTE); + if (rc) + goto fail1; + cfg = 0xff & ~(1 << P1_SPARE_LBN); + rc = efx_i2c_write(i2c, PCA9539, P1_CONFIG, &cfg, EFX_BYTE); + if (rc) + goto fail2; + + /* Turn all power off then wait 1 sec. This ensures PHY is reset */ + out = 0xff & ~((0 << P0_EN_1V2_LBN) | (0 << P0_EN_2V5_LBN) | + (0 << P0_EN_3V3X_LBN) | (0 << P0_EN_5V_LBN) | + (0 << P0_EN_1V0X_LBN)); + rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, EFX_BYTE); + if (rc) + goto fail3; + + schedule_timeout_uninterruptible(HZ); + count = 0; + do { + /* Turn on 1.2V, 2.5V, 3.3V and 5V power rails */ + out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) | + (1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) | + (1 << P0_X_TRST_LBN)); + + rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, EFX_BYTE); + if (rc) + goto fail3; + mdelay(10); + + /* Turn on 1V power rail */ + out &= ~(1 << P0_EN_1V0X_LBN); + rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, EFX_BYTE); + if (rc) + goto fail3; + + EFX_INFO(efx, "waiting for power (attempt %d)...\n", count); + + schedule_timeout_uninterruptible(HZ); + + /* Check DSP is powered */ + rc = efx_i2c_read(i2c, PCA9539, P1_IN, &in, EFX_BYTE); + if (rc) + goto fail3; + if (in & (1 << P1_AFE_PWD_LBN)) + goto done; + + } while (++count < 20); + + EFX_INFO(efx, "timed out waiting for power\n"); + rc = -ETIMEDOUT; + goto fail3; + +done: + EFX_INFO(efx, "PHY is powered on\n"); + return 0; + +fail3: + /* Turn off all power rails */ + out = 0xff; + (void) efx_i2c_write(i2c, PCA9539, P0_OUT, &out, EFX_BYTE); + /* Disable port 1 outputs on IO expander */ + out = 0xff; + (void) efx_i2c_write(i2c, PCA9539, P1_CONFIG, &out, EFX_BYTE); +fail2: + /* Disable port 0 outputs on IO expander */ + out = 0xff; + (void) efx_i2c_write(i2c, PCA9539, P1_CONFIG, &out, EFX_BYTE); +fail1: + return rc; +} Index: head-2008-03-17/drivers/net/sfc/spi.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/spi.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,186 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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_SPI_H +#define EFX_SPI_H + +#include "net_driver.h" + +/************************************************************************** + * + * Basic SPI command set and bit definitions + * + *************************************************************************/ + +/* + * Commands common to all known devices. + * + */ + +/* Write status register */ +#define SPI_WRSR 0x01 + +/* Write data to memory array */ +#define SPI_WRITE 0x02 + +/* Read data from memory array */ +#define SPI_READ 0x03 + +/* Reset write enable latch */ +#define SPI_WRDI 0x04 + +/* Read status register */ +#define SPI_RDSR 0x05 + +/* Set write enable latch */ +#define SPI_WREN 0x06 + +/* SST: Enable write to status register */ +#define SPI_SST_EWSR 0x50 + +/* + * Status register bits. Not all bits are supported on all devices. + * + */ + +/* Write-protect pin enabled */ +#define SPI_STATUS_WPEN 0x80 + +/* Block protection bit 2 */ +#define SPI_STATUS_BP2 0x10 + +/* Block protection bit 1 */ +#define SPI_STATUS_BP1 0x08 + +/* Block protection bit 0 */ +#define SPI_STATUS_BP0 0x04 + +/* State of the write enable latch */ +#define SPI_STATUS_WEN 0x02 + +/* Device busy flag */ +#define SPI_STATUS_NRDY 0x01 + +/************************************************************************** + * + * Efx SPI devices + * + ************************************************************************** + */ + +/** + * struct efx_spi_device - an Efx SPI (Serial Peripheral Interface) device + * @device_id: Controller's id for the device + * @size: Size (in bytes) + * @addr_len: Number of address bytes in read/write commands + * @munge_address: Flag whether addresses should be munged. + * Some devices with 9-bit addresses (e.g. AT25040A EEPROM) + * use bit 3 of the command byte as address bit A8, rather + * than having a two-byte address. If this flag is set, then + * commands should be munged in this way. + * @erase_command: Erase command (or 0 if sector erase not needed). + * @erase_size: Erase sector size (in bytes) + * Erase commands affect sectors with this size and alignment. + * This must be a power of two. + * @block_size: Write block size (in bytes). + * Write commands are limited to blocks with this size and alignment. + * @read: Read function for the device + * @write: Write function for the device + */ +struct efx_spi_device { + int device_id; + unsigned int size; + unsigned int addr_len; + unsigned int munge_address:1; + u8 erase_command; + unsigned int erase_size; + unsigned int block_size; + int (*read) (const struct efx_spi_device *spi, + struct efx_nic *efx, unsigned int command, + int address, void *data, unsigned int len); + int (*write) (const struct efx_spi_device *spi, + struct efx_nic *efx, unsigned int command, + int address, const void *data, unsigned int len); +}; + +/* Maximum length for SPI read or write through Falcon */ +#define FALCON_SPI_MAX_LEN 16U + +/** + * efx_spi_write_limit - calculate maximum permitted length for write + * @spi: SPI device description + * @start: Starting address + * + * Return the maximum length for a write starting at the given address + * in the device. + * + * SPI writes must not cross block boundaries. Devices tend + * to wrap addresses at block boundaries; e.g. trying to write 5 bytes + * starting at offset 14 with a block size of 16 might write + * {14,15,0,1,2} rather than {14,15,16,17,18}. + */ +static inline unsigned int +efx_spi_write_limit(const struct efx_spi_device *spi, unsigned int start) +{ + return min(FALCON_SPI_MAX_LEN, + (spi->block_size - (start & (spi->block_size - 1)))); +} + +/** + * efx_spi_read_limit - calculate maximum permitted length for read + * @spi: SPI device description + * @start: Starting address + * + * Return the maximum length for a read starting at the given address + * in the device. + */ +static inline unsigned int +efx_spi_read_limit(const struct efx_spi_device *spi __attribute__ ((unused)), + unsigned int start __attribute__ ((unused))) +{ + return FALCON_SPI_MAX_LEN; +} + +/** + * efx_spi_munge_command - adjust command as necessary for given address + * @spi: SPI device description + * @command: Normal SPI command + * @address: Address for command + * + * Some devices with 9-bit addresses (e.g. AT25040A EEPROM) use bit 3 + * of the command byte as address bit A8, rather than having a + * two-byte address. This function calculates the appropriate command + * byte for the device, taking this munging into account. + */ +static inline u8 efx_spi_munge_command(const struct efx_spi_device *spi, + const u8 command, + const unsigned int address) +{ + return (command | (((address >> 8) & spi->munge_address) << 3)); +} + +#endif /* EFX_SPI_H */ Index: head-2008-03-17/drivers/net/sfc/tenxpress.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/tenxpress.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,695 @@ +/**************************************************************************** + * Driver for Solarflare 802.3an compliant PHY + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed by Solarflare Communications + * + * 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 +#include +#include "efx.h" +#include "debugfs.h" +#include "gmii.h" +#include "mdio_10g.h" +#include "falcon.h" +#include "phy.h" +#include "falcon_hwdefs.h" +#include "boards.h" + +/* We expect these MMDs to be in the package */ +/* AN not here as mdio_check_mmds() requires STAT2 support */ +#define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PMAPMD | \ + MDIO_MMDREG_DEVS0_PCS | \ + MDIO_MMDREG_DEVS0_PHYXS) + +#define TENXPRESS_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ + (1 << LOOPBACK_PCS) | \ + (1 << LOOPBACK_PMAPMD) | \ + (1 << LOOPBACK_NETWORK)) + +/* We complain if we fail to see the link partner as 10G capable this many + * times in a row (must be > 1 as sampling the autoneg. registers is racy) + */ +#define MAX_BAD_LP_TRIES (5) + +/* SNR operating margin register */ +#define PMA_PMD_SNR_MARGIN_0 (133) +#define PMA_PMD_SNR_MARGIN_1 (134) +#define PMA_PMD_SNR_MARGIN_2 (135) +#define PMA_PMD_SNR_MARGIN_3 (136) + +/* Extended control register */ +#define PMA_PMD_XCONTROL_REG 0xc000 +#define PMA_PMD_LNPGA_POWERDOWN_LBN 8 +#define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1 +#define PMA_PMD_AFE_POWERDOWN_LBN 9 +#define PMA_PMD_AFE_POWERDOWN_WIDTH 1 +#define PMA_PMD_DSP_POWERDOWN_LBN 10 +#define PMA_PMD_DSP_POWERDOWN_WIDTH 1 +#define PMA_PMD_PHY_POWERDOWN_LBN 11 +#define PMA_PMD_PHY_POWERDOWN_WI + +/* extended status register */ +#define PMA_PMD_XSTATUS_REG 0xc001 +#define PMA_PMD_XSTAT_FLP_LBN (12) + + +/* LED control register */ +#define PMA_PMD_LED_CTRL_REG (0xc007) +#define PMA_PMA_LED_ACTIVITY_LBN (3) + +/* LED function override register */ +#define PMA_PMD_LED_OVERR_REG (0xc009) +/* Bit positions for different LEDs (there are more but not wired on SFE4001)*/ +#define PMA_PMD_LED_LINK_LBN (0) +#define PMA_PMD_LED_SPEED_LBN (2) +#define PMA_PMD_LED_TX_LBN (4) +#define PMA_PMD_LED_RX_LBN (6) +/* Override settings */ +#define PMA_PMD_LED_AUTO (0) /* H/W control */ +#define PMA_PMD_LED_ON (1) +#define PMA_PMD_LED_OFF (2) +#define PMA_PMD_LED_FLASH (3) +/* All LEDs under hardware control */ +#define PMA_PMD_LED_FULL_AUTO (0) +/* Green and Amber under hardware control, Red off */ +#define PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) + + +/* Self test (BIST) control register */ +#define PMA_PMD_BIST_CTRL_REG (0xc014) +#define PMA_PMD_BIST_BER_LBN (2) /* Run BER test */ +#define PMA_PMD_BIST_CONT_LBN (1) /* Run continuous BIST until cleared */ +#define PMA_PMD_BIST_SINGLE_LBN (0) /* Run 1 BIST iteration (self clears) */ +/* Self test status register */ +#define PMA_PMD_BIST_STAT_REG (0xc015) +#define PMA_PMD_BIST_ENX_LBN (3) +#define PMA_PMD_BIST_PMA_LBN (2) +#define PMA_PMD_BIST_RXD_LBN (1) +#define PMA_PMD_BIST_AFE_LBN (0) + +/* Special Software reset register */ +#define PMA_PMD_EXT_CTRL_REG 49152 +#define PMA_PMD_EXT_SSR_LBN 15 + +#define BIST_MAX_DELAY (1000) +#define BIST_POLL_DELAY (10) + +static const char *bist_names[] = { + [PMA_PMD_BIST_AFE_LBN] = "AFE communication", + [PMA_PMD_BIST_RXD_LBN] = "RX data path", + [PMA_PMD_BIST_PMA_LBN] = "PMA loopback", + [PMA_PMD_BIST_ENX_LBN] = "ENX" +}; + +/* Identifier registers: each identifier has 4 part number and 2 revision + * registers starting at one of these addresses */ +#define PMA_PMD_AFE_ID_REG 49174 +#define PMA_PMD_DSP_ID_REG 49180 +#define PMA_PMD_FIRMWARE_ID_REG 49186 + +/* Misc register defines */ +#define PCS_CLOCK_CTRL_REG 0xd801 +#define PLL312_RST_N_LBN 2 + +#define PCS_SOFT_RST2_REG 0xd806 +#define SERDES_RST_N_LBN 13 +#define XGXS_RST_N_LBN 12 + +#define PCS_TEST_SELECT_REG 0xd807 /* PRM 10.5.8 */ +#define CLK312_EN_LBN 3 + +/* PHYXS registers */ +#define PHYXS_TEST1 (49162) +#define LOOPBACK_NEAR_LBN (8) +#define LOOPBACK_NEAR_WIDTH (1) + +/* Boot status register */ +#define PCS_BOOT_STATUS_REG (0xd000) +#define PCS_BOOT_FATAL_ERR_LBN (0) +#define PCS_BOOT_PROGRESS_LBN (1) +#define PCS_BOOT_PROGRESS_WIDTH (2) +#define PCS_BOOT_COMPLETE_LBN (3) + +#define PCS_BOOT_MAX_DELAY (100) +#define PCS_BOOT_POLL_DELAY (10) + +#define TENXPRESS_ID_PN_LEN (8) +#define TENXPRESS_ID_REV_LEN (4) +#define TENXPRESS_ID_LEN (TENXPRESS_ID_PN_LEN+1+TENXPRESS_ID_REV_LEN) + +static const int bist_max = ARRAY_SIZE(bist_names); + +/* Time to wait between powering down the LNPGA and turning off the power + * rails */ +#define LNPGA_PDOWN_WAIT (HZ / 5) + + +static int crc_error_reset_threshold = 100; +module_param(crc_error_reset_threshold, int, 0644); +MODULE_PARM_DESC(crc_error_reset_threshold, + "Max number of CRC errors before XAUI reset"); + +struct tenxpress_phy_data { +#ifdef CONFIG_SFC_DEBUGFS + char phy_snr[4]; + char phy_afe_id[TENXPRESS_ID_LEN + 1]; + char phy_dsp_id[TENXPRESS_ID_LEN + 1]; + char phy_firmware_id[TENXPRESS_ID_LEN + 1]; + struct efx_nic *efx; +#endif + enum tenxpress_state state; + enum efx_loopback_mode loopback_mode; + atomic_t bad_crc_count; + int phy_powered; + int tx_disabled; + int bad_lp_tries; +}; + +static int tenxpress_state_is(struct efx_nic *efx, int state) +{ + struct tenxpress_phy_data *phy_data = efx->phy_data; + return (phy_data != NULL) && (state == phy_data->state); +} + +void tenxpress_set_state(struct efx_nic *efx, + enum tenxpress_state state) +{ + struct tenxpress_phy_data *phy_data = efx->phy_data; + if (phy_data != NULL) + phy_data->state = state; +} + +void tenxpress_crc_err(struct efx_nic *efx) +{ + struct tenxpress_phy_data *phy_data = efx->phy_data; + if (phy_data != NULL) + atomic_inc(&phy_data->bad_crc_count); +} + +#ifdef CONFIG_SFC_DEBUGFS + +/* debugfs entries for this PHY */ +static int tenxpress_ber_read(struct seq_file *file, void *data) +{ + struct efx_nic *efx = *(struct efx_nic **)data; + int reg, ber; + + reg = mdio_clause45_read(efx, efx->mii.phy_id, MDIO_MMD_PCS, + MDIO_PCS_10GBT_STATUS2); + + /* Extract the BER */ + ber = (reg >> MDIO_PCS_10GBT_STATUS2_BER_LBN) & + ((1 << MDIO_PCS_10GBT_STATUS2_BER_WIDTH) - 1); + + return seq_printf(file, "%d", ber); +} + + +static int tenxpress_snr_read(struct seq_file *file, void *data) +{ + struct tenxpress_phy_data *phy_data = NULL; + struct efx_nic *efx; + int lane = *(char *) data; + int reg, snr; + + EFX_BUG_ON_PARANOID(lane < 0 || lane >= 4); + phy_data = container_of(data, struct tenxpress_phy_data, phy_snr[lane]); + efx = phy_data->efx; + + reg = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PMAPMD, PMA_PMD_SNR_MARGIN_0 + lane); + + /* Convert from SNR margin to SNR to match phychk output */ + snr = (reg - 0x8000 + 238); + + return seq_printf(file, "%d.%d", snr / 10, snr % 10); +} + + +static struct efx_debugfs_parameter debug_entries[] = { + EFX_PER_LANE_PARAMETER("phy_lane", "_snr", + struct tenxpress_phy_data, phy_snr, char, + tenxpress_snr_read), + EFX_NAMED_PARAMETER(phy_ber, struct tenxpress_phy_data, efx, + struct efx_nic *, tenxpress_ber_read), + EFX_STRING_PARAMETER(struct tenxpress_phy_data, phy_afe_id), + EFX_STRING_PARAMETER(struct tenxpress_phy_data, phy_dsp_id), + EFX_STRING_PARAMETER(struct tenxpress_phy_data, phy_firmware_id), + {NULL} +}; + +static void tenxpress_phy_get_id(struct efx_nic *efx, + char *id_buf, int id_addr) +{ + int i, reg; + char ch; + + for (i = TENXPRESS_ID_PN_LEN / 2 - 1; i >= 0; --i) { + reg = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PMAPMD, id_addr + i); + ch = reg & 0xFF; + *id_buf++ = ch ? ch : ' '; + ch = (reg & 0xFF00) >> 8; + *id_buf++ = ch ? ch : ' '; + } + *id_buf++ = ' '; + for (i = TENXPRESS_ID_REV_LEN / 2 - 1; i >= 0; --i) { + reg = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PMAPMD, + id_addr + TENXPRESS_ID_PN_LEN / 2 + i); + ch = reg & 0xFF; + *id_buf++ = ch ? ch : ' '; + ch = (reg & 0xFF00) >> 8; + *id_buf++ = ch ? ch : ' '; + } +} + +static int tenxpress_debugfs_init(struct efx_nic *efx) +{ + struct tenxpress_phy_data *phy_data = efx->phy_data; + int lane, rc; + + for (lane = 0; lane < 4; lane++) + phy_data->phy_snr[lane] = lane; + + phy_data->efx = efx; + rc = efx_extend_debugfs_port(efx, efx->phy_data, + debug_entries); + if (rc < 0) + return rc; + + tenxpress_phy_get_id(efx, phy_data->phy_afe_id, + PMA_PMD_AFE_ID_REG); + tenxpress_phy_get_id(efx, phy_data->phy_dsp_id, + PMA_PMD_DSP_ID_REG); + tenxpress_phy_get_id(efx, phy_data->phy_firmware_id, + PMA_PMD_FIRMWARE_ID_REG); + + return 0; +} + +#endif /* CONFIG_SFC_DEBUGFS */ + +/* Check that the C166 has booted successfully */ +static int tenxpress_phy_check(struct efx_nic *efx) +{ + int phy_id = efx->mii.phy_id; + int count = PCS_BOOT_MAX_DELAY / PCS_BOOT_POLL_DELAY; + int boot_stat; + + /* Wait for the boot to complete (or not) */ + while (count) { + boot_stat = mdio_clause45_read(efx, phy_id, + MDIO_MMD_PCS, + PCS_BOOT_STATUS_REG); + if (boot_stat & (1 << PCS_BOOT_COMPLETE_LBN)) + break; + count--; + udelay(PCS_BOOT_POLL_DELAY); + } + + if (!count) { + EFX_ERR(efx, "%s: PHY boot timed out. Last status " + "%x\n", __FUNCTION__, + (boot_stat >> PCS_BOOT_PROGRESS_LBN) & + ((1 << PCS_BOOT_PROGRESS_WIDTH) - 1)); + return -ETIMEDOUT; + } + + return 0; +} + +static void tenxpress_reset_xaui(struct efx_nic *efx); + +/* Initialise the part post power on reset or software special reset */ +static int tenxpress_init(struct efx_nic *efx) +{ + int rc, reg; + + /* Turn on the clock */ + reg = (1 << CLK312_EN_LBN); + mdio_clause45_write(efx, efx->mii.phy_id, + MDIO_MMD_PCS, PCS_TEST_SELECT_REG, reg); + + rc = tenxpress_phy_check(efx); + if (rc < 0) + return rc; + + /* Set the LEDs up as: Green = Link, Amber = Link/Act, Red = Off */ + reg = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG); + reg |= (1 << PMA_PMA_LED_ACTIVITY_LBN); + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_LED_CTRL_REG, reg); + + reg = PMA_PMD_LED_DEFAULT; + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_LED_OVERR_REG, reg); + + return rc; +} + +static int tenxpress_phy_init(struct efx_nic *efx) +{ + struct tenxpress_phy_data *phy_data; + int rc = 0; + + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); + efx->phy_data = phy_data; + phy_data->phy_powered = efx->phy_powered; + + tenxpress_set_state(efx, TENXPRESS_STATUS_NORMAL); + + rc = mdio_clause45_wait_reset_mmds(efx, + TENXPRESS_REQUIRED_DEVS); + if (rc < 0) + goto fail; + + rc = mdio_clause45_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0); + if (rc < 0) + goto fail; + + rc = tenxpress_init(efx); + if (rc < 0) + goto fail; + + rc = tenxpress_debugfs_init(efx); + if (rc < 0) + goto fail; + + schedule_timeout_uninterruptible(HZ / 5); /* 200ms */ + + /* Let XGXS and SerDes out of reset and resets 10XPress */ + falcon_reset_xaui(efx); + + return 0; + + fail: + kfree(efx->phy_data); + efx->phy_data = NULL; + return rc; +} + +static int tenxpress_special_reset(struct efx_nic *efx) +{ + int rc, reg; + + EFX_TRACE(efx, "%s\n", __FUNCTION__); + + /* Initiate reset */ + reg = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PMAPMD, PMA_PMD_EXT_CTRL_REG); + reg |= (1 << PMA_PMD_EXT_SSR_LBN); + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_EXT_CTRL_REG, reg); + + mdelay(200); + + /* Wait for the blocks to come out of reset */ + rc = mdio_clause45_wait_reset_mmds(efx, + TENXPRESS_REQUIRED_DEVS); + if (rc < 0) + return rc; + + /* Try and reconfigure the device */ + rc = tenxpress_init(efx); + if (rc < 0) + return rc; + + return 0; +} + +static void tenxpress_set_bad_lp(struct efx_nic *efx, int bad_lp) +{ + struct tenxpress_phy_data *pd = efx->phy_data; + int reg; + + /* Nothing to do if all is well and was previously so. */ + if (!(bad_lp || pd->bad_lp_tries)) + return; + + reg = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG); + + if (bad_lp) + pd->bad_lp_tries++; + else + pd->bad_lp_tries = 0; + + if (pd->bad_lp_tries == MAX_BAD_LP_TRIES) { + pd->bad_lp_tries = 0; /* Restart count */ + reg &= ~(PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN); + reg |= (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN); + EFX_ERR(efx, "This NIC appears to be plugged into" + " a port that is not 10GBASE-T capable.\n" + " This PHY is 10GBASE-T ONLY, so no link can" + " be established.\n"); + } else { + reg |= (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN); + } + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_LED_OVERR_REG, reg); +} + +/* Check link status and return a boolean OK value. If the link is NOT + * OK we have a quick rummage round to see if we appear to be plugged + * into a non-10GBT port and if so warn the user that they won't get + * link any time soon as we are 10GBT only, unless caller specified + * not to do this check (it isn't useful in loopback) */ +static int tenxpress_link_ok(struct efx_nic *efx, int check_lp) +{ + int ok = mdio_clause45_links_ok(efx, TENXPRESS_REQUIRED_DEVS); + + if (ok) { + tenxpress_set_bad_lp(efx, 0); + } else if (check_lp) { + /* Are we plugged into the wrong sort of link? */ + int bad_lp = 0; + int phy_id = efx->mii.phy_id; + int an_stat = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, + MDIO_AN_STATUS); + int xphy_stat = mdio_clause45_read(efx, phy_id, + MDIO_MMD_PMAPMD, + PMA_PMD_XSTATUS_REG); + /* Are we plugged into anything that sends FLPs? If + * not we can't distinguish between not being plugged + * in and being plugged into a non-AN antique. The FLP + * bit has the advantage of not clearing when autoneg + * restarts. */ + if (!(xphy_stat & (1 << PMA_PMD_XSTAT_FLP_LBN))) { + tenxpress_set_bad_lp(efx, 0); + return ok; + } + + /* If it can do 10GBT it must be XNP capable */ + bad_lp = !(an_stat & (1 << MDIO_AN_STATUS_XNP_LBN)); + if (!bad_lp && (an_stat & (1 << MDIO_AN_STATUS_PAGE_LBN))) { + bad_lp = !(mdio_clause45_read(efx, phy_id, + MDIO_MMD_AN, MDIO_AN_10GBT_STATUS) & + (1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN)); + } + tenxpress_set_bad_lp(efx, bad_lp); + } + return ok; +} + +static void tenxpress_phyxs_loopback(struct efx_nic *efx) +{ + int phy_id = efx->mii.phy_id; + int ctrl1, ctrl2; + + ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS, + PHYXS_TEST1); + if (efx->loopback_mode == LOOPBACK_PHYXS) + ctrl2 |= (1 << LOOPBACK_NEAR_LBN); + else + ctrl2 &= ~(1 << LOOPBACK_NEAR_LBN); + if (ctrl1 != ctrl2) + mdio_clause45_write(efx, phy_id, MDIO_MMD_PHYXS, + PHYXS_TEST1, ctrl2); +} + +static void tenxpress_phy_reconfigure(struct efx_nic *efx) +{ + struct tenxpress_phy_data *phy_data = efx->phy_data; + int loop_change = LOOPBACK_OUT_OF(phy_data, efx, + TENXPRESS_LOOPBACKS); + + if (!tenxpress_state_is(efx, TENXPRESS_STATUS_NORMAL)) + return; + + /* When coming out of transmit disable, coming out of low power + * mode, or moving out of any PHY internal loopback mode, + * perform a special software reset */ + if (((efx->phy_powered && !efx->tx_disabled) && + (!phy_data->phy_powered || phy_data->tx_disabled)) || + loop_change) { + (void) tenxpress_special_reset(efx); + falcon_reset_xaui(efx); + } + + mdio_clause45_transmit_disable(efx, efx->tx_disabled); + mdio_clause45_phy_reconfigure(efx); + tenxpress_phyxs_loopback(efx); + + phy_data->tx_disabled = efx->tx_disabled; + phy_data->loopback_mode = efx->loopback_mode; + phy_data->phy_powered = efx->phy_powered; + efx->link_up = tenxpress_link_ok(efx, 0); + efx->link_options = GM_LPA_10000FULL; +} + +static void tenxpress_phy_clear_interrupt(struct efx_nic *efx) +{ + /* Nothing done here - LASI interrupts aren't reliable so poll */ +} + + +/* Poll PHY for interrupt */ +static int tenxpress_phy_check_hw(struct efx_nic *efx) +{ + struct tenxpress_phy_data *phy_data = efx->phy_data; + int phy_up = tenxpress_state_is(efx, TENXPRESS_STATUS_NORMAL); + int link_ok, rc = 0; + + link_ok = phy_up && tenxpress_link_ok(efx, 1); + + if (link_ok != efx->link_up) { + efx->link_up = link_ok; + efx->mac_op->fake_phy_event(efx); + } + + /* Nothing to check if we've already shut down the PHY */ + if (!phy_up) + return 0; + + if (atomic_read(&phy_data->bad_crc_count) > crc_error_reset_threshold) { + EFX_ERR(efx, "Resetting XAUI due to too many CRC errors\n"); + falcon_reset_xaui(efx); + atomic_set(&phy_data->bad_crc_count, 0); + } + + rc = efx->board_info.monitor(efx); + if (rc) + efx->link_up = 0; + + return rc; +} + +static void tenxpress_phy_fini(struct efx_nic *efx) +{ + int reg; + +#ifdef CONFIG_SFC_DEBUGFS + efx_trim_debugfs_port(efx, debug_entries); +#endif + /* Power down the LNPGA */ + reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN); + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_XCONTROL_REG, reg); + + /* Waiting here ensures that the board fini, which can turn off the + * power to the PHY, won't get run until the LNPGA powerdown has been + * given long enough to complete. */ + schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */ + + kfree(efx->phy_data); + efx->phy_data = NULL; +} + + +/* Set the RX and TX LEDs and Link LED flashing. The other LEDs + * (which probably aren't wired anyway) are left in AUTO mode */ +void tenxpress_phy_blink(struct efx_nic *efx, int blink) +{ + int reg; + + if (blink) + reg = (PMA_PMD_LED_FLASH << PMA_PMD_LED_TX_LBN) | + (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN) | + (PMA_PMD_LED_FLASH << PMA_PMD_LED_LINK_LBN); + else + reg = PMA_PMD_LED_DEFAULT; + + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + PMA_PMD_LED_OVERR_REG, reg); +} + +static void tenxpress_reset_xaui(struct efx_nic *efx) +{ + int phy = efx->mii.phy_id; + int clk_ctrl, test_select, soft_rst2; + + /* Real work is done on clock_ctrl other resets are thought to be + * optional but make the reset more reliable + */ + + /* Read */ + clk_ctrl = mdio_clause45_read(efx, phy, MDIO_MMD_PCS, + PCS_CLOCK_CTRL_REG); + test_select = mdio_clause45_read(efx, phy, MDIO_MMD_PCS, + PCS_TEST_SELECT_REG); + soft_rst2 = mdio_clause45_read(efx, phy, MDIO_MMD_PCS, + PCS_SOFT_RST2_REG); + + /* Modify => put in reset */ + test_select &= ~(1 << CLK312_EN_LBN); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + PCS_TEST_SELECT_REG, test_select); + + soft_rst2 &= ~((1 << XGXS_RST_N_LBN) | (1 << SERDES_RST_N_LBN)); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + PCS_SOFT_RST2_REG, soft_rst2); + + clk_ctrl &= ~(1 << PLL312_RST_N_LBN); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + PCS_CLOCK_CTRL_REG, clk_ctrl); + udelay(10); + + /* Modify => remove reset */ + clk_ctrl |= (1 << PLL312_RST_N_LBN); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + PCS_CLOCK_CTRL_REG, clk_ctrl); + udelay(10); + + soft_rst2 |= ((1 << XGXS_RST_N_LBN) | (1 << SERDES_RST_N_LBN)); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + PCS_SOFT_RST2_REG, soft_rst2); + udelay(10); + + test_select |= (1 << CLK312_EN_LBN); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + PCS_TEST_SELECT_REG, test_select); + udelay(10); +} + + +struct efx_phy_operations falcon_tenxpress_phy_ops = { + .init = tenxpress_phy_init, + .reconfigure = tenxpress_phy_reconfigure, + .check_hw = tenxpress_phy_check_hw, + .fini = tenxpress_phy_fini, + .clear_interrupt = tenxpress_phy_clear_interrupt, + .reset_xaui = tenxpress_reset_xaui, + .mmds = TENXPRESS_REQUIRED_DEVS, + .loopbacks = TENXPRESS_LOOPBACKS, + .startup_loopback = LOOPBACK_PCS, +}; Index: head-2008-03-17/drivers/net/sfc/tx.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/tx.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,515 @@ +/**************************************************************************** + * 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 + * Maintained by Solarflare Communications + * + * 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 +#include +#include +#include +#include +#include +#include "net_driver.h" +#include "tx.h" +#include "efx.h" +#include "falcon.h" +#include "workarounds.h" + + +/* + * TX descriptor ring full threshold + * + * The tx_queue descriptor ring fill-level must fall below this value + * before we restart the netif queue + */ +#define EFX_NETDEV_TX_THRESHOLD(_tx_queue) \ + (_tx_queue->efx->type->txd_ring_mask / 2u) + + + +/* We want to be able to nest calls to netif_stop_queue(), since each + * channel can have an individual stop on the queue. + */ +void efx_stop_queue(struct efx_nic *efx) +{ + spin_lock_bh(&efx->netif_stop_lock); + EFX_TRACE(efx, "stop TX queue\n"); + + atomic_inc(&efx->netif_stop_count); + if (likely(efx->net_dev_registered)) + netif_stop_queue(efx->net_dev); + + spin_unlock_bh(&efx->netif_stop_lock); +} + +/* Wake netif's TX queue + * We want to be able to nest calls to netif_stop_queue(), since each + * channel can have an individual stop on the queue. + */ +inline void efx_wake_queue(struct efx_nic *efx) +{ + local_bh_disable(); + if (atomic_dec_and_lock(&efx->netif_stop_count, + &efx->netif_stop_lock)) { + EFX_TRACE(efx, "waking TX queue\n"); + if (likely(efx->net_dev_registered)) + netif_wake_queue(efx->net_dev); + spin_unlock(&efx->netif_stop_lock); + } + local_bh_enable(); +} + +/* + * Add a socket buffer to a TX queue + * + * This maps all fragments of a socket buffer for DMA and adds them to + * the TX queue. The queue's insert pointer will be incremented by + * the number of fragments in the socket buffer. + * + * If any DMA mapping fails, any mapped fragments will be unmapped, + * the queue's insert pointer will be restored to its original value. + * + * Returns NETDEV_TX_OK or NETDEV_TX_BUSY + * You must hold netif_tx_lock() to call this function. + */ +static inline int efx_enqueue_skb(struct efx_tx_queue *tx_queue, + const struct sk_buff *skb) +{ + struct efx_nic *efx = tx_queue->efx; + struct pci_dev *pci_dev = efx->pci_dev; + struct efx_tx_buffer *buffer; + skb_frag_t *fragment; + struct page *page; + int page_offset; + unsigned int len, unmap_len = 0, fill_level, insert_ptr, misalign; + dma_addr_t dma_addr, unmap_addr = 0; + unsigned int dma_len; + unsigned unmap_single; + int q_space, i = 0; + int rc = NETDEV_TX_OK; + + EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); + + /* Get size of the initial fragment */ + len = skb_headlen(skb); + + fill_level = tx_queue->insert_count - tx_queue->old_read_count; + q_space = efx->type->txd_ring_mask - 1 - fill_level; + + /* Map for DMA. Use pci_map_single rather than pci_map_page + * since this is more efficient on machines with sparse + * memory. + */ + unmap_single = 1; + dma_addr = pci_map_single(pci_dev, skb->data, len, PCI_DMA_TODEVICE); + + /* Process all fragments */ + while (1) { + if (unlikely(pci_dma_mapping_error(dma_addr))) + goto pci_err; + + /* Store fields for marking in the per-fragment final + * descriptor */ + unmap_len = len; + unmap_addr = dma_addr; + + /* Add to TX queue, splitting across DMA boundaries */ + do { + if (unlikely(q_space-- <= 0)) { + /* It might be that completions have + * happened since the xmit path last + * checked. Update the xmit path's + * copy of read_count. + */ + ++tx_queue->stopped; + /* This memory barrier protects the + * change of stopped from the access + * of read_count. */ + smp_mb(); + tx_queue->old_read_count = + *(volatile unsigned *) + &tx_queue->read_count; + fill_level = (tx_queue->insert_count + - tx_queue->old_read_count); + q_space = (efx->type->txd_ring_mask - 1 - + fill_level); + if (unlikely(q_space-- <= 0)) + goto stop; + smp_mb(); + --tx_queue->stopped; + } + + insert_ptr = (tx_queue->insert_count & + efx->type->txd_ring_mask); + buffer = &tx_queue->buffer[insert_ptr]; + EFX_BUG_ON_PARANOID(buffer->skb); + EFX_BUG_ON_PARANOID(buffer->len); + EFX_BUG_ON_PARANOID(buffer->continuation != 1); + EFX_BUG_ON_PARANOID(buffer->unmap_len); + + dma_len = (((~dma_addr) & efx->type->tx_dma_mask) + 1); + if (likely(dma_len > len)) + dma_len = len; + + misalign = (unsigned)dma_addr & efx->type->bug5391_mask; + if (misalign && dma_len + misalign > 512) + dma_len = 512 - misalign; + + /* Fill out per descriptor fields */ + buffer->len = dma_len; + buffer->dma_addr = dma_addr; + len -= dma_len; + dma_addr += dma_len; + ++tx_queue->insert_count; + } while (len); + + /* Transfer ownership of the unmapping to the final buffer */ + buffer->unmap_addr = unmap_addr; + buffer->unmap_single = unmap_single; + buffer->unmap_len = unmap_len; + unmap_len = 0; + + /* Get address and size of next fragment */ + if (i >= skb_shinfo(skb)->nr_frags) + break; + fragment = &skb_shinfo(skb)->frags[i]; + len = fragment->size; + page = fragment->page; + page_offset = fragment->page_offset; + i++; + /* Map for DMA */ + unmap_single = 0; + dma_addr = pci_map_page(pci_dev, page, page_offset, len, + PCI_DMA_TODEVICE); + } + + /* Transfer ownership of the skb to the final buffer */ + buffer->skb = skb; + buffer->continuation = 0; + + /* Pass off to hardware */ + falcon_push_buffers(tx_queue); + + return NETDEV_TX_OK; + + pci_err: + EFX_ERR_RL(efx, " TX queue %d could not map skb with %d bytes %d " + "fragments for DMA\n", tx_queue->queue, skb->len, + skb_shinfo(skb)->nr_frags + 1); + + /* Mark the packet as transmitted, and free the SKB ourselves */ + dev_kfree_skb_any((struct sk_buff *)skb); + goto unwind; + + stop: + rc = NETDEV_TX_BUSY; + + /* Stop the queue if it wasn't stopped before. */ + if (tx_queue->stopped == 1) + efx_stop_queue(efx); + + unwind: + /* Work backwards until we hit the original insert pointer value */ + while (tx_queue->insert_count != tx_queue->write_count) { + --tx_queue->insert_count; + insert_ptr = tx_queue->insert_count & efx->type->txd_ring_mask; + buffer = &tx_queue->buffer[insert_ptr]; + if (buffer->unmap_len) { + if (buffer->unmap_single) + pci_unmap_single(pci_dev, buffer->unmap_addr, + buffer->unmap_len, + PCI_DMA_TODEVICE); + else + pci_unmap_page(pci_dev, buffer->unmap_addr, + buffer->unmap_len, + PCI_DMA_TODEVICE); + } + buffer->unmap_len = 0; + buffer->len = 0; + } + + /* Free the fragment we were mid-way through pushing */ + if (unmap_len) + pci_unmap_page(pci_dev, unmap_addr, unmap_len, + PCI_DMA_TODEVICE); + + return rc; +} + +/* Remove packets from the TX queue + * + * This removes packets from the TX queue, up to and including the + * specified index. + */ +static inline void efx_dequeue_buffers(struct efx_tx_queue *tx_queue, + unsigned int index) +{ + struct pci_dev *pci_dev = tx_queue->efx->pci_dev; + struct efx_tx_buffer *buffer; + unsigned int stop_index, read_ptr; + + /* Calculate the stopping point. Doing the check this way + * avoids wrongly completing every buffer in the ring if we + * get called twice with the same index. (Hardware should + * never do this, since it can't complete that many buffers in + * one go.) + */ + stop_index = (index + 1) & tx_queue->efx->type->txd_ring_mask; + read_ptr = tx_queue->read_count & tx_queue->efx->type->txd_ring_mask; + + while (read_ptr != stop_index) { + buffer = &tx_queue->buffer[read_ptr]; + if (unlikely(buffer->len == 0)) { + EFX_ERR(tx_queue->efx, "TX queue %d spurious TX " + "completion id %x\n", tx_queue->queue, + read_ptr); + atomic_inc(&tx_queue->efx->errors.spurious_tx); + /* Don't reset */ + } else { + if (buffer->unmap_len) { + if (buffer->unmap_single) + pci_unmap_single(pci_dev, + buffer->unmap_addr, + buffer->unmap_len, + PCI_DMA_TODEVICE); + else + pci_unmap_page(pci_dev, + buffer->unmap_addr, + buffer->unmap_len, + PCI_DMA_TODEVICE); + buffer->unmap_single = 0; + buffer->unmap_len = 0; + } + if (buffer->skb) { + dev_kfree_skb_any((struct sk_buff *) + buffer->skb); + buffer->skb = NULL; + EFX_TRACE(tx_queue->efx, "TX queue %d " + "transmission id %x complete\n", + tx_queue->queue, read_ptr); + } + buffer->continuation = 1; + buffer->len = 0; + } + ++tx_queue->read_count; + read_ptr = (tx_queue->read_count & + tx_queue->efx->type->txd_ring_mask); + } +} + +/* Initiate a packet transmission on the specified TX queue. + * Note that returning anything other than NETDEV_TX_OK will cause the + * OS to free the skb. + * + * This function is split out from efx_hard_start_xmit to allow the + * loopback test to direct packets via specific TX queues. It is + * therefore a non-static inline, so as not to penalise performance + * for non-loopback transmissions. + * + * Context: netif_tx_lock held + */ +inline int efx_xmit(struct efx_nic *efx, + struct efx_tx_queue *tx_queue, struct sk_buff *skb) +{ + int rc; + + /* Map fragments for DMA and add to TX queue */ + rc = efx_enqueue_skb(tx_queue, skb); + if (unlikely(rc != NETDEV_TX_OK)) + goto out; + + /* Update last TX timer */ + efx->net_dev->trans_start = jiffies; + + out: + return rc; +} + +/* Initiate a packet transmission. We use one channel per CPU + * (sharing when we have more CPUs than channels). On Falcon, the TX + * completion events will be directed back to the CPU that transmitted + * the packet, which should be cache-efficient. + * + * Context: non-blocking. + * Note that returning anything other than NETDEV_TX_OK will cause the + * OS to free the skb. + */ +int efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev) +{ + struct efx_nic *efx = net_dev->priv; + struct efx_tx_queue *tx_queue; + enum efx_veto veto; + int rc = NETDEV_TX_OK; + + /* We have one TX queue. */ + tx_queue = &efx->tx_queue[0]; + + /* See if driverlink wants to veto the packet. */ + veto = EFX_DL_CALLBACK(efx, tx_packet, skb); + if (unlikely(veto)) { + EFX_LOG(efx, "TX queue %d packet vetoed by " + "driverlink %s driver\n", tx_queue->queue, + efx->dl_cb_dev.tx_packet->driver->name); + /* Free the skb; nothing else will do it */ + dev_kfree_skb_any((struct sk_buff *)skb); + goto out; + } + + rc = efx_xmit(efx, tx_queue, skb); +out: + return rc; +} + +void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) +{ + unsigned long flags __attribute__ ((unused)); + unsigned fill_level; + struct efx_nic *efx = tx_queue->efx; + + EFX_BUG_ON_PARANOID(index > efx->type->txd_ring_mask); + + /* Remove buffers from TX queue */ + efx_dequeue_buffers(tx_queue, index); + + /* See if we need to restart the netif queue. This barrier + * separates the update of read_count from the test of + * stopped. */ + smp_mb(); + if (unlikely(tx_queue->stopped)) { + fill_level = tx_queue->insert_count - tx_queue->read_count; + if (fill_level < EFX_NETDEV_TX_THRESHOLD(tx_queue)) { + /* If the port is stopped and the net_dev isn't + * registered, then the caller must be performing + * flow control manually */ + if (unlikely(!efx->net_dev_registered)) + return; + + /* Do this under netif_tx_lock(), to avoid racing + * with efx_xmit(). */ + netif_tx_lock(efx->net_dev); + if (tx_queue->stopped) { + tx_queue->stopped = 0; + efx_wake_queue(efx); + } + netif_tx_unlock(efx->net_dev); + } + } +} + +int efx_probe_tx_queue(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + unsigned int txq_size; + int i, rc; + + EFX_LOG(efx, "creating TX queue %d\n", tx_queue->queue); + + /* Allocate software ring */ + txq_size = (efx->type->txd_ring_mask + 1) * sizeof(*tx_queue->buffer); + tx_queue->buffer = kzalloc(txq_size, GFP_KERNEL); + if (!tx_queue->buffer) { + rc = -ENOMEM; + goto fail1; + } + for (i = 0; i <= efx->type->txd_ring_mask; ++i) + tx_queue->buffer[i].continuation = 1; + + /* Allocate hardware ring */ + rc = falcon_probe_tx(tx_queue); + if (rc) + goto fail2; + + return 0; + + fail2: + kfree(tx_queue->buffer); + tx_queue->buffer = NULL; + fail1: + /* Mark queue as unused */ + tx_queue->used = 0; + + return rc; +} + +int efx_init_tx_queue(struct efx_tx_queue *tx_queue) +{ + EFX_LOG(tx_queue->efx, "initialising TX queue %d\n", tx_queue->queue); + + ASSERT_RTNL(); + + /* Initialise fields */ + tx_queue->insert_count = 0; + tx_queue->write_count = 0; + tx_queue->read_count = 0; + tx_queue->old_read_count = 0; + BUG_ON(tx_queue->stopped); + + /* Set up TX descriptor ring */ + return falcon_init_tx(tx_queue); +} + +void efx_release_tx_buffers(struct efx_tx_queue *tx_queue) +{ + unsigned int last_index, mask; + if (tx_queue->buffer) { + /* Free any buffers left in the ring */ + mask = tx_queue->efx->type->txd_ring_mask; + last_index = (tx_queue->insert_count - 1) & mask; + EFX_LOG(tx_queue->efx, "Will dequeue up to 0x%x from 0x%x\n", + last_index, tx_queue->read_count & mask); + efx_dequeue_buffers(tx_queue, last_index); + } +} + +void efx_fini_tx_queue(struct efx_tx_queue *tx_queue) +{ + EFX_LOG(tx_queue->efx, "shutting down TX queue %d\n", tx_queue->queue); + + ASSERT_RTNL(); + + /* Flush TX queue, remove descriptor ring */ + falcon_fini_tx(tx_queue); + + /* Release TX buffers */ + efx_release_tx_buffers(tx_queue); + + /* Release queue's stop on port, if any */ + if (tx_queue->stopped) { + tx_queue->stopped = 0; + efx_wake_queue(tx_queue->efx); + } +} + +void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) +{ + EFX_LOG(tx_queue->efx, "destroying TX queue %d\n", tx_queue->queue); + falcon_remove_tx(tx_queue); + + kfree(tx_queue->buffer); + tx_queue->buffer = NULL; + tx_queue->used = 0; +} + + Index: head-2008-03-17/drivers/net/sfc/tx.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/tx.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,41 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2006: Fen Systems Ltd. + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Initially developed by Michael Brown + * Maintained by Solarflare Communications + * + * 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_TX_H +#define EFX_TX_H + +#include "net_driver.h" + +int efx_probe_tx_queue(struct efx_tx_queue *tx_queue); +void efx_remove_tx_queue(struct efx_tx_queue *tx_queue); +int efx_init_tx_queue(struct efx_tx_queue *tx_queue); +void efx_fini_tx_queue(struct efx_tx_queue *tx_queue); + +int efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); +void efx_release_tx_buffers(struct efx_tx_queue *tx_queue); + +#endif /* EFX_TX_H */ Index: head-2008-03-17/drivers/net/sfc/txc43128_phy.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/txc43128_phy.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,713 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed by Solarflare Communications + * + * 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 + **************************************************************************** + */ +/* + * Driver for Transwitch/Mysticom CX4 retimer + * see www.transwitch.com, part is TXC-43128 + */ + +#include +#include +#include "efx.h" +#include "debugfs.h" +#include "gmii.h" +#include "mdio_10g.h" +#include "xenpack.h" +#include "phy.h" +#include "lm87_support.h" +#include "falcon.h" +#include "workarounds.h" + +/* We expect these MMDs to be in the package */ +#define TXC_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PCS | \ + MDIO_MMDREG_DEVS0_PMAPMD | \ + MDIO_MMDREG_DEVS0_PHYXS) + +#define TXC_LOOPBACKS ((1 << LOOPBACK_PCS) | \ + (1 << LOOPBACK_PMAPMD) | \ + (1 << LOOPBACK_NETWORK)) + +/************************************************************************** + * + * Compile-time config + * + ************************************************************************** + */ +#define TXCNAME "TXC43128" +/* Total length of time we'll wait for the PHY to come out of reset */ +#define TXC_MAX_RESET_TIME 500 +/* Interval between checks */ +#define TXC_RESET_WAIT 10 +/* How long to run BIST: At 10Gbps 50 microseconds should be plenty to get + * some stats */ +#define TXC_BIST_DURATION (50) + +#define BER_INTERVAL (10 * efx_monitor_interval) + +/************************************************************************** + * + * Register definitions + * + ************************************************************************** + */ +#define XAUI_NUM_LANES (4) + +/*** Global register bank */ +/* Silicon ID register */ +#define TXC_GLRGS_SLID (0xc000) +#define TXC_GLRGS_SLID_MASK (0x1f) + +/* Command register */ +#define TXC_GLRGS_GLCMD (0xc004) +/* Useful bits in command register */ +/* Lane power-down */ +#define TXC_GLCMD_L01PD_LBN (5) +#define TXC_GLCMD_L23PD_LBN (6) +/* Limited SW reset: preserves configuration but + * initiates a logic reset. Self-clearing */ +#define TXC_GLCMD_LMTSWRST_LBN (14) + +/* Signal Quality Control */ +#define TXC_GLRGS_GSGQLCTL (0xc01a) +/* Enable bit */ +#define TXC_GSGQLCT_SGQLEN_LBN (15) +/* Lane selection */ +#define TXC_GSGQLCT_LNSL_LBN (13) +#define TXC_GSGQLCT_LNSL_WIDTH (2) + +/* Signal Quality Input */ +#define TXC_GLRGS_GSGQLIN (0xc01b) +/* Signal Quality Grade */ +#define TXC_GLRGS_GSGQLGRD (0xc01c) +/* Drift sign */ +#define TXC_GSGQLGRD_DRFTSGN_LBN (15) +/* Grade valid flag */ +#define TXC_GSGQLGRD_GRDVAL_LBN (14) +/* Remaining bits are the actual grade */ +#define TXC_GSGQLGRD_GRADE_LBN (0) +#define TXC_GSGQLGRD_GRADE_WIDTH (14) + +/* Signal Quality Drift: 16-bit drift value */ +#define TXC_GLRGS_GSGQLDRFT (0xc01d) + +/**** Analog register bank */ +#define TXC_ALRGS_ATXCTL (0xc040) +/* Lane power-down */ +#define TXC_ATXCTL_TXPD3_LBN (15) +#define TXC_ATXCTL_TXPD2_LBN (14) +#define TXC_ATXCTL_TXPD1_LBN (13) +#define TXC_ATXCTL_TXPD0_LBN (12) + +/* Amplitude on lanes 0, 1 */ +#define TXC_ALRGS_ATXAMP0 (0xc041) +/* Amplitude on lanes 2, 3 */ +#define TXC_ALRGS_ATXAMP1 (0xc042) +/* Bit position of value for lane 0 (or 2) */ +#define TXC_ATXAMP_LANE02_LBN (3) +/* Bit position of value for lane 1 (or 3) */ +#define TXC_ATXAMP_LANE13_LBN (11) + +#define TXC_ATXAMP_1280_mV (0) +#define TXC_ATXAMP_1200_mV (8) +#define TXC_ATXAMP_1120_mV (12) +#define TXC_ATXAMP_1060_mV (14) +#define TXC_ATXAMP_0820_mV (25) +#define TXC_ATXAMP_0720_mV (26) +#define TXC_ATXAMP_0580_mV (27) +#define TXC_ATXAMP_0440_mV (28) + +#define TXC_ATXAMP_0820_BOTH \ + ((TXC_ATXAMP_0820_mV << TXC_ATXAMP_LANE02_LBN) \ + | (TXC_ATXAMP_0820_mV << TXC_ATXAMP_LANE13_LBN)) + +#define TXC_ATXAMP_DEFAULT (0x6060) /* From databook */ + +/* Preemphasis on lanes 0, 1 */ +#define TXC_ALRGS_ATXPRE0 (0xc043) +/* Preemphasis on lanes 2, 3 */ +#define TXC_ALRGS_ATXPRE1 (0xc044) + +#define TXC_ATXPRE_NONE (0) +#define TXC_ATXPRE_DEFAULT (0x1010) /* From databook */ + +#define TXC_ALRGS_ARXCTL (0xc045) +/* Lane power-down */ +#define TXC_ARXCTL_RXPD3_LBN (15) +#define TXC_ARXCTL_RXPD2_LBN (14) +#define TXC_ARXCTL_RXPD1_LBN (13) +#define TXC_ARXCTL_RXPD0_LBN (12) + +/*** receiver control registers: Bit Error Rate measurement */ +/* Per lane BER timers */ +#define TXC_RXCTL_BERTMR0 (0xc0d4) +#define TXC_RXCTL_BERTMR1 (0xc154) +#define TXC_RXCTL_BERTMR2 (0xc1d4) +#define TXC_RXCTL_BERTMR3 (0xc254) +/* Per lane BER counters */ +#define TXC_RXCTL_BERCNT0 (0xc0d5) +#define TXC_RXCTL_BERCNT1 (0xc155) +#define TXC_RXCTL_BERCNT2 (0xc1d5) +#define TXC_RXCTL_BERCNT3 (0xc255) + +#define BER_REG_SPACING (TXC_RXCTL_BERTMR1 - TXC_RXCTL_BERTMR0) + +/*** Main user-defined register set */ +/* Main control */ +#define TXC_MRGS_CTL (0xc340) +/* Bits in main control */ +#define TXC_MCTL_RESET_LBN (15) /* Self clear */ +#define TXC_MCTL_TXLED_LBN (14) /* 1 to show align status */ +#define TXC_MCTL_RXLED_LBN (13) /* 1 to show align status */ + +/* GPIO output */ +#define TXC_GPIO_OUTPUT (0xc346) +#define TXC_GPIO_DIR (0xc348) + +/*** Vendor-specific BIST registers */ +#define TXC_BIST_CTL (0xc280) +#define TXC_BIST_TXFRMCNT (0xc281) +#define TXC_BIST_RX0FRMCNT (0xc282) +#define TXC_BIST_RX1FRMCNT (0xc283) +#define TXC_BIST_RX2FRMCNT (0xc284) +#define TXC_BIST_RX3FRMCNT (0xc285) +#define TXC_BIST_RX0ERRCNT (0xc286) +#define TXC_BIST_RX1ERRCNT (0xc287) +#define TXC_BIST_RX2ERRCNT (0xc288) +#define TXC_BIST_RX3ERRCNT (0xc289) + +/*** BIST control bits */ +/* BIST type (controls bit patter in test) */ +#define TXC_BIST_CTRL_TYPE_LBN (10) +#define TXC_BIST_CTRL_TYPE_TSD (0) /* TranSwitch Deterministic */ +#define TXC_BIST_CTRL_TYPE_CRP (1) /* CRPAT standard */ +#define TXC_BIST_CTRL_TYPE_CJP (2) /* CJPAT standard */ +#define TXC_BIST_CTRL_TYPE_TSR (3) /* TranSwitch pseudo-random */ +/* Set this to 1 for 10 bit and 0 for 8 bit */ +#define TXC_BIST_CTRL_B10EN_LBN (12) +/* Enable BIST (write 0 to disable) */ +#define TXC_BIST_CTRL_ENAB_LBN (13) +/*Stop BIST (self-clears when stop complete) */ +#define TXC_BIST_CTRL_STOP_LBN (14) +/* Start BIST (cleared by writing 1 to STOP) */ +#define TXC_BIST_CTRL_STRT_LBN (15) + +/* Mt. Diablo test configuration */ +#define TXC_MTDIABLO_CTRL (0xc34f) +#define TXC_MTDIABLO_CTRL_PMA_LOOP_LBN (10) + +struct txc43128_data { +#ifdef CONFIG_SFC_DEBUGFS + /* BER stats update from check_hw. Note that this is in errors/second, + * converting it to errors/bit is left as an exercise for user-space. + */ + unsigned phy_ber_pcs[4]; + unsigned phy_ber_phyxs[4]; +#endif + /* Debounce resets by storing state locally */ + int phy_powered; + int tx_disabled; + enum efx_loopback_mode loopback_mode; +}; + + +/* Perform a reset that doesn't clear configuration changes */ +static void txc_reset_logic(struct efx_nic *efx); + +/* Set the output value of a gpio */ +void txc_set_gpio_val(struct efx_nic *efx, int pin, int on) +{ + int outputs; + + outputs = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PHYXS, TXC_GPIO_OUTPUT); + + outputs = (outputs & ~(1 << pin)) | (on << pin); + + mdio_clause45_write(efx, efx->mii.phy_id, + MDIO_MMD_PHYXS, TXC_GPIO_OUTPUT, + outputs); +} + +/* Set up the GPIO direction register */ +void txc_set_gpio_dir(struct efx_nic *efx, int pin, int dir) +{ + int dirs; + + if (efx->board_info.minor < 3 && + efx->board_info.major == 0) + return; + + dirs = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PHYXS, TXC_GPIO_DIR); + dirs = (dir & ~(1 << pin)) | (dir << pin); + mdio_clause45_write(efx, efx->mii.phy_id, + MDIO_MMD_PHYXS, TXC_GPIO_DIR, dirs); + +} + +/* Reset the PMA/PMD MMD. The documentation is explicit that this does a + * global reset (it's less clear what reset of other MMDs does).*/ +static int txc_reset_phy(struct efx_nic *efx) +{ + int rc = mdio_clause45_reset_mmd(efx, MDIO_MMD_PMAPMD, + TXC_MAX_RESET_TIME / TXC_RESET_WAIT, + TXC_RESET_WAIT); + if (rc < 0) + goto fail; + + /* Check that all the MMDs we expect are present and responding. We + * expect faults on some if the link is down, but not on the PHY XS */ + rc = mdio_clause45_check_mmds(efx, TXC_REQUIRED_DEVS, 0); + if (rc < 0) + goto fail; + + return 0; + + fail: + EFX_ERR(efx, TXCNAME ": reset timed out!\n"); + return rc; +} + +/* Run a single BIST on one MMD*/ +static int txc_bist_one(struct efx_nic *efx, int mmd, int test) +{ + int phy = efx->mii.phy_id; + int ctrl, bctl; + int lane; + int rc = 0; + + EFX_INFO(efx, "" TXCNAME ": running BIST on %s MMD\n", + mdio_clause45_mmd_name(mmd)); + + /* Set PMA to test into loopback using Mt Diablo reg as per app note */ + ctrl = mdio_clause45_read(efx, phy, MDIO_MMD_PCS, + TXC_MTDIABLO_CTRL); + ctrl |= (1 << TXC_MTDIABLO_CTRL_PMA_LOOP_LBN); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + TXC_MTDIABLO_CTRL, ctrl); + + + /* The BIST app. note lists these as 3 distinct steps. */ + /* Set the BIST type */ + bctl = (test << TXC_BIST_CTRL_TYPE_LBN); + mdio_clause45_write(efx, phy, mmd, TXC_BIST_CTL, bctl); + + /* Set the BSTEN bit in the BIST Control register to enable */ + bctl |= (1 << TXC_BIST_CTRL_ENAB_LBN); + mdio_clause45_write(efx, phy, mmd, TXC_BIST_CTL, bctl); + + /* Set the BSTRT bit in the BIST Control register */ + mdio_clause45_write(efx, phy, mmd, TXC_BIST_CTL, bctl | + (1 << TXC_BIST_CTRL_STRT_LBN)); + + /* Wait. */ + udelay(TXC_BIST_DURATION); + + /* Set the BSTOP bit in the BIST Control register */ + bctl |= (1 << TXC_BIST_CTRL_STOP_LBN); + mdio_clause45_write(efx, phy, mmd, TXC_BIST_CTL, bctl); + + /* The STOP bit should go off when things have stopped */ + while (bctl & (1 << TXC_BIST_CTRL_STOP_LBN)) + bctl = mdio_clause45_read(efx, phy, mmd, TXC_BIST_CTL); + + /* Check all the error counts are 0 and all the frame counts are + non-zero */ + for (lane = 0; lane < 4; lane++) { + int count = mdio_clause45_read(efx, phy, mmd, + TXC_BIST_RX0ERRCNT + lane); + if (count != 0) { + EFX_ERR(efx, ""TXCNAME": BIST error. " + "Lane %d had %d errs\n", lane, count); + rc = -EIO; + } + count = mdio_clause45_read(efx, phy, mmd, + TXC_BIST_RX0FRMCNT + lane); + if (count == 0) { + EFX_ERR(efx, ""TXCNAME": BIST error. " + "Lane %d got 0 frames\n", lane); + rc = -EIO; + } + } + + if (rc == 0) + EFX_INFO(efx, ""TXCNAME": BIST pass\n"); + + /* Disable BIST */ + mdio_clause45_write(efx, phy, mmd, TXC_BIST_CTL, 0); + + /* Turn off loopback */ + ctrl &= ~(1 << TXC_MTDIABLO_CTRL_PMA_LOOP_LBN); + mdio_clause45_write(efx, phy, MDIO_MMD_PCS, + TXC_MTDIABLO_CTRL, ctrl); + + return rc; +} + +/* Run all the desired BIST tests for the PHY */ +static int txc_bist(struct efx_nic *efx) +{ + int rc; + /*!\todo: experiment with running more of the BIST patterns to + * see if it actually shows up more problems. */ + rc = txc_bist_one(efx, MDIO_MMD_PCS, TXC_BIST_CTRL_TYPE_TSD); + return rc; +} + +#ifdef CONFIG_SFC_DEBUGFS + +/* debugfs entries for this PHY */ +static struct efx_debugfs_parameter debug_entries[] = { + EFX_PER_LANE_PARAMETER("phy_ber_lane", "_pcs", + struct txc43128_data, phy_ber_pcs, + unsigned, efx_debugfs_read_uint), + EFX_PER_LANE_PARAMETER("phy_ber_lane", "_phyxs", + struct txc43128_data, phy_ber_phyxs, + unsigned, efx_debugfs_read_uint), + EFX_INT_PARAMETER(struct txc43128_data, phy_powered), + {NULL} +}; + +#endif /* CONFIG_SFC_DEBUGFS */ + +/* Push the non-configurable defaults into the PHY. This must be + * done after every full reset */ +static void txc_apply_defaults(struct efx_nic *efx) +{ + int mctrl; + + /* Turn amplitude down and preemphasis off on the host side + * (PHY<->MAC) as this is believed less likely to upset Falcon + * and no adverse effects have been noted. It probably also + * saves a picowatt or two */ + + /* Turn off preemphasis */ + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PHYXS, + TXC_ALRGS_ATXPRE0, TXC_ATXPRE_NONE); + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PHYXS, + TXC_ALRGS_ATXPRE1, TXC_ATXPRE_NONE); + + /* Turn down the amplitude */ + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PHYXS, + TXC_ALRGS_ATXAMP0, TXC_ATXAMP_0820_BOTH); + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PHYXS, + TXC_ALRGS_ATXAMP1, TXC_ATXAMP_0820_BOTH); + + /* Set the line side amplitude and preemphasis to the databook + * defaults as an erratum causes them to be 0 on at least some + * PHY rev.s */ + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + TXC_ALRGS_ATXPRE0, TXC_ATXPRE_DEFAULT); + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + TXC_ALRGS_ATXPRE1, TXC_ATXPRE_DEFAULT); + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + TXC_ALRGS_ATXAMP0, TXC_ATXAMP_DEFAULT); + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + TXC_ALRGS_ATXAMP1, TXC_ATXAMP_DEFAULT); + + /* Set up the LEDs */ + mctrl = mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PHYXS, TXC_MRGS_CTL); + + /* Set the Green and Red LEDs to their default modes */ + mctrl &= ~((1 << TXC_MCTL_TXLED_LBN) | (1 << TXC_MCTL_RXLED_LBN)); + mdio_clause45_write(efx, efx->mii.phy_id, + MDIO_MMD_PHYXS, TXC_MRGS_CTL, mctrl); + + /* Databook recommends doing this after configuration changes */ + txc_reset_logic(efx); + + efx->board_info.init_leds(efx); +} + +/* Initialisation entry point for this PHY driver */ +static int txc43128_phy_init(struct efx_nic *efx) +{ + u32 devid; + int rc = 0; + struct txc43128_data *phy_data; + + devid = mdio_clause45_read_id(efx, MDIO_MMD_PHYXS); + + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); + efx->phy_data = phy_data; + + /* This is the default after reset */ + phy_data->phy_powered = efx->phy_powered; + phy_data->tx_disabled = efx->tx_disabled; + +#ifdef CONFIG_SFC_DEBUGFS + rc = efx_extend_debugfs_port(efx, phy_data, debug_entries); + if (rc < 0) + goto fail1; +#endif + EFX_INFO(efx, ""TXCNAME ": PHY ID reg %x (OUI %x model %x " + "revision %x)\n", devid, MDIO_ID_OUI(devid), + MDIO_ID_MODEL(devid), MDIO_ID_REV(devid)); + + EFX_INFO(efx, ""TXCNAME ": Silicon ID %x\n", + mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PHYXS, TXC_GLRGS_SLID) & + TXC_GLRGS_SLID_MASK); + + rc = txc_reset_phy(efx); + if (rc < 0) + goto fail2; + + rc = txc_bist(efx); + if (rc < 0) + goto fail2; + + txc_apply_defaults(efx); + + return 0; + + fail2: +#ifdef CONFIG_SFC_DEBUGFS + efx_trim_debugfs_port(efx, debug_entries); + /* fall-thru */ + fail1: +#endif + kfree(efx->phy_data); + efx->phy_data = NULL; + return rc; +} + +/* Set the lane power down state in the global registers */ +static void txc_glrgs_lane_power(struct efx_nic *efx, int mmd) +{ + int pd = (1 << TXC_GLCMD_L01PD_LBN) | (1 << TXC_GLCMD_L23PD_LBN); + int ctl = mdio_clause45_read(efx, efx->mii.phy_id, + mmd, TXC_GLRGS_GLCMD); + + if (efx->phy_powered) + ctl &= ~pd; + else + ctl |= pd; + + mdio_clause45_write(efx, efx->mii.phy_id, + mmd, TXC_GLRGS_GLCMD, ctl); +} + +/* Set the lane power down state in the analog control registers */ +static void txc_analog_lane_power(struct efx_nic *efx, int mmd) +{ + int txpd = (1 << TXC_ATXCTL_TXPD3_LBN) | (1 << TXC_ATXCTL_TXPD2_LBN) + | (1 << TXC_ATXCTL_TXPD1_LBN) | (1 << TXC_ATXCTL_TXPD0_LBN); + + int rxpd = (1 << TXC_ATXCTL_TXPD3_LBN) | (1 << TXC_ATXCTL_TXPD2_LBN) + | (1 << TXC_ATXCTL_TXPD1_LBN) | (1 << TXC_ATXCTL_TXPD0_LBN); + + int txctl = mdio_clause45_read(efx, efx->mii.phy_id, + mmd, TXC_ALRGS_ATXCTL); + int rxctl = mdio_clause45_read(efx, efx->mii.phy_id, + mmd, TXC_ALRGS_ARXCTL); + + if (efx->phy_powered) { + txctl &= ~txpd; + rxctl &= ~rxpd; + } else { + txctl |= txpd; + rxctl |= rxpd; + } + + mdio_clause45_write(efx, efx->mii.phy_id, + mmd, TXC_ALRGS_ATXCTL, txctl); + mdio_clause45_write(efx, efx->mii.phy_id, + mmd, TXC_ALRGS_ARXCTL, rxctl); +} + +static void txc_set_power(struct efx_nic *efx) +{ + /* According to the data book, all the MMDs can do low power */ + mdio_clause45_set_mmds_lpower(efx, !efx->phy_powered, + TXC_REQUIRED_DEVS); + + /* Global register bank is in PCS, PHY XS. These control the host + * side and line side settings respectively. */ + txc_glrgs_lane_power(efx, MDIO_MMD_PCS); + txc_glrgs_lane_power(efx, MDIO_MMD_PHYXS); + + /* Analog register bank in PMA/PMD, PHY XS */ + txc_analog_lane_power(efx, MDIO_MMD_PMAPMD); + txc_analog_lane_power(efx, MDIO_MMD_PHYXS); +} + + +static void txc_reset_logic_mmd(struct efx_nic *efx, int mmd) +{ + int portid = efx->mii.phy_id; + int val = mdio_clause45_read(efx, portid, mmd, TXC_GLRGS_GLCMD); + int tries = 50; + val |= (1 << TXC_GLCMD_LMTSWRST_LBN); + mdio_clause45_write(efx, portid, mmd, TXC_GLRGS_GLCMD, val); + while (tries--) { + val = mdio_clause45_read(efx, portid, mmd, + TXC_GLRGS_GLCMD); + if (!(val & (1 << TXC_GLCMD_LMTSWRST_LBN))) + break; + udelay(1); + } + if (!tries) + EFX_INFO(efx, TXCNAME " Logic reset timed out!\n"); +} + + +/* Perform a logic reset. This preserves the configuration registers + * and is needed for some configuration changes to take effect */ +static void txc_reset_logic(struct efx_nic *efx) +{ + /* The data sheet claims we can do the logic reset on either the + * PCS or the PHYXS and the result is a reset of both host- and + * line-side logic. */ + txc_reset_logic_mmd(efx, MDIO_MMD_PCS); +} + +static int txc43128_phy_read_link(struct efx_nic *efx) +{ + return mdio_clause45_links_ok(efx, TXC_REQUIRED_DEVS); +} + +static void txc43128_phy_reconfigure(struct efx_nic *efx) +{ + struct txc43128_data *phy_data = efx->phy_data; + int power_change = (efx->phy_powered != phy_data->phy_powered); + int loop_change = LOOPBACK_CHANGED(phy_data, efx, TXC_LOOPBACKS); + int disable_change = (efx->tx_disabled != phy_data->tx_disabled); + + if (!phy_data->tx_disabled && efx->tx_disabled) { + txc_reset_phy(efx); + txc_apply_defaults(efx); + falcon_reset_xaui(efx); + } + + mdio_clause45_transmit_disable(efx, efx->tx_disabled); + mdio_clause45_phy_reconfigure(efx); + if (power_change) + txc_set_power(efx); + + /* The data sheet claims this is required after every reconfiguration + * (note at end of 7.1), but we mustn't do it when nothing changes as + * it glitches the link, and reconfigure gets called on link change, + * so we get an IRQ storm on link up. */ + if (loop_change || power_change || disable_change) + txc_reset_logic(efx); + + phy_data->phy_powered = efx->phy_powered; + phy_data->loopback_mode = efx->loopback_mode; + phy_data->tx_disabled = efx->tx_disabled; + efx->link_up = txc43128_phy_read_link(efx); + efx->link_options = GM_LPA_10000FULL; +} + +static void txc43128_phy_fini(struct efx_nic *efx) +{ + efx->board_info.blink(efx, 0); + + /* Disable link events */ + xenpack_disable_lasi_irqs(efx); + +#ifdef CONFIG_SFC_DEBUGFS + /* Remove the extra debug entries and free data */ + efx_trim_debugfs_port(efx, debug_entries); +#endif + kfree(efx->phy_data); + efx->phy_data = NULL; +} + +/* Periodic callback: this exists mainly to poll link status as we currently + * don't use LASI interrupts. Also update the BER counters and poll the lm87 */ +static int txc43128_phy_check_hw(struct efx_nic *efx) +{ +#ifdef CONFIG_SFC_DEBUGFS + struct txc43128_data *data = efx->phy_data; + int phy = efx->mii.phy_id; + int timer, count, i, mmd; +#endif + int rc = 0; + int link_up = txc43128_phy_read_link(efx); + + /* Simulate a PHY event if link state has changed */ + if (link_up != efx->link_up) { + efx->link_up = link_up; + efx->mac_op->fake_phy_event(efx); + } else if (EFX_WORKAROUND_10934(efx) && + (efx->loopback_mode == LOOPBACK_NONE) && !link_up) + txc_reset_logic(efx); + + rc = efx->board_info.monitor(efx); + if (rc) { + EFX_ERR(efx, "" TXCNAME + ": sensor alert! Putting PHY into low power.\n"); + efx->phy_powered = 0; + txc_set_power(efx); + } + +#ifdef CONFIG_SFC_DEBUGFS + /* There are 2 MMDs with RX BER counters: PCS and PHY XS, + * which happen to be consecutively numbered */ + for (mmd = MDIO_MMD_PCS; mmd <= MDIO_MMD_PHYXS; mmd++) { + for (i = 0; i < XAUI_NUM_LANES; i++) { + timer = mdio_clause45_read(efx, phy, mmd, + TXC_RXCTL_BERTMR0 + + i * BER_REG_SPACING); + count = mdio_clause45_read(efx, phy, mmd, + TXC_RXCTL_BERCNT0 + + i * BER_REG_SPACING); + /* The BER timer counts down in seconds. If it would + * expire before the next check_hw, update the stats & + * restart the timer (clears the count) */ + if (timer * HZ < efx_monitor_interval) { + /* Record count, allowing for the fact that the + * timer may not have reached zero */ + unsigned ber = (count * BER_INTERVAL) / + (BER_INTERVAL - timer * HZ); + if (mmd == MDIO_MMD_PCS) + data->phy_ber_pcs[i] = ber; + else + data->phy_ber_phyxs[i] = ber; + /* Reprogram the timer */ + mdio_clause45_write(efx, phy, mmd, + TXC_RXCTL_BERTMR0 + + i * BER_REG_SPACING, + BER_INTERVAL / HZ); + } + } + mmd = (mmd == MDIO_MMD_PCS) ? MDIO_MMD_PHYXS : 0; + } +#endif /* CONFIG_SFC_DEBUGFS */ + return rc; +} + +struct efx_phy_operations falcon_txc_phy_ops = { + .init = txc43128_phy_init, + .reconfigure = txc43128_phy_reconfigure, + .check_hw = txc43128_phy_check_hw, + .fini = txc43128_phy_fini, + .clear_interrupt = efx_port_dummy_op_void, + .reset_xaui = efx_port_dummy_op_void, + .mmds = TXC_REQUIRED_DEVS, + .loopbacks = TXC_LOOPBACKS, + .startup_loopback = LOOPBACK_PMAPMD, +}; Index: head-2008-03-17/drivers/net/sfc/workarounds.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/workarounds.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,92 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed by Solarflare Communications + * + * 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_WORKAROUNDS_H +#define EFX_WORKAROUNDS_H + +/* + * Hardware workarounds. + * Bug numbers are from Solarflare's Bugzilla. + */ + +#define EFX_WORKAROUND_ALWAYS(efx) 1 +#define EFX_WORKAROUND_FALCON_A(efx) (FALCON_REV(efx) <= FALCON_REV_A1) +#define EFX_WORKAROUND_FALCON_B0FPGA(efx) \ + (FALCON_REV(efx) == FALCON_REV_B0 && !(efx)->is_asic) + +/* XAUI resets if link not detected */ +#define EFX_WORKAROUND_5147 EFX_WORKAROUND_ALWAYS +/* SNAP frames have TOBE_DISC set */ +#define EFX_WORKAROUND_5475 EFX_WORKAROUND_ALWAYS +/* PHY interrupts can go to the wrong port */ +#define EFX_WORKAROUND_6263 EFX_WORKAROUND_ALWAYS +/* Reprog PCIe ACK timer to workaround issue in PCIe IP block */ +#define EFX_WORKAROUND_6943 EFX_WORKAROUND_ALWAYS +/* RX PCIe double split performance issue */ +#define EFX_WORKAROUND_7575 EFX_WORKAROUND_ALWAYS +/* Bit-bashed I2C reads cause performance drop */ +#define EFX_WORKAROUND_7884 EFX_WORKAROUND_ALWAYS +/* Selftests need to be retried */ +#define EFX_WORKAROUND_8909 EFX_WORKAROUND_ALWAYS +/* Queued ACKs aren't flushed before L1 entry */ +#define EFX_WORKAROUND_9096 EFX_WORKAROUND_ALWAYS +/* TX pkt parser problem with <= 16 byte TXes */ +#define EFX_WORKAROUND_9141 EFX_WORKAROUND_ALWAYS +/* XGXS and XAUI reset sequencing in SW */ +#define EFX_WORKAROUND_9388 EFX_WORKAROUND_ALWAYS +/* Low rate CRC errors require XAUI reset */ +#define EFX_WORKAROUND_10750 EFX_WORKAROUND_ALWAYS +/* CX4 retimer fails to bring link up after reset */ +#define EFX_WORKAROUND_10934 EFX_WORKAROUND_ALWAYS +/* Transmit flow control may get disabled */ +#define EFX_WORKAROUND_11482 EFX_WORKAROUND_ALWAYS + +/* Spurious parity errors in TSORT buffers */ +#define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A +/* No unaligned TX over 512 byte boundaries */ +#define EFX_WORKAROUND_5391 EFX_WORKAROUND_FALCON_A +/* iSCSI parsing errors */ +#define EFX_WORKAROUND_5583 EFX_WORKAROUND_FALCON_A +/* RX events go missing */ +#define EFX_WORKAROUND_5676 EFX_WORKAROUND_FALCON_A +/* RX_RESET on A1 */ +#define EFX_WORKAROUND_6555 EFX_WORKAROUND_FALCON_A +/* Spurious duplicate RX events */ +#define EFX_WORKAROUND_7062 EFX_WORKAROUND_FALCON_A +/* Increase filter depth to avoid RX_RESET */ +#define EFX_WORKAROUND_7244 EFX_WORKAROUND_FALCON_A +/* TX flush may never complete */ +#define EFX_WORKAROUND_7803 EFX_WORKAROUND_FALCON_A +/* Leak overlength packets rather than free */ +#define EFX_WORKAROUND_8071 EFX_WORKAROUND_FALCON_A + +/* Memory needs clearing at start-of-day */ +#define EFX_WORKAROUND_8202 EFX_WORKAROUND_FALCON_B0FPGA +/* MAC statistics are transient */ +#define EFX_WORKAROUND_8419 EFX_WORKAROUND_FALCON_B0FPGA +/* Prefetch watchdog timer may trigger erroneously on busy systems */ +#define EFX_WORKAROUND_9008 EFX_WORKAROUND_FALCON_B0FPGA + +#endif /* EFX_WORKAROUNDS_H */ Index: head-2008-03-17/drivers/net/sfc/xenpack.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/xenpack.h 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,80 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2006: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed by Solarflare Communications + * + * 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_XENPACK_H +#define EFX_XENPACK_H + +/* Exported functions from Xenpack standard PHY control */ + +#include "mdio_10g.h" + +/****************************************************************************/ +/* XENPACK MDIO register extensions */ +#define MDIO_XP_LASI_RX_CTRL (0x9000) +#define MDIO_XP_LASI_TX_CTRL (0x9001) +#define MDIO_XP_LASI_CTRL (0x9002) +#define MDIO_XP_LASI_RX_STAT (0x9003) +#define MDIO_XP_LASI_TX_STAT (0x9004) +#define MDIO_XP_LASI_STAT (0x9005) + +/* Control/Status bits */ +#define XP_LASI_LS_ALARM (1 << 0) +#define XP_LASI_TX_ALARM (1 << 1) +#define XP_LASI_RX_ALARM (1 << 2) +/* These two are Quake vendor extensions to the standard XENPACK defines */ +#define XP_LASI_LS_INTB (1 << 3) +#define XP_LASI_TEST (1 << 7) + +/* Enable LASI interrupts for PHY */ +static inline void xenpack_enable_lasi_irqs(struct efx_nic *efx) +{ + int reg; + int phy_id = efx->mii.phy_id; + /* Read to clear LASI status register */ + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_XP_LASI_STAT); + + /* Enable LASI interrupts from PMA/PMD */ + mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_XP_LASI_CTRL, XP_LASI_LS_ALARM); +} + +/* Read the LASI interrupt status to clear the interrupt. */ +static inline int xenpack_clear_lasi_irqs(struct efx_nic *efx) +{ + /* Read to clear link status alarm */ + return mdio_clause45_read(efx, efx->mii.phy_id, + MDIO_MMD_PMAPMD, MDIO_XP_LASI_STAT); +} + +/* Turn off LASI interrupts */ +static inline void xenpack_disable_lasi_irqs(struct efx_nic *efx) +{ + /* Turn LASI interrupts off */ + mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, + MDIO_XP_LASI_CTRL, 0); +} + +#endif /* EFX_XENPACK_H */ Index: head-2008-03-17/drivers/net/sfc/xfp_phy.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-03-17/drivers/net/sfc/xfp_phy.c 2008-03-17 14:26:32.000000000 +0100 @@ -0,0 +1,206 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed by Solarflare Communications + * + * 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 + **************************************************************************** + */ +/* + * Driver for XFP optical PHYs (plus some support specific to the Quake 2032) + * See www.amcc.com for details (search for qt2032) + */ + +#include +#include +#include "efx.h" +#include "gmii.h" +#include "mdio_10g.h" +#include "xenpack.h" +#include "phy.h" + +#define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PCS | \ + MDIO_MMDREG_DEVS0_PMAPMD | \ + MDIO_MMDREG_DEVS0_PHYXS) + +#define XFP_LOOPBACKS ((1 << LOOPBACK_PCS) | \ + (1 << LOOPBACK_PMAPMD) | \ + (1 << LOOPBACK_NETWORK)) + +/****************************************************************************/ +/* Quake-specific MDIO registers */ +#define MDIO_QUAKE_LED0_REG (0xD006) + + +void xfp_set_led(struct efx_nic *p, int led, int mode) +{ + int addr = MDIO_QUAKE_LED0_REG + led; + mdio_clause45_write(p, p->mii.phy_id, MDIO_MMD_PMAPMD, addr, + mode); +} + +struct xfp_phy_data { + int phy_powered; + int tx_disabled; +}; + + +#define XFP_MAX_RESET_TIME 500 +#define XFP_RESET_WAIT 10 + +/* Reset the PHYXS MMD. This is documented (for the Quake PHY) as doing + * a complete soft reset. + */ +static int xfp_reset_phy(struct efx_nic *efx) +{ + int rc; + + rc = mdio_clause45_reset_mmd(efx, MDIO_MMD_PHYXS, + XFP_MAX_RESET_TIME / XFP_RESET_WAIT, + XFP_RESET_WAIT); + if (rc < 0) + goto fail; + + /* Wait 250ms for the PHY to complete bootup */ + msleep(250); + + /* Check that all the MMDs we expect are present and responding. We + * expect faults on some if the link is down, but not on the PHY XS */ + rc = mdio_clause45_check_mmds(efx, XFP_REQUIRED_DEVS, + MDIO_MMDREG_DEVS0_PHYXS); + if (rc < 0) + goto fail; + + efx->board_info.init_leds(efx); + + return rc; + + fail: + EFX_ERR(efx, "XFP: reset timed out!\n"); + return rc; +} + + +static int xfp_phy_init(struct efx_nic *efx) +{ + struct xfp_phy_data *phy_data; + u32 devid = mdio_clause45_read_id(efx, MDIO_MMD_PHYXS); + int rc; + + phy_data = kzalloc(sizeof(struct xfp_phy_data), GFP_KERNEL); + efx->phy_data = (void *) phy_data; + + EFX_INFO(efx, "XFP: PHY ID reg %x (OUI %x model %x revision" + " %x)\n", devid, MDIO_ID_OUI(devid), MDIO_ID_MODEL(devid), + MDIO_ID_REV(devid)); + + phy_data->phy_powered = efx->phy_powered; + phy_data->tx_disabled = efx->tx_disabled; + + rc = xfp_reset_phy(efx); + if (rc < 0) + goto fail; + + EFX_INFO(efx, "XFP: PHY init %s.\n", + rc ? "failed" : "successful"); + return 0; + + fail: + kfree(efx->phy_data); + efx->phy_data = NULL; + return rc; +} + +static void xfp_phy_clear_interrupt(struct efx_nic *efx) +{ + xenpack_clear_lasi_irqs(efx); +} + +static int xfp_link_ok(struct efx_nic *efx) +{ + return mdio_clause45_links_ok(efx, XFP_REQUIRED_DEVS); +} + +static int xfp_phy_check_hw(struct efx_nic *efx) +{ + int rc = 0; + int link_up = xfp_link_ok(efx); + /* Simulate a PHY event if link state has changed */ + if (link_up != efx->link_up) { + efx->link_up = link_up; + efx->mac_op->fake_phy_event(efx); + } + + rc = efx->board_info.monitor(efx); + if (rc) { + EFX_ERR(efx, ": XFP sensor alert! Putting PHY into " + "low power.\n"); + efx->phy_powered = 0; + + mdio_clause45_set_mmds_lpower(efx, 1, XFP_REQUIRED_DEVS); + } + + return rc; +} + +static void xfp_phy_reconfigure(struct efx_nic *efx) +{ + struct xfp_phy_data *phy_data = efx->phy_data; + + /* Reset the PHY when moving from transmitter off or powered off, + * to transmitter on and powered on */ + if ((efx->phy_powered && !efx->tx_disabled) && + (!phy_data->phy_powered || phy_data->tx_disabled)) + xfp_reset_phy(efx); + + mdio_clause45_transmit_disable(efx, efx->tx_disabled); + mdio_clause45_set_mmds_lpower(efx, !efx->phy_powered, + XFP_REQUIRED_DEVS); + mdio_clause45_phy_reconfigure(efx); + + phy_data->tx_disabled = efx->tx_disabled; + phy_data->phy_powered = efx->phy_powered; + efx->link_up = xfp_link_ok(efx); + efx->link_options = GM_LPA_10000FULL; +} + + +static void xfp_phy_fini(struct efx_nic *efx) +{ + /* Clobber the LED if it was blinking */ + efx->board_info.blink(efx, 0); + + /* Free the context block */ + kfree(efx->phy_data); + efx->phy_data = NULL; +} + +struct efx_phy_operations falcon_xfp_phy_ops = { + .init = xfp_phy_init, + .reconfigure = xfp_phy_reconfigure, + .check_hw = xfp_phy_check_hw, + .fini = xfp_phy_fini, + .clear_interrupt = xfp_phy_clear_interrupt, + .reset_xaui = efx_port_dummy_op_void, + .mmds = XFP_REQUIRED_DEVS, + .loopbacks = XFP_LOOPBACKS, + /* No loopback appears to be reliable enough for self-test + * operation. So don't do it. */ + .startup_loopback = LOOPBACK_PCS, +};