Update Solarflare Communications net driver to version 3.0.2.2074 Bring net driver in Xen tree in line with kernel.org tree Add support for new SFC9000 series NICs Signed-off-by: Kieran Mansley diff -r aa133dee7792 MAINTAINERS --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2559,11 +2559,12 @@ S: Supported SFC NETWORK DRIVER -P: Steve Hodgson -P: Ben Hutchings -P: Robert Stonehouse -M: linux-net-drivers@xxxxxxxxxxxxxx +M: Solarflare linux maintainers +M: Steve Hodgson +M: Ben Hutchings +L: netdev@xxxxxxxxxxxxxxx S: Supported +F: drivers/net/sfc/ SGI VISUAL WORKSTATION 320 AND 540 P: Andrey Panin diff -r aa133dee7792 drivers/net/sfc/Kconfig --- a/drivers/net/sfc/Kconfig +++ b/drivers/net/sfc/Kconfig @@ -1,35 +1,39 @@ config SFC - tristate "Solarflare Solarstorm SFC4000 support" + tristate "Solarflare Solarstorm SFC4000/SFC9000-family support" depends on PCI && INET - select MII select CRC32 + select I2C + select I2C_ALGOBIT help This driver supports 10-gigabit Ethernet cards based on - the Solarflare Communications Solarstorm SFC4000 controller. + the Solarflare Communications Solarstorm SFC4000 and + SFC9000-family controllers. To compile this driver as a module, choose M here. The module will be called sfc. config SFC_DEBUGFS - bool "Solarflare Solarstorm SFC4000 debugging support" - depends on SFC && DEBUG_FS - default N - help - This option creates an "sfc" subdirectory of debugfs with - debugging information for the SFC4000 driver. + bool "Solarflare Solarstorm SFC4000 debugging support" + depends on SFC && DEBUG_FS + default N + help + This option creates an "sfc" subdirectory of debugfs with + debugging information for the SFC4000 driver. - If unsure, say N. + If unsure, say N. config SFC_MTD - depends on SFC && MTD && MTD_PARTITIONS - tristate "Solarflare Solarstorm SFC4000 flash/EEPROM support" + bool "Solarflare Solarstorm SFC4000/SFC9000-family MTD support" + depends on SFC && MTD && !(SFC=y && MTD=m) + default y help - This module exposes the on-board flash and/or EEPROM memory as - MTD devices (e.g. /dev/mtd1). This makes it possible to upload a - new boot ROM to the NIC. + This exposes the on-board flash and/or EEPROM memory as MTD + devices (e.g. /dev/mtd1). This makes it possible to upload + new boot code to the NIC. config SFC_RESOURCE depends on SFC && X86 - tristate "Solarflare Solarstorm SFC4000 resource driver" + tristate "Solarflare Solarstorm SFC4000/SFC9000 resource driver" help This module provides the SFC resource manager driver. + diff -r aa133dee7792 drivers/net/sfc/Makefile --- a/drivers/net/sfc/Makefile +++ b/drivers/net/sfc/Makefile @@ -1,13 +1,14 @@ -sfc-y += efx.o falcon.o tx.o rx.o mentormac.o falcon_gmac.o \ - falcon_xmac.o alaska.o i2c-direct.o selftest.o \ - driverlink.o ethtool.o xfp_phy.o mdio_10g.o \ - txc43128_phy.o tenxpress.o lm87_support.o boards.o \ - sfe4001.o pm8358_phy.o null_phy.o kernel_compat.o + +sfc-y += efx.o nic.o falcon.o siena.o tx.o rx.o \ + falcon_gmac.o falcon_xmac.o mcdi_mac.o selftest.o \ + driverlink.o ethtool.o qt202x_phy.o mdio_10g.o \ + tenxpress.o falcon_boards.o mcdi.o linux_mdio.o \ + mcdi_phy.o ioctl.o kernel_compat.o lm87.o lm90.o +sfc-$(CONFIG_SFC_MTD) += mtd.o sfc-$(CONFIG_SFC_DEBUGFS) += debugfs.o + obj-$(CONFIG_SFC) += sfc.o -sfc_mtd-y = mtd.o -obj-$(CONFIG_SFC_MTD) += sfc_mtd.o - obj-$(CONFIG_SFC_RESOURCE) += sfc_resource/ +EXTRA_CFLAGS += -DEFX_USE_KCOMPAT=1 -Wno-unused-label diff -r aa133dee7792 drivers/net/sfc/alaska.c --- a/drivers/net/sfc/alaska.c +++ /dev/null @@ -1,159 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005: Fen Systems Ltd. - * Copyright 2006-2007: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * 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); -} - -static int alaska_init(struct efx_nic *efx) -{ - struct mii_if_info *gmii = &efx->mii; - u32 ier, leds, ctrl_1g, phy_spec; - - /* Read ISR to clear any outstanding PHY interrupts */ - gmii->mdio_read(gmii->dev, gmii->phy_id, GMII_ISR); - - /* Enable PHY interrupts */ - ier = gmii->mdio_read(gmii->dev, gmii->phy_id, GMII_IER); - ier |= IER_LINK_CHG; - gmii->mdio_write(gmii->dev, gmii->phy_id, GMII_IER, ier); - - /* Remove 1G half-duplex as unsupported in Mentor MAC */ - ctrl_1g = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_CTRL1000); - ctrl_1g &= ~(ADVERTISE_1000HALF); - gmii->mdio_write(gmii->dev, gmii->phy_id, MII_CTRL1000, ctrl_1g); - - /* - * The PHY can save power when there is no external connection - * (sleep mode). However, this is incompatible with PHY - * loopback, and if enable and disable it quickly the PHY can - * go to sleep even when sleep mode is disabled. (SFC bug - * 9309.) Therefore we disable it all the time. - */ - phy_spec = gmii->mdio_read(gmii->dev, gmii->phy_id, - ALASKA_PHY_SPECIFIC); - phy_spec &= ~ALASKA_ALLOW_SLEEP; - gmii->mdio_write(gmii->dev, gmii->phy_id, ALASKA_PHY_SPECIFIC, - phy_spec); - - /* Configure LEDs */ - leds = gmii->mdio_read(gmii->dev, gmii->phy_id, ALASKA_LED_CONTROL); - leds &= ~(LED_BLINK_MASK | LED_TX_CTRL_MASK); - leds |= (LED_BLINK_FAST | LED_TX_CTRL_LINK_AND_ACTIVITY); - gmii->mdio_write(gmii->dev, gmii->phy_id, ALASKA_LED_CONTROL, leds); - - return 0; -} - -static void alaska_fini(struct efx_nic *efx) -{ - struct mii_if_info *gmii = &efx->mii; - u32 ier; - - /* Disable PHY interrupts */ - ier = gmii->mdio_read(gmii->dev, gmii->phy_id, GMII_IER); - ier &= ~IER_LINK_CHG; - gmii->mdio_write(gmii->dev, gmii->phy_id, GMII_IER, ier); -} - - -struct efx_phy_operations alaska_phy_operations = { - .init = alaska_init, - .fini = alaska_fini, - .reconfigure = alaska_reconfigure, - .clear_interrupt = alaska_clear_interrupt, - .loopbacks = (1 << LOOPBACK_PHY) | (1 << LOOPBACK_NETWORK), - .startup_loopback = LOOPBACK_PHY, -}; diff -r aa133dee7792 drivers/net/sfc/autocompat.h --- /dev/null +++ b/drivers/net/sfc/autocompat.h @@ -0,0 +1,98 @@ +#define EFX_HAVE_OLD_NAPI yes +#define EFX_HAVE_OLD_CSUM yes +#define EFX_HAVE_OLD_IP_FAST_CSUM yes +// #define EFX_NEED_BYTEORDER_TYPES +#define EFX_NEED_CSUM_UNFOLDED yes +// #define EFX_NEED_CSUM_TCPUDP_NOFOLD +// #define EFX_NEED_DEV_NOTICE +// #define EFX_NEED_DUMMY_PCI_DISABLE_MSI +// #define EFX_NEED_DUMMY_MSIX +// #define EFX_NEED_ENABLE_MSIX +// #define EFX_HAVE_GRO +// #define EFX_NEED_GFP_T +#define EFX_NEED_HEX_DUMP yes +// #define EFX_NEED_HEX_DUMP_CONST_FIX +// #define EFX_NEED_IF_MII +#define EFX_HAVE_IRQ_HANDLER_REGS yes +#define EFX_NEED_IRQ_HANDLER_T yes +// #define EFX_NEED_KZALLOC +// #define EFX_NEED_MII_CONSTANTS +#define EFX_NEED_MII_ADVERTISE_FLOWCTRL yes +#define EFX_NEED_MII_RESOLVE_FLOWCTRL_FDX yes +// #define EFX_HAVE_LINUX_MDIO_H +// #define EFX_NEED_MSECS_TO_JIFFIES +// #define EFX_NEED_MDELAY +// #define EFX_NEED_MSLEEP +// #define EFX_NEED_SSLEEP +// #define EFX_NEED_MTD_ERASE_CALLBACK +// #define EFX_NEED_MUTEX +// #define EFX_NEED_NETDEV_ALLOC_SKB +#define EFX_NEED_NETDEV_TX_T yes +#define EFX_NEED_NETIF_NAPI_DEL yes +// #define EFX_NEED_NETIF_TX_LOCK +#define EFX_NEED_NETIF_ADDR_LOCK yes +#define EFX_NEED_PCI_CLEAR_MASTER yes +// #define EFX_NEED_PCI_MATCH_ID +// #define EFX_NEED_PCI_SAVE_RESTORE_WRAPPERS +#define EFX_NEED_PRINT_MAC yes +// #define EFX_NEED_RANDOM_ETHER_ADDR +// #define EFX_NEED_RESOURCE_SIZE_T +// #define EFX_NEED_RTNL_TRYLOCK +// #define EFX_HAVE_ROUND_JIFFIES_UP +// #define EFX_NEED_SAFE_LISTS +// #define EFX_NEED_SCHEDULE_TIMEOUT_INTERRUPTIBLE +// #define EFX_NEED_SCHEDULE_TIMEOUT_UNINTERRUPTIBLE +// #define EFX_NEED_SETUP_TIMER +#define EFX_NEED_SKB_HEADER_MACROS yes +// #define EFX_NEED_ETH_HDR +#define EFX_NEED_TCP_HDR yes +#define EFX_NEED_IP_HDR yes +#define EFX_NEED_IPV6_HDR yes +#define EFX_NEED_WORK_API_WRAPPERS yes +// #define EFX_USE_CANCEL_DELAYED_WORK_SYNC +// #define EFX_USE_CANCEL_WORK_SYNC +// #define EFX_USE_ETHTOOL_ETH_TP_MDIX +#define EFX_USE_ETHTOOL_GET_PERM_ADDR yes +// #define EFX_USE_ETHTOOL_FLAGS +// #define EFX_USE_ETHTOOL_LP_ADVERTISING +// #define EFX_USE_ETHTOOL_MDIO_SUPPORT +#define EFX_USE_LINUX_IO_H yes +#define EFX_USE_LINUX_UACCESS_H yes +#define EFX_USE_MTD_ERASE_FAIL_ADDR yes +#define EFX_USE_MTD_WRITESIZE yes +// #define EFX_USE_NETDEV_STATS +// #define EFX_USE_PCI_DEV_REVISION +// #define EFX_USE_NETDEV_VLAN_FEATURES +#define EFX_USE_I2C_LEGACY yes +#define EFX_NEED_I2C_NEW_DUMMY yes +// #define EFX_HAVE_OLD_I2C_DRIVER_PROBE +// #define EFX_HAVE_OLD_I2C_NEW_DUMMY +// #define EFX_USE_I2C_DRIVER_NAME +#define EFX_HAVE_HWMON_H yes +// #define EFX_NEED_HWMON_VID +// #define EFX_HAVE_I2C_SENSOR_H +#define EFX_HAVE_HWMON_CLASS_DEVICE yes +// #define EFX_HAVE_OLD_DEVICE_ATTRIBUTE +#define EFX_NEED_BOOL yes +// #define EFX_USE_ETHTOOL_GET_SSET_COUNT +// #define EFX_HAVE_ETHTOOL_RESET +#define EFX_NEED_I2C_LOCK_ADAPTER yes +// #define EFX_USE_I2C_BUS_SEMAPHORE +#define EFX_HAVE_OLD_PCI_DMA_MAPPING_ERROR yes +// #define EFX_HAVE_LINUX_SEMAPHORE_H +#define EFX_NEED_DEV_GET_STATS yes +#define EFX_HAVE_OLD_CPUMASK_SCNPRINTF yes +#define EFX_USE_PM yes +// #define EFX_USE_PM_EXT_OPS +// #define EFX_USE_DEV_PM_OPS +#define EFX_NEED_ATOMIC_CMPXCHG yes +#define EFX_NEED_WARN_ON yes +// #define EFX_NEED_WAIT_EVENT_TIMEOUT +// #define EFX_NEED_ETHTOOL_CONSTANTS +#define EFX_NEED_PCI_WAKE_FROM_D3 yes +// #define EFX_HAVE_DEV_DISABLE_LRO +#define EFX_NEED_UNMASK_MSIX_VECTORS yes +// #define EFX_NEED_FOR_EACH_PCI_DEV +#define EFX_NEED_SCSI_SGLIST yes +#define EFX_NEED_SG_NEXT yes +// #define EFX_NEED_KFIFO diff -r aa133dee7792 drivers/net/sfc/bitfield.h --- a/drivers/net/sfc/bitfield.h +++ b/drivers/net/sfc/bitfield.h @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2006-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_BITFIELD_H @@ -54,6 +37,8 @@ #define EFX_DWORD_2_WIDTH 32 #define EFX_DWORD_3_LBN 96 #define EFX_DWORD_3_WIDTH 32 +#define EFX_QWORD_0_LBN 0 +#define EFX_QWORD_0_WIDTH 64 /* Specified attribute (e.g. LBN) of the specified field */ #define EFX_VAL(field, attribute) field ## _ ## attribute @@ -69,9 +54,9 @@ * * The maximum width mask that can be generated is 64 bits. */ -#define EFX_MASK64(field) \ - (EFX_WIDTH(field) == 64 ? ~((u64) 0) : \ - (((((u64) 1) << EFX_WIDTH(field))) - 1)) +#define EFX_MASK64(width) \ + ((width) == 64 ? ~((u64) 0) : \ + (((((u64) 1) << (width))) - 1)) /* Mask equal in width to the specified field. * @@ -80,9 +65,9 @@ * The maximum width mask that can be generated is 32 bits. Use * EFX_MASK64 for higher width fields. */ -#define EFX_MASK32(field) \ - (EFX_WIDTH(field) == 32 ? ~((u32) 0) : \ - (((((u32) 1) << EFX_WIDTH(field))) - 1)) +#define EFX_MASK32(width) \ + ((width) == 32 ? ~((u32) 0) : \ + (((((u32) 1) << (width))) - 1)) /* A doubleword (i.e. 4 byte) datatype - little-endian in HW */ typedef union efx_dword { @@ -155,44 +140,49 @@ EFX_EXTRACT_NATIVE(le32_to_cpu(element), min, max, low, high) #define EFX_EXTRACT_OWORD64(oword, low, high) \ - (EFX_EXTRACT64((oword).u64[0], 0, 63, low, high) | \ - EFX_EXTRACT64((oword).u64[1], 64, 127, low, high)) + ((EFX_EXTRACT64((oword).u64[0], 0, 63, low, high) | \ + EFX_EXTRACT64((oword).u64[1], 64, 127, low, high)) & \ + EFX_MASK64(high + 1 - low)) #define EFX_EXTRACT_QWORD64(qword, low, high) \ - EFX_EXTRACT64((qword).u64[0], 0, 63, low, high) + (EFX_EXTRACT64((qword).u64[0], 0, 63, low, high) & \ + EFX_MASK64(high + 1 - low)) #define EFX_EXTRACT_OWORD32(oword, low, high) \ - (EFX_EXTRACT32((oword).u32[0], 0, 31, low, high) | \ - EFX_EXTRACT32((oword).u32[1], 32, 63, low, high) | \ - EFX_EXTRACT32((oword).u32[2], 64, 95, low, high) | \ - EFX_EXTRACT32((oword).u32[3], 96, 127, low, high)) + ((EFX_EXTRACT32((oword).u32[0], 0, 31, low, high) | \ + EFX_EXTRACT32((oword).u32[1], 32, 63, low, high) | \ + EFX_EXTRACT32((oword).u32[2], 64, 95, low, high) | \ + EFX_EXTRACT32((oword).u32[3], 96, 127, low, high)) & \ + EFX_MASK32(high + 1 - low)) #define EFX_EXTRACT_QWORD32(qword, low, high) \ - (EFX_EXTRACT32((qword).u32[0], 0, 31, low, high) | \ - EFX_EXTRACT32((qword).u32[1], 32, 63, low, high)) + ((EFX_EXTRACT32((qword).u32[0], 0, 31, low, high) | \ + EFX_EXTRACT32((qword).u32[1], 32, 63, low, high)) & \ + EFX_MASK32(high + 1 - low)) -#define EFX_EXTRACT_DWORD(dword, low, high) \ - EFX_EXTRACT32((dword).u32[0], 0, 31, low, high) +#define EFX_EXTRACT_DWORD(dword, low, high) \ + (EFX_EXTRACT32((dword).u32[0], 0, 31, low, high) & \ + EFX_MASK32(high + 1 - low)) -#define EFX_OWORD_FIELD64(oword, field) \ - (EFX_EXTRACT_OWORD64(oword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ - & EFX_MASK64(field)) +#define EFX_OWORD_FIELD64(oword, field) \ + EFX_EXTRACT_OWORD64(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) -#define EFX_QWORD_FIELD64(qword, field) \ - (EFX_EXTRACT_QWORD64(qword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ - & EFX_MASK64(field)) +#define EFX_QWORD_FIELD64(qword, field) \ + EFX_EXTRACT_QWORD64(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) -#define EFX_OWORD_FIELD32(oword, field) \ - (EFX_EXTRACT_OWORD32(oword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ - & EFX_MASK32(field)) +#define EFX_OWORD_FIELD32(oword, field) \ + EFX_EXTRACT_OWORD32(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) -#define EFX_QWORD_FIELD32(qword, field) \ - (EFX_EXTRACT_QWORD32(qword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ - & EFX_MASK32(field)) +#define EFX_QWORD_FIELD32(qword, field) \ + EFX_EXTRACT_QWORD32(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) -#define EFX_DWORD_FIELD(dword, field) \ - (EFX_EXTRACT_DWORD(dword, EFX_LOW_BIT(field), EFX_HIGH_BIT(field)) \ - & EFX_MASK32(field)) +#define EFX_DWORD_FIELD(dword, field) \ + EFX_EXTRACT_DWORD(dword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field)) #define EFX_OWORD_IS_ZERO64(oword) \ (((oword).u64[0] | (oword).u64[1]) == (__force __le64) 0) @@ -428,69 +418,102 @@ * 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)); \ +#define EFX_AND_OWORD(oword, from, mask) \ + do { \ + (oword).u64[0] = (from).u64[0] & (mask).u64[0]; \ + (oword).u64[1] = (from).u64[1] & (mask).u64[1]; \ } while (0) -#define EFX_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)); \ +#define EFX_OR_OWORD(oword, from, mask) \ + do { \ + (oword).u64[0] = (from).u64[0] | (mask).u64[0]; \ + (oword).u64[1] = (from).u64[1] | (mask).u64[1]; \ } while (0) -#define EFX_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)); \ +#define EFX_INSERT64(min, max, low, high, value) \ + cpu_to_le64(EFX_INSERT_NATIVE(min, max, low, high, value)) + +#define EFX_INSERT32(min, max, low, high, value) \ + cpu_to_le32(EFX_INSERT_NATIVE(min, max, low, high, value)) + +#define EFX_INPLACE_MASK64(min, max, low, high) \ + EFX_INSERT64(min, max, low, high, EFX_MASK64(high + 1 - low)) + +#define EFX_INPLACE_MASK32(min, max, low, high) \ + EFX_INSERT32(min, max, low, high, EFX_MASK32(high + 1 - low)) + +#define EFX_SET_OWORD64(oword, low, high, value) do { \ + (oword).u64[0] = (((oword).u64[0] \ + & ~EFX_INPLACE_MASK64(0, 63, low, high)) \ + | EFX_INSERT64(0, 63, low, high, value)); \ + (oword).u64[1] = (((oword).u64[1] \ + & ~EFX_INPLACE_MASK64(64, 127, low, high)) \ + | EFX_INSERT64(64, 127, low, high, 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)); \ +#define EFX_SET_QWORD64(qword, low, high, value) do { \ + (qword).u64[0] = (((qword).u64[0] \ + & ~EFX_INPLACE_MASK64(0, 63, low, high)) \ + | EFX_INSERT64(0, 63, low, high, 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)); \ +#define EFX_SET_OWORD32(oword, low, high, value) do { \ + (oword).u32[0] = (((oword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, low, high)) \ + | EFX_INSERT32(0, 31, low, high, value)); \ + (oword).u32[1] = (((oword).u32[1] \ + & ~EFX_INPLACE_MASK32(32, 63, low, high)) \ + | EFX_INSERT32(32, 63, low, high, value)); \ + (oword).u32[2] = (((oword).u32[2] \ + & ~EFX_INPLACE_MASK32(64, 95, low, high)) \ + | EFX_INSERT32(64, 95, low, high, value)); \ + (oword).u32[3] = (((oword).u32[3] \ + & ~EFX_INPLACE_MASK32(96, 127, low, high)) \ + | EFX_INSERT32(96, 127, low, high, value)); \ } while (0) +#define EFX_SET_QWORD32(qword, low, high, value) do { \ + (qword).u32[0] = (((qword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, low, high)) \ + | EFX_INSERT32(0, 31, low, high, value)); \ + (qword).u32[1] = (((qword).u32[1] \ + & ~EFX_INPLACE_MASK32(32, 63, low, high)) \ + | EFX_INSERT32(32, 63, low, high, value)); \ + } while (0) + +#define EFX_SET_DWORD32(dword, low, high, value) do { \ + (dword).u32[0] = (((dword).u32[0] \ + & ~EFX_INPLACE_MASK32(0, 31, low, high)) \ + | EFX_INSERT32(0, 31, low, high, value)); \ + } while (0) + +#define EFX_SET_OWORD_FIELD64(oword, field, value) \ + EFX_SET_OWORD64(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_QWORD_FIELD64(qword, field, value) \ + EFX_SET_QWORD64(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_OWORD_FIELD32(oword, field, value) \ + EFX_SET_OWORD32(oword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_QWORD_FIELD32(qword, field, value) \ + EFX_SET_QWORD32(qword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + +#define EFX_SET_DWORD_FIELD(dword, field, value) \ + EFX_SET_DWORD32(dword, EFX_LOW_BIT(field), \ + EFX_HIGH_BIT(field), value) + + + #if BITS_PER_LONG == 64 #define EFX_SET_OWORD_FIELD EFX_SET_OWORD_FIELD64 #define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD64 @@ -499,27 +522,17 @@ #define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD32 #endif -#define EFX_SET_OWORD_FIELD_VER(efx, oword, field, value) do { \ - if (FALCON_REV(efx) >= FALCON_REV_B0) { \ - EFX_SET_OWORD_FIELD((oword), field##_B0, (value)); \ - } else { \ - EFX_SET_OWORD_FIELD((oword), field##_A1, (value)); \ - } \ -} while (0) - -#define EFX_QWORD_FIELD_VER(efx, qword, field) \ - (FALCON_REV(efx) >= FALCON_REV_B0 ? \ - EFX_QWORD_FIELD((qword), field##_B0) : \ - EFX_QWORD_FIELD((qword), field##_A1)) - /* Used to avoid compiler warnings about shift range exceeding width * of the data types when dma_addr_t is only 32 bits wide. */ #define DMA_ADDR_T_WIDTH (8 * sizeof(dma_addr_t)) #define EFX_DMA_TYPE_WIDTH(width) \ (((width) < DMA_ADDR_T_WIDTH) ? (width) : DMA_ADDR_T_WIDTH) -#define EFX_DMA_MAX_MASK ((DMA_ADDR_T_WIDTH == 64) ? \ - ~((u64) 0) : ~((u32) 0)) -#define EFX_DMA_MASK(mask) ((mask) & EFX_DMA_MAX_MASK) + + +/* Static initialiser */ +#define EFX_OWORD32(a, b, c, d) \ + { .u32 = { __constant_cpu_to_le32(a), __constant_cpu_to_le32(b), \ + __constant_cpu_to_le32(c), __constant_cpu_to_le32(d) } } #endif /* EFX_BITFIELD_H */ diff -r aa133dee7792 drivers/net/sfc/boards.c --- a/drivers/net/sfc/boards.c +++ /dev/null @@ -1,528 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2007: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Developed by Solarflare Communications - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** - */ - -#include "net_driver.h" -#include "phy.h" -#include "lm87_support.h" -#include "boards.h" -#include "efx.h" - -/* Macros for unpacking the board revision */ -/* The revision info is in host byte order. */ -#define BOARD_TYPE(_rev) (_rev >> 8) -#define BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf) -#define BOARD_MINOR(_rev) (_rev & 0xf) - -/* Blink support. If the PHY has no auto-blink mode so we hang it off a timer */ -#define BLINK_INTERVAL (HZ/2) - -static void blink_led_timer(unsigned long context) -{ - struct efx_nic *efx = (struct efx_nic *)context; - struct efx_blinker *bl = &efx->board_info.blinker; - efx->board_info.set_fault_led(efx, bl->state); - bl->state = !bl->state; - if (bl->resubmit) { - bl->timer.expires = jiffies + BLINK_INTERVAL; - add_timer(&bl->timer); - } -} - -static void board_blink(struct efx_nic *efx, int blink) -{ - struct efx_blinker *blinker = &efx->board_info.blinker; - - /* The rtnl mutex serialises all ethtool ioctls, so - * nothing special needs doing here. */ - if (blink) { - blinker->resubmit = 1; - blinker->state = 0; - setup_timer(&blinker->timer, blink_led_timer, - (unsigned long)efx); - blinker->timer.expires = jiffies + BLINK_INTERVAL; - add_timer(&blinker->timer); - } else { - blinker->resubmit = 0; - if (blinker->timer.function) - del_timer_sync(&blinker->timer); - efx->board_info.set_fault_led(efx, 0); - } -} - - -struct sensor_conf { - const char *name; - const unsigned high; - const unsigned low; -}; - -#define NO_LIMIT ((unsigned)-1) - -#define LM87_SENSOR_BYTES (18) - -static int sensor_limits_to_bytes(const struct sensor_conf *limits, - int nlimits, u8 *bytes, int maxbytes) -{ - int i, nbytes; - nbytes = 0; - for (i = 0; i < nlimits; i++) { - bytes[nbytes++] = limits[i].high; - if (limits[i].low != NO_LIMIT) - bytes[nbytes++] = limits[i].low; - /* We may have overrun by one at this point, but this test - * should only trigger in development drivers as the sizes - * are not dynamic. */ - if (nbytes > maxbytes) { - printk(KERN_ERR "%s: out of space!\n", __func__); - break; - } - } - return nbytes; -} - -/***************************************************************************** - * Support for the SFE4002 - * - */ -/* LM87 configuration data for the sensor on the SFE4002 board */ -static const struct sensor_conf sfe4002_lm87_limits[] = { - {"1.8V line", 0x91, 0x83}, /* 2.5V sensor, scaled for 1.8V */ - {"1.2V line", 0x5a, 0x51}, /* Vccp1 */ - {"3.3V line", 0xca, 0xb6}, - {"5V line", 0xc9, 0xb6}, - {"12V line", 0xe0, 0xb0}, - {"1V line", 0x4b, 0x44}, /* vccp2 */ - {"Ext. temp.", 0x46, 0x0a}, /* ASIC temp. */ - {"Int. temp.", 0x3c, 0x0a}, /* Board temp. */ - {"1.66V line", 0xb2, NO_LIMIT}, /* AIN1 only takes 1 value */ - {"1.5V line", 0xa1, NO_LIMIT} /* AIN2 only takes 1 value */ -}; - -static const int sfe4002_lm87_nlimits = ARRAY_SIZE(sfe4002_lm87_limits); - -static u16 sfe4002_lm87_irq_mask = EFX_LM87_NO_INTS; - -/* I2C ID of the onboard LM87 chip. This is board-specific as the bottom two - * bits are set by strap pins */ -#define SFE4002_LM87_I2C_ID (0x2e) - -/****************************************************************************/ -/* LED allocations. Note that on rev A0 boards the schematic and the reality - * differ: red and green are swapped. Below is the fixed (A1) layout (there - * are only 3 A0 boards in existence, so no real reason to make this - * conditional). - */ -#define SFE4002_FAULT_LED (2) /* Red */ -#define SFE4002_RX_LED (0) /* Green */ -#define SFE4002_TX_LED (1) /* Amber */ - -static int sfe4002_init_leds(struct efx_nic *efx) -{ - /* Set the TX and RX LEDs to reflect status and activity, and the - * fault LED off */ - xfp_set_led(efx, SFE4002_TX_LED, - QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT); - xfp_set_led(efx, SFE4002_RX_LED, - QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT); - xfp_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF); - efx->board_info.blinker.led_num = SFE4002_FAULT_LED; - return 0; -} - -static void sfe4002_fault_led(struct efx_nic *efx, int state) -{ - xfp_set_led(efx, SFE4002_FAULT_LED, state ? QUAKE_LED_ON : - QUAKE_LED_OFF); -} - -static int sfe4002_sensor_meaning(struct efx_nic *efx, int limit_num, - unsigned val) -{ - const struct sensor_conf *lim = &sfe4002_lm87_limits[limit_num]; - if (lim->low == NO_LIMIT) - EFX_ERR(efx, "%10s 0x%02x (nominal value 0x%02x)\n", lim->name, - val, lim->high); - else - EFX_ERR(efx, "%10s 0x%02x (nominal range 0x%02x - 0x%02x)\n", - lim->name, val, lim->high, lim->low); - return 1; -} - -static int sfe4002_check_hw(struct efx_nic *efx) -{ - int rc; - - /* A0 board rev. 4002s report a temperature fault the whole time - * (bad sensor) so we mask it out. */ - unsigned alarm_mask = (efx->board_info.minor > 0) ? - 0 : ~EFX_LM87_ETMP_INT; - - /* Check the sensor (NOP if not present). */ - rc = efx_check_lm87(efx, alarm_mask); - - /* We treat both lm87 interrupts and failure to talk to the lm87 - * as problems (since failure will only be reported if we did - * find the sensor at probe time. */ - if (rc) - EFX_ERR(efx, "sensor alert!\n"); - return rc; -} - -static int sfe4002_init(struct efx_nic *efx) -{ - u8 lm87_bytes[LM87_SENSOR_BYTES]; - int nbytes; - int rc; - - efx->board_info.monitor = sfe4002_check_hw; - efx->board_info.interpret_sensor = sfe4002_sensor_meaning; - efx->board_info.init_leds = sfe4002_init_leds; - efx->board_info.set_fault_led = sfe4002_fault_led; - efx->board_info.blink = board_blink; - /* To clean up shut down the lm87 (NOP if not present) */ - efx->board_info.fini = efx_remove_lm87; - - nbytes = sensor_limits_to_bytes(sfe4002_lm87_limits, - sfe4002_lm87_nlimits, lm87_bytes, - LM87_SENSOR_BYTES); - - /* Activate the lm87 sensor if present (succeeds if nothing there) */ - rc = efx_probe_lm87(efx, SFE4002_LM87_I2C_ID, - lm87_bytes, nbytes, sfe4002_lm87_irq_mask); - - return rc; -} - -/***************************************************************************** - * Support for the SFE4003 - * - */ -/* LM87 configuration data for the sensor on the SFE4003 board */ -static const struct sensor_conf sfe4003_lm87_limits[] = { - {"1.5V line", 0x78, 0x6d}, /* 2.5V input, values scaled for 1.5V */ - {"1.2V line", 0x5a, 0x51}, /* Vccp1 */ - {"3.3V line", 0xca, 0xb6}, - {"5V line", 0xc0, 0x00}, /* Sensor not connected. */ - {"12V line", 0xe0, 0xb0}, - {"1V line", 0x4b, 0x44}, /* Vccp2 */ - {"Ext. temp.", 0x46, 0x0a}, /* ASIC temp. */ - {"Int. temp.", 0x3c, 0x0a}, /* Board temp. */ - {"", 0xff, NO_LIMIT}, /* FAN1/AIN1 unused */ - {"", 0xff, NO_LIMIT} /* FAN2/AIN2 unused */ -}; - -static const int sfe4003_lm87_nlimits = ARRAY_SIZE(sfe4003_lm87_limits); - -static u16 sfe4003_lm87_irq_mask = EFX_LM87_NO_INTS; - - -static int sfe4003_sensor_meaning(struct efx_nic *efx, int limit_num, - unsigned val) -{ - const struct sensor_conf *lim = &sfe4003_lm87_limits[limit_num]; - if (lim->low == NO_LIMIT) - return 0; /* Neither AIN1 nor AIN2 mean anything to us */ - else - EFX_ERR(efx, "%10s 0x%02x (nominal range 0x%02x - 0x%02x)\n", - lim->name, val, lim->high, lim->low); - return 1; -} - -/* I2C ID of the onboard LM87 chip. This is board-specific as the bottom two - * bits are set by strap pins */ -#define SFE4003_LM87_I2C_ID (0x2e) - -/* Board-specific LED info. */ -#define SFE4003_RED_LED_GPIO (11) -#define SFE4003_LED_ON (1) -#define SFE4003_LED_OFF (0) - -static void sfe4003_fault_led(struct efx_nic *efx, int state) -{ - /* The LEDs were not wired to GPIOs before A3 */ - if (efx->board_info.minor < 3 && efx->board_info.major == 0) - return; - - txc_set_gpio_val(efx, SFE4003_RED_LED_GPIO, - state ? SFE4003_LED_ON : SFE4003_LED_OFF); -} - -static int sfe4003_init_leds(struct efx_nic *efx) -{ - /* The LEDs were not wired to GPIOs before A3 */ - if (efx->board_info.minor < 3 && efx->board_info.major == 0) - return 0; - - txc_set_gpio_dir(efx, SFE4003_RED_LED_GPIO, TXC_GPIO_DIR_OUTPUT); - txc_set_gpio_val(efx, SFE4003_RED_LED_GPIO, SFE4003_LED_OFF); - return 0; -} - -static int sfe4003_check_hw(struct efx_nic *efx) -{ - int rc; - /* A0/A1/A2 board rev. 4003s report a temperature fault the whole time - * (bad sensor) so we mask it out. */ - unsigned alarm_mask = - ~(EFX_LM87_ETMP_INT | EFX_LM87_FAN1_INT | EFX_LM87_FAN2_INT); - - /* Check the sensor (NOP if not present). */ - - rc = efx_check_lm87(efx, alarm_mask); - /* We treat both lm87 interrupts and failure to talk to the lm87 - * as problems (since failure will only be reported if we did - * find the sensor at probe time. */ - if (rc) - EFX_ERR(efx, "sensor alert!\n"); - - return rc; -} - -static int sfe4003_init(struct efx_nic *efx) -{ - u8 lm87_bytes[LM87_SENSOR_BYTES]; - int nbytes; - int rc; - efx->board_info.monitor = sfe4003_check_hw; - efx->board_info.interpret_sensor = sfe4003_sensor_meaning; - efx->board_info.init_leds = sfe4003_init_leds; - efx->board_info.set_fault_led = sfe4003_fault_led; - efx->board_info.blink = board_blink; - /* To clean up shut down the lm87 (NOP if not present) */ - efx->board_info.fini = efx_remove_lm87; - - nbytes = sensor_limits_to_bytes(sfe4003_lm87_limits, - sfe4003_lm87_nlimits, lm87_bytes, - LM87_SENSOR_BYTES); - - /* Activate the lm87 sensor if present (succeeds if nothing there) */ - rc = efx_probe_lm87(efx, SFE4003_LM87_I2C_ID, - lm87_bytes, nbytes, sfe4003_lm87_irq_mask); - - if (rc < 0) - EFX_ERR(efx, "Temperature sensor probe failure: " - "please check the jumper position\n"); - return rc; -} - -/***************************************************************************** - * Support for the SFE4005 - * - */ -/* LM87 configuration data for the sensor on the SFE4005 board */ -static const u8 sfe4005_lm87_limits[] = { - 0x51, /* 2.5V high lim. (actually monitor 1.0V line, so 1050mV) */ - 0x49, /* 2.5V low lim. (950mV) */ - 0xf6, /* Vccp1 high lim. (3.3V rail, 3465 mV) */ - 0xde, /* Vcpp1 low lim. (3.3V rail, 3135 mV) */ - 0xca, /* 3.3V AUX high lim. (3465 mV) */ - 0xb6, /* 3.3V AUX low lim. (3135mV) */ - 0xc0, /* 5V high lim. not connected) */ - 0x00, /* 5V low lim. (not connected) */ - 0xd0, /* 12V high lim. (13000mV) */ - 0xb0, /* 12V low lim. (11000mV) */ - 0xc0, /* Vccp2 high lim. (unused) */ - 0x00, /* Vccp2 low lim. (unused) */ - 0x46, /* Ext temp 1 (ASIC) high lim. */ - 0x0a, /* Ext temp 1 low lim. */ - 0x3c, /* Int temp (board) high lim. */ - 0x0a, /* Int temp 1 low lim. */ - 0xff, /* Fan 1 high (unused) */ - 0xff, /* Fan 2 high (unused) */ -}; - -#define SFE4005_LM87_I2C_ID (0x2e) - -/* Until the LM87 monitoring is interrupt driven. */ -#define SFE4005_LM87_IRQMASK EFX_LM87_NO_INTS - -#define SFE4005_PCF8575_I2C_ID (0x20) -/* Definitions for the I/O expander that controls the CX4 chip: - * which PCF8575 pin maps to which function */ -#define SFE4005_PORT0_EXTLOOP (1 << 0) -#define SFE4005_PORT1_EXTLOOP (1 << 1) -#define SFE4005_HOSTPROT_LOOP (1 << 2) -#define SFE4005_BCAST (1 << 3) /* TX on both ports */ -#define SFE4005_PORT0_EQ (1 << 4) -#define SFE4005_PORT1_EQ (1 << 5) -#define SFE4005_HOSTPORT_EQ (1 << 6) -#define SFE4005_PORTSEL (1 << 7) /* Which port (for RX in BCAST mode) */ -#define SFE4005_PORT0_PRE_LBN (8) /* Preemphasis on port 0 (2 bits)*/ -#define SFE4005_PORT1_PRE_LBN (10) /* Preemphasis on port 1 (2 bits)*/ -#define SFE4005_HOSTPORT_PRE_LBN (12) /* Preemphasis on host port (2 bits) */ -#define SFE4005_UNUSED (1 << 14) -#define SFE4005_CX4uC_nRESET (1 << 15) /* Reset the controller on CX4 chip */ - - -/* By default only turn on host port EQ. Can also OR in SFE4005_PORT0_EQ, - * SFE4005_PORT1_EQ but this hasn't been seen to make a difference. */ -#define SFE4005_CX4_DEFAULTS (SFE4005_CX4uC_nRESET | SFE4005_HOSTPORT_EQ) - -static int sfe4005_write_ioexpander(struct efx_nic *efx) -{ - unsigned long iobits = (unsigned long)efx->phy_data; - struct efx_i2c_interface *i2c = &efx->i2c; - u8 send[2], check[2]; - int rc; - /* Do not, EVER, deassert nRESET as that will reset Falcon too, - * and the driver won't know to repush the configuration, so - * nothing will work until the next power cycle. */ - BUG_ON(!(iobits & SFE4005_CX4uC_nRESET)); - send[0] = (iobits & 0xff); - send[1] = ((iobits >> 8) & 0xff); - rc = efx_i2c_send_bytes(i2c, SFE4005_PCF8575_I2C_ID, send, 2); - if (rc) { - EFX_ERR(efx, "failed to write to I/O expander: %d\n", rc); - return rc; - } - /* Paranoia: just check what the I/O expander reads back */ - rc = efx_i2c_recv_bytes(i2c, SFE4005_PCF8575_I2C_ID, check, 2); - if (rc) - EFX_ERR(efx, "failed to read back from I/O expander: %d\n", rc); - else if (check[0] != send[0] || check[1] != send[1]) - EFX_ERR(efx, "read back wrong value from I/O expander: " - "wanted %.2x%.2x, got %.2x%.2x\n", - send[1], send[0], check[1], check[0]); - return rc; -} - -static int sfe4005_init(struct efx_nic *efx) -{ - unsigned long iobits = SFE4005_CX4_DEFAULTS; - int rc; - - /* There is no PHY as such on the SFE4005 so phy_data is ours. */ - efx->phy_data = (void *)iobits; - - /* Push the values */ - rc = sfe4005_write_ioexpander(efx); - if (rc) - return rc; - - /* Activate the lm87 sensor if present (succeeds if nothing there) */ - rc = efx_probe_lm87(efx, SFE4005_LM87_I2C_ID, - sfe4005_lm87_limits, - sizeof(sfe4005_lm87_limits), SFE4005_LM87_IRQMASK); - - /* To clean up shut down the lm87 (NOP if not present) */ - efx->board_info.fini = efx_remove_lm87; - - return rc; -} - -/* This will get expanded as board-specific details get moved out of the - * PHY drivers. */ -struct efx_board_data { - const char *ref_model; - const char *gen_type; - int (*init) (struct efx_nic *nic); - unsigned mwatts; -}; - -static void dummy_fini(struct efx_nic *nic) -{ -} - -static int dummy_init(struct efx_nic *nic) -{ - nic->board_info.fini = dummy_fini; - return 0; -} - -/* Maximum board power (mW) - * Falcon controller ASIC accounts for 2.2W - * 10Xpress PHY accounts for 12W - * - */ -#define SFE4001_POWER 18000 -#define SFE4002_POWER 7500 -#define SFE4003_POWER 4500 -#define SFE4005_POWER 4500 - -static struct efx_board_data board_data[] = { - [EFX_BOARD_INVALID] = - {NULL, NULL, dummy_init, 0}, - [EFX_BOARD_SFE4001] = - {"SFE4001", "10GBASE-T adapter", sfe4001_poweron, SFE4001_POWER }, - [EFX_BOARD_SFE4002] = - {"SFE4002", "XFP adapter", sfe4002_init, SFE4002_POWER }, - [EFX_BOARD_SFE4003] = - {"SFE4003", "10GBASE-CX4 adapter", sfe4003_init, SFE4003_POWER }, - [EFX_BOARD_SFE4005] = - {"SFE4005", "10G blade adapter", sfe4005_init, SFE4005_POWER }, -}; - -int efx_set_board_info(struct efx_nic *efx, u16 revision_info) -{ - int rc = 0; - struct efx_board_data *data; - - if (BOARD_TYPE(revision_info) >= EFX_BOARD_MAX) { - EFX_ERR(efx, "squashing unknown board type %d\n", - BOARD_TYPE(revision_info)); - revision_info = 0; - } - - if (BOARD_TYPE(revision_info) == 0) { - efx->board_info.major = 0; - efx->board_info.minor = 0; - /* For early boards that don't have revision info. there is - * only 1 board for each PHY type, so we can work it out, with - * the exception of the PHY-less boards. */ - switch (efx->phy_type) { - case PHY_TYPE_10XPRESS: - efx->board_info.type = EFX_BOARD_SFE4001; - break; - case PHY_TYPE_XFP: - efx->board_info.type = EFX_BOARD_SFE4002; - break; - case PHY_TYPE_CX4_RTMR: - efx->board_info.type = EFX_BOARD_SFE4003; - break; - default: - efx->board_info.type = 0; - break; - } - } else { - efx->board_info.type = BOARD_TYPE(revision_info); - efx->board_info.major = BOARD_MAJOR(revision_info); - efx->board_info.minor = BOARD_MINOR(revision_info); - } - - data = &board_data[efx->board_info.type]; - - /* Report the board model number or generic type for recognisable - * boards. */ - if (efx->board_info.type != 0) - EFX_INFO(efx, "board is %s rev %c%d\n", - (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC) - ? data->ref_model : data->gen_type, - 'A' + efx->board_info.major, efx->board_info.minor); - - efx->board_info.init = data->init; - efx->board_info.mwatts = data->mwatts; - - return rc; -} diff -r aa133dee7792 drivers/net/sfc/boards.h --- a/drivers/net/sfc/boards.h +++ /dev/null @@ -1,51 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2007: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Developed by Solarflare Communications - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** - */ - -#ifndef EFX_BOARDS_H -#define EFX_BOARDS_H - -/* Board IDs (must fit in 8 bits). Note that 0 must never be assigned because - * on early boards it means there is no revision info. Board types pre 400x - * are not covered here, but this is not a problem because: - * - the early Falcon boards (FPGA, 401, 403) don't have any extra H/W we - * need care about and aren't being updated. - */ -enum efx_board_type { - EFX_BOARD_INVALID = 0, /* Early boards do not have board rev. info. */ - EFX_BOARD_SFE4001 = 1, - EFX_BOARD_SFE4002 = 2, - EFX_BOARD_SFE4003 = 3, - EFX_BOARD_SFE4005 = 4, - /* Insert new types before here */ - EFX_BOARD_MAX -}; - -extern int efx_set_board_info(struct efx_nic *efx, u16 revision_info); - -/* SFE4001 (10GBASE-T) */ -extern int sfe4001_poweron(struct efx_nic *efx); -extern void sfe4001_poweroff(struct efx_nic *efx); - -#endif diff -r aa133dee7792 drivers/net/sfc/config.h --- a/drivers/net/sfc/config.h +++ /dev/null @@ -1,1 +0,0 @@ -/* SFC config options can go here */ diff -r aa133dee7792 drivers/net/sfc/debugfs.c --- a/drivers/net/sfc/debugfs.c +++ b/drivers/net/sfc/debugfs.c @@ -1,48 +1,35 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include #include +/* For out-of-tree builds we always need procfs, if only for a compatibility + * symlink. + */ #include #include #include #include "net_driver.h" #include "efx.h" #include "debugfs.h" -#include "falcon.h" +#include "nic.h" -#ifndef PRIu64 -# if (BITS_PER_LONG == 64) -# define PRIu64 "lu" -# else -# define PRIu64 "llu" -# endif +/* EFX_USE_DEBUGFS is defined by kernel_compat.h so we can't decide whether to + * include this earlier. + */ +#ifdef EFX_USE_DEBUGFS +#include #endif +#ifndef EFX_USE_DEBUGFS + static void efx_debugfs_remove(struct proc_dir_entry *entry) { if (entry) @@ -53,6 +40,7 @@ #define debugfs_create_dir proc_mkdir #define debugfs_create_symlink proc_symlink +#endif /* !EFX_USE_DEBUGFS */ /* Parameter definition bound to a structure - each file has one of these */ struct efx_debugfs_bound_param { @@ -66,19 +54,36 @@ /* Top-level debug directory ([/sys/kernel]/debug/sfc) */ -static struct dentry *efx_debug_root; +static efx_debugfs_entry *efx_debug_root; /* "cards" directory ([/sys/kernel]/debug/sfc/cards) */ -static struct dentry *efx_debug_cards; +static efx_debugfs_entry *efx_debug_cards; /* Sequential file interface to bound parameters */ +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_DEBUGFS) + static int efx_debugfs_seq_show(struct seq_file *file, void *v) { - struct proc_dir_entry *entry = (struct proc_dir_entry *)file->private; - struct efx_debugfs_parameter *param = - (struct efx_debugfs_parameter *)entry->data; + struct efx_debugfs_bound_param *binding = file->private; + + return binding->param->reader(file, + binding->structure + + binding->param->offset); +} + +static int efx_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, efx_debugfs_seq_show, inode->i_private); +} + +#else /* EFX_USE_KCOMPAT && !EFX_USE_DEBUGFS */ + +static int efx_debugfs_seq_show(struct seq_file *file, void *v) +{ + struct proc_dir_entry *entry = file->private; + struct efx_debugfs_parameter *param = entry->data; void *structure = (void *)entry->read_proc; if (!structure) @@ -92,6 +97,8 @@ return single_open(file, efx_debugfs_seq_show, PROC_I(inode)->pde); } +#endif /* !EFX_USE_KCOMPAT || EFX_USE_DEBUGFS */ + static struct file_operations efx_debugfs_file_ops = { .owner = THIS_MODULE, @@ -102,11 +109,42 @@ }; +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_DEBUGFS) + +/** + * efx_fini_debugfs_child - remove a named child of a debugfs directory + * @dir: Directory + * @name: Name of child + * + * This removes the named child from the directory, if it exists. + */ +void efx_fini_debugfs_child(efx_debugfs_entry *dir, const char *name) +{ + struct qstr child_name; + efx_debugfs_entry *child; + + child_name.len = strlen(name); + child_name.name = name; + child_name.hash = full_name_hash(child_name.name, child_name.len); + child = d_lookup(dir, &child_name); + if (child) { + /* If it's a "regular" file, free its parameter binding */ + if (S_ISREG(child->d_inode->i_mode)) + kfree(child->d_inode->i_private); + debugfs_remove(child); + dput(child); + } +} + +#else /* EFX_USE_KCOMPAT && !EFX_USE_DEBUGFS */ + void efx_fini_debugfs_child(struct proc_dir_entry *dir, const char *name) { remove_proc_entry(name, dir); } +#endif /* !EFX_USE_KCOMPAT || EFX_USE_DEBUGFS */ + /* * Remove a debugfs directory. * @@ -114,7 +152,7 @@ * directory, and the directory itself. It does not do any recursion * to subdirectories. */ -static void efx_fini_debugfs_dir(struct dentry *dir, +static void efx_fini_debugfs_dir(efx_debugfs_entry *dir, struct efx_debugfs_parameter *params, const char *const *symlink_names) { @@ -159,6 +197,11 @@ return seq_printf(file, "%#x\n", value); } +int efx_debugfs_read_bool(struct seq_file *file, void *data) +{ + return seq_printf(file, "%d\n", *(bool *)data); +} + static int efx_debugfs_read_int_mode(struct seq_file *file, void *data) { unsigned int value = *(enum efx_int_mode *) data; @@ -183,18 +226,6 @@ 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); @@ -210,14 +241,30 @@ * Add parameter-files to the given debugfs directory. Return a * negative error code or 0 on success. */ -static int efx_init_debugfs_files(struct dentry *parent, +static int efx_init_debugfs_files(efx_debugfs_entry *parent, struct efx_debugfs_parameter *params, void *structure) { struct efx_debugfs_parameter *param = params; while (param->name) { - struct dentry *entry; + efx_debugfs_entry *entry; +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_DEBUGFS) + struct efx_debugfs_bound_param *binding; + + binding = kmalloc(sizeof(*binding), GFP_KERNEL); + if (!binding) + goto err; + binding->param = param; + binding->structure = structure; + + entry = debugfs_create_file(param->name, S_IRUGO, parent, + binding, &efx_debugfs_file_ops); + if (!entry) { + kfree(binding); + goto err; + } +#else entry = create_proc_entry(param->name, S_IRUGO, parent); if (!entry) goto err; @@ -233,6 +280,7 @@ entry->proc_fops = &efx_debugfs_file_ops; smp_wmb(); entry->read_proc = (read_proc_t *) structure; +#endif param++; } @@ -258,7 +306,7 @@ */ int efx_init_debugfs_netdev(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); char name[EFX_DEBUGFS_NAME_LEN]; char target[EFX_DEBUGFS_NAME_LEN]; size_t len; @@ -298,7 +346,7 @@ */ void efx_fini_debugfs_netdev(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); debugfs_remove(efx->debug_port_symlink); efx->debug_port_symlink = NULL; @@ -309,16 +357,25 @@ /* Per-port parameters */ static struct efx_debugfs_parameter efx_debugfs_port_parameters[] = { EFX_NAMED_PARAMETER(enabled, struct efx_nic, port_enabled, - int, efx_debugfs_read_int), - EFX_INT_PARAMETER(struct efx_nic, rx_checksum_enabled), + bool, efx_debugfs_read_bool), +#if defined(EFX_USE_KCOMPAT) && !defined(NETIF_F_LRO) + EFX_BOOL_PARAMETER(struct efx_nic, lro_enabled), +#endif + EFX_BOOL_PARAMETER(struct efx_nic, rx_checksum_enabled), EFX_ATOMIC_PARAMETER(struct efx_nic, netif_stop_count), - EFX_INT_PARAMETER(struct efx_nic, link_up), - EFX_UINT_PARAMETER(struct efx_nic, link_options), - EFX_INT_PARAMETER(struct efx_nic, promiscuous), - EFX_UINT_PARAMETER(struct efx_nic, loopback_modes), + EFX_NAMED_PARAMETER(link_up, struct efx_nic, link_state.up, + bool, efx_debugfs_read_bool), + EFX_BOOL_PARAMETER(struct efx_nic, xmac_poll_required), + EFX_NAMED_PARAMETER(link_fd, struct efx_nic, link_state.fd, + bool, efx_debugfs_read_bool), + EFX_NAMED_PARAMETER(link_speed, struct efx_nic, link_state.speed, + unsigned int, efx_debugfs_read_uint), + EFX_BOOL_PARAMETER(struct efx_nic, promiscuous), + EFX_U64_PARAMETER(struct efx_nic, loopback_modes), EFX_LOOPBACK_MODE_PARAMETER(struct efx_nic, loopback_mode), - EFX_PHY_TYPE_PARAMETER(struct efx_nic, phy_type), - EFX_NAMED_PARAMETER(phy_id, struct efx_nic, mii.phy_id, + EFX_UINT_PARAMETER(struct efx_nic, phy_type), + EFX_STRING_PARAMETER(struct efx_nic, phy_name), + EFX_NAMED_PARAMETER(phy_id, struct efx_nic, mdio.prtad, int, efx_debugfs_read_int), EFX_UINT_PARAMETER(struct efx_nic, n_link_state_changes), {NULL}, @@ -344,7 +401,7 @@ /* Create files */ rc = efx_init_debugfs_files(efx->debug_port_dir, efx_debugfs_port_parameters, - (void *)efx); + efx); if (rc) efx_fini_debugfs_port(efx); @@ -393,7 +450,7 @@ void efx_trim_debugfs_port(struct efx_nic *efx, struct efx_debugfs_parameter *params) { - struct dentry *dir = efx->debug_port_dir; + efx_debugfs_entry *dir = efx->debug_port_dir; if (dir) { struct efx_debugfs_parameter *field; @@ -408,6 +465,9 @@ EFX_UINT_PARAMETER(struct efx_tx_queue, write_count), EFX_UINT_PARAMETER(struct efx_tx_queue, read_count), EFX_INT_PARAMETER(struct efx_tx_queue, stopped), + EFX_UINT_PARAMETER(struct efx_tx_queue, tso_bursts), + EFX_UINT_PARAMETER(struct efx_tx_queue, tso_long_headers), + EFX_UINT_PARAMETER(struct efx_tx_queue, tso_packets), {NULL}, }; @@ -439,7 +499,7 @@ /* Create files */ rc = efx_init_debugfs_files(tx_queue->debug_dir, efx_debugfs_tx_queue_parameters, - (void *)tx_queue); + tx_queue); if (rc) goto err; @@ -527,7 +587,7 @@ /* Create files */ rc = efx_init_debugfs_files(rx_queue->debug_dir, efx_debugfs_rx_queue_parameters, - (void *)rx_queue); + rx_queue); if (rc) goto err; @@ -570,29 +630,21 @@ /* Per-channel parameters */ static struct efx_debugfs_parameter efx_debugfs_channel_parameters[] = { - EFX_INT_PARAMETER(struct efx_channel, enabled), + EFX_BOOL_PARAMETER(struct efx_channel, enabled), EFX_INT_PARAMETER(struct efx_channel, irq), - EFX_UINT_PARAMETER(struct efx_channel, has_interrupt), EFX_UINT_PARAMETER(struct efx_channel, irq_moderation), EFX_UINT_PARAMETER(struct efx_channel, eventq_read_ptr), EFX_UINT_PARAMETER(struct efx_channel, n_rx_tobe_disc), - EFX_UINT_PARAMETER(struct efx_channel, n_rx_ip_frag_err), + EFX_UINT_PARAMETER(struct efx_channel, n_rx_ip_frag), EFX_UINT_PARAMETER(struct efx_channel, n_rx_ip_hdr_chksum_err), EFX_UINT_PARAMETER(struct efx_channel, n_rx_tcp_udp_chksum_err), + EFX_UINT_PARAMETER(struct efx_channel, n_rx_eth_crc_err), + EFX_UINT_PARAMETER(struct efx_channel, n_rx_mcast_mismatch), EFX_UINT_PARAMETER(struct efx_channel, n_rx_frm_trunc), EFX_UINT_PARAMETER(struct efx_channel, n_rx_overlength), EFX_UINT_PARAMETER(struct efx_channel, n_skbuff_leaks), EFX_INT_PARAMETER(struct efx_channel, rx_alloc_level), EFX_INT_PARAMETER(struct efx_channel, rx_alloc_push_pages), - EFX_INT_PARAMETER(struct efx_channel, rx_alloc_pop_pages), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_merges), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_bursts), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_slow_start), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_misorder), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_too_many), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_new_stream), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_drop_idle), - EFX_UINT_PARAMETER(struct efx_channel, ssr.n_drop_closed), {NULL}, }; @@ -622,7 +674,7 @@ /* Create files */ rc = efx_init_debugfs_files(channel->debug_dir, efx_debugfs_channel_parameters, - (void *)channel); + channel); if (rc) goto err; @@ -654,12 +706,13 @@ /* Per-NIC parameters */ static struct efx_debugfs_parameter efx_debugfs_nic_parameters[] = { EFX_INT_PARAMETER(struct efx_nic, legacy_irq), - EFX_INT_PARAMETER(struct efx_nic, rss_queues), + EFX_INT_PARAMETER(struct efx_nic, n_rx_queues), EFX_UINT_PARAMETER(struct efx_nic, rx_buffer_len), EFX_INT_MODE_PARAMETER(struct efx_nic, interrupt_mode), + EFX_UINT_PARAMETER(struct efx_nic, state), {.name = "hardware_desc", .offset = 0, - .reader = falcon_debugfs_read_hardware_desc}, + .reader = efx_nic_debugfs_read_desc}, {NULL}, }; @@ -763,12 +816,12 @@ /* Create files */ rc = efx_init_debugfs_files(efx->debug_dir, - efx_debugfs_nic_parameters, (void *)efx); + efx_debugfs_nic_parameters, efx); if (rc) goto err; rc = efx_init_debugfs_files(efx->errors.debug_dir, efx_debugfs_nic_error_parameters, - (void *)&efx->errors); + &efx->errors); if (rc) goto err; @@ -807,7 +860,11 @@ int efx_init_debugfs(void) { /* Create top-level directory */ - efx_debug_root = proc_mkdir("sfc", proc_root_driver); +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_DEBUGFS) + efx_debug_root = debugfs_create_dir("sfc", NULL); +#else + efx_debug_root = proc_mkdir("driver/sfc", NULL); +#endif if (!efx_debug_root) goto err; @@ -816,6 +873,11 @@ if (!efx_debug_cards) goto err; +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_DEBUGFS) + /* Create compatibility sym-link */ + if (!proc_symlink("driver/sfc", NULL, "/sys/kernel/debug/sfc")) + goto err; +#endif return 0; err: @@ -830,7 +892,9 @@ */ void efx_fini_debugfs(void) { - remove_proc_entry("sfc", proc_root_driver); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_DEBUGFS) + remove_proc_entry("driver/sfc", NULL); +#endif debugfs_remove(efx_debug_cards); efx_debug_cards = NULL; debugfs_remove(efx_debug_root); diff -r aa133dee7792 drivers/net/sfc/debugfs.h --- a/drivers/net/sfc/debugfs.h +++ b/drivers/net/sfc/debugfs.h @@ -1,146 +1,16 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_DEBUGFS_H #define EFX_DEBUGFS_H -#ifdef CONFIG_SFC_DEBUGFS - -struct seq_file; - -struct efx_debugfs_parameter { - const char *name; - size_t offset; - int (*reader)(struct seq_file *, void *); -}; - -extern void efx_fini_debugfs_child(struct dentry *dir, const char *name); -extern int efx_init_debugfs_netdev(struct net_device *net_dev); -extern void efx_fini_debugfs_netdev(struct net_device *net_dev); -extern int efx_init_debugfs_port(struct efx_nic *efx); -extern void efx_fini_debugfs_port(struct efx_nic *efx); -extern int efx_init_debugfs_nic(struct efx_nic *efx); -extern void efx_fini_debugfs_nic(struct efx_nic *efx); -extern int efx_init_debugfs_channels(struct efx_nic *efx); -extern void efx_fini_debugfs_channels(struct efx_nic *efx); -extern int efx_init_debugfs(void); -extern void efx_fini_debugfs(void); -extern int efx_extend_debugfs_port(struct efx_nic *efx, - void *context, - struct efx_debugfs_parameter *params); -extern void efx_trim_debugfs_port(struct efx_nic *efx, - struct efx_debugfs_parameter *params); - -/* Helpers for handling debugfs entry reads */ -extern int efx_debugfs_read_uint(struct seq_file *, void *); -extern int efx_debugfs_read_string(struct seq_file *, void *); -extern int efx_debugfs_read_int(struct seq_file *, void *); -extern int efx_debugfs_read_atomic(struct seq_file *, void *); -extern int efx_debugfs_read_dword(struct seq_file *, void *); - -/* Handy macros for filling out parameters */ - -/* Initialiser for a struct efx_debugfs_parameter with type-checking */ -#define EFX_PARAMETER(container_type, parameter, field_type, \ - reader_function) { \ - .name = #parameter, \ - .offset = ((((field_type *) 0) == \ - &((container_type *) 0)->parameter) ? \ - offsetof(container_type, parameter) : \ - offsetof(container_type, parameter)), \ - .reader = reader_function, \ -} - -/* Likewise, but the file name is not taken from the field name */ -#define EFX_NAMED_PARAMETER(_name, container_type, parameter, field_type, \ - reader_function) { \ - .name = #_name, \ - .offset = ((((field_type *) 0) == \ - &((container_type *) 0)->parameter) ? \ - offsetof(container_type, parameter) : \ - offsetof(container_type, parameter)), \ - .reader = reader_function, \ -} - -/* Likewise, but with one file for each of 4 lanes */ -#define EFX_PER_LANE_PARAMETER(prefix, suffix, container_type, parameter, \ - field_type, reader_function) { \ - .name = prefix "0" suffix, \ - .offset = ((((field_type *) 0) == \ - ((container_type *) 0)->parameter) ? \ - offsetof(container_type, parameter[0]) : \ - offsetof(container_type, parameter[0])), \ - .reader = reader_function, \ -}, { \ - .name = prefix "1" suffix, \ - .offset = offsetof(container_type, parameter[1]), \ - .reader = reader_function, \ -}, { \ - .name = prefix "2" suffix, \ - .offset = offsetof(container_type, parameter[2]), \ - .reader = reader_function, \ -}, { \ - .name = prefix "3" suffix, \ - .offset = offsetof(container_type, parameter[3]), \ - .reader = reader_function, \ -} - -/* A string parameter (string embedded in the structure) */ -#define EFX_STRING_PARAMETER(container_type, parameter) { \ - .name = #parameter, \ - .offset = ((((char *) 0) == \ - ((container_type *) 0)->parameter) ? \ - offsetof(container_type, parameter) : \ - offsetof(container_type, parameter)), \ - .reader = efx_debugfs_read_string, \ -} - -/* An unsigned integer parameter */ -#define EFX_UINT_PARAMETER(container_type, parameter) \ - EFX_PARAMETER(container_type, parameter, \ - unsigned int, efx_debugfs_read_uint) - -/* A dword parameter */ -#define EFX_DWORD_PARAMETER(container_type, parameter) \ - EFX_PARAMETER(container_type, parameter, \ - efx_dword_t, efx_debugfs_read_dword) - -/* An atomic_t parameter */ -#define EFX_ATOMIC_PARAMETER(container_type, parameter) \ - EFX_PARAMETER(container_type, parameter, \ - atomic_t, efx_debugfs_read_atomic) - -/* An integer parameter */ -#define EFX_INT_PARAMETER(container_type, parameter) \ - EFX_PARAMETER(container_type, parameter, \ - int, efx_debugfs_read_int) - -#else /* !CONFIG_SFC_DEBUGFS */ - static inline int efx_init_debugfs_netdev(struct net_device *net_dev) { return 0; @@ -167,6 +37,4 @@ } static inline void efx_fini_debugfs(void) {} -#endif /* CONFIG_SFC_DEBUGFS */ - #endif /* EFX_DEBUGFS_H */ diff -r aa133dee7792 drivers/net/sfc/driverlink.c --- a/drivers/net/sfc/driverlink.c +++ b/drivers/net/sfc/driverlink.c @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005: Fen Systems Ltd. - * Copyright 2005-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005 Fen Systems Ltd. + * Copyright 2005-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include @@ -31,13 +14,10 @@ #include #include "net_driver.h" #include "efx.h" +#include "driverlink_api.h" #include "driverlink.h" -/* Driverlink semaphore - * This semaphore must be held for any operation that modifies any of - * the driverlink lists. - */ -static DEFINE_MUTEX(efx_driverlink_lock); +/* Global lists are protected by rtnl_lock */ /* List of all registered drivers */ static LIST_HEAD(efx_driver_list); @@ -45,28 +25,27 @@ /* List of all registered Efx ports */ static LIST_HEAD(efx_port_list); -/* Driver link handle used internally to track devices */ +/** + * Driver link handle used internally to track devices + * @efx_dev: driverlink device handle exported to consumers + * @efx: efx_nic backing the driverlink device + * @port_node: per-device list head + * @driver_node: per-driver list head + */ struct efx_dl_handle { - /* The efx_dl_device consumers see */ struct efx_dl_device efx_dev; - /* The efx_nic providers provide */ struct efx_nic *efx; - /* Per-device list */ struct list_head port_node; - /* Per-driver list */ struct list_head driver_node; }; -/* Get the handle for an efx_dl_device */ static struct efx_dl_handle *efx_dl_handle(struct efx_dl_device *efx_dev) { return container_of(efx_dev, struct efx_dl_handle, efx_dev); } -/* Remove an Efx device - * You must hold the efx_driverlink_lock before calling this - * function. - */ +/* Remove an Efx device, and call the driver's remove() callback if + * present. The caller must hold rtnl_lock. */ static void efx_dl_del_device(struct efx_dl_device *efx_dev) { struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); @@ -74,23 +53,18 @@ EFX_INFO(efx_handle->efx, "%s driverlink client unregistering\n", efx_dev->driver->name); - /* Call driver's remove() routine */ if (efx_dev->driver->remove) efx_dev->driver->remove(efx_dev); - /* Remove handle from per-driver and per-NIC lists */ list_del(&efx_handle->driver_node); list_del(&efx_handle->port_node); - /* Free efx_handle structure */ kfree(efx_handle); } -/* Try to add an Efx device - * Attempt to probe the given device with the driver, creating a - * new efx_dl_device. If the probe routine fails, because the driver - * doesn't support this port, then the efx_dl_device is destroyed, - */ +/* Attempt to probe the given device with the driver, creating a + * new &struct efx_dl_device. If the probe routine returns an error, + * then the &struct efx_dl_device is destroyed */ static void efx_dl_try_add_device(struct efx_nic *efx, struct efx_dl_driver *driver) { @@ -98,8 +72,9 @@ struct efx_dl_device *efx_dev; int rc; - /* Allocate and initialise new efx_dl_device structure */ efx_handle = kzalloc(sizeof(*efx_handle), GFP_KERNEL); + if (!efx_handle) + goto fail; efx_dev = &efx_handle->efx_dev; efx_handle->efx = efx; efx_dev->driver = driver; @@ -107,13 +82,11 @@ INIT_LIST_HEAD(&efx_handle->port_node); INIT_LIST_HEAD(&efx_handle->driver_node); - /* Attempt driver probe */ rc = driver->probe(efx_dev, efx->net_dev, - efx->dl_info, efx->silicon_rev); + efx->dl_info, efx->type->dl_revision); if (rc) goto fail; - /* Add device to per-driver and per-NIC lists */ list_add_tail(&efx_handle->driver_node, &driver->device_list); list_add_tail(&efx_handle->port_node, &efx->dl_device_list); @@ -123,16 +96,11 @@ fail: EFX_INFO(efx, "%s driverlink client skipped\n", driver->name); - kfree(efx_dev); + kfree(efx_handle); } -/** - * 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. - */ +/* Unregister a driver from the driverlink layer, calling the + * driver's remove() callback for every attached device */ void efx_dl_unregister_driver(struct efx_dl_driver *driver) { struct efx_dl_handle *efx_handle, *efx_handle_n; @@ -140,8 +108,7 @@ printk(KERN_INFO "Efx driverlink unregistering %s driver\n", driver->name); - /* Acquire lock. We can't return failure */ - mutex_lock(&efx_driverlink_lock); + rtnl_lock(); list_for_each_entry_safe(efx_handle, efx_handle_n, &driver->device_list, driver_node) @@ -149,46 +116,29 @@ list_del(&driver->node); - mutex_unlock(&efx_driverlink_lock); + rtnl_unlock(); } EXPORT_SYMBOL(efx_dl_unregister_driver); -/** - * efx_dl_register_driver - register an Efx device driver - * @driver: Efx driverlink driver - * - * Registers a new Efx driver. The driver's probe() method will be - * called for all Efx NICs currently registered. - * - * Return a negative error code or 0 on success. - */ +/* Register a new driver with the driverlink layer. The driver's + * probe routine will be called for every attached nic. */ int efx_dl_register_driver(struct efx_dl_driver *driver) { struct efx_nic *efx; - int rc; printk(KERN_INFO "Efx driverlink registering %s driver\n", driver->name); - /* Initialise driver list structures */ INIT_LIST_HEAD(&driver->node); INIT_LIST_HEAD(&driver->device_list); - /* Acquire lock */ - rc = mutex_lock_interruptible(&efx_driverlink_lock); - if (rc) - return rc; + rtnl_lock(); - /* 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); - + rtnl_unlock(); return 0; } EXPORT_SYMBOL(efx_dl_register_driver); @@ -197,85 +147,60 @@ { struct efx_dl_handle *efx_handle, *efx_handle_n; - if (!efx) - return; + ASSERT_RTNL(); - /* 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) +void efx_dl_register_nic(struct efx_nic *efx) { struct efx_dl_driver *driver; - int rc; - /* Acquire lock */ - rc = mutex_lock_interruptible(&efx_driverlink_lock); - if (rc) - return rc; + ASSERT_RTNL(); - /* 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); +/* 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 +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +fastcall +#endif +efx_dummy_tx_packet_callback(struct efx_dl_device *efx_dev, + struct sk_buff *skb) +{ + return EFX_ALLOW_PACKET; +} + +static enum efx_veto +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +fastcall +#endif +efx_dummy_rx_packet_callback(struct efx_dl_device *efx_dev, + const char *pkt_buf, int len) +{ + return EFX_ALLOW_PACKET; +} + +static int efx_dummy_request_mtu_callback(struct efx_dl_device *efx_dev, + int new_mtu) +{ 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 fastcall -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 fastcall -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) +static void efx_dummy_mtu_changed_callback(struct efx_dl_device *efx_dev, + int mtu) { return; } @@ -288,106 +213,54 @@ struct efx_dl_callbacks efx_default_callbacks = { .tx_packet = efx_dummy_tx_packet_callback, .rx_packet = efx_dummy_rx_packet_callback, - .link_change = efx_dummy_link_change_callback, .request_mtu = efx_dummy_request_mtu_callback, .mtu_changed = efx_dummy_mtu_changed_callback, .event = efx_dummy_event_callback, }; -#define EFX_DL_UNREGISTER_CALLBACK(_port, _dev, _member) \ - do { \ - BUG_ON((_port)->dl_cb_dev._member != (_dev)); \ - (_port)->dl_cb._member = \ - efx_default_callbacks._member; \ - (_port)->dl_cb_dev._member = NULL; \ - } while (0) - - -#define EFX_DL_REGISTER_CALLBACK(_port, _dev, _from, _member) \ - if ((_from)->_member) { \ - BUG_ON((_port)->dl_cb_dev._member != NULL); \ - (_port)->dl_cb._member = (_from)->_member; \ - (_port)->dl_cb_dev._member = _dev; \ - } - -/** - * efx_dl_unregister_callbacks - unregister callbacks for an Efx NIC - * @efx_dev: Efx driverlink device - * @callbacks: Callback list - * - * This removes a set of callbacks registered with - * efx_dl_register_callbacks(). It should be called as part of the - * client's remove() method. - * - * The net driver will ensure that all callback functions have - * returned to the net driver before efx_dl_unregister_callbacks() - * returns. Note that the device itself may still be running when the - * client's remove() method is called. The client must therefore - * unhook its callbacks using efx_dl_unregister_callbacks() and only - * then ensure that any delayed tasks triggered by callback methods - * (e.g. scheduled tasklets) have completed. - */ void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev, struct efx_dl_callbacks *callbacks) { struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); struct efx_nic *efx = efx_handle->efx; - /* Suspend net driver operations */ - efx_suspend(efx); + ASSERT_RTNL(); + + efx_stop_all(efx); EFX_INFO(efx, "removing callback hooks into %s driver\n", efx_dev->driver->name); - if (callbacks->tx_packet) - EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, tx_packet); + if (callbacks->tx_packet) { + BUG_ON(efx->dl_cb_dev.tx_packet != efx_dev); + efx->dl_cb.tx_packet = efx_default_callbacks.tx_packet; + efx->dl_cb_dev.tx_packet = NULL; + } + if (callbacks->rx_packet) { + BUG_ON(efx->dl_cb_dev.rx_packet != efx_dev); + efx->dl_cb.rx_packet = efx_default_callbacks.rx_packet; + efx->dl_cb_dev.rx_packet = NULL; + } + if (callbacks->request_mtu) { + BUG_ON(efx->dl_cb_dev.request_mtu != efx_dev); + efx->dl_cb.request_mtu = efx_default_callbacks.request_mtu; + efx->dl_cb_dev.request_mtu = NULL; + } + if (callbacks->mtu_changed) { + BUG_ON(efx->dl_cb_dev.mtu_changed != efx_dev); + efx->dl_cb.mtu_changed = efx_default_callbacks.mtu_changed; + efx->dl_cb_dev.mtu_changed = NULL; + } + if (callbacks->event) { + BUG_ON(efx->dl_cb_dev.event != efx_dev); + efx->dl_cb.event = efx_default_callbacks.event; + efx->dl_cb_dev.event = NULL; + } - 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); + efx_start_all(efx); } EXPORT_SYMBOL(efx_dl_unregister_callbacks); -/** - * efx_dl_register_callbacks - register callbacks for an Efx NIC - * @efx_dev: Efx driverlink device - * @callbacks: Callback list - * - * This registers a set of callback functions with the net driver. - * These functions will be called at various key points to allow - * external code to monitor and/or modify the behaviour of the network - * driver. Any of the callback function pointers may be %NULL if a - * callback is not required. The intended user of this mechanism is - * the SFC char driver. - * - * This client should call efx_dl_register_callbacks() during its - * probe() method. The client must ensure that it also calls - * efx_dl_unregister_callbacks() as part of its remove() method. - * - * Only one function may be registered for each callback per NIC. - * If a requested callback is already registered for this NIC, this - * function will return -%EBUSY. - * - * The device may already be running, so the client must be prepared - * for callbacks to be triggered immediately after calling - * efx_dl_register_callbacks(). - * - * Return a negative error code or 0 on success. - */ int efx_dl_register_callbacks(struct efx_dl_device *efx_dev, struct efx_dl_callbacks *callbacks) { @@ -395,13 +268,13 @@ struct efx_nic *efx = efx_handle->efx; int rc = 0; - /* Suspend net driver operations */ - efx_suspend(efx); + ASSERT_RTNL(); + + efx_stop_all(efx); /* Check that the requested callbacks are not already hooked. */ if ((callbacks->tx_packet && efx->dl_cb_dev.tx_packet) || (callbacks->rx_packet && efx->dl_cb_dev.rx_packet) || - (callbacks->link_change && efx->dl_cb_dev.link_change) || (callbacks->request_mtu && efx->dl_cb_dev.request_mtu) || (callbacks->mtu_changed && efx->dl_cb_dev.mtu_changed) || (callbacks->event && efx->dl_cb_dev.event)) { @@ -412,35 +285,36 @@ EFX_INFO(efx, "adding callback hooks to %s driver\n", efx_dev->driver->name); - /* Hook in callbacks. For maximum speed, we never check to - * see whether these are NULL before calling; therefore we - * must ensure that they are never NULL. If the set we're - * being asked to hook in is sparse, we leave the default - * values in place for the empty hooks. - */ - EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, tx_packet); - EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, rx_packet); - EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, link_change); - EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, request_mtu); - EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, mtu_changed); - EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, event); + /* Hook in the requested callbacks, leaving any NULL members + * referencing the members of @efx_default_callbacks */ + if (callbacks->tx_packet) { + efx->dl_cb.tx_packet = callbacks->tx_packet; + efx->dl_cb_dev.tx_packet = efx_dev; + } + if (callbacks->rx_packet) { + efx->dl_cb.rx_packet = callbacks->rx_packet; + efx->dl_cb_dev.rx_packet = efx_dev; + } + if (callbacks->request_mtu) { + efx->dl_cb.request_mtu = callbacks->request_mtu; + efx->dl_cb_dev.request_mtu = efx_dev; + } + if (callbacks->mtu_changed) { + efx->dl_cb.mtu_changed = callbacks->mtu_changed; + efx->dl_cb_dev.mtu_changed = efx_dev; + } + if (callbacks->event) { + efx->dl_cb.event = callbacks->event; + efx->dl_cb_dev.event = efx_dev; + } out: - /* Resume net driver operations */ - efx_resume(efx); + efx_start_all(efx); return rc; } EXPORT_SYMBOL(efx_dl_register_callbacks); -/** - * efx_dl_schedule_reset - schedule an Efx NIC reset - * @efx_dev: Efx driverlink device - * - * This schedules a hardware reset for a short time in the future. It - * can be called from any context, and so can be used when - * efx_dl_reset() cannot be called. - */ void efx_dl_schedule_reset(struct efx_dl_device *efx_dev) { struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev); @@ -450,41 +324,15 @@ } EXPORT_SYMBOL(efx_dl_schedule_reset); -/* - * Lock the driverlink layer before a reset - * To avoid deadlock, efx_driverlink_lock needs to be acquired before - * efx->suspend_lock. - */ -void efx_dl_reset_lock(void) -{ - /* Acquire lock */ - mutex_lock(&efx_driverlink_lock); -} - -/* - * Unlock the driverlink layer after a reset - * This call must be matched against efx_dl_reset_lock. - */ -void efx_dl_reset_unlock(void) -{ - /* Acquire lock */ - mutex_unlock(&efx_driverlink_lock); -} - -/* - * Suspend ready for reset - * This calls the reset_suspend method of all drivers registered to - * the specified NIC. It must only be called between - * efx_dl_reset_lock and efx_dl_reset_unlock. - */ +/* Suspend ready for reset, calling the reset_suspend() callback of every + * registered driver */ void efx_dl_reset_suspend(struct efx_nic *efx) { struct efx_dl_handle *efx_handle; struct efx_dl_device *efx_dev; - BUG_ON(!mutex_is_locked(&efx_driverlink_lock)); + ASSERT_RTNL(); - /* Call suspend method of each driver in turn */ list_for_each_entry_reverse(efx_handle, &efx->dl_device_list, port_node) { @@ -494,20 +342,15 @@ } } -/* - * Resume after a reset - * This calls the reset_resume method of all drivers registered to the - * specified NIC. It must only be called between efx_dl_reset_lock - * and efx_dl_reset_unlock. - */ +/* Resume after a reset, calling the resume() callback of every registered + * driver */ void efx_dl_reset_resume(struct efx_nic *efx, int ok) { struct efx_dl_handle *efx_handle; struct efx_dl_device *efx_dev; - BUG_ON(!mutex_is_locked(&efx_driverlink_lock)); + ASSERT_RTNL(); - /* 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; @@ -516,14 +359,9 @@ } } -/** - * efx_dl_get_nic - obtain the Efx NIC for the given driverlink device - * @efx_dev: Efx driverlink device - * - * Get a pointer to the &struct efx_nic corresponding to - * @efx_dev. This can be used by driverlink clients built along with - * the sfc driver, which may have intimate knowledge of its internals. - */ +/* Obtain a pointer to the &struct efx_nic corresponding to @efx_dev. This + * can be used by driverlink clients built along with the sfc driver, which + * may have initimate knowledge of its internals. */ struct efx_nic *efx_dl_get_nic(struct efx_dl_device *efx_dev) { return efx_dl_handle(efx_dev)->efx; diff -r aa133dee7792 drivers/net/sfc/driverlink.h --- a/drivers/net/sfc/driverlink.h +++ b/drivers/net/sfc/driverlink.h @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005: Fen Systems Ltd. - * Copyright 2006: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_DRIVERLINK_H @@ -32,59 +15,29 @@ struct efx_dl_device; struct efx_nic; -/* - * Efx driverlink - * - * This header file defines the portions of the Efx driverlink - * interface that are used only within the sfc module. It also - * declares efx_dl_get_nic(), which may be used by sfc_mtd - * and any other module built along with sfc. - */ - - /* Efx callback devices * * A list of the devices that own each callback. The partner to - * struct efx_dl_callbacks + * struct efx_dl_callbacks. */ struct efx_dl_cb_devices { - /* Device owning the tx_packet callback */ struct efx_dl_device *tx_packet; - /* Device owning the rx_packet callback */ struct efx_dl_device *rx_packet; - /* Device owning the link_change callback. */ - struct efx_dl_device *link_change; - /* Device owning the request_mtu callback. */ struct efx_dl_device *request_mtu; - /* Device owning the mtu_changed callback. */ struct efx_dl_device *mtu_changed; - /* Device owning the event callback. */ struct efx_dl_device *event; }; -/* No-op callbacks used for initialisation */ extern struct efx_dl_callbacks efx_default_callbacks; -/* Macro used to invoke callbacks */ #define EFX_DL_CALLBACK(_port, _name, ...) \ (_port)->dl_cb._name((_port)->dl_cb_dev._name, __VA_ARGS__) -/* Register an Efx NIC */ -extern int efx_dl_register_nic(struct efx_nic *efx); - -/* Unregister an Efx NIC */ +extern void efx_dl_register_nic(struct efx_nic *efx); extern void efx_dl_unregister_nic(struct efx_nic *efx); -/* Lock the driverlink layer prior to a reset */ -extern void efx_dl_reset_lock(void); - -/* Unlock the driverlink layer following a reset */ -extern void efx_dl_reset_unlock(void); - -/* Suspend all drivers prior to a hardware reset */ +/* Suspend and resume client drivers over a hardware reset */ extern void efx_dl_reset_suspend(struct efx_nic *efx); - -/* Resume all drivers after a hardware reset */ extern void efx_dl_reset_resume(struct efx_nic *efx, int ok); /* Obtain the Efx NIC for the given driverlink device. */ diff -r aa133dee7792 drivers/net/sfc/driverlink_api.h --- a/drivers/net/sfc/driverlink_api.h +++ b/drivers/net/sfc/driverlink_api.h @@ -1,82 +1,24 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2005-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2008 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_DRIVERLINK_API_H #define EFX_DRIVERLINK_API_H -#include /* for struct list_head */ -#include -#define EFX_USE_FASTCALL yes - -/** - * DOC: Efx driverlink API - * - * This file must be included by any driver that wishes to attach to - * devices claimed by the Solarflare NIC driver (sfc). It allows separate - * kernel modules to expose other functionality offered by the NIC, with - * the sfc driver remaining in overall control. - * - * Overview: - * - * Driverlink clients define a &struct efx_dl_driver, and register - * this structure with the driverlink layer using - * efx_dl_register_driver(), which is exported by the sfc driver. - * - * The probe() routine of each driverlink client driver is called by - * the driverlink layer for each physical port in the system, after - * the sfc driver has performed start-of-day hardware initialisation - * and self-test. If ports are added or removed via pci hotplug then - * the &struct efx_dl_driver probe() or remove() routines are called - * as appropriate. - * - * If the port doesn't provide the necessary hardware resources for a - * client, then that client can return failure from its probe() - * routine. Information provided to the client driver at probe time - * includes - * - * Each probe() routine is given a unique &struct efx_dl_device per - * port, which means it can safely use the @priv member to store any - * useful state it needs. The probe routine also has the opportunity - * to provide a &struct efx_dl_callbacks via - * efx_dl_register_callbacks(), which allows the client to intercept - * the sfc driver's operations at strategic points. - * - * Occasionally, the underlying Efx device may need to be reset to - * recover from an error condition. The client's reset_suspend() and - * reset_resume() methods [if provided] will be called to enable the - * client to suspend operations and preserve any state before the - * reset. The client can itself request a reset using efx_dl_reset() - * or efx_dl_schedule_reset(), should it detect an error condition - * necessitating a reset. - * - * Example: - * - * The MTD driver (mtd.c) uses the driverlink layer. - */ +#include +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_FASTCALL) + #include + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + #define EFX_USE_FASTCALL yes + #include + #endif +#endif /* Forward declarations */ struct pci_dev; @@ -85,160 +27,45 @@ struct efx_dl_device; struct efx_dl_device_info; -/* - * This is used to guard against the registration of driverlink - * clients using an incorrect version of the API. - */ +/* An extra safeguard in addition to symbol versioning */ #define EFX_DRIVERLINK_API_VERSION 1 - /** * struct efx_dl_driver - An Efx driverlink device driver * - * This is the analogue of a struct pci_driver for a normal PCI - * driver. Driverlink clients should register themselves using - * efx_dl_register_driver() at module initialisation, and deregister - * themselves using efx_dl_unregister_driver() at module exit. - * - * All calls to members of efx_dl_driver are serialised by a single - * semaphore, so you are allowed to sleep in these functions. Take care - * to not call driverlink methods from within these callbacks, otherwise - * a deadlock is possible. + * A driverlink client defines and initializes as many instances of + * efx_dl_driver as required, registering each one with + * efx_dl_register_driver(). * * @name: Name of the driver * @probe: Called when device added + * The client should use the @def_info linked list and @silicon_rev + * to determine if they wish to attach to this device. + * Context: process, rtnl_lock or driverlink mutex held * @remove: Called when device removed + * The client must ensure the finish all operations with this + * device before returning from this method. + * Context: process, rtnl_lock or driverlink mutex held * @reset_suspend: Called before device is reset + * Called immediately before a hardware reset. The client must stop all + * hardware processing before returning from this method. Callbacks will + * be inactive when this method is called. + * Context: process, rtnl_lock and/or driverlink mutex held * @reset_resume: Called after device is reset + * Called after a hardware reset. If @ok is true, the client should + * state and resume normal operations. If @ok is false, the client should + * abandon use of the hardware resources. remove() will still be called. + * Context: process, rtnl_lock and/or driverlink mutex held */ struct efx_dl_driver { const char *name; - /* - * probe - Handle device addition. - * @efx_dev: Efx driverlink device - * @net_dev: The net_dev relevant to this port - * @dev_info: A linked list of device information. - * @silicon_rev: Silicon revision name. - * - * This will be called after driverlink client registration for - * every port on the system, and for every port that appears - * thereafter via hotplug. - * - * The client may use either @efx_dev->pci_dev, the dev_info linked - * list of available driver information, or the silicon revision - * name to determine if they can support this port. If they can, - * they should return 0 to indicate the probe was successful. Any - * other return code indicates that the probe failed, and the - * @efx_dl_dev will be invalidated. - * - * The client should perform whatever initialisation it - * requires, and store a pointer to its private data in - * @efx_dl_dev->priv (which is not shared between clients). - * It may also wish to hook in a callbacks table using - * efx_dl_register_callbacks(). - * - * Return a negative error code or 0 on success. - */ int (*probe) (struct efx_dl_device *efx_dl_dev, const struct net_device *net_dev, const struct efx_dl_device_info *dev_info, const char *silicon_rev); - - /* - * remove - Handle device removal. - * @efx_dev: Efx driverlink device - * - * This will be called at driver exit (or hotplug removal) for - * each registered driverlink client. - * - * The client must ensure that it has finished all operations - * using this device before returning from this method. If it - * has hooked in a callbacks table using - * efx_dl_register_callbacks(), it must unhook it using - * efx_dl_unregister_callbacks(), and then ensure that all - * callback-triggered operations (e.g. scheduled tasklets) - * have completed before returning. (It does not need to - * explicitly wait for callback methods to finish executing, - * since efx_dl_unregister_callbacks() will sleep until all - * callbacks have returned anyway.) - * - * Note that the device itself may not have been removed; it - * may be simply that the client is being unloaded - * via efx_dl_unregister_driver(). In this case other clients - * (and the sfc driver itself) will still be using the device, - * so the client cannot assume that the device itself is quiescent. - * In particular, callbacks may continue to be triggered at any - * point until efx_dl_unregister_callbacks() is called. - */ void (*remove) (struct efx_dl_device *efx_dev); - - /* - * reset_suspend - Suspend ready for reset. - * @efx_dev: Efx driverlink device - * - * This method will be called immediately before a hardware - * reset (which may or may not have been initiated by the - * driverlink client). This client must save any state that it - * will need to restore after the reset, and suspend all - * operations that might access the hardware. It must not - * return until the client can guarantee to have stopped - * touching the hardware. - * - * It is guaranteed that callbacks will be inactive by the - * time this method is called; the driverlink layer will - * already have prevented new callbacks being made and waited - * for all callbacks functions to return before calling - * reset_suspend(). However, any delayed work scheduled by - * the callback functions (e.g. tasklets) may not yet have - * completed. - * - * This method is allowed to sleep, so waiting on tasklets, - * work queues etc. is permitted. There will always be a - * corresponding call to the reset_resume() method, so it is - * safe to e.g. down a semaphore within reset_suspend() and up - * it within reset_resume(). (However, you obviously cannot - * do the same with a spinlock). - * - * Note that the reset operation may be being carried out in - * the context of scheduled work, so you cannot use - * flush_scheduled_work() to ensure that any work you may have - * scheduled has completed. - * - * During hardware reset, there is a chance of receiving - * spurious interrupts, so the client's ISR (if any) should be - * unhooked or otherwise disabled. - */ void (*reset_suspend) (struct efx_dl_device *efx_dev); - - /* - * reset_resume - Restore after a reset. - * @efx_dev: Efx driverlink device - * @ok: Reset success indicator - * - * This method will be called after a hardware reset. There - * will always have been a corresponding call to the - * reset_suspend() method beforehand. - * - * If @ok is non-zero, the client should restore the state - * that it saved during the call to reset_suspend() and resume - * normal operations. - * - * If @ok is zero, the reset operation has failed and the - * hardware is currently in an unusable state. In this case, - * the client should release any locks taken out by - * reset_suspend(), but should not take any other action; in - * particular, it must not access the hardware, nor resume - * normal operations. The hardware is effectively dead at - * this point, and our sole aim is to avoid deadlocking or - * crashing the host. - * - * The driverlink layer will still be locked when - * reset_resume() is called, so the client may not call - * driverlink functions. In particular, if the reset failed, - * the client must not call efx_dl_unregister_callbacks() at - * this point; it should wait until remove() is called. - */ void (*reset_resume) (struct efx_dl_device *efx_dev, int ok); /* private: */ @@ -247,23 +74,10 @@ }; /** - * DOC: Efx driverlink device information - * - * Each &struct efx_dl_device makes certain hardware resources visible - * to driverlink clients, and they describe which resources are - * available by passing a linked list of &struct efx_dl_device_info - * into the probe() routine. - * - * The driverlink client's probe function can iterate through the linked list, - * and provided that it understands the resources that are exported, it can - * choose to make use of them through an external interface. - */ - -/** * enum efx_dl_device_info_type - Device information identifier. * - * Each distinct hardware resource API will have a member in this - * enumeration. + * Used to identify each item in the &struct efx_dl_device_info linked list + * provided to each driverlink client in the probe() @dev_info member. * * @EFX_DL_FALCON_RESOURCES: Information type is &struct efx_dl_falcon_resources */ @@ -274,14 +88,9 @@ /** * struct efx_dl_device_info - device information structure + * * @next: Link to next structure, if any * @type: Type code for this structure - * - * This structure is embedded in other structures provided by the - * driverlink device provider, and implements a linked list of - * resources pertinent to a driverlink client. - * - * Example: &struct efx_dl_falcon_resources */ struct efx_dl_device_info { struct efx_dl_device_info *next; @@ -291,18 +100,16 @@ /** * enum efx_dl_falcon_resource_flags - Falcon resource information flags. * - * Flags that describe hardware variations for the described Falcon based port. + * Flags that describe hardware variations for the current Falcon device. * * @EFX_DL_FALCON_DUAL_FUNC: Port is dual-function. * Certain silicon revisions have two pci functions, and require * certain hardware resources to be accessed via the secondary - * function. See the discussion of @pci_dev in &struct efx_dl_device - * below. + * function * @EFX_DL_FALCON_USE_MSI: Port is initialised to use MSI/MSI-X interrupts. * Falcon supports traditional legacy interrupts and MSI/MSI-X - * interrupts. Since the sfc driver supports either, as a run - * time configuration, driverlink drivers need to be aware of which - * one to use for their interrupting resources. + * interrupts. The choice is made at run time by the sfc driver, and + * notified to the clients by this enumeration */ enum efx_dl_falcon_resource_flags { EFX_DL_FALCON_DUAL_FUNC = 0x1, @@ -336,20 +143,23 @@ struct efx_dl_falcon_resources { struct efx_dl_device_info hdr; spinlock_t *biu_lock; - unsigned buffer_table_min, buffer_table_lim; - unsigned evq_timer_min, evq_timer_lim; - unsigned evq_int_min, evq_int_lim; - unsigned rxq_min, rxq_lim; - unsigned txq_min, txq_lim; + unsigned buffer_table_min; + unsigned buffer_table_lim; + unsigned evq_timer_min; + unsigned evq_timer_lim; + unsigned evq_int_min; + unsigned evq_int_lim; + unsigned rxq_min; + unsigned rxq_lim; + unsigned txq_min; + unsigned txq_lim; enum efx_dl_falcon_resource_flags flags; }; /** * struct efx_dl_device - An Efx driverlink device. * - * @pci_dev: Underlying PCI device. - * This is the PCI device used by the sfc driver. It will - * already have been enabled for bus-mastering DMA etc. + * @pci_dev: PCI device used by the sfc driver. * @priv: Driver private data * Driverlink clients can use this to store a pointer to their * internal per-device data structure. Each (driver, device) @@ -380,139 +190,57 @@ /** * struct efx_dl_callbacks - Efx callbacks * - * These methods can be hooked in to the sfc driver via + * These methods can be hooked into the sfc driver via * efx_dl_register_callbacks(). They allow clients to intercept and/or * modify the behaviour of the sfc driver at predetermined points. * - * For efficiency, only one client can hook each callback. - * - * Since these callbacks are called on packet transmit and reception - * paths, clients should avoid acquiring locks or allocating memory. + * For efficiency, only one client can hook each callback. Since these + * callbacks are called on packet transmit and reception paths, and the + * sfc driver may have multiple tx and rx queues per port, clients should + * avoid acquiring locks or allocating memory. * * @tx_packet: Called when packet is about to be transmitted + * Called for every packet about to be transmitted, providing means + * for the client to snoop traffic, and veto transmission by returning + * %EFX_VETO_PACKET (the sfc driver will subsequently free the skb). + * Context: tasklet, netif_tx_lock held * @rx_packet: Called when packet is received - * @link_change: Called when link status has changed - * @request_mtu: Called to request MTU change - * @mtu_changed: Called when MTU has been changed - * @event: Called when NIC event is not handled by the sfc driver + * Called for every received packet (after LRO), allowing the client + * to snoop every received packet (on every rx queue), and veto + * reception by returning %EFX_VETO_PACKET. + * Context: tasklet + * @link_change: Never used + * @request_mtu: Called to request MTU change. + * Called whenever the user requests the net_dev mtu to be changed. + * If the client returns an error, the mtu change is aborted. The sfc + * driver guarantees that no other callbacks are running. + * Context: process, rtnl_lock held. + * @mtu_changed: Called when MTU has been changed. + * Called after the mtu has been successfully changed, always after + * a previous call to request_mtu(). The sfc driver guarantees that no + * other callbacks are running. + * Context: process, rtnl_lock held. + * @event: Called when a hardware NIC event is not understood by the sfc driver. + * Context: tasklet. */ struct efx_dl_callbacks { - /* - * tx_packet - Packet about to be transmitted. - * @efx_dev: Efx driverlink device - * @skb: Socket buffer containing the packet to be sent - * - * This method is called for every packet about to be - * transmitted. It allows the client to snoop on traffic sent - * via the kernel queues. - * - * The method may return %EFX_VETO_PACKET in order to prevent - * the sfc driver from transmitting the packet. The net - * driver will then discard the packet. If the client wishes - * to retain a reference to the packet data after returning - * %EFX_VETO_PACKET, it must obtain its own copy of the - * packet (e.g. by calling skb_get(), or by copying out the - * packet data to an external buffer). - * - * This method must return quickly, since it will have a - * direct performance impact upon the sfc driver. It will be - * called with interrupts disabled (and may be called in - * interrupt context), so may not sleep. Since the sfc driver - * may have multiple TX queues, running in parallel, please avoid - * the need for locking if it all possible. - */ +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) enum efx_veto fastcall (*tx_packet) (struct efx_dl_device *efx_dev, struct sk_buff *skb); - - /* - * rx_packet - Packet received. - * @efx_dev: Efx driverlink device - * @pkt_hdr: Pointer to received packet - * @pkt_len: Length of received packet - * - * This method is called for every received packet. It allows - * the client to snoop on traffic received by the kernel - * queues. - * - * The method may return %EFX_VETO_PACKET in order to prevent - * the sfc driver from passing the packet to the kernel. The net - * driver will then discard the packet. - * - * This method must return quickly, since it will have a - * direct performance impact upon the sfc driver. It is - * called in tasklet context, so may not sleep. Note that - * there are per-channel tasklets in the sfc driver, so - * rx_packet() may be called simultaneously on different CPUs - * and must lock appropriately. The design of the sfc driver - * allows for lockless operation between receive channels, so - * please avoid the need for locking if at all possible. - */ +#else + enum efx_veto (*tx_packet) (struct efx_dl_device *efx_dev, + struct sk_buff *skb); +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) enum efx_veto fastcall (*rx_packet) (struct efx_dl_device *efx_dev, const char *pkt_hdr, int pkt_len); - - /* - * link_change - Link status change. - * @efx_dev: Efx driverlink device - * @link_up: Link up indicator - * - * This method is called to inform the driverlink client - * whenever the PHY link status changes. By the time this - * function is called, the MAC has already been reconfigured - * with the new autonegotiation settings from the PHY. - * - * This method is called from tasklet context and may not - * sleep. - */ - void (*link_change) (struct efx_dl_device *efx_dev, int link_up); - - /* - * request_mtu: Request MTU change. - * @efx_dev: Efx driverlink device - * @new_mtu: Requested new MTU - * - * This method is called whenever the user requests an MTU - * change on an interface. The client may return an error, in - * which case the MTU change request will be denied. If the - * client returns success, the MAC will be reconfigured with a - * new maxmimum frame length equal to - * EFX_MAX_FRAME_LEN(new_mtu). The client will be notified - * via the mtu_changed() method once the MAC has been - * reconfigured. - * - * The current MTU for the port can be obtained via - * efx_dl_get_netdev(efx_dl_device)->mtu. - * - * The sfc driver guarantees that no other callback functions - * are in progress when this method is called. This function - * is called in process context and may sleep. - * - * Return a negative error code or 0 on success. - */ +#else + enum efx_veto (*rx_packet) (struct efx_dl_device *efx_dev, + const char *pkt_hdr, int pkt_len); +#endif + void (*link_change) (struct efx_dl_device *, int); int (*request_mtu) (struct efx_dl_device *efx_dev, int new_mtu); - - /* - * mtu_changed - MTU has been changed. - * @efx_dev: Efx driverlink device - * @mtu: The new MTU - * - * This method is called once the MAC has been reconfigured - * with a new MTU. There will have been a preceding call to - * request_mtu(). - * - * The sfc driver guarantees that no other callback functions - * are in progress when this method is called. This function - * is called in process context and may sleep. - */ void (*mtu_changed) (struct efx_dl_device *efx_dev, int mtu); - - /* - * event - Event callback. - * @efx_dev: Efx driverlink device - * @p_event: Pointer to event - * - * This method is called for each event that is not handled by the - * sfc driver. - */ void (*event) (struct efx_dl_device *efx_dev, void *p_event); }; @@ -523,16 +251,47 @@ efx_dl_stringify_2(efx_dl_register_driver_api_ver_, \ EFX_DRIVERLINK_API_VERSION) +/** + * efx_dl_register_driver() - Register a client driver + * @driver: Driver operations structure + * + * This acquires the rtnl_lock and therefore must be called from + * process context. + */ extern int efx_dl_register_driver(struct efx_dl_driver *driver); +/** + * efx_dl_unregister_driver() - Unregister a client driver + * @driver: Driver operations structure + * + * This acquires the rtnl_lock and therefore must be called from + * process context. + */ extern void efx_dl_unregister_driver(struct efx_dl_driver *driver); +/** + * efx_dl_register_callbacks() - Set callback functions for a device + * @efx_dev: Device to be affected + * @callbacks: Callback functions structure + * + * This must be called in process context from the driver's probe() + * operation. + */ extern int efx_dl_register_callbacks(struct efx_dl_device *efx_dev, struct efx_dl_callbacks *callbacks); +/** + * efx_dl_unregister_callbacks() - Unset callback functions for a device + * @efx_dev: Device to be affected + * @callbacks: Callback functions structure + * + * This must be called in process context from the driver's remove() + * operation. + */ extern void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev, struct efx_dl_callbacks *callbacks); +/* Schedule a reset without grabbing any locks */ extern void efx_dl_schedule_reset(struct efx_dl_device *efx_dev); /** @@ -544,19 +303,13 @@ * @_p: Iterator variable * * Example: - * - * static int driver_dl_probe(... const struct efx_dl_device_info *dev_info ...) - * { - * struct efx_dl_falcon_resources *res; - * - * efx_dl_for_each_device_info_matching(dev_info,EFX_DL_FALCON_RESOURCES, - * struct efx_dl_falcon_resources, - * hdr, res) { - * if (res->flags & EFX_DL_FALCON_DUAL_FUNC) { - * ..... - * } - * } - * } + * struct efx_dl_falcon_resources *res; + * efx_dl_for_each_device_info_matching(dev_info, EFX_DL_FALCON_RESOURCES, + * struct efx_dl_falcon_resources, + * hdr, res) { + * if (res->flags & EFX_DL_FALCON_DUAL_FUNC) + * .... + * } */ #define efx_dl_for_each_device_info_matching(_dev_info, _type, \ _info_type, _field, _p) \ @@ -576,17 +329,11 @@ * @_p: Result variable * * Example: - * - * static int driver_dl_probe(... const struct efx_dl_device_info *dev_info ...) - * { - * struct efx_dl_falcon_resources *res; - * - * efx_dl_search_device_info(dev_info, EFX_DL_FALCON_RESOURCES, - * struct efx_dl_falcon_resources, hdr, res); - * if (res != NULL) { - * .... - * } - * } + * struct efx_dl_falcon_resources *res; + * efx_dl_search_device_info(dev_info, EFX_DL_FALCON_RESOURCES, + * struct efx_dl_falcon_resources, hdr, res); + * if (res) + * .... */ #define efx_dl_search_device_info(_dev_info, _type, _info_type, \ _field, _p) \ diff -r aa133dee7792 drivers/net/sfc/efx.c --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2005-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include @@ -36,18 +19,16 @@ #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" +#include "nic.h" +#include "efx_ioctl.h" + +#include "mcdi.h" /************************************************************************** * @@ -59,16 +40,33 @@ /* Loopback mode names (see LOOPBACK_MODE()) */ const unsigned int efx_loopback_mode_max = LOOPBACK_MAX; const char *efx_loopback_mode_names[] = { - [LOOPBACK_NONE] = "NONE", - [LOOPBACK_MAC] = "MAC", - [LOOPBACK_XGMII] = "XGMII", - [LOOPBACK_XGXS] = "XGXS", - [LOOPBACK_XAUI] = "XAUI", - [LOOPBACK_PHY] = "PHY", - [LOOPBACK_PHYXS] = "PHY(XS)", - [LOOPBACK_PCS] = "PHY(PCS)", - [LOOPBACK_PMAPMD] = "PHY(PMAPMD)", - [LOOPBACK_NETWORK] = "NETWORK", + [LOOPBACK_NONE] = "NONE", + [LOOPBACK_DATA] = "DATAPATH", + [LOOPBACK_GMAC] = "GMAC", + [LOOPBACK_XGMII] = "XGMII", + [LOOPBACK_XGXS] = "XGXS", + [LOOPBACK_XAUI] = "XAUI", + [LOOPBACK_GMII] = "GMII", + [LOOPBACK_SGMII] = "SGMII", + [LOOPBACK_XGBR] = "XGBR", + [LOOPBACK_XFI] = "XFI", + [LOOPBACK_XAUI_FAR] = "XAUI_FAR", + [LOOPBACK_GMII_FAR] = "GMII_FAR", + [LOOPBACK_SGMII_FAR] = "SGMII_FAR", + [LOOPBACK_XFI_FAR] = "XFI_FAR", + [LOOPBACK_GPHY] = "GPHY", + [LOOPBACK_PHYXS] = "PHYXS", + [LOOPBACK_PCS] = "PCS", + [LOOPBACK_PMAPMD] = "PMA/PMD", + [LOOPBACK_XPORT] = "XPORT", + [LOOPBACK_XGMII_WS] = "XGMII_WS", + [LOOPBACK_XAUI_WS] = "XAUI_WS", + [LOOPBACK_XAUI_WS_FAR] = "XAUI_WS_FAR", + [LOOPBACK_XAUI_WS_NEAR] = "XAUI_WS_NEAR", + [LOOPBACK_GMII_WS] = "GMII_WS", + [LOOPBACK_XFI_WS] = "XFI_WS", + [LOOPBACK_XFI_WS_FAR] = "XFI_WS_FAR", + [LOOPBACK_PHYXS_WS] = "PHYXS_WS", }; /* Interrupt mode names (see INT_MODE())) */ @@ -79,38 +77,19 @@ [EFX_INT_MODE_LEGACY] = "legacy", }; -/* PHY type names (see PHY_TYPE())) */ -const unsigned int efx_phy_type_max = PHY_TYPE_MAX; -const char *efx_phy_type_names[] = { - [PHY_TYPE_NONE] = "none", - [PHY_TYPE_CX4_RTMR] = "Mysticom CX4", - [PHY_TYPE_1G_ALASKA] = "1G Alaska", - [PHY_TYPE_10XPRESS] = "SFC 10Xpress", - [PHY_TYPE_XFP] = "Quake XFP", - [PHY_TYPE_PM8358] = "PM8358 XAUI", -}; - const unsigned int efx_reset_type_max = RESET_TYPE_MAX; const char *efx_reset_type_names[] = { [RESET_TYPE_INVISIBLE] = "INVISIBLE", [RESET_TYPE_ALL] = "ALL", [RESET_TYPE_WORLD] = "WORLD", [RESET_TYPE_DISABLE] = "DISABLE", - [RESET_TYPE_MONITOR] = "MONITOR", + [RESET_TYPE_TX_WATCHDOG] = "TX_WATCHDOG", [RESET_TYPE_INT_ERROR] = "INT_ERROR", [RESET_TYPE_RX_RECOVERY] = "RX_RECOVERY", [RESET_TYPE_RX_DESC_FETCH] = "RX_DESC_FETCH", [RESET_TYPE_TX_DESC_FETCH] = "TX_DESC_FETCH", [RESET_TYPE_TX_SKIP] = "TX_SKIP", -}; - -const unsigned int efx_nic_state_max = STATE_MAX; -const char *efx_nic_state_names[] = { - [STATE_INIT] = "INIT", - [STATE_RUNNING] = "RUNNING", - [STATE_FINI] = "FINI", - [STATE_RESETTING] = "RESETTING", - [STATE_DISABLED] = "DISABLED", + [RESET_TYPE_MC_FAILURE] = "MC_FAILURE", }; #define EFX_MAX_MTU (9 * 1024) @@ -122,32 +101,42 @@ */ static struct workqueue_struct *refill_workqueue; +/* Reset workqueue. If any NIC has a hardware failure then a reset will be + * queued onto this work queue. This is not a per-nic work queue, because + * efx_reset_work() acquires the rtnl lock, so resets are naturally serialised. + */ +static struct workqueue_struct *reset_workqueue; + /************************************************************************** * * Configurable values * *************************************************************************/ +#if defined(EFX_USE_KCOMPAT) && (defined(EFX_USE_GRO) || defined(EFX_USE_SFC_LRO)) /* * Enable large receive offload (LRO) aka soft segment reassembly (SSR) * * This sets the default for new devices. It can be controlled later * using ethtool. */ -static int lro = 1; +static int lro = true; module_param(lro, int, 0644); MODULE_PARM_DESC(lro, "Large receive offload acceleration"); +#endif /* * Use separate channels for TX and RX events * - * Set this to 1 to use separate channels for TX and RX. It allows us to - * apply a higher level of interrupt moderation to TX events. + * Set this to 1 to use separate channels for TX and RX. It allows us + * to control interrupt affinity separately for TX and RX. * - * This is forced to 0 for MSI interrupt mode as the interrupt vector - * is not written + * This is only used in MSI-X interrupt mode */ -static unsigned int separate_tx_and_rx_channels = 1; +static unsigned int separate_tx_channels = false; +module_param(separate_tx_channels, uint, 0644); +MODULE_PARM_DESC(separate_tx_channels, + "Use separate channels for TX and RX"); /* This is the weight assigned to each of the (per-channel) virtual * NAPI devices. @@ -160,11 +149,6 @@ */ 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 @@ -193,13 +177,6 @@ */ static unsigned int tx_irq_mod_usec = 150; -/* Ignore online self-test failures at load - * - * If set to 1, then the driver will not fail to load - * if the online self-test fails. Useful only during testing - */ -static unsigned int allow_load_on_failure; - /* This is the first interrupt mode to try out of: * 0 => MSI-X * 1 => MSI @@ -207,22 +184,50 @@ */ static unsigned int 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 +/* This is the requested number of CPUs to use for Receive-Side Scaling + * (RSS), i.e. the number of CPUs among which we may distribute + * simultaneous interrupt handling. Or alternatively it may be set to + * "packages", "cores" or "hyperthreads" to get one receive channel per + * package, core or hyperthread. + * + * Systems without MSI-X will only target one CPU via legacy or MSI + * interrupt. The default is "packages". */ -static unsigned int onload_offline_selftest = 1; - -/* This is the requested number of CPUs to use for Receive-Side Scaling (RSS), - * i.e. the number of CPUs among which we may distribute simultaneous - * interrupt handling. - * - * Cards without MSI-X will only target one CPU via legacy or MSI interrupt. - * The default (0) means to assign an interrupt to each package (level II cache) - */ -static unsigned int rss_cpus; -module_param(rss_cpus, uint, 0444); -MODULE_PARM_DESC(rss_cpus, "Number of CPUs to use for Receive-Side Scaling"); +static char *rss_cpus; +module_param(rss_cpus, charp, 0444); +MODULE_PARM_DESC(rss_cpus, "Number of CPUs to use for Receive-Side Scaling, " + "or 'packages', 'cores' or 'hyperthreads'"); + +enum rss_mode { + EFX_RSS_PACKAGES, + EFX_RSS_CORES, + EFX_RSS_HYPERTHREADS, + EFX_RSS_CUSTOM, +}; + +static int phy_flash_cfg; +module_param(phy_flash_cfg, int, 0644); +MODULE_PARM_DESC(phy_flash_cfg, "Set PHYs into reflash mode initially"); + +static unsigned irq_adapt_enable = 1; +module_param(irq_adapt_enable, uint, 0444); +MODULE_PARM_DESC(irq_adapt_enable, + "Enable adaptive interrupt moderation"); + +static unsigned irq_adapt_low_thresh = 10000; +module_param(irq_adapt_low_thresh, uint, 0644); +MODULE_PARM_DESC(irq_adapt_low_thresh, + "Threshold score for reducing IRQ moderation"); + +static unsigned irq_adapt_high_thresh = 20000; +module_param(irq_adapt_high_thresh, uint, 0644); +MODULE_PARM_DESC(irq_adapt_high_thresh, + "Threshold score for increasing IRQ moderation"); + +static unsigned irq_adapt_irqs = 1000; +module_param(irq_adapt_irqs, uint, 0644); +MODULE_PARM_DESC(irq_adapt_irqs, + "Number of IRQs per IRQ moderation adaptation"); /************************************************************************** * @@ -237,7 +242,7 @@ #define EFX_ASSERT_RESET_SERIALISED(efx) \ do { \ if ((efx->state == STATE_RUNNING) || \ - (efx->state == STATE_RESETTING)) \ + (efx->state == STATE_DISABLED)) \ ASSERT_RTNL(); \ } while (0) @@ -254,16 +259,18 @@ * 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) +static 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 || + struct efx_nic *efx = channel->efx; + int rx_packets; + + if (unlikely(efx->reset_pending != RESET_TYPE_NONE || !channel->enabled)) - return rx_quota; - - rxdmaqs = falcon_process_eventq(channel, &rx_quota); + return 0; + + rx_packets = efx_nic_process_eventq(channel, rx_quota); + if (rx_packets == 0) + return 0; /* Deliver last RX packet. */ if (channel->rx_pkt) { @@ -272,19 +279,11 @@ channel->rx_pkt = NULL; } - efx_flush_lro(channel); efx_rx_strategy(channel); - /* Refill descriptor rings as necessary */ - rx_queue = &channel->efx->rx_queue[0]; - while (rxdmaqs) { - if (rxdmaqs & 0x01) - efx_fast_push_rx_descriptors(rx_queue); - rx_queue++; - rxdmaqs >>= 1; - } - - return rx_quota; + efx_fast_push_rx_descriptors(&efx->rx_queue[channel->channel]); + + return rx_packets; } /* Mark channel as finished processing @@ -295,12 +294,13 @@ */ static inline void efx_channel_processed(struct efx_channel *channel) { - /* Write to EVQ_RPTR_REG. If a new event arrived in a race - * with finishing processing, a new interrupt will be raised. - */ - channel->work_pending = 0; - smp_wmb(); /* Ensure channel updated before any new interrupt. */ - falcon_eventq_read_ack(channel); + /* The interrupt handler for this channel may set work_pending + * as soon as we acknowledge the events we've seen. Make sure + * it's cleared before then. */ + channel->work_pending = false; + smp_wmb(); + + efx_nic_eventq_read_ack(channel); } /* NAPI poll handler @@ -308,33 +308,66 @@ * NAPI guarantees serialisation of polls of the same device, which * provides the guarantee required by efx_process_channel(). */ +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_OLD_NAPI) +static int efx_poll(struct napi_struct *napi, int budget) +{ + struct efx_channel *channel = + container_of(napi, struct efx_channel, napi_str); +#else static int efx_poll(struct net_device *napi, int *budget_ret) { - struct net_device *napi_dev = napi; - struct efx_channel *channel = napi_dev->priv; - int budget = min(napi_dev->quota, *budget_ret); - int unused; + struct efx_channel *channel = napi->priv; + int budget = min(napi->quota, *budget_ret); +#endif int rx_packets; EFX_TRACE(channel->efx, "channel %d NAPI poll executing on CPU %d\n", channel->channel, raw_smp_processor_id()); - unused = efx_process_channel(channel, budget); - rx_packets = (budget - unused); - napi_dev->quota -= rx_packets; + rx_packets = efx_process_channel(channel, budget); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_HAVE_OLD_NAPI) + napi->quota -= rx_packets; *budget_ret -= rx_packets; +#endif if (rx_packets < budget) { + struct efx_nic *efx = channel->efx; + + if (channel->used_flags & EFX_USED_BY_RX && + efx->irq_rx_adaptive && + unlikely(++channel->irq_count == irq_adapt_irqs)) { + if (unlikely(channel->irq_mod_score < + irq_adapt_low_thresh)) { + if (channel->irq_moderation > 1) { + channel->irq_moderation -= 1; + efx->type->push_irq_moderation(channel); + } + } else if (unlikely(channel->irq_mod_score > + irq_adapt_high_thresh)) { + if (channel->irq_moderation < + efx->irq_rx_moderation) { + channel->irq_moderation += 1; + efx->type->push_irq_moderation(channel); + } + } + channel->irq_count = 0; + channel->irq_mod_score = 0; + } + /* There is no race here; although napi_disable() will - * only wait for netif_rx_complete(), this isn't a problem + * only wait for napi_complete(), this isn't a problem * since efx_channel_processed() will have no effect if * interrupts have already been disabled. */ - netif_rx_complete(napi_dev, napi); + napi_complete(napi); efx_channel_processed(channel); } +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_OLD_NAPI) + return rx_packets; +#else return (rx_packets >= budget); +#endif } /* Process the eventq of the specified channel immediately on this CPU @@ -353,27 +386,24 @@ BUG_ON(!channel->enabled); /* Disable interrupts and wait for ISRs to complete */ - falcon_disable_interrupts(efx); + efx_nic_disable_interrupts(efx); if (efx->legacy_irq) synchronize_irq(efx->legacy_irq); - if (channel->has_interrupt && channel->irq) + if (channel->irq) synchronize_irq(channel->irq); /* Wait for any NAPI processing to complete */ napi_disable(&channel->napi_str); /* Poll the channel */ - (void) efx_process_channel(channel, efx->type->evq_size); + efx_process_channel(channel, EFX_EVQ_SIZE); /* Ack the eventq. This may cause an interrupt to be generated * when they are reenabled */ efx_channel_processed(channel); - /* Reenable NAPI polling */ napi_enable(&channel->napi_str); - - /* Reenable interrupts */ - falcon_enable_interrupts(efx); + efx_nic_enable_interrupts(efx); } /* Create event queue @@ -385,32 +415,31 @@ { EFX_LOG(channel->efx, "chan %d create event queue\n", channel->channel); - return falcon_probe_eventq(channel); + return efx_nic_probe_eventq(channel); } /* Prepare channel's event queue */ -static int efx_init_eventq(struct efx_channel *channel) +static void efx_init_eventq(struct efx_channel *channel) { EFX_LOG(channel->efx, "chan %d init event queue\n", channel->channel); - /* Initialise fields */ channel->eventq_read_ptr = 0; - return falcon_init_eventq(channel); + efx_nic_init_eventq(channel); } static void efx_fini_eventq(struct efx_channel *channel) { EFX_LOG(channel->efx, "chan %d fini event queue\n", channel->channel); - falcon_fini_eventq(channel); + efx_nic_fini_eventq(channel); } static void efx_remove_eventq(struct efx_channel *channel) { EFX_LOG(channel->efx, "chan %d remove event queue\n", channel->channel); - falcon_remove_eventq(channel); + efx_nic_remove_eventq(channel); } /************************************************************************** @@ -419,26 +448,6 @@ * *************************************************************************/ -/* Setup per-NIC RX buffer parameters. - * Calculate the rx buffer allocation parameters required to support - * the current MTU, including padding for header alignment and overruns. - */ -static void efx_calc_rx_buffer_params(struct efx_nic *efx) -{ - unsigned int order, len; - - len = (max(EFX_PAGE_IP_ALIGN, NET_IP_ALIGN) + - EFX_MAX_FRAME_LEN(efx->net_dev->mtu) + - efx->type->rx_buffer_padding); - - /* Calculate page-order */ - for (order = 0; ((1u << order) * PAGE_SIZE) < len; ++order) - ; - - efx->rx_buffer_len = len; - efx->rx_buffer_order = order; -} - static int efx_probe_channel(struct efx_channel *channel) { struct efx_tx_queue *tx_queue; @@ -478,54 +487,64 @@ } +static void efx_set_channel_names(struct efx_nic *efx) +{ + struct efx_channel *channel; + const char *type = ""; + int number; + + efx_for_each_channel(channel, efx) { + number = channel->channel; + if (efx->n_channels > efx->n_rx_queues) { + if (channel->channel < efx->n_rx_queues) { + type = "-rx"; + } else { + type = "-tx"; + number -= efx->n_rx_queues; + } + } + snprintf(channel->name, sizeof(channel->name), + "%s%s-%d", efx->name, type, number); + } +} + /* Channels are shutdown and reinitialised whilst the NIC is running * to propagate configuration changes (mtu, checksum offload), or * to clear hardware error conditions */ -static int efx_init_channels(struct efx_nic *efx) +static void efx_init_channels(struct efx_nic *efx) { struct efx_tx_queue *tx_queue; struct efx_rx_queue *rx_queue; struct efx_channel *channel; - int rc = 0; - - /* Recalculate the rx buffer parameters */ - efx_calc_rx_buffer_params(efx); + + /* Calculate the rx buffer allocation parameters required to + * support the current MTU, including padding for header + * alignment and overruns. + */ + efx->rx_buffer_len = (max(EFX_PAGE_IP_ALIGN, NET_IP_ALIGN) + + EFX_MAX_FRAME_LEN(efx->net_dev->mtu) + + efx->type->rx_buffer_padding); + efx->rx_buffer_order = get_order(efx->rx_buffer_len); /* Initialise the channels */ efx_for_each_channel(channel, efx) { EFX_LOG(channel->efx, "init chan %d\n", channel->channel); - rc = efx_init_eventq(channel); - if (rc) - goto err; - - efx_for_each_channel_tx_queue(tx_queue, channel) { - rc = efx_init_tx_queue(tx_queue); - if (rc) - goto err; - } + efx_init_eventq(channel); + + efx_for_each_channel_tx_queue(tx_queue, channel) + efx_init_tx_queue(tx_queue); /* The rx buffer allocation strategy is MTU dependent */ efx_rx_strategy(channel); - efx_for_each_channel_rx_queue(rx_queue, channel) { - rc = efx_init_rx_queue(rx_queue); - if (rc) - goto err; - } + efx_for_each_channel_rx_queue(rx_queue, channel) + efx_init_rx_queue(rx_queue); WARN_ON(channel->rx_pkt != NULL); efx_rx_strategy(channel); } - - return 0; - - err: - EFX_ERR(efx, "failed to initialise channel %d\n", - channel ? channel->channel : -1); - efx_fini_channels(efx); - return rc; } /* This enables event queue processing and packet transmission. @@ -539,16 +558,13 @@ EFX_LOG(channel->efx, "starting chan %d\n", channel->channel); - if (!(channel->efx->net_dev->flags & IFF_UP)) - netif_napi_add(channel->napi_dev, &channel->napi_str, - efx_poll, napi_weight); - - /* Mark channel as enabled */ - channel->work_pending = 0; - channel->enabled = 1; - smp_wmb(); /* ensure channel updated before first interrupt */ - - /* Enable NAPI poll handler */ + /* The interrupt handler for this channel may set work_pending + * as soon as we enable it. Make sure it's cleared before + * then. Similarly, make sure it sees the enabled flag set. */ + channel->work_pending = false; + channel->enabled = true; + smp_wmb(); + napi_enable(&channel->napi_str); /* Load up RX descriptors */ @@ -569,10 +585,7 @@ EFX_LOG(channel->efx, "stop chan %d\n", channel->channel); - /* Mark channel as disabled */ - channel->enabled = 0; - - /* Wait for any NAPI processing to complete */ + channel->enabled = false; napi_disable(&channel->napi_str); /* Ensure that any worker threads have exited or will be no-ops */ @@ -587,10 +600,17 @@ struct efx_channel *channel; struct efx_tx_queue *tx_queue; struct efx_rx_queue *rx_queue; + int rc; EFX_ASSERT_RESET_SERIALISED(efx); BUG_ON(efx->port_enabled); + rc = efx_nic_flush_queues(efx); + if (rc) + EFX_ERR(efx, "failed to flush queues\n"); + else + EFX_LOG(efx, "successfully flushed all queues\n"); + efx_for_each_channel(channel, efx) { EFX_LOG(channel->efx, "shut down chan %d\n", channel->channel); @@ -598,13 +618,6 @@ efx_fini_rx_queue(rx_queue); efx_for_each_channel_tx_queue(tx_queue, channel) efx_fini_tx_queue(tx_queue); - } - - /* Do the event queues last so that we can handle flush events - * for all DMA queues. */ - efx_for_each_channel(channel, efx) { - EFX_LOG(channel->efx, "shut down evq %d\n", channel->channel); - efx_fini_eventq(channel); } } @@ -640,9 +653,9 @@ * netif_carrier_on/off) of the link status, and also maintains the * link status's stop on the port's TX queue. */ -static void efx_link_status_changed(struct efx_nic *efx) +void efx_link_status_changed(struct efx_nic *efx) { - int carrier_ok; + struct efx_link_state *link_state = &efx->link_state; /* SFC Bug 5356: A net_dev notifier is registered, so we must ensure * that no events are triggered between unregister_netdev() and the @@ -651,119 +664,169 @@ if (!netif_running(efx->net_dev)) return; - carrier_ok = netif_carrier_ok(efx->net_dev) ? 1 : 0; - if (efx->link_up != carrier_ok) { + if (efx->port_inhibited) { + netif_carrier_off(efx->net_dev); + return; + } + + if (link_state->up != netif_carrier_ok(efx->net_dev)) { efx->n_link_state_changes++; - if (efx->link_up) + if (link_state->up) netif_carrier_on(efx->net_dev); else netif_carrier_off(efx->net_dev); } - /* Inform driverlink client */ - EFX_DL_CALLBACK(efx, link_change, efx->link_up); - /* Status message for kernel log */ - if (efx->link_up) { - struct mii_if_info *gmii = &efx->mii; - unsigned adv, lpa; - /* NONE here means direct XAUI from the controller, with no - * MDIO-attached device we can query. */ - if (efx->phy_type != PHY_TYPE_NONE) { - adv = gmii_advertised(gmii); - lpa = gmii_lpa(gmii); - } else { - lpa = GM_LPA_10000 | LPA_DUPLEX; - adv = lpa; - } - EFX_INFO(efx, "link up at %dMbps %s-duplex " - "(adv %04x lpa %04x) (MTU %d)%s%s%s%s\n", - (efx->link_options & GM_LPA_10000 ? 10000 : - (efx->link_options & GM_LPA_1000 ? 1000 : - (efx->link_options & GM_LPA_100 ? 100 : - 10))), - (efx->link_options & GM_LPA_DUPLEX ? - "full" : "half"), - adv, lpa, + if (link_state->up) { + EFX_INFO(efx, "link up at %uMbps %s-duplex " + "(MTU %d)%s%s%s%s\n", + link_state->speed, link_state->fd ? "full" : "half", efx->net_dev->mtu, (efx->loopback_mode ? " [" : ""), (efx->loopback_mode ? LOOPBACK_MODE(efx) : ""), (efx->loopback_mode ? " LOOPBACK]" : ""), (efx->promiscuous ? " [PROMISC]" : "")); + + if ((efx->wanted_fc & EFX_FC_AUTO) && + (efx->wanted_fc & EFX_FC_TX) && + (~efx->link_state.fc & EFX_FC_TX)) + /* There is no way to report this state through ethtool, so + * print this information to the kernel log */ + EFX_ERR(efx, "Flow control autonegotiated " + "tx OFF (wanted ON)\n"); } else { EFX_INFO(efx, "link down%s\n", - efx->phy_powered ? "" : " [OFF]"); + (efx->phy_mode & PHY_MODE_LOW_POWER) ? " [OFF]" : ""); } } -/* This call reinitialises the MAC to pick up new PHY settings. The - * caller must hold the mac_lock */ -static void __efx_reconfigure_port(struct efx_nic *efx) +void efx_link_set_advertising(struct efx_nic *efx, u32 advertising) { + efx->link_advertising = advertising; + if (advertising) { + if (advertising & ADVERTISED_Pause) + efx->wanted_fc |= (EFX_FC_TX | EFX_FC_RX); + else + efx->wanted_fc &= ~(EFX_FC_TX | EFX_FC_RX); + if (advertising & ADVERTISED_Asym_Pause) + efx->wanted_fc ^= EFX_FC_TX; + } +} + +void efx_link_set_wanted_fc(struct efx_nic *efx, enum efx_fc_type wanted_fc) +{ + efx->wanted_fc = wanted_fc; + if (efx->link_advertising) { + if (wanted_fc & EFX_FC_RX) + efx->link_advertising |= (ADVERTISED_Pause | + ADVERTISED_Asym_Pause); + else + efx->link_advertising &= ~(ADVERTISED_Pause | + ADVERTISED_Asym_Pause); + if (wanted_fc & EFX_FC_TX) + efx->link_advertising ^= ADVERTISED_Asym_Pause; + } +} + +static void efx_fini_port(struct efx_nic *efx); + +/* Push loopback/power/transmit disable settings to the PHY, and reconfigure + * the MAC appropriately. All other PHY configuration changes are pushed + * through phy_op->set_settings(), and pushed asynchronously to the MAC + * through efx_monitor(). + * + * Callers must hold the mac_lock + */ +int __efx_reconfigure_port(struct efx_nic *efx) +{ + enum efx_phy_mode phy_mode; + int rc; + WARN_ON(!mutex_is_locked(&efx->mac_lock)); - EFX_LOG(efx, "reconfiguring MAC from PHY settings on CPU %d\n", - raw_smp_processor_id()); - - efx->mac_op->reconfigure(efx); - - /* Inform kernel of loss/gain of carrier */ - efx_link_status_changed(efx); + /* Serialise the promiscuous flag with efx_set_multicast_list. */ + if (efx_dev_registered(efx)) { + netif_addr_lock_bh(efx->net_dev); + netif_addr_unlock_bh(efx->net_dev); + } + + /* Disable PHY transmit in mac level loopbacks */ + phy_mode = efx->phy_mode; + if (LOOPBACK_INTERNAL(efx)) + efx->phy_mode |= PHY_MODE_TX_DISABLED; + else + efx->phy_mode &= ~PHY_MODE_TX_DISABLED; + + rc = efx->type->reconfigure_port(efx); + + if (rc) + efx->phy_mode = phy_mode; + + return rc; } /* Reinitialise the MAC to pick up new PHY settings, even if the port is * disabled. */ -void efx_reconfigure_port(struct efx_nic *efx) +int efx_reconfigure_port(struct efx_nic *efx) { + int rc; + EFX_ASSERT_RESET_SERIALISED(efx); mutex_lock(&efx->mac_lock); - __efx_reconfigure_port(efx); + rc = __efx_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); + + return rc; +} + +/* Asynchronous work item for changing MAC promiscuity and multicast + * hash. Avoid a drain/rx_ingress enable by reconfiguring the current + * MAC directly. */ +static void efx_mac_work(struct work_struct *data) +{ + struct efx_nic *efx = container_of(data, struct efx_nic, mac_work); + + mutex_lock(&efx->mac_lock); + if (efx->port_enabled) { + efx->type->push_multicast_hash(efx); + efx->mac_op->reconfigure(efx); + } mutex_unlock(&efx->mac_lock); } -/* Asynchronous efx_reconfigure_port work item. To speed up efx_flush_all() - * we don't efx_reconfigure_port() if the port is disabled. Care is taken - * in efx_stop_all() and efx_start_port() to prevent PHY events being lost */ -static void efx_reconfigure_work(struct work_struct *data) -{ - struct efx_nic *efx = container_of(data, struct efx_nic, - reconfigure_work); - - mutex_lock(&efx->mac_lock); - if (efx->port_enabled) - __efx_reconfigure_port(efx); - mutex_unlock(&efx->mac_lock); -} - static int efx_probe_port(struct efx_nic *efx) { - unsigned char *dev_addr; int rc; EFX_LOG(efx, "create port\n"); - /* Connect up MAC/PHY operations table and read MAC address */ - rc = falcon_probe_port(efx); + if (phy_flash_cfg) + efx->phy_mode = PHY_MODE_SPECIAL; + + /* Connect up MAC/PHY operations table */ + rc = efx->type->probe_port(efx); if (rc) goto err; /* Sanity check MAC address */ - dev_addr = efx->mac_address; - if (!is_valid_ether_addr(dev_addr)) { + if (is_valid_ether_addr(efx->mac_address)) { + memcpy(efx->net_dev->dev_addr, efx->mac_address, ETH_ALEN); + } else { DECLARE_MAC_BUF(mac); - EFX_ERR(efx, "invalid MAC address %s\n", - print_mac(mac, dev_addr)); if (!allow_bad_hwaddr) { + EFX_ERR(efx, "invalid MAC address %s\n", + print_mac(mac, efx->mac_address)); rc = -EINVAL; goto err; } - random_ether_addr(dev_addr); + random_ether_addr(efx->net_dev->dev_addr); EFX_INFO(efx, "using locally-generated MAC %s\n", - print_mac(mac, dev_addr)); + print_mac(mac, efx->net_dev->dev_addr)); } /* Register debugfs entries */ @@ -784,52 +847,61 @@ EFX_LOG(efx, "init port\n"); - /* The default power state is ON */ - efx->phy_powered = 1; - - /* Initialise the MAC and PHY */ - rc = efx->mac_op->init(efx); + mutex_lock(&efx->mac_lock); + + rc = efx->phy_op->init(efx); if (rc) - return rc; - - efx->port_initialized = 1; - - /* Reconfigure port to program MAC registers */ + goto fail1; + + efx->port_initialized = true; + + /* Reconfigure the MAC before creating dma queues (required for + * Falcon/A1 where RX_INGR_EN/TX_DRAIN_EN isn't supported) */ efx->mac_op->reconfigure(efx); + /* Ensure the PHY advertises the correct flow control settings */ + rc = efx->phy_op->reconfigure(efx); + if (rc) + goto fail2; + + mutex_unlock(&efx->mac_lock); return 0; + +fail2: + efx->phy_op->fini(efx); +fail1: + mutex_unlock(&efx->mac_lock); + return rc; } -/* Allow efx_reconfigure_port() to be scheduled, and close the window - * between efx_stop_port and efx_flush_all whereby a previously scheduled - * efx_reconfigure_port() may have been cancelled */ static void efx_start_port(struct efx_nic *efx) { EFX_LOG(efx, "start port\n"); BUG_ON(efx->port_enabled); mutex_lock(&efx->mac_lock); - efx->port_enabled = 1; - __efx_reconfigure_port(efx); + efx->port_enabled = true; + + /* efx_mac_work() might have been scheduled after efx_stop_port(), + * and then cancelled by efx_flush_all() */ + efx->type->push_multicast_hash(efx); + efx->mac_op->reconfigure(efx); + mutex_unlock(&efx->mac_lock); } -/* Prevent efx_reconfigure_work and efx_monitor() from executing, and - * efx_set_multicast_list() from scheduling efx_reconfigure_work. - * efx_reconfigure_work can still be scheduled via NAPI processing - * until efx_flush_all() is called */ +/* Prevent efx_mac_work() and efx_monitor() from working */ static void efx_stop_port(struct efx_nic *efx) { EFX_LOG(efx, "stop port\n"); mutex_lock(&efx->mac_lock); - efx->port_enabled = 0; + efx->port_enabled = false; mutex_unlock(&efx->mac_lock); - /* Serialise against efx_set_multicast_list() */ - if (NET_DEV_REGISTERED(efx)) { - netif_tx_lock_bh(efx->net_dev); - netif_tx_unlock_bh(efx->net_dev); + if (efx_dev_registered(efx)) { + netif_addr_lock_bh(efx->net_dev); + netif_addr_unlock_bh(efx->net_dev); } } @@ -840,11 +912,10 @@ if (!efx->port_initialized) return; - efx->mac_op->fini(efx); - efx->port_initialized = 0; - - /* Mark the link down */ - efx->link_up = 0; + efx->phy_op->fini(efx); + efx->port_initialized = false; + + efx->link_state.up = false; efx_link_status_changed(efx); } @@ -853,7 +924,7 @@ EFX_LOG(efx, "destroying port\n"); efx_fini_debugfs_port(efx); - falcon_remove_port(efx); + efx->type->remove_port(efx); } /************************************************************************** @@ -871,7 +942,6 @@ EFX_LOG(efx, "initialising I/O\n"); - /* Generic device-enabling code */ rc = pci_enable_device(pci_dev); if (rc) { EFX_ERR(efx, "failed to enable PCI device\n"); @@ -906,10 +976,14 @@ goto fail2; } - /* Get memory base address */ - efx->membase_phys = pci_resource_start(efx->pci_dev, - efx->type->mem_bar); - rc = pci_request_region(pci_dev, efx->type->mem_bar, "sfc"); + efx->membase_phys = pci_resource_start(efx->pci_dev, EFX_MEM_BAR); +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_MSIX_TABLE_RESERVED) + rc = pci_request_region(pci_dev, EFX_MEM_BAR, "sfc"); +#else + if (!request_mem_region(efx->membase_phys, efx->type->mem_map_size, + "sfc")) + rc = -EIO; +#endif if (rc) { EFX_ERR(efx, "request for memory BAR failed\n"); rc = -EIO; @@ -918,23 +992,26 @@ efx->membase = ioremap_nocache(efx->membase_phys, efx->type->mem_map_size); if (!efx->membase) { - EFX_ERR(efx, "could not map memory BAR %d at %lx+%x\n", - efx->type->mem_bar, efx->membase_phys, + EFX_ERR(efx, "could not map memory BAR at %llx+%x\n", + (unsigned long long)efx->membase_phys, efx->type->mem_map_size); rc = -ENOMEM; goto fail4; } - EFX_LOG(efx, "memory BAR %u at %lx+%x (virtual %p)\n", - efx->type->mem_bar, efx->membase_phys, efx->type->mem_map_size, - efx->membase); + EFX_LOG(efx, "memory BAR at %llx+%x (virtual %p)\n", + (unsigned long long)efx->membase_phys, + efx->type->mem_map_size, efx->membase); return 0; fail4: +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_MSIX_TABLE_RESERVED) + pci_release_region(efx->pci_dev, EFX_MEM_BAR); +#else release_mem_region(efx->membase_phys, efx->type->mem_map_size); +#endif fail3: - efx->membase_phys = 0UL; - /* fall-thru */ + efx->membase_phys = 0; fail2: pci_disable_device(efx->pci_dev); fail1: @@ -951,65 +1028,176 @@ } if (efx->membase_phys) { - pci_release_region(efx->pci_dev, efx->type->mem_bar); - efx->membase_phys = 0UL; +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_MSIX_TABLE_RESERVED) + pci_release_region(efx->pci_dev, EFX_MEM_BAR); +#else + release_mem_region(efx->membase_phys, efx->type->mem_map_size); +#endif + efx->membase_phys = 0; } pci_disable_device(efx->pci_dev); } -/* Probe the number and type of interrupts we are able to obtain. */ +#if !defined(EFX_USE_KCOMPAT) || (defined(topology_core_siblings) && !defined(__VMKLNX__) && !defined(CONFIG_XEN)) +#define HAVE_EFX_NUM_PACKAGES +static int efx_num_packages(void) +{ + cpumask_t core_mask; + int count; + int cpu; + + cpus_clear(core_mask); + count = 0; + for_each_online_cpu(cpu) { + if (!cpu_isset(cpu, core_mask)) { + ++count; + cpus_or(core_mask, core_mask, + topology_core_siblings(cpu)); + } + } + + /* Create two RSS queues even on a single package host */ + if (count == 1) + count = 2; + + return count; +} +#endif + +#if !defined(EFX_USE_KCOMPAT) || (defined(topology_thread_siblings) && !defined(__VMKLNX__) && !defined(CONFIG_XEN)) +#define HAVE_EFX_NUM_CORES +static int efx_num_cores(void) +{ + cpumask_t core_mask; + int count; + int cpu; + + cpus_clear(core_mask); + count = 0; + for_each_online_cpu(cpu) { + if (!cpu_isset(cpu, core_mask)) { + ++count; + cpus_or(core_mask, core_mask, + topology_thread_siblings(cpu)); + } + } + + return count; +} +#endif + +/* Get number of RX queues wanted. */ +static int efx_wanted_rx_queues(struct efx_nic *efx) +{ + enum rss_mode rss_mode = EFX_RSS_PACKAGES; + bool selected = false; + int n_rxq = -1; + + if (rss_cpus == NULL) { + /* Leave at default. */ + } else if (strcmp(rss_cpus, "packages") == 0) { + rss_mode = EFX_RSS_PACKAGES; + selected = true; + } else if (strcmp(rss_cpus, "cores") == 0) { + rss_mode = EFX_RSS_CORES; + selected = true; + } else if (strcmp(rss_cpus, "hyperthreads") == 0) { + rss_mode = EFX_RSS_HYPERTHREADS; + selected = true; + } else if (sscanf(rss_cpus, "%d", &n_rxq) == 1 && n_rxq > 0) { + rss_mode = EFX_RSS_CUSTOM; + selected = true; + } else { + EFX_ERR(efx, "ERROR: Bad value for module parameter " + "rss_cpus='%s'\n", rss_cpus); + } + + switch (rss_mode) { +#if defined(HAVE_EFX_NUM_PACKAGES) + case EFX_RSS_PACKAGES: + n_rxq = efx_num_packages(); + break; +#elif defined(CONFIG_XEN) + case EFX_RSS_PACKAGES: + EFX_ERR(efx, "WARNING: Unable to determine CPU topology on Xen" + "reliably. Using 4 rss channels.\n" ); + n_rxq = 4; + break; +#endif +#if defined(HAVE_EFX_NUM_CORES) + case EFX_RSS_CORES: + n_rxq = efx_num_cores(); + break; +#elif defined(CONFIG_XEN) + case EFX_RSS_CORES: + EFX_ERR(efx, "WARNING: Unable to determine CPU topology on Xen" + "reliably. Assuming hyperthreading enabled.\n"); + n_rxq = max(1, num_online_cpus() / 2); + break; +#endif + case EFX_RSS_HYPERTHREADS: + n_rxq = num_online_cpus(); + break; + case EFX_RSS_CUSTOM: + break; + default: + if (selected) + EFX_ERR(efx, "ERROR: Selected rss mode '%s' not " + "available\n", rss_cpus); + rss_mode = EFX_RSS_HYPERTHREADS; + n_rxq = num_online_cpus(); + break; + } + + if (n_rxq > EFX_MAX_RX_QUEUES) { + EFX_ERR(efx, "WARNING: Reducing number of rss channels from " + "%d to %d.\n", n_rxq, EFX_MAX_RX_QUEUES); + n_rxq = EFX_MAX_RX_QUEUES; + } + + return n_rxq; +} + +/* Probe the number and type of interrupts we are able to obtain, and + * the resulting numbers of channels and RX queues. + */ static void efx_probe_interrupts(struct efx_nic *efx) { - int max_channel = efx->type->phys_addr_channels - 1; - struct msix_entry xentries[EFX_MAX_CHANNELS]; + int max_channels = + min_t(int, efx->type->phys_addr_channels, EFX_MAX_CHANNELS); int rc, i; if (efx->interrupt_mode == EFX_INT_MODE_MSIX) { - BUG_ON(!pci_find_capability(efx->pci_dev, PCI_CAP_ID_MSIX)); - - if (rss_cpus == 0) { -#ifdef topology_core_siblings - cpumask_t core_mask; - int cpu; - - cpus_clear(core_mask); - efx->rss_queues = 0; - for_each_online_cpu(cpu) { - if (!cpu_isset(cpu, core_mask)) { - ++efx->rss_queues; - cpus_or(core_mask, core_mask, - topology_core_siblings(cpu)); - } - } -#else - efx->rss_queues = num_online_cpus(); -#endif - } else { - efx->rss_queues = rss_cpus; + struct msix_entry xentries[EFX_MAX_CHANNELS]; + int wanted_ints; + int rx_queues; + + /* We will need one interrupt per channel. We need one + * channel per RX queue, and possibly one for TX queues. + */ + rx_queues = efx_wanted_rx_queues(efx); + wanted_ints = rx_queues + (separate_tx_channels ? 1 : 0); + wanted_ints = min(wanted_ints, max_channels); + + for (i = 0; i < wanted_ints; i++) + xentries[i].entry = i; + rc = pci_enable_msix(efx->pci_dev, xentries, wanted_ints); + if (rc > 0) { + EFX_ERR(efx, "WARNING: Insufficient MSI-X vectors" + " available (%d < %d).\n", rc, wanted_ints); + EFX_ERR(efx, "WARNING: Performance may be reduced.\n"); + EFX_BUG_ON_PARANOID(rc >= wanted_ints); + wanted_ints = rc; + rc = pci_enable_msix(efx->pci_dev, xentries, + wanted_ints); } - /* Limit the number of rss queues appropriately */ - efx->rss_queues = min(efx->rss_queues, max_channel + 1); - efx->rss_queues = min(efx->rss_queues, EFX_MAX_CHANNELS); - - /* Request maximum number of MSI interrupts, and fill out - * the channel interrupt information the allowed allocation */ - for (i = 0; i < efx->rss_queues; i++) - xentries[i].entry = i; - rc = pci_enable_msix(efx->pci_dev, xentries, efx->rss_queues); - if (rc > 0) { - EFX_BUG_ON_PARANOID(rc >= efx->rss_queues); - efx->rss_queues = rc; - rc = pci_enable_msix(efx->pci_dev, xentries, - efx->rss_queues); - } - if (rc == 0) { - for (i = 0; i < efx->rss_queues; i++) { - efx->channel[i].has_interrupt = 1; + efx->n_rx_queues = min(rx_queues, wanted_ints); + efx->n_channels = wanted_ints; + for (i = 0; i < wanted_ints; i++) efx->channel[i].irq = xentries[i].vector; - } } else { /* Fall back to single channel MSI */ efx->interrupt_mode = EFX_INT_MODE_MSI; @@ -1019,11 +1207,11 @@ /* Try single interrupt MSI */ if (efx->interrupt_mode == EFX_INT_MODE_MSI) { - efx->rss_queues = 1; + efx->n_rx_queues = 1; + efx->n_channels = 1; rc = pci_enable_msi(efx->pci_dev); if (rc == 0) { efx->channel[0].irq = efx->pci_dev->irq; - efx->channel[0].has_interrupt = 1; } else { EFX_ERR(efx, "could not enable MSI\n"); efx->interrupt_mode = EFX_INT_MODE_LEGACY; @@ -1032,10 +1220,8 @@ /* Assume legacy interrupts */ if (efx->interrupt_mode == EFX_INT_MODE_LEGACY) { - efx->rss_queues = 1; - /* Every channel is interruptible */ - for (i = 0; i < EFX_MAX_CHANNELS; i++) - efx->channel[i].has_interrupt = 1; + efx->n_rx_queues = 1; + efx->n_channels = 1 + (separate_tx_channels ? 1 : 0); efx->legacy_irq = efx->pci_dev->irq; } } @@ -1045,7 +1231,7 @@ struct efx_channel *channel; /* Remove MSI/MSI-X interrupts */ - efx_for_each_channel_with_interrupt(channel, efx) + efx_for_each_channel(channel, efx) channel->irq = 0; pci_disable_msi(efx->pci_dev); pci_disable_msix(efx->pci_dev); @@ -1054,45 +1240,22 @@ efx->legacy_irq = 0; } -/* Select number of used resources - * Should be called after probe_interrupts() - */ -static void efx_select_used(struct efx_nic *efx) +static void efx_set_channels(struct efx_nic *efx) { struct efx_tx_queue *tx_queue; struct efx_rx_queue *rx_queue; - int i; - - /* TX queues. One per port per channel with TX capability - * (more than one per port won't work on Linux, due to out - * of order issues... but will be fine on Solaris) - */ - tx_queue = &efx->tx_queue[0]; - - /* Perform this for each channel with TX capabilities. - * At the moment, we only support a single TX queue - */ - tx_queue->used = 1; - if ((!EFX_INT_MODE_USE_MSI(efx)) && separate_tx_and_rx_channels) - tx_queue->channel = &efx->channel[1]; - else - tx_queue->channel = &efx->channel[0]; - tx_queue->channel->used_flags |= EFX_USED_BY_TX; - tx_queue++; - - /* RX queues. Each has a dedicated channel. */ - for (i = 0; i < EFX_MAX_RX_QUEUES; i++) { - rx_queue = &efx->rx_queue[i]; - - if (i < efx->rss_queues) { - rx_queue->used = 1; - /* If we allow multiple RX queues per channel - * we need to decide that here - */ - rx_queue->channel = &efx->channel[rx_queue->queue]; - rx_queue->channel->used_flags |= EFX_USED_BY_RX; - rx_queue++; - } + + efx_for_each_tx_queue(tx_queue, efx) { + if (separate_tx_channels) + tx_queue->channel = &efx->channel[efx->n_channels-1]; + else + tx_queue->channel = &efx->channel[0]; + tx_queue->channel->used_flags |= EFX_USED_BY_TX; + } + + efx_for_each_rx_queue(rx_queue, efx) { + rx_queue->channel = &efx->channel[rx_queue->queue]; + rx_queue->channel->used_flags |= EFX_USED_BY_RX; } } @@ -1102,8 +1265,13 @@ EFX_LOG(efx, "creating NIC\n"); + /* Initialise NIC resource information */ + efx->resources = efx->type->resources; + efx->resources.biu_lock = &efx->biu_lock; + efx->dl_info = &efx->resources.hdr; + /* Carry out hardware-type specific initialisation */ - rc = falcon_probe_nic(efx); + rc = efx->type->probe(efx); if (rc) goto fail1; @@ -1111,22 +1279,26 @@ * in MSI-X interrupts. */ efx_probe_interrupts(efx); - /* Determine number of RX queues and TX queues */ - efx_select_used(efx); + if (EFX_INT_MODE_USE_MSI(efx)) + efx->resources.flags |= EFX_DL_FALCON_USE_MSI; + + efx_set_channels(efx); /* Register debugfs entries */ rc = efx_init_debugfs_nic(efx); if (rc) goto fail2; /* Initialise the interrupt moderation settings */ - efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec); + efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec, + irq_adapt_enable); return 0; fail2: efx_remove_interrupts(efx); - falcon_remove_nic(efx); + efx->type->remove(efx); fail1: + efx->dl_info = NULL; return rc; } @@ -1135,7 +1307,8 @@ EFX_LOG(efx, "destroying NIC\n"); efx_remove_interrupts(efx); - falcon_remove_nic(efx); + efx->type->remove(efx); + efx->dl_info = NULL; efx_fini_debugfs_nic(efx); } @@ -1174,6 +1347,7 @@ goto fail3; } } + efx_set_channel_names(efx); return 0; @@ -1192,7 +1366,7 @@ * and ensures that the port is scheduled to be reconfigured. * This function is safe to call multiple times when the NIC is in any * state. */ -static void efx_start_all(struct efx_nic *efx) +void efx_start_all(struct efx_nic *efx) { struct efx_channel *channel; @@ -1204,22 +1378,47 @@ return; if ((efx->state != STATE_RUNNING) && (efx->state != STATE_INIT)) return; - if (NET_DEV_REGISTERED(efx) && !netif_running(efx->net_dev)) + if (efx_dev_registered(efx) && !netif_running(efx->net_dev)) return; /* Mark the port as enabled so port reconfigurations can start, then * restart the transmit interface early so the watchdog timer stops */ efx_start_port(efx); - efx_wake_queue(efx); + if (efx_dev_registered(efx)) + efx_wake_queue(efx); efx_for_each_channel(channel, efx) efx_start_channel(channel); - falcon_enable_interrupts(efx); - - /* Start hardware monitor if we're in RUNNING */ - if (efx->state == STATE_RUNNING) + efx_nic_enable_interrupts(efx); + + /* Switch to event based MCDI completions after enabling interrupts. + * If a reset has been scheduled, then we need to stay in polled mode. + * Rather than serialising efx_mcdi_mode_event() [which sleeps] and + * reset_pending [modified from an atomic context], we instead guarantee + * that efx_mcdi_mode_poll() isn't reverted erroneously */ + efx_mcdi_mode_event(efx); + if (efx->reset_pending != RESET_TYPE_NONE) + efx_mcdi_mode_poll(efx); + + /* Start the hardware monitor if there is one. Otherwise (we're link + * event driven), we have to poll the PHY because after an event queue + * flush, we could have a missed a link state change */ + if (efx->type->monitor != NULL) { +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_NEED_WORK_API_WRAPPERS) + queue_delayed_work(efx->workqueue, &efx->monitor_work, + efx_monitor_interval); +#else queue_work(efx->workqueue, &efx->monitor_work); +#endif + } else { + mutex_lock(&efx->mac_lock); + if (efx->phy_op->poll(efx)) + efx_link_status_changed(efx); + mutex_unlock(&efx->mac_lock); + } + + efx->type->start_stats(efx); } /* Flush all delayed work. Should only be called when no more delayed work @@ -1227,13 +1426,28 @@ * since we're holding the rtnl_lock at this point. */ static void efx_flush_all(struct efx_nic *efx) { - /* Ensure that the hardware monitor and asynchronous port - * reconfigurations are complete, which are the only two consumers - * of efx->workqueue. Since the hardware monitor runs on a long period, - * we put in some effort to cancel the delayed work safely rather - * than just flushing the queue twice (which is guaranteed to flush - * all the work since both efx_monitor and efx_reconfigure_work disarm - * if !efx->port_enabled. */ +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_CANCEL_DELAYED_WORK_SYNC) + struct efx_rx_queue *rx_queue; + + /* Make sure the hardware monitor is stopped */ + cancel_delayed_work_sync(&efx->monitor_work); + + /* Ensure that all RX slow refills are complete. */ + efx_for_each_rx_queue(rx_queue, efx) + cancel_delayed_work_sync(&rx_queue->work); +#endif + +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_CANCEL_WORK_SYNC) + /* Stop scheduled port reconfigurations */ + cancel_work_sync(&efx->mac_work); +#endif +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_CANCEL_DELAYED_WORK_SYNC) + /* Ensure efx_monitor() and efx_mac_work() are complete, which + * are the only two consumers of efx->workqueue. Since the hardware + * monitor runs on a long period, we put in some effort to cancel + * the delayed work safely rather than just flushing the queue twice + * (which is guaranteed to flush all the work since efx_monitor(), + * and efx_mac_work() disarm if !efx->port_enabled). */ if (timer_pending(&efx->monitor_work.timer)) cancel_delayed_work(&efx->monitor_work); flush_workqueue(efx->workqueue); @@ -1245,6 +1459,7 @@ * flush the refill workqueue twice as well. */ flush_workqueue(refill_workqueue); flush_workqueue(refill_workqueue); +#endif } /* Quiesce hardware and software without bringing the link down. @@ -1252,7 +1467,7 @@ * state. The caller is guaranteed to subsequently be in a position * to modify any hardware and software state they see fit without * taking locks. */ -static void efx_stop_all(struct efx_nic *efx) +void efx_stop_all(struct efx_nic *efx) { struct efx_channel *channel; @@ -1262,13 +1477,19 @@ if (!efx->port_enabled) return; + efx->type->stop_stats(efx); + + /* Switch to MCDI polling on Siena before disabling interrupts */ + efx_mcdi_mode_poll(efx); + /* Disable interrupts and wait for ISR to complete */ - falcon_disable_interrupts(efx); + efx_nic_disable_interrupts(efx); if (efx->legacy_irq) synchronize_irq(efx->legacy_irq); - efx_for_each_channel_with_interrupt(channel, efx) + efx_for_each_channel(channel, efx) { if (channel->irq) synchronize_irq(channel->irq); + } /* Stop all NAPI processing and synchronous rx refills */ efx_for_each_channel(channel, efx) @@ -1279,18 +1500,13 @@ * window to loose phy events */ efx_stop_port(efx); - /* Flush reconfigure_work, refill_workqueue, monitor_work */ + /* Flush efx_mac_work(), refill_workqueue and efx_monitor_work() */ efx_flush_all(efx); - /* Isolate the MAC from the TX and RX engines, so that queue - * flushes will complete in a timely fashion. */ - falcon_deconfigure_mac_wrapper(efx); - falcon_drain_tx_fifo(efx); - /* Stop the kernel transmit interface late, so the watchdog * timer isn't ticking over the flush */ - efx_stop_queue(efx); - if (NET_DEV_REGISTERED(efx)) { + if (efx_dev_registered(efx)) { + efx_stop_queue(efx); netif_tx_lock_bh(efx->net_dev); netif_tx_unlock_bh(efx->net_dev); } @@ -1306,79 +1522,39 @@ efx_remove_nic(efx); } -static int efx_run_selftests(struct efx_nic *efx) -{ - struct efx_self_tests tests; - unsigned modes = efx->startup_loopbacks & efx->loopback_modes; - int rc; - - rc = efx_online_test(efx, &tests); - if (rc) { - EFX_ERR(efx, "failed self-tests with interrupt_mode of %s\n", - INT_MODE(efx)); - goto fail; - } - - if (onload_offline_selftest && modes) { - /* Run offline self test */ - EFX_LOG(efx, "performing on-load offline self-tests\n"); - rc = efx_offline_test(efx, &tests, modes); - EFX_LOG(efx, "%s on-load offline self-tests\n", - rc ? "FAILED" : "PASSED"); - if (rc) - goto fail; - } - - return 0; - - fail: - EFX_ERR(efx, "self-tests failed. Given up!\n"); - if (allow_load_on_failure) - rc = 0; - - return rc; -} - -/* A convinience function to safely flush all the queues */ -int efx_flush_queues(struct efx_nic *efx) -{ - int rc; - - EFX_ASSERT_RESET_SERIALISED(efx); - - efx_stop_all(efx); - - efx_fini_channels(efx); - rc = efx_init_channels(efx); - if (rc) { - efx_schedule_reset(efx, RESET_TYPE_DISABLE); - return rc; - } - - efx_start_all(efx); - - return 0; -} - /************************************************************************** * * Interrupt moderation * **************************************************************************/ +static unsigned irq_mod_ticks(int usecs, int resolution) +{ + if (usecs <= 0) + return 0; /* cannot receive interrupts ahead of time :-) */ + if (usecs < resolution) + return 1; /* never round down to 0 */ + return usecs / resolution; +} + /* Set interrupt moderation parameters */ -void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs) +void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs, + bool rx_adaptive) { struct efx_tx_queue *tx_queue; struct efx_rx_queue *rx_queue; + unsigned tx_ticks = irq_mod_ticks(tx_usecs, EFX_IRQ_MOD_RESOLUTION); + unsigned rx_ticks = irq_mod_ticks(rx_usecs, EFX_IRQ_MOD_RESOLUTION); EFX_ASSERT_RESET_SERIALISED(efx); efx_for_each_tx_queue(tx_queue, efx) - tx_queue->channel->irq_moderation = tx_usecs; - + tx_queue->channel->irq_moderation = tx_ticks; + + efx->irq_rx_adaptive = rx_adaptive; + efx->irq_rx_moderation = rx_ticks; efx_for_each_rx_queue(rx_queue, efx) - rx_queue->channel->irq_moderation = rx_usecs; + rx_queue->channel->irq_moderation = rx_ticks; } /************************************************************************** @@ -1391,35 +1567,40 @@ * efx_reconfigure_port via the mac_lock */ static void efx_monitor(struct work_struct *data) { +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_NEED_WORK_API_WRAPPERS) + struct efx_nic *efx = container_of(data, struct efx_nic, + monitor_work.work); +#else struct efx_nic *efx = container_of(data, struct efx_nic, monitor_work); - int rc = 0; +#endif EFX_TRACE(efx, "hardware monitor executing on CPU %d\n", raw_smp_processor_id()); - + BUG_ON(efx->type->monitor == NULL); + +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_CANCEL_DELAYED_WORK_SYNC) /* Without cancel_delayed_work_sync(), we have to make sure that - * we don't rearm when port_enabled == 0 */ + * we don't rearm when !port_enabled */ mutex_lock(&efx->mac_lock); if (!efx->port_enabled) { mutex_unlock(&efx->mac_lock); return; } - - rc = efx->mac_op->check_hw(efx); +#else + /* If the mac_lock is already held then it is likely a port + * reconfiguration is already in place, which will likely do + * most of the work of check_hw() anyway. */ + if (!mutex_trylock(&efx->mac_lock)) + goto out_requeue; + if (!efx->port_enabled) + goto out_unlock; +#endif + efx->type->monitor(efx); + +out_unlock: mutex_unlock(&efx->mac_lock); - - if (rc) { - if (monitor_reset) { - EFX_ERR(efx, "hardware monitor detected a fault: " - "triggering reset\n"); - efx_schedule_reset(efx, RESET_TYPE_MONITOR); - } else { - EFX_ERR(efx, "hardware monitor detected a fault, " - "skipping reset\n"); - } - } - +out_requeue: queue_delayed_work(efx->workqueue, &efx->monitor_work, efx_monitor_interval); } @@ -1435,12 +1616,36 @@ */ static int efx_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd) { - struct efx_nic *efx = net_dev->priv; - - if (!in_interrupt()) - EFX_ASSERT_RESET_SERIALISED(efx); - - return generic_mii_ioctl(&efx->mii, if_mii(ifr), cmd, NULL); + struct efx_nic *efx = netdev_priv(net_dev); + struct mii_ioctl_data *data = if_mii(ifr); + +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_BONDING_HACKS) + if (in_interrupt()) + /* We can't execute mdio requests from an atomic context + * on Siena. Luckily, the bonding driver falls back to + * the ethtool API if this command fails. */ + return -ENOSYS; +#endif + EFX_ASSERT_RESET_SERIALISED(efx); + +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_HAVE_ETHTOOL_RESET) + if (cmd == SIOCDEVPRIVATE) { + struct efx_sock_ioctl __user *user_data = + (struct efx_sock_ioctl __user *)ifr->ifr_data; + u16 efx_cmd; + + if (copy_from_user(&efx_cmd, &user_data->cmd, sizeof(efx_cmd))) + return -EFAULT; + return efx_private_ioctl(efx, efx_cmd, &user_data->u); + } +#endif + + /* Convert phy_id from older PRTAD/DEVAD format */ + if ((cmd == SIOCGMIIREG || cmd == SIOCSMIIREG) && + (data->phy_id & 0xfc00) == 0x0400) + data->phy_id ^= MDIO_PHY_ID_C45 | 0x0400; + + return mdio_mii_ioctl(&efx->mdio, data, cmd); } /************************************************************************** @@ -1449,75 +1654,43 @@ * **************************************************************************/ -/* Allocate the NAPI dev's. - * Called after we know how many channels there are. - */ static int efx_init_napi(struct efx_nic *efx) { struct efx_channel *channel; - int rc; - - /* Allocate the NAPI dev for the port */ - efx->net_dev = alloc_etherdev(0); - if (!efx->net_dev) { - rc = -ENOMEM; - goto err; - } - efx->net_dev->priv = efx; - efx->mii.dev = efx->net_dev; - - efx->net_dev->features |= (NETIF_F_IP_CSUM | NETIF_F_SG | - NETIF_F_HIGHDMA); - efx->lro_enabled = lro; - - /* Copy MAC address */ - memcpy(&efx->net_dev->dev_addr, efx->mac_address, ETH_ALEN); - - /* Allocate the per channel devs */ + efx_for_each_channel(channel, efx) { +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_OLD_NAPI) + channel->napi_dev = efx->net_dev; +#else channel->napi_dev = alloc_etherdev(0); if (!channel->napi_dev) { - rc = -ENOMEM; - goto err; + efx_fini_napi(efx); + return -ENOMEM; } channel->napi_dev->priv = channel; atomic_set(&channel->napi_dev->refcnt, 1); - - /* Initialise LRO/SSR */ - rc = efx_ssr_init(&channel->ssr, efx); - if (rc) - goto err; +#endif + netif_napi_add(channel->napi_dev, &channel->napi_str, + efx_poll, napi_weight); } - return 0; - err: - efx_fini_napi(efx); - return rc; } -/* Free the NAPI state for the port and channels */ static void efx_fini_napi(struct efx_nic *efx) { struct efx_channel *channel; efx_for_each_channel(channel, efx) { - /* Fini LRO/SSR */ - efx_ssr_fini(&channel->ssr); - - /* Finish per channel NAPI */ + if (channel->napi_dev) + netif_napi_del(&channel->napi_str); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_HAVE_OLD_NAPI) if (channel->napi_dev) { channel->napi_dev->priv = NULL; free_netdev(channel->napi_dev); } +#endif channel->napi_dev = NULL; } - - /* Finish port NAPI */ - if (efx->net_dev) { - efx->net_dev->priv = NULL; - free_netdev(efx->net_dev); - efx->net_dev = NULL; - } } /************************************************************************** @@ -1534,10 +1707,10 @@ */ static void efx_netpoll(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_channel *channel; - efx_for_each_channel_with_interrupt(channel, efx) + efx_for_each_channel(channel, efx) efx_schedule_channel(channel); } @@ -1552,12 +1725,25 @@ /* Context: process, rtnl_lock() held. */ static int efx_net_open(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); EFX_ASSERT_RESET_SERIALISED(efx); EFX_LOG(efx, "opening device %s on CPU %d\n", net_dev->name, raw_smp_processor_id()); + if (efx->state == STATE_DISABLED) { + EFX_ERR(efx, "Device is disabled.\n"); + return -EIO; + } + if (efx->phy_mode & PHY_MODE_SPECIAL) + return -EBUSY; + if (efx_mcdi_poll_reboot(efx) && efx_reset(efx, RESET_TYPE_ALL)) + return -EIO; + + /* Notify the kernel of the link state polled during driver load, + * before the monitor starts running */ + efx_link_status_changed(efx); + efx_start_all(efx); return 0; } @@ -1568,36 +1754,35 @@ */ static int efx_net_stop(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; - int rc; + struct efx_nic *efx = netdev_priv(net_dev); EFX_LOG(efx, "closing %s on CPU %d\n", net_dev->name, raw_smp_processor_id()); - /* Stop the device and flush all the channels */ - efx_stop_all(efx); - efx_fini_channels(efx); - rc = efx_init_channels(efx); - if (rc) - efx_schedule_reset(efx, RESET_TYPE_DISABLE); + if (efx->state != STATE_DISABLED) { + /* Stop the device and flush all the channels */ + efx_stop_all(efx); + efx_fini_channels(efx); + efx_init_channels(efx); + } return 0; } -/* Context: process, dev_base_lock held, non-blocking. */ +/* Context: process, dev_base_lock or RTNL held, non-blocking. */ static struct net_device_stats *efx_net_stats(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_mac_stats *mac_stats = &efx->mac_stats; +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_NETDEV_STATS) + struct net_device_stats *stats = &net_dev->stats; +#else struct net_device_stats *stats = &efx->stats; - - if (!spin_trylock(&efx->stats_lock)) - return stats; - if (efx->state == STATE_RUNNING) { - efx->mac_op->update_stats(efx); - falcon_update_nic_stats(efx); - } - spin_unlock(&efx->stats_lock); +#endif + + spin_lock_bh(&efx->stats_lock); + efx->type->update_stats(efx); + spin_unlock_bh(&efx->stats_lock); stats->rx_packets = mac_stats->rx_packets; stats->tx_packets = mac_stats->tx_packets; @@ -1630,21 +1815,20 @@ /* Context: netif_tx_lock held, BHs disabled. */ static void efx_watchdog(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; - - EFX_ERR(efx, "TX stuck with stop_count=%d port_enabled=%d: %s\n", - atomic_read(&efx->netif_stop_count), efx->port_enabled, - monitor_reset ? "resetting channels" : "skipping reset"); - - if (monitor_reset) - efx_schedule_reset(efx, RESET_TYPE_MONITOR); + struct efx_nic *efx = netdev_priv(net_dev); + + EFX_ERR(efx, "TX stuck with stop_count=%d port_enabled=%d:" + " resetting channels\n", + atomic_read(&efx->netif_stop_count), efx->port_enabled); + + efx_schedule_reset(efx, RESET_TYPE_TX_WATCHDOG); } /* Context: process, rtnl_lock() held. */ static int efx_change_mtu(struct net_device *net_dev, int new_mtu) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); int rc = 0; EFX_ASSERT_RESET_SERIALISED(efx); @@ -1665,10 +1849,15 @@ EFX_LOG(efx, "changing MTU to %d\n", new_mtu); efx_fini_channels(efx); + + mutex_lock(&efx->mac_lock); + /* Reconfigure the MAC before enabling the dma queues so that + * the RX buffers don't overflow */ net_dev->mtu = new_mtu; - rc = efx_init_channels(efx); - if (rc) - goto fail; + efx->mac_op->reconfigure(efx); + mutex_unlock(&efx->mac_lock); + + efx_init_channels(efx); /* Notify driverlink client of new MTU */ EFX_DL_CALLBACK(efx, mtu_changed, new_mtu); @@ -1676,17 +1865,13 @@ out: efx_start_all(efx); return rc; - - fail: - efx_schedule_reset(efx, RESET_TYPE_DISABLE); - return rc; } static int efx_set_mac_address(struct net_device *net_dev, void *data) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct sockaddr *addr = data; - char *new_addr = addr->sa_data; + u8 *new_addr = addr->sa_data; EFX_ASSERT_RESET_SERIALISED(efx); @@ -1700,34 +1885,27 @@ memcpy(net_dev->dev_addr, new_addr, net_dev->addr_len); /* Reconfigure the MAC */ - efx_reconfigure_port(efx); + mutex_lock(&efx->mac_lock); + efx->mac_op->reconfigure(efx); + mutex_unlock(&efx->mac_lock); return 0; } -/* Context: netif_tx_lock held, BHs disabled. */ +/* Context: netif_addr_lock held, BHs disabled. */ static void efx_set_multicast_list(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct dev_mc_list *mc_list = net_dev->mc_list; union efx_multicast_hash *mc_hash = &efx->multicast_hash; - int promiscuous; u32 crc; int bit; int i; - /* Set per-MAC promiscuity flag and reconfigure MAC if necessary */ - promiscuous = (net_dev->flags & IFF_PROMISC) ? 1 : 0; - if (efx->promiscuous != promiscuous) { - efx->promiscuous = promiscuous; - /* Close the window between efx_stop_port() and efx_flush_all() - * by only queuing work when the port is enabled. */ - if (efx->port_enabled) - queue_work(efx->workqueue, &efx->reconfigure_work); - } + efx->promiscuous = !!(net_dev->flags & IFF_PROMISC); /* Build multicast hash table */ - if (promiscuous || (net_dev->flags & IFF_ALLMULTI)) { + if (efx->promiscuous || (net_dev->flags & IFF_ALLMULTI)) { memset(mc_hash, 0xff, sizeof(*mc_hash)); } else { memset(mc_hash, 0x00, sizeof(*mc_hash)); @@ -1737,24 +1915,59 @@ set_bit_le(bit, mc_hash->byte); mc_list = mc_list->next; } + + /* Broadcast packets go through the multicast hash filter. + * ether_crc_le() of the broadcast address is 0xbe2612ff + * so we always add bit 0xff to the mask. + */ + set_bit_le(0xff, mc_hash->byte); } - /* Create and activate new global multicast hash table */ - falcon_set_multicast_hash(efx); + if (efx->port_enabled) + queue_work(efx->workqueue, &efx->mac_work); + /* Otherwise efx_start_port() will do this */ } -/* Handle net device notifier events */ +#if !defined(EFX_USE_KCOMPAT) || defined(HAVE_NET_DEVICE_OPS) +static const struct net_device_ops efx_netdev_ops = { + .ndo_open = efx_net_open, + .ndo_stop = efx_net_stop, + .ndo_get_stats = efx_net_stats, + .ndo_tx_timeout = efx_watchdog, + .ndo_start_xmit = efx_hard_start_xmit, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = efx_ioctl, + .ndo_change_mtu = efx_change_mtu, + .ndo_set_mac_address = efx_set_mac_address, + .ndo_set_multicast_list = efx_set_multicast_list, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = efx_netpoll, +#endif +}; +#endif + +static void efx_update_name(struct efx_nic *efx) +{ + strcpy(efx->name, efx->net_dev->name); + efx_mtd_rename(efx); + efx_set_channel_names(efx); +} + static int efx_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *net_dev = (struct net_device *)ptr; - + struct net_device *net_dev = ptr; + +#if !defined(EFX_USE_KCOMPAT) || defined(HAVE_NET_DEVICE_OPS) + if (net_dev->netdev_ops == &efx_netdev_ops && + event == NETDEV_CHANGENAME) { +#else if (net_dev->open == efx_net_open && event == NETDEV_CHANGENAME) { - struct efx_nic *efx = net_dev->priv; - - strcpy(efx->name, net_dev->name); - efx_fini_debugfs_netdev(net_dev); - efx_init_debugfs_netdev(net_dev); +#endif + struct efx_nic *efx = netdev_priv(net_dev); + + if (efx->state == STATE_RUNNING) + efx_update_name(efx); } return NOTIFY_DONE; @@ -1764,6 +1977,7 @@ .notifier_call = efx_netdev_event, }; +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_BONDING_HACKS) /* Prior to Linux 2.6.24, the bonding driver may call change_mtu() * without holding the RTNL, unlike all other callers. We try to * mitigate the risk of a race with other reconfiguration using @@ -1778,6 +1992,15 @@ return rc; } #define efx_change_mtu efx_locked_change_mtu +#endif + +static ssize_t +show_phy_type(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + return sprintf(buf, "%d\n", efx->phy_type); +} +static DEVICE_ATTR(phy_type, 0644, show_phy_type, NULL); static int efx_register_netdev(struct efx_nic *efx) { @@ -1786,10 +2009,13 @@ net_dev->watchdog_timeo = 5 * HZ; net_dev->irq = efx->pci_dev->irq; +#if !defined(EFX_USE_KCOMPAT) || defined(HAVE_NET_DEVICE_OPS) + net_dev->netdev_ops = &efx_netdev_ops; +#else net_dev->open = efx_net_open; net_dev->stop = efx_net_stop; net_dev->get_stats = efx_net_stats; - net_dev->tx_timeout = &efx_watchdog; + net_dev->tx_timeout = efx_watchdog; net_dev->hard_start_xmit = efx_hard_start_xmit; net_dev->do_ioctl = efx_ioctl; net_dev->change_mtu = efx_change_mtu; @@ -1798,42 +2024,65 @@ #ifdef CONFIG_NET_POLL_CONTROLLER net_dev->poll_controller = efx_netpoll; #endif +#endif SET_NETDEV_DEV(net_dev, &efx->pci_dev->dev); SET_ETHTOOL_OPS(net_dev, &efx_ethtool_ops); - /* Always start with carrier off; PHY events will detect the link */ - netif_carrier_off(efx->net_dev); - /* Clear MAC statistics */ efx->mac_op->update_stats(efx); memset(&efx->mac_stats, 0, sizeof(efx->mac_stats)); - rc = register_netdev(net_dev); - if (rc) { - EFX_ERR(efx, "could not register net dev\n"); - return rc; - } - strcpy(efx->name, net_dev->name); + rtnl_lock(); + + rc = dev_alloc_name(net_dev, net_dev->name); + if (rc < 0) + goto fail_locked; + efx_update_name(efx); + + rc = register_netdevice(net_dev); + if (rc) + goto fail_locked; + + /* Always start with carrier off; PHY events will detect the link */ + netif_carrier_off(net_dev); + + /* Register with driverlink layer */ + efx_dl_register_nic(efx); + + rtnl_unlock(); /* Create debugfs symlinks */ rc = efx_init_debugfs_netdev(net_dev); if (rc) { EFX_ERR(efx, "failed to init net dev debugfs\n"); - unregister_netdev(efx->net_dev); - return rc; + goto fail_registered; } + rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_type); + if (rc) { + EFX_ERR(efx, "failed to init net dev attributes\n"); + goto fail_debugfs; + } + return 0; + +fail_debugfs: + efx_fini_debugfs_netdev(net_dev); +fail_registered: + rtnl_lock(); + efx_dl_unregister_nic(efx); + unregister_netdevice(net_dev); +fail_locked: + rtnl_unlock(); + EFX_ERR(efx, "could not register net dev\n"); + return rc; } static void efx_unregister_netdev(struct efx_nic *efx) { struct efx_tx_queue *tx_queue; - if (!efx->net_dev) - return; - - BUG_ON(efx->net_dev->priv != efx); + BUG_ON(netdev_priv(efx->net_dev) != efx); /* Free up any skbs still remaining. This has to happen before * we try to unregister the netdev as running their destructors @@ -1841,8 +2090,9 @@ efx_for_each_tx_queue(tx_queue, efx) efx_release_tx_buffers(tx_queue); - if (NET_DEV_REGISTERED(efx)) { + if (efx_dev_registered(efx)) { strlcpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name)); + device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_type); efx_fini_debugfs_netdev(efx->net_dev); unregister_netdev(efx->net_dev); } @@ -1854,122 +2104,93 @@ * **************************************************************************/ -/* Serialise access to the driverlink callbacks, by quiescing event processing - * (without flushing the descriptor queues), and acquiring the rtnl_lock */ -void efx_suspend(struct efx_nic *efx) +/* Tears down driverlink clients, the entire software state, + * and most of the hardware state before reset. */ +void efx_reset_down(struct efx_nic *efx, enum reset_type method) { - EFX_LOG(efx, "suspending operations\n"); - - rtnl_lock(); + EFX_ASSERT_RESET_SERIALISED(efx); + efx_stop_all(efx); + mutex_lock(&efx->mac_lock); + mutex_lock(&efx->spi_lock); + + efx_fini_channels(efx); + if (efx->port_initialized && method != RESET_TYPE_INVISIBLE) + efx->phy_op->fini(efx); + efx->type->fini(efx); } -void efx_resume(struct efx_nic *efx) -{ - EFX_LOG(efx, "resuming operations\n"); - - efx_start_all(efx); - rtnl_unlock(); -} - -/* The final hardware and software finalisation before reset. - * This function does not handle serialisation with the kernel, it - * assumes the caller has done this */ -static int efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd) +/* This function will always ensure that the locks acquired in + * efx_reset_down() are released. A failure return code indicates + * that we were unable to reinitialise the hardware, and the + * driver should be disabled. If ok is false, then the rx and tx + * engines are not restarted, pending a RESET_DISABLE. */ +int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok) { int rc; EFX_ASSERT_RESET_SERIALISED(efx); - rc = efx->mac_op->get_settings(efx, ecmd); + /* Ensure that SRAM is initialised even if we're disabling the device */ + rc = efx->type->init(efx); if (rc) { - EFX_ERR(efx, "could not back up PHY settings\n"); + EFX_ERR(efx, "failed to initialise NIC\n"); goto fail; } - efx_fini_channels(efx); + if (!ok) + goto fail; + + if (efx->port_initialized && method != RESET_TYPE_INVISIBLE) { + rc = efx->phy_op->init(efx); + if (rc) + goto fail; + if (efx->phy_op->reconfigure(efx)) + EFX_ERR(efx, "could not restore PHY settings\n"); + } + + efx->mac_op->reconfigure(efx); + + efx_init_channels(efx); + + mutex_unlock(&efx->spi_lock); + mutex_unlock(&efx->mac_lock); + + efx_start_all(efx); + return 0; - fail: +fail: + efx->port_initialized = false; + + mutex_unlock(&efx->spi_lock); + mutex_unlock(&efx->mac_lock); + 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) +/* Reset the NIC using the specified method. Note that the reset may + * fail, in which case the card will be left in an unusable state. + * + * Caller must hold the rtnl_lock. + */ +int efx_reset(struct efx_nic *efx, enum reset_type method) { - int rc; - - rc = efx_init_channels(efx); - if (rc) - goto fail1; - - /* Restore MAC and PHY settings. */ - rc = efx->mac_op->set_settings(efx, ecmd); - if (rc) { - EFX_ERR(efx, "could not restore PHY settings\n"); - goto fail2; - } - - return 0; - - fail2: - efx_fini_channels(efx); - fail1: - return rc; -} - -/* Reset the NIC as transparently as possible. Do not reset the PHY - * Note that the reset may fail, in which case the card will be left - * in a most-probably-unusable state. - * - * This function will sleep. You cannot reset from within an atomic - * state; use efx_schedule_reset() instead. - * - * Grabs the dl_reset_lock, and to serialise with kernel interfaces the - * rtnl_lock. - */ -static int efx_reset(struct efx_nic *efx) -{ - struct ethtool_cmd ecmd; - enum reset_type method = efx->reset_pending; - int rc; - - /* Notify driverlink clients of imminent reset. */ - efx_dl_reset_lock(); + int rc, rc2; + bool disabled; + + /* Notify driverlink clients of imminent reset then serialise + * against other driver operations */ efx_dl_reset_suspend(efx); - /* Serialise with kernel interfaces */ - rtnl_lock(); - - /* If we're not RUNNING then don't reset. Leave the reset_pending - * flag set so that efx_pci_probe_main will be retried */ - if (efx->state != STATE_RUNNING) { - EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n"); - goto unlock_rtnl; - } - - efx->state = STATE_RESETTING; EFX_INFO(efx, "resetting (%s)\n", RESET_TYPE(method)); - /* The net_dev->get_stats handler is quite slow, and will fail - * if a fetch is pending over reset. Serialise against it. */ - spin_lock(&efx->stats_lock); - spin_unlock(&efx->stats_lock); - - efx_stop_all(efx); - mutex_lock(&efx->mac_lock); - - rc = efx_reset_down(efx, &ecmd); - if (rc) - goto fail1; - falcon_fini_nic(efx); - - rc = falcon_reset_hw(efx, method); + efx_reset_down(efx, method); + + rc = efx->type->reset(efx, method); if (rc) { EFX_ERR(efx, "failed to reset hardware\n"); - goto fail2; + goto out; } /* Allow resets to be rescheduled. */ @@ -1981,54 +2202,23 @@ * can respond to requests. */ pci_set_master(efx->pci_dev); - /* Reinitialise device. This is appropriate in the RESET_TYPE_DISABLE - * case so the driver can talk to external SRAM */ - rc = falcon_init_nic(efx); - if (rc) { - EFX_ERR(efx, "failed to initialise NIC\n"); - goto fail3; +out: + /* Leave device stopped if necessary */ + disabled = rc || method == RESET_TYPE_DISABLE; + rc2 = efx_reset_up(efx, method, !disabled); + if (rc2) { + disabled = true; + if (!rc) + rc = rc2; } - /* 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; + if (disabled) { + EFX_ERR(efx, "has been disabled\n"); + efx->state = STATE_DISABLED; + } else { + EFX_LOG(efx, "reset complete\n"); } - - rc = efx_reset_up(efx, &ecmd); - if (rc) - goto fail5; - - mutex_unlock(&efx->mac_lock); - EFX_LOG(efx, "reset complete\n"); - - efx->state = STATE_RUNNING; - efx_start_all(efx); - - unlock_rtnl: - rtnl_unlock(); - efx_dl_reset_resume(efx, 1); - efx_dl_reset_unlock(); - return 0; - - fail5: - fail4: - fail3: - fail2: - fail1: - EFX_ERR(efx, "has been disabled\n"); - efx->state = STATE_DISABLED; - - mutex_unlock(&efx->mac_lock); - rtnl_unlock(); - /* Remove the net_dev */ - efx_unregister_netdev(efx); - efx_fini_port(efx); - efx_dl_reset_resume(efx, 0); - efx_dl_reset_unlock(); + efx_dl_reset_resume(efx, !disabled); return rc; } @@ -2037,9 +2227,19 @@ */ static void efx_reset_work(struct work_struct *data) { - struct efx_nic *nic = container_of(data, struct efx_nic, reset_work); - - efx_reset(nic); + struct efx_nic *efx = container_of(data, struct efx_nic, reset_work); + + /* If we're not RUNNING then don't reset. Leave the reset_pending + * flag set so that efx_pci_probe_main will be retried */ + if (efx->state != STATE_RUNNING) { + EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n"); + return; + } + + rtnl_lock(); + if (efx_reset(efx, efx->reset_pending)) + dev_close(efx->net_dev); + rtnl_unlock(); } void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) @@ -2064,6 +2264,7 @@ case RESET_TYPE_TX_SKIP: method = RESET_TYPE_INVISIBLE; break; + case RESET_TYPE_MC_FAILURE: default: method = RESET_TYPE_ALL; break; @@ -2077,7 +2278,11 @@ efx->reset_pending = method; - queue_work(efx->reset_workqueue, &efx->reset_work); + /* efx_process_channel() will no longer read events once a + * reset is scheduled. So switch back to poll'd MCDI completions. */ + efx_mcdi_mode_poll(efx); + + queue_work(reset_workqueue, &efx->reset_work); } /************************************************************************** @@ -2088,18 +2293,24 @@ /* PCI device ID table */ static struct pci_device_id efx_pci_table[] __devinitdata = { +#ifndef __VMKERNEL_MODULE__ {PCI_DEVICE(EFX_VENDID_SFC, FALCON_A_P_DEVID), - .driver_data = (unsigned long) &falcon_a_nic_type}, + .driver_data = (unsigned long) &falcon_a1_nic_type}, +#endif {PCI_DEVICE(EFX_VENDID_SFC, FALCON_B_P_DEVID), - .driver_data = (unsigned long) &falcon_b_nic_type}, + .driver_data = (unsigned long) &falcon_b0_nic_type}, + {PCI_DEVICE(EFX_VENDID_SFC, BETHPAGE_A_P_DEVID), + .driver_data = (unsigned long) &siena_a0_nic_type}, + {PCI_DEVICE(EFX_VENDID_SFC, SIENA_A_P_DEVID), + .driver_data = (unsigned long) &siena_a0_nic_type}, {0} /* end of list */ }; /************************************************************************** * - * Dummy PHY/MAC/Board operations + * Dummy PHY/MAC operations * - * Can be used where the MAC does not implement this operation + * Can be used for some unimplemented operations * Needed so all function pointers are valid and do not have to be tested * before use * @@ -2109,38 +2320,19 @@ return 0; } void efx_port_dummy_op_void(struct efx_nic *efx) {} -void efx_port_dummy_op_blink(struct efx_nic *efx, int blink) {} - -static struct efx_mac_operations efx_dummy_mac_operations = { - .init = efx_port_dummy_op_int, - .reconfigure = efx_port_dummy_op_void, - .fini = efx_port_dummy_op_void, -}; +void efx_port_dummy_op_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ +} +bool efx_port_dummy_op_poll(struct efx_nic *efx) +{ + return false; +} static struct efx_phy_operations efx_dummy_phy_operations = { .init = efx_port_dummy_op_int, - .reconfigure = efx_port_dummy_op_void, - .check_hw = efx_port_dummy_op_int, + .reconfigure = efx_port_dummy_op_int, + .poll = efx_port_dummy_op_poll, .fini = efx_port_dummy_op_void, - .clear_interrupt = efx_port_dummy_op_void, - .reset_xaui = efx_port_dummy_op_void, -}; - -/* Dummy board operations */ -static int efx_nic_dummy_op_int(struct efx_nic *nic) -{ - return 0; -} - -static void efx_nic_dummy_op_void(struct efx_nic *nic) {} - -static struct efx_board efx_dummy_board_info = { - .init = efx_nic_dummy_op_int, - .init_leds = efx_port_dummy_op_int, - .set_fault_led = efx_port_dummy_op_blink, - .monitor = efx_nic_dummy_op_int, - .blink = efx_port_dummy_op_blink, - .fini = efx_nic_dummy_op_void, }; /************************************************************************** @@ -2153,51 +2345,58 @@ * efx_nic (including all sub-structures). */ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, - struct pci_dev *pci_dev) + struct pci_dev *pci_dev, struct net_device *net_dev) { struct efx_channel *channel; struct efx_tx_queue *tx_queue; struct efx_rx_queue *rx_queue; - int i, rc; + int i; /* Initialise common structures */ memset(efx, 0, sizeof(*efx)); spin_lock_init(&efx->biu_lock); - spin_lock_init(&efx->phy_lock); + mutex_init(&efx->mdio_lock); mutex_init(&efx->spi_lock); +#ifdef CONFIG_SFC_MTD + INIT_LIST_HEAD(&efx->mtd_list); +#endif INIT_WORK(&efx->reset_work, efx_reset_work); INIT_DELAYED_WORK(&efx->monitor_work, efx_monitor); efx->pci_dev = pci_dev; efx->state = STATE_INIT; efx->reset_pending = RESET_TYPE_NONE; strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); - efx->board_info = efx_dummy_board_info; - - efx->rx_checksum_enabled = 1; + + efx->net_dev = net_dev; + efx->rx_checksum_enabled = true; +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_SFC_LRO) && !defined(NETIF_F_LRO) + efx->lro_enabled = lro; +#endif spin_lock_init(&efx->netif_stop_lock); spin_lock_init(&efx->stats_lock); mutex_init(&efx->mac_lock); - efx->mac_op = &efx_dummy_mac_operations; + efx->mac_op = type->default_mac_ops; efx->phy_op = &efx_dummy_phy_operations; + efx->mdio.dev = net_dev; INIT_LIST_HEAD(&efx->dl_node); INIT_LIST_HEAD(&efx->dl_device_list); efx->dl_cb = efx_default_callbacks; - INIT_WORK(&efx->reconfigure_work, efx_reconfigure_work); + INIT_WORK(&efx->mac_work, efx_mac_work); atomic_set(&efx->netif_stop_count, 1); for (i = 0; i < EFX_MAX_CHANNELS; i++) { channel = &efx->channel[i]; channel->efx = efx; channel->channel = i; - channel->evqnum = i; - channel->work_pending = 0; + channel->work_pending = false; } - for (i = 0; i < EFX_MAX_TX_QUEUES; i++) { + for (i = 0; i < EFX_TX_QUEUE_COUNT; i++) { tx_queue = &efx->tx_queue[i]; tx_queue->efx = efx; tx_queue->queue = i; tx_queue->buffer = NULL; tx_queue->channel = &efx->channel[0]; /* for safety */ + tx_queue->tso_headers_free = NULL; } for (i = 0; i < EFX_MAX_RX_QUEUES; i++) { rx_queue = &efx->rx_queue[i]; @@ -2211,52 +2410,37 @@ efx->type = type; - /* Sanity-check NIC type */ - EFX_BUG_ON_PARANOID(efx->type->txd_ring_mask & - (efx->type->txd_ring_mask + 1)); - EFX_BUG_ON_PARANOID(efx->type->rxd_ring_mask & - (efx->type->rxd_ring_mask + 1)); - EFX_BUG_ON_PARANOID(efx->type->evq_size & - (efx->type->evq_size - 1)); /* As close as we can get to guaranteeing that we don't overflow */ - EFX_BUG_ON_PARANOID(efx->type->evq_size < - (efx->type->txd_ring_mask + 1 + - efx->type->rxd_ring_mask + 1)); + BUILD_BUG_ON(EFX_EVQ_SIZE < EFX_TXQ_SIZE + EFX_RXQ_SIZE); EFX_BUG_ON_PARANOID(efx->type->phys_addr_channels > EFX_MAX_CHANNELS); /* Higher numbered interrupt modes are less capable! */ efx->interrupt_mode = max(efx->type->max_interrupt_mode, interrupt_mode); - - efx->workqueue = create_singlethread_workqueue("sfc_work"); - if (!efx->workqueue) { - rc = -ENOMEM; - goto fail1; - } - - efx->reset_workqueue = create_singlethread_workqueue("sfc_reset"); - if (!efx->reset_workqueue) { - rc = -ENOMEM; - goto fail2; - } +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_DUMMY_MSIX) + if (efx->interrupt_mode == EFX_INT_MODE_MSIX) + efx->interrupt_mode = EFX_INT_MODE_MSI; +#endif + + /* Would be good to use the net_dev name, but we're too early */ +#if defined(EFX_USE_KCOMPAT) && defined(EFX_HAVE_WORKQUEUE_NAME_LIMIT) + snprintf(efx->workqueue_name, 10 + 1, "sfc%02x:%02x.%d", + pci_dev->bus->number, PCI_SLOT(pci_dev->devfn), + PCI_FUNC(pci_dev->devfn)); +#else + snprintf(efx->workqueue_name, sizeof(efx->workqueue_name), "sfc%s", + pci_name(pci_dev)); +#endif + efx->workqueue = create_singlethread_workqueue(efx->workqueue_name); + if (!efx->workqueue) + return -ENOMEM; return 0; - - fail2: - destroy_workqueue(efx->workqueue); - efx->workqueue = NULL; - - fail1: - return rc; } static void efx_fini_struct(struct efx_nic *efx) { - if (efx->reset_workqueue) { - destroy_workqueue(efx->reset_workqueue); - efx->reset_workqueue = NULL; - } if (efx->workqueue) { destroy_workqueue(efx->workqueue); efx->workqueue = NULL; @@ -2274,22 +2458,10 @@ */ static void efx_pci_remove_main(struct efx_nic *efx) { - EFX_ASSERT_RESET_SERIALISED(efx); - - /* Skip everything if we never obtained a valid membase */ - if (!efx->membase) - return; - + efx_nic_fini_interrupt(efx); efx_fini_channels(efx); efx_fini_port(efx); - - /* Shutdown the board, then the NIC and board state */ - efx->board_info.fini(efx); - falcon_fini_nic(efx); - falcon_fini_interrupt(efx); - efx->board_info.fini(efx); - - /* Tear down NAPI and LRO */ + efx->type->fini(efx); efx_fini_napi(efx); efx_remove_all(efx); } @@ -2305,38 +2477,38 @@ if (!efx) return; - /* Unregister driver from driverlink layer */ - efx_dl_unregister_nic(efx); - /* Mark the NIC as fini, then stop the interface */ rtnl_lock(); + efx_dl_unregister_nic(efx); efx->state = STATE_FINI; dev_close(efx->net_dev); /* Allow any queued efx_resets() to complete */ rtnl_unlock(); - if (efx->membase == NULL) - goto out; - efx_unregister_netdev(efx); + + efx_mtd_remove(efx); efx_fini_debugfs_channels(efx); /* Wait for any scheduled resets to complete. No more will be * scheduled from this point because efx_stop_all() has been * called, we are no longer registered with driverlink, and * the net_device's have been removed. */ - flush_workqueue(efx->reset_workqueue); +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_CANCEL_WORK_SYNC) + cancel_work_sync(&efx->reset_work); +#else + flush_workqueue(reset_workqueue); +#endif efx_pci_remove_main(efx); -out: efx_fini_io(efx); EFX_LOG(efx, "shutdown successful\n"); pci_set_drvdata(pci_dev, NULL); efx_fini_struct(efx); - kfree(efx); + free_netdev(efx->net_dev); }; /* Main body of NIC initialisation @@ -2351,65 +2523,38 @@ 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); + rc = efx->type->init(efx); if (rc) { - EFX_ERR(efx, "failed to initialise board\n"); + EFX_ERR(efx, "failed to initialise NIC\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 fail4; + } + + efx_init_channels(efx); + + rc = efx_nic_init_interrupt(efx); + if (rc) goto fail5; - } - - /* Initialise channels */ - rc = efx_init_channels(efx); - if (rc) - goto fail6; - - rc = falcon_init_interrupt(efx); - if (rc) - goto fail7; - - /* Start up device - interrupts can occur from this point */ - efx_start_all(efx); - - /* Check basic functionality and set interrupt mode */ - rc = efx_run_selftests(efx); - if (rc) - goto fail8; - - /* Stop the NIC */ + + return 0; + + fail6: efx_stop_all(efx); - - return 0; - - fail8: - efx_stop_all(efx); - falcon_fini_interrupt(efx); - fail7: + efx_nic_fini_interrupt(efx); + fail5: efx_fini_channels(efx); - fail6: efx_fini_port(efx); - fail5: - falcon_fini_nic(efx); fail4: - efx->board_info.fini(efx); + efx->type->fini(efx); fail3: efx_fini_napi(efx); fail2: @@ -2430,103 +2575,288 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *entry) { + struct efx_nic_type *type = (struct efx_nic_type *) entry->driver_data; + struct net_device *net_dev; struct efx_nic *efx; - struct efx_nic_type *type = (struct efx_nic_type *) entry->driver_data; int i, rc; - /* Allocate and initialise a struct efx_nic */ - efx = kmalloc(sizeof(*efx), GFP_KERNEL); - if (!efx) { - rc = -ENOMEM; + /* Allocate and initialise a struct net_device and struct efx_nic */ + net_dev = alloc_etherdev(sizeof(*efx)); + if (!net_dev) + return -ENOMEM; + net_dev->features |= (type->offload_features | NETIF_F_SG | + NETIF_F_HIGHDMA | NETIF_F_TSO); +#if !defined(EFX_USE_KCOMPAT) || defined(NETIF_F_TSO6) + if (type->offload_features & NETIF_F_V6_CSUM) + net_dev->features |= NETIF_F_TSO6; +#endif +#if defined(EFX_USE_GRO) + if (lro) + net_dev->features |= NETIF_F_GRO; +#endif +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_NETDEV_VLAN_FEATURES) + /* Mask for features that also apply to VLAN devices */ + net_dev->vlan_features |= (NETIF_F_ALL_CSUM | NETIF_F_SG | + NETIF_F_HIGHDMA | NETIF_F_TSO); +#endif + efx = netdev_priv(net_dev); + pci_set_drvdata(pci_dev, efx); + rc = efx_init_struct(efx, type, pci_dev, net_dev); + if (rc) goto fail1; - } - pci_set_drvdata(pci_dev, efx); - rc = efx_init_struct(efx, type, pci_dev); - if (rc) - goto fail2; - - EFX_INFO(efx, "Solarflare Communications NIC detected\n"); + + EFX_INFO(efx, "Solarflare Communications NIC detected PCI(%x:%x)\n", + pci_dev->vendor, pci_dev->device); /* Set up basic I/O (BAR mappings etc) */ rc = efx_init_io(efx); if (rc) - goto fail3; + goto fail2; /* No serialisation is required with the reset path because * we're in STATE_INIT. */ for (i = 0; i < 5; i++) { rc = efx_pci_probe_main(efx); - if (rc == 0) - break; /* Serialise against efx_reset(). No more resets will be * scheduled since efx_stop_all() has been called, and we * have not and never have been registered with either * the rtnetlink or driverlink layers. */ - flush_workqueue(efx->reset_workqueue); +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_CANCEL_WORK_SYNC) + cancel_work_sync(&efx->reset_work); +#else + flush_workqueue(reset_workqueue); +#endif + + if (rc == 0) { + if (efx->reset_pending != RESET_TYPE_NONE) { + /* If there was a scheduled reset during + * probe, the NIC is probably hosed anyway */ + efx_pci_remove_main(efx); + rc = -EIO; + } else { + break; + } + } /* Retry if a recoverably reset event has been scheduled */ if ((efx->reset_pending != RESET_TYPE_INVISIBLE) && - (efx->reset_pending != RESET_TYPE_ALL)) - goto fail4; + (efx->reset_pending != RESET_TYPE_ALL)) { + if (efx->reset_pending != RESET_TYPE_NONE) + EFX_ERR(efx, "Unrecoverable scheduled reset: " + "%s\n", RESET_TYPE(efx->reset_pending)); + goto fail3; + } efx->reset_pending = RESET_TYPE_NONE; } if (rc) { - EFX_ERR(efx, "Could not reset NIC\n"); - goto fail5; + EFX_ERR(efx, "Initialisation failed due to persistent reset\n"); + goto fail4; } /* Self-tests have all passed */ rc = efx_init_debugfs_channels(efx); if (rc) - goto fail6; - - /* Switch to the running state before we expose the device to - * the OS. This is to ensure that the initial gathering of - * MAC stats succeeds. */ - rtnl_lock(); + goto fail5; + + /* Switch to the running state before we expose the device to the OS, + * so that dev_open()|efx_start_all() will actually start the device */ efx->state = STATE_RUNNING; - rtnl_unlock(); rc = efx_register_netdev(efx); if (rc) - goto fail7; + goto fail6; EFX_LOG(efx, "initialisation successful\n"); - /* Register with driverlink layer */ - rc = efx_dl_register_nic(efx); - if (rc) - goto fail8; - + rtnl_lock(); + efx_mtd_probe(efx); /* allowed to fail */ + rtnl_unlock(); return 0; - fail8: - efx_unregister_netdev(efx); - fail7: + fail6: efx_fini_debugfs_channels(efx); - fail6: + fail5: efx_pci_remove_main(efx); - fail5: fail4: + fail3: efx_fini_io(efx); - fail3: + fail2: efx_fini_struct(efx); - fail2: - kfree(efx); fail1: EFX_LOG(efx, "initialisation failed. rc=%d\n", rc); + free_netdev(net_dev); return rc; } -/* PCI driver definition */ +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_PM) + +static int efx_pm_freeze(struct device *dev) +{ + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + + rtnl_lock(); + efx_dl_reset_suspend(efx); + rtnl_unlock(); + + efx->state = STATE_FINI; + + netif_device_detach(efx->net_dev); + + efx_stop_all(efx); + efx_fini_channels(efx); + + return 0; +} + +static int efx_pm_thaw(struct device *dev) +{ + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + + efx->state = STATE_INIT; + + efx_init_channels(efx); + + mutex_lock(&efx->mac_lock); + efx->phy_op->reconfigure(efx); + mutex_unlock(&efx->mac_lock); + + efx_start_all(efx); + + netif_device_attach(efx->net_dev); + + efx->state = STATE_RUNNING; + + rtnl_lock(); + efx_dl_reset_resume(efx, true); + rtnl_unlock(); + + efx->type->resume_wol(efx); + + return 0; +} + +static int efx_pm_poweroff(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct efx_nic *efx = pci_get_drvdata(pci_dev); + + efx->type->fini(efx); + + efx->reset_pending = RESET_TYPE_NONE; + + pci_save_state(pci_dev); + return pci_set_power_state(pci_dev, PCI_D3hot); +} + +/* Used for both resume and restore */ +static int efx_pm_resume(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct efx_nic *efx = pci_get_drvdata(pci_dev); + int rc; + + rc = pci_set_power_state(pci_dev, PCI_D0); + if (rc) + goto fail; + pci_restore_state(pci_dev); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_UNMASK_MSIX_VECTORS) + efx_unmask_msix_vectors(pci_dev); +#endif + rc = pci_enable_device(pci_dev); + if (rc) + goto fail; + pci_set_master(efx->pci_dev); + rc = efx->type->reset(efx, RESET_TYPE_ALL); + if (rc) + goto fail; + rc = efx->type->init(efx); + if (rc) + goto fail; + efx_pm_thaw(dev); + return 0; + +fail: + efx_dl_reset_resume(efx, false); + return rc; +} + +static int efx_pm_suspend(struct device *dev) +{ + int rc; + + efx_pm_freeze(dev); + rc = efx_pm_poweroff(dev); + if (rc) + efx_pm_resume(dev); + return rc; +} + +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_DEV_PM_OPS) + +static struct dev_pm_ops efx_pm_ops = { + .suspend = efx_pm_suspend, + .resume = efx_pm_resume, + .freeze = efx_pm_freeze, + .thaw = efx_pm_thaw, + .poweroff = efx_pm_poweroff, + .restore = efx_pm_resume, +}; + +#elif defined(EFX_USE_PM_EXT_OPS) + +static struct pm_ext_ops efx_pm_ops = { + .base = { + .suspend = efx_pm_suspend, + .resume = efx_pm_resume, + .freeze = efx_pm_freeze, + .thaw = efx_pm_thaw, + .poweroff = efx_pm_poweroff, + .restore = efx_pm_resume, + } +}; + +#else /* !EFX_USE_DEV_PM_OPS && !EFX_USE_PM_EXT_OPS */ + +static int efx_pm_old_suspend(struct pci_dev *dev, pm_message_t state) +{ + switch (state.event) { + case PM_EVENT_FREEZE: +#if defined(PM_EVENT_QUIESCE) + case PM_EVENT_QUIESCE: +#elif defined(PM_EVENT_PRETHAW) + case PM_EVENT_PRETHAW: +#endif + return efx_pm_freeze(&dev->dev); + default: + return efx_pm_suspend(&dev->dev); + } +} + +static int efx_pm_old_resume(struct pci_dev *dev) +{ + return efx_pm_resume(&dev->dev); +} + +#endif /* EFX_USE_PM_EXT_OPS */ + +#endif /* EFX_USE_PM */ + static struct pci_driver efx_pci_driver = { .name = EFX_DRIVER_NAME, .id_table = efx_pci_table, .probe = efx_pci_probe, .remove = efx_pci_remove, +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_DEV_PM_OPS) + .driver.pm = &efx_pm_ops, +#elif defined(EFX_USE_PM_EXT_OPS) + .pm = &efx_pm_ops, +#elif defined(EFX_USE_PM) + .suspend = efx_pm_old_suspend, + .resume = efx_pm_old_resume, +#endif }; /************************************************************************** @@ -2539,9 +2869,6 @@ 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; @@ -2561,6 +2888,27 @@ rc = -ENOMEM; goto err_refill; } + reset_workqueue = create_singlethread_workqueue("sfc_reset"); + if (!reset_workqueue) { + rc = -ENOMEM; + goto err_reset; + } + +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_LM87_DRIVER) + rc = i2c_add_driver(&efx_lm87_driver); + if (rc < 0) + goto err_lm87; +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_LM90_DRIVER) + rc = i2c_add_driver(&efx_lm90_driver); + if (rc < 0) + goto err_lm90; +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_I2C_NEW_DUMMY) + rc = i2c_add_driver(&efx_i2c_dummy_driver); + if (rc < 0) + goto err_i2c_dummy; +#endif rc = pci_register_driver(&efx_pci_driver); if (rc < 0) @@ -2569,6 +2917,20 @@ return 0; err_pci: +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_I2C_NEW_DUMMY) + i2c_del_driver(&efx_i2c_dummy_driver); + err_i2c_dummy: +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_LM90_DRIVER) + i2c_del_driver(&efx_lm90_driver); + err_lm90: +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_LM87_DRIVER) + i2c_del_driver(&efx_lm87_driver); + err_lm87: +#endif + destroy_workqueue(reset_workqueue); + err_reset: destroy_workqueue(refill_workqueue); err_refill: unregister_netdevice_notifier(&efx_netdev_notifier); @@ -2583,6 +2945,16 @@ printk(KERN_INFO "Solarflare NET driver unloading\n"); pci_unregister_driver(&efx_pci_driver); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_I2C_NEW_DUMMY) + i2c_del_driver(&efx_i2c_dummy_driver); +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_LM90_DRIVER) + i2c_del_driver(&efx_lm90_driver); +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_LM87_DRIVER) + i2c_del_driver(&efx_lm87_driver); +#endif + destroy_workqueue(reset_workqueue); destroy_workqueue(refill_workqueue); unregister_netdevice_notifier(&efx_netdev_notifier); efx_fini_debugfs(); @@ -2592,8 +2964,9 @@ module_init(efx_init_module); module_exit(efx_exit_module); -MODULE_AUTHOR("Michael Brown and " - "Solarflare Communications"); +MODULE_AUTHOR("Solarflare Communications and " + "Michael Brown "); MODULE_DESCRIPTION("Solarflare Communications network driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, efx_pci_table); +MODULE_VERSION(EFX_DRIVER_VERSION); diff -r aa133dee7792 drivers/net/sfc/efx.h --- a/drivers/net/sfc/efx.h +++ b/drivers/net/sfc/efx.h @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2006-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_EFX_H @@ -35,42 +18,103 @@ #define FALCON_A_P_DEVID 0x0703 #define FALCON_A_S_DEVID 0x6703 #define FALCON_B_P_DEVID 0x0710 +#define BETHPAGE_A_P_DEVID 0x0803 +#define SIENA_A_P_DEVID 0x0813 + +/* Solarstorm controllers use BAR 0 for I/O space and BAR 2(&3) for memory */ +#define EFX_MEM_BAR 2 /* TX */ -extern int efx_xmit(struct efx_nic *efx, - struct efx_tx_queue *tx_queue, struct sk_buff *skb); +extern int efx_probe_tx_queue(struct efx_tx_queue *tx_queue); +extern void efx_remove_tx_queue(struct efx_tx_queue *tx_queue); +extern void efx_init_tx_queue(struct efx_tx_queue *tx_queue); +extern void efx_fini_tx_queue(struct efx_tx_queue *tx_queue); +extern void efx_release_tx_buffers(struct efx_tx_queue *tx_queue); +extern netdev_tx_t +efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); +extern netdev_tx_t +efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +extern void fastcall efx_xmit_done(struct efx_tx_queue *tx_queue, + unsigned int index); +#else +extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); +#endif extern void efx_stop_queue(struct efx_nic *efx); extern void efx_wake_queue(struct efx_nic *efx); +#define EFX_TXQ_SIZE 1024 +#define EFX_TXQ_MASK (EFX_TXQ_SIZE - 1) /* RX */ -extern void fastcall efx_xmit_done(struct efx_tx_queue *tx_queue, - unsigned int index); +extern int efx_probe_rx_queue(struct efx_rx_queue *rx_queue); +extern void efx_remove_rx_queue(struct efx_rx_queue *rx_queue); +extern void efx_init_rx_queue(struct efx_rx_queue *rx_queue); +extern void efx_fini_rx_queue(struct efx_rx_queue *rx_queue); +extern void efx_rx_strategy(struct efx_channel *channel); +extern void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue); +extern void efx_rx_work(struct work_struct *data); +extern void __efx_rx_packet(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, bool checksummed); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) extern void fastcall efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, unsigned int len, - int checksummed, int discard); + bool checksummed, bool discard); +#else +extern void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, + unsigned int len, bool checksummed, bool discard); +#endif extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay); +#define EFX_RXQ_SIZE 1024 +#define EFX_RXQ_MASK (EFX_RXQ_SIZE - 1) /* Channels */ extern void efx_process_channel_now(struct efx_channel *channel); -extern int efx_flush_queues(struct efx_nic *efx); +#define EFX_EVQ_SIZE 4096 +#define EFX_EVQ_MASK (EFX_EVQ_SIZE - 1) /* Ports */ -extern void efx_reconfigure_port(struct efx_nic *efx); +extern int efx_reconfigure_port(struct efx_nic *efx); +extern int __efx_reconfigure_port(struct efx_nic *efx); + +/* Ethtool support */ +extern int efx_ethtool_get_settings(struct net_device *net_dev, + struct ethtool_cmd *ecmd); +extern int efx_ethtool_set_settings(struct net_device *net_dev, + struct ethtool_cmd *ecmd); +extern int efx_ethtool_reset(struct net_device *net_dev, u32 *flags); +extern const struct ethtool_ops efx_ethtool_ops; + +/* Reset handling */ +extern int efx_reset(struct efx_nic *efx, enum reset_type method); +extern void efx_reset_down(struct efx_nic *efx, enum reset_type method); +extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok); /* Global */ extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); -extern void efx_suspend(struct efx_nic *efx); -extern void efx_resume(struct efx_nic *efx); +extern void efx_start_all(struct efx_nic *efx); +extern void efx_stop_all(struct efx_nic *efx); extern void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, - int rx_usecs); + int rx_usecs, bool rx_adaptive); extern int efx_request_power(struct efx_nic *efx, int mw, const char *name); extern void efx_hex_dump(const u8 *, unsigned int, const char *); /* Dummy PHY ops for PHY drivers */ extern int efx_port_dummy_op_int(struct efx_nic *efx); extern void efx_port_dummy_op_void(struct efx_nic *efx); -extern void efx_port_dummy_op_blink(struct efx_nic *efx, int blink); +extern void +efx_port_dummy_op_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); +extern bool efx_port_dummy_op_poll(struct efx_nic *efx); +/* MTD */ +#ifdef CONFIG_SFC_MTD +extern int efx_mtd_probe(struct efx_nic *efx); +extern void efx_mtd_rename(struct efx_nic *efx); +extern void efx_mtd_remove(struct efx_nic *efx); +#else +static inline int efx_mtd_probe(struct efx_nic *efx) { return 0; } +static inline void efx_mtd_rename(struct efx_nic *efx) {} +static inline void efx_mtd_remove(struct efx_nic *efx) {} +#endif extern unsigned int efx_monitor_interval; @@ -78,10 +122,13 @@ { EFX_TRACE(channel->efx, "channel %d scheduling NAPI poll on CPU%d\n", channel->channel, raw_smp_processor_id()); - channel->work_pending = 1; + channel->work_pending = true; - if (!test_and_set_bit(__LINK_STATE_RX_SCHED, &channel->napi_dev->state)) - __netif_rx_schedule(channel->napi_dev); + napi_schedule(&channel->napi_str); } +extern void efx_link_status_changed(struct efx_nic *efx); +extern void efx_link_set_advertising(struct efx_nic *efx, u32); +extern void efx_link_set_wanted_fc(struct efx_nic *efx, enum efx_fc_type); + #endif /* EFX_EFX_H */ diff -r aa133dee7792 drivers/net/sfc/efx_ioctl.h --- /dev/null +++ b/drivers/net/sfc/efx_ioctl.h @@ -0,0 +1,71 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2005-2006: Fen Systems Ltd. + * Copyright 2006-2009: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Initially developed by Michael Brown + * 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_IOCTL_H +#define EFX_IOCTL_H + +#if defined(__KERNEL__) +#include +#else +#include +#ifndef _LINUX_IF_H +#define _LINUX_IF_H /* prevent from conflicting with */ +#endif +#endif +#include +#include + +/* + * Efx private ioctls + */ + +/* Reset selected components, like ETHTOOL_RESET ****************************/ +#define EFX_RESET_FLAGS 0xef0d +struct efx_reset_flags { + __u32 flags; +}; + +/* Efx private ioctl command structures *************************************/ + +union efx_ioctl_data { + struct efx_reset_flags reset_flags; +}; + +struct efx_sock_ioctl { + /* Command to run */ + __u16 cmd; + __u16 reserved; + /* Parameters */ + union efx_ioctl_data u; +}; + +#ifdef __KERNEL__ +extern int efx_private_ioctl(struct efx_nic *efx, u16 cmd, + union efx_ioctl_data __user *data); +#endif + +#endif /* EFX_IOCTL_H */ diff -r aa133dee7792 drivers/net/sfc/enum.h --- a/drivers/net/sfc/enum.h +++ b/drivers/net/sfc/enum.h @@ -1,26 +1,10 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2007: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Developed by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2007-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_ENUM_H @@ -29,57 +13,116 @@ /** * enum efx_loopback_mode - loopback modes * @LOOPBACK_NONE: no loopback - * @LOOPBACK_NEAR: loopback nearest to bus - * @LOOPBACK_MAC: loopback within MAC unspecified level - * @LOOPBACK_XGMII: loopback within MAC at XGMII level - * @LOOPBACK_XGXS: loopback within MAC at XGXS level - * @LOOPBACK_XAUI: loopback within MAC at XAUI level - * @LOOPBACK_PHY: loopback within PHY unspecified level - * @LOOPBACK_PHYXS: loopback within PHY at PHYXS level - * @LOOPBACK_PCS: loopback within PHY at PCS level - * @LOOPBACK_PMAPMD: loopback within PHY at PMAPMD level - * @LOOPBACK_FAR: loopback furthest from bus - * @LOOPBACK_NETWORK: reflecting loopback (even further than furthest!) + * @LOOPBACK_DATA: data path loopback + * @LOOPBACK_GMAC: loopback within GMAC + * @LOOPBACK_XGMII: loopback after XMAC + * @LOOPBACK_XGXS: loopback within BPX after XGXS + * @LOOPBACK_XAUI: loopback within BPX before XAUI serdes + * @LOOPBACK_GMII: loopback within BPX after GMAC + * @LOOPBACK_SGMII: loopback within BPX within SGMII + * @LOOPBACK_XGBR: loopback within BPX within XGBR + * @LOOPBACK_XFI: loopback within BPX before XFI serdes + * @LOOPBACK_XAUI_FAR: loopback within BPX after XAUI serdes + * @LOOPBACK_GMII_FAR: loopback within BPX before SGMII + * @LOOPBACK_SGMII_FAR: loopback within BPX after SGMII + * @LOOPBACK_XFI_FAR: loopback after XFI serdes + * @LOOPBACK_GPHY: loopback within 1G PHY at unspecified level + * @LOOPBACK_PHYXS: loopback within 10G PHY at PHYXS level + * @LOOPBACK_PCS: loopback within 10G PHY at PCS level + * @LOOPBACK_PMAPMD: loopback within 10G PHY at PMAPMD level + * @LOOPBACK_XPORT: cross port loopback + * @LOOPBACK_XGMII_WS: wireside loopback excluding XMAC + * @LOOPBACK_XAUI_WS: wireside loopback within BPX within XAUI serdes + * @LOOPBACK_XAUI_WS_FAR: wireside loopback within BPX including XAUI serdes + * @LOOPBACK_XAUI_WS_NEAR: wireside loopback within BPX excluding XAUI serdes + * @LOOPBACK_GMII_WS: wireside loopback excluding GMAC + * @LOOPBACK_XFI_WS: wireside loopback excluding XFI serdes + * @LOOPBACK_XFI_WS_FAR: wireside loopback including XFI serdes + * @LOOPBACK_PHYXS_WS: wireside loopback within 10G PHY at PHYXS level */ -/* Please keep in order and up-to-date w.r.t the following two #defines */ +/* Please keep up-to-date w.r.t the following two #defines */ enum efx_loopback_mode { LOOPBACK_NONE = 0, - LOOPBACK_NEAR = 1, - LOOPBACK_MAC = 2, + LOOPBACK_DATA = 1, + LOOPBACK_GMAC = 2, LOOPBACK_XGMII = 3, LOOPBACK_XGXS = 4, LOOPBACK_XAUI = 5, - LOOPBACK_PHY = 6, - LOOPBACK_PHYXS = 7, - LOOPBACK_PCS = 8, - LOOPBACK_PMAPMD = 9, - LOOPBACK_FAR = 10, - LOOPBACK_NETWORK = 11, + LOOPBACK_GMII = 6, + LOOPBACK_SGMII = 7, + LOOPBACK_XGBR = 8, + LOOPBACK_XFI = 9, + LOOPBACK_XAUI_FAR = 10, + LOOPBACK_GMII_FAR = 11, + LOOPBACK_SGMII_FAR = 12, + LOOPBACK_XFI_FAR = 13, + LOOPBACK_GPHY = 14, + LOOPBACK_PHYXS = 15, + LOOPBACK_PCS = 16, + LOOPBACK_PMAPMD = 17, + LOOPBACK_XPORT = 18, + LOOPBACK_XGMII_WS = 19, + LOOPBACK_XAUI_WS = 20, + LOOPBACK_XAUI_WS_FAR = 21, + LOOPBACK_XAUI_WS_NEAR = 22, + LOOPBACK_GMII_WS = 23, + LOOPBACK_XFI_WS = 24, + LOOPBACK_XFI_WS_FAR = 25, + LOOPBACK_PHYXS_WS = 26, LOOPBACK_MAX }; -#define LOOPBACK_TEST_MAX LOOPBACK_FAR +#define LOOPBACK_TEST_MAX LOOPBACK_PMAPMD /* These loopbacks occur within the controller */ -#define LOOPBACKS_10G_INTERNAL ((1 << LOOPBACK_XGMII)| \ - (1 << LOOPBACK_XGXS) | \ - (1 << LOOPBACK_XAUI)) +#define LOOPBACKS_INTERNAL ((1 << LOOPBACK_DATA) | \ + (1 << LOOPBACK_GMAC) | \ + (1 << LOOPBACK_XGMII)| \ + (1 << LOOPBACK_XGXS) | \ + (1 << LOOPBACK_XAUI) | \ + (1 << LOOPBACK_GMII) | \ + (1 << LOOPBACK_SGMII) | \ + (1 << LOOPBACK_SGMII) | \ + (1 << LOOPBACK_XGBR) | \ + (1 << LOOPBACK_XFI) | \ + (1 << LOOPBACK_XAUI_FAR) | \ + (1 << LOOPBACK_GMII_FAR) | \ + (1 << LOOPBACK_SGMII_FAR) | \ + (1 << LOOPBACK_XFI_FAR) | \ + (1 << LOOPBACK_XGMII_WS) | \ + (1 << LOOPBACK_XAUI_WS) | \ + (1 << LOOPBACK_XAUI_WS_FAR) | \ + (1 << LOOPBACK_XAUI_WS_NEAR) | \ + (1 << LOOPBACK_GMII_WS) | \ + (1 << LOOPBACK_XFI_WS) | \ + (1 << LOOPBACK_XFI_WS_FAR)) -#define LOOPBACKS_1G_INTERNAL (1 << LOOPBACK_MAC) +#define LOOPBACKS_WS ((1 << LOOPBACK_XGMII_WS) | \ + (1 << LOOPBACK_XAUI_WS) | \ + (1 << LOOPBACK_XAUI_WS_FAR) | \ + (1 << LOOPBACK_XAUI_WS_NEAR) | \ + (1 << LOOPBACK_GMII_WS) | \ + (1 << LOOPBACK_XFI_WS) | \ + (1 << LOOPBACK_XFI_WS_FAR) | \ + (1 << LOOPBACK_PHYXS_WS)) + +#define LOOPBACKS_EXTERNAL(_efx) \ + ((_efx)->loopback_modes & ~LOOPBACKS_INTERNAL & \ + ~(1 << LOOPBACK_NONE)) #define LOOPBACK_MASK(_efx) \ (1 << (_efx)->loopback_mode) -#define LOOPBACK_INTERNAL(_efx) \ - (((LOOPBACKS_10G_INTERNAL | LOOPBACKS_1G_INTERNAL) & \ - LOOPBACK_MASK(_efx)) ? 1 : 0) +#define LOOPBACK_INTERNAL(_efx) \ + (!!(LOOPBACKS_INTERNAL & LOOPBACK_MASK(_efx))) -#define LOOPBACK_CHANGED(_from, _to, _mask) \ - ((LOOPBACK_MASK(_from) ^ LOOPBACK_MASK(_to)) & \ - (_mask) ? 1 : 0) +#define LOOPBACK_EXTERNAL(_efx) \ + (!!(LOOPBACK_MASK(_efx) & LOOPBACKS_EXTERNAL(_efx))) -#define LOOPBACK_OUT_OF(_from, _to, _mask) \ - (((LOOPBACK_MASK(_from) & (_mask)) && \ - ((LOOPBACK_MASK(_to) & (_mask)) == 0)) ? 1 : 0) +#define LOOPBACK_CHANGED(_from, _to, _mask) \ + (!!((LOOPBACK_MASK(_from) ^ LOOPBACK_MASK(_to)) & (_mask))) + +#define LOOPBACK_OUT_OF(_from, _to, _mask) \ + ((LOOPBACK_MASK(_from) & (_mask)) && !(LOOPBACK_MASK(_to) & (_mask))) /*****************************************************************************/ @@ -95,12 +138,13 @@ * @RESET_TYPE_ALL: reset everything but PCI core blocks * @RESET_TYPE_WORLD: reset everything, save & restore PCI config * @RESET_TYPE_DISABLE: disable NIC - * @RESET_TYPE_MONITOR: reset due to hardware monitor + * @RESET_TYPE_TX_WATCHDOG: reset due to TX watchdog * @RESET_TYPE_INT_ERROR: reset due to internal error * @RESET_TYPE_RX_RECOVERY: reset to recover from RX datapath errors * @RESET_TYPE_RX_DESC_FETCH: pcie error during rx descriptor fetch * @RESET_TYPE_TX_DESC_FETCH: pcie error during tx descriptor fetch * @RESET_TYPE_TX_SKIP: hardware completed empty tx descriptors + * @RESET_TYPE_MC_FAILURE: MC reboot/assertion */ enum reset_type { RESET_TYPE_NONE = -1, @@ -109,12 +153,13 @@ RESET_TYPE_WORLD = 2, RESET_TYPE_DISABLE = 3, RESET_TYPE_MAX_METHOD, - RESET_TYPE_MONITOR, + RESET_TYPE_TX_WATCHDOG, RESET_TYPE_INT_ERROR, RESET_TYPE_RX_RECOVERY, RESET_TYPE_RX_DESC_FETCH, RESET_TYPE_TX_DESC_FETCH, RESET_TYPE_TX_SKIP, + RESET_TYPE_MC_FAILURE, RESET_TYPE_MAX, }; diff -r aa133dee7792 drivers/net/sfc/ethtool.c --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -1,41 +1,23 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2006-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include #include #include #include "net_driver.h" +#include "workarounds.h" #include "selftest.h" #include "efx.h" -#include "ethtool.h" -#include "falcon.h" -#include "gmii.h" - -static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable); +#include "nic.h" +#include "spi.h" +#include "mdio_10g.h" struct ethtool_string { char name[ETH_GSTRING_LEN]; @@ -170,12 +152,25 @@ EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tcp_udp_chksum_err), + EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_eth_crc_err), + EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_mcast_mismatch), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_char_error_lane0), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_char_error_lane1), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_char_error_lane2), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_char_error_lane3), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_disp_error_lane0), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_disp_error_lane1), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_disp_error_lane2), + EFX_ETHTOOL_ULONG_MAC_STAT(rx_disp_error_lane3), + EFX_ETHTOOL_U64_MAC_STAT(rx_match_fault), }; /* Number of ethtool statistics */ #define EFX_ETHTOOL_NUM_STATS ARRAY_SIZE(efx_ethtool_stats) +#define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB + /************************************************************************** * * Ethtool operations @@ -184,13 +179,19 @@ */ /* Identify device by flashing LEDs */ -static int efx_ethtool_phys_id(struct net_device *net_dev, u32 seconds) +static int efx_ethtool_phys_id(struct net_device *net_dev, u32 count) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); - efx->board_info.blink(efx, 1); - schedule_timeout_interruptible(seconds * HZ); - efx->board_info.blink(efx, 0); + do { + efx->type->set_id_led(efx, EFX_LED_ON); + schedule_timeout_interruptible(HZ / 2); + + efx->type->set_id_led(efx, EFX_LED_OFF); + schedule_timeout_interruptible(HZ / 2); + } while (!signal_pending(current) && --count != 0); + + efx->type->set_id_led(efx, EFX_LED_DEFAULT); return 0; } @@ -198,41 +199,75 @@ int efx_ethtool_get_settings(struct net_device *net_dev, struct ethtool_cmd *ecmd) { - struct efx_nic *efx = net_dev->priv; - int rc; + struct efx_nic *efx = netdev_priv(net_dev); + struct efx_link_state *link_state = &efx->link_state; - if (!in_interrupt()) - mutex_lock(&efx->mac_lock); - rc = efx->mac_op->get_settings(efx, ecmd); - if (!in_interrupt()) - mutex_unlock(&efx->mac_lock); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_BONDING_HACKS) + if (in_interrupt()) { + memset(ecmd, 0, sizeof(*ecmd)); + ecmd->speed = link_state->speed; + ecmd->duplex = link_state->fd ? DUPLEX_FULL : DUPLEX_HALF; + return 0; + } +#endif - return rc; + mutex_lock(&efx->mac_lock); + efx->phy_op->get_settings(efx, ecmd); + mutex_unlock(&efx->mac_lock); + + /* GMAC does not support 1000Mbps HD */ + ecmd->supported &= ~SUPPORTED_1000baseT_Half; + /* Both MACs support pause frames (bidirectional and respond-only) */ + ecmd->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + + if (LOOPBACK_INTERNAL(efx)) { + ecmd->speed = link_state->speed; + ecmd->duplex = link_state->fd ? DUPLEX_FULL : DUPLEX_HALF; + } + + return 0; } /* This must be called with rtnl_lock held. */ int efx_ethtool_set_settings(struct net_device *net_dev, struct ethtool_cmd *ecmd) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); int rc; + /* Older versions of ethtool don't set all the right bits when + * turning autoneg on with no speed/duplex specified. But they + * always set more than one bit in this case, so test for that. + * Allow overriding this in ethtool 6 by setting + * ADVERTISED_Autoneg = 0x40. + */ + if (ecmd->advertising & (ecmd->advertising - 1) && + !(ecmd->advertising & ADVERTISED_Autoneg)) + ecmd->advertising = ecmd->supported; + + /* GMAC does not support 1000Mbps HD */ + if (ecmd->speed == SPEED_1000 && ecmd->duplex != DUPLEX_FULL) { + EFX_LOG(efx, "rejecting unsupported 1000Mbps HD" + " setting\n"); + return -EINVAL; + } + mutex_lock(&efx->mac_lock); - rc = efx->mac_op->set_settings(efx, ecmd); + rc = efx->phy_op->set_settings(efx, ecmd); mutex_unlock(&efx->mac_lock); - if (!rc) - efx_reconfigure_port(efx); - return rc; } static void efx_ethtool_get_drvinfo(struct net_device *net_dev, struct ethtool_drvinfo *info) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); strlcpy(info->driver, EFX_DRIVER_NAME, sizeof(info->driver)); strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version)); + if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) + siena_print_fwver(efx, info->fw_version, + sizeof(info->fw_version)); strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); } @@ -242,10 +277,10 @@ * @strings: Ethtool strings, or %NULL * @data: Ethtool test results, or %NULL * @test: Pointer to test result (used only if data != %NULL) - * @unit_format: Unit name format (e.g. "channel\%d") - * @unit_id: Unit id (e.g. 0 for "channel0") + * @unit_format: Unit name format (e.g. "chan\%d") + * @unit_id: Unit id (e.g. 0 for "chan0") * @test_format: Test name format (e.g. "loopback.\%s.tx.sent") - * @test_id: Test id (e.g. "PHY" for "loopback.PHY.tx_sent") + * @test_id: Test id (e.g. "PHYXS" for "loopback.PHYXS.tx_sent") * * Fill in an individual self-test entry. */ @@ -262,17 +297,21 @@ /* Fill string, if applicable */ if (strings) { - snprintf(unit_str.name, sizeof(unit_str.name), - unit_format, unit_id); + if (strchr(unit_format, '%')) + snprintf(unit_str.name, sizeof(unit_str.name), + unit_format, unit_id); + else + strcpy(unit_str.name, unit_format); snprintf(test_str.name, sizeof(test_str.name), test_format, test_id); snprintf(strings[test_index].name, sizeof(strings[test_index].name), - "%-9s%-17s", unit_str.name, test_str.name); + "%-6s %-24s", unit_str.name, test_str.name); } } -#define EFX_PORT_NAME "port%d", 0 +#define EFX_LOOPBACK_NAME(_mode, _counter) \ + "loopback.%s." _counter, STRING_TABLE_LOOKUP(_mode, efx_loopback_mode) /** * efx_fill_loopback_test - fill in a block of loopback self-test entries @@ -298,24 +337,20 @@ efx_fill_test(test_index++, strings, data, &lb_tests->tx_sent[tx_queue->queue], EFX_TX_QUEUE_NAME(tx_queue), - "loopback.%s.tx_sent", - efx_loopback_mode_names[mode]); + EFX_LOOPBACK_NAME(mode, "tx_sent")); efx_fill_test(test_index++, strings, data, &lb_tests->tx_done[tx_queue->queue], EFX_TX_QUEUE_NAME(tx_queue), - "loopback.%s.tx_done", - efx_loopback_mode_names[mode]); + EFX_LOOPBACK_NAME(mode, "tx_done")); } efx_fill_test(test_index++, strings, data, &lb_tests->rx_good, - EFX_PORT_NAME, - "loopback.%s.rx_good", - efx_loopback_mode_names[mode]); + "rx", 0, + EFX_LOOPBACK_NAME(mode, "rx_good")); efx_fill_test(test_index++, strings, data, &lb_tests->rx_bad, - EFX_PORT_NAME, - "loopback.%s.rx_bad", - efx_loopback_mode_names[mode]); + "rx", 0, + EFX_LOOPBACK_NAME(mode, "rx_bad")); return test_index; } @@ -339,10 +374,13 @@ u64 *data) { struct efx_channel *channel; - unsigned int n = 0; + unsigned int n = 0, i; enum efx_loopback_mode mode; - /* Interrupt */ + efx_fill_test(n++, strings, data, &tests->phy_alive, + "phy", 0, "alive", NULL); + efx_fill_test(n++, strings, data, &tests->nvram, + "core", 0, "nvram", NULL); efx_fill_test(n++, strings, data, &tests->interrupt, "core", 0, "interrupt", NULL); @@ -362,16 +400,29 @@ "eventq.poll", NULL); } - /* PHY presence */ - efx_fill_test(n++, strings, data, &tests->phy_ok, - EFX_PORT_NAME, "phy_ok", NULL); + efx_fill_test(n++, strings, data, &tests->memory, + "core", 0, "memory", NULL); + efx_fill_test(n++, strings, data, &tests->registers, + "core", 0, "registers", NULL); + + if (efx->phy_op->run_tests != NULL) { + EFX_BUG_ON_PARANOID(efx->phy_op->test_name == NULL); + + for (i = 0; true; ++i) { + const char *name; + + EFX_BUG_ON_PARANOID(i >= EFX_MAX_PHY_TESTS); + name = efx->phy_op->test_name(efx, i); + if (name == NULL) + break; + + efx_fill_test(n++, strings, data, &tests->phy_ext[i], + "phy", 0, name, NULL); + } + } /* Loopback tests */ - efx_fill_test(n++, strings, data, &tests->loopback_speed, - EFX_PORT_NAME, "loopback.speed", NULL); - efx_fill_test(n++, strings, data, &tests->loopback_full_duplex, - EFX_PORT_NAME, "loopback.full_duplex", NULL); - for (mode = LOOPBACK_NONE; mode < LOOPBACK_TEST_MAX; mode++) { + for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) { if (!(efx->loopback_modes & (1 << mode))) continue; n = efx_fill_loopback_test(efx, @@ -382,22 +433,35 @@ return n; } +static int efx_ethtool_get_sset_count(struct net_device *net_dev, + int string_set) +{ + switch (string_set) { + case ETH_SS_STATS: + return EFX_ETHTOOL_NUM_STATS; + case ETH_SS_TEST: + return efx_ethtool_fill_self_tests(netdev_priv(net_dev), + NULL, NULL, NULL); + default: + return -EINVAL; + } +} + +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_ETHTOOL_GET_SSET_COUNT) static int efx_ethtool_get_stats_count(struct net_device *net_dev) { - return EFX_ETHTOOL_NUM_STATS; + return efx_ethtool_get_sset_count(net_dev, ETH_SS_STATS); } - static int efx_ethtool_self_test_count(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; - - return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL); + return efx_ethtool_get_sset_count(net_dev, ETH_SS_TEST); } +#endif static void efx_ethtool_get_strings(struct net_device *net_dev, u32 string_set, u8 *strings) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct ethtool_string *ethtool_strings = (struct ethtool_string *)strings; int i; @@ -423,7 +487,7 @@ struct ethtool_stats *stats __attribute__ ((unused)), u64 *data) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_mac_stats *mac_stats = &efx->mac_stats; struct efx_ethtool_stat *stat; struct efx_channel *channel; @@ -432,7 +496,7 @@ EFX_BUG_ON_PARANOID(stats->n_stats != EFX_ETHTOOL_NUM_STATS); /* Update MAC and NIC statistics */ - net_dev->get_stats(net_dev); + dev_get_stats(net_dev); /* Fill detailed statistics buffer */ for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) { @@ -455,35 +519,53 @@ } } +static int efx_ethtool_set_tso(struct net_device *net_dev, u32 enable) +{ + struct efx_nic *efx __attribute__ ((unused)) = netdev_priv(net_dev); + unsigned long features; + + features = NETIF_F_TSO; +#if !defined(EFX_USE_KCOMPAT) || defined(NETIF_F_TSO6) + if (efx->type->offload_features & NETIF_F_V6_CSUM) + features |= NETIF_F_TSO6; +#endif + + if (enable) + net_dev->features |= features; + else + net_dev->features &= ~features; + + return 0; +} + static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) { - struct efx_nic *efx = net_dev->priv; - int rc; + struct efx_nic *efx = netdev_priv(net_dev); + unsigned long features = efx->type->offload_features & NETIF_F_ALL_CSUM; - rc = ethtool_op_set_tx_csum(net_dev, enable); - if (rc) - return rc; - - efx_flush_queues(efx); + if (enable) + net_dev->features |= features; + else + net_dev->features &= ~features; return 0; } static int efx_ethtool_set_rx_csum(struct net_device *net_dev, u32 enable) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); /* No way to stop the hardware doing the checks; we just * ignore the result. */ - efx->rx_checksum_enabled = (enable ? 1 : 0); + efx->rx_checksum_enabled = !!enable; return 0; } static u32 efx_ethtool_get_rx_csum(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); return efx->rx_checksum_enabled; } @@ -491,71 +573,48 @@ static void efx_ethtool_self_test(struct net_device *net_dev, struct ethtool_test *test, u64 *data) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_self_tests efx_tests; - int offline, already_up; + bool already_up; int rc; - /* Make sure we've got rtnl lock since we're playing with - * interrupts, and calling efx_process_channel_now and others - */ ASSERT_RTNL(); - - /* If the NIC isn't in the RUNNING state then exit */ if (efx->state != STATE_RUNNING) { - rc = -EIO; - goto fail1; + rc = -EBUSY; + goto out; } - /* 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. */ + if (test->flags & ETH_TEST_FL_OFFLINE) + efx_dl_reset_suspend(efx); + + /* We need rx buffers and interrupts. */ already_up = (efx->net_dev->flags & IFF_UP); if (!already_up) { rc = dev_open(efx->net_dev); if (rc) { EFX_ERR(efx, "failed opening device.\n"); - goto fail2; + goto out_resume; } } memset(&efx_tests, 0, sizeof(efx_tests)); - offline = (test->flags & ETH_TEST_FL_OFFLINE); - /* Perform online self tests first */ - rc = efx_online_test(efx, &efx_tests); - if (rc) - goto out; + rc = efx_selftest(efx, &efx_tests, test->flags); - /* Perform offline tests only if online tests passed */ - if (offline) { - /* Stop the kernel from sending packets during the test. The - * selftest will be consistently bringing the port up and down - * as it moves between loopback modes, so the watchdog timer - * probably won't run anyway */ - efx_stop_queue(efx); - - rc = efx_flush_queues(efx); - if (rc != 0) - goto out_offline; - - rc = efx_offline_test(efx, &efx_tests, - efx->loopback_modes); - out_offline: - efx_wake_queue(efx); - } - - /* fall-thru */ - out: if (!already_up) dev_close(efx->net_dev); - EFX_LOG(efx, "%s all %sline self-tests\n", - rc == 0 ? "passed" : "failed", offline ? "off" : "on"); + EFX_LOG(efx, "%s %sline self-tests\n", + rc == 0 ? "passed" : "failed", + (test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on"); - fail2: - fail1: - /* Fill ethtool results structures */ +out_resume: + if (test->flags & ETH_TEST_FL_OFFLINE) { + /* Return success because if efx_reset_up() failed, an + * EFX_RESET_DISABLE reset will have been scheduled */ + efx_dl_reset_resume(efx, true); + } +out: efx_ethtool_fill_self_tests(efx, &efx_tests, NULL, data); if (rc) test->flags |= ETH_TEST_FL_FAILED; @@ -564,24 +623,78 @@ /* Restart autonegotiation */ static int efx_ethtool_nway_reset(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); - return mii_nway_restart(&efx->mii); + return mdio45_nway_restart(&efx->mdio); } static u32 efx_ethtool_get_link(struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); - return efx->link_up; + return efx->link_state.up; +} + +static int efx_ethtool_get_eeprom_len(struct net_device *net_dev) +{ + struct efx_nic *efx = netdev_priv(net_dev); + struct efx_spi_device *spi = efx->spi_eeprom; + + if (!spi) + return 0; + return min(spi->size, EFX_EEPROM_BOOTCONFIG_END) - + min(spi->size, EFX_EEPROM_BOOTCONFIG_START); +} + +static int efx_ethtool_get_eeprom(struct net_device *net_dev, + struct ethtool_eeprom *eeprom, u8 *buf) +{ + struct efx_nic *efx = netdev_priv(net_dev); + struct efx_spi_device *spi = efx->spi_eeprom; + size_t len; + int rc; + + rc = mutex_lock_interruptible(&efx->spi_lock); + if (rc) + return rc; + rc = falcon_spi_read(efx, spi, + eeprom->offset + EFX_EEPROM_BOOTCONFIG_START, + eeprom->len, &len, buf); + mutex_unlock(&efx->spi_lock); + + eeprom->magic = EFX_ETHTOOL_EEPROM_MAGIC; + eeprom->len = len; + return rc; +} + +static int efx_ethtool_set_eeprom(struct net_device *net_dev, + struct ethtool_eeprom *eeprom, u8 *buf) +{ + struct efx_nic *efx = netdev_priv(net_dev); + struct efx_spi_device *spi = efx->spi_eeprom; + size_t len; + int rc; + + if (eeprom->magic != EFX_ETHTOOL_EEPROM_MAGIC) + return -EINVAL; + + rc = mutex_lock_interruptible(&efx->spi_lock); + if (rc) + return rc; + rc = falcon_spi_write(efx, spi, + eeprom->offset + EFX_EEPROM_BOOTCONFIG_START, + eeprom->len, &len, buf); + mutex_unlock(&efx->spi_lock); + + eeprom->len = len; + return rc; } static int efx_ethtool_get_coalesce(struct net_device *net_dev, struct ethtool_coalesce *coalesce) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_tx_queue *tx_queue; - struct efx_rx_queue *rx_queue; struct efx_channel *channel; memset(coalesce, 0, sizeof(*coalesce)); @@ -599,14 +712,11 @@ } } - /* Find lowest IRQ moderation across all used RX queues */ - coalesce->rx_coalesce_usecs_irq = ~((u32) 0); - efx_for_each_rx_queue(rx_queue, efx) { - channel = rx_queue->channel; - if (channel->irq_moderation < coalesce->rx_coalesce_usecs_irq) - coalesce->rx_coalesce_usecs_irq = - channel->irq_moderation; - } + coalesce->use_adaptive_rx_coalesce = efx->irq_rx_adaptive; + coalesce->rx_coalesce_usecs_irq = efx->irq_rx_moderation; + + coalesce->tx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION; + coalesce->rx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION; return 0; } @@ -617,13 +727,12 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev, struct ethtool_coalesce *coalesce) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_channel *channel; struct efx_tx_queue *tx_queue; - unsigned tx_usecs, rx_usecs; + unsigned tx_usecs, rx_usecs, adaptive; - if (coalesce->use_adaptive_rx_coalesce || - coalesce->use_adaptive_tx_coalesce) + if (coalesce->use_adaptive_tx_coalesce) return -EOPNOTSUPP; if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) { @@ -634,6 +743,7 @@ rx_usecs = coalesce->rx_coalesce_usecs_irq; tx_usecs = coalesce->tx_coalesce_usecs_irq; + adaptive = coalesce->use_adaptive_rx_coalesce; /* If the channel is shared only allow RX parameters to be set */ efx_for_each_tx_queue(tx_queue, efx) { @@ -645,14 +755,9 @@ } } - efx_init_irq_moderation(efx, tx_usecs, rx_usecs); - - /* Reset channel to pick up new moderation value. Note that - * this may change the value of the irq_moderation field - * (e.g. to allow for hardware timer granularity). - */ + efx_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive); efx_for_each_channel(channel, efx) - falcon_set_int_moderation(channel); + efx->type->push_irq_moderation(channel); return 0; } @@ -660,54 +765,151 @@ 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; + struct efx_nic *efx = netdev_priv(net_dev); + enum efx_fc_type wanted_fc, old_fc; + u32 old_adv; + bool reset; + int rc = 0; - 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; + mutex_lock(&efx->mac_lock); - /* Try to push the pause parameters */ - mutex_lock(&efx->mac_lock); - rc = efx->mac_op->set_pause(efx, flow_control); + wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) | + (pause->tx_pause ? EFX_FC_TX : 0) | + (pause->autoneg ? EFX_FC_AUTO : 0)); + + if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) { + EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n"); + rc = -EINVAL; + goto out; + } + + if ((wanted_fc & EFX_FC_AUTO) && !efx->link_advertising) { + EFX_LOG(efx, "Autonegotiation is disabled\n"); + rc = -EINVAL; + goto out; + } + + /* TX flow control may automatically turn itself off if the + * link partner (intermittently) stops responding to pause + * frames. There isn't any indication that this has happened, + * so the best we do is leave it up to the user to spot this + * and fix it be cycling transmit flow control on this end. */ + reset = (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & EFX_FC_TX); + if (EFX_WORKAROUND_11482(efx) && reset) { + if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) { + /* Recover by resetting the EM block */ + falcon_stop_nic_stats(efx); + falcon_drain_tx_fifo(efx); + efx->mac_op->reconfigure(efx); + falcon_start_nic_stats(efx); + } else { + /* Schedule a reset to recover */ + efx_schedule_reset(efx, RESET_TYPE_INVISIBLE); + } + } + + old_adv = efx->link_advertising; + old_fc = efx->wanted_fc; + efx_link_set_wanted_fc(efx, wanted_fc); + if (efx->link_advertising != old_adv || + (efx->wanted_fc ^ old_fc) & EFX_FC_AUTO) { + rc = efx->phy_op->reconfigure(efx); + if (rc) { + EFX_ERR(efx, "Unable to advertise requested flow " + "control setting\n"); + goto out; + } + } + + /* Reconfigure the MAC. The PHY *may* generate a link state change event + * if the user just changed the advertised capabilities, but there's no + * harm doing this twice */ + efx->mac_op->reconfigure(efx); + +out: mutex_unlock(&efx->mac_lock); - if (!rc) - efx_reconfigure_port(efx); - return rc; } static void efx_ethtool_get_pauseparam(struct net_device *net_dev, struct ethtool_pauseparam *pause) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); - 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; + pause->rx_pause = !!(efx->wanted_fc & EFX_FC_RX); + pause->tx_pause = !!(efx->wanted_fc & EFX_FC_TX); + pause->autoneg = !!(efx->wanted_fc & EFX_FC_AUTO); } +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_ETHTOOL_GET_PERM_ADDR) static int efx_ethtool_op_get_perm_addr(struct net_device *net_dev, struct ethtool_perm_addr *addr, u8 *data) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); memcpy(data, efx->mac_address, ETH_ALEN); return 0; } +#endif -struct ethtool_ops efx_ethtool_ops = { + +static void efx_ethtool_get_wol(struct net_device *net_dev, + struct ethtool_wolinfo *wol) +{ + struct efx_nic *efx = netdev_priv(net_dev); + return efx->type->get_wol(efx, wol); +} + + +static int efx_ethtool_set_wol(struct net_device *net_dev, + struct ethtool_wolinfo *wol) +{ + struct efx_nic *efx = netdev_priv(net_dev); + return efx->type->set_wol(efx, wol->wolopts); +} + +extern int efx_ethtool_reset(struct net_device *net_dev, u32 *flags) +{ + struct efx_nic *efx = netdev_priv(net_dev); + enum reset_type method; + enum { + ETH_RESET_EFX_INVISIBLE = (ETH_RESET_DMA | ETH_RESET_FILTER | + ETH_RESET_OFFLOAD | ETH_RESET_MAC) + }; + + /* Check for minimal reset flags */ + if ((*flags & ETH_RESET_EFX_INVISIBLE) != ETH_RESET_EFX_INVISIBLE) + return -EINVAL; + *flags ^= ETH_RESET_EFX_INVISIBLE; + method = RESET_TYPE_INVISIBLE; + + if (*flags & ETH_RESET_PHY) { + *flags ^= ETH_RESET_PHY; + method = RESET_TYPE_ALL; + } + + if ((*flags & efx->type->reset_world_flags) == + efx->type->reset_world_flags) { + *flags ^= efx->type->reset_world_flags; + method = RESET_TYPE_WORLD; + } + + return efx_reset(efx, method); +} + +const struct ethtool_ops efx_ethtool_ops = { .get_settings = efx_ethtool_get_settings, .set_settings = efx_ethtool_set_settings, .get_drvinfo = efx_ethtool_get_drvinfo, .nway_reset = efx_ethtool_nway_reset, .get_link = efx_ethtool_get_link, + .get_eeprom_len = efx_ethtool_get_eeprom_len, + .get_eeprom = efx_ethtool_get_eeprom, + .set_eeprom = efx_ethtool_set_eeprom, .get_coalesce = efx_ethtool_get_coalesce, .set_coalesce = efx_ethtool_set_coalesce, .get_pauseparam = efx_ethtool_get_pauseparam, @@ -715,14 +917,33 @@ .get_rx_csum = efx_ethtool_get_rx_csum, .set_rx_csum = efx_ethtool_set_rx_csum, .get_tx_csum = ethtool_op_get_tx_csum, + /* Need to enable/disable IPv6 too */ .set_tx_csum = efx_ethtool_set_tx_csum, .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, + .get_tso = ethtool_op_get_tso, + /* Need to enable/disable TSO-IPv6 too */ + .set_tso = efx_ethtool_set_tso, +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_ETHTOOL_FLAGS) + .get_flags = ethtool_op_get_flags, + .set_flags = ethtool_op_set_flags, +#endif +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_ETHTOOL_GET_SSET_COUNT) + .get_sset_count = efx_ethtool_get_sset_count, +#else .self_test_count = efx_ethtool_self_test_count, + .get_stats_count = efx_ethtool_get_stats_count, +#endif .self_test = efx_ethtool_self_test, .get_strings = efx_ethtool_get_strings, .phys_id = efx_ethtool_phys_id, - .get_stats_count = efx_ethtool_get_stats_count, .get_ethtool_stats = efx_ethtool_get_stats, +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_ETHTOOL_GET_PERM_ADDR) .get_perm_addr = efx_ethtool_op_get_perm_addr, +#endif + .get_wol = efx_ethtool_get_wol, + .set_wol = efx_ethtool_set_wol, +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_HAVE_ETHTOOL_RESET) + .reset = efx_ethtool_reset, +#endif }; diff -r aa133dee7792 drivers/net/sfc/ethtool.h --- a/drivers/net/sfc/ethtool.h +++ /dev/null @@ -1,44 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005: Fen Systems Ltd. - * Copyright 2006: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * 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 */ diff -r aa133dee7792 drivers/net/sfc/extraversion.h --- a/drivers/net/sfc/extraversion.h +++ /dev/null @@ -1,4 +0,0 @@ -/* - * If compiling on kernels with backported features you may need to - * define EFX_DIST_KVER_ symbols here - */ diff -r aa133dee7792 drivers/net/sfc/falcon.c --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2006-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include @@ -30,63 +13,22 @@ #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 "nic.h" +#include "regs.h" +#include "io.h" #include "mdio_10g.h" #include "phy.h" -#include "boards.h" #include "driverlink.h" #include "workarounds.h" -/* Falcon hardware control. - * Falcon is the internal codename for the SFC4000 controller that is - * present in SFE400X evaluation boards - */ - -/** - * struct falcon_nic_data - Falcon NIC state - * @tx_dc_entries: Number of entries in each TX queue descriptor cache - * @rx_dc_entries: Number of entries in each RX queue descriptor cache - * @tx_dc_base: Base address in SRAM of TX queue descriptor caches - * @rx_dc_base: Base address in SRAM of RX queue descriptor caches - * @old_loopback_mode: Previous loopback mode used in deconfigure_mac_wrapper - * @external_sram_cfg: Size and number of banks of external SRAM - * @pci_dev2: The secondary PCI device if present - * @resources: Driverlink parameters - */ -struct falcon_nic_data { - unsigned tx_dc_entries; - unsigned rx_dc_entries; - unsigned tx_dc_base; - unsigned rx_dc_base; - - enum efx_loopback_mode old_loopback_mode; - - struct pci_dev *pci_dev2; - - int external_sram_cfg; - - struct efx_dl_falcon_resources resources; -}; - -/************************************************************************** - * - * Configurable values - * - ************************************************************************** - */ - -static int disable_dma_stats; - -/* Specify the size of the RX descriptor cache */ -static int descriptor_cache_size = 64; +/* Hardware control for SFC4000 (aka Falcon). */ /* * Override EEPROM/flash type from non-volatile configuration or GPIO; @@ -95,152 +37,64 @@ static unsigned int eeprom_type = -1; static unsigned int 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"); +static const unsigned int +/* "Small" EEPROM device: Atmel AT25040 or similar + * 512 B, 9-bit address, 8 B write block */ +small_eeprom_type = ((9 << SPI_DEV_TYPE_SIZE_LBN) + | (1 << SPI_DEV_TYPE_ADDR_LEN_LBN) + | (3 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)), +/* "Large" EEPROM device: Atmel AT25640 or similar + * 8 KB, 16-bit address, 32 B write block */ +large_eeprom_type = ((13 << SPI_DEV_TYPE_SIZE_LBN) + | (2 << SPI_DEV_TYPE_ADDR_LEN_LBN) + | (5 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)), +/* Default flash device: Atmel AT25F1024 + * 128 KB, 24-bit address, 32 KB erase block, 256 B write block */ +default_flash_type = ((17 << SPI_DEV_TYPE_SIZE_LBN) + | (3 << SPI_DEV_TYPE_ADDR_LEN_LBN) + | (0x52 << SPI_DEV_TYPE_ERASE_CMD_LBN) + | (15 << SPI_DEV_TYPE_ERASE_SIZE_LBN) + | (8 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)); -/* RX FIFO XON watermark - * - * When the amount of the RX FIFO used decreases below this - * watermark send XON. Only used if TX flow control is enabled (ethtool -A) - * This also has an effect on RX/TX arbitration - */ -static int rx_xon_thresh_bytes = -1; -module_param(rx_xon_thresh_bytes, int, 0644); -MODULE_PARM_DESC(rx_xon_thresh_bytes, "RX fifo XON threshold"); - -/* TX descriptor ring size - min 512 max 4k */ -#define FALCON_TXD_RING_ORDER TX_DESCQ_SIZE_1K -#define FALCON_TXD_RING_SIZE 1024 -#define FALCON_TXD_RING_MASK (FALCON_TXD_RING_SIZE - 1) - -/* RX descriptor ring size - min 512 max 4k */ -#define FALCON_RXD_RING_ORDER RX_DESCQ_SIZE_1K -#define FALCON_RXD_RING_SIZE 1024 -#define FALCON_RXD_RING_MASK (FALCON_RXD_RING_SIZE - 1) - -/* Event queue size - max 32k */ -#define FALCON_EVQ_ORDER EVQ_SIZE_4K -#define FALCON_EVQ_SIZE 4096 -#define FALCON_EVQ_MASK (FALCON_EVQ_SIZE - 1) - -/* Max number of internal errors. After this resets will not be performed */ -#define FALCON_MAX_INT_ERRORS 4 - -/* Maximum period that we wait for flush events. If the flush event - * doesn't arrive in this period of time then we check if the queue - * was disabled anyway. */ -#define FALCON_FLUSH_TIMEOUT 10 /* 10ms */ - -/************************************************************************** - * - * Falcon constants - * - ************************************************************************** - */ - -/* DMA address mask (up to 46-bit, avoiding compiler warnings) - * - * Note that it is possible to have a platform with 64-bit longs and - * 32-bit DMA addresses, or vice versa. EFX_DMA_MASK takes care of the - * platform DMA mask. - */ -#if BITS_PER_LONG == 64 -#define FALCON_DMA_MASK EFX_DMA_MASK(0x00003fffffffffffUL) -#else -#define FALCON_DMA_MASK EFX_DMA_MASK(0x00003fffffffffffULL) -#endif - -/* TX DMA length mask (13-bit) */ -#define FALCON_TX_DMA_MASK (4096 - 1) - -/* Size and alignment of special buffers (4KB) */ -#define FALCON_BUF_SIZE 4096 - -/* Dummy SRAM size code */ -#define SRM_NB_BSZ_ONCHIP_ONLY (-1) - -/* Be nice if these (or equiv.) were in linux/pci_regs.h, but they're not. */ -#define PCI_EXP_DEVCAP_PWR_VAL_LBN 18 -#define PCI_EXP_DEVCAP_PWR_SCL_LBN 26 -#define PCI_EXP_DEVCTL_PAYLOAD_LBN 5 -#define PCI_EXP_LNKSTA_LNK_WID 0x3f0 -#define PCI_EXP_LNKSTA_LNK_WID_LBN 4 - -#define FALCON_IS_DUAL_FUNC(efx) \ - (FALCON_REV(efx) < FALCON_REV_B0) - -/************************************************************************** - * - * Falcon hardware access - * - **************************************************************************/ - -/* Read the current event from the event queue */ -static inline efx_qword_t *falcon_event(struct efx_channel *channel, - unsigned int index) -{ - return (((efx_qword_t *) (channel->eventq.addr)) + index); -} - -/* See if an event is present - * - * We check both the high and low dword of the event for all ones. We - * wrote all ones when we cleared the event, and no valid event can - * have all ones in either its high or low dwords. This approach is - * robust against reordering. - * - * Note that using a single 64-bit comparison is incorrect; even - * though the CPU read will be atomic, the DMA write may not be. - */ -static inline int falcon_event_present(efx_qword_t *event) -{ - return (!(EFX_DWORD_IS_ALL_ONES(event->dword[0]) | - EFX_DWORD_IS_ALL_ONES(event->dword[1]))); -} +/* Dummy SRAM configuration code */ +#define SRAM_CONFIG_INTERNAL (-1) /* Read dword from a Falcon PCIE core register */ -static void falcon_pcie_core_read_reg(struct efx_nic *efx, int address, +static void falcon_b0_pcie_core_read_reg(struct efx_nic *efx, int address, efx_dword_t *result) { efx_oword_t temp; - BUG_ON(FALCON_REV(efx) < FALCON_REV_B0); + BUG_ON(efx_nic_rev(efx) != EFX_REV_FALCON_B0); BUG_ON(address & 3 || address < 0); - EFX_POPULATE_OWORD_1(temp, PCIE_CORE_ADDR, address); + EFX_POPULATE_OWORD_1(temp, FRF_BB_PCIE_CORE_TARGET_REG_ADRS, address); - falcon_write(efx, &temp, PCIE_CORE_INDIRECT_REG); - falcon_read(efx, &temp, PCIE_CORE_INDIRECT_REG); + efx_writeo(efx, &temp, FR_BB_PCIE_CORE_INDIRECT); + efx_reado(efx, &temp, FR_BB_PCIE_CORE_INDIRECT); /* Extract PCIE_CORE_VALUE without byte-swapping */ - BUILD_BUG_ON(PCIE_CORE_VALUE_LBN != 32 || - PCIE_CORE_VALUE_WIDTH != 32); + BUILD_BUG_ON(FRF_BB_PCIE_CORE_TARGET_DATA_LBN != 32 || + FRF_BB_PCIE_CORE_TARGET_DATA_WIDTH != 32); result->u32[0] = temp.u32[1]; } /* Write dword to a Falcon PCIE core register */ -static void falcon_pcie_core_write_reg(struct efx_nic *efx, int address, +static void falcon_b0_pcie_core_write_reg(struct efx_nic *efx, int address, efx_dword_t value) { efx_oword_t temp; - BUG_ON(FALCON_REV(efx) < FALCON_REV_B0); + BUG_ON(efx_nic_rev(efx) != EFX_REV_FALCON_B0); BUG_ON(address & 0x3 || address < 0); EFX_POPULATE_OWORD_2(temp, - PCIE_CORE_ADDR, address, - PCIE_CORE_RW, 1); + FRF_BB_PCIE_CORE_TARGET_REG_ADRS, address, + FRF_BB_PCIE_CORE_INDIRECT_ACCESS_DIR, 1); /* Fill PCIE_CORE_VALUE without byte-swapping */ - BUILD_BUG_ON(PCIE_CORE_VALUE_LBN != 32 || - PCIE_CORE_VALUE_WIDTH != 32); + BUILD_BUG_ON(FRF_BB_PCIE_CORE_TARGET_DATA_LBN != 32 || + FRF_BB_PCIE_CORE_TARGET_DATA_WIDTH != 32); temp.u32[1] = value.u32[0]; - falcon_write(efx, &temp, PCIE_CORE_INDIRECT_REG); + efx_writeo(efx, &temp, FR_BB_PCIE_CORE_INDIRECT); } /************************************************************************** @@ -251,1257 +105,87 @@ * ************************************************************************** */ -static void falcon_setsdascl(struct efx_i2c_interface *i2c) +static void falcon_setsda(void *data, int state) { + struct efx_nic *efx = (struct efx_nic *)data; efx_oword_t reg; - falcon_read(i2c->efx, ®, GPIO_CTL_REG_KER); - EFX_SET_OWORD_FIELD(reg, GPIO0_OEN, (i2c->scl ? 0 : 1)); - EFX_SET_OWORD_FIELD(reg, GPIO3_OEN, (i2c->sda ? 0 : 1)); - falcon_write(i2c->efx, ®, GPIO_CTL_REG_KER); + efx_reado(efx, ®, FR_AB_GPIO_CTL); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO3_OEN, !state); + efx_writeo(efx, ®, FR_AB_GPIO_CTL); } -static int falcon_getsda(struct efx_i2c_interface *i2c) +static void falcon_setscl(void *data, int state) { + struct efx_nic *efx = (struct efx_nic *)data; efx_oword_t reg; - falcon_read(i2c->efx, ®, GPIO_CTL_REG_KER); - return EFX_OWORD_FIELD(reg, GPIO3_IN); + efx_reado(efx, ®, FR_AB_GPIO_CTL); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO0_OEN, !state); + efx_writeo(efx, ®, FR_AB_GPIO_CTL); } -static int falcon_getscl(struct efx_i2c_interface *i2c) +static int falcon_getsda(void *data) { + struct efx_nic *efx = (struct efx_nic *)data; efx_oword_t reg; - falcon_read(i2c->efx, ®, GPIO_CTL_REG_KER); - return EFX_DWORD_FIELD(reg, GPIO0_IN); + efx_reado(efx, ®, FR_AB_GPIO_CTL); + return EFX_OWORD_FIELD(reg, FRF_AB_GPIO3_IN); } -static struct efx_i2c_bit_operations falcon_i2c_bit_operations = { - .setsda = falcon_setsdascl, - .setscl = falcon_setsdascl, +static int falcon_getscl(void *data) +{ + struct efx_nic *efx = (struct efx_nic *)data; + efx_oword_t reg; + + efx_reado(efx, ®, FR_AB_GPIO_CTL); + return EFX_OWORD_FIELD(reg, FRF_AB_GPIO0_IN); +} + +static struct i2c_algo_bit_data falcon_i2c_bit_operations = { + .setsda = falcon_setsda, + .setscl = falcon_setscl, .getsda = falcon_getsda, .getscl = falcon_getscl, - .udelay = 100, - .mdelay = 10, + .udelay = 5, + /* Wait up to 50 ms for slave to let us pull SCL high */ + .timeout = DIV_ROUND_UP(HZ, 20), }; -/************************************************************************** - * - * Falcon special buffer handling - * Special buffers are used for event queues and the TX and RX - * descriptor rings. - * - *************************************************************************/ - -/* - * Initialise a Falcon special buffer - * - * This will define a buffer (previously allocated via - * falcon_alloc_special_buffer()) in Falcon's buffer table, allowing - * it to be used for event queues, descriptor rings etc. - */ -static int -falcon_init_special_buffer(struct efx_nic *efx, - struct efx_special_buffer *buffer) -{ - efx_qword_t buf_desc; - int index; - dma_addr_t dma_addr; - int i; - - EFX_BUG_ON_PARANOID(!buffer->addr); - - /* Write buffer descriptors to NIC */ - for (i = 0; i < buffer->entries; i++) { - index = buffer->index + i; - dma_addr = buffer->dma_addr + (i * 4096); - EFX_LOG(efx, "mapping special buffer %d at %llx\n", - index, (unsigned long long)dma_addr); - EFX_POPULATE_QWORD_4(buf_desc, - IP_DAT_BUF_SIZE, IP_DAT_BUF_SIZE_4K, - BUF_ADR_REGION, 0, - BUF_ADR_FBUF, (dma_addr >> 12), - BUF_OWNER_ID_FBUF, 0); - falcon_write_sram(efx, &buf_desc, index); - } - - return 0; -} - -/* Unmaps a buffer from Falcon and clears the buffer table entries */ -static void -falcon_fini_special_buffer(struct efx_nic *efx, - struct efx_special_buffer *buffer) -{ - efx_oword_t buf_tbl_upd; - unsigned int start = buffer->index; - unsigned int end = (buffer->index + buffer->entries - 1); - - if (!buffer->entries) - return; - - EFX_LOG(efx, "unmapping special buffers %d-%d\n", - buffer->index, buffer->index + buffer->entries - 1); - - EFX_POPULATE_OWORD_4(buf_tbl_upd, - BUF_UPD_CMD, 0, - BUF_CLR_CMD, 1, - BUF_CLR_END_ID, end, - BUF_CLR_START_ID, start); - falcon_write(efx, &buf_tbl_upd, BUF_TBL_UPD_REG_KER); -} - -/* - * Allocate a new Falcon special buffer - * - * This allocates memory for a new buffer, clears it and allocates a - * new buffer ID range. It does not write into Falcon's buffer table. - * - * This call will allocate 4KB buffers, since Falcon can't use 8KB - * buffers for event queues and descriptor rings. - */ -static int falcon_alloc_special_buffer(struct efx_nic *efx, - struct efx_special_buffer *buffer, - unsigned int len) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - - len = ALIGN(len, FALCON_BUF_SIZE); - - /* Allocate buffer as consistent PCI DMA space */ - buffer->addr = pci_alloc_consistent(efx->pci_dev, len, - &buffer->dma_addr); - if (!buffer->addr) - return -ENOMEM; - buffer->len = len; - buffer->entries = len / FALCON_BUF_SIZE; - BUG_ON(buffer->dma_addr & (FALCON_BUF_SIZE - 1)); - - /* All zeros is a potentially valid event so memset to 0xff */ - memset(buffer->addr, 0xff, len); - - /* Select new buffer ID */ - buffer->index = nic_data->resources.buffer_table_min; - nic_data->resources.buffer_table_min += buffer->entries; - - EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x " - "(virt %p phys %lx)\n", buffer->index, - buffer->index + buffer->entries - 1, - (unsigned long long)buffer->dma_addr, len, - buffer->addr, virt_to_phys(buffer->addr)); - - return 0; -} - -/* Release the buffer memory. */ -static void falcon_free_special_buffer(struct efx_nic *efx, - struct efx_special_buffer *buffer) -{ - if (!buffer->addr) - return; - - EFX_LOG(efx, "deallocating special buffers %d-%d at %llx+%x " - "(virt %p phys %lx)\n", buffer->index, - buffer->index + buffer->entries - 1, - (unsigned long long)buffer->dma_addr, buffer->len, - buffer->addr, virt_to_phys(buffer->addr)); - - pci_free_consistent(efx->pci_dev, buffer->len, buffer->addr, - buffer->dma_addr); - buffer->addr = NULL; - buffer->entries = 0; -} - -/************************************************************************** - * - * Falcon generic buffer handling - * These buffers are used for interrupt status and MAC stats - * - **************************************************************************/ - -static int falcon_alloc_buffer(struct efx_nic *efx, - struct efx_buffer *buffer, unsigned int len) -{ - buffer->addr = pci_alloc_consistent(efx->pci_dev, len, - &buffer->dma_addr); - if (!buffer->addr) - return -ENOMEM; - buffer->len = len; - memset(buffer->addr, 0, len); - return 0; -} - -static void falcon_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer) -{ - if (buffer->addr) { - pci_free_consistent(efx->pci_dev, buffer->len, - buffer->addr, buffer->dma_addr); - buffer->addr = NULL; - } -} - -/************************************************************************** - * - * Falcon TX path - * - **************************************************************************/ - -/* Returns a pointer to the specified transmit descriptor in the TX - * descriptor queue belonging to the specified channel. - */ -static inline efx_qword_t *falcon_tx_desc(struct efx_tx_queue *tx_queue, - unsigned int index) -{ - return (((efx_qword_t *) (tx_queue->txd.addr)) + index); -} - -/* Update TX descriptor write pointer - * This writes to the TX_DESC_WPTR register for the specified - * channel's transmit descriptor ring. - */ -static inline void falcon_notify_tx_desc(struct efx_tx_queue *tx_queue) -{ - unsigned write_ptr; - efx_dword_t reg; - - write_ptr = tx_queue->write_count & FALCON_TXD_RING_MASK; - EFX_POPULATE_DWORD_1(reg, TX_DESC_WPTR_DWORD, write_ptr); - falcon_writel_page(tx_queue->efx, ®, - TX_DESC_UPD_REG_KER_DWORD, tx_queue->queue); -} - - -/* For each entry inserted into the software descriptor ring, create a - * descriptor in the hardware TX descriptor ring (in host memory), and - * write a doorbell. - */ -void fastcall falcon_push_buffers(struct efx_tx_queue *tx_queue) -{ - - struct efx_tx_buffer *buffer; - efx_qword_t *txd; - unsigned write_ptr; - - BUG_ON(tx_queue->write_count == tx_queue->insert_count); - - do { - write_ptr = tx_queue->write_count & FALCON_TXD_RING_MASK; - buffer = &tx_queue->buffer[write_ptr]; - txd = falcon_tx_desc(tx_queue, write_ptr); - ++tx_queue->write_count; - - /* Create TX descriptor ring entry */ - EFX_POPULATE_QWORD_5(*txd, - TX_KER_PORT, 0, - TX_KER_CONT, buffer->continuation, - TX_KER_BYTE_CNT, buffer->len, - TX_KER_BUF_REGION, 0, - TX_KER_BUF_ADR, buffer->dma_addr); - } while (tx_queue->write_count != tx_queue->insert_count); - - wmb(); /* Ensure descriptors are written before they are fetched */ - falcon_notify_tx_desc(tx_queue); -} - -/* Allocate hardware resources for a TX queue */ -int falcon_probe_tx(struct efx_tx_queue *tx_queue) -{ - struct efx_nic *efx = tx_queue->efx; - struct falcon_nic_data *nic_data = efx->nic_data; - int rc; - - rc = falcon_alloc_special_buffer(efx, &tx_queue->txd, - FALCON_TXD_RING_SIZE * - sizeof(efx_qword_t)); - if (rc) - return rc; - - nic_data->resources.txq_min = max(nic_data->resources.txq_min, - (unsigned)tx_queue->queue + 1); - - return 0; -} - -/* Prepare channel's TX datapath. */ -int falcon_init_tx(struct efx_tx_queue *tx_queue) -{ - efx_oword_t tx_desc_ptr; - struct efx_nic *efx = tx_queue->efx; - int rc; - - /* Pin TX descriptor ring */ - rc = falcon_init_special_buffer(efx, &tx_queue->txd); - if (rc) - return rc; - - /* Push TX descriptor ring to card */ - EFX_POPULATE_OWORD_10(tx_desc_ptr, - TX_DESCQ_EN, 1, - TX_ISCSI_DDIG_EN, 0, - TX_ISCSI_HDIG_EN, 0, - TX_DESCQ_BUF_BASE_ID, tx_queue->txd.index, - TX_DESCQ_EVQ_ID, tx_queue->channel->evqnum, - TX_DESCQ_OWNER_ID, 0, - TX_DESCQ_LABEL, tx_queue->queue, - TX_DESCQ_SIZE, FALCON_TXD_RING_ORDER, - TX_DESCQ_TYPE, 0, /* kernel queue */ - TX_NON_IP_DROP_DIS_B0, 1); - - if (FALCON_REV(efx) >= FALCON_REV_B0) { - int csum = !(efx->net_dev->features & NETIF_F_IP_CSUM); - EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_IP_CHKSM_DIS_B0, csum); - EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_TCP_CHKSM_DIS_B0, csum); - } - - falcon_write_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, - tx_queue->queue); - - if (FALCON_REV(efx) < FALCON_REV_B0) { - efx_oword_t reg; - - /* Only 128 bits in this register */ - BUG_ON(tx_queue->queue >= 128); - - falcon_read(efx, ®, TX_CHKSM_CFG_REG_KER_A1); - if (efx->net_dev->features & NETIF_F_IP_CSUM) - clear_bit_le(tx_queue->queue, (void *)®); - else - set_bit_le(tx_queue->queue, (void *)®); - falcon_write(efx, ®, TX_CHKSM_CFG_REG_KER_A1); - } - - return 0; -} - -static int falcon_flush_tx_queue(struct efx_tx_queue *tx_queue) -{ - struct efx_nic *efx = tx_queue->efx; - struct efx_channel *channel = &efx->channel[0]; - efx_oword_t tx_flush_descq; - unsigned int read_ptr, i; - - /* Post a flush command */ - EFX_POPULATE_OWORD_2(tx_flush_descq, - TX_FLUSH_DESCQ_CMD, 1, - TX_FLUSH_DESCQ, tx_queue->queue); - falcon_write(efx, &tx_flush_descq, TX_FLUSH_DESCQ_REG_KER); - msleep(FALCON_FLUSH_TIMEOUT); - - if (EFX_WORKAROUND_7803(efx)) - return 0; - - /* Look for a flush completed event */ - read_ptr = channel->eventq_read_ptr; - for (i = 0; i < FALCON_EVQ_SIZE; ++i) { - efx_qword_t *event = falcon_event(channel, read_ptr); - int ev_code, ev_sub_code, ev_queue; - if (!falcon_event_present(event)) - break; - - ev_code = EFX_QWORD_FIELD(*event, EV_CODE); - ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE); - ev_queue = EFX_QWORD_FIELD(*event, DRIVER_EV_TX_DESCQ_ID); - if ((ev_sub_code == TX_DESCQ_FLS_DONE_EV_DECODE) && - (ev_queue == tx_queue->queue)) { - EFX_LOG(efx, "tx queue %d flush command succesful\n", - tx_queue->queue); - return 0; - } - - read_ptr = (read_ptr + 1) & FALCON_EVQ_MASK; - } - - if (EFX_WORKAROUND_11557(efx)) { - efx_oword_t reg; - int enabled; - - falcon_read_table(efx, ®, efx->type->txd_ptr_tbl_base, - tx_queue->queue); - enabled = EFX_OWORD_FIELD(reg, TX_DESCQ_EN); - if (!enabled) { - EFX_LOG(efx, "tx queue %d disabled without a " - "flush event seen\n", tx_queue->queue); - return 0; - } - } - - EFX_ERR(efx, "tx queue %d flush command timed out\n", tx_queue->queue); - return -ETIMEDOUT; -} - -void falcon_fini_tx(struct efx_tx_queue *tx_queue) -{ - struct efx_nic *efx = tx_queue->efx; - efx_oword_t tx_desc_ptr; - - /* Stop the hardware using the queue */ - if (falcon_flush_tx_queue(tx_queue)) - EFX_ERR(efx, "failed to flush tx queue %d\n", tx_queue->queue); - - /* Remove TX descriptor ring from card */ - EFX_ZERO_OWORD(tx_desc_ptr); - falcon_write_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, - tx_queue->queue); - - /* Unpin TX descriptor ring */ - falcon_fini_special_buffer(efx, &tx_queue->txd); -} - -/* Free buffers backing TX queue */ -void falcon_remove_tx(struct efx_tx_queue *tx_queue) -{ - falcon_free_special_buffer(tx_queue->efx, &tx_queue->txd); -} - -/************************************************************************** - * - * Falcon RX path - * - **************************************************************************/ - -/* Returns a pointer to the specified transmit descriptor in the RX - * descriptor queue. - */ -static inline efx_qword_t *falcon_rx_desc(struct efx_rx_queue *rx_queue, - unsigned int index) -{ - return (((efx_qword_t *) (rx_queue->rxd.addr)) + index); -} - -/* This creates an entry in the RX descriptor queue corresponding to - * the receive buffer. - */ -static inline void falcon_build_rx_desc(struct efx_rx_queue *rx_queue, - unsigned index) -{ - struct efx_rx_buffer *rx_buf; - efx_qword_t *rxd; - - rxd = falcon_rx_desc(rx_queue, index); - rx_buf = efx_rx_buffer(rx_queue, index); - EFX_POPULATE_QWORD_3(*rxd, - RX_KER_BUF_SIZE, - rx_buf->len - - rx_queue->efx->type->rx_buffer_padding, - RX_KER_BUF_REGION, 0, - RX_KER_BUF_ADR, rx_buf->dma_addr); -} - -/* This writes to the RX_DESC_WPTR register for the specified receive - * descriptor ring. - */ -void fastcall falcon_notify_rx_desc(struct efx_rx_queue *rx_queue) -{ - efx_dword_t reg; - unsigned write_ptr; - - while (rx_queue->notified_count != rx_queue->added_count) { - falcon_build_rx_desc(rx_queue, - rx_queue->notified_count & - FALCON_RXD_RING_MASK); - ++rx_queue->notified_count; - } - - wmb(); - write_ptr = rx_queue->added_count & FALCON_RXD_RING_MASK; - EFX_POPULATE_DWORD_1(reg, RX_DESC_WPTR_DWORD, write_ptr); - falcon_writel_page(rx_queue->efx, ®, - RX_DESC_UPD_REG_KER_DWORD, rx_queue->queue); -} - -int falcon_probe_rx(struct efx_rx_queue *rx_queue) -{ - struct efx_nic *efx = rx_queue->efx; - struct falcon_nic_data *nic_data = efx->nic_data; - int rc; - - rc = falcon_alloc_special_buffer(efx, &rx_queue->rxd, - FALCON_RXD_RING_SIZE * - sizeof(efx_qword_t)); - if (rc) - return rc; - - /* Increment the rxq_min counter */ - nic_data->resources.rxq_min = max(nic_data->resources.rxq_min, - (unsigned)rx_queue->queue + 1); - - return 0; -} - -int falcon_init_rx(struct efx_rx_queue *rx_queue) -{ - efx_oword_t rx_desc_ptr; - struct efx_nic *efx = rx_queue->efx; - int rc; - int is_b0 = FALCON_REV(efx) >= FALCON_REV_B0; - int iscsi_digest_en = is_b0; - - EFX_LOG(efx, "RX queue %d ring in special buffers %d-%d\n", - rx_queue->queue, rx_queue->rxd.index, - rx_queue->rxd.index + rx_queue->rxd.entries - 1); - - /* Pin RX descriptor ring */ - rc = falcon_init_special_buffer(efx, &rx_queue->rxd); - if (rc) - return rc; - - /* Push RX descriptor ring to card */ - EFX_POPULATE_OWORD_10(rx_desc_ptr, - RX_ISCSI_DDIG_EN, iscsi_digest_en, - RX_ISCSI_HDIG_EN, iscsi_digest_en, - RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index, - RX_DESCQ_EVQ_ID, rx_queue->channel->evqnum, - RX_DESCQ_OWNER_ID, 0, - RX_DESCQ_LABEL, rx_queue->queue, - RX_DESCQ_SIZE, FALCON_RXD_RING_ORDER, - RX_DESCQ_TYPE, 0 /* kernel queue */ , - /* For >=B0 this is scatter so disable */ - RX_DESCQ_JUMBO, !is_b0, - RX_DESCQ_EN, 1); - falcon_write_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, - rx_queue->queue); - return 0; -} - -static int falcon_flush_rx_queue(struct efx_rx_queue *rx_queue) -{ - struct efx_nic *efx = rx_queue->efx; - struct efx_channel *channel = &efx->channel[0]; - unsigned int read_ptr, i; - efx_oword_t rx_flush_descq; - - /* Post a flush command */ - EFX_POPULATE_OWORD_2(rx_flush_descq, - RX_FLUSH_DESCQ_CMD, 1, - RX_FLUSH_DESCQ, rx_queue->queue); - - falcon_write(efx, &rx_flush_descq, RX_FLUSH_DESCQ_REG_KER); - msleep(FALCON_FLUSH_TIMEOUT); - - if (EFX_WORKAROUND_7803(efx)) - return 0; - - /* Look for a flush completed event */ - read_ptr = channel->eventq_read_ptr; - for (i = 0; i < FALCON_EVQ_SIZE; ++i) { - efx_qword_t *event = falcon_event(channel, read_ptr); - int ev_code, ev_sub_code, ev_queue, ev_failed; - if (!falcon_event_present(event)) - break; - - ev_code = EFX_QWORD_FIELD(*event, EV_CODE); - ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE); - ev_queue = EFX_QWORD_FIELD(*event, DRIVER_EV_RX_DESCQ_ID); - ev_failed = EFX_QWORD_FIELD(*event, DRIVER_EV_RX_FLUSH_FAIL); - - if ((ev_sub_code == RX_DESCQ_FLS_DONE_EV_DECODE) && - (ev_queue == rx_queue->queue)) { - if (ev_failed) { - EFX_INFO(efx, "rx queue %d flush command " - "failed\n", rx_queue->queue); - return -EAGAIN; - } else { - EFX_LOG(efx, "rx queue %d flush command " - "succesful\n", rx_queue->queue); - return 0; - } - } - - read_ptr = (read_ptr + 1) & FALCON_EVQ_MASK; - } - - if (EFX_WORKAROUND_11557(efx)) { - efx_oword_t reg; - int enabled; - - falcon_read_table(efx, ®, efx->type->rxd_ptr_tbl_base, - rx_queue->queue); - enabled = EFX_OWORD_FIELD(reg, RX_DESCQ_EN); - if (!enabled) { - EFX_LOG(efx, "rx queue %d disabled without a " - "flush event seen\n", rx_queue->queue); - return 0; - } - } - - EFX_ERR(efx, "rx queue %d flush command timed out\n", rx_queue->queue); - return -ETIMEDOUT; -} - -void falcon_fini_rx(struct efx_rx_queue *rx_queue) -{ - efx_oword_t rx_desc_ptr; - struct efx_nic *efx = rx_queue->efx; - int i, rc; - - /* Try and flush the rx queue. This may need to be repeated */ - for (i = 0; i < 5; i++) { - rc = falcon_flush_rx_queue(rx_queue); - if (rc == -EAGAIN) - continue; - break; - } - if (rc) { - EFX_ERR(efx, "failed to flush rx queue %d\n", rx_queue->queue); - efx_schedule_reset(efx, RESET_TYPE_INVISIBLE); - } - - /* Remove RX descriptor ring from card */ - EFX_ZERO_OWORD(rx_desc_ptr); - falcon_write_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, - rx_queue->queue); - - /* Unpin RX descriptor ring */ - falcon_fini_special_buffer(efx, &rx_queue->rxd); -} - -/* Free buffers backing RX queue */ -void falcon_remove_rx(struct efx_rx_queue *rx_queue) -{ - falcon_free_special_buffer(rx_queue->efx, &rx_queue->rxd); -} - -/************************************************************************** - * - * Falcon event queue processing - * Event queues are processed by per-channel tasklets. - * - **************************************************************************/ - -/* Update a channel's event queue's read pointer (RPTR) register - * - * This writes the EVQ_RPTR_REG register for the specified channel's - * event queue. - * - * Note that EVQ_RPTR_REG contains the index of the "last read" event, - * whereas channel->eventq_read_ptr contains the index of the "next to - * read" event. - */ -void fastcall falcon_eventq_read_ack(struct efx_channel *channel) -{ - efx_dword_t reg; - struct efx_nic *efx = channel->efx; - - EFX_POPULATE_DWORD_1(reg, EVQ_RPTR_DWORD, channel->eventq_read_ptr); - falcon_writel_table(efx, ®, efx->type->evq_rptr_tbl_base, - channel->evqnum); -} - -/* Use HW to insert a SW defined event */ -void falcon_generate_event(struct efx_channel *channel, efx_qword_t *event) -{ - efx_oword_t drv_ev_reg; - - EFX_POPULATE_OWORD_2(drv_ev_reg, - DRV_EV_QID, channel->evqnum, - DRV_EV_DATA, - EFX_QWORD_FIELD64(*event, WHOLE_EVENT)); - falcon_write(channel->efx, &drv_ev_reg, DRV_EV_REG_KER); -} - -/* Handle a transmit completion event - * - * Falcon batches TX completion events; the message we receive is of - * the form "complete all TX events up to this index". - */ -static inline void falcon_handle_tx_event(struct efx_channel *channel, - efx_qword_t *event) -{ - unsigned int tx_ev_desc_ptr; - unsigned int tx_ev_q_label; - struct efx_tx_queue *tx_queue; - struct efx_nic *efx = channel->efx; - - if (likely(EFX_QWORD_FIELD(*event, TX_EV_COMP))) { - /* Transmit completion */ - tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, TX_EV_DESC_PTR); - tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL); - tx_queue = &efx->tx_queue[tx_ev_q_label]; - efx_xmit_done(tx_queue, tx_ev_desc_ptr); - } else if (EFX_QWORD_FIELD(*event, TX_EV_WQ_FF_FULL)) { - /* Rewrite the FIFO write pointer */ - tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL); - tx_queue = &efx->tx_queue[tx_ev_q_label]; - - if (NET_DEV_REGISTERED(efx)) - netif_tx_lock(efx->net_dev); - falcon_notify_tx_desc(tx_queue); - if (NET_DEV_REGISTERED(efx)) - netif_tx_unlock(efx->net_dev); - } else if (EFX_QWORD_FIELD(*event, TX_EV_PKT_ERR) && - EFX_WORKAROUND_10727(efx)) { - efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); - } else { - EFX_ERR(efx, "channel %d unexpected TX event " - EFX_QWORD_FMT"\n", channel->channel, - EFX_QWORD_VAL(*event)); - } -} - -/* Check received packet's destination MAC address. */ -static int check_dest_mac(struct efx_rx_queue *rx_queue, - const efx_qword_t *event) -{ - struct efx_rx_buffer *rx_buf; - struct efx_nic *efx = rx_queue->efx; - int rx_ev_desc_ptr; - struct ethhdr *eh; - - if (efx->promiscuous) - return 1; - - rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, RX_EV_DESC_PTR); - rx_buf = efx_rx_buffer(rx_queue, rx_ev_desc_ptr); - eh = (struct ethhdr *)rx_buf->data; - if (memcmp(eh->h_dest, efx->net_dev->dev_addr, ETH_ALEN)) - return 0; - return 1; -} - -/* Detect errors included in the rx_evt_pkt_ok bit. */ -static void falcon_handle_rx_not_ok(struct efx_rx_queue *rx_queue, - const efx_qword_t *event, - unsigned *rx_ev_pkt_ok, - int *discard, int byte_count) -{ - struct efx_nic *efx = rx_queue->efx; - unsigned rx_ev_buf_owner_id_err, rx_ev_ip_hdr_chksum_err; - unsigned rx_ev_tcp_udp_chksum_err, rx_ev_eth_crc_err; - unsigned rx_ev_frm_trunc, rx_ev_drib_nib, rx_ev_tobe_disc; - unsigned rx_ev_pkt_type, rx_ev_other_err, rx_ev_pause_frm; - unsigned rx_ev_ip_frag_err, rx_ev_hdr_type, rx_ev_mcast_pkt; - int snap, non_ip; - - rx_ev_hdr_type = EFX_QWORD_FIELD(*event, RX_EV_HDR_TYPE); - rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, RX_EV_MCAST_PKT); - rx_ev_tobe_disc = EFX_QWORD_FIELD(*event, RX_EV_TOBE_DISC); - rx_ev_pkt_type = EFX_QWORD_FIELD(*event, RX_EV_PKT_TYPE); - rx_ev_buf_owner_id_err = EFX_QWORD_FIELD(*event, - RX_EV_BUF_OWNER_ID_ERR); - rx_ev_ip_frag_err = EFX_QWORD_FIELD(*event, RX_EV_IF_FRAG_ERR); - rx_ev_ip_hdr_chksum_err = EFX_QWORD_FIELD(*event, - RX_EV_IP_HDR_CHKSUM_ERR); - rx_ev_tcp_udp_chksum_err = EFX_QWORD_FIELD(*event, - RX_EV_TCP_UDP_CHKSUM_ERR); - rx_ev_eth_crc_err = EFX_QWORD_FIELD(*event, RX_EV_ETH_CRC_ERR); - rx_ev_frm_trunc = EFX_QWORD_FIELD(*event, RX_EV_FRM_TRUNC); - rx_ev_drib_nib = ((FALCON_REV(efx) >= FALCON_REV_B0) ? - 0 : EFX_QWORD_FIELD(*event, RX_EV_DRIB_NIB)); - rx_ev_pause_frm = EFX_QWORD_FIELD(*event, RX_EV_PAUSE_FRM_ERR); - - /* Every error apart from tobe_disc and pause_frm */ - rx_ev_other_err = (rx_ev_drib_nib | rx_ev_tcp_udp_chksum_err | - rx_ev_buf_owner_id_err | rx_ev_eth_crc_err | - rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err); - - snap = (rx_ev_pkt_type == RX_EV_PKT_TYPE_LLC_DECODE) || - (rx_ev_pkt_type == RX_EV_PKT_TYPE_VLAN_LLC_DECODE); - non_ip = (rx_ev_hdr_type == RX_EV_HDR_TYPE_NON_IP_DECODE); - - /* SFC bug 5475/8970: The Falcon XMAC incorrectly calculates the - * length field of an LLC frame, which sets TOBE_DISC. We could set - * PASS_LEN_ERR, but we want the MAC to filter out short frames (to - * protect the RX block). - * - * bug5475 - LLC/SNAP: Falcon identifies SNAP packets. - * bug8970 - LLC/noSNAP: Falcon does not provide an LLC flag. - * LLC can't encapsulate IP, so by definition - * these packets are NON_IP. - * - * Unicast mismatch will also cause TOBE_DISC, so the driver needs - * to check this. - */ - if (EFX_WORKAROUND_5475(efx) && rx_ev_tobe_disc && (snap || non_ip)) { - /* If all the other flags are zero then we can state the - * entire packet is ok, which will flag to the kernel not - * to recalculate checksums. - */ - if (!(non_ip | rx_ev_other_err | rx_ev_pause_frm)) - *rx_ev_pkt_ok = 1; - - rx_ev_tobe_disc = 0; - - /* TOBE_DISC is set for unicast mismatch. But given that - * we can't trust TOBE_DISC here, we must validate the dest - * MAC address ourselves. - */ - if (!rx_ev_mcast_pkt && !check_dest_mac(rx_queue, event)) - rx_ev_tobe_disc = 1; - } - - /* Count errors that are not in MAC stats. */ - if (rx_ev_frm_trunc) - ++rx_queue->channel->n_rx_frm_trunc; - else if (rx_ev_tobe_disc) - ++rx_queue->channel->n_rx_tobe_disc; - else if (rx_ev_ip_hdr_chksum_err) - ++rx_queue->channel->n_rx_ip_hdr_chksum_err; - else if (rx_ev_tcp_udp_chksum_err) - ++rx_queue->channel->n_rx_tcp_udp_chksum_err; - if (rx_ev_ip_frag_err) - ++rx_queue->channel->n_rx_ip_frag_err; - - /* The frame must be discarded if any of these are true. */ - *discard = (rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_drib_nib | - rx_ev_tobe_disc | rx_ev_pause_frm); - - /* TOBE_DISC is expected on unicast mismatches; don't print out an - * error message. FRM_TRUNC indicates RXDP dropped the packet due - * to a FIFO overflow. - */ -#ifdef EFX_ENABLE_DEBUG - if (rx_ev_other_err) { - EFX_INFO_RL(efx, " RX queue %d unexpected RX event " - EFX_QWORD_FMT "%s%s%s%s%s%s%s%s%s\n", - rx_queue->queue, EFX_QWORD_VAL(*event), - rx_ev_buf_owner_id_err ? " [OWNER_ID_ERR]" : "", - rx_ev_ip_hdr_chksum_err ? - " [IP_HDR_CHKSUM_ERR]" : "", - rx_ev_tcp_udp_chksum_err ? - " [TCP_UDP_CHKSUM_ERR]" : "", - rx_ev_eth_crc_err ? " [ETH_CRC_ERR]" : "", - rx_ev_frm_trunc ? " [FRM_TRUNC]" : "", - rx_ev_drib_nib ? " [DRIB_NIB]" : "", - rx_ev_tobe_disc ? " [TOBE_DISC]" : "", - rx_ev_pause_frm ? " [PAUSE]" : "", - snap ? " [SNAP/LLC]" : ""); - } -#endif - - if (unlikely(rx_ev_eth_crc_err && EFX_WORKAROUND_10750(efx) && - efx->phy_type == PHY_TYPE_10XPRESS)) - tenxpress_crc_err(efx); -} - - -/* Handle receive events that are not in-order. */ -static void falcon_handle_rx_bad_index(struct efx_rx_queue *rx_queue, - unsigned index) -{ - struct efx_nic *efx = rx_queue->efx; - unsigned expected, dropped; - - expected = rx_queue->removed_count & FALCON_RXD_RING_MASK; - dropped = ((index + FALCON_RXD_RING_SIZE - expected) & - FALCON_RXD_RING_MASK); - EFX_INFO(efx, "dropped %d events (index=%d expected=%d)\n", - dropped, index, expected); - - atomic_inc(&efx->errors.missing_event); - efx_schedule_reset(efx, EFX_WORKAROUND_5676(efx) ? - RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); -} - - -/* Handle a packet received event - * - * Falcon silicon gives a "discard" flag if it's a unicast packet with the - * wrong destination address - * Also "is multicast" and "matches multicast filter" flags can be used to - * discard non-matching multicast packets. - */ -static inline int falcon_handle_rx_event(struct efx_channel *channel, - const efx_qword_t *event) -{ - unsigned int rx_ev_q_label, rx_ev_desc_ptr, rx_ev_byte_cnt; - unsigned int rx_ev_pkt_ok, rx_ev_hdr_type, rx_ev_mcast_pkt; - unsigned expected_ptr; - int discard = 0, checksummed; - struct efx_rx_queue *rx_queue; - struct efx_nic *efx = channel->efx; - - /* Basic packet information */ - rx_ev_byte_cnt = EFX_QWORD_FIELD(*event, RX_EV_BYTE_CNT); - rx_ev_pkt_ok = EFX_QWORD_FIELD(*event, RX_EV_PKT_OK); - rx_ev_hdr_type = EFX_QWORD_FIELD(*event, RX_EV_HDR_TYPE); - WARN_ON(EFX_QWORD_FIELD(*event, RX_EV_JUMBO_CONT)); - WARN_ON(EFX_QWORD_FIELD(*event, RX_EV_SOP) != 1); - - rx_ev_q_label = EFX_QWORD_FIELD(*event, RX_EV_Q_LABEL); - rx_queue = &efx->rx_queue[rx_ev_q_label]; - - rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, RX_EV_DESC_PTR); - expected_ptr = rx_queue->removed_count & FALCON_RXD_RING_MASK; - if (unlikely(rx_ev_desc_ptr != expected_ptr)) { - falcon_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr); - return rx_ev_q_label; - } - - if (likely(rx_ev_pkt_ok)) { - /* If packet is marked as OK and packet type is TCP/IPv4 or - * UDP/IPv4, then we can rely on the hardware checksum. - */ - checksummed = RX_EV_HDR_TYPE_HAS_CHECKSUMS(rx_ev_hdr_type); - } else { - falcon_handle_rx_not_ok(rx_queue, event, &rx_ev_pkt_ok, - &discard, rx_ev_byte_cnt); - checksummed = 0; - } - - /* Detect multicast packets that didn't match the filter */ - rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, RX_EV_MCAST_PKT); - if (rx_ev_mcast_pkt) { - unsigned int rx_ev_mcast_hash_match = - EFX_QWORD_FIELD(*event, RX_EV_MCAST_HASH_MATCH); - - if (unlikely(!rx_ev_mcast_hash_match)) - discard = 1; - } - - /* Handle received packet */ - efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt, - checksummed, discard); - - return rx_ev_q_label; -} - -/* Global events are basically PHY events */ -static void falcon_handle_global_event(struct efx_channel *channel, - efx_qword_t *event) -{ - struct efx_nic *efx = channel->efx; - int is_phy_event = 0, handled = 0; - - /* Check for interrupt on either port. Some boards have a - * single PHY wired to the interrupt line for port 1. */ - if (EFX_QWORD_FIELD(*event, G_PHY0_INTR) || - EFX_QWORD_FIELD(*event, G_PHY1_INTR) || - EFX_QWORD_FIELD(*event, XG_PHY_INTR)) - is_phy_event = 1; - - if ((FALCON_REV(efx) >= FALCON_REV_B0) && - EFX_OWORD_FIELD(*event, XG_MNT_INTR_B0)) - is_phy_event = 1; - - if (is_phy_event) { - efx->phy_op->clear_interrupt(efx); - queue_work(efx->workqueue, &efx->reconfigure_work); - handled = 1; - } - - if (EFX_QWORD_FIELD_VER(efx, *event, RX_RECOVERY)) { - EFX_ERR(efx, "channel %d seen global RX_RESET " - "event. Resetting.\n", channel->channel); - - atomic_inc(&efx->errors.rx_reset); - efx_schedule_reset(efx, EFX_WORKAROUND_6555(efx) ? - RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); - handled = 1; - } - - if (!handled) - EFX_ERR(efx, "channel %d unknown global event " - EFX_QWORD_FMT "\n", channel->channel, - EFX_QWORD_VAL(*event)); -} - -static void falcon_handle_driver_event(struct efx_channel *channel, - efx_qword_t *event) -{ - struct efx_nic *efx = channel->efx; - unsigned int ev_sub_code; - unsigned int ev_sub_data; - - ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE); - ev_sub_data = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_DATA); - - switch (ev_sub_code) { - case TX_DESCQ_FLS_DONE_EV_DECODE: - EFX_TRACE(efx, "channel %d TXQ %d flushed\n", - channel->channel, ev_sub_data); - EFX_DL_CALLBACK(efx, event, event); - break; - case RX_DESCQ_FLS_DONE_EV_DECODE: - EFX_TRACE(efx, "channel %d RXQ %d flushed\n", - channel->channel, ev_sub_data); - EFX_DL_CALLBACK(efx, event, event); - break; - case EVQ_INIT_DONE_EV_DECODE: - EFX_LOG(efx, "channel %d EVQ %d initialised\n", - channel->channel, ev_sub_data); - break; - case SRM_UPD_DONE_EV_DECODE: - EFX_TRACE(efx, "channel %d SRAM update done\n", - channel->channel); - EFX_DL_CALLBACK(efx, event, event); - break; - case WAKE_UP_EV_DECODE: - EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n", - channel->channel, ev_sub_data); - EFX_DL_CALLBACK(efx, event, event); - break; - case TIMER_EV_DECODE: - EFX_TRACE(efx, "channel %d RX queue %d timer expired\n", - channel->channel, ev_sub_data); - EFX_DL_CALLBACK(efx, event, event); - break; - case RX_RECOVERY_EV_DECODE: - EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. " - "Resetting.\n", channel->channel); - - atomic_inc(&efx->errors.rx_reset); - efx_schedule_reset(efx, - EFX_WORKAROUND_6555(efx) ? - RESET_TYPE_RX_RECOVERY : - RESET_TYPE_DISABLE); - break; - case RX_DSC_ERROR_EV_DECODE: - EFX_ERR(efx, "RX DMA Q %d reports descriptor fetch error." - " RX Q %d is disabled.\n", ev_sub_data, ev_sub_data); - atomic_inc(&efx->errors.rx_desc_fetch); - efx_schedule_reset(efx, RESET_TYPE_RX_DESC_FETCH); - break; - case TX_DSC_ERROR_EV_DECODE: - EFX_ERR(efx, "TX DMA Q %d reports descriptor fetch error." - " TX Q %d is disabled.\n", ev_sub_data, ev_sub_data); - atomic_inc(&efx->errors.tx_desc_fetch); - efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); - break; - default: - EFX_TRACE(efx, "channel %d unknown driver event code %d " - "data %04x\n", channel->channel, ev_sub_code, - ev_sub_data); - EFX_DL_CALLBACK(efx, event, event); - break; - } -} - -int fastcall falcon_process_eventq(struct efx_channel *channel, int *rx_quota) -{ - unsigned int read_ptr; - efx_qword_t event, *p_event; - int ev_code; - int rxq; - int rxdmaqs = 0; - - read_ptr = channel->eventq_read_ptr; - - do { - p_event = falcon_event(channel, read_ptr); - event = *p_event; - - if (!falcon_event_present(&event)) - /* End of events */ - break; - - EFX_TRACE(channel->efx, "channel %d event is "EFX_QWORD_FMT"\n", - channel->channel, EFX_QWORD_VAL(event)); - - /* Clear this event by marking it all ones */ - EFX_SET_QWORD(*p_event); - - ev_code = EFX_QWORD_FIELD(event, EV_CODE); - - switch (ev_code) { - case RX_IP_EV_DECODE: - rxq = falcon_handle_rx_event(channel, &event); - rxdmaqs |= (1 << rxq); - (*rx_quota)--; - break; - case TX_IP_EV_DECODE: - falcon_handle_tx_event(channel, &event); - break; - case DRV_GEN_EV_DECODE: - channel->eventq_magic - = EFX_QWORD_FIELD(event, EVQ_MAGIC); - EFX_LOG(channel->efx, "channel %d received generated " - "event "EFX_QWORD_FMT"\n", channel->channel, - EFX_QWORD_VAL(event)); - break; - case GLOBAL_EV_DECODE: - falcon_handle_global_event(channel, &event); - break; - case DRIVER_EV_DECODE: - falcon_handle_driver_event(channel, &event); - break; - default: - EFX_ERR(channel->efx, "channel %d unknown event type %d" - " (data " EFX_QWORD_FMT ")\n", channel->channel, - ev_code, EFX_QWORD_VAL(event)); - } - - /* Increment read pointer */ - read_ptr = (read_ptr + 1) & FALCON_EVQ_MASK; - - } while (*rx_quota); - - channel->eventq_read_ptr = read_ptr; - return rxdmaqs; -} - -void falcon_set_int_moderation(struct efx_channel *channel) +static void falcon_push_irq_moderation(struct efx_channel *channel) { efx_dword_t timer_cmd; struct efx_nic *efx = channel->efx; /* Set timer register */ if (channel->irq_moderation) { - /* Round to resolution supported by hardware. The value we - * program is based at 0. So actual interrupt moderation - * achieved is ((x + 1) * res). - */ - unsigned int res = 5; - channel->irq_moderation -= (channel->irq_moderation % res); - if (channel->irq_moderation < res) - channel->irq_moderation = res; EFX_POPULATE_DWORD_2(timer_cmd, - TIMER_MODE, TIMER_MODE_INT_HLDOFF, - TIMER_VAL, - (channel->irq_moderation / res) - 1); + FRF_AB_TC_TIMER_MODE, + FFE_BB_TIMER_MODE_INT_HLDOFF, + FRF_AB_TC_TIMER_VAL, + channel->irq_moderation - 1); } else { EFX_POPULATE_DWORD_2(timer_cmd, - TIMER_MODE, TIMER_MODE_DIS, - TIMER_VAL, 0); + FRF_AB_TC_TIMER_MODE, + FFE_BB_TIMER_MODE_DIS, + FRF_AB_TC_TIMER_VAL, 0); } - falcon_writel_page_locked(efx, &timer_cmd, TIMER_CMD_REG_KER, - channel->evqnum); - + BUILD_BUG_ON(FR_AA_TIMER_COMMAND_KER != FR_BZ_TIMER_COMMAND_P0); + efx_writed_page_locked(efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0, + channel->channel); } -/* Allocate buffer table entries for event queue */ -int falcon_probe_eventq(struct efx_channel *channel) +static void falcon_deconfigure_mac_wrapper(struct efx_nic *efx); + +static void falcon_prepare_flush(struct efx_nic *efx) { - struct efx_nic *efx = channel->efx; - struct falcon_nic_data *nic_data = efx->nic_data; - unsigned int evq_size; - int rc; + falcon_deconfigure_mac_wrapper(efx); - 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); + /* Wait for the tx and rx fifo's to get to the next packet boundary + * (~1ms without back-pressure), then to drain the remainder of the + * fifo's at data path speeds (negligible), with a healthy margin. */ + msleep(10); } /* Acknowledge a legacy interrupt from Falcon @@ -1514,115 +198,25 @@ * * NB most hardware supports MSI interrupts */ -static inline void falcon_irq_ack_a1(struct efx_nic *efx) +inline void falcon_irq_ack_a1(struct efx_nic *efx) { efx_dword_t reg; - EFX_POPULATE_DWORD_1(reg, INT_ACK_DUMMY_DATA, 0xb7eb7e); - falcon_writel(efx, ®, INT_ACK_REG_KER_A1); - falcon_readl(efx, ®, WORK_AROUND_BROKEN_PCI_READS_REG_KER_A1); + EFX_POPULATE_DWORD_1(reg, FRF_AA_INT_ACK_KER_FIELD, 0xb7eb7e); + efx_writed(efx, ®, FR_AA_INT_ACK_KER); + efx_readd(efx, ®, FR_AA_WORK_AROUND_BROKEN_PCI_READS); } -/* Process a fatal interrupt - * Disable bus mastering ASAP and schedule a reset - */ -static irqreturn_t falcon_fatal_interrupt(struct efx_nic *efx) +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_IRQ_HANDLER_REGS) +irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id) +#else +irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id, + struct pt_regs *regs + __attribute__ ((unused))) +#endif { - struct falcon_nic_data *nic_data = efx->nic_data; - efx_oword_t *int_ker = (efx_oword_t *) efx->irq_status.addr; - efx_oword_t fatal_intr; - int error, mem_perr; - static int n_int_errors; - - falcon_read(efx, &fatal_intr, FATAL_INTR_REG_KER); - error = EFX_OWORD_FIELD(fatal_intr, INT_KER_ERROR); - - EFX_ERR(efx, "SYSTEM ERROR " EFX_OWORD_FMT " status " - EFX_OWORD_FMT ": %s\n", EFX_OWORD_VAL(*int_ker), - EFX_OWORD_VAL(fatal_intr), - error ? "disabling bus mastering" : "no recognised error"); - if (error == 0) - goto out; - - /* If this is a memory parity error dump which blocks are offending */ - mem_perr = EFX_OWORD_FIELD(fatal_intr, MEM_PERR_INT_KER); - if (mem_perr) { - efx_oword_t reg; - falcon_read(efx, ®, MEM_STAT_REG_KER); - EFX_ERR(efx, "SYSTEM ERROR: memory parity error " - EFX_OWORD_FMT "\n", EFX_OWORD_VAL(reg)); - } - - /* Disable DMA bus mastering on both devices */ - pci_disable_device(efx->pci_dev); - if (FALCON_IS_DUAL_FUNC(efx)) - pci_disable_device(nic_data->pci_dev2); - - if (++n_int_errors < FALCON_MAX_INT_ERRORS) { - EFX_ERR(efx, "SYSTEM ERROR - reset scheduled\n"); - efx_schedule_reset(efx, RESET_TYPE_INT_ERROR); - } else { - EFX_ERR(efx, "SYSTEM ERROR - max number of errors seen." - "NIC will be disabled\n"); - efx_schedule_reset(efx, RESET_TYPE_DISABLE); - } -out: - return IRQ_HANDLED; -} - -/* Handle a legacy interrupt from Falcon - * Acknowledges the interrupt and schedule event queue processing. - * - * This routine must guarantee not to touch the hardware when - * interrupts are disabled, to allow for correct semantics of - * efx_suspend() and efx_resume(). - */ -static irqreturn_t falcon_legacy_interrupt_b0(int irq, void *dev_id, - struct pt_regs *regs - __attribute__ ((unused))) -{ - struct efx_nic *efx = (struct efx_nic *)dev_id; - efx_oword_t *int_ker = (efx_oword_t *) efx->irq_status.addr; - struct efx_channel *channel; - efx_dword_t reg; - u32 queues; - int syserr; - - /* Read the ISR which also ACKs the interrupts */ - falcon_readl(efx, ®, INT_ISR0_B0); - queues = EFX_EXTRACT_DWORD(reg, 0, 31); - - /* Check to see if we have a serious error condition */ - syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT); - if (unlikely(syserr)) - return falcon_fatal_interrupt(efx); - - if (queues == 0) - return IRQ_NONE; - - efx->last_irq_cpu = raw_smp_processor_id(); - EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_DWORD_FMT "\n", - irq, raw_smp_processor_id(), EFX_DWORD_VAL(reg)); - - /* Schedule processing of any interrupting queues */ - channel = &efx->channel[0]; - while (queues) { - if (queues & 0x01) - efx_schedule_channel(channel); - channel++; - queues >>= 1; - } - - return IRQ_HANDLED; -} - - -static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id, - struct pt_regs *regs - __attribute__ ((unused))) -{ - struct efx_nic *efx = (struct efx_nic *)dev_id; - efx_oword_t *int_ker = (efx_oword_t *) efx->irq_status.addr; + struct efx_nic *efx = dev_id; + efx_oword_t *int_ker = efx->irq_status.addr; struct efx_channel *channel; int syserr; int queues; @@ -1640,15 +234,15 @@ 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); + syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); if (unlikely(syserr)) - return falcon_fatal_interrupt(efx); + return efx_nic_fatal_interrupt(efx); /* Determine interrupting queues, clear interrupt status * register and acknowledge the device interrupt. */ - BUILD_BUG_ON(INT_EVQS_WIDTH > EFX_MAX_CHANNELS); - queues = EFX_OWORD_FIELD(*int_ker, INT_EVQS); + BUILD_BUG_ON(FSF_AZ_NET_IVEC_INT_Q_WIDTH > EFX_MAX_CHANNELS); + queues = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_INT_Q); EFX_ZERO_OWORD(*int_ker); wmb(); /* Ensure the vector is cleared before interrupt ack */ falcon_irq_ack_a1(efx); @@ -1665,130 +259,6 @@ 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 pt_regs *regs - __attribute__ ((unused))) -{ - 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; - 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 @@ -1796,135 +266,214 @@ ************************************************************************** */ +#define FALCON_SPI_MAX_LEN sizeof(efx_oword_t) + +static int falcon_spi_poll(struct efx_nic *efx) +{ + efx_oword_t reg; + efx_reado(efx, ®, FR_AB_EE_SPI_HCMD); + return EFX_OWORD_FIELD(reg, FRF_AB_EE_SPI_HCMD_CMD_EN) ? -EBUSY : 0; +} + /* 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; + /* Most commands will finish quickly, so we start polling at + * very short intervals. Sometimes the command may have to + * wait for VPD or expansion ROM access outside of our + * control, so we allow up to 100 ms. */ + unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 10); + int i; - 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) + for (i = 0; i < 10; i++) { + if (!falcon_spi_poll(efx)) return 0; udelay(10); - } while (++count < 10000); /* wait upto 100msec */ - EFX_ERR(efx, "timed out waiting for SPI\n"); - return -ETIMEDOUT; + } + + for (;;) { + if (!falcon_spi_poll(efx)) + return 0; + if (time_after_eq(jiffies, timeout)) { + EFX_ERR(efx, "timed out waiting for SPI\n"); + return -ETIMEDOUT; + } + schedule_timeout_uninterruptible(1); + } } -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 falcon_spi_cmd(struct efx_nic *efx, const struct efx_spi_device *spi, + unsigned int command, int address, + const void *in, void *out, size_t len) { - int addressed = (address >= 0); + bool addressed = (address >= 0); + bool reading = (out != NULL); efx_oword_t reg; int rc; /* Input validation */ if (len > FALCON_SPI_MAX_LEN) return -EINVAL; + BUG_ON(!mutex_is_locked(&efx->spi_lock)); - /* Acquire SPI lock */ - mutex_lock(&efx->spi_lock); - - /* Check SPI not currently being accessed */ - rc = falcon_spi_wait(efx); + /* Check that previous command is not still running */ + rc = falcon_spi_poll(efx); if (rc) - goto out; + return rc; /* 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); + EFX_POPULATE_OWORD_1(reg, FRF_AB_EE_SPI_HADR_ADR, address); + efx_writeo(efx, ®, FR_AB_EE_SPI_HADR); } - /* Issue read command */ + /* Program data register, if we have data */ + if (in != NULL) { + memcpy(®, in, len); + efx_writeo(efx, ®, FR_AB_EE_SPI_HDATA); + } + + /* Issue read/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_READ, - EE_SPI_HCMD_DUBCNT, 0, - EE_SPI_HCMD_ADBCNT, + FRF_AB_EE_SPI_HCMD_CMD_EN, 1, + FRF_AB_EE_SPI_HCMD_SF_SEL, spi->device_id, + FRF_AB_EE_SPI_HCMD_DABCNT, len, + FRF_AB_EE_SPI_HCMD_READ, reading, + FRF_AB_EE_SPI_HCMD_DUBCNT, 0, + FRF_AB_EE_SPI_HCMD_ADBCNT, (addressed ? spi->addr_len : 0), - EE_SPI_HCMD_ENC, command); - falcon_write(efx, ®, EE_SPI_HCMD_REG_KER); + FRF_AB_EE_SPI_HCMD_ENC, command); + efx_writeo(efx, ®, FR_AB_EE_SPI_HCMD); - /* Wait for read to complete */ + /* Wait for read/write to complete */ rc = falcon_spi_wait(efx); if (rc) - goto out; + return rc; /* Read data */ - falcon_read(efx, ®, EE_SPI_HDATA_REG_KER); - memcpy(data, ®, len); + if (out != NULL) { + efx_reado(efx, ®, FR_AB_EE_SPI_HDATA); + memcpy(out, ®, len); + } - out: - /* Release SPI lock */ - mutex_unlock(&efx->spi_lock); + return 0; +} +static size_t +falcon_spi_write_limit(const struct efx_spi_device *spi, size_t start) +{ + return min(FALCON_SPI_MAX_LEN, + (spi->block_size - (start & (spi->block_size - 1)))); +} + +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); +} + +/* Wait up to 10 ms for buffered write completion */ +int +falcon_spi_wait_write(struct efx_nic *efx, const struct efx_spi_device *spi) +{ + unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 100); + u8 status; + int rc; + + for (;;) { + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, + &status, sizeof(status)); + if (rc) + return rc; + if (!(status & SPI_STATUS_NRDY)) + return 0; + if (time_after_eq(jiffies, timeout)) { + EFX_ERR(efx, "SPI write timeout on device %d" + " last status=0x%02x\n", + spi->device_id, status); + return -ETIMEDOUT; + } + schedule_timeout_uninterruptible(1); + } +} + +int falcon_spi_read(struct efx_nic *efx, const struct efx_spi_device *spi, + loff_t start, size_t len, size_t *retlen, u8 *buffer) +{ + size_t block_len, pos = 0; + unsigned int command; + int rc = 0; + + while (pos < len) { + block_len = min(len - pos, FALCON_SPI_MAX_LEN); + + command = efx_spi_munge_command(spi, SPI_READ, start + pos); + rc = falcon_spi_cmd(efx, spi, command, start + pos, NULL, + buffer + pos, block_len); + if (rc) + break; + pos += block_len; + + /* Avoid locking up the system */ + cond_resched(); + if (signal_pending(current)) { + rc = -EINTR; + break; + } + } + + if (retlen) + *retlen = pos; 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 +falcon_spi_write(struct efx_nic *efx, const struct efx_spi_device *spi, + loff_t start, size_t len, size_t *retlen, const u8 *buffer) { - int addressed = (address >= 0); - efx_oword_t reg; - int rc; + u8 verify_buffer[FALCON_SPI_MAX_LEN]; + size_t block_len, pos = 0; + unsigned int command; + int rc = 0; - /* Input validation */ - if (len > (addressed ? efx_spi_write_limit(spi, address) - : FALCON_SPI_MAX_LEN)) - return -EINVAL; + while (pos < len) { + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); + if (rc) + break; - /* Acquire SPI lock */ - mutex_lock(&efx->spi_lock); + block_len = min(len - pos, + falcon_spi_write_limit(spi, start + pos)); + command = efx_spi_munge_command(spi, SPI_WRITE, start + pos); + rc = falcon_spi_cmd(efx, spi, command, start + pos, + buffer + pos, NULL, block_len); + if (rc) + break; - /* Check SPI not currently being accessed */ - rc = falcon_spi_wait(efx); - if (rc) - goto out; + rc = falcon_spi_wait_write(efx, spi); + if (rc) + break; - /* 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); + command = efx_spi_munge_command(spi, SPI_READ, start + pos); + rc = falcon_spi_cmd(efx, spi, command, start + pos, + NULL, verify_buffer, block_len); + if (memcmp(verify_buffer, buffer + pos, block_len)) { + rc = -EIO; + break; + } + + pos += block_len; + + /* Avoid locking up the system */ + cond_resched(); + if (signal_pending(current)) { + rc = -EINTR; + break; + } } - /* 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); - + if (retlen) + *retlen = pos; return rc; } @@ -1934,39 +483,71 @@ * ************************************************************************** */ -void falcon_drain_tx_fifo(struct efx_nic *efx) + +static void falcon_push_multicast_hash(struct efx_nic *efx) { - efx_oword_t temp; + union efx_multicast_hash *mc_hash = &efx->multicast_hash; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + + efx_writeo(efx, &mc_hash->oword[0], FR_AB_MAC_MC_HASH_REG0); + efx_writeo(efx, &mc_hash->oword[1], FR_AB_MAC_MC_HASH_REG1); +} + +static void falcon_reset_macs(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + efx_oword_t reg, mac_ctrl; int count; - if (FALCON_REV(efx) < FALCON_REV_B0) - return; + if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) { + /* It's not safe to use GLB_CTL_REG to reset the + * macs, so instead use the internal MAC resets + */ + if (!EFX_IS10G(efx)) { + EFX_POPULATE_OWORD_1(reg, FRF_AB_GM_SW_RST, 1); + efx_writeo(efx, ®, FR_AB_GM_CFG1); + udelay(1000); - 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_POPULATE_OWORD_1(reg, FRF_AB_GM_SW_RST, 0); + efx_writeo(efx, ®, FR_AB_GM_CFG1); + udelay(1000); + return; + } else { + EFX_POPULATE_OWORD_1(reg, FRF_AB_XM_CORE_RST, 1); + efx_writeo(efx, ®, FR_AB_XM_GLB_CFG); - /* MAC stats will fail whilst the TX fifo is draining. Serialise - * the drain sequence with the statistics fetch */ - spin_lock(&efx->stats_lock); + for (count = 0; count < 10000; count++) { + efx_reado(efx, ®, FR_AB_XM_GLB_CFG); + if (EFX_OWORD_FIELD(reg, FRF_AB_XM_CORE_RST) == + 0) + return; + udelay(10); + } - EFX_SET_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0, 1); - falcon_write(efx, &temp, MAC0_CTRL_REG_KER); + EFX_ERR(efx, "timed out waiting for XMAC core reset\n"); + } + } - /* 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); + /* Mac stats will fail whist the TX fifo is draining */ + WARN_ON(nic_data->stats_disable_count == 0); + + efx_reado(efx, &mac_ctrl, FR_AB_MAC_CTRL); + EFX_SET_OWORD_FIELD(mac_ctrl, FRF_BB_TXFIFO_DRAIN_EN, 1); + efx_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL); + + efx_reado(efx, ®, FR_AB_GLB_CTL); + EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_XGTX, 1); + EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_XGRX, 1); + EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_EM, 1); + efx_writeo(efx, ®, FR_AB_GLB_CTL); 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_reado(efx, ®, FR_AB_GLB_CTL); + if (!EFX_OWORD_FIELD(reg, FRF_AB_RST_XGTX) && + !EFX_OWORD_FIELD(reg, FRF_AB_RST_XGRX) && + !EFX_OWORD_FIELD(reg, FRF_AB_RST_EM)) { EFX_LOG(efx, "Completed MAC reset after %d loops\n", count); break; @@ -1979,135 +560,198 @@ udelay(10); } - spin_unlock(&efx->stats_lock); - - /* 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); + /* Ensure the correct MAC is selected before statistics + * are re-enabled by the caller */ + efx_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL); } -void falcon_deconfigure_mac_wrapper(struct efx_nic *efx) +void falcon_drain_tx_fifo(struct efx_nic *efx) { - struct falcon_nic_data *nic_data = efx->nic_data; - efx_oword_t temp; - int changing_loopback; + efx_oword_t reg; - if (FALCON_REV(efx) < FALCON_REV_B0) + if ((efx_nic_rev(efx) < EFX_REV_FALCON_B0) || + (efx->loopback_mode != LOOPBACK_NONE)) + return; + + efx_reado(efx, ®, FR_AB_MAC_CTRL); + /* There is no point in draining more than once */ + if (EFX_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN)) + return; + + falcon_reset_macs(efx); +} + +static void falcon_deconfigure_mac_wrapper(struct efx_nic *efx) +{ + efx_oword_t reg; + + if (efx_nic_rev(efx) < EFX_REV_FALCON_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); + efx_reado(efx, ®, FR_AZ_RX_CFG); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 0); + efx_writeo(efx, ®, FR_AZ_RX_CFG); - /* 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 (EFX_WORKAROUND_11667(efx) && (efx->phy_type == PHY_TYPE_10XPRESS)) { - if (changing_loopback) - return; - } - - if (changing_loopback || !efx->link_up) - falcon_drain_tx_fifo(efx); + /* Isolate TX -> MAC */ + falcon_drain_tx_fifo(efx); } void falcon_reconfigure_mac_wrapper(struct efx_nic *efx) { + struct efx_link_state *link_state = &efx->link_state; efx_oword_t reg; int link_speed; - 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; + switch (link_state->speed) { + case 10000: link_speed = 3; break; + case 1000: link_speed = 2; break; + case 100: link_speed = 1; break; + default: link_speed = 0; break; + } + /* 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); + FRF_AB_MAC_XOFF_VAL, 0xffff /* max pause time */, + FRF_AB_MAC_BCAD_ACPT, 1, + FRF_AB_MAC_UC_PROM, efx->promiscuous, + FRF_AB_MAC_LINK_STATUS, 1, /* always set */ + FRF_AB_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); + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { + EFX_SET_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN, + !link_state->up); } - falcon_write(efx, ®, MAC0_CTRL_REG_KER); + efx_writeo(efx, ®, FR_AB_MAC_CTRL); /* Restore the multicast hash registers. */ - falcon_set_multicast_hash(efx); + falcon_push_multicast_hash(efx); - /* Transmission of pause frames when RX crosses the threshold is - * covered by RX_XOFF_MAC_EN and XM_TX_CFG_REG:XM_FCNTL. - * Action on receipt of pause frames is controller by XM_DIS_FCNTL */ - tx_fc = (efx->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); - + efx_reado(efx, ®, FR_AZ_RX_CFG); + /* Enable XOFF signal from RX FIFO (we enabled it during NIC + * initialisation but it may read back as 0) */ + EFX_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, 1); /* Unisolate the MAC -> RX */ - if (FALCON_REV(efx) >= FALCON_REV_B0) - EFX_SET_OWORD_FIELD(reg, RX_INGR_EN_B0, 1); - falcon_write(efx, ®, RX_CFG_REG_KER); + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 1); + efx_writeo(efx, ®, FR_AZ_RX_CFG); } -int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset) +static void falcon_stats_request(struct efx_nic *efx) { + struct falcon_nic_data *nic_data = efx->nic_data; efx_oword_t reg; - u32 *dma_done; - int i; - if (disable_dma_stats) - return 0; + WARN_ON(nic_data->stats_pending); + WARN_ON(nic_data->stats_disable_count); - /* Statistics fetch will fail if the MAC is in TX drain */ - if (FALCON_REV(efx) >= FALCON_REV_B0) { - efx_oword_t temp; - falcon_read(efx, &temp, MAC0_CTRL_REG_KER); - if (EFX_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0)) - return 0; - } + if (nic_data->stats_dma_done == NULL) + return; /* no mac selected */ - /* Clear completion pointer */ - dma_done = (efx->stats_buffer.addr + done_offset); - *dma_done = FALCON_STATS_NOT_DONE; + *nic_data->stats_dma_done = FALCON_STATS_NOT_DONE; + nic_data->stats_pending = true; wmb(); /* ensure done flag is clear */ /* Initiate DMA transfer of stats */ EFX_POPULATE_OWORD_2(reg, - MAC_STAT_DMA_CMD, 1, - MAC_STAT_DMA_ADR, + FRF_AB_MAC_STAT_DMA_CMD, 1, + FRF_AB_MAC_STAT_DMA_ADR, efx->stats_buffer.dma_addr); - falcon_write(efx, ®, MAC0_STAT_DMA_REG_KER); + efx_writeo(efx, ®, FR_AB_MAC_STAT_DMA); - /* Wait for transfer to complete */ - for (i = 0; i < 400; i++) { - if (*(volatile u32 *)dma_done == FALCON_STATS_DONE) - return 0; - udelay(10); - } +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_HAVE_ROUND_JIFFIES_UP) + mod_timer(&nic_data->stats_timer, round_jiffies_up(jiffies + HZ / 2)); +#else + mod_timer(&nic_data->stats_timer, jiffies + HZ); +#endif +} - if (EFX_WORKAROUND_8419(efx)) { - disable_dma_stats = 1; - EFX_INFO(efx, "MAC stats DMA disabled\n"); +static void falcon_stats_complete(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + + if (!nic_data->stats_pending) + return; + + nic_data->stats_pending = 0; + if (*nic_data->stats_dma_done == FALCON_STATS_DONE) { + rmb(); /* read the done flag before the stats */ + efx->mac_op->update_stats(efx); } else { EFX_ERR(efx, "timed out waiting for statistics\n"); } +} - return -ETIMEDOUT; +static void falcon_stats_timer_func(unsigned long context) +{ + struct efx_nic *efx = (struct efx_nic *)context; + struct falcon_nic_data *nic_data = efx->nic_data; + + spin_lock(&efx->stats_lock); + + falcon_stats_complete(efx); + if (nic_data->stats_disable_count == 0) + falcon_stats_request(efx); + + spin_unlock(&efx->stats_lock); +} + +static void falcon_switch_mac(struct efx_nic *efx); + +static bool falcon_loopback_link_poll(struct efx_nic *efx) +{ + struct efx_link_state old_state = efx->link_state; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + WARN_ON(!LOOPBACK_INTERNAL(efx)); + + efx->link_state.fd = true; + efx->link_state.fc = efx->wanted_fc; + efx->link_state.up = true; + + if (efx->loopback_mode == LOOPBACK_GMAC) + efx->link_state.speed = 1000; + else + efx->link_state.speed = 10000; + + return !efx_link_state_equal(&efx->link_state, &old_state); +} + +static int falcon_reconfigure_port(struct efx_nic *efx) +{ + int rc; + + WARN_ON(efx_nic_rev(efx) > EFX_REV_FALCON_B0); + + /* Poll the PHY link state *before* reconfiguring it. This means we + * will pick up the correct speed (in loopback) to select the correct + * MAC. + */ + if (LOOPBACK_INTERNAL(efx)) + falcon_loopback_link_poll(efx); + else + efx->phy_op->poll(efx); + + falcon_stop_nic_stats(efx); + falcon_deconfigure_mac_wrapper(efx); + + falcon_switch_mac(efx); + + efx->phy_op->reconfigure(efx); + rc = efx->mac_op->reconfigure(efx); + BUG_ON(rc); + + falcon_start_nic_stats(efx); + + /* Synchronise efx->link_state with the kernel */ + efx_link_status_changed(efx); + + return 0; } /************************************************************************** @@ -2117,40 +761,21 @@ ************************************************************************** */ -/* 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; + efx_oword_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) { + /* wait upto 50ms - taken max from datasheet */ + for (count = 0; count < 5000; count++) { + efx_reado(efx, &md_stat, FR_AB_MD_STAT); + if (EFX_OWORD_FIELD(md_stat, FRF_AB_MD_BSY) == 0) { + if (EFX_OWORD_FIELD(md_stat, FRF_AB_MD_LNFL) != 0 || + EFX_OWORD_FIELD(md_stat, FRF_AB_MD_BSERR) != 0) { EFX_ERR(efx, "error from GMII access " - EFX_DWORD_FMT"\n", - EFX_DWORD_VAL(md_stat)); + EFX_OWORD_FMT"\n", + EFX_OWORD_VAL(md_stat)); return -EIO; } return 0; @@ -2161,304 +786,430 @@ 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) +/* Write an MDIO register of a PHY connected to Falcon. */ +static int falcon_mdio_write(struct net_device *net_dev, + int prtad, int devad, u16 addr, u16 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; + struct efx_nic *efx = netdev_priv(net_dev); efx_oword_t reg; + int rc; - /* 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); + EFX_REGDUMP(efx, "writing MDIO %d register %d.%d with 0x%04x\n", + prtad, devad, addr, value); - if (phy_id2 == PHY_ADDR_INVALID) - return; + mutex_lock(&efx->mdio_lock); - /* 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) + /* Check MDIO not currently being accessed */ + rc = falcon_gmii_wait(efx); + if (rc) goto out; /* Write the address/ID register */ - EFX_POPULATE_OWORD_1(reg, MD_PHY_ADR, addr); - falcon_write(efx, ®, MD_PHY_ADR_REG_KER); + EFX_POPULATE_OWORD_1(reg, FRF_AB_MD_PHY_ADR, addr); + efx_writeo(efx, ®, FR_AB_MD_PHY_ADR); - 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); + EFX_POPULATE_OWORD_2(reg, FRF_AB_MD_PRT_ADR, prtad, + FRF_AB_MD_DEV_ADR, devad); + efx_writeo(efx, ®, FR_AB_MD_ID); /* Write data */ - EFX_POPULATE_OWORD_1(reg, MD_TXD, value); - falcon_write(efx, ®, MD_TXD_REG_KER); + EFX_POPULATE_OWORD_1(reg, FRF_AB_MD_TXD, value); + efx_writeo(efx, ®, FR_AB_MD_TXD); EFX_POPULATE_OWORD_2(reg, - MD_WRC, 1, - MD_GC, (phy_10g ? 0 : 1)); - falcon_write(efx, ®, MD_CS_REG_KER); + FRF_AB_MD_WRC, 1, + FRF_AB_MD_GC, 0); + efx_writeo(efx, ®, FR_AB_MD_CS); /* Wait for data to be written */ - if (falcon_gmii_wait(efx) != 0) { + rc = falcon_gmii_wait(efx); + if (rc) { /* Abort the write operation */ EFX_POPULATE_OWORD_2(reg, - MD_WRC, 0, - MD_GC, 1); - falcon_write(efx, ®, MD_CS_REG_KER); + FRF_AB_MD_WRC, 0, + FRF_AB_MD_GC, 1); + efx_writeo(efx, ®, FR_AB_MD_CS); udelay(10); } - out: - /* Release PHY lock */ - spin_unlock_bh(&efx->phy_lock); +out: + mutex_unlock(&efx->mdio_lock); + return rc; } -/* 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) +/* Read an MDIO register of a PHY connected to Falcon. */ +static int falcon_mdio_read(struct net_device *net_dev, + int prtad, int devad, u16 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; + struct efx_nic *efx = netdev_priv(net_dev); efx_oword_t reg; - int value = -1; + int rc; - if (phy_addr == PHY_ADDR_INVALID) - return -1; + mutex_lock(&efx->mdio_lock); - /* 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) + /* Check MDIO not currently being accessed */ + rc = falcon_gmii_wait(efx); + if (rc) 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, FRF_AB_MD_PHY_ADR, addr); + efx_writeo(efx, ®, FR_AB_MD_PHY_ADR); - EFX_POPULATE_OWORD_1(reg, MD_PRT_DEV_ADR, phy_addr); - falcon_write(efx, ®, MD_ID_REG_KER); + EFX_POPULATE_OWORD_2(reg, FRF_AB_MD_PRT_ADR, prtad, + FRF_AB_MD_DEV_ADR, devad); + efx_writeo(efx, ®, FR_AB_MD_ID); - /* Request data to be read */ - EFX_POPULATE_OWORD_2(reg, - MD_RDC, 1, - MD_GC, 0); - } - falcon_write(efx, ®, MD_CS_REG_KER); + /* Request data to be read */ + EFX_POPULATE_OWORD_2(reg, FRF_AB_MD_RDC, 1, FRF_AB_MD_GC, 0); + efx_writeo(efx, ®, FR_AB_MD_CS); /* 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); + rc = falcon_gmii_wait(efx); + if (rc == 0) { + efx_reado(efx, ®, FR_AB_MD_RXD); + rc = EFX_OWORD_FIELD(reg, FRF_AB_MD_RXD); + EFX_REGDUMP(efx, "read from MDIO %d register %d.%d, got %04x\n", + prtad, devad, addr, rc); } else { /* Abort the read operation */ EFX_POPULATE_OWORD_2(reg, - MD_RIC, 0, - MD_GC, 1); - falcon_write(efx, ®, MD_CS_REG_KER); + FRF_AB_MD_RIC, 0, + FRF_AB_MD_GC, 1); + efx_writeo(efx, ®, FR_AB_MD_CS); - EFX_LOG(efx, "read from GMII 0x%x register %02x, got " - "error %d\n", phy_id, addr, value); + EFX_LOG(efx, "read from MDIO %d register %d.%d, got error %d\n", + prtad, devad, addr, rc); } - out: - /* Release PHY lock */ - spin_unlock_bh(&efx->phy_lock); - - return value; +out: + mutex_unlock(&efx->mdio_lock); + return rc; } -static void falcon_init_mdio(struct mii_if_info *gmii) +static void falcon_clock_mac(struct efx_nic *efx) { - 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_PHY_ADR)) - 1); + unsigned strap_val; + efx_oword_t nic_stat; + + /* Configure the NIC generated MAC clock correctly */ + efx_reado(efx, &nic_stat, FR_AB_NIC_STAT); + strap_val = EFX_IS10G(efx) ? 5 : 3; + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { + EFX_SET_OWORD_FIELD(nic_stat, FRF_BB_EE_STRAP_EN, 1); + EFX_SET_OWORD_FIELD(nic_stat, FRF_BB_EE_STRAP, strap_val); + efx_writeo(efx, &nic_stat, FR_AB_NIC_STAT); + } else { + /* Falcon A1 does not support 1G/10G speed switching + * and must not be used with a PHY that does. */ + BUG_ON(EFX_OWORD_FIELD(nic_stat, FRF_AB_STRAP_PINS) != + strap_val); + } } -static int falcon_probe_gmac_port(struct efx_nic *efx) +static void falcon_switch_mac(struct efx_nic *efx) { - struct efx_phy_operations *phy_op = efx->phy_op; + struct efx_mac_operations *old_mac_op = efx->mac_op; + struct falcon_nic_data *nic_data = efx->nic_data; + unsigned int stats_done_offset; - 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; + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + WARN_ON(nic_data->stats_disable_count == 0); + + efx->mac_op = (EFX_IS10G(efx) ? + &falcon_xmac_operations : &falcon_gmac_operations); + + if (EFX_IS10G(efx)) + stats_done_offset = XgDmaDone_offset; + else + stats_done_offset = GDmaDone_offset; + nic_data->stats_dma_done = efx->stats_buffer.addr + stats_done_offset; + + if (old_mac_op == efx->mac_op) + return; + + falcon_clock_mac(efx); + + EFX_LOG(efx, "selected %cMAC\n", EFX_IS10G(efx) ? 'X' : 'G'); + /* Not all macs support a mac-level link state */ + efx->xmac_poll_required = false; + falcon_reset_macs(efx); } -static int falcon_probe_xmac_port(struct efx_nic *efx) +/* This call is responsible for hooking in the MAC and PHY operations */ +static int falcon_probe_port(struct efx_nic *efx) { - struct efx_phy_operations *phy_op = efx->phy_op; + int rc; - 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) -{ + /* Hook in PHY operations table */ switch (efx->phy_type) { - case PHY_TYPE_1G_ALASKA: - efx->phy_op = &alaska_phy_operations; + case PHY_TYPE_SFX7101: + efx->phy_op = &falcon_sfx7101_phy_ops; break; - case PHY_TYPE_10XPRESS: - efx->phy_op = &falcon_tenxpress_phy_ops; + case PHY_TYPE_SFT9001A: + case PHY_TYPE_SFT9001B: + efx->phy_op = &falcon_sft9001_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; + case PHY_TYPE_QT2022C2: + case PHY_TYPE_QT2025C: + efx->phy_op = &falcon_qt202x_phy_ops; break; default: EFX_ERR(efx, "Unknown PHY type %d\n", efx->phy_type); - return -1; + return -ENODEV; } - return 0; -} -/* This call is responsible for hooking in the MAC and PHY operations */ -int falcon_probe_port(struct efx_nic *efx) -{ - int rc; + /* Fill out MDIO structure and loopback modes */ + efx->mdio.mdio_read = falcon_mdio_read; + efx->mdio.mdio_write = falcon_mdio_write; + rc = efx->phy_op->probe(efx); + if (rc != 0) + return rc; - /* Hook in PHY operations table */ - rc = falcon_probe_phy(efx); + /* Initial assumption */ + efx->link_state.speed = 10000; + efx->link_state.fd = true; + + /* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */ + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) + efx->wanted_fc = EFX_FC_RX | EFX_FC_TX; + else + efx->wanted_fc = EFX_FC_RX; + if (efx->mdio.mmds & MDIO_DEVS_AN) + efx->wanted_fc |= EFX_FC_AUTO; + + /* Allocate buffer for stats */ + rc = efx_nic_alloc_buffer(efx, &efx->stats_buffer, + FALCON_MAC_STATS_SIZE); 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_LOG(efx, "stats buffer at %llx (virt %p phys %llx)\n", + (u64)efx->stats_buffer.dma_addr, efx->stats_buffer.addr, - virt_to_phys(efx->stats_buffer.addr)); + (u64)virt_to_phys(efx->stats_buffer.addr)); return 0; } -void falcon_remove_port(struct efx_nic *efx) +static void falcon_remove_port(struct efx_nic *efx) { - /* Free stats buffer */ - falcon_free_buffer(efx, &efx->stats_buffer); + efx->phy_op->remove(efx); + efx_nic_free_buffer(efx, &efx->stats_buffer); } /************************************************************************** * - * Multicast filtering + * Falcon test code * - ************************************************************************** - */ + **************************************************************************/ -void falcon_set_multicast_hash(struct efx_nic *efx) +/* Read and verify the correctness of the nvram, optionally returning a copy + * of the nvram configuration section if appropriate */ +int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out) { - union efx_multicast_hash *mc_hash = &efx->multicast_hash; + struct falcon_nvconfig *nvconfig; + struct efx_spi_device *spi; + void *region; + int rc, magic_num, struct_ver; + __le16 *word, *limit; + u32 csum; - /* Broadcast packets go through the multicast hash filter. - * ether_crc_le() of the broadcast address is 0xbe2612ff - * so we always add bit 0xff to the mask. - */ - set_bit_le(0xff, mc_hash->byte); + spi = efx->spi_flash ? efx->spi_flash : efx->spi_eeprom; + if (!spi) + return -EINVAL; - falcon_write(efx, &mc_hash->oword[0], MAC_MCAST_HASH_REG0_KER); - falcon_write(efx, &mc_hash->oword[1], MAC_MCAST_HASH_REG1_KER); + region = kmalloc(FALCON_NVCONFIG_END, GFP_KERNEL); + if (!region) + return -ENOMEM; + nvconfig = region + FALCON_NVCONFIG_OFFSET; + + mutex_lock(&efx->spi_lock); + rc = falcon_spi_read(efx, spi, 0, FALCON_NVCONFIG_END, NULL, region); + mutex_unlock(&efx->spi_lock); + if (rc) { + EFX_ERR(efx, "Failed to read %s\n", + efx->spi_flash ? "flash" : "EEPROM"); + rc = -EIO; + goto out; + } + + magic_num = le16_to_cpu(nvconfig->board_magic_num); + struct_ver = le16_to_cpu(nvconfig->board_struct_ver); + + rc = -EINVAL; + if (magic_num != FALCON_NVCONFIG_BOARD_MAGIC_NUM) { + EFX_ERR(efx, "NVRAM bad magic 0x%x\n", magic_num); + goto out; + } + if (struct_ver < 2) { + EFX_ERR(efx, "NVRAM has ancient version 0x%x\n", struct_ver); + goto out; + } else if (struct_ver < 4) { + word = &nvconfig->board_magic_num; + limit = (__le16 *) (nvconfig + 1); + } else { + word = region; + limit = region + FALCON_NVCONFIG_END; + } + for (csum = 0; word < limit; ++word) + csum += le16_to_cpu(*word); + + if (~csum & 0xffff) { + EFX_ERR(efx, "NVRAM has incorrect checksum\n"); + goto out; + } + + rc = 0; + if (nvconfig_out) + memcpy(nvconfig_out, nvconfig, sizeof(*nvconfig)); + + out: + kfree(region); + return rc; +} + +static int +falcon_b0_test_sram(struct efx_nic *efx, + void (*pattern)(unsigned, efx_qword_t *, int, int), + int a, int b) +{ + efx_qword_t buf1, buf2; + int wptr, rptr, finish, rc = 0; + void __iomem *membase; + + /* So we can test the sram allocated to descriptor caches, access + * via SRAM_DBG_REG rather than BUF_FULL_TBL. */ + BUG_ON(efx->port_enabled); + membase = ioremap_nocache(efx->membase_phys + FR_BZ_SRM_DBG, + efx->sram_lim); + if (!membase) { + EFX_ERR(efx, "Unable to map SRAM_DBG\n"); + return -EIO; + } + + finish = efx->sram_lim / sizeof(buf1) - 1; + + /* write and verify the pattern in 64 8-byte chunks, to avoid + * potentially overrunning the sram update fifo */ + for (rptr = 0, wptr = 0; wptr <= finish; ++wptr) { + pattern(wptr, &buf1, a, b); + efx_sram_writeq(efx, membase, &buf1, wptr); + + if (((wptr - rptr) < 64) && (wptr < finish)) + continue; + + for (; rptr <= wptr; ++rptr) { + pattern(rptr, &buf1, a, b); + efx_sram_readq(efx, membase, &buf2, rptr); + + if (!memcmp(&buf1, &buf2, sizeof(buf1))) + continue; + + EFX_ERR(efx, "sram test failed at index 0x%x. " + "wrote "EFX_QWORD_FMT" read "EFX_QWORD_FMT"\n", + rptr, EFX_QWORD_VAL(buf1), EFX_QWORD_VAL(buf2)); + rc = -EIO; + goto out_unmap; + } + } + +out_unmap: + iounmap(membase); + + return rc; +} + +static int falcon_test_nvram(struct efx_nic *efx) +{ + return falcon_read_nvram(efx, NULL); +} + +static const struct efx_nic_register_test falcon_b0_register_tests[] = { + { FR_AZ_ADR_REGION, + EFX_OWORD32(0x0003FFFF, 0x0003FFFF, 0x0003FFFF, 0x0003FFFF) }, + { FR_AZ_RX_CFG, + EFX_OWORD32(0xFFFFFFFE, 0x00017FFF, 0x00000000, 0x00000000) }, + { FR_AZ_TX_CFG, + EFX_OWORD32(0x7FFF0037, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_TX_RESERVED, + EFX_OWORD32(0xFFFEFE80, 0x1FFFFFFF, 0x020000FE, 0x007FFFFF) }, + { FR_AB_MAC_CTRL, + EFX_OWORD32(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_SRM_TX_DC_CFG, + EFX_OWORD32(0x001FFFFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_DC_CFG, + EFX_OWORD32(0x0000000F, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_DC_PF_WM, + EFX_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_BZ_DP_CTRL, + EFX_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_GM_CFG2, + EFX_OWORD32(0x00007337, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_GMF_CFG0, + EFX_OWORD32(0x00001F1F, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XM_GLB_CFG, + EFX_OWORD32(0x00000C68, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XM_TX_CFG, + EFX_OWORD32(0x00080164, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XM_RX_CFG, + EFX_OWORD32(0x07100A0C, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XM_RX_PARAM, + EFX_OWORD32(0x00001FF8, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XM_FC, + EFX_OWORD32(0xFFFF0001, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XM_ADR_LO, + EFX_OWORD32(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AB_XX_SD_CTL, + EFX_OWORD32(0x0003FF0F, 0x00000000, 0x00000000, 0x00000000) }, +}; + +static const struct efx_nic_table_test falcon_b0_table_tests[] = { + { FR_BZ_RX_FILTER_TBL0, FR_BZ_RX_FILTER_TBL0_STEP, + FR_BZ_RX_FILTER_TBL0_ROWS, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000003FF) }, + { FR_BB_RX_FILTER_TBL1, FR_BB_RX_FILTER_TBL1_STEP, + FR_BB_RX_FILTER_TBL1_ROWS, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000003FF) }, + { FR_BZ_RX_DESC_PTR_TBL, FR_BZ_RX_DESC_PTR_TBL_STEP, + FR_BB_RX_DESC_PTR_TBL_ROWS, + EFX_OWORD32(0xFFFFFFFF, 0x0FFFFFFF, 0x01800000, 0x00000000) }, + { FR_BZ_TX_DESC_PTR_TBL, FR_BZ_TX_DESC_PTR_TBL_STEP, + FR_BB_TX_DESC_PTR_TBL_ROWS, + EFX_OWORD32(0xFFFFFFFE, 0x0FFFFFFF, 0x0C000000, 0x00000000) }, +}; + +static int falcon_b0_test_registers(struct efx_nic *efx) +{ + return efx_nic_test_registers(efx, falcon_b0_register_tests, + ARRAY_SIZE(falcon_b0_register_tests)); +} + +static int +falcon_b0_test_memory(struct efx_nic *efx, + void (*pattern)(unsigned, efx_qword_t *, int, int), + int a, int b) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + int rc, i; + + rc = falcon_b0_test_sram(efx, pattern, a, b); + if (rc) + return rc; + + for (i = 0; i < ARRAY_SIZE(falcon_b0_table_tests); i++) { + /* In internal SRAM mode, the odd filter bank is + * remapped as a descriptor cache */ + if (falcon_b0_table_tests[i].address == FR_BB_RX_FILTER_TBL1 && + nic_data->sram_config == SRAM_CONFIG_INTERNAL) + continue; + + rc = efx_nic_test_table(efx, &falcon_b0_table_tests[i], + pattern, a, b); + if (rc) + return rc; + } + + return 0; } /************************************************************************** @@ -2468,79 +1219,9 @@ ************************************************************************** */ -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) +static int falcon_reset_hw(struct efx_nic *efx, enum reset_type method) { struct falcon_nic_data *nic_data = efx->nic_data; efx_oword_t glb_ctl_reg_ker; @@ -2550,14 +1231,13 @@ /* 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 (FALCON_IS_DUAL_FUNC(efx)) { + if (efx_nic_is_dual_func(efx)) { rc = pci_save_state(nic_data->pci_dev2); if (rc) { EFX_ERR(efx, "failed to backup PCI state of " @@ -2568,30 +1248,31 @@ } EFX_POPULATE_OWORD_2(glb_ctl_reg_ker, - EXT_PHY_RST_DUR, 0x7, - SWRST, 1); + FRF_AB_EXT_PHY_RST_DUR, + FFE_AB_EXT_PHY_RST_DUR_10240US, + FRF_AB_SWRST, 1); } else { - int reset_phy = (method == RESET_TYPE_INVISIBLE ? - EXCLUDE_FROM_RESET : 0); + EFX_POPULATE_OWORD_7(glb_ctl_reg_ker, + /* exclude PHY from "invisible" reset */ + FRF_AB_EXT_PHY_RST_CTL, + method == RESET_TYPE_INVISIBLE, + /* exclude EEPROM/flash and PCIe */ + FRF_AB_PCIE_CORE_RST_CTL, 1, + FRF_AB_PCIE_NSTKY_RST_CTL, 1, + FRF_AB_PCIE_SD_RST_CTL, 1, + FRF_AB_EE_RST_CTL, 1, + FRF_AB_EXT_PHY_RST_DUR, + FFE_AB_EXT_PHY_RST_DUR_10240US, + FRF_AB_SWRST, 1); + } + efx_writeo(efx, &glb_ctl_reg_ker, FR_AB_GLB_CTL); - 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 (FALCON_IS_DUAL_FUNC(efx)) { + if (efx_nic_is_dual_func(efx)) { rc = pci_restore_state(nic_data->pci_dev2); if (rc) { EFX_ERR(efx, "failed to restore PCI config for " @@ -2609,59 +1290,77 @@ } /* 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) { + efx_reado(efx, &glb_ctl_reg_ker, FR_AB_GLB_CTL); + if (EFX_OWORD_FIELD(glb_ctl_reg_ker, FRF_AB_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; } +static void falcon_monitor(struct efx_nic *efx) +{ + bool link_changed; + int rc; + + BUG_ON(!mutex_is_locked(&efx->mac_lock)); + + rc = falcon_board(efx)->type->monitor(efx); + if (rc) { + EFX_ERR(efx, "Board sensor %s; shutting down PHY\n", + (rc == -ERANGE) ? "reported fault" : "failed"); + efx->phy_mode |= PHY_MODE_LOW_POWER; + rc = __efx_reconfigure_port(efx); + WARN_ON(rc); + } + + if (LOOPBACK_INTERNAL(efx)) + link_changed = falcon_loopback_link_poll(efx); + else + link_changed = efx->phy_op->poll(efx); + + if (link_changed) { + falcon_stop_nic_stats(efx); + falcon_deconfigure_mac_wrapper(efx); + + falcon_switch_mac(efx); + rc = efx->mac_op->reconfigure(efx); + BUG_ON(rc); + + falcon_start_nic_stats(efx); + + efx_link_status_changed(efx); + } + + if (EFX_IS10G(efx)) + falcon_poll_xmac(efx); +} + /* Zeroes out the SRAM contents. This routine must be called in * process context and is allowed to sleep. */ -static int falcon_reset_sram(struct efx_nic *efx) +static int falcon_reset_sram(struct efx_nic *efx, int sram_config) { - struct falcon_nic_data *nic_data = efx->nic_data; - 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 = (nic_data->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); + efx_oword_t srm_cfg_reg_ker; + int count; /* Initiate SRAM reset */ - sram_cfg_val = nic_data->external_sram_cfg; - if (nic_data->external_sram_cfg == SRM_NB_BSZ_ONCHIP_ONLY) - sram_cfg_val = 0; - 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); + FRF_AZ_SRM_INIT_EN, 1, + FRF_AZ_SRM_NB_SZ, sram_config); + efx_writeo(efx, &srm_cfg_reg_ker, FR_AZ_SRM_CFG); /* Wait for SRAM reset to complete */ count = 0; @@ -2672,8 +1371,8 @@ 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_reado(efx, &srm_cfg_reg_ker, FR_AZ_SRM_CFG); + if (!EFX_OWORD_FIELD(srm_cfg_reg_ker, FRF_AZ_SRM_INIT_EN)) { EFX_LOG(efx, "SRAM reset complete\n"); return 0; @@ -2684,13 +1383,16 @@ return -ETIMEDOUT; } -static void falcon_spi_device_init(struct efx_spi_device **spi_device_ret, - unsigned int device_id, u32 device_type) +static int falcon_spi_device_init(struct efx_nic *efx, + 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 = kzalloc(sizeof(*spi_device), GFP_KERNEL); + if (!spi_device) + return -ENOMEM; spi_device->device_id = device_id; spi_device->size = 1 << SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_SIZE); @@ -2706,387 +1408,15 @@ 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) -{ - struct falcon_nic_data *nic_data = efx->nic_data; - struct falcon_nvconfig *nvconfig; - struct efx_spi_device *spi; - size_t offset, len; - int magic_num, struct_ver, board_rev, onchip_sram; - int rc; - - 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 = v2->port0_phy_type; - efx->mii.phy_id = 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) { - __le32 fl = v3->spi_device_type[EE_SPI_FLASH]; - __le32 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) { - nic_data->external_sram_cfg = SRM_NB_BSZ_ONCHIP_ONLY; - } else { - nic_data->external_sram_cfg = - EFX_OWORD_FIELD(nvconfig->srm_cfg_reg, - SRM_NUM_BANKS_AND_BANK_SIZE); - WARN_ON(nic_data->external_sram_cfg == SRM_NB_BSZ_RESERVED); - /* Replace invalid setting with the smallest defaults */ - if (nic_data->external_sram_cfg == SRM_NB_BSZ_DEFAULT) - nic_data->external_sram_cfg = SRM_NB_BSZ_1BANKS_2M; - } - EFX_LOG(efx, "external_sram_cfg=%d (>=0 is external)\n", - nic_data->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 = 16; - res->txq_min = 16; - res->evq_int_min = 4; - res->evq_int_lim = 5; - res->evq_timer_min = 5; - res->evq_timer_lim = 4096; - internal_dcs_entries = 8192; - break; - case FALCON_REV_B0: - default: - res->rxq_min = 0; - res->txq_min = 0; - res->evq_int_min = 0; - res->evq_int_lim = 64; - res->evq_timer_min = 64; - res->evq_timer_lim = 4096; - internal_dcs_entries = 4096; - break; - } - - buffer_entry_bytes = 8; - - if (nic_data->external_sram_cfg == SRM_NB_BSZ_ONCHIP_ONLY) { - res->rxq_lim = internal_dcs_entries / nic_data->rx_dc_entries; - res->txq_lim = 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_lim = 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 (nic_data->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_lim = n_vnics; - res->txq_lim = 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_lim = nic_data->rx_dc_base / 8; - } - - if (FALCON_IS_DUAL_FUNC(efx)) - 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; - - { - int rc; - rc = pci_read_config_byte(efx->pci_dev, PCI_CLASS_REVISION, - &efx->revision); - if (rc) - return rc; - } - - 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); @@ -3095,75 +1425,210 @@ efx->spi_flash = NULL; } -#ifdef CONFIG_SFC_DEBUGFS +/* Extract non-volatile configuration */ +static int falcon_probe_nvconfig(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + struct falcon_nvconfig *nvconfig; + int rc; -/* 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; + nvconfig = kmalloc(sizeof(*nvconfig), GFP_KERNEL); + if (!nvconfig) + return -ENOMEM; - if (efx->is_asic) { - rc = seq_puts(file, "Falcon ASIC"); + rc = falcon_read_nvram(efx, nvconfig); + if (rc) + goto out; + + memcpy(efx->mac_address, nvconfig->mac_address[0], ETH_ALEN); + + efx->phy_type = nvconfig->board_v2.port0_phy_type; + efx->mdio.prtad = nvconfig->board_v2.port0_phy_addr; + EFX_LOG(efx, "PHY is %s(%d) phy_id %d\n", efx->phy_name, + efx->phy_type, efx->mdio.prtad); + + falcon_probe_board(efx, le16_to_cpu(nvconfig->board_v2.board_revision)); + + if (le16_to_cpu(nvconfig->board_struct_ver) >= 3) { + rc = falcon_spi_device_init( + efx, &efx->spi_flash, FFE_AB_SPI_DEVICE_FLASH, + le32_to_cpu(nvconfig->board_v3 + .spi_device_type[FFE_AB_SPI_DEVICE_FLASH])); + if (rc) + goto out; + rc = falcon_spi_device_init( + efx, &efx->spi_eeprom, FFE_AB_SPI_DEVICE_EEPROM, + le32_to_cpu(nvconfig->board_v3 + .spi_device_type[FFE_AB_SPI_DEVICE_EEPROM])); + if (rc) + goto out; + } + + /* Read the SRAM configuration. The register is initialised + * automatically but might may been reset since boot. + */ + if (EFX_OWORD_FIELD(nvconfig->nic_stat_reg, FRF_AB_ONCHIP_SRAM)) { + nic_data->sram_config = SRAM_CONFIG_INTERNAL; } else { - falcon_read(efx, &altera_build, ALTERA_BUILD_REG_KER); + nic_data->sram_config = EFX_OWORD_FIELD(nvconfig->srm_cfg_reg, + FRF_AZ_SRM_NB_SZ); + WARN_ON(nic_data->sram_config == FFE_AB_SRM_NB_SZ_RES); + /* Replace invalid setting with the smallest defaults */ + if (nic_data->sram_config == FFE_AB_SRM_NB_SZ_DEF) + nic_data->sram_config = FFE_AB_SRM_NB1_SZ2M; + } + EFX_LOG(efx, "sram_config=%d (>=0 is external)\n", + nic_data->sram_config); - 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; +out: + kfree(nvconfig); + return rc; } -#endif /* CONFIG_SFC_DEBUGFS */ +static int falcon_dimension_resources(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; -int falcon_probe_nic(struct efx_nic *efx) + if (nic_data->sram_config == SRAM_CONFIG_INTERNAL) { + /* We scavenge internal memories to serve as the + * buffer table and descriptor caches. */ + struct efx_dl_falcon_resources *res = &efx->resources; + unsigned internal_dcs_entries = + (efx_nic_rev(efx) == EFX_REV_FALCON_A1) ? 8192 : 4096; + + efx->rx_dc_entries = 64; + efx->tx_dc_entries = 16; + res->rxq_lim = internal_dcs_entries / efx->rx_dc_entries; + res->txq_lim = internal_dcs_entries / efx->tx_dc_entries; + res->buffer_table_lim = 8192; + efx->rx_dc_base = 0x100000; + efx->tx_dc_base = 0x130000; + efx->sram_lim = 0x140000; + return 0; + } else { + /* Otherwise we have a large block of external SRAM */ + size_t sram_size; + + switch (nic_data->sram_config) { + case FFE_AB_SRM_NB1_SZ2M: + default: + sram_size = 2 * 1024 * 1024; + break; + case FFE_AB_SRM_NB1_SZ4M: + case FFE_AB_SRM_NB2_SZ4M: + sram_size = 4 * 1024 * 1024; + break; + case FFE_AB_SRM_NB1_SZ8M: + case FFE_AB_SRM_NB2_SZ8M: + sram_size = 8 * 1024 * 1024; + break; + case FFE_AB_SRM_NB2_SZ16M: + sram_size = 16 * 1024 * 1024; + break; + } + return efx_nic_dimension_resources(efx, sram_size, 512); + } +} + +/* 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; + int boot_dev; + + efx_reado(efx, &gpio_ctl, FR_AB_GPIO_CTL); + efx_reado(efx, &nic_stat, FR_AB_NIC_STAT); + efx_reado(efx, &ee_vpd_cfg, FR_AB_EE_VPD_CFG0); + + if (EFX_OWORD_FIELD(gpio_ctl, FRF_AB_GPIO3_PWRUP_VALUE)) { + boot_dev = (EFX_OWORD_FIELD(nic_stat, FRF_AB_SF_PRST) ? + FFE_AB_SPI_DEVICE_FLASH : FFE_AB_SPI_DEVICE_EEPROM); + EFX_LOG(efx, "Booted from %s\n", + boot_dev == FFE_AB_SPI_DEVICE_FLASH ? "flash" : "EEPROM"); + } else { + /* Disable VPD and set clock dividers to safe + * values for initial programming. */ + boot_dev = -1; + EFX_LOG(efx, "Booted from internal ASIC settings;" + " setting SPI config\n"); + EFX_POPULATE_OWORD_3(ee_vpd_cfg, FRF_AB_EE_VPD_EN, 0, + /* 125 MHz / 7 ~= 20 MHz */ + FRF_AB_EE_SF_CLOCK_DIV, 7, + /* 125 MHz / 63 ~= 2 MHz */ + FRF_AB_EE_EE_CLOCK_DIV, 63); + efx_writeo(efx, &ee_vpd_cfg, FR_AB_EE_VPD_CFG0); + } + + if (boot_dev == FFE_AB_SPI_DEVICE_FLASH || flash_type != -1) { + u32 nic_flash_type = + flash_type == -1 ? default_flash_type : flash_type; + falcon_spi_device_init(efx, &efx->spi_flash, + FFE_AB_SPI_DEVICE_FLASH, + nic_flash_type); + } + + if (boot_dev == FFE_AB_SPI_DEVICE_EEPROM || eeprom_type != -1) { + /* 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 + */ + u32 nic_eeprom_type; + if (eeprom_type == 0) + nic_eeprom_type = small_eeprom_type; + else if (eeprom_type == -1 || eeprom_type == 1) + nic_eeprom_type = large_eeprom_type; + else + nic_eeprom_type = eeprom_type; + falcon_spi_device_init(efx, &efx->spi_eeprom, + FFE_AB_SPI_DEVICE_EEPROM, + nic_eeprom_type); + } +} + +static int falcon_probe_nic(struct efx_nic *efx) { struct falcon_nic_data *nic_data; + struct falcon_board *board; 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; - /* Allocate storage for hardware specific data */ nic_data = kzalloc(sizeof(*nic_data), GFP_KERNEL); - efx->nic_data = (void *) nic_data; + if (!nic_data) + return -ENOMEM; + efx->nic_data = nic_data; - /* Determine number of ports etc. */ - rc = falcon_probe_nic_variant(efx); - if (rc) + rc = -ENODEV; + + if (efx_nic_fpga_ver(efx) != 0) { + EFX_ERR(efx, "Falcon FPGA not supported\n"); goto fail1; + } - /* Probe secondary function if expected */ - if (FALCON_IS_DUAL_FUNC(efx)) { - struct pci_dev *dev = pci_dev_get(efx->pci_dev); + if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) { + efx_oword_t nic_stat; + struct pci_dev *dev; +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_PCI_DEV_REVISION) + u8 pci_rev; + pci_read_config_byte(efx->pci_dev, PCI_CLASS_REVISION, &pci_rev); +#else + u8 pci_rev = efx->pci_dev->revision; +#endif + if ((pci_rev == 0xff) || (pci_rev == 0)) { + EFX_ERR(efx, "Falcon rev A0 not supported\n"); + goto fail1; + } + efx_reado(efx, &nic_stat, FR_AB_NIC_STAT); + if (EFX_OWORD_FIELD(nic_stat, FRF_AB_STRAP_10G) == 0) { + EFX_ERR(efx, "Falcon rev A1 1G not supported\n"); + goto fail1; + } + if (EFX_OWORD_FIELD(nic_stat, FRF_AA_STRAP_PCIE) == 0) { + EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n"); + goto fail1; + } + + dev = pci_dev_get(efx->pci_dev); while ((dev = pci_get_device(EFX_VENDID_SFC, FALCON_A_S_DEVID, dev))) { if (dev->bus == efx->pci_dev->bus && @@ -3174,8 +1639,7 @@ } if (!nic_data->pci_dev2) { EFX_ERR(efx, "failed to find secondary function\n"); - rc = -ENODEV; - goto fail2; + goto fail1; } } @@ -3183,94 +1647,80 @@ rc = falcon_reset_hw(efx, RESET_TYPE_ALL); if (rc) { EFX_ERR(efx, "failed to reset NIC\n"); - goto fail3; + goto fail2; } /* Allocate memory for INT_KER */ - rc = falcon_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t)); + rc = efx_nic_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t)); if (rc) - goto fail4; + goto fail3; 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)); + EFX_LOG(efx, "INT_KER at %llx (virt %p phys %llx)\n", + (u64)efx->irq_status.dma_addr, + efx->irq_status.addr, (u64)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; + if (rc == -EINVAL) { + EFX_ERR(efx, "NVRAM is invalid therefore using defaults\n"); + efx->phy_type = PHY_TYPE_NONE; + efx->mdio.prtad = MDIO_PRTAD_NONE; + nic_data->sram_config = SRAM_CONFIG_INTERNAL; + } else if (rc) { + goto fail4; } rc = falcon_dimension_resources(efx); if (rc) + goto fail5; + + /* Initialise I2C adapter */ + board = falcon_board(efx); + board->i2c_adap.owner = THIS_MODULE; + board->i2c_data = falcon_i2c_bit_operations; + board->i2c_data.data = efx; + board->i2c_adap.algo_data = &board->i2c_data; + board->i2c_adap.dev.parent = &efx->pci_dev->dev; + strlcpy(board->i2c_adap.name, "SFC4000 GPIO", + sizeof(board->i2c_adap.name)); + rc = i2c_bit_add_bus(&board->i2c_adap); + if (rc) + goto fail5; + + rc = falcon_board(efx)->type->init(efx); + if (rc) { + EFX_ERR(efx, "failed to initialise board\n"); goto fail6; + } + + nic_data->stats_disable_count = 1; + setup_timer(&nic_data->stats_timer, &falcon_stats_timer_func, + (unsigned long)efx); return 0; - fail6: - efx->dl_info = NULL; - fail5: +fail6: + BUG_ON(i2c_del_adapter(&board->i2c_adap)); + memset(&board->i2c_adap, 0, sizeof(board->i2c_adap)); +fail5: +fail4: falcon_remove_spi_devices(efx); - falcon_free_buffer(efx, &efx->irq_status); - fail4: - /* fall-thru */ - fail3: + efx_nic_free_buffer(efx, &efx->irq_status); +fail3: +fail2: if (nic_data->pci_dev2) { pci_dev_put(nic_data->pci_dev2); nic_data->pci_dev2 = NULL; } - fail2: - /* fall-thru */ - fail1: - kfree(efx->nic_data); +fail1: + kfree(nic_data); 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) +static void falcon_b0_init_ack_repl_timer(struct efx_nic *efx, int num_lanes) { unsigned tlp_size; efx_dword_t pcie_ack_rpl_reg; @@ -3293,29 +1743,27 @@ struct efx_tlp_ack_factor *tlp_ack_factor; /* Get TLP size */ - falcon_pcie_core_read_reg(efx, PCIE_CORE_ADDR_PCIE_DEVICE_CTRL_STAT, - &pcie_ctrl_stat_reg); + falcon_b0_pcie_core_read_reg(efx, FPCR_BB_PCIE_DEVICE_CTRL_STAT, + &pcie_ctrl_stat_reg); pcie_devicectrl = (u16) EFX_EXTRACT_DWORD(pcie_ctrl_stat_reg, 0, 15); tlp_size = ((PCI_EXP_DEVCTL_PAYLOAD & pcie_devicectrl) >> - PCI_EXP_DEVCTL_PAYLOAD_LBN); + __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); - 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); + falcon_b0_pcie_core_read_reg(efx, FPCR_BB_ACK_RPL_TIMER, + &pcie_ack_rpl_reg); + current_replay = EFX_DWORD_FIELD(pcie_ack_rpl_reg, FPCRF_BB_RPL_TL); + current_ack_timer = EFX_DWORD_FIELD(pcie_ack_rpl_reg, FPCRF_BB_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); + falcon_b0_pcie_core_read_reg(efx, FPCR_BB_ACK_FREQ, &pcie_ack_freq_reg); current_ack_freq = EFX_DWORD_FIELD(pcie_ack_freq_reg, - PCIE_CORE_ACK_FREQ); + FPCRF_BB_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=" @@ -3327,60 +1775,30 @@ /* 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, + EFX_SET_DWORD_FIELD(pcie_ack_rpl_reg, FPCRF_BB_RPL_TL, expected_replay); - falcon_pcie_core_write_reg(efx, PCIE_CORE_ADDR_ACK_RPL_TIMER, - pcie_ack_rpl_reg); + falcon_b0_pcie_core_write_reg(efx, FPCR_BB_ACK_RPL_TIMER, + pcie_ack_rpl_reg); } } 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; + unsigned num_lanes = efx_nic_check_pcie_link(efx, 8, 1); if (EFX_WORKAROUND_6943(efx) && num_lanes > 0) - falcon_init_ack_repl_timer(efx, num_lanes); + falcon_b0_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); - 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); + falcon_b0_pcie_core_read_reg(efx, FPCR_BB_ACK_FREQ, + &pcie_ack_freq_reg); + EFX_SET_DWORD_FIELD(pcie_ack_freq_reg, FPCRF_BB_ACK_FREQ, 0); + falcon_b0_pcie_core_write_reg(efx, FPCR_BB_ACK_FREQ, + pcie_ack_freq_reg); } return 0; @@ -3390,208 +1808,197 @@ { 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); - 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); + falcon_b0_pcie_core_read_reg(efx, FPCR_BB_ACK_FREQ, + &pcie_ack_freq_reg); + EFX_SET_DWORD_FIELD(pcie_ack_freq_reg, FPCRF_BB_ACK_FREQ, 1); + falcon_b0_pcie_core_write_reg(efx, FPCR_BB_ACK_FREQ, + pcie_ack_freq_reg); } } +static void falcon_init_rx_cfg(struct efx_nic *efx) +{ + /* Prior to Siena the RX DMA engine will split each frame at + * intervals of RX_USR_BUF_SIZE (32-byte units). We set it to + * be so large that that never happens. */ + const unsigned huge_buf_size = (3 * 4096) >> 5; + /* RX control FIFO thresholds (32 entries) */ + const unsigned ctrl_xon_thr = 20; + const unsigned ctrl_xoff_thr = 25; + /* RX data FIFO thresholds (256-byte units; size varies) */ + int data_xon_thr = efx_nic_rx_xon_thresh >> 8; + int data_xoff_thr = efx_nic_rx_xoff_thresh >> 8; + efx_oword_t reg; + + efx_reado(efx, ®, FR_AZ_RX_CFG); + if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) { + /* Data FIFO size is 5.5K */ + if (data_xon_thr < 0) + data_xon_thr = 512 >> 8; + if (data_xoff_thr < 0) + data_xoff_thr = 2048 >> 8; + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_DESC_PUSH_EN, 0); + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_USR_BUF_SIZE, + huge_buf_size); + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_MAC_TH, data_xon_thr); + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_MAC_TH, data_xoff_thr); + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_TX_TH, ctrl_xon_thr); + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_TX_TH, ctrl_xoff_thr); + } else { + /* Data FIFO size is 80K; register fields moved */ + if (data_xon_thr < 0) + data_xon_thr = 27648 >> 8; /* ~3*max MTU */ + if (data_xoff_thr < 0) + data_xoff_thr = 54272 >> 8; /* ~80Kb - 3*max MTU */ + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_DESC_PUSH_EN, 0); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_USR_BUF_SIZE, + huge_buf_size); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_MAC_TH, data_xon_thr); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_MAC_TH, data_xoff_thr); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_TX_TH, ctrl_xon_thr); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_TX_TH, ctrl_xoff_thr); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 1); + } + /* Always enable XOFF signal from RX FIFO. We enable + * or disable transmission of pause frames at the MAC. */ + EFX_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, 1); + efx_writeo(efx, ®, FR_AZ_RX_CFG); +} + /* 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) +static int falcon_init_nic(struct efx_nic *efx) { struct falcon_nic_data *nic_data = efx->nic_data; - struct falcon_nic_data *data; efx_oword_t temp; - unsigned thresh; int rc; - data = (struct falcon_nic_data *)efx->nic_data; + /* Use on-chip SRAM if needed */ + efx_reado(efx, &temp, FR_AB_NIC_STAT); + EFX_SET_OWORD_FIELD(temp, FRF_AB_ONCHIP_SRAM, + nic_data->sram_config == SRAM_CONFIG_INTERNAL); + efx_writeo(efx, &temp, FR_AB_NIC_STAT); - /* 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); + /* Set the source of the GMAC clock */ + if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) { + efx_reado(efx, &temp, FR_AB_GPIO_CTL); + EFX_SET_OWORD_FIELD(temp, FRF_AB_USE_NIC_CLK, true); + efx_writeo(efx, &temp, FR_AB_GPIO_CTL); + } - /* Use on-chip SRAM if needed. - */ - falcon_read(efx, &temp, NIC_STAT_REG); - if (nic_data->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; + /* Select the correct MAC */ + falcon_clock_mac(efx); /* Warn if <8 lanes of PCIe detected & set pcie timers */ rc = falcon_init_pcie_core(efx); if (rc) - return rc; + goto fail1; - /* 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); + /* Set the SRAM wake/sleep GPIO appropriately. */ + efx_reado(efx, &temp, FR_AB_GPIO_CTL); + EFX_SET_OWORD_FIELD(temp, FRF_AB_GPIO1_OEN, 1); + EFX_SET_OWORD_FIELD(temp, FRF_AB_GPIO1_OUT, + nic_data->sram_config == SRAM_CONFIG_INTERNAL); + efx_writeo(efx, &temp, FR_AB_GPIO_CTL); - rc = falcon_reset_sram(efx); + rc = falcon_reset_sram(efx, + nic_data->sram_config != SRAM_CONFIG_INTERNAL ? + nic_data->sram_config : 0); 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); + goto fail2; /* 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); + efx_reado(efx, &temp, FR_AZ_CSR_SPARE); + EFX_SET_OWORD_FIELD(temp, FRF_AB_MEM_PERR_EN_TX_DATA, 0); + efx_writeo(efx, &temp, FR_AZ_CSR_SPARE); } - /* Enable all the genuinely fatal interrupts. (They are still - * masked by the overall interrupt mask, controlled by - * falcon_interrupts()). - * - * Note: All other fatal interrupts are enabled - */ - EFX_POPULATE_OWORD_3(temp, - 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); + if (EFX_WORKAROUND_7244(efx)) { + efx_reado(efx, &temp, FR_BZ_RX_FILTER_CTL); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_UDP_FULL_SRCH_LIMIT, 8); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_UDP_WILD_SRCH_LIMIT, 8); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_TCP_FULL_SRCH_LIMIT, 8); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_TCP_WILD_SRCH_LIMIT, 8); + efx_writeo(efx, &temp, FR_BZ_RX_FILTER_CTL); + } - /* 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); + if (EFX_WORKAROUND_11368(efx)) { + /* Ensure that invalid BAR accesses are detected in + * time (prevents denial of service by vNIC users). + */ + efx_reado(efx, &temp, FR_AZ_HW_INIT); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_POST_WR_MASK, 0x0f); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_WD_TIMER, 0x10); + efx_writeo(efx, &temp, FR_AZ_HW_INIT); } - falcon_write(efx, &temp, RX_FILTER_CTL_REG); - falcon_setup_rss_indir_table(efx); - + /* XXX This is documented only for Falcon A0/A1 */ /* Setup RX. Wait for descriptor is broken and must * be disabled. RXDP recovery shouldn't be needed, but is. */ - 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); + efx_reado(efx, &temp, FR_AA_RX_SELF_RST); + EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_NODESC_WAIT_DIS, 1); + EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_SELF_RST_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); + EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_ISCSI_DIS, 1); + efx_writeo(efx, &temp, FR_AA_RX_SELF_RST); /* 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); + efx_reado(efx, &temp, FR_AZ_TX_CFG); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_NO_EOP_DISC_EN, 0); + efx_writeo(efx, &temp, FR_AZ_TX_CFG); - /* 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); + falcon_init_rx_cfg(efx); /* 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); + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { + EFX_POPULATE_OWORD_1(temp, FRF_BZ_FLS_EVQ_ID, 0); + efx_writeo(efx, &temp, FR_BZ_DP_CTRL); } + efx_nic_init_common(efx); + return 0; + +fail2: +fail1: + falcon_fini_pcie_core(efx); + return rc; } -void falcon_fini_nic(struct efx_nic *efx) +static void falcon_fini_nic(struct efx_nic *efx) { falcon_fini_pcie_core(efx); } -void falcon_remove_nic(struct efx_nic *efx) +static void falcon_remove_nic(struct efx_nic *efx) { struct falcon_nic_data *nic_data = efx->nic_data; + struct falcon_board *board = falcon_board(efx); + int rc; + + board->type->fini(efx); + + /* Remove I2C adapter and clear it in preparation for a retry */ + rc = i2c_del_adapter(&board->i2c_adap); + BUG_ON(rc); + memset(&board->i2c_adap, 0, sizeof(board->i2c_adap)); falcon_remove_spi_devices(efx); - falcon_free_buffer(efx, &efx->irq_status); + efx_nic_free_buffer(efx, &efx->irq_status); - /* Reset the NIC finally */ - (void) falcon_reset_hw(efx, RESET_TYPE_ALL); + falcon_reset_hw(efx, RESET_TYPE_ALL); /* Release the second function after the reset */ if (nic_data->pci_dev2) { @@ -3601,70 +2008,196 @@ /* Tear down the private nic state, and the driverlink nic params */ kfree(efx->nic_data); - efx->nic_data = efx->dl_info = NULL; + efx->nic_data = NULL; } -void falcon_update_nic_stats(struct efx_nic *efx) +static void falcon_update_nic_stats(struct efx_nic *efx) { + struct falcon_nic_data *nic_data = efx->nic_data; efx_oword_t cnt; - /* 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); + if (nic_data->stats_disable_count) + return; + + efx_reado(efx, &cnt, FR_AZ_RX_NODESC_DROP); + efx->n_rx_nodesc_drop_cnt += + EFX_OWORD_FIELD(cnt, FRF_AB_RX_NODESC_DROP_CNT); + + if (nic_data->stats_pending && + *nic_data->stats_dma_done == FALCON_STATS_DONE) { + nic_data->stats_pending = false; + rmb(); /* read the done flag before the stats */ + efx->mac_op->update_stats(efx); + } +} + +void falcon_start_nic_stats(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + + spin_lock_bh(&efx->stats_lock); + if (--nic_data->stats_disable_count == 0) + falcon_stats_request(efx); + spin_unlock_bh(&efx->stats_lock); +} + +void falcon_stop_nic_stats(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + int i; + + might_sleep(); + + spin_lock_bh(&efx->stats_lock); + ++nic_data->stats_disable_count; + spin_unlock_bh(&efx->stats_lock); + + del_timer_sync(&nic_data->stats_timer); + + /* Wait enough time for the most recent transfer to + * complete. */ + for (i = 0; i < 4 && nic_data->stats_pending; i++) { + if (*nic_data->stats_dma_done == FALCON_STATS_DONE) + break; + msleep(1); + } + + spin_lock_bh(&efx->stats_lock); + falcon_stats_complete(efx); + spin_unlock_bh(&efx->stats_lock); +} + +static void falcon_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ + falcon_board(efx)->type->set_id_led(efx, mode); } /************************************************************************** * - * Revision-dependent attributes used by efx.c + * Wake on LAN * ************************************************************************** */ -struct efx_nic_type falcon_a_nic_type = { - .mem_bar = 2, +static void falcon_get_wol(struct efx_nic *efx, struct ethtool_wolinfo *wol) +{ + wol->supported = 0; + wol->wolopts = 0; + memset(&wol->sopass, 0, sizeof(wol->sopass)); +} + +static int falcon_set_wol(struct efx_nic *efx, u32 type) +{ + if (type != 0) + return -EINVAL; + return 0; +} + +/************************************************************************** + * + * Revision-dependent attributes used by efx.c and nic.c + * + ************************************************************************** + */ + +struct efx_nic_type falcon_a1_nic_type = { + .probe = falcon_probe_nic, + .remove = falcon_remove_nic, + .init = falcon_init_nic, + .fini = falcon_fini_nic, + .monitor = falcon_monitor, + .reset = falcon_reset_hw, + .probe_port = falcon_probe_port, + .remove_port = falcon_remove_port, + .prepare_flush = falcon_prepare_flush, + .update_stats = falcon_update_nic_stats, + .start_stats = falcon_start_nic_stats, + .stop_stats = falcon_stop_nic_stats, + .set_id_led = falcon_set_id_led, + .push_irq_moderation = falcon_push_irq_moderation, + .push_multicast_hash = falcon_push_multicast_hash, + .reconfigure_port = falcon_reconfigure_port, + .get_wol = falcon_get_wol, + .set_wol = falcon_set_wol, + .resume_wol = efx_port_dummy_op_void, + .test_nvram = falcon_test_nvram, + .default_mac_ops = &falcon_xmac_operations, + + .revision = EFX_REV_FALCON_A1, + .dl_revision = "falcon/a1", .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, + .txd_ptr_tbl_base = FR_AA_TX_DESC_PTR_TBL_KER, + .rxd_ptr_tbl_base = FR_AA_RX_DESC_PTR_TBL_KER, + .buf_tbl_base = FR_AA_BUF_FULL_TBL_KER, + .evq_ptr_tbl_base = FR_AA_EVQ_PTR_TBL_KER, + .evq_rptr_tbl_base = FR_AA_EVQ_RPTR_KER, + .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH), .rx_buffer_padding = 0x24, .max_interrupt_mode = EFX_INT_MODE_MSI, .phys_addr_channels = 4, + .resources = { + .hdr.type = EFX_DL_FALCON_RESOURCES, + .flags = EFX_DL_FALCON_DUAL_FUNC, + .rxq_min = 16, .rxq_lim = 4096, + .txq_min = 16, .txq_lim = 4096, + .evq_int_min = 4, .evq_int_lim = 5, + .evq_timer_min = 5, .evq_timer_lim = 4096, + }, + .offload_features = NETIF_F_IP_CSUM, + .reset_world_flags = ETH_RESET_IRQ, }; -struct efx_nic_type falcon_b_nic_type = { - .mem_bar = 2, +struct efx_nic_type falcon_b0_nic_type = { + .probe = falcon_probe_nic, + .remove = falcon_remove_nic, + .init = falcon_init_nic, + .fini = falcon_fini_nic, + .monitor = falcon_monitor, + .reset = falcon_reset_hw, + .probe_port = falcon_probe_port, + .remove_port = falcon_remove_port, + .prepare_flush = falcon_prepare_flush, + .update_stats = falcon_update_nic_stats, + .start_stats = falcon_start_nic_stats, + .stop_stats = falcon_stop_nic_stats, + .set_id_led = falcon_set_id_led, + .push_irq_moderation = falcon_push_irq_moderation, + .push_multicast_hash = falcon_push_multicast_hash, + .reconfigure_port = falcon_reconfigure_port, + .get_wol = falcon_get_wol, + .set_wol = falcon_set_wol, + .resume_wol = efx_port_dummy_op_void, + .test_registers = falcon_b0_test_registers, + .test_memory = falcon_b0_test_memory, + .test_nvram = falcon_test_nvram, + .default_mac_ops = &falcon_xmac_operations, + + .revision = EFX_REV_FALCON_B0, + .dl_revision = "falcon/b0", /* Map everything up to and including the RSS indirection * table. Don't map MSI-X table, MSI-X PBA since Linux * requires that they not be mapped. */ - .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 */ + .mem_map_size = (FR_BZ_RX_INDIRECTION_TBL + + FR_BZ_RX_INDIRECTION_TBL_STEP * + FR_BZ_RX_INDIRECTION_TBL_ROWS), + .txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL, + .rxd_ptr_tbl_base = FR_BZ_RX_DESC_PTR_TBL, + .buf_tbl_base = FR_BZ_BUF_FULL_TBL, + .evq_ptr_tbl_base = FR_BZ_EVQ_PTR_TBL, + .evq_rptr_tbl_base = FR_BZ_EVQ_RPTR, + .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH), .rx_buffer_padding = 0, .max_interrupt_mode = EFX_INT_MODE_MSIX, .phys_addr_channels = 32, /* Hardware limit is 64, but the legacy * interrupt handler only supports 32 * channels */ - + .resources = { + .hdr.type = EFX_DL_FALCON_RESOURCES, + .rxq_min = 0, .rxq_lim = 4096, + .txq_min = 0, .txq_lim = 4096, + .evq_int_min = 0, .evq_int_lim = 64, + .evq_timer_min = 64, .evq_timer_lim = 4096, + }, + .offload_features = NETIF_F_IP_CSUM, + .reset_world_flags = ETH_RESET_IRQ, }; - diff -r aa133dee7792 drivers/net/sfc/falcon.h --- a/drivers/net/sfc/falcon.h +++ /dev/null @@ -1,154 +0,0 @@ -/**************************************************************************** - * 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 "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)->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 fastcall 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 fastcall 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 fastcall falcon_process_eventq(struct efx_channel *channel, - int *rx_quota); -extern void fastcall falcon_eventq_read_ack(struct efx_channel *channel); - -/* Ports */ -extern int falcon_probe_port(struct efx_nic *efx); -extern void falcon_remove_port(struct efx_nic *efx); - -/* MAC/PHY */ -extern int falcon_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( \ - *((__force __le16 *) \ - (efx->stats_buffer.addr + \ - FALCON_STAT_OFFSET(falcon_stat)))); \ - else if (FALCON_STAT_WIDTH(falcon_stat) == 32) \ - (efx)->mac_stats.efx_stat += le32_to_cpu( \ - *((__force __le32 *) \ - (efx->stats_buffer.addr + \ - FALCON_STAT_OFFSET(falcon_stat)))); \ - else \ - (efx)->mac_stats.efx_stat += le64_to_cpu( \ - *((__force __le64 *) \ - (efx->stats_buffer.addr + \ - FALCON_STAT_OFFSET(falcon_stat)))); \ - } while (0) - -#define FALCON_MAC_STATS_SIZE 0x100 - -#define MAC_DATA_LBN 0 -#define MAC_DATA_WIDTH 32 - -extern void falcon_generate_event(struct efx_channel *channel, - efx_qword_t *event); - -#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 */ diff -r aa133dee7792 drivers/net/sfc/falcon_boards.c --- /dev/null +++ b/drivers/net/sfc/falcon_boards.c @@ -0,0 +1,739 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2007-2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "net_driver.h" +#include "phy.h" +#include "efx.h" +#include "nic.h" +#include "regs.h" +#include "io.h" +#include "workarounds.h" + +/* Macros for unpacking the board revision */ +/* The revision info is in host byte order. */ +#define FALCON_BOARD_TYPE(_rev) (_rev >> 8) +#define FALCON_BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf) +#define FALCON_BOARD_MINOR(_rev) (_rev & 0xf) + +/* Board types */ +#define FALCON_BOARD_SFE4001 0x01 +#define FALCON_BOARD_SFE4002 0x02 +#define FALCON_BOARD_SFN4111T 0x51 +#define FALCON_BOARD_SFN4112F 0x52 + +/***************************************************************************** + * Support for LM87 sensor chip used on several boards + */ +#define LM87_REG_ALARMS1 0x41 +#define LM87_REG_ALARMS2 0x42 +#define LM87_IN_LIMITS(nr, _min, _max) \ + 0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min +#define LM87_AIN_LIMITS(nr, _min, _max) \ + 0x3B + (nr), _max, 0x1A + (nr), _min +#define LM87_TEMP_INT_LIMITS(_min, _max) \ + 0x39, _max, 0x3A, _min +#define LM87_TEMP_EXT1_LIMITS(_min, _max) \ + 0x37, _max, 0x38, _min + +#define LM87_ALARM_TEMP_INT 0x10 +#define LM87_ALARM_TEMP_EXT1 0x20 + +static int efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info, + const u8 *reg_values) +{ + struct falcon_board *board = falcon_board(efx); + struct i2c_client *client = i2c_new_device(&board->i2c_adap, info); + int rc; + + if (!client) + return -EIO; + + while (*reg_values) { + u8 reg = *reg_values++; + u8 value = *reg_values++; + rc = i2c_smbus_write_byte_data(client, reg, value); + if (rc) + goto err; + } + + board->hwmon_client = client; + return 0; + +err: + i2c_unregister_device(client); + return rc; +} + +static void efx_fini_lm87(struct efx_nic *efx) +{ + i2c_unregister_device(falcon_board(efx)->hwmon_client); +} + +static int efx_check_lm87(struct efx_nic *efx, unsigned mask) +{ + struct i2c_client *client = falcon_board(efx)->hwmon_client; + s32 alarms1, alarms2; + + /* If link is up then do not monitor temperature */ + if (EFX_WORKAROUND_7884(efx) && efx->link_state.up) + return 0; + + alarms1 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1); + alarms2 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2); + if (alarms1 < 0) + return alarms1; + if (alarms2 < 0) + return alarms2; + alarms1 &= mask; + alarms2 &= mask >> 8; + if (alarms1 || alarms2) { + EFX_ERR(efx, + "LM87 detected a hardware failure (status %02x:%02x)" + "%s%s\n", + alarms1, alarms2, + (alarms1 & LM87_ALARM_TEMP_INT) ? " INTERNAL" : "", + (alarms1 & LM87_ALARM_TEMP_EXT1) ? " EXTERNAL" : ""); + return -ERANGE; + } + + return 0; +} + +/***************************************************************************** + * Support for the SFE4001 and SFN4111T NICs. + * + * The SFE4001 does not power-up fully at reset due to its high power + * consumption. We control its power via a PCA9539 I/O expander. + * Both boards have a MAX6647 temperature monitor which we expose to + * the lm90 driver. + * + * This also provides minimal support for reflashing the PHY, which is + * initiated by resetting it with the FLASH_CFG_1 pin pulled down. + * On SFE4001 rev A2 and later this is connected to the 3V3X output of + * the IO-expander; on the SFN4111T it is connected to Falcon's GPIO3. + * We represent reflash mode as PHY_MODE_SPECIAL and make it mutually + * exclusive with the network device being open. + */ + +/************************************************************************** + * Support for I2C IO Expander device on SFE4001 + */ +#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 MAX664X_REG_RSL 0x02 +#define MAX664X_REG_WLHO 0x0B + +static void sfe4001_poweroff(struct efx_nic *efx) +{ + struct i2c_client *ioexp_client = falcon_board(efx)->ioexp_client; + struct i2c_client *hwmon_client = falcon_board(efx)->hwmon_client; + + /* Turn off all power rails and disable outputs */ + i2c_smbus_write_byte_data(ioexp_client, P0_OUT, 0xff); + i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, 0xff); + i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0xff); + + /* Clear any over-temperature alert */ + i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL); +} + +static int sfe4001_poweron(struct efx_nic *efx) +{ + struct i2c_client *ioexp_client = falcon_board(efx)->ioexp_client; + struct i2c_client *hwmon_client = falcon_board(efx)->hwmon_client; + unsigned int i, j; + int rc; + u8 out; + + /* Clear any previous over-temperature alert */ + rc = i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL); + if (rc < 0) + return rc; + + /* Enable port 0 and port 1 outputs on IO expander */ + rc = i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0x00); + if (rc) + return rc; + rc = i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, + 0xff & ~(1 << P1_SPARE_LBN)); + if (rc) + goto fail_on; + + /* If PHY power is on, turn it all off and wait 1 second to + * ensure a full reset. + */ + rc = i2c_smbus_read_byte_data(ioexp_client, P0_OUT); + if (rc < 0) + goto fail_on; + 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)); + if (rc != out) { + EFX_INFO(efx, "power-cycling PHY\n"); + rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); + if (rc) + goto fail_on; + schedule_timeout_uninterruptible(HZ); + } + + for (i = 0; i < 20; ++i) { + /* 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)); + if (efx->phy_mode & PHY_MODE_SPECIAL) + out |= 1 << P0_EN_3V3X_LBN; + + rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); + if (rc) + goto fail_on; + msleep(10); + + /* Turn on 1V power rail */ + out &= ~(1 << P0_EN_1V0X_LBN); + rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); + if (rc) + goto fail_on; + + EFX_INFO(efx, "waiting for DSP boot (attempt %d)...\n", i); + + /* In flash config mode, DSP does not turn on AFE, so + * just wait 1 second. + */ + if (efx->phy_mode & PHY_MODE_SPECIAL) { + schedule_timeout_uninterruptible(HZ); + return 0; + } + + for (j = 0; j < 10; ++j) { + msleep(100); + + /* Check DSP has asserted AFE power line */ + rc = i2c_smbus_read_byte_data(ioexp_client, P1_IN); + if (rc < 0) + goto fail_on; + if (rc & (1 << P1_AFE_PWD_LBN)) + return 0; + } + } + + EFX_INFO(efx, "timed out waiting for DSP boot\n"); + rc = -ETIMEDOUT; +fail_on: + sfe4001_poweroff(efx); + return rc; +} + +static int sfn4111t_reset(struct efx_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + efx_oword_t reg; + + /* GPIO 3 and the GPIO register are shared with I2C, so block that */ + i2c_lock_adapter(&board->i2c_adap); + + /* Pull RST_N (GPIO 2) low then let it up again, setting the + * FLASH_CFG_1 strap (GPIO 3) appropriately. Only change the + * output enables; the output levels should always be 0 (low) + * and we rely on external pull-ups. */ + efx_reado(efx, ®, FR_AB_GPIO_CTL); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO2_OEN, true); + efx_writeo(efx, ®, FR_AB_GPIO_CTL); + msleep(1000); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO2_OEN, false); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO3_OEN, + !!(efx->phy_mode & PHY_MODE_SPECIAL)); + efx_writeo(efx, ®, FR_AB_GPIO_CTL); + msleep(1); + + i2c_unlock_adapter(&board->i2c_adap); + + ssleep(1); + return 0; +} + +static ssize_t show_phy_flash_cfg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + return sprintf(buf, "%d\n", !!(efx->phy_mode & PHY_MODE_SPECIAL)); +} + +static ssize_t set_phy_flash_cfg(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + enum efx_phy_mode old_mode, new_mode; + int err; + + rtnl_lock(); + old_mode = efx->phy_mode; + if (count == 0 || *buf == '0') + new_mode = old_mode & ~PHY_MODE_SPECIAL; + else + new_mode = PHY_MODE_SPECIAL; + if (old_mode == new_mode) { + err = 0; + } else if (efx->state != STATE_RUNNING || netif_running(efx->net_dev)) { + err = -EBUSY; + } else { + /* Reset the PHY, reconfigure the MAC and enable/disable + * MAC stats accordingly. */ + efx->phy_mode = new_mode; + if (new_mode & PHY_MODE_SPECIAL) + falcon_stop_nic_stats(efx); + if (falcon_board(efx)->type->id == FALCON_BOARD_SFE4001) + err = sfe4001_poweron(efx); + else + err = sfn4111t_reset(efx); + if (!err) + err = efx_reconfigure_port(efx); + if (!(new_mode & PHY_MODE_SPECIAL)) + falcon_start_nic_stats(efx); + } + rtnl_unlock(); + + return err ? err : count; +} + +static DEVICE_ATTR(phy_flash_cfg, 0644, show_phy_flash_cfg, set_phy_flash_cfg); + +static void sfe4001_fini(struct efx_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + + EFX_INFO(efx, "%s\n", __func__); + + device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); + sfe4001_poweroff(efx); + i2c_unregister_device(board->ioexp_client); + i2c_unregister_device(board->hwmon_client); +} + +static int sfe4001_check_hw(struct efx_nic *efx) +{ + s32 status; + + /* If XAUI link is up then do not monitor */ + if (EFX_WORKAROUND_7884(efx) && !efx->xmac_poll_required) + return 0; + if (efx->phy_mode != PHY_MODE_NORMAL) + return 0; + + /* 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. + * We know we can read from the IO expander because we did + * it during power-on. Assume failure now is bad news. */ + status = i2c_smbus_read_byte_data(falcon_board(efx)->ioexp_client, P1_IN); + if (status >= 0 && + (status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0) + return 0; + + /* Use board power control, not PHY power control */ + sfe4001_poweroff(efx); + efx->phy_mode = PHY_MODE_OFF; + + return (status < 0) ? -EIO : -ERANGE; +} + +static struct i2c_board_info sfe4001_hwmon_info = { + I2C_BOARD_INFO("max6647", 0x4e), +}; + +/* 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 + */ +static int sfe4001_init(struct efx_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + int rc; + + board->hwmon_client = + i2c_new_device(&board->i2c_adap, &sfe4001_hwmon_info); + if (!board->hwmon_client) + return -EIO; + + /* Raise board/PHY high limit from 85 to 90 degrees Celsius */ + rc = i2c_smbus_write_byte_data(board->hwmon_client, + MAX664X_REG_WLHO, 90); + if (rc) + goto fail_hwmon; + + board->ioexp_client = i2c_new_dummy(&board->i2c_adap, PCA9539); + if (!board->ioexp_client) { + rc = -EIO; + goto fail_hwmon; + } + + if (efx->phy_mode & PHY_MODE_SPECIAL) { + /* PHY won't generate a 156.25 MHz clock and MAC stats fetch + * will fail. */ + falcon_stop_nic_stats(efx); + } + rc = sfe4001_poweron(efx); + if (rc) + goto fail_ioexp; + + rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); + if (rc) + goto fail_on; + + EFX_INFO(efx, "PHY is powered on\n"); + return 0; + +fail_on: + sfe4001_poweroff(efx); +fail_ioexp: + i2c_unregister_device(board->ioexp_client); +fail_hwmon: + i2c_unregister_device(board->hwmon_client); + return rc; +} + +static int sfn4111t_check_hw(struct efx_nic *efx) +{ + s32 status; + + /* If XAUI link is up then do not monitor */ + if (EFX_WORKAROUND_7884(efx) && !efx->xmac_poll_required) + return 0; + + /* Test LHIGH, RHIGH, FAULT, EOT and IOT alarms */ + status = i2c_smbus_read_byte_data(falcon_board(efx)->hwmon_client, + MAX664X_REG_RSL); + if (status < 0) + return -EIO; + if (status & 0x57) + return -ERANGE; + return 0; +} + +static void sfn4111t_fini(struct efx_nic *efx) +{ + EFX_INFO(efx, "%s\n", __func__); + + device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); + i2c_unregister_device(falcon_board(efx)->hwmon_client); +} + +static struct i2c_board_info sfn4111t_a0_hwmon_info = { + I2C_BOARD_INFO("max6647", 0x4e), +}; + +static struct i2c_board_info sfn4111t_r5_hwmon_info = { + I2C_BOARD_INFO("max6646", 0x4d), +}; + +static void sfn4111t_init_phy(struct efx_nic *efx) +{ + if (!(efx->phy_mode & PHY_MODE_SPECIAL)) { + if (sft9001_wait_boot(efx) != -EINVAL) + return; + + efx->phy_mode = PHY_MODE_SPECIAL; + falcon_stop_nic_stats(efx); + } + + sfn4111t_reset(efx); + sft9001_wait_boot(efx); +} + +static int sfn4111t_init(struct efx_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + int rc; + + board->hwmon_client = + i2c_new_device(&board->i2c_adap, + (board->minor < 5) ? + &sfn4111t_a0_hwmon_info : + &sfn4111t_r5_hwmon_info); + if (!board->hwmon_client) + return -EIO; + + rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); + if (rc) + goto fail_hwmon; + + if (efx->phy_mode & PHY_MODE_SPECIAL) + /* PHY may not generate a 156.25 MHz clock and MAC + * stats fetch will fail. */ + falcon_stop_nic_stats(efx); + + return 0; + +fail_hwmon: + i2c_unregister_device(board->hwmon_client); + return rc; +} + +/***************************************************************************** + * Support for the SFE4002 + * + */ +static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */ + +static const u8 sfe4002_lm87_regs[] = { + LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */ + LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */ + LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */ + LM87_IN_LIMITS(3, 0xb0, 0xc9), /* 5V: 4.6-5.2V */ + LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */ + LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */ + LM87_AIN_LIMITS(0, 0xa0, 0xb2), /* AIN1: 1.66V +/- 5% */ + LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */ + LM87_TEMP_INT_LIMITS(10, 60), /* board */ + LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */ + 0 +}; + +static struct i2c_board_info sfe4002_hwmon_info = { +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_NEED_LM87_DRIVER) + I2C_BOARD_INFO("lm87", 0x2e), +#else + I2C_BOARD_INFO("sfc_lm87", 0x2e), +#endif + .platform_data = &sfe4002_lm87_channel, +}; + +/****************************************************************************/ +/* 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 void sfe4002_init_phy(struct efx_nic *efx) +{ + /* Set the TX and RX LEDs to reflect status and activity, and the + * fault LED off */ + falcon_qt202x_set_led(efx, SFE4002_TX_LED, + QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT); + falcon_qt202x_set_led(efx, SFE4002_RX_LED, + QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT); + falcon_qt202x_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF); +} + +static void sfe4002_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ + falcon_qt202x_set_led( + efx, SFE4002_FAULT_LED, + (mode == EFX_LED_ON) ? QUAKE_LED_ON : QUAKE_LED_OFF); +} + +static int sfe4002_check_hw(struct efx_nic *efx) +{ + struct falcon_board *board = falcon_board(efx); + + /* A0 board rev. 4002s report a temperature fault the whole time + * (bad sensor) so we mask it out. */ + unsigned alarm_mask = + (board->major == 0 && board->minor == 0) ? + ~LM87_ALARM_TEMP_EXT1 : ~0; + + return efx_check_lm87(efx, alarm_mask); +} + +static int sfe4002_init(struct efx_nic *efx) +{ + return efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs); +} + +/***************************************************************************** + * Support for the SFN4112F + * + */ +static u8 sfn4112f_lm87_channel = 0x03; /* use AIN not FAN inputs */ + +static const u8 sfn4112f_lm87_regs[] = { + LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */ + LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */ + LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */ + LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */ + LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */ + LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */ + LM87_TEMP_INT_LIMITS(10, 60), /* board */ + LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */ + 0 +}; + +static struct i2c_board_info sfn4112f_hwmon_info = { +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_NEED_LM87_DRIVER) + I2C_BOARD_INFO("lm87", 0x2e), +#else + I2C_BOARD_INFO("sfc_lm87", 0x2e), +#endif + .platform_data = &sfn4112f_lm87_channel, +}; + +#define SFN4112F_ACT_LED 0 +#define SFN4112F_LINK_LED 1 + +static void sfn4112f_init_phy(struct efx_nic *efx) +{ + falcon_qt202x_set_led(efx, SFN4112F_ACT_LED, + QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACT); + falcon_qt202x_set_led(efx, SFN4112F_LINK_LED, + QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT); +} + +static void sfn4112f_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ + int reg; + + switch (mode) { + case EFX_LED_OFF: + reg = QUAKE_LED_OFF; + break; + case EFX_LED_ON: + reg = QUAKE_LED_ON; + break; + default: + reg = QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT; + break; + } + + falcon_qt202x_set_led(efx, SFN4112F_LINK_LED, reg); +} + +static int sfn4112f_check_hw(struct efx_nic *efx) +{ + /* Mask out unused sensors */ + return efx_check_lm87(efx, ~0x48); +} + +static int sfn4112f_init(struct efx_nic *efx) +{ + return efx_init_lm87(efx, &sfn4112f_hwmon_info, sfn4112f_lm87_regs); +} + +static const struct falcon_board_type board_types[] = { + { + .id = FALCON_BOARD_SFE4001, + .mwatts = 18000, + .ref_model = "SFE4001", + .gen_type = "10GBASE-T adapter", + .init = sfe4001_init, + .init_phy = efx_port_dummy_op_void, + .fini = sfe4001_fini, + .set_id_led = tenxpress_set_id_led, + .monitor = sfe4001_check_hw, + }, + { + .id = FALCON_BOARD_SFE4002, + .mwatts = 7500, + .ref_model = "SFE4002", + .gen_type = "XFP adapter", + .init = sfe4002_init, + .init_phy = sfe4002_init_phy, + .fini = efx_fini_lm87, + .set_id_led = sfe4002_set_id_led, + .monitor = sfe4002_check_hw, + }, + { + .id = FALCON_BOARD_SFN4111T, + .mwatts = 10000, + .ref_model = "SFN4111T", + .gen_type = "100/1000/10GBASE-T adapter", + .init = sfn4111t_init, + .init_phy = sfn4111t_init_phy, + .fini = sfn4111t_fini, + .set_id_led = tenxpress_set_id_led, + .monitor = sfn4111t_check_hw, + }, + { + .id = FALCON_BOARD_SFN4112F, + .mwatts = 7500, + .ref_model = "SFN4112F", + .gen_type = "SFP+ adapter", + .init = sfn4112f_init, + .init_phy = sfn4112f_init_phy, + .fini = efx_fini_lm87, + .set_id_led = sfn4112f_set_id_led, + .monitor = sfn4112f_check_hw, + }, +}; + +static const struct falcon_board_type falcon_dummy_board = { + .init = efx_port_dummy_op_int, + .init_phy = efx_port_dummy_op_void, + .fini = efx_port_dummy_op_void, + .set_id_led = efx_port_dummy_op_set_id_led, + .monitor = efx_port_dummy_op_int, +}; + +void falcon_probe_board(struct efx_nic *efx, u16 revision_info) +{ + struct falcon_board *board = falcon_board(efx); + u8 type_id = FALCON_BOARD_TYPE(revision_info); + int i; + + board->major = FALCON_BOARD_MAJOR(revision_info); + board->minor = FALCON_BOARD_MINOR(revision_info); + + for (i = 0; i < ARRAY_SIZE(board_types); i++) + if (board_types[i].id == type_id) + board->type = &board_types[i]; + + if (board->type) { + EFX_INFO(efx, "board is %s rev %c%d\n", + (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC) + ? board->type->ref_model : board->type->gen_type, + 'A' + board->major, board->minor); + } else { + EFX_ERR(efx, "unknown board type %d\n", type_id); + board->type = &falcon_dummy_board; + } +} diff -r aa133dee7792 drivers/net/sfc/falcon_gmac.c --- a/drivers/net/sfc/falcon_gmac.c +++ b/drivers/net/sfc/falcon_gmac.c @@ -1,66 +1,20 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include #include "net_driver.h" #include "efx.h" -#include "falcon.h" +#include "nic.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)); -} +#include "regs.h" +#include "io.h" /************************************************************************** * @@ -68,59 +22,116 @@ * *************************************************************************/ -static int falcon_init_gmac(struct efx_nic *efx) +static int falcon_reconfigure_gmac(struct efx_nic *efx) { - int rc; + struct efx_link_state *link_state = &efx->link_state; + bool loopback, tx_fc, rx_fc, bytemode; + int if_mode; + unsigned int max_frame_len; + efx_oword_t reg; - /* Reset the MAC */ - mentormac_reset(efx); + /* Configuration register 1 */ + tx_fc = (link_state->fc & EFX_FC_TX) || !link_state->fd; + rx_fc = !!(link_state->fc & EFX_FC_RX); + loopback = (efx->loopback_mode == LOOPBACK_GMAC); + bytemode = (link_state->speed == 1000); - /* Initialise PHY */ - rc = efx->phy_op->init(efx); - if (rc) - return rc; + EFX_POPULATE_OWORD_5(reg, + FRF_AB_GM_LOOP, loopback, + FRF_AB_GM_TX_EN, 1, + FRF_AB_GM_TX_FC_EN, tx_fc, + FRF_AB_GM_RX_EN, 1, + FRF_AB_GM_RX_FC_EN, rx_fc); + efx_writeo(efx, ®, FR_AB_GM_CFG1); + udelay(10); + + /* Configuration register 2 */ + if_mode = (bytemode) ? 2 : 1; + EFX_POPULATE_OWORD_5(reg, + FRF_AB_GM_IF_MODE, if_mode, + FRF_AB_GM_PAD_CRC_EN, 1, + FRF_AB_GM_LEN_CHK, 1, + FRF_AB_GM_FD, link_state->fd, + FRF_AB_GM_PAMBL_LEN, 0x7/*datasheet recommended */); + + efx_writeo(efx, ®, FR_AB_GM_CFG2); + udelay(10); + + /* Max frame len register */ + max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu); + EFX_POPULATE_OWORD_1(reg, FRF_AB_GM_MAX_FLEN, max_frame_len); + efx_writeo(efx, ®, FR_AB_GM_MAX_FLEN); + udelay(10); + + /* FIFO configuration register 0 */ + EFX_POPULATE_OWORD_5(reg, + FRF_AB_GMF_FTFENREQ, 1, + FRF_AB_GMF_STFENREQ, 1, + FRF_AB_GMF_FRFENREQ, 1, + FRF_AB_GMF_SRFENREQ, 1, + FRF_AB_GMF_WTMENREQ, 1); + efx_writeo(efx, ®, FR_AB_GMF_CFG0); + udelay(10); + + /* FIFO configuration register 1 */ + EFX_POPULATE_OWORD_2(reg, + FRF_AB_GMF_CFGFRTH, 0x12, + FRF_AB_GMF_CFGXOFFRTX, 0xffff); + efx_writeo(efx, ®, FR_AB_GMF_CFG1); + udelay(10); + + /* FIFO configuration register 2 */ + EFX_POPULATE_OWORD_2(reg, + FRF_AB_GMF_CFGHWM, 0x3f, + FRF_AB_GMF_CFGLWM, 0xa); + efx_writeo(efx, ®, FR_AB_GMF_CFG2); + udelay(10); + + /* FIFO configuration register 3 */ + EFX_POPULATE_OWORD_2(reg, + FRF_AB_GMF_CFGHWMFT, 0x1c, + FRF_AB_GMF_CFGFTTH, 0x08); + efx_writeo(efx, ®, FR_AB_GMF_CFG3); + udelay(10); + + /* FIFO configuration register 4 */ + EFX_POPULATE_OWORD_1(reg, FRF_AB_GMF_HSTFLTRFRM_PAUSE, 1); + efx_writeo(efx, ®, FR_AB_GMF_CFG4); + udelay(10); + + /* FIFO configuration register 5 */ + efx_reado(efx, ®, FR_AB_GMF_CFG5); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GMF_CFGBYTMODE, bytemode); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GMF_CFGHDPLX, !link_state->fd); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GMF_HSTDRPLT64, !link_state->fd); + EFX_SET_OWORD_FIELD(reg, FRF_AB_GMF_HSTFLTRFRMDC_PAUSE, 0); + efx_writeo(efx, ®, FR_AB_GMF_CFG5); + udelay(10); + + /* MAC address */ + EFX_POPULATE_OWORD_4(reg, + FRF_AB_GM_ADR_B0, efx->net_dev->dev_addr[5], + FRF_AB_GM_ADR_B1, efx->net_dev->dev_addr[4], + FRF_AB_GM_ADR_B2, efx->net_dev->dev_addr[3], + FRF_AB_GM_ADR_B3, efx->net_dev->dev_addr[2]); + efx_writeo(efx, ®, FR_AB_GM_ADR1); + udelay(10); + EFX_POPULATE_OWORD_2(reg, + FRF_AB_GM_ADR_B4, efx->net_dev->dev_addr[1], + FRF_AB_GM_ADR_B5, efx->net_dev->dev_addr[0]); + efx_writeo(efx, ®, FR_AB_GM_ADR2); + udelay(10); + + falcon_reconfigure_mac_wrapper(efx); 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; @@ -207,114 +218,13 @@ mac_stats->rx_lt64 = mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64; } -static int falcon_check_gmac(struct efx_nic *efx) +static bool falcon_gmac_check_fault(struct efx_nic *efx) { - /* Nothing to do */ - return 0; + return false; } -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, + .check_fault = falcon_gmac_check_fault, }; diff -r aa133dee7792 drivers/net/sfc/falcon_hwdefs.h --- a/drivers/net/sfc/falcon_hwdefs.h +++ /dev/null @@ -1,1638 +0,0 @@ -/**************************************************************************** - * 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 */ -/* XGMAC management interrupt mask register */ -#define XM_MGT_INT_MSK_REG_MAC_B0 0x5 -#define XM_MSK_PRMBLE_ERR_LBN 2 -#define XM_MSK_PRMBLE_ERR_WIDTH 1 -#define XM_MSK_RMTFLT_LBN 1 -#define XM_MSK_RMTFLT_WIDTH 1 -#define XM_MSK_LCLFLT_LBN 0 -#define XM_MSK_LCLFLT_WIDTH 1 - -#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 -/* XGMAC management interrupt status register */ -#define XM_MGT_INT_REG_MAC_B0 0x0f -#define XM_PRMBLE_ERR 2 -#define XM_PRMBLE_WIDTH 1 -#define XM_RMTFLT_LBN 1 -#define XM_RMTFLT_WIDTH 1 -#define XM_LCLFLT_LBN 0 -#define XM_LCLFLT_WIDTH 1 - -#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_ERR_LBN 38 -#define TX_EV_PKT_ERR_WIDTH 1 -#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 { - __le16 nports; - u8 port0_phy_addr; - u8 port0_phy_type; - u8 port1_phy_addr; - u8 port1_phy_type; - __le16 asic_sub_revision; - __le16 board_revision; -} __attribute__ ((packed)); - -/* Board configuration v3 extra information */ -struct falcon_nvconfig_board_v3 { - __le32 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 */ - __le16 board_magic_num; /* 0x3A0 */ - __le16 board_struct_ver; - __le16 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 */ diff -r aa133dee7792 drivers/net/sfc/falcon_io.h --- a/drivers/net/sfc/falcon_io.h +++ /dev/null @@ -1,260 +0,0 @@ -/**************************************************************************** - * 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 -#include -#include "net_driver.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((__force u64) (value), (efx)->membase + (reg)) -#define _falcon_writel(efx, value, reg) \ - __raw_writel((__force u32) (value), (efx)->membase + (reg)) -#define _falcon_readq(efx, reg) \ - ((__force __le64) __raw_readq((efx)->membase + (reg))) -#define _falcon_readl(efx, reg) \ - ((__force __le32) __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)); -} - -/* Read to a register forming part of a table */ -static inline void falcon_read_table(struct efx_nic *efx, efx_oword_t *value, - unsigned int reg, unsigned int index) -{ - falcon_read(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 */ diff -r aa133dee7792 drivers/net/sfc/falcon_xmac.c --- a/drivers/net/sfc/falcon_xmac.c +++ b/drivers/net/sfc/falcon_xmac.c @@ -1,218 +1,81 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include #include "net_driver.h" #include "efx.h" -#include "falcon.h" -#include "falcon_hwdefs.h" -#include "falcon_io.h" +#include "nic.h" +#include "regs.h" +#include "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; + efx_oword_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_reado(efx, &sdctl, FR_AB_XX_SD_CTL); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVD, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVD, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVC, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVC, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVB, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVB, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVA, FFE_AB_XX_SD_CTL_DRV_DEF); + EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVA, FFE_AB_XX_SD_CTL_DRV_DEF); + efx_writeo(efx, &sdctl, FR_AB_XX_SD_CTL); - 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); + EFX_POPULATE_OWORD_8(txdrv, + FRF_AB_XX_DEQD, FFE_AB_XX_TXDRV_DEQ_DEF, + FRF_AB_XX_DEQC, FFE_AB_XX_TXDRV_DEQ_DEF, + FRF_AB_XX_DEQB, FFE_AB_XX_TXDRV_DEQ_DEF, + FRF_AB_XX_DEQA, FFE_AB_XX_TXDRV_DEQ_DEF, + FRF_AB_XX_DTXD, FFE_AB_XX_TXDRV_DTX_DEF, + FRF_AB_XX_DTXC, FFE_AB_XX_TXDRV_DTX_DEF, + FRF_AB_XX_DTXB, FFE_AB_XX_TXDRV_DTX_DEF, + FRF_AB_XX_DTXA, FFE_AB_XX_TXDRV_DTX_DEF); + efx_writeo(efx, &txdrv, FR_AB_XX_TXDRV_CTL); } -static void falcon_hold_xaui_in_rst(struct efx_nic *efx) +int falcon_reset_xaui(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; + struct falcon_nic_data *nic_data = efx->nic_data; + efx_oword_t reg; int count; - if (!efx->is_asic) - return 0; + /* Don't fetch MAC statistics over an XMAC reset */ + WARN_ON(nic_data->stats_disable_count == 0); - EFX_POPULATE_DWORD_1(reg, XX_RST_XX_EN, 1); - efx->mac_op->mac_writel(efx, ®, XX_PWR_RST_REG_MAC); + /* Start reset sequence */ + EFX_POPULATE_OWORD_1(reg, FRF_AB_XX_RST_XX_EN, 1); + efx_writeo(efx, ®, FR_AB_XX_PWR_RST); - /* 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) { + /* Wait up to 10 ms for completion, then reinitialise */ + for (count = 0; count < 1000; count++) { + efx_reado(efx, ®, FR_AB_XX_PWR_RST); + if (EFX_OWORD_FIELD(reg, FRF_AB_XX_RST_XX_EN) == 0 && + EFX_OWORD_FIELD(reg, FRF_AB_XX_SD_RST_ACT) == 0) { falcon_setup_xaui(efx); return 0; } @@ -222,345 +85,220 @@ return -ETIMEDOUT; } -int falcon_reset_xaui(struct efx_nic *efx) +static void falcon_mask_status_intr(struct efx_nic *efx, bool enable) { - int rc; + efx_oword_t reg; - 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; -} + if ((efx_nic_rev(efx) != EFX_REV_FALCON_B0) || LOOPBACK_INTERNAL(efx)) + return; -static int falcon_xgmii_status(struct efx_nic *efx) -{ - efx_dword_t reg; + /* We expect xgmii faults if the wireside link is up */ + if (!EFX_WORKAROUND_5147(efx) || !efx->link_state.up) + return; - if (FALCON_REV(efx) < FALCON_REV_B0) - return 1; - - /* The ISR latches, so clear it and re-read */ - efx->mac_op->mac_readl(efx, ®, XM_MGT_INT_REG_MAC_B0); - efx->mac_op->mac_readl(efx, ®, XM_MGT_INT_REG_MAC_B0); - - if (EFX_DWORD_FIELD(reg, XM_LCLFLT) || - EFX_DWORD_FIELD(reg, XM_RMTFLT)) { - EFX_INFO(efx, "MGT_INT: "EFX_DWORD_FMT"\n", EFX_DWORD_VAL(reg)); - return 0; - } - - return 1; -} - -static void falcon_mask_status_intr(struct efx_nic *efx, int enable) -{ - efx_dword_t reg; - - if ((FALCON_REV(efx) < FALCON_REV_B0) || LOOPBACK_INTERNAL(efx)) + /* We can only use this interrupt to signal the negative edge of + * xaui_align [we have to poll the positive edge]. */ + if (efx->xmac_poll_required) return; /* Flush the ISR */ if (enable) - efx->mac_op->mac_readl(efx, ®, XM_MGT_INT_REG_MAC_B0); + efx_reado(efx, ®, FR_AB_XM_MGT_INT_MSK); - EFX_POPULATE_DWORD_2(reg, - XM_MSK_RMTFLT, !enable, - XM_MSK_LCLFLT, !enable); - efx->mac_op->mac_writel(efx, ®, XM_MGT_INT_MSK_REG_MAC_B0); + EFX_POPULATE_OWORD_2(reg, + FRF_AB_XM_MSK_RMTFLT, !enable, + FRF_AB_XM_MSK_LCLFLT, !enable); + efx_writeo(efx, ®, FR_AB_XM_MGT_INT_MASK); } -static int falcon_init_xmac(struct efx_nic *efx) +static bool falcon_xgxs_link_ok(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; - - falcon_mask_status_intr(efx, 1); - 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, sync_status, 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; + efx_oword_t reg; + bool align_done, link_ok = false; + int sync_status; /* Read link status */ - efx->mac_op->mac_readl(efx, ®, XX_CORE_STAT_REG_MAC); + efx_reado(efx, ®, FR_AB_XX_CORE_STAT); - 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; + align_done = EFX_OWORD_FIELD(reg, FRF_AB_XX_ALIGN_DONE); + sync_status = EFX_OWORD_FIELD(reg, FRF_AB_XX_SYNC_STAT); + if (align_done && (sync_status == FFE_AB_XX_STAT_ALL_LANES)) + link_ok = true; /* 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); - - /* If the link is up, then check the phy side of the xaui link - * (error conditions from the wire side propoagate back through - * the phy to the xaui side). */ - if (efx->link_up && link_ok) { - int has_phyxs = efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS); - if (has_phyxs) - link_ok = mdio_clause45_phyxgxs_lane_sync(efx); - } - - /* If the PHY and XAUI links are up, then check the mac's xgmii - * fault state */ - if (efx->link_up && link_ok) - link_ok = falcon_xgmii_status(efx); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_COMMA_DET, FFE_AB_XX_STAT_ALL_LANES); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_CHAR_ERR, FFE_AB_XX_STAT_ALL_LANES); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_DISPERR, FFE_AB_XX_STAT_ALL_LANES); + efx_writeo(efx, ®, FR_AB_XX_CORE_STAT); return link_ok; } -static void falcon_reconfigure_xmac_core(struct efx_nic *efx) +static bool falcon_xmac_link_ok(struct efx_nic *efx) +{ + /* + * Check MAC's XGXS link status except when using XGMII loopback + * which bypasses the XGXS block. + * If possible, check PHY's XGXS link status except when using + * MAC loopback. + */ + return (efx->loopback_mode == LOOPBACK_XGMII || + falcon_xgxs_link_ok(efx)) && + (!(efx->mdio.mmds & (1 << MDIO_MMD_PHYXS)) || + LOOPBACK_INTERNAL(efx) || + efx_mdio_phyxgxs_lane_sync(efx)); +} + +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; - } + efx_oword_t reg; + bool rx_fc = !!(efx->link_state.fc & EFX_FC_RX); + bool tx_fc = !!(efx->link_state.fc & EFX_FC_TX); /* Configure MAC - cut-thru mode is hard wired on */ - EFX_POPULATE_DWORD_3(reg, - 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); + EFX_POPULATE_OWORD_3(reg, + FRF_AB_XM_RX_JUMBO_MODE, 1, + FRF_AB_XM_TX_STAT_EN, 1, + FRF_AB_XM_RX_STAT_EN, 1); + efx_writeo(efx, ®, FR_AB_XM_GLB_CFG); /* Configure TX */ - EFX_POPULATE_DWORD_6(reg, - 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); + EFX_POPULATE_OWORD_6(reg, + FRF_AB_XM_TXEN, 1, + FRF_AB_XM_TX_PRMBL, 1, + FRF_AB_XM_AUTO_PAD, 1, + FRF_AB_XM_TXCRC, 1, + FRF_AB_XM_FCNTL, tx_fc, + FRF_AB_XM_IPG, 0x3); + efx_writeo(efx, ®, FR_AB_XM_TX_CFG); /* 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); + EFX_POPULATE_OWORD_5(reg, + FRF_AB_XM_RXEN, 1, + FRF_AB_XM_AUTO_DEPAD, 0, + FRF_AB_XM_ACPT_ALL_MCAST, 1, + FRF_AB_XM_ACPT_ALL_UCAST, efx->promiscuous, + FRF_AB_XM_PASS_CRC_ERR, 1); + efx_writeo(efx, ®, FR_AB_XM_RX_CFG); /* 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_OWORD_1(reg, FRF_AB_XM_MAX_RX_FRM_SIZE, max_frame_len); + efx_writeo(efx, ®, FR_AB_XM_RX_PARAM); + EFX_POPULATE_OWORD_2(reg, + FRF_AB_XM_MAX_TX_FRM_SIZE, max_frame_len, + FRF_AB_XM_TX_JUMBO_MODE, 1); + efx_writeo(efx, ®, FR_AB_XM_TX_PARAM); - EFX_POPULATE_DWORD_2(reg, - XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */ - XM_DIS_FCNTL, rx_fc ? 0 : 1); - efx->mac_op->mac_writel(efx, ®, XM_FC_REG_MAC); + EFX_POPULATE_OWORD_2(reg, + FRF_AB_XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */ + FRF_AB_XM_DIS_FCNTL, !rx_fc); + efx_writeo(efx, ®, FR_AB_XM_FC); /* 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); - } + memcpy(®, &efx->net_dev->dev_addr[0], 4); + efx_writeo(efx, ®, FR_AB_XM_ADR_LO); + memcpy(®, &efx->net_dev->dev_addr[4], 2); + efx_writeo(efx, ®, FR_AB_XM_ADR_HI); } 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; + efx_oword_t reg; + bool xgxs_loopback = (efx->loopback_mode == LOOPBACK_XGXS); + bool xaui_loopback = (efx->loopback_mode == LOOPBACK_XAUI); + bool xgmii_loopback = (efx->loopback_mode == LOOPBACK_XGMII); /* 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; + bool old_xgmii_loopback, old_xgxs_loopback, old_xaui_loopback; + bool 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_reado(efx, ®, FR_AB_XX_CORE_STAT); + old_xgxs_loopback = EFX_OWORD_FIELD(reg, FRF_AB_XX_XGXS_LB_EN); + old_xgmii_loopback = + EFX_OWORD_FIELD(reg, FRF_AB_XX_XGMII_LB_EN); - efx->mac_op->mac_readl(efx, ®, XX_SD_CTL_REG_MAC); - old_xaui_loopback = EFX_DWORD_FIELD(reg, XX_LPBKA); + efx_reado(efx, ®, FR_AB_XX_SD_CTL); + old_xaui_loopback = EFX_OWORD_FIELD(reg, FRF_AB_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); - } + + if (reset_xgxs) + falcon_reset_xaui(efx); } - efx->mac_op->mac_readl(efx, ®, XX_CORE_STAT_REG_MAC); - EFX_SET_DWORD_FIELD(reg, XX_FORCE_SIG, + efx_reado(efx, ®, FR_AB_XX_CORE_STAT); + EFX_SET_OWORD_FIELD(reg, FRF_AB_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); + FFE_AB_XX_FORCE_SIG_ALL_LANES : 0); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_XGXS_LB_EN, xgxs_loopback); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_XGMII_LB_EN, xgmii_loopback); + efx_writeo(efx, ®, FR_AB_XX_CORE_STAT); - 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); + efx_reado(efx, ®, FR_AB_XX_SD_CTL); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKD, xaui_loopback); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKC, xaui_loopback); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKB, xaui_loopback); + EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKA, xaui_loopback); + efx_writeo(efx, ®, FR_AB_XX_SD_CTL); } -/* Try and bring the Falcon side of the Falcon-Phy XAUI link fails - * to come back up. Bash it until it comes back up */ -static int falcon_check_xaui_link_up(struct efx_nic *efx) +/* Try to bring up the Falcon side of the Falcon-Phy XAUI link */ +static bool falcon_xmac_link_ok_retry(struct efx_nic *efx, int tries) { - int max_tries, tries; - tries = EFX_WORKAROUND_5147(efx) ? 5 : 1; - max_tries = tries; + bool mac_up = falcon_xmac_link_ok(efx); - if ((efx->loopback_mode == LOOPBACK_NETWORK) || - (efx->phy_type == PHY_TYPE_NONE) || - !efx->phy_powered) - return 0; + if (LOOPBACK_MASK(efx) & LOOPBACKS_EXTERNAL(efx) & LOOPBACKS_WS || + efx_phy_mode_disabled(efx->phy_mode)) + /* XAUI link is expected to be down */ + return mac_up; - while (tries) { - if (falcon_xaui_link_ok(efx)) - return 1; + falcon_stop_nic_stats(efx); - EFX_LOG(efx, "%s Clobbering XAUI (%d tries left).\n", - __func__, tries); - (void) falcon_reset_xaui(efx); + while (!mac_up && tries) { + EFX_LOG(efx, "bashing xaui\n"); + falcon_reset_xaui(efx); udelay(200); - tries--; + + mac_up = falcon_xmac_link_ok(efx); + --tries; } - EFX_LOG(efx, "Failed to bring XAUI link back up in %d tries!\n", - max_tries); - return 0; + falcon_start_nic_stats(efx); + + return mac_up; } -static void falcon_reconfigure_xmac(struct efx_nic *efx) +static bool falcon_xmac_check_fault(struct efx_nic *efx) { - int xaui_link_ok; + return !falcon_xmac_link_ok_retry(efx, 5); +} - falcon_mask_status_intr(efx, 0); - - /* Deconfigure the mac wrapper, draining the tx fifo if necessary */ - falcon_deconfigure_mac_wrapper(efx); - - /* Reconfigure the PHY, disabling transmit in mac level loopback. */ - efx->tx_disabled = LOOPBACK_INTERNAL(efx); - efx->phy_op->reconfigure(efx); +static int falcon_reconfigure_xmac(struct efx_nic *efx) +{ + falcon_mask_status_intr(efx, false); falcon_reconfigure_xgxs_core(efx); falcon_reconfigure_xmac_core(efx); - /* Reconfigure MAC wrapper */ falcon_reconfigure_mac_wrapper(efx); - /* Ensure XAUI link is up */ - xaui_link_ok = falcon_check_xaui_link_up(efx); + efx->xmac_poll_required = !falcon_xmac_link_ok_retry(efx, 5); + falcon_mask_status_intr(efx, true); - if (xaui_link_ok && efx->link_up) - falcon_mask_status_intr(efx, 1); -} - -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); + return 0; } static void falcon_update_stats_xmac(struct efx_nic *efx) { struct efx_mac_stats *mac_stats = &efx->mac_stats; - int rc; - - rc = falcon_dma_stats(efx, XgDmaDone_offset); - if (rc) - return; /* Update MAC stats from DMAed values */ FALCON_STAT(efx, XgRxOctets, rx_bytes); @@ -611,122 +349,26 @@ /* Update derived statistics */ mac_stats->tx_good_bytes = - (mac_stats->tx_bytes - mac_stats->tx_bad_bytes); + (mac_stats->tx_bytes - mac_stats->tx_bad_bytes - + mac_stats->tx_control * 64); mac_stats->rx_bad_bytes = - (mac_stats->rx_bytes - mac_stats->rx_good_bytes); + (mac_stats->rx_bytes - mac_stats->rx_good_bytes - + mac_stats->rx_control * 64); } -#define EFX_XAUI_RETRAIN_MAX 8 +void falcon_poll_xmac(struct efx_nic *efx) +{ + if (!EFX_WORKAROUND_5147(efx) || !efx->link_state.up || + !efx->xmac_poll_required) + return; -static int falcon_check_xmac(struct efx_nic *efx) -{ - unsigned xaui_link_ok; - int rc; - - if ((efx->loopback_mode == LOOPBACK_NETWORK) || - (efx->phy_type == PHY_TYPE_NONE) || - !efx->phy_powered) - return 0; - - falcon_mask_status_intr(efx, 0); - xaui_link_ok = falcon_xaui_link_ok(efx); - - if (EFX_WORKAROUND_5147(efx) && !xaui_link_ok) - (void) falcon_reset_xaui(efx); - - /* Call the PHY check_hw routine */ - rc = efx->phy_op->check_hw(efx); - - /* Unmask interrupt if everything was (and still is) ok */ - if (xaui_link_ok && efx->link_up) - falcon_mask_status_intr(efx, 1); - - return rc; -} - -/* 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 */ - if (efx->link_up) - falcon_drain_tx_fifo(efx); - } else { - /* Schedule a reset to recover */ - efx_schedule_reset(efx, RESET_TYPE_INVISIBLE); - } - } - - efx->flow_control = flow_control; - - return 0; + falcon_mask_status_intr(efx, false); + efx->xmac_poll_required = !falcon_xmac_link_ok_retry(efx, 1); + falcon_mask_status_intr(efx, true); } 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, + .check_fault = falcon_xmac_check_fault, }; diff -r aa133dee7792 drivers/net/sfc/gmii.h --- a/drivers/net/sfc/gmii.h +++ /dev/null @@ -1,212 +0,0 @@ -/**************************************************************************** - * 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 */ diff -r aa133dee7792 drivers/net/sfc/i2c-direct.c --- a/drivers/net/sfc/i2c-direct.c +++ /dev/null @@ -1,398 +0,0 @@ -/**************************************************************************** - * 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 "net_driver.h" -#include "i2c-direct.h" - -/* - * I2C data (SDA) and clock (SCL) line read/writes with appropriate - * delays. - */ - -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); - /* Devices may time out if operations do not end */ - 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", __func__); - 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)) -} diff -r aa133dee7792 drivers/net/sfc/i2c-direct.h --- a/drivers/net/sfc/i2c-direct.h +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005: Fen Systems Ltd. - * Copyright 2006: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * 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 */ diff -r aa133dee7792 drivers/net/sfc/io.h --- /dev/null +++ b/drivers/net/sfc/io.h @@ -0,0 +1,260 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_IO_H +#define EFX_IO_H + +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_LINUX_IO_H) +#include +#else +#include +#endif +#include + +/************************************************************************** + * + * NIC register I/O + * + ************************************************************************** + * + * Notes on locking strategy: + * + * Most NIC registers require 16-byte (or 8-byte, for SRAM) atomic writes + * which necessitates locking. + * Under normal operation few writes to NIC registers 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 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). + */ + +#if BITS_PER_LONG == 64 +#define EFX_USE_QWORD_IO 1 +#endif + +#ifdef EFX_USE_QWORD_IO +static inline void _efx_writeq(struct efx_nic *efx, __le64 value, + unsigned int reg) +{ + __raw_writeq((__force u64)value, efx->membase + reg); +} +static inline __le64 _efx_readq(struct efx_nic *efx, unsigned int reg) +{ + return (__force __le64)__raw_readq(efx->membase + reg); +} +#endif + +static inline void _efx_writed(struct efx_nic *efx, __le32 value, + unsigned int reg) +{ + __raw_writel((__force u32)value, efx->membase + reg); +} +static inline __le32 _efx_readd(struct efx_nic *efx, unsigned int reg) +{ + return (__force __le32)__raw_readl(efx->membase + reg); +} + +/* Writes to a normal 16-byte Efx register, locking as appropriate. */ +static inline void efx_writeo(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 EFX_USE_QWORD_IO + _efx_writeq(efx, value->u64[0], reg + 0); + wmb(); + _efx_writeq(efx, value->u64[1], reg + 8); +#else + _efx_writed(efx, value->u32[0], reg + 0); + _efx_writed(efx, value->u32[1], reg + 4); + _efx_writed(efx, value->u32[2], reg + 8); + wmb(); + _efx_writed(efx, value->u32[3], reg + 12); +#endif + mmiowb(); + spin_unlock_irqrestore(&efx->biu_lock, flags); +} + +/* Write an 8-byte NIC SRAM entry through the supplied mapping, + * locking as appropriate. */ +static inline void efx_sram_writeq(struct efx_nic *efx, void __iomem *membase, + efx_qword_t *value, unsigned int index) +{ + unsigned int addr = index * sizeof(*value); + unsigned long flags __attribute__ ((unused)); + + EFX_REGDUMP(efx, "writing SRAM address %x with " EFX_QWORD_FMT "\n", + addr, EFX_QWORD_VAL(*value)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef EFX_USE_QWORD_IO + __raw_writeq((__force u64)value->u64[0], membase + addr); +#else + __raw_writel((__force u32)value->u32[0], membase + addr); + wmb(); + __raw_writel((__force u32)value->u32[1], membase + addr + 4); +#endif + mmiowb(); + spin_unlock_irqrestore(&efx->biu_lock, flags); +} + +/* Write dword to NIC register that allows partial writes + * + * Some 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 efx_writed(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 */ + _efx_writed(efx, value->u32[0], reg); +} + +/* Read from a NIC register + * + * This reads an entire 16-byte register in one go, locking as + * appropriate. It is essential to read the first dword first, as this + * prompts the NIC to load the current value into the shadow register. + */ +static inline void efx_reado(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] = _efx_readd(efx, reg + 0); + rmb(); + value->u32[1] = _efx_readd(efx, reg + 4); + value->u32[2] = _efx_readd(efx, reg + 8); + value->u32[3] = _efx_readd(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)); +} + +/* Read an 8-byte SRAM entry through supplied mapping, + * locking as appropriate. */ +static inline void efx_sram_readq(struct efx_nic *efx, void __iomem *membase, + efx_qword_t *value, unsigned int index) +{ + unsigned int addr = index * sizeof(*value); + unsigned long flags __attribute__ ((unused)); + + spin_lock_irqsave(&efx->biu_lock, flags); +#ifdef EFX_USE_QWORD_IO + value->u64[0] = (__force __le64)__raw_readq(membase + addr); +#else + value->u32[0] = (__force __le32)__raw_readl(membase + addr); + rmb(); + value->u32[1] = (__force __le32)__raw_readl(membase + addr + 4); +#endif + spin_unlock_irqrestore(&efx->biu_lock, flags); + + EFX_REGDUMP(efx, "read from SRAM address %x, got "EFX_QWORD_FMT"\n", + addr, EFX_QWORD_VAL(*value)); +} + +/* Read dword from register that allows partial writes (sic) */ +static inline void efx_readd(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg) +{ + value->u32[0] = _efx_readd(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 efx_writeo_table(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg, unsigned int index) +{ + efx_writeo(efx, value, reg + index * sizeof(efx_oword_t)); +} + +/* Read to a register forming part of a table */ +static inline void efx_reado_table(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg, unsigned int index) +{ + efx_reado(efx, value, reg + index * sizeof(efx_oword_t)); +} + +/* Write to a dword register forming part of a table */ +static inline void efx_writed_table(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg, unsigned int index) +{ + efx_writed(efx, value, reg + index * sizeof(efx_oword_t)); +} + +/* Page-mapped register block size */ +#define EFX_PAGE_BLOCK_SIZE 0x2000 + +/* Calculate offset to page-mapped register block */ +#define EFX_PAGED_REG(page, reg) \ + ((page) * EFX_PAGE_BLOCK_SIZE + (reg)) + +/* As for efx_writeo(), but for a page-mapped register. */ +static inline void efx_writeo_page(struct efx_nic *efx, efx_oword_t *value, + unsigned int reg, unsigned int page) +{ + efx_writeo(efx, value, EFX_PAGED_REG(page, reg)); +} + +/* As for efx_writed(), but for a page-mapped register. */ +static inline void efx_writed_page(struct efx_nic *efx, efx_dword_t *value, + unsigned int reg, unsigned int page) +{ + efx_writed(efx, value, EFX_PAGED_REG(page, reg)); +} + +/* Write dword to page-mapped register with an extra lock. + * + * As for efx_writed_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 efx_writed_page_locked(struct efx_nic *efx, + efx_dword_t *value, + unsigned int reg, + unsigned int page) +{ + unsigned long flags __attribute__ ((unused)); + + if (page == 0) { + spin_lock_irqsave(&efx->biu_lock, flags); + efx_writed(efx, value, EFX_PAGED_REG(page, reg)); + spin_unlock_irqrestore(&efx->biu_lock, flags); + } else { + efx_writed(efx, value, EFX_PAGED_REG(page, reg)); + } +} + +#endif /* EFX_IO_H */ diff -r aa133dee7792 drivers/net/sfc/ioctl.c --- /dev/null +++ b/drivers/net/sfc/ioctl.c @@ -0,0 +1,70 @@ +/**************************************************************************** + * Driver for Solarflare network controllers + * (including support for SFE4001 10GBT NIC) + * + * Copyright 2005-2006: Fen Systems Ltd. + * Copyright 2005-2009: 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 "efx.h" +#include "efx_ioctl.h" +#include "nic.h" + +static int +efx_ioctl_reset_flags(struct efx_nic *efx, union efx_ioctl_data *data) +{ + return efx_ethtool_reset(efx->net_dev, &data->reset_flags.flags); +} + +/*****************************************************************************/ + +int efx_private_ioctl(struct efx_nic *efx, u16 cmd, + union efx_ioctl_data __user *user_data) +{ + int (*op)(struct efx_nic *, union efx_ioctl_data *); + union efx_ioctl_data data; + size_t size; + int rc; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + switch (cmd) { + case EFX_RESET_FLAGS: + size = sizeof(data.reset_flags); + op = efx_ioctl_reset_flags; + break; + default: + EFX_ERR(efx, "unknown private ioctl cmd %x\n", cmd); + return -EOPNOTSUPP; + } + + if (copy_from_user(&data, user_data, size)) + return -EFAULT; + rc = op(efx, &data); + if (rc) + return rc; + if (copy_to_user(user_data, &data, size)) + return -EFAULT; + return 0; +} + diff -r aa133dee7792 drivers/net/sfc/kernel_compat.c --- a/drivers/net/sfc/kernel_compat.c +++ b/drivers/net/sfc/kernel_compat.c @@ -1,31 +1,16 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ -#include "net_driver.h" +#define EFX_IN_KCOMPAT_C 1 + +#include "efx.h" #include #include #include @@ -34,8 +19,6 @@ #include #include #include -#include "gmii.h" -#include "ethtool.h" /* * Kernel backwards compatibility @@ -45,22 +28,193 @@ /************************************************************************** * + * unregister_netdevice_notifier : Has a race before 2.6.17 + * + ************************************************************************** + * + */ + +#ifdef EFX_NEED_UNREGISTER_NETDEVICE_NOTIFIER_FIX +/** + * efx_unregister_netdevice_notifier - fixed unregister_netdevice_notifier + * @nb: notifier to unregister + * + * unregister_netdevice_notifier() does not wait for the notifier + * to be unused before 2.6.17. This wrapper fixes that. + */ +int efx_unregister_netdevice_notifier(struct notifier_block *nb) +{ + int res; + + res = unregister_netdevice_notifier(nb); + /* Ensure any outstanding calls complete. */ + rtnl_lock(); + rtnl_unlock(); + return res; +} +#endif /* NEED_EFX_UNREGISTER_NETDEVICE_NOTIFIER */ + +#ifdef EFX_NEED_COMPOUND_PAGE_FIX + +void efx_compound_page_destructor(struct page *page) +{ + /* Fake up page state to keep __free_pages happy */ + set_page_count(page, 1); + page[1].mapping = NULL; + + __free_pages(page, (unsigned long)page[1].index); +} + +#endif /* NEED_COMPOUND_PAGE_FIX */ + +/************************************************************************** + * + * print_hex_dump, taken from lib/hexdump.c. + * + ************************************************************************** + * + */ +#ifdef EFX_NEED_HEX_DUMP + +#define hex_asc(x) "0123456789abcdef"[x] +#define isascii(c) (((unsigned char)(c))<=0x7f) + +static void hex_dump_to_buffer(const void *buf, size_t len, int rowsize, + int groupsize, char *linebuf, size_t linebuflen, + int ascii) +{ + const u8 *ptr = buf; + u8 ch; + int j, lx = 0; + int ascii_column; + + if (rowsize != 16 && rowsize != 32) + rowsize = 16; + + if (!len) + goto nil; + if (len > rowsize) /* limit to one line at a time */ + len = rowsize; + if ((len % groupsize) != 0) /* no mixed size output */ + groupsize = 1; + + switch (groupsize) { + case 8: { + const u64 *ptr8 = buf; + int ngroups = len / groupsize; + + for (j = 0; j < ngroups; j++) + lx += scnprintf(linebuf + lx, linebuflen - lx, + "%16.16llx ", (unsigned long long)*(ptr8 + j)); + ascii_column = 17 * ngroups + 2; + break; + } + + case 4: { + const u32 *ptr4 = buf; + int ngroups = len / groupsize; + + for (j = 0; j < ngroups; j++) + lx += scnprintf(linebuf + lx, linebuflen - lx, + "%8.8x ", *(ptr4 + j)); + ascii_column = 9 * ngroups + 2; + break; + } + + case 2: { + const u16 *ptr2 = buf; + int ngroups = len / groupsize; + + for (j = 0; j < ngroups; j++) + lx += scnprintf(linebuf + lx, linebuflen - lx, + "%4.4x ", *(ptr2 + j)); + ascii_column = 5 * ngroups + 2; + break; + } + + default: + for (j = 0; (j < rowsize) && (j < len) && (lx + 4) < linebuflen; + j++) { + ch = ptr[j]; + linebuf[lx++] = hex_asc(ch >> 4); + linebuf[lx++] = hex_asc(ch & 0x0f); + linebuf[lx++] = ' '; + } + ascii_column = 3 * rowsize + 2; + break; + } + if (!ascii) + goto nil; + + while (lx < (linebuflen - 1) && lx < (ascii_column - 1)) + linebuf[lx++] = ' '; + /* Removed is_print() check */ + for (j = 0; (j < rowsize) && (j < len) && (lx + 2) < linebuflen; j++) + linebuf[lx++] = isascii(ptr[j]) ? ptr[j] : '.'; +nil: + linebuf[lx++] = '\0'; +} + +void print_hex_dump(const char *level, const char *prefix_str, int prefix_type, + int rowsize, int groupsize, + const void *buf, size_t len, int ascii) +{ + const u8 *ptr = buf; + int i, linelen, remaining = len; + char linebuf[200]; + + if (rowsize != 16 && rowsize != 32) + rowsize = 16; + + for (i = 0; i < len; i += rowsize) { + linelen = min(remaining, rowsize); + remaining -= rowsize; + hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, + linebuf, sizeof(linebuf), ascii); + + switch (prefix_type) { + case DUMP_PREFIX_ADDRESS: + printk("%s%s%*p: %s\n", level, prefix_str, + (int)(2 * sizeof(void *)), ptr + i, linebuf); + break; + case DUMP_PREFIX_OFFSET: + printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf); + break; + default: + printk("%s%s%s\n", level, prefix_str, linebuf); + break; + } + } +} + +#endif /* EFX_NEED_HEX_DUMP */ + +/************************************************************************** + * * print_mac, from net/ethernet/eth.c in v2.6.24 * ************************************************************************** * */ +#ifdef EFX_NEED_PRINT_MAC char *print_mac(char *buf, const u8 *addr) { sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); return buf; } +#endif /* EFX_NEED_PRINT_MAC */ #ifdef EFX_NEED_CSUM_TCPUDP_NOFOLD +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +__wsum +csum_tcpudp_nofold (__be32 saddr, __be32 daddr, unsigned short len, + unsigned short proto, __wsum sum) +#else __wsum csum_tcpudp_nofold (unsigned long saddr, unsigned long daddr, unsigned short len, unsigned short proto, __wsum sum) +#endif { unsigned long result; @@ -76,3 +230,278 @@ } #endif /* EFX_NEED_CSUM_TCPUDP_NOFOLD */ + +#ifdef EFX_NEED_RANDOM_ETHER_ADDR +/* Generate random MAC address */ +void efx_random_ether_addr(uint8_t *addr) { + get_random_bytes (addr, ETH_ALEN); + addr [0] &= 0xfe; /* clear multicast bit */ + addr [0] |= 0x02; /* set local assignment bit (IEEE802) */ +} +#endif /* EFX_NEED_RANDOM_ETHER_ADDR */ + +#ifdef EFX_NEED_MSECS_TO_JIFFIES +/* + * When we convert to jiffies then we interpret incoming values + * the following way: + * + * - negative values mean 'infinite timeout' (MAX_JIFFY_OFFSET) + * + * - 'too large' values [that would result in larger than + * MAX_JIFFY_OFFSET values] mean 'infinite timeout' too. + * + * - all other values are converted to jiffies by either multiplying + * the input value by a factor or dividing it with a factor + * + * We must also be careful about 32-bit overflows. + */ +#ifndef MSEC_PER_SEC +#define MSEC_PER_SEC 1000L +#endif +unsigned long msecs_to_jiffies(const unsigned int m) +{ + /* + * Negative value, means infinite timeout: + */ + if ((int)m < 0) + return MAX_JIFFY_OFFSET; + +#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ) + /* + * HZ is equal to or smaller than 1000, and 1000 is a nice + * round multiple of HZ, divide with the factor between them, + * but round upwards: + */ + return (m + (MSEC_PER_SEC / HZ) - 1) / (MSEC_PER_SEC / HZ); +#elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC) + /* + * HZ is larger than 1000, and HZ is a nice round multiple of + * 1000 - simply multiply with the factor between them. + * + * But first make sure the multiplication result cannot + * overflow: + */ + if (m > jiffies_to_msecs(MAX_JIFFY_OFFSET)) + return MAX_JIFFY_OFFSET; + + return m * (HZ / MSEC_PER_SEC); +#else + /* + * Generic case - multiply, round and divide. But first + * check that if we are doing a net multiplication, that + * we wouldnt overflow: + */ + if (HZ > MSEC_PER_SEC && m > jiffies_to_msecs(MAX_JIFFY_OFFSET)) + return MAX_JIFFY_OFFSET; + + return (m * HZ + MSEC_PER_SEC - 1) / MSEC_PER_SEC; +#endif +} +#endif /* EFX_NEED_MSECS_TO_JIFFIES */ + +#ifdef EFX_NEED_MSLEEP +/** + * msleep - sleep safely even with waitqueue interruptions + * @msecs: Time in milliseconds to sleep for + */ +void msleep(unsigned int msecs) +{ + unsigned long timeout = msecs_to_jiffies(msecs) + 1; + + while (timeout) + timeout = schedule_timeout_uninterruptible(timeout); +} +#endif + +#ifdef EFX_USE_I2C_LEGACY + +struct i2c_client *i2c_new_device(struct i2c_adapter *adap, + struct i2c_board_info *info) +{ + return i2c_new_probed_device(adap, info, NULL); +} + +struct i2c_client *i2c_new_probed_device(struct i2c_adapter *adap, + struct i2c_board_info *info, + const unsigned short *addr_list) +{ + int (*probe)(struct i2c_client *, const struct i2c_device_id *); + struct i2c_client *client; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return NULL; + + client->adapter = adap; + client->dev.platform_data = info->platform_data; + client->flags = info->flags; + client->addr = addr_list ? addr_list[0] : info->addr; /* FIXME */ + strlcpy(client->name, info->type, sizeof client->name); + + if (!strcmp(client->name, "sfc_lm87")) { + client->driver = &efx_lm87_driver; + probe = efx_lm87_probe; + } else if (!strcmp(client->name, "max6646") || + !strcmp(client->name, "max6647")) { + client->driver = &efx_lm90_driver; + probe = efx_lm90_probe; + } else { + BUG(); + probe = NULL; + } + + if (i2c_attach_client(client)) + goto fail_client; + + if (probe(client, NULL)) + goto fail_attached; + + return client; + +fail_attached: + i2c_detach_client(client); +fail_client: + kfree(client); + return NULL; +} + +void i2c_unregister_device(struct i2c_client *client) +{ + if (client->driver->detach_client) { + client->driver->detach_client(client); + } else { + if (!i2c_detach_client(client)) + kfree(client); + } +} + +#endif /* EFX_USE_I2C_LEGACY */ + +#ifdef EFX_NEED_I2C_NEW_DUMMY + +struct i2c_driver efx_i2c_dummy_driver = { +#ifdef EFX_USE_I2C_DRIVER_NAME + .name = "sfc_i2c_dummy" +#else + .driver.name = "sfc_i2c_dummy" +#endif +}; + +struct i2c_client *efx_i2c_new_dummy(struct i2c_adapter *adap, u16 address) +{ + struct i2c_client *client; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return NULL; + + client->adapter = adap; + client->addr = address; + strcpy(client->name, efx_i2c_dummy_driver.driver.name); + + client->driver = &efx_i2c_dummy_driver; + + if (i2c_attach_client(client)) { + kfree(client); + return NULL; + } + + return client; +} + +#endif /* EFX_NEED_I2C_NEW_DUMMY */ + +#ifdef EFX_NEED_PCI_CLEAR_MASTER + +void pci_clear_master(struct pci_dev *dev) +{ + u16 old_cmd, cmd; + + pci_read_config_word(dev, PCI_COMMAND, &old_cmd); + cmd = old_cmd & ~PCI_COMMAND_MASTER; + if (cmd != old_cmd) { + dev_dbg(&dev->dev, "disabling bus mastering\n"); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + dev->is_busmaster = false; +} + +#endif /* EFX_NEED_PCI_CLEAR_MASTER */ + + +#ifdef EFX_NEED_PCI_WAKE_FROM_D3 + +#ifndef PCI_D3hot +#define PCI_D3hot 3 +#endif + +int pci_wake_from_d3(struct pci_dev *dev, bool enable) +{ + /* We always support waking from D3hot on boards that do WoL, + * so no need to check capabilities */ + return pci_enable_wake(dev, PCI_D3hot, enable); +} + +#endif /* EFX_NEED_PCI_WAKE_FROM_D3 */ + +#ifdef EFX_NEED_UNMASK_MSIX_VECTORS + +#ifdef EFX_HAVE_MSIX_TABLE_RESERVED + +void efx_unmask_msix_vectors(struct pci_dev *pci_dev) +{ + dev_dbg(&pci_dev->dev, "cannot unmask MSI-X interrupt\n"); +} + +#else + +#include + +#define PCI_MSIX_TABLE 4 +#define PCI_MSIX_PBA 8 +#define PCI_MSIX_BIR 0x7 + +void efx_unmask_msix_vectors(struct pci_dev *pci_dev) +{ + struct efx_nic *efx = pci_get_drvdata(pci_dev); + resource_size_t membase_phys; + void __iomem *membase; + int msix, offset, bar, length, i; + u32 dword, mask; + + if (efx->interrupt_mode != EFX_INT_MODE_MSIX) + return; + + /* Find the location (bar, offset) of the MSI-X table */ + msix = pci_find_capability(pci_dev, PCI_CAP_ID_MSIX); + if (!msix) + return; + pci_read_config_dword(pci_dev, msix + PCI_MSIX_TABLE, &dword); + bar = dword & PCI_MSIX_BIR; + offset = dword & ~PCI_MSIX_BIR; + + /* Map enough of the table for all our interrupts */ + membase_phys = pci_resource_start(pci_dev, bar); + length = efx->n_channels * 0x10; + membase = ioremap_nocache(membase_phys + offset, length); + if (!membase) { + dev_dbg(&pci_dev->dev, "failed to remap MSI-X table\n"); + return; + } + + /* Unmask every vector */ + for (i = 0; i < efx->n_channels; i++) { + offset = (i << 4) + 0xc; + mask = readl(membase + offset); + writel(mask & ~0x1, membase + offset); + dev_dbg(&pci_dev->dev, "writing value %d for channel %d\n", + mask & ~0x1, i); + } + + /* Release the mapping */ + iounmap(membase); +} + +#endif /* EFX_HAVE_MSIX_TABLE_RESERVED */ + +#endif /* EFX_NEED_UNMASK_MSIX_VECTORS */ diff -r aa133dee7792 drivers/net/sfc/kernel_compat.h --- a/drivers/net/sfc/kernel_compat.h +++ b/drivers/net/sfc/kernel_compat.h @@ -1,34 +1,18 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_KERNEL_COMPAT_H #define EFX_KERNEL_COMPAT_H #include +#include #include #include #include @@ -38,31 +22,421 @@ #include #include #include +#include +#include +#include +#include +#include +#include -/* - * Kernel backwards compatibility +/************************************************************************** * - * This file provides macros to facilitate backporting the driver. + * Autoconf compatability + * + **************************************************************************/ + +#include "autocompat.h" + +/************************************************************************** + * + * Version/config/architecture compatability. + * + ************************************************************************** + * + * The preferred kernel compatability mechanism is through the autoconf + * layer above. For compatability fixes that are safe to apply to any kernel + * version, or those that are impossible (or slow) to determine in autconf, + * it is acceptable to have a version check here. */ -#ifdef __ia64__ - /* csum_tcpudp_nofold() is extern but not exported */ - #define EFX_NEED_CSUM_TCPUDP_NOFOLD yes +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5) + #error "This kernel version is now unsupported" #endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12) + #define EFX_HAVE_MSIX_TABLE_RESERVED yes +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) + #define EFX_HAVE_WORKQUEUE_NAME_LIMIT yes +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) || defined(CONFIG_HUGETLB_PAGE) + #define EFX_USE_COMPOUND_PAGES yes +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) && defined(EFX_USE_COMPOUND_PAGES) + #define EFX_NEED_COMPOUND_PAGE_FIX +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) + #define EFX_NEED_UNREGISTER_NETDEVICE_NOTIFIER_FIX yes +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) + #define EFX_USE_GSO_SIZE_FOR_MSS yes +#else + #define EFX_HAVE_NONCONST_ETHTOOL_OPS yes +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + #define EFX_USE_FASTCALL yes +#endif + +/* debugfs only supports sym-links from 2.6.21 */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) && defined(CONFIG_DEBUG_FS) + #define EFX_USE_DEBUGFS yes +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + #define EFX_NEED_BONDING_HACKS yes +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) + #define EFX_NEED_LM87_DRIVER yes + #define EFX_NEED_LM90_DRIVER yes +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) + #define EFX_USE_NET_DEVICE_LAST_RX yes +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) + #define EFX_USE_NET_DEVICE_TRANS_START yes +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) + #ifdef EFX_HAVE_LINUX_MDIO_H + /* mdio module has some bugs in pause frame advertising */ + #define EFX_NEED_MDIO45_FLOW_CONTROL_HACKS yes + #endif +#endif + #ifdef CONFIG_PPC64 /* __raw_writel and friends are broken on ppc64 */ #define EFX_NEED_RAW_READ_AND_WRITE_FIX yes #endif -typedef irqreturn_t (*irq_handler_t)(int, void *, struct pt_regs *); +/************************************************************************** + * + * Definitions of missing constants, types, functions and macros + * + ************************************************************************** + * + */ -#define skb_mac_header(skb) (skb)->mac.raw -#define skb_network_header(skb) (skb)->nh.raw -#define eth_hdr(skb) ((struct ethhdr *)skb_mac_header(skb)) -#define tcp_hdr(skb) (skb)->h.th -#define ip_hdr(skb) (skb)->nh.iph -#define skb_tail_pointer(skb) (skb)->tail +#ifndef spin_trylock_irqsave + #define spin_trylock_irqsave(lock, flags) \ + ({ \ + local_irq_save(flags); \ + spin_trylock(lock) ? \ + 1 : ({local_irq_restore(flags); 0;}); \ + }) +#endif +#ifndef raw_smp_processor_id + #define raw_smp_processor_id() (current_thread_info()->cpu) +#endif + +#ifndef NETIF_F_GEN_CSUM + #define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) +#endif +#ifndef NETIF_F_V4_CSUM + #define NETIF_F_V4_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IP_CSUM) +#endif +#ifndef NETIF_F_V6_CSUM + #define NETIF_F_V6_CSUM NETIF_F_GEN_CSUM +#endif +#ifndef NETIF_F_ALL_CSUM + #define NETIF_F_ALL_CSUM (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM) +#endif + +/* Cope with small changes in PCI constants between minor kernel revisions */ +#if PCI_X_STATUS != 4 + #undef PCI_X_STATUS + #define PCI_X_STATUS 4 + #undef PCI_X_STATUS_MAX_SPLIT + #define PCI_X_STATUS_MAX_SPLIT 0x03800000 +#endif + +#ifndef __GFP_COMP + #define __GFP_COMP 0 +#endif + +#ifndef __iomem + #define __iomem +#endif + +#ifndef NET_IP_ALIGN + #define NET_IP_ALIGN 2 +#endif + +#ifndef PCI_EXP_FLAGS + #define PCI_EXP_FLAGS 2 /* Capabilities register */ + #define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ + #define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ + #define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ + #define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ +#endif + +#ifndef PCI_EXP_DEVCAP + #define PCI_EXP_DEVCAP 4 /* Device capabilities */ + #define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ + #define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ + #define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ +#endif + +#ifndef PCI_EXP_DEVCTL + #define PCI_EXP_DEVCTL 8 /* Device Control */ + #define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ + #define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ +#endif + +#ifndef PCI_EXP_LNKSTA + #define PCI_EXP_LNKSTA 18 /* Link Status */ +#endif +#ifndef PCI_EXP_LNKSTA_CLS + #define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */ +#endif +#ifndef PCI_EXP_LNKSTA_NLW + #define PCI_EXP_LNKSTA_NLW 0x03f0 /* Nogotiated Link Width */ +#endif + +#ifndef NETDEV_TX_OK + #define NETDEV_TX_OK 0 +#endif + +#ifndef NETDEV_TX_BUSY + #define NETDEV_TX_BUSY 1 +#endif + +#ifndef __force + #define __force +#endif + +#if ! defined(for_each_cpu_mask) && ! defined(CONFIG_SMP) + #define for_each_cpu_mask(cpu, mask) \ + for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask) +#endif + +#ifndef IRQF_PROBE_SHARED + #ifdef SA_PROBEIRQ + #define IRQF_PROBE_SHARED SA_PROBEIRQ + #else + #define IRQF_PROBE_SHARED 0 + #endif +#endif + +#ifndef IRQF_SHARED + #define IRQF_SHARED SA_SHIRQ +#endif + +#ifndef mmiowb + #if defined(__i386__) || defined(__x86_64__) + #define mmiowb() + #elif defined(__ia64__) + #ifndef ia64_mfa + #define ia64_mfa() asm volatile ("mf.a" ::: "memory") + #endif + #define mmiowb ia64_mfa + #else + #error "Need definition for mmiowb()" + #endif +#endif + +#ifndef CHECKSUM_PARTIAL + #define CHECKSUM_PARTIAL CHECKSUM_HW +#endif + +#ifndef DMA_BIT_MASK + #define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL<<(n))-1)) +#endif + +#if defined(__GNUC__) && !defined(inline) + #define inline inline __attribute__ ((always_inline)) +#endif + +#if defined(__GNUC__) && !defined(__packed) + #define __packed __attribute__((packed)) +#endif + +#ifndef DIV_ROUND_UP + #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#endif + +#ifndef __ATTR + #define __ATTR(_name,_mode,_show,_store) { \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ + } +#endif + +#ifndef DEVICE_ATTR + #define DEVICE_ATTR(_name, _mode, _show, _store) \ + struct device_attribute dev_attr_ ## _name = \ + __ATTR(_name, _mode, _show, _store) +#endif + +#ifndef to_i2c_adapter + #define to_i2c_adapter dev_to_i2c_adapter +#endif + +#if defined(CONFIG_X86) && !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) + #define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +#endif + +#ifndef BUILD_BUG_ON_ZERO + #define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1) +#endif + +#ifndef __bitwise + #define __bitwise +#endif + +#ifdef EFX_NEED_ATOMIC_CMPXCHG + #define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new)) +#endif + +/**************************************************************************/ + +#ifdef EFX_NEED_IRQ_HANDLER_T + typedef irqreturn_t (*irq_handler_t)(int, void *, struct pt_regs *); +#endif + +/* linux_mdio.h needs this */ +#ifdef EFX_NEED_BOOL + typedef _Bool bool; + enum { false, true }; +#endif + +#ifdef EFX_HAVE_LINUX_MDIO_H + #include +#else + #include "linux_mdio.h" +#endif + +#ifdef EFX_NEED_MII_CONSTANTS + #define BMCR_SPEED1000 0x0040 + #define ADVERTISE_PAUSE_ASYM 0x0800 + #define ADVERTISE_PAUSE_CAP 0x0400 +#endif + +#ifdef EFX_NEED_ETHTOOL_CONSTANTS + #define ADVERTISED_Pause (1 << 13) + #define ADVERTISED_Asym_Pause (1 << 14) +#endif + +#ifndef EFX_HAVE_ETHTOOL_RESET + enum ethtool_reset_flags { + /* These flags represent components dedicated to the interface + * the command is addressed to. Shift any flag left by + * ETH_RESET_SHARED_SHIFT to reset a shared component of the + * same type. + */ + ETH_RESET_MGMT = 1 << 0, /* Management processor */ + ETH_RESET_IRQ = 1 << 1, /* Interrupt requester */ + ETH_RESET_DMA = 1 << 2, /* DMA engine */ + ETH_RESET_FILTER = 1 << 3, /* Filtering/flow direction */ + ETH_RESET_OFFLOAD = 1 << 4, /* Protocol offload */ + ETH_RESET_MAC = 1 << 5, /* Media access controller */ + ETH_RESET_PHY = 1 << 6, /* Transceiver/PHY */ + ETH_RESET_RAM = 1 << 7, /* RAM shared between + * multiple components */ + + ETH_RESET_DEDICATED = 0x0000ffff, /* All components dedicated to + * this interface */ + ETH_RESET_ALL = 0xffffffff, /* All components used by this + * interface, even if shared */ + }; + #define ETH_RESET_SHARED_SHIFT 16 +#endif + +#ifndef FLOW_CTRL_TX + #define FLOW_CTRL_TX 0x01 + #define FLOW_CTRL_RX 0x02 +#endif + +#ifdef EFX_NEED_MII_RESOLVE_FLOWCTRL_FDX + /** + * mii_resolve_flowctrl_fdx + * @lcladv: value of MII ADVERTISE register + * @rmtadv: value of MII LPA register + * + * Resolve full duplex flow control as per IEEE 802.3-2005 table 28B-3 + */ + static inline u8 mii_resolve_flowctrl_fdx(u16 lcladv, u16 rmtadv) + { + u8 cap = 0; + + if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) { + cap = FLOW_CTRL_TX | FLOW_CTRL_RX; + } else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) { + if (lcladv & ADVERTISE_PAUSE_CAP) + cap = FLOW_CTRL_RX; + else if (rmtadv & ADVERTISE_PAUSE_CAP) + cap = FLOW_CTRL_TX; + } + + return cap; + } +#endif + +#ifdef EFX_NEED_MII_ADVERTISE_FLOWCTRL + /** + * mii_advertise_flowctrl - get flow control advertisement flags + * @cap: Flow control capabilities (FLOW_CTRL_RX, FLOW_CTRL_TX or both) + */ + static inline u16 mii_advertise_flowctrl(int cap) + { + u16 adv = 0; + + if (cap & FLOW_CTRL_RX) + adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; + if (cap & FLOW_CTRL_TX) + adv ^= ADVERTISE_PAUSE_ASYM; + + return adv; + } +#endif + +#ifndef PORT_OTHER + #define PORT_OTHER 0xff +#endif + +#ifndef SUPPORTED_Pause + #define SUPPORTED_Pause (1 << 13) + #define SUPPORTED_Asym_Pause (1 << 14) +#endif + +#ifndef SUPPORTED_Backplane + #define SUPPORTED_Backplane (1 << 16) + #define SUPPORTED_1000baseKX_Full (1 << 17) + #define SUPPORTED_10000baseKX4_Full (1 << 18) + #define SUPPORTED_10000baseKR_Full (1 << 19) + #define SUPPORTED_10000baseR_FEC (1 << 20) +#endif + +#ifdef EFX_NEED_SKB_HEADER_MACROS + #define skb_mac_header(skb) ((skb)->mac.raw) + #define skb_network_header(skb) ((skb)->nh.raw) + #define skb_tail_pointer(skb) ((skb)->tail) +#endif + +#ifdef EFX_NEED_ETH_HDR + #define eth_hdr(skb) ((struct ethhdr *)skb_mac_header(skb)) +#endif + +#ifdef EFX_NEED_TCP_HDR + #define tcp_hdr(skb) ((skb)->h.th) +#endif + +#ifdef EFX_NEED_IP_HDR + #define ip_hdr(skb) ((skb)->nh.iph) +#endif + +#ifdef EFX_NEED_IPV6_HDR + #define ipv6_hdr(skb) ((skb)->nh.ipv6h) +#endif #ifdef EFX_NEED_RAW_READ_AND_WRITE_FIX #include @@ -125,60 +499,725 @@ #define __raw_readq efx_raw_readq #endif -typedef u32 __wsum; -#define csum_unfold(x) ((__force __wsum) x) +#ifdef EFX_NEED_SCHEDULE_TIMEOUT_INTERRUPTIBLE + static inline signed long + schedule_timeout_interruptible(signed long timeout) + { + set_current_state(TASK_INTERRUPTIBLE); + return schedule_timeout(timeout); + } +#endif -#define DECLARE_MAC_BUF(var) char var[18] __attribute__((unused)) -extern char *print_mac(char *buf, const u8 *addr); +#ifdef EFX_NEED_SCHEDULE_TIMEOUT_UNINTERRUPTIBLE + static inline signed long + schedule_timeout_uninterruptible(signed long timeout) + { + set_current_state(TASK_UNINTERRUPTIBLE); + return schedule_timeout(timeout); + } +#endif + +#ifdef EFX_NEED_KZALLOC + static inline void *kzalloc(size_t size, int flags) + { + void *buf = kmalloc(size, flags); + if (buf) + memset(buf, 0,size); + return buf; + } +#endif + +#ifdef EFX_NEED_SETUP_TIMER + static inline void setup_timer(struct timer_list * timer, + void (*function)(unsigned long), + unsigned long data) + { + timer->function = function; + timer->data = data; + init_timer(timer); + } +#endif + +#ifdef EFX_NEED_MUTEX + #define EFX_DEFINE_MUTEX(x) DECLARE_MUTEX(x) + #undef DEFINE_MUTEX + #define DEFINE_MUTEX EFX_DEFINE_MUTEX + + #define efx_mutex semaphore + #undef mutex + #define mutex efx_mutex + + #define efx_mutex_init(x) init_MUTEX(x) + #undef mutex_init + #define mutex_init efx_mutex_init + + #define efx_mutex_destroy(x) do { } while(0) + #undef mutex_destroy + #define mutex_destroy efx_mutex_destroy + + #define efx_mutex_lock(x) down(x) + #undef mutex_lock + #define mutex_lock efx_mutex_lock + + #define efx_mutex_lock_interruptible(x) down_interruptible(x) + #undef mutex_lock_interruptible + #define mutex_lock_interruptible efx_mutex_lock_interruptible + + #define efx_mutex_unlock(x) up(x) + #undef mutex_unlock + #define mutex_unlock efx_mutex_unlock + + #define efx_mutex_trylock(x) (!down_trylock(x)) + #undef mutex_trylock + #define mutex_trylock efx_mutex_trylock + + static inline int efx_mutex_is_locked(struct efx_mutex *m) + { + /* NB. This is quite inefficient, but it's the best we + * can do with the semaphore API. */ + if ( down_trylock(m) ) + return 1; + /* Undo the effect of down_trylock. */ + up(m); + return 0; + } + #undef mutex_is_locked + #define mutex_is_locked efx_mutex_is_locked +#endif + +#ifndef NETIF_F_GSO + #define efx_gso_size tso_size + #undef gso_size + #define gso_size efx_gso_size + #define efx_gso_segs tso_segs + #undef gso_segs + #define gso_segs efx_gso_segs +#endif + +#ifdef EFX_NEED_NETDEV_ALLOC_SKB + #ifndef NET_SKB_PAD + #define NET_SKB_PAD 16 + #endif + + static inline + struct sk_buff *netdev_alloc_skb(struct net_device *dev, + unsigned int length) + { + struct sk_buff *skb = alloc_skb(length + NET_SKB_PAD, + GFP_ATOMIC | __GFP_COLD); + if (likely(skb)) { + skb_reserve(skb, NET_SKB_PAD); + skb->dev = dev; + } + return skb; + } +#endif + +#ifdef EFX_NEED_NETDEV_TX_T + typedef int netdev_tx_t; +#endif + +#ifdef EFX_HAVE_NONCONST_ETHTOOL_OPS + #undef SET_ETHTOOL_OPS + #define SET_ETHTOOL_OPS(netdev, ops) \ + ((netdev)->ethtool_ops = (struct ethtool_ops *)(ops)) +#endif + +#ifdef EFX_NEED_RTNL_TRYLOCK + static inline int rtnl_trylock(void) { + return !rtnl_shlock_nowait(); + } +#endif + +#ifdef EFX_NEED_NETIF_TX_LOCK + static inline void netif_tx_lock(struct net_device *dev) + { + spin_lock(&dev->xmit_lock); + dev->xmit_lock_owner = smp_processor_id(); + } + static inline void netif_tx_lock_bh(struct net_device *dev) + { + spin_lock_bh(&dev->xmit_lock); + dev->xmit_lock_owner = smp_processor_id(); + } + static inline void netif_tx_unlock_bh(struct net_device *dev) + { + dev->xmit_lock_owner = -1; + spin_unlock_bh(&dev->xmit_lock); + } + static inline void netif_tx_unlock(struct net_device *dev) + { + dev->xmit_lock_owner = -1; + spin_unlock(&dev->xmit_lock); + } +#endif + +#ifdef EFX_NEED_NETIF_ADDR_LOCK + static inline void netif_addr_lock(struct net_device *dev) + { + netif_tx_lock(dev); + } + static inline void netif_addr_lock_bh(struct net_device *dev) + { + netif_tx_lock_bh(dev); + } + static inline void netif_addr_unlock_bh(struct net_device *dev) + { + netif_tx_unlock_bh(dev); + } + static inline void netif_addr_unlock(struct net_device *dev) + { + netif_tx_unlock(dev); + } +#endif + +#ifdef EFX_NEED_DEV_GET_STATS + static inline const struct net_device_stats * + dev_get_stats(struct net_device *dev) + { + return dev->get_stats(dev); + } +#endif + +#ifdef EFX_HAVE_OLD_IP_FAST_CSUM + #define ip_fast_csum(iph, ihl) ip_fast_csum((unsigned char *)iph, ihl) +#endif + +#ifdef EFX_HAVE_OLD_CSUM + typedef u16 __sum16; + typedef u32 __wsum; + #define csum_unfold(x) ((__force __wsum) x) +#endif + +#ifdef EFX_NEED_HEX_DUMP + enum { + DUMP_PREFIX_NONE, + DUMP_PREFIX_ADDRESS, + DUMP_PREFIX_OFFSET + }; +#endif + +#ifndef DECLARE_MAC_BUF + #define DECLARE_MAC_BUF(var) char var[18] __attribute__((unused)) +#endif + +#ifdef EFX_NEED_GFP_T + typedef unsigned int gfp_t; +#endif + +#ifdef EFX_NEED_SAFE_LISTS + #define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, \ + n = list_entry(n->member.prev, typeof(*n), member)) +#endif + +#ifdef EFX_NEED_DEV_NOTICE + #define dev_notice dev_warn +#endif + +#ifdef EFX_NEED_IF_MII + #include + static inline struct mii_ioctl_data *efx_if_mii ( struct ifreq *rq ) { + return ( struct mii_ioctl_data * ) &rq->ifr_ifru; + } + #undef if_mii + #define if_mii efx_if_mii +#endif + +#ifdef EFX_NEED_MTD_ERASE_CALLBACK + #include + static inline void efx_mtd_erase_callback(struct erase_info *instr) { + if ( instr->callback ) + instr->callback ( instr ); + } + #undef mtd_erase_callback + #define mtd_erase_callback efx_mtd_erase_callback +#endif + +#ifdef EFX_NEED_DUMMY_PCI_DISABLE_MSI + #include + static inline void dummy_pci_disable_msi ( struct pci_dev *dev ) { + /* Do nothing */ + } + #undef pci_disable_msi + #define pci_disable_msi dummy_pci_disable_msi +#endif + +#ifdef EFX_NEED_DUMMY_MSIX + struct msix_entry { + u16 vector; /* kernel uses to write allocated vector */ + u16 entry; /* driver uses to specify entry, OS writes */ + }; + static inline int pci_enable_msix(struct pci_dev* dev, + struct msix_entry *entries, int nvec) + {return -1;} + static inline void pci_disable_msix(struct pci_dev *dev) { /* Do nothing */} +#endif + +#ifdef EFX_NEED_BYTEORDER_TYPES + typedef __u16 __be16; + typedef __u32 __be32; + typedef __u64 __be64; + typedef __u16 __le16; + typedef __u32 __le32; + typedef __u64 __le64; +#endif + +#ifdef EFX_NEED_RESOURCE_SIZE_T + typedef unsigned long resource_size_t; +#endif + +#ifdef EFX_USE_I2C_LEGACY + struct i2c_board_info { + char type[I2C_NAME_SIZE]; + unsigned short flags; + unsigned short addr; + void *platform_data; + int irq; + }; + #define I2C_BOARD_INFO(dev_type, dev_addr) \ + .type = (dev_type), .addr = (dev_addr) + struct i2c_client * + i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info *info); + struct i2c_client * + i2c_new_probed_device(struct i2c_adapter *adap, + struct i2c_board_info *info, + const unsigned short *addr_list); + void i2c_unregister_device(struct i2c_client *); + struct i2c_device_id; +#endif + +#ifdef EFX_NEED_I2C_NEW_DUMMY + extern struct i2c_driver efx_i2c_dummy_driver; + struct i2c_client * + efx_i2c_new_dummy(struct i2c_adapter *adap, u16 address); + #undef i2c_new_dummy + #define i2c_new_dummy efx_i2c_new_dummy +#endif + +#ifdef EFX_HAVE_OLD_I2C_NEW_DUMMY + static inline struct i2c_client * + efx_i2c_new_dummy(struct i2c_adapter *adap, u16 address) + { + return i2c_new_dummy(adap, address, "dummy"); + } + #undef i2c_new_dummy + #define i2c_new_dummy efx_i2c_new_dummy +#endif + +#ifdef EFX_NEED_I2C_LOCK_ADAPTER + #ifdef EFX_USE_I2C_BUS_SEMAPHORE + static inline void i2c_lock_adapter(struct i2c_adapter *adap) + { + down(&adap->bus_lock); + } + static inline void i2c_unlock_adapter(struct i2c_adapter *adap) + { + up(&adap->bus_lock); + } + #else + static inline void i2c_lock_adapter(struct i2c_adapter *adap) + { + mutex_lock(&adap->bus_lock); + } + static inline void i2c_unlock_adapter(struct i2c_adapter *adap) + { + mutex_unlock(&adap->bus_lock); + } + #endif +#endif + +#ifdef EFX_HAVE_OLD_PCI_DMA_MAPPING_ERROR + static inline int + efx_pci_dma_mapping_error(struct pci_dev *dev, dma_addr_t dma_addr) + { + return pci_dma_mapping_error(dma_addr); + } + #undef pci_dma_mapping_error + #define pci_dma_mapping_error efx_pci_dma_mapping_error +#endif + +#ifdef EFX_NEED_FOR_EACH_PCI_DEV + #define for_each_pci_dev(d) \ + while ((d = pci_get_device(PCI_ANY_ID, \ + PCI_ANY_ID, d)) != NULL) +#endif + +#ifdef EFX_NEED_LM87_DRIVER +#ifdef EFX_HAVE_OLD_I2C_DRIVER_PROBE +int efx_lm87_probe(struct i2c_client *client); +#else +int efx_lm87_probe(struct i2c_client *client, const struct i2c_device_id *); +#endif +extern struct i2c_driver efx_lm87_driver; +#endif + +#ifdef EFX_NEED_LM90_DRIVER +#ifdef EFX_HAVE_OLD_I2C_DRIVER_PROBE +int efx_lm90_probe(struct i2c_client *client); +#else +int efx_lm90_probe(struct i2c_client *client, const struct i2c_device_id *); +#endif +extern struct i2c_driver efx_lm90_driver; +#endif + +#ifdef EFX_NEED_WAIT_EVENT_TIMEOUT +#define __wait_event_timeout(wq, condition, ret) \ +do { \ + DEFINE_WAIT(__wait); \ + \ + for (;;) { \ + prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ + if (condition) \ + break; \ + ret = schedule_timeout(ret); \ + if (!ret) \ + break; \ + } \ + finish_wait(&wq, &__wait); \ +} while (0) /** - * queue_delayed_work in pre 2.6.20 can't rearm from inside - * the work member. So instead do a rather hacky sleep + * wait_event_timeout - sleep until a condition gets true or a timeout elapses + * @wq: the waitqueue to wait on + * @condition: a C expression for the event to wait for + * @timeout: timeout, in jiffies + * + * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the + * @condition evaluates to true. The @condition is checked each time + * the waitqueue @wq is woken up. + * + * wake_up() has to be called after changing any variable that could + * change the result of the wait condition. + * + * The function returns 0 if the @timeout elapsed, and the remaining + * jiffies if the condition evaluated to true before the timeout elapsed. */ -#define delayed_work work_struct -#define INIT_DELAYED_WORK INIT_WORK +#define wait_event_timeout(wq, condition, timeout) \ +({ \ + long __ret = timeout; \ + if (!(condition)) \ + __wait_event_timeout(wq, condition, __ret); \ + __ret; \ +}) +#endif -static int inline efx_queue_delayed_work(struct workqueue_struct *wq, - struct work_struct *work, - unsigned long delay) -{ - if (unlikely(delay > 0)) - schedule_timeout_uninterruptible(delay); - return queue_work(wq, work); -} -#define queue_delayed_work efx_queue_delayed_work +/************************************************************************** + * + * Missing functions provided by kernel_compat.c + * + ************************************************************************** + * + */ +#ifdef EFX_NEED_RANDOM_ETHER_ADDR + extern void efx_random_ether_addr(uint8_t *addr); + #ifndef EFX_IN_KCOMPAT_C + #undef random_ether_addr + #define random_ether_addr efx_random_ether_addr + #endif +#endif -/** - * The old and new work-function prototypes just differ - * in the type of the pointer returned, so it's safe - * to cast between the prototypes. +#ifdef EFX_NEED_UNREGISTER_NETDEVICE_NOTIFIER_FIX + extern int efx_unregister_netdevice_notifier(struct notifier_block *nb); + #ifndef EFX_IN_KCOMPAT_C + #undef unregister_netdevice_notifier + #define unregister_netdevice_notifier \ + efx_unregister_netdevice_notifier + #endif +#endif + +#ifdef EFX_NEED_PRINT_MAC + extern char *print_mac(char *buf, const u8 *addr); +#endif + +#ifdef EFX_NEED_COMPOUND_PAGE_FIX + extern void efx_compound_page_destructor(struct page *page); +#endif + +#ifdef EFX_NEED_HEX_DUMP + extern void + print_hex_dump(const char *level, const char *prefix_str, + int prefix_type, int rowsize, int groupsize, + const void *buf, size_t len, int ascii); +#endif + +#ifdef EFX_NEED_PCI_CLEAR_MASTER + extern void pci_clear_master(struct pci_dev *dev); +#endif + +#ifdef EFX_NEED_PCI_WAKE_FROM_D3 + extern int pci_wake_from_d3(struct pci_dev *dev, bool enable); +#endif + +#ifdef EFX_NEED_MSECS_TO_JIFFIES + extern unsigned long msecs_to_jiffies(const unsigned int m); +#endif + +#ifdef EFX_NEED_MSLEEP + extern void msleep(unsigned int msecs); +#endif + +#ifdef EFX_NEED_SSLEEP + static inline void ssleep(unsigned int seconds) + { + msleep(seconds * 1000); + } +#endif + +#ifdef EFX_NEED_MDELAY + #include + #undef mdelay + #define mdelay(_n) \ + do { \ + unsigned long __ms = _n; \ + while (__ms--) udelay(1000); \ + } while (0); +#endif + +#ifdef EFX_NEED_UNMASK_MSIX_VECTORS + extern void efx_unmask_msix_vectors(struct pci_dev *dev); +#endif + +/************************************************************************** + * + * Wrappers to fix bugs and parameter changes + * + ************************************************************************** + * */ -typedef void (*efx_old_work_func_t)(void *p); +#ifdef EFX_NEED_PCI_SAVE_RESTORE_WRAPPERS + #define pci_save_state(_dev) \ + pci_save_state(_dev, (_dev)->saved_config_space) -#undef INIT_WORK -#define INIT_WORK(_work, _func) \ - do { \ - INIT_LIST_HEAD(&(_work)->entry); \ - (_work)->pending = 0; \ - PREPARE_WORK((_work), \ - (efx_old_work_func_t) (_func), \ - (_work)); \ - } while (0) + #define pci_restore_state(_dev) \ + pci_restore_state(_dev, (_dev)->saved_config_space) +#endif -#define napi_str napi_dev[0] +#ifdef EFX_NEED_PCI_MATCH_ID + #define pci_match_id pci_match_device +#endif -static inline void netif_napi_add(struct net_device *dev, - struct net_device *dummy, - int (*poll) (struct net_device *, int *), - int weight) -{ - dev->weight = weight; - dev->poll = poll; -} +#ifdef EFX_NEED_WORK_API_WRAPPERS + #define delayed_work work_struct + #define INIT_DELAYED_WORK INIT_WORK -#define napi_enable netif_poll_enable -#define napi_disable netif_poll_disable + /** + * The old and new work-function prototypes just differ + * in the type of the pointer returned, so it's safe + * to cast between the prototypes. + */ + typedef void (*efx_old_work_func_t)(void *p); -#define netif_rx_complete(dev, dummy) netif_rx_complete(dev) + #undef INIT_WORK + #define INIT_WORK(_work, _func) \ + do { \ + INIT_LIST_HEAD(&(_work)->entry); \ + (_work)->pending = 0; \ + PREPARE_WORK((_work), \ + (efx_old_work_func_t) (_func), \ + (_work)); \ + init_timer(&(_work)->timer); \ + } while (0) +#endif + +#if defined(EFX_HAVE_OLD_NAPI) + #define napi_str napi_dev[0] + + static inline void netif_napi_add(struct net_device *dev, + struct net_device *dummy, + int (*poll) (struct net_device *, + int *), + int weight) + { + dev->weight = weight; + dev->poll = poll; + } + static inline void netif_napi_del(struct net_device *dev) {} + + #define napi_enable netif_poll_enable + #define napi_disable netif_poll_disable + #define napi_complete netif_rx_complete + + static inline void napi_schedule(struct net_device *dev) + { + if (!test_and_set_bit(__LINK_STATE_RX_SCHED, &dev->state)) + __netif_rx_schedule(dev); + } +#elif defined(EFX_NEED_NETIF_NAPI_DEL) + static inline void netif_napi_del(struct napi_struct *napi) + { + #ifdef CONFIG_NETPOLL + list_del(&napi->dev_list); + #endif + } +#endif + +#ifdef EFX_NEED_COMPOUND_PAGE_FIX + static inline + struct page *efx_alloc_pages(gfp_t flags, unsigned int order) + { + struct page *p = alloc_pages(flags, order); + if ((flags & __GFP_COMP) && (p != NULL) && (order > 0)) + p[1].mapping = (void *)efx_compound_page_destructor; + return p; + } + #undef alloc_pages + #define alloc_pages efx_alloc_pages + + static inline + void efx_free_pages(struct page *p, unsigned int order) + { + if ((order > 0) && (page_count(p) == 1)) + p[1].mapping = NULL; + __free_pages(p, order); + } + #define __free_pages efx_free_pages +#endif + +#ifdef EFX_NEED_HEX_DUMP_CONST_FIX + #define print_hex_dump(v,s,t,r,g,b,l,a) \ + print_hex_dump((v),(s),(t),(r),(g),(void*)(b),(l),(a)) +#endif + +#ifndef EFX_HAVE_HWMON_H + static inline struct device *hwmon_device_register(struct device *dev) + { + return dev; + } + static inline void hwmon_device_unregister(struct device *cdev) + { + } +#endif + +#ifdef EFX_NEED_HWMON_VID + #include + static inline u8 efx_vid_which_vrm(void) + { + /* we don't use i2c on the cpu */ + return 0; + } + #define vid_which_vrm efx_vid_which_vrm +#endif + +#ifdef EFX_HAVE_OLD_DEVICE_ATTRIBUTE + /* + * show and store methods do not receive a pointer to the + * device_attribute. We have to add wrapper functions. + */ + + #undef DEVICE_ATTR + #define DEVICE_ATTR(_name, _mode, _show, _store) \ + /*static*/ ssize_t __##_name##_##show(struct device *dev, \ + char *buf) \ + { \ + return _show(dev, NULL, buf); \ + } \ + static ssize_t __##_name##_##store(struct device *dev, \ + const char *buf, \ + size_t count) \ + { \ + ssize_t (*fn)(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, \ + size_t count) = _store; \ + return fn(dev, NULL, buf, count); \ + } \ + static ssize_t __##_name##_##store(struct device *, \ + const char *, size_t) \ + __attribute__((unused)); \ + static struct device_attribute dev_attr_##_name = \ + __ATTR(_name, _mode, __##_name##_##show, \ + __builtin_choose_expr \ + (__builtin_constant_p(_store) && _store == NULL, \ + NULL, __##_name##_##store)) + + struct sensor_device_attribute { + struct device_attribute dev_attr; + int index; + }; + + #define SENSOR_ATTR(_name, _mode, _show, _store, _index) \ + { .dev_attr = __ATTR(_name, _mode, _show, _store), \ + .index = _index } + + #define SENSOR_DEVICE_ATTR(_name, _mode, _show, \ + _store, _index) \ + /*static*/ ssize_t __##_name##_show_##_index(struct device *dev, \ + char *buf) \ + { \ + struct sensor_device_attribute attr; \ + attr.index = _index; \ + return _show(dev, &attr.dev_attr, buf); \ + } \ + static ssize_t __##_name##_store_##_index(struct device *dev, \ + const char *buf, \ + size_t count) \ + { \ + ssize_t (*fn)(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, \ + size_t count) = _store; \ + struct sensor_device_attribute attr; \ + attr.index = _index; \ + return fn(dev, &attr.dev_attr, buf, count); \ + } \ + static ssize_t __##_name##_store_##_index(struct device *, \ + const char *, size_t) \ + __attribute__((unused)); \ + static struct sensor_device_attribute \ + sensor_dev_attr_##_name = \ + SENSOR_ATTR(_name, _mode, \ + __##_name##_show_##_index, \ + __builtin_choose_expr \ + (__builtin_constant_p(_store) && _store == NULL, \ + NULL, __##_name##_store_##_index), \ + _index) + + #define to_sensor_dev_attr(_dev_attr) \ + container_of(_dev_attr, struct sensor_device_attribute, \ + dev_attr) + +#endif + + +#ifdef EFX_NEED_SCSI_SGLIST + #include + #include + #define scsi_sglist(sc) ((struct scatterlist *)((sc)->request_buffer)) + #define scsi_bufflen(sc) ((sc)->request_bufflen) + #define scsi_sg_count(sc) ((sc)->use_sg) + static inline void scsi_set_resid(struct scsi_cmnd *sc, int resid) + { + sc->resid = resid; + } + static inline int scsi_get_resid(struct scsi_cmnd *sc) + { + return sc->resid; + } +#endif + + +#ifdef EFX_NEED_SG_NEXT + #define sg_page(sg) ((sg)->page) + #define sg_next(sg) ((sg) + 1) + #define for_each_sg(sglist, sg, nr, __i) \ + for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg)) +#endif + +#ifdef EFX_NEED_WARN_ON + #undef WARN_ON + #define WARN_ON(condition) ({ \ + typeof(condition) __ret_warn_on = (condition); \ + if (unlikely(__ret_warn_on)) { \ + printk("BUG: warning at %s:%d/%s()\n", \ + __FILE__, __LINE__, __FUNCTION__); \ + dump_stack(); \ + } \ + unlikely(__ret_warn_on); \ + }) +#endif #endif /* EFX_KERNEL_COMPAT_H */ diff -r aa133dee7792 drivers/net/sfc/linux_mdio.c --- /dev/null +++ b/drivers/net/sfc/linux_mdio.c @@ -0,0 +1,430 @@ +/* + * mdio.c: Generic support for MDIO-compatible transceivers + * Copyright 2006-2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "kernel_compat.h" + +#ifndef EFX_HAVE_LINUX_MDIO_H + +#include +#include +#include +#include +#include "linux_mdio.h" +#include + +/** + * mdio45_probe - probe for an MDIO (clause 45) device + * @mdio: MDIO interface + * @prtad: Expected PHY address + * + * This sets @prtad and @mmds in the MDIO interface if successful. + * Returns 0 on success, negative on error. + */ +int mdio45_probe(struct mdio_if_info *mdio, int prtad) +{ + int mmd, stat2, devs1, devs2; + + /* Assume PHY must have at least one of PMA/PMD, WIS, PCS, PHY + * XS or DTE XS; give up if none is present. */ + for (mmd = 1; mmd <= 5; mmd++) { + /* Is this MMD present? */ + stat2 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_STAT2); + if (stat2 < 0 || + (stat2 & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_VAL) + continue; + + /* It should tell us about all the other MMDs */ + devs1 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_DEVS1); + devs2 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_DEVS2); + if (devs1 < 0 || devs2 < 0) + continue; + + mdio->prtad = prtad; + mdio->mmds = devs1 | (devs2 << 16); + return 0; + } + + return -ENODEV; +} +/*EXPORT_SYMBOL(mdio45_probe);*/ + +/** + * mdio_set_flag - set or clear flag in an MDIO register + * @mdio: MDIO interface + * @prtad: PHY address + * @devad: MMD address + * @addr: Register address + * @mask: Mask for flag (single bit set) + * @sense: New value of flag + * + * This debounces changes: it does not write the register if the flag + * already has the proper value. Returns 0 on success, negative on error. + */ +int mdio_set_flag(const struct mdio_if_info *mdio, + int prtad, int devad, u16 addr, int mask, + bool sense) +{ + int old_val = mdio->mdio_read(mdio->dev, prtad, devad, addr); + int new_val; + + if (old_val < 0) + return old_val; + if (sense) + new_val = old_val | mask; + else + new_val = old_val & ~mask; + if (old_val == new_val) + return 0; + return mdio->mdio_write(mdio->dev, prtad, devad, addr, new_val); +} +/*EXPORT_SYMBOL(mdio_set_flag);*/ + +/** + * mdio_link_ok - is link status up/OK + * @mdio: MDIO interface + * @mmd_mask: Mask for MMDs to check + * + * Returns 1 if the PHY reports link status up/OK, 0 otherwise. + * @mmd_mask is normally @mdio->mmds, but if loopback is enabled + * the MMDs being bypassed should be excluded from the mask. + */ +int mdio45_links_ok(const struct mdio_if_info *mdio, u32 mmd_mask) +{ + int devad, reg; + + if (!mmd_mask) { + /* Use absence of XGMII faults in lieu of link state */ + reg = mdio->mdio_read(mdio->dev, mdio->prtad, + MDIO_MMD_PHYXS, MDIO_STAT2); + return reg >= 0 && !(reg & MDIO_STAT2_RXFAULT); + } + + for (devad = 0; mmd_mask; devad++) { + if (mmd_mask & (1 << devad)) { + mmd_mask &= ~(1 << devad); + + /* Reset the latched status and fault flags */ + mdio->mdio_read(mdio->dev, mdio->prtad, + devad, MDIO_STAT1); + if (devad == MDIO_MMD_PMAPMD || devad == MDIO_MMD_PCS || + devad == MDIO_MMD_PHYXS || devad == MDIO_MMD_DTEXS) + mdio->mdio_read(mdio->dev, mdio->prtad, + devad, MDIO_STAT2); + + /* Check the current status and fault flags */ + reg = mdio->mdio_read(mdio->dev, mdio->prtad, + devad, MDIO_STAT1); + if (reg < 0 || + (reg & (MDIO_STAT1_FAULT | MDIO_STAT1_LSTATUS)) != + MDIO_STAT1_LSTATUS) + return false; + } + } + + return true; +} +/*EXPORT_SYMBOL(mdio45_links_ok);*/ + +/** + * mdio45_nway_restart - restart auto-negotiation for this interface + * @mdio: MDIO interface + * + * Returns 0 on success, negative on error. + */ +int mdio45_nway_restart(const struct mdio_if_info *mdio) +{ + if (!(mdio->mmds & MDIO_DEVS_AN)) + return -EOPNOTSUPP; + + mdio_set_flag(mdio, mdio->prtad, MDIO_MMD_AN, MDIO_CTRL1, + MDIO_AN_CTRL1_RESTART, true); + return 0; +} +/*EXPORT_SYMBOL(mdio45_nway_restart);*/ + +static u32 mdio45_get_an(const struct mdio_if_info *mdio, u16 addr) +{ + u32 result = 0; + int reg; + + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN, addr); + if (reg & ADVERTISE_10HALF) + result |= ADVERTISED_10baseT_Half; + if (reg & ADVERTISE_10FULL) + result |= ADVERTISED_10baseT_Full; + if (reg & ADVERTISE_100HALF) + result |= ADVERTISED_100baseT_Half; + if (reg & ADVERTISE_100FULL) + result |= ADVERTISED_100baseT_Full; + if (reg & ADVERTISE_PAUSE_CAP) + result |= ADVERTISED_Pause; + if (reg & ADVERTISE_PAUSE_ASYM) + result |= ADVERTISED_Asym_Pause; + return result; +} + +/** + * mdio45_ethtool_gset_npage - get settings for ETHTOOL_GSET + * @mdio: MDIO interface + * @ecmd: Ethtool request structure + * @npage_adv: Modes currently advertised on next pages + * @npage_lpa: Modes advertised by link partner on next pages + * + * Since the CSRs for auto-negotiation using next pages are not fully + * standardised, this function does not attempt to decode them. The + * caller must pass them in. + */ +void mdio45_ethtool_gset_npage(const struct mdio_if_info *mdio, + struct ethtool_cmd *ecmd, + u32 npage_adv, u32 npage_lpa) +{ + int reg; + + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = mdio->prtad; + /*ecmd->mdio_support = + mdio->mode_support & (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22);*/ + + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD, + MDIO_CTRL2); + switch (reg & MDIO_PMA_CTRL2_TYPE) { + case MDIO_PMA_CTRL2_10GBT: + case MDIO_PMA_CTRL2_1000BT: + case MDIO_PMA_CTRL2_100BTX: + case MDIO_PMA_CTRL2_10BT: + ecmd->port = PORT_TP; + ecmd->supported = SUPPORTED_TP; + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD, + MDIO_SPEED); + if (reg & MDIO_SPEED_10G) + ecmd->supported |= SUPPORTED_10000baseT_Full; + if (reg & MDIO_PMA_SPEED_1000) + ecmd->supported |= (SUPPORTED_1000baseT_Full | + SUPPORTED_1000baseT_Half); + if (reg & MDIO_PMA_SPEED_100) + ecmd->supported |= (SUPPORTED_100baseT_Full | + SUPPORTED_100baseT_Half); + if (reg & MDIO_PMA_SPEED_10) + ecmd->supported |= (SUPPORTED_10baseT_Full | + SUPPORTED_10baseT_Half); + ecmd->advertising = ADVERTISED_TP; + break; + + case MDIO_PMA_CTRL2_10GBCX4: + ecmd->port = PORT_OTHER; + ecmd->supported = 0; + ecmd->advertising = 0; + break; + +/* + case MDIO_PMA_CTRL2_10GBKX4: + case MDIO_PMA_CTRL2_10GBKR: + case MDIO_PMA_CTRL2_1000BKX: + ecmd->port = PORT_OTHER; + ecmd->supported = SUPPORTED_Backplane; + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD, + MDIO_PMA_EXTABLE); + if (reg & MDIO_PMA_EXTABLE_10GBKX4) + ecmd->supported |= SUPPORTED_10000baseKX4_Full; + if (reg & MDIO_PMA_EXTABLE_10GBKR) + ecmd->supported |= SUPPORTED_10000baseKR_Full; + if (reg & MDIO_PMA_EXTABLE_1000BKX) + ecmd->supported |= SUPPORTED_1000baseKX_Full; + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD, + MDIO_PMA_10GBR_FECABLE); + if (reg & MDIO_PMA_10GBR_FECABLE_ABLE) + ecmd->supported |= SUPPORTED_10000baseR_FEC; + ecmd->advertising = ADVERTISED_Backplane; + break; +*/ + + /* All the other defined modes are flavours of optical */ + default: + ecmd->port = PORT_FIBRE; + ecmd->supported = SUPPORTED_FIBRE; + ecmd->advertising = ADVERTISED_FIBRE; + break; + } + + if (mdio->mmds & MDIO_DEVS_AN) { + ecmd->supported |= SUPPORTED_Autoneg; + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN, + MDIO_CTRL1); + if (reg & MDIO_AN_CTRL1_ENABLE) { + ecmd->autoneg = AUTONEG_ENABLE; + ecmd->advertising |= + ADVERTISED_Autoneg | + mdio45_get_an(mdio, MDIO_AN_ADVERTISE) | + npage_adv; + } else { + ecmd->autoneg = AUTONEG_DISABLE; + } + } else { + ecmd->autoneg = AUTONEG_DISABLE; + } + + if (ecmd->autoneg) { + u32 modes = 0; + int an_stat = mdio->mdio_read(mdio->dev, mdio->prtad, + MDIO_MMD_AN, MDIO_STAT1); + + /* If AN is complete and successful, report best common + * mode, otherwise report best advertised mode. */ + if (an_stat & MDIO_AN_STAT1_COMPLETE) { + u32 lp_advertising; + /*ecmd->*/lp_advertising = + mdio45_get_an(mdio, MDIO_AN_LPA) | npage_lpa; + /*if (an_stat & MDIO_AN_STAT1_LPABLE) + ecmd->lp_advertising |= ADVERTISED_Autoneg;*/ + modes = ecmd->advertising & /*ecmd->*/lp_advertising; + } + if ((modes & ~ADVERTISED_Autoneg) == 0) + modes = ecmd->advertising; + + if (modes & (ADVERTISED_10000baseT_Full/* | + ADVERTISED_10000baseKX4_Full | + ADVERTISED_10000baseKR_Full*/)) { + ecmd->speed = SPEED_10000; + ecmd->duplex = DUPLEX_FULL; + } else if (modes & (ADVERTISED_1000baseT_Full | + ADVERTISED_1000baseT_Half/* | + ADVERTISED_1000baseKX_Full*/)) { + ecmd->speed = SPEED_1000; + ecmd->duplex = !(modes & ADVERTISED_1000baseT_Half); + } else if (modes & (ADVERTISED_100baseT_Full | + ADVERTISED_100baseT_Half)) { + ecmd->speed = SPEED_100; + ecmd->duplex = !!(modes & ADVERTISED_100baseT_Full); + } else { + ecmd->speed = SPEED_10; + ecmd->duplex = !!(modes & ADVERTISED_10baseT_Full); + } + } else { + /* Report forced settings */ + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD, + MDIO_CTRL1); + ecmd->speed = (((reg & MDIO_PMA_CTRL1_SPEED1000) ? 100 : 1) * + ((reg & MDIO_PMA_CTRL1_SPEED100) ? 100 : 10)); + ecmd->duplex = (reg & MDIO_CTRL1_FULLDPLX || + ecmd->speed == SPEED_10000); + } + +} +/*EXPORT_SYMBOL(mdio45_ethtool_gset_npage);*/ + +/** + * mdio45_ethtool_spauseparam_an - set auto-negotiated pause parameters + * @mdio: MDIO interface + * @ecmd: Ethtool request structure + * + * This function assumes that the PHY has an auto-negotiation MMD. It + * will enable and disable advertising of flow control as appropriate. + */ +void mdio45_ethtool_spauseparam_an(const struct mdio_if_info *mdio, + const struct ethtool_pauseparam *ecmd) +{ + int adv, old_adv; + + WARN_ON(!(mdio->mmds & MDIO_DEVS_AN)); + + old_adv = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN, + MDIO_AN_ADVERTISE); + adv = ((old_adv & ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)) | + mii_advertise_flowctrl((ecmd->rx_pause ? FLOW_CTRL_RX : 0) | + (ecmd->tx_pause ? FLOW_CTRL_TX : 0))); + if (adv != old_adv) { + mdio->mdio_write(mdio->dev, mdio->prtad, MDIO_MMD_AN, + MDIO_AN_ADVERTISE, adv); + mdio45_nway_restart(mdio); + } +} +/*EXPORT_SYMBOL(mdio45_ethtool_spauseparam_an);*/ + +/** + * mdio_mii_ioctl - MII ioctl interface for MDIO (clause 22 or 45) PHYs + * @mdio: MDIO interface + * @mii_data: MII ioctl data structure + * @cmd: MII ioctl command + * + * Returns 0 on success, negative on error. + */ +int mdio_mii_ioctl(const struct mdio_if_info *mdio, + struct mii_ioctl_data *mii_data, int cmd) +{ + int prtad, devad; + u16 addr = mii_data->reg_num; + + /* Validate/convert cmd to one of SIOC{G,S}MIIREG */ + switch (cmd) { + case SIOCGMIIPHY: + if (mdio->prtad == MDIO_PRTAD_NONE) + return -EOPNOTSUPP; + mii_data->phy_id = mdio->prtad; + cmd = SIOCGMIIREG; + break; + case SIOCGMIIREG: + case SIOCSMIIREG: + break; + default: + return -EOPNOTSUPP; + } + + /* Validate/convert phy_id */ + if ((mdio->mode_support & MDIO_SUPPORTS_C45) && + mdio_phy_id_is_c45(mii_data->phy_id)) { + prtad = mdio_phy_id_prtad(mii_data->phy_id); + devad = mdio_phy_id_devad(mii_data->phy_id); + } else if ((mdio->mode_support & MDIO_SUPPORTS_C22) && + mii_data->phy_id < 0x20) { + prtad = mii_data->phy_id; + devad = MDIO_DEVAD_NONE; + addr &= 0x1f; + } else if ((mdio->mode_support & MDIO_EMULATE_C22) && + mdio->prtad != MDIO_PRTAD_NONE && + mii_data->phy_id == mdio->prtad) { + /* Remap commonly-used MII registers. */ + prtad = mdio->prtad; + switch (addr) { + case MII_BMCR: + case MII_BMSR: + case MII_PHYSID1: + case MII_PHYSID2: + devad = __ffs(mdio->mmds); + break; + case MII_ADVERTISE: + case MII_LPA: + if (!(mdio->mmds & MDIO_DEVS_AN)) + return -EINVAL; + devad = MDIO_MMD_AN; + if (addr == MII_ADVERTISE) + addr = MDIO_AN_ADVERTISE; + else + addr = MDIO_AN_LPA; + break; + default: + return -EINVAL; + } + } else { + return -EINVAL; + } + + if (cmd == SIOCGMIIREG) { + int rc = mdio->mdio_read(mdio->dev, prtad, devad, addr); + if (rc < 0) + return rc; + mii_data->val_out = rc; + return 0; + } else { + return mdio->mdio_write(mdio->dev, prtad, devad, addr, + mii_data->val_in); + } +} +/*EXPORT_SYMBOL(mdio_mii_ioctl);*/ + +#endif /* EFX_HAVE_LINUX_MDIO_H */ diff -r aa133dee7792 drivers/net/sfc/linux_mdio.h --- /dev/null +++ b/drivers/net/sfc/linux_mdio.h @@ -0,0 +1,347 @@ +/* + * linux/mdio.h: definitions for MDIO (clause 45) transceivers + * Copyright 2006-2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef __LINUX_MDIO_H__ +#define __LINUX_MDIO_H__ + +#include + +/* MDIO Manageable Devices (MMDs). */ +#define MDIO_MMD_PMAPMD 1 /* Physical Medium Attachment/ + * Physical Medium Dependent */ +#define MDIO_MMD_WIS 2 /* WAN Interface Sublayer */ +#define MDIO_MMD_PCS 3 /* Physical Coding Sublayer */ +#define MDIO_MMD_PHYXS 4 /* PHY Extender Sublayer */ +#define MDIO_MMD_DTEXS 5 /* DTE Extender Sublayer */ +#define MDIO_MMD_TC 6 /* Transmission Convergence */ +#define MDIO_MMD_AN 7 /* Auto-Negotiation */ +#define MDIO_MMD_C22EXT 29 /* Clause 22 extension */ +#define MDIO_MMD_VEND1 30 /* Vendor specific 1 */ +#define MDIO_MMD_VEND2 31 /* Vendor specific 2 */ + +/* Generic MDIO registers. */ +#define MDIO_CTRL1 MII_BMCR +#define MDIO_STAT1 MII_BMSR +#define MDIO_DEVID1 MII_PHYSID1 +#define MDIO_DEVID2 MII_PHYSID2 +#define MDIO_SPEED 4 /* Speed ability */ +#define MDIO_DEVS1 5 /* Devices in package */ +#define MDIO_DEVS2 6 +#define MDIO_CTRL2 7 /* 10G control 2 */ +#define MDIO_STAT2 8 /* 10G status 2 */ +#define MDIO_PMA_TXDIS 9 /* 10G PMA/PMD transmit disable */ +#define MDIO_PMA_RXDET 10 /* 10G PMA/PMD receive signal detect */ +#define MDIO_PMA_EXTABLE 11 /* 10G PMA/PMD extended ability */ +#define MDIO_PKGID1 14 /* Package identifier */ +#define MDIO_PKGID2 15 +#define MDIO_AN_ADVERTISE 16 /* AN advertising (base page) */ +#define MDIO_AN_LPA 19 /* AN LP abilities (base page) */ +#define MDIO_PHYXS_LNSTAT 24 /* PHY XGXS lane state */ + +/* Media-dependent registers. */ +#define MDIO_PMA_10GBT_TXPWR 131 /* 10GBASE-T TX power control */ +#define MDIO_PMA_10GBT_SNR 133 /* 10GBASE-T SNR margin, lane A. + * Lanes B-D are numbered 134-136. */ +#define MDIO_PMA_10GBR_FECABLE 170 /* 10GBASE-R FEC ability */ +#define MDIO_PCS_10GBX_STAT1 24 /* 10GBASE-X PCS status 1 */ +#define MDIO_PCS_10GBRT_STAT1 32 /* 10GBASE-R/-T PCS status 1 */ +#define MDIO_PCS_10GBRT_STAT2 33 /* 10GBASE-R/-T PCS status 2 */ +#define MDIO_AN_10GBT_CTRL 32 /* 10GBASE-T auto-negotiation control */ +#define MDIO_AN_10GBT_STAT 33 /* 10GBASE-T auto-negotiation status */ + +/* LASI (Link Alarm Status Interrupt) registers, defined by XENPAK MSA. */ +#define MDIO_PMA_LASI_RXCTRL 0x9000 /* RX_ALARM control */ +#define MDIO_PMA_LASI_TXCTRL 0x9001 /* TX_ALARM control */ +#define MDIO_PMA_LASI_CTRL 0x9002 /* LASI control */ +#define MDIO_PMA_LASI_RXSTAT 0x9003 /* RX_ALARM status */ +#define MDIO_PMA_LASI_TXSTAT 0x9004 /* TX_ALARM status */ +#define MDIO_PMA_LASI_STAT 0x9005 /* LASI status */ + +/* Control register 1. */ +/* Enable extended speed selection */ +#define MDIO_CTRL1_SPEEDSELEXT (BMCR_SPEED1000 | BMCR_SPEED100) +/* All speed selection bits */ +#define MDIO_CTRL1_SPEEDSEL (MDIO_CTRL1_SPEEDSELEXT | 0x003c) +#define MDIO_CTRL1_FULLDPLX BMCR_FULLDPLX +#define MDIO_CTRL1_LPOWER BMCR_PDOWN +#define MDIO_CTRL1_RESET BMCR_RESET +#define MDIO_PMA_CTRL1_LOOPBACK 0x0001 +#define MDIO_PMA_CTRL1_SPEED1000 BMCR_SPEED1000 +#define MDIO_PMA_CTRL1_SPEED100 BMCR_SPEED100 +#define MDIO_PCS_CTRL1_LOOPBACK BMCR_LOOPBACK +#define MDIO_PHYXS_CTRL1_LOOPBACK BMCR_LOOPBACK +#define MDIO_AN_CTRL1_RESTART BMCR_ANRESTART +#define MDIO_AN_CTRL1_ENABLE BMCR_ANENABLE +#define MDIO_AN_CTRL1_XNP 0x2000 /* Enable extended next page */ + +/* 10 Gb/s */ +#define MDIO_CTRL1_SPEED10G (MDIO_CTRL1_SPEEDSELEXT | 0x00) +/* 10PASS-TS/2BASE-TL */ +#define MDIO_CTRL1_SPEED10P2B (MDIO_CTRL1_SPEEDSELEXT | 0x04) + +/* Status register 1. */ +#define MDIO_STAT1_LPOWERABLE 0x0002 /* Low-power ability */ +#define MDIO_STAT1_LSTATUS BMSR_LSTATUS +#define MDIO_STAT1_FAULT 0x0080 /* Fault */ +#define MDIO_AN_STAT1_LPABLE 0x0001 /* Link partner AN ability */ +#define MDIO_AN_STAT1_ABLE BMSR_ANEGCAPABLE +#define MDIO_AN_STAT1_RFAULT BMSR_RFAULT +#define MDIO_AN_STAT1_COMPLETE BMSR_ANEGCOMPLETE +#define MDIO_AN_STAT1_PAGE 0x0040 /* Page received */ +#define MDIO_AN_STAT1_XNP 0x0080 /* Extended next page status */ + +/* Speed register. */ +#define MDIO_SPEED_10G 0x0001 /* 10G capable */ +#define MDIO_PMA_SPEED_2B 0x0002 /* 2BASE-TL capable */ +#define MDIO_PMA_SPEED_10P 0x0004 /* 10PASS-TS capable */ +#define MDIO_PMA_SPEED_1000 0x0010 /* 1000M capable */ +#define MDIO_PMA_SPEED_100 0x0020 /* 100M capable */ +#define MDIO_PMA_SPEED_10 0x0040 /* 10M capable */ +#define MDIO_PCS_SPEED_10P2B 0x0002 /* 10PASS-TS/2BASE-TL capable */ + +/* Device present registers. */ +#define MDIO_DEVS_PRESENT(devad) (1 << (devad)) +#define MDIO_DEVS_PMAPMD MDIO_DEVS_PRESENT(MDIO_MMD_PMAPMD) +#define MDIO_DEVS_WIS MDIO_DEVS_PRESENT(MDIO_MMD_WIS) +#define MDIO_DEVS_PCS MDIO_DEVS_PRESENT(MDIO_MMD_PCS) +#define MDIO_DEVS_PHYXS MDIO_DEVS_PRESENT(MDIO_MMD_PHYXS) +#define MDIO_DEVS_DTEXS MDIO_DEVS_PRESENT(MDIO_MMD_DTEXS) +#define MDIO_DEVS_TC MDIO_DEVS_PRESENT(MDIO_MMD_TC) +#define MDIO_DEVS_AN MDIO_DEVS_PRESENT(MDIO_MMD_AN) +#define MDIO_DEVS_C22EXT MDIO_DEVS_PRESENT(MDIO_MMD_C22EXT) + +/* Control register 2. */ +#define MDIO_PMA_CTRL2_TYPE 0x000f /* PMA/PMD type selection */ +#define MDIO_PMA_CTRL2_10GBCX4 0x0000 /* 10GBASE-CX4 type */ +#define MDIO_PMA_CTRL2_10GBEW 0x0001 /* 10GBASE-EW type */ +#define MDIO_PMA_CTRL2_10GBLW 0x0002 /* 10GBASE-LW type */ +#define MDIO_PMA_CTRL2_10GBSW 0x0003 /* 10GBASE-SW type */ +#define MDIO_PMA_CTRL2_10GBLX4 0x0004 /* 10GBASE-LX4 type */ +#define MDIO_PMA_CTRL2_10GBER 0x0005 /* 10GBASE-ER type */ +#define MDIO_PMA_CTRL2_10GBLR 0x0006 /* 10GBASE-LR type */ +#define MDIO_PMA_CTRL2_10GBSR 0x0007 /* 10GBASE-SR type */ +#define MDIO_PMA_CTRL2_10GBLRM 0x0008 /* 10GBASE-LRM type */ +#define MDIO_PMA_CTRL2_10GBT 0x0009 /* 10GBASE-T type */ +#define MDIO_PMA_CTRL2_10GBKX4 0x000a /* 10GBASE-KX4 type */ +#define MDIO_PMA_CTRL2_10GBKR 0x000b /* 10GBASE-KR type */ +#define MDIO_PMA_CTRL2_1000BT 0x000c /* 1000BASE-T type */ +#define MDIO_PMA_CTRL2_1000BKX 0x000d /* 1000BASE-KX type */ +#define MDIO_PMA_CTRL2_100BTX 0x000e /* 100BASE-TX type */ +#define MDIO_PMA_CTRL2_10BT 0x000f /* 10BASE-T type */ +#define MDIO_PCS_CTRL2_TYPE 0x0003 /* PCS type selection */ +#define MDIO_PCS_CTRL2_10GBR 0x0000 /* 10GBASE-R type */ +#define MDIO_PCS_CTRL2_10GBX 0x0001 /* 10GBASE-X type */ +#define MDIO_PCS_CTRL2_10GBW 0x0002 /* 10GBASE-W type */ +#define MDIO_PCS_CTRL2_10GBT 0x0003 /* 10GBASE-T type */ + +/* Status register 2. */ +#define MDIO_STAT2_RXFAULT 0x0400 /* Receive fault */ +#define MDIO_STAT2_TXFAULT 0x0800 /* Transmit fault */ +#define MDIO_STAT2_DEVPRST 0xc000 /* Device present */ +#define MDIO_STAT2_DEVPRST_VAL 0x8000 /* Device present value */ +#define MDIO_PMA_STAT2_LBABLE 0x0001 /* PMA loopback ability */ +#define MDIO_PMA_STAT2_10GBEW 0x0002 /* 10GBASE-EW ability */ +#define MDIO_PMA_STAT2_10GBLW 0x0004 /* 10GBASE-LW ability */ +#define MDIO_PMA_STAT2_10GBSW 0x0008 /* 10GBASE-SW ability */ +#define MDIO_PMA_STAT2_10GBLX4 0x0010 /* 10GBASE-LX4 ability */ +#define MDIO_PMA_STAT2_10GBER 0x0020 /* 10GBASE-ER ability */ +#define MDIO_PMA_STAT2_10GBLR 0x0040 /* 10GBASE-LR ability */ +#define MDIO_PMA_STAT2_10GBSR 0x0080 /* 10GBASE-SR ability */ +#define MDIO_PMD_STAT2_TXDISAB 0x0100 /* PMD TX disable ability */ +#define MDIO_PMA_STAT2_EXTABLE 0x0200 /* Extended abilities */ +#define MDIO_PMA_STAT2_RXFLTABLE 0x1000 /* Receive fault ability */ +#define MDIO_PMA_STAT2_TXFLTABLE 0x2000 /* Transmit fault ability */ +#define MDIO_PCS_STAT2_10GBR 0x0001 /* 10GBASE-R capable */ +#define MDIO_PCS_STAT2_10GBX 0x0002 /* 10GBASE-X capable */ +#define MDIO_PCS_STAT2_10GBW 0x0004 /* 10GBASE-W capable */ +#define MDIO_PCS_STAT2_RXFLTABLE 0x1000 /* Receive fault ability */ +#define MDIO_PCS_STAT2_TXFLTABLE 0x2000 /* Transmit fault ability */ + +/* Transmit disable register. */ +#define MDIO_PMD_TXDIS_GLOBAL 0x0001 /* Global PMD TX disable */ +#define MDIO_PMD_TXDIS_0 0x0002 /* PMD TX disable 0 */ +#define MDIO_PMD_TXDIS_1 0x0004 /* PMD TX disable 1 */ +#define MDIO_PMD_TXDIS_2 0x0008 /* PMD TX disable 2 */ +#define MDIO_PMD_TXDIS_3 0x0010 /* PMD TX disable 3 */ + +/* Receive signal detect register. */ +#define MDIO_PMD_RXDET_GLOBAL 0x0001 /* Global PMD RX signal detect */ +#define MDIO_PMD_RXDET_0 0x0002 /* PMD RX signal detect 0 */ +#define MDIO_PMD_RXDET_1 0x0004 /* PMD RX signal detect 1 */ +#define MDIO_PMD_RXDET_2 0x0008 /* PMD RX signal detect 2 */ +#define MDIO_PMD_RXDET_3 0x0010 /* PMD RX signal detect 3 */ + +/* Extended abilities register. */ +#define MDIO_PMA_EXTABLE_10GCX4 0x0001 /* 10GBASE-CX4 ability */ +#define MDIO_PMA_EXTABLE_10GBLRM 0x0002 /* 10GBASE-LRM ability */ +#define MDIO_PMA_EXTABLE_10GBT 0x0004 /* 10GBASE-T ability */ +#define MDIO_PMA_EXTABLE_10GBKX4 0x0008 /* 10GBASE-KX4 ability */ +#define MDIO_PMA_EXTABLE_10GBKR 0x0010 /* 10GBASE-KR ability */ +#define MDIO_PMA_EXTABLE_1000BT 0x0020 /* 1000BASE-T ability */ +#define MDIO_PMA_EXTABLE_1000BKX 0x0040 /* 1000BASE-KX ability */ +#define MDIO_PMA_EXTABLE_100BTX 0x0080 /* 100BASE-TX ability */ +#define MDIO_PMA_EXTABLE_10BT 0x0100 /* 10BASE-T ability */ + +/* PHY XGXS lane state register. */ +#define MDIO_PHYXS_LNSTAT_SYNC0 0x0001 +#define MDIO_PHYXS_LNSTAT_SYNC1 0x0002 +#define MDIO_PHYXS_LNSTAT_SYNC2 0x0004 +#define MDIO_PHYXS_LNSTAT_SYNC3 0x0008 +#define MDIO_PHYXS_LNSTAT_ALIGN 0x1000 + +/* PMA 10GBASE-T TX power register. */ +#define MDIO_PMA_10GBT_TXPWR_SHORT 0x0001 /* Short-reach mode */ + +/* PMA 10GBASE-T SNR registers. */ +/* Value is SNR margin in dB, clamped to range [-127, 127], plus 0x8000. */ +#define MDIO_PMA_10GBT_SNR_BIAS 0x8000 +#define MDIO_PMA_10GBT_SNR_MAX 127 + +/* PMA 10GBASE-R FEC ability register. */ +#define MDIO_PMA_10GBR_FECABLE_ABLE 0x0001 /* FEC ability */ +#define MDIO_PMA_10GBR_FECABLE_ERRABLE 0x0002 /* FEC error indic. ability */ + +/* PCS 10GBASE-R/-T status register 1. */ +#define MDIO_PCS_10GBRT_STAT1_BLKLK 0x0001 /* Block lock attained */ + +/* PCS 10GBASE-R/-T status register 2. */ +#define MDIO_PCS_10GBRT_STAT2_ERR 0x00ff +#define MDIO_PCS_10GBRT_STAT2_BER 0x3f00 + +/* AN 10GBASE-T control register. */ +#define MDIO_AN_10GBT_CTRL_ADV10G 0x1000 /* Advertise 10GBASE-T */ + +/* AN 10GBASE-T status register. */ +#define MDIO_AN_10GBT_STAT_LPTRR 0x0200 /* LP training reset req. */ +#define MDIO_AN_10GBT_STAT_LPLTABLE 0x0400 /* LP loop timing ability */ +#define MDIO_AN_10GBT_STAT_LP10G 0x0800 /* LP is 10GBT capable */ +#define MDIO_AN_10GBT_STAT_REMOK 0x1000 /* Remote OK */ +#define MDIO_AN_10GBT_STAT_LOCOK 0x2000 /* Local OK */ +#define MDIO_AN_10GBT_STAT_MS 0x4000 /* Master/slave config */ +#define MDIO_AN_10GBT_STAT_MSFLT 0x8000 /* Master/slave config fault */ + +/* LASI RX_ALARM control/status registers. */ +#define MDIO_PMA_LASI_RX_PHYXSLFLT 0x0001 /* PHY XS RX local fault */ +#define MDIO_PMA_LASI_RX_PCSLFLT 0x0008 /* PCS RX local fault */ +#define MDIO_PMA_LASI_RX_PMALFLT 0x0010 /* PMA/PMD RX local fault */ +#define MDIO_PMA_LASI_RX_OPTICPOWERFLT 0x0020 /* RX optical power fault */ +#define MDIO_PMA_LASI_RX_WISLFLT 0x0200 /* WIS local fault */ + +/* LASI TX_ALARM control/status registers. */ +#define MDIO_PMA_LASI_TX_PHYXSLFLT 0x0001 /* PHY XS TX local fault */ +#define MDIO_PMA_LASI_TX_PCSLFLT 0x0008 /* PCS TX local fault */ +#define MDIO_PMA_LASI_TX_PMALFLT 0x0010 /* PMA/PMD TX local fault */ +#define MDIO_PMA_LASI_TX_LASERPOWERFLT 0x0080 /* Laser output power fault */ +#define MDIO_PMA_LASI_TX_LASERTEMPFLT 0x0100 /* Laser temperature fault */ +#define MDIO_PMA_LASI_TX_LASERBICURRFLT 0x0200 /* Laser bias current fault */ + +/* LASI control/status registers. */ +#define MDIO_PMA_LASI_LSALARM 0x0001 /* LS_ALARM enable/status */ +#define MDIO_PMA_LASI_TXALARM 0x0002 /* TX_ALARM enable/status */ +#define MDIO_PMA_LASI_RXALARM 0x0004 /* RX_ALARM enable/status */ + +/* Mapping between MDIO PRTAD/DEVAD and mii_ioctl_data::phy_id */ + +#define MDIO_PHY_ID_C45 0x8000 +#define MDIO_PHY_ID_PRTAD 0x03e0 +#define MDIO_PHY_ID_DEVAD 0x001f +#define MDIO_PHY_ID_C45_MASK \ + (MDIO_PHY_ID_C45 | MDIO_PHY_ID_PRTAD | MDIO_PHY_ID_DEVAD) + +static inline __u16 mdio_phy_id_c45(int prtad, int devad) +{ + return MDIO_PHY_ID_C45 | (prtad << 5) | devad; +} + +static inline bool mdio_phy_id_is_c45(int phy_id) +{ + return (phy_id & MDIO_PHY_ID_C45) && !(phy_id & ~MDIO_PHY_ID_C45_MASK); +} + +static inline __u16 mdio_phy_id_prtad(int phy_id) +{ + return (phy_id & MDIO_PHY_ID_PRTAD) >> 5; +} + +static inline __u16 mdio_phy_id_devad(int phy_id) +{ + return phy_id & MDIO_PHY_ID_DEVAD; +} + +#define MDIO_SUPPORTS_C22 1 +#define MDIO_SUPPORTS_C45 2 + +#ifdef __KERNEL__ + +/** + * struct mdio_if_info - Ethernet controller MDIO interface + * @prtad: PRTAD of the PHY (%MDIO_PRTAD_NONE if not present/unknown) + * @mmds: Mask of MMDs expected to be present in the PHY. This must be + * non-zero unless @prtad = %MDIO_PRTAD_NONE. + * @mode_support: MDIO modes supported. If %MDIO_SUPPORTS_C22 is set then + * MII register access will be passed through with @devad = + * %MDIO_DEVAD_NONE. If %MDIO_EMULATE_C22 is set then access to + * commonly used clause 22 registers will be translated into + * clause 45 registers. + * @dev: Net device structure + * @mdio_read: Register read function; returns value or negative error code + * @mdio_write: Register write function; returns 0 or negative error code + */ +struct mdio_if_info { + int prtad; + u32 mmds; + unsigned mode_support; + + struct net_device *dev; + int (*mdio_read)(struct net_device *dev, int prtad, int devad, + u16 addr); + int (*mdio_write)(struct net_device *dev, int prtad, int devad, + u16 addr, u16 val); +}; + +#define MDIO_PRTAD_NONE (-1) +#define MDIO_DEVAD_NONE (-1) +#define MDIO_EMULATE_C22 4 + +struct ethtool_cmd; +struct ethtool_pauseparam; +extern int mdio45_probe(struct mdio_if_info *mdio, int prtad); +extern int mdio_set_flag(const struct mdio_if_info *mdio, + int prtad, int devad, u16 addr, int mask, + bool sense); +extern int mdio45_links_ok(const struct mdio_if_info *mdio, u32 mmds); +extern int mdio45_nway_restart(const struct mdio_if_info *mdio); +extern void mdio45_ethtool_gset_npage(const struct mdio_if_info *mdio, + struct ethtool_cmd *ecmd, + u32 npage_adv, u32 npage_lpa); +extern void +mdio45_ethtool_spauseparam_an(const struct mdio_if_info *mdio, + const struct ethtool_pauseparam *ecmd); + +/** + * mdio45_ethtool_gset - get settings for ETHTOOL_GSET + * @mdio: MDIO interface + * @ecmd: Ethtool request structure + * + * Since the CSRs for auto-negotiation using next pages are not fully + * standardised, this function does not attempt to decode them. Use + * mdio45_ethtool_gset_npage() to specify advertisement bits from next + * pages. + */ +static inline void mdio45_ethtool_gset(const struct mdio_if_info *mdio, + struct ethtool_cmd *ecmd) +{ + mdio45_ethtool_gset_npage(mdio, ecmd, 0, 0); +} + +extern int mdio_mii_ioctl(const struct mdio_if_info *mdio, + struct mii_ioctl_data *mii_data, int cmd); + +#endif /* __KERNEL__ */ +#endif /* __LINUX_MDIO_H__ */ diff -r aa133dee7792 drivers/net/sfc/lm87.c --- /dev/null +++ b/drivers/net/sfc/lm87.c @@ -0,0 +1,959 @@ +/* + * lm87.c + * + * Copyright (C) 2000 Frodo Looijaard + * Philip Edelbrock + * Stephen Rousset + * Dan Eaton + * Copyright (C) 2004-2008 Jean Delvare + * + * Original port to Linux 2.6 by Jeff Oliver. + * + * The LM87 is a sensor chip made by National Semiconductor. It monitors up + * to 8 voltages (including its own power source), up to three temperatures + * (its own plus up to two external ones) and up to two fans. The default + * configuration is 6 voltages, two temperatures and two fans (see below). + * Voltages are scaled internally with ratios such that the nominal value of + * each voltage correspond to a register value of 192 (which means a + * resolution of about 0.5% of the nominal value). Temperature values are + * reported with a 1 deg resolution and a 3-4 deg accuracy. Complete + * datasheet can be obtained from National's website at: + * http://www.national.com/pf/LM/LM87.html + * + * Some functions share pins, so not all functions are available at the same + * time. Which are depends on the hardware setup. This driver normally + * assumes that firmware configured the chip correctly. Where this is not + * the case, platform code must set the I2C client's platform_data to point + * to a u8 value to be written to the channel register. + * For reference, here is the list of exclusive functions: + * - in0+in5 (default) or temp3 + * - fan1 (default) or in6 + * - fan2 (default) or in7 + * - VID lines (default) or IRQ lines (not handled by this driver) + * + * The LM87 additionally features an analog output, supposedly usable to + * control the speed of a fan. All new chips use pulse width modulation + * instead. The LM87 is the only hardware monitoring chipset I know of + * which uses amplitude modulation. Be careful when using this feature. + * + * This driver also supports the ADM1024, a sensor chip made by Analog + * Devices. That chip is fully compatible with the LM87. Complete + * datasheet can be obtained from Analog's website at: + * http://www.analog.com/en/prod/0,2877,ADM1024,00.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "kernel_compat.h" +#ifdef EFX_HAVE_HWMON_H +#include +#endif +#ifdef EFX_HAVE_I2C_SENSOR_H +#include +#endif +#ifndef EFX_HAVE_OLD_DEVICE_ATTRIBUTE +#include +#endif +#ifndef EFX_NEED_HWMON_VID +#include +#endif + +#ifdef EFX_NEED_LM87_DRIVER + +/* + * The LM87 registers + */ + +/* nr in 0..5 */ +#define LM87_REG_IN(nr) (0x20 + (nr)) +#define LM87_REG_IN_MAX(nr) (0x2B + (nr) * 2) +#define LM87_REG_IN_MIN(nr) (0x2C + (nr) * 2) +/* nr in 0..1 */ +#define LM87_REG_AIN(nr) (0x28 + (nr)) +#define LM87_REG_AIN_MIN(nr) (0x1A + (nr)) +#define LM87_REG_AIN_MAX(nr) (0x3B + (nr)) + +static u8 LM87_REG_TEMP[3] = { 0x27, 0x26, 0x20 }; +static u8 LM87_REG_TEMP_HIGH[3] = { 0x39, 0x37, 0x2B }; +static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C }; + +#define LM87_REG_TEMP_HW_INT_LOCK 0x13 +#define LM87_REG_TEMP_HW_EXT_LOCK 0x14 +#define LM87_REG_TEMP_HW_INT 0x17 +#define LM87_REG_TEMP_HW_EXT 0x18 + +/* nr in 0..1 */ +#define LM87_REG_FAN(nr) (0x28 + (nr)) +#define LM87_REG_FAN_MIN(nr) (0x3B + (nr)) +#define LM87_REG_AOUT 0x19 + +#define LM87_REG_CONFIG 0x40 +#define LM87_REG_CHANNEL_MODE 0x16 +#define LM87_REG_VID_FAN_DIV 0x47 +#define LM87_REG_VID4 0x49 + +#define LM87_REG_ALARMS1 0x41 +#define LM87_REG_ALARMS2 0x42 + +#define LM87_REG_COMPANY_ID 0x3E +#define LM87_REG_REVISION 0x3F + +/* + * Conversions and various macros + * The LM87 uses signed 8-bit values for temperatures. + */ + +#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192) +#define IN_TO_REG(val,scale) ((val) <= 0 ? 0 : \ + (val) * 192 >= (scale) * 255 ? 255 : \ + ((val) * 192 + (scale)/2) / (scale)) + +#define TEMP_FROM_REG(reg) ((reg) * 1000) +#define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \ + (val) >= 126500 ? 127 : \ + (((val) < 0 ? (val)-500 : (val)+500) / 1000)) + +#define FAN_FROM_REG(reg,div) ((reg) == 255 || (reg) == 0 ? 0 : \ + (1350000 + (reg)*(div) / 2) / ((reg)*(div))) +#define FAN_TO_REG(val,div) ((val)*(div) * 255 <= 1350000 ? 255 : \ + (1350000 + (val)*(div) / 2) / ((val)*(div))) + +#define FAN_DIV_FROM_REG(reg) (1 << (reg)) + +/* analog out is 9.80mV/LSB */ +#define AOUT_FROM_REG(reg) (((reg) * 98 + 5) / 10) +#define AOUT_TO_REG(val) ((val) <= 0 ? 0 : \ + (val) >= 2500 ? 255 : \ + ((val) * 10 + 49) / 98) + +/* nr in 0..1 */ +#define CHAN_NO_FAN(nr) (1 << (nr)) +#define CHAN_TEMP3 (1 << 2) +#define CHAN_VCC_5V (1 << 3) +#define CHAN_NO_VID (1 << 7) + +/* + * Functions declaration + */ + +static void lm87_init_client(struct i2c_client *client); +static int lm87_remove(struct i2c_client *client); +static struct lm87_data *lm87_update_device(struct device *dev); +#ifdef EFX_USE_I2C_LEGACY +static int lm87_detach_client(struct i2c_client *client); +#endif + +/* + * Driver data (common to all clients) + */ + +#if !defined(EFX_USE_I2C_LEGACY) && !defined(EFX_HAVE_OLD_I2C_DRIVER_PROBE) +static const struct i2c_device_id lm87_id[] = { + { "sfc_lm87" }, + { } +}; +#endif + +struct i2c_driver efx_lm87_driver = { +#ifdef EFX_USE_I2C_DRIVER_NAME + .name = "sfc_lm87", +#else + .driver.name = "sfc_lm87", +#endif +#ifdef EFX_USE_I2C_LEGACY + .detach_client = lm87_detach_client, +#else + .probe = efx_lm87_probe, + .remove = lm87_remove, +#ifndef EFX_HAVE_OLD_I2C_DRIVER_PROBE + .id_table = lm87_id, +#endif +#endif +}; + +/* + * Client data (each client gets its own) + */ + +struct lm87_data { +#ifdef EFX_HAVE_HWMON_CLASS_DEVICE + struct class_device *hwmon_dev; +#else + struct device *hwmon_dev; +#endif + struct mutex update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 channel; /* register value */ + u8 config; /* original register value */ + + u8 in[8]; /* register value */ + u8 in_max[8]; /* register value */ + u8 in_min[8]; /* register value */ + u16 in_scale[8]; + + s8 temp[3]; /* register value */ + s8 temp_high[3]; /* register value */ + s8 temp_low[3]; /* register value */ + s8 temp_crit_int; /* min of two register values */ + s8 temp_crit_ext; /* min of two register values */ + + u8 fan[2]; /* register value */ + u8 fan_min[2]; /* register value */ + u8 fan_div[2]; /* register value, shifted right */ + u8 aout; /* register value */ + + u16 alarms; /* register values, combined */ + u8 vid; /* register values, combined */ + u8 vrm; +}; + +/* + * Sysfs stuff + */ + +static inline int lm87_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +#define show_in(offset) \ +static ssize_t show_in##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \ + data->in_scale[offset])); \ +} \ +static ssize_t show_in##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \ + data->in_scale[offset])); \ +} \ +static ssize_t show_in##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \ + data->in_scale[offset])); \ +} \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ + show_in##offset##_input, NULL); +show_in(0); +show_in(1); +show_in(2); +show_in(3); +show_in(4); +show_in(5); +show_in(6); +show_in(7); + +static void set_in_min(struct device *dev, const char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->in_min[nr] = IN_TO_REG(val, data->in_scale[nr]); + lm87_write_value(client, nr<6 ? LM87_REG_IN_MIN(nr) : + LM87_REG_AIN_MIN(nr-6), data->in_min[nr]); + mutex_unlock(&data->update_lock); +} + +static void set_in_max(struct device *dev, const char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->in_max[nr] = IN_TO_REG(val, data->in_scale[nr]); + lm87_write_value(client, nr<6 ? LM87_REG_IN_MAX(nr) : + LM87_REG_AIN_MAX(nr-6), data->in_max[nr]); + mutex_unlock(&data->update_lock); +} + +#define set_in(offset) \ +static ssize_t set_in##offset##_min(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + set_in_min(dev, buf, offset); \ + return count; \ +} \ +static ssize_t set_in##offset##_max(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + set_in_max(dev, buf, offset); \ + return count; \ +} \ +static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in##offset##_min, set_in##offset##_min); \ +static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in##offset##_max, set_in##offset##_max); +set_in(0); +set_in(1); +set_in(2); +set_in(3); +set_in(4); +set_in(5); +set_in(6); +set_in(7); + +#define show_temp(offset) \ +static ssize_t show_temp##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \ +} \ +static ssize_t show_temp##offset##_low(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[offset-1])); \ +} \ +static ssize_t show_temp##offset##_high(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[offset-1])); \ +}\ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ + show_temp##offset##_input, NULL); +show_temp(1); +show_temp(2); +show_temp(3); + +static void set_temp_low(struct device *dev, const char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->temp_low[nr] = TEMP_TO_REG(val); + lm87_write_value(client, LM87_REG_TEMP_LOW[nr], data->temp_low[nr]); + mutex_unlock(&data->update_lock); +} + +static void set_temp_high(struct device *dev, const char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->temp_high[nr] = TEMP_TO_REG(val); + lm87_write_value(client, LM87_REG_TEMP_HIGH[nr], data->temp_high[nr]); + mutex_unlock(&data->update_lock); +} + +#define set_temp(offset) \ +static ssize_t set_temp##offset##_low(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + set_temp_low(dev, buf, offset-1); \ + return count; \ +} \ +static ssize_t set_temp##offset##_high(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + set_temp_high(dev, buf, offset-1); \ + return count; \ +} \ +static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ + show_temp##offset##_high, set_temp##offset##_high); \ +static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ + show_temp##offset##_low, set_temp##offset##_low); +set_temp(1); +set_temp(2); +set_temp(3); + +static ssize_t show_temp_crit_int(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_int)); +} + +static ssize_t show_temp_crit_ext(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_ext)); +} + +static DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit_int, NULL); +static DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit_ext, NULL); +static DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit_ext, NULL); + +#define show_fan(offset) \ +static ssize_t show_fan##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[offset-1], \ + FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \ +} \ +static ssize_t show_fan##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[offset-1], \ + FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \ +} \ +static ssize_t show_fan##offset##_div(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[offset-1])); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ + show_fan##offset##_input, NULL); +show_fan(1); +show_fan(2); + +static void set_fan_min(struct device *dev, const char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->fan_min[nr] = FAN_TO_REG(val, + FAN_DIV_FROM_REG(data->fan_div[nr])); + lm87_write_value(client, LM87_REG_FAN_MIN(nr), data->fan_min[nr]); + mutex_unlock(&data->update_lock); +} + +/* Note: we save and restore the fan minimum here, because its value is + determined in part by the fan clock divider. This follows the principle + of least surprise; the user doesn't expect the fan minimum to change just + because the divider changed. */ +static ssize_t set_fan_div(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + unsigned long min; + u8 reg; + + mutex_lock(&data->update_lock); + min = FAN_FROM_REG(data->fan_min[nr], + FAN_DIV_FROM_REG(data->fan_div[nr])); + + switch (val) { + case 1: data->fan_div[nr] = 0; break; + case 2: data->fan_div[nr] = 1; break; + case 4: data->fan_div[nr] = 2; break; + case 8: data->fan_div[nr] = 3; break; + default: + mutex_unlock(&data->update_lock); + return -EINVAL; + } + + reg = lm87_read_value(client, LM87_REG_VID_FAN_DIV); + switch (nr) { + case 0: + reg = (reg & 0xCF) | (data->fan_div[0] << 4); + break; + case 1: + reg = (reg & 0x3F) | (data->fan_div[1] << 6); + break; + } + lm87_write_value(client, LM87_REG_VID_FAN_DIV, reg); + + data->fan_min[nr] = FAN_TO_REG(min, val); + lm87_write_value(client, LM87_REG_FAN_MIN(nr), + data->fan_min[nr]); + mutex_unlock(&data->update_lock); + + return count; +} + +#define set_fan(offset) \ +static ssize_t set_fan##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + set_fan_min(dev, buf, offset-1); \ + return count; \ +} \ +static ssize_t set_fan##offset##_div(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return set_fan_div(dev, buf, count, offset-1); \ +} \ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan##offset##_min, set_fan##offset##_min); \ +static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ + show_fan##offset##_div, set_fan##offset##_div); +set_fan(1); +set_fan(2); + +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + return sprintf(buf, "%d\n", data->alarms); +} +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); +} +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + +static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", data->vrm); +} +static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct lm87_data *data = dev_get_drvdata(dev); + data->vrm = simple_strtoul(buf, NULL, 10); + return count; +} +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); + +static ssize_t show_aout(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout)); +} +static ssize_t set_aout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->aout = AOUT_TO_REG(val); + lm87_write_value(client, LM87_REG_AOUT, data->aout); + mutex_unlock(&data->update_lock); + return count; +} +static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout); + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + int bitnr = to_sensor_dev_attr(attr)->index; + return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); +} +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7); +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 14); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 15); + +/* + * Real code + */ + +static struct attribute *lm87_attributes[] = { + &dev_attr_in1_input.attr, + &dev_attr_in1_min.attr, + &dev_attr_in1_max.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, + &dev_attr_in2_input.attr, + &dev_attr_in2_min.attr, + &dev_attr_in2_max.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &dev_attr_in3_input.attr, + &dev_attr_in3_min.attr, + &dev_attr_in3_max.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &dev_attr_in4_input.attr, + &dev_attr_in4_min.attr, + &dev_attr_in4_max.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + + &dev_attr_temp1_input.attr, + &dev_attr_temp1_max.attr, + &dev_attr_temp1_min.attr, + &dev_attr_temp1_crit.attr, + &sensor_dev_attr_temp1_alarm.dev_attr.attr, + &dev_attr_temp2_input.attr, + &dev_attr_temp2_max.attr, + &dev_attr_temp2_min.attr, + &dev_attr_temp2_crit.attr, + &sensor_dev_attr_temp2_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + + &dev_attr_alarms.attr, + &dev_attr_aout_output.attr, + + NULL +}; + +static const struct attribute_group lm87_group = { + .attrs = lm87_attributes, +}; + +static struct attribute *lm87_attributes_opt[] = { + &dev_attr_in6_input.attr, + &dev_attr_in6_min.attr, + &dev_attr_in6_max.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, + + &dev_attr_fan1_input.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan1_div.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + + &dev_attr_in7_input.attr, + &dev_attr_in7_min.attr, + &dev_attr_in7_max.attr, + &sensor_dev_attr_in7_alarm.dev_attr.attr, + + &dev_attr_fan2_input.attr, + &dev_attr_fan2_min.attr, + &dev_attr_fan2_div.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + + &dev_attr_temp3_input.attr, + &dev_attr_temp3_max.attr, + &dev_attr_temp3_min.attr, + &dev_attr_temp3_crit.attr, + &sensor_dev_attr_temp3_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + + &dev_attr_in0_input.attr, + &dev_attr_in0_min.attr, + &dev_attr_in0_max.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + &dev_attr_in5_input.attr, + &dev_attr_in5_min.attr, + &dev_attr_in5_max.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + + &dev_attr_cpu0_vid.attr, + &dev_attr_vrm.attr, + + NULL +}; + +static const struct attribute_group lm87_group_opt = { + .attrs = lm87_attributes_opt, +}; + +#ifdef EFX_HAVE_OLD_I2C_DRIVER_PROBE +int efx_lm87_probe(struct i2c_client *new_client) +#else +int efx_lm87_probe(struct i2c_client *new_client, + const struct i2c_device_id *id) +#endif +{ + struct lm87_data *data; + int err; + + data = kzalloc(sizeof(struct lm87_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(new_client, data); + data->valid = 0; + mutex_init(&data->update_lock); + + /* Initialize the LM87 chip */ + lm87_init_client(new_client); + + data->in_scale[0] = 2500; + data->in_scale[1] = 2700; + data->in_scale[2] = (data->channel & CHAN_VCC_5V) ? 5000 : 3300; + data->in_scale[3] = 5000; + data->in_scale[4] = 12000; + data->in_scale[5] = 2700; + data->in_scale[6] = 1875; + data->in_scale[7] = 1875; + + /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &lm87_group))) + goto exit_free; + + if (data->channel & CHAN_NO_FAN(0)) { + if ((err = device_create_file(&new_client->dev, + &dev_attr_in6_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in6_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in6_max)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_in6_alarm.dev_attr))) + goto exit_remove; + } else { + if ((err = device_create_file(&new_client->dev, + &dev_attr_fan1_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan1_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan1_div)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan1_alarm.dev_attr))) + goto exit_remove; + } + + if (data->channel & CHAN_NO_FAN(1)) { + if ((err = device_create_file(&new_client->dev, + &dev_attr_in7_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in7_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in7_max)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_in7_alarm.dev_attr))) + goto exit_remove; + } else { + if ((err = device_create_file(&new_client->dev, + &dev_attr_fan2_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan2_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_fan2_div)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_fan2_alarm.dev_attr))) + goto exit_remove; + } + + if (data->channel & CHAN_TEMP3) { + if ((err = device_create_file(&new_client->dev, + &dev_attr_temp3_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp3_max)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp3_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_temp3_crit)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_temp3_alarm.dev_attr)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_temp3_fault.dev_attr))) + goto exit_remove; + } else { + if ((err = device_create_file(&new_client->dev, + &dev_attr_in0_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in0_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in0_max)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_in0_alarm.dev_attr)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in5_input)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in5_min)) + || (err = device_create_file(&new_client->dev, + &dev_attr_in5_max)) + || (err = device_create_file(&new_client->dev, + &sensor_dev_attr_in5_alarm.dev_attr))) + goto exit_remove; + } + + if (!(data->channel & CHAN_NO_VID)) { + data->vrm = vid_which_vrm(); + if ((err = device_create_file(&new_client->dev, + &dev_attr_cpu0_vid)) + || (err = device_create_file(&new_client->dev, + &dev_attr_vrm))) + goto exit_remove; + } + + data->hwmon_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + sysfs_remove_group(&new_client->dev.kobj, &lm87_group); + sysfs_remove_group(&new_client->dev.kobj, &lm87_group_opt); +exit_free: + lm87_write_value(new_client, LM87_REG_CONFIG, data->config); + kfree(data); +exit: + return err; +} + +static void lm87_init_client(struct i2c_client *client) +{ + struct lm87_data *data = i2c_get_clientdata(client); + + if (client->dev.platform_data) { + data->channel = *(u8 *)client->dev.platform_data; + lm87_write_value(client, + LM87_REG_CHANNEL_MODE, data->channel); + } else { + data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + } + data->config = lm87_read_value(client, LM87_REG_CONFIG); + + if (!(data->config & 0x01)) { + int i; + + /* Limits are left uninitialized after power-up */ + for (i = 1; i < 6; i++) { + lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00); + lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF); + } + for (i = 0; i < 2; i++) { + lm87_write_value(client, LM87_REG_TEMP_HIGH[i], 0x7F); + lm87_write_value(client, LM87_REG_TEMP_LOW[i], 0x00); + lm87_write_value(client, LM87_REG_AIN_MIN(i), 0x00); + lm87_write_value(client, LM87_REG_AIN_MAX(i), 0xFF); + } + if (data->channel & CHAN_TEMP3) { + lm87_write_value(client, LM87_REG_TEMP_HIGH[2], 0x7F); + lm87_write_value(client, LM87_REG_TEMP_LOW[2], 0x00); + } else { + lm87_write_value(client, LM87_REG_IN_MIN(0), 0x00); + lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF); + } + } + + /* Make sure Start is set and INT#_Clear is clear */ + if ((data->config & 0x09) != 0x01) + lm87_write_value(client, LM87_REG_CONFIG, + (data->config & 0x77) | 0x01); +} + +static int lm87_remove(struct i2c_client *client) +{ + struct lm87_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &lm87_group); + sysfs_remove_group(&client->dev.kobj, &lm87_group_opt); + + lm87_write_value(client, LM87_REG_CONFIG, data->config); + kfree(data); + return 0; +} + +#ifdef EFX_USE_I2C_LEGACY +static int lm87_detach_client(struct i2c_client *client) +{ + int err; + + lm87_remove(client); + err = i2c_detach_client(client); + if (!err) + kfree(client); + return err; +} +#endif + +static struct lm87_data *lm87_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + int i, j; + + dev_dbg(&client->dev, "Updating data.\n"); + + i = (data->channel & CHAN_TEMP3) ? 1 : 0; + j = (data->channel & CHAN_TEMP3) ? 5 : 6; + for (; i < j; i++) { + data->in[i] = lm87_read_value(client, + LM87_REG_IN(i)); + data->in_min[i] = lm87_read_value(client, + LM87_REG_IN_MIN(i)); + data->in_max[i] = lm87_read_value(client, + LM87_REG_IN_MAX(i)); + } + + for (i = 0; i < 2; i++) { + if (data->channel & CHAN_NO_FAN(i)) { + data->in[6+i] = lm87_read_value(client, + LM87_REG_AIN(i)); + data->in_max[6+i] = lm87_read_value(client, + LM87_REG_AIN_MAX(i)); + data->in_min[6+i] = lm87_read_value(client, + LM87_REG_AIN_MIN(i)); + + } else { + data->fan[i] = lm87_read_value(client, + LM87_REG_FAN(i)); + data->fan_min[i] = lm87_read_value(client, + LM87_REG_FAN_MIN(i)); + } + } + + j = (data->channel & CHAN_TEMP3) ? 3 : 2; + for (i = 0 ; i < j; i++) { + data->temp[i] = lm87_read_value(client, + LM87_REG_TEMP[i]); + data->temp_high[i] = lm87_read_value(client, + LM87_REG_TEMP_HIGH[i]); + data->temp_low[i] = lm87_read_value(client, + LM87_REG_TEMP_LOW[i]); + } + + i = lm87_read_value(client, LM87_REG_TEMP_HW_INT_LOCK); + j = lm87_read_value(client, LM87_REG_TEMP_HW_INT); + data->temp_crit_int = min(i, j); + + i = lm87_read_value(client, LM87_REG_TEMP_HW_EXT_LOCK); + j = lm87_read_value(client, LM87_REG_TEMP_HW_EXT); + data->temp_crit_ext = min(i, j); + + i = lm87_read_value(client, LM87_REG_VID_FAN_DIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + data->vid = (i & 0x0F) + | (lm87_read_value(client, LM87_REG_VID4) & 0x01) + << 4; + + data->alarms = lm87_read_value(client, LM87_REG_ALARMS1) + | (lm87_read_value(client, LM87_REG_ALARMS2) + << 8); + data->aout = lm87_read_value(client, LM87_REG_AOUT); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +#endif /* EFX_NEED_LM87_DRIVER */ diff -r aa133dee7792 drivers/net/sfc/lm87_support.c --- a/drivers/net/sfc/lm87_support.c +++ /dev/null @@ -1,295 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2007: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Developed by Solarflare Communications - * - * 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; -} diff -r aa133dee7792 drivers/net/sfc/lm87_support.h --- a/drivers/net/sfc/lm87_support.h +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2007: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Developed by Solarflare Communications - * - * 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 */ diff -r aa133dee7792 drivers/net/sfc/lm90.c --- /dev/null +++ b/drivers/net/sfc/lm90.c @@ -0,0 +1,829 @@ +/* + * lm90.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2003-2008 Jean Delvare + * + * Based on the lm83 driver. The LM90 is a sensor chip made by National + * Semiconductor. It reports up to two temperatures (its own plus up to + * one external one) with a 0.125 deg resolution (1 deg for local + * temperature) and a 3-4 deg accuracy. + * + * This driver also supports the LM89 and LM99, two other sensor chips + * made by National Semiconductor. Both have an increased remote + * temperature measurement accuracy (1 degree), and the LM99 + * additionally shifts remote temperatures (measured and limits) by 16 + * degrees, which allows for higher temperatures measurement. The + * driver doesn't handle it since it can be done easily in user-space. + * Note that there is no way to differentiate between both chips. + * + * This driver also supports the LM86, another sensor chip made by + * National Semiconductor. It is exactly similar to the LM90 except it + * has a higher accuracy. + * + * This driver also supports the ADM1032, a sensor chip made by Analog + * Devices. That chip is similar to the LM90, with a few differences + * that are not handled by this driver. Among others, it has a higher + * accuracy than the LM90, much like the LM86 does. + * + * This driver also supports the MAX6657, MAX6658 and MAX6659 sensor + * chips made by Maxim. These chips are similar to the LM86. + * Note that there is no easy way to differentiate between the three + * variants. The extra address and features of the MAX6659 are not + * supported by this driver. These chips lack the remote temperature + * offset feature. + * + * This driver also supports the MAX6646, MAX6647 and MAX6649 chips + * made by Maxim. These are again similar to the LM86, but they use + * unsigned temperature values and can report temperatures from 0 to + * 145 degrees. + * + * This driver also supports the MAX6680 and MAX6681, two other sensor + * chips made by Maxim. These are quite similar to the other Maxim + * chips. The MAX6680 and MAX6681 only differ in the pinout so they can + * be treated identically. + * + * This driver also supports the ADT7461 chip from Analog Devices. + * It's supported in both compatibility and extended mode. It is mostly + * compatible with LM90 except for a data format difference for the + * temperature value registers. + * + * Since the LM90 was the first chipset supported by this driver, most + * comments will refer to this chipset, but are actually general and + * concern all supported chipsets, unless mentioned otherwise. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "kernel_compat.h" +#ifndef EFX_HAVE_OLD_DEVICE_ATTRIBUTE +#include +#endif +#ifdef EFX_HAVE_HWMON_H +#include +#endif +#ifdef EFX_HAVE_I2C_SENSOR_H +#include +#endif + +#ifdef EFX_NEED_LM90_DRIVER + +enum { lm90 = 1, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646 }; + +/* + * The LM90 registers + */ + +#define LM90_REG_R_MAN_ID 0xFE +#define LM90_REG_R_CHIP_ID 0xFF +#define LM90_REG_R_CONFIG1 0x03 +#define LM90_REG_W_CONFIG1 0x09 +#define LM90_REG_R_CONFIG2 0xBF +#define LM90_REG_W_CONFIG2 0xBF +#define LM90_REG_R_CONVRATE 0x04 +#define LM90_REG_W_CONVRATE 0x0A +#define LM90_REG_R_STATUS 0x02 +#define LM90_REG_R_LOCAL_TEMP 0x00 +#define LM90_REG_R_LOCAL_HIGH 0x05 +#define LM90_REG_W_LOCAL_HIGH 0x0B +#define LM90_REG_R_LOCAL_LOW 0x06 +#define LM90_REG_W_LOCAL_LOW 0x0C +#define LM90_REG_R_LOCAL_CRIT 0x20 +#define LM90_REG_W_LOCAL_CRIT 0x20 +#define LM90_REG_R_REMOTE_TEMPH 0x01 +#define LM90_REG_R_REMOTE_TEMPL 0x10 +#define LM90_REG_R_REMOTE_OFFSH 0x11 +#define LM90_REG_W_REMOTE_OFFSH 0x11 +#define LM90_REG_R_REMOTE_OFFSL 0x12 +#define LM90_REG_W_REMOTE_OFFSL 0x12 +#define LM90_REG_R_REMOTE_HIGHH 0x07 +#define LM90_REG_W_REMOTE_HIGHH 0x0D +#define LM90_REG_R_REMOTE_HIGHL 0x13 +#define LM90_REG_W_REMOTE_HIGHL 0x13 +#define LM90_REG_R_REMOTE_LOWH 0x08 +#define LM90_REG_W_REMOTE_LOWH 0x0E +#define LM90_REG_R_REMOTE_LOWL 0x14 +#define LM90_REG_W_REMOTE_LOWL 0x14 +#define LM90_REG_R_REMOTE_CRIT 0x19 +#define LM90_REG_W_REMOTE_CRIT 0x19 +#define LM90_REG_R_TCRIT_HYST 0x21 +#define LM90_REG_W_TCRIT_HYST 0x21 + +/* MAX6646/6647/6649/6657/6658/6659 registers */ + +#define MAX6657_REG_R_LOCAL_TEMPL 0x11 + +/* + * Device flags + */ +#define LM90_FLAG_ADT7461_EXT 0x01 /* ADT7461 extended mode */ + +/* + * Functions declaration + */ + +static void lm90_init_client(struct i2c_client *client); +static int lm90_remove(struct i2c_client *client); +#ifdef EFX_USE_I2C_LEGACY +static int lm90_detach_client(struct i2c_client *client); +#endif +static struct lm90_data *lm90_update_device(struct device *dev); + +/* + * Driver data (common to all clients) + */ + +#if !defined(EFX_USE_I2C_LEGACY) && !defined(EFX_HAVE_OLD_I2C_DRIVER_PROBE) +static const struct i2c_device_id lm90_id[] = { + { "sfc_max6647" }, + { } +}; +#endif + +struct i2c_driver efx_lm90_driver = { +#ifdef EFX_USE_I2C_DRIVER_NAME + .name = "sfc_lm90", +#else + .driver.name = "sfc_lm90", +#endif +#ifdef EFX_USE_I2C_LEGACY + .detach_client = lm90_detach_client, +#else + .probe = efx_lm90_probe, + .remove = lm90_remove, +#ifndef EFX_HAVE_OLD_I2C_DRIVER_PROBE + .id_table = lm90_id, +#endif +#endif +}; + +/* + * Client data (each client gets its own) + */ + +struct lm90_data { +#ifdef EFX_HAVE_HWMON_CLASS_DEVICE + struct class_device *hwmon_dev; +#else + struct device *hwmon_dev; +#endif + struct mutex update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + int kind; + int flags; + + /* registers values */ + s8 temp8[4]; /* 0: local low limit + 1: local high limit + 2: local critical limit + 3: remote critical limit */ + s16 temp11[5]; /* 0: remote input + 1: remote low limit + 2: remote high limit + 3: remote offset (except max6646 and max6657) + 4: local input */ + u8 temp_hyst; + u8 alarms; /* bitvector */ +}; + +/* + * Conversions + * For local temperatures and limits, critical limits and the hysteresis + * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. + * For remote temperatures and limits, it uses signed 11-bit values with + * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. Some + * Maxim chips use unsigned values. + */ + +static inline int temp_from_s8(s8 val) +{ + return val * 1000; +} + +static inline int temp_from_u8(u8 val) +{ + return val * 1000; +} + +static inline int temp_from_s16(s16 val) +{ + return val / 32 * 125; +} + +static inline int temp_from_u16(u16 val) +{ + return val / 32 * 125; +} + +static s8 temp_to_s8(long val) +{ + if (val <= -128000) + return -128; + if (val >= 127000) + return 127; + if (val < 0) + return (val - 500) / 1000; + return (val + 500) / 1000; +} + +static u8 temp_to_u8(long val) +{ + if (val <= 0) + return 0; + if (val >= 255000) + return 255; + return (val + 500) / 1000; +} + +static s16 temp_to_s16(long val) +{ + if (val <= -128000) + return 0x8000; + if (val >= 127875) + return 0x7FE0; + if (val < 0) + return (val - 62) / 125 * 32; + return (val + 62) / 125 * 32; +} + +static u8 hyst_to_reg(long val) +{ + if (val <= 0) + return 0; + if (val >= 30500) + return 31; + return (val + 500) / 1000; +} + +/* + * ADT7461 in compatibility mode is almost identical to LM90 except that + * attempts to write values that are outside the range 0 < temp < 127 are + * treated as the boundary value. + * + * ADT7461 in "extended mode" operation uses unsigned integers offset by + * 64 (e.g., 0 -> -64 degC). The range is restricted to -64..191 degC. + */ +static inline int temp_from_u8_adt7461(struct lm90_data *data, u8 val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) + return (val - 64) * 1000; + else + return temp_from_s8(val); +} + +static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) + return (val - 0x4000) / 64 * 250; + else + return temp_from_s16(val); +} + +static u8 temp_to_u8_adt7461(struct lm90_data *data, long val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) { + if (val <= -64000) + return 0; + if (val >= 191000) + return 0xFF; + return (val + 500 + 64000) / 1000; + } else { + if (val <= 0) + return 0; + if (val >= 127000) + return 127; + return (val + 500) / 1000; + } +} + +static u16 temp_to_u16_adt7461(struct lm90_data *data, long val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) { + if (val <= -64000) + return 0; + if (val >= 191750) + return 0xFFC0; + return (val + 64000 + 125) / 250 * 64; + } else { + if (val <= 0) + return 0; + if (val >= 127750) + return 0x7FC0; + return (val + 125) / 250 * 64; + } +} + +/* + * Sysfs stuff + */ + +static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm90_data *data = lm90_update_device(dev); + int temp; + + if (data->kind == adt7461) + temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); + else if (data->kind == max6646) + temp = temp_from_u8(data->temp8[attr->index]); + else + temp = temp_from_s8(data->temp8[attr->index]); + + return sprintf(buf, "%d\n", temp); +} + +static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + static const u8 reg[4] = { + LM90_REG_W_LOCAL_LOW, + LM90_REG_W_LOCAL_HIGH, + LM90_REG_W_LOCAL_CRIT, + LM90_REG_W_REMOTE_CRIT, + }; + + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct lm90_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + int nr = attr->index; + + mutex_lock(&data->update_lock); + if (data->kind == adt7461) + data->temp8[nr] = temp_to_u8_adt7461(data, val); + else if (data->kind == max6646) + data->temp8[nr] = temp_to_u8(val); + else + data->temp8[nr] = temp_to_s8(val); + i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm90_data *data = lm90_update_device(dev); + int temp; + + if (data->kind == adt7461) + temp = temp_from_u16_adt7461(data, data->temp11[attr->index]); + else if (data->kind == max6646) + temp = temp_from_u16(data->temp11[attr->index]); + else + temp = temp_from_s16(data->temp11[attr->index]); + + return sprintf(buf, "%d\n", temp); +} + +static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + static const u8 reg[6] = { + LM90_REG_W_REMOTE_LOWH, + LM90_REG_W_REMOTE_LOWL, + LM90_REG_W_REMOTE_HIGHH, + LM90_REG_W_REMOTE_HIGHL, + LM90_REG_W_REMOTE_OFFSH, + LM90_REG_W_REMOTE_OFFSL, + }; + + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct lm90_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + int nr = attr->index; + + mutex_lock(&data->update_lock); + if (data->kind == adt7461) + data->temp11[nr] = temp_to_u16_adt7461(data, val); + else if (data->kind == max6657 || data->kind == max6680) + data->temp11[nr] = temp_to_s8(val) << 8; + else if (data->kind == max6646) + data->temp11[nr] = temp_to_u8(val) << 8; + else + data->temp11[nr] = temp_to_s16(val); + + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], + data->temp11[nr] >> 8); + if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646) + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], + data->temp11[nr] & 0xff); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_temphyst(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm90_data *data = lm90_update_device(dev); + int temp; + + if (data->kind == adt7461) + temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); + else + temp = temp_from_s8(data->temp8[attr->index]); + + return sprintf(buf, "%d\n", temp - temp_from_s8(data->temp_hyst)); +} + +static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm90_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + long hyst; + + mutex_lock(&data->update_lock); + hyst = temp_from_s8(data->temp8[2]) - val; + i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, + hyst_to_reg(hyst)); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, + char *buf) +{ + struct lm90_data *data = lm90_update_device(dev); + return sprintf(buf, "%d\n", data->alarms); +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm90_data *data = lm90_update_device(dev); + int bitnr = attr->index; + + return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp11, NULL, 4); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 0); +static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 1); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 1); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 2); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 2); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 3); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, + set_temphyst, 2); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 3); +static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 3); + +/* Individual alarm files */ +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6); +/* Raw alarm file for compatibility */ +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +static struct attribute *lm90_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, + + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &dev_attr_alarms.attr, + NULL +}; + +static const struct attribute_group lm90_group = { + .attrs = lm90_attributes, +}; + +/* pec used for ADM1032 only */ +static ssize_t show_pec(struct device *dev, struct device_attribute *dummy, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC)); +} + +static ssize_t set_pec(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + long val = simple_strtol(buf, NULL, 10); + + switch (val) { + case 0: + client->flags &= ~I2C_CLIENT_PEC; + break; + case 1: + client->flags |= I2C_CLIENT_PEC; + break; + default: + return -EINVAL; + } + + return count; +} + +static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec); + +/* + * Real code + */ + +/* The ADM1032 supports PEC but not on write byte transactions, so we need + to explicitly ask for a transaction without PEC. */ +static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value) +{ + return i2c_smbus_xfer(client->adapter, client->addr, + client->flags & ~I2C_CLIENT_PEC, + I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); +} + +/* It is assumed that client->update_lock is held (unless we are in + detection or initialization steps). This matters when PEC is enabled, + because we don't want the address pointer to change between the write + byte and the read byte transactions. */ +static int lm90_read_reg(struct i2c_client* client, u8 reg, u8 *value) +{ + int err; + + if (client->flags & I2C_CLIENT_PEC) { + err = adm1032_write_byte(client, reg); + if (err >= 0) + err = i2c_smbus_read_byte(client); + } else + err = i2c_smbus_read_byte_data(client, reg); + + if (err < 0) { + dev_warn(&client->dev, "Register %#02x read failed (%d)\n", + reg, err); + return err; + } + *value = err; + + return 0; +} + +#ifdef EFX_HAVE_OLD_I2C_DRIVER_PROBE +int efx_lm90_probe(struct i2c_client *new_client) +#else +int efx_lm90_probe(struct i2c_client *new_client, + const struct i2c_device_id *id) +#endif +{ + struct lm90_data *data; + int err; + + data = kzalloc(sizeof(struct lm90_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + i2c_set_clientdata(new_client, data); + mutex_init(&data->update_lock); + + /* Set the device type */ + data->kind = max6646; + + /* Initialize the LM90 chip */ + lm90_init_client(new_client); + + /* Register sysfs hooks */ + if ((err = sysfs_create_group(&new_client->dev.kobj, &lm90_group))) + goto exit_free; + if (new_client->flags & I2C_CLIENT_PEC) { + if ((err = device_create_file(&new_client->dev, + &dev_attr_pec))) + goto exit_remove_files; + } + if (data->kind != max6657 && data->kind != max6646) { + if ((err = device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_offset.dev_attr))) + goto exit_remove_files; + } + + data->hwmon_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + return 0; + +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &lm90_group); + device_remove_file(&new_client->dev, &dev_attr_pec); +exit_free: + kfree(data); +exit: + return err; +} + +static void lm90_init_client(struct i2c_client *client) +{ + u8 config, config_orig; + struct lm90_data *data = i2c_get_clientdata(client); + + /* + * Start the conversions. + */ + i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, + 5); /* 2 Hz */ + if (lm90_read_reg(client, LM90_REG_R_CONFIG1, &config) < 0) { + dev_warn(&client->dev, "Initialization failed!\n"); + return; + } + config_orig = config; + + /* Check Temperature Range Select */ + if (data->kind == adt7461) { + if (config & 0x04) + data->flags |= LM90_FLAG_ADT7461_EXT; + } + + /* + * Put MAX6680/MAX8881 into extended resolution (bit 0x10, + * 0.125 degree resolution) and range (0x08, extend range + * to -64 degree) mode for the remote temperature sensor. + */ + if (data->kind == max6680) { + config |= 0x18; + } + + config &= 0xBF; /* run */ + if (config != config_orig) /* Only write if changed */ + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config); +} + +static int lm90_remove(struct i2c_client *client) +{ + struct lm90_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &lm90_group); + device_remove_file(&client->dev, &dev_attr_pec); + if (data->kind != max6657 && data->kind != max6646) + device_remove_file(&client->dev, + &sensor_dev_attr_temp2_offset.dev_attr); + + kfree(data); + return 0; +} + +#ifdef EFX_USE_I2C_LEGACY +static int lm90_detach_client(struct i2c_client *client) +{ + int err; + + lm90_remove(client); + err = i2c_detach_client(client); + if (!err) + kfree(client); + return err; +} +#endif + +static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value) +{ + int err; + u8 oldh, newh, l; + + /* + * There is a trick here. We have to read two registers to have the + * sensor temperature, but we have to beware a conversion could occur + * inbetween the readings. The datasheet says we should either use + * the one-shot conversion register, which we don't want to do + * (disables hardware monitoring) or monitor the busy bit, which is + * impossible (we can't read the values and monitor that bit at the + * exact same time). So the solution used here is to read the high + * byte once, then the low byte, then the high byte again. If the new + * high byte matches the old one, then we have a valid reading. Else + * we have to read the low byte again, and now we believe we have a + * correct reading. + */ + if ((err = lm90_read_reg(client, regh, &oldh)) + || (err = lm90_read_reg(client, regl, &l)) + || (err = lm90_read_reg(client, regh, &newh))) + return err; + if (oldh != newh) { + err = lm90_read_reg(client, regl, &l); + if (err) + return err; + } + *value = (newh << 8) | l; + + return 0; +} + +static struct lm90_data *lm90_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm90_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { + u8 h, l; + + dev_dbg(&client->dev, "Updating lm90 data.\n"); + lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]); + lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]); + lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]); + lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]); + lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); + + if (data->kind == max6657 || data->kind == max6646) { + lm90_read16(client, LM90_REG_R_LOCAL_TEMP, + MAX6657_REG_R_LOCAL_TEMPL, + &data->temp11[4]); + } else { + if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, + &h) == 0) + data->temp11[4] = h << 8; + } + lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, + LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); + + if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { + data->temp11[1] = h << 8; + if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646 + && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, + &l) == 0) + data->temp11[1] |= l; + } + if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { + data->temp11[2] = h << 8; + if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646 + && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, + &l) == 0) + data->temp11[2] |= l; + } + + if (data->kind != max6657 && data->kind != max6646) { + if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, + &h) == 0 + && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, + &l) == 0) + data->temp11[3] = (h << 8) | l; + } + lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +#endif /* EFX_NEED_LM90_DRIVER */ diff -r aa133dee7792 drivers/net/sfc/mac.h --- a/drivers/net/sfc/mac.h +++ b/drivers/net/sfc/mac.h @@ -1,28 +1,11 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_MAC_H @@ -30,9 +13,11 @@ #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; +extern struct efx_mac_operations efx_mcdi_mac_operations; +extern void falcon_reconfigure_xmac_core(struct efx_nic *efx); +extern int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr, + u32 dma_len, int enable, int clear); #endif diff -r aa133dee7792 drivers/net/sfc/mcdi.c --- /dev/null +++ b/drivers/net/sfc/mcdi.c @@ -0,0 +1,1178 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2008-2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include "net_driver.h" +#include "nic.h" +#include "io.h" +#include "regs.h" +#include "mcdi_pcol.h" +#include "phy.h" + +/************************************************************************** + * + * Management-Controller-to-Driver Interface + * + ************************************************************************** + */ + +/* Software-defined structure to the shared-memory */ +#define CMD_NOTIFY_PORT0 0 +#define CMD_NOTIFY_PORT1 4 +#define CMD_PDU_PORT0 0x008 +#define CMD_PDU_PORT1 0x108 +#define REBOOT_FLAG_PORT0 0x3f8 +#define REBOOT_FLAG_PORT1 0x3fc + +#define MCDI_RPC_TIMEOUT 10 /*seconds */ + +#define MCDI_PDU(efx) \ + (efx_port_num(efx) ? CMD_PDU_PORT1 : CMD_PDU_PORT0) +#define MCDI_DOORBELL(efx) \ + (efx_port_num(efx) ? CMD_NOTIFY_PORT1 : CMD_NOTIFY_PORT0) +#define MCDI_REBOOT_FLAG(efx) \ + (efx_port_num(efx) ? REBOOT_FLAG_PORT1 : REBOOT_FLAG_PORT0) + +#define SEQ_MASK \ + EFX_MASK32(EFX_WIDTH(MCDI_HEADER_SEQ)) + +static inline struct efx_mcdi_iface *efx_mcdi(struct efx_nic *efx) +{ + struct siena_nic_data *nic_data; + EFX_BUG_ON_PARANOID(efx_nic_rev(efx) < EFX_REV_SIENA_A0); + nic_data = efx->nic_data; + return &nic_data->mcdi; +} + +void efx_mcdi_init(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi; + + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return; + + mcdi = efx_mcdi(efx); + init_waitqueue_head(&mcdi->wq); + spin_lock_init(&mcdi->iface_lock); + atomic_set(&mcdi->state, MCDI_STATE_QUIESCENT); + mcdi->mode = MCDI_MODE_POLL; + + (void) efx_mcdi_poll_reboot(efx); +} + +static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd, + const u8 *inbuf, size_t inlen) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); + unsigned doorbell = FR_CZ_MC_TREG_SMEM + MCDI_DOORBELL(efx); + unsigned int i; + efx_dword_t hdr; + u32 xflags, seqno; + + BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT); + BUG_ON(inlen & 3 || inlen >= 0x100); + + seqno = mcdi->seqno & SEQ_MASK; + xflags = 0; + if (mcdi->mode == MCDI_MODE_EVENTS) + xflags |= MCDI_HEADER_XFLAGS_EVREQ; + + EFX_POPULATE_DWORD_6(hdr, + MCDI_HEADER_RESPONSE, 0, + MCDI_HEADER_RESYNC, 1, + MCDI_HEADER_CODE, cmd, + MCDI_HEADER_DATALEN, inlen, + MCDI_HEADER_SEQ, seqno, + MCDI_HEADER_XFLAGS, xflags); + + efx_writed(efx, &hdr, pdu); + + for (i = 0; i < inlen; i += 4) + _efx_writed(efx, *((__le32 *)(inbuf + i)), pdu + 4 + i); + + /* Ensure the payload is written out before the header */ + wmb(); + + /* ring the doorbell with a distinctive value */ + _efx_writed(efx, (__force __le32) 0x45789abc, doorbell); +} + +static void efx_mcdi_copyout(struct efx_nic *efx, u8 *outbuf, size_t outlen) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); + int i; + + BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT); + BUG_ON(outlen & 3 || outlen >= 0x100); + + for (i = 0; i < outlen; i += 4) + *((__le32 *)(outbuf + i)) = _efx_readd(efx, pdu + 4 + i); +} + +static int efx_mcdi_poll(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + unsigned int time, finish; + unsigned int respseq, respcmd, error; + unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); + unsigned int rc, spins; + efx_dword_t reg; + + /* Check for a reboot atomically with respect to efx_mcdi_copyout() */ + rc = efx_mcdi_poll_reboot(efx); + if (rc) + goto out; + + /* Poll for completion. Poll quickly (once a us) for the 1st jiffy, + * because generally mcdi responses are fast. After that, back off + * and poll once a jiffy (approximately) + */ + spins = TICK_USEC; + finish = get_seconds() + MCDI_RPC_TIMEOUT; + + while (1) { + if (spins != 0) { + --spins; + udelay(1); + } else + schedule(); + + time = get_seconds(); + + rmb(); + efx_readd(efx, ®, pdu); + + /* All 1's indicates that shared memory is in reset (and is + * not a valid header). Wait for it to come out reset before + * completing the command */ + if (EFX_DWORD_FIELD(reg, EFX_DWORD_0) != 0xffffffff && + EFX_DWORD_FIELD(reg, MCDI_HEADER_RESPONSE)) + break; + + if (time >= finish) + return -ETIMEDOUT; + } + + mcdi->resplen = EFX_DWORD_FIELD(reg, MCDI_HEADER_DATALEN); + respseq = EFX_DWORD_FIELD(reg, MCDI_HEADER_SEQ); + respcmd = EFX_DWORD_FIELD(reg, MCDI_HEADER_CODE); + error = EFX_DWORD_FIELD(reg, MCDI_HEADER_ERROR); + + if (error && mcdi->resplen == 0) { + EFX_ERR(efx, "MC rebooted\n"); + rc = EIO; + } else if ((respseq ^ mcdi->seqno) & SEQ_MASK) { + EFX_ERR(efx, "MC response mismatch tx seq 0x%x rx seq 0x%x\n", + respseq, mcdi->seqno); + rc = EIO; + } else if (error) { + efx_readd(efx, ®, pdu + 4); + switch (EFX_DWORD_FIELD(reg, EFX_DWORD_0)) { +#define TRANSLATE_ERROR(name) \ + case MC_CMD_ERR_ ## name: \ + rc = name; \ + break + TRANSLATE_ERROR(ENOENT); + TRANSLATE_ERROR(EINTR); + TRANSLATE_ERROR(EACCES); + TRANSLATE_ERROR(EBUSY); + TRANSLATE_ERROR(EINVAL); + TRANSLATE_ERROR(EDEADLK); + TRANSLATE_ERROR(ENOSYS); + TRANSLATE_ERROR(ETIME); +#undef TRANSLATE_ERROR + default: + rc = EIO; + break; + } + } else + rc = 0; + +out: + mcdi->resprc = rc; + if (rc) + mcdi->resplen = 0; + + /* Return rc=0 like wait_event_timeout() */ + return 0; +} + +/* Test and clear MC-rebooted flag for this port/function */ +int efx_mcdi_poll_reboot(struct efx_nic *efx) +{ + unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_REBOOT_FLAG(efx); + efx_dword_t reg; + uint32_t value; + + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return false; + + efx_readd(efx, ®, addr); + value = EFX_DWORD_FIELD(reg, EFX_DWORD_0); + + if (value == 0) + return 0; + + EFX_ZERO_DWORD(reg); + efx_writed(efx, ®, addr); + + if (value == MC_STATUS_DWORD_ASSERT) + return -EINTR; + else + return -EIO; +} + +static void efx_mcdi_acquire(struct efx_mcdi_iface *mcdi) +{ + /* Wait until the interface becomes QUIESCENT and we win the race + * to mark it RUNNING. */ + wait_event(mcdi->wq, + atomic_cmpxchg(&mcdi->state, + MCDI_STATE_QUIESCENT, + MCDI_STATE_RUNNING) + == MCDI_STATE_QUIESCENT); +} + +static int efx_mcdi_await_completion(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + + if (wait_event_timeout( + mcdi->wq, + atomic_read(&mcdi->state) == MCDI_STATE_COMPLETED, + msecs_to_jiffies(MCDI_RPC_TIMEOUT * 1000)) == 0) + return -ETIMEDOUT; + + /* Check if efx_mcdi_set_mode() switched us back to polled completions. + * In which case, poll for completions directly. If efx_mcdi_ev_cpl() + * completed the request first, then we'll just end up completing the + * request again, which is safe. + * + * We need an smp_rmb() to synchronise with efx_mcdi_mode_poll(), which + * wait_event_timeout() implicitly provides. + */ + if (mcdi->mode == MCDI_MODE_POLL) + return efx_mcdi_poll(efx); + + return 0; +} + +static bool efx_mcdi_complete(struct efx_mcdi_iface *mcdi) +{ + /* If the interface is RUNNING, then move to COMPLETED and wake any + * waiters. If the interface isn't in RUNNING then we've received a + * duplicate completion after we've already transitioned back to + * QUIESCENT. [A subsequent invocation would increment seqno, so would + * have failed the seqno check]. + */ + if (atomic_cmpxchg(&mcdi->state, + MCDI_STATE_RUNNING, + MCDI_STATE_COMPLETED) == MCDI_STATE_RUNNING) { + wake_up(&mcdi->wq); + return true; + } + + return false; +} + +static void efx_mcdi_release(struct efx_mcdi_iface *mcdi) +{ + atomic_set(&mcdi->state, MCDI_STATE_QUIESCENT); + wake_up(&mcdi->wq); +} + +static void efx_mcdi_ev_cpl(struct efx_nic *efx, unsigned int seqno, + unsigned int datalen, unsigned int errno) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + bool wake = false; + + spin_lock(&mcdi->iface_lock); + + if ((seqno ^ mcdi->seqno) & SEQ_MASK) { + if (mcdi->credits) + /* The request has been cancelled */ + --mcdi->credits; + else + EFX_ERR(efx, "MC response mismatch tx seq 0x%x rx " + "seq 0x%x\n", seqno, mcdi->seqno); + } else { + mcdi->resprc = errno; + mcdi->resplen = datalen; + + wake = true; + } + + spin_unlock(&mcdi->iface_lock); + + if (wake) + efx_mcdi_complete(mcdi); +} + +/* Issue the given command by writing the data into the shared memory PDU, + * ring the doorbell and wait for completion. Copyout the result. */ +int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, + const u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen, + size_t *outlen_actual) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + int rc; + BUG_ON(efx_nic_rev(efx) < EFX_REV_SIENA_A0); + + efx_mcdi_acquire(mcdi); + + /* Serialise with efx_mcdi_ev_cpl() and efx_mcdi_ev_death() */ + spin_lock_bh(&mcdi->iface_lock); + ++mcdi->seqno; + spin_unlock_bh(&mcdi->iface_lock); + + efx_mcdi_copyin(efx, cmd, inbuf, inlen); + + if (mcdi->mode == MCDI_MODE_POLL) + rc = efx_mcdi_poll(efx); + else + rc = efx_mcdi_await_completion(efx); + + if (rc != 0) { + /* Close the race with efx_mcdi_ev_cpl() executing just too late + * and completing a request we've just cancelled, by ensuring + * that the seqno check therein fails. + */ + spin_lock_bh(&mcdi->iface_lock); + ++mcdi->seqno; + ++mcdi->credits; + spin_unlock_bh(&mcdi->iface_lock); + + EFX_ERR(efx, "MC command 0x%x inlen %d mode %d timed out\n", + cmd, (int)inlen, mcdi->mode); + } else { + size_t resplen; + + /* At the very least we need a memory barrier here to ensure + * we pick up changes from efx_mcdi_ev_cpl(). Protect against + * a spurious efx_mcdi_ev_cpl() running concurrently by + * acquiring the iface_lock. */ + spin_lock_bh(&mcdi->iface_lock); + rc = -mcdi->resprc; + resplen = mcdi->resplen; + spin_unlock_bh(&mcdi->iface_lock); + + if (rc == 0) { + efx_mcdi_copyout(efx, outbuf, + min(outlen, mcdi->resplen + 3) & ~0x3); + if (outlen_actual != NULL) + *outlen_actual = resplen; + } else if (cmd == MC_CMD_REBOOT && rc == -EIO) + ; /* Don't reset if MC_CMD_REBOOT returns EIO */ + else if (rc == -EIO || rc == -EINTR) { + EFX_ERR(efx, "MC fatal error %d\n", -rc); + efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE); + } else + EFX_ERR(efx, "MC command 0x%x inlen %d failed rc=%d\n", + cmd, (int)inlen, -rc); + } + + efx_mcdi_release(mcdi); + return rc; +} + +void efx_mcdi_mode_poll(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi; + + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return; + + mcdi = efx_mcdi(efx); + if (mcdi->mode == MCDI_MODE_POLL) + return; + + /* We can switch from event completion to polled completion, because + * mcdi requests are always completed in shared memory. We do this by + * switching the mode to POLL'd then completing the request. + * efx_mcdi_await_completion() will then call efx_mcdi_poll(). + * + * We need an smp_wmb() to synchronise with efx_mcdi_await_completion(), + * which efx_mcdi_complete() provides for us. + */ + mcdi->mode = MCDI_MODE_POLL; + + efx_mcdi_complete(mcdi); +} + +void efx_mcdi_mode_event(struct efx_nic *efx) +{ + struct efx_mcdi_iface *mcdi; + + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return; + + mcdi = efx_mcdi(efx); + + if (mcdi->mode == MCDI_MODE_EVENTS) + return; + + /* We can't switch from polled to event completion in the middle of a + * request, because the completion method is specified in the request. + * So acquire the interface to serialise the requestors. We don't need + * to acquire the iface_lock to change the mode here, but we do need a + * write memory barrier ensure that efx_mcdi_rpc() sees it, which + * efx_mcdi_acquire() provides. + */ + efx_mcdi_acquire(mcdi); + mcdi->mode = MCDI_MODE_EVENTS; + efx_mcdi_release(mcdi); +} + +static void efx_mcdi_ev_death(struct efx_nic *efx, int rc) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + + /* If there is an outstanding MCDI request, it has been terminated + * either by a BADASSERT or REBOOT event. If the mcdi interface is + * in polled mode, then do nothing because the MC reboot handler will + * set the header correctly. However, if the mcdi interface is waiting + * for a CMDDONE event it won't receive it [and since all MCDI events + * are sent to the same queue, we can't be racing with + * efx_mcdi_ev_cpl()] + * + * There's a race here with efx_mcdi_rpc(), because we might receive + * a REBOOT event *before* the request has been copied out. In polled + * mode (during startup) this is irrelevent, because efx_mcdi_complete() + * is ignored. In event mode, this condition is just an edge-case of + * receiving a REBOOT event after posting the MCDI request. Did the mc + * reboot before or after the copyout? The best we can do always is + * just return failure. + */ + spin_lock(&mcdi->iface_lock); + if (efx_mcdi_complete(mcdi)) { + if (mcdi->mode == MCDI_MODE_EVENTS) { + mcdi->resprc = rc; + mcdi->resplen = 0; + } + } else + /* Nobody was waiting for an MCDI request, so trigger a reset */ + efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE); + + spin_unlock(&mcdi->iface_lock); +} + +static unsigned int efx_mcdi_event_link_speed[] = { + [MCDI_EVENT_LINKCHANGE_SPEED_100M] = 100, + [MCDI_EVENT_LINKCHANGE_SPEED_1G] = 1000, + [MCDI_EVENT_LINKCHANGE_SPEED_10G] = 10000, +}; + + +static void efx_mcdi_process_link_change(struct efx_nic *efx, efx_qword_t *ev) +{ + u32 flags, fcntl, speed, lpa; + + speed = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_SPEED); + EFX_BUG_ON_PARANOID(speed >= ARRAY_SIZE(efx_mcdi_event_link_speed)); + speed = efx_mcdi_event_link_speed[speed]; + + flags = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LINK_FLAGS); + fcntl = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_FCNTL); + lpa = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LP_CAP); + + /* efx->link_state is only modified by efx_mcdi_phy_get_link(), + * which is only run after flushing the event queues. Therefore, it + * is safe to modify the link state outside of the mac_lock here. + */ + efx_mcdi_phy_decode_link(efx, &efx->link_state, speed, flags, fcntl); + + efx_mcdi_phy_check_fcntl(efx, lpa); + + efx_link_status_changed(efx); +} + +static const char *sensor_names[] = { + [MC_CMD_SENSOR_CONTROLLER_TEMP] = "Controller temp. sensor", + [MC_CMD_SENSOR_PHY_COMMON_TEMP] = "PHY shared temp. sensor", + [MC_CMD_SENSOR_CONTROLLER_COOLING] = "Controller cooling", + [MC_CMD_SENSOR_PHY0_TEMP] = "PHY 0 temp. sensor", + [MC_CMD_SENSOR_PHY0_COOLING] = "PHY 0 cooling", + [MC_CMD_SENSOR_PHY1_TEMP] = "PHY 1 temp. sensor", + [MC_CMD_SENSOR_PHY1_COOLING] = "PHY 1 cooling", + [MC_CMD_SENSOR_IN_1V0] = "1.0V supply sensor", + [MC_CMD_SENSOR_IN_1V2] = "1.2V supply sensor", + [MC_CMD_SENSOR_IN_1V8] = "1.8V supply sensor", + [MC_CMD_SENSOR_IN_2V5] = "2.5V supply sensor", + [MC_CMD_SENSOR_IN_3V3] = "3.3V supply sensor", + [MC_CMD_SENSOR_IN_12V0] = "12V supply sensor" +}; + +static const char *sensor_status_names[] = { + [MC_CMD_SENSOR_STATE_OK] = "OK", + [MC_CMD_SENSOR_STATE_WARNING] = "Warning", + [MC_CMD_SENSOR_STATE_FATAL] = "Fatal", + [MC_CMD_SENSOR_STATE_BROKEN] = "Device failure", +}; + +static void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev) +{ + unsigned int monitor, state, value; + const char *name, *state_txt; + monitor = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_MONITOR); + state = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_STATE); + value = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_VALUE); + /* Deal gracefully with the board having more drivers than we + * know about, but do not expect new sensor states. */ + name = (monitor >= ARRAY_SIZE(sensor_names)) + ? "No sensor name available" : + sensor_names[monitor]; + EFX_BUG_ON_PARANOID(state >= ARRAY_SIZE(sensor_status_names)); + state_txt = sensor_status_names[state]; + + EFX_ERR(efx, "Sensor %d (%s) reports condition '%s' for raw value %d\n", + monitor, name, state_txt, value); +} + +/* Called from falcon_process_eventq for MCDI events */ +void efx_mcdi_process_event(struct efx_channel *channel, + efx_qword_t *event) +{ + struct efx_nic *efx = channel->efx; + int code = EFX_QWORD_FIELD(*event, MCDI_EVENT_CODE); + u32 data = EFX_QWORD_FIELD(*event, MCDI_EVENT_DATA); + + switch (code) { + case MCDI_EVENT_CODE_BADSSERT: + EFX_ERR(efx, "MC watchdog or assertion failure at 0x%x\n", data); + efx_mcdi_ev_death(efx, EINTR); + break; + + case MCDI_EVENT_CODE_PMNOTICE: + EFX_INFO(efx, "MCDI PM event.\n"); + break; + + case MCDI_EVENT_CODE_CMDDONE: + efx_mcdi_ev_cpl(efx, + MCDI_EVENT_FIELD(*event, CMDDONE_SEQ), + MCDI_EVENT_FIELD(*event, CMDDONE_DATALEN), + MCDI_EVENT_FIELD(*event, CMDDONE_ERRNO)); + break; + + case MCDI_EVENT_CODE_LINKCHANGE: + efx_mcdi_process_link_change(efx, event); + break; + case MCDI_EVENT_CODE_SENSOREVT: + efx_mcdi_sensor_event(efx, event); + break; + case MCDI_EVENT_CODE_SCHEDERR: + EFX_INFO(efx, "MC Scheduler error address=0x%x\n", data); + break; + case MCDI_EVENT_CODE_REBOOT: + EFX_INFO(efx, "MC Reboot\n"); + efx_mcdi_ev_death(efx, EIO); + break; + case MCDI_EVENT_CODE_MAC_STATS_DMA: + /* MAC stats are gather lazily. We can ignore this. */ + break; + + default: + EFX_ERR(efx, "Unknown MCDI event 0x%x\n", code); + } +} + +/************************************************************************** + * + * Specific request functions + * + ************************************************************************** + */ + +int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build) +{ + u8 outbuf[ALIGN(MC_CMD_GET_VERSION_V1_OUT_LEN, 4)]; + size_t outlength; + const __le16 *ver_words; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_VERSION_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_VERSION, NULL, 0, + outbuf, sizeof(outbuf), &outlength); + if (rc) + goto fail; + + if (outlength == MC_CMD_GET_VERSION_V0_OUT_LEN) { + *version = 0; + *build = MCDI_DWORD(outbuf, GET_VERSION_OUT_FIRMWARE); + return 0; + } + + if (outlength < MC_CMD_GET_VERSION_V1_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + ver_words = (__le16 *)MCDI_PTR(outbuf, GET_VERSION_OUT_VERSION); + *version = (((u64)le16_to_cpu(ver_words[0]) << 48) | + ((u64)le16_to_cpu(ver_words[1]) << 32) | + ((u64)le16_to_cpu(ver_words[2]) << 16) | + le16_to_cpu(ver_words[3])); + *build = MCDI_DWORD(outbuf, GET_VERSION_OUT_FIRMWARE); + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, + bool *was_attached) +{ + u8 inbuf[MC_CMD_DRV_ATTACH_IN_LEN]; + u8 outbuf[MC_CMD_DRV_ATTACH_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_NEW_STATE, + driver_operating ? 1 : 0); + MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_UPDATE, 1); + + rc = efx_mcdi_rpc(efx, MC_CMD_DRV_ATTACH, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + if (outlen < MC_CMD_DRV_ATTACH_OUT_LEN) + goto fail; + + if (was_attached != NULL) + *was_attached = MCDI_DWORD(outbuf, DRV_ATTACH_OUT_OLD_STATE); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address, + u16 *fw_subtype_list) +{ + uint8_t outbuf[MC_CMD_GET_BOARD_CFG_OUT_LEN]; + size_t outlen; + int port_num = efx_port_num(efx); + int offset; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_BOARD_CFG, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_GET_BOARD_CFG_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + offset = (port_num) + ? MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_OFST + : MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_OFST; + if (mac_address) + memcpy(mac_address, outbuf + offset, ETH_ALEN); + if (fw_subtype_list) + memcpy(fw_subtype_list, + outbuf + MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST, + MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN); + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d len=%d\n", __func__, rc, (int)outlen); + + return rc; +} + +int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, u32 dest_evq) +{ + u8 inbuf[MC_CMD_LOG_CTRL_IN_LEN]; + u32 dest = 0; + int rc; + + if (uart) + dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_UART; + if (evq) + dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ; + + MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST, dest); + MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST_EVQ, dest_evq); + + BUILD_BUG_ON(MC_CMD_LOG_CTRL_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_LOG_CTRL, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out) +{ + u8 outbuf[MC_CMD_NVRAM_TYPES_OUT_LEN]; + size_t outlen; + int rc; + + BUILD_BUG_ON(MC_CMD_NVRAM_TYPES_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_TYPES, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + if (outlen < MC_CMD_NVRAM_TYPES_OUT_LEN) + goto fail; + + *nvram_types_out = MCDI_DWORD(outbuf, NVRAM_TYPES_OUT_TYPES); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", + __func__, rc); + return rc; +} + +int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type, + size_t *size_out, size_t *erase_size_out, + bool *protected_out) +{ + u8 inbuf[MC_CMD_NVRAM_INFO_IN_LEN]; + u8 outbuf[MC_CMD_NVRAM_INFO_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_INFO_IN_TYPE, type); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_INFO, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + if (outlen < MC_CMD_NVRAM_INFO_OUT_LEN) + goto fail; + + *size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_SIZE); + *erase_size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_ERASESIZE); + *protected_out = !!(MCDI_DWORD(outbuf, NVRAM_INFO_OUT_FLAGS) & + (1 << MC_CMD_NVRAM_PROTECTED_LBN)); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type) +{ + u8 inbuf[MC_CMD_NVRAM_UPDATE_START_IN_LEN]; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_START_IN_TYPE, type); + + BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_START_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_START, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type, + loff_t offset, u8 *buffer, size_t length) +{ + u8 inbuf[MC_CMD_NVRAM_READ_IN_LEN]; + u8 outbuf[MC_CMD_NVRAM_READ_OUT_LEN(length)]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_TYPE, type); + MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_OFFSET, offset); + MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_LENGTH, length); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_READ, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + memcpy(buffer, MCDI_PTR(outbuf, NVRAM_READ_OUT_READ_BUFFER), length); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type, + loff_t offset, const u8 *buffer, size_t length) +{ + u8 inbuf[MC_CMD_NVRAM_WRITE_IN_LEN(length)]; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type); + MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset); + MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length); + memcpy(MCDI_PTR(inbuf, NVRAM_WRITE_IN_WRITE_BUFFER), buffer, length); + + BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type, + loff_t offset, size_t length) +{ + u8 inbuf[MC_CMD_NVRAM_ERASE_IN_LEN]; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_TYPE, type); + MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_OFFSET, offset); + MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_LENGTH, length); + + BUILD_BUG_ON(MC_CMD_NVRAM_ERASE_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_ERASE, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type) +{ + u8 inbuf[MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN]; + u32 reboot; + int rc; + + /* Reboot PHY's into the new firmware. mcfw reboot is handled + * explicity via ethtool. */ + reboot = (type == MC_CMD_NVRAM_TYPE_PHY_PORT0 || + type == MC_CMD_NVRAM_TYPE_PHY_PORT1 || + type == MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO); + MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type); + MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_REBOOT, reboot); + + BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +static int efx_mcdi_nvram_test(struct efx_nic *efx, unsigned int type) +{ + u8 inbuf[MC_CMD_NVRAM_TEST_IN_LEN]; + u8 outbuf[MC_CMD_NVRAM_TEST_OUT_LEN]; + int rc; + + MCDI_SET_DWORD(inbuf, NVRAM_TEST_IN_TYPE, type); + + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_TEST, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), NULL); + if (rc) + return rc; + + switch (MCDI_DWORD(outbuf, NVRAM_TEST_OUT_RESULT)) { + case MC_CMD_NVRAM_TEST_PASS: + case MC_CMD_NVRAM_TEST_NOTSUPP: + return 0; + default: + return -EIO; + } +} + +int efx_mcdi_nvram_test_all(struct efx_nic *efx) +{ + u32 nvram_types; + unsigned int type; + int rc; + + rc = efx_mcdi_nvram_types(efx, &nvram_types); + if (rc) + return rc; + + type = 0; + while (nvram_types != 0) { + if (nvram_types & 1) { + rc = efx_mcdi_nvram_test(efx, type); + if (rc) + return rc; + } + type++; + nvram_types >>= 1; + } + + return 0; +} + +static int efx_mcdi_read_assertion(struct efx_nic *efx) +{ + u8 inbuf[MC_CMD_GET_ASSERTS_IN_LEN]; + u8 outbuf[MC_CMD_GET_ASSERTS_OUT_LEN]; + unsigned int flags, index, ofst; + const char *reason; + size_t outlen; + int retry; + int rc; + + /* Attempt to read any stored assertion state before we reboot + * the mcfw out of the assertion handler. Retry twice, once + * because a boot-time assertion might cause this command to fail + * with EINTR. And once again because GET_ASSERTS can race with + * MC_CMD_REBOOT running on the other port. */ + retry = 2; + do { + MCDI_SET_DWORD(inbuf, GET_ASSERTS_IN_CLEAR, 1); + rc = efx_mcdi_rpc(efx, MC_CMD_GET_ASSERTS, + inbuf, MC_CMD_GET_ASSERTS_IN_LEN, + outbuf, sizeof(outbuf), &outlen); + } while ((rc == -EINTR || rc == -EIO) && retry-- > 0); + + if (rc) + return rc; + if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN) + return -EINVAL; + + /* Print out any recorded assertion state */ + flags = MCDI_DWORD(outbuf, GET_ASSERTS_OUT_GLOBAL_FLAGS); + if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS) + return 0; + + reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL) + ? "system-level assertion" + : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL) + ? "thread-level assertion" + : (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED) + ? "watchdog reset" + : "unknown assertion"; + EFX_ERR(efx, "MCPU %s at PC = 0x%.8x in thread 0x%.8x\n", reason, + MCDI_DWORD(outbuf, GET_ASSERTS_OUT_SAVED_PC_OFFS), + MCDI_DWORD(outbuf, GET_ASSERTS_OUT_THREAD_OFFS)); + + /* Print out the registers */ + ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST; + for (index = 1; index < 32; index++) { + EFX_ERR(efx, "R%.2d (?): 0x%.8x\n", index, + MCDI_DWORD2(outbuf, ofst)); + ofst += sizeof(efx_dword_t); + } + + return 0; +} + +static void efx_mcdi_exit_assertion(struct efx_nic *efx) +{ + u8 inbuf[MC_CMD_REBOOT_IN_LEN]; + + /* Atomically reboot the mcfw out of the assertion handler */ + BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0); + MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS, + MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION); + efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf, MC_CMD_REBOOT_IN_LEN, + NULL, 0, NULL); +} + +int efx_mcdi_handle_assertion(struct efx_nic *efx) +{ + int rc; + + rc = efx_mcdi_read_assertion(efx); + if (rc) + return rc; + + efx_mcdi_exit_assertion(efx); + + return 0; +} + +void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) +{ + u8 inbuf[MC_CMD_SET_ID_LED_IN_LEN]; + int rc; + + BUILD_BUG_ON(EFX_LED_OFF != MC_CMD_LED_OFF); + BUILD_BUG_ON(EFX_LED_ON != MC_CMD_LED_ON); + BUILD_BUG_ON(EFX_LED_DEFAULT != MC_CMD_LED_DEFAULT); + + BUILD_BUG_ON(MC_CMD_SET_ID_LED_OUT_LEN != 0); + + MCDI_SET_DWORD(inbuf, SET_ID_LED_IN_STATE, mode); + + rc = efx_mcdi_rpc(efx, MC_CMD_SET_ID_LED, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); +} + +int efx_mcdi_reset_port(struct efx_nic *efx) +{ + int rc = efx_mcdi_rpc(efx, MC_CMD_PORT_RESET, NULL, 0, NULL, 0, NULL); + if (rc) + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_reset_mc(struct efx_nic *efx) +{ + u8 inbuf[MC_CMD_REBOOT_IN_LEN]; + int rc; + + BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0); + MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS, 0); + rc = efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf, sizeof(inbuf), + NULL, 0, NULL); + /* White is black, and up is down */ + if (rc == -EIO) + return 0; + if (rc == 0) + rc = -EIO; + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type, + const u8 *mac, int *id_out) +{ + u8 inbuf[MC_CMD_WOL_FILTER_SET_IN_LEN]; + u8 outbuf[MC_CMD_WOL_FILTER_SET_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_WOL_TYPE, type); + MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_FILTER_MODE, + MC_CMD_FILTER_MODE_SIMPLE); + memcpy(MCDI_PTR(inbuf, WOL_FILTER_SET_IN_MAGIC_MAC), mac, ETH_ALEN); + + rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_SET, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_WOL_FILTER_SET_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + *id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_SET_OUT_FILTER_ID); + + return 0; + +fail: + *id_out = -1; + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; + +} + + +int +efx_mcdi_wol_filter_set_magic(struct efx_nic *efx, const u8 *mac, int *id_out) +{ + return efx_mcdi_wol_filter_set(efx, MC_CMD_WOL_TYPE_MAGIC, mac, id_out); +} + + +int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out) +{ + u8 outbuf[MC_CMD_WOL_FILTER_GET_OUT_LEN]; + size_t outlen; + int rc; + + rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_GET, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_WOL_FILTER_GET_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + *id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_GET_OUT_FILTER_ID); + + return 0; + +fail: + *id_out = -1; + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + + +int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id) +{ + u8 inbuf[MC_CMD_WOL_FILTER_REMOVE_IN_LEN]; + int rc; + + MCDI_SET_DWORD(inbuf, WOL_FILTER_REMOVE_IN_FILTER_ID, (u32)id); + + rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_REMOVE, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + + +int efx_mcdi_wol_filter_reset(struct efx_nic *efx) +{ + int rc; + + rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_RESET, NULL, 0, NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + diff -r aa133dee7792 drivers/net/sfc/mcdi.h --- /dev/null +++ b/drivers/net/sfc/mcdi.h @@ -0,0 +1,131 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2008-2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_MCDI_H +#define EFX_MCDI_H + +/** + * enum efx_mcdi_state + * @MCDI_STATE_QUIESCENT: No pending MCDI requests. If the caller holds the + * mcdi_lock then they are able to move to MCDI_STATE_RUNNING + * @MCDI_STATE_RUNNING: There is an MCDI request pending. Only the thread that + * moved into this state is allowed to move out of it. + * @MCDI_STATE_COMPLETED: An MCDI request has completed, but the owning thread + * has not yet consumed the result. For all other threads, equivalent to + * MCDI_STATE_RUNNING. + */ +enum efx_mcdi_state { + MCDI_STATE_QUIESCENT, + MCDI_STATE_RUNNING, + MCDI_STATE_COMPLETED, +}; + +enum efx_mcdi_mode { + MCDI_MODE_POLL, + MCDI_MODE_EVENTS, +}; + +/** + * struct efx_mcdi_iface + * @state: Interface state. Waited for by mcdi_wq. + * @wq: Wait queue for threads waiting for state != STATE_RUNNING + * @iface_lock: Protects @credits, @seqno, @resprc, @resplen + * @mode: Poll for mcdi completion, or wait for an mcdi_event. + * Serialised by @lock + * @seqno: The next sequence number to use for mcdi requests. + * Serialised by @lock + * @credits: Number of spurious MCDI completion events allowed before we + * trigger a fatal error. Protected by @lock + * @resprc: Returned MCDI completion + * @resplen: Returned payload length + */ +struct efx_mcdi_iface { + atomic_t state; + wait_queue_head_t wq; + spinlock_t iface_lock; + enum efx_mcdi_mode mode; + unsigned int credits; + unsigned int seqno; + unsigned int resprc; + size_t resplen; +}; + +extern void efx_mcdi_init(struct efx_nic *efx); + +extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const u8 *inbuf, + size_t inlen, u8 *outbuf, size_t outlen, + size_t *outlen_actual); + +extern int efx_mcdi_poll_reboot(struct efx_nic *efx); +extern void efx_mcdi_mode_poll(struct efx_nic *efx); +extern void efx_mcdi_mode_event(struct efx_nic *efx); + +extern void efx_mcdi_process_event(struct efx_channel *channel, + efx_qword_t *event); + +#define MCDI_PTR2(_buf, _ofst) \ + (((u8 *)_buf) + _ofst) +#define MCDI_SET_DWORD2(_buf, _ofst, _value) \ + EFX_POPULATE_DWORD_1(*((efx_dword_t *)MCDI_PTR2(_buf, _ofst)), \ + EFX_DWORD_0, _value) +#define MCDI_DWORD2(_buf, _ofst) \ + EFX_DWORD_FIELD(*((efx_dword_t *)MCDI_PTR2(_buf, _ofst)), \ + EFX_DWORD_0) +#define MCDI_QWORD2(_buf, _ofst) \ + EFX_QWORD_FIELD64(*((efx_qword_t *)MCDI_PTR2(_buf, _ofst)), \ + EFX_QWORD_0) + +#define MCDI_PTR(_buf, _ofst) \ + MCDI_PTR2(_buf, MC_CMD_ ## _ofst ## _OFST) +#define MCDI_SET_DWORD(_buf, _ofst, _value) \ + MCDI_SET_DWORD2(_buf, MC_CMD_ ## _ofst ## _OFST, _value) +#define MCDI_DWORD(_buf, _ofst) \ + MCDI_DWORD2(_buf, MC_CMD_ ## _ofst ## _OFST) +#define MCDI_QWORD(_buf, _ofst) \ + MCDI_QWORD2(_buf, MC_CMD_ ## _ofst ## _OFST) + +#define MCDI_EVENT_FIELD(_ev, _field) \ + EFX_QWORD_FIELD(_ev, MCDI_EVENT_ ## _field) + +extern int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build); +extern int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, + bool *was_attached_out); +extern int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address, + u16 *fw_subtype_list); +extern int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, + u32 dest_evq); +extern int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out); +extern int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type, + size_t *size_out, size_t *erase_size_out, + bool *protected_out); +extern int efx_mcdi_nvram_update_start(struct efx_nic *efx, + unsigned int type); +extern int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type, + loff_t offset, u8 *buffer, size_t length); +extern int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type, + loff_t offset, const u8 *buffer, + size_t length); +extern int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type, + loff_t offset, size_t length); +extern int efx_mcdi_nvram_update_finish(struct efx_nic *efx, + unsigned int type); +extern int efx_mcdi_nvram_test_all(struct efx_nic *efx); +extern int efx_mcdi_handle_assertion(struct efx_nic *efx); +extern void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); +extern int efx_mcdi_reset_port(struct efx_nic *efx); +extern int efx_mcdi_reset_mc(struct efx_nic *efx); +extern int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type, + const u8 *mac, int *id_out); +extern int efx_mcdi_wol_filter_set_magic(struct efx_nic *efx, + const u8 *mac, int *id_out); +extern int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out); +extern int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id); +extern int efx_mcdi_wol_filter_reset(struct efx_nic *efx); + +#endif /* EFX_MCDI_H */ diff -r aa133dee7792 drivers/net/sfc/mcdi_mac.c --- /dev/null +++ b/drivers/net/sfc/mcdi_mac.c @@ -0,0 +1,152 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "net_driver.h" +#include "efx.h" +#include "mac.h" +#include "mcdi.h" +#include "mcdi_pcol.h" + +static int efx_mcdi_set_mac(struct efx_nic *efx) +{ + u32 reject, fcntl; + u8 cmdbytes[MC_CMD_SET_MAC_IN_LEN]; + + memcpy(cmdbytes + MC_CMD_SET_MAC_IN_ADDR_OFST, + efx->net_dev->dev_addr, ETH_ALEN); + + MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_MTU, + EFX_MAX_FRAME_LEN(efx->net_dev->mtu)); + MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_DRAIN, 0); + + /* The MCDI command provides for controlling accept/reject + * of broadcast packets too, but the driver doesn't currently + * expose this. */ + reject = (efx->promiscuous) ? 0 : + (1 << MC_CMD_SET_MAC_IN_REJECT_UNCST_LBN); + MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_REJECT, reject); + + switch (efx->wanted_fc) { + case EFX_FC_RX | EFX_FC_TX: + fcntl = MC_CMD_FCNTL_BIDIR; + break; + case EFX_FC_RX: + fcntl = MC_CMD_FCNTL_RESPOND; + break; + default: + fcntl = MC_CMD_FCNTL_OFF; + break; + } + if (efx->wanted_fc & EFX_FC_AUTO) + fcntl = MC_CMD_FCNTL_AUTO; + + MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_FCNTL, fcntl); + + return efx_mcdi_rpc(efx, MC_CMD_SET_MAC, cmdbytes, sizeof(cmdbytes), + NULL, 0, NULL); +} + +static int efx_mcdi_get_mac_faults(struct efx_nic *efx, u32 *faults) +{ + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + size_t outlength; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), &outlength); + if (rc) + goto fail; + + *faults = MCDI_DWORD(outbuf, GET_LINK_OUT_MAC_FAULT); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", + __func__, rc); + return rc; +} + +int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr, + u32 dma_len, int enable, int clear) +{ + u8 inbuf[MC_CMD_MAC_STATS_IN_LEN]; + int rc; + efx_dword_t *cmd_ptr; + int period = 1000; + u32 addr_hi; + u32 addr_lo; + + BUILD_BUG_ON(MC_CMD_MAC_STATS_OUT_LEN != 0); + + addr_lo = ((u64)dma_addr) >> 0; + addr_hi = ((u64)dma_addr) >> 32; + + MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_ADDR_LO, addr_lo); + MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_ADDR_HI, addr_hi); + cmd_ptr = (efx_dword_t *)MCDI_PTR(inbuf, MAC_STATS_IN_CMD); + if (enable) + EFX_POPULATE_DWORD_6(*cmd_ptr, + MC_CMD_MAC_STATS_CMD_DMA, 1, + MC_CMD_MAC_STATS_CMD_CLEAR, clear, + MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE, 1, + MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE, 1, + MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR, 0, + MC_CMD_MAC_STATS_CMD_PERIOD_MS, period); + else + EFX_POPULATE_DWORD_5(*cmd_ptr, + MC_CMD_MAC_STATS_CMD_DMA, 0, + MC_CMD_MAC_STATS_CMD_CLEAR, clear, + MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE, 1, + MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE, 0, + MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR, 0); + MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_LEN, dma_len); + + rc = efx_mcdi_rpc(efx, MC_CMD_MAC_STATS, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: %s failed rc=%d\n", + __func__, enable ? "enable" : "disable", rc); + return rc; +} + +static int efx_mcdi_mac_reconfigure(struct efx_nic *efx) +{ + int rc; + + rc = efx_mcdi_set_mac(efx); + if (rc != 0) + return rc; + + /* Restore the multicast hash registers. */ + efx->type->push_multicast_hash(efx); + + return 0; +} + + +static bool efx_mcdi_mac_check_fault(struct efx_nic *efx) +{ + u32 faults; + int rc = efx_mcdi_get_mac_faults(efx, &faults); + return (rc != 0) || (faults != 0); +} + + +struct efx_mac_operations efx_mcdi_mac_operations = { + .reconfigure = efx_mcdi_mac_reconfigure, + .update_stats = efx_port_dummy_op_void, + .check_fault = efx_mcdi_mac_check_fault, +}; diff -r aa133dee7792 drivers/net/sfc/mcdi_pcol.h --- /dev/null +++ b/drivers/net/sfc/mcdi_pcol.h @@ -0,0 +1,1729 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + + +#ifndef MCDI_PCOL_H +#define MCDI_PCOL_H + +/* Values to be written into FMCR_CZ_RESET_STATE_REG to control boot. */ +/* Power-on reset state */ +#define MC_FW_STATE_POR (1) +/* If this is set in MC_RESET_STATE_REG then it should be + * possible to jump into IMEM without loading code from flash. */ +#define MC_FW_WARM_BOOT_OK (2) +/* The MC main image has started to boot. */ +#define MC_FW_STATE_BOOTING (4) +/* The Scheduler has started. */ +#define MC_FW_STATE_SCHED (8) + +/* Values to be written to the per-port status dword in shared + * memory on reboot and assert */ +#define MC_STATUS_DWORD_REBOOT (0xb007b007) +#define MC_STATUS_DWORD_ASSERT (0xdeaddead) + +/* The current version of the MCDI protocol. + * + * Note that the ROM burnt into the card only talks V0, so at the very + * least every driver must support version 0 and MCDI_PCOL_VERSION + */ +#define MCDI_PCOL_VERSION 1 + +/** + * MCDI version 1 + * + * Each MCDI request starts with an MCDI_HEADER, which is a 32byte + * structure, filled in by the client. + * + * 0 7 8 16 20 22 23 24 31 + * | CODE | R | LEN | SEQ | Rsvd | E | R | XFLAGS | + * | | | + * | | \--- Response + * | \------- Error + * \------------------------------ Resync (always set) + * + * The client writes it's request into MC shared memory, and rings the + * doorbell. Each request is completed by either by the MC writting + * back into shared memory, or by writting out an event. + * + * All MCDI commands support completion by shared memory response. Each + * request may also contain additional data (accounted for by HEADER.LEN), + * and some response's may also contain additional data (again, accounted + * for by HEADER.LEN). + * + * Some MCDI commands support completion by event, in which any associated + * response data is included in the event. + * + * The protocol requires one response to be delivered for every request, a + * request should not be sent unless the response for the previous request + * has been received (either by polling shared memory, or by receiving + * an event). + */ + +/** Request/Response structure */ +#define MCDI_HEADER_OFST 0 +#define MCDI_HEADER_CODE_LBN 0 +#define MCDI_HEADER_CODE_WIDTH 7 +#define MCDI_HEADER_RESYNC_LBN 7 +#define MCDI_HEADER_RESYNC_WIDTH 1 +#define MCDI_HEADER_DATALEN_LBN 8 +#define MCDI_HEADER_DATALEN_WIDTH 8 +#define MCDI_HEADER_SEQ_LBN 16 +#define MCDI_HEADER_RSVD_LBN 20 +#define MCDI_HEADER_RSVD_WIDTH 2 +#define MCDI_HEADER_SEQ_WIDTH 4 +#define MCDI_HEADER_ERROR_LBN 22 +#define MCDI_HEADER_ERROR_WIDTH 1 +#define MCDI_HEADER_RESPONSE_LBN 23 +#define MCDI_HEADER_RESPONSE_WIDTH 1 +#define MCDI_HEADER_XFLAGS_LBN 24 +#define MCDI_HEADER_XFLAGS_WIDTH 8 +/* Request response using event */ +#define MCDI_HEADER_XFLAGS_EVREQ 0x01 + +/* Maximum number of payload bytes */ +#define MCDI_CTL_SDU_LEN_MAX 0xfc + +/* The MC can generate events for two reasons: + * - To complete a shared memory request if XFLAGS_EVREQ was set + * - As a notification (link state, i2c event), controlled + * via MC_CMD_LOG_CTRL + * + * Both events share a common structure: + * + * 0 32 33 36 44 52 60 + * | Data | Cont | Level | Src | Code | Rsvd | + * | + * \ There is another event pending in this notification + * + * If Code==CMDDONE, then the fields are further interpreted as: + * + * - LEVEL==INFO Command succeded + * - LEVEL==ERR Command failed + * + * 0 8 16 24 32 + * | Seq | Datalen | Errno | Rsvd | + * + * These fields are taken directly out of the standard MCDI header, i.e., + * LEVEL==ERR, Datalen == 0 => Reboot + * + * Events can be squirted out of the UART (using LOG_CTRL) without a + * MCDI header. An event can be distinguished from a MCDI response by + * examining the first byte which is 0xc0. This corresponds to the + * non-existent MCDI command MC_CMD_DEBUG_LOG. + * + * 0 7 8 + * | command | Resync | = 0xc0 + * + * Since the event is written in big-endian byte order, this works + * providing bits 56-63 of the event are 0xc0. + * + * 56 60 63 + * | Rsvd | Code | = 0xc0 + * + * Which means for convenience the event code is 0xc for all MC + * generated events. + */ +#define FSE_AZ_EV_CODE_MCDI_EVRESPONSE 0xc + +#define MCDI_EVENT_DATA_LBN 0 +#define MCDI_EVENT_DATA_WIDTH 32 +#define MCDI_EVENT_CONT_LBN 32 +#define MCDI_EVENT_CONT_WIDTH 1 +#define MCDI_EVENT_LEVEL_LBN 33 +#define MCDI_EVENT_LEVEL_WIDTH 3 +#define MCDI_EVENT_LEVEL_INFO (0) +#define MCDI_EVENT_LEVEL_WARN (1) +#define MCDI_EVENT_LEVEL_ERR (2) +#define MCDI_EVENT_LEVEL_FATAL (3) +#define MCDI_EVENT_SRC_LBN 36 +#define MCDI_EVENT_SRC_WIDTH 8 +#define MCDI_EVENT_CODE_LBN 44 +#define MCDI_EVENT_CODE_WIDTH 8 +#define MCDI_EVENT_CODE_BADSSERT (1) +#define MCDI_EVENT_CODE_PMNOTICE (2) +#define MCDI_EVENT_CODE_CMDDONE (3) +#define MCDI_EVENT_CMDDONE_SEQ_LBN 0 +#define MCDI_EVENT_CMDDONE_SEQ_WIDTH 8 +#define MCDI_EVENT_CMDDONE_DATALEN_LBN 8 +#define MCDI_EVENT_CMDDONE_DATALEN_WIDTH 8 +#define MCDI_EVENT_CMDDONE_ERRNO_LBN 16 +#define MCDI_EVENT_CMDDONE_ERRNO_WIDTH 8 +#define MCDI_EVENT_CODE_LINKCHANGE (4) +#define MCDI_EVENT_LINKCHANGE_LP_CAP_LBN 0 +#define MCDI_EVENT_LINKCHANGE_LP_CAP_WIDTH 16 +#define MCDI_EVENT_LINKCHANGE_SPEED_LBN 16 +#define MCDI_EVENT_LINKCHANGE_SPEED_WIDTH 4 +#define MCDI_EVENT_LINKCHANGE_SPEED_100M 1 +#define MCDI_EVENT_LINKCHANGE_SPEED_1G 2 +#define MCDI_EVENT_LINKCHANGE_SPEED_10G 3 +#define MCDI_EVENT_LINKCHANGE_FCNTL_LBN 20 +#define MCDI_EVENT_LINKCHANGE_FCNTL_WIDTH 4 +#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_LBN 24 +#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_WIDTH 8 +#define MCDI_EVENT_CODE_SENSOREVT (5) +#define MCDI_EVENT_SENSOREVT_MONITOR_LBN 0 +#define MCDI_EVENT_SENSOREVT_MONITOR_WIDTH 8 +#define MCDI_EVENT_SENSOREVT_STATE_LBN 8 +#define MCDI_EVENT_SENSOREVT_STATE_WIDTH 8 +#define MCDI_EVENT_SENSOREVT_VALUE_LBN 16 +#define MCDI_EVENT_SENSOREVT_VALUE_WIDTH 16 +#define MCDI_EVENT_CODE_SCHEDERR (6) +#define MCDI_EVENT_CODE_REBOOT (7) +#define MCDI_EVENT_CODE_MAC_STATS_DMA (8) +#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_LBN 0 +#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_WIDTH 32 + +/* Non-existent command target */ +#define MC_CMD_ERR_ENOENT 2 +/* assert() has killed the MC */ +#define MC_CMD_ERR_EINTR 4 +/* Caller does not hold required locks */ +#define MC_CMD_ERR_EACCES 13 +/* Resource is currently unavailable (e.g. lock contention) */ +#define MC_CMD_ERR_EBUSY 16 +/* Invalid argument to target */ +#define MC_CMD_ERR_EINVAL 22 +/* Non-recursive resource is already acquired */ +#define MC_CMD_ERR_EDEADLK 35 +/* Operation not implemented */ +#define MC_CMD_ERR_ENOSYS 38 +/* Operation timed out */ +#define MC_CMD_ERR_ETIME 62 + +#define MC_CMD_ERR_CODE_OFST 0 + + +/* MC_CMD_READ32: (debug, variadic out) + * Read multiple 32byte words from MC memory + */ +#define MC_CMD_READ32 0x01 +#define MC_CMD_READ32_IN_LEN 8 +#define MC_CMD_READ32_IN_ADDR_OFST 0 +#define MC_CMD_READ32_IN_NUMWORDS_OFST 4 +#define MC_CMD_READ32_OUT_LEN(_numwords) \ + (4 * (_numwords)) +#define MC_CMD_READ32_OUT_BUFFER_OFST 0 + +/* MC_CMD_WRITE32: (debug, variadic in) + * Write multiple 32byte words to MC memory + */ +#define MC_CMD_WRITE32 0x02 +#define MC_CMD_WRITE32_IN_LEN(_numwords) (((_numwords) * 4) + 4) +#define MC_CMD_WRITE32_IN_ADDR_OFST 0 +#define MC_CMD_WRITE32_IN_BUFFER_OFST 4 +#define MC_CMD_WRITE32_OUT_LEN 0 + +/* MC_CMD_COPYCODE: (debug) + * Copy MC code between two locations and jump + */ +#define MC_CMD_COPYCODE 0x03 +#define MC_CMD_COPYCODE_IN_LEN 16 +#define MC_CMD_COPYCODE_IN_SRC_ADDR_OFST 0 +#define MC_CMD_COPYCODE_IN_DEST_ADDR_OFST 4 +#define MC_CMD_COPYCODE_IN_NUMWORDS_OFST 8 +#define MC_CMD_COPYCODE_IN_JUMP_OFST 12 +/* Control should return to the caller rather than jumping */ +#define MC_CMD_COPYCODE_JUMP_NONE 1 +#define MC_CMD_COPYCODE_OUT_LEN 0 + +/* MC_CMD_SET_FUNC: (debug) + * Select function for function-specific commands. + */ +#define MC_CMD_SET_FUNC 0x04 +#define MC_CMD_SET_FUNC_IN_LEN 4 +#define MC_CMD_SET_FUNC_IN_FUNC_OFST 0 +#define MC_CMD_SET_FUNC_OUT_LEN 0 + +/* MC_CMD_GET_BOOT_STATUS: + * Get the instruction address from which the MC booted. + */ +#define MC_CMD_GET_BOOT_STATUS 0x05 +#define MC_CMD_GET_BOOT_STATUS_IN_LEN 0 +#define MC_CMD_GET_BOOT_STATUS_OUT_LEN 8 +#define MC_CMD_GET_BOOT_STATUS_OUT_BOOT_OFFSET_OFST 0 +#define MC_CMD_GET_BOOT_STATUS_OUT_FLAGS_OFST 4 +/* Reboot caused by watchdog */ +#define MC_CMD_GET_BOOT_STATUS_FLAGS_WATCHDOG_LBN (0) +#define MC_CMD_GET_BOOT_STATUS_FLAGS_WATCHDOG_WIDTH (1) +/* MC booted from primary flash partition */ +#define MC_CMD_GET_BOOT_STATUS_FLAGS_PRIMARY_LBN (1) +#define MC_CMD_GET_BOOT_STATUS_FLAGS_PRIMARY_WIDTH (1) +/* MC booted from backup flash partition */ +#define MC_CMD_GET_BOOT_STATUS_FLAGS_BACKUP_LBN (2) +#define MC_CMD_GET_BOOT_STATUS_FLAGS_BACKUP_WIDTH (1) + +/* MC_CMD_GET_ASSERTS: (debug, variadic out) + * Get (and optionally clear) the current assertion status. + * + * Only OUT.GLOBAL_FLAGS is guaranteed to exist in the completion + * payload. The other fields will only be present if + * OUT.GLOBAL_FLAGS != NO_FAILS + */ +#define MC_CMD_GET_ASSERTS 0x06 +#define MC_CMD_GET_ASSERTS_IN_LEN 4 +#define MC_CMD_GET_ASSERTS_IN_CLEAR_OFST 0 +#define MC_CMD_GET_ASSERTS_OUT_LEN 140 +/* Assertion status flag */ +#define MC_CMD_GET_ASSERTS_OUT_GLOBAL_FLAGS_OFST 0 +/*! No assertions have failed. */ +#define MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS 1 +/*! A system-level assertion has failed. */ +#define MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL 2 +/*! A thread-level assertion has failed. */ +#define MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL 3 +/*! The system was reset by the watchdog. */ +#define MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED 4 +/* Failing PC value */ +#define MC_CMD_GET_ASSERTS_OUT_SAVED_PC_OFFS_OFST 4 +/* Saved GP regs */ +#define MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST 8 +#define MC_CMD_GET_ASSERTS_OUT_GP_REGS_LEN 124 +/* Failing thread address */ +#define MC_CMD_GET_ASSERTS_OUT_THREAD_OFFS_OFST 132 + +/* MC_CMD_LOG_CTRL: + * Determine the output stream for various events and messages + */ +#define MC_CMD_LOG_CTRL 0x07 +#define MC_CMD_LOG_CTRL_IN_LEN 8 +#define MC_CMD_LOG_CTRL_IN_LOG_DEST_OFST 0 +#define MC_CMD_LOG_CTRL_IN_LOG_DEST_UART (1) +#define MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ (2) +#define MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ_OFST 4 +#define MC_CMD_LOG_CTRL_OUT_LEN 0 + +/* MC_CMD_GET_VERSION: + * Get version information about the MC firmware + */ +#define MC_CMD_GET_VERSION 0x08 +#define MC_CMD_GET_VERSION_IN_LEN 0 +#define MC_CMD_GET_VERSION_V0_OUT_LEN 4 +#define MC_CMD_GET_VERSION_V1_OUT_LEN 32 +#define MC_CMD_GET_VERSION_OUT_FIRMWARE_OFST 0 +/* Reserved version number to indicate "any" version. */ +#define MC_CMD_GET_VERSION_OUT_FIRMWARE_ANY 0xffffffff +/* The version response of a boot ROM awaiting rescue */ +#define MC_CMD_GET_VERSION_OUT_FIRMWARE_BOOTROM 0xb0070000 +#define MC_CMD_GET_VERSION_V1_OUT_PCOL_OFST 4 +/* 128bit mask of functions supported by the current firmware */ +#define MC_CMD_GET_VERSION_V1_OUT_SUPPORTED_FUNCS_OFST 8 +/* The command set exported by the boot ROM (MCDI v0) */ +#define MC_CMD_GET_VERSION_V0_SUPPORTED_FUNCS { \ + (1 << MC_CMD_READ32) | \ + (1 << MC_CMD_WRITE32) | \ + (1 << MC_CMD_COPYCODE) | \ + (1 << MC_CMD_GET_VERSION), \ + 0, 0, 0 } +#define MC_CMD_GET_VERSION_OUT_VERSION_OFST 24 + +/* Vectors in the boot ROM */ +/* Point to the copycode entry point. */ +#define MC_BOOTROM_COPYCODE_VEC (0x7f4) +/* Points to the recovery mode entry point. */ +#define MC_BOOTROM_NOFLASH_VEC (0x7f8) + +/* Test execution limits */ +#define MC_TESTEXEC_VARIANT_COUNT 16 +#define MC_TESTEXEC_RESULT_COUNT 7 + +/* MC_CMD_SET_TESTVARS: (debug, variadic in) + * Write variant words for test. + * + * The user supplies a bitmap of the variants they wish to set. + * They must ensure that IN.LEN >= 4 + 4 * ffs(BITMAP) + */ +#define MC_CMD_SET_TESTVARS 0x09 +#define MC_CMD_SET_TESTVARS_IN_LEN(_numwords) \ + (4 + 4*(_numwords)) +#define MC_CMD_SET_TESTVARS_IN_ARGS_BITMAP_OFST 0 +/* Up to MC_TESTEXEC_VARIANT_COUNT of 32byte words start here */ +#define MC_CMD_SET_TESTVARS_IN_ARGS_BUFFER_OFST 4 +#define MC_CMD_SET_TESTVARS_OUT_LEN 0 + +/* MC_CMD_GET_TESTRCS: (debug, variadic out) + * Return result words from test. + */ +#define MC_CMD_GET_TESTRCS 0x0a +#define MC_CMD_GET_TESTRCS_IN_LEN 4 +#define MC_CMD_GET_TESTRCS_IN_NUMWORDS_OFST 0 +#define MC_CMD_GET_TESTRCS_OUT_LEN(_numwords) \ + (4 * (_numwords)) +#define MC_CMD_GET_TESTRCS_OUT_BUFFER_OFST 0 + +/* MC_CMD_RUN_TEST: (debug) + * Run the test exported by this firmware image + */ +#define MC_CMD_RUN_TEST 0x0b +#define MC_CMD_RUN_TEST_IN_LEN 0 +#define MC_CMD_RUN_TEST_OUT_LEN 0 + +/* MC_CMD_CSR_READ32: (debug, variadic out) + * Read 32bit words from the indirect memory map + */ +#define MC_CMD_CSR_READ32 0x0c +#define MC_CMD_CSR_READ32_IN_LEN 12 +#define MC_CMD_CSR_READ32_IN_ADDR_OFST 0 +#define MC_CMD_CSR_READ32_IN_STEP_OFST 4 +#define MC_CMD_CSR_READ32_IN_NUMWORDS_OFST 8 +#define MC_CMD_CSR_READ32_OUT_LEN(_numwords) \ + (((_numwords) * 4) + 4) +/* IN.NUMWORDS of 32bit words start here */ +#define MC_CMD_CSR_READ32_OUT_BUFFER_OFST 0 +#define MC_CMD_CSR_READ32_OUT_IREG_STATUS_OFST(_numwords) \ + ((_numwords) * 4) + +/* MC_CMD_CSR_WRITE32: (debug, variadic in) + * Write 32bit dwords to the indirect memory map + */ +#define MC_CMD_CSR_WRITE32 0x0d +#define MC_CMD_CSR_WRITE32_IN_LEN(_numwords) \ + (((_numwords) * 4) + 8) +#define MC_CMD_CSR_WRITE32_IN_ADDR_OFST 0 +#define MC_CMD_CSR_WRITE32_IN_STEP_OFST 4 +/* Multiple 32bit words of data to write start here */ +#define MC_CMD_CSR_WRITE32_IN_BUFFER_OFST 8 +#define MC_CMD_CSR_WRITE32_OUT_LEN 4 +#define MC_CMD_CSR_WRITE32_OUT_STATUS_OFST 0 + +/* MC_CMD_JTAG_WORK: (debug, fpga only) + * Process JTAG work buffer for RBF acceleration. + * + * Host: bit count, (up to) 32 words of data to clock out to JTAG + * (bits 1,0=TMS,TDO for first bit; bits 3,2=TMS,TDO for second bit, etc.) + * MC: bit count, (up to) 32 words of data clocked in from JTAG + * (bit 0=TDI for first bit, bit 1=TDI for second bit, etc.; [31:16] unused) + */ +#define MC_CMD_JTAG_WORK 0x0e + +/* MC_CMD_STACKINFO: (debug, variadic out) + * Get stack information + * + * Host: nothing + * MC: (thread ptr, stack size, free space) for each thread in system + */ +#define MC_CMD_STACKINFO 0x0f + +/* MC_CMD_MDIO_READ: + * MDIO register read + */ +#define MC_CMD_MDIO_READ 0x10 +#define MC_CMD_MDIO_READ_IN_LEN 16 +#define MC_CMD_MDIO_READ_IN_BUS_OFST 0 +#define MC_CMD_MDIO_READ_IN_PRTAD_OFST 4 +#define MC_CMD_MDIO_READ_IN_DEVAD_OFST 8 +#define MC_CMD_MDIO_READ_IN_ADDR_OFST 12 +#define MC_CMD_MDIO_READ_OUT_LEN 8 +#define MC_CMD_MDIO_READ_OUT_VALUE_OFST 0 +#define MC_CMD_MDIO_READ_OUT_STATUS_OFST 4 + +/* MC_CMD_MDIO_WRITE: + * MDIO register write + */ +#define MC_CMD_MDIO_WRITE 0x11 +#define MC_CMD_MDIO_WRITE_IN_LEN 20 +#define MC_CMD_MDIO_WRITE_IN_BUS_OFST 0 +#define MC_CMD_MDIO_WRITE_IN_PRTAD_OFST 4 +#define MC_CMD_MDIO_WRITE_IN_DEVAD_OFST 8 +#define MC_CMD_MDIO_WRITE_IN_ADDR_OFST 12 +#define MC_CMD_MDIO_WRITE_IN_VALUE_OFST 16 +#define MC_CMD_MDIO_WRITE_OUT_LEN 4 +#define MC_CMD_MDIO_WRITE_OUT_STATUS_OFST 0 + +/* By default all the MCDI MDIO operations perform clause45 mode. + * If you want to use clause22 then set DEVAD = MC_CMD_MDIO_CLAUSE22. + */ +#define MC_CMD_MDIO_CLAUSE22 32 + +/* There are two MDIO buses: one for the internal PHY, and one for external + * devices. + */ +#define MC_CMD_MDIO_BUS_INTERNAL 0 +#define MC_CMD_MDIO_BUS_EXTERNAL 1 + +/* The MDIO commands return the raw status bits from the MDIO block. A "good" + * transaction should have the DONE bit set and all other bits clear. + */ +#define MC_CMD_MDIO_STATUS_GOOD 0x08 + + +/* MC_CMD_DBI_WRITE: (debug) + * Write DBI register(s) + * + * Host: address, byte-enables (and VF selection, and cs2 flag), + * value [,address ...] + * MC: nothing + */ +#define MC_CMD_DBI_WRITE 0x12 +#define MC_CMD_DBI_WRITE_IN_LEN(_numwords) \ + (12 * (_numwords)) +#define MC_CMD_DBI_WRITE_IN_ADDRESS_OFST(_word) \ + (((_word) * 12) + 0) +#define MC_CMD_DBI_WRITE_IN_BYTE_MASK_OFST(_word) \ + (((_word) * 12) + 4) +#define MC_CMD_DBI_WRITE_IN_VALUE_OFST(_word) \ + (((_word) * 12) + 8) +#define MC_CMD_DBI_WRITE_OUT_LEN 0 + +/* MC_CMD_DBI_READ: (debug) + * Read DBI register(s) + * + * Host: address, [,address ...] + * MC: value [,value ...] + * (note: this does not support reading from VFs, but is retained for backwards + * compatibility; see MC_CMD_DBI_READX below) + */ +#define MC_CMD_DBI_READ 0x13 +#define MC_CMD_DBI_READ_IN_LEN(_numwords) \ + (4 * (_numwords)) +#define MC_CMD_DBI_READ_OUT_LEN(_numwords) \ + (4 * (_numwords)) + +/* MC_CMD_PORT_READ32: (debug) + * Read a 32-bit register from the indirect port register map. + * + * The port to access is implied by the Shared memory channel used. + */ +#define MC_CMD_PORT_READ32 0x14 +#define MC_CMD_PORT_READ32_IN_LEN 4 +#define MC_CMD_PORT_READ32_IN_ADDR_OFST 0 +#define MC_CMD_PORT_READ32_OUT_LEN 8 +#define MC_CMD_PORT_READ32_OUT_VALUE_OFST 0 +#define MC_CMD_PORT_READ32_OUT_STATUS_OFST 4 + +/* MC_CMD_PORT_WRITE32: (debug) + * Write a 32-bit register to the indirect port register map. + * + * The port to access is implied by the Shared memory channel used. + */ +#define MC_CMD_PORT_WRITE32 0x15 +#define MC_CMD_PORT_WRITE32_IN_LEN 8 +#define MC_CMD_PORT_WRITE32_IN_ADDR_OFST 0 +#define MC_CMD_PORT_WRITE32_IN_VALUE_OFST 4 +#define MC_CMD_PORT_WRITE32_OUT_LEN 4 +#define MC_CMD_PORT_WRITE32_OUT_STATUS_OFST 0 + +/* MC_CMD_PORT_READ128: (debug) + * Read a 128-bit register from indirect port register map + * + * The port to access is implied by the Shared memory channel used. + */ +#define MC_CMD_PORT_READ128 0x16 +#define MC_CMD_PORT_READ128_IN_LEN 4 +#define MC_CMD_PORT_READ128_IN_ADDR_OFST 0 +#define MC_CMD_PORT_READ128_OUT_LEN 20 +#define MC_CMD_PORT_READ128_OUT_VALUE_OFST 0 +#define MC_CMD_PORT_READ128_OUT_STATUS_OFST 16 + +/* MC_CMD_PORT_WRITE128: (debug) + * Write a 128-bit register to indirect port register map. + * + * The port to access is implied by the Shared memory channel used. + */ +#define MC_CMD_PORT_WRITE128 0x17 +#define MC_CMD_PORT_WRITE128_IN_LEN 20 +#define MC_CMD_PORT_WRITE128_IN_ADDR_OFST 0 +#define MC_CMD_PORT_WRITE128_IN_VALUE_OFST 4 +#define MC_CMD_PORT_WRITE128_OUT_LEN 4 +#define MC_CMD_PORT_WRITE128_OUT_STATUS_OFST 0 + +/* MC_CMD_GET_BOARD_CFG: + * Returns the MC firmware configuration structure + * + * The FW_SUBTYPE_LIST contains a 16-bit value for each of the 12 types of + * NVRAM area. The values are defined in the firmware/mc/platform/.c file + * for a specific board type, but otherwise have no meaning to the MC; they + * are used by the driver to manage selection of appropriate firmware updates. + */ +#define MC_CMD_GET_BOARD_CFG 0x18 +#define MC_CMD_GET_BOARD_CFG_IN_LEN 0 +#define MC_CMD_GET_BOARD_CFG_OUT_LEN 96 +#define MC_CMD_GET_BOARD_CFG_OUT_BOARD_TYPE_OFST 0 +#define MC_CMD_GET_BOARD_CFG_OUT_BOARD_NAME_OFST 4 +#define MC_CMD_GET_BOARD_CFG_OUT_BOARD_NAME_LEN 32 +#define MC_CMD_GET_BOARD_CFG_OUT_CAPABILITIES_PORT0_OFST 36 +#define MC_CMD_GET_BOARD_CFG_OUT_CAPABILITIES_PORT1_OFST 40 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_OFST 44 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_LEN 6 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_OFST 50 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_LEN 6 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_COUNT_PORT0_OFST 56 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_COUNT_PORT1_OFST 60 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_STRIDE_PORT0_OFST 64 +#define MC_CMD_GET_BOARD_CFG_OUT_MAC_STRIDE_PORT1_OFST 68 +#define MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST 72 +#define MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN 24 + +/* MC_CMD_DBI_READX: (debug) + * Read DBI register(s) -- extended functionality + * + * Host: vf selection, address, [,vf selection ...] + * MC: value [,value ...] + */ +#define MC_CMD_DBI_READX 0x19 +#define MC_CMD_DBI_READX_IN_LEN(_numwords) \ + (8*(_numwords)) +#define MC_CMD_DBI_READX_OUT_LEN(_numwords) \ + (4*(_numwords)) + +/* MC_CMD_SET_RAND_SEED: + * Set the 16byte seed for the MC psuedo-random generator + */ +#define MC_CMD_SET_RAND_SEED 0x1a +#define MC_CMD_SET_RAND_SEED_IN_LEN 16 +#define MC_CMD_SET_RAND_SEED_IN_SEED_OFST 0 +#define MC_CMD_SET_RAND_SEED_OUT_LEN 0 + +/* MC_CMD_LTSSM_HIST: (debug) + * Retrieve the history of the LTSSM, if the build supports it. + * + * Host: nothing + * MC: variable number of LTSSM values, as bytes + * The history is read-to-clear. + */ +#define MC_CMD_LTSSM_HIST 0x1b + +/* MC_CMD_DRV_ATTACH: + * Inform MCPU that this port is managed on the host (i.e. driver active) + */ +#define MC_CMD_DRV_ATTACH 0x1c +#define MC_CMD_DRV_ATTACH_IN_LEN 8 +#define MC_CMD_DRV_ATTACH_IN_NEW_STATE_OFST 0 +#define MC_CMD_DRV_ATTACH_IN_UPDATE_OFST 4 +#define MC_CMD_DRV_ATTACH_OUT_LEN 4 +#define MC_CMD_DRV_ATTACH_OUT_OLD_STATE_OFST 0 + +/* MC_CMD_NCSI_PROD: (debug) + * Trigger an NC-SI event (and possibly an AEN in response) + */ +#define MC_CMD_NCSI_PROD 0x1d +#define MC_CMD_NCSI_PROD_IN_LEN 4 +#define MC_CMD_NCSI_PROD_IN_EVENTS_OFST 0 +#define MC_CMD_NCSI_PROD_LINKCHANGE_LBN 0 +#define MC_CMD_NCSI_PROD_LINKCHANGE_WIDTH 1 +#define MC_CMD_NCSI_PROD_RESET_LBN 1 +#define MC_CMD_NCSI_PROD_RESET_WIDTH 1 +#define MC_CMD_NCSI_PROD_DRVATTACH_LBN 2 +#define MC_CMD_NCSI_PROD_DRVATTACH_WIDTH 1 +#define MC_CMD_NCSI_PROD_OUT_LEN 0 + +/* Enumeration */ +#define MC_CMD_NCSI_PROD_LINKCHANGE 0 +#define MC_CMD_NCSI_PROD_RESET 1 +#define MC_CMD_NCSI_PROD_DRVATTACH 2 + +/* MC_CMD_DEVEL: (debug) + * Reserved for development + */ +#define MC_CMD_DEVEL 0x1e + +/* MC_CMD_SHMUART: (debug) + * Route UART output to circular buffer in shared memory instead. + */ +#define MC_CMD_SHMUART 0x1f +#define MC_CMD_SHMUART_IN_FLAG_OFST 0 +#define MC_CMD_SHMUART_IN_LEN 4 +#define MC_CMD_SHMUART_OUT_LEN 0 + +/* MC_CMD_PORT_RESET: + * Generic per-port reset. There is no equivalent for per-board reset. + * + * Locks required: None + * Return code: 0, ETIME + */ +#define MC_CMD_PORT_RESET 0x20 +#define MC_CMD_PORT_RESET_IN_LEN 0 +#define MC_CMD_PORT_RESET_OUT_LEN 0 + +/* MC_CMD_RESOURCE_LOCK: + * Generic resource lock/unlock interface. + * + * Locks required: None + * Return code: 0, + * EBUSY (if trylock is contended by other port), + * EDEADLK (if trylock is already acquired by this port) + * EINVAL (if unlock doesn't own the lock) + */ +#define MC_CMD_RESOURCE_LOCK 0x21 +#define MC_CMD_RESOURCE_LOCK_IN_LEN 8 +#define MC_CMD_RESOURCE_LOCK_IN_ACTION_OFST 0 +#define MC_CMD_RESOURCE_LOCK_ACTION_TRYLOCK 1 +#define MC_CMD_RESOURCE_LOCK_ACTION_UNLOCK 0 +#define MC_CMD_RESOURCE_LOCK_IN_RESOURCE_OFST 4 +#define MC_CMD_RESOURCE_LOCK_I2C 2 +#define MC_CMD_RESOURCE_LOCK_PHY 3 +#define MC_CMD_RESOURCE_LOCK_OUT_LEN 0 + +/* MC_CMD_SPI_COMMAND: (variadic in, variadic out) + * Read/Write to/from the SPI device. + * + * Locks required: SPI_LOCK + * Return code: 0, ETIME, EINVAL, EACCES (if SPI_LOCK is not held) + */ +#define MC_CMD_SPI_COMMAND 0x22 +#define MC_CMD_SPI_COMMAND_IN_LEN(_write_bytes) (12 + (_write_bytes)) +#define MC_CMD_SPI_COMMAND_IN_ARGS_OFST 0 +#define MC_CMD_SPI_COMMAND_IN_ARGS_ADDRESS_OFST 0 +#define MC_CMD_SPI_COMMAND_IN_ARGS_READ_BYTES_OFST 4 +#define MC_CMD_SPI_COMMAND_IN_ARGS_CHIP_SELECT_OFST 8 +/* Data to write here */ +#define MC_CMD_SPI_COMMAND_IN_WRITE_BUFFER_OFST 12 +#define MC_CMD_SPI_COMMAND_OUT_LEN(_read_bytes) (_read_bytes) +/* Data read here */ +#define MC_CMD_SPI_COMMAND_OUT_READ_BUFFER_OFST 0 + +/* MC_CMD_I2C_READ_WRITE: (variadic in, variadic out) + * Read/Write to/from the I2C bus. + * + * Locks required: I2C_LOCK + * Return code: 0, ETIME, EINVAL, EACCES (if I2C_LOCK is not held) + */ +#define MC_CMD_I2C_RW 0x23 +#define MC_CMD_I2C_RW_IN_LEN(_write_bytes) (8 + (_write_bytes)) +#define MC_CMD_I2C_RW_IN_ARGS_OFST 0 +#define MC_CMD_I2C_RW_IN_ARGS_ADDR_OFST 0 +#define MC_CMD_I2C_RW_IN_ARGS_READ_BYTES_OFST 4 +/* Data to write here */ +#define MC_CMD_I2C_RW_IN_WRITE_BUFFER_OFSET 8 +#define MC_CMD_I2C_RW_OUT_LEN(_read_bytes) (_read_bytes) +/* Data read here */ +#define MC_CMD_I2C_RW_OUT_READ_BUFFER_OFST 0 + +/* Generic phy capability bitmask */ +#define MC_CMD_PHY_CAP_10HDX_LBN 1 +#define MC_CMD_PHY_CAP_10HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_10FDX_LBN 2 +#define MC_CMD_PHY_CAP_10FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_100HDX_LBN 3 +#define MC_CMD_PHY_CAP_100HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_100FDX_LBN 4 +#define MC_CMD_PHY_CAP_100FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_1000HDX_LBN 5 +#define MC_CMD_PHY_CAP_1000HDX_WIDTH 1 +#define MC_CMD_PHY_CAP_1000FDX_LBN 6 +#define MC_CMD_PHY_CAP_1000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_10000FDX_LBN 7 +#define MC_CMD_PHY_CAP_10000FDX_WIDTH 1 +#define MC_CMD_PHY_CAP_PAUSE_LBN 8 +#define MC_CMD_PHY_CAP_PAUSE_WIDTH 1 +#define MC_CMD_PHY_CAP_ASYM_LBN 9 +#define MC_CMD_PHY_CAP_ASYM_WIDTH 1 +#define MC_CMD_PHY_CAP_AN_LBN 10 +#define MC_CMD_PHY_CAP_AN_WIDTH 1 + +/* Generic loopback enumeration */ +#define MC_CMD_LOOPBACK_NONE 0 +#define MC_CMD_LOOPBACK_DATA 1 +#define MC_CMD_LOOPBACK_GMAC 2 +#define MC_CMD_LOOPBACK_XGMII 3 +#define MC_CMD_LOOPBACK_XGXS 4 +#define MC_CMD_LOOPBACK_XAUI 5 +#define MC_CMD_LOOPBACK_GMII 6 +#define MC_CMD_LOOPBACK_SGMII 7 +#define MC_CMD_LOOPBACK_XGBR 8 +#define MC_CMD_LOOPBACK_XFI 9 +#define MC_CMD_LOOPBACK_XAUI_FAR 10 +#define MC_CMD_LOOPBACK_GMII_FAR 11 +#define MC_CMD_LOOPBACK_SGMII_FAR 12 +#define MC_CMD_LOOPBACK_XFI_FAR 13 +#define MC_CMD_LOOPBACK_GPHY 14 +#define MC_CMD_LOOPBACK_PHYXS 15 +#define MC_CMD_LOOPBACK_PCS 16 +#define MC_CMD_LOOPBACK_PMAPMD 17 +#define MC_CMD_LOOPBACK_XPORT 18 +#define MC_CMD_LOOPBACK_XGMII_WS 19 +#define MC_CMD_LOOPBACK_XAUI_WS 20 +#define MC_CMD_LOOPBACK_XAUI_WS_FAR 21 +#define MC_CMD_LOOPBACK_XAUI_WS_NEAR 22 +#define MC_CMD_LOOPBACK_GMII_WS 23 +#define MC_CMD_LOOPBACK_XFI_WS 24 +#define MC_CMD_LOOPBACK_XFI_WS_FAR 25 +#define MC_CMD_LOOPBACK_PHYXS_WS 26 + +/* Generic PHY statistics enumeration */ +#define MC_CMD_OUI 0 +#define MC_CMD_PMA_PMD_LINK_UP 1 +#define MC_CMD_PMA_PMD_RX_FAULT 2 +#define MC_CMD_PMA_PMD_TX_FAULT 3 +#define MC_CMD_PMA_PMD_SIGNAL 4 +#define MC_CMD_PMA_PMD_SNR_A 5 +#define MC_CMD_PMA_PMD_SNR_B 6 +#define MC_CMD_PMA_PMD_SNR_C 7 +#define MC_CMD_PMA_PMD_SNR_D 8 +#define MC_CMD_PCS_LINK_UP 9 +#define MC_CMD_PCS_RX_FAULT 10 +#define MC_CMD_PCS_TX_FAULT 11 +#define MC_CMD_PCS_BER 12 +#define MC_CMD_PCS_BLOCK_ERRORS 13 +#define MC_CMD_PHYXS_LINK_UP 14 +#define MC_CMD_PHYXS_RX_FAULT 15 +#define MC_CMD_PHYXS_TX_FAULT 16 +#define MC_CMD_PHYXS_ALIGN 17 +#define MC_CMD_PHYXS_SYNC 18 +#define MC_CMD_AN_LINK_UP 19 +#define MC_CMD_AN_COMPLETE 20 +#define MC_CMD_AN_10GBT_STATUS 21 +#define MC_CMD_CL22_LINK_UP 22 +#define MC_CMD_PHY_NSTATS 23 + +/* MC_CMD_GET_PHY_CFG: + * Report PHY configuration. This guarantees to succeed even if the PHY is in + * a "zombie" state. + * + * Locks required: None + * Return code: 0 + */ +#define MC_CMD_GET_PHY_CFG 0x24 + +#define MC_CMD_GET_PHY_CFG_IN_LEN 0 +#define MC_CMD_GET_PHY_CFG_OUT_LEN 72 + +#define MC_CMD_GET_PHY_CFG_OUT_FLAGS_OFST 0 +#define MC_CMD_GET_PHY_CFG_PRESENT_LBN 0 +#define MC_CMD_GET_PHY_CFG_PRESENT_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_BIST_CABLE_SHORT_LBN 1 +#define MC_CMD_GET_PHY_CFG_BIST_CABLE_SHORT_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_BIST_CABLE_LONG_LBN 2 +#define MC_CMD_GET_PHY_CFG_BIST_CABLE_LONG_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_LOWPOWER_LBN 3 +#define MC_CMD_GET_PHY_CFG_LOWPOWER_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_POWEROFF_LBN 4 +#define MC_CMD_GET_PHY_CFG_POWEROFF_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_TXDIS_LBN 5 +#define MC_CMD_GET_PHY_CFG_TXDIS_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_BIST_LBN 6 +#define MC_CMD_GET_PHY_CFG_BIST_WIDTH 1 +#define MC_CMD_GET_PHY_CFG_OUT_TYPE_OFST 4 +/* Bitmask of supported capabilities */ +#define MC_CMD_GET_PHY_CFG_OUT_SUPPORTED_CAP_OFST 8 +#define MC_CMD_GET_PHY_CFG_OUT_CHANNEL_OFST 12 +#define MC_CMD_GET_PHY_CFG_OUT_PRT_OFST 16 +/* PHY statistics bitmap */ +#define MC_CMD_GET_PHY_CFG_OUT_STATS_MASK_OFST 20 +/* PHY type/name string */ +#define MC_CMD_GET_PHY_CFG_OUT_NAME_OFST 24 +#define MC_CMD_GET_PHY_CFG_OUT_NAME_LEN 20 +#define MC_CMD_GET_PHY_CFG_OUT_MEDIA_TYPE_OFST 44 +#define MC_CMD_MEDIA_XAUI 1 +#define MC_CMD_MEDIA_CX4 2 +#define MC_CMD_MEDIA_KX4 3 +#define MC_CMD_MEDIA_XFP 4 +#define MC_CMD_MEDIA_SFP_PLUS 5 +#define MC_CMD_MEDIA_BASE_T 6 +/* MDIO "MMDS" supported */ +#define MC_CMD_GET_PHY_CFG_OUT_MMD_MASK_OFST 48 +/* Native clause 22 */ +#define MC_CMD_MMD_CLAUSE22 0 +#define MC_CMD_MMD_CLAUSE45_PMAPMD 1 +#define MC_CMD_MMD_CLAUSE45_WIS 2 +#define MC_CMD_MMD_CLAUSE45_PCS 3 +#define MC_CMD_MMD_CLAUSE45_PHYXS 4 +#define MC_CMD_MMD_CLAUSE45_DTEXS 5 +#define MC_CMD_MMD_CLAUSE45_TC 6 +#define MC_CMD_MMD_CLAUSE45_AN 7 +/* Clause22 proxied over clause45 by PHY */ +#define MC_CMD_MMD_CLAUSE45_C22EXT 29 +#define MC_CMD_MMD_CLAUSE45_VEND1 30 +#define MC_CMD_MMD_CLAUSE45_VEND2 31 +/* PHY stepping version */ +#define MC_CMD_GET_PHY_CFG_OUT_REVISION_OFST 52 +#define MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN 20 + +/* MC_CMD_START_BIST: + * Start a BIST test on the PHY. + * + * Locks required: PHY_LOCK if doing a PHY BIST + * Return code: 0, EINVAL, EACCES (if PHY_LOCK is not held) + */ +#define MC_CMD_START_BIST 0x25 +#define MC_CMD_START_BIST_IN_LEN 4 +#define MC_CMD_START_BIST_IN_TYPE_OFST 0 +#define MC_CMD_START_BIST_OUT_LEN 0 + +/* Run the PHY's short cable BIST */ +#define MC_CMD_PHY_BIST_CABLE_SHORT 1 +/* Run the PHY's long cable BIST */ +#define MC_CMD_PHY_BIST_CABLE_LONG 2 +/* Run BIST on the currently selected BPX Serdes (XAUI or XFI) */ +#define MC_CMD_BPX_SERDES_BIST 3 +/* Run the MC loopback tests */ +#define MC_CMD_MC_LOOPBACK_BIST 4 +/* Run the PHY's standard BIST */ +#define MC_CMD_PHY_BIST 5 + +/* MC_CMD_POLL_PHY_BIST: (variadic output) + * Poll for BIST completion + * + * Returns a single status code, and optionally some PHY specific + * bist output. The driver should only consume the BIST output + * after validating OUTLEN and PHY_CFG.PHY_TYPE. + * + * If a driver can't succesfully parse the BIST output, it should + * still respect the pass/Fail in OUT.RESULT + * + * Locks required: PHY_LOCK if doing a PHY BIST + * Return code: 0, EACCES (if PHY_LOCK is not held) + */ +#define MC_CMD_POLL_BIST 0x26 +#define MC_CMD_POLL_BIST_IN_LEN 0 +#define MC_CMD_POLL_BIST_OUT_LEN UNKNOWN +#define MC_CMD_POLL_BIST_OUT_SFT9001_LEN 40 +#define MC_CMD_POLL_BIST_OUT_MRSFP_LEN 8 +#define MC_CMD_POLL_BIST_OUT_RESULT_OFST 0 +#define MC_CMD_POLL_BIST_RUNNING 1 +#define MC_CMD_POLL_BIST_PASSED 2 +#define MC_CMD_POLL_BIST_FAILED 3 +#define MC_CMD_POLL_BIST_TIMEOUT 4 +/* Generic: */ +#define MC_CMD_POLL_BIST_OUT_PRIVATE_OFST 4 +/* SFT9001-specific: */ +/* (offset 4 unused?) */ +#define MC_CMD_POLL_BIST_OUT_SFT9001_CABLE_LENGTH_A_OFST 8 +#define MC_CMD_POLL_BIST_OUT_SFT9001_CABLE_LENGTH_B_OFST 12 +#define MC_CMD_POLL_BIST_OUT_SFT9001_CABLE_LENGTH_C_OFST 16 +#define MC_CMD_POLL_BIST_OUT_SFT9001_CABLE_LENGTH_D_OFST 20 +#define MC_CMD_POLL_BIST_OUT_SFT9001_CABLE_STATUS_A_OFST 24 +#define MC_CMD_POLL_BIST_OUT_SFT9001_CABLE_STATUS_B_OFST 28 +#define MC_CMD_POLL_BIST_OUT_SFT9001_CABLE_STATUS_C_OFST 32 +#define MC_CMD_POLL_BIST_OUT_SFT9001_CABLE_STATUS_D_OFST 36 +#define MC_CMD_POLL_BIST_SFT9001_PAIR_OK 1 +#define MC_CMD_POLL_BIST_SFT9001_PAIR_OPEN 2 +#define MC_CMD_POLL_BIST_SFT9001_INTRA_PAIR_SHORT 3 +#define MC_CMD_POLL_BIST_SFT9001_INTER_PAIR_SHORT 4 +#define MC_CMD_POLL_BIST_SFT9001_PAIR_BUSY 9 +/* mrsfp "PHY" driver: */ +#define MC_CMD_POLL_BIST_OUT_MRSFP_TEST_OFST 4 +#define MC_CMD_POLL_BIST_MRSFP_TEST_COMPLETE 0 +#define MC_CMD_POLL_BIST_MRSFP_TEST_BUS_SWITCH_OFF_I2C_WRITE 1 +#define MC_CMD_POLL_BIST_MRSFP_TEST_BUS_SWITCH_OFF_I2C_NO_ACCESS_IO_EXP 2 +#define MC_CMD_POLL_BIST_MRSFP_TEST_BUS_SWITCH_OFF_I2C_NO_ACCESS_MODULE 3 +#define MC_CMD_POLL_BIST_MRSFP_TEST_IO_EXP_I2C_CONFIGURE 4 +#define MC_CMD_POLL_BIST_MRSFP_TEST_BUS_SWITCH_I2C_NO_CROSSTALK 5 +#define MC_CMD_POLL_BIST_MRSFP_TEST_MODULE_PRESENCE 6 +#define MC_CMD_POLL_BIST_MRSFP_TEST_MODULE_ID_I2C_ACCESS 7 +#define MC_CMD_POLL_BIST_MRSFP_TEST_MODULE_ID_SANE_VALUE 8 + +/* MC_CMD_PHY_SPI: (variadic in, variadic out) + * Read/Write/Erase the PHY SPI device + * + * Locks required: PHY_LOCK + * Return code: 0, ETIME, EINVAL, EACCES (if PHY_LOCK is not held) + */ +#define MC_CMD_PHY_SPI 0x27 +#define MC_CMD_PHY_SPI_IN_LEN(_write_bytes) (12 + (_write_bytes)) +#define MC_CMD_PHY_SPI_IN_ARGS_OFST 0 +#define MC_CMD_PHY_SPI_IN_ARGS_ADDR_OFST 0 +#define MC_CMD_PHY_SPI_IN_ARGS_READ_BYTES_OFST 4 +#define MC_CMD_PHY_SPI_IN_ARGS_ERASE_ALL_OFST 8 +/* Data to write here */ +#define MC_CMD_PHY_SPI_IN_WRITE_BUFFER_OFSET 12 +#define MC_CMD_PHY_SPI_OUT_LEN(_read_bytes) (_read_bytes) +/* Data read here */ +#define MC_CMD_PHY_SPI_OUT_READ_BUFFER_OFST 0 + + +/* MC_CMD_GET_LOOPBACK_MODES: + * Returns a bitmask of loopback modes evailable at each speed. + * + * Locks required: None + * Return code: 0 + */ +#define MC_CMD_GET_LOOPBACK_MODES 0x28 +#define MC_CMD_GET_LOOPBACK_MODES_IN_LEN 0 +#define MC_CMD_GET_LOOPBACK_MODES_OUT_LEN 32 +#define MC_CMD_GET_LOOPBACK_MODES_100M_OFST 0 +#define MC_CMD_GET_LOOPBACK_MODES_1G_OFST 8 +#define MC_CMD_GET_LOOPBACK_MODES_10G_OFST 16 +#define MC_CMD_GET_LOOPBACK_MODES_SUGGESTED_OFST 24 + +/* Flow control enumeration */ +#define MC_CMD_FCNTL_OFF 0 +#define MC_CMD_FCNTL_RESPOND 1 +#define MC_CMD_FCNTL_BIDIR 2 +/* Auto - Use what the link has autonegotiated + * - The driver should modify the advertised capabilities via SET_LINK.CAP + * to control the negotiated flow control mode. + * - Can only be set if the PHY supports PAUSE+ASYM capabilities + * - Never returned by GET_LINK as the value programmed into the MAC + */ +#define MC_CMD_FCNTL_AUTO 3 + +/* Generic mac fault bitmask */ +#define MC_CMD_MAC_FAULT_XGMII_LOCAL_LBN 0 +#define MC_CMD_MAC_FAULT_XGMII_LOCAL_WIDTH 1 +#define MC_CMD_MAC_FAULT_XGMII_REMOTE_LBN 1 +#define MC_CMD_MAC_FAULT_XGMII_REMOTE_WIDTH 1 +#define MC_CMD_MAC_FAULT_SGMII_REMOTE_LBN 2 +#define MC_CMD_MAC_FAULT_SGMII_REMOTE_WIDTH 1 + +/* MC_CMD_GET_LINK: + * Read the unified MAC/PHY link state + * + * Locks required: None + * Return code: 0, ETIME + */ +#define MC_CMD_GET_LINK 0x29 +#define MC_CMD_GET_LINK_IN_LEN 0 +#define MC_CMD_GET_LINK_OUT_LEN 28 +/* near-side and link-partner advertised capabilities */ +#define MC_CMD_GET_LINK_OUT_CAP_OFST 0 +#define MC_CMD_GET_LINK_OUT_LP_CAP_OFST 4 +/* Autonegotiated speed in mbit/s. The link may still be down + * even if this reads non-zero */ +#define MC_CMD_GET_LINK_OUT_LINK_SPEED_OFST 8 +#define MC_CMD_GET_LINK_OUT_LOOPBACK_MODE_OFST 12 +#define MC_CMD_GET_LINK_OUT_FLAGS_OFST 16 +/* Whether we have overall link up */ +#define MC_CMD_GET_LINK_LINK_UP_LBN 0 +#define MC_CMD_GET_LINK_LINK_UP_WIDTH 1 +#define MC_CMD_GET_LINK_FULL_DUPLEX_LBN 1 +#define MC_CMD_GET_LINK_FULL_DUPLEX_WIDTH 1 +/* Whether we have link at the layers provided by the BPX */ +#define MC_CMD_GET_LINK_BPX_LINK_LBN 2 +#define MC_CMD_GET_LINK_BPX_LINK_WIDTH 1 +/* Whether the PHY has external link */ +#define MC_CMD_GET_LINK_PHY_LINK_LBN 3 +#define MC_CMD_GET_LINK_PHY_LINK_WIDTH 1 +#define MC_CMD_GET_LINK_OUT_FCNTL_OFST 20 +#define MC_CMD_GET_LINK_OUT_MAC_FAULT_OFST 24 + +/* MC_CMD_SET_LINK: + * Write the unified MAC/PHY link configuration + * + * A loopback speed of "0" is supported, and means + * (choose any available speed) + * + * Locks required: None + * Return code: 0, EINVAL, ETIME + */ +#define MC_CMD_SET_LINK 0x2a +#define MC_CMD_SET_LINK_IN_LEN 16 +#define MC_CMD_SET_LINK_IN_CAP_OFST 0 +#define MC_CMD_SET_LINK_IN_FLAGS_OFST 4 +#define MC_CMD_SET_LINK_LOWPOWER_LBN 0 +#define MC_CMD_SET_LINK_LOWPOWER_WIDTH 1 +#define MC_CMD_SET_LINK_POWEROFF_LBN 1 +#define MC_CMD_SET_LINK_POWEROFF_WIDTH 1 +#define MC_CMD_SET_LINK_TXDIS_LBN 2 +#define MC_CMD_SET_LINK_TXDIS_WIDTH 1 +#define MC_CMD_SET_LINK_IN_LOOPBACK_MODE_OFST 8 +#define MC_CMD_SET_LINK_IN_LOOPBACK_SPEED_OFST 12 +#define MC_CMD_SET_LINK_OUT_LEN 0 + +/* MC_CMD_SET_ID_LED: + * Set indentification LED state + * + * Locks required: None + * Return code: 0, EINVAL + */ +#define MC_CMD_SET_ID_LED 0x2b +#define MC_CMD_SET_ID_LED_IN_LEN 4 +#define MC_CMD_SET_ID_LED_IN_STATE_OFST 0 +#define MC_CMD_LED_OFF 0 +#define MC_CMD_LED_ON 1 +#define MC_CMD_LED_DEFAULT 2 +#define MC_CMD_SET_ID_LED_OUT_LEN 0 + +/* MC_CMD_SET_MAC: + * Set MAC configuration + * + * The MTU is the MTU programmed directly into the XMAC/GMAC + * (inclusive of EtherII, VLAN, bug16011 padding) + * + * Locks required: None + * Return code: 0, EINVAL + */ +#define MC_CMD_SET_MAC 0x2c +#define MC_CMD_SET_MAC_IN_LEN 24 +#define MC_CMD_SET_MAC_IN_MTU_OFST 0 +#define MC_CMD_SET_MAC_IN_DRAIN_OFST 4 +#define MC_CMD_SET_MAC_IN_ADDR_OFST 8 +#define MC_CMD_SET_MAC_IN_REJECT_OFST 16 +#define MC_CMD_SET_MAC_IN_REJECT_UNCST_LBN 0 +#define MC_CMD_SET_MAC_IN_REJECT_UNCST_WIDTH 1 +#define MC_CMD_SET_MAC_IN_REJECT_BRDCST_LBN 1 +#define MC_CMD_SET_MAC_IN_REJECT_BRDCST_WIDTH 1 +#define MC_CMD_SET_MAC_IN_FCNTL_OFST 20 +#define MC_CMD_SET_MAC_OUT_LEN 0 + +/* MC_CMD_PHY_STATS: + * Get generic PHY statistics + * + * This call returns the statistics for a generic PHY, by direct DMA + * into host memory, in a sparse array (indexed by the enumerate). + * Each value is represented by a 32bit number. + * + * Locks required: None + * Returns: 0, ETIME + * Response methods: shared memory, event + */ +#define MC_CMD_PHY_STATS 0x2d +#define MC_CMD_PHY_STATS_IN_LEN 8 +#define MC_CMD_PHY_STATS_IN_DMA_ADDR_LO_OFST 0 +#define MC_CMD_PHY_STATS_IN_DMA_ADDR_HI_OFST 4 +#define MC_CMD_PHY_STATS_OUT_LEN 0 + +/* Unified MAC statistics enumeration */ +#define MC_CMD_MAC_GENERATION_START 0 +#define MC_CMD_MAC_TX_PKTS 1 +#define MC_CMD_MAC_TX_PAUSE_PKTS 2 +#define MC_CMD_MAC_TX_CONTROL_PKTS 3 +#define MC_CMD_MAC_TX_UNICAST_PKTS 4 +#define MC_CMD_MAC_TX_MULTICAST_PKTS 5 +#define MC_CMD_MAC_TX_BROADCAST_PKTS 6 +#define MC_CMD_MAC_TX_BYTES 7 +#define MC_CMD_MAC_TX_BAD_BYTES 8 +#define MC_CMD_MAC_TX_LT64_PKTS 9 +#define MC_CMD_MAC_TX_64_PKTS 10 +#define MC_CMD_MAC_TX_65_TO_127_PKTS 11 +#define MC_CMD_MAC_TX_128_TO_255_PKTS 12 +#define MC_CMD_MAC_TX_256_TO_511_PKTS 13 +#define MC_CMD_MAC_TX_512_TO_1023_PKTS 14 +#define MC_CMD_MAC_TX_1024_TO_15XX_PKTS 15 +#define MC_CMD_MAC_TX_15XX_TO_JUMBO_PKTS 16 +#define MC_CMD_MAC_TX_GTJUMBO_PKTS 17 +#define MC_CMD_MAC_TX_BAD_FCS_PKTS 18 +#define MC_CMD_MAC_TX_SINGLE_COLLISION_PKTS 19 +#define MC_CMD_MAC_TX_MULTIPLE_COLLISION_PKTS 20 +#define MC_CMD_MAC_TX_EXCESSIVE_COLLISION_PKTS 21 +#define MC_CMD_MAC_TX_LATE_COLLISION_PKTS 22 +#define MC_CMD_MAC_TX_DEFERRED_PKTS 23 +#define MC_CMD_MAC_TX_EXCESSIVE_DEFERRED_PKTS 24 +#define MC_CMD_MAC_TX_NON_TCPUDP_PKTS 25 +#define MC_CMD_MAC_TX_MAC_SRC_ERR_PKTS 26 +#define MC_CMD_MAC_TX_IP_SRC_ERR_PKTS 27 +#define MC_CMD_MAC_RX_PKTS 28 +#define MC_CMD_MAC_RX_PAUSE_PKTS 29 +#define MC_CMD_MAC_RX_GOOD_PKTS 30 +#define MC_CMD_MAC_RX_CONTROL_PKTS 31 +#define MC_CMD_MAC_RX_UNICAST_PKTS 32 +#define MC_CMD_MAC_RX_MULTICAST_PKTS 33 +#define MC_CMD_MAC_RX_BROADCAST_PKTS 34 +#define MC_CMD_MAC_RX_BYTES 35 +#define MC_CMD_MAC_RX_BAD_BYTES 36 +#define MC_CMD_MAC_RX_64_PKTS 37 +#define MC_CMD_MAC_RX_65_TO_127_PKTS 38 +#define MC_CMD_MAC_RX_128_TO_255_PKTS 39 +#define MC_CMD_MAC_RX_256_TO_511_PKTS 40 +#define MC_CMD_MAC_RX_512_TO_1023_PKTS 41 +#define MC_CMD_MAC_RX_1024_TO_15XX_PKTS 42 +#define MC_CMD_MAC_RX_15XX_TO_JUMBO_PKTS 43 +#define MC_CMD_MAC_RX_GTJUMBO_PKTS 44 +#define MC_CMD_MAC_RX_UNDERSIZE_PKTS 45 +#define MC_CMD_MAC_RX_BAD_FCS_PKTS 46 +#define MC_CMD_MAC_RX_OVERFLOW_PKTS 47 +#define MC_CMD_MAC_RX_FALSE_CARRIER_PKTS 48 +#define MC_CMD_MAC_RX_SYMBOL_ERROR_PKTS 49 +#define MC_CMD_MAC_RX_ALIGN_ERROR_PKTS 50 +#define MC_CMD_MAC_RX_LENGTH_ERROR_PKTS 51 +#define MC_CMD_MAC_RX_INTERNAL_ERROR_PKTS 52 +#define MC_CMD_MAC_RX_JABBER_PKTS 53 +#define MC_CMD_MAC_RX_NODESC_DROPS 54 +#define MC_CMD_MAC_RX_LANES01_CHAR_ERR 55 +#define MC_CMD_MAC_RX_LANES23_CHAR_ERR 56 +#define MC_CMD_MAC_RX_LANES01_DISP_ERR 57 +#define MC_CMD_MAC_RX_LANES23_DISP_ERR 58 +#define MC_CMD_MAC_RX_MATCH_FAULT 59 +/* Insert new members here. */ +#define MC_CMD_MAC_GENERATION_END 60 +#define MC_CMD_MAC_NSTATS (MC_CMD_MAC_GENERATION_END+1) + +/* MC_CMD_MAC_STATS: + * Get unified GMAC/XMAC statistics + * + * This call returns unified statistics maintained by the MC as it + * switches between the GMAC and XMAC. The MC will write out all + * supported stats. The driver should zero initialise the buffer to + * guarantee consistent results. + * + * Locks required: None + * Returns: 0 + * Response methods: shared memory, event + */ +#define MC_CMD_MAC_STATS 0x2e +#define MC_CMD_MAC_STATS_IN_LEN 16 +#define MC_CMD_MAC_STATS_IN_DMA_ADDR_LO_OFST 0 +#define MC_CMD_MAC_STATS_IN_DMA_ADDR_HI_OFST 4 +#define MC_CMD_MAC_STATS_IN_CMD_OFST 8 +#define MC_CMD_MAC_STATS_CMD_DMA_LBN 0 +#define MC_CMD_MAC_STATS_CMD_DMA_WIDTH 1 +#define MC_CMD_MAC_STATS_CMD_CLEAR_LBN 1 +#define MC_CMD_MAC_STATS_CMD_CLEAR_WIDTH 1 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE_LBN 2 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE_WIDTH 1 +/* Fields only relevent when PERIODIC_CHANGE is set */ +#define MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE_LBN 3 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE_WIDTH 1 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR_LBN 4 +#define MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR_WIDTH 1 +#define MC_CMD_MAC_STATS_CMD_PERIOD_MS_LBN 16 +#define MC_CMD_MAC_STATS_CMD_PERIOD_MS_WIDTH 16 +#define MC_CMD_MAC_STATS_IN_DMA_LEN_OFST 12 + +#define MC_CMD_MAC_STATS_OUT_LEN 0 + +/* Callisto flags */ +#define MC_CMD_SFT9001_ROBUST_LBN 0 +#define MC_CMD_SFT9001_ROBUST_WIDTH 1 +#define MC_CMD_SFT9001_SHORT_REACH_LBN 1 +#define MC_CMD_SFT9001_SHORT_REACH_WIDTH 1 + +/* MC_CMD_SFT9001_GET: + * Read current callisto specific setting + * + * Locks required: None + * Returns: 0, ETIME + */ +#define MC_CMD_SFT9001_GET 0x30 +#define MC_CMD_SFT9001_GET_IN_LEN 0 +#define MC_CMD_SFT9001_GET_OUT_LEN 4 +#define MC_CMD_SFT9001_GET_OUT_FLAGS_OFST 0 + +/* MC_CMD_SFT9001_SET: + * Write current callisto specific setting + * + * Locks required: None + * Returns: 0, ETIME, EINVAL + */ +#define MC_CMD_SFT9001_SET 0x31 +#define MC_CMD_SFT9001_SET_IN_LEN 4 +#define MC_CMD_SFT9001_SET_IN_FLAGS_OFST 0 +#define MC_CMD_SFT9001_SET_OUT_LEN 0 + + +/* MC_CMD_WOL_FILTER_SET: + * Set a WoL filter + * + * Locks required: None + * Returns: 0, EBUSY, EINVAL, ENOSYS + */ +#define MC_CMD_WOL_FILTER_SET 0x32 +#define MC_CMD_WOL_FILTER_SET_IN_LEN 192 /* 190 rounded up to a word */ +#define MC_CMD_WOL_FILTER_SET_IN_FILTER_MODE_OFST 0 +#define MC_CMD_WOL_FILTER_SET_IN_WOL_TYPE_OFST 4 + +/* There is a union at offset 8, following defines overlap due to + * this */ +#define MC_CMD_WOL_FILTER_SET_IN_DATA_OFST 8 + +#define MC_CMD_WOL_FILTER_SET_IN_MAGIC_MAC_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + +#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_SRC_IP_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST +#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_DST_IP_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 4) +#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_SRC_PORT_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 8) +#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_DST_PORT_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 10) + +#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_SRC_IP_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST +#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_DST_IP_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 16) +#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_SRC_PORT_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 32) +#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_DST_PORT_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 34) + +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_MASK_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 48) +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_LEN_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 176) +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_LAYER3_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 177) +#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_LAYER4_OFST \ + (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 178) + +#define MC_CMD_WOL_FILTER_SET_IN_LINK_MASK_OFST \ + MC_CMD_WOL_FILTER_SET_IN_DATA_OFST +#define MC_CMD_WOL_FILTER_SET_IN_LINK_UP_LBN 0 +#define MC_CMD_WOL_FILTER_SET_IN_LINK_DOWN_LBN 1 + +#define MC_CMD_WOL_FILTER_SET_OUT_LEN 4 +#define MC_CMD_WOL_FILTER_SET_OUT_FILTER_ID_OFST 0 + +/* WOL Filter types enumeration */ +#define MC_CMD_WOL_TYPE_MAGIC 0x0 + /* unused 0x1 */ +#define MC_CMD_WOL_TYPE_WIN_MAGIC 0x2 +#define MC_CMD_WOL_TYPE_IPV4_SYN 0x3 +#define MC_CMD_WOL_TYPE_IPV6_SYN 0x4 +#define MC_CMD_WOL_TYPE_BITMAP 0x5 +#define MC_CMD_WOL_TYPE_LINK 0x6 +#define MC_CMD_WOL_TYPE_MAX 0x7 + +#define MC_CMD_FILTER_MODE_SIMPLE 0x0 +#define MC_CMD_FILTER_MODE_STRUCTURED 0xffffffff + +/* MC_CMD_WOL_FILTER_REMOVE: + * Remove a WoL filter + * + * Locks required: None + * Returns: 0, EINVAL, ENOSYS + */ +#define MC_CMD_WOL_FILTER_REMOVE 0x33 +#define MC_CMD_WOL_FILTER_REMOVE_IN_LEN 4 +#define MC_CMD_WOL_FILTER_REMOVE_IN_FILTER_ID_OFST 0 +#define MC_CMD_WOL_FILTER_REMOVE_OUT_LEN 0 + + +/* MC_CMD_WOL_FILTER_RESET: + * Reset (i.e. remove all) WoL filters + * + * Locks required: None + * Returns: 0, ENOSYS + */ +#define MC_CMD_WOL_FILTER_RESET 0x34 +#define MC_CMD_WOL_FILTER_RESET_IN_LEN 0 +#define MC_CMD_WOL_FILTER_RESET_OUT_LEN 0 + +/* MC_CMD_SET_MCAST_HASH: + * Set the MCASH hash value without otherwise + * reconfiguring the MAC + */ +#define MC_CMD_SET_MCAST_HASH 0x35 +#define MC_CMD_SET_MCAST_HASH_IN_LEN 32 +#define MC_CMD_SET_MCAST_HASH_IN_HASH0_OFST 0 +#define MC_CMD_SET_MCAST_HASH_IN_HASH1_OFST 16 +#define MC_CMD_SET_MCAST_HASH_OUT_LEN 0 + +/* MC_CMD_NVRAM_TYPES: + * Return bitfield indicating available types of virtual NVRAM partitions + * + * Locks required: none + * Returns: 0 + */ +#define MC_CMD_NVRAM_TYPES 0x36 +#define MC_CMD_NVRAM_TYPES_IN_LEN 0 +#define MC_CMD_NVRAM_TYPES_OUT_LEN 4 +#define MC_CMD_NVRAM_TYPES_OUT_TYPES_OFST 0 + +/* Supported NVRAM types */ +#define MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO 0 +#define MC_CMD_NVRAM_TYPE_MC_FW 1 +#define MC_CMD_NVRAM_TYPE_MC_FW_BACKUP 2 +#define MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0 3 +#define MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1 4 +#define MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 5 +#define MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1 6 +#define MC_CMD_NVRAM_TYPE_EXP_ROM 7 +#define MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0 8 +#define MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1 9 +#define MC_CMD_NVRAM_TYPE_PHY_PORT0 10 +#define MC_CMD_NVRAM_TYPE_PHY_PORT1 11 +#define MC_CMD_NVRAM_TYPE_LOG 12 + +/* MC_CMD_NVRAM_INFO: + * Read info about a virtual NVRAM partition + * + * Locks required: none + * Returns: 0, EINVAL (bad type) + */ +#define MC_CMD_NVRAM_INFO 0x37 +#define MC_CMD_NVRAM_INFO_IN_LEN 4 +#define MC_CMD_NVRAM_INFO_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_INFO_OUT_LEN 24 +#define MC_CMD_NVRAM_INFO_OUT_TYPE_OFST 0 +#define MC_CMD_NVRAM_INFO_OUT_SIZE_OFST 4 +#define MC_CMD_NVRAM_INFO_OUT_ERASESIZE_OFST 8 +#define MC_CMD_NVRAM_INFO_OUT_FLAGS_OFST 12 +#define MC_CMD_NVRAM_PROTECTED_LBN 0 +#define MC_CMD_NVRAM_PROTECTED_WIDTH 1 +#define MC_CMD_NVRAM_INFO_OUT_PHYSDEV_OFST 16 +#define MC_CMD_NVRAM_INFO_OUT_PHYSADDR_OFST 20 + +/* MC_CMD_NVRAM_UPDATE_START: + * Start a group of update operations on a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_UPDATE_START 0x38 +#define MC_CMD_NVRAM_UPDATE_START_IN_LEN 4 +#define MC_CMD_NVRAM_UPDATE_START_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_UPDATE_START_OUT_LEN 0 + +/* MC_CMD_NVRAM_READ: + * Read data from a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_READ 0x39 +#define MC_CMD_NVRAM_READ_IN_LEN 12 +#define MC_CMD_NVRAM_READ_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_READ_IN_OFFSET_OFST 4 +#define MC_CMD_NVRAM_READ_IN_LENGTH_OFST 8 +#define MC_CMD_NVRAM_READ_OUT_LEN(_read_bytes) (_read_bytes) +#define MC_CMD_NVRAM_READ_OUT_READ_BUFFER_OFST 0 + +/* MC_CMD_NVRAM_WRITE: + * Write data to a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_WRITE 0x3a +#define MC_CMD_NVRAM_WRITE_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_WRITE_IN_OFFSET_OFST 4 +#define MC_CMD_NVRAM_WRITE_IN_LENGTH_OFST 8 +#define MC_CMD_NVRAM_WRITE_IN_WRITE_BUFFER_OFST 12 +#define MC_CMD_NVRAM_WRITE_IN_LEN(_write_bytes) (12 + _write_bytes) +#define MC_CMD_NVRAM_WRITE_OUT_LEN 0 + +/* MC_CMD_NVRAM_ERASE: + * Erase sector(s) from a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_ERASE 0x3b +#define MC_CMD_NVRAM_ERASE_IN_LEN 12 +#define MC_CMD_NVRAM_ERASE_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_ERASE_IN_OFFSET_OFST 4 +#define MC_CMD_NVRAM_ERASE_IN_LENGTH_OFST 8 +#define MC_CMD_NVRAM_ERASE_OUT_LEN 0 + +/* MC_CMD_NVRAM_UPDATE_FINISH: + * Finish a group of update operations on a virtual NVRAM partition + * + * Locks required: PHY_LOCK if type==*PHY* + * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held) + */ +#define MC_CMD_NVRAM_UPDATE_FINISH 0x3c +#define MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN 8 +#define MC_CMD_NVRAM_UPDATE_FINISH_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_UPDATE_FINISH_IN_REBOOT_OFST 4 +#define MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN 0 + +/* MC_CMD_REBOOT: + * Reboot the MC. + * + * The AFTER_ASSERTION flag is intended to be used when the driver notices + * an assertion failure (at which point it is expected to perform a complete + * tear down and reinitialise), to allow both ports to reset the MC once + * in an atomic fashion. + * + * Production mc firmwares are generally compiled with REBOOT_ON_ASSERT=1, + * which means that they will automatically reboot out of the assertion + * handler, so this is in practise an optional operation. It is still + * recommended that drivers execute this to support custom firmwares + * with REBOOT_ON_ASSERT=0. + * + * Locks required: NONE + * Returns: Nothing. You get back a response with ERR=1, DATALEN=0 + */ +#define MC_CMD_REBOOT 0x3d +#define MC_CMD_REBOOT_IN_LEN 4 +#define MC_CMD_REBOOT_IN_FLAGS_OFST 0 +#define MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION 1 +#define MC_CMD_REBOOT_OUT_LEN 0 + +/* MC_CMD_SCHEDINFO: + * Request scheduler info. from the MC. + * + * Locks required: NONE + * Returns: An array of (timeslice,maximum overrun), one for each thread, + * in ascending order of thread address.s + */ +#define MC_CMD_SCHEDINFO 0x3e +#define MC_CMD_SCHEDINFO_IN_LEN 0 + + +/* MC_CMD_SET_REBOOT_MODE: (debug) + * Set the mode for the next MC reboot. + * + * Locks required: NONE + * + * Sets the reboot mode to the specified value. Returns the old mode. + */ +#define MC_CMD_REBOOT_MODE 0x3f +#define MC_CMD_REBOOT_MODE_IN_LEN 4 +#define MC_CMD_REBOOT_MODE_IN_VALUE_OFST 0 +#define MC_CMD_REBOOT_MODE_OUT_LEN 4 +#define MC_CMD_REBOOT_MODE_OUT_VALUE_OFST 0 +#define MC_CMD_REBOOT_MODE_NORMAL 0 +#define MC_CMD_REBOOT_MODE_SNAPPER 3 + +/* MC_CMD_DEBUG_LOG: + * Null request/response command (debug) + * - sequence number is always zero + * - only supported on the UART interface + * (the same set of bytes is delivered as an + * event over PCI) + */ +#define MC_CMD_DEBUG_LOG 0x40 +#define MC_CMD_DEBUG_LOG_IN_LEN 0 +#define MC_CMD_DEBUG_LOG_OUT_LEN 0 + +/* Generic sensor enumeration. Note that a dual port NIC + * will EITHER expose PHY_COMMON_TEMP OR PHY0_TEMP and + * PHY1_TEMP depending on whether there is a single sensor + * in the vicinity of the two port, or one per port. + */ +#define MC_CMD_SENSOR_CONTROLLER_TEMP 0 /* degC */ +#define MC_CMD_SENSOR_PHY_COMMON_TEMP 1 /* degC */ +#define MC_CMD_SENSOR_CONTROLLER_COOLING 2 /* bool */ +#define MC_CMD_SENSOR_PHY0_TEMP 3 /* degC */ +#define MC_CMD_SENSOR_PHY0_COOLING 4 /* bool */ +#define MC_CMD_SENSOR_PHY1_TEMP 5 /* degC */ +#define MC_CMD_SENSOR_PHY1_COOLING 6 /* bool */ +#define MC_CMD_SENSOR_IN_1V0 7 /* mV */ +#define MC_CMD_SENSOR_IN_1V2 8 /* mV */ +#define MC_CMD_SENSOR_IN_1V8 9 /* mV */ +#define MC_CMD_SENSOR_IN_2V5 10 /* mV */ +#define MC_CMD_SENSOR_IN_3V3 11 /* mV */ +#define MC_CMD_SENSOR_IN_12V0 12 /* mV */ + + +/* Sensor state */ +#define MC_CMD_SENSOR_STATE_OK 0 +#define MC_CMD_SENSOR_STATE_WARNING 1 +#define MC_CMD_SENSOR_STATE_FATAL 2 +#define MC_CMD_SENSOR_STATE_BROKEN 3 + +/* MC_CMD_SENSOR_INFO: + * Returns information about every available sensor. + * + * Each sensor has a single (16bit) value, and a corresponding state. + * The mapping between value and sensor is nominally determined by the + * MC, but in practise is implemented as zero (BROKEN), one (TEMPERATURE), + * or two (VOLTAGE) ranges per sensor per state. + * + * This call returns a mask (32bit) of the sensors that are supported + * by this platform, then an array (indexed by MC_CMD_SENSOR) of byte + * offsets to the per-sensor arrays. Each sensor array has four 16bit + * numbers, min1, max1, min2, max2. + * + * Locks required: None + * Returns: 0 + */ +#define MC_CMD_SENSOR_INFO 0x41 +#define MC_CMD_SENSOR_INFO_IN_LEN 0 +#define MC_CMD_SENSOR_INFO_OUT_MASK_OFST 0 +#define MC_CMD_SENSOR_INFO_OUT_OFFSET_OFST(_x) \ + (4 + (_x)) +#define MC_CMD_SENSOR_INFO_OUT_MIN1_OFST(_ofst) \ + ((_ofst) + 0) +#define MC_CMD_SENSOR_INFO_OUT_MAX1_OFST(_ofst) \ + ((_ofst) + 2) +#define MC_CMD_SENSOR_INFO_OUT_MIN2_OFST(_ofst) \ + ((_ofst) + 4) +#define MC_CMD_SENSOR_INFO_OUT_MAX2_OFST(_ofst) \ + ((_ofst) + 6) + +/* MC_CMD_READ_SENSORS + * Returns the current reading from each sensor + * + * Returns a sparse array of sensor readings (indexed by the sensor + * type) into host memory. Each array element is a dword. + * + * The MC will send a SENSOREVT event every time any sensor changes state. The + * driver is responsible for ensuring that it doesn't miss any events. The board + * will function normally if all sensors are in STATE_OK or state_WARNING. + * Otherwise the board should not be expected to function. + */ +#define MC_CMD_READ_SENSORS 0x42 +#define MC_CMD_READ_SENSORS_IN_LEN 8 +#define MC_CMD_READ_SENSORS_IN_DMA_ADDR_LO_OFST 0 +#define MC_CMD_READ_SENSORS_IN_DMA_ADDR_HI_OFST 4 +#define MC_CMD_READ_SENSORS_OUT_LEN 0 + +/* Sensor reading fields */ +#define MC_CMD_READ_SENSOR_VALUE_LBN 0 +#define MC_CMD_READ_SENSOR_VALUE_WIDTH 16 +#define MC_CMD_READ_SENSOR_STATE_LBN 16 +#define MC_CMD_READ_SENSOR_STATE_WIDTH 8 + + +/* MC_CMD_GET_PHY_STATE: + * Report current state of PHY. A "zombie" PHY is a PHY that has failed to + * boot (e.g. due to missing or corrupted firmware). + * + * Locks required: None + * Return code: 0 + */ +#define MC_CMD_GET_PHY_STATE 0x43 + +#define MC_CMD_GET_PHY_STATE_IN_LEN 0 +#define MC_CMD_GET_PHY_STATE_OUT_LEN 4 +#define MC_CMD_GET_PHY_STATE_STATE_OFST 0 +/* PHY state enumeration: */ +#define MC_CMD_PHY_STATE_OK 1 +#define MC_CMD_PHY_STATE_ZOMBIE 2 + + +/* 802.1Qbb control. 8 Tx queues that map to priorities 0 - 7. Use all 1s to + * disable 802.Qbb for a given priority. */ +#define MC_CMD_SETUP_8021QBB 0x44 +#define MC_CMD_SETUP_8021QBB_IN_LEN 32 +#define MC_CMD_SETUP_8021QBB_OUT_LEN 0 +#define MC_CMD_SETUP_8021QBB_IN_TXQS_OFFST 0 + + +/* MC_CMD_WOL_FILTER_GET: + * Retrieve ID of any WoL filters + * + * Locks required: None + * Returns: 0, ENOSYS + */ +#define MC_CMD_WOL_FILTER_GET 0x45 +#define MC_CMD_WOL_FILTER_GET_IN_LEN 0 +#define MC_CMD_WOL_FILTER_GET_OUT_LEN 4 +#define MC_CMD_WOL_FILTER_GET_OUT_FILTER_ID_OFST 0 + + +/* MC_CMD_ADD_LIGHTSOUT_OFFLOAD: + * Offload a protocol to NIC for lights-out state + * + * Locks required: None + * Returns: 0, ENOSYS + */ +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD 0x46 + +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_LEN 16 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL_OFST 0 + +/* There is a union at offset 4, following defines overlap due to + * this */ +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_DATA_OFST 4 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARPMAC_OFST 4 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARPIP_OFST 10 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NSMAC_OFST 4 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NSSNIPV6_OFST 10 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NSIPV6_OFST 26 + +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN 4 +#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_FILTER_ID_OFST 0 + + +/* MC_CMD_REMOVE_LIGHTSOUT_PROTOCOL_OFFLOAD: + * Offload a protocol to NIC for lights-out state + * + * Locks required: None + * Returns: 0, ENOSYS + */ +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD 0x47 +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN 8 +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN 0 + +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL_OFST 0 +#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_FILTER_ID_OFST 4 + +/* Lights-out offload protocols enumeration */ +#define MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP 0x1 +#define MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS 0x2 + + +/* MC_CMD_MAC_RESET_RESTORE: + * Restore MAC after block reset + * + * Locks required: None + * Returns: 0 + */ + +#define MC_CMD_MAC_RESET_RESTORE 0x48 +#define MC_CMD_MAC_RESET_RESTORE_IN_LEN 0 +#define MC_CMD_MAC_RESET_RESTORE_OUT_LEN 0 + + +/* MC_CMD_TEST_ASSERT: + * Deliberately trigger an assert-detonation in the firmware for testing + * purposes (i.e. to allow tests that the driver copes gracefully). + * + * Locks required: None + * Returns: 0 + */ + +#define MC_CMD_TESTASSERT 0x49 +#define MC_CMD_TESTASSERT_IN_LEN 0 +#define MC_CMD_TESTASSERT_OUT_LEN 0 + +/* MC_CMD_WORKAROUND 0x4a + * + * Enable/Disable a given workaround. The mcfw will return EINVAL if it + * doesn't understand the given workaround number - which should not + * be treated as a hard error by client code. + * + * This op does not imply any semantics about each workaround, that's between + * the driver and the mcfw on a per-workaround basis. + * + * Locks required: None + * Returns: 0, EINVAL + */ +#define MC_CMD_WORKAROUND 0x4a +#define MC_CMD_WORKAROUND_IN_LEN 8 +#define MC_CMD_WORKAROUND_IN_TYPE_OFST 0 +#define MC_CMD_WORKAROUND_BUG17230 1 +#define MC_CMD_WORKAROUND_IN_ENABLED_OFST 4 +#define MC_CMD_WORKAROUND_OUT_LEN 0 + +/* MC_CMD_GET_PHY_MEDIA_INFO: + * Read media-specific data from PHY (e.g. SFP/SFP+ module ID information for + * SFP+ PHYs). + * + * The "media type" can be found via GET_PHY_CFG (GET_PHY_CFG_OUT_MEDIA_TYPE); + * the valid "page number" input values, and the output data, are interpreted + * on a per-type basis. + * + * For SFP+: PAGE=0 or 1 returns a 128-byte block read from module I2C address + * 0xA0 offset 0 or 0x80. + * Anything else: currently undefined. + * + * Locks required: None + * Return code: 0 + */ +#define MC_CMD_GET_PHY_MEDIA_INFO 0x4b +#define MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN 4 +#define MC_CMD_GET_PHY_MEDIA_INFO_IN_PAGE_OFST 0 +#define MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(_num_bytes) (4 + (_num_bytes)) +#define MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATALEN_OFST 0 +#define MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST 4 + +/* MC_CMD_NVRAM_TEST: + * Test a particular NVRAM partition for valid contents (where "valid" + * depends on the type of partition). + * + * Locks required: None + * Return code: 0 + */ +#define MC_CMD_NVRAM_TEST 0x4c +#define MC_CMD_NVRAM_TEST_IN_LEN 4 +#define MC_CMD_NVRAM_TEST_IN_TYPE_OFST 0 +#define MC_CMD_NVRAM_TEST_OUT_LEN 4 +#define MC_CMD_NVRAM_TEST_OUT_RESULT_OFST 0 +#define MC_CMD_NVRAM_TEST_PASS 0 +#define MC_CMD_NVRAM_TEST_FAIL 1 +#define MC_CMD_NVRAM_TEST_NOTSUPP 2 + +/* MC_CMD_MRSFP_TWEAK: (debug) + * Read status and/or set parameters for the "mrsfp" driver in mr_rusty builds. + * I2C I/O expander bits are always read; if equaliser parameters are supplied, + * they are configured first. + * + * Locks required: None + * Return code: 0, EINVAL + */ +#define MC_CMD_MRSFP_TWEAK 0x4d +#define MC_CMD_MRSFP_TWEAK_IN_LEN_READ_ONLY 0 +#define MC_CMD_MRSFP_TWEAK_IN_LEN_EQ_CONFIG 16 +#define MC_CMD_MRSFP_TWEAK_IN_TXEQ_LEVEL_OFST 0 /* 0-6 dB de-emphasis */ +#define MC_CMD_MRSFP_TWEAK_IN_TXEQ_DT_CFG_OFST 4 /* 0-8 low->high ref.V */ +#define MC_CMD_MRSFP_TWEAK_IN_RXEQ_BOOST_OFST 8 /* 0-8 dB boost */ +#define MC_CMD_MRSFP_TWEAK_IN_RXEQ_DT_CFG_OFST 12 /* 0-8 low->high ref.V */ +#define MC_CMD_MRSFP_TWEAK_OUT_LEN 12 +#define MC_CMD_MRSFP_TWEAK_OUT_IOEXP_INPUTS_OFST 0 /* input bits */ +#define MC_CMD_MRSFP_TWEAK_OUT_IOEXP_OUTPUTS_OFST 4 /* output bits */ +#define MC_CMD_MRSFP_TWEAK_OUT_IOEXP_DIRECTION_OFST 8 /* dirs: 0=out, 1=in */ + + +#endif /* MCDI_PCOL_H */ diff -r aa133dee7792 drivers/net/sfc/mcdi_phy.c --- /dev/null +++ b/drivers/net/sfc/mcdi_phy.c @@ -0,0 +1,616 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +/* + * Driver for PHY related operations via MCDI. + */ + +#include "efx.h" +#include "phy.h" +#include "mcdi.h" +#include "mcdi_pcol.h" +#include "mdio_10g.h" + +struct efx_mcdi_phy_cfg { + u32 flags; + u32 type; + u32 supported_cap; + u32 channel; + u32 port; + u32 stats_mask; + u8 name[20]; + u32 media; + u32 mmd_mask; + u8 revision[20]; + u32 forced_cap; +}; + +static int +efx_mcdi_get_phy_cfg(struct efx_nic *efx, struct efx_mcdi_phy_cfg *cfg) +{ + u8 outbuf[MC_CMD_GET_PHY_CFG_OUT_LEN]; + size_t outlen; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_IN_LEN != 0); + BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_OUT_NAME_LEN != sizeof(cfg->name)); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_CFG, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_GET_PHY_CFG_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + cfg->flags = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_FLAGS); + cfg->type = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_TYPE); + cfg->supported_cap = + MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_SUPPORTED_CAP); + cfg->channel = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_CHANNEL); + cfg->port = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_PRT); + cfg->stats_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_STATS_MASK); + memcpy(cfg->name, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_NAME), + sizeof(cfg->name)); + cfg->media = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MEDIA_TYPE); + cfg->mmd_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MMD_MASK); + memcpy(cfg->revision, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_REVISION), + sizeof(cfg->revision)); + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +static int efx_mcdi_set_link(struct efx_nic *efx, u32 capabilities, + u32 flags, u32 loopback_mode, + u32 loopback_speed) +{ + u8 inbuf[MC_CMD_SET_LINK_IN_LEN]; + int rc; + + BUILD_BUG_ON(MC_CMD_SET_LINK_OUT_LEN != 0); + + MCDI_SET_DWORD(inbuf, SET_LINK_IN_CAP, capabilities); + MCDI_SET_DWORD(inbuf, SET_LINK_IN_FLAGS, flags); + MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_MODE, loopback_mode); + MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_SPEED, loopback_speed); + + rc = efx_mcdi_rpc(efx, MC_CMD_SET_LINK, inbuf, sizeof(inbuf), + NULL, 0, NULL); + if (rc) + goto fail; + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +static int efx_mcdi_loopback_modes(struct efx_nic *efx, u64 *loopback_modes) +{ + u8 outbuf[MC_CMD_GET_LOOPBACK_MODES_OUT_LEN]; + size_t outlen; + int rc; + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LOOPBACK_MODES, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + if (outlen < MC_CMD_GET_LOOPBACK_MODES_OUT_LEN) { + rc = -EMSGSIZE; + goto fail; + } + + *loopback_modes = MCDI_QWORD(outbuf, GET_LOOPBACK_MODES_SUGGESTED); + + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_mdio_read(struct efx_nic *efx, unsigned int bus, + unsigned int prtad, unsigned int devad, u16 addr, + u16 *value_out, u32 *status_out) +{ + u8 inbuf[MC_CMD_MDIO_READ_IN_LEN]; + u8 outbuf[MC_CMD_MDIO_READ_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, MDIO_READ_IN_BUS, bus); + MCDI_SET_DWORD(inbuf, MDIO_READ_IN_PRTAD, prtad); + MCDI_SET_DWORD(inbuf, MDIO_READ_IN_DEVAD, devad); + MCDI_SET_DWORD(inbuf, MDIO_READ_IN_ADDR, addr); + + rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_READ, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + *value_out = (u16)MCDI_DWORD(outbuf, MDIO_READ_OUT_VALUE); + *status_out = MCDI_DWORD(outbuf, MDIO_READ_OUT_STATUS); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +int efx_mcdi_mdio_write(struct efx_nic *efx, unsigned int bus, + unsigned int prtad, unsigned int devad, u16 addr, + u16 value, u32 *status_out) +{ + u8 inbuf[MC_CMD_MDIO_WRITE_IN_LEN]; + u8 outbuf[MC_CMD_MDIO_WRITE_OUT_LEN]; + size_t outlen; + int rc; + + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_BUS, bus); + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_PRTAD, prtad); + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_DEVAD, devad); + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_ADDR, addr); + MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_VALUE, value); + + rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_WRITE, inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), &outlen); + if (rc) + goto fail; + + *status_out = MCDI_DWORD(outbuf, MDIO_WRITE_OUT_STATUS); + return 0; + +fail: + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return rc; +} + +static u32 mcdi_to_ethtool_cap(u32 media, u32 cap) +{ + u32 result = 0; + + switch (media) { + case MC_CMD_MEDIA_KX4: + result |= SUPPORTED_Backplane; + if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) + result |= SUPPORTED_1000baseKX_Full; + if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) + result |= SUPPORTED_10000baseKX4_Full; + break; + + case MC_CMD_MEDIA_XFP: + case MC_CMD_MEDIA_SFP_PLUS: + result |= SUPPORTED_FIBRE; + break; + + case MC_CMD_MEDIA_BASE_T: + result |= SUPPORTED_TP; + if (cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN)) + result |= SUPPORTED_10baseT_Half; + if (cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN)) + result |= SUPPORTED_10baseT_Full; + if (cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN)) + result |= SUPPORTED_100baseT_Half; + if (cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN)) + result |= SUPPORTED_100baseT_Full; + if (cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN)) + result |= SUPPORTED_1000baseT_Half; + if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) + result |= SUPPORTED_1000baseT_Full; + if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) + result |= SUPPORTED_10000baseT_Full; + break; + } + + if (cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN)) + result |= SUPPORTED_Pause; + if (cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) + result |= SUPPORTED_Asym_Pause; + if (cap & (1 << MC_CMD_PHY_CAP_AN_LBN)) + result |= SUPPORTED_Autoneg; + + return result; +} + +static u32 ethtool_to_mcdi_cap(u32 cap) +{ + u32 result = 0; + + if (cap & SUPPORTED_10baseT_Half) + result |= (1 << MC_CMD_PHY_CAP_10HDX_LBN); + if (cap & SUPPORTED_10baseT_Full) + result |= (1 << MC_CMD_PHY_CAP_10FDX_LBN); + if (cap & SUPPORTED_100baseT_Half) + result |= (1 << MC_CMD_PHY_CAP_100HDX_LBN); + if (cap & SUPPORTED_100baseT_Full) + result |= (1 << MC_CMD_PHY_CAP_100FDX_LBN); + if (cap & SUPPORTED_1000baseT_Half) + result |= (1 << MC_CMD_PHY_CAP_1000HDX_LBN); + if (cap & (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseKX_Full)) + result |= (1 << MC_CMD_PHY_CAP_1000FDX_LBN); + if (cap & (SUPPORTED_10000baseT_Full | SUPPORTED_10000baseKX4_Full)) + result |= (1 << MC_CMD_PHY_CAP_10000FDX_LBN); + if (cap & SUPPORTED_Pause) + result |= (1 << MC_CMD_PHY_CAP_PAUSE_LBN); + if (cap & SUPPORTED_Asym_Pause) + result |= (1 << MC_CMD_PHY_CAP_ASYM_LBN); + if (cap & SUPPORTED_Autoneg) + result |= (1 << MC_CMD_PHY_CAP_AN_LBN); + + return result; +} + +static u32 efx_get_mcdi_phy_flags(struct efx_nic *efx) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + enum efx_phy_mode mode, supported; + u32 flags; + + /* TODO: Advertise the capabilities supported by this PHY */ + supported = 0; + if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_TXDIS_LBN)) + supported |= PHY_MODE_TX_DISABLED; + if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_LOWPOWER_LBN)) + supported |= PHY_MODE_LOW_POWER; + if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_POWEROFF_LBN)) + supported |= PHY_MODE_OFF; + + mode = efx->phy_mode & supported; + + flags = 0; + if (mode & PHY_MODE_TX_DISABLED) + flags |= (1 << MC_CMD_SET_LINK_TXDIS_LBN); + if (mode & PHY_MODE_LOW_POWER) + flags |= (1 << MC_CMD_SET_LINK_LOWPOWER_LBN); + if (mode & PHY_MODE_OFF) + flags |= (1 << MC_CMD_SET_LINK_POWEROFF_LBN); + + return flags; +} + +static u32 mcdi_to_ethtool_media(u32 media) +{ + switch (media) { + case MC_CMD_MEDIA_XAUI: + case MC_CMD_MEDIA_CX4: + case MC_CMD_MEDIA_KX4: + return PORT_OTHER; + + case MC_CMD_MEDIA_XFP: + case MC_CMD_MEDIA_SFP_PLUS: + return PORT_FIBRE; + + case MC_CMD_MEDIA_BASE_T: + return PORT_TP; + + default: + return PORT_OTHER; + } +} + +static int efx_mcdi_phy_probe(struct efx_nic *efx) +{ + struct efx_mcdi_phy_cfg *phy_data; + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + u32 caps; + int rc; + + /* Initialise and populate phy_data */ + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); + if (phy_data == NULL) + return -ENOMEM; + + rc = efx_mcdi_get_phy_cfg(efx, phy_data); + if (rc != 0) + goto fail; + + /* Read initial link advertisement */ + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), NULL); + if (rc) + goto fail; + + /* Fill out nic state */ + efx->phy_data = phy_data; + efx->phy_type = phy_data->type; + strlcpy(efx->phy_name, phy_data->name, sizeof(efx->phy_name)); + + efx->mdio_bus = phy_data->channel; + efx->mdio.prtad = phy_data->port; + efx->mdio.mmds = phy_data->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22); + efx->mdio.mode_support = 0; + if (phy_data->mmd_mask & (1 << MC_CMD_MMD_CLAUSE22)) + efx->mdio.mode_support |= MDIO_SUPPORTS_C22; + if (phy_data->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22)) + efx->mdio.mode_support |= MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + + caps = MCDI_DWORD(outbuf, GET_LINK_OUT_CAP); + if (caps & (1 << MC_CMD_PHY_CAP_AN_LBN)) + efx->link_advertising = + mcdi_to_ethtool_cap(phy_data->media, caps); + else + phy_data->forced_cap = caps; + + /* Assert that we can map efx -> mcdi loopback modes */ + BUILD_BUG_ON(LOOPBACK_NONE != MC_CMD_LOOPBACK_NONE); + BUILD_BUG_ON(LOOPBACK_DATA != MC_CMD_LOOPBACK_DATA); + BUILD_BUG_ON(LOOPBACK_GMAC != MC_CMD_LOOPBACK_GMAC); + BUILD_BUG_ON(LOOPBACK_XGMII != MC_CMD_LOOPBACK_XGMII); + BUILD_BUG_ON(LOOPBACK_XGXS != MC_CMD_LOOPBACK_XGXS); + BUILD_BUG_ON(LOOPBACK_XAUI != MC_CMD_LOOPBACK_XAUI); + BUILD_BUG_ON(LOOPBACK_GMII != MC_CMD_LOOPBACK_GMII); + BUILD_BUG_ON(LOOPBACK_SGMII != MC_CMD_LOOPBACK_SGMII); + BUILD_BUG_ON(LOOPBACK_XGBR != MC_CMD_LOOPBACK_XGBR); + BUILD_BUG_ON(LOOPBACK_XFI != MC_CMD_LOOPBACK_XFI); + BUILD_BUG_ON(LOOPBACK_XAUI_FAR != MC_CMD_LOOPBACK_XAUI_FAR); + BUILD_BUG_ON(LOOPBACK_GMII_FAR != MC_CMD_LOOPBACK_GMII_FAR); + BUILD_BUG_ON(LOOPBACK_SGMII_FAR != MC_CMD_LOOPBACK_SGMII_FAR); + BUILD_BUG_ON(LOOPBACK_XFI_FAR != MC_CMD_LOOPBACK_XFI_FAR); + BUILD_BUG_ON(LOOPBACK_GPHY != MC_CMD_LOOPBACK_GPHY); + BUILD_BUG_ON(LOOPBACK_PHYXS != MC_CMD_LOOPBACK_PHYXS); + BUILD_BUG_ON(LOOPBACK_PCS != MC_CMD_LOOPBACK_PCS); + BUILD_BUG_ON(LOOPBACK_PMAPMD != MC_CMD_LOOPBACK_PMAPMD); + BUILD_BUG_ON(LOOPBACK_XPORT != MC_CMD_LOOPBACK_XPORT); + BUILD_BUG_ON(LOOPBACK_XGMII_WS != MC_CMD_LOOPBACK_XGMII_WS); + BUILD_BUG_ON(LOOPBACK_XAUI_WS != MC_CMD_LOOPBACK_XAUI_WS); + BUILD_BUG_ON(LOOPBACK_XAUI_WS_FAR != MC_CMD_LOOPBACK_XAUI_WS_FAR); + BUILD_BUG_ON(LOOPBACK_XAUI_WS_NEAR != MC_CMD_LOOPBACK_XAUI_WS_NEAR); + BUILD_BUG_ON(LOOPBACK_GMII_WS != MC_CMD_LOOPBACK_GMII_WS); + BUILD_BUG_ON(LOOPBACK_XFI_WS != MC_CMD_LOOPBACK_XFI_WS); + BUILD_BUG_ON(LOOPBACK_XFI_WS_FAR != MC_CMD_LOOPBACK_XFI_WS_FAR); + BUILD_BUG_ON(LOOPBACK_PHYXS_WS != MC_CMD_LOOPBACK_PHYXS_WS); + + rc = efx_mcdi_loopback_modes(efx, &efx->loopback_modes); + if (rc != 0) + goto fail; + /* The MC indicates that LOOPBACK_NONE is a valid loopback mode, + * but by convention we don't */ + efx->loopback_modes &= ~(1 << LOOPBACK_NONE); + efx->startup_loopback_mode = __ffs(efx->loopback_modes); + + /* Set the initial link mode */ + efx_mcdi_phy_decode_link( + efx, &efx->link_state, + MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED), + MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS), + MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL)); + + /* Default to Autonegotiated flow control if the PHY supports it */ + efx->wanted_fc = EFX_FC_RX | EFX_FC_TX; + if (phy_data->supported_cap & (1 << MC_CMD_PHY_CAP_AN_LBN)) + efx->wanted_fc |= EFX_FC_AUTO; + + return 0; + +fail: + kfree(phy_data); + return rc; +} + +int efx_mcdi_phy_reconfigure(struct efx_nic *efx) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + u32 caps = (efx->link_advertising ? + ethtool_to_mcdi_cap(efx->link_advertising) : + phy_cfg->forced_cap); + + return efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx), + efx->loopback_mode, 0); +} + +void efx_mcdi_phy_decode_link(struct efx_nic *efx, + struct efx_link_state *link_state, + u32 speed, u32 flags, u32 fcntl) +{ + switch (fcntl) { + case MC_CMD_FCNTL_AUTO: + WARN_ON(1); /* This is not a link mode */ + link_state->fc = EFX_FC_AUTO | EFX_FC_TX | EFX_FC_RX; + break; + case MC_CMD_FCNTL_BIDIR: + link_state->fc = EFX_FC_TX | EFX_FC_RX; + break; + case MC_CMD_FCNTL_RESPOND: + link_state->fc = EFX_FC_RX; + break; + default: + WARN_ON(1); + case MC_CMD_FCNTL_OFF: + link_state->fc = 0; + break; + } + + link_state->up = !!(flags & (1 << MC_CMD_GET_LINK_LINK_UP_LBN)); + link_state->fd = !!(flags & (1 << MC_CMD_GET_LINK_FULL_DUPLEX_LBN)); + link_state->speed = speed; +} + +/* Verify that the forced flow control settings (!EFX_FC_AUTO) are + * supported by the link partner. Warn the user if this isn't the case + */ +void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + u32 rmtadv; + + /* The link partner capabilities are only relevent if the + * link supports flow control autonegotiation */ + if (~phy_cfg->supported_cap & (1 << MC_CMD_PHY_CAP_AN_LBN)) + return; + + /* If flow control autoneg is supported and enabled, then fine */ + if (efx->wanted_fc & EFX_FC_AUTO) + return; + + rmtadv = 0; + if (lpa & (1 << MC_CMD_PHY_CAP_PAUSE_LBN)) + rmtadv |= ADVERTISED_Pause; + if (lpa & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) + rmtadv |= ADVERTISED_Asym_Pause; + + if ((efx->wanted_fc & EFX_FC_TX) && rmtadv == ADVERTISED_Asym_Pause) + EFX_ERR(efx, "warning: link partner doesn't support " + "pause frames"); +} + +static bool efx_mcdi_phy_poll(struct efx_nic *efx) +{ + struct efx_link_state old_state = efx->link_state; + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + int rc; + + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), NULL); + if (rc) { + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + efx->link_state.up = false; + } else { + efx_mcdi_phy_decode_link( + efx, &efx->link_state, + MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED), + MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS), + MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL)); + } + + return !efx_link_state_equal(&efx->link_state, &old_state); +} + +static void efx_mcdi_phy_remove(struct efx_nic *efx) +{ + struct efx_mcdi_phy_data *phy_data = efx->phy_data; + + efx->phy_data = NULL; + kfree(phy_data); +} + +static void efx_mcdi_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_ETHTOOL_LP_ADVERTISING) + u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; + int rc; +#endif + + ecmd->supported = + mcdi_to_ethtool_cap(phy_cfg->media, phy_cfg->supported_cap); + ecmd->advertising = efx->link_advertising; + ecmd->speed = efx->link_state.speed; + ecmd->duplex = efx->link_state.fd; + ecmd->port = mcdi_to_ethtool_media(phy_cfg->media); + ecmd->phy_address = phy_cfg->port; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->autoneg = !!(efx->link_advertising & ADVERTISED_Autoneg); +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_ETHTOOL_MDIO_SUPPORT) + ecmd->mdio_support = (efx->mdio.mode_support & + (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22)); +#endif + +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_ETHTOOL_LP_ADVERTISING) + BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); + rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, + outbuf, sizeof(outbuf), NULL); + if (rc) { + EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); + return; + } + ecmd->lp_advertising = + mcdi_to_ethtool_cap(phy_cfg->media, + MCDI_DWORD(outbuf, GET_LINK_OUT_LP_CAP)); +#endif +} + +static int efx_mcdi_phy_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; + u32 caps; + int rc; + + if (ecmd->autoneg) { + caps = (ethtool_to_mcdi_cap(ecmd->advertising) | + 1 << MC_CMD_PHY_CAP_AN_LBN); + } else if (ecmd->duplex) { + switch (ecmd->speed) { + case 10: caps = 1 << MC_CMD_PHY_CAP_10FDX_LBN; break; + case 100: caps = 1 << MC_CMD_PHY_CAP_100FDX_LBN; break; + case 1000: caps = 1 << MC_CMD_PHY_CAP_1000FDX_LBN; break; + case 10000: caps = 1 << MC_CMD_PHY_CAP_10000FDX_LBN; break; + default: return -EINVAL; + } + } else { + switch (ecmd->speed) { + case 10: caps = 1 << MC_CMD_PHY_CAP_10HDX_LBN; break; + case 100: caps = 1 << MC_CMD_PHY_CAP_100HDX_LBN; break; + case 1000: caps = 1 << MC_CMD_PHY_CAP_1000HDX_LBN; break; + default: return -EINVAL; + } + } + + rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx), + efx->loopback_mode, 0); + if (rc) + return rc; + + if (ecmd->autoneg) { + efx_link_set_advertising( + efx, ecmd->advertising | ADVERTISED_Autoneg); + phy_cfg->forced_cap = 0; + } else { + efx_link_set_advertising(efx, 0); + phy_cfg->forced_cap = caps; + } + return 0; +} + +static int efx_mcdi_phy_test_alive(struct efx_nic *efx) +{ + u8 outbuf[MC_CMD_GET_PHY_STATE_OUT_LEN]; + size_t outlen; + int rc; + + BUILD_BUG_ON(MC_CMD_GET_PHY_STATE_IN_LEN != 0); + + rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_STATE, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + return rc; + + if (outlen < MC_CMD_GET_PHY_STATE_OUT_LEN) + return -EMSGSIZE; + if (MCDI_DWORD(outbuf, GET_PHY_STATE_STATE) != MC_CMD_PHY_STATE_OK) + return -EINVAL; + + return 0; +} + +struct efx_phy_operations efx_mcdi_phy_ops = { + .probe = efx_mcdi_phy_probe, + .init = efx_port_dummy_op_int, + .reconfigure = efx_mcdi_phy_reconfigure, + .poll = efx_mcdi_phy_poll, + .fini = efx_port_dummy_op_void, + .remove = efx_mcdi_phy_remove, + .get_settings = efx_mcdi_phy_get_settings, + .set_settings = efx_mcdi_phy_set_settings, + .test_alive = efx_mcdi_phy_test_alive, + .run_tests = NULL, + .test_name = NULL, +}; diff -r aa133dee7792 drivers/net/sfc/mdio_10g.c --- a/drivers/net/sfc/mdio_10g.c +++ b/drivers/net/sfc/mdio_10g.c @@ -1,26 +1,10 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ /* * Useful functions for working with MDIO clause 45 PHYs @@ -30,7 +14,8 @@ #include #include "net_driver.h" #include "mdio_10g.h" -#include "boards.h" +#include "workarounds.h" +#include "nic.h" static const char *mmd_block_names[] = { [0] = "(illegal)", @@ -41,58 +26,70 @@ [MDIO_MMD_DTEXS] = "DTE XS", [MDIO_MMD_TC] = "TC", [MDIO_MMD_AN] = "AN", + [MDIO_MMD_C22EXT] = "c22 ext", }; static const int mmd_block_max = ARRAY_SIZE(mmd_block_names); -const char *mdio_clause45_mmd_name(int mmd) +const char *efx_mdio_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) +unsigned efx_mdio_id_oui(u32 id) +{ + unsigned oui = 0; + int i; + + /* The bits of the OUI are designated a..x, with a=0 and b variable. + * In the id register c is the MSB but the OUI is conventionally + * written as bytes h..a, p..i, x..q. Reorder the bits accordingly. */ + for (i = 0; i < 22; ++i) + if (id & (1 << (i + 10))) + oui |= 1 << (i ^ 7); + + return oui; +} + +int efx_mdio_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)); + efx_mdio_write(port, mmd, MDIO_CTRL1, MDIO_CTRL1_RESET); /* Wait for the reset bit to clear. */ do { msleep(spintime); - ctrl = mdio_clause45_read(port, phy_id, mmd, MDIO_MMDREG_CTRL1); + ctrl = efx_mdio_read(port, mmd, MDIO_CTRL1); spins--; - } while (spins && (ctrl & (1 << MDIO_MMDREG_CTRL1_RESET_LBN))); + } while (spins && (ctrl & MDIO_CTRL1_RESET)); return spins ? spins : -ETIMEDOUT; } -static int mdio_clause45_check_mmd(struct efx_nic *efx, int mmd, - int fault_fatal) +static int efx_mdio_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; + if (mmd != MDIO_MMD_AN) { + /* Read MMD STATUS2 to check it is responding. */ + status = efx_mdio_read(efx, mmd, MDIO_STAT2); + if ((status & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_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) { + status = efx_mdio_read(efx, mmd, MDIO_STAT1); + if (status & MDIO_STAT1_FAULT) { if (fault_fatal) { EFX_ERR(efx, "PHY MMD %s reporting fatal" " fault: status %x\n", mmdname, status); @@ -109,8 +106,7 @@ #define MDIO45_RESET_TIME 1000 /* ms */ #define MDIO45_RESET_ITERS 100 -int mdio_clause45_wait_reset_mmds(struct efx_nic *efx, - unsigned int mmd_mask) +int efx_mdio_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; @@ -124,10 +120,7 @@ in_reset = 0; while (mask) { if (mask & 1) { - stat = mdio_clause45_read(efx, - efx->mii.phy_id, - mmd, - MDIO_MMDREG_CTRL1); + stat = efx_mdio_read(efx, mmd, MDIO_CTRL1); if (stat < 0) { EFX_ERR(efx, "failed to read" " status of MMD %s\n", @@ -135,7 +128,7 @@ mmd_block)); return -EIO; } - if (stat & (1 << MDIO_MMDREG_CTRL1_RESET_LBN)) + if (stat & MDIO_CTRL1_RESET) in_reset |= (1 << mmd); } mask = mask >> 1; @@ -151,28 +144,32 @@ " MMDs still in reset: %x\n", in_reset); rc = -ETIMEDOUT; } + else + EFX_INFO(efx, "mmds %x came out of reset in %d ms\n", + mmd_mask, spintime * (MDIO45_RESET_ITERS - tries)); return rc; } -int mdio_clause45_check_mmds(struct efx_nic *efx, - unsigned int mmd_mask, unsigned int fatal_mask) +int efx_mdio_check_mmds(struct efx_nic *efx, + unsigned int mmd_mask, unsigned int fatal_mask) { - int devices, mmd = 0; - int probe_mmd; + int mmd = 0, probe_mmd, devs1, devs2; + u32 devices; /* 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 : + probe_mmd = (mmd_mask & MDIO_DEVS_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) { + devs1 = efx_mdio_read(efx, probe_mmd, MDIO_DEVS1); + devs2 = efx_mdio_read(efx, probe_mmd, MDIO_DEVS2); + if (devs1 < 0 || devs2 < 0) { EFX_ERR(efx, "failed to read devices present\n"); return -EIO; } + devices = devs1 | (devs2 << 16); if ((devices & mmd_mask) != mmd_mask) { EFX_ERR(efx, "required MMDs not present: got %x, " "wanted %x\n", devices, mmd_mask); @@ -184,7 +181,7 @@ while (mmd_mask) { if (mmd_mask & 1) { int fault_fatal = fatal_mask & 1; - if (mdio_clause45_check_mmd(efx, mmd, fault_fatal)) + if (efx_mdio_check_mmd(efx, mmd, fault_fatal)) return -EIO; } mmd_mask = mmd_mask >> 1; @@ -195,247 +192,198 @@ return 0; } -int mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask) +bool efx_mdio_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; + WARN_ON(LOOPBACK_INTERNAL(efx)); /* 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; + return true; + else if ((1 << efx->loopback_mode) & LOOPBACKS_WS) + return false; + else if (efx_phy_mode_disabled(efx->phy_mode)) + return false; else if (efx->loopback_mode == LOOPBACK_PHYXS) - mmd_mask &= ~(MDIO_MMDREG_DEVS0_PHYXS | - MDIO_MMDREG_DEVS0_PCS | - MDIO_MMDREG_DEVS0_PMAPMD); + mmd_mask &= ~(MDIO_DEVS_PHYXS | + MDIO_DEVS_PCS | + MDIO_DEVS_PMAPMD | + MDIO_DEVS_AN); else if (efx->loopback_mode == LOOPBACK_PCS) - mmd_mask &= ~(MDIO_MMDREG_DEVS0_PCS | - MDIO_MMDREG_DEVS0_PMAPMD); + mmd_mask &= ~(MDIO_DEVS_PCS | + MDIO_DEVS_PMAPMD | + MDIO_DEVS_AN); else if (efx->loopback_mode == LOOPBACK_PMAPMD) - mmd_mask &= ~MDIO_MMDREG_DEVS0_PMAPMD; + mmd_mask &= ~(MDIO_DEVS_PMAPMD | + MDIO_DEVS_AN); - 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; + return mdio45_links_ok(&efx->mdio, mmd_mask); } -void mdio_clause45_transmit_disable(struct efx_nic *efx, int disable) +void efx_mdio_transmit_disable(struct efx_nic *efx) { - 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); + efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, + MDIO_PMA_TXDIS, MDIO_PMD_TXDIS_GLOBAL, + efx->phy_mode & PHY_MODE_TX_DISABLED); } -void mdio_clause45_phy_reconfigure(struct efx_nic *efx) +void efx_mdio_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); + efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, + MDIO_CTRL1, MDIO_PMA_CTRL1_LOOPBACK, + efx->loopback_mode == LOOPBACK_PMAPMD); + efx_mdio_set_flag(efx, MDIO_MMD_PCS, + MDIO_CTRL1, MDIO_PCS_CTRL1_LOOPBACK, + efx->loopback_mode == LOOPBACK_PCS); + efx_mdio_set_flag(efx, MDIO_MMD_PHYXS, + MDIO_CTRL1, MDIO_PHYXS_CTRL1_LOOPBACK, + efx->loopback_mode == LOOPBACK_PHYXS_WS); } -static void mdio_clause45_set_mmd_lpower(struct efx_nic *efx, - int lpower, int mmd) +static void efx_mdio_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; + const char *mmdname __attribute__((unused)) = + STRING_TABLE_LOOKUP(mmd, mmd_block); + int stat = efx_mdio_read(efx, mmd, MDIO_STAT1); 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); + if (stat & MDIO_STAT1_LPOWERABLE) { + efx_mdio_set_flag(efx, mmd, MDIO_CTRL1, + MDIO_CTRL1_LPOWER, lpower); } } -void mdio_clause45_set_mmds_lpower(struct efx_nic *efx, - int low_power, unsigned int mmd_mask) +void efx_mdio_set_mmds_lpower(struct efx_nic *efx, + int low_power, unsigned int mmd_mask) { int mmd = 0; + mmd_mask &= ~MDIO_DEVS_AN; while (mmd_mask) { if (mmd_mask & 1) - mdio_clause45_set_mmd_lpower(efx, low_power, mmd); + efx_mdio_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_mdio_set_settings - Set (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. + * @ecmd: New settings */ -void mdio_clause45_get_settings(struct efx_nic *efx, - struct ethtool_cmd *ecmd) +int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) { - int pma_type; + struct ethtool_cmd prev; - /* 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; - } + efx->phy_op->get_settings(efx, &prev); - pma_type = mdio_clause45_read(efx, efx->mii.phy_id, - MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL2); - pma_type &= MDIO_PMAPMD_CTRL2_TYPE_MASK; + if (ecmd->advertising == prev.advertising && + ecmd->speed == prev.speed && + ecmd->duplex == prev.duplex && + ecmd->port == prev.port && + ecmd->autoneg == prev.autoneg) + return 0; - 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; - } + /* We can only change these settings for -T PHYs */ + if (prev.port != PORT_TP || ecmd->port != PORT_TP) + return -EINVAL; + + /* Check that PHY supports these settings */ + if (!ecmd->autoneg || + (ecmd->advertising | SUPPORTED_Autoneg) & ~prev.supported) + return -EINVAL; + + efx_link_set_advertising(efx, ecmd->advertising | ADVERTISED_Autoneg); + efx_mdio_an_reconfigure(efx); + return 0; } /** - * mdio_clause45_set_settings - Set (some of) the PHY settings over MDIO. + * efx_mdio_an_reconfigure - Push advertising flags and restart autonegotiation * @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) +void efx_mdio_an_reconfigure(struct efx_nic *efx) { - 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; + bool xnp = (efx->link_advertising & ADVERTISED_10000baseT_Full + || EFX_WORKAROUND_13204(efx)); + int reg; + + WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN)); + + /* Set up the base page */ + reg = ADVERTISE_CSMA; + if (efx->link_advertising & ADVERTISED_10baseT_Half) + reg |= ADVERTISE_10HALF; + if (efx->link_advertising & ADVERTISED_10baseT_Full) + reg |= ADVERTISE_10FULL; + if (efx->link_advertising & ADVERTISED_100baseT_Half) + reg |= ADVERTISE_100HALF; + if (efx->link_advertising & ADVERTISED_100baseT_Full) + reg |= ADVERTISE_100FULL; + if (xnp) + reg |= ADVERTISE_RESV; + else if (efx->link_advertising & (ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full)) + reg |= ADVERTISE_NPAGE; + if (efx->link_advertising & ADVERTISED_Pause) + reg |= ADVERTISE_PAUSE_CAP; + if (efx->link_advertising & ADVERTISED_Asym_Pause) + reg |= ADVERTISE_PAUSE_ASYM; + efx_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg); + + /* Set up the (extended) next page if necessary */ + if (efx->phy_op->set_npage_adv) + efx->phy_op->set_npage_adv(efx, efx->link_advertising); + + /* Enable and restart AN */ + reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_CTRL1); + reg |= MDIO_AN_CTRL1_ENABLE; + if (!(EFX_WORKAROUND_15195(efx) && LOOPBACK_EXTERNAL(efx))) + reg |= MDIO_AN_CTRL1_RESTART; + if (xnp) + reg |= MDIO_AN_CTRL1_XNP; + else + reg &= ~MDIO_AN_CTRL1_XNP; + efx_mdio_write(efx, MDIO_MMD_AN, MDIO_CTRL1, reg); } + +enum efx_fc_type efx_mdio_get_pause(struct efx_nic *efx) +{ + BUILD_BUG_ON(EFX_FC_AUTO & (EFX_FC_RX | EFX_FC_TX)); + + if (!(efx->wanted_fc & EFX_FC_AUTO)) + return efx->wanted_fc; + + WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN)); + + return mii_resolve_flowctrl_fdx( + mii_advertise_flowctrl(efx->wanted_fc), + efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_LPA)); +} + +int efx_mdio_test_alive(struct efx_nic *efx) +{ + int rc; + int devad = __ffs(efx->mdio.mmds); + u16 physid1, physid2; + + mutex_lock(&efx->mac_lock); + + physid1 = efx_mdio_read(efx, devad, MDIO_DEVID1); + physid2 = efx_mdio_read(efx, devad, MDIO_DEVID2); + + if ((physid1 == 0x0000) || (physid1 == 0xffff) || + (physid2 == 0x0000) || (physid2 == 0xffff)) { + EFX_ERR(efx, "no MDIO PHY present with ID %d\n", + efx->mdio.prtad); + rc = -EINVAL; + } else { + rc = efx_mdio_check_mmds(efx, efx->mdio.mmds, 0); + } + + mutex_unlock(&efx->mac_lock); + return rc; +} diff -r aa133dee7792 drivers/net/sfc/mdio_10g.h --- a/drivers/net/sfc/mdio_10g.h +++ b/drivers/net/sfc/mdio_10g.h @@ -1,256 +1,61 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_MDIO_10G_H #define EFX_MDIO_10G_H +#include "kernel_compat.h" + /* - * Definitions needed for doing 10G MDIO as specified in clause 45 - * MDIO, which do not appear in Linux yet. Also some helper functions. + * Helper functions for doing 10G MDIO as specified in IEEE 802.3 clause 45. */ #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) +static inline unsigned efx_mdio_id_rev(u32 id) { return id & 0xf; } +static inline unsigned efx_mdio_id_model(u32 id) { return (id >> 4) & 0x3f; } +extern unsigned efx_mdio_id_oui(u32 id); -/* 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) +static inline int efx_mdio_read(struct efx_nic *efx, int devad, int addr) { - 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); + return efx->mdio.mdio_read(efx->net_dev, efx->mdio.prtad, devad, addr); } -static inline void mdio_clause45_unpack(u32 val, u8 *prt, u8 *dev) +static inline void +efx_mdio_write(struct efx_nic *efx, int devad, int addr, int value) { - 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); + efx->mdio.mdio_write(efx->net_dev, efx->mdio.prtad, devad, addr, value); } -static inline int mdio_clause45_read(struct efx_nic *efx, - u8 prt, u8 dev, u16 addr) +static inline u32 efx_mdio_read_id(struct efx_nic *efx, int mmd) { - 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); + u16 id_low = efx_mdio_read(efx, mmd, MDIO_DEVID2); + u16 id_hi = efx_mdio_read(efx, mmd, MDIO_DEVID1); return (id_hi << 16) | (id_low); } -static inline int mdio_clause45_phyxgxs_lane_sync(struct efx_nic *efx) +static inline bool efx_mdio_phyxgxs_lane_sync(struct efx_nic *efx) { - int i, sync, lane_status; + int i, lane_status; + bool sync; for (i = 0; i < 2; ++i) - lane_status = mdio_clause45_read(efx, efx->mii.phy_id, - MDIO_MMD_PHYXS, - MDIO_PHYXS_LANE_STATE); + lane_status = efx_mdio_read(efx, MDIO_MMD_PHYXS, + MDIO_PHYXS_LNSTAT); - sync = (lane_status & (1 << MDIO_PHYXS_LANE_ALIGNED_LBN)) != 0; + sync = !!(lane_status & MDIO_PHYXS_LNSTAT_ALIGN); if (!sync) - EFX_INFO(efx, "XGXS lane status: %x\n", lane_status); + EFX_LOG(efx, "XGXS lane status: %x\n", lane_status); return sync; } -extern const char *mdio_clause45_mmd_name(int mmd); +extern const char *efx_mdio_mmd_name(int mmd); /* * Reset a specific MMD and wait for reset to clear. @@ -258,38 +63,50 @@ * * This function will sleep */ -extern int mdio_clause45_reset_mmd(struct efx_nic *efx, int mmd, - int spins, int spintime); +extern int efx_mdio_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); +/* As efx_mdio_check_mmd but for multiple MMDs */ +int efx_mdio_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); +extern bool efx_mdio_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); +extern void efx_mdio_transmit_disable(struct efx_nic *efx); /* Generic part of reconfigure: set/clear loopback bits */ -extern void mdio_clause45_phy_reconfigure(struct efx_nic *efx); +extern void efx_mdio_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); +extern void efx_mdio_set_mmds_lpower(struct efx_nic *efx, + int low_power, unsigned int mmd_mask); /* Set (some of) the PHY settings over MDIO */ -extern int mdio_clause45_set_settings(struct efx_nic *efx, - struct ethtool_cmd *ecmd); +extern int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd); + +/* Push advertising flags and restart autonegotiation */ +extern void efx_mdio_an_reconfigure(struct efx_nic *efx); + +/* Get pause parameters from AN if available (otherwise return + * requested pause parameters) + */ +enum efx_fc_type efx_mdio_get_pause(struct efx_nic *efx); /* 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); +extern int efx_mdio_wait_reset_mmds(struct efx_nic *efx, + unsigned int mmd_mask); + +/* Set or clear flag, debouncing */ +static inline void +efx_mdio_set_flag(struct efx_nic *efx, int devad, int addr, + int mask, bool state) +{ + mdio_set_flag(&efx->mdio, efx->mdio.prtad, devad, addr, mask, state); +} + +/* Liveness self-test for MDIO PHYs */ +extern int efx_mdio_test_alive(struct efx_nic *efx); #endif /* EFX_MDIO_10G_H */ diff -r aa133dee7792 drivers/net/sfc/mentormac.c --- a/drivers/net/sfc/mentormac.c +++ /dev/null @@ -1,506 +0,0 @@ -/**************************************************************************** - * 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); -} diff -r aa133dee7792 drivers/net/sfc/mtd.c --- a/drivers/net/sfc/mtd.c +++ b/drivers/net/sfc/mtd.c @@ -1,103 +1,85 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ +#include #include #include -#include #include #define EFX_DRIVER_NAME "sfc_mtd" -#include "driverlink_api.h" #include "net_driver.h" #include "spi.h" +#include "efx.h" +#include "nic.h" +#include "mcdi.h" +#include "mcdi_pcol.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_SPI_VERIFY_BUF_LEN 16 +#define EFX_MCDI_CHUNK_LEN 128 -#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. +/* Some partitions should only be written during manufacturing. Not + * only should they not be rewritten later, but exposing all of them + * can easily fill up the MTD table (16 or 32 entries). */ static unsigned int efx_allow_nvconfig_writes; +struct efx_mtd_partition { + struct mtd_info mtd; + union { + struct { + bool updating; + u8 nvram_type; + u16 fw_subtype; + } mcdi; + size_t offset; + }; + const char *type_name; + char name[IFNAMSIZ + 20]; +}; + +struct efx_mtd_ops { + int (*read)(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, u8 *buffer); + int (*erase)(struct mtd_info *mtd, loff_t start, size_t len); + int (*write)(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, const u8 *buffer); + int (*sync)(struct mtd_info *mtd); +}; + struct efx_mtd { - struct 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 list_head node; 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; + const struct efx_spi_device *spi; + const char *name; + const struct efx_mtd_ops *ops; + size_t n_parts; + struct efx_mtd_partition part[0]; }; +#define efx_for_each_partition(part, efx_mtd) \ + for ((part) = &(efx_mtd)->part[0]; \ + (part) != &(efx_mtd)->part[(efx_mtd)->n_parts]; \ + (part)++) + +#define to_efx_mtd_partition(mtd) \ + container_of(mtd, struct efx_mtd_partition, mtd) + +static int falcon_mtd_probe(struct efx_nic *efx); +static int siena_mtd_probe(struct efx_nic *efx); + /* SPI utilities */ -static int efx_spi_fast_wait(struct efx_mtd *efx_mtd) +static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible) { - 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; + const struct efx_spi_device *spi = efx_mtd->spi; + struct efx_nic *efx = efx_mtd->efx; u8 status; int rc, i; @@ -106,8 +88,8 @@ __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)); + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, + &status, sizeof(status)); if (rc) return rc; if (!(status & SPI_STATUS_NRDY)) @@ -115,484 +97,650 @@ if (signal_pending(current)) return -EINTR; } - EFX_ERR(efx_mtd->efx, "timed out waiting for %s\n", efx_mtd->name); + EFX_ERR(efx, "timed out waiting for %s\n", efx_mtd->name); return -ETIMEDOUT; } static int -efx_spi_write_enable(struct efx_mtd *efx_mtd) +efx_spi_unlock(struct efx_nic *efx, const struct efx_spi_device *spi) { - 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); + SPI_STATUS_BP0); u8 status; int rc; - rc = spi->read(spi, efx_mtd->efx, SPI_RDSR, -1, &status, - sizeof(status)); + rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, + &status, sizeof(status)); if (rc) return rc; if (!(status & unlock_mask)) return 0; /* already unlocked */ - rc = efx_spi_write_enable(efx_mtd); + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); if (rc) return rc; - rc = spi->write(spi, efx_mtd->efx, SPI_SST_EWSR, -1, NULL, 0); + rc = falcon_spi_cmd(efx, spi, SPI_SST_EWSR, -1, NULL, NULL, 0); if (rc) return rc; status &= ~unlock_mask; - rc = spi->write(spi, efx_mtd->efx, SPI_WRSR, -1, &status, - sizeof(status)); + rc = falcon_spi_cmd(efx, spi, SPI_WRSR, -1, &status, + NULL, sizeof(status)); if (rc) return rc; - rc = efx_spi_fast_wait(efx_mtd); + rc = falcon_spi_wait_write(efx, spi); 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) +static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t 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; + const struct efx_spi_device *spi = efx_mtd->spi; + struct efx_nic *efx = efx_mtd->efx; + unsigned pos, block_len; + u8 empty[EFX_SPI_VERIFY_BUF_LEN]; + u8 buffer[EFX_SPI_VERIFY_BUF_LEN]; int rc; - rc = down_interruptible(&efx_mtd->access_lock); + if (len != spi->erase_size) + return -EINVAL; + + if (spi->erase_command == 0) + return -EOPNOTSUPP; + + rc = efx_spi_unlock(efx, spi); if (rc) - goto out; - spi = efx_mtd->spi; + return rc; + rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); + if (rc) + return rc; + rc = falcon_spi_cmd(efx, spi, spi->erase_command, start, NULL, + NULL, 0); + if (rc) + return rc; + rc = efx_spi_slow_wait(efx_mtd, false); - 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); + /* Verify the entire region has been wiped */ + memset(empty, 0xff, sizeof(empty)); + for (pos = 0; pos < len; pos += block_len) { + block_len = min(len - pos, sizeof(buffer)); + rc = falcon_spi_read(efx, spi, start + pos, block_len, + NULL, buffer); if (rc) - break; - pos += block_len; + return rc; + if (memcmp(empty, buffer, block_len)) + return -EIO; /* Avoid locking up the system */ cond_resched(); - if (signal_pending(current)) { - rc = -EINTR; - break; - } + if (signal_pending(current)) + return -EINTR; } - 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; -} +/* MTD interface */ 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: + rc = efx_mtd->ops->erase(mtd, erase->addr, erase->len); if (rc == 0) { erase->state = MTD_ERASE_DONE; } else { erase->state = MTD_ERASE_FAILED; +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_MTD_ERASE_FAIL_ADDR) erase->fail_addr = 0xffffffff; +#endif } 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; + struct efx_nic *efx = efx_mtd->efx; int rc; - down(&efx_mtd->access_lock); - rc = efx_spi_slow_wait(efx_mtd, 1); + rc = efx_mtd->ops->sync(mtd); if (rc) - EFX_ERR(efx_mtd->efx, "%s sync failed (%d)\n", - efx_mtd->name, rc); - up(&efx_mtd->access_lock); + EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc); } -/* Driverlink interface */ +static void efx_mtd_remove_partition(struct efx_mtd_partition *part) +{ + int rc; -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); + for (;;) { + rc = del_mtd_device(&part->mtd); + if (rc != -EBUSY) + break; + ssleep(1); + } + WARN_ON(rc); } -static void efx_mtd_reset_resume(struct efx_dl_device *efx_dev, int ok) +static void efx_mtd_remove_device(struct efx_mtd *efx_mtd) { - struct efx_mtd *efx_mtd = efx_dev->priv; + struct efx_mtd_partition *part; - if (!efx_mtd) - return; + efx_for_each_partition(part, efx_mtd) + efx_mtd_remove_partition(part); + list_del(&efx_mtd->node); + kfree(efx_mtd); +} - /* 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); +static void efx_mtd_rename_device(struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + + efx_for_each_partition(part, efx_mtd) + if (efx_nic_rev(efx_mtd->efx) >= EFX_REV_SIENA_A0) + snprintf(part->name, sizeof(part->name), + "%s %s:%02x", efx_mtd->efx->name, + part->type_name, part->mcdi.fw_subtype); + else + snprintf(part->name, sizeof(part->name), + "%s %s", efx_mtd->efx->name, + part->type_name); +} + +static int efx_mtd_probe_device(struct efx_nic *efx, struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + + efx_mtd->efx = efx; + + efx_mtd_rename_device(efx_mtd); + + efx_for_each_partition(part, efx_mtd) { +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_MTD_WRITESIZE) + part->mtd.writesize = 1; +#endif + if (efx_allow_nvconfig_writes) + part->mtd.flags |= MTD_WRITEABLE; + + part->mtd.owner = THIS_MODULE; + part->mtd.priv = efx_mtd; + part->mtd.name = part->name; + part->mtd.erase = efx_mtd_erase; + part->mtd.read = efx_mtd->ops->read; + part->mtd.write = efx_mtd->ops->write; + part->mtd.sync = efx_mtd_sync; + + if (add_mtd_device(&part->mtd)) + goto fail; } - up(&efx_mtd->access_lock); + list_add(&efx_mtd->node, &efx->mtd_list); + return 0; + +fail: + while (part != &efx_mtd->part[0]) { + --part; + efx_mtd_remove_partition(part); + } + /* add_mtd_device() returns 1 if the MTD table is full */ + return -ENOMEM; } -static void efx_mtd_remove(struct efx_dl_device *efx_dev) +void efx_mtd_remove(struct efx_nic *efx) { - struct efx_mtd *efx_mtd = efx_dev->priv; + struct efx_mtd *efx_mtd, *next; - del_mtd_partitions(&efx_mtd->mtd); - kfree(efx_mtd); - efx_dev->priv = NULL; + WARN_ON(efx_dev_registered(efx)); + + list_for_each_entry_safe(efx_mtd, next, &efx->mtd_list, node) + efx_mtd_remove_device(efx_mtd); } -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) +void efx_mtd_rename(struct efx_nic *efx) { - int i; + struct efx_mtd *efx_mtd; - efx_dev->priv = efx_mtd; + ASSERT_RTNL(); - efx_mtd->efx_dev = efx_dev; - efx_mtd->efx = efx; - efx_mtd->spi = spi; - sema_init(&efx_mtd->access_lock, 1); + list_for_each_entry(efx_mtd, &efx->mtd_list, node) + efx_mtd_rename_device(efx_mtd); +} - 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; +int efx_mtd_probe(struct efx_nic *efx) +{ + if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) + return siena_mtd_probe(efx); + else + return falcon_mtd_probe(efx); +} - 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; +/* Implementation of MTD operations for Falcon */ - 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; +static int falcon_mtd_read(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, u8 *buffer) +{ + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + const struct efx_spi_device *spi = efx_mtd->spi; + struct efx_nic *efx = efx_mtd->efx; + int rc; - if (efx_allow_nvconfig_writes) - efx_mtd->part[i].mask_flags &= ~MTD_WRITEABLE; + rc = mutex_lock_interruptible(&efx->spi_lock); + if (rc) + return rc; + rc = falcon_spi_read(efx, spi, part->offset + start, len, + retlen, buffer); + mutex_unlock(&efx->spi_lock); + return rc; +} + +static int falcon_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len) +{ + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + int rc; + + rc = mutex_lock_interruptible(&efx->spi_lock); + if (rc) + return rc; + rc = efx_spi_erase(efx_mtd, part->offset + start, len); + mutex_unlock(&efx->spi_lock); + return rc; +} + +static int falcon_mtd_write(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, const u8 *buffer) +{ + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + const struct efx_spi_device *spi = efx_mtd->spi; + struct efx_nic *efx = efx_mtd->efx; + int rc; + + rc = mutex_lock_interruptible(&efx->spi_lock); + if (rc) + return rc; + rc = falcon_spi_write(efx, spi, part->offset + start, len, + retlen, buffer); + mutex_unlock(&efx->spi_lock); + return rc; +} + +static int falcon_mtd_sync(struct mtd_info *mtd) +{ + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + int rc; + + mutex_lock(&efx->spi_lock); + rc = efx_spi_slow_wait(efx_mtd, true); + mutex_unlock(&efx->spi_lock); + return rc; +} + +static struct efx_mtd_ops falcon_mtd_ops = { + .read = falcon_mtd_read, + .erase = falcon_mtd_erase, + .write = falcon_mtd_write, + .sync = falcon_mtd_sync, +}; + +static int falcon_mtd_probe(struct efx_nic *efx) +{ + struct efx_mtd *efx_mtd; + int rc = -ENODEV; + + ASSERT_RTNL(); + + if (efx->spi_flash) { + size_t n_parts = + (efx->spi_flash->size > FALCON_FLASH_BOOTCODE_START) + ? 2 : 1; + + efx_mtd = kzalloc(sizeof(*efx_mtd) + + n_parts * sizeof(efx_mtd->part[0]), + GFP_KERNEL); + if (!efx_mtd) + return -ENOMEM; + + efx_mtd->spi = efx->spi_flash; + efx_mtd->name = "flash"; + + efx_mtd->ops = &falcon_mtd_ops; + + efx_mtd->n_parts = n_parts; + + efx_mtd->part[0].mtd.type = MTD_NORFLASH; + efx_mtd->part[0].mtd.flags = MTD_CAP_NORFLASH & ~MTD_WRITEABLE; + efx_mtd->part[0].mtd.size = min(efx->spi_flash->size, + FALCON_FLASH_BOOTCODE_START); + efx_mtd->part[0].mtd.erasesize = efx->spi_flash->erase_size; + efx_mtd->part[0].offset = 0; + efx_mtd->part[0].type_name = "sfc_flash_config"; + + if (n_parts > 1) { + efx_mtd->part[1].mtd.type = MTD_NORFLASH; + efx_mtd->part[1].mtd.flags = MTD_CAP_NORFLASH; + efx_mtd->part[1].mtd.size = (efx->spi_flash->size + - FALCON_FLASH_BOOTCODE_START); + efx_mtd->part[1].mtd.erasesize = + efx->spi_flash->erase_size; + efx_mtd->part[1].offset = FALCON_FLASH_BOOTCODE_START; + efx_mtd->part[1].type_name = "sfc_flash_bootrom"; + } + + rc = efx_mtd_probe_device(efx, efx_mtd); + if (rc) { + kfree(efx_mtd); + return rc; + } } - return add_mtd_partitions(&efx_mtd->mtd, efx_mtd->part, num_parts); + if (efx->spi_eeprom) { + size_t n_parts = + (efx->spi_eeprom->size > EFX_EEPROM_BOOTCONFIG_END) + ? 3 + : (efx->spi_eeprom->size > EFX_EEPROM_BOOTCONFIG_START) + ? 2 : 1; + efx_mtd = kzalloc(sizeof(*efx_mtd) + + n_parts * sizeof(efx_mtd->part[0]), + GFP_KERNEL); + if (!efx_mtd) + return -ENOMEM; + + efx_mtd->spi = efx->spi_eeprom; + efx_mtd->name = "EEPROM"; + + efx_mtd->ops = &falcon_mtd_ops; + + efx_mtd->n_parts = n_parts; + + efx_mtd->part[0].mtd.type = MTD_RAM; + efx_mtd->part[0].mtd.flags = MTD_CAP_RAM; + efx_mtd->part[0].mtd.size = min(efx->spi_eeprom->size, + EFX_EEPROM_BOOTCONFIG_START); + efx_mtd->part[0].mtd.erasesize = efx->spi_eeprom->erase_size; + efx_mtd->part[0].offset = 0; + if (efx->spi_eeprom->size >= FALCON_NVCONFIG_END) { + /* Chip and board config are in EEPROM */ + efx_mtd->part[0].mtd.flags &= ~MTD_WRITEABLE; + efx_mtd->part[0].type_name = "sfc_large_config"; + } else { + efx_mtd->part[0].type_name = "sfc_small_config"; + } + + if (n_parts > 1) { + efx_mtd->part[1].mtd.type = MTD_RAM; + efx_mtd->part[1].mtd.flags = MTD_CAP_RAM; + efx_mtd->part[1].mtd.size = + min(efx->spi_eeprom->size, EFX_EEPROM_BOOTCONFIG_END) - + EFX_EEPROM_BOOTCONFIG_START; + efx_mtd->part[1].mtd.erasesize = + efx->spi_eeprom->erase_size; + efx_mtd->part[1].offset = EFX_EEPROM_BOOTCONFIG_START; + efx_mtd->part[1].type_name = "sfc_bootconfig"; + } + + if (n_parts > 2) { + efx_mtd->part[2].mtd.type = MTD_RAM; + efx_mtd->part[2].mtd.flags = MTD_CAP_RAM; + efx_mtd->part[2].mtd.size = + efx->spi_eeprom->size - EFX_EEPROM_BOOTCONFIG_END; + efx_mtd->part[2].mtd.erasesize = + efx->spi_eeprom->erase_size; + efx_mtd->part[2].offset = EFX_EEPROM_BOOTCONFIG_END; + efx_mtd->part[2].type_name = "sfc_eeprom_spare"; + } + + rc = efx_mtd_probe_device(efx, efx_mtd); + if (rc) { + kfree(efx_mtd); + return rc; + } + } + + return rc; } -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) +/* Implementation of MTD operations for Siena */ + +static int siena_mtd_read(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, u8 *buffer) { - 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; + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + loff_t offset = start; + loff_t end = min_t(loff_t, start + len, mtd->size); + size_t chunk; + int rc = 0; + + while (offset < end) { + chunk = min_t(size_t, end - offset, EFX_MCDI_CHUNK_LEN); + rc = efx_mcdi_nvram_read(efx, part->mcdi.nvram_type, offset, + buffer, chunk); + if (rc) + goto out; + offset += chunk; + buffer += chunk; + } +out: + *retlen = offset - start; + return rc; +} + +static int siena_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len) +{ + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + loff_t offset = start & ~((loff_t)(mtd->erasesize - 1)); + loff_t end = min_t(loff_t, start + len, mtd->size); + size_t chunk = part->mtd.erasesize; + int rc = 0; + + if (!part->mcdi.updating) { + rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type); + if (rc) + goto out; + part->mcdi.updating = 1; + } + + /* The MCDI interface can in fact do multiple erase blocks at once; + * but erasing may be slow, so we make multiple calls here to avoid + * tripping the MCDI RPC timeout. */ + while (offset < end) { + rc = efx_mcdi_nvram_erase(efx, part->mcdi.nvram_type, offset, + chunk); + if (rc) + goto out; + offset += chunk; + } +out: + return rc; +} + +static int siena_mtd_write(struct mtd_info *mtd, loff_t start, + size_t len, size_t *retlen, const u8 *buffer) +{ + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + loff_t offset = start; + loff_t end = min_t(loff_t, start + len, mtd->size); + size_t chunk; + int rc = 0; + + if (!part->mcdi.updating) { + rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type); + if (rc) + goto out; + part->mcdi.updating = 1; + } + + while (offset < end) { + chunk = min_t(size_t, end - offset, EFX_MCDI_CHUNK_LEN); + rc = efx_mcdi_nvram_write(efx, part->mcdi.nvram_type, offset, + buffer, chunk); + if (rc) + goto out; + offset += chunk; + buffer += chunk; + } +out: + *retlen = offset - start; + return rc; +} + +static int siena_mtd_sync(struct mtd_info *mtd) +{ + struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); + struct efx_mtd *efx_mtd = mtd->priv; + struct efx_nic *efx = efx_mtd->efx; + int rc = 0; + + if (part->mcdi.updating) { + part->mcdi.updating = 0; + rc = efx_mcdi_nvram_update_finish(efx, part->mcdi.nvram_type); + } + + return rc; +} + +static struct efx_mtd_ops siena_mtd_ops = { + .read = siena_mtd_read, + .erase = siena_mtd_erase, + .write = siena_mtd_write, + .sync = siena_mtd_sync, +}; + +struct siena_nvram_type_info { + int port; + const char *name; +}; + +static struct siena_nvram_type_info siena_nvram_types[] = { + [MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO] = { 0, "sfc_dummy_phy" }, + [MC_CMD_NVRAM_TYPE_MC_FW] = { 0, "sfc_mcfw" }, + [MC_CMD_NVRAM_TYPE_MC_FW_BACKUP] = { 0, "sfc_mcfw_backup" }, + [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0] = { 0, "sfc_static_cfg" }, + [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1] = { 1, "sfc_static_cfg" }, + [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0] = { 0, "sfc_dynamic_cfg" }, + [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1] = { 1, "sfc_dynamic_cfg" }, + [MC_CMD_NVRAM_TYPE_EXP_ROM] = { 0, "sfc_exp_rom" }, + [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0] = { 0, "sfc_exp_rom_cfg" }, + [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1] = { 1, "sfc_exp_rom_cfg" }, + [MC_CMD_NVRAM_TYPE_PHY_PORT0] = { 0, "sfc_phy_fw" }, + [MC_CMD_NVRAM_TYPE_PHY_PORT1] = { 1, "sfc_phy_fw" }, +}; + +static int siena_mtd_probe_partition(struct efx_nic *efx, + struct efx_mtd *efx_mtd, + unsigned int part_id, + unsigned int type) +{ + struct efx_mtd_partition *part = &efx_mtd->part[part_id]; + struct siena_nvram_type_info *info; + size_t size, erase_size; + bool protected; int rc; - if (!efx->spi_flash) + if (type >= ARRAY_SIZE(siena_nvram_types)) return -ENODEV; - efx_mtd = kzalloc(sizeof(*efx_mtd), GFP_KERNEL); + info = &siena_nvram_types[type]; + + if (info->port != efx_port_num(efx)) + return -ENODEV; + + rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &protected); + if (rc) + return rc; + if (protected && !efx_allow_nvconfig_writes) + return -ENODEV; /* hide it */ + + part->mcdi.nvram_type = type; + part->type_name = info->name; + + part->mtd.type = MTD_NORFLASH; + part->mtd.flags = MTD_CAP_NORFLASH; + part->mtd.size = size; + part->mtd.erasesize = erase_size; + + return 0; +} + +static int siena_mtd_get_fw_subtypes(struct efx_nic *efx, + struct efx_mtd *efx_mtd) +{ + struct efx_mtd_partition *part; + uint16_t fw_subtype_list[MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN / + sizeof(uint16_t)]; + int rc; + + rc = efx_mcdi_get_board_cfg(efx, NULL, fw_subtype_list); + if (rc) + return rc; + + efx_for_each_partition(part, efx_mtd) + part->mcdi.fw_subtype = fw_subtype_list[part->mcdi.nvram_type]; + + return 0; +} + +static int siena_mtd_probe(struct efx_nic *efx) +{ + struct efx_mtd *efx_mtd; + int rc = -ENODEV; + u32 nvram_types; + unsigned int type; + + ASSERT_RTNL(); + + rc = efx_mcdi_nvram_types(efx, &nvram_types); + if (rc) + return rc; + + efx_mtd = kzalloc(sizeof(*efx_mtd) + + hweight32(nvram_types) * sizeof(efx_mtd->part[0]), + GFP_KERNEL); if (!efx_mtd) return -ENOMEM; - efx_mtd->mtd.type = MTD_NORFLASH; - efx_mtd->mtd.flags = MTD_CAP_NORFLASH; + efx_mtd->name = "Siena NVRAM manager"; - 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; + efx_mtd->ops = &siena_mtd_ops; - 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; + type = 0; + efx_mtd->n_parts = 0; + + while (nvram_types != 0) { + if (nvram_types & 1) { + rc = siena_mtd_probe_partition(efx, efx_mtd, + efx_mtd->n_parts, type); + if (rc == 0) + efx_mtd->n_parts++; + else if (rc != -ENODEV) + goto fail; + } + type++; + nvram_types >>= 1; } - rc = efx_mtd_register(efx_mtd, efx_dev, efx, efx->spi_flash, - "sfc_flash", part_type_name, num_parts); + rc = siena_mtd_get_fw_subtypes(efx, efx_mtd); + if (rc) + goto fail; + + rc = efx_mtd_probe_device(efx, efx_mtd); +fail: if (rc) kfree(efx_mtd); return rc; } -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"); diff -r aa133dee7792 drivers/net/sfc/net_driver.h --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2005-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ /* Common definitions for all Efx net driver code */ @@ -35,11 +18,10 @@ #include #include #include -#include -#include #include #include #include +#include /* Must come before other headers */ #include "kernel_compat.h" @@ -48,10 +30,14 @@ #include "bitfield.h" #include "driverlink_api.h" #include "driverlink.h" -#include "i2c-direct.h" -/* Sick, but we have no other use for dentry */ -#define dentry proc_dir_entry + #ifdef EFX_USE_DEBUGFS + struct dentry; + typedef struct dentry efx_debugfs_entry; + #else + struct proc_dir_entry; + typedef struct proc_dir_entry efx_debugfs_entry; + #endif /************************************************************************** * @@ -61,7 +47,7 @@ #ifndef EFX_DRIVER_NAME #define EFX_DRIVER_NAME "sfc" #endif -#define EFX_DRIVER_VERSION "2.2.0204" +#define EFX_DRIVER_VERSION "3.0.2.2074" #ifdef EFX_ENABLE_DEBUG #define EFX_BUG_ON_PARANOID(x) BUG_ON(x) @@ -71,29 +57,19 @@ #define EFX_WARN_ON_PARANOID(x) do {} while (0) #endif -#define NET_DEV_REGISTERED(efx) \ - ((efx)->net_dev && \ - ((efx)->net_dev->reg_state == NETREG_REGISTERED)) - -/* 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) (NET_DEV_REGISTERED(efx) ? (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) +dev_err(&((efx)->pci_dev->dev), "ERR: %s " fmt, efx_dev_name(efx), ##args) #define EFX_INFO(efx, fmt, args...) \ -dev_info(&((efx)->pci_dev->dev), "INFO: %s " fmt, NET_DEV_NAME(efx), ##args) +dev_info(&((efx)->pci_dev->dev), "INFO: %s " fmt, efx_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) +dev_info(&((efx)->pci_dev->dev), "DBG: %s " fmt, efx_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) +dev_dbg(&((efx)->pci_dev->dev), "DBG: %s " fmt, efx_dev_name(efx), ##args) #endif #define EFX_TRACE(efx, fmt, args...) do {} while (0) @@ -110,11 +86,6 @@ #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 @@ -122,9 +93,12 @@ **************************************************************************/ #define EFX_MAX_CHANNELS 32 -#define EFX_MAX_TX_QUEUES 1 #define EFX_MAX_RX_QUEUES EFX_MAX_CHANNELS +#define EFX_TX_QUEUE_OFFLOAD_CSUM 0 +#define EFX_TX_QUEUE_NO_CSUM 1 +#define EFX_TX_QUEUE_COUNT 2 + /** * struct efx_special_buffer - An Efx special buffer * @addr: CPU base address of the buffer @@ -137,7 +111,7 @@ * 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. + * Note that for TX and RX descriptor queues live in host memory. * Allocation and freeing procedures must take this into account. */ struct efx_special_buffer { @@ -148,27 +122,35 @@ int entries; }; +enum efx_flush_state { + FLUSH_NONE, + FLUSH_PENDING, + FLUSH_FAILED, + FLUSH_DONE, +}; + /** * struct efx_tx_buffer - An Efx TX buffer * @skb: The associated socket buffer. * Set only on the final fragment of a packet; %NULL for all other * fragments. When this fragment completes, then we can free this * skb. + * @tsoh: The associated TSO header structure, or %NULL if this + * buffer is not a TSO header. * @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; + struct efx_tso_header *tsoh; dma_addr_t dma_addr; unsigned short len; - unsigned char continuation; - unsigned char unmap_single; - dma_addr_t unmap_addr; + bool continuation; + bool unmap_single; unsigned short unmap_len; }; @@ -187,14 +169,14 @@ * * @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 + * @flushed: Used when handling queue flushing * @read_count: Current read pointer. * This is the number of buffers that have been removed from both rings. - * @stopped: Stopped flag. + * @stopped: Stopped count. * 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 @@ -208,19 +190,23 @@ * variable indicates that the queue is full. This is to * avoid cache-line ping-pong between the xmit path and the * completion path. + * @tso_headers_free: A list of TSO headers allocated for this TX queue + * that are not in use, and so available for new TSO sends. The list + * is protected by the TX queue lock. + * @tso_bursts: Number of times TSO xmit invoked by kernel + * @tso_long_headers: Number of packets with headers too long for standard + * blocks + * @tso_packets: Number of packets via the TSO xmit 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 + enum efx_flush_state flushed; /* Members used mainly on the completion path */ unsigned int read_count ____cacheline_aligned_in_smp; @@ -230,6 +216,10 @@ unsigned int insert_count ____cacheline_aligned_in_smp; unsigned int write_count; unsigned int old_read_count; + struct efx_tso_header *tso_headers_free; + unsigned int tso_bursts; + unsigned int tso_long_headers; + unsigned int tso_packets; }; /** @@ -256,7 +246,6 @@ * 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 @@ -286,12 +275,12 @@ * the remaining space in the allocation. * @buf_dma_addr: Page's DMA address. * @buf_data: Page's host address. + * @flushed: Use when handling queue flushing * @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; @@ -313,10 +302,8 @@ struct page *buf_page; dma_addr_t buf_dma_addr; char *buf_data; + enum efx_flush_state flushed; -#ifdef CONFIG_SFC_DEBUGFS - struct dentry *debug_dir; -#endif }; /** @@ -325,7 +312,7 @@ * @dma_addr: DMA base address of the buffer * @len: Buffer length, in bytes * - * Falcon uses these buffers for its interrupt status registers and + * The NIC uses these buffers for its interrupt status registers and * MAC stats dumps. */ struct efx_buffer { @@ -335,83 +322,6 @@ }; - -/** - * struct efx_ssr_conn - Connection state for Soft Segment Reassembly (SSR) aka LRO - * @link: Link for hash table and free list. - * @active_link: Link for active_conns list - * @saddr: Source IP address - * @daddr: Destination IP address - * @source: Source TCP port number - * @dest: Destination TCP port number - * @n_in_order_pkts: Number of in-order packets we've seen with payload. - * @next_seq: Next in-order sequence number. - * @last_pkt_jiffies: Time we last saw a packet on this connection. - * @skb: The SKB we are currently holding. - * If %NULL, then all following fields are undefined. - * @skb_tail: The tail of the frag_list of SKBs we're holding. - * Only valid after at least one merge. - * @eh: The ethernet header of the skb we are holding. - * @iph: The IP header of the skb we are holding. - * @th: The TCP header of the skb we are holding. - * @th_last: The TCP header of the last packet merged. - */ -struct efx_ssr_conn { - struct list_head link; - struct list_head active_link; - unsigned saddr, daddr; - unsigned short source, dest; - unsigned n_in_order_pkts; - unsigned next_seq; - unsigned long last_pkt_jiffies; - struct sk_buff *skb; - struct sk_buff *skb_tail; - struct ethhdr *eh; - struct iphdr *iph; - struct tcphdr *th; - struct tcphdr *th_last; -}; - -/** - * struct efx_ssr_state - Port state for Soft Segment Reassembly (SSR) aka LRO - * @efx: The associated NIC. - * @conns_mask: Number of hash buckets - 1. - * @conns: Hash buckets for tracked connections. - * @conns_n: Length of linked list for each hash bucket. - * @active_conns: Connections that are holding a packet. - * Connections are self-linked when not in this list. - * @free_conns: Free efx_ssr_conn instances. - * @last_purge_jiffies: The value of jiffies last time we purged idle - * connections. - * @n_merges: Number of packets absorbed by SSR. - * @n_bursts: Number of bursts spotted by SSR. - * @n_slow_start: Number of packets not merged because connection may be in - * slow-start. - * @n_misorder: Number of out-of-order packets seen in tracked streams. - * @n_too_many: Incremented when we're trying to track too many streams. - * @n_new_stream: Number of distinct streams we've tracked. - * @n_drop_idle: Number of streams discarded because they went idle. - * @n_drop_closed: Number of streams that have seen a FIN or RST. - */ -struct efx_ssr_state { - struct efx_nic *efx; - unsigned conns_mask; - struct list_head *conns; - unsigned *conns_n; - struct list_head active_conns; - struct list_head free_conns; - unsigned long last_purge_jiffies; - unsigned n_merges; - unsigned n_bursts; - unsigned n_slow_start; - unsigned n_misorder; - unsigned n_too_many; - unsigned n_new_stream; - unsigned n_drop_idle; - unsigned n_drop_closed; -}; - - /* Flags for channel->used_flags */ #define EFX_USED_BY_RX 1 #define EFX_USED_BY_TX 2 @@ -431,13 +341,12 @@ * queue. * * @efx: Associated Efx NIC - * @evqnum: Event queue number * @channel: Channel instance number + * @name: Name for channel and IRQ * @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) + * @irq_moderation: IRQ moderation value (in hardware ticks) * @napi_dev: Net device used with NAPI * @napi_str: NAPI control structure * @reset_work: Scheduled reset work thread @@ -446,52 +355,53 @@ * @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 + * @irq_count: Number of IRQs since last adaptive moderation decision + * @irq_mod_score: IRQ moderation score * @debug_dir: debugfs directory - * @ssr: LRO/SSR 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_frag: Count of RX packets containing fragmented IP * @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_eth_crc_err: Count of RX CRC errors + * @n_rx_mcast_mismatch: Count of unmatched multicast frames * @n_rx_frm_trunc: Count of RX_FRM_TRUNC errors * @n_rx_overlength: Count of RX_OVERLENGTH errors * @n_skbuff_leaks: Count of skbuffs leaked due to RX overrun */ struct efx_channel { struct efx_nic *efx; - int evqnum; int channel; + char name[IFNAMSIZ + 6]; int used_flags; - int enabled; + bool enabled; int irq; - unsigned int has_interrupt; unsigned int irq_moderation; struct net_device *napi_dev; - struct work_struct reset_work; - int work_pending; +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_OLD_NAPI) + struct napi_struct napi_str; +#endif + bool 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 + unsigned int irq_count; + unsigned int irq_mod_score; - struct efx_ssr_state ssr; 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_frag; unsigned n_rx_ip_hdr_chksum_err; unsigned n_rx_tcp_udp_chksum_err; + unsigned n_rx_eth_crc_err; + unsigned n_rx_mcast_mismatch; unsigned n_rx_frm_trunc; unsigned n_rx_overlength; unsigned n_skbuff_leaks; @@ -500,59 +410,14 @@ * access with prefetches. */ struct efx_rx_buffer *rx_pkt; - int rx_pkt_csummed; + bool 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; +enum efx_led_mode { + EFX_LED_OFF = 0, + EFX_LED_ON = 1, + EFX_LED_DEFAULT = 2 }; #define STRING_TABLE_LOOKUP(val, member) \ @@ -573,16 +438,6 @@ #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, @@ -592,27 +447,13 @@ }; #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) +#define EFX_IS10G(efx) ((efx)->link_state.speed == 10000) enum nic_state { STATE_INIT = 0, STATE_RUNNING = 1, STATE_FINI = 2, - STATE_RESETTING = 3, /* rtnl_lock always held */ - STATE_DISABLED = 4, + STATE_DISABLED = 3, STATE_MAX, }; @@ -623,7 +464,7 @@ * This is the equivalent of NET_IP_ALIGN [which controls the alignment * of the skb->head for hardware DMA]. */ -#if defined(__i386__) || defined(__x86_64__) +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS #define EFX_PAGE_IP_ALIGN 0 #else #define EFX_PAGE_IP_ALIGN NET_IP_ALIGN @@ -643,70 +484,100 @@ /* Pseudo bit-mask flow control field */ enum efx_fc_type { - EFX_FC_RX = 1, - EFX_FC_TX = 2, + EFX_FC_RX = FLOW_CTRL_RX, + EFX_FC_TX = FLOW_CTRL_TX, EFX_FC_AUTO = 4, }; /** + * struct efx_link_state - Current state of the link + * @up: Link is up + * @fd: Link is full-duplex + * @fc: Actual flow control flags + * @speed: Link speed (Mbps) + */ +struct efx_link_state { + bool up; + bool fd; + enum efx_fc_type fc; + unsigned int speed; +}; + +static inline bool efx_link_state_equal(const struct efx_link_state *left, + const struct efx_link_state *right) +{ + return left->up == right->up && left->fd == right->fd && + left->fc == right->fc && left->speed == right->speed; +} + +/** * struct efx_mac_operations - Efx MAC operations table - * @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. Serialised by the mac_lock + * @reconfigure: Reconfigure MAC. Serialised by the mac_lock * @update_stats: Update statistics - * @fini: Shut down MAC and PHY - * @check_hw: Check hardware. Serialised by the mac_lock - * @fake_phy_event: Simulate a PHY event on a port - * @get_settings: Get ethtool settings. Serialised by the mac_lock - * @set_settings: Set ethtool settings. Serialised by the mac_lock - * @set_pause: Set pause parameters. Serialised by the mac_lock + * @check_fault: Check fault state. True if fault present. */ 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); + int (*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); + bool (*check_fault)(struct efx_nic *efx); }; /** * struct efx_phy_operations - Efx PHY operations table + * @probe: Probe PHY and initialise efx->mdio.mode_support, efx->mdio.mmds, + * efx->loopback_modes. * @init: Initialise PHY * @fini: Shut down PHY * @reconfigure: Reconfigure PHY (e.g. for new link parameters) - * @clear_interrupt: Clear down interrupt - * @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 + * @poll: Update @link_state and report whether it changed. + * Serialised by the mac_lock. + * @get_settings: Get ethtool settings. Serialised by the mac_lock. + * @set_settings: Set ethtool settings. Serialised by the mac_lock. + * @set_npage_adv: Set abilities advertised in (Extended) Next Page + * (only needed where AN bit is set in mmds) + * @test_alive: Test that PHY is 'alive' (online) + * @test_name: Get the name of a PHY-specific test/result + * @run_tests: Run tests and record results as appropriate (offline). + * Flags are the ethtool tests flags. */ struct efx_phy_operations { + int (*probe) (struct efx_nic *efx); int (*init) (struct efx_nic *efx); void (*fini) (struct efx_nic *efx); - void (*reconfigure) (struct efx_nic *efx); - void (*clear_interrupt) (struct efx_nic *efx); - int (*check_hw) (struct efx_nic *efx); - void (*reset_xaui) (struct efx_nic *efx); - int mmds; - unsigned loopbacks; - unsigned startup_loopback; + void (*remove) (struct efx_nic *efx); + int (*reconfigure) (struct efx_nic *efx); + bool (*poll) (struct efx_nic *efx); + void (*get_settings) (struct efx_nic *efx, + struct ethtool_cmd *ecmd); + int (*set_settings) (struct efx_nic *efx, + struct ethtool_cmd *ecmd); + void (*set_npage_adv) (struct efx_nic *efx, u32); + int (*test_alive) (struct efx_nic *efx); + const char *(*test_name) (struct efx_nic *efx, unsigned int index); + int (*run_tests) (struct efx_nic *efx, int *results, unsigned flags); }; +/** + * @enum efx_phy_mode - PHY operating mode flags + * @PHY_MODE_NORMAL: on and should pass traffic + * @PHY_MODE_TX_DISABLED: on with TX disabled + * @PHY_MODE_LOW_POWER: set to low power through MDIO + * @PHY_MODE_OFF: switched off through external control + * @PHY_MODE_SPECIAL: on but will not pass traffic + */ +enum efx_phy_mode { + PHY_MODE_NORMAL = 0, + PHY_MODE_TX_DISABLED = 1, + PHY_MODE_LOW_POWER = 2, + PHY_MODE_OFF = 4, + PHY_MODE_SPECIAL = 8, +}; + +static inline bool efx_phy_mode_disabled(enum efx_phy_mode mode) +{ + return !!(mode & ~PHY_MODE_TX_DISABLED); +} + /* * Efx extended statistics * @@ -776,6 +647,15 @@ unsigned long rx_length_error; unsigned long rx_internal_error; unsigned long rx_good_lt64; + unsigned long rx_char_error_lane0; + unsigned long rx_char_error_lane1; + unsigned long rx_char_error_lane2; + unsigned long rx_char_error_lane3; + unsigned long rx_disp_error_lane0; + unsigned long rx_disp_error_lane1; + unsigned long rx_disp_error_lane2; + unsigned long rx_disp_error_lane3; + u64 rx_match_fault; }; /* Number of bits used in a multicast filter hash address */ @@ -798,56 +678,67 @@ 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 + * @revision: Hardware architecture revision + * @dl_revision: Revision name for driverlink * @type: Controller type attributes * @legacy_irq: IRQ number - * @workqueue: Workqueue for resets, port reconfigures and the HW monitor + * @workqueue: Workqueue for port reconfigures and the HW monitor. + * Work items do not hold and must not acquire RTNL. + * @workqueue_name: Name of 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) - * @i2c: I2C interface - * @board_info: Board-level information + * @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues + * @irq_rx_moderation: IRQ moderation time for RX event queues * @state: Device state flag. Serialised by the rtnl_lock. * @reset_pending: Pending reset method (normally RESET_TYPE_NONE) * @tx_queue: TX DMA queues * @rx_queue: RX DMA queues * @channel: Channels - * @rss_queues: Number of RSS queues + * @tx_dc_entries: Number of entries in each TX queue descriptor cache + * @rx_dc_entries: Number of entries in each RX queue descriptor cache + * @tx_dc_base: Base address in SRAM of TX queue descriptor caches + * @rx_dc_base: Base address in SRAM of RX queue descriptor caches + * @sram_lim: High address limit of SRAM + * @resources: Driverlink parameters + * @n_rx_queues: Number of RX queues + * @n_channels: Number of channels in use * @rx_buffer_len: RX buffer length * @rx_buffer_order: Order (log2) of number of pages for each RX buffer * @errors: Error condition stats + * @int_error_count: Number of internal errors seen recently + * @int_error_expire: Time at which error count will be expired * @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() + * interrupt is handled. It is used by efx_nic_test_interrupt() * to verify that an interrupt has occurred. * @spi_flash: SPI flash device - * This field will be %NULL if no flash device is present. + * This field will be %NULL if no flash device is present (or for Siena). * @spi_eeprom: SPI EEPROM device - * This field will be %NULL if no EEPROM device is present. + * This field will be %NULL if no EEPROM device is present (or for Siena). * @spi_lock: SPI bus lock + * @mtd_list: List of MTDs attached to the NIC * @n_rx_nodesc_drop_cnt: RX no descriptor drop count * @nic_data: Hardware dependant state - * @mac_lock: MAC access lock. Protects @port_enabled, efx_monitor() and - * efx_reconfigure_port() + * @mac_lock: MAC access lock. Protects @port_enabled, @link_up, @phy_mode, + * @port_inhibited, efx_monitor() and efx_mac_work() + * @mac_work: Work item for changing MAC promiscuity and multicast hash * @port_enabled: Port enabled indicator. - * Serialises efx_stop_all(), efx_start_all() and efx_monitor() and - * efx_reconfigure_work with kernel interfaces. Safe to read under any + * Serialises efx_stop_all(), efx_start_all(), efx_monitor() and + * efx_mac_work() with kernel interfaces. Safe to read under any * one of the rtnl_lock, mac_lock, or netif_tx_lock, but all three must * be held to modify it. + * @port_inhibited: If set, the netif_carrier is always off. Hold the mac_lock * @port_initialized: Port initialized? * @net_dev: Operating system network device. Consider holding the rtnl lock * @rx_checksum_enabled: RX checksumming enabled @@ -857,27 +748,26 @@ * can provide. Generic code converts these into a standard * &struct net_device_stats. * @stats_buffer: DMA buffer for statistics - * @stats_lock: Statistics update lock + * @stats_lock: Statistics update lock. Serialises statistics fetches * @mac_op: MAC interface * @mac_address: Permanent MAC address * @phy_type: PHY type - * @phy_lock: PHY access lock + * @mdio_lock: MDIO lock * @phy_op: PHY interface * @phy_data: PHY private data (including PHY-specific stats) - * @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) + * @mdio: PHY MDIO interface + * @mdio_bus: PHY MDIO bus ID (only used by Siena) + * @phy_mode: PHY operating mode. Serialised by @mac_lock. + * @xmac_poll_required: XMAC link state needs polling + * @link_advertising: Autonegotiation advertising flags + * @link_state: Current state of the link * @n_link_state_changes: Number of times the link has changed state * @promiscuous: Promiscuous flag. Protected by netif_tx_lock. * @multicast_hash: Multicast hash table - * @flow_control: Flow control flags - separate RX/TX so can't use link_options - * @reconfigure_work: work item for dealing with PHY events + * @wanted_fc: Wanted flow control flags * @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 @@ -888,157 +778,230 @@ * @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. + * This is stored in the private area of the &struct net_device. */ struct efx_nic { char name[IFNAMSIZ]; struct pci_dev *pci_dev; - u8 revision; const struct efx_nic_type *type; int legacy_irq; struct workqueue_struct *workqueue; - /* Since we can't use cancel_delayed_work_sync efx_reset() has to - * flush efx->workqueue to serialise against efx_reconfigure_port - * and efx_monitor. So it can't also run on workqueue */ - struct workqueue_struct *reset_workqueue; + char workqueue_name[16]; struct work_struct reset_work; struct delayed_work monitor_work; - unsigned long membase_phys; + resource_size_t 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; - - struct efx_i2c_interface i2c; - struct efx_board board_info; + bool irq_rx_adaptive; + unsigned int irq_rx_moderation; enum nic_state state; enum reset_type reset_pending; - struct efx_tx_queue tx_queue[EFX_MAX_TX_QUEUES]; + struct efx_tx_queue tx_queue[EFX_TX_QUEUE_COUNT]; struct efx_rx_queue rx_queue[EFX_MAX_RX_QUEUES]; struct efx_channel channel[EFX_MAX_CHANNELS]; - int rss_queues; + unsigned tx_dc_entries; + unsigned rx_dc_entries; + unsigned tx_dc_base; + unsigned rx_dc_base; + unsigned sram_lim; + struct efx_dl_falcon_resources resources; + + int n_rx_queues; + int n_channels; unsigned int rx_buffer_len; unsigned int rx_buffer_order; struct efx_nic_errors errors; + unsigned int_error_count; + unsigned long int_error_expire; struct efx_buffer irq_status; volatile signed int last_irq_cpu; + unsigned long irq_zero_count; struct efx_spi_device *spi_flash; struct efx_spi_device *spi_eeprom; struct mutex spi_lock; +#ifdef CONFIG_SFC_MTD + struct list_head mtd_list; +#endif unsigned n_rx_nodesc_drop_cnt; void *nic_data; struct mutex mac_lock; - int port_enabled; + struct work_struct mac_work; + bool port_enabled; + bool port_inhibited; - int port_initialized; + bool port_initialized; struct net_device *net_dev; - int rx_checksum_enabled; - int lro_enabled; + bool rx_checksum_enabled; +#if defined(EFX_USE_KCOMPAT) && !defined(NETIF_F_LRO) + bool lro_enabled; +#endif atomic_t netif_stop_count; spinlock_t netif_stop_lock; struct efx_mac_stats mac_stats; +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_NETDEV_STATS) struct net_device_stats stats; +#endif 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; + unsigned int phy_type; + char phy_name[20]; + struct mutex mdio_lock; struct efx_phy_operations *phy_op; void *phy_data; - struct mii_if_info mii; - unsigned phy_powered; - unsigned tx_disabled; + struct mdio_if_info mdio; + unsigned int mdio_bus; + enum efx_phy_mode phy_mode; - int link_up; - unsigned int link_options; + bool xmac_poll_required; + u32 link_advertising; + struct efx_link_state link_state; unsigned int n_link_state_changes; - int promiscuous; + bool promiscuous; union efx_multicast_hash multicast_hash; - enum efx_fc_type flow_control; - struct work_struct reconfigure_work; + enum efx_fc_type wanted_fc; enum efx_loopback_mode loopback_mode; - unsigned int loopback_modes; - unsigned int startup_loopbacks; - + u64 loopback_modes; + unsigned int startup_loopback_mode; 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 }; +static inline int efx_dev_registered(struct efx_nic *efx) +{ + return efx->net_dev->reg_state == NETREG_REGISTERED; +} + +/* Net device name, for inclusion in log messages if it has been registered. + * Use efx->name not efx->net_dev->name so that races with (un)registration + * are harmless. + */ +static inline const char *efx_dev_name(struct efx_nic *efx) +{ + return efx_dev_registered(efx) ? efx->name : ""; +} + +static inline unsigned int efx_port_num(struct efx_nic *efx) +{ + return PCI_FUNC(efx->pci_dev->devfn); +} + +struct efx_nic_register_mask; +struct efx_nic_table_mask; + /** * struct efx_nic_type - Efx device type definition - * @mem_bar: Memory BAR number + * @probe: Probe the controller + * @remove: Free resources allocated by probe() + * @init: Initialise the controller + * @fini: Shut down the controller + * @monitor: Periodic function for polling link state and hardware monitor + * @reset: Reset the controller hardware and possibly the PHY. This will + * be called while the controller is uninitialised. + * @probe_port: Probe the MAC and PHY + * @remove_port: Free resources allocated by probe_port() + * @prepare_flush: Prepare the hardware for flushing the DMA queues + * @update_stats: Update statistics not provided by event handling + * @start_stats: Start the regular fetching of statistics + * @stop_stats: Stop the regular fetching of statistics + * @set_id_led: Set state of identifying LED or revert to automatic function + * @push_irq_moderation: Apply interrupt moderation value + * @push_multicast_hash: Apply multicast hash table + * @reconfigure_port: Push loopback/power/txdis changes to the MAC and PHY + * @get_wol: Get WoL configuration from driver state + * @set_wol: Push WoL configuration to the NIC + * @resume_wol: Synchronise WoL state between driver and MC (e.g. after resume) + * @test_registers: Test read/write functionality of control registers + * @test_memory: Test read/write functionality of memory blocks, using + * the given test pattern generator + * @test_nvram: Test validity of NVRAM contents + * @default_mac_ops: efx_mac_operations to set at startup + * @revision: Hardware architecture revision + * @dl_revision: Driverlink revision string * @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 + * @resources: Resources to be shared via driverlink (copied and + * updated as efx_nic::resources) + * @offload_features: net_device feature flags for protocol offload + * features implemented in hardware + * @reset_world_flags: Flags for additional components covered by + * reset method RESET_TYPE_WORLD */ struct efx_nic_type { - unsigned int mem_bar; + int (*probe)(struct efx_nic *efx); + void (*remove)(struct efx_nic *efx); + int (*init)(struct efx_nic *efx); + void (*fini)(struct efx_nic *efx); + void (*monitor)(struct efx_nic *efx); + int (*reset)(struct efx_nic *efx, enum reset_type method); + int (*probe_port)(struct efx_nic *efx); + void (*remove_port)(struct efx_nic *efx); + void (*prepare_flush)(struct efx_nic *efx); + void (*update_stats)(struct efx_nic *efx); + void (*start_stats)(struct efx_nic *efx); + void (*stop_stats)(struct efx_nic *efx); + void (*set_id_led)(struct efx_nic *efx, enum efx_led_mode mode); + void (*push_irq_moderation)(struct efx_channel *channel); + void (*push_multicast_hash)(struct efx_nic *efx); + int (*reconfigure_port)(struct efx_nic *efx); + void (*get_wol)(struct efx_nic *efx, struct ethtool_wolinfo *wol); + int (*set_wol)(struct efx_nic *efx, u32 type); + void (*resume_wol)(struct efx_nic *efx); + int (*test_registers)(struct efx_nic *efx); + int (*test_memory)(struct efx_nic *efx, + void (*pattern)(unsigned, efx_qword_t *, int, int), + int a, int b); + int (*test_nvram)(struct efx_nic *efx); + struct efx_mac_operations *default_mac_ops; + + int revision; + const char *dl_revision; unsigned int mem_map_size; unsigned int txd_ptr_tbl_base; unsigned int rxd_ptr_tbl_base; unsigned int buf_tbl_base; unsigned int evq_ptr_tbl_base; unsigned int evq_rptr_tbl_base; - - 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; + u64 max_dma_mask; unsigned int rx_buffer_padding; unsigned int max_interrupt_mode; unsigned int phys_addr_channels; + struct efx_dl_falcon_resources resources; + unsigned long offload_features; + u32 reset_world_flags; }; /************************************************************************** @@ -1056,55 +1019,38 @@ 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 + _tx_queue < &_efx->tx_queue[EFX_TX_QUEUE_COUNT]; \ + _tx_queue++) /* 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 < &_channel->efx->tx_queue[EFX_TX_QUEUE_COUNT]; \ _tx_queue++) \ - if ((!_tx_queue->used) || \ - (_tx_queue->channel != _channel)) \ + if (_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 + _rx_queue < &_efx->rx_queue[_efx->n_rx_queues]; \ + _rx_queue++) /* 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)) \ + for (_rx_queue = &_channel->efx->rx_queue[_channel->channel]; \ + _rx_queue; \ + _rx_queue = NULL) \ + if (_rx_queue->channel != _channel) \ continue; \ else /* Name formats */ -#define EFX_CHANNEL_NAME(_channel) "channel%d", _channel->channel +#define EFX_CHANNEL_NAME(_channel) "chan%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 @@ -1118,13 +1064,13 @@ } /* Set bit in a little-endian bitfield */ -static inline void set_bit_le(int nr, unsigned char *addr) +static inline void set_bit_le(unsigned 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) +static inline void clear_bit_le(unsigned nr, unsigned char *addr) { addr[nr / 8] &= ~(1 << (nr % 8)); } @@ -1139,11 +1085,16 @@ * 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 + * The 10G MAC requires 8-byte alignment on the frame * length, so we round up to the nearest 8. + * + * Re-clocking by the XGXS on RX can reduce an IPG to 32 bits (half an + * XGMII cycle). If the frame length reaches the maximum value in the + * same cycle, the XMAC can miss the IPG altogether. We work around + * this by adding a further 16 bytes. */ #define EFX_MAX_FRAME_LEN(mtu) \ - ((((mtu) + ETH_HLEN + VLAN_HLEN + 4/* FCS */) + 7) & ~7) + ((((mtu) + ETH_HLEN + VLAN_HLEN + 4/* FCS */ + 7) & ~7) + 16) #endif /* EFX_NET_DRIVER_H */ diff -r aa133dee7792 drivers/net/sfc/nic.c --- /dev/null +++ b/drivers/net/sfc/nic.c @@ -0,0 +1,1785 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include "net_driver.h" +#include "bitfield.h" +#include "efx.h" +#include "nic.h" +#include "regs.h" +#include "io.h" +#include "workarounds.h" + +/************************************************************************** + * + * Configurable values + * + ************************************************************************** + */ + +/* Specify the size of the RX descriptor cache. + * Values 16, 32 and 64 are supported (8 won't work). + */ +static int rx_desc_cache_size = 64; + +/* 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. + */ +static int tx_desc_cache_size = 16; + + +/* RX FIFO XOFF watermark + * + * When the amount of the RX FIFO increases used increases past this + * watermark send XOFF. Only used if RX flow control is enabled (ethtool -A) + * This also has an effect on RX/TX arbitration + */ +int efx_nic_rx_xoff_thresh = -1; +module_param_named(rx_xoff_thresh_bytes, efx_nic_rx_xoff_thresh, int, 0644); +MODULE_PARM_DESC(rx_xoff_thresh_bytes, "RX fifo XOFF threshold"); + +/* RX FIFO XON watermark + * + * When the amount of the RX FIFO used decreases below this + * watermark send XON. Only used if TX flow control is enabled (ethtool -A) + * This also has an effect on RX/TX arbitration + */ +int efx_nic_rx_xon_thresh = -1; +module_param_named(rx_xon_thresh_bytes, efx_nic_rx_xon_thresh, int, 0644); +MODULE_PARM_DESC(rx_xon_thresh_bytes, "RX fifo XON threshold"); + +/* If EFX_MAX_INT_ERRORS internal errors occur within + * EFX_INT_ERROR_EXPIRE seconds, we consider the NIC broken and + * disable it. + */ +#define EFX_INT_ERROR_EXPIRE 3600 +#define EFX_MAX_INT_ERRORS 5 + +/* We poll for events every FLUSH_INTERVAL ms, and check FLUSH_POLL_COUNT times + */ +#define EFX_FLUSH_INTERVAL 10 +#define EFX_FLUSH_POLL_COUNT 100 + +/* Size and alignment of special buffers (4KB) */ +#define EFX_BUF_SIZE 4096 + +/* Depth of RX flush request fifo */ +#define EFX_RX_FLUSH_COUNT 4 + +/************************************************************************** + * + * Solarstorm hardware access + * + **************************************************************************/ + +/* Write to a buffer table entry, locking as appropriate. */ +static inline void efx_write_buf_tbl(struct efx_nic *efx, efx_qword_t *value, + unsigned int index) +{ + efx_sram_writeq(efx, efx->membase + efx->type->buf_tbl_base, + value, index); +} + +/* Read the current event from the event queue */ +static inline efx_qword_t *efx_event(struct efx_channel *channel, + unsigned int index) +{ + return (((efx_qword_t *) (channel->eventq.addr)) + index); +} + +/* See if an event is present + * + * We check both the high and low dword of the event for all ones. We + * wrote all ones when we cleared the event, and no valid event can + * have all ones in either its high or low dwords. This approach is + * robust against reordering. + * + * Note that using a single 64-bit comparison is incorrect; even + * though the CPU read will be atomic, the DMA write may not be. + */ +static inline int efx_event_present(efx_qword_t *event) +{ + return (!(EFX_DWORD_IS_ALL_ONES(event->dword[0]) | + EFX_DWORD_IS_ALL_ONES(event->dword[1]))); +} + +static bool efx_masked_compare_oword(const efx_oword_t *a, const efx_oword_t *b, + const efx_oword_t *mask) +{ + return ((a->u64[0] ^ b->u64[0]) & mask->u64[0]) || + ((a->u64[1] ^ b->u64[1]) & mask->u64[1]); +} + +int efx_nic_test_registers(struct efx_nic *efx, + const struct efx_nic_register_test *regs, + size_t n_regs) +{ + unsigned address = 0, i, j; + efx_oword_t mask, imask, original, reg, buf; + + /* Falcon should be in loopback to isolate the XMAC from the PHY */ + WARN_ON(!LOOPBACK_INTERNAL(efx)); + + for (i = 0; i < n_regs; ++i) { + address = regs[i].address; + mask = imask = regs[i].mask; + EFX_INVERT_OWORD(imask); + + efx_reado(efx, &original, address); + + /* bit sweep on and off */ + for (j = 0; j < 128; j++) { + if (!EFX_EXTRACT_OWORD32(mask, j, j)) + continue; + + /* Test this testable bit can be set in isolation */ + EFX_AND_OWORD(reg, original, mask); + EFX_SET_OWORD32(reg, j, j, 1); + + efx_writeo(efx, ®, address); + efx_reado(efx, &buf, address); + + if (efx_masked_compare_oword(®, &buf, &mask)) + goto fail; + + /* Test this testable bit can be cleared in isolation */ + EFX_OR_OWORD(reg, original, mask); + EFX_SET_OWORD32(reg, j, j, 0); + + efx_writeo(efx, ®, address); + efx_reado(efx, &buf, address); + + if (efx_masked_compare_oword(®, &buf, &mask)) + goto fail; + } + + efx_writeo(efx, &original, address); + } + + return 0; + +fail: + EFX_ERR(efx, "wrote "EFX_OWORD_FMT" read "EFX_OWORD_FMT + " at address 0x%x mask "EFX_OWORD_FMT"\n", EFX_OWORD_VAL(reg), + EFX_OWORD_VAL(buf), address, EFX_OWORD_VAL(mask)); + return -EIO; +} + +int efx_nic_test_table(struct efx_nic *efx, + const struct efx_nic_table_test *table, + void (*pattern)(unsigned, efx_qword_t *, int, int), + int a, int b) +{ + efx_oword_t reg, buf; + int index; + + /* write */ + for (index = 0; index < table->rows; index += table->step) { + pattern(2 * index + 0, ®.qword[0], a, b); + pattern(2 * index + 1, ®.qword[1], a, b); + reg.u64[0] &= table->mask.u64[0]; + reg.u64[1] &= table->mask.u64[1]; + + efx_writeo_table(efx, ®, table->address, index); + } + + /* verify */ + for (index = 0; index < table->rows; index += table->step) { + pattern(2 * index + 0, ®.qword[0], a, b); + pattern(2 * index + 1, ®.qword[1], a, b); + reg.u64[0] &= table->mask.u64[0]; + reg.u64[1] &= table->mask.u64[1]; + + efx_reado_table(efx, &buf, table->address, index); + if (memcmp(®, &buf, sizeof(reg))) { + EFX_ERR(efx, "table test failed at address 0x%x" + " index 0x%x. wrote "EFX_OWORD_FMT + " read "EFX_OWORD_FMT"\n", + table->address, index, + EFX_OWORD_VAL(reg), EFX_OWORD_VAL(buf)); + return -EIO; + } + } + + return 0; +} + +/************************************************************************** + * + * Special buffer handling + * Special buffers are used for event queues and the TX and RX + * descriptor rings. + * + *************************************************************************/ + +/* + * Initialise a special buffer + * + * This will define a buffer (previously allocated via + * efx_alloc_special_buffer()) in the buffer table, allowing + * it to be used for event queues, descriptor rings etc. + */ +static void +efx_init_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer) +{ + efx_qword_t buf_desc; + int index; + dma_addr_t dma_addr; + int i; + + EFX_BUG_ON_PARANOID(!buffer->addr); + + /* Write buffer descriptors to NIC */ + for (i = 0; i < buffer->entries; i++) { + index = buffer->index + i; + dma_addr = buffer->dma_addr + (i * 4096); + EFX_LOG(efx, "mapping special buffer %d at %llx\n", + index, (unsigned long long)dma_addr); + EFX_POPULATE_QWORD_3(buf_desc, + FRF_AZ_BUF_ADR_REGION, 0, + FRF_AZ_BUF_ADR_FBUF, dma_addr >> 12, + FRF_AZ_BUF_OWNER_ID_FBUF, 0); + efx_write_buf_tbl(efx, &buf_desc, index); + } +} + +/* Unmaps a buffer and clears the buffer table entries */ +static void +efx_fini_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer) +{ + efx_oword_t buf_tbl_upd; + unsigned int start = buffer->index; + unsigned int end = (buffer->index + buffer->entries - 1); + + if (!buffer->entries) + return; + + EFX_LOG(efx, "unmapping special buffers %d-%d\n", + buffer->index, buffer->index + buffer->entries - 1); + + EFX_POPULATE_OWORD_4(buf_tbl_upd, + FRF_AZ_BUF_UPD_CMD, 0, + FRF_AZ_BUF_CLR_CMD, 1, + FRF_AZ_BUF_CLR_END_ID, end, + FRF_AZ_BUF_CLR_START_ID, start); + efx_writeo(efx, &buf_tbl_upd, FR_AZ_BUF_TBL_UPD); +} + +/* + * Allocate a new special buffer + * + * This allocates memory for a new buffer, clears it and allocates a + * new buffer ID range. It does not write into the buffer table. + * + * This call will allocate 4KB buffers, since 8KB buffers can't be + * used for event queues and descriptor rings. + */ +static int efx_alloc_special_buffer(struct efx_nic *efx, + struct efx_special_buffer *buffer, + unsigned int len) +{ + len = ALIGN(len, EFX_BUF_SIZE); + + buffer->addr = pci_alloc_consistent(efx->pci_dev, len, + &buffer->dma_addr); + if (!buffer->addr) + return -ENOMEM; + buffer->len = len; + buffer->entries = len / EFX_BUF_SIZE; + BUG_ON(buffer->dma_addr & (EFX_BUF_SIZE - 1)); + + /* All zeros is a potentially valid event so memset to 0xff */ + memset(buffer->addr, 0xff, len); + + /* Select new buffer ID */ + buffer->index = efx->resources.buffer_table_min; + efx->resources.buffer_table_min += buffer->entries; + + EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x " + "(virt %p phys %llx)\n", buffer->index, + buffer->index + buffer->entries - 1, + (u64)buffer->dma_addr, len, + buffer->addr, (u64)virt_to_phys(buffer->addr)); + + return 0; +} + +static void +efx_free_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer) +{ + if (!buffer->addr) + return; + + EFX_LOG(efx, "deallocating special buffers %d-%d at %llx+%x " + "(virt %p phys %llx)\n", buffer->index, + buffer->index + buffer->entries - 1, + (u64)buffer->dma_addr, buffer->len, + buffer->addr, (u64)virt_to_phys(buffer->addr)); + + pci_free_consistent(efx->pci_dev, buffer->len, buffer->addr, + buffer->dma_addr); + buffer->addr = NULL; + buffer->entries = 0; +} + +/************************************************************************** + * + * Generic buffer handling + * These buffers are used for interrupt status and MAC stats + * + **************************************************************************/ + +int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer, + unsigned int len) +{ + buffer->addr = pci_alloc_consistent(efx->pci_dev, len, + &buffer->dma_addr); + if (!buffer->addr) + return -ENOMEM; + buffer->len = len; + memset(buffer->addr, 0, len); + return 0; +} + +void efx_nic_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer) +{ + if (buffer->addr) { + pci_free_consistent(efx->pci_dev, buffer->len, + buffer->addr, buffer->dma_addr); + buffer->addr = NULL; + } +} + +/************************************************************************** + * + * TX path + * + **************************************************************************/ + +/* Returns a pointer to the specified transmit descriptor in the TX + * descriptor queue belonging to the specified channel. + */ +static inline efx_qword_t * +efx_tx_desc(struct efx_tx_queue *tx_queue, unsigned int index) +{ + return (((efx_qword_t *) (tx_queue->txd.addr)) + index); +} + +/* This writes to the TX_DESC_WPTR; write pointer for TX descriptor ring */ +static inline void efx_notify_tx_desc(struct efx_tx_queue *tx_queue) +{ + unsigned write_ptr; + efx_dword_t reg; + + write_ptr = tx_queue->write_count & EFX_TXQ_MASK; + EFX_POPULATE_DWORD_1(reg, FRF_AZ_TX_DESC_WPTR_DWORD, write_ptr); + efx_writed_page(tx_queue->efx, ®, + FR_AZ_TX_DESC_UPD_DWORD_P0, tx_queue->queue); +} + + +/* For each entry inserted into the software descriptor ring, create a + * descriptor in the hardware TX descriptor ring (in host memory), and + * write a doorbell. + */ +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +void fastcall efx_nic_push_buffers(struct efx_tx_queue *tx_queue) +#else +void efx_nic_push_buffers(struct efx_tx_queue *tx_queue) +#endif +{ + + struct efx_tx_buffer *buffer; + efx_qword_t *txd; + unsigned write_ptr; + + BUG_ON(tx_queue->write_count == tx_queue->insert_count); + + do { + write_ptr = tx_queue->write_count & EFX_TXQ_MASK; + buffer = &tx_queue->buffer[write_ptr]; + txd = efx_tx_desc(tx_queue, write_ptr); + ++tx_queue->write_count; + + /* Create TX descriptor ring entry */ + EFX_POPULATE_QWORD_4(*txd, + FSF_AZ_TX_KER_CONT, buffer->continuation, + FSF_AZ_TX_KER_BYTE_COUNT, buffer->len, + FSF_AZ_TX_KER_BUF_REGION, 0, + FSF_AZ_TX_KER_BUF_ADDR, buffer->dma_addr); + } while (tx_queue->write_count != tx_queue->insert_count); + + wmb(); /* Ensure descriptors are written before they are fetched */ + efx_notify_tx_desc(tx_queue); +} + +/* Allocate hardware resources for a TX queue */ +int efx_nic_probe_tx(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + int rc; + + BUILD_BUG_ON(EFX_TXQ_SIZE < 512 || EFX_TXQ_SIZE > 4096 || + EFX_TXQ_SIZE & EFX_TXQ_MASK); + rc = efx_alloc_special_buffer(efx, &tx_queue->txd, + EFX_TXQ_SIZE * sizeof(efx_qword_t)); + if (rc) + return rc; + + efx->resources.txq_min = max(efx->resources.txq_min, + (unsigned)tx_queue->queue + 1); + + return 0; +} + +void efx_nic_init_tx(struct efx_tx_queue *tx_queue) +{ + efx_oword_t tx_desc_ptr; + struct efx_nic *efx = tx_queue->efx; + + tx_queue->flushed = FLUSH_NONE; + + /* Pin TX descriptor ring */ + efx_init_special_buffer(efx, &tx_queue->txd); + + /* Push TX descriptor ring to card */ + EFX_POPULATE_OWORD_10(tx_desc_ptr, + FRF_AZ_TX_DESCQ_EN, 1, + FRF_AZ_TX_ISCSI_DDIG_EN, 0, + FRF_AZ_TX_ISCSI_HDIG_EN, 0, + FRF_AZ_TX_DESCQ_BUF_BASE_ID, tx_queue->txd.index, + FRF_AZ_TX_DESCQ_EVQ_ID, + tx_queue->channel->channel, + FRF_AZ_TX_DESCQ_OWNER_ID, 0, + FRF_AZ_TX_DESCQ_LABEL, tx_queue->queue, + FRF_AZ_TX_DESCQ_SIZE, + __ffs(tx_queue->txd.entries), + FRF_AZ_TX_DESCQ_TYPE, 0, + FRF_BZ_TX_NON_IP_DROP_DIS, 1); + + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) { + int csum = tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM; + EFX_SET_OWORD_FIELD(tx_desc_ptr, FRF_BZ_TX_IP_CHKSM_DIS, !csum); + EFX_SET_OWORD_FIELD(tx_desc_ptr, FRF_BZ_TX_TCP_CHKSM_DIS, + !csum); + } + + efx_writeo_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, + tx_queue->queue); + + if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) { + efx_oword_t reg; + + /* Only 128 bits in this register */ + BUILD_BUG_ON(EFX_TX_QUEUE_COUNT >= 128); + + efx_reado(efx, ®, FR_AA_TX_CHKSM_CFG); + if (tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM) + clear_bit_le(tx_queue->queue, (void *)®); + else + set_bit_le(tx_queue->queue, (void *)®); + efx_writeo(efx, ®, FR_AA_TX_CHKSM_CFG); + } +} + +static void efx_flush_tx_queue(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + efx_oword_t tx_flush_descq; + + tx_queue->flushed = FLUSH_PENDING; + + /* Post a flush command */ + EFX_POPULATE_OWORD_2(tx_flush_descq, + FRF_AZ_TX_FLUSH_DESCQ_CMD, 1, + FRF_AZ_TX_FLUSH_DESCQ, tx_queue->queue); + efx_writeo(efx, &tx_flush_descq, FR_AZ_TX_FLUSH_DESCQ); +} + +void efx_nic_fini_tx(struct efx_tx_queue *tx_queue) +{ + struct efx_nic *efx = tx_queue->efx; + efx_oword_t tx_desc_ptr; + + /* The queue should have been flushed */ + WARN_ON(tx_queue->flushed != FLUSH_DONE); + + /* Remove TX descriptor ring from card */ + EFX_ZERO_OWORD(tx_desc_ptr); + efx_writeo_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base, + tx_queue->queue); + + /* Unpin TX descriptor ring */ + efx_fini_special_buffer(efx, &tx_queue->txd); +} + +/* Free buffers backing TX queue */ +void efx_nic_remove_tx(struct efx_tx_queue *tx_queue) +{ + efx_free_special_buffer(tx_queue->efx, &tx_queue->txd); +} + +/************************************************************************** + * + * RX path + * + **************************************************************************/ + +/* Returns a pointer to the specified descriptor in the RX descriptor queue */ +static inline efx_qword_t * +efx_rx_desc(struct efx_rx_queue *rx_queue, unsigned int index) +{ + return (((efx_qword_t *) (rx_queue->rxd.addr)) + index); +} + +/* This creates an entry in the RX descriptor queue */ +static inline void +efx_build_rx_desc(struct efx_rx_queue *rx_queue, unsigned index) +{ + struct efx_rx_buffer *rx_buf; + efx_qword_t *rxd; + + rxd = efx_rx_desc(rx_queue, index); + rx_buf = efx_rx_buffer(rx_queue, index); + EFX_POPULATE_QWORD_3(*rxd, + FSF_AZ_RX_KER_BUF_SIZE, + rx_buf->len - + rx_queue->efx->type->rx_buffer_padding, + FSF_AZ_RX_KER_BUF_REGION, 0, + FSF_AZ_RX_KER_BUF_ADDR, rx_buf->dma_addr); +} + +/* This writes to the RX_DESC_WPTR register for the specified receive + * descriptor ring. + */ +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +void fastcall efx_nic_notify_rx_desc(struct efx_rx_queue *rx_queue) +#else +void efx_nic_notify_rx_desc(struct efx_rx_queue *rx_queue) +#endif +{ + efx_dword_t reg; + unsigned write_ptr; + + while (rx_queue->notified_count != rx_queue->added_count) { + efx_build_rx_desc(rx_queue, + rx_queue->notified_count & + EFX_RXQ_MASK); + ++rx_queue->notified_count; + } + + wmb(); + write_ptr = rx_queue->added_count & EFX_RXQ_MASK; + EFX_POPULATE_DWORD_1(reg, FRF_AZ_RX_DESC_WPTR_DWORD, write_ptr); + efx_writed_page(rx_queue->efx, ®, + FR_AZ_RX_DESC_UPD_DWORD_P0, rx_queue->queue); +} + +int efx_nic_probe_rx(struct efx_rx_queue *rx_queue) +{ + struct efx_nic *efx = rx_queue->efx; + int rc; + + BUILD_BUG_ON(EFX_RXQ_SIZE < 512 || EFX_RXQ_SIZE > 4096 || + EFX_RXQ_SIZE & EFX_RXQ_MASK); + rc = efx_alloc_special_buffer(efx, &rx_queue->rxd, + EFX_RXQ_SIZE * sizeof(efx_qword_t)); + if (rc) + return rc; + + /* Increment the rxq_min counter */ + efx->resources.rxq_min = max(efx->resources.rxq_min, + (unsigned)rx_queue->queue + 1); + + return 0; +} + +void efx_nic_init_rx(struct efx_rx_queue *rx_queue) +{ + efx_oword_t rx_desc_ptr; + struct efx_nic *efx = rx_queue->efx; + bool is_b0 = efx_nic_rev(efx) >= EFX_REV_FALCON_B0; + bool iscsi_digest_en = is_b0; + + EFX_LOG(efx, "RX queue %d ring in special buffers %d-%d\n", + rx_queue->queue, rx_queue->rxd.index, + rx_queue->rxd.index + rx_queue->rxd.entries - 1); + + rx_queue->flushed = FLUSH_NONE; + + /* Pin RX descriptor ring */ + efx_init_special_buffer(efx, &rx_queue->rxd); + + /* Push RX descriptor ring to card */ + EFX_POPULATE_OWORD_10(rx_desc_ptr, + FRF_AZ_RX_ISCSI_DDIG_EN, iscsi_digest_en, + FRF_AZ_RX_ISCSI_HDIG_EN, iscsi_digest_en, + FRF_AZ_RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index, + FRF_AZ_RX_DESCQ_EVQ_ID, + rx_queue->channel->channel, + FRF_AZ_RX_DESCQ_OWNER_ID, 0, + FRF_AZ_RX_DESCQ_LABEL, rx_queue->queue, + FRF_AZ_RX_DESCQ_SIZE, + __ffs(rx_queue->rxd.entries), + FRF_AZ_RX_DESCQ_TYPE, 0 /* kernel queue */ , + /* For >=B0 this is scatter so disable */ + FRF_AZ_RX_DESCQ_JUMBO, !is_b0, + FRF_AZ_RX_DESCQ_EN, 1); + efx_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, + rx_queue->queue); +} + +static void efx_flush_rx_queue(struct efx_rx_queue *rx_queue) +{ + struct efx_nic *efx = rx_queue->efx; + efx_oword_t rx_flush_descq; + + rx_queue->flushed = FLUSH_PENDING; + + /* Post a flush command */ + EFX_POPULATE_OWORD_2(rx_flush_descq, + FRF_AZ_RX_FLUSH_DESCQ_CMD, 1, + FRF_AZ_RX_FLUSH_DESCQ, rx_queue->queue); + efx_writeo(efx, &rx_flush_descq, FR_AZ_RX_FLUSH_DESCQ); +} + +void efx_nic_fini_rx(struct efx_rx_queue *rx_queue) +{ + efx_oword_t rx_desc_ptr; + struct efx_nic *efx = rx_queue->efx; + + /* The queue should already have been flushed */ + WARN_ON(rx_queue->flushed != FLUSH_DONE); + + /* Remove RX descriptor ring from card */ + EFX_ZERO_OWORD(rx_desc_ptr); + efx_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base, + rx_queue->queue); + + /* Unpin RX descriptor ring */ + efx_fini_special_buffer(efx, &rx_queue->rxd); +} + +/* Free buffers backing RX queue */ +void efx_nic_remove_rx(struct efx_rx_queue *rx_queue) +{ + efx_free_special_buffer(rx_queue->efx, &rx_queue->rxd); +} + +/************************************************************************** + * + * Event queue processing + * Event queues are processed by per-channel tasklets. + * + **************************************************************************/ + +/* Update a channel's event queue's read pointer (RPTR) register + * + * This writes the EVQ_RPTR_REG register for the specified channel's + * event queue. + */ +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +void fastcall efx_nic_eventq_read_ack(struct efx_channel *channel) +#else +void efx_nic_eventq_read_ack(struct efx_channel *channel) +#endif +{ + efx_dword_t reg; + struct efx_nic *efx = channel->efx; + + EFX_POPULATE_DWORD_1(reg, FRF_AZ_EVQ_RPTR, channel->eventq_read_ptr); + efx_writed_table(efx, ®, efx->type->evq_rptr_tbl_base, + channel->channel); +} + +/* Use HW to insert a SW defined event */ +void efx_generate_event(struct efx_channel *channel, efx_qword_t *event) +{ + efx_oword_t drv_ev_reg; + + BUILD_BUG_ON(FRF_AZ_DRV_EV_DATA_LBN != 0 || + FRF_AZ_DRV_EV_DATA_WIDTH != 64); + drv_ev_reg.u32[0] = event->u32[0]; + drv_ev_reg.u32[1] = event->u32[1]; + drv_ev_reg.u32[2] = 0; + drv_ev_reg.u32[3] = 0; + EFX_SET_OWORD_FIELD(drv_ev_reg, FRF_AZ_DRV_EV_QID, channel->channel); + efx_writeo(channel->efx, &drv_ev_reg, FR_AZ_DRV_EV); +} + +/* Handle a transmit completion event + * + * The NIC batches TX completion events; the message we receive is of + * the form "complete all TX events up to this index". + */ +static void +efx_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) +{ + unsigned int tx_ev_desc_ptr; + unsigned int tx_ev_q_label; + struct efx_tx_queue *tx_queue; + struct efx_nic *efx = channel->efx; + + if (likely(EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_COMP))) { + /* Transmit completion */ + tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_DESC_PTR); + tx_ev_q_label = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_Q_LABEL); + tx_queue = &efx->tx_queue[tx_ev_q_label]; + channel->irq_mod_score += + (tx_ev_desc_ptr - tx_queue->read_count) & + EFX_TXQ_MASK; + efx_xmit_done(tx_queue, tx_ev_desc_ptr); + } else if (EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_WQ_FF_FULL)) { + /* Rewrite the FIFO write pointer */ + tx_ev_q_label = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_Q_LABEL); + tx_queue = &efx->tx_queue[tx_ev_q_label]; + + if (efx_dev_registered(efx)) + netif_tx_lock(efx->net_dev); + efx_notify_tx_desc(tx_queue); + if (efx_dev_registered(efx)) + netif_tx_unlock(efx->net_dev); + } else if (EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_PKT_ERR) && + EFX_WORKAROUND_10727(efx)) { + EFX_ERR(efx, "TX DMA Q reports TX_EV_PKT_ERR.\n"); + efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); + } else { + EFX_ERR(efx, "channel %d unexpected TX event " + EFX_QWORD_FMT"\n", channel->channel, + EFX_QWORD_VAL(*event)); + } +} + +/* Detect errors included in the rx_evt_pkt_ok bit. */ +static void efx_handle_rx_not_ok(struct efx_rx_queue *rx_queue, + const efx_qword_t *event, + bool *rx_ev_pkt_ok, + bool *discard) +{ + struct efx_nic *efx = rx_queue->efx; + bool rx_ev_buf_owner_id_err, rx_ev_ip_hdr_chksum_err; + bool rx_ev_tcp_udp_chksum_err, rx_ev_eth_crc_err; + bool rx_ev_frm_trunc, rx_ev_drib_nib, rx_ev_tobe_disc; + bool rx_ev_other_err, rx_ev_pause_frm; + bool rx_ev_ip_frag_err, rx_ev_hdr_type, rx_ev_mcast_pkt; + unsigned rx_ev_pkt_type; + + rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE); + rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_PKT); + rx_ev_tobe_disc = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_TOBE_DISC); + rx_ev_pkt_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_TYPE); + rx_ev_buf_owner_id_err = EFX_QWORD_FIELD(*event, + FSF_AZ_RX_EV_BUF_OWNER_ID_ERR); + rx_ev_ip_frag_err = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_IP_FRAG_ERR); + rx_ev_ip_hdr_chksum_err = EFX_QWORD_FIELD(*event, + FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR); + rx_ev_tcp_udp_chksum_err = EFX_QWORD_FIELD(*event, + FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR); + rx_ev_eth_crc_err = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_ETH_CRC_ERR); + rx_ev_frm_trunc = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_FRM_TRUNC); + rx_ev_drib_nib = ((efx_nic_rev(efx) >= EFX_REV_FALCON_B0) ? + 0 : EFX_QWORD_FIELD(*event, FSF_AA_RX_EV_DRIB_NIB)); + rx_ev_pause_frm = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PAUSE_FRM_ERR); + + /* Every error apart from tobe_disc and pause_frm */ + rx_ev_other_err = (rx_ev_drib_nib | rx_ev_tcp_udp_chksum_err | + rx_ev_buf_owner_id_err | rx_ev_eth_crc_err | + rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err); + + /* Count errors that are not in MAC stats. Ignore expected + * checksum errors during self-test. */ + if (rx_ev_frm_trunc) + ++rx_queue->channel->n_rx_frm_trunc; + else if (rx_ev_tobe_disc) + ++rx_queue->channel->n_rx_tobe_disc; + else if (!efx->loopback_selftest) { + if (rx_ev_eth_crc_err) + ++rx_queue->channel->n_rx_eth_crc_err; + 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; + + /* The frame must be discarded if any of these are true. */ + *discard = (rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_drib_nib | + rx_ev_tobe_disc | rx_ev_pause_frm); + + /* TOBE_DISC is expected on unicast mismatches; don't print out an + * error message. FRM_TRUNC indicates RXDP dropped the packet due + * to a FIFO overflow. + */ +#ifdef EFX_ENABLE_DEBUG + if (rx_ev_other_err) { + EFX_INFO_RL(efx, " RX queue %d unexpected RX event " + EFX_QWORD_FMT "%s%s%s%s%s%s%s%s\n", + rx_queue->queue, EFX_QWORD_VAL(*event), + rx_ev_buf_owner_id_err ? " [OWNER_ID_ERR]" : "", + rx_ev_ip_hdr_chksum_err ? + " [IP_HDR_CHKSUM_ERR]" : "", + rx_ev_tcp_udp_chksum_err ? + " [TCP_UDP_CHKSUM_ERR]" : "", + rx_ev_eth_crc_err ? " [ETH_CRC_ERR]" : "", + rx_ev_frm_trunc ? " [FRM_TRUNC]" : "", + rx_ev_drib_nib ? " [DRIB_NIB]" : "", + rx_ev_tobe_disc ? " [TOBE_DISC]" : "", + rx_ev_pause_frm ? " [PAUSE]" : ""); + } +#endif +} + +/* Handle receive events that are not in-order. */ +static void +efx_handle_rx_bad_index(struct efx_rx_queue *rx_queue, unsigned index) +{ + struct efx_nic *efx = rx_queue->efx; + unsigned expected, dropped; + + expected = rx_queue->removed_count & EFX_RXQ_MASK; + dropped = (index - expected) & EFX_RXQ_MASK; + EFX_INFO(efx, "dropped %d events (index=%d expected=%d)\n", + dropped, index, expected); + + 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 + * + * The NIC gives a "discard" flag if it's a unicast packet with the + * wrong destination address + * Also "is multicast" and "matches multicast filter" flags can be used to + * discard non-matching multicast packets. + */ +static void +efx_handle_rx_event(struct efx_channel *channel, const efx_qword_t *event) +{ + unsigned int rx_ev_desc_ptr, rx_ev_byte_cnt; + unsigned int rx_ev_hdr_type, rx_ev_mcast_pkt; + unsigned expected_ptr; + bool rx_ev_pkt_ok, discard = false, checksummed; + struct efx_rx_queue *rx_queue; + struct efx_nic *efx = channel->efx; + + /* Basic packet information */ + rx_ev_byte_cnt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_BYTE_CNT); + rx_ev_pkt_ok = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_OK); + rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE); + WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_JUMBO_CONT)); + WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_SOP) != 1); + WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_Q_LABEL) != + channel->channel); + + rx_queue = &efx->rx_queue[channel->channel]; + + rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_DESC_PTR); + expected_ptr = rx_queue->removed_count & EFX_RXQ_MASK; + if (unlikely(rx_ev_desc_ptr != expected_ptr)) + efx_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr); + + if (likely(rx_ev_pkt_ok)) { + /* If packet is marked as OK and packet type is TCP/IP or + * UDP/IP, then we can rely on the hardware checksum. + */ + checksummed = + likely(efx->rx_checksum_enabled) && + (rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP || + rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP); + } else { + efx_handle_rx_not_ok(rx_queue, event, &rx_ev_pkt_ok, &discard); + checksummed = false; + } + + /* Detect multicast packets that didn't match the filter */ + rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_PKT); + if (rx_ev_mcast_pkt) { + unsigned int rx_ev_mcast_hash_match = + EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_HASH_MATCH); + + if (unlikely(!rx_ev_mcast_hash_match)) { + ++channel->n_rx_mcast_mismatch; + discard = true; + } + } + + channel->irq_mod_score += 2; + + /* Handle received packet */ + efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt, + checksummed, discard); +} + +/* Global events are basically PHY events */ +static void +efx_handle_global_event(struct efx_channel *channel, efx_qword_t *event) +{ + struct efx_nic *efx = channel->efx; + bool handled = false; + + if (EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_G_PHY0_INTR) || + EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_XG_PHY0_INTR) || + EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_XFP_PHY0_INTR)) { + /* Ignored */ + handled = true; + } + + if ((efx_nic_rev(efx) >= EFX_REV_FALCON_B0) && + EFX_QWORD_FIELD(*event, FSF_BB_GLB_EV_XG_MGT_INTR)) { + efx->xmac_poll_required = true; + handled = true; + } + + if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1 ? + EFX_QWORD_FIELD(*event, FSF_AA_GLB_EV_RX_RECOVERY) : + EFX_QWORD_FIELD(*event, FSF_BB_GLB_EV_RX_RECOVERY)) { + EFX_ERR(efx, "channel %d seen global RX_RESET " + "event. Resetting.\n", channel->channel); + + atomic_inc(&efx->errors.rx_reset); + efx_schedule_reset(efx, EFX_WORKAROUND_6555(efx) ? + RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE); + handled = true; + } + + if (!handled) + EFX_ERR(efx, "channel %d unknown global event " + EFX_QWORD_FMT "\n", channel->channel, + EFX_QWORD_VAL(*event)); +} + +static void +efx_handle_driver_event(struct efx_channel *channel, efx_qword_t *event) +{ + struct efx_nic *efx = channel->efx; + unsigned int ev_sub_code; + unsigned int ev_sub_data; + + ev_sub_code = EFX_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBCODE); + ev_sub_data = EFX_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBDATA); + + switch (ev_sub_code) { + case FSE_AZ_TX_DESCQ_FLS_DONE_EV: + EFX_TRACE(efx, "channel %d TXQ %d flushed\n", + channel->channel, ev_sub_data); + EFX_DL_CALLBACK(efx, event, event); + break; + case FSE_AZ_RX_DESCQ_FLS_DONE_EV: + EFX_TRACE(efx, "channel %d RXQ %d flushed\n", + channel->channel, ev_sub_data); + EFX_DL_CALLBACK(efx, event, event); + break; + case FSE_AZ_EVQ_INIT_DONE_EV: + EFX_LOG(efx, "channel %d EVQ %d initialised\n", + channel->channel, ev_sub_data); + break; + case FSE_AZ_SRM_UPD_DONE_EV: + EFX_TRACE(efx, "channel %d SRAM update done\n", + channel->channel); + EFX_DL_CALLBACK(efx, event, event); + break; + case FSE_AZ_WAKE_UP_EV: + EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n", + channel->channel, ev_sub_data); + EFX_DL_CALLBACK(efx, event, event); + break; + case FSE_AZ_TIMER_EV: + EFX_TRACE(efx, "channel %d RX queue %d timer expired\n", + channel->channel, ev_sub_data); + EFX_DL_CALLBACK(efx, event, event); + break; + case FSE_AA_RX_RECOVER_EV: + 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 FSE_BZ_RX_DSC_ERROR_EV: + EFX_ERR(efx, "RX DMA Q %d reports descriptor fetch error." + " RX Q %d is disabled.\n", ev_sub_data, ev_sub_data); + atomic_inc(&efx->errors.rx_desc_fetch); + efx_schedule_reset(efx, RESET_TYPE_RX_DESC_FETCH); + break; + case FSE_BZ_TX_DSC_ERROR_EV: + EFX_ERR(efx, "TX DMA Q %d reports descriptor fetch error." + " TX Q %d is disabled.\n", ev_sub_data, ev_sub_data); + atomic_inc(&efx->errors.tx_desc_fetch); + efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH); + break; + default: + EFX_TRACE(efx, "channel %d unknown driver event code %d " + "data %04x\n", channel->channel, ev_sub_code, + ev_sub_data); + EFX_DL_CALLBACK(efx, event, event); + break; + } +} + +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +int fastcall efx_nic_process_eventq(struct efx_channel *channel, int rx_quota) +#else +int efx_nic_process_eventq(struct efx_channel *channel, int rx_quota) +#endif +{ + unsigned int read_ptr; + efx_qword_t event, *p_event; + int ev_code; + int rx_packets = 0; + + read_ptr = channel->eventq_read_ptr; + + do { + p_event = efx_event(channel, read_ptr); + event = *p_event; + + if (!efx_event_present(&event)) + /* End of events */ + break; + + EFX_TRACE(channel->efx, "channel %d event is "EFX_QWORD_FMT"\n", + channel->channel, EFX_QWORD_VAL(event)); + + /* Clear this event by marking it all ones */ + EFX_SET_QWORD(*p_event); + + ev_code = EFX_QWORD_FIELD(event, FSF_AZ_EV_CODE); + + switch (ev_code) { + case FSE_AZ_EV_CODE_RX_EV: + efx_handle_rx_event(channel, &event); + ++rx_packets; + break; + case FSE_AZ_EV_CODE_TX_EV: + efx_handle_tx_event(channel, &event); + break; + case FSE_AZ_EV_CODE_DRV_GEN_EV: + channel->eventq_magic = EFX_QWORD_FIELD( + event, FSF_AZ_DRV_GEN_EV_MAGIC); + EFX_LOG(channel->efx, "channel %d received generated " + "event "EFX_QWORD_FMT"\n", channel->channel, + EFX_QWORD_VAL(event)); + break; + case FSE_AZ_EV_CODE_GLOBAL_EV: + efx_handle_global_event(channel, &event); + break; + case FSE_AZ_EV_CODE_DRIVER_EV: + efx_handle_driver_event(channel, &event); + break; + case FSE_CZ_EV_CODE_MCDI_EV: + efx_mcdi_process_event(channel, &event); + break; + default: + EFX_ERR(channel->efx, "channel %d unknown event type %d" + " (data " EFX_QWORD_FMT ")\n", channel->channel, + ev_code, EFX_QWORD_VAL(event)); + } + + /* Increment read pointer */ + read_ptr = (read_ptr + 1) & EFX_EVQ_MASK; + + } while (rx_packets < rx_quota); + + channel->eventq_read_ptr = read_ptr; + return rx_packets; +} + + +/* Allocate buffer table entries for event queue */ +int efx_nic_probe_eventq(struct efx_channel *channel) +{ + struct efx_nic *efx = channel->efx; + int rc; + + BUILD_BUG_ON(EFX_EVQ_SIZE < 512 || EFX_EVQ_SIZE > 32768 || + EFX_EVQ_SIZE & EFX_EVQ_MASK); + rc = efx_alloc_special_buffer(efx, &channel->eventq, + EFX_EVQ_SIZE * sizeof(efx_qword_t)); + if (rc) + return rc; + + efx->resources.evq_int_min = max(efx->resources.evq_int_min, + (unsigned)channel->channel + 1); + + return 0; +} + +void efx_nic_init_eventq(struct efx_channel *channel) +{ + efx_oword_t reg; + struct efx_nic *efx = channel->efx; + + EFX_LOG(efx, "channel %d event queue in special buffers %d-%d\n", + channel->channel, channel->eventq.index, + channel->eventq.index + channel->eventq.entries - 1); + + if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) { + EFX_POPULATE_OWORD_3(reg, + FRF_CZ_TIMER_Q_EN, 1, + FRF_CZ_HOST_NOTIFY_MODE, 0, + FRF_CZ_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS); + efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, channel->channel); + } + + /* Pin event queue buffer */ + efx_init_special_buffer(efx, &channel->eventq); + + /* Fill event queue with all ones (i.e. empty events) */ + memset(channel->eventq.addr, 0xff, channel->eventq.len); + + /* Push event queue to card */ + EFX_POPULATE_OWORD_3(reg, + FRF_AZ_EVQ_EN, 1, + FRF_AZ_EVQ_SIZE, __ffs(channel->eventq.entries), + FRF_AZ_EVQ_BUF_BASE_ID, channel->eventq.index); + efx_writeo_table(efx, ®, efx->type->evq_ptr_tbl_base, + channel->channel); + + efx->type->push_irq_moderation(channel); +} + +void efx_nic_fini_eventq(struct efx_channel *channel) +{ + efx_oword_t reg; + struct efx_nic *efx = channel->efx; + + /* Remove event queue from card */ + EFX_ZERO_OWORD(reg); + efx_writeo_table(efx, ®, efx->type->evq_ptr_tbl_base, + channel->channel); + if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) + efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, channel->channel); + + /* Unpin event queue */ + efx_fini_special_buffer(efx, &channel->eventq); +} + +/* Free buffers backing event queue */ +void efx_nic_remove_eventq(struct efx_channel *channel) +{ + efx_free_special_buffer(channel->efx, &channel->eventq); +} + + +/* Generates a test event on the event queue. A subsequent call to + * process_eventq() should pick up the event and place the value of + * "magic" into channel->eventq_magic; + */ +void efx_nic_generate_test_event(struct efx_channel *channel, unsigned int magic) +{ + efx_qword_t test_event; + + EFX_POPULATE_QWORD_2(test_event, FSF_AZ_EV_CODE, + FSE_AZ_EV_CODE_DRV_GEN_EV, + FSF_AZ_DRV_GEN_EV_MAGIC, magic); + efx_generate_event(channel, &test_event); +} + +/************************************************************************** + * + * Flush handling + * + **************************************************************************/ + + +static void efx_poll_flush_events(struct efx_nic *efx) +{ + struct efx_channel *channel = &efx->channel[0]; + struct efx_tx_queue *tx_queue; + struct efx_rx_queue *rx_queue; + unsigned int read_ptr = channel->eventq_read_ptr; + unsigned int end_ptr = (read_ptr - 1) & EFX_EVQ_MASK; + + do { + efx_qword_t *event = efx_event(channel, read_ptr); + int ev_code, ev_sub_code, ev_queue; + bool ev_failed; + + if (!efx_event_present(event)) + break; + + ev_code = EFX_QWORD_FIELD(*event, FSF_AZ_EV_CODE); + ev_sub_code = EFX_QWORD_FIELD(*event, + FSF_AZ_DRIVER_EV_SUBCODE); + if (ev_code == FSE_AZ_EV_CODE_DRIVER_EV && + ev_sub_code == FSE_AZ_TX_DESCQ_FLS_DONE_EV) { + ev_queue = EFX_QWORD_FIELD(*event, + FSF_AZ_DRIVER_EV_SUBDATA); + if (ev_queue < EFX_TX_QUEUE_COUNT) { + tx_queue = efx->tx_queue + ev_queue; + tx_queue->flushed = FLUSH_DONE; + } + } else if (ev_code == FSE_AZ_EV_CODE_DRIVER_EV && + ev_sub_code == FSE_AZ_RX_DESCQ_FLS_DONE_EV) { + ev_queue = EFX_QWORD_FIELD( + *event, FSF_AZ_DRIVER_EV_RX_DESCQ_ID); + ev_failed = EFX_QWORD_FIELD( + *event, FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL); + if (ev_queue < efx->n_rx_queues) { + rx_queue = efx->rx_queue + ev_queue; + rx_queue->flushed = + ev_failed ? FLUSH_FAILED : FLUSH_DONE; + } + } + + /* We're about to destroy the queue anyway, so + * it's ok to throw away every non-flush event */ + EFX_SET_QWORD(*event); + + read_ptr = (read_ptr + 1) & EFX_EVQ_MASK; + } while (read_ptr != end_ptr); + + channel->eventq_read_ptr = read_ptr; +} + +/* Handle tx and rx flushes at the same time, since they run in + * parallel in the hardware and there's no reason for us to + * serialise them */ +int efx_nic_flush_queues(struct efx_nic *efx) +{ + struct efx_rx_queue *rx_queue; + struct efx_tx_queue *tx_queue; + int i, tx_pending, rx_pending; + + /* If necessary prepare the hardware for flushing */ + efx->type->prepare_flush(efx); + + /* Flush all tx queues in parallel */ + efx_for_each_tx_queue(tx_queue, efx) + efx_flush_tx_queue(tx_queue); + + /* The hardware supports four concurrent rx flushes, each of which may + * need to be retried if there is an outstanding descriptor fetch */ + for (i = 0; i < EFX_FLUSH_POLL_COUNT; ++i) { + rx_pending = tx_pending = 0; + efx_for_each_rx_queue(rx_queue, efx) { + if (rx_queue->flushed == FLUSH_PENDING) + ++rx_pending; + } + efx_for_each_rx_queue(rx_queue, efx) { + if (rx_pending == EFX_RX_FLUSH_COUNT) + break; + if (rx_queue->flushed == FLUSH_FAILED || + rx_queue->flushed == FLUSH_NONE) { + efx_flush_rx_queue(rx_queue); + ++rx_pending; + } + } + efx_for_each_tx_queue(tx_queue, efx) { + if (tx_queue->flushed != FLUSH_DONE) + ++tx_pending; + } + + if (rx_pending == 0 && tx_pending == 0) + return 0; + + msleep(EFX_FLUSH_INTERVAL); + efx_poll_flush_events(efx); + } + + /* Mark the queues as all flushed. We're going to return failure + * leading to a reset, or fake up success anyway */ + efx_for_each_tx_queue(tx_queue, efx) { + if (tx_queue->flushed != FLUSH_DONE) + EFX_ERR(efx, "tx queue %d flush command timed out\n", + tx_queue->queue); + tx_queue->flushed = FLUSH_DONE; + } + efx_for_each_rx_queue(rx_queue, efx) { + if (rx_queue->flushed != FLUSH_DONE) + EFX_ERR(efx, "rx queue %d flush command timed out\n", + rx_queue->queue); + rx_queue->flushed = FLUSH_DONE; + } + + if (EFX_WORKAROUND_7803(efx)) + return 0; + + return -ETIMEDOUT; +} + +/************************************************************************** + * + * Hardware interrupts + * The hardware interrupt handler does very little work; all the event + * queue processing is carried out by per-channel tasklets. + * + **************************************************************************/ + +/* Enable/disable/generate interrupts */ +static inline void efx_nic_interrupts(struct efx_nic *efx, + bool enabled, bool force) +{ + efx_oword_t int_en_reg_ker; + unsigned int level = 0; + + if (EFX_WORKAROUND_17213(efx) && !EFX_INT_MODE_USE_MSI(efx)) + /* Set the level always even if we're generating a test + * interrupt, because our legacy interrupt handler is safe */ + level = 0x1f; + + EFX_POPULATE_OWORD_3(int_en_reg_ker, + FRF_AZ_KER_INT_LEVE_SEL, level, + FRF_AZ_KER_INT_KER, force, + FRF_AZ_DRV_INT_EN_KER, enabled); + efx_writeo(efx, &int_en_reg_ker, FR_AZ_INT_EN_KER); +} + +void efx_nic_enable_interrupts(struct efx_nic *efx) +{ + struct efx_channel *channel; + + EFX_ZERO_OWORD(*((efx_oword_t *) efx->irq_status.addr)); + wmb(); /* Ensure interrupt vector is clear before interrupts enabled */ + + /* Enable interrupts */ + efx_nic_interrupts(efx, true, false); + + /* Force processing of all the channels to get the EVQ RPTRs up to + date */ + efx_for_each_channel(channel, efx) + efx_schedule_channel(channel); +} + +void efx_nic_disable_interrupts(struct efx_nic *efx) +{ + /* Disable interrupts */ + efx_nic_interrupts(efx, false, false); +} + +/* Generate a test interrupt + * Interrupt must already have been enabled, otherwise nasty things + * may happen. + */ +void efx_nic_generate_interrupt(struct efx_nic *efx) +{ + efx_nic_interrupts(efx, true, true); +} + +/* Process a fatal interrupt + * Disable bus mastering ASAP and schedule a reset + */ +irqreturn_t efx_nic_fatal_interrupt(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data = efx->nic_data; + efx_oword_t *int_ker = efx->irq_status.addr; + efx_oword_t fatal_intr; + int error, mem_perr; + + efx_reado(efx, &fatal_intr, FR_AZ_FATAL_INTR_KER); + error = EFX_OWORD_FIELD(fatal_intr, FRF_AZ_FATAL_INTR); + + EFX_ERR(efx, "SYSTEM ERROR " EFX_OWORD_FMT " status " + EFX_OWORD_FMT ": %s\n", EFX_OWORD_VAL(*int_ker), + EFX_OWORD_VAL(fatal_intr), + error ? "disabling bus mastering" : "no recognised error"); + if (error == 0) + goto out; + + /* If this is a memory parity error dump which blocks are offending */ + mem_perr = EFX_OWORD_FIELD(fatal_intr, FRF_AZ_MEM_PERR_INT_KER); + if (mem_perr) { + efx_oword_t reg; + efx_reado(efx, ®, FR_AZ_MEM_STAT); + EFX_ERR(efx, "SYSTEM ERROR: memory parity error " + EFX_OWORD_FMT "\n", EFX_OWORD_VAL(reg)); + } + + /* Disable both devices */ + pci_clear_master(efx->pci_dev); + if (efx_nic_is_dual_func(efx)) + pci_clear_master(nic_data->pci_dev2); + efx_nic_disable_interrupts(efx); + + /* Count errors and reset or disable the NIC accordingly */ + if (efx->int_error_count == 0 || + time_after(jiffies, efx->int_error_expire)) { + efx->int_error_count = 0; + efx->int_error_expire = + jiffies + EFX_INT_ERROR_EXPIRE * HZ; + } + if (++efx->int_error_count < EFX_MAX_INT_ERRORS) { + EFX_ERR(efx, "SYSTEM ERROR - reset scheduled\n"); + efx_schedule_reset(efx, RESET_TYPE_INT_ERROR); + } else { + EFX_ERR(efx, "SYSTEM ERROR - max number of errors seen." + "NIC will be disabled\n"); + efx_schedule_reset(efx, RESET_TYPE_DISABLE); + } +out: + return IRQ_HANDLED; +} + +/* Handle a legacy interrupt + * Acknowledges the interrupt and schedule event queue processing. + * + * This routine must guarantee not to touch the hardware when + * interrupts are disabled, to allow for correct semantics of + * efx_suspend() and efx_resume(). + */ +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_IRQ_HANDLER_REGS) +static irqreturn_t efx_legacy_interrupt(int irq, void *dev_id) +#else +static irqreturn_t efx_legacy_interrupt(int irq, void *dev_id, + struct pt_regs *regs + __attribute__ ((unused))) +#endif +{ + struct efx_nic *efx = dev_id; + efx_oword_t *int_ker = efx->irq_status.addr; + irqreturn_t result = IRQ_NONE; + struct efx_channel *channel; + efx_dword_t reg; + u32 queues; + int syserr; + + /* Read the ISR which also ACKs the interrupts */ + efx_readd(efx, ®, FR_BZ_INT_ISR0); + queues = EFX_EXTRACT_DWORD(reg, 0, 31); + + /* Check to see if we have a serious error condition */ + syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); + if (unlikely(syserr)) + return efx_nic_fatal_interrupt(efx); + + if (queues != 0) { + if (EFX_WORKAROUND_15783(efx)) + efx->irq_zero_count = 0; + + /* Schedule processing of any interrupting queues */ + efx_for_each_channel(channel, efx) { + if (queues & 1) + efx_schedule_channel(channel); + queues >>= 1; + } + result = IRQ_HANDLED; + + } else if (EFX_WORKAROUND_15783(efx) && + efx->irq_zero_count++ == 0) { + efx_qword_t *event; + + /* Ensure we rearm all event queues */ + efx_for_each_channel(channel, efx) { + event = efx_event(channel, channel->eventq_read_ptr); + if (efx_event_present(event)) + efx_schedule_channel(channel); + } + + result = IRQ_HANDLED; + } + + if (result == IRQ_HANDLED) { + efx->last_irq_cpu = raw_smp_processor_id(); + EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_DWORD_FMT "\n", + irq, raw_smp_processor_id(), EFX_DWORD_VAL(reg)); + } else if (EFX_WORKAROUND_15783(efx)){ + /* We can't return IRQ_HANDLED more than once on seeing ISR0=0 + * because this might be a shared interrupt, but we do need to + * check the channel every time and preemptively rearm it if + * it's idle. */ + efx_for_each_channel(channel, efx) { + if (!channel->work_pending) + efx_nic_eventq_read_ack(channel); + } + } + + return result; +} + +/* Handle an MSI interrupt + * + * Handle an MSI hardware interrupt. This routine schedules event + * queue processing. No interrupt acknowledgement cycle is necessary. + * Also, we never need to check that the interrupt is for us, since + * MSI interrupts cannot be shared. + * + * This routine must guarantee not to touch the hardware when + * interrupts are disabled, to allow for correct semantics of + * efx_suspend() and efx_resume(). + */ +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_IRQ_HANDLER_REGS) +static irqreturn_t efx_msi_interrupt(int irq, void *dev_id) +#else +static irqreturn_t efx_msi_interrupt(int irq, void *dev_id, + struct pt_regs *regs + __attribute__ ((unused))) +#endif +{ + struct efx_channel *channel = dev_id; + struct efx_nic *efx = channel->efx; + efx_oword_t *int_ker = efx->irq_status.addr; + int syserr; + + efx->last_irq_cpu = raw_smp_processor_id(); + EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_OWORD_FMT "\n", + irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker)); + + /* Check to see if we have a serious error condition */ + syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT); + if (unlikely(syserr)) + return efx_nic_fatal_interrupt(efx); + + /* Schedule processing of the channel */ + efx_schedule_channel(channel); + + return IRQ_HANDLED; +} + +/* Setup RSS indirection table. + * This maps from the hash value of the packet to RXQ + */ +static void efx_setup_rss_indir_table(struct efx_nic *efx) +{ + int i = 0; + unsigned long offset; + efx_dword_t dword; + + if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) + return; + + for (offset = FR_BZ_RX_INDIRECTION_TBL; + offset < FR_BZ_RX_INDIRECTION_TBL + 0x800; + offset += 0x10) { + EFX_POPULATE_DWORD_1(dword, FRF_BZ_IT_QUEUE, + i % efx->n_rx_queues); + efx_writed(efx, &dword, offset); + i++; + } +} + +/* Hook interrupt handler(s) + * Try MSI and then legacy interrupts. + */ +int efx_nic_init_interrupt(struct efx_nic *efx) +{ + struct efx_channel *channel; + int rc; + + if (!EFX_INT_MODE_USE_MSI(efx)) { + irq_handler_t handler; + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) + handler = efx_legacy_interrupt; + else + handler = falcon_legacy_interrupt_a1; + + rc = request_irq(efx->legacy_irq, handler, IRQF_SHARED, + efx->name, efx); + if (rc) { + EFX_ERR(efx, "failed to hook legacy IRQ %d\n", + efx->pci_dev->irq); + goto fail1; + } + return 0; + } + + /* Hook MSI or MSI-X interrupt */ + efx_for_each_channel(channel, efx) { + rc = request_irq(channel->irq, efx_msi_interrupt, + IRQF_PROBE_SHARED, /* Not shared */ + channel->name, channel); + if (rc) { + EFX_ERR(efx, "failed to hook IRQ %d\n", channel->irq); + goto fail2; + } + } + + return 0; + + fail2: + efx_for_each_channel(channel, efx) + free_irq(channel->irq, channel); + fail1: + return rc; +} + +void efx_nic_fini_interrupt(struct efx_nic *efx) +{ + struct efx_channel *channel; + efx_oword_t reg; + + /* Disable MSI/MSI-X interrupts */ + efx_for_each_channel(channel, efx) { + if (channel->irq) + free_irq(channel->irq, channel); + } + + /* ACK legacy interrupt */ + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) + efx_reado(efx, ®, FR_BZ_INT_ISR0); + else + falcon_irq_ack_a1(efx); + + /* Disable legacy interrupt */ + if (efx->legacy_irq) + free_irq(efx->legacy_irq, efx); +} + +/* Looks at available SRAM resources and works out how many queues we + * can support, and where things like descriptor caches should live. */ +/* TODO: Improve the allocation heuristic and remove the buffers_per_vnic + * parameter */ +int efx_nic_dimension_resources(struct efx_nic *efx, size_t sram_size, + unsigned buffers_per_vnic) +{ + struct efx_dl_falcon_resources *res = &efx->resources; + unsigned dcs, vnic_bytes, max_vnics, n_vnics; + + efx->sram_lim = sram_size; + + efx->rx_dc_entries = rx_desc_cache_size; + + efx->tx_dc_entries = tx_desc_cache_size; + + /* Buffer table space for evqs and dmaqs is relatively + * trivial, so not considered in this calculation. */ + vnic_bytes = (buffers_per_vnic + + efx->rx_dc_entries + + efx->tx_dc_entries) * 8; + max_vnics = res->evq_timer_min + sram_size / vnic_bytes; + for (n_vnics = 1; (n_vnics * 2) < max_vnics; n_vnics *= 2) + ; + if (!WARN_ON(n_vnics > res->rxq_lim)) + res->rxq_lim = n_vnics; + if (!WARN_ON(n_vnics > res->txq_lim)) + res->txq_lim = n_vnics; + + dcs = n_vnics * efx->tx_dc_entries * 8; + efx->tx_dc_base = sram_size - dcs; + dcs = n_vnics * efx->rx_dc_entries * 8; + efx->rx_dc_base = efx->tx_dc_base - dcs; + res->buffer_table_lim = efx->rx_dc_base / 8; + + EFX_LOG(efx, "Available resources: %u RXQs, %u TXQs," + " %u buffer table entries\n", + res->rxq_lim, res->txq_lim, res->buffer_table_lim); + + return 0; +} + +u32 efx_nic_fpga_ver(struct efx_nic *efx) +{ + efx_oword_t altera_build; + efx_reado(efx, &altera_build, FR_AZ_ALTERA_BUILD); + return EFX_OWORD_FIELD(altera_build, FRF_AZ_ALTERA_BUILD_VER); +} + +void efx_nic_init_common(struct efx_nic *efx) +{ + efx_oword_t temp; + + /* Set positions of descriptor caches in SRAM. */ + EFX_POPULATE_OWORD_1(temp, FRF_AZ_SRM_TX_DC_BASE_ADR, + efx->tx_dc_base / 8); + efx_writeo(efx, &temp, FR_AZ_SRM_TX_DC_CFG); + EFX_POPULATE_OWORD_1(temp, FRF_AZ_SRM_RX_DC_BASE_ADR, + efx->rx_dc_base / 8); + efx_writeo(efx, &temp, FR_AZ_SRM_RX_DC_CFG); + + /* Set TX descriptor cache size. */ + EFX_POPULATE_OWORD_1(temp, FRF_AZ_TX_DC_SIZE, + ffs(efx->tx_dc_entries) - 4); + efx_writeo(efx, &temp, FR_AZ_TX_DC_CFG); + + /* Set RX descriptor cache size. Set low watermark to size-8, as + * this allows most efficient prefetching. + */ + EFX_POPULATE_OWORD_1(temp, FRF_AZ_RX_DC_SIZE, + ffs(efx->rx_dc_entries) - 4); + efx_writeo(efx, &temp, FR_AZ_RX_DC_CFG); + EFX_POPULATE_OWORD_1(temp, FRF_AZ_RX_DC_PF_LWM, + efx->rx_dc_entries - 8); + efx_writeo(efx, &temp, FR_AZ_RX_DC_PF_WM); + + /* Program INT_KER address */ + EFX_POPULATE_OWORD_2(temp, + FRF_AZ_NORM_INT_VEC_DIS_KER, + EFX_INT_MODE_USE_MSI(efx), + FRF_AZ_INT_ADR_KER, efx->irq_status.dma_addr); + efx_writeo(efx, &temp, FR_AZ_INT_ADR_KER); + + /* Enable all the genuinely fatal interrupts. (They are still + * masked by the overall interrupt mask, controlled by + * falcon_interrupts()). + * + * Note: All other fatal interrupts are enabled + */ + EFX_POPULATE_OWORD_3(temp, + FRF_AZ_ILL_ADR_INT_KER_EN, 1, + FRF_AZ_RBUF_OWN_INT_KER_EN, 1, + FRF_AZ_TBUF_OWN_INT_KER_EN, 1); + EFX_INVERT_OWORD(temp); + efx_writeo(efx, &temp, FR_AZ_FATAL_INTR_KER); + + efx_setup_rss_indir_table(efx); + + /* Disable the ugly timer-based TX DMA backoff and allow TX DMA to be + * controlled by the RX FIFO fill level. Set arbitration to one pkt/Q. + */ + efx_reado(efx, &temp, FR_AZ_TX_RESERVED); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER, 0xfe); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER_EN, 1); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_ONE_PKT_PER_Q, 1); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PUSH_EN, 0); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_DIS_NON_IP_EV, 1); + /* Enable SW_EV to inherit in char driver - assume harmless here */ + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_SOFT_EVT_EN, 1); + /* Prefetch threshold 2 => fetch when descriptor cache half empty */ + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PREF_THRESHOLD, 2); + /* Disable hardware watchdog which can misfire */ + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PREF_WD_TMR, 0x3fffff); + /* Squash TX of packets of 16 bytes or less */ + if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) + EFX_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1); + efx_writeo(efx, &temp, FR_AZ_TX_RESERVED); +} + +unsigned +efx_nic_check_pcie_link(struct efx_nic *efx, + unsigned full_width, unsigned full_speed) +{ + int cap = pci_find_capability(efx->pci_dev, PCI_CAP_ID_EXP); + u16 stat; + unsigned width, speed; + + if (!cap || + pci_read_config_word(efx->pci_dev, cap + PCI_EXP_LNKSTA, &stat)) + return 0; + + width = (stat & PCI_EXP_LNKSTA_NLW) >> __ffs(PCI_EXP_LNKSTA_NLW); + EFX_WARN_ON_PARANOID(width == 0 || width > full_width); + speed = stat & PCI_EXP_LNKSTA_CLS; + EFX_WARN_ON_PARANOID(speed == 0 || speed > full_speed); + + if (width < full_width || speed < full_speed) + + EFX_ERR(efx, "This Solarflare Network Adapter requires a " + "slot with %d lanes at PCI Express %d speed for " + "full performance, but is currently limited to " + "%d lanes at PCI Express %d speed. Consult your " + "motherboard documentation to find a more " + "suitable slot\n", + full_width, full_speed, width, speed); + + return width; +} diff -r aa133dee7792 drivers/net/sfc/nic.h --- /dev/null +++ b/drivers/net/sfc/nic.h @@ -0,0 +1,307 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_NIC_H +#define EFX_NIC_H + +#include +#include + +#include "net_driver.h" +#include "efx.h" +#include "mcdi.h" + +/* + * Falcon hardware control + */ + +enum { + EFX_REV_FALCON_A0 = 0, + EFX_REV_FALCON_A1 = 1, + EFX_REV_FALCON_B0 = 2, + EFX_REV_SIENA_A0 = 3, +}; + +static inline int efx_nic_rev(struct efx_nic *efx) +{ + return efx->type->revision; +} + +extern u32 efx_nic_fpga_ver(struct efx_nic *efx); + +static inline bool efx_nic_has_mc(struct efx_nic *efx) +{ + return efx_nic_rev(efx) >= EFX_REV_SIENA_A0; +} +/* NIC has two interlinked PCI functions for the same port. */ +static inline bool efx_nic_is_dual_func(struct efx_nic *efx) +{ + return efx_nic_rev(efx) < EFX_REV_FALCON_B0; +} + +enum { + PHY_TYPE_NONE = 0, + PHY_TYPE_TXC43128 = 1, + PHY_TYPE_88E1111 = 2, + PHY_TYPE_SFX7101 = 3, + PHY_TYPE_QT2022C2 = 4, + PHY_TYPE_PM8358 = 6, + PHY_TYPE_SFT9001A = 8, + PHY_TYPE_QT2025C = 9, + PHY_TYPE_SFT9001B = 10, +}; + +#define FALCON_XMAC_LOOPBACKS \ + ((1 << LOOPBACK_XGMII) | \ + (1 << LOOPBACK_XGXS) | \ + (1 << LOOPBACK_XAUI)) + +#define FALCON_GMAC_LOOPBACKS \ + (1 << LOOPBACK_GMAC) + +/** + * struct falcon_board_type - board operations and type information + * @id: Board type id, as found in NVRAM + * @mwatts: Power requirements (mW) + * @ref_model: Model number of Solarflare reference design + * @gen_type: Generic board type description + * @init: Allocate resources and initialise peripheral hardware + * @init_phy: Do board-specific PHY initialisation + * @fini: Shut down hardware and free resources + * @set_id_led: Set state of identifying LED or revert to automatic function + * @monitor: Board-specific health check function + */ +struct falcon_board_type { + u8 id; + unsigned mwatts; + const char *ref_model; + const char *gen_type; + int (*init) (struct efx_nic *nic); + void (*init_phy) (struct efx_nic *efx); + void (*fini) (struct efx_nic *nic); + void (*set_id_led) (struct efx_nic *efx, enum efx_led_mode mode); + int (*monitor) (struct efx_nic *nic); +}; + +/** + * struct falcon_board - board information + * @type: Type of board + * @major: Major rev. ('A', 'B' ...) + * @minor: Minor rev. (0, 1, ...) + * @i2c_adap: I2C adapter for on-board peripherals + * @i2c_data: Data for bit-banging algorithm + * @hwmon_client: I2C client for hardware monitor + * @ioexp_client: I2C client for power/port control + */ +struct falcon_board { + const struct falcon_board_type *type; + int major; + int minor; + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_data; + struct i2c_client *hwmon_client; + struct i2c_client *ioexp_client; +}; + +/** + * struct falcon_nic_data - Falcon NIC state + * @sram_config: NIC SRAM configuration code + * @pci_dev2: Secondary function of Falcon A + * @board: Board state and functions + * @stats_disable_count: Nest count for disabling statistics fetches + * @stats_pending: Is there a pending DMA of MAC statistics. + * @stats_timer: A timer for regularly fetching MAC statistics. + * @stats_dma_done: Pointer to the flag which indicates DMA completion. + */ +struct falcon_nic_data { + int sram_config; + struct pci_dev *pci_dev2; + struct falcon_board board; + unsigned int stats_disable_count; + bool stats_pending; + struct timer_list stats_timer; + u32 *stats_dma_done; +}; + +static inline struct falcon_board *falcon_board(struct efx_nic *efx) +{ + struct falcon_nic_data *nic_data; + EFX_BUG_ON_PARANOID(efx_nic_rev(efx) > EFX_REV_FALCON_B0); + nic_data = efx->nic_data; + return &nic_data->board; +} + +/** + * struct siena_nic_data - Siena NIC state + * @fw_version: Management controller firmware version + * @fw_build: Firmware build number + * @mcdi: Management-Controller-to-Driver Interface + * @wol_filter_id: Wake-on-LAN packet filter id + */ +struct siena_nic_data { + u64 fw_version; + u32 fw_build; + struct efx_mcdi_iface mcdi; + int wol_filter_id; +}; + +extern void siena_print_fwver(struct efx_nic *efx, char *buf, size_t len); + +extern struct efx_nic_type falcon_a1_nic_type; +extern struct efx_nic_type falcon_b0_nic_type; +extern struct efx_nic_type siena_a0_nic_type; + +/************************************************************************** + * + * Externs + * + ************************************************************************** + */ + +extern void falcon_probe_board(struct efx_nic *efx, u16 revision_info); + +/* TX data path */ +extern int efx_nic_probe_tx(struct efx_tx_queue *tx_queue); +extern void efx_nic_init_tx(struct efx_tx_queue *tx_queue); +extern void efx_nic_fini_tx(struct efx_tx_queue *tx_queue); +extern void efx_nic_remove_tx(struct efx_tx_queue *tx_queue); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +extern void fastcall efx_nic_push_buffers(struct efx_tx_queue *tx_queue); +#else +extern void efx_nic_push_buffers(struct efx_tx_queue *tx_queue); +#endif + +/* RX data path */ +extern int efx_nic_probe_rx(struct efx_rx_queue *rx_queue); +extern void efx_nic_init_rx(struct efx_rx_queue *rx_queue); +extern void efx_nic_fini_rx(struct efx_rx_queue *rx_queue); +extern void efx_nic_remove_rx(struct efx_rx_queue *rx_queue); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +extern void fastcall efx_nic_notify_rx_desc(struct efx_rx_queue *rx_queue); +#else +extern void efx_nic_notify_rx_desc(struct efx_rx_queue *rx_queue); +#endif + +/* Event data path */ +extern int efx_nic_probe_eventq(struct efx_channel *channel); +extern void efx_nic_init_eventq(struct efx_channel *channel); +extern void efx_nic_fini_eventq(struct efx_channel *channel); +extern void efx_nic_remove_eventq(struct efx_channel *channel); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +extern int fastcall efx_nic_process_eventq(struct efx_channel *channel, + int rx_quota); +#else +extern int efx_nic_process_eventq(struct efx_channel *channel, int rx_quota); +#endif +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) +extern void fastcall efx_nic_eventq_read_ack(struct efx_channel *channel); +#else +extern void efx_nic_eventq_read_ack(struct efx_channel *channel); +#endif + +/* MAC/PHY */ +extern void falcon_drain_tx_fifo(struct efx_nic *efx); +extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx); +extern int efx_nic_rx_xoff_thresh, efx_nic_rx_xon_thresh; + +/* Interrupts and test events */ +extern int efx_nic_init_interrupt(struct efx_nic *efx); +extern void efx_nic_enable_interrupts(struct efx_nic *efx); +extern void efx_nic_generate_test_event(struct efx_channel *channel, + unsigned int magic); +extern void efx_nic_generate_interrupt(struct efx_nic *efx); +extern void efx_nic_disable_interrupts(struct efx_nic *efx); +extern void efx_nic_fini_interrupt(struct efx_nic *efx); +extern irqreturn_t efx_nic_fatal_interrupt(struct efx_nic *efx); +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_IRQ_HANDLER_REGS) +extern irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id); +#else +extern irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id, + struct pt_regs *); +#endif +extern void falcon_irq_ack_a1(struct efx_nic *efx); + +#define EFX_IRQ_MOD_RESOLUTION 5 + +/* Global Resources */ +extern int efx_nic_flush_queues(struct efx_nic *efx); +extern void falcon_start_nic_stats(struct efx_nic *efx); +extern void falcon_stop_nic_stats(struct efx_nic *efx); +extern int falcon_reset_xaui(struct efx_nic *efx); +extern int efx_nic_dimension_resources(struct efx_nic *efx, size_t sram_size, + unsigned buffers_per_vnic); +extern void efx_nic_init_common(struct efx_nic *efx); +extern unsigned efx_nic_check_pcie_link(struct efx_nic *efx, + unsigned full_width, + unsigned full_speed); + +int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer, + unsigned int len); +void efx_nic_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer); + +/* Tests */ +struct efx_nic_register_test { + unsigned address; + efx_oword_t mask; +}; +struct efx_nic_table_test { + unsigned address; + unsigned step; + unsigned rows; + efx_oword_t mask; +}; +extern int efx_nic_test_registers(struct efx_nic *efx, + const struct efx_nic_register_test *regs, + size_t n_regs); +extern int efx_nic_test_table(struct efx_nic *efx, + const struct efx_nic_table_test *table, + void (*pattern)(unsigned, efx_qword_t *, int, int), + int a, int b); + +/************************************************************************** + * + * Falcon MAC stats + * + ************************************************************************** + */ + +#define FALCON_STAT_OFFSET(falcon_stat) EFX_VAL(falcon_stat, offset) +#define FALCON_STAT_WIDTH(falcon_stat) EFX_VAL(falcon_stat, WIDTH) + +/* Retrieve statistic from statistics block */ +#define FALCON_STAT(efx, falcon_stat, efx_stat) do { \ + if (FALCON_STAT_WIDTH(falcon_stat) == 16) \ + (efx)->mac_stats.efx_stat += le16_to_cpu( \ + *((__force __le16 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + else if (FALCON_STAT_WIDTH(falcon_stat) == 32) \ + (efx)->mac_stats.efx_stat += le32_to_cpu( \ + *((__force __le32 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + else \ + (efx)->mac_stats.efx_stat += le64_to_cpu( \ + *((__force __le64 *) \ + (efx->stats_buffer.addr + \ + FALCON_STAT_OFFSET(falcon_stat)))); \ + } while (0) + +#define FALCON_MAC_STATS_SIZE 0x100 + +#define MAC_DATA_LBN 0 +#define MAC_DATA_WIDTH 32 + +extern void efx_nic_generate_event(struct efx_channel *channel, + efx_qword_t *event); + +extern void falcon_poll_xmac(struct efx_nic *efx); + +#endif /* EFX_NIC_H */ diff -r aa133dee7792 drivers/net/sfc/null_phy.c --- a/drivers/net/sfc/null_phy.c +++ b/drivers/net/sfc/null_phy.c @@ -1,60 +1,96 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include "efx.h" -#include "falcon.h" -#include "gmii.h" +#include "nic.h" #include "phy.h" -static int falcon_null_phy_check_hw(struct efx_nic *efx) +static int falcon_null_phy_probe(struct efx_nic *efx) { - int link_ok = falcon_xaui_link_ok(efx); + efx->mdio.mmds = 0; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; - /* Generate PHY event that a PHY would have generated */ - if (link_ok != efx->link_up) - efx->mac_op->fake_phy_event(efx); + efx->loopback_modes = FALCON_XMAC_LOOPBACKS; + efx->startup_loopback_mode = LOOPBACK_XGMII; + + strlcpy(efx->phy_name, "none", sizeof(efx->phy_name)); return 0; } -static void falcon_null_phy_reconfigure(struct efx_nic *efx) +static bool falcon_null_phy_poll(struct efx_nic *efx) { - /* CX4 is always 10000FD only */ - efx->link_options = GM_LPA_10000FULL; + bool was_up = efx->link_state.up; - efx->link_up = falcon_xaui_link_ok(efx); + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->link_state.fc = efx->wanted_fc; + efx->link_state.up = !efx->xmac_poll_required; + + return efx->link_state.up != was_up; +} + +static void falcon_null_phy_get_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd) +{ + struct efx_link_state *link_state = &efx->link_state; + + /* There is no port type for this so we fudge it as fibre. */ + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = MDIO_PRTAD_NONE; + ecmd->supported = SUPPORTED_FIBRE; + ecmd->advertising = ADVERTISED_FIBRE; + ecmd->port = PORT_FIBRE; + ecmd->autoneg = AUTONEG_DISABLE; + + ecmd->duplex = link_state->fd; + ecmd->speed = link_state->speed; +} + +static int falcon_null_phy_set_settings(struct efx_nic *efx, + struct ethtool_cmd *ecmd) +{ + struct efx_link_state *link_state = &efx->link_state; + + switch (ecmd->speed) { + case 10: + case 100: + case 1000: + /* TODO: verify PCS mode */ + if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) + return -EOPNOTSUPP; + /* fall through */ + case 10000: + link_state->speed = ecmd->speed; + break; + default: + return -EOPNOTSUPP; + } + + if (ecmd->transceiver != XCVR_INTERNAL || + ecmd->phy_address != (u8)MDIO_PRTAD_NONE || + ecmd->supported != SUPPORTED_FIBRE || + ecmd->advertising != ADVERTISED_FIBRE || + ecmd->duplex != DUPLEX_FULL || + ecmd->port != PORT_FIBRE || + ecmd->autoneg != AUTONEG_DISABLE) + return -EOPNOTSUPP; + return 0; } 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, + .probe = falcon_null_phy_probe, + .init = efx_port_dummy_op_int, + .reconfigure = efx_port_dummy_op_int, + .poll = falcon_null_phy_poll, + .fini = efx_port_dummy_op_void, + .remove = efx_port_dummy_op_void, + .get_settings = falcon_null_phy_get_settings, + .set_settings = falcon_null_phy_set_settings, }; diff -r aa133dee7792 drivers/net/sfc/phy.c --- a/drivers/net/sfc/phy.c +++ /dev/null @@ -1,28 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2007: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Developed by Solarflare Communications - * - * 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" - diff -r aa133dee7792 drivers/net/sfc/phy.h --- a/drivers/net/sfc/phy.h +++ b/drivers/net/sfc/phy.h @@ -1,74 +1,33 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2007-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_PHY_H #define EFX_PHY_H /**************************************************************************** - * 10Xpress (SFX7101) PHY + * 10Xpress (SFX7101 and SFT9001) PHYs */ -extern struct efx_phy_operations falcon_tenxpress_phy_ops; +extern struct efx_phy_operations falcon_sfx7101_phy_ops; +extern struct efx_phy_operations falcon_sft9001_phy_ops; -enum tenxpress_state { - TENXPRESS_STATUS_OFF = 0, - TENXPRESS_STATUS_OTEMP = 1, - TENXPRESS_STATUS_NORMAL = 2, -}; +extern void tenxpress_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); -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); +/* Wait for the PHY to boot. Return 0 on success, -EINVAL if the PHY failed + * to boot due to corrupt flash, or some other negative error code. */ +extern int sft9001_wait_boot(struct efx_nic *efx); /**************************************************************************** - * Marvell 88E1111 "Alaska" PHY control + * AMCC/Quake QT202x PHYs */ -extern struct efx_phy_operations alaska_phy_operations; +extern struct efx_phy_operations falcon_qt202x_phy_ops; -/**************************************************************************** -* 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 */ +/* These PHYs provide 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) @@ -80,11 +39,23 @@ #define QUAKE_LED_TXLINK (0) #define QUAKE_LED_RXLINK (8) -extern void xfp_set_led(struct efx_nic *p, int led, int state); +extern void falcon_qt202x_set_led(struct efx_nic *p, int led, int state); /**************************************************************************** - * NULL PHY ops + * Siena managed PHYs */ -extern struct efx_phy_operations falcon_null_phy_ops; +extern struct efx_phy_operations efx_mcdi_phy_ops; + +extern int efx_mcdi_mdio_read(struct efx_nic *efx, unsigned int bus, + unsigned int prtad, unsigned int devad, + u16 addr, u16 *value_out, u32 *status_out); +extern int efx_mcdi_mdio_write(struct efx_nic *efx, unsigned int bus, + unsigned int prtad, unsigned int devad, + u16 addr, u16 value, u32 *status_out); +extern void efx_mcdi_phy_decode_link(struct efx_nic *efx, + struct efx_link_state *link_state, + u32 speed, u32 flags, u32 fcntl); +extern int efx_mcdi_phy_reconfigure(struct efx_nic *efx); +extern void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa); #endif diff -r aa133dee7792 drivers/net/sfc/pm8358_phy.c --- a/drivers/net/sfc/pm8358_phy.c +++ /dev/null @@ -1,204 +0,0 @@ -/* 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->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, -}; diff -r aa133dee7792 drivers/net/sfc/qt202x_phy.c --- /dev/null +++ b/drivers/net/sfc/qt202x_phy.c @@ -0,0 +1,478 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006-2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ +/* + * Driver for AMCC QT202x SFP+ and XFP adapters; see www.amcc.com for details + */ + +#include +#include +#include "efx.h" +#include "mdio_10g.h" +#include "phy.h" +#include "nic.h" + +#define QT202X_REQUIRED_DEVS (MDIO_DEVS_PCS | \ + MDIO_DEVS_PMAPMD | \ + MDIO_DEVS_PHYXS) + +#define QT202X_LOOPBACKS ((1 << LOOPBACK_PCS) | \ + (1 << LOOPBACK_PMAPMD) | \ + (1 << LOOPBACK_PHYXS_WS)) + +/****************************************************************************/ +/* Quake-specific MDIO registers */ +#define MDIO_QUAKE_LED0_REG (0xD006) + +/* QT2025C only */ +#define PCS_FW_HEARTBEAT_REG 0xd7ee +#define PCS_FW_HEARTB_LBN 0 +#define PCS_FW_HEARTB_WIDTH 8 +#define PCS_FW_PRODUCT_CODE_1 0xd7f0 +#define PCS_FW_VERSION_1 0xd7f3 +#define PCS_FW_BUILD_1 0xd7f6 +#define PCS_UC8051_STATUS_REG 0xd7fd +#define PCS_UC_STATUS_LBN 0 +#define PCS_UC_STATUS_WIDTH 8 +#define PCS_UC_STATUS_FW_SAVE 0x20 +#define PMA_PMD_FTX_CTRL2_REG 0xc309 +#define PMA_PMD_FTX_STATIC_LBN 13 +#define PMA_PMD_VEND1_REG 0xc001 +#define PMA_PMD_VEND1_LBTXD_LBN 15 +#define PCS_VEND1_REG 0xc000 +#define PCS_VEND1_LBTXD_LBN 5 + +void falcon_qt202x_set_led(struct efx_nic *p, int led, int mode) +{ + int addr = MDIO_QUAKE_LED0_REG + led; + efx_mdio_write(p, MDIO_MMD_PMAPMD, addr, mode); +} + +struct qt202x_phy_data { + enum efx_phy_mode phy_mode; + bool bug17190_in_bad_state; + unsigned long bug17190_timer; + u32 firmware_ver; +}; + +#define QT2022C2_MAX_RESET_TIME 500 +#define QT2022C2_RESET_WAIT 10 + +#define QT2025C_MAX_HEARTB_TIME (5 * HZ) +#define QT2025C_HEARTB_WAIT 100 +#define QT2025C_MAX_FWSTART_TIME (25 * HZ / 10) +#define QT2025C_FWSTART_WAIT 100 + +#define BUG17190_INTERVAL (2 * HZ) + +static int qt2025c_wait_heartbeat(struct efx_nic *efx) +{ + unsigned long timeout = jiffies + QT2025C_MAX_HEARTB_TIME; + int reg, old_counter = 0; + + /* Wait for firmware heartbeat to start */ + for (;;) { + int counter; + reg = efx_mdio_read(efx, MDIO_MMD_PCS, PCS_FW_HEARTBEAT_REG); + if (reg < 0) + return reg; + counter = ((reg >> PCS_FW_HEARTB_LBN) & + ((1 << PCS_FW_HEARTB_WIDTH) - 1)); + if (old_counter == 0) + old_counter = counter; + else if (counter != old_counter) + break; + if (time_after(jiffies, timeout)) { + /* Some cables have EEPROMs that conflict with the + * PHY's on-board EEPROM so it cannot load firmware */ + EFX_ERR(efx, "If an SFP+ direct attach cable is" + " connected, please check that it complies" + " with the SFP+ specification\n"); + return -ETIMEDOUT; + } + msleep(QT2025C_HEARTB_WAIT); + } + + return 0; +} + +static int qt2025c_wait_fw_status_good(struct efx_nic *efx) +{ + unsigned long timeout = jiffies + QT2025C_MAX_FWSTART_TIME; + int reg; + + /* Wait for firmware status to look good */ + for (;;) { + reg = efx_mdio_read(efx, MDIO_MMD_PCS, PCS_UC8051_STATUS_REG); + if (reg < 0) + return reg; + if ((reg & + ((1 << PCS_UC_STATUS_WIDTH) - 1) << PCS_UC_STATUS_LBN) >= + PCS_UC_STATUS_FW_SAVE) + break; + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + msleep(QT2025C_FWSTART_WAIT); + } + + return 0; +} + +static void qt2025c_restart_firmware(struct efx_nic *efx) +{ + /* Restart microcontroller execution of firmware from RAM */ + efx_mdio_write(efx, 3, 0xe854, 0x00c0); + efx_mdio_write(efx, 3, 0xe854, 0x0040); + msleep(50); +} + +static int qt2025c_wait_reset(struct efx_nic *efx) +{ + int rc; + + rc = qt2025c_wait_heartbeat(efx); + if (rc != 0) + return rc; + + rc = qt2025c_wait_fw_status_good(efx); + if (rc == -ETIMEDOUT) { + /* Bug 17689: occasionally heartbeat starts but firmware status + * code never progresses beyond 0x00. Try again, once, after + * restarting execution of the firmware image. */ + EFX_LOG(efx, "bashing QT2025C microcontroller\n"); + qt2025c_restart_firmware(efx); + rc = qt2025c_wait_heartbeat(efx); + if (rc != 0) + return rc; + rc = qt2025c_wait_fw_status_good(efx); + } + + return rc; +} + +static void qt2025c_firmware_id(struct efx_nic *efx) +{ + struct qt202x_phy_data *phy_data = efx->phy_data; + u8 firmware_id[9]; + size_t i; + + for (i = 0; i < sizeof(firmware_id); i++) + firmware_id[i] = efx_mdio_read(efx, MDIO_MMD_PCS, + PCS_FW_PRODUCT_CODE_1 + i); + EFX_INFO(efx, "QT2025C firmware %xr%d v%d.%d.%d.%d [20%02d-%02d-%02d]\n", + (firmware_id[0] << 8) | firmware_id[1], firmware_id[2], + firmware_id[3] >> 4, firmware_id[3] & 0xf, + firmware_id[4], firmware_id[5], + firmware_id[6], firmware_id[7], firmware_id[8]); + phy_data->firmware_ver = ((firmware_id[3] & 0xf0) << 20) | + ((firmware_id[3] & 0x0f) << 16) | + (firmware_id[4] << 8) | firmware_id[5]; +} + +static void qt2025c_bug17190_workaround_init(struct efx_nic *efx) +{ + struct qt202x_phy_data *phy_data = efx->phy_data; + + phy_data->bug17190_in_bad_state = false; + phy_data->bug17190_timer = 0; +} + +static void qt2025c_bug17190_workaround(struct efx_nic *efx) +{ + struct qt202x_phy_data *phy_data = efx->phy_data; + + /* The PHY can get stuck in a state where it reports PHY_XS and PMA/PMD + * layers up, but PCS down (no block_lock). If we notice this state + * persisting for a couple of seconds, we switch PMA/PMD loopback + * briefly on and then off again, which is normally sufficient to + * recover it. + */ + if (efx->link_state.up || + !efx_mdio_links_ok(efx, MDIO_DEVS_PMAPMD | MDIO_DEVS_PHYXS)) { + phy_data->bug17190_in_bad_state = false; + return; + } + + if (!phy_data->bug17190_in_bad_state) { + phy_data->bug17190_in_bad_state = true; + phy_data->bug17190_timer = jiffies + BUG17190_INTERVAL; + return; + } + + if (time_after_eq(jiffies, phy_data->bug17190_timer)) { + EFX_LOG(efx, "bashing QT2025C PMA/PMD\n"); + efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1, + MDIO_PMA_CTRL1_LOOPBACK, true); + msleep(100); + efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1, + MDIO_PMA_CTRL1_LOOPBACK, false); + phy_data->bug17190_timer = jiffies + BUG17190_INTERVAL; + } +} + +static int qt2025c_select_phy_mode(struct efx_nic *efx) +{ + struct qt202x_phy_data *phy_data = efx->phy_data; + struct falcon_board *board = falcon_board(efx); + int reg, rc, i; + uint16_t phy_op_mode; + + /* Only 2.0.1.0+ PHY firmware supports the more optimal SFP+ + * Self-Configure mode. Don't attempt any switching if we encounter + * older firmware. */ + if (phy_data->firmware_ver < 0x02000100) + return 0; + + /* In general we will get optimal behaviour in "SFP+ Self-Configure" + * mode; however, that powers down most of the PHY when no module is + * present, so we must use a different mode (any fixed mode will do) + * to be sure that loopbacks will work. */ + phy_op_mode = (efx->loopback_mode == LOOPBACK_NONE) ? 0x0038 : 0x0020; + + /* Only change mode if really necessary */ + reg = efx_mdio_read(efx, 1, 0xc319); + if ((reg & 0x0038) == phy_op_mode) + return 0; + EFX_LOG(efx, "Switching PHY to mode 0x%04x\n", phy_op_mode); + + /* This sequence replicates the register writes configured in the boot + * EEPROM (including the differences between board revisions), except + * that the operating mode is changed, and the PHY is prevented from + * unnecessarily reloading the main firmware image again. */ + efx_mdio_write(efx, 1, 0xc300, 0x0000); + /* (Note: this portion of the boot EEPROM sequence, which bit-bashes 9 + * STOPs onto the firmware/module I2C bus to reset it, varies across + * board revisions, as the bus is connected to different GPIO/LED + * outputs on the PHY.) */ + if (board->major == 0 && board->minor < 2) { + efx_mdio_write(efx, 1, 0xc303, 0x4498); + for (i = 0; i < 9; i++) { + efx_mdio_write(efx, 1, 0xc303, 0x4488); + efx_mdio_write(efx, 1, 0xc303, 0x4480); + efx_mdio_write(efx, 1, 0xc303, 0x4490); + efx_mdio_write(efx, 1, 0xc303, 0x4498); + } + } else { + efx_mdio_write(efx, 1, 0xc303, 0x0920); + efx_mdio_write(efx, 1, 0xd008, 0x0004); + for (i = 0; i < 9; i++) { + efx_mdio_write(efx, 1, 0xc303, 0x0900); + efx_mdio_write(efx, 1, 0xd008, 0x0005); + efx_mdio_write(efx, 1, 0xc303, 0x0920); + efx_mdio_write(efx, 1, 0xd008, 0x0004); + } + efx_mdio_write(efx, 1, 0xc303, 0x4900); + } + efx_mdio_write(efx, 1, 0xc303, 0x4900); + efx_mdio_write(efx, 1, 0xc302, 0x0004); + efx_mdio_write(efx, 1, 0xc316, 0x0013); + efx_mdio_write(efx, 1, 0xc318, 0x0054); + efx_mdio_write(efx, 1, 0xc319, phy_op_mode); + efx_mdio_write(efx, 1, 0xc31a, 0x0098); + efx_mdio_write(efx, 3, 0x0026, 0x0e00); + efx_mdio_write(efx, 3, 0x0027, 0x0013); + efx_mdio_write(efx, 3, 0x0028, 0xa528); + efx_mdio_write(efx, 1, 0xd006, 0x000a); + efx_mdio_write(efx, 1, 0xd007, 0x0009); + efx_mdio_write(efx, 1, 0xd008, 0x0004); + /* This additional write is not present in the boot EEPROM. It + * prevents the PHY's internal boot ROM doing another pointless (and + * slow) reload of the firmware image (the microcontroller's code + * memory is not affected by the microcontroller reset). */ + efx_mdio_write(efx, 1, 0xc317, 0x00ff); + efx_mdio_write(efx, 1, 0xc300, 0x0002); + msleep(20); + + /* Restart microcontroller execution of firmware from RAM */ + qt2025c_restart_firmware(efx); + + /* Wait for the microcontroller to be ready again */ + rc = qt2025c_wait_reset(efx); + if (rc < 0) { + EFX_ERR(efx, "PHY microcontroller reset during mode switch " + "timed out\n"); + return rc; + } + + return 0; +} + +static int qt202x_reset_phy(struct efx_nic *efx) +{ + int rc; + + if (efx->phy_type == PHY_TYPE_QT2025C) { + /* Wait for the reset triggered by falcon_reset_hw() + * to complete */ + rc = qt2025c_wait_reset(efx); + if (rc < 0) + goto fail; + } else { + /* Reset the PHYXS MMD. This is documented as doing + * a complete soft reset. */ + rc = efx_mdio_reset_mmd(efx, MDIO_MMD_PHYXS, + QT2022C2_MAX_RESET_TIME / + QT2022C2_RESET_WAIT, + QT2022C2_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 = efx_mdio_check_mmds(efx, QT202X_REQUIRED_DEVS, MDIO_DEVS_PHYXS); + if (rc < 0) + goto fail; + + falcon_board(efx)->type->init_phy(efx); + + return rc; + + fail: + EFX_ERR(efx, "PHY reset timed out\n"); + return rc; +} + +static int qt202x_phy_probe(struct efx_nic *efx) +{ + struct qt202x_phy_data *phy_data; + + phy_data = kzalloc(sizeof(struct qt202x_phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + efx->phy_data = phy_data; + phy_data->phy_mode = efx->phy_mode; + + if (efx->phy_type == PHY_TYPE_QT2025C) + qt2025c_bug17190_workaround_init(efx); + + efx->mdio.mmds = QT202X_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + + efx->loopback_modes = QT202X_LOOPBACKS | FALCON_XMAC_LOOPBACKS; + efx->startup_loopback_mode = LOOPBACK_PCS; + + strlcpy(efx->phy_name, + (efx->phy_type == PHY_TYPE_QT2025C) ? "Quake SFP+" : "Quake XFP", + sizeof(efx->phy_name)); + + return 0; +} + +static int qt202x_phy_init(struct efx_nic *efx) +{ + u32 devid; + int rc; + + /* Reset the PHY before reading the firmware version */ + rc = qt202x_reset_phy(efx); + if (rc) { + EFX_ERR(efx, "PHY init failed\n"); + return rc; + } + + devid = efx_mdio_read_id(efx, MDIO_MMD_PHYXS); + EFX_INFO(efx, "PHY ID reg %x (OUI %06x model %02x revision %x)\n", + devid, efx_mdio_id_oui(devid), efx_mdio_id_model(devid), + efx_mdio_id_rev(devid)); + if (efx->phy_type == PHY_TYPE_QT2025C) + qt2025c_firmware_id(efx); + + return 0; +} + +static int qt202x_link_ok(struct efx_nic *efx) +{ + return efx_mdio_links_ok(efx, QT202X_REQUIRED_DEVS); +} + +static bool qt202x_phy_poll(struct efx_nic *efx) +{ + bool was_up = efx->link_state.up; + + efx->link_state.up = qt202x_link_ok(efx); + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->link_state.fc = efx->wanted_fc; + + if (efx->phy_type == PHY_TYPE_QT2025C) + qt2025c_bug17190_workaround(efx); + + return efx->link_state.up != was_up; +} + +static int qt202x_phy_reconfigure(struct efx_nic *efx) +{ + struct qt202x_phy_data *phy_data = efx->phy_data; + + if (efx->phy_type == PHY_TYPE_QT2025C) { + int rc = qt2025c_select_phy_mode(efx); + if (rc) + return rc; + + /* There are several different register bits which can + * disable TX (and save power) on direct-attach cables + * or optical transceivers, varying somewhat between + * firmware versions. Only 'static mode' appears to + * cover everything. */ + mdio_set_flag( + &efx->mdio, efx->mdio.prtad, MDIO_MMD_PMAPMD, + PMA_PMD_FTX_CTRL2_REG, 1 << PMA_PMD_FTX_STATIC_LBN, + efx->phy_mode & PHY_MODE_TX_DISABLED || + efx->phy_mode & PHY_MODE_LOW_POWER || + efx->loopback_mode == LOOPBACK_PCS || + efx->loopback_mode == LOOPBACK_PMAPMD); + } else { + /* Reset the PHY when moving from transmitter off or powered + * off, to transmitter on and powered on */ + unsigned mask = PHY_MODE_TX_DISABLED | PHY_MODE_LOW_POWER; + if (!(efx->phy_mode & mask) && (phy_data->phy_mode & mask)) { + int rc = qt202x_reset_phy(efx); + if (rc) + EFX_ERR(efx, "Error resetting phy\n"); + } + + efx_mdio_transmit_disable(efx); + efx_mdio_set_mmds_lpower(efx, + !!(efx->phy_mode & PHY_MODE_LOW_POWER), + QT202X_REQUIRED_DEVS); + } + + efx_mdio_phy_reconfigure(efx); + + phy_data->phy_mode = efx->phy_mode; + + return 0; +} + +static void qt202x_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + mdio45_ethtool_gset(&efx->mdio, ecmd); +} + +static void qt202x_phy_remove(struct efx_nic *efx) +{ + /* Free the context block */ + kfree(efx->phy_data); + efx->phy_data = NULL; +} + +struct efx_phy_operations falcon_qt202x_phy_ops = { + .probe = qt202x_phy_probe, + .init = qt202x_phy_init, + .reconfigure = qt202x_phy_reconfigure, + .poll = qt202x_phy_poll, + .fini = efx_port_dummy_op_void, + .remove = qt202x_phy_remove, + .get_settings = qt202x_phy_get_settings, + .set_settings = efx_mdio_set_settings, + .test_alive = efx_mdio_test_alive, +}; diff -r aa133dee7792 drivers/net/sfc/regs.h --- /dev/null +++ b/drivers/net/sfc/regs.h @@ -0,0 +1,3168 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_REGS_H +#define EFX_REGS_H + +/* + * Falcon hardware architecture definitions have a name prefix following + * the format: + * + * F__ + * + * The following strings are used: + * + * MMIO register MC register Host memory structure + * ------------------------------------------------------------- + * Address R MCR + * Bitfield RF MCRF SF + * Enumerator FE MCFE SE + * + * is the first revision to which the definition applies: + * + * A: Falcon A1 (SFC4000AB) + * B: Falcon B0 (SFC4000BA) + * C: Siena A0 (SFL9021AA) + * + * If the definition has been changed or removed in later revisions + * then is the last revision to which the definition applies; + * otherwise it is "Z". + */ + +/************************************************************************** + * + * Falcon/Siena registers and descriptors + * + ************************************************************************** + */ + +/* ADR_REGION_REG: Address region register */ +#define FR_AZ_ADR_REGION 0x00000000 +#define FRF_AZ_ADR_REGION3_LBN 96 +#define FRF_AZ_ADR_REGION3_WIDTH 18 +#define FRF_AZ_ADR_REGION2_LBN 64 +#define FRF_AZ_ADR_REGION2_WIDTH 18 +#define FRF_AZ_ADR_REGION1_LBN 32 +#define FRF_AZ_ADR_REGION1_WIDTH 18 +#define FRF_AZ_ADR_REGION0_LBN 0 +#define FRF_AZ_ADR_REGION0_WIDTH 18 + +/* INT_EN_REG_KER: Kernel driver Interrupt enable register */ +#define FR_AZ_INT_EN_KER 0x00000010 +#define FRF_AZ_KER_INT_LEVE_SEL_LBN 8 +#define FRF_AZ_KER_INT_LEVE_SEL_WIDTH 6 +#define FRF_AZ_KER_INT_CHAR_LBN 4 +#define FRF_AZ_KER_INT_CHAR_WIDTH 1 +#define FRF_AZ_KER_INT_KER_LBN 3 +#define FRF_AZ_KER_INT_KER_WIDTH 1 +#define FRF_AZ_DRV_INT_EN_KER_LBN 0 +#define FRF_AZ_DRV_INT_EN_KER_WIDTH 1 + +/* INT_EN_REG_CHAR: Char Driver interrupt enable register */ +#define FR_BZ_INT_EN_CHAR 0x00000020 +#define FRF_BZ_CHAR_INT_LEVE_SEL_LBN 8 +#define FRF_BZ_CHAR_INT_LEVE_SEL_WIDTH 6 +#define FRF_BZ_CHAR_INT_CHAR_LBN 4 +#define FRF_BZ_CHAR_INT_CHAR_WIDTH 1 +#define FRF_BZ_CHAR_INT_KER_LBN 3 +#define FRF_BZ_CHAR_INT_KER_WIDTH 1 +#define FRF_BZ_DRV_INT_EN_CHAR_LBN 0 +#define FRF_BZ_DRV_INT_EN_CHAR_WIDTH 1 + +/* INT_ADR_REG_KER: Interrupt host address for Kernel driver */ +#define FR_AZ_INT_ADR_KER 0x00000030 +#define FRF_AZ_NORM_INT_VEC_DIS_KER_LBN 64 +#define FRF_AZ_NORM_INT_VEC_DIS_KER_WIDTH 1 +#define FRF_AZ_INT_ADR_KER_LBN 0 +#define FRF_AZ_INT_ADR_KER_WIDTH 64 + +/* INT_ADR_REG_CHAR: Interrupt host address for Char driver */ +#define FR_BZ_INT_ADR_CHAR 0x00000040 +#define FRF_BZ_NORM_INT_VEC_DIS_CHAR_LBN 64 +#define FRF_BZ_NORM_INT_VEC_DIS_CHAR_WIDTH 1 +#define FRF_BZ_INT_ADR_CHAR_LBN 0 +#define FRF_BZ_INT_ADR_CHAR_WIDTH 64 + +/* INT_ACK_KER: Kernel interrupt acknowledge register */ +#define FR_AA_INT_ACK_KER 0x00000050 +#define FRF_AA_INT_ACK_KER_FIELD_LBN 0 +#define FRF_AA_INT_ACK_KER_FIELD_WIDTH 32 + +/* INT_ISR0_REG: Function 0 Interrupt Acknowlege Status register */ +#define FR_BZ_INT_ISR0 0x00000090 +#define FRF_BZ_INT_ISR_REG_LBN 0 +#define FRF_BZ_INT_ISR_REG_WIDTH 64 + +/* HW_INIT_REG: Hardware initialization register */ +#define FR_AZ_HW_INIT 0x000000c0 +#define FRF_BB_BDMRD_CPLF_FULL_LBN 124 +#define FRF_BB_BDMRD_CPLF_FULL_WIDTH 1 +#define FRF_BB_PCIE_CPL_TIMEOUT_CTRL_LBN 121 +#define FRF_BB_PCIE_CPL_TIMEOUT_CTRL_WIDTH 3 +#define FRF_CZ_TX_MRG_TAGS_LBN 120 +#define FRF_CZ_TX_MRG_TAGS_WIDTH 1 +#define FRF_AB_TRGT_MASK_ALL_LBN 100 +#define FRF_AB_TRGT_MASK_ALL_WIDTH 1 +#define FRF_AZ_DOORBELL_DROP_LBN 92 +#define FRF_AZ_DOORBELL_DROP_WIDTH 8 +#define FRF_AB_TX_RREQ_MASK_EN_LBN 76 +#define FRF_AB_TX_RREQ_MASK_EN_WIDTH 1 +#define FRF_AB_PE_EIDLE_DIS_LBN 75 +#define FRF_AB_PE_EIDLE_DIS_WIDTH 1 +#define FRF_AA_FC_BLOCKING_EN_LBN 45 +#define FRF_AA_FC_BLOCKING_EN_WIDTH 1 +#define FRF_BZ_B2B_REQ_EN_LBN 45 +#define FRF_BZ_B2B_REQ_EN_WIDTH 1 +#define FRF_AA_B2B_REQ_EN_LBN 44 +#define FRF_AA_B2B_REQ_EN_WIDTH 1 +#define FRF_BB_FC_BLOCKING_EN_LBN 44 +#define FRF_BB_FC_BLOCKING_EN_WIDTH 1 +#define FRF_AZ_POST_WR_MASK_LBN 40 +#define FRF_AZ_POST_WR_MASK_WIDTH 4 +#define FRF_AZ_TLP_TC_LBN 34 +#define FRF_AZ_TLP_TC_WIDTH 3 +#define FRF_AZ_TLP_ATTR_LBN 32 +#define FRF_AZ_TLP_ATTR_WIDTH 2 +#define FRF_AB_INTB_VEC_LBN 24 +#define FRF_AB_INTB_VEC_WIDTH 5 +#define FRF_AB_INTA_VEC_LBN 16 +#define FRF_AB_INTA_VEC_WIDTH 5 +#define FRF_AZ_WD_TIMER_LBN 8 +#define FRF_AZ_WD_TIMER_WIDTH 8 +#define FRF_AZ_US_DISABLE_LBN 5 +#define FRF_AZ_US_DISABLE_WIDTH 1 +#define FRF_AZ_TLP_EP_LBN 4 +#define FRF_AZ_TLP_EP_WIDTH 1 +#define FRF_AZ_ATTR_SEL_LBN 3 +#define FRF_AZ_ATTR_SEL_WIDTH 1 +#define FRF_AZ_TD_SEL_LBN 1 +#define FRF_AZ_TD_SEL_WIDTH 1 +#define FRF_AZ_TLP_TD_LBN 0 +#define FRF_AZ_TLP_TD_WIDTH 1 + +/* EE_SPI_HCMD_REG: SPI host command register */ +#define FR_AB_EE_SPI_HCMD 0x00000100 +#define FRF_AB_EE_SPI_HCMD_CMD_EN_LBN 31 +#define FRF_AB_EE_SPI_HCMD_CMD_EN_WIDTH 1 +#define FRF_AB_EE_WR_TIMER_ACTIVE_LBN 28 +#define FRF_AB_EE_WR_TIMER_ACTIVE_WIDTH 1 +#define FRF_AB_EE_SPI_HCMD_SF_SEL_LBN 24 +#define FRF_AB_EE_SPI_HCMD_SF_SEL_WIDTH 1 +#define FRF_AB_EE_SPI_HCMD_DABCNT_LBN 16 +#define FRF_AB_EE_SPI_HCMD_DABCNT_WIDTH 5 +#define FRF_AB_EE_SPI_HCMD_READ_LBN 15 +#define FRF_AB_EE_SPI_HCMD_READ_WIDTH 1 +#define FRF_AB_EE_SPI_HCMD_DUBCNT_LBN 12 +#define FRF_AB_EE_SPI_HCMD_DUBCNT_WIDTH 2 +#define FRF_AB_EE_SPI_HCMD_ADBCNT_LBN 8 +#define FRF_AB_EE_SPI_HCMD_ADBCNT_WIDTH 2 +#define FRF_AB_EE_SPI_HCMD_ENC_LBN 0 +#define FRF_AB_EE_SPI_HCMD_ENC_WIDTH 8 + +/* USR_EV_CFG: User Level Event Configuration register */ +#define FR_CZ_USR_EV_CFG 0x00000100 +#define FRF_CZ_USREV_DIS_LBN 16 +#define FRF_CZ_USREV_DIS_WIDTH 1 +#define FRF_CZ_DFLT_EVQ_LBN 0 +#define FRF_CZ_DFLT_EVQ_WIDTH 10 + +/* EE_SPI_HADR_REG: SPI host address register */ +#define FR_AB_EE_SPI_HADR 0x00000110 +#define FRF_AB_EE_SPI_HADR_DUBYTE_LBN 24 +#define FRF_AB_EE_SPI_HADR_DUBYTE_WIDTH 8 +#define FRF_AB_EE_SPI_HADR_ADR_LBN 0 +#define FRF_AB_EE_SPI_HADR_ADR_WIDTH 24 + +/* EE_SPI_HDATA_REG: SPI host data register */ +#define FR_AB_EE_SPI_HDATA 0x00000120 +#define FRF_AB_EE_SPI_HDATA3_LBN 96 +#define FRF_AB_EE_SPI_HDATA3_WIDTH 32 +#define FRF_AB_EE_SPI_HDATA2_LBN 64 +#define FRF_AB_EE_SPI_HDATA2_WIDTH 32 +#define FRF_AB_EE_SPI_HDATA1_LBN 32 +#define FRF_AB_EE_SPI_HDATA1_WIDTH 32 +#define FRF_AB_EE_SPI_HDATA0_LBN 0 +#define FRF_AB_EE_SPI_HDATA0_WIDTH 32 + +/* EE_BASE_PAGE_REG: Expansion ROM base mirror register */ +#define FR_AB_EE_BASE_PAGE 0x00000130 +#define FRF_AB_EE_EXPROM_MASK_LBN 16 +#define FRF_AB_EE_EXPROM_MASK_WIDTH 13 +#define FRF_AB_EE_EXP_ROM_WINDOW_BASE_LBN 0 +#define FRF_AB_EE_EXP_ROM_WINDOW_BASE_WIDTH 13 + +/* EE_VPD_CFG0_REG: SPI/VPD configuration register 0 */ +#define FR_AB_EE_VPD_CFG0 0x00000140 +#define FRF_AB_EE_SF_FASTRD_EN_LBN 127 +#define FRF_AB_EE_SF_FASTRD_EN_WIDTH 1 +#define FRF_AB_EE_SF_CLOCK_DIV_LBN 120 +#define FRF_AB_EE_SF_CLOCK_DIV_WIDTH 7 +#define FRF_AB_EE_VPD_WIP_POLL_LBN 119 +#define FRF_AB_EE_VPD_WIP_POLL_WIDTH 1 +#define FRF_AB_EE_EE_CLOCK_DIV_LBN 112 +#define FRF_AB_EE_EE_CLOCK_DIV_WIDTH 7 +#define FRF_AB_EE_EE_WR_TMR_VALUE_LBN 96 +#define FRF_AB_EE_EE_WR_TMR_VALUE_WIDTH 16 +#define FRF_AB_EE_VPDW_LENGTH_LBN 80 +#define FRF_AB_EE_VPDW_LENGTH_WIDTH 15 +#define FRF_AB_EE_VPDW_BASE_LBN 64 +#define FRF_AB_EE_VPDW_BASE_WIDTH 15 +#define FRF_AB_EE_VPD_WR_CMD_EN_LBN 56 +#define FRF_AB_EE_VPD_WR_CMD_EN_WIDTH 8 +#define FRF_AB_EE_VPD_BASE_LBN 32 +#define FRF_AB_EE_VPD_BASE_WIDTH 24 +#define FRF_AB_EE_VPD_LENGTH_LBN 16 +#define FRF_AB_EE_VPD_LENGTH_WIDTH 15 +#define FRF_AB_EE_VPD_AD_SIZE_LBN 8 +#define FRF_AB_EE_VPD_AD_SIZE_WIDTH 5 +#define FRF_AB_EE_VPD_ACCESS_ON_LBN 5 +#define FRF_AB_EE_VPD_ACCESS_ON_WIDTH 1 +#define FRF_AB_EE_VPD_ACCESS_BLOCK_LBN 4 +#define FRF_AB_EE_VPD_ACCESS_BLOCK_WIDTH 1 +#define FRF_AB_EE_VPD_DEV_SF_SEL_LBN 2 +#define FRF_AB_EE_VPD_DEV_SF_SEL_WIDTH 1 +#define FRF_AB_EE_VPD_EN_AD9_MODE_LBN 1 +#define FRF_AB_EE_VPD_EN_AD9_MODE_WIDTH 1 +#define FRF_AB_EE_VPD_EN_LBN 0 +#define FRF_AB_EE_VPD_EN_WIDTH 1 + +/* EE_VPD_SW_CNTL_REG: VPD access SW control register */ +#define FR_AB_EE_VPD_SW_CNTL 0x00000150 +#define FRF_AB_EE_VPD_CYCLE_PENDING_LBN 31 +#define FRF_AB_EE_VPD_CYCLE_PENDING_WIDTH 1 +#define FRF_AB_EE_VPD_CYC_WRITE_LBN 28 +#define FRF_AB_EE_VPD_CYC_WRITE_WIDTH 1 +#define FRF_AB_EE_VPD_CYC_ADR_LBN 0 +#define FRF_AB_EE_VPD_CYC_ADR_WIDTH 15 + +/* EE_VPD_SW_DATA_REG: VPD access SW data register */ +#define FR_AB_EE_VPD_SW_DATA 0x00000160 +#define FRF_AB_EE_VPD_CYC_DAT_LBN 0 +#define FRF_AB_EE_VPD_CYC_DAT_WIDTH 32 + +/* PBMX_DBG_IADDR_REG: Capture Module address register */ +#define FR_CZ_PBMX_DBG_IADDR 0x000001f0 +#define FRF_CZ_PBMX_DBG_IADDR_LBN 0 +#define FRF_CZ_PBMX_DBG_IADDR_WIDTH 32 + +/* PCIE_CORE_INDIRECT_REG: Indirect Access to PCIE Core registers */ +#define FR_BB_PCIE_CORE_INDIRECT 0x000001f0 +#define FRF_BB_PCIE_CORE_TARGET_DATA_LBN 32 +#define FRF_BB_PCIE_CORE_TARGET_DATA_WIDTH 32 +#define FRF_BB_PCIE_CORE_INDIRECT_ACCESS_DIR_LBN 15 +#define FRF_BB_PCIE_CORE_INDIRECT_ACCESS_DIR_WIDTH 1 +#define FRF_BB_PCIE_CORE_TARGET_REG_ADRS_LBN 0 +#define FRF_BB_PCIE_CORE_TARGET_REG_ADRS_WIDTH 12 + +/* PBMX_DBG_IDATA_REG: Capture Module data register */ +#define FR_CZ_PBMX_DBG_IDATA 0x000001f8 +#define FRF_CZ_PBMX_DBG_IDATA_LBN 0 +#define FRF_CZ_PBMX_DBG_IDATA_WIDTH 64 + +/* NIC_STAT_REG: NIC status register */ +#define FR_AB_NIC_STAT 0x00000200 +#define FRF_BB_AER_DIS_LBN 34 +#define FRF_BB_AER_DIS_WIDTH 1 +#define FRF_BB_EE_STRAP_EN_LBN 31 +#define FRF_BB_EE_STRAP_EN_WIDTH 1 +#define FRF_BB_EE_STRAP_LBN 24 +#define FRF_BB_EE_STRAP_WIDTH 4 +#define FRF_BB_REVISION_ID_LBN 17 +#define FRF_BB_REVISION_ID_WIDTH 7 +#define FRF_AB_ONCHIP_SRAM_LBN 16 +#define FRF_AB_ONCHIP_SRAM_WIDTH 1 +#define FRF_AB_SF_PRST_LBN 9 +#define FRF_AB_SF_PRST_WIDTH 1 +#define FRF_AB_EE_PRST_LBN 8 +#define FRF_AB_EE_PRST_WIDTH 1 +#define FRF_AB_ATE_MODE_LBN 3 +#define FRF_AB_ATE_MODE_WIDTH 1 +#define FRF_AB_STRAP_PINS_LBN 0 +#define FRF_AB_STRAP_PINS_WIDTH 3 + +/* GPIO_CTL_REG: GPIO control register */ +#define FR_AB_GPIO_CTL 0x00000210 +#define FRF_AB_GPIO_OUT3_LBN 112 +#define FRF_AB_GPIO_OUT3_WIDTH 16 +#define FRF_AB_GPIO_IN3_LBN 104 +#define FRF_AB_GPIO_IN3_WIDTH 8 +#define FRF_AB_GPIO_PWRUP_VALUE3_LBN 96 +#define FRF_AB_GPIO_PWRUP_VALUE3_WIDTH 8 +#define FRF_AB_GPIO_OUT2_LBN 80 +#define FRF_AB_GPIO_OUT2_WIDTH 16 +#define FRF_AB_GPIO_IN2_LBN 72 +#define FRF_AB_GPIO_IN2_WIDTH 8 +#define FRF_AB_GPIO_PWRUP_VALUE2_LBN 64 +#define FRF_AB_GPIO_PWRUP_VALUE2_WIDTH 8 +#define FRF_AB_GPIO15_OEN_LBN 63 +#define FRF_AB_GPIO15_OEN_WIDTH 1 +#define FRF_AB_GPIO14_OEN_LBN 62 +#define FRF_AB_GPIO14_OEN_WIDTH 1 +#define FRF_AB_GPIO13_OEN_LBN 61 +#define FRF_AB_GPIO13_OEN_WIDTH 1 +#define FRF_AB_GPIO12_OEN_LBN 60 +#define FRF_AB_GPIO12_OEN_WIDTH 1 +#define FRF_AB_GPIO11_OEN_LBN 59 +#define FRF_AB_GPIO11_OEN_WIDTH 1 +#define FRF_AB_GPIO10_OEN_LBN 58 +#define FRF_AB_GPIO10_OEN_WIDTH 1 +#define FRF_AB_GPIO9_OEN_LBN 57 +#define FRF_AB_GPIO9_OEN_WIDTH 1 +#define FRF_AB_GPIO8_OEN_LBN 56 +#define FRF_AB_GPIO8_OEN_WIDTH 1 +#define FRF_AB_GPIO15_OUT_LBN 55 +#define FRF_AB_GPIO15_OUT_WIDTH 1 +#define FRF_AB_GPIO14_OUT_LBN 54 +#define FRF_AB_GPIO14_OUT_WIDTH 1 +#define FRF_AB_GPIO13_OUT_LBN 53 +#define FRF_AB_GPIO13_OUT_WIDTH 1 +#define FRF_AB_GPIO12_OUT_LBN 52 +#define FRF_AB_GPIO12_OUT_WIDTH 1 +#define FRF_AB_GPIO11_OUT_LBN 51 +#define FRF_AB_GPIO11_OUT_WIDTH 1 +#define FRF_AB_GPIO10_OUT_LBN 50 +#define FRF_AB_GPIO10_OUT_WIDTH 1 +#define FRF_AB_GPIO9_OUT_LBN 49 +#define FRF_AB_GPIO9_OUT_WIDTH 1 +#define FRF_AB_GPIO8_OUT_LBN 48 +#define FRF_AB_GPIO8_OUT_WIDTH 1 +#define FRF_AB_GPIO15_IN_LBN 47 +#define FRF_AB_GPIO15_IN_WIDTH 1 +#define FRF_AB_GPIO14_IN_LBN 46 +#define FRF_AB_GPIO14_IN_WIDTH 1 +#define FRF_AB_GPIO13_IN_LBN 45 +#define FRF_AB_GPIO13_IN_WIDTH 1 +#define FRF_AB_GPIO12_IN_LBN 44 +#define FRF_AB_GPIO12_IN_WIDTH 1 +#define FRF_AB_GPIO11_IN_LBN 43 +#define FRF_AB_GPIO11_IN_WIDTH 1 +#define FRF_AB_GPIO10_IN_LBN 42 +#define FRF_AB_GPIO10_IN_WIDTH 1 +#define FRF_AB_GPIO9_IN_LBN 41 +#define FRF_AB_GPIO9_IN_WIDTH 1 +#define FRF_AB_GPIO8_IN_LBN 40 +#define FRF_AB_GPIO8_IN_WIDTH 1 +#define FRF_AB_GPIO15_PWRUP_VALUE_LBN 39 +#define FRF_AB_GPIO15_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO14_PWRUP_VALUE_LBN 38 +#define FRF_AB_GPIO14_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO13_PWRUP_VALUE_LBN 37 +#define FRF_AB_GPIO13_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO12_PWRUP_VALUE_LBN 36 +#define FRF_AB_GPIO12_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO11_PWRUP_VALUE_LBN 35 +#define FRF_AB_GPIO11_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO10_PWRUP_VALUE_LBN 34 +#define FRF_AB_GPIO10_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO9_PWRUP_VALUE_LBN 33 +#define FRF_AB_GPIO9_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO8_PWRUP_VALUE_LBN 32 +#define FRF_AB_GPIO8_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_CLK156_OUT_EN_LBN 31 +#define FRF_AB_CLK156_OUT_EN_WIDTH 1 +#define FRF_AB_USE_NIC_CLK_LBN 30 +#define FRF_AB_USE_NIC_CLK_WIDTH 1 +#define FRF_AB_GPIO5_OEN_LBN 29 +#define FRF_AB_GPIO5_OEN_WIDTH 1 +#define FRF_AB_GPIO4_OEN_LBN 28 +#define FRF_AB_GPIO4_OEN_WIDTH 1 +#define FRF_AB_GPIO3_OEN_LBN 27 +#define FRF_AB_GPIO3_OEN_WIDTH 1 +#define FRF_AB_GPIO2_OEN_LBN 26 +#define FRF_AB_GPIO2_OEN_WIDTH 1 +#define FRF_AB_GPIO1_OEN_LBN 25 +#define FRF_AB_GPIO1_OEN_WIDTH 1 +#define FRF_AB_GPIO0_OEN_LBN 24 +#define FRF_AB_GPIO0_OEN_WIDTH 1 +#define FRF_AB_GPIO7_OUT_LBN 23 +#define FRF_AB_GPIO7_OUT_WIDTH 1 +#define FRF_AB_GPIO6_OUT_LBN 22 +#define FRF_AB_GPIO6_OUT_WIDTH 1 +#define FRF_AB_GPIO5_OUT_LBN 21 +#define FRF_AB_GPIO5_OUT_WIDTH 1 +#define FRF_AB_GPIO4_OUT_LBN 20 +#define FRF_AB_GPIO4_OUT_WIDTH 1 +#define FRF_AB_GPIO3_OUT_LBN 19 +#define FRF_AB_GPIO3_OUT_WIDTH 1 +#define FRF_AB_GPIO2_OUT_LBN 18 +#define FRF_AB_GPIO2_OUT_WIDTH 1 +#define FRF_AB_GPIO1_OUT_LBN 17 +#define FRF_AB_GPIO1_OUT_WIDTH 1 +#define FRF_AB_GPIO0_OUT_LBN 16 +#define FRF_AB_GPIO0_OUT_WIDTH 1 +#define FRF_AB_GPIO7_IN_LBN 15 +#define FRF_AB_GPIO7_IN_WIDTH 1 +#define FRF_AB_GPIO6_IN_LBN 14 +#define FRF_AB_GPIO6_IN_WIDTH 1 +#define FRF_AB_GPIO5_IN_LBN 13 +#define FRF_AB_GPIO5_IN_WIDTH 1 +#define FRF_AB_GPIO4_IN_LBN 12 +#define FRF_AB_GPIO4_IN_WIDTH 1 +#define FRF_AB_GPIO3_IN_LBN 11 +#define FRF_AB_GPIO3_IN_WIDTH 1 +#define FRF_AB_GPIO2_IN_LBN 10 +#define FRF_AB_GPIO2_IN_WIDTH 1 +#define FRF_AB_GPIO1_IN_LBN 9 +#define FRF_AB_GPIO1_IN_WIDTH 1 +#define FRF_AB_GPIO0_IN_LBN 8 +#define FRF_AB_GPIO0_IN_WIDTH 1 +#define FRF_AB_GPIO7_PWRUP_VALUE_LBN 7 +#define FRF_AB_GPIO7_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO6_PWRUP_VALUE_LBN 6 +#define FRF_AB_GPIO6_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO5_PWRUP_VALUE_LBN 5 +#define FRF_AB_GPIO5_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO4_PWRUP_VALUE_LBN 4 +#define FRF_AB_GPIO4_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO3_PWRUP_VALUE_LBN 3 +#define FRF_AB_GPIO3_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO2_PWRUP_VALUE_LBN 2 +#define FRF_AB_GPIO2_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO1_PWRUP_VALUE_LBN 1 +#define FRF_AB_GPIO1_PWRUP_VALUE_WIDTH 1 +#define FRF_AB_GPIO0_PWRUP_VALUE_LBN 0 +#define FRF_AB_GPIO0_PWRUP_VALUE_WIDTH 1 + +/* GLB_CTL_REG: Global control register */ +#define FR_AB_GLB_CTL 0x00000220 +#define FRF_AB_EXT_PHY_RST_CTL_LBN 63 +#define FRF_AB_EXT_PHY_RST_CTL_WIDTH 1 +#define FRF_AB_XAUI_SD_RST_CTL_LBN 62 +#define FRF_AB_XAUI_SD_RST_CTL_WIDTH 1 +#define FRF_AB_PCIE_SD_RST_CTL_LBN 61 +#define FRF_AB_PCIE_SD_RST_CTL_WIDTH 1 +#define FRF_AA_PCIX_RST_CTL_LBN 60 +#define FRF_AA_PCIX_RST_CTL_WIDTH 1 +#define FRF_BB_BIU_RST_CTL_LBN 60 +#define FRF_BB_BIU_RST_CTL_WIDTH 1 +#define FRF_AB_PCIE_STKY_RST_CTL_LBN 59 +#define FRF_AB_PCIE_STKY_RST_CTL_WIDTH 1 +#define FRF_AB_PCIE_NSTKY_RST_CTL_LBN 58 +#define FRF_AB_PCIE_NSTKY_RST_CTL_WIDTH 1 +#define FRF_AB_PCIE_CORE_RST_CTL_LBN 57 +#define FRF_AB_PCIE_CORE_RST_CTL_WIDTH 1 +#define FRF_AB_XGRX_RST_CTL_LBN 56 +#define FRF_AB_XGRX_RST_CTL_WIDTH 1 +#define FRF_AB_XGTX_RST_CTL_LBN 55 +#define FRF_AB_XGTX_RST_CTL_WIDTH 1 +#define FRF_AB_EM_RST_CTL_LBN 54 +#define FRF_AB_EM_RST_CTL_WIDTH 1 +#define FRF_AB_EV_RST_CTL_LBN 53 +#define FRF_AB_EV_RST_CTL_WIDTH 1 +#define FRF_AB_SR_RST_CTL_LBN 52 +#define FRF_AB_SR_RST_CTL_WIDTH 1 +#define FRF_AB_RX_RST_CTL_LBN 51 +#define FRF_AB_RX_RST_CTL_WIDTH 1 +#define FRF_AB_TX_RST_CTL_LBN 50 +#define FRF_AB_TX_RST_CTL_WIDTH 1 +#define FRF_AB_EE_RST_CTL_LBN 49 +#define FRF_AB_EE_RST_CTL_WIDTH 1 +#define FRF_AB_CS_RST_CTL_LBN 48 +#define FRF_AB_CS_RST_CTL_WIDTH 1 +#define FRF_AB_HOT_RST_CTL_LBN 40 +#define FRF_AB_HOT_RST_CTL_WIDTH 2 +#define FRF_AB_RST_EXT_PHY_LBN 31 +#define FRF_AB_RST_EXT_PHY_WIDTH 1 +#define FRF_AB_RST_XAUI_SD_LBN 30 +#define FRF_AB_RST_XAUI_SD_WIDTH 1 +#define FRF_AB_RST_PCIE_SD_LBN 29 +#define FRF_AB_RST_PCIE_SD_WIDTH 1 +#define FRF_AA_RST_PCIX_LBN 28 +#define FRF_AA_RST_PCIX_WIDTH 1 +#define FRF_BB_RST_BIU_LBN 28 +#define FRF_BB_RST_BIU_WIDTH 1 +#define FRF_AB_RST_PCIE_STKY_LBN 27 +#define FRF_AB_RST_PCIE_STKY_WIDTH 1 +#define FRF_AB_RST_PCIE_NSTKY_LBN 26 +#define FRF_AB_RST_PCIE_NSTKY_WIDTH 1 +#define FRF_AB_RST_PCIE_CORE_LBN 25 +#define FRF_AB_RST_PCIE_CORE_WIDTH 1 +#define FRF_AB_RST_XGRX_LBN 24 +#define FRF_AB_RST_XGRX_WIDTH 1 +#define FRF_AB_RST_XGTX_LBN 23 +#define FRF_AB_RST_XGTX_WIDTH 1 +#define FRF_AB_RST_EM_LBN 22 +#define FRF_AB_RST_EM_WIDTH 1 +#define FRF_AB_RST_EV_LBN 21 +#define FRF_AB_RST_EV_WIDTH 1 +#define FRF_AB_RST_SR_LBN 20 +#define FRF_AB_RST_SR_WIDTH 1 +#define FRF_AB_RST_RX_LBN 19 +#define FRF_AB_RST_RX_WIDTH 1 +#define FRF_AB_RST_TX_LBN 18 +#define FRF_AB_RST_TX_WIDTH 1 +#define FRF_AB_RST_SF_LBN 17 +#define FRF_AB_RST_SF_WIDTH 1 +#define FRF_AB_RST_CS_LBN 16 +#define FRF_AB_RST_CS_WIDTH 1 +#define FRF_AB_INT_RST_DUR_LBN 4 +#define FRF_AB_INT_RST_DUR_WIDTH 3 +#define FRF_AB_EXT_PHY_RST_DUR_LBN 1 +#define FRF_AB_EXT_PHY_RST_DUR_WIDTH 3 +#define FFE_AB_EXT_PHY_RST_DUR_10240US 7 +#define FFE_AB_EXT_PHY_RST_DUR_5120US 6 +#define FFE_AB_EXT_PHY_RST_DUR_2560US 5 +#define FFE_AB_EXT_PHY_RST_DUR_1280US 4 +#define FFE_AB_EXT_PHY_RST_DUR_640US 3 +#define FFE_AB_EXT_PHY_RST_DUR_320US 2 +#define FFE_AB_EXT_PHY_RST_DUR_160US 1 +#define FFE_AB_EXT_PHY_RST_DUR_80US 0 +#define FRF_AB_SWRST_LBN 0 +#define FRF_AB_SWRST_WIDTH 1 + +/* FATAL_INTR_REG_KER: Fatal interrupt register for Kernel */ +#define FR_AZ_FATAL_INTR_KER 0x00000230 +#define FRF_CZ_SRAM_PERR_INT_P_KER_EN_LBN 44 +#define FRF_CZ_SRAM_PERR_INT_P_KER_EN_WIDTH 1 +#define FRF_AB_PCI_BUSERR_INT_KER_EN_LBN 43 +#define FRF_AB_PCI_BUSERR_INT_KER_EN_WIDTH 1 +#define FRF_CZ_MBU_PERR_INT_KER_EN_LBN 43 +#define FRF_CZ_MBU_PERR_INT_KER_EN_WIDTH 1 +#define FRF_AZ_SRAM_OOB_INT_KER_EN_LBN 42 +#define FRF_AZ_SRAM_OOB_INT_KER_EN_WIDTH 1 +#define FRF_AZ_BUFID_OOB_INT_KER_EN_LBN 41 +#define FRF_AZ_BUFID_OOB_INT_KER_EN_WIDTH 1 +#define FRF_AZ_MEM_PERR_INT_KER_EN_LBN 40 +#define FRF_AZ_MEM_PERR_INT_KER_EN_WIDTH 1 +#define FRF_AZ_RBUF_OWN_INT_KER_EN_LBN 39 +#define FRF_AZ_RBUF_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_TBUF_OWN_INT_KER_EN_LBN 38 +#define FRF_AZ_TBUF_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_RDESCQ_OWN_INT_KER_EN_LBN 37 +#define FRF_AZ_RDESCQ_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_TDESCQ_OWN_INT_KER_EN_LBN 36 +#define FRF_AZ_TDESCQ_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_EVQ_OWN_INT_KER_EN_LBN 35 +#define FRF_AZ_EVQ_OWN_INT_KER_EN_WIDTH 1 +#define FRF_AZ_EVF_OFLO_INT_KER_EN_LBN 34 +#define FRF_AZ_EVF_OFLO_INT_KER_EN_WIDTH 1 +#define FRF_AZ_ILL_ADR_INT_KER_EN_LBN 33 +#define FRF_AZ_ILL_ADR_INT_KER_EN_WIDTH 1 +#define FRF_AZ_SRM_PERR_INT_KER_EN_LBN 32 +#define FRF_AZ_SRM_PERR_INT_KER_EN_WIDTH 1 +#define FRF_CZ_SRAM_PERR_INT_P_KER_LBN 12 +#define FRF_CZ_SRAM_PERR_INT_P_KER_WIDTH 1 +#define FRF_AB_PCI_BUSERR_INT_KER_LBN 11 +#define FRF_AB_PCI_BUSERR_INT_KER_WIDTH 1 +#define FRF_CZ_MBU_PERR_INT_KER_LBN 11 +#define FRF_CZ_MBU_PERR_INT_KER_WIDTH 1 +#define FRF_AZ_SRAM_OOB_INT_KER_LBN 10 +#define FRF_AZ_SRAM_OOB_INT_KER_WIDTH 1 +#define FRF_AZ_BUFID_DC_OOB_INT_KER_LBN 9 +#define FRF_AZ_BUFID_DC_OOB_INT_KER_WIDTH 1 +#define FRF_AZ_MEM_PERR_INT_KER_LBN 8 +#define FRF_AZ_MEM_PERR_INT_KER_WIDTH 1 +#define FRF_AZ_RBUF_OWN_INT_KER_LBN 7 +#define FRF_AZ_RBUF_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_TBUF_OWN_INT_KER_LBN 6 +#define FRF_AZ_TBUF_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_RDESCQ_OWN_INT_KER_LBN 5 +#define FRF_AZ_RDESCQ_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_TDESCQ_OWN_INT_KER_LBN 4 +#define FRF_AZ_TDESCQ_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_EVQ_OWN_INT_KER_LBN 3 +#define FRF_AZ_EVQ_OWN_INT_KER_WIDTH 1 +#define FRF_AZ_EVF_OFLO_INT_KER_LBN 2 +#define FRF_AZ_EVF_OFLO_INT_KER_WIDTH 1 +#define FRF_AZ_ILL_ADR_INT_KER_LBN 1 +#define FRF_AZ_ILL_ADR_INT_KER_WIDTH 1 +#define FRF_AZ_SRM_PERR_INT_KER_LBN 0 +#define FRF_AZ_SRM_PERR_INT_KER_WIDTH 1 + +/* FATAL_INTR_REG_CHAR: Fatal interrupt register for Char */ +#define FR_BZ_FATAL_INTR_CHAR 0x00000240 +#define FRF_CZ_SRAM_PERR_INT_P_CHAR_EN_LBN 44 +#define FRF_CZ_SRAM_PERR_INT_P_CHAR_EN_WIDTH 1 +#define FRF_BB_PCI_BUSERR_INT_CHAR_EN_LBN 43 +#define FRF_BB_PCI_BUSERR_INT_CHAR_EN_WIDTH 1 +#define FRF_CZ_MBU_PERR_INT_CHAR_EN_LBN 43 +#define FRF_CZ_MBU_PERR_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_SRAM_OOB_INT_CHAR_EN_LBN 42 +#define FRF_BZ_SRAM_OOB_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_BUFID_OOB_INT_CHAR_EN_LBN 41 +#define FRF_BZ_BUFID_OOB_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_MEM_PERR_INT_CHAR_EN_LBN 40 +#define FRF_BZ_MEM_PERR_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_RBUF_OWN_INT_CHAR_EN_LBN 39 +#define FRF_BZ_RBUF_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_TBUF_OWN_INT_CHAR_EN_LBN 38 +#define FRF_BZ_TBUF_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_RDESCQ_OWN_INT_CHAR_EN_LBN 37 +#define FRF_BZ_RDESCQ_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_TDESCQ_OWN_INT_CHAR_EN_LBN 36 +#define FRF_BZ_TDESCQ_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_EVQ_OWN_INT_CHAR_EN_LBN 35 +#define FRF_BZ_EVQ_OWN_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_EVF_OFLO_INT_CHAR_EN_LBN 34 +#define FRF_BZ_EVF_OFLO_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_ILL_ADR_INT_CHAR_EN_LBN 33 +#define FRF_BZ_ILL_ADR_INT_CHAR_EN_WIDTH 1 +#define FRF_BZ_SRM_PERR_INT_CHAR_EN_LBN 32 +#define FRF_BZ_SRM_PERR_INT_CHAR_EN_WIDTH 1 +#define FRF_CZ_SRAM_PERR_INT_P_CHAR_LBN 12 +#define FRF_CZ_SRAM_PERR_INT_P_CHAR_WIDTH 1 +#define FRF_BB_PCI_BUSERR_INT_CHAR_LBN 11 +#define FRF_BB_PCI_BUSERR_INT_CHAR_WIDTH 1 +#define FRF_CZ_MBU_PERR_INT_CHAR_LBN 11 +#define FRF_CZ_MBU_PERR_INT_CHAR_WIDTH 1 +#define FRF_BZ_SRAM_OOB_INT_CHAR_LBN 10 +#define FRF_BZ_SRAM_OOB_INT_CHAR_WIDTH 1 +#define FRF_BZ_BUFID_DC_OOB_INT_CHAR_LBN 9 +#define FRF_BZ_BUFID_DC_OOB_INT_CHAR_WIDTH 1 +#define FRF_BZ_MEM_PERR_INT_CHAR_LBN 8 +#define FRF_BZ_MEM_PERR_INT_CHAR_WIDTH 1 +#define FRF_BZ_RBUF_OWN_INT_CHAR_LBN 7 +#define FRF_BZ_RBUF_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_TBUF_OWN_INT_CHAR_LBN 6 +#define FRF_BZ_TBUF_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_RDESCQ_OWN_INT_CHAR_LBN 5 +#define FRF_BZ_RDESCQ_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_TDESCQ_OWN_INT_CHAR_LBN 4 +#define FRF_BZ_TDESCQ_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_EVQ_OWN_INT_CHAR_LBN 3 +#define FRF_BZ_EVQ_OWN_INT_CHAR_WIDTH 1 +#define FRF_BZ_EVF_OFLO_INT_CHAR_LBN 2 +#define FRF_BZ_EVF_OFLO_INT_CHAR_WIDTH 1 +#define FRF_BZ_ILL_ADR_INT_CHAR_LBN 1 +#define FRF_BZ_ILL_ADR_INT_CHAR_WIDTH 1 +#define FRF_BZ_SRM_PERR_INT_CHAR_LBN 0 +#define FRF_BZ_SRM_PERR_INT_CHAR_WIDTH 1 + +/* DP_CTRL_REG: Datapath control register */ +#define FR_BZ_DP_CTRL 0x00000250 +#define FRF_BZ_FLS_EVQ_ID_LBN 0 +#define FRF_BZ_FLS_EVQ_ID_WIDTH 12 + +/* MEM_STAT_REG: Memory status register */ +#define FR_AZ_MEM_STAT 0x00000260 +#define FRF_AB_MEM_PERR_VEC_LBN 53 +#define FRF_AB_MEM_PERR_VEC_WIDTH 38 +#define FRF_AB_MBIST_CORR_LBN 38 +#define FRF_AB_MBIST_CORR_WIDTH 15 +#define FRF_AB_MBIST_ERR_LBN 0 +#define FRF_AB_MBIST_ERR_WIDTH 40 +#define FRF_CZ_MEM_PERR_VEC_LBN 0 +#define FRF_CZ_MEM_PERR_VEC_WIDTH 35 + +/* CS_DEBUG_REG: Debug register */ +#define FR_AZ_CS_DEBUG 0x00000270 +#define FRF_AB_GLB_DEBUG2_SEL_LBN 50 +#define FRF_AB_GLB_DEBUG2_SEL_WIDTH 3 +#define FRF_AB_DEBUG_BLK_SEL2_LBN 47 +#define FRF_AB_DEBUG_BLK_SEL2_WIDTH 3 +#define FRF_AB_DEBUG_BLK_SEL1_LBN 44 +#define FRF_AB_DEBUG_BLK_SEL1_WIDTH 3 +#define FRF_AB_DEBUG_BLK_SEL0_LBN 41 +#define FRF_AB_DEBUG_BLK_SEL0_WIDTH 3 +#define FRF_CZ_CS_PORT_NUM_LBN 40 +#define FRF_CZ_CS_PORT_NUM_WIDTH 2 +#define FRF_AB_MISC_DEBUG_ADDR_LBN 36 +#define FRF_AB_MISC_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_SERDES_DEBUG_ADDR_LBN 31 +#define FRF_AB_SERDES_DEBUG_ADDR_WIDTH 5 +#define FRF_CZ_CS_PORT_FPE_LBN 1 +#define FRF_CZ_CS_PORT_FPE_WIDTH 35 +#define FRF_AB_EM_DEBUG_ADDR_LBN 26 +#define FRF_AB_EM_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_SR_DEBUG_ADDR_LBN 21 +#define FRF_AB_SR_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_EV_DEBUG_ADDR_LBN 16 +#define FRF_AB_EV_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_RX_DEBUG_ADDR_LBN 11 +#define FRF_AB_RX_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_TX_DEBUG_ADDR_LBN 6 +#define FRF_AB_TX_DEBUG_ADDR_WIDTH 5 +#define FRF_AB_CS_BIU_DEBUG_ADDR_LBN 1 +#define FRF_AB_CS_BIU_DEBUG_ADDR_WIDTH 5 +#define FRF_AZ_CS_DEBUG_EN_LBN 0 +#define FRF_AZ_CS_DEBUG_EN_WIDTH 1 + +/* DRIVER_REG: Driver scratch register [0-7] */ +#define FR_AZ_DRIVER 0x00000280 +#define FR_AZ_DRIVER_STEP 16 +#define FR_AZ_DRIVER_ROWS 8 +#define FRF_AZ_DRIVER_DW0_LBN 0 +#define FRF_AZ_DRIVER_DW0_WIDTH 32 + +/* ALTERA_BUILD_REG: Altera build register */ +#define FR_AZ_ALTERA_BUILD 0x00000300 +#define FRF_AZ_ALTERA_BUILD_VER_LBN 0 +#define FRF_AZ_ALTERA_BUILD_VER_WIDTH 32 + +/* CSR_SPARE_REG: Spare register */ +#define FR_AZ_CSR_SPARE 0x00000310 +#define FRF_AB_MEM_PERR_EN_LBN 64 +#define FRF_AB_MEM_PERR_EN_WIDTH 38 +#define FRF_CZ_MEM_PERR_EN_LBN 64 +#define FRF_CZ_MEM_PERR_EN_WIDTH 35 +#define FRF_AB_MEM_PERR_EN_TX_DATA_LBN 72 +#define FRF_AB_MEM_PERR_EN_TX_DATA_WIDTH 2 +#define FRF_AZ_CSR_SPARE_BITS_LBN 0 +#define FRF_AZ_CSR_SPARE_BITS_WIDTH 32 + +/* PCIE_SD_CTL0123_REG: PCIE SerDes control register 0 to 3 */ +#define FR_AB_PCIE_SD_CTL0123 0x00000320 +#define FRF_AB_PCIE_TESTSIG_H_LBN 96 +#define FRF_AB_PCIE_TESTSIG_H_WIDTH 19 +#define FRF_AB_PCIE_TESTSIG_L_LBN 64 +#define FRF_AB_PCIE_TESTSIG_L_WIDTH 19 +#define FRF_AB_PCIE_OFFSET_LBN 56 +#define FRF_AB_PCIE_OFFSET_WIDTH 8 +#define FRF_AB_PCIE_OFFSETEN_H_LBN 55 +#define FRF_AB_PCIE_OFFSETEN_H_WIDTH 1 +#define FRF_AB_PCIE_OFFSETEN_L_LBN 54 +#define FRF_AB_PCIE_OFFSETEN_L_WIDTH 1 +#define FRF_AB_PCIE_HIVMODE_H_LBN 53 +#define FRF_AB_PCIE_HIVMODE_H_WIDTH 1 +#define FRF_AB_PCIE_HIVMODE_L_LBN 52 +#define FRF_AB_PCIE_HIVMODE_L_WIDTH 1 +#define FRF_AB_PCIE_PARRESET_H_LBN 51 +#define FRF_AB_PCIE_PARRESET_H_WIDTH 1 +#define FRF_AB_PCIE_PARRESET_L_LBN 50 +#define FRF_AB_PCIE_PARRESET_L_WIDTH 1 +#define FRF_AB_PCIE_LPBKWDRV_H_LBN 49 +#define FRF_AB_PCIE_LPBKWDRV_H_WIDTH 1 +#define FRF_AB_PCIE_LPBKWDRV_L_LBN 48 +#define FRF_AB_PCIE_LPBKWDRV_L_WIDTH 1 +#define FRF_AB_PCIE_LPBK_LBN 40 +#define FRF_AB_PCIE_LPBK_WIDTH 8 +#define FRF_AB_PCIE_PARLPBK_LBN 32 +#define FRF_AB_PCIE_PARLPBK_WIDTH 8 +#define FRF_AB_PCIE_RXTERMADJ_H_LBN 30 +#define FRF_AB_PCIE_RXTERMADJ_H_WIDTH 2 +#define FRF_AB_PCIE_RXTERMADJ_L_LBN 28 +#define FRF_AB_PCIE_RXTERMADJ_L_WIDTH 2 +#define FFE_AB_PCIE_RXTERMADJ_MIN15PCNT 3 +#define FFE_AB_PCIE_RXTERMADJ_PL10PCNT 2 +#define FFE_AB_PCIE_RXTERMADJ_MIN17PCNT 1 +#define FFE_AB_PCIE_RXTERMADJ_NOMNL 0 +#define FRF_AB_PCIE_TXTERMADJ_H_LBN 26 +#define FRF_AB_PCIE_TXTERMADJ_H_WIDTH 2 +#define FRF_AB_PCIE_TXTERMADJ_L_LBN 24 +#define FRF_AB_PCIE_TXTERMADJ_L_WIDTH 2 +#define FFE_AB_PCIE_TXTERMADJ_MIN15PCNT 3 +#define FFE_AB_PCIE_TXTERMADJ_PL10PCNT 2 +#define FFE_AB_PCIE_TXTERMADJ_MIN17PCNT 1 +#define FFE_AB_PCIE_TXTERMADJ_NOMNL 0 +#define FRF_AB_PCIE_RXEQCTL_H_LBN 18 +#define FRF_AB_PCIE_RXEQCTL_H_WIDTH 2 +#define FRF_AB_PCIE_RXEQCTL_L_LBN 16 +#define FRF_AB_PCIE_RXEQCTL_L_WIDTH 2 +#define FFE_AB_PCIE_RXEQCTL_OFF_ALT 3 +#define FFE_AB_PCIE_RXEQCTL_OFF 2 +#define FFE_AB_PCIE_RXEQCTL_MIN 1 +#define FFE_AB_PCIE_RXEQCTL_MAX 0 +#define FRF_AB_PCIE_HIDRV_LBN 8 +#define FRF_AB_PCIE_HIDRV_WIDTH 8 +#define FRF_AB_PCIE_LODRV_LBN 0 +#define FRF_AB_PCIE_LODRV_WIDTH 8 + +/* PCIE_SD_CTL45_REG: PCIE SerDes control register 4 and 5 */ +#define FR_AB_PCIE_SD_CTL45 0x00000330 +#define FRF_AB_PCIE_DTX7_LBN 60 +#define FRF_AB_PCIE_DTX7_WIDTH 4 +#define FRF_AB_PCIE_DTX6_LBN 56 +#define FRF_AB_PCIE_DTX6_WIDTH 4 +#define FRF_AB_PCIE_DTX5_LBN 52 +#define FRF_AB_PCIE_DTX5_WIDTH 4 +#define FRF_AB_PCIE_DTX4_LBN 48 +#define FRF_AB_PCIE_DTX4_WIDTH 4 +#define FRF_AB_PCIE_DTX3_LBN 44 +#define FRF_AB_PCIE_DTX3_WIDTH 4 +#define FRF_AB_PCIE_DTX2_LBN 40 +#define FRF_AB_PCIE_DTX2_WIDTH 4 +#define FRF_AB_PCIE_DTX1_LBN 36 +#define FRF_AB_PCIE_DTX1_WIDTH 4 +#define FRF_AB_PCIE_DTX0_LBN 32 +#define FRF_AB_PCIE_DTX0_WIDTH 4 +#define FRF_AB_PCIE_DEQ7_LBN 28 +#define FRF_AB_PCIE_DEQ7_WIDTH 4 +#define FRF_AB_PCIE_DEQ6_LBN 24 +#define FRF_AB_PCIE_DEQ6_WIDTH 4 +#define FRF_AB_PCIE_DEQ5_LBN 20 +#define FRF_AB_PCIE_DEQ5_WIDTH 4 +#define FRF_AB_PCIE_DEQ4_LBN 16 +#define FRF_AB_PCIE_DEQ4_WIDTH 4 +#define FRF_AB_PCIE_DEQ3_LBN 12 +#define FRF_AB_PCIE_DEQ3_WIDTH 4 +#define FRF_AB_PCIE_DEQ2_LBN 8 +#define FRF_AB_PCIE_DEQ2_WIDTH 4 +#define FRF_AB_PCIE_DEQ1_LBN 4 +#define FRF_AB_PCIE_DEQ1_WIDTH 4 +#define FRF_AB_PCIE_DEQ0_LBN 0 +#define FRF_AB_PCIE_DEQ0_WIDTH 4 + +/* PCIE_PCS_CTL_STAT_REG: PCIE PCS control and status register */ +#define FR_AB_PCIE_PCS_CTL_STAT 0x00000340 +#define FRF_AB_PCIE_PRBSERRCOUNT0_H_LBN 52 +#define FRF_AB_PCIE_PRBSERRCOUNT0_H_WIDTH 4 +#define FRF_AB_PCIE_PRBSERRCOUNT0_L_LBN 48 +#define FRF_AB_PCIE_PRBSERRCOUNT0_L_WIDTH 4 +#define FRF_AB_PCIE_PRBSERR_LBN 40 +#define FRF_AB_PCIE_PRBSERR_WIDTH 8 +#define FRF_AB_PCIE_PRBSERRH0_LBN 32 +#define FRF_AB_PCIE_PRBSERRH0_WIDTH 8 +#define FRF_AB_PCIE_FASTINIT_H_LBN 15 +#define FRF_AB_PCIE_FASTINIT_H_WIDTH 1 +#define FRF_AB_PCIE_FASTINIT_L_LBN 14 +#define FRF_AB_PCIE_FASTINIT_L_WIDTH 1 +#define FRF_AB_PCIE_CTCDISABLE_H_LBN 13 +#define FRF_AB_PCIE_CTCDISABLE_H_WIDTH 1 +#define FRF_AB_PCIE_CTCDISABLE_L_LBN 12 +#define FRF_AB_PCIE_CTCDISABLE_L_WIDTH 1 +#define FRF_AB_PCIE_PRBSSYNC_H_LBN 11 +#define FRF_AB_PCIE_PRBSSYNC_H_WIDTH 1 +#define FRF_AB_PCIE_PRBSSYNC_L_LBN 10 +#define FRF_AB_PCIE_PRBSSYNC_L_WIDTH 1 +#define FRF_AB_PCIE_PRBSERRACK_H_LBN 9 +#define FRF_AB_PCIE_PRBSERRACK_H_WIDTH 1 +#define FRF_AB_PCIE_PRBSERRACK_L_LBN 8 +#define FRF_AB_PCIE_PRBSERRACK_L_WIDTH 1 +#define FRF_AB_PCIE_PRBSSEL_LBN 0 +#define FRF_AB_PCIE_PRBSSEL_WIDTH 8 + +/* DEBUG_DATA_OUT_REG: Live Debug and Debug 2 out ports */ +#define FR_BB_DEBUG_DATA_OUT 0x00000350 +#define FRF_BB_DEBUG2_PORT_LBN 25 +#define FRF_BB_DEBUG2_PORT_WIDTH 15 +#define FRF_BB_DEBUG1_PORT_LBN 0 +#define FRF_BB_DEBUG1_PORT_WIDTH 25 + +/* EVQ_RPTR_REGP0: Event queue read pointer register */ +#define FR_BZ_EVQ_RPTR_P0 0x00000400 +#define FR_BZ_EVQ_RPTR_P0_STEP 8192 +#define FR_BZ_EVQ_RPTR_P0_ROWS 1024 +/* EVQ_RPTR_REG_KER: Event queue read pointer register */ +#define FR_AA_EVQ_RPTR_KER 0x00011b00 +#define FR_AA_EVQ_RPTR_KER_STEP 4 +#define FR_AA_EVQ_RPTR_KER_ROWS 4 +/* EVQ_RPTR_REG: Event queue read pointer register */ +#define FR_BZ_EVQ_RPTR 0x00fa0000 +#define FR_BZ_EVQ_RPTR_STEP 16 +#define FR_BB_EVQ_RPTR_ROWS 4096 +#define FR_CZ_EVQ_RPTR_ROWS 1024 +/* EVQ_RPTR_REGP123: Event queue read pointer register */ +#define FR_BB_EVQ_RPTR_P123 0x01000400 +#define FR_BB_EVQ_RPTR_P123_STEP 8192 +#define FR_BB_EVQ_RPTR_P123_ROWS 3072 +#define FRF_AZ_EVQ_RPTR_VLD_LBN 15 +#define FRF_AZ_EVQ_RPTR_VLD_WIDTH 1 +#define FRF_AZ_EVQ_RPTR_LBN 0 +#define FRF_AZ_EVQ_RPTR_WIDTH 15 + +/* TIMER_COMMAND_REGP0: Timer Command Registers */ +#define FR_BZ_TIMER_COMMAND_P0 0x00000420 +#define FR_BZ_TIMER_COMMAND_P0_STEP 8192 +#define FR_BZ_TIMER_COMMAND_P0_ROWS 1024 +/* TIMER_COMMAND_REG_KER: Timer Command Registers */ +#define FR_AA_TIMER_COMMAND_KER 0x00000420 +#define FR_AA_TIMER_COMMAND_KER_STEP 8192 +#define FR_AA_TIMER_COMMAND_KER_ROWS 4 +/* TIMER_COMMAND_REGP123: Timer Command Registers */ +#define FR_BB_TIMER_COMMAND_P123 0x01000420 +#define FR_BB_TIMER_COMMAND_P123_STEP 8192 +#define FR_BB_TIMER_COMMAND_P123_ROWS 3072 +#define FRF_CZ_TC_TIMER_MODE_LBN 14 +#define FRF_CZ_TC_TIMER_MODE_WIDTH 2 +#define FRF_AB_TC_TIMER_MODE_LBN 12 +#define FRF_AB_TC_TIMER_MODE_WIDTH 2 +#define FRF_CZ_TC_TIMER_VAL_LBN 0 +#define FRF_CZ_TC_TIMER_VAL_WIDTH 14 +#define FRF_AB_TC_TIMER_VAL_LBN 0 +#define FRF_AB_TC_TIMER_VAL_WIDTH 12 + +/* DRV_EV_REG: Driver generated event register */ +#define FR_AZ_DRV_EV 0x00000440 +#define FRF_AZ_DRV_EV_QID_LBN 64 +#define FRF_AZ_DRV_EV_QID_WIDTH 12 +#define FRF_AZ_DRV_EV_DATA_LBN 0 +#define FRF_AZ_DRV_EV_DATA_WIDTH 64 + +/* EVQ_CTL_REG: Event queue control register */ +#define FR_AZ_EVQ_CTL 0x00000450 +#define FRF_CZ_RX_EVQ_WAKEUP_MASK_LBN 15 +#define FRF_CZ_RX_EVQ_WAKEUP_MASK_WIDTH 10 +#define FRF_BB_RX_EVQ_WAKEUP_MASK_LBN 15 +#define FRF_BB_RX_EVQ_WAKEUP_MASK_WIDTH 6 +#define FRF_AZ_EVQ_OWNERR_CTL_LBN 14 +#define FRF_AZ_EVQ_OWNERR_CTL_WIDTH 1 +#define FRF_AZ_EVQ_FIFO_AF_TH_LBN 7 +#define FRF_AZ_EVQ_FIFO_AF_TH_WIDTH 7 +#define FRF_AZ_EVQ_FIFO_NOTAF_TH_LBN 0 +#define FRF_AZ_EVQ_FIFO_NOTAF_TH_WIDTH 7 + +/* EVQ_CNT1_REG: Event counter 1 register */ +#define FR_AZ_EVQ_CNT1 0x00000460 +#define FRF_AZ_EVQ_CNT_PRE_FIFO_LBN 120 +#define FRF_AZ_EVQ_CNT_PRE_FIFO_WIDTH 7 +#define FRF_AZ_EVQ_CNT_TOBIU_LBN 100 +#define FRF_AZ_EVQ_CNT_TOBIU_WIDTH 20 +#define FRF_AZ_EVQ_TX_REQ_CNT_LBN 80 +#define FRF_AZ_EVQ_TX_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_RX_REQ_CNT_LBN 60 +#define FRF_AZ_EVQ_RX_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_EM_REQ_CNT_LBN 40 +#define FRF_AZ_EVQ_EM_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_CSR_REQ_CNT_LBN 20 +#define FRF_AZ_EVQ_CSR_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_ERR_REQ_CNT_LBN 0 +#define FRF_AZ_EVQ_ERR_REQ_CNT_WIDTH 20 + +/* EVQ_CNT2_REG: Event counter 2 register */ +#define FR_AZ_EVQ_CNT2 0x00000470 +#define FRF_AZ_EVQ_UPD_REQ_CNT_LBN 104 +#define FRF_AZ_EVQ_UPD_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_CLR_REQ_CNT_LBN 84 +#define FRF_AZ_EVQ_CLR_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_RDY_CNT_LBN 80 +#define FRF_AZ_EVQ_RDY_CNT_WIDTH 4 +#define FRF_AZ_EVQ_WU_REQ_CNT_LBN 60 +#define FRF_AZ_EVQ_WU_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_WET_REQ_CNT_LBN 40 +#define FRF_AZ_EVQ_WET_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_INIT_REQ_CNT_LBN 20 +#define FRF_AZ_EVQ_INIT_REQ_CNT_WIDTH 20 +#define FRF_AZ_EVQ_TM_REQ_CNT_LBN 0 +#define FRF_AZ_EVQ_TM_REQ_CNT_WIDTH 20 + +/* USR_EV_REG: Event mailbox register */ +#define FR_CZ_USR_EV 0x00000540 +#define FR_CZ_USR_EV_STEP 8192 +#define FR_CZ_USR_EV_ROWS 1024 +#define FRF_CZ_USR_EV_DATA_LBN 0 +#define FRF_CZ_USR_EV_DATA_WIDTH 32 + +/* BUF_TBL_CFG_REG: Buffer table configuration register */ +#define FR_AZ_BUF_TBL_CFG 0x00000600 +#define FRF_AZ_BUF_TBL_MODE_LBN 3 +#define FRF_AZ_BUF_TBL_MODE_WIDTH 1 + +/* SRM_RX_DC_CFG_REG: SRAM receive descriptor cache configuration register */ +#define FR_AZ_SRM_RX_DC_CFG 0x00000610 +#define FRF_AZ_SRM_CLK_TMP_EN_LBN 21 +#define FRF_AZ_SRM_CLK_TMP_EN_WIDTH 1 +#define FRF_AZ_SRM_RX_DC_BASE_ADR_LBN 0 +#define FRF_AZ_SRM_RX_DC_BASE_ADR_WIDTH 21 + +/* SRM_TX_DC_CFG_REG: SRAM transmit descriptor cache configuration register */ +#define FR_AZ_SRM_TX_DC_CFG 0x00000620 +#define FRF_AZ_SRM_TX_DC_BASE_ADR_LBN 0 +#define FRF_AZ_SRM_TX_DC_BASE_ADR_WIDTH 21 + +/* SRM_CFG_REG: SRAM configuration register */ +#define FR_AZ_SRM_CFG 0x00000630 +#define FRF_AZ_SRM_OOB_ADR_INTEN_LBN 5 +#define FRF_AZ_SRM_OOB_ADR_INTEN_WIDTH 1 +#define FRF_AZ_SRM_OOB_BUF_INTEN_LBN 4 +#define FRF_AZ_SRM_OOB_BUF_INTEN_WIDTH 1 +#define FRF_AZ_SRM_INIT_EN_LBN 3 +#define FRF_AZ_SRM_INIT_EN_WIDTH 1 +#define FRF_AZ_SRM_NUM_BANK_LBN 2 +#define FRF_AZ_SRM_NUM_BANK_WIDTH 1 +#define FRF_AZ_SRM_BANK_SIZE_LBN 0 +#define FRF_AZ_SRM_BANK_SIZE_WIDTH 2 + +/* BUF_TBL_UPD_REG: Buffer table update register */ +#define FR_AZ_BUF_TBL_UPD 0x00000650 +#define FRF_AZ_BUF_UPD_CMD_LBN 63 +#define FRF_AZ_BUF_UPD_CMD_WIDTH 1 +#define FRF_AZ_BUF_CLR_CMD_LBN 62 +#define FRF_AZ_BUF_CLR_CMD_WIDTH 1 +#define FRF_AZ_BUF_CLR_END_ID_LBN 32 +#define FRF_AZ_BUF_CLR_END_ID_WIDTH 20 +#define FRF_AZ_BUF_CLR_START_ID_LBN 0 +#define FRF_AZ_BUF_CLR_START_ID_WIDTH 20 + +/* SRM_UPD_EVQ_REG: Buffer table update register */ +#define FR_AZ_SRM_UPD_EVQ 0x00000660 +#define FRF_AZ_SRM_UPD_EVQ_ID_LBN 0 +#define FRF_AZ_SRM_UPD_EVQ_ID_WIDTH 12 + +/* SRAM_PARITY_REG: SRAM parity register. */ +#define FR_AZ_SRAM_PARITY 0x00000670 +#define FRF_CZ_BYPASS_ECC_LBN 3 +#define FRF_CZ_BYPASS_ECC_WIDTH 1 +#define FRF_CZ_SEC_INT_LBN 2 +#define FRF_CZ_SEC_INT_WIDTH 1 +#define FRF_CZ_FORCE_SRAM_DOUBLE_ERR_LBN 1 +#define FRF_CZ_FORCE_SRAM_DOUBLE_ERR_WIDTH 1 +#define FRF_AB_FORCE_SRAM_PERR_LBN 0 +#define FRF_AB_FORCE_SRAM_PERR_WIDTH 1 +#define FRF_CZ_FORCE_SRAM_SINGLE_ERR_LBN 0 +#define FRF_CZ_FORCE_SRAM_SINGLE_ERR_WIDTH 1 + +/* RX_CFG_REG: Receive configuration register */ +#define FR_AZ_RX_CFG 0x00000800 +#define FRF_CZ_RX_MIN_KBUF_SIZE_LBN 72 +#define FRF_CZ_RX_MIN_KBUF_SIZE_WIDTH 14 +#define FRF_CZ_RX_HDR_SPLIT_EN_LBN 71 +#define FRF_CZ_RX_HDR_SPLIT_EN_WIDTH 1 +#define FRF_CZ_RX_HDR_SPLIT_PLD_BUF_SIZE_LBN 62 +#define FRF_CZ_RX_HDR_SPLIT_PLD_BUF_SIZE_WIDTH 9 +#define FRF_CZ_RX_HDR_SPLIT_HDR_BUF_SIZE_LBN 53 +#define FRF_CZ_RX_HDR_SPLIT_HDR_BUF_SIZE_WIDTH 9 +#define FRF_CZ_RX_PRE_RFF_IPG_LBN 49 +#define FRF_CZ_RX_PRE_RFF_IPG_WIDTH 4 +#define FRF_BZ_RX_TCP_SUP_LBN 48 +#define FRF_BZ_RX_TCP_SUP_WIDTH 1 +#define FRF_BZ_RX_INGR_EN_LBN 47 +#define FRF_BZ_RX_INGR_EN_WIDTH 1 +#define FRF_BZ_RX_IP_HASH_LBN 46 +#define FRF_BZ_RX_IP_HASH_WIDTH 1 +#define FRF_BZ_RX_HASH_ALG_LBN 45 +#define FRF_BZ_RX_HASH_ALG_WIDTH 1 +#define FRF_BZ_RX_HASH_INSRT_HDR_LBN 44 +#define FRF_BZ_RX_HASH_INSRT_HDR_WIDTH 1 +#define FRF_BZ_RX_DESC_PUSH_EN_LBN 43 +#define FRF_BZ_RX_DESC_PUSH_EN_WIDTH 1 +#define FRF_BZ_RX_RDW_PATCH_EN_LBN 42 +#define FRF_BZ_RX_RDW_PATCH_EN_WIDTH 1 +#define FRF_BB_RX_PCI_BURST_SIZE_LBN 39 +#define FRF_BB_RX_PCI_BURST_SIZE_WIDTH 3 +#define FRF_BZ_RX_OWNERR_CTL_LBN 38 +#define FRF_BZ_RX_OWNERR_CTL_WIDTH 1 +#define FRF_BZ_RX_XON_TX_TH_LBN 33 +#define FRF_BZ_RX_XON_TX_TH_WIDTH 5 +#define FRF_AA_RX_DESC_PUSH_EN_LBN 35 +#define FRF_AA_RX_DESC_PUSH_EN_WIDTH 1 +#define FRF_AA_RX_RDW_PATCH_EN_LBN 34 +#define FRF_AA_RX_RDW_PATCH_EN_WIDTH 1 +#define FRF_AA_RX_PCI_BURST_SIZE_LBN 31 +#define FRF_AA_RX_PCI_BURST_SIZE_WIDTH 3 +#define FRF_BZ_RX_XOFF_TX_TH_LBN 28 +#define FRF_BZ_RX_XOFF_TX_TH_WIDTH 5 +#define FRF_AA_RX_OWNERR_CTL_LBN 30 +#define FRF_AA_RX_OWNERR_CTL_WIDTH 1 +#define FRF_AA_RX_XON_TX_TH_LBN 25 +#define FRF_AA_RX_XON_TX_TH_WIDTH 5 +#define FRF_BZ_RX_USR_BUF_SIZE_LBN 19 +#define FRF_BZ_RX_USR_BUF_SIZE_WIDTH 9 +#define FRF_AA_RX_XOFF_TX_TH_LBN 20 +#define FRF_AA_RX_XOFF_TX_TH_WIDTH 5 +#define FRF_AA_RX_USR_BUF_SIZE_LBN 11 +#define FRF_AA_RX_USR_BUF_SIZE_WIDTH 9 +#define FRF_BZ_RX_XON_MAC_TH_LBN 10 +#define FRF_BZ_RX_XON_MAC_TH_WIDTH 9 +#define FRF_AA_RX_XON_MAC_TH_LBN 6 +#define FRF_AA_RX_XON_MAC_TH_WIDTH 5 +#define FRF_BZ_RX_XOFF_MAC_TH_LBN 1 +#define FRF_BZ_RX_XOFF_MAC_TH_WIDTH 9 +#define FRF_AA_RX_XOFF_MAC_TH_LBN 1 +#define FRF_AA_RX_XOFF_MAC_TH_WIDTH 5 +#define FRF_AZ_RX_XOFF_MAC_EN_LBN 0 +#define FRF_AZ_RX_XOFF_MAC_EN_WIDTH 1 + +/* RX_FILTER_CTL_REG: Receive filter control registers */ +#define FR_BZ_RX_FILTER_CTL 0x00000810 +#define FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT_LBN 94 +#define FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT_WIDTH 8 +#define FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT_LBN 86 +#define FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT_WIDTH 8 +#define FRF_CZ_RX_FILTER_ALL_VLAN_ETHERTYPES_LBN 85 +#define FRF_CZ_RX_FILTER_ALL_VLAN_ETHERTYPES_WIDTH 1 +#define FRF_CZ_RX_VLAN_MATCH_ETHERTYPE_LBN 69 +#define FRF_CZ_RX_VLAN_MATCH_ETHERTYPE_WIDTH 16 +#define FRF_CZ_MULTICAST_NOMATCH_Q_ID_LBN 57 +#define FRF_CZ_MULTICAST_NOMATCH_Q_ID_WIDTH 12 +#define FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED_LBN 56 +#define FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED_WIDTH 1 +#define FRF_CZ_MULTICAST_NOMATCH_IP_OVERRIDE_LBN 55 +#define FRF_CZ_MULTICAST_NOMATCH_IP_OVERRIDE_WIDTH 1 +#define FRF_CZ_UNICAST_NOMATCH_Q_ID_LBN 43 +#define FRF_CZ_UNICAST_NOMATCH_Q_ID_WIDTH 12 +#define FRF_CZ_UNICAST_NOMATCH_RSS_ENABLED_LBN 42 +#define FRF_CZ_UNICAST_NOMATCH_RSS_ENABLED_WIDTH 1 +#define FRF_CZ_UNICAST_NOMATCH_IP_OVERRIDE_LBN 41 +#define FRF_CZ_UNICAST_NOMATCH_IP_OVERRIDE_WIDTH 1 +#define FRF_BZ_SCATTER_ENBL_NO_MATCH_Q_LBN 40 +#define FRF_BZ_SCATTER_ENBL_NO_MATCH_Q_WIDTH 1 +#define FRF_BZ_UDP_FULL_SRCH_LIMIT_LBN 32 +#define FRF_BZ_UDP_FULL_SRCH_LIMIT_WIDTH 8 +#define FRF_BZ_NUM_KER_LBN 24 +#define FRF_BZ_NUM_KER_WIDTH 2 +#define FRF_BZ_UDP_WILD_SRCH_LIMIT_LBN 16 +#define FRF_BZ_UDP_WILD_SRCH_LIMIT_WIDTH 8 +#define FRF_BZ_TCP_WILD_SRCH_LIMIT_LBN 8 +#define FRF_BZ_TCP_WILD_SRCH_LIMIT_WIDTH 8 +#define FRF_BZ_TCP_FULL_SRCH_LIMIT_LBN 0 +#define FRF_BZ_TCP_FULL_SRCH_LIMIT_WIDTH 8 + +/* RX_FLUSH_DESCQ_REG: Receive flush descriptor queue register */ +#define FR_AZ_RX_FLUSH_DESCQ 0x00000820 +#define FRF_AZ_RX_FLUSH_DESCQ_CMD_LBN 24 +#define FRF_AZ_RX_FLUSH_DESCQ_CMD_WIDTH 1 +#define FRF_AZ_RX_FLUSH_DESCQ_LBN 0 +#define FRF_AZ_RX_FLUSH_DESCQ_WIDTH 12 + +/* RX_DESC_UPD_REGP0: Receive descriptor update register. */ +#define FR_BZ_RX_DESC_UPD_P0 0x00000830 +#define FR_BZ_RX_DESC_UPD_P0_STEP 8192 +#define FR_BZ_RX_DESC_UPD_P0_ROWS 1024 +/* RX_DESC_UPD_REG_KER: Receive descriptor update register. */ +#define FR_AA_RX_DESC_UPD_KER 0x00000830 +#define FR_AA_RX_DESC_UPD_KER_STEP 8192 +#define FR_AA_RX_DESC_UPD_KER_ROWS 4 +/* RX_DESC_UPD_REGP123: Receive descriptor update register. */ +#define FR_BB_RX_DESC_UPD_P123 0x01000830 +#define FR_BB_RX_DESC_UPD_P123_STEP 8192 +#define FR_BB_RX_DESC_UPD_P123_ROWS 3072 +#define FRF_AZ_RX_DESC_WPTR_LBN 96 +#define FRF_AZ_RX_DESC_WPTR_WIDTH 12 +#define FRF_AZ_RX_DESC_PUSH_CMD_LBN 95 +#define FRF_AZ_RX_DESC_PUSH_CMD_WIDTH 1 +#define FRF_AZ_RX_DESC_LBN 0 +#define FRF_AZ_RX_DESC_WIDTH 64 + +/* RX_DC_CFG_REG: Receive descriptor cache configuration register */ +#define FR_AZ_RX_DC_CFG 0x00000840 +#define FRF_AB_RX_MAX_PF_LBN 2 +#define FRF_AB_RX_MAX_PF_WIDTH 2 +#define FRF_AZ_RX_DC_SIZE_LBN 0 +#define FRF_AZ_RX_DC_SIZE_WIDTH 2 +#define FFE_AZ_RX_DC_SIZE_64 3 +#define FFE_AZ_RX_DC_SIZE_32 2 +#define FFE_AZ_RX_DC_SIZE_16 1 +#define FFE_AZ_RX_DC_SIZE_8 0 + +/* RX_DC_PF_WM_REG: Receive descriptor cache pre-fetch watermark register */ +#define FR_AZ_RX_DC_PF_WM 0x00000850 +#define FRF_AZ_RX_DC_PF_HWM_LBN 6 +#define FRF_AZ_RX_DC_PF_HWM_WIDTH 6 +#define FRF_AZ_RX_DC_PF_LWM_LBN 0 +#define FRF_AZ_RX_DC_PF_LWM_WIDTH 6 + +/* RX_RSS_TKEY_REG: RSS Toeplitz hash key */ +#define FR_BZ_RX_RSS_TKEY 0x00000860 +#define FRF_BZ_RX_RSS_TKEY_HI_LBN 64 +#define FRF_BZ_RX_RSS_TKEY_HI_WIDTH 64 +#define FRF_BZ_RX_RSS_TKEY_LO_LBN 0 +#define FRF_BZ_RX_RSS_TKEY_LO_WIDTH 64 + +/* RX_NODESC_DROP_REG: Receive dropped packet counter register */ +#define FR_AZ_RX_NODESC_DROP 0x00000880 +#define FRF_CZ_RX_NODESC_DROP_CNT_LBN 0 +#define FRF_CZ_RX_NODESC_DROP_CNT_WIDTH 32 +#define FRF_AB_RX_NODESC_DROP_CNT_LBN 0 +#define FRF_AB_RX_NODESC_DROP_CNT_WIDTH 16 + +/* RX_SELF_RST_REG: Receive self reset register */ +#define FR_AA_RX_SELF_RST 0x00000890 +#define FRF_AA_RX_ISCSI_DIS_LBN 17 +#define FRF_AA_RX_ISCSI_DIS_WIDTH 1 +#define FRF_AA_RX_SW_RST_REG_LBN 16 +#define FRF_AA_RX_SW_RST_REG_WIDTH 1 +#define FRF_AA_RX_NODESC_WAIT_DIS_LBN 9 +#define FRF_AA_RX_NODESC_WAIT_DIS_WIDTH 1 +#define FRF_AA_RX_SELF_RST_EN_LBN 8 +#define FRF_AA_RX_SELF_RST_EN_WIDTH 1 +#define FRF_AA_RX_MAX_PF_LAT_LBN 4 +#define FRF_AA_RX_MAX_PF_LAT_WIDTH 4 +#define FRF_AA_RX_MAX_LU_LAT_LBN 0 +#define FRF_AA_RX_MAX_LU_LAT_WIDTH 4 + +/* RX_DEBUG_REG: undocumented register */ +#define FR_AZ_RX_DEBUG 0x000008a0 +#define FRF_AZ_RX_DEBUG_LBN 0 +#define FRF_AZ_RX_DEBUG_WIDTH 64 + +/* RX_PUSH_DROP_REG: Receive descriptor push dropped counter register */ +#define FR_AZ_RX_PUSH_DROP 0x000008b0 +#define FRF_AZ_RX_PUSH_DROP_CNT_LBN 0 +#define FRF_AZ_RX_PUSH_DROP_CNT_WIDTH 32 + +/* RX_RSS_IPV6_REG1: IPv6 RSS Toeplitz hash key low bytes */ +#define FR_CZ_RX_RSS_IPV6_REG1 0x000008d0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_LO_LBN 0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_LO_WIDTH 128 + +/* RX_RSS_IPV6_REG2: IPv6 RSS Toeplitz hash key middle bytes */ +#define FR_CZ_RX_RSS_IPV6_REG2 0x000008e0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_MID_LBN 0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_MID_WIDTH 128 + +/* RX_RSS_IPV6_REG3: IPv6 RSS Toeplitz hash key upper bytes and IPv6 RSS settings */ +#define FR_CZ_RX_RSS_IPV6_REG3 0x000008f0 +#define FRF_CZ_RX_RSS_IPV6_THASH_ENABLE_LBN 66 +#define FRF_CZ_RX_RSS_IPV6_THASH_ENABLE_WIDTH 1 +#define FRF_CZ_RX_RSS_IPV6_IP_THASH_ENABLE_LBN 65 +#define FRF_CZ_RX_RSS_IPV6_IP_THASH_ENABLE_WIDTH 1 +#define FRF_CZ_RX_RSS_IPV6_TCP_SUPPRESS_LBN 64 +#define FRF_CZ_RX_RSS_IPV6_TCP_SUPPRESS_WIDTH 1 +#define FRF_CZ_RX_RSS_IPV6_TKEY_HI_LBN 0 +#define FRF_CZ_RX_RSS_IPV6_TKEY_HI_WIDTH 64 + +/* TX_FLUSH_DESCQ_REG: Transmit flush descriptor queue register */ +#define FR_AZ_TX_FLUSH_DESCQ 0x00000a00 +#define FRF_AZ_TX_FLUSH_DESCQ_CMD_LBN 12 +#define FRF_AZ_TX_FLUSH_DESCQ_CMD_WIDTH 1 +#define FRF_AZ_TX_FLUSH_DESCQ_LBN 0 +#define FRF_AZ_TX_FLUSH_DESCQ_WIDTH 12 + +/* TX_DESC_UPD_REGP0: Transmit descriptor update register. */ +#define FR_BZ_TX_DESC_UPD_P0 0x00000a10 +#define FR_BZ_TX_DESC_UPD_P0_STEP 8192 +#define FR_BZ_TX_DESC_UPD_P0_ROWS 1024 +/* TX_DESC_UPD_REG_KER: Transmit descriptor update register. */ +#define FR_AA_TX_DESC_UPD_KER 0x00000a10 +#define FR_AA_TX_DESC_UPD_KER_STEP 8192 +#define FR_AA_TX_DESC_UPD_KER_ROWS 8 +/* TX_DESC_UPD_REGP123: Transmit descriptor update register. */ +#define FR_BB_TX_DESC_UPD_P123 0x01000a10 +#define FR_BB_TX_DESC_UPD_P123_STEP 8192 +#define FR_BB_TX_DESC_UPD_P123_ROWS 3072 +#define FRF_AZ_TX_DESC_WPTR_LBN 96 +#define FRF_AZ_TX_DESC_WPTR_WIDTH 12 +#define FRF_AZ_TX_DESC_PUSH_CMD_LBN 95 +#define FRF_AZ_TX_DESC_PUSH_CMD_WIDTH 1 +#define FRF_AZ_TX_DESC_LBN 0 +#define FRF_AZ_TX_DESC_WIDTH 95 + +/* TX_DC_CFG_REG: Transmit descriptor cache configuration register */ +#define FR_AZ_TX_DC_CFG 0x00000a20 +#define FRF_AZ_TX_DC_SIZE_LBN 0 +#define FRF_AZ_TX_DC_SIZE_WIDTH 2 +#define FFE_AZ_TX_DC_SIZE_32 2 +#define FFE_AZ_TX_DC_SIZE_16 1 +#define FFE_AZ_TX_DC_SIZE_8 0 + +/* TX_CHKSM_CFG_REG: Transmit checksum configuration register */ +#define FR_AA_TX_CHKSM_CFG 0x00000a30 +#define FRF_AA_TX_Q_CHKSM_DIS_96_127_LBN 96 +#define FRF_AA_TX_Q_CHKSM_DIS_96_127_WIDTH 32 +#define FRF_AA_TX_Q_CHKSM_DIS_64_95_LBN 64 +#define FRF_AA_TX_Q_CHKSM_DIS_64_95_WIDTH 32 +#define FRF_AA_TX_Q_CHKSM_DIS_32_63_LBN 32 +#define FRF_AA_TX_Q_CHKSM_DIS_32_63_WIDTH 32 +#define FRF_AA_TX_Q_CHKSM_DIS_0_31_LBN 0 +#define FRF_AA_TX_Q_CHKSM_DIS_0_31_WIDTH 32 + +/* TX_CFG_REG: Transmit configuration register */ +#define FR_AZ_TX_CFG 0x00000a50 +#define FRF_CZ_TX_CONT_LOOKUP_THRESH_RANGE_LBN 114 +#define FRF_CZ_TX_CONT_LOOKUP_THRESH_RANGE_WIDTH 8 +#define FRF_CZ_TX_FILTER_TEST_MODE_BIT_LBN 113 +#define FRF_CZ_TX_FILTER_TEST_MODE_BIT_WIDTH 1 +#define FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE_LBN 105 +#define FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE_LBN 97 +#define FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE_LBN 89 +#define FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE_LBN 81 +#define FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE_LBN 73 +#define FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE_LBN 65 +#define FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE_WIDTH 8 +#define FRF_CZ_TX_FILTER_ALL_VLAN_ETHERTYPES_BIT_LBN 64 +#define FRF_CZ_TX_FILTER_ALL_VLAN_ETHERTYPES_BIT_WIDTH 1 +#define FRF_CZ_TX_VLAN_MATCH_ETHERTYPE_RANGE_LBN 48 +#define FRF_CZ_TX_VLAN_MATCH_ETHERTYPE_RANGE_WIDTH 16 +#define FRF_CZ_TX_FILTER_EN_BIT_LBN 47 +#define FRF_CZ_TX_FILTER_EN_BIT_WIDTH 1 +#define FRF_AZ_TX_IP_ID_P0_OFS_LBN 16 +#define FRF_AZ_TX_IP_ID_P0_OFS_WIDTH 15 +#define FRF_AZ_TX_NO_EOP_DISC_EN_LBN 5 +#define FRF_AZ_TX_NO_EOP_DISC_EN_WIDTH 1 +#define FRF_AZ_TX_P1_PRI_EN_LBN 4 +#define FRF_AZ_TX_P1_PRI_EN_WIDTH 1 +#define FRF_AZ_TX_OWNERR_CTL_LBN 2 +#define FRF_AZ_TX_OWNERR_CTL_WIDTH 1 +#define FRF_AA_TX_NON_IP_DROP_DIS_LBN 1 +#define FRF_AA_TX_NON_IP_DROP_DIS_WIDTH 1 +#define FRF_AZ_TX_IP_ID_REP_EN_LBN 0 +#define FRF_AZ_TX_IP_ID_REP_EN_WIDTH 1 + +/* TX_PUSH_DROP_REG: Transmit push dropped register */ +#define FR_AZ_TX_PUSH_DROP 0x00000a60 +#define FRF_AZ_TX_PUSH_DROP_CNT_LBN 0 +#define FRF_AZ_TX_PUSH_DROP_CNT_WIDTH 32 + +/* TX_RESERVED_REG: Transmit configuration register */ +#define FR_AZ_TX_RESERVED 0x00000a80 +#define FRF_AZ_TX_EVT_CNT_LBN 121 +#define FRF_AZ_TX_EVT_CNT_WIDTH 7 +#define FRF_AZ_TX_PREF_AGE_CNT_LBN 119 +#define FRF_AZ_TX_PREF_AGE_CNT_WIDTH 2 +#define FRF_AZ_TX_RD_COMP_TMR_LBN 96 +#define FRF_AZ_TX_RD_COMP_TMR_WIDTH 23 +#define FRF_AZ_TX_PUSH_EN_LBN 89 +#define FRF_AZ_TX_PUSH_EN_WIDTH 1 +#define FRF_AZ_TX_PUSH_CHK_DIS_LBN 88 +#define FRF_AZ_TX_PUSH_CHK_DIS_WIDTH 1 +#define FRF_AZ_TX_D_FF_FULL_P0_LBN 85 +#define FRF_AZ_TX_D_FF_FULL_P0_WIDTH 1 +#define FRF_AZ_TX_DMAR_ST_P0_LBN 81 +#define FRF_AZ_TX_DMAR_ST_P0_WIDTH 1 +#define FRF_AZ_TX_DMAQ_ST_LBN 78 +#define FRF_AZ_TX_DMAQ_ST_WIDTH 1 +#define FRF_AZ_TX_RX_SPACER_LBN 64 +#define FRF_AZ_TX_RX_SPACER_WIDTH 8 +#define FRF_AZ_TX_DROP_ABORT_EN_LBN 60 +#define FRF_AZ_TX_DROP_ABORT_EN_WIDTH 1 +#define FRF_AZ_TX_SOFT_EVT_EN_LBN 59 +#define FRF_AZ_TX_SOFT_EVT_EN_WIDTH 1 +#define FRF_AZ_TX_PS_EVT_DIS_LBN 58 +#define FRF_AZ_TX_PS_EVT_DIS_WIDTH 1 +#define FRF_AZ_TX_RX_SPACER_EN_LBN 57 +#define FRF_AZ_TX_RX_SPACER_EN_WIDTH 1 +#define FRF_AZ_TX_XP_TIMER_LBN 52 +#define FRF_AZ_TX_XP_TIMER_WIDTH 5 +#define FRF_AZ_TX_PREF_SPACER_LBN 44 +#define FRF_AZ_TX_PREF_SPACER_WIDTH 8 +#define FRF_AZ_TX_PREF_WD_TMR_LBN 22 +#define FRF_AZ_TX_PREF_WD_TMR_WIDTH 22 +#define FRF_AZ_TX_ONLY1TAG_LBN 21 +#define FRF_AZ_TX_ONLY1TAG_WIDTH 1 +#define FRF_AZ_TX_PREF_THRESHOLD_LBN 19 +#define FRF_AZ_TX_PREF_THRESHOLD_WIDTH 2 +#define FRF_AZ_TX_ONE_PKT_PER_Q_LBN 18 +#define FRF_AZ_TX_ONE_PKT_PER_Q_WIDTH 1 +#define FRF_AZ_TX_DIS_NON_IP_EV_LBN 17 +#define FRF_AZ_TX_DIS_NON_IP_EV_WIDTH 1 +#define FRF_AA_TX_DMA_FF_THR_LBN 16 +#define FRF_AA_TX_DMA_FF_THR_WIDTH 1 +#define FRF_AZ_TX_DMA_SPACER_LBN 8 +#define FRF_AZ_TX_DMA_SPACER_WIDTH 8 +#define FRF_AA_TX_TCP_DIS_LBN 7 +#define FRF_AA_TX_TCP_DIS_WIDTH 1 +#define FRF_BZ_TX_FLUSH_MIN_LEN_EN_LBN 7 +#define FRF_BZ_TX_FLUSH_MIN_LEN_EN_WIDTH 1 +#define FRF_AA_TX_IP_DIS_LBN 6 +#define FRF_AA_TX_IP_DIS_WIDTH 1 +#define FRF_AZ_TX_MAX_CPL_LBN 2 +#define FRF_AZ_TX_MAX_CPL_WIDTH 2 +#define FFE_AZ_TX_MAX_CPL_16 3 +#define FFE_AZ_TX_MAX_CPL_8 2 +#define FFE_AZ_TX_MAX_CPL_4 1 +#define FFE_AZ_TX_MAX_CPL_NOLIMIT 0 +#define FRF_AZ_TX_MAX_PREF_LBN 0 +#define FRF_AZ_TX_MAX_PREF_WIDTH 2 +#define FFE_AZ_TX_MAX_PREF_32 3 +#define FFE_AZ_TX_MAX_PREF_16 2 +#define FFE_AZ_TX_MAX_PREF_8 1 +#define FFE_AZ_TX_MAX_PREF_OFF 0 + +/* TX_PACE_REG: Transmit pace control register */ +#define FR_BZ_TX_PACE 0x00000a90 +#define FRF_BZ_TX_PACE_SB_NOT_AF_LBN 19 +#define FRF_BZ_TX_PACE_SB_NOT_AF_WIDTH 10 +#define FRF_BZ_TX_PACE_SB_AF_LBN 9 +#define FRF_BZ_TX_PACE_SB_AF_WIDTH 10 +#define FRF_BZ_TX_PACE_FB_BASE_LBN 5 +#define FRF_BZ_TX_PACE_FB_BASE_WIDTH 4 +#define FRF_BZ_TX_PACE_BIN_TH_LBN 0 +#define FRF_BZ_TX_PACE_BIN_TH_WIDTH 5 + +/* TX_PACE_DROP_QID_REG: PACE Drop QID Counter */ +#define FR_BZ_TX_PACE_DROP_QID 0x00000aa0 +#define FRF_BZ_TX_PACE_QID_DRP_CNT_LBN 0 +#define FRF_BZ_TX_PACE_QID_DRP_CNT_WIDTH 16 + +/* TX_VLAN_REG: Transmit VLAN tag register */ +#define FR_BB_TX_VLAN 0x00000ae0 +#define FRF_BB_TX_VLAN_EN_LBN 127 +#define FRF_BB_TX_VLAN_EN_WIDTH 1 +#define FRF_BB_TX_VLAN7_PORT1_EN_LBN 125 +#define FRF_BB_TX_VLAN7_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN7_PORT0_EN_LBN 124 +#define FRF_BB_TX_VLAN7_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN7_LBN 112 +#define FRF_BB_TX_VLAN7_WIDTH 12 +#define FRF_BB_TX_VLAN6_PORT1_EN_LBN 109 +#define FRF_BB_TX_VLAN6_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN6_PORT0_EN_LBN 108 +#define FRF_BB_TX_VLAN6_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN6_LBN 96 +#define FRF_BB_TX_VLAN6_WIDTH 12 +#define FRF_BB_TX_VLAN5_PORT1_EN_LBN 93 +#define FRF_BB_TX_VLAN5_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN5_PORT0_EN_LBN 92 +#define FRF_BB_TX_VLAN5_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN5_LBN 80 +#define FRF_BB_TX_VLAN5_WIDTH 12 +#define FRF_BB_TX_VLAN4_PORT1_EN_LBN 77 +#define FRF_BB_TX_VLAN4_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN4_PORT0_EN_LBN 76 +#define FRF_BB_TX_VLAN4_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN4_LBN 64 +#define FRF_BB_TX_VLAN4_WIDTH 12 +#define FRF_BB_TX_VLAN3_PORT1_EN_LBN 61 +#define FRF_BB_TX_VLAN3_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN3_PORT0_EN_LBN 60 +#define FRF_BB_TX_VLAN3_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN3_LBN 48 +#define FRF_BB_TX_VLAN3_WIDTH 12 +#define FRF_BB_TX_VLAN2_PORT1_EN_LBN 45 +#define FRF_BB_TX_VLAN2_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN2_PORT0_EN_LBN 44 +#define FRF_BB_TX_VLAN2_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN2_LBN 32 +#define FRF_BB_TX_VLAN2_WIDTH 12 +#define FRF_BB_TX_VLAN1_PORT1_EN_LBN 29 +#define FRF_BB_TX_VLAN1_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN1_PORT0_EN_LBN 28 +#define FRF_BB_TX_VLAN1_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN1_LBN 16 +#define FRF_BB_TX_VLAN1_WIDTH 12 +#define FRF_BB_TX_VLAN0_PORT1_EN_LBN 13 +#define FRF_BB_TX_VLAN0_PORT1_EN_WIDTH 1 +#define FRF_BB_TX_VLAN0_PORT0_EN_LBN 12 +#define FRF_BB_TX_VLAN0_PORT0_EN_WIDTH 1 +#define FRF_BB_TX_VLAN0_LBN 0 +#define FRF_BB_TX_VLAN0_WIDTH 12 + +/* TX_IPFIL_PORTEN_REG: Transmit filter control register */ +#define FR_BZ_TX_IPFIL_PORTEN 0x00000af0 +#define FRF_BZ_TX_MADR0_FIL_EN_LBN 64 +#define FRF_BZ_TX_MADR0_FIL_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL31_PORT_EN_LBN 62 +#define FRF_BB_TX_IPFIL31_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL30_PORT_EN_LBN 60 +#define FRF_BB_TX_IPFIL30_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL29_PORT_EN_LBN 58 +#define FRF_BB_TX_IPFIL29_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL28_PORT_EN_LBN 56 +#define FRF_BB_TX_IPFIL28_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL27_PORT_EN_LBN 54 +#define FRF_BB_TX_IPFIL27_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL26_PORT_EN_LBN 52 +#define FRF_BB_TX_IPFIL26_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL25_PORT_EN_LBN 50 +#define FRF_BB_TX_IPFIL25_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL24_PORT_EN_LBN 48 +#define FRF_BB_TX_IPFIL24_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL23_PORT_EN_LBN 46 +#define FRF_BB_TX_IPFIL23_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL22_PORT_EN_LBN 44 +#define FRF_BB_TX_IPFIL22_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL21_PORT_EN_LBN 42 +#define FRF_BB_TX_IPFIL21_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL20_PORT_EN_LBN 40 +#define FRF_BB_TX_IPFIL20_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL19_PORT_EN_LBN 38 +#define FRF_BB_TX_IPFIL19_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL18_PORT_EN_LBN 36 +#define FRF_BB_TX_IPFIL18_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL17_PORT_EN_LBN 34 +#define FRF_BB_TX_IPFIL17_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL16_PORT_EN_LBN 32 +#define FRF_BB_TX_IPFIL16_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL15_PORT_EN_LBN 30 +#define FRF_BB_TX_IPFIL15_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL14_PORT_EN_LBN 28 +#define FRF_BB_TX_IPFIL14_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL13_PORT_EN_LBN 26 +#define FRF_BB_TX_IPFIL13_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL12_PORT_EN_LBN 24 +#define FRF_BB_TX_IPFIL12_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL11_PORT_EN_LBN 22 +#define FRF_BB_TX_IPFIL11_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL10_PORT_EN_LBN 20 +#define FRF_BB_TX_IPFIL10_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL9_PORT_EN_LBN 18 +#define FRF_BB_TX_IPFIL9_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL8_PORT_EN_LBN 16 +#define FRF_BB_TX_IPFIL8_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL7_PORT_EN_LBN 14 +#define FRF_BB_TX_IPFIL7_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL6_PORT_EN_LBN 12 +#define FRF_BB_TX_IPFIL6_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL5_PORT_EN_LBN 10 +#define FRF_BB_TX_IPFIL5_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL4_PORT_EN_LBN 8 +#define FRF_BB_TX_IPFIL4_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL3_PORT_EN_LBN 6 +#define FRF_BB_TX_IPFIL3_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL2_PORT_EN_LBN 4 +#define FRF_BB_TX_IPFIL2_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL1_PORT_EN_LBN 2 +#define FRF_BB_TX_IPFIL1_PORT_EN_WIDTH 1 +#define FRF_BB_TX_IPFIL0_PORT_EN_LBN 0 +#define FRF_BB_TX_IPFIL0_PORT_EN_WIDTH 1 + +/* TX_IPFIL_TBL: Transmit IP source address filter table */ +#define FR_BB_TX_IPFIL_TBL 0x00000b00 +#define FR_BB_TX_IPFIL_TBL_STEP 16 +#define FR_BB_TX_IPFIL_TBL_ROWS 16 +#define FRF_BB_TX_IPFIL_MASK_1_LBN 96 +#define FRF_BB_TX_IPFIL_MASK_1_WIDTH 32 +#define FRF_BB_TX_IP_SRC_ADR_1_LBN 64 +#define FRF_BB_TX_IP_SRC_ADR_1_WIDTH 32 +#define FRF_BB_TX_IPFIL_MASK_0_LBN 32 +#define FRF_BB_TX_IPFIL_MASK_0_WIDTH 32 +#define FRF_BB_TX_IP_SRC_ADR_0_LBN 0 +#define FRF_BB_TX_IP_SRC_ADR_0_WIDTH 32 + +/* MD_TXD_REG: PHY management transmit data register */ +#define FR_AB_MD_TXD 0x00000c00 +#define FRF_AB_MD_TXD_LBN 0 +#define FRF_AB_MD_TXD_WIDTH 16 + +/* MD_RXD_REG: PHY management receive data register */ +#define FR_AB_MD_RXD 0x00000c10 +#define FRF_AB_MD_RXD_LBN 0 +#define FRF_AB_MD_RXD_WIDTH 16 + +/* MD_CS_REG: PHY management configuration & status register */ +#define FR_AB_MD_CS 0x00000c20 +#define FRF_AB_MD_RD_EN_CMD_LBN 15 +#define FRF_AB_MD_RD_EN_CMD_WIDTH 1 +#define FRF_AB_MD_WR_EN_CMD_LBN 14 +#define FRF_AB_MD_WR_EN_CMD_WIDTH 1 +#define FRF_AB_MD_ADDR_CMD_LBN 13 +#define FRF_AB_MD_ADDR_CMD_WIDTH 1 +#define FRF_AB_MD_PT_LBN 7 +#define FRF_AB_MD_PT_WIDTH 3 +#define FRF_AB_MD_PL_LBN 6 +#define FRF_AB_MD_PL_WIDTH 1 +#define FRF_AB_MD_INT_CLR_LBN 5 +#define FRF_AB_MD_INT_CLR_WIDTH 1 +#define FRF_AB_MD_GC_LBN 4 +#define FRF_AB_MD_GC_WIDTH 1 +#define FRF_AB_MD_PRSP_LBN 3 +#define FRF_AB_MD_PRSP_WIDTH 1 +#define FRF_AB_MD_RIC_LBN 2 +#define FRF_AB_MD_RIC_WIDTH 1 +#define FRF_AB_MD_RDC_LBN 1 +#define FRF_AB_MD_RDC_WIDTH 1 +#define FRF_AB_MD_WRC_LBN 0 +#define FRF_AB_MD_WRC_WIDTH 1 + +/* MD_PHY_ADR_REG: PHY management PHY address register */ +#define FR_AB_MD_PHY_ADR 0x00000c30 +#define FRF_AB_MD_PHY_ADR_LBN 0 +#define FRF_AB_MD_PHY_ADR_WIDTH 16 + +/* MD_ID_REG: PHY management ID register */ +#define FR_AB_MD_ID 0x00000c40 +#define FRF_AB_MD_PRT_ADR_LBN 11 +#define FRF_AB_MD_PRT_ADR_WIDTH 5 +#define FRF_AB_MD_DEV_ADR_LBN 6 +#define FRF_AB_MD_DEV_ADR_WIDTH 5 + +/* MD_STAT_REG: PHY management status & mask register */ +#define FR_AB_MD_STAT 0x00000c50 +#define FRF_AB_MD_PINT_LBN 4 +#define FRF_AB_MD_PINT_WIDTH 1 +#define FRF_AB_MD_DONE_LBN 3 +#define FRF_AB_MD_DONE_WIDTH 1 +#define FRF_AB_MD_BSERR_LBN 2 +#define FRF_AB_MD_BSERR_WIDTH 1 +#define FRF_AB_MD_LNFL_LBN 1 +#define FRF_AB_MD_LNFL_WIDTH 1 +#define FRF_AB_MD_BSY_LBN 0 +#define FRF_AB_MD_BSY_WIDTH 1 + +/* MAC_STAT_DMA_REG: Port MAC statistical counter DMA register */ +#define FR_AB_MAC_STAT_DMA 0x00000c60 +#define FRF_AB_MAC_STAT_DMA_CMD_LBN 48 +#define FRF_AB_MAC_STAT_DMA_CMD_WIDTH 1 +#define FRF_AB_MAC_STAT_DMA_ADR_LBN 0 +#define FRF_AB_MAC_STAT_DMA_ADR_WIDTH 48 + +/* MAC_CTRL_REG: Port MAC control register */ +#define FR_AB_MAC_CTRL 0x00000c80 +#define FRF_AB_MAC_XOFF_VAL_LBN 16 +#define FRF_AB_MAC_XOFF_VAL_WIDTH 16 +#define FRF_BB_TXFIFO_DRAIN_EN_LBN 7 +#define FRF_BB_TXFIFO_DRAIN_EN_WIDTH 1 +#define FRF_AB_MAC_XG_DISTXCRC_LBN 5 +#define FRF_AB_MAC_XG_DISTXCRC_WIDTH 1 +#define FRF_AB_MAC_BCAD_ACPT_LBN 4 +#define FRF_AB_MAC_BCAD_ACPT_WIDTH 1 +#define FRF_AB_MAC_UC_PROM_LBN 3 +#define FRF_AB_MAC_UC_PROM_WIDTH 1 +#define FRF_AB_MAC_LINK_STATUS_LBN 2 +#define FRF_AB_MAC_LINK_STATUS_WIDTH 1 +#define FRF_AB_MAC_SPEED_LBN 0 +#define FRF_AB_MAC_SPEED_WIDTH 2 +#define FFE_AB_MAC_SPEED_10G 3 +#define FFE_AB_MAC_SPEED_1G 2 +#define FFE_AB_MAC_SPEED_100M 1 +#define FFE_AB_MAC_SPEED_10M 0 + +/* GEN_MODE_REG: General Purpose mode register (external interrupt mask) */ +#define FR_BB_GEN_MODE 0x00000c90 +#define FRF_BB_XFP_PHY_INT_POL_SEL_LBN 3 +#define FRF_BB_XFP_PHY_INT_POL_SEL_WIDTH 1 +#define FRF_BB_XG_PHY_INT_POL_SEL_LBN 2 +#define FRF_BB_XG_PHY_INT_POL_SEL_WIDTH 1 +#define FRF_BB_XFP_PHY_INT_MASK_LBN 1 +#define FRF_BB_XFP_PHY_INT_MASK_WIDTH 1 +#define FRF_BB_XG_PHY_INT_MASK_LBN 0 +#define FRF_BB_XG_PHY_INT_MASK_WIDTH 1 + +/* MAC_MC_HASH_REG0: Multicast address hash table */ +#define FR_AB_MAC_MC_HASH_REG0 0x00000ca0 +#define FRF_AB_MAC_MCAST_HASH0_LBN 0 +#define FRF_AB_MAC_MCAST_HASH0_WIDTH 128 + +/* MAC_MC_HASH_REG1: Multicast address hash table */ +#define FR_AB_MAC_MC_HASH_REG1 0x00000cb0 +#define FRF_AB_MAC_MCAST_HASH1_LBN 0 +#define FRF_AB_MAC_MCAST_HASH1_WIDTH 128 + +/* GM_CFG1_REG: GMAC configuration register 1 */ +#define FR_AB_GM_CFG1 0x00000e00 +#define FRF_AB_GM_SW_RST_LBN 31 +#define FRF_AB_GM_SW_RST_WIDTH 1 +#define FRF_AB_GM_SIM_RST_LBN 30 +#define FRF_AB_GM_SIM_RST_WIDTH 1 +#define FRF_AB_GM_RST_RX_MAC_CTL_LBN 19 +#define FRF_AB_GM_RST_RX_MAC_CTL_WIDTH 1 +#define FRF_AB_GM_RST_TX_MAC_CTL_LBN 18 +#define FRF_AB_GM_RST_TX_MAC_CTL_WIDTH 1 +#define FRF_AB_GM_RST_RX_FUNC_LBN 17 +#define FRF_AB_GM_RST_RX_FUNC_WIDTH 1 +#define FRF_AB_GM_RST_TX_FUNC_LBN 16 +#define FRF_AB_GM_RST_TX_FUNC_WIDTH 1 +#define FRF_AB_GM_LOOP_LBN 8 +#define FRF_AB_GM_LOOP_WIDTH 1 +#define FRF_AB_GM_RX_FC_EN_LBN 5 +#define FRF_AB_GM_RX_FC_EN_WIDTH 1 +#define FRF_AB_GM_TX_FC_EN_LBN 4 +#define FRF_AB_GM_TX_FC_EN_WIDTH 1 +#define FRF_AB_GM_SYNC_RXEN_LBN 3 +#define FRF_AB_GM_SYNC_RXEN_WIDTH 1 +#define FRF_AB_GM_RX_EN_LBN 2 +#define FRF_AB_GM_RX_EN_WIDTH 1 +#define FRF_AB_GM_SYNC_TXEN_LBN 1 +#define FRF_AB_GM_SYNC_TXEN_WIDTH 1 +#define FRF_AB_GM_TX_EN_LBN 0 +#define FRF_AB_GM_TX_EN_WIDTH 1 + +/* GM_CFG2_REG: GMAC configuration register 2 */ +#define FR_AB_GM_CFG2 0x00000e10 +#define FRF_AB_GM_PAMBL_LEN_LBN 12 +#define FRF_AB_GM_PAMBL_LEN_WIDTH 4 +#define FRF_AB_GM_IF_MODE_LBN 8 +#define FRF_AB_GM_IF_MODE_WIDTH 2 +#define FFE_AB_IF_MODE_BYTE_MODE 2 +#define FFE_AB_IF_MODE_NIBBLE_MODE 1 +#define FRF_AB_GM_HUGE_FRM_EN_LBN 5 +#define FRF_AB_GM_HUGE_FRM_EN_WIDTH 1 +#define FRF_AB_GM_LEN_CHK_LBN 4 +#define FRF_AB_GM_LEN_CHK_WIDTH 1 +#define FRF_AB_GM_PAD_CRC_EN_LBN 2 +#define FRF_AB_GM_PAD_CRC_EN_WIDTH 1 +#define FRF_AB_GM_CRC_EN_LBN 1 +#define FRF_AB_GM_CRC_EN_WIDTH 1 +#define FRF_AB_GM_FD_LBN 0 +#define FRF_AB_GM_FD_WIDTH 1 + +/* GM_IPG_REG: GMAC IPG register */ +#define FR_AB_GM_IPG 0x00000e20 +#define FRF_AB_GM_NONB2B_IPG1_LBN 24 +#define FRF_AB_GM_NONB2B_IPG1_WIDTH 7 +#define FRF_AB_GM_NONB2B_IPG2_LBN 16 +#define FRF_AB_GM_NONB2B_IPG2_WIDTH 7 +#define FRF_AB_GM_MIN_IPG_ENF_LBN 8 +#define FRF_AB_GM_MIN_IPG_ENF_WIDTH 8 +#define FRF_AB_GM_B2B_IPG_LBN 0 +#define FRF_AB_GM_B2B_IPG_WIDTH 7 + +/* GM_HD_REG: GMAC half duplex register */ +#define FR_AB_GM_HD 0x00000e30 +#define FRF_AB_GM_ALT_BOFF_VAL_LBN 20 +#define FRF_AB_GM_ALT_BOFF_VAL_WIDTH 4 +#define FRF_AB_GM_ALT_BOFF_EN_LBN 19 +#define FRF_AB_GM_ALT_BOFF_EN_WIDTH 1 +#define FRF_AB_GM_BP_NO_BOFF_LBN 18 +#define FRF_AB_GM_BP_NO_BOFF_WIDTH 1 +#define FRF_AB_GM_DIS_BOFF_LBN 17 +#define FRF_AB_GM_DIS_BOFF_WIDTH 1 +#define FRF_AB_GM_EXDEF_TX_EN_LBN 16 +#define FRF_AB_GM_EXDEF_TX_EN_WIDTH 1 +#define FRF_AB_GM_RTRY_LIMIT_LBN 12 +#define FRF_AB_GM_RTRY_LIMIT_WIDTH 4 +#define FRF_AB_GM_COL_WIN_LBN 0 +#define FRF_AB_GM_COL_WIN_WIDTH 10 + +/* GM_MAX_FLEN_REG: GMAC maximum frame length register */ +#define FR_AB_GM_MAX_FLEN 0x00000e40 +#define FRF_AB_GM_MAX_FLEN_LBN 0 +#define FRF_AB_GM_MAX_FLEN_WIDTH 16 + +/* GM_TEST_REG: GMAC test register */ +#define FR_AB_GM_TEST 0x00000e70 +#define FRF_AB_GM_MAX_BOFF_LBN 3 +#define FRF_AB_GM_MAX_BOFF_WIDTH 1 +#define FRF_AB_GM_REG_TX_FLOW_EN_LBN 2 +#define FRF_AB_GM_REG_TX_FLOW_EN_WIDTH 1 +#define FRF_AB_GM_TEST_PAUSE_LBN 1 +#define FRF_AB_GM_TEST_PAUSE_WIDTH 1 +#define FRF_AB_GM_SHORT_SLOT_LBN 0 +#define FRF_AB_GM_SHORT_SLOT_WIDTH 1 + +/* GM_ADR1_REG: GMAC station address register 1 */ +#define FR_AB_GM_ADR1 0x00000f00 +#define FRF_AB_GM_ADR_B0_LBN 24 +#define FRF_AB_GM_ADR_B0_WIDTH 8 +#define FRF_AB_GM_ADR_B1_LBN 16 +#define FRF_AB_GM_ADR_B1_WIDTH 8 +#define FRF_AB_GM_ADR_B2_LBN 8 +#define FRF_AB_GM_ADR_B2_WIDTH 8 +#define FRF_AB_GM_ADR_B3_LBN 0 +#define FRF_AB_GM_ADR_B3_WIDTH 8 + +/* GM_ADR2_REG: GMAC station address register 2 */ +#define FR_AB_GM_ADR2 0x00000f10 +#define FRF_AB_GM_ADR_B4_LBN 24 +#define FRF_AB_GM_ADR_B4_WIDTH 8 +#define FRF_AB_GM_ADR_B5_LBN 16 +#define FRF_AB_GM_ADR_B5_WIDTH 8 + +/* GMF_CFG0_REG: GMAC FIFO configuration register 0 */ +#define FR_AB_GMF_CFG0 0x00000f20 +#define FRF_AB_GMF_FTFENRPLY_LBN 20 +#define FRF_AB_GMF_FTFENRPLY_WIDTH 1 +#define FRF_AB_GMF_STFENRPLY_LBN 19 +#define FRF_AB_GMF_STFENRPLY_WIDTH 1 +#define FRF_AB_GMF_FRFENRPLY_LBN 18 +#define FRF_AB_GMF_FRFENRPLY_WIDTH 1 +#define FRF_AB_GMF_SRFENRPLY_LBN 17 +#define FRF_AB_GMF_SRFENRPLY_WIDTH 1 +#define FRF_AB_GMF_WTMENRPLY_LBN 16 +#define FRF_AB_GMF_WTMENRPLY_WIDTH 1 +#define FRF_AB_GMF_FTFENREQ_LBN 12 +#define FRF_AB_GMF_FTFENREQ_WIDTH 1 +#define FRF_AB_GMF_STFENREQ_LBN 11 +#define FRF_AB_GMF_STFENREQ_WIDTH 1 +#define FRF_AB_GMF_FRFENREQ_LBN 10 +#define FRF_AB_GMF_FRFENREQ_WIDTH 1 +#define FRF_AB_GMF_SRFENREQ_LBN 9 +#define FRF_AB_GMF_SRFENREQ_WIDTH 1 +#define FRF_AB_GMF_WTMENREQ_LBN 8 +#define FRF_AB_GMF_WTMENREQ_WIDTH 1 +#define FRF_AB_GMF_HSTRSTFT_LBN 4 +#define FRF_AB_GMF_HSTRSTFT_WIDTH 1 +#define FRF_AB_GMF_HSTRSTST_LBN 3 +#define FRF_AB_GMF_HSTRSTST_WIDTH 1 +#define FRF_AB_GMF_HSTRSTFR_LBN 2 +#define FRF_AB_GMF_HSTRSTFR_WIDTH 1 +#define FRF_AB_GMF_HSTRSTSR_LBN 1 +#define FRF_AB_GMF_HSTRSTSR_WIDTH 1 +#define FRF_AB_GMF_HSTRSTWT_LBN 0 +#define FRF_AB_GMF_HSTRSTWT_WIDTH 1 + +/* GMF_CFG1_REG: GMAC FIFO configuration register 1 */ +#define FR_AB_GMF_CFG1 0x00000f30 +#define FRF_AB_GMF_CFGFRTH_LBN 16 +#define FRF_AB_GMF_CFGFRTH_WIDTH 5 +#define FRF_AB_GMF_CFGXOFFRTX_LBN 0 +#define FRF_AB_GMF_CFGXOFFRTX_WIDTH 16 + +/* GMF_CFG2_REG: GMAC FIFO configuration register 2 */ +#define FR_AB_GMF_CFG2 0x00000f40 +#define FRF_AB_GMF_CFGHWM_LBN 16 +#define FRF_AB_GMF_CFGHWM_WIDTH 6 +#define FRF_AB_GMF_CFGLWM_LBN 0 +#define FRF_AB_GMF_CFGLWM_WIDTH 6 + +/* GMF_CFG3_REG: GMAC FIFO configuration register 3 */ +#define FR_AB_GMF_CFG3 0x00000f50 +#define FRF_AB_GMF_CFGHWMFT_LBN 16 +#define FRF_AB_GMF_CFGHWMFT_WIDTH 6 +#define FRF_AB_GMF_CFGFTTH_LBN 0 +#define FRF_AB_GMF_CFGFTTH_WIDTH 6 + +/* GMF_CFG4_REG: GMAC FIFO configuration register 4 */ +#define FR_AB_GMF_CFG4 0x00000f60 +#define FRF_AB_GMF_HSTFLTRFRM_LBN 0 +#define FRF_AB_GMF_HSTFLTRFRM_WIDTH 18 + +/* GMF_CFG5_REG: GMAC FIFO configuration register 5 */ +#define FR_AB_GMF_CFG5 0x00000f70 +#define FRF_AB_GMF_CFGHDPLX_LBN 22 +#define FRF_AB_GMF_CFGHDPLX_WIDTH 1 +#define FRF_AB_GMF_SRFULL_LBN 21 +#define FRF_AB_GMF_SRFULL_WIDTH 1 +#define FRF_AB_GMF_HSTSRFULLCLR_LBN 20 +#define FRF_AB_GMF_HSTSRFULLCLR_WIDTH 1 +#define FRF_AB_GMF_CFGBYTMODE_LBN 19 +#define FRF_AB_GMF_CFGBYTMODE_WIDTH 1 +#define FRF_AB_GMF_HSTDRPLT64_LBN 18 +#define FRF_AB_GMF_HSTDRPLT64_WIDTH 1 +#define FRF_AB_GMF_HSTFLTRFRMDC_LBN 0 +#define FRF_AB_GMF_HSTFLTRFRMDC_WIDTH 18 + +/* TX_SRC_MAC_TBL: Transmit IP source address filter table */ +#define FR_BB_TX_SRC_MAC_TBL 0x00001000 +#define FR_BB_TX_SRC_MAC_TBL_STEP 16 +#define FR_BB_TX_SRC_MAC_TBL_ROWS 16 +#define FRF_BB_TX_SRC_MAC_ADR_1_LBN 64 +#define FRF_BB_TX_SRC_MAC_ADR_1_WIDTH 48 +#define FRF_BB_TX_SRC_MAC_ADR_0_LBN 0 +#define FRF_BB_TX_SRC_MAC_ADR_0_WIDTH 48 + +/* TX_SRC_MAC_CTL_REG: Transmit MAC source address filter control */ +#define FR_BB_TX_SRC_MAC_CTL 0x00001100 +#define FRF_BB_TX_SRC_DROP_CTR_LBN 16 +#define FRF_BB_TX_SRC_DROP_CTR_WIDTH 16 +#define FRF_BB_TX_SRC_FLTR_EN_LBN 15 +#define FRF_BB_TX_SRC_FLTR_EN_WIDTH 1 +#define FRF_BB_TX_DROP_CTR_CLR_LBN 12 +#define FRF_BB_TX_DROP_CTR_CLR_WIDTH 1 +#define FRF_BB_TX_MAC_QID_SEL_LBN 0 +#define FRF_BB_TX_MAC_QID_SEL_WIDTH 3 + +/* XM_ADR_LO_REG: XGMAC address register low */ +#define FR_AB_XM_ADR_LO 0x00001200 +#define FRF_AB_XM_ADR_LO_LBN 0 +#define FRF_AB_XM_ADR_LO_WIDTH 32 + +/* XM_ADR_HI_REG: XGMAC address register high */ +#define FR_AB_XM_ADR_HI 0x00001210 +#define FRF_AB_XM_ADR_HI_LBN 0 +#define FRF_AB_XM_ADR_HI_WIDTH 16 + +/* XM_GLB_CFG_REG: XGMAC global configuration */ +#define FR_AB_XM_GLB_CFG 0x00001220 +#define FRF_AB_XM_RMTFLT_GEN_LBN 17 +#define FRF_AB_XM_RMTFLT_GEN_WIDTH 1 +#define FRF_AB_XM_DEBUG_MODE_LBN 16 +#define FRF_AB_XM_DEBUG_MODE_WIDTH 1 +#define FRF_AB_XM_RX_STAT_EN_LBN 11 +#define FRF_AB_XM_RX_STAT_EN_WIDTH 1 +#define FRF_AB_XM_TX_STAT_EN_LBN 10 +#define FRF_AB_XM_TX_STAT_EN_WIDTH 1 +#define FRF_AB_XM_RX_JUMBO_MODE_LBN 6 +#define FRF_AB_XM_RX_JUMBO_MODE_WIDTH 1 +#define FRF_AB_XM_WAN_MODE_LBN 5 +#define FRF_AB_XM_WAN_MODE_WIDTH 1 +#define FRF_AB_XM_INTCLR_MODE_LBN 3 +#define FRF_AB_XM_INTCLR_MODE_WIDTH 1 +#define FRF_AB_XM_CORE_RST_LBN 0 +#define FRF_AB_XM_CORE_RST_WIDTH 1 + +/* XM_TX_CFG_REG: XGMAC transmit configuration */ +#define FR_AB_XM_TX_CFG 0x00001230 +#define FRF_AB_XM_TX_PROG_LBN 24 +#define FRF_AB_XM_TX_PROG_WIDTH 1 +#define FRF_AB_XM_IPG_LBN 16 +#define FRF_AB_XM_IPG_WIDTH 4 +#define FRF_AB_XM_FCNTL_LBN 10 +#define FRF_AB_XM_FCNTL_WIDTH 1 +#define FRF_AB_XM_TXCRC_LBN 8 +#define FRF_AB_XM_TXCRC_WIDTH 1 +#define FRF_AB_XM_EDRC_LBN 6 +#define FRF_AB_XM_EDRC_WIDTH 1 +#define FRF_AB_XM_AUTO_PAD_LBN 5 +#define FRF_AB_XM_AUTO_PAD_WIDTH 1 +#define FRF_AB_XM_TX_PRMBL_LBN 2 +#define FRF_AB_XM_TX_PRMBL_WIDTH 1 +#define FRF_AB_XM_TXEN_LBN 1 +#define FRF_AB_XM_TXEN_WIDTH 1 +#define FRF_AB_XM_TX_RST_LBN 0 +#define FRF_AB_XM_TX_RST_WIDTH 1 + +/* XM_RX_CFG_REG: XGMAC receive configuration */ +#define FR_AB_XM_RX_CFG 0x00001240 +#define FRF_AB_XM_PASS_LENERR_LBN 26 +#define FRF_AB_XM_PASS_LENERR_WIDTH 1 +#define FRF_AB_XM_PASS_CRC_ERR_LBN 25 +#define FRF_AB_XM_PASS_CRC_ERR_WIDTH 1 +#define FRF_AB_XM_PASS_PRMBLE_ERR_LBN 24 +#define FRF_AB_XM_PASS_PRMBLE_ERR_WIDTH 1 +#define FRF_AB_XM_REJ_BCAST_LBN 20 +#define FRF_AB_XM_REJ_BCAST_WIDTH 1 +#define FRF_AB_XM_ACPT_ALL_MCAST_LBN 11 +#define FRF_AB_XM_ACPT_ALL_MCAST_WIDTH 1 +#define FRF_AB_XM_ACPT_ALL_UCAST_LBN 9 +#define FRF_AB_XM_ACPT_ALL_UCAST_WIDTH 1 +#define FRF_AB_XM_AUTO_DEPAD_LBN 8 +#define FRF_AB_XM_AUTO_DEPAD_WIDTH 1 +#define FRF_AB_XM_RXCRC_LBN 3 +#define FRF_AB_XM_RXCRC_WIDTH 1 +#define FRF_AB_XM_RX_PRMBL_LBN 2 +#define FRF_AB_XM_RX_PRMBL_WIDTH 1 +#define FRF_AB_XM_RXEN_LBN 1 +#define FRF_AB_XM_RXEN_WIDTH 1 +#define FRF_AB_XM_RX_RST_LBN 0 +#define FRF_AB_XM_RX_RST_WIDTH 1 + +/* XM_MGT_INT_MASK: documentation to be written for sum_XM_MGT_INT_MASK */ +#define FR_AB_XM_MGT_INT_MASK 0x00001250 +#define FRF_AB_XM_MSK_STA_INTR_LBN 16 +#define FRF_AB_XM_MSK_STA_INTR_WIDTH 1 +#define FRF_AB_XM_MSK_STAT_CNTR_HF_LBN 9 +#define FRF_AB_XM_MSK_STAT_CNTR_HF_WIDTH 1 +#define FRF_AB_XM_MSK_STAT_CNTR_OF_LBN 8 +#define FRF_AB_XM_MSK_STAT_CNTR_OF_WIDTH 1 +#define FRF_AB_XM_MSK_PRMBLE_ERR_LBN 2 +#define FRF_AB_XM_MSK_PRMBLE_ERR_WIDTH 1 +#define FRF_AB_XM_MSK_RMTFLT_LBN 1 +#define FRF_AB_XM_MSK_RMTFLT_WIDTH 1 +#define FRF_AB_XM_MSK_LCLFLT_LBN 0 +#define FRF_AB_XM_MSK_LCLFLT_WIDTH 1 + +/* XM_FC_REG: XGMAC flow control register */ +#define FR_AB_XM_FC 0x00001270 +#define FRF_AB_XM_PAUSE_TIME_LBN 16 +#define FRF_AB_XM_PAUSE_TIME_WIDTH 16 +#define FRF_AB_XM_RX_MAC_STAT_LBN 11 +#define FRF_AB_XM_RX_MAC_STAT_WIDTH 1 +#define FRF_AB_XM_TX_MAC_STAT_LBN 10 +#define FRF_AB_XM_TX_MAC_STAT_WIDTH 1 +#define FRF_AB_XM_MCNTL_PASS_LBN 8 +#define FRF_AB_XM_MCNTL_PASS_WIDTH 2 +#define FRF_AB_XM_REJ_CNTL_UCAST_LBN 6 +#define FRF_AB_XM_REJ_CNTL_UCAST_WIDTH 1 +#define FRF_AB_XM_REJ_CNTL_MCAST_LBN 5 +#define FRF_AB_XM_REJ_CNTL_MCAST_WIDTH 1 +#define FRF_AB_XM_ZPAUSE_LBN 2 +#define FRF_AB_XM_ZPAUSE_WIDTH 1 +#define FRF_AB_XM_XMIT_PAUSE_LBN 1 +#define FRF_AB_XM_XMIT_PAUSE_WIDTH 1 +#define FRF_AB_XM_DIS_FCNTL_LBN 0 +#define FRF_AB_XM_DIS_FCNTL_WIDTH 1 + +/* XM_PAUSE_TIME_REG: XGMAC pause time register */ +#define FR_AB_XM_PAUSE_TIME 0x00001290 +#define FRF_AB_XM_TX_PAUSE_CNT_LBN 16 +#define FRF_AB_XM_TX_PAUSE_CNT_WIDTH 16 +#define FRF_AB_XM_RX_PAUSE_CNT_LBN 0 +#define FRF_AB_XM_RX_PAUSE_CNT_WIDTH 16 + +/* XM_TX_PARAM_REG: XGMAC transmit parameter register */ +#define FR_AB_XM_TX_PARAM 0x000012d0 +#define FRF_AB_XM_TX_JUMBO_MODE_LBN 31 +#define FRF_AB_XM_TX_JUMBO_MODE_WIDTH 1 +#define FRF_AB_XM_MAX_TX_FRM_SIZE_HI_LBN 19 +#define FRF_AB_XM_MAX_TX_FRM_SIZE_HI_WIDTH 11 +#define FRF_AB_XM_MAX_TX_FRM_SIZE_LO_LBN 16 +#define FRF_AB_XM_MAX_TX_FRM_SIZE_LO_WIDTH 3 +#define FRF_AB_XM_PAD_CHAR_LBN 0 +#define FRF_AB_XM_PAD_CHAR_WIDTH 8 + +/* XM_RX_PARAM_REG: XGMAC receive parameter register */ +#define FR_AB_XM_RX_PARAM 0x000012e0 +#define FRF_AB_XM_MAX_RX_FRM_SIZE_HI_LBN 3 +#define FRF_AB_XM_MAX_RX_FRM_SIZE_HI_WIDTH 11 +#define FRF_AB_XM_MAX_RX_FRM_SIZE_LO_LBN 0 +#define FRF_AB_XM_MAX_RX_FRM_SIZE_LO_WIDTH 3 + +/* XM_MGT_INT_MSK_REG: XGMAC management interrupt mask register */ +#define FR_AB_XM_MGT_INT_MSK 0x000012f0 +#define FRF_AB_XM_STAT_CNTR_OF_LBN 9 +#define FRF_AB_XM_STAT_CNTR_OF_WIDTH 1 +#define FRF_AB_XM_STAT_CNTR_HF_LBN 8 +#define FRF_AB_XM_STAT_CNTR_HF_WIDTH 1 +#define FRF_AB_XM_PRMBLE_ERR_LBN 2 +#define FRF_AB_XM_PRMBLE_ERR_WIDTH 1 +#define FRF_AB_XM_RMTFLT_LBN 1 +#define FRF_AB_XM_RMTFLT_WIDTH 1 +#define FRF_AB_XM_LCLFLT_LBN 0 +#define FRF_AB_XM_LCLFLT_WIDTH 1 + +/* XX_PWR_RST_REG: XGXS/XAUI powerdown/reset register */ +#define FR_AB_XX_PWR_RST 0x00001300 +#define FRF_AB_XX_PWRDND_SIG_LBN 31 +#define FRF_AB_XX_PWRDND_SIG_WIDTH 1 +#define FRF_AB_XX_PWRDNC_SIG_LBN 30 +#define FRF_AB_XX_PWRDNC_SIG_WIDTH 1 +#define FRF_AB_XX_PWRDNB_SIG_LBN 29 +#define FRF_AB_XX_PWRDNB_SIG_WIDTH 1 +#define FRF_AB_XX_PWRDNA_SIG_LBN 28 +#define FRF_AB_XX_PWRDNA_SIG_WIDTH 1 +#define FRF_AB_XX_SIM_MODE_LBN 27 +#define FRF_AB_XX_SIM_MODE_WIDTH 1 +#define FRF_AB_XX_RSTPLLCD_SIG_LBN 25 +#define FRF_AB_XX_RSTPLLCD_SIG_WIDTH 1 +#define FRF_AB_XX_RSTPLLAB_SIG_LBN 24 +#define FRF_AB_XX_RSTPLLAB_SIG_WIDTH 1 +#define FRF_AB_XX_RESETD_SIG_LBN 23 +#define FRF_AB_XX_RESETD_SIG_WIDTH 1 +#define FRF_AB_XX_RESETC_SIG_LBN 22 +#define FRF_AB_XX_RESETC_SIG_WIDTH 1 +#define FRF_AB_XX_RESETB_SIG_LBN 21 +#define FRF_AB_XX_RESETB_SIG_WIDTH 1 +#define FRF_AB_XX_RESETA_SIG_LBN 20 +#define FRF_AB_XX_RESETA_SIG_WIDTH 1 +#define FRF_AB_XX_RSTXGXSRX_SIG_LBN 18 +#define FRF_AB_XX_RSTXGXSRX_SIG_WIDTH 1 +#define FRF_AB_XX_RSTXGXSTX_SIG_LBN 17 +#define FRF_AB_XX_RSTXGXSTX_SIG_WIDTH 1 +#define FRF_AB_XX_SD_RST_ACT_LBN 16 +#define FRF_AB_XX_SD_RST_ACT_WIDTH 1 +#define FRF_AB_XX_PWRDND_EN_LBN 15 +#define FRF_AB_XX_PWRDND_EN_WIDTH 1 +#define FRF_AB_XX_PWRDNC_EN_LBN 14 +#define FRF_AB_XX_PWRDNC_EN_WIDTH 1 +#define FRF_AB_XX_PWRDNB_EN_LBN 13 +#define FRF_AB_XX_PWRDNB_EN_WIDTH 1 +#define FRF_AB_XX_PWRDNA_EN_LBN 12 +#define FRF_AB_XX_PWRDNA_EN_WIDTH 1 +#define FRF_AB_XX_RSTPLLCD_EN_LBN 9 +#define FRF_AB_XX_RSTPLLCD_EN_WIDTH 1 +#define FRF_AB_XX_RSTPLLAB_EN_LBN 8 +#define FRF_AB_XX_RSTPLLAB_EN_WIDTH 1 +#define FRF_AB_XX_RESETD_EN_LBN 7 +#define FRF_AB_XX_RESETD_EN_WIDTH 1 +#define FRF_AB_XX_RESETC_EN_LBN 6 +#define FRF_AB_XX_RESETC_EN_WIDTH 1 +#define FRF_AB_XX_RESETB_EN_LBN 5 +#define FRF_AB_XX_RESETB_EN_WIDTH 1 +#define FRF_AB_XX_RESETA_EN_LBN 4 +#define FRF_AB_XX_RESETA_EN_WIDTH 1 +#define FRF_AB_XX_RSTXGXSRX_EN_LBN 2 +#define FRF_AB_XX_RSTXGXSRX_EN_WIDTH 1 +#define FRF_AB_XX_RSTXGXSTX_EN_LBN 1 +#define FRF_AB_XX_RSTXGXSTX_EN_WIDTH 1 +#define FRF_AB_XX_RST_XX_EN_LBN 0 +#define FRF_AB_XX_RST_XX_EN_WIDTH 1 + +/* XX_SD_CTL_REG: XGXS/XAUI powerdown/reset control register */ +#define FR_AB_XX_SD_CTL 0x00001310 +#define FRF_AB_XX_TERMADJ1_LBN 17 +#define FRF_AB_XX_TERMADJ1_WIDTH 1 +#define FRF_AB_XX_TERMADJ0_LBN 16 +#define FRF_AB_XX_TERMADJ0_WIDTH 1 +#define FRF_AB_XX_HIDRVD_LBN 15 +#define FRF_AB_XX_HIDRVD_WIDTH 1 +#define FRF_AB_XX_LODRVD_LBN 14 +#define FRF_AB_XX_LODRVD_WIDTH 1 +#define FRF_AB_XX_HIDRVC_LBN 13 +#define FRF_AB_XX_HIDRVC_WIDTH 1 +#define FRF_AB_XX_LODRVC_LBN 12 +#define FRF_AB_XX_LODRVC_WIDTH 1 +#define FRF_AB_XX_HIDRVB_LBN 11 +#define FRF_AB_XX_HIDRVB_WIDTH 1 +#define FRF_AB_XX_LODRVB_LBN 10 +#define FRF_AB_XX_LODRVB_WIDTH 1 +#define FRF_AB_XX_HIDRVA_LBN 9 +#define FRF_AB_XX_HIDRVA_WIDTH 1 +#define FRF_AB_XX_LODRVA_LBN 8 +#define FRF_AB_XX_LODRVA_WIDTH 1 +#define FRF_AB_XX_LPBKD_LBN 3 +#define FRF_AB_XX_LPBKD_WIDTH 1 +#define FRF_AB_XX_LPBKC_LBN 2 +#define FRF_AB_XX_LPBKC_WIDTH 1 +#define FRF_AB_XX_LPBKB_LBN 1 +#define FRF_AB_XX_LPBKB_WIDTH 1 +#define FRF_AB_XX_LPBKA_LBN 0 +#define FRF_AB_XX_LPBKA_WIDTH 1 + +/* XX_TXDRV_CTL_REG: XAUI SerDes transmit drive control register */ +#define FR_AB_XX_TXDRV_CTL 0x00001320 +#define FRF_AB_XX_DEQD_LBN 28 +#define FRF_AB_XX_DEQD_WIDTH 4 +#define FRF_AB_XX_DEQC_LBN 24 +#define FRF_AB_XX_DEQC_WIDTH 4 +#define FRF_AB_XX_DEQB_LBN 20 +#define FRF_AB_XX_DEQB_WIDTH 4 +#define FRF_AB_XX_DEQA_LBN 16 +#define FRF_AB_XX_DEQA_WIDTH 4 +#define FRF_AB_XX_DTXD_LBN 12 +#define FRF_AB_XX_DTXD_WIDTH 4 +#define FRF_AB_XX_DTXC_LBN 8 +#define FRF_AB_XX_DTXC_WIDTH 4 +#define FRF_AB_XX_DTXB_LBN 4 +#define FRF_AB_XX_DTXB_WIDTH 4 +#define FRF_AB_XX_DTXA_LBN 0 +#define FRF_AB_XX_DTXA_WIDTH 4 + +/* XX_PRBS_CTL_REG: documentation to be written for sum_XX_PRBS_CTL_REG */ +#define FR_AB_XX_PRBS_CTL 0x00001330 +#define FRF_AB_XX_CH3_RX_PRBS_SEL_LBN 30 +#define FRF_AB_XX_CH3_RX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH3_RX_PRBS_INV_LBN 29 +#define FRF_AB_XX_CH3_RX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH3_RX_PRBS_CHKEN_LBN 28 +#define FRF_AB_XX_CH3_RX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH2_RX_PRBS_SEL_LBN 26 +#define FRF_AB_XX_CH2_RX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH2_RX_PRBS_INV_LBN 25 +#define FRF_AB_XX_CH2_RX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH2_RX_PRBS_CHKEN_LBN 24 +#define FRF_AB_XX_CH2_RX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH1_RX_PRBS_SEL_LBN 22 +#define FRF_AB_XX_CH1_RX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH1_RX_PRBS_INV_LBN 21 +#define FRF_AB_XX_CH1_RX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH1_RX_PRBS_CHKEN_LBN 20 +#define FRF_AB_XX_CH1_RX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH0_RX_PRBS_SEL_LBN 18 +#define FRF_AB_XX_CH0_RX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH0_RX_PRBS_INV_LBN 17 +#define FRF_AB_XX_CH0_RX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH0_RX_PRBS_CHKEN_LBN 16 +#define FRF_AB_XX_CH0_RX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH3_TX_PRBS_SEL_LBN 14 +#define FRF_AB_XX_CH3_TX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH3_TX_PRBS_INV_LBN 13 +#define FRF_AB_XX_CH3_TX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH3_TX_PRBS_CHKEN_LBN 12 +#define FRF_AB_XX_CH3_TX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH2_TX_PRBS_SEL_LBN 10 +#define FRF_AB_XX_CH2_TX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH2_TX_PRBS_INV_LBN 9 +#define FRF_AB_XX_CH2_TX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH2_TX_PRBS_CHKEN_LBN 8 +#define FRF_AB_XX_CH2_TX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH1_TX_PRBS_SEL_LBN 6 +#define FRF_AB_XX_CH1_TX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH1_TX_PRBS_INV_LBN 5 +#define FRF_AB_XX_CH1_TX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH1_TX_PRBS_CHKEN_LBN 4 +#define FRF_AB_XX_CH1_TX_PRBS_CHKEN_WIDTH 1 +#define FRF_AB_XX_CH0_TX_PRBS_SEL_LBN 2 +#define FRF_AB_XX_CH0_TX_PRBS_SEL_WIDTH 2 +#define FRF_AB_XX_CH0_TX_PRBS_INV_LBN 1 +#define FRF_AB_XX_CH0_TX_PRBS_INV_WIDTH 1 +#define FRF_AB_XX_CH0_TX_PRBS_CHKEN_LBN 0 +#define FRF_AB_XX_CH0_TX_PRBS_CHKEN_WIDTH 1 + +/* XX_PRBS_CHK_REG: documentation to be written for sum_XX_PRBS_CHK_REG */ +#define FR_AB_XX_PRBS_CHK 0x00001340 +#define FRF_AB_XX_REV_LB_EN_LBN 16 +#define FRF_AB_XX_REV_LB_EN_WIDTH 1 +#define FRF_AB_XX_CH3_DEG_DET_LBN 15 +#define FRF_AB_XX_CH3_DEG_DET_WIDTH 1 +#define FRF_AB_XX_CH3_LFSR_LOCK_IND_LBN 14 +#define FRF_AB_XX_CH3_LFSR_LOCK_IND_WIDTH 1 +#define FRF_AB_XX_CH3_PRBS_FRUN_LBN 13 +#define FRF_AB_XX_CH3_PRBS_FRUN_WIDTH 1 +#define FRF_AB_XX_CH3_ERR_CHK_LBN 12 +#define FRF_AB_XX_CH3_ERR_CHK_WIDTH 1 +#define FRF_AB_XX_CH2_DEG_DET_LBN 11 +#define FRF_AB_XX_CH2_DEG_DET_WIDTH 1 +#define FRF_AB_XX_CH2_LFSR_LOCK_IND_LBN 10 +#define FRF_AB_XX_CH2_LFSR_LOCK_IND_WIDTH 1 +#define FRF_AB_XX_CH2_PRBS_FRUN_LBN 9 +#define FRF_AB_XX_CH2_PRBS_FRUN_WIDTH 1 +#define FRF_AB_XX_CH2_ERR_CHK_LBN 8 +#define FRF_AB_XX_CH2_ERR_CHK_WIDTH 1 +#define FRF_AB_XX_CH1_DEG_DET_LBN 7 +#define FRF_AB_XX_CH1_DEG_DET_WIDTH 1 +#define FRF_AB_XX_CH1_LFSR_LOCK_IND_LBN 6 +#define FRF_AB_XX_CH1_LFSR_LOCK_IND_WIDTH 1 +#define FRF_AB_XX_CH1_PRBS_FRUN_LBN 5 +#define FRF_AB_XX_CH1_PRBS_FRUN_WIDTH 1 +#define FRF_AB_XX_CH1_ERR_CHK_LBN 4 +#define FRF_AB_XX_CH1_ERR_CHK_WIDTH 1 +#define FRF_AB_XX_CH0_DEG_DET_LBN 3 +#define FRF_AB_XX_CH0_DEG_DET_WIDTH 1 +#define FRF_AB_XX_CH0_LFSR_LOCK_IND_LBN 2 +#define FRF_AB_XX_CH0_LFSR_LOCK_IND_WIDTH 1 +#define FRF_AB_XX_CH0_PRBS_FRUN_LBN 1 +#define FRF_AB_XX_CH0_PRBS_FRUN_WIDTH 1 +#define FRF_AB_XX_CH0_ERR_CHK_LBN 0 +#define FRF_AB_XX_CH0_ERR_CHK_WIDTH 1 + +/* XX_PRBS_ERR_REG: documentation to be written for sum_XX_PRBS_ERR_REG */ +#define FR_AB_XX_PRBS_ERR 0x00001350 +#define FRF_AB_XX_CH3_PRBS_ERR_CNT_LBN 24 +#define FRF_AB_XX_CH3_PRBS_ERR_CNT_WIDTH 8 +#define FRF_AB_XX_CH2_PRBS_ERR_CNT_LBN 16 +#define FRF_AB_XX_CH2_PRBS_ERR_CNT_WIDTH 8 +#define FRF_AB_XX_CH1_PRBS_ERR_CNT_LBN 8 +#define FRF_AB_XX_CH1_PRBS_ERR_CNT_WIDTH 8 +#define FRF_AB_XX_CH0_PRBS_ERR_CNT_LBN 0 +#define FRF_AB_XX_CH0_PRBS_ERR_CNT_WIDTH 8 + +/* XX_CORE_STAT_REG: XAUI XGXS core status register */ +#define FR_AB_XX_CORE_STAT 0x00001360 +#define FRF_AB_XX_FORCE_SIG3_LBN 31 +#define FRF_AB_XX_FORCE_SIG3_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG3_VAL_LBN 30 +#define FRF_AB_XX_FORCE_SIG3_VAL_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG2_LBN 29 +#define FRF_AB_XX_FORCE_SIG2_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG2_VAL_LBN 28 +#define FRF_AB_XX_FORCE_SIG2_VAL_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG1_LBN 27 +#define FRF_AB_XX_FORCE_SIG1_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG1_VAL_LBN 26 +#define FRF_AB_XX_FORCE_SIG1_VAL_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG0_LBN 25 +#define FRF_AB_XX_FORCE_SIG0_WIDTH 1 +#define FRF_AB_XX_FORCE_SIG0_VAL_LBN 24 +#define FRF_AB_XX_FORCE_SIG0_VAL_WIDTH 1 +#define FRF_AB_XX_XGXS_LB_EN_LBN 23 +#define FRF_AB_XX_XGXS_LB_EN_WIDTH 1 +#define FRF_AB_XX_XGMII_LB_EN_LBN 22 +#define FRF_AB_XX_XGMII_LB_EN_WIDTH 1 +#define FRF_AB_XX_MATCH_FAULT_LBN 21 +#define FRF_AB_XX_MATCH_FAULT_WIDTH 1 +#define FRF_AB_XX_ALIGN_DONE_LBN 20 +#define FRF_AB_XX_ALIGN_DONE_WIDTH 1 +#define FRF_AB_XX_SYNC_STAT3_LBN 19 +#define FRF_AB_XX_SYNC_STAT3_WIDTH 1 +#define FRF_AB_XX_SYNC_STAT2_LBN 18 +#define FRF_AB_XX_SYNC_STAT2_WIDTH 1 +#define FRF_AB_XX_SYNC_STAT1_LBN 17 +#define FRF_AB_XX_SYNC_STAT1_WIDTH 1 +#define FRF_AB_XX_SYNC_STAT0_LBN 16 +#define FRF_AB_XX_SYNC_STAT0_WIDTH 1 +#define FRF_AB_XX_COMMA_DET_CH3_LBN 15 +#define FRF_AB_XX_COMMA_DET_CH3_WIDTH 1 +#define FRF_AB_XX_COMMA_DET_CH2_LBN 14 +#define FRF_AB_XX_COMMA_DET_CH2_WIDTH 1 +#define FRF_AB_XX_COMMA_DET_CH1_LBN 13 +#define FRF_AB_XX_COMMA_DET_CH1_WIDTH 1 +#define FRF_AB_XX_COMMA_DET_CH0_LBN 12 +#define FRF_AB_XX_COMMA_DET_CH0_WIDTH 1 +#define FRF_AB_XX_CGRP_ALIGN_CH3_LBN 11 +#define FRF_AB_XX_CGRP_ALIGN_CH3_WIDTH 1 +#define FRF_AB_XX_CGRP_ALIGN_CH2_LBN 10 +#define FRF_AB_XX_CGRP_ALIGN_CH2_WIDTH 1 +#define FRF_AB_XX_CGRP_ALIGN_CH1_LBN 9 +#define FRF_AB_XX_CGRP_ALIGN_CH1_WIDTH 1 +#define FRF_AB_XX_CGRP_ALIGN_CH0_LBN 8 +#define FRF_AB_XX_CGRP_ALIGN_CH0_WIDTH 1 +#define FRF_AB_XX_CHAR_ERR_CH3_LBN 7 +#define FRF_AB_XX_CHAR_ERR_CH3_WIDTH 1 +#define FRF_AB_XX_CHAR_ERR_CH2_LBN 6 +#define FRF_AB_XX_CHAR_ERR_CH2_WIDTH 1 +#define FRF_AB_XX_CHAR_ERR_CH1_LBN 5 +#define FRF_AB_XX_CHAR_ERR_CH1_WIDTH 1 +#define FRF_AB_XX_CHAR_ERR_CH0_LBN 4 +#define FRF_AB_XX_CHAR_ERR_CH0_WIDTH 1 +#define FRF_AB_XX_DISPERR_CH3_LBN 3 +#define FRF_AB_XX_DISPERR_CH3_WIDTH 1 +#define FRF_AB_XX_DISPERR_CH2_LBN 2 +#define FRF_AB_XX_DISPERR_CH2_WIDTH 1 +#define FRF_AB_XX_DISPERR_CH1_LBN 1 +#define FRF_AB_XX_DISPERR_CH1_WIDTH 1 +#define FRF_AB_XX_DISPERR_CH0_LBN 0 +#define FRF_AB_XX_DISPERR_CH0_WIDTH 1 + +/* RX_DESC_PTR_TBL_KER: Receive descriptor pointer table */ +#define FR_AA_RX_DESC_PTR_TBL_KER 0x00011800 +#define FR_AA_RX_DESC_PTR_TBL_KER_STEP 16 +#define FR_AA_RX_DESC_PTR_TBL_KER_ROWS 4 +/* RX_DESC_PTR_TBL: Receive descriptor pointer table */ +#define FR_BZ_RX_DESC_PTR_TBL 0x00f40000 +#define FR_BZ_RX_DESC_PTR_TBL_STEP 16 +#define FR_BB_RX_DESC_PTR_TBL_ROWS 4096 +#define FR_CZ_RX_DESC_PTR_TBL_ROWS 1024 +#define FRF_CZ_RX_HDR_SPLIT_LBN 90 +#define FRF_CZ_RX_HDR_SPLIT_WIDTH 1 +#define FRF_AA_RX_RESET_LBN 89 +#define FRF_AA_RX_RESET_WIDTH 1 +#define FRF_AZ_RX_ISCSI_DDIG_EN_LBN 88 +#define FRF_AZ_RX_ISCSI_DDIG_EN_WIDTH 1 +#define FRF_AZ_RX_ISCSI_HDIG_EN_LBN 87 +#define FRF_AZ_RX_ISCSI_HDIG_EN_WIDTH 1 +#define FRF_AZ_RX_DESC_PREF_ACT_LBN 86 +#define FRF_AZ_RX_DESC_PREF_ACT_WIDTH 1 +#define FRF_AZ_RX_DC_HW_RPTR_LBN 80 +#define FRF_AZ_RX_DC_HW_RPTR_WIDTH 6 +#define FRF_AZ_RX_DESCQ_HW_RPTR_LBN 68 +#define FRF_AZ_RX_DESCQ_HW_RPTR_WIDTH 12 +#define FRF_AZ_RX_DESCQ_SW_WPTR_LBN 56 +#define FRF_AZ_RX_DESCQ_SW_WPTR_WIDTH 12 +#define FRF_AZ_RX_DESCQ_BUF_BASE_ID_LBN 36 +#define FRF_AZ_RX_DESCQ_BUF_BASE_ID_WIDTH 20 +#define FRF_AZ_RX_DESCQ_EVQ_ID_LBN 24 +#define FRF_AZ_RX_DESCQ_EVQ_ID_WIDTH 12 +#define FRF_AZ_RX_DESCQ_OWNER_ID_LBN 10 +#define FRF_AZ_RX_DESCQ_OWNER_ID_WIDTH 14 +#define FRF_AZ_RX_DESCQ_LABEL_LBN 5 +#define FRF_AZ_RX_DESCQ_LABEL_WIDTH 5 +#define FRF_AZ_RX_DESCQ_SIZE_LBN 3 +#define FRF_AZ_RX_DESCQ_SIZE_WIDTH 2 +#define FFE_AZ_RX_DESCQ_SIZE_4K 3 +#define FFE_AZ_RX_DESCQ_SIZE_2K 2 +#define FFE_AZ_RX_DESCQ_SIZE_1K 1 +#define FFE_AZ_RX_DESCQ_SIZE_512 0 +#define FRF_AZ_RX_DESCQ_TYPE_LBN 2 +#define FRF_AZ_RX_DESCQ_TYPE_WIDTH 1 +#define FRF_AZ_RX_DESCQ_JUMBO_LBN 1 +#define FRF_AZ_RX_DESCQ_JUMBO_WIDTH 1 +#define FRF_AZ_RX_DESCQ_EN_LBN 0 +#define FRF_AZ_RX_DESCQ_EN_WIDTH 1 + +/* TX_DESC_PTR_TBL_KER: Transmit descriptor pointer */ +#define FR_AA_TX_DESC_PTR_TBL_KER 0x00011900 +#define FR_AA_TX_DESC_PTR_TBL_KER_STEP 16 +#define FR_AA_TX_DESC_PTR_TBL_KER_ROWS 8 +/* TX_DESC_PTR_TBL: Transmit descriptor pointer */ +#define FR_BZ_TX_DESC_PTR_TBL 0x00f50000 +#define FR_BZ_TX_DESC_PTR_TBL_STEP 16 +#define FR_BB_TX_DESC_PTR_TBL_ROWS 4096 +#define FR_CZ_TX_DESC_PTR_TBL_ROWS 1024 +#define FRF_CZ_TX_DPT_Q_MASK_WIDTH_LBN 94 +#define FRF_CZ_TX_DPT_Q_MASK_WIDTH_WIDTH 2 +#define FRF_CZ_TX_DPT_ETH_FILT_EN_LBN 93 +#define FRF_CZ_TX_DPT_ETH_FILT_EN_WIDTH 1 +#define FRF_CZ_TX_DPT_IP_FILT_EN_LBN 92 +#define FRF_CZ_TX_DPT_IP_FILT_EN_WIDTH 1 +#define FRF_BZ_TX_NON_IP_DROP_DIS_LBN 91 +#define FRF_BZ_TX_NON_IP_DROP_DIS_WIDTH 1 +#define FRF_BZ_TX_IP_CHKSM_DIS_LBN 90 +#define FRF_BZ_TX_IP_CHKSM_DIS_WIDTH 1 +#define FRF_BZ_TX_TCP_CHKSM_DIS_LBN 89 +#define FRF_BZ_TX_TCP_CHKSM_DIS_WIDTH 1 +#define FRF_AZ_TX_DESCQ_EN_LBN 88 +#define FRF_AZ_TX_DESCQ_EN_WIDTH 1 +#define FRF_AZ_TX_ISCSI_DDIG_EN_LBN 87 +#define FRF_AZ_TX_ISCSI_DDIG_EN_WIDTH 1 +#define FRF_AZ_TX_ISCSI_HDIG_EN_LBN 86 +#define FRF_AZ_TX_ISCSI_HDIG_EN_WIDTH 1 +#define FRF_AZ_TX_DC_HW_RPTR_LBN 80 +#define FRF_AZ_TX_DC_HW_RPTR_WIDTH 6 +#define FRF_AZ_TX_DESCQ_HW_RPTR_LBN 68 +#define FRF_AZ_TX_DESCQ_HW_RPTR_WIDTH 12 +#define FRF_AZ_TX_DESCQ_SW_WPTR_LBN 56 +#define FRF_AZ_TX_DESCQ_SW_WPTR_WIDTH 12 +#define FRF_AZ_TX_DESCQ_BUF_BASE_ID_LBN 36 +#define FRF_AZ_TX_DESCQ_BUF_BASE_ID_WIDTH 20 +#define FRF_AZ_TX_DESCQ_EVQ_ID_LBN 24 +#define FRF_AZ_TX_DESCQ_EVQ_ID_WIDTH 12 +#define FRF_AZ_TX_DESCQ_OWNER_ID_LBN 10 +#define FRF_AZ_TX_DESCQ_OWNER_ID_WIDTH 14 +#define FRF_AZ_TX_DESCQ_LABEL_LBN 5 +#define FRF_AZ_TX_DESCQ_LABEL_WIDTH 5 +#define FRF_AZ_TX_DESCQ_SIZE_LBN 3 +#define FRF_AZ_TX_DESCQ_SIZE_WIDTH 2 +#define FFE_AZ_TX_DESCQ_SIZE_4K 3 +#define FFE_AZ_TX_DESCQ_SIZE_2K 2 +#define FFE_AZ_TX_DESCQ_SIZE_1K 1 +#define FFE_AZ_TX_DESCQ_SIZE_512 0 +#define FRF_AZ_TX_DESCQ_TYPE_LBN 1 +#define FRF_AZ_TX_DESCQ_TYPE_WIDTH 2 +#define FRF_AZ_TX_DESCQ_FLUSH_LBN 0 +#define FRF_AZ_TX_DESCQ_FLUSH_WIDTH 1 + +/* EVQ_PTR_TBL_KER: Event queue pointer table */ +#define FR_AA_EVQ_PTR_TBL_KER 0x00011a00 +#define FR_AA_EVQ_PTR_TBL_KER_STEP 16 +#define FR_AA_EVQ_PTR_TBL_KER_ROWS 4 +/* EVQ_PTR_TBL: Event queue pointer table */ +#define FR_BZ_EVQ_PTR_TBL 0x00f60000 +#define FR_BZ_EVQ_PTR_TBL_STEP 16 +#define FR_CZ_EVQ_PTR_TBL_ROWS 1024 +#define FR_BB_EVQ_PTR_TBL_ROWS 4096 +#define FRF_BZ_EVQ_RPTR_IGN_LBN 40 +#define FRF_BZ_EVQ_RPTR_IGN_WIDTH 1 +#define FRF_AB_EVQ_WKUP_OR_INT_EN_LBN 39 +#define FRF_AB_EVQ_WKUP_OR_INT_EN_WIDTH 1 +#define FRF_CZ_EVQ_DOS_PROTECT_EN_LBN 39 +#define FRF_CZ_EVQ_DOS_PROTECT_EN_WIDTH 1 +#define FRF_AZ_EVQ_NXT_WPTR_LBN 24 +#define FRF_AZ_EVQ_NXT_WPTR_WIDTH 15 +#define FRF_AZ_EVQ_EN_LBN 23 +#define FRF_AZ_EVQ_EN_WIDTH 1 +#define FRF_AZ_EVQ_SIZE_LBN 20 +#define FRF_AZ_EVQ_SIZE_WIDTH 3 +#define FFE_AZ_EVQ_SIZE_32K 6 +#define FFE_AZ_EVQ_SIZE_16K 5 +#define FFE_AZ_EVQ_SIZE_8K 4 +#define FFE_AZ_EVQ_SIZE_4K 3 +#define FFE_AZ_EVQ_SIZE_2K 2 +#define FFE_AZ_EVQ_SIZE_1K 1 +#define FFE_AZ_EVQ_SIZE_512 0 +#define FRF_AZ_EVQ_BUF_BASE_ID_LBN 0 +#define FRF_AZ_EVQ_BUF_BASE_ID_WIDTH 20 + +/* BUF_HALF_TBL_KER: Buffer table in half buffer table mode direct access by driver */ +#define FR_AA_BUF_HALF_TBL_KER 0x00018000 +#define FR_AA_BUF_HALF_TBL_KER_STEP 8 +#define FR_AA_BUF_HALF_TBL_KER_ROWS 4096 +/* BUF_HALF_TBL: Buffer table in half buffer table mode direct access by driver */ +#define FR_BZ_BUF_HALF_TBL 0x00800000 +#define FR_BZ_BUF_HALF_TBL_STEP 8 +#define FR_CZ_BUF_HALF_TBL_ROWS 147456 +#define FR_BB_BUF_HALF_TBL_ROWS 524288 +#define FRF_AZ_BUF_ADR_HBUF_ODD_LBN 44 +#define FRF_AZ_BUF_ADR_HBUF_ODD_WIDTH 20 +#define FRF_AZ_BUF_OWNER_ID_HBUF_ODD_LBN 32 +#define FRF_AZ_BUF_OWNER_ID_HBUF_ODD_WIDTH 12 +#define FRF_AZ_BUF_ADR_HBUF_EVEN_LBN 12 +#define FRF_AZ_BUF_ADR_HBUF_EVEN_WIDTH 20 +#define FRF_AZ_BUF_OWNER_ID_HBUF_EVEN_LBN 0 +#define FRF_AZ_BUF_OWNER_ID_HBUF_EVEN_WIDTH 12 + +/* BUF_FULL_TBL_KER: Buffer table in full buffer table mode direct access by driver */ +#define FR_AA_BUF_FULL_TBL_KER 0x00018000 +#define FR_AA_BUF_FULL_TBL_KER_STEP 8 +#define FR_AA_BUF_FULL_TBL_KER_ROWS 4096 +/* BUF_FULL_TBL: Buffer table in full buffer table mode direct access by driver */ +#define FR_BZ_BUF_FULL_TBL 0x00800000 +#define FR_BZ_BUF_FULL_TBL_STEP 8 +#define FR_CZ_BUF_FULL_TBL_ROWS 147456 +#define FR_BB_BUF_FULL_TBL_ROWS 917504 +#define FRF_AZ_BUF_FULL_UNUSED_LBN 51 +#define FRF_AZ_BUF_FULL_UNUSED_WIDTH 13 +#define FRF_AZ_IP_DAT_BUF_SIZE_LBN 50 +#define FRF_AZ_IP_DAT_BUF_SIZE_WIDTH 1 +#define FRF_AZ_BUF_ADR_REGION_LBN 48 +#define FRF_AZ_BUF_ADR_REGION_WIDTH 2 +#define FFE_AZ_BUF_ADR_REGN3 3 +#define FFE_AZ_BUF_ADR_REGN2 2 +#define FFE_AZ_BUF_ADR_REGN1 1 +#define FFE_AZ_BUF_ADR_REGN0 0 +#define FRF_AZ_BUF_ADR_FBUF_LBN 14 +#define FRF_AZ_BUF_ADR_FBUF_WIDTH 34 +#define FRF_AZ_BUF_OWNER_ID_FBUF_LBN 0 +#define FRF_AZ_BUF_OWNER_ID_FBUF_WIDTH 14 + +/* RX_FILTER_TBL0: TCP/IPv4 Receive filter table */ +#define FR_BZ_RX_FILTER_TBL0 0x00f00000 +#define FR_BZ_RX_FILTER_TBL0_STEP 32 +#define FR_BZ_RX_FILTER_TBL0_ROWS 8192 +/* RX_FILTER_TBL1: TCP/IPv4 Receive filter table */ +#define FR_BB_RX_FILTER_TBL1 0x00f00010 +#define FR_BB_RX_FILTER_TBL1_STEP 32 +#define FR_BB_RX_FILTER_TBL1_ROWS 8192 +#define FRF_BZ_RSS_EN_LBN 110 +#define FRF_BZ_RSS_EN_WIDTH 1 +#define FRF_BZ_SCATTER_EN_LBN 109 +#define FRF_BZ_SCATTER_EN_WIDTH 1 +#define FRF_BZ_TCP_UDP_LBN 108 +#define FRF_BZ_TCP_UDP_WIDTH 1 +#define FRF_BZ_RXQ_ID_LBN 96 +#define FRF_BZ_RXQ_ID_WIDTH 12 +#define FRF_BZ_DEST_IP_LBN 64 +#define FRF_BZ_DEST_IP_WIDTH 32 +#define FRF_BZ_DEST_PORT_TCP_LBN 48 +#define FRF_BZ_DEST_PORT_TCP_WIDTH 16 +#define FRF_BZ_SRC_IP_LBN 16 +#define FRF_BZ_SRC_IP_WIDTH 32 +#define FRF_BZ_SRC_TCP_DEST_UDP_LBN 0 +#define FRF_BZ_SRC_TCP_DEST_UDP_WIDTH 16 + +/* RX_MAC_FILTER_TBL0: Receive Ethernet filter table */ +#define FR_CZ_RX_MAC_FILTER_TBL0 0x00f00010 +#define FR_CZ_RX_MAC_FILTER_TBL0_STEP 32 +#define FR_CZ_RX_MAC_FILTER_TBL0_ROWS 512 +#define FRF_CZ_RMFT_RSS_EN_LBN 75 +#define FRF_CZ_RMFT_RSS_EN_WIDTH 1 +#define FRF_CZ_RMFT_SCATTER_EN_LBN 74 +#define FRF_CZ_RMFT_SCATTER_EN_WIDTH 1 +#define FRF_CZ_RMFT_IP_OVERRIDE_LBN 73 +#define FRF_CZ_RMFT_IP_OVERRIDE_WIDTH 1 +#define FRF_CZ_RMFT_RXQ_ID_LBN 61 +#define FRF_CZ_RMFT_RXQ_ID_WIDTH 12 +#define FRF_CZ_RMFT_WILDCARD_MATCH_LBN 60 +#define FRF_CZ_RMFT_WILDCARD_MATCH_WIDTH 1 +#define FRF_CZ_RMFT_DEST_MAC_LBN 16 +#define FRF_CZ_RMFT_DEST_MAC_WIDTH 44 +#define FRF_CZ_RMFT_VLAN_ID_LBN 0 +#define FRF_CZ_RMFT_VLAN_ID_WIDTH 12 + +/* TIMER_TBL: Timer table */ +#define FR_BZ_TIMER_TBL 0x00f70000 +#define FR_BZ_TIMER_TBL_STEP 16 +#define FR_CZ_TIMER_TBL_ROWS 1024 +#define FR_BB_TIMER_TBL_ROWS 4096 +#define FRF_CZ_TIMER_Q_EN_LBN 33 +#define FRF_CZ_TIMER_Q_EN_WIDTH 1 +#define FRF_CZ_INT_ARMD_LBN 32 +#define FRF_CZ_INT_ARMD_WIDTH 1 +#define FRF_CZ_INT_PEND_LBN 31 +#define FRF_CZ_INT_PEND_WIDTH 1 +#define FRF_CZ_HOST_NOTIFY_MODE_LBN 30 +#define FRF_CZ_HOST_NOTIFY_MODE_WIDTH 1 +#define FRF_CZ_RELOAD_TIMER_VAL_LBN 16 +#define FRF_CZ_RELOAD_TIMER_VAL_WIDTH 14 +#define FRF_CZ_TIMER_MODE_LBN 14 +#define FRF_CZ_TIMER_MODE_WIDTH 2 +#define FFE_CZ_TIMER_MODE_INT_HLDOFF 3 +#define FFE_CZ_TIMER_MODE_TRIG_START 2 +#define FFE_CZ_TIMER_MODE_IMMED_START 1 +#define FFE_CZ_TIMER_MODE_DIS 0 +#define FRF_BB_TIMER_MODE_LBN 12 +#define FRF_BB_TIMER_MODE_WIDTH 2 +#define FFE_BB_TIMER_MODE_INT_HLDOFF 2 +#define FFE_BB_TIMER_MODE_TRIG_START 2 +#define FFE_BB_TIMER_MODE_IMMED_START 1 +#define FFE_BB_TIMER_MODE_DIS 0 +#define FRF_CZ_TIMER_VAL_LBN 0 +#define FRF_CZ_TIMER_VAL_WIDTH 14 +#define FRF_BB_TIMER_VAL_LBN 0 +#define FRF_BB_TIMER_VAL_WIDTH 12 + +/* TX_PACE_TBL: Transmit pacing table */ +#define FR_BZ_TX_PACE_TBL 0x00f80000 +#define FR_BZ_TX_PACE_TBL_STEP 16 +#define FR_CZ_TX_PACE_TBL_ROWS 1024 +#define FR_BB_TX_PACE_TBL_ROWS 4096 +#define FRF_BZ_TX_PACE_LBN 0 +#define FRF_BZ_TX_PACE_WIDTH 5 + +/* RX_INDIRECTION_TBL: RX Indirection Table */ +#define FR_BZ_RX_INDIRECTION_TBL 0x00fb0000 +#define FR_BZ_RX_INDIRECTION_TBL_STEP 16 +#define FR_BZ_RX_INDIRECTION_TBL_ROWS 128 +#define FRF_BZ_IT_QUEUE_LBN 0 +#define FRF_BZ_IT_QUEUE_WIDTH 6 + +/* TX_FILTER_TBL0: TCP/IPv4 Transmit filter table */ +#define FR_CZ_TX_FILTER_TBL0 0x00fc0000 +#define FR_CZ_TX_FILTER_TBL0_STEP 16 +#define FR_CZ_TX_FILTER_TBL0_ROWS 8192 +#define FRF_CZ_TIFT_TCP_UDP_LBN 108 +#define FRF_CZ_TIFT_TCP_UDP_WIDTH 1 +#define FRF_CZ_TIFT_TXQ_ID_LBN 96 +#define FRF_CZ_TIFT_TXQ_ID_WIDTH 12 +#define FRF_CZ_TIFT_DEST_IP_LBN 64 +#define FRF_CZ_TIFT_DEST_IP_WIDTH 32 +#define FRF_CZ_TIFT_DEST_PORT_TCP_LBN 48 +#define FRF_CZ_TIFT_DEST_PORT_TCP_WIDTH 16 +#define FRF_CZ_TIFT_SRC_IP_LBN 16 +#define FRF_CZ_TIFT_SRC_IP_WIDTH 32 +#define FRF_CZ_TIFT_SRC_TCP_DEST_UDP_LBN 0 +#define FRF_CZ_TIFT_SRC_TCP_DEST_UDP_WIDTH 16 + +/* TX_MAC_FILTER_TBL0: Transmit Ethernet filter table */ +#define FR_CZ_TX_MAC_FILTER_TBL0 0x00fe0000 +#define FR_CZ_TX_MAC_FILTER_TBL0_STEP 16 +#define FR_CZ_TX_MAC_FILTER_TBL0_ROWS 512 +#define FRF_CZ_TMFT_TXQ_ID_LBN 61 +#define FRF_CZ_TMFT_TXQ_ID_WIDTH 12 +#define FRF_CZ_TMFT_WILDCARD_MATCH_LBN 60 +#define FRF_CZ_TMFT_WILDCARD_MATCH_WIDTH 1 +#define FRF_CZ_TMFT_SRC_MAC_LBN 16 +#define FRF_CZ_TMFT_SRC_MAC_WIDTH 44 +#define FRF_CZ_TMFT_VLAN_ID_LBN 0 +#define FRF_CZ_TMFT_VLAN_ID_WIDTH 12 + +/* MC_TREG_SMEM: MC Shared Memory */ +#define FR_CZ_MC_TREG_SMEM 0x00ff0000 +#define FR_CZ_MC_TREG_SMEM_STEP 4 +#define FR_CZ_MC_TREG_SMEM_ROWS 512 +#define FRF_CZ_MC_TREG_SMEM_ROW_LBN 0 +#define FRF_CZ_MC_TREG_SMEM_ROW_WIDTH 32 + +/* MSIX_VECTOR_TABLE: MSIX Vector Table */ +#define FR_BB_MSIX_VECTOR_TABLE 0x00ff0000 +#define FR_BZ_MSIX_VECTOR_TABLE_STEP 16 +#define FR_BB_MSIX_VECTOR_TABLE_ROWS 64 +/* MSIX_VECTOR_TABLE: MSIX Vector Table */ +#define FR_CZ_MSIX_VECTOR_TABLE 0x00000000 +/* FR_BZ_MSIX_VECTOR_TABLE_STEP 16 */ +#define FR_CZ_MSIX_VECTOR_TABLE_ROWS 1024 +#define FRF_BZ_MSIX_VECTOR_RESERVED_LBN 97 +#define FRF_BZ_MSIX_VECTOR_RESERVED_WIDTH 31 +#define FRF_BZ_MSIX_VECTOR_MASK_LBN 96 +#define FRF_BZ_MSIX_VECTOR_MASK_WIDTH 1 +#define FRF_BZ_MSIX_MESSAGE_DATA_LBN 64 +#define FRF_BZ_MSIX_MESSAGE_DATA_WIDTH 32 +#define FRF_BZ_MSIX_MESSAGE_ADDRESS_HI_LBN 32 +#define FRF_BZ_MSIX_MESSAGE_ADDRESS_HI_WIDTH 32 +#define FRF_BZ_MSIX_MESSAGE_ADDRESS_LO_LBN 0 +#define FRF_BZ_MSIX_MESSAGE_ADDRESS_LO_WIDTH 32 + +/* MSIX_PBA_TABLE: MSIX Pending Bit Array */ +#define FR_BB_MSIX_PBA_TABLE 0x00ff2000 +#define FR_BZ_MSIX_PBA_TABLE_STEP 4 +#define FR_BB_MSIX_PBA_TABLE_ROWS 2 +/* MSIX_PBA_TABLE: MSIX Pending Bit Array */ +#define FR_CZ_MSIX_PBA_TABLE 0x00008000 +/* FR_BZ_MSIX_PBA_TABLE_STEP 4 */ +#define FR_CZ_MSIX_PBA_TABLE_ROWS 32 +#define FRF_BZ_MSIX_PBA_PEND_DWORD_LBN 0 +#define FRF_BZ_MSIX_PBA_PEND_DWORD_WIDTH 32 + +/* SRM_DBG_REG: SRAM debug access */ +#define FR_BZ_SRM_DBG 0x03000000 +#define FR_BZ_SRM_DBG_STEP 8 +#define FR_CZ_SRM_DBG_ROWS 262144 +#define FR_BB_SRM_DBG_ROWS 2097152 +#define FRF_BZ_SRM_DBG_LBN 0 +#define FRF_BZ_SRM_DBG_WIDTH 64 + +/* TB_MSIX_PBA_TABLE: MSIX Pending Bit Array */ +#define FR_CZ_TB_MSIX_PBA_TABLE 0x00008000 +#define FR_CZ_TB_MSIX_PBA_TABLE_STEP 4 +#define FR_CZ_TB_MSIX_PBA_TABLE_ROWS 1024 +#define FRF_CZ_TB_MSIX_PBA_PEND_DWORD_LBN 0 +#define FRF_CZ_TB_MSIX_PBA_PEND_DWORD_WIDTH 32 + +/* DRIVER_EV */ +#define FSF_AZ_DRIVER_EV_SUBCODE_LBN 56 +#define FSF_AZ_DRIVER_EV_SUBCODE_WIDTH 4 +#define FSE_BZ_TX_DSC_ERROR_EV 15 +#define FSE_BZ_RX_DSC_ERROR_EV 14 +#define FSE_AA_RX_RECOVER_EV 11 +#define FSE_AZ_TIMER_EV 10 +#define FSE_AZ_TX_PKT_NON_TCP_UDP 9 +#define FSE_AZ_WAKE_UP_EV 6 +#define FSE_AZ_SRM_UPD_DONE_EV 5 +#define FSE_AB_EVQ_NOT_EN_EV 3 +#define FSE_AZ_EVQ_INIT_DONE_EV 2 +#define FSE_AZ_RX_DESCQ_FLS_DONE_EV 1 +#define FSE_AZ_TX_DESCQ_FLS_DONE_EV 0 +#define FSF_AZ_DRIVER_EV_SUBDATA_LBN 0 +#define FSF_AZ_DRIVER_EV_SUBDATA_WIDTH 14 + +/* EVENT_ENTRY */ +#define FSF_AZ_EV_CODE_LBN 60 +#define FSF_AZ_EV_CODE_WIDTH 4 +#define FSE_CZ_EV_CODE_MCDI_EV 12 +#define FSE_CZ_EV_CODE_USER_EV 8 +#define FSE_AZ_EV_CODE_DRV_GEN_EV 7 +#define FSE_AZ_EV_CODE_GLOBAL_EV 6 +#define FSE_AZ_EV_CODE_DRIVER_EV 5 +#define FSE_AZ_EV_CODE_TX_EV 2 +#define FSE_AZ_EV_CODE_RX_EV 0 +#define FSF_AZ_EV_DATA_LBN 0 +#define FSF_AZ_EV_DATA_WIDTH 60 + +/* GLOBAL_EV */ +#define FSF_BB_GLB_EV_RX_RECOVERY_LBN 12 +#define FSF_BB_GLB_EV_RX_RECOVERY_WIDTH 1 +#define FSF_AA_GLB_EV_RX_RECOVERY_LBN 11 +#define FSF_AA_GLB_EV_RX_RECOVERY_WIDTH 1 +#define FSF_BB_GLB_EV_XG_MGT_INTR_LBN 11 +#define FSF_BB_GLB_EV_XG_MGT_INTR_WIDTH 1 +#define FSF_AB_GLB_EV_XFP_PHY0_INTR_LBN 10 +#define FSF_AB_GLB_EV_XFP_PHY0_INTR_WIDTH 1 +#define FSF_AB_GLB_EV_XG_PHY0_INTR_LBN 9 +#define FSF_AB_GLB_EV_XG_PHY0_INTR_WIDTH 1 +#define FSF_AB_GLB_EV_G_PHY0_INTR_LBN 7 +#define FSF_AB_GLB_EV_G_PHY0_INTR_WIDTH 1 + +/* LEGACY_INT_VEC */ +#define FSF_AZ_NET_IVEC_FATAL_INT_LBN 64 +#define FSF_AZ_NET_IVEC_FATAL_INT_WIDTH 1 +#define FSF_AZ_NET_IVEC_INT_Q_LBN 40 +#define FSF_AZ_NET_IVEC_INT_Q_WIDTH 4 +#define FSF_AZ_NET_IVEC_INT_FLAG_LBN 32 +#define FSF_AZ_NET_IVEC_INT_FLAG_WIDTH 1 +#define FSF_AZ_NET_IVEC_EVQ_FIFO_HF_LBN 1 +#define FSF_AZ_NET_IVEC_EVQ_FIFO_HF_WIDTH 1 +#define FSF_AZ_NET_IVEC_EVQ_FIFO_AF_LBN 0 +#define FSF_AZ_NET_IVEC_EVQ_FIFO_AF_WIDTH 1 + +/* MC_XGMAC_FLTR_RULE_DEF */ +#define FSF_CZ_MC_XFRC_MODE_LBN 416 +#define FSF_CZ_MC_XFRC_MODE_WIDTH 1 +#define FSE_CZ_MC_XFRC_MODE_LAYERED 1 +#define FSE_CZ_MC_XFRC_MODE_SIMPLE 0 +#define FSF_CZ_MC_XFRC_HASH_LBN 384 +#define FSF_CZ_MC_XFRC_HASH_WIDTH 32 +#define FSF_CZ_MC_XFRC_LAYER4_BYTE_MASK_LBN 256 +#define FSF_CZ_MC_XFRC_LAYER4_BYTE_MASK_WIDTH 128 +#define FSF_CZ_MC_XFRC_LAYER3_BYTE_MASK_LBN 128 +#define FSF_CZ_MC_XFRC_LAYER3_BYTE_MASK_WIDTH 128 +#define FSF_CZ_MC_XFRC_LAYER2_OR_SIMPLE_BYTE_MASK_LBN 0 +#define FSF_CZ_MC_XFRC_LAYER2_OR_SIMPLE_BYTE_MASK_WIDTH 128 + +/* RX_EV */ +#define FSF_CZ_RX_EV_PKT_NOT_PARSED_LBN 58 +#define FSF_CZ_RX_EV_PKT_NOT_PARSED_WIDTH 1 +#define FSF_CZ_RX_EV_IPV6_PKT_LBN 57 +#define FSF_CZ_RX_EV_IPV6_PKT_WIDTH 1 +#define FSF_AZ_RX_EV_PKT_OK_LBN 56 +#define FSF_AZ_RX_EV_PKT_OK_WIDTH 1 +#define FSF_AZ_RX_EV_PAUSE_FRM_ERR_LBN 55 +#define FSF_AZ_RX_EV_PAUSE_FRM_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_BUF_OWNER_ID_ERR_LBN 54 +#define FSF_AZ_RX_EV_BUF_OWNER_ID_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_IP_FRAG_ERR_LBN 53 +#define FSF_AZ_RX_EV_IP_FRAG_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR_LBN 52 +#define FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR_LBN 51 +#define FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_ETH_CRC_ERR_LBN 50 +#define FSF_AZ_RX_EV_ETH_CRC_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_FRM_TRUNC_LBN 49 +#define FSF_AZ_RX_EV_FRM_TRUNC_WIDTH 1 +#define FSF_AA_RX_EV_DRIB_NIB_LBN 49 +#define FSF_AA_RX_EV_DRIB_NIB_WIDTH 1 +#define FSF_AZ_RX_EV_TOBE_DISC_LBN 47 +#define FSF_AZ_RX_EV_TOBE_DISC_WIDTH 1 +#define FSF_AZ_RX_EV_PKT_TYPE_LBN 44 +#define FSF_AZ_RX_EV_PKT_TYPE_WIDTH 3 +#define FSE_AZ_RX_EV_PKT_TYPE_VLAN_JUMBO 5 +#define FSE_AZ_RX_EV_PKT_TYPE_VLAN_LLC 4 +#define FSE_AZ_RX_EV_PKT_TYPE_VLAN 3 +#define FSE_AZ_RX_EV_PKT_TYPE_JUMBO 2 +#define FSE_AZ_RX_EV_PKT_TYPE_LLC 1 +#define FSE_AZ_RX_EV_PKT_TYPE_ETH 0 +#define FSF_AZ_RX_EV_HDR_TYPE_LBN 42 +#define FSF_AZ_RX_EV_HDR_TYPE_WIDTH 2 +#define FSE_AZ_RX_EV_HDR_TYPE_OTHER 3 +#define FSE_AB_RX_EV_HDR_TYPE_IPV4_OTHER 2 +#define FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_OTHER 2 +#define FSE_AB_RX_EV_HDR_TYPE_IPV4_UDP 1 +#define FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP 1 +#define FSE_AB_RX_EV_HDR_TYPE_IPV4_TCP 0 +#define FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP 0 +#define FSF_AZ_RX_EV_DESC_Q_EMPTY_LBN 41 +#define FSF_AZ_RX_EV_DESC_Q_EMPTY_WIDTH 1 +#define FSF_AZ_RX_EV_MCAST_HASH_MATCH_LBN 40 +#define FSF_AZ_RX_EV_MCAST_HASH_MATCH_WIDTH 1 +#define FSF_AZ_RX_EV_MCAST_PKT_LBN 39 +#define FSF_AZ_RX_EV_MCAST_PKT_WIDTH 1 +#define FSF_AA_RX_EV_RECOVERY_FLAG_LBN 37 +#define FSF_AA_RX_EV_RECOVERY_FLAG_WIDTH 1 +#define FSF_AZ_RX_EV_Q_LABEL_LBN 32 +#define FSF_AZ_RX_EV_Q_LABEL_WIDTH 5 +#define FSF_AZ_RX_EV_JUMBO_CONT_LBN 31 +#define FSF_AZ_RX_EV_JUMBO_CONT_WIDTH 1 +#define FSF_AZ_RX_EV_PORT_LBN 30 +#define FSF_AZ_RX_EV_PORT_WIDTH 1 +#define FSF_AZ_RX_EV_BYTE_CNT_LBN 16 +#define FSF_AZ_RX_EV_BYTE_CNT_WIDTH 14 +#define FSF_AZ_RX_EV_SOP_LBN 15 +#define FSF_AZ_RX_EV_SOP_WIDTH 1 +#define FSF_AZ_RX_EV_ISCSI_PKT_OK_LBN 14 +#define FSF_AZ_RX_EV_ISCSI_PKT_OK_WIDTH 1 +#define FSF_AZ_RX_EV_ISCSI_DDIG_ERR_LBN 13 +#define FSF_AZ_RX_EV_ISCSI_DDIG_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_ISCSI_HDIG_ERR_LBN 12 +#define FSF_AZ_RX_EV_ISCSI_HDIG_ERR_WIDTH 1 +#define FSF_AZ_RX_EV_DESC_PTR_LBN 0 +#define FSF_AZ_RX_EV_DESC_PTR_WIDTH 12 + +/* RX_KER_DESC */ +#define FSF_AZ_RX_KER_BUF_SIZE_LBN 48 +#define FSF_AZ_RX_KER_BUF_SIZE_WIDTH 14 +#define FSF_AZ_RX_KER_BUF_REGION_LBN 46 +#define FSF_AZ_RX_KER_BUF_REGION_WIDTH 2 +#define FSF_AZ_RX_KER_BUF_ADDR_LBN 0 +#define FSF_AZ_RX_KER_BUF_ADDR_WIDTH 46 + +/* RX_USER_DESC */ +#define FSF_AZ_RX_USER_2BYTE_OFFSET_LBN 20 +#define FSF_AZ_RX_USER_2BYTE_OFFSET_WIDTH 12 +#define FSF_AZ_RX_USER_BUF_ID_LBN 0 +#define FSF_AZ_RX_USER_BUF_ID_WIDTH 20 + +/* TX_EV */ +#define FSF_AZ_TX_EV_PKT_ERR_LBN 38 +#define FSF_AZ_TX_EV_PKT_ERR_WIDTH 1 +#define FSF_AZ_TX_EV_PKT_TOO_BIG_LBN 37 +#define FSF_AZ_TX_EV_PKT_TOO_BIG_WIDTH 1 +#define FSF_AZ_TX_EV_Q_LABEL_LBN 32 +#define FSF_AZ_TX_EV_Q_LABEL_WIDTH 5 +#define FSF_AZ_TX_EV_PORT_LBN 16 +#define FSF_AZ_TX_EV_PORT_WIDTH 1 +#define FSF_AZ_TX_EV_WQ_FF_FULL_LBN 15 +#define FSF_AZ_TX_EV_WQ_FF_FULL_WIDTH 1 +#define FSF_AZ_TX_EV_BUF_OWNER_ID_ERR_LBN 14 +#define FSF_AZ_TX_EV_BUF_OWNER_ID_ERR_WIDTH 1 +#define FSF_AZ_TX_EV_COMP_LBN 12 +#define FSF_AZ_TX_EV_COMP_WIDTH 1 +#define FSF_AZ_TX_EV_DESC_PTR_LBN 0 +#define FSF_AZ_TX_EV_DESC_PTR_WIDTH 12 + +/* TX_KER_DESC */ +#define FSF_AZ_TX_KER_CONT_LBN 62 +#define FSF_AZ_TX_KER_CONT_WIDTH 1 +#define FSF_AZ_TX_KER_BYTE_COUNT_LBN 48 +#define FSF_AZ_TX_KER_BYTE_COUNT_WIDTH 14 +#define FSF_AZ_TX_KER_BUF_REGION_LBN 46 +#define FSF_AZ_TX_KER_BUF_REGION_WIDTH 2 +#define FSF_AZ_TX_KER_BUF_ADDR_LBN 0 +#define FSF_AZ_TX_KER_BUF_ADDR_WIDTH 46 + +/* TX_USER_DESC */ +#define FSF_AZ_TX_USER_SW_EV_EN_LBN 48 +#define FSF_AZ_TX_USER_SW_EV_EN_WIDTH 1 +#define FSF_AZ_TX_USER_CONT_LBN 46 +#define FSF_AZ_TX_USER_CONT_WIDTH 1 +#define FSF_AZ_TX_USER_BYTE_CNT_LBN 33 +#define FSF_AZ_TX_USER_BYTE_CNT_WIDTH 13 +#define FSF_AZ_TX_USER_BUF_ID_LBN 13 +#define FSF_AZ_TX_USER_BUF_ID_WIDTH 20 +#define FSF_AZ_TX_USER_BYTE_OFS_LBN 0 +#define FSF_AZ_TX_USER_BYTE_OFS_WIDTH 13 + +/* USER_EV */ +#define FSF_CZ_USER_QID_LBN 32 +#define FSF_CZ_USER_QID_WIDTH 10 +#define FSF_CZ_USER_EV_REG_VALUE_LBN 0 +#define FSF_CZ_USER_EV_REG_VALUE_WIDTH 32 + +/************************************************************************** + * + * Falcon B0 PCIe core indirect registers + * + ************************************************************************** + */ + +#define FPCR_BB_PCIE_DEVICE_CTRL_STAT 0x68 + +#define FPCR_BB_PCIE_LINK_CTRL_STAT 0x70 + +#define FPCR_BB_ACK_RPL_TIMER 0x700 +#define FPCRF_BB_ACK_TL_LBN 0 +#define FPCRF_BB_ACK_TL_WIDTH 16 +#define FPCRF_BB_RPL_TL_LBN 16 +#define FPCRF_BB_RPL_TL_WIDTH 16 + +#define FPCR_BB_ACK_FREQ 0x70C +#define FPCRF_BB_ACK_FREQ_LBN 0 +#define FPCRF_BB_ACK_FREQ_WIDTH 7 + +/************************************************************************** + * + * Pseudo-registers and fields + * + ************************************************************************** + */ + +/* Interrupt acknowledge work-around register (A0/A1 only) */ +#define FR_AA_WORK_AROUND_BROKEN_PCI_READS 0x0070 + +/* EE_SPI_HCMD_REG: SPI host command register */ +/* Values for the EE_SPI_HCMD_SF_SEL register field */ +#define FFE_AB_SPI_DEVICE_EEPROM 0 +#define FFE_AB_SPI_DEVICE_FLASH 1 + +/* NIC_STAT_REG: NIC status register */ +#define FRF_AB_STRAP_10G_LBN 2 +#define FRF_AB_STRAP_10G_WIDTH 1 +#define FRF_AA_STRAP_PCIE_LBN 0 +#define FRF_AA_STRAP_PCIE_WIDTH 1 + +/* FATAL_INTR_REG_KER: Fatal interrupt register for Kernel */ +#define FRF_AZ_FATAL_INTR_LBN 0 +#define FRF_AZ_FATAL_INTR_WIDTH 12 + +/* SRM_CFG_REG: SRAM configuration register */ +/* We treat the number of SRAM banks and bank size as a single field */ +#define FRF_AZ_SRM_NB_SZ_LBN FRF_AZ_SRM_BANK_SIZE_LBN +#define FRF_AZ_SRM_NB_SZ_WIDTH \ + (FRF_AZ_SRM_BANK_SIZE_WIDTH + FRF_AZ_SRM_NUM_BANK_WIDTH) +#define FFE_AB_SRM_NB1_SZ2M 0 +#define FFE_AB_SRM_NB1_SZ4M 1 +#define FFE_AB_SRM_NB1_SZ8M 2 +#define FFE_AB_SRM_NB_SZ_DEF 3 +#define FFE_AB_SRM_NB2_SZ4M 4 +#define FFE_AB_SRM_NB2_SZ8M 5 +#define FFE_AB_SRM_NB2_SZ16M 6 +#define FFE_AB_SRM_NB_SZ_RES 7 + +/* RX_DESC_UPD_REGP0: Receive descriptor update register. */ +/* We write just the last dword of these registers */ +#define FR_AZ_RX_DESC_UPD_DWORD_P0 \ + (BUILD_BUG_ON_ZERO(FR_AA_RX_DESC_UPD_KER != FR_BZ_RX_DESC_UPD_P0) + \ + FR_BZ_RX_DESC_UPD_P0 + 3 * 4) +#define FRF_AZ_RX_DESC_WPTR_DWORD_LBN (FRF_AZ_RX_DESC_WPTR_LBN - 3 * 32) +#define FRF_AZ_RX_DESC_WPTR_DWORD_WIDTH FRF_AZ_RX_DESC_WPTR_WIDTH + +/* TX_DESC_UPD_REGP0: Transmit descriptor update register. */ +#define FR_AZ_TX_DESC_UPD_DWORD_P0 \ + (BUILD_BUG_ON_ZERO(FR_AA_TX_DESC_UPD_KER != FR_BZ_TX_DESC_UPD_P0) + \ + FR_BZ_TX_DESC_UPD_P0 + 3 * 4) +#define FRF_AZ_TX_DESC_WPTR_DWORD_LBN (FRF_AZ_TX_DESC_WPTR_LBN - 3 * 32) +#define FRF_AZ_TX_DESC_WPTR_DWORD_WIDTH FRF_AZ_TX_DESC_WPTR_WIDTH + +/* GMF_CFG4_REG: GMAC FIFO configuration register 4 */ +#define FRF_AB_GMF_HSTFLTRFRM_PAUSE_LBN 12 +#define FRF_AB_GMF_HSTFLTRFRM_PAUSE_WIDTH 1 + +/* GMF_CFG5_REG: GMAC FIFO configuration register 5 */ +#define FRF_AB_GMF_HSTFLTRFRMDC_PAUSE_LBN 12 +#define FRF_AB_GMF_HSTFLTRFRMDC_PAUSE_WIDTH 1 + +/* XM_TX_PARAM_REG: XGMAC transmit parameter register */ +#define FRF_AB_XM_MAX_TX_FRM_SIZE_LBN FRF_AB_XM_MAX_TX_FRM_SIZE_LO_LBN +#define FRF_AB_XM_MAX_TX_FRM_SIZE_WIDTH (FRF_AB_XM_MAX_TX_FRM_SIZE_HI_WIDTH + \ + FRF_AB_XM_MAX_TX_FRM_SIZE_LO_WIDTH) + +/* XM_RX_PARAM_REG: XGMAC receive parameter register */ +#define FRF_AB_XM_MAX_RX_FRM_SIZE_LBN FRF_AB_XM_MAX_RX_FRM_SIZE_LO_LBN +#define FRF_AB_XM_MAX_RX_FRM_SIZE_WIDTH (FRF_AB_XM_MAX_RX_FRM_SIZE_HI_WIDTH + \ + FRF_AB_XM_MAX_RX_FRM_SIZE_LO_WIDTH) + +/* XX_TXDRV_CTL_REG: XAUI SerDes transmit drive control register */ +/* Default values */ +#define FFE_AB_XX_TXDRV_DEQ_DEF 0xe /* deq=.6 */ +#define FFE_AB_XX_TXDRV_DTX_DEF 0x5 /* 1.25 */ +#define FFE_AB_XX_SD_CTL_DRV_DEF 0 /* 20mA */ + +/* XX_CORE_STAT_REG: XAUI XGXS core status register */ +/* XGXS all-lanes status fields */ +#define FRF_AB_XX_SYNC_STAT_LBN FRF_AB_XX_SYNC_STAT0_LBN +#define FRF_AB_XX_SYNC_STAT_WIDTH 4 +#define FRF_AB_XX_COMMA_DET_LBN FRF_AB_XX_COMMA_DET_CH0_LBN +#define FRF_AB_XX_COMMA_DET_WIDTH 4 +#define FRF_AB_XX_CHAR_ERR_LBN FRF_AB_XX_CHAR_ERR_CH0_LBN +#define FRF_AB_XX_CHAR_ERR_WIDTH 4 +#define FRF_AB_XX_DISPERR_LBN FRF_AB_XX_DISPERR_CH0_LBN +#define FRF_AB_XX_DISPERR_WIDTH 4 +#define FFE_AB_XX_STAT_ALL_LANES 0xf +#define FRF_AB_XX_FORCE_SIG_LBN FRF_AB_XX_FORCE_SIG0_VAL_LBN +#define FRF_AB_XX_FORCE_SIG_WIDTH 8 +#define FFE_AB_XX_FORCE_SIG_ALL_LANES 0xff + +/* DRIVER_EV */ +/* Sub-fields of an RX flush completion event */ +#define FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL_LBN 12 +#define FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL_WIDTH 1 +#define FSF_AZ_DRIVER_EV_RX_DESCQ_ID_LBN 0 +#define FSF_AZ_DRIVER_EV_RX_DESCQ_ID_WIDTH 12 + +/* EVENT_ENTRY */ +/* Magic number field for event test */ +#define FSF_AZ_DRV_GEN_EV_MAGIC_LBN 0 +#define FSF_AZ_DRV_GEN_EV_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 + +/************************************************************************** + * + * Falcon non-volatile configuration + * + ************************************************************************** + */ + +/* Board configuration v2 (v1 is obsolete; later versions are compatible) */ +struct falcon_nvconfig_board_v2 { + __le16 nports; + u8 port0_phy_addr; + u8 port0_phy_type; + u8 port1_phy_addr; + u8 port1_phy_type; + __le16 asic_sub_revision; + __le16 board_revision; +} __packed; + +/* Board configuration v3 extra information */ +struct falcon_nvconfig_board_v3 { + __le32 spi_device_type[2]; +} __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(EFX_WIDTH(field))) + +#define FALCON_NVCONFIG_OFFSET 0x300 + +#define FALCON_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 */ + __le16 board_magic_num; /* 0x3A0 */ + __le16 board_struct_ver; + __le16 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 */ +} __packed; + +#endif /* EFX_REGS_H */ diff -r aa133dee7792 drivers/net/sfc/rx.c --- a/drivers/net/sfc/rx.c +++ b/drivers/net/sfc/rx.c @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2005-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include @@ -31,15 +14,14 @@ #include #include #include +#include #include #include "net_driver.h" -#include "rx.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #include "selftest.h" #include "workarounds.h" - /* Number of RX descriptors pushed at once. */ #define EFX_RX_BATCH 8 @@ -79,7 +61,15 @@ * rx_alloc_method = (rx_alloc_level > RX_ALLOC_LEVEL_LRO ? * RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB) */ +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_GRO) || defined(EFX_HAVE_DEV_DISABLE_LRO) +static int rx_alloc_method = RX_ALLOC_METHOD_AUTO; +#else +/* LRO using SKB chaining will cause a BUG() if bridging is used. This was + * fixed by GRO, and worked around by dev_disable_lro(). Change the default + * to avoid the BUG(), but allow the user to override this. + */ static int rx_alloc_method = RX_ALLOC_METHOD_PAGE; +#endif #define RX_ALLOC_LEVEL_LRO 0x2000 #define RX_ALLOC_LEVEL_MAX 0x3000 @@ -104,14 +94,17 @@ */ #define EFX_RXD_HEAD_ROOM 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)) +static inline unsigned int efx_rx_buf_offset(struct efx_rx_buffer *buf) +{ + /* Offset is always within one page, so we don't need to consider + * the page order. + */ + return (__force unsigned long) buf->data & (PAGE_SIZE - 1); +} +static inline unsigned int efx_rx_buf_size(struct efx_nic *efx) +{ + return PAGE_SIZE << efx->rx_buffer_order; +} /** @@ -124,8 +117,8 @@ * 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) +static 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; @@ -139,16 +132,13 @@ skb_reserve(rx_buf->skb, NET_IP_ALIGN); rx_buf->len = skb_len - NET_IP_ALIGN; rx_buf->data = (char *)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 */ + if (unlikely(pci_dma_mapping_error(efx->pci_dev, rx_buf->dma_addr))) { dev_kfree_skb_any(rx_buf->skb); rx_buf->skb = NULL; return -EIO; @@ -167,8 +157,8 @@ * 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) +static 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; @@ -186,12 +176,11 @@ 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), + 0, efx_rx_buf_size(efx), PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(dma_addr))) { + if (unlikely(pci_dma_mapping_error(efx->pci_dev, dma_addr))) { __free_pages(rx_buf->page, efx->rx_buffer_order); rx_buf->page = NULL; return -EIO; @@ -199,14 +188,14 @@ rx_queue->buf_page = rx_buf->page; rx_queue->buf_dma_addr = dma_addr; - rx_queue->buf_data = ((char *) page_address(rx_buf->page) + + rx_queue->buf_data = (page_address(rx_buf->page) + EFX_PAGE_IP_ALIGN); } - offset = RX_DATA_OFFSET(rx_queue->buf_data); rx_buf->len = bytes; + rx_buf->data = rx_queue->buf_data; + offset = efx_rx_buf_offset(rx_buf); 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) { @@ -214,7 +203,7 @@ rx_queue->buf_data += ((bytes + 0x1ff) & ~0x1ff); offset += ((bytes + 0x1ff) & ~0x1ff); - space = RX_PAGE_SIZE(efx) - offset; + space = efx_rx_buf_size(efx) - offset; if (space >= bytes) { /* Refs dropped on kernel releasing each skb */ get_page(rx_queue->buf_page); @@ -235,8 +224,8 @@ * 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) +static int efx_init_rx_buffer(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *new_rx_buf) { int rc = 0; @@ -256,14 +245,15 @@ return rc; } -static inline void efx_unmap_rx_buffer(struct efx_nic *efx, - struct efx_rx_buffer *rx_buf) +static 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); + efx_rx_buf_size(efx), + PCI_DMA_FROMDEVICE); rx_buf->unmap_addr = 0; } } else if (likely(rx_buf->skb)) { @@ -272,8 +262,8 @@ } } -static inline void efx_free_rx_buffer(struct efx_nic *efx, - struct efx_rx_buffer *rx_buf) +static 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); @@ -284,13 +274,10 @@ } } -static inline void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue, - struct efx_rx_buffer *rx_buf) +static 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); } @@ -314,17 +301,17 @@ * 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); + EFX_BUG_ON_PARANOID(fill_level > EFX_RXQ_SIZE); /* 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 (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 @@ -336,8 +323,7 @@ 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); + EFX_BUG_ON_PARANOID(fill_level > EFX_RXQ_SIZE); space = rx_queue->fast_fill_limit - fill_level; if (space < EFX_RX_BATCH) goto out_unlock; @@ -349,8 +335,7 @@ do { for (i = 0; i < EFX_RX_BATCH; ++i) { - index = (rx_queue->added_count & - rx_queue->efx->type->rxd_ring_mask); + index = rx_queue->added_count & EFX_RXQ_MASK; rx_buf = efx_rx_buffer(rx_queue, index); rc = efx_init_rx_buffer(rx_queue, rx_buf); if (unlikely(rc)) @@ -365,7 +350,7 @@ out: /* Send write pointer to card. */ - falcon_notify_rx_desc(rx_queue); + efx_nic_notify_rx_desc(rx_queue); /* If the fast fill is running inside from the refill tasklet, then * for SMP systems it may be running on a different CPU to @@ -375,7 +360,6 @@ goto retry; out_unlock: - /* Release RX add lock */ spin_unlock_bh(&rx_queue->add_lock); return rc; @@ -408,7 +392,11 @@ struct efx_rx_queue *rx_queue; int rc; +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_NEED_WORK_API_WRAPPERS) + rx_queue = container_of(data, struct efx_rx_queue, work.work); +#else rx_queue = container_of(data, struct efx_rx_queue, work); +#endif if (unlikely(!rx_queue->channel->enabled)) return; @@ -424,10 +412,10 @@ efx_schedule_slow_fill(rx_queue, 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) +static void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue, + struct efx_rx_buffer *rx_buf, + int len, bool *discard, + bool *leak_packet) { struct efx_nic *efx = rx_queue->efx; unsigned max_len = rx_buf->len - efx->type->rx_buffer_padding; @@ -438,7 +426,7 @@ /* The packet must be discarded, but this is only a fatal error * if the caller indicated it was */ - *discard = 1; + *discard = true; if ((len > rx_buf->len) && EFX_WORKAROUND_8071(efx)) { EFX_ERR_RL(efx, " RX queue %d seriously overlength " @@ -459,10 +447,74 @@ rx_queue->channel->n_rx_overlength++; } +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_GRO) + +/* Pass a received packet up through the generic LRO stack + * + * Handles driverlink veto, and passes the fragment up via + * the appropriate LRO method + */ +static void efx_rx_packet_lro(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, + bool checksummed) +{ + struct efx_nic *efx = channel->efx; + struct napi_struct *napi = &channel->napi_str; + 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 sk_buff *skb = napi_get_frags(napi); + + if (!skb) { + put_page(rx_buf->page); + goto out; + } + + skb_shinfo(skb)->frags[0].page = rx_buf->page; + skb_shinfo(skb)->frags[0].page_offset = + efx_rx_buf_offset(rx_buf); + skb_shinfo(skb)->frags[0].size = rx_buf->len; + skb_shinfo(skb)->nr_frags = 1; + + skb->len = rx_buf->len; + skb->data_len = rx_buf->len; + skb->truesize += rx_buf->len; + skb->ip_summed = + checksummed ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE; + + napi_gro_frags(napi); + +out: + EFX_BUG_ON_PARANOID(rx_buf->skb); + rx_buf->page = NULL; + } else { + EFX_BUG_ON_PARANOID(!rx_buf->skb); + EFX_BUG_ON_PARANOID(!checksummed); + + napi_gro_receive(napi, rx_buf->skb); + rx_buf->skb = NULL; + } +} + +#else /* EFX_USE_KCOMPAT && !EFX_USE_GRO */ + /* 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) +static struct sk_buff *efx_rx_mk_skb(struct efx_rx_buffer *rx_buf, + struct efx_nic *efx, + int hdr_len) { struct sk_buff *skb; @@ -488,7 +540,7 @@ 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->page_offset = efx_rx_buf_offset(rx_buf) + hdr_len; frag->size = skb->len - hdr_len; skb_shinfo(skb)->nr_frags = 1; skb->data_len = frag->size; @@ -506,13 +558,20 @@ return skb; } +#endif /* !EFX_USE_KCOMPAT || EFX_USE_GRO */ + +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) void fastcall efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, unsigned int len, - int checksummed, int discard) + bool checksummed, bool discard) +#else +void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, + unsigned int len, bool checksummed, bool discard) +#endif { struct efx_nic *efx = rx_queue->efx; struct efx_rx_buffer *rx_buf; - int leak_packet = 0; + bool leak_packet = false; rx_buf = efx_rx_buffer(rx_queue, index); EFX_BUG_ON_PARANOID(!rx_buf->data); @@ -568,14 +627,62 @@ rx_queue->channel->rx_pkt_csummed = checksummed; } +static void efx_rx_deliver(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, int checksummed) +{ + struct efx_nic *efx = channel->efx; + struct sk_buff *skb; + enum efx_veto veto; + + /* 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); + return; + } + + /* We now own the SKB */ + skb = rx_buf->skb; + rx_buf->skb = NULL; + +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_GRO) + /* 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); + return; + } + } +#endif + + EFX_BUG_ON_PARANOID(rx_buf->page); + EFX_BUG_ON_PARANOID(rx_buf->skb); + EFX_BUG_ON_PARANOID(!skb); + + /* Set the SKB flags */ +#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_GRO) + if (unlikely(!checksummed)) + /* fall through to next statement, else skip */ +#endif + 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; +} + /* 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_rx_buffer *rx_buf, bool checksummed) { struct efx_nic *efx = channel->efx; - enum efx_veto veto; - struct sk_buff *skb; - int lro = efx->lro_enabled; /* If we're in loopback test, then pass the packet directly to the * loopback layer, and free the rx_buf here @@ -583,14 +690,12 @@ 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; + return; } 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 @@ -599,64 +704,15 @@ 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)) { - if (efx_ssr(&channel->ssr, rx_buf)) { - channel->rx_alloc_level += RX_ALLOC_FACTOR_LRO; - 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 */ +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_GRO) + if (likely(checksummed || rx_buf->page)) + efx_rx_packet_lro(channel, rx_buf, checksummed); + else +#endif + efx_rx_deliver(channel, rx_buf, checksummed); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_NET_DEVICE_LAST_RX) efx->net_dev->last_rx = jiffies; +#endif } void efx_rx_strategy(struct efx_channel *channel) @@ -664,7 +720,11 @@ enum efx_rx_alloc_method method = rx_alloc_method; /* Only makes sense to use page based allocation if LRO is enabled */ - if (!(channel->efx->lro_enabled)) { +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_GRO) + if (!(channel->efx->net_dev->features & NETIF_F_GRO)) { +#else + if (1) { +#endif method = RX_ALLOC_METHOD_SKB; } else if (method == RX_ALLOC_METHOD_AUTO) { /* Constrain the rx_alloc_level */ @@ -691,32 +751,21 @@ 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); + rxq_size = EFX_RXQ_SIZE * sizeof(*rx_queue->buffer); rx_queue->buffer = kzalloc(rxq_size, GFP_KERNEL); - if (!rx_queue->buffer) { - rc = -ENOMEM; - goto fail1; + if (!rx_queue->buffer) + return -ENOMEM; + + rc = efx_nic_probe_rx(rx_queue); + if (rc) { + kfree(rx_queue->buffer); + rx_queue->buffer = NULL; } - - 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) +void efx_init_rx_queue(struct efx_rx_queue *rx_queue) { - struct efx_nic *efx = rx_queue->efx; unsigned int max_fill, trigger, limit; EFX_LOG(rx_queue->efx, "initialising RX queue %d\n", rx_queue->queue); @@ -729,7 +778,7 @@ rx_queue->min_overfill = -1U; /* Initialise limit fields */ - max_fill = efx->type->rxd_ring_mask + 1 - EFX_RXD_HEAD_ROOM; + max_fill = EFX_RXQ_SIZE - EFX_RXD_HEAD_ROOM; trigger = max_fill * min(rx_refill_threshold, 100U) / 100U; limit = max_fill * min(rx_refill_limit, 100U) / 100U; @@ -738,7 +787,7 @@ rx_queue->fast_fill_limit = limit; /* Set up RX descriptor ring */ - return falcon_init_rx(rx_queue); + efx_nic_init_rx(rx_queue); } void efx_fini_rx_queue(struct efx_rx_queue *rx_queue) @@ -748,12 +797,11 @@ EFX_LOG(rx_queue->efx, "shutting down RX queue %d\n", rx_queue->queue); - /* Flush RX queue and remove descriptor ring */ - falcon_fini_rx(rx_queue); + efx_nic_fini_rx(rx_queue); /* Release RX buffers NB start at index 0 not current HW ptr */ if (rx_queue->buffer) { - for (i = 0; i <= rx_queue->efx->type->rxd_ring_mask; i++) { + for (i = 0; i <= EFX_RXQ_MASK; i++) { rx_buf = efx_rx_buffer(rx_queue, i); efx_fini_rx_buffer(rx_queue, rx_buf); } @@ -762,7 +810,8 @@ /* 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); + efx_rx_buf_size(rx_queue->efx), + PCI_DMA_FROMDEVICE); __free_pages(rx_queue->buf_page, rx_queue->efx->rx_buffer_order); rx_queue->buf_page = NULL; @@ -773,17 +822,10 @@ { EFX_LOG(rx_queue->efx, "destroying RX queue %d\n", rx_queue->queue); - falcon_remove_rx(rx_queue); + efx_nic_remove_rx(rx_queue); kfree(rx_queue->buffer); rx_queue->buffer = NULL; - rx_queue->used = 0; -} - -/* Flush LRO/SSR state for the given channel */ -void efx_flush_lro(struct efx_channel *channel) -{ - efx_ssr_end_of_burst(&channel->ssr); } @@ -794,441 +836,3 @@ MODULE_PARM_DESC(rx_refill_threshold, "RX descriptor ring fast/slow fill threshold (%)"); - - -/* Size of the LRO hash table. Must be a power of 2. A larger table - * means we can accelerate a larger number of streams. - */ -static unsigned lro_table_size = 128; -module_param(lro_table_size, uint, 0644); -MODULE_PARM_DESC(lro_table_size, - "Size of the LRO hash table. Must be a power of 2"); - -/* Maximum length of a hash chain. If chains get too long then the lookup - * time increases and may exceed the benefit of LRO. - */ -static unsigned lro_chain_max = 20; -module_param(lro_chain_max, uint, 0644); -MODULE_PARM_DESC(lro_chain_max, - "Maximum length of chains in the LRO hash table"); - - -/* Maximum time (in jiffies) that a connection can be idle before it's LRO - * state is discarded. - */ -static unsigned lro_idle_jiffies = HZ / 10 + 1; /* 100ms */ -module_param(lro_idle_jiffies, uint, 0644); -MODULE_PARM_DESC(lro_idle_jiffies, "Time (in jiffies) after which an" - " idle connection's LRO state is discarded"); - - -/* Number of packets with payload that must arrive in-order before a - * connection is eligible for LRO. The idea is we should avoid coalescing - * segments when the sender is in slow-start because reducing the ACK rate - * can damage performance. - */ -static unsigned lro_slow_start_packets = 20; -module_param(lro_slow_start_packets, uint, 0644); -MODULE_PARM_DESC(lro_slow_start_packets, "Number of packets that must " - "pass in-order before starting LRO."); - - -int efx_ssr_init(struct efx_ssr_state *st, struct efx_nic *efx) -{ - unsigned i; - st->conns_mask = lro_table_size - 1; - if ((st->conns_mask + 1) & st->conns_mask) { - EFX_ERR(efx, "lro_table_size(=%u) must be a power of 2\n", - lro_table_size); - return -EINVAL; - } - st->efx = efx; - st->conns = kmalloc((st->conns_mask + 1) - * sizeof(st->conns[0]), GFP_KERNEL); - if (st->conns == NULL) - return -ENOMEM; - st->conns_n = kmalloc((st->conns_mask + 1) - * sizeof(st->conns_n[0]), GFP_KERNEL); - if (st->conns_n == NULL) { - kfree(st->conns); - st->conns = NULL; - return -ENOMEM; - } - for (i = 0; i <= st->conns_mask; ++i) { - INIT_LIST_HEAD(&st->conns[i]); - st->conns_n[i] = 0; - } - INIT_LIST_HEAD(&st->active_conns); - INIT_LIST_HEAD(&st->free_conns); - return 0; -} - -/* Drop the given connection, and add it to the free list */ -static inline void efx_ssr_drop(struct efx_ssr_state *st, - struct efx_ssr_conn *c, unsigned conn_hash) -{ - EFX_BUG_ON_PARANOID(c->skb); - EFX_BUG_ON_PARANOID(st->conns_n[conn_hash] <= 0); - --st->conns_n[conn_hash]; - list_del(&c->link); - list_add(&c->link, &st->free_conns); -} - -void efx_ssr_fini(struct efx_ssr_state *st) -{ - struct efx_ssr_conn *c; - unsigned i; - - /* Return cleanly if efx_ssr_init() has not been called. */ - if (st->conns == NULL) - return; - - EFX_BUG_ON_PARANOID(!list_empty(&st->active_conns)); - - for (i = 0; i <= st->conns_mask; ++i) - while (!list_empty(&st->conns[i])) { - c = list_entry(st->conns[i].prev, - struct efx_ssr_conn, link); - efx_ssr_drop(st, c, i); - } - - while (!list_empty(&st->free_conns)) { - c = list_entry(st->free_conns.prev, struct efx_ssr_conn, link); - list_del(&c->link); - EFX_BUG_ON_PARANOID(c->skb); - kfree(c); - } - - kfree(st->conns_n); - kfree(st->conns); - st->conns = NULL; -} - -/* Calc IP checksum and deliver to the OS */ -static void efx_ssr_deliver(struct efx_ssr_state *st, struct efx_ssr_conn *c) -{ - struct efx_nic *efx = st->efx; - int veto, len; - - EFX_BUG_ON_PARANOID(!c->skb); - - ++st->n_bursts; - - /* Finish off packet munging and recalculate IP header checksum. */ - c->iph->tot_len = htons(c->iph->tot_len); - c->iph->check = 0; - c->iph->check = ip_fast_csum((u8 *) c->iph, c->iph->ihl); - - len = c->skb->len + ((char *)c->iph - (char *)c->eh); - c->skb->truesize = len + sizeof(struct sk_buff); - - c->th->window = c->th_last->window; - c->th->ack_seq = c->th_last->ack_seq; - if (c->th->doff == c->th_last->doff) { - /* Copy TCP options (take care to avoid going negative). */ - len = ((c->th->doff - 5) & 0xf) << 2u; - memcpy(c->th + 1, c->th_last + 1, len); - } - - /* Allow callback to veto the packet. */ - veto = EFX_DL_CALLBACK(efx, rx_packet, (char *)c->eh, len); - if (unlikely(veto)) { - EFX_LOG(efx, "RX vetoed by driverlink %s driver\n", - efx->dl_cb_dev.rx_packet->driver->name); - dev_kfree_skb_any(c->skb); - } else { - netif_receive_skb(c->skb); - } - - c->skb = NULL; - list_del_init(&c->active_link); -} - -/* Stop tracking connections that have gone idle in order to keep hash - * chains short. - */ -static void efx_ssr_purge_idle(struct efx_ssr_state *st, unsigned now) -{ - struct efx_ssr_conn *c; - unsigned i; - - EFX_BUG_ON_PARANOID(!list_empty(&st->active_conns)); - - st->last_purge_jiffies = now; - for (i = 0; i <= st->conns_mask; ++i) { - if (list_empty(&st->conns[i])) - continue; - - c = list_entry(st->conns[i].prev, struct efx_ssr_conn, link); - if (now - c->last_pkt_jiffies > lro_idle_jiffies) { - ++st->n_drop_idle; - efx_ssr_drop(st, c, i); - } - } -} - -/* Push held skbs down into network stack. - * Only called when active list is non-empty. - */ -void __efx_ssr_end_of_burst(struct efx_ssr_state *st) -{ - struct efx_ssr_conn *c; - unsigned j; - - EFX_BUG_ON_PARANOID(list_empty(&st->active_conns)); - - do { - c = list_entry(st->active_conns.next, struct efx_ssr_conn, - active_link); - EFX_BUG_ON_PARANOID(!c->skb); - efx_ssr_deliver(st, c); - } while (!list_empty(&st->active_conns)); - - j = jiffies; - if (unlikely(j != st->last_purge_jiffies)) - efx_ssr_purge_idle(st, j); -} - -/* Construct an skb Push held skbs down into network stack. - * Only called when active list is non-empty. - */ -static inline int -efx_ssr_merge(struct efx_ssr_state *st, struct efx_ssr_conn *c, - struct tcphdr *th, int data_length) -{ - /* Increase lengths appropriately */ - c->skb->len += data_length; - c->skb->data_len += data_length; - - /* - * Keep track of max MSS seen and store in - * gso_size for kernel to use - */ - if (data_length > skb_shinfo(c->skb)->gso_size) - skb_shinfo(c->skb)->gso_size = data_length; - - /* Update the connection state flags */ - c->iph->tot_len += data_length; - c->th->psh |= th->psh; - c->th_last = th; - ++st->n_merges; - - /* Pass packet up now if another segment could overflow the IP - * length. - */ - return (c->skb->len > 65536 - 9200); -} - -static inline void -efx_ssr_start(struct efx_ssr_state *st, struct efx_ssr_conn *c, - struct tcphdr *th, int data_length) -{ - /* Initialise gso_size appropriately */ - skb_shinfo(c->skb)->gso_size = data_length; - - /* Mangle header fields for later processing */ - c->iph->tot_len = ntohs(c->iph->tot_len); - - /* Move this connection the head of the active list */ - list_del(&c->active_link); - list_add(&c->active_link, &st->active_conns); -} - -static inline int -efx_ssr_conn_page(struct efx_ssr_state *st, struct efx_ssr_conn *c, - struct efx_rx_buffer *rx_buf, struct tcphdr *th, - int hdr_length, int data_length) -{ - if (likely(c->skb)) { - struct skb_frag_struct *frag; - frag = skb_shinfo(c->skb)->frags; - frag += skb_shinfo(c->skb)->nr_frags; - frag->page = rx_buf->page; - frag->page_offset = RX_BUF_OFFSET(rx_buf) + hdr_length; - frag->size = data_length; - ++skb_shinfo(c->skb)->nr_frags; - rx_buf->page = NULL; - - if (efx_ssr_merge(st, c, th, data_length) || - (skb_shinfo(c->skb)->nr_frags == MAX_SKB_FRAGS)) - efx_ssr_deliver(st, c); - - return 1; - } else { - c->skb = efx_rx_mk_skb(rx_buf, st->efx, hdr_length); - if (unlikely(c->skb == NULL)) - return 0; - - c->eh = eth_hdr(c->skb); - c->iph = (struct iphdr *)c->skb->data; - c->th = (struct tcphdr *)((u8 *) c->iph + c->iph->ihl * 4); - c->th_last = c->th; - - efx_ssr_start(st, c, th, data_length); - - return 1; - } -} - -static inline void -efx_ssr_conn_skb(struct efx_ssr_state *st, struct efx_ssr_conn *c, - struct efx_rx_buffer *rx_buf, struct ethhdr *eh, - struct iphdr *iph, struct tcphdr *th, int data_length) -{ - /* Transfer ownership of the rx_buf->skb to the LRO chain */ - struct sk_buff *skb = rx_buf->skb; - rx_buf->skb = NULL; - - if (likely(c->skb)) { - /* Remove the headers */ - skb_pull(skb, skb->len - data_length); - - /* Tack the new skb onto the head skb's frag_list. */ - EFX_BUG_ON_PARANOID(skb->next); - if (!skb_shinfo(c->skb)->frag_list) - skb_shinfo(c->skb)->frag_list = skb; - else - c->skb_tail->next = skb; - c->skb_tail = skb; - - if (efx_ssr_merge(st, c, th, data_length)) - efx_ssr_deliver(st, c); - } else { - c->skb = skb; - c->eh = eh; - c->iph = iph; - c->th = th; - c->th_last = th; - - efx_ssr_start(st, c, th, data_length); - } -} - -/* Process SKB and decide whether to dispatch it to the stack now or - * later. - */ -int efx_ssr(struct efx_ssr_state *st, struct efx_rx_buffer *rx_buf) -{ - - int eh_proto, data_length, hdr_length, dont_merge; - struct efx_ssr_conn *c; - struct ethhdr *eh; - struct iphdr *iph; - struct tcphdr *th; - unsigned th_seq, conn_hash, pkt_length; - - /* This does not handle VLAN code */ - /* Find the IP header. The ethernet header is always at rx_buf->data */ - eh = (struct ethhdr *)rx_buf->data; - if (rx_buf->page) { - eh_proto = eh->h_proto; - iph = (struct iphdr *)(eh + 1); - } else { - /* The skb head is at the IP header */ - eh_proto = rx_buf->skb->protocol; - iph = (struct iphdr *)rx_buf->skb->data; - } - - /* We're not interested if it isn't TCP over IPv4, or if fragged. */ - if ((eh_proto - htons(ETH_P_IP)) | - (iph->protocol - IPPROTO_TCP) | - (iph->frag_off & htons(IP_MF | IP_OFFSET))) - return 0; - - /* Get the TCP protocol */ - th = (struct tcphdr *)((u8 *) iph + iph->ihl * 4); - hdr_length = (u8 *) th + th->doff * 4 - (u8 *) eh; - /* Cope with padding after IP header */ - pkt_length = ntohs(iph->tot_len) + (u8 *)iph - (u8 *)eh; - rx_buf->len = min(pkt_length, rx_buf->len); - data_length = rx_buf->len - hdr_length; - th_seq = ntohl(th->seq); - dont_merge = ((data_length <= 0) - | th->urg | th->syn | th->rst | th->fin); - - /* Very cheap and crude hash. */ - conn_hash = (th->source ^ th->dest) & st->conns_mask; - - list_for_each_entry(c, &st->conns[conn_hash], link) { - if ((c->saddr - iph->saddr) | (c->daddr - iph->daddr) | - (c->source - th->source) | (c->dest - th->dest)) - continue; - - /* Re-insert at head of list to reduce lookup time. */ - list_del(&c->link); - list_add(&c->link, &st->conns[conn_hash]); - - if (unlikely(th_seq - c->next_seq)) { - /* Out-of-order, so start counting again. */ - if (c->skb) - efx_ssr_deliver(st, c); - c->n_in_order_pkts = 0; - c->next_seq = th_seq + data_length; - ++st->n_misorder; - return 0; - } - c->next_seq = th_seq + data_length; - c->last_pkt_jiffies = jiffies; - - if (c->n_in_order_pkts < lro_slow_start_packets) { - /* May be in slow-start, so don't merge. */ - ++st->n_slow_start; - ++c->n_in_order_pkts; - return 0; - } - - if (unlikely(dont_merge)) { - if (c->skb) - efx_ssr_deliver(st, c); - if (th->fin || th->rst) { - ++st->n_drop_closed; - efx_ssr_drop(st, c, conn_hash); - } - return 0; - } - - if (rx_buf->page) { - return efx_ssr_conn_page(st, c, rx_buf, th, hdr_length, - data_length); - } else { - efx_ssr_conn_skb(st, c, rx_buf, eh, iph, th, - data_length); - return 1; - } - } - - /* We're not yet tracking this connection. */ - if (dont_merge) - return 0; - - if (st->conns_n[conn_hash] >= lro_chain_max) { - ++st->n_too_many; - return 0; - } - - if (!list_empty(&st->free_conns)) { - c = list_entry(st->free_conns.next, struct efx_ssr_conn, link); - list_del(&c->link); - } else { - c = kmalloc(sizeof(*c), GFP_ATOMIC); - if (c == NULL) - return 0; - c->skb = NULL; - INIT_LIST_HEAD(&c->active_link); - } - - /* Create the connection tracking data */ - ++st->conns_n[conn_hash]; - list_add(&c->link, &st->conns[conn_hash]); - c->saddr = iph->saddr; - c->daddr = iph->daddr; - c->source = th->source; - c->dest = th->dest; - c->next_seq = th_seq + data_length; - c->n_in_order_pkts = 0; - EFX_BUG_ON_PARANOID(c->skb); - ++st->n_new_stream; - return 0; -} - - diff -r aa133dee7792 drivers/net/sfc/rx.h --- a/drivers/net/sfc/rx.h +++ /dev/null @@ -1,61 +0,0 @@ -/**************************************************************************** - * 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 -#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); - -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); - - - -extern int efx_ssr_init(struct efx_ssr_state *st, struct efx_nic *efx); -extern void efx_ssr_fini(struct efx_ssr_state *st); - -extern void __efx_ssr_end_of_burst(struct efx_ssr_state *st); -extern int efx_ssr(struct efx_ssr_state *st, struct efx_rx_buffer *rx_buf); - - -static inline void efx_ssr_end_of_burst(struct efx_ssr_state *st) -{ - if (!list_empty(&st->active_conns)) - __efx_ssr_end_of_burst(st); -} - - -#endif /* EFX_RX_H */ diff -r aa133dee7792 drivers/net/sfc/selftest.c --- a/drivers/net/sfc/selftest.c +++ b/drivers/net/sfc/selftest.c @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2006-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include @@ -35,15 +18,13 @@ #include #include #include +#include #include "net_driver.h" -#include "ethtool.h" #include "efx.h" -#include "falcon.h" +#include "nic.h" #include "selftest.h" -#include "boards.h" #include "workarounds.h" - -/* Self tests */ +#include "mdio_10g.h" /* * Loopback test packet structure @@ -64,43 +45,138 @@ 0x00, 0x0f, 0x53, 0x1b, 0x1b, 0x1b, }; -static const char *payload_msg = +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 */ +/** + * efx_loopback_state - persistent state during a loopback selftest + * @flush: Drop all packets in efx_loopback_rx_packet + * @packet_count: Number of packets being used in this test + * @skbs: An array of skbs transmitted + * @offload_csum: Checksums are being offloaded + * @rx_good: RX good packet count + * @rx_bad: RX bad packet count + * @payload: Payload used in tests + */ +struct efx_loopback_state { + bool flush; int packet_count; - - /* RX good packet count */ + struct sk_buff **skbs; + bool offload_csum; atomic_t rx_good; - - /* RX bad packet count */ atomic_t rx_bad; - - /* Payload used in tests */ struct efx_loopback_payload payload; }; +/* How long to wait for all the packets to arrive (in ms) */ +#define LOOPBACK_TIMEOUT_MS (100) + /************************************************************************** * - * Configurable values + * MII, NVRAM and register tests * **************************************************************************/ -/* 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; +static int efx_test_phy_alive(struct efx_nic *efx, struct efx_self_tests *tests) +{ + int rc = 0; + + if (efx->phy_op->test_alive) { + rc = efx->phy_op->test_alive(efx); + EFX_LOG(efx, "%s PHY liveness selftest\n", + rc ? "Failed" : "Passed"); + tests->phy_alive = rc ? -1 : 1; + } + + return rc; +} + +static int efx_test_nvram(struct efx_nic *efx, struct efx_self_tests *tests) +{ + int rc = 0; + + if (efx->type->test_nvram) { + rc = efx->type->test_nvram(efx); + tests->nvram = rc ? -1 : 1; + } + + return rc; +} + +static void memtest_simple(unsigned id, efx_qword_t *reg, int a, int b) +{ + EFX_POPULATE_QWORD_2(*reg, EFX_DWORD_0, a, EFX_DWORD_1, b); +} + +static void memtest_changing_bytes(unsigned id, efx_qword_t *reg, + int a, int b) +{ + int i; + u8 *byte; + for (i = 0; i < sizeof(efx_qword_t); i++) { + unsigned int addr = id * sizeof(efx_qword_t) + i; + byte = (u8 *)reg + i; + if ((addr >> 8) == 0) + *byte = (u8) (addr % 257); + else + *byte = (u8) ~((addr >> 8) % 257); + + if (addr & 0x40) + *byte = ~*byte; + } +} + +static void memtest_bit_sweep(unsigned int id, efx_qword_t *reg, + int a, int b) +{ + int bit = id % 64; + int xor = (id & 64) ? ~0 : 0; + + EFX_POPULATE_QWORD_2(*reg, + EFX_DWORD_0, (bit < 32 ? bit : 0) ^ xor, + EFX_DWORD_1, (bit >= 32 ? bit - 32 : 0) ^ xor); +} + +struct memtest { + void (*pattern)(unsigned id, efx_qword_t *reg, int a, int b); + int a; + int b; +}; + +static struct memtest memtests[] = { + {memtest_simple, 0x55AA55AA, 0x55AA55AA}, + {memtest_simple, 0xAA55AA55, 0xAA55AA55}, + {memtest_changing_bytes, 0, 0}, + {memtest_bit_sweep, 0, 0}, +}; + +static int efx_test_chip(struct efx_nic *efx, struct efx_self_tests *tests) +{ + int rc, i; + + /* Test SRAM and register tables */ + if (efx->type->test_memory) { + for (i = 0; i < ARRAY_SIZE(memtests); ++i) { + rc = efx->type->test_memory( + efx, memtests[i].pattern, + memtests[i].a, memtests[i].b); + if (rc) + break; + } + EFX_LOG(efx, "%s memory selftest\n", rc ? "Failed" : "Passed"); + tests->memory = rc ? -1 : 1; + } + + /* Test register access */ + if (efx->type->test_registers) { + rc = efx->type->test_registers(efx); + EFX_LOG(efx, "%s register selftest\n", + rc ? "Failed" : "Passed"); + tests->registers = rc ? -1 : 1; + } + + return (tests->memory >= 0 && tests->registers >= 0) ? 0 : -EIO; +} /************************************************************************** * @@ -108,18 +184,11 @@ * **************************************************************************/ -/* - * 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. - */ +/* Test generation and receipt of interrupts */ 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; @@ -128,24 +197,16 @@ 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 */ + /* ACK each interrupting event queue. Receiving an interrupt due to + * traffic before a test event is raised is considered a pass */ + efx_for_each_channel(channel, efx) { 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); + efx_nic_generate_interrupt(efx); /* Wait for arrival of test interrupt. */ EFX_LOG(efx, "waiting for test interrupt\n"); @@ -153,8 +214,7 @@ if (efx->last_irq_cpu >= 0) goto success; - EFX_ERR(efx, "timed out in %ld jiffies waiting for interrupt\n", - jiffies - j_start); + EFX_ERR(efx, "timed out waiting for interrupt\n"); return -ETIMEDOUT; success: @@ -164,58 +224,11 @@ 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. - */ +/* Test generation and receipt of interrupting events */ 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); @@ -231,8 +244,7 @@ channel->eventq_magic = 0; smp_wmb(); - /* Generate test event */ - falcon_generate_test_event(channel, magic); + efx_nic_generate_test_event(channel, magic); /* Wait for arrival of interrupt */ count = 0; @@ -246,8 +258,8 @@ 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); + EFX_ERR(channel->efx, "channel %d timed out waiting for event queue\n", + channel->channel); /* See if interrupt arrived */ if (channel->efx->last_irq_cpu >= 0) { @@ -275,44 +287,19 @@ return 0; } -/************************************************************************** - * - * PHY testing - * - **************************************************************************/ +static int efx_test_phy(struct efx_nic *efx, struct efx_self_tests *tests, + unsigned flags) +{ + int rc; -/* - * 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) + if (!efx->phy_op->run_tests) 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; + mutex_lock(&efx->mac_lock); + rc = efx->phy_op->run_tests(efx, tests->phy_ext, flags); + mutex_unlock(&efx->mac_lock); + EFX_INFO(efx, "%s phy selftest\n", rc ? "Failed" : "Passed"); + return rc; } /************************************************************************** @@ -328,7 +315,7 @@ 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_state *state = efx->loopback_selftest; struct efx_loopback_payload *received; struct efx_loopback_payload *payload; @@ -340,62 +327,52 @@ 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 = (struct efx_loopback_payload *) buf_ptr; received->ip.saddr = payload->ip.saddr; - received->ip.check = payload->ip.check; + if (state->offload_csum) + 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)); + 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 */ + /* Check that the ethernet header exists */ if (memcmp(&received->header, &payload->header, ETH_HLEN) != 0) { - EFX_ERR(efx, "saw non-loopback RX packet in" - " %s loopback test\n", + 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), + 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", + 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", + 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)); + 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; } @@ -423,7 +400,7 @@ /* 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 efx_loopback_state *state = efx->loopback_selftest; struct net_device *net_dev = efx->net_dev; struct efx_loopback_payload *payload = &state->payload; @@ -435,7 +412,7 @@ /* saddr set later and used as incrementing count */ payload->ip.daddr = htonl(INADDR_LOOPBACK); payload->ip.ihl = 5; - payload->ip.check = 0; /* offloaded */ + payload->ip.check = htons(0xdead); payload->ip.tot_len = htons(sizeof(*payload) - sizeof(struct ethhdr)); payload->ip.version = IPVERSION; payload->ip.protocol = IPPROTO_UDP; @@ -456,86 +433,117 @@ 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) +static int efx_begin_loopback(struct efx_tx_queue *tx_queue) { - struct efx_selftest_state *state = efx->loopback_selftest; + struct efx_nic *efx = tx_queue->efx; + struct efx_loopback_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)); + int i; + netdev_tx_t rc; /* 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 = htonl(INADDR_LOOPBACK | (i << 2)); + /* Allocate an skb, holding an extra reference for + * transmit completion counting */ + skb = alloc_skb(sizeof(state->payload), GFP_KERNEL); + if (!skb) + return -ENOMEM; + state->skbs[i] = skb; 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; + /* Copy the payload in, incrementing the source address to + * exercise the rss vectors */ + payload = ((struct efx_loopback_payload *) + skb_put(skb, sizeof(state->payload))); + memcpy(payload, &state->payload, sizeof(state->payload)); + payload->ip.saddr = htonl(INADDR_LOOPBACK | (i << 2)); + + /* Ensure everything we've written is visible to the + * interrupt handler. */ + smp_wmb(); + + if (efx_dev_registered(efx)) + netif_tx_lock_bh(efx->net_dev); + rc = efx_enqueue_skb(tx_queue, skb); + if (efx_dev_registered(efx)) + netif_tx_unlock_bh(efx->net_dev); + + if (rc != 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)); + + /* Defer cleaning up the other skbs for the caller */ + kfree_skb(skb); + return -EPIPE; } - - /* Avoid hogging the PCI bus */ - udelay(10); } - /* Allow time for processing */ - schedule_timeout_uninterruptible(HZ / 10); + return 0; +} - if (state->flush) - goto out3; +static int efx_poll_loopback(struct efx_nic *efx) +{ + struct efx_loopback_state *state = efx->loopback_selftest; +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_OLD_NAPI) + struct efx_channel *channel; + + /* NAPI polling is not enabled, so process channels + * synchronously */ + efx_for_each_channel(channel, efx) { + if (channel->work_pending) + efx_process_channel_now(channel); + } +#endif + return atomic_read(&state->rx_good) == state->packet_count; +} + +static int efx_end_loopback(struct efx_tx_queue *tx_queue, + struct efx_loopback_self_tests *lb_tests) +{ + struct efx_nic *efx = tx_queue->efx; + struct efx_loopback_state *state = efx->loopback_selftest; + struct sk_buff *skb; + int tx_done = 0, rx_good, rx_bad; + int i, rc = 0; + + if (efx_dev_registered(efx)) + netif_tx_lock_bh(efx->net_dev); + + /* Count the number of tx completions, and decrement the refcnt. Any + * skbs not already completed will be free'd when the queue is flushed */ + for (i=0; i < state->packet_count; i++) { + skb = state->skbs[i]; + if (skb && !skb_shared(skb)) + ++tx_done; + dev_kfree_skb_any(skb); + } + + if (efx_dev_registered(efx)) + netif_tx_unlock_bh(efx->net_dev); /* 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)); + 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)); + 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 */ } @@ -546,262 +554,295 @@ lb_tests->rx_good += rx_good; lb_tests->rx_bad += rx_bad; - out3: - out2: - /* Free skb */ - if (skb) { - /* If the selftest failed then the skb needs its reference - * count decreasing */ - while (skb_shared(skb)) - kfree_skb(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) +efx_test_loopback(struct efx_tx_queue *tx_queue, + struct efx_loopback_self_tests *lb_tests, + int level) { - struct efx_selftest_state *state = efx->loopback_selftest; - int i, rc = 0; + struct efx_nic *efx = tx_queue->efx; + struct efx_loopback_state *state = efx->loopback_selftest; + int i, begin_rc, end_rc; - for (i = 0; i < loopback_test_level; i++) { - /* Determine how many packets to send */ - state->packet_count = (efx->type->txd_ring_mask + 1) / 3; + for (i = 0; i < level; i++) { + /* Determine how many packets to send - up to 16**(level-1) */ + state->packet_count = EFX_TXQ_SIZE / 3; state->packet_count = min(1 << (i << 2), state->packet_count); - state->flush = 0; + state->skbs = kzalloc(sizeof(state->skbs[0]) * + state->packet_count, GFP_KERNEL); + if (!state->skbs) + return -ENOMEM; + state->flush = false; - EFX_LOG(efx, "TX queue %d testing %s loopback" - " with %d packets\n", tx_queue->queue, - LOOPBACK_MODE(efx), state->packet_count); + 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) { + efx_iterate_state(efx); + begin_rc = efx_begin_loopback(tx_queue); + + /* This will normally complete very quickly, but be + * prepared to wait much longer. */ + msleep(1); + if (!efx_poll_loopback(efx)) { + msleep(LOOPBACK_TIMEOUT_MS); + efx_poll_loopback(efx); + } + + end_rc = efx_end_loopback(tx_queue, lb_tests); + kfree(state->skbs); + + if (begin_rc || end_rc) { /* Wait a while to ensure there are no packets - * floating around after a failure. - */ - schedule_timeout_uninterruptible(HZ / 5); - return rc; + * floating around after a failure. */ + schedule_timeout_uninterruptible(HZ / 10); + return begin_rc ? begin_rc : end_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); + 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; + return 0; } -static int efx_test_loopbacks(struct efx_nic *efx, - struct efx_self_tests *tests, - unsigned int loopback_modes) +/* Wait for link up. On Falcon, we would prefer to rely on efx_monitor, but + * any contention on the mac lock (via e.g. efx_mac_mcast_work) causes it + * to delay and retry. Therefore, it's safer to just poll directly. Wait + * for link up and any faults to dissipate. */ +static int efx_wait_for_link(struct efx_nic *efx) { - struct efx_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, link_up; - int retry = EFX_WORKAROUND_8909(efx); + struct efx_link_state *link_state = &efx->link_state; + int count; + bool link_up; - /* 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; + for (count = 0; count < 40; count++) { + schedule_timeout_uninterruptible(HZ / 10); - /* 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; + if (efx->type->monitor != NULL) { + mutex_lock(&efx->mac_lock); + efx->type->monitor(efx); + mutex_unlock(&efx->mac_lock); + } else { +#if !defined(EFX_USE_KCOMPAT) || !defined(EFX_HAVE_OLD_NAPI) + struct efx_channel *channel = &efx->channel[0]; + if (channel->work_pending) + efx_process_channel_now(channel); +#endif + } + + mutex_lock(&efx->mac_lock); + link_up = link_state->up; + if (link_up) + link_up = !efx->mac_op->check_fault(efx); + mutex_unlock(&efx->mac_lock); + + if (link_up) + return 0; } - 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; + return -ETIMEDOUT; +} + +static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests, + unsigned int loopback_modes, int level) +{ + enum efx_loopback_mode mode; + struct efx_loopback_state *state; + struct efx_tx_queue *tx_queue; + int rc = 0; + bool retry; + + /* 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 */ + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + BUG_ON(efx->loopback_selftest); + state->flush = true; + efx->loopback_selftest = state; /* Test all supported loopback modes */ - for (mode = LOOPBACK_NONE; mode < LOOPBACK_TEST_MAX; mode++) { + for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) { if (!(loopback_modes & (1 << mode))) continue; + retry = EFX_WORKAROUND_8568(efx); + set_loopback: /* Move the port into the specified loopback mode. */ - state->flush = 1; - efx->phy_powered = 1; + state->flush = true; + mutex_lock(&efx->mac_lock); efx->loopback_mode = mode; - efx_reconfigure_port(efx); - - /* Wait for the PHY to signal the link is up */ - count = 0; - do { - struct efx_channel *channel = &efx->channel[0]; - - (void) efx->mac_op->check_hw(efx); - schedule_timeout_uninterruptible(HZ / 10); - if (channel->work_pending) - efx_process_channel_now(channel); - /* Wait for PHY events to be processed */ - flush_workqueue(efx->workqueue); - rmb(); - - /* efx->link_up can be 1 even if the XAUI link is down, - * (bug5762). Usually, it's not worth bothering with the - * difference, but for selftests, we need that extra - * guarantee that the link is really, really, up. - */ - link_up = efx->link_up; - if (EFX_IS10G(efx) && !falcon_xaui_link_ok(efx)) - link_up = 0; - - } while ((++count < 20) && !link_up); - - /* The link should now be up. If it isn't, there is no point - * in attempting a loopback test */ - if (!link_up) { - EFX_ERR(efx, "loopback %s never came up\n", + rc = __efx_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); + if (rc) { + EFX_ERR(efx, "unable to move into %s loopback\n", LOOPBACK_MODE(efx)); - rc = -EIO; goto out; } - EFX_LOG(efx, "link came up in %s loopback in %d iterations\n", - LOOPBACK_MODE(efx), count); + rc = efx_wait_for_link(efx); + if (rc) { + EFX_ERR(efx, "loopback %s never came up\n", + LOOPBACK_MODE(efx)); + goto out; + } /* 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; + state->offload_csum = (tx_queue->queue == + EFX_TX_QUEUE_OFFLOAD_CSUM); + rc = efx_test_loopback(tx_queue, + &tests->loopback[mode], + level); + if (rc && !retry) + goto out; + if (rc) { + /* Give the PHY a kick by moving into + * a Falcon internal loopback mode and + * then back out */ + int first = ffs(efx->loopback_modes) - 1; + + EFX_INFO(efx, "retrying %s loopback\n", + LOOPBACK_MODE(efx)); + + state->flush = true; + mutex_lock(&efx->mac_lock); + efx->loopback_mode = first; + __efx_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); + + memset(&tests->loopback[mode], 0, + sizeof(tests->loopback[mode])); + retry = false; + goto set_loopback; + } } - - 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); - - 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); + /* Remove the flush. The caller will remove the loopback setting */ + state->flush = true; + efx->loopback_selftest = NULL; + wmb(); + kfree(state); return rc; } /************************************************************************** * - * Entry points + * Entry point * *************************************************************************/ -/* 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) +int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, + unsigned flags) { + enum efx_loopback_mode loopback_mode = efx->loopback_mode; + int phy_mode = efx->phy_mode; struct efx_channel *channel; - int rc = 0; + int rc_test = 0, rc_reset = 0, rc; - EFX_LOG(efx, "performing online self-tests\n"); + /* Online (i.e. non-disruptive) testing + * This checks interrupt generation, event delivery and PHY presence. */ - rc |= efx_test_interrupts(efx, tests); + { + rc = efx_test_phy_alive(efx, tests); + if (rc && !rc_test) + rc_test = rc; + + rc = efx_test_nvram(efx, tests); + if (rc && !rc_test) + rc_test = rc; + } + + rc = efx_test_interrupts(efx, tests); + if (rc && !rc_test) + rc_test = rc; + efx_for_each_channel(channel, efx) { - if (channel->has_interrupt) - rc |= efx_test_eventq_irq(channel, tests); + rc = efx_test_eventq_irq(channel, tests); + if (rc && !rc_test) + rc_test = rc; + } + + if (rc_test) + return rc_test; + + if (!(flags & ETH_TEST_FL_OFFLINE)) { + return efx_test_phy(efx, tests, flags); + } + + /* Offline (i.e. disruptive) testing + * This checks MAC and PHY loopback on the specified port. */ + + /* force the carrier state off so the kernel doesn't transmit during + * the loopback test, and the watchdog timeout doesn't fire. Also put + * falcon into loopback for the register test. + */ + mutex_lock(&efx->mac_lock); + efx->port_inhibited = true; + if (efx->loopback_modes) { + /* We need the 312 clock from the PHY to test the XMAC + * registers, so move into XGMII loopback if available */ + if (efx->loopback_modes & (1 << LOOPBACK_XGMII)) + efx->loopback_mode = LOOPBACK_XGMII; else - rc |= efx_test_eventq(channel, tests); + efx->loopback_mode = __ffs(efx->loopback_modes); } - rc |= efx_test_phy(efx, tests); - if (rc) - EFX_ERR(efx, "failed online self-tests\n"); + __efx_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); - return rc; + { + enum reset_type method = RESET_TYPE_INVISIBLE; + + /* free up all consumers of SRAM (including all the queues) */ + efx_reset_down(efx, method); + + rc = efx_test_chip(efx, tests); + if (rc && !rc_test) + rc_test = rc; + + /* reset the chip to recover from the register test */ + rc_reset = efx->type->reset(efx, method); + + /* Ensure that the phy is powered and out of loopback + * for the bist and loopback tests */ + efx->phy_mode &= ~PHY_MODE_LOW_POWER; + efx->loopback_mode = LOOPBACK_NONE; + rc = efx_reset_up(efx, method, rc_reset == 0); + if (rc && !rc_reset) + rc_reset = rc; + + if (rc_reset) { + EFX_ERR(efx, "Unable to recover from chip test\n"); + efx_schedule_reset(efx, RESET_TYPE_DISABLE); + return rc_reset; + } + + rc = efx_test_phy(efx, tests, flags); + if (rc && !rc_test) + rc_test = rc; + + rc = efx_test_loopbacks(efx, tests, efx->loopback_modes, 3); + if (rc && !rc_test) + rc_test = rc; + } + + /* restore the PHY to the previous state */ + mutex_lock(&efx->mac_lock); + efx->phy_mode = phy_mode; + efx->port_inhibited = false; + efx->loopback_mode = loopback_mode; + __efx_reconfigure_port(efx); + mutex_unlock(&efx->mac_lock); + + return rc_test; } -/* 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; - - 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; - } - - /* 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); - - out: - if (rc) - EFX_ERR(efx, "failed offline self-tests\n"); - - return rc; -} - diff -r aa133dee7792 drivers/net/sfc/selftest.h --- a/drivers/net/sfc/selftest.h +++ b/drivers/net/sfc/selftest.h @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005-2006: Fen Systems Ltd. - * Copyright 2006-2008: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2008 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_SELFTEST_H @@ -35,33 +18,37 @@ */ struct efx_loopback_self_tests { - int tx_sent[EFX_MAX_TX_QUEUES]; - int tx_done[EFX_MAX_TX_QUEUES]; + int tx_sent[EFX_TX_QUEUE_COUNT]; + int tx_done[EFX_TX_QUEUE_COUNT]; int rx_good; int rx_bad; }; +#define EFX_MAX_PHY_TESTS 20 + /* Efx self test results * For fields which are not counters, 1 indicates success and -1 * indicates failure. */ struct efx_self_tests { + /* online tests */ + int phy_alive; + int nvram; 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]; + /* offline tests */ + int memory; + int registers; + int phy_ext[EFX_MAX_PHY_TESTS]; + struct efx_loopback_self_tests loopback[LOOPBACK_TEST_MAX + 1]; }; 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); +extern int efx_selftest(struct efx_nic *efx, + struct efx_self_tests *tests, + unsigned flags); #endif /* EFX_SELFTEST_H */ diff -r aa133dee7792 drivers/net/sfc/sfe4001.c --- a/drivers/net/sfc/sfe4001.c +++ /dev/null @@ -1,315 +0,0 @@ -/**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2007: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Developed by Solarflare Communications - * - * 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", __func__); - - /* Turn off all power rails */ - out = 0xff; - (void) efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1); - - /* Disable port 1 outputs on IO expander */ - cfg = 0xff; - (void) efx_i2c_write(i2c, PCA9539, P1_CONFIG, &cfg, 1); - - /* Disable port 0 outputs on IO expander */ - cfg = 0xff; - (void) efx_i2c_write(i2c, PCA9539, P0_CONFIG, &cfg, 1); - - /* Clear any over-temperature alert */ - (void) efx_i2c_read(i2c, MAX6647, RSL, &in, 1); -} - -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, 1); - 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", - __func__, status); - rc = -EIO; - } else { - EFX_ERR(efx, "%s: Failed to read PHY status!" - " Shutting down PHY.\n", - __func__); - } - } - - 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, 1); - if (rc) - goto fail1; - - /* Read it back and verify */ - rc = efx_i2c_read(i2c, MAX6647, RLHN, &in, 1); - 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, 1); - 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, 1); - if (rc) - goto fail1; - cfg = 0xff & ~(1 << P1_SPARE_LBN); - rc = efx_i2c_write(i2c, PCA9539, P1_CONFIG, &cfg, 1); - 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, 1); - 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, 1); - if (rc) - goto fail3; - msleep(10); - - /* Turn on 1V power rail */ - out &= ~(1 << P0_EN_1V0X_LBN); - rc = efx_i2c_write(i2c, PCA9539, P0_OUT, &out, 1); - 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, 1); - 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, 1); - /* Disable port 1 outputs on IO expander */ - out = 0xff; - (void) efx_i2c_write(i2c, PCA9539, P1_CONFIG, &out, 1); -fail2: - /* Disable port 0 outputs on IO expander */ - out = 0xff; - (void) efx_i2c_write(i2c, PCA9539, P0_CONFIG, &out, 1); -fail1: - return rc; -} diff -r aa133dee7792 drivers/net/sfc/siena.c --- /dev/null +++ b/drivers/net/sfc/siena.c @@ -0,0 +1,743 @@ +/**************************************************************************** + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2006-2009 Solarflare Communications Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include "net_driver.h" +#include "bitfield.h" +#include "efx.h" +#include "nic.h" +#include "mac.h" +#include "spi.h" +#include "regs.h" +#include "io.h" +#include "phy.h" +#include "workarounds.h" +#include "mcdi.h" +#include "mcdi_pcol.h" + +/* Hardware control for SFC9000 family including SFL9021 (aka Siena). */ + +static void siena_init_wol(struct efx_nic *efx); + + +static void siena_push_irq_moderation(struct efx_channel *channel) +{ + efx_dword_t timer_cmd; + + if (channel->irq_moderation) + EFX_POPULATE_DWORD_2(timer_cmd, + FRF_CZ_TC_TIMER_MODE, + FFE_CZ_TIMER_MODE_INT_HLDOFF, + FRF_CZ_TC_TIMER_VAL, + channel->irq_moderation - 1); + else + EFX_POPULATE_DWORD_2(timer_cmd, + FRF_CZ_TC_TIMER_MODE, + FFE_CZ_TIMER_MODE_DIS, + FRF_CZ_TC_TIMER_VAL, 0); + efx_writed_page_locked(channel->efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0, + channel->channel); +} + +static void siena_push_multicast_hash(struct efx_nic *efx) +{ + WARN_ON(!mutex_is_locked(&efx->mac_lock)); + + efx_mcdi_rpc(efx, MC_CMD_SET_MCAST_HASH, + efx->multicast_hash.byte, sizeof(efx->multicast_hash), + NULL, 0, NULL); +} + +static int siena_mdio_write(struct net_device *net_dev, + int prtad, int devad, u16 addr, u16 value) +{ + struct efx_nic *efx = netdev_priv(net_dev); + uint32_t status; + int rc; + + rc = efx_mcdi_mdio_write(efx, efx->mdio_bus, prtad, devad, + addr, value, &status); + if (rc) + return rc; + if (status != MC_CMD_MDIO_STATUS_GOOD) + return -EIO; + + return 0; +} + +static int siena_mdio_read(struct net_device *net_dev, + int prtad, int devad, u16 addr) +{ + struct efx_nic *efx = netdev_priv(net_dev); + uint16_t value; + uint32_t status; + int rc; + + rc = efx_mcdi_mdio_read(efx, efx->mdio_bus, prtad, devad, + addr, &value, &status); + if (rc) + return rc; + if (status != MC_CMD_MDIO_STATUS_GOOD) + return -EIO; + + return (int)value; +} + +/* This call is responsible for hooking in the MAC and PHY operations */ +static int siena_probe_port(struct efx_nic *efx) +{ + int rc; + + /* Hook in PHY operations table */ + efx->phy_op = &efx_mcdi_phy_ops; + + /* Set up MDIO structure for PHY */ + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + efx->mdio.mdio_read = siena_mdio_read; + efx->mdio.mdio_write = siena_mdio_write; + + /* Fill out MDIO structure, loopback modes, and initial link state */ + rc = efx->phy_op->probe(efx); + if (rc != 0) + return rc; + + /* Allocate buffer for stats */ + rc = efx_nic_alloc_buffer(efx, &efx->stats_buffer, + MC_CMD_MAC_NSTATS * sizeof(u64)); + if (rc) + return rc; + EFX_LOG(efx, "stats buffer at %llx (virt %p phys %llx)\n", + (u64)efx->stats_buffer.dma_addr, + efx->stats_buffer.addr, + (u64)virt_to_phys(efx->stats_buffer.addr)); + + efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 1); + + return 0; +} + +void siena_remove_port(struct efx_nic *efx) +{ + efx->phy_op->remove(efx); + efx_nic_free_buffer(efx, &efx->stats_buffer); +} + +static int siena_test_sram(struct efx_nic *efx, + void (*pattern)(unsigned, efx_qword_t *, int, int), + int a, int b) +{ + void __iomem *membase = efx->membase + FR_BZ_BUF_FULL_TBL; + int finish = efx->sram_lim / 8; + efx_qword_t buf1, buf2; + efx_oword_t reg; + int wptr = 0, rptr = 0; + + /* Move descriptor caches out into space so we can treat all + * SRAM as buffer table. These registers will be restored by + * a following reset. */ + EFX_POPULATE_OWORD_1(reg, FRF_AZ_SRM_RX_DC_BASE_ADR, finish); + efx_writeo(efx, ®, FR_AZ_SRM_RX_DC_CFG); + EFX_POPULATE_OWORD_1(reg, FRF_AZ_SRM_TX_DC_BASE_ADR, finish + 64); + efx_writeo(efx, ®, FR_AZ_SRM_TX_DC_CFG); + + while (wptr < finish) { + pattern(wptr, &buf1, a, b); + efx_sram_writeq(efx, membase, &buf1, wptr); + wptr++; + + /* Buffer table writes are not performed synchronously + * but go through a 128-entry FIFO, so we must switch + * from writing to reading after at most 128 writes. + * We choose 125 to make the following calculation + * result in a round number. */ + if ((wptr - rptr) < 125 && wptr < finish) + continue; + + /* The SRAM arbiter will allow 2 writes per 8 cycles + * with a cycle time of 8 ns. Each MMIO access takes + * at least one cycle. So in order to avoid reads + * overtaking writes we must wait for at least + * 125 * (8 / 2 - 2) * 8 ns = 2 us */ + udelay(2); + + for (; rptr < wptr; ++rptr) { + pattern(rptr, &buf1, a, b); + efx_sram_readq(efx, membase, &buf2, rptr); + + if (!memcmp(&buf1, &buf2, sizeof(buf1))) + continue; + + EFX_ERR(efx, "sram test failed at index 0x%x. " + "wrote "EFX_QWORD_FMT" read "EFX_QWORD_FMT"\n", + rptr, EFX_QWORD_VAL(buf1), EFX_QWORD_VAL(buf2)); + return -EIO; + } + } + + return 0; +} + +static const struct efx_nic_register_test siena_register_tests[] = { + { FR_AZ_ADR_REGION, + EFX_OWORD32(0x0003FFFF, 0x0003FFFF, 0x0003FFFF, 0x0003FFFF) }, + { FR_CZ_USR_EV_CFG, + EFX_OWORD32(0x000103FF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_CFG, + EFX_OWORD32(0xFFFFFFFE, 0xFFFFFFFF, 0x0003FFFF, 0x00000000) }, + { FR_AZ_TX_CFG, + EFX_OWORD32(0x7FFF0037, 0xFFFF8000, 0xFFFFFFFF, 0x03FFFFFF) }, + { FR_AZ_TX_RESERVED, + EFX_OWORD32(0xFFFEFE80, 0x1FFFFFFF, 0x020000FE, 0x007FFFFF) }, + { FR_AZ_SRM_TX_DC_CFG, + EFX_OWORD32(0x001FFFFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_DC_CFG, + EFX_OWORD32(0x00000003, 0x00000000, 0x00000000, 0x00000000) }, + { FR_AZ_RX_DC_PF_WM, + EFX_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_BZ_DP_CTRL, + EFX_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_BZ_RX_RSS_TKEY, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) }, + { FR_CZ_RX_RSS_IPV6_REG1, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) }, + { FR_CZ_RX_RSS_IPV6_REG2, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) }, + { FR_CZ_RX_RSS_IPV6_REG3, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0x00000007, 0x00000000) }, +}; + +static const struct efx_nic_table_test siena_table_tests[] = { + { FR_BZ_RX_FILTER_TBL0, + FR_BZ_RX_FILTER_TBL0_STEP, FR_BZ_RX_FILTER_TBL0_ROWS, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000003FF) }, + { FR_CZ_RX_MAC_FILTER_TBL0, + FR_CZ_RX_MAC_FILTER_TBL0_STEP, FR_CZ_RX_MAC_FILTER_TBL0_ROWS, + EFX_OWORD32(0xFFFF0FFF, 0xFFFFFFFF, 0x00000E7F, 0x00000000) }, + { FR_BZ_RX_DESC_PTR_TBL, + FR_BZ_RX_DESC_PTR_TBL_STEP, FR_CZ_RX_DESC_PTR_TBL_ROWS, + EFX_OWORD32(0xFFFFFFFF, 0x0FFFFFFF, 0x01800000, 0x00000000) }, + { FR_BZ_TX_DESC_PTR_TBL, + FR_BZ_TX_DESC_PTR_TBL_STEP, FR_CZ_TX_DESC_PTR_TBL_ROWS, + EFX_OWORD32(0xFFFFFFFE, 0x0FFFFFFF, 0x0C000000, 0x00000000) }, + { FR_BZ_TIMER_TBL, + FR_BZ_TIMER_TBL_STEP, FR_CZ_TIMER_TBL_ROWS, + EFX_OWORD32(0x3FFFFFFF, 0x00000000, 0x00000000, 0x00000000) }, + { FR_CZ_TX_FILTER_TBL0, + FR_CZ_TX_FILTER_TBL0_STEP, FR_CZ_TX_FILTER_TBL0_ROWS, + EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000013FF) }, + { FR_CZ_TX_MAC_FILTER_TBL0, + FR_CZ_TX_MAC_FILTER_TBL0_STEP, FR_CZ_TX_MAC_FILTER_TBL0_ROWS, + EFX_OWORD32(0xFFFF07FF, 0xFFFFFFFF, 0x0000007F, 0x00000000) }, +}; + +static int siena_test_registers(struct efx_nic *efx) +{ + return efx_nic_test_registers(efx, siena_register_tests, + ARRAY_SIZE(siena_register_tests)); +} + +static int +siena_test_tables(struct efx_nic *efx, + void (*pattern)(unsigned, efx_qword_t *, int, int), + int a, int b) +{ + int rc, i; + + rc = siena_test_sram(efx, pattern, a, b); + if (rc) + return rc; + + for (i = 0; i < ARRAY_SIZE(siena_table_tests); i++) { + rc = efx_nic_test_table(efx, &siena_table_tests[i], + pattern, a, b); + if (rc) + return rc; + } + + return 0; +} + +/************************************************************************** + * + * Device reset + * + ************************************************************************** + */ + +static int siena_reset_hw(struct efx_nic *efx, enum reset_type method) +{ + int rc; + + /* Recover from a failed assertion pre-reset */ + rc = efx_mcdi_handle_assertion(efx); + if (rc) + return rc; + + if (method == RESET_TYPE_WORLD) + return efx_mcdi_reset_mc(efx); + else + return efx_mcdi_reset_port(efx); +} + +static int siena_probe_nvconfig(struct efx_nic *efx) +{ + int rc; + + rc = efx_mcdi_get_board_cfg(efx, efx->mac_address, NULL); + if (rc) + return rc; + + return 0; +} + +static int siena_dimension_resources(struct efx_nic *efx) +{ + /* There is a small block of internal SRAM dedicated to the + * buffer table and descriptor caches. */ + size_t sram_size = 72 * 1024 * 64 / 8; + + return efx_nic_dimension_resources(efx, sram_size, 512); +} + +static int siena_probe_nic(struct efx_nic *efx) +{ + struct siena_nic_data *nic_data; + bool already_attached = 0; + int rc; + + /* Allocate storage for hardware specific data */ + nic_data = kzalloc(sizeof(struct siena_nic_data), GFP_KERNEL); + if (!nic_data) + return -ENOMEM; + efx->nic_data = nic_data; + + if (efx_nic_fpga_ver(efx) != 0) { + EFX_ERR(efx, "Siena FPGA not supported\n"); + rc = -ENODEV; + goto fail1; + } + + efx_mcdi_init(efx); + + /* Recover from a failed assertion before probing */ + rc = efx_mcdi_handle_assertion(efx); + if (rc) + goto fail1; + + rc = efx_mcdi_fwver(efx, &nic_data->fw_version, &nic_data->fw_build); + if (rc) { + EFX_ERR(efx, "Failed to read MCPU firmware version - " + "rc %d\n", rc); + goto fail1; /* MCPU absent? */ + } + + /* Let the BMC know that the driver is now in charge of link and + * filter settings. We must do this before we reset the NIC */ + rc = efx_mcdi_drv_attach(efx, true, &already_attached); + if (rc) { + EFX_ERR(efx, "Unable to register driver with MCPU\n"); + goto fail2; + } + if (already_attached) + /* Not a fatal error */ + EFX_ERR(efx, "Host already registered with MCPU\n"); + + /* Now we can reset the NIC */ + rc = siena_reset_hw(efx, RESET_TYPE_ALL); + if (rc) { + EFX_ERR(efx, "failed to reset NIC\n"); + goto fail3; + } + + siena_init_wol(efx); + + /* Allocate memory for INT_KER */ + rc = efx_nic_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t)); + if (rc) + goto fail4; + BUG_ON(efx->irq_status.dma_addr & 0x0f); + + EFX_LOG(efx, "INT_KER at %llx (virt %p phys %llx)\n", + (unsigned long long)efx->irq_status.dma_addr, + efx->irq_status.addr, + (unsigned long long)virt_to_phys(efx->irq_status.addr)); + + /* Read in the non-volatile configuration */ + rc = siena_probe_nvconfig(efx); + if (rc == -EINVAL) { + EFX_ERR(efx, "NVRAM is invalid therefore using defaults\n"); + efx->phy_type = PHY_TYPE_NONE; + efx->mdio.prtad = MDIO_PRTAD_NONE; + } else if (rc) { + goto fail5; + } + + rc = siena_dimension_resources(efx); + if (rc) + goto fail6; + + return 0; + +fail6: +fail5: + efx_nic_free_buffer(efx, &efx->irq_status); +fail4: +fail3: + efx_mcdi_drv_attach(efx, false, NULL); +fail2: +fail1: + kfree(efx->nic_data); + return rc; +} + +/* This call performs hardware-specific global initialisation, such as + * defining the descriptor cache sizes and number of RSS channels. + * It does not set up any buffers, descriptor rings or event queues. + */ +static int siena_init_nic(struct efx_nic *efx) +{ + efx_oword_t temp; + int rc; + + /* Recover from a failed assertion post-reset */ + rc = efx_mcdi_handle_assertion(efx); + if (rc) + return rc; + + efx_nic_check_pcie_link(efx, 8, 2); + + /* Do not enable TX_NO_EOP_DISC_EN, since it limits packets to 16 + * descriptors (which is bad). + */ + efx_reado(efx, &temp, FR_AZ_TX_CFG); + EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_NO_EOP_DISC_EN, 0); + EFX_SET_OWORD_FIELD(temp, FRF_CZ_TX_FILTER_EN_BIT, 1); + efx_writeo(efx, &temp, FR_AZ_TX_CFG); + + efx_reado(efx, &temp, FR_AZ_RX_CFG); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_DESC_PUSH_EN, 0); + EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_INGR_EN, 1); + efx_writeo(efx, &temp, FR_AZ_RX_CFG); + + if (efx_nic_rx_xoff_thresh >= 0 || efx_nic_rx_xon_thresh >= 0) + /* No MCDI operation has been defined to set thresholds */ + EFX_ERR(efx, "ignoring RX flow control thresholds\n"); + + /* Enable event logging */ + rc = efx_mcdi_log_ctrl(efx, true, false, 0); + if (rc) + return rc; + + /* Set destination of both TX and RX Flush events */ + EFX_POPULATE_OWORD_1(temp, FRF_BZ_FLS_EVQ_ID, 0); + efx_writeo(efx, &temp, FR_BZ_DP_CTRL); + + EFX_POPULATE_OWORD_1(temp, FRF_CZ_USREV_DIS, 1); + efx_writeo(efx, &temp, FR_CZ_USR_EV_CFG); + + efx_nic_init_common(efx); + return 0; +} + +static void siena_remove_nic(struct efx_nic *efx) +{ + efx_nic_free_buffer(efx, &efx->irq_status); + + siena_reset_hw(efx, RESET_TYPE_ALL); + + /* Relinquish the device back to the BMC */ + if (efx_nic_has_mc(efx)) + efx_mcdi_drv_attach(efx, false, NULL); + + /* Tear down the private nic state, and the driverlink nic params */ + kfree(efx->nic_data); + efx->nic_data = NULL; +} + +#define STATS_GENERATION_INVALID ((u64)(-1)) + +static int siena_try_update_nic_stats(struct efx_nic *efx) +{ + u64 *dma_stats; + struct efx_mac_stats *mac_stats; + u64 generation_start; + u64 generation_end; + u64 stat_val; + + mac_stats = &efx->mac_stats; + dma_stats = (u64 *)efx->stats_buffer.addr; + + generation_end = dma_stats[MC_CMD_MAC_GENERATION_END]; + if (generation_end == STATS_GENERATION_INVALID) + return 0; + rmb(); + +#define MAC_STAT(M, D) \ + mac_stats->M = dma_stats[MC_CMD_MAC_ ## D] + + MAC_STAT(tx_bytes, TX_BYTES); + MAC_STAT(tx_bad_bytes, TX_BAD_BYTES); + mac_stats->tx_good_bytes = (mac_stats->tx_bytes - + mac_stats->tx_bad_bytes); + MAC_STAT(tx_packets, TX_PKTS); + MAC_STAT(tx_bad, TX_BAD_FCS_PKTS); + MAC_STAT(tx_pause, TX_PAUSE_PKTS); + MAC_STAT(tx_control, TX_CONTROL_PKTS); + MAC_STAT(tx_unicast, TX_UNICAST_PKTS); + MAC_STAT(tx_multicast, TX_MULTICAST_PKTS); + MAC_STAT(tx_broadcast, TX_BROADCAST_PKTS); + MAC_STAT(tx_lt64, TX_LT64_PKTS); + MAC_STAT(tx_64, TX_64_PKTS); + MAC_STAT(tx_65_to_127, TX_65_TO_127_PKTS); + MAC_STAT(tx_128_to_255, TX_128_TO_255_PKTS); + MAC_STAT(tx_256_to_511, TX_256_TO_511_PKTS); + MAC_STAT(tx_512_to_1023, TX_512_TO_1023_PKTS); + MAC_STAT(tx_1024_to_15xx, TX_1024_TO_15XX_PKTS); + MAC_STAT(tx_15xx_to_jumbo, TX_15XX_TO_JUMBO_PKTS); + MAC_STAT(tx_gtjumbo, TX_GTJUMBO_PKTS); + mac_stats->tx_collision = 0; + MAC_STAT(tx_single_collision, TX_SINGLE_COLLISION_PKTS); + MAC_STAT(tx_multiple_collision, TX_MULTIPLE_COLLISION_PKTS); + MAC_STAT(tx_excessive_collision, TX_EXCESSIVE_COLLISION_PKTS); + MAC_STAT(tx_deferred, TX_DEFERRED_PKTS); + MAC_STAT(tx_late_collision, TX_LATE_COLLISION_PKTS); + mac_stats->tx_collision = (mac_stats->tx_single_collision + + mac_stats->tx_multiple_collision + + mac_stats->tx_excessive_collision + + mac_stats->tx_late_collision); + MAC_STAT(tx_excessive_deferred, TX_EXCESSIVE_DEFERRED_PKTS); + MAC_STAT(tx_non_tcpudp, TX_NON_TCPUDP_PKTS); + MAC_STAT(tx_mac_src_error, TX_MAC_SRC_ERR_PKTS); + MAC_STAT(tx_ip_src_error, TX_IP_SRC_ERR_PKTS); + MAC_STAT(rx_bytes, RX_BYTES); + MAC_STAT(rx_bad_bytes, RX_BAD_BYTES); + mac_stats->rx_good_bytes = (mac_stats->rx_bytes - + mac_stats->rx_bad_bytes); + MAC_STAT(rx_packets, RX_PKTS); + MAC_STAT(rx_good, RX_GOOD_PKTS); + mac_stats->rx_bad = mac_stats->rx_packets - mac_stats->rx_good; + MAC_STAT(rx_pause, RX_PAUSE_PKTS); + MAC_STAT(rx_control, RX_CONTROL_PKTS); + MAC_STAT(rx_unicast, RX_UNICAST_PKTS); + MAC_STAT(rx_multicast, RX_MULTICAST_PKTS); + MAC_STAT(rx_broadcast, RX_BROADCAST_PKTS); + MAC_STAT(rx_lt64, RX_UNDERSIZE_PKTS); + MAC_STAT(rx_64, RX_64_PKTS); + MAC_STAT(rx_65_to_127, RX_65_TO_127_PKTS); + MAC_STAT(rx_128_to_255, RX_128_TO_255_PKTS); + MAC_STAT(rx_256_to_511, RX_256_TO_511_PKTS); + MAC_STAT(rx_512_to_1023, RX_512_TO_1023_PKTS); + MAC_STAT(rx_1024_to_15xx, RX_1024_TO_15XX_PKTS); + MAC_STAT(rx_15xx_to_jumbo, RX_15XX_TO_JUMBO_PKTS); + MAC_STAT(rx_gtjumbo, RX_GTJUMBO_PKTS); + mac_stats->rx_bad_lt64 = 0; + mac_stats->rx_bad_64_to_15xx = 0; + mac_stats->rx_bad_15xx_to_jumbo = 0; + MAC_STAT(rx_bad_gtjumbo, RX_JABBER_PKTS); + MAC_STAT(rx_overflow, RX_OVERFLOW_PKTS); + mac_stats->rx_missed = 0; + MAC_STAT(rx_false_carrier, RX_FALSE_CARRIER_PKTS); + MAC_STAT(rx_symbol_error, RX_SYMBOL_ERROR_PKTS); + MAC_STAT(rx_align_error, RX_ALIGN_ERROR_PKTS); + MAC_STAT(rx_length_error, RX_LENGTH_ERROR_PKTS); + MAC_STAT(rx_internal_error, RX_INTERNAL_ERROR_PKTS); + mac_stats->rx_good_lt64 = 0; + + efx->n_rx_nodesc_drop_cnt = dma_stats[MC_CMD_MAC_RX_NODESC_DROPS]; + + stat_val = dma_stats[MC_CMD_MAC_RX_LANES01_CHAR_ERR]; + mac_stats->rx_char_error_lane0 = (stat_val >> 0) & 0xffffffff; + mac_stats->rx_char_error_lane1 = (stat_val >> 32) & 0xffffffff; + stat_val = dma_stats[MC_CMD_MAC_RX_LANES23_CHAR_ERR]; + mac_stats->rx_char_error_lane2 = (stat_val >> 0) & 0xffffffff; + mac_stats->rx_char_error_lane3 = (stat_val >> 32) & 0xffffffff; + stat_val = dma_stats[MC_CMD_MAC_RX_LANES01_DISP_ERR]; + mac_stats->rx_disp_error_lane0 = (stat_val >> 0) & 0xffffffff; + mac_stats->rx_disp_error_lane1 = (stat_val >> 32) & 0xffffffff; + stat_val = dma_stats[MC_CMD_MAC_RX_LANES23_DISP_ERR]; + mac_stats->rx_disp_error_lane2 = (stat_val >> 0) & 0xffffffff; + mac_stats->rx_disp_error_lane3 = (stat_val >> 32) & 0xffffffff; + MAC_STAT(rx_match_fault, RX_MATCH_FAULT); +#undef MAC_STAT + + rmb(); + generation_start = dma_stats[MC_CMD_MAC_GENERATION_START]; + if (generation_end != generation_start) + return -EAGAIN; + + return 0; +} + +static void siena_update_nic_stats(struct efx_nic *efx) +{ + while (siena_try_update_nic_stats(efx) == -EAGAIN) + cpu_relax(); +} + +static void siena_start_nic_stats(struct efx_nic *efx) +{ + u64 *dma_stats = (u64 *)efx->stats_buffer.addr; + + dma_stats[MC_CMD_MAC_GENERATION_END] = STATS_GENERATION_INVALID; + + efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, + MC_CMD_MAC_NSTATS * sizeof(u64), 1, 0); +} + +static void siena_stop_nic_stats(struct efx_nic *efx) +{ + efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 0); +} + +void siena_print_fwver(struct efx_nic *efx, char *buf, size_t len) +{ + struct siena_nic_data *nic_data = efx->nic_data; + snprintf(buf, len, "%u.%u.%u.%u", + (unsigned int)(nic_data->fw_version >> 48), + (unsigned int)(nic_data->fw_version >> 32 & 0xffff), + (unsigned int)(nic_data->fw_version >> 16 & 0xffff), + (unsigned int)(nic_data->fw_version & 0xffff)); +} + +/************************************************************************** + * + * Wake on LAN + * + ************************************************************************** + */ + +static void siena_get_wol(struct efx_nic *efx, struct ethtool_wolinfo *wol) +{ + struct siena_nic_data *nic_data = efx->nic_data; + + wol->supported = WAKE_MAGIC; + if (nic_data->wol_filter_id != -1) + wol->wolopts = WAKE_MAGIC; + else + wol->wolopts = 0; + memset(&wol->sopass, 0, sizeof(wol->sopass)); +} + + +static int siena_set_wol(struct efx_nic *efx, u32 type) +{ + struct siena_nic_data *nic_data = efx->nic_data; + int rc; + + if (type & ~WAKE_MAGIC) + return -EINVAL; + + if (type & WAKE_MAGIC) { + if (nic_data->wol_filter_id != -1) + efx_mcdi_wol_filter_remove(efx, + nic_data->wol_filter_id); + rc = efx_mcdi_wol_filter_set_magic(efx, efx->mac_address, + &nic_data->wol_filter_id); + if (rc) + goto fail; + + pci_wake_from_d3(efx->pci_dev, true); + } else { + rc = efx_mcdi_wol_filter_reset(efx); + nic_data->wol_filter_id = -1; + pci_wake_from_d3(efx->pci_dev, false); + if (rc) + goto fail; + } + + return 0; + fail: + EFX_ERR(efx, "%s failed: type=%d rc=%d\n", __func__, type, rc); + return rc; +} + + +static void siena_init_wol(struct efx_nic *efx) +{ + struct siena_nic_data *nic_data = efx->nic_data; + int rc; + + rc = efx_mcdi_wol_filter_get_magic(efx, &nic_data->wol_filter_id); + + if (rc != 0) { + /* If it failed, attempt to get into a synchronised + * state with MC by resetting any set WoL filters */ + efx_mcdi_wol_filter_reset(efx); + nic_data->wol_filter_id = -1; + } else if (nic_data->wol_filter_id != -1) { + pci_wake_from_d3(efx->pci_dev, true); + } +} + + +/************************************************************************** + * + * Revision-dependent attributes used by efx.c and nic.c + * + ************************************************************************** + */ + +struct efx_nic_type siena_a0_nic_type = { + .probe = siena_probe_nic, + .remove = siena_remove_nic, + .init = siena_init_nic, + .fini = efx_port_dummy_op_void, + .monitor = NULL, + .reset = siena_reset_hw, + .probe_port = siena_probe_port, + .remove_port = siena_remove_port, + .prepare_flush = efx_port_dummy_op_void, + .update_stats = siena_update_nic_stats, + .start_stats = siena_start_nic_stats, + .stop_stats = siena_stop_nic_stats, + .set_id_led = efx_mcdi_set_id_led, + .push_irq_moderation = siena_push_irq_moderation, + .push_multicast_hash = siena_push_multicast_hash, + .reconfigure_port = efx_mcdi_phy_reconfigure, + .get_wol = siena_get_wol, + .set_wol = siena_set_wol, + .resume_wol = siena_init_wol, + .test_registers = siena_test_registers, + .test_memory = siena_test_tables, + .test_nvram = efx_mcdi_nvram_test_all, + .default_mac_ops = &efx_mcdi_mac_operations, + + .revision = EFX_REV_SIENA_A0, + .dl_revision = "siena/a0", + .mem_map_size = (FR_CZ_MC_TREG_SMEM + + FR_CZ_MC_TREG_SMEM_STEP * FR_CZ_MC_TREG_SMEM_ROWS), + .txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL, + .rxd_ptr_tbl_base = FR_BZ_RX_DESC_PTR_TBL, + .buf_tbl_base = FR_BZ_BUF_FULL_TBL, + .evq_ptr_tbl_base = FR_BZ_EVQ_PTR_TBL, + .evq_rptr_tbl_base = FR_BZ_EVQ_RPTR, + .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH), + .rx_buffer_padding = 0, + .max_interrupt_mode = EFX_INT_MODE_MSIX, + .phys_addr_channels = 32, /* Hardware limit is 64, but the legacy + * interrupt handler only supports 32 + * channels */ + .resources = { + .hdr.type = EFX_DL_FALCON_RESOURCES, + .rxq_min = 0, .rxq_lim = 1024, + .txq_min = 0, .txq_lim = 1024, + .evq_int_min = 0, .evq_int_lim = 64, + .evq_timer_min = 64, .evq_timer_lim = 1024, + }, +#if !defined(EFX_USE_KCOMPAT) || defined(NETIF_F_IPV6_CSUM) + .offload_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM, +#else + .offload_features = NETIF_F_HW_CSUM, +#endif + .reset_world_flags = ETH_RESET_MGMT << ETH_RESET_SHARED_SHIFT, +}; diff -r aa133dee7792 drivers/net/sfc/spi.h --- a/drivers/net/sfc/spi.h +++ b/drivers/net/sfc/spi.h @@ -1,28 +1,11 @@ /**************************************************************************** - * Driver for Solarflare network controllers - * (including support for SFE4001 10GBT NIC) - * - * Copyright 2005: Fen Systems Ltd. - * Copyright 2006: Solarflare Communications Inc, - * 9501 Jeronimo Road, Suite 250, - * Irvine, CA 92618, USA - * - * Initially developed by Michael Brown - * Maintained by Solarflare Communications + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005 Fen Systems Ltd. + * Copyright 2006 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_SPI_H @@ -36,61 +19,20 @@ * *************************************************************************/ -/* - * Commands common to all known devices. - * - */ +#define SPI_WRSR 0x01 /* Write status register */ +#define SPI_WRITE 0x02 /* Write data to memory array */ +#define SPI_READ 0x03 /* Read data from memory array */ +#define SPI_WRDI 0x04 /* Reset write enable latch */ +#define SPI_RDSR 0x05 /* Read status register */ +#define SPI_WREN 0x06 /* Set write enable latch */ +#define SPI_SST_EWSR 0x50 /* SST: Enable write to status register */ -/* 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 - * - ************************************************************************** - */ +#define SPI_STATUS_WPEN 0x80 /* Write-protect pin enabled */ +#define SPI_STATUS_BP2 0x10 /* Block protection bit 2 */ +#define SPI_STATUS_BP1 0x08 /* Block protection bit 1 */ +#define SPI_STATUS_BP0 0x04 /* Block protection bit 0 */ +#define SPI_STATUS_WEN 0x02 /* State of the write enable latch */ +#define SPI_STATUS_NRDY 0x01 /* Device busy flag */ /** * struct efx_spi_device - an Efx SPI (Serial Peripheral Interface) device @@ -108,8 +50,6 @@ * 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; @@ -119,68 +59,36 @@ 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 +int falcon_spi_cmd(struct efx_nic *efx, + const struct efx_spi_device *spi, unsigned int command, + int address, const void* in, void *out, size_t len); +int falcon_spi_wait_write(struct efx_nic *efx, + const struct efx_spi_device *spi); +int falcon_spi_read(struct efx_nic *efx, + const struct efx_spi_device *spi, loff_t start, + size_t len, size_t *retlen, u8 *buffer); +int falcon_spi_write(struct efx_nic *efx, + const struct efx_spi_device *spi, loff_t start, + size_t len, size_t *retlen, const u8 *buffer); -/** - * 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}. +/* + * SFC4000 flash is partitioned into: + * 0-0x400 chip and board config (see falcon_hwdefs.h) + * 0x400-0x8000 unused (or may contain VPD if EEPROM not present) + * 0x8000-end boot code (mapped to PCI expansion ROM) + * SFC4000 small EEPROM (size < 0x400) is used for VPD only. + * SFC4000 large EEPROM (size >= 0x400) is partitioned into: + * 0-0x400 chip and board config + * configurable VPD + * 0x800-0x1800 boot config + * Aside from the chip and board config, all of these are optional and may + * be absent or truncated depending on the devices used. */ -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)); -} +#define FALCON_NVCONFIG_END 0x400U +#define FALCON_FLASH_BOOTCODE_START 0x8000U +#define EFX_EEPROM_BOOTCONFIG_START 0x800U +#define EFX_EEPROM_BOOTCONFIG_END 0x1800U #endif /* EFX_SPI_H */ diff -r aa133dee7792 drivers/net/sfc/tenxpress.c --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -1,82 +1,76 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2007-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - ****************************************************************************/ + */ #include #include #include "efx.h" #include "debugfs.h" -#include "gmii.h" #include "mdio_10g.h" -#include "falcon.h" +#include "nic.h" #include "phy.h" -#include "falcon_hwdefs.h" -#include "boards.h" +#include "regs.h" +#include "workarounds.h" +#include "selftest.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) +/* We expect these MMDs to be in the package. SFT9001 also has a + * clause 22 extension MMD, but since it doesn't have all the generic + * MMD registers it is pointless to include it here. + */ +#define TENXPRESS_REQUIRED_DEVS (MDIO_DEVS_PMAPMD | \ + MDIO_DEVS_PCS | \ + MDIO_DEVS_PHYXS | \ + MDIO_DEVS_AN) -#define TENXPRESS_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ - (1 << LOOPBACK_PCS) | \ - (1 << LOOPBACK_PMAPMD) | \ - (1 << LOOPBACK_NETWORK)) +#define SFX7101_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ + (1 << LOOPBACK_PCS) | \ + (1 << LOOPBACK_PMAPMD) | \ + (1 << LOOPBACK_PHYXS_WS)) + +#define SFT9001_LOOPBACKS ((1 << LOOPBACK_GPHY) | \ + (1 << LOOPBACK_PHYXS) | \ + (1 << LOOPBACK_PCS) | \ + (1 << LOOPBACK_PMAPMD) | \ + (1 << LOOPBACK_PHYXS_WS)) /* We complain if we fail to see the link partner as 10G capable this many * times in a row (must be > 1 as sampling the autoneg. registers is racy) */ #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 +#define PMA_PMD_XCONTROL_REG 49152 +#define PMA_PMD_EXT_GMII_EN_LBN 1 +#define PMA_PMD_EXT_GMII_EN_WIDTH 1 +#define PMA_PMD_EXT_CLK_OUT_LBN 2 +#define PMA_PMD_EXT_CLK_OUT_WIDTH 1 +#define PMA_PMD_LNPGA_POWERDOWN_LBN 8 /* SFX7101 only */ +#define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1 +#define PMA_PMD_EXT_CLK312_LBN 8 /* SFT9001 only */ +#define PMA_PMD_EXT_CLK312_WIDTH 1 +#define PMA_PMD_EXT_LPOWER_LBN 12 +#define PMA_PMD_EXT_LPOWER_WIDTH 1 +#define PMA_PMD_EXT_ROBUST_LBN 14 +#define PMA_PMD_EXT_ROBUST_WIDTH 1 +#define PMA_PMD_EXT_SSR_LBN 15 +#define PMA_PMD_EXT_SSR_WIDTH 1 /* extended status register */ -#define PMA_PMD_XSTATUS_REG 0xc001 +#define PMA_PMD_XSTATUS_REG 49153 +#define PMA_PMD_XSTAT_MDIX_LBN 14 #define PMA_PMD_XSTAT_FLP_LBN (12) - /* LED control register */ -#define PMA_PMD_LED_CTRL_REG (0xc007) +#define PMA_PMD_LED_CTRL_REG 49159 #define PMA_PMA_LED_ACTIVITY_LBN (3) /* LED function override register */ -#define PMA_PMD_LED_OVERR_REG (0xc009) +#define PMA_PMD_LED_OVERR_REG 49161 /* 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) @@ -87,37 +81,11 @@ #define PMA_PMD_LED_ON (1) #define PMA_PMD_LED_OFF (2) #define PMA_PMD_LED_FLASH (3) +#define PMA_PMD_LED_MASK 3 /* All LEDs under hardware control */ -#define PMA_PMD_LED_FULL_AUTO (0) +#define SFT9001_PMA_PMD_LED_DEFAULT 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" -}; +#define SFX7101_PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) /* Identifier registers: each identifier has 4 part number and 2 revision * registers starting at one of these addresses */ @@ -125,571 +93,812 @@ #define PMA_PMD_DSP_ID_REG 49180 #define PMA_PMD_FIRMWARE_ID_REG 49186 -/* Misc register defines */ -#define PCS_CLOCK_CTRL_REG 0xd801 +#define PMA_PMD_SPEED_ENABLE_REG 49192 +#define PMA_PMD_100TX_ADV_LBN 1 +#define PMA_PMD_100TX_ADV_WIDTH 1 +#define PMA_PMD_1000T_ADV_LBN 2 +#define PMA_PMD_1000T_ADV_WIDTH 1 +#define PMA_PMD_10000T_ADV_LBN 3 +#define PMA_PMD_10000T_ADV_WIDTH 1 +#define PMA_PMD_SPEED_LBN 4 +#define PMA_PMD_SPEED_WIDTH 4 + +/* Cable diagnostics - SFT9001 only */ +#define PMA_PMD_CDIAG_CTRL_REG 49213 +#define CDIAG_CTRL_IMMED_LBN 15 +#define CDIAG_CTRL_BRK_LINK_LBN 12 +#define CDIAG_CTRL_IN_PROG_LBN 11 +#define CDIAG_CTRL_LEN_UNIT_LBN 10 +#define CDIAG_CTRL_LEN_METRES 1 +#define PMA_PMD_CDIAG_RES_REG 49174 +#define CDIAG_RES_A_LBN 12 +#define CDIAG_RES_B_LBN 8 +#define CDIAG_RES_C_LBN 4 +#define CDIAG_RES_D_LBN 0 +#define CDIAG_RES_WIDTH 4 +#define CDIAG_RES_OPEN 2 +#define CDIAG_RES_OK 1 +#define CDIAG_RES_INVALID 0 +/* Set of 4 registers for pairs A-D */ +#define PMA_PMD_CDIAG_LEN_REG 49175 + +/* Serdes control registers - SFT9001 only */ +#define PMA_PMD_CSERDES_CTRL_REG 64258 +/* Set the 156.25 MHz output to 312.5 MHz to drive Falcon's XMAC */ +#define PMA_PMD_CSERDES_DEFAULT 0x000f + +/* Misc register defines - SFX7101 only */ +#define PCS_CLOCK_CTRL_REG 55297 #define PLL312_RST_N_LBN 2 -#define PCS_SOFT_RST2_REG 0xd806 +#define PCS_SOFT_RST2_REG 55302 #define SERDES_RST_N_LBN 13 #define XGXS_RST_N_LBN 12 -#define PCS_TEST_SELECT_REG 0xd807 /* PRM 10.5.8 */ +#define PCS_TEST_SELECT_REG 55303 /* PRM 10.5.8 */ #define CLK312_EN_LBN 3 /* PHYXS registers */ +#define PHYXS_XCONTROL_REG 49152 +#define PHYXS_RESET_LBN 15 +#define PHYXS_RESET_WIDTH 1 + #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_STATUS_REG 53248 +#define PCS_BOOT_FATAL_ERROR_LBN 0 +#define PCS_BOOT_PROGRESS_LBN 1 +#define PCS_BOOT_PROGRESS_WIDTH 2 +#define PCS_BOOT_PROGRESS_INIT 0 +#define PCS_BOOT_PROGRESS_WAIT_MDIO 1 +#define PCS_BOOT_PROGRESS_CHECKSUM 2 +#define PCS_BOOT_PROGRESS_JUMP 3 +#define PCS_BOOT_DOWNLOAD_WAIT_LBN 3 +#define PCS_BOOT_CODE_STARTED_LBN 4 -#define PCS_BOOT_MAX_DELAY (100) -#define PCS_BOOT_POLL_DELAY (10) +/* 100M/1G PHY registers */ +#define GPHY_XCONTROL_REG 49152 +#define GPHY_ISOLATE_LBN 10 +#define GPHY_ISOLATE_WIDTH 1 +#define GPHY_DUPLEX_LBN 8 +#define GPHY_DUPLEX_WIDTH 1 +#define GPHY_LOOPBACK_NEAR_LBN 14 +#define GPHY_LOOPBACK_NEAR_WIDTH 1 + +#define C22EXT_STATUS_REG 49153 +#define C22EXT_STATUS_LINK_LBN 2 +#define C22EXT_STATUS_LINK_WIDTH 1 + +#define C22EXT_MSTSLV_CTRL 49161 +#define C22EXT_MSTSLV_CTRL_ADV_1000_HD_LBN 8 +#define C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN 9 + +#define C22EXT_MSTSLV_STATUS 49162 +#define C22EXT_MSTSLV_STATUS_LP_1000_HD_LBN 10 +#define C22EXT_MSTSLV_STATUS_LP_1000_FD_LBN 11 #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; + enum efx_phy_mode phy_mode; int bad_lp_tries; }; -static int tenxpress_state_is(struct efx_nic *efx, int state) +static ssize_t show_phy_short_reach(struct device *dev, + struct device_attribute *attr, char *buf) { - struct tenxpress_phy_data *phy_data = efx->phy_data; - return (phy_data != NULL) && (state == phy_data->state); + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + int reg; + + reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TXPWR); + return sprintf(buf, "%d\n", !!(reg & MDIO_PMA_10GBT_TXPWR_SHORT)); } -void tenxpress_set_state(struct efx_nic *efx, - enum tenxpress_state state) +static ssize_t set_phy_short_reach(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct tenxpress_phy_data *phy_data = efx->phy_data; - if (phy_data != NULL) - phy_data->state = state; + struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); + int rc; + + rtnl_lock(); + if (efx->state != STATE_RUNNING) + rc = -EBUSY; + else { + efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TXPWR, + MDIO_PMA_10GBT_TXPWR_SHORT, + count != 0 && *buf != '0'); + rc = efx_reconfigure_port(efx); + } + rtnl_unlock(); + + return rc < 0 ? rc : (ssize_t)count; } -void tenxpress_crc_err(struct efx_nic *efx) +static DEVICE_ATTR(phy_short_reach, 0644, show_phy_short_reach, + set_phy_short_reach); + +int sft9001_wait_boot(struct efx_nic *efx) { - struct tenxpress_phy_data *phy_data = efx->phy_data; - if (phy_data != NULL) - atomic_inc(&phy_data->bad_crc_count); -} + unsigned long timeout = jiffies + HZ + 1; + int boot_stat; -#ifdef CONFIG_SFC_DEBUGFS + for (;;) { + boot_stat = efx_mdio_read(efx, MDIO_MMD_PCS, + PCS_BOOT_STATUS_REG); + if (boot_stat >= 0) { + EFX_LOG(efx, "PHY boot status = %#x\n", boot_stat); + switch (boot_stat & + ((1 << PCS_BOOT_FATAL_ERROR_LBN) | + (3 << PCS_BOOT_PROGRESS_LBN) | + (1 << PCS_BOOT_DOWNLOAD_WAIT_LBN) | + (1 << PCS_BOOT_CODE_STARTED_LBN))) { + case ((1 << PCS_BOOT_FATAL_ERROR_LBN) | + (PCS_BOOT_PROGRESS_CHECKSUM << + PCS_BOOT_PROGRESS_LBN)): + case ((1 << PCS_BOOT_FATAL_ERROR_LBN) | + (PCS_BOOT_PROGRESS_INIT << + PCS_BOOT_PROGRESS_LBN) | + (1 << PCS_BOOT_DOWNLOAD_WAIT_LBN)): + return -EINVAL; + case ((PCS_BOOT_PROGRESS_WAIT_MDIO << + PCS_BOOT_PROGRESS_LBN) | + (1 << PCS_BOOT_DOWNLOAD_WAIT_LBN)): + return (efx->phy_mode & PHY_MODE_SPECIAL) ? + 0 : -EIO; + case ((PCS_BOOT_PROGRESS_JUMP << + PCS_BOOT_PROGRESS_LBN) | + (1 << PCS_BOOT_CODE_STARTED_LBN)): + case ((PCS_BOOT_PROGRESS_JUMP << + PCS_BOOT_PROGRESS_LBN) | + (1 << PCS_BOOT_DOWNLOAD_WAIT_LBN) | + (1 << PCS_BOOT_CODE_STARTED_LBN)): + return (efx->phy_mode & PHY_MODE_SPECIAL) ? + -EIO : 0; + default: + if (boot_stat & (1 << PCS_BOOT_FATAL_ERROR_LBN)) + return -EIO; + break; + } + } -/* 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; + if (time_after_eq(jiffies, timeout)) + return -ETIMEDOUT; - 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 : ' '; + msleep(50); } } -static int tenxpress_debugfs_init(struct efx_nic *efx) +/* Initialise the part post power on reset or software special reset */ +static int tenxpress_init(struct efx_nic *efx) { - struct tenxpress_phy_data *phy_data = efx->phy_data; - int lane, rc; + int reg; - for (lane = 0; lane < 4; lane++) - phy_data->phy_snr[lane] = lane; + if (efx->phy_type == PHY_TYPE_SFX7101) { + /* Enable 312.5 MHz clock */ + efx_mdio_write(efx, MDIO_MMD_PCS, PCS_TEST_SELECT_REG, + 1 << CLK312_EN_LBN); + } else { + /* Enable 312.5 MHz clock and GMII */ + reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG); + reg |= ((1 << PMA_PMD_EXT_GMII_EN_LBN) | + (1 << PMA_PMD_EXT_CLK_OUT_LBN) | + (1 << PMA_PMD_EXT_CLK312_LBN) | + (1 << PMA_PMD_EXT_ROBUST_LBN)); - 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); + efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG, reg); + efx_mdio_set_flag(efx, MDIO_MMD_C22EXT, + GPHY_XCONTROL_REG, 1 << GPHY_ISOLATE_LBN, + false); } - if (!count) { - EFX_ERR(efx, "%s: PHY boot timed out. Last status " - "%x\n", __func__, - (boot_stat >> PCS_BOOT_PROGRESS_LBN) & - ((1 << PCS_BOOT_PROGRESS_WIDTH) - 1)); - return -ETIMEDOUT; + /* Set the LEDs up as: Green = Link, Amber = Link/Act, Red = Off */ + if (efx->phy_type == PHY_TYPE_SFX7101) { + efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG, + 1 << PMA_PMA_LED_ACTIVITY_LBN, true); + efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG, + SFX7101_PMA_PMD_LED_DEFAULT); } return 0; } -static void tenxpress_reset_xaui(struct efx_nic *efx); +static int tenxpress_phy_probe(struct efx_nic *efx) +{ + struct tenxpress_phy_data *phy_data; + int rc; -/* Initialise the part post power on reset or software special reset */ -static int tenxpress_init(struct efx_nic *efx) -{ - int rc, reg; + /* Allocate phy private storage */ + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + efx->phy_data = phy_data; + phy_data->phy_mode = efx->phy_mode; - /* 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); + /* Create any special files */ + if (efx->phy_type == PHY_TYPE_SFT9001B) { + rc = device_create_file(&efx->pci_dev->dev, + &dev_attr_phy_short_reach); + if (rc) + goto fail; + } - rc = tenxpress_phy_check(efx); - if (rc < 0) - return rc; + if (efx->phy_type == PHY_TYPE_SFX7101) { + efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45; - /* 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); + efx->loopback_modes = SFX7101_LOOPBACKS | FALCON_XMAC_LOOPBACKS; + efx->startup_loopback_mode = LOOPBACK_PCS; - reg = PMA_PMD_LED_DEFAULT; - mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, - PMA_PMD_LED_OVERR_REG, reg); + strlcpy(efx->phy_name, "SFX7101 10GBASE-T", + sizeof(efx->phy_name)); + efx->link_advertising = (ADVERTISED_TP | ADVERTISED_Autoneg | + ADVERTISED_10000baseT_Full); + } else { + efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + + efx->loopback_modes = (SFT9001_LOOPBACKS | + FALCON_XMAC_LOOPBACKS | + FALCON_GMAC_LOOPBACKS); + efx->startup_loopback_mode = LOOPBACK_PCS; + + strlcpy(efx->phy_name, "SFT9001 100/1000/10GBASE-T", + sizeof(efx->phy_name)); + + efx->link_advertising = (ADVERTISED_TP | ADVERTISED_Autoneg | + ADVERTISED_10000baseT_Full | + ADVERTISED_1000baseT_Full | + ADVERTISED_100baseT_Full); + } + + return 0; + +fail_devattr: + if (efx->phy_type == PHY_TYPE_SFT9001B) + device_remove_file(&efx->pci_dev->dev, + &dev_attr_phy_short_reach); +fail: + kfree(efx->phy_data); + efx->phy_data = NULL; return rc; } static int tenxpress_phy_init(struct efx_nic *efx) { - struct tenxpress_phy_data *phy_data; - int rc = 0; + int rc; - phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); - efx->phy_data = phy_data; - phy_data->phy_powered = efx->phy_powered; + falcon_board(efx)->type->init_phy(efx); - tenxpress_set_state(efx, TENXPRESS_STATUS_NORMAL); + if (!(efx->phy_mode & PHY_MODE_SPECIAL)) { + if (efx->phy_type == PHY_TYPE_SFT9001A) { + int reg; + reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, + PMA_PMD_XCONTROL_REG); + reg |= (1 << PMA_PMD_EXT_SSR_LBN); + efx_mdio_write(efx, MDIO_MMD_PMAPMD, + PMA_PMD_XCONTROL_REG, reg); + mdelay(200); + } - rc = mdio_clause45_wait_reset_mmds(efx, - TENXPRESS_REQUIRED_DEVS); - if (rc < 0) - goto fail; + rc = efx_mdio_wait_reset_mmds(efx, TENXPRESS_REQUIRED_DEVS); + if (rc < 0) + return rc; - rc = mdio_clause45_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0); - if (rc < 0) - goto fail; + rc = efx_mdio_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0); + if (rc < 0) + return rc; + } rc = tenxpress_init(efx); if (rc < 0) - goto fail; + return rc; -#ifdef CONFIG_SFC_DEBUGFS - rc = tenxpress_debugfs_init(efx); - if (rc < 0) - goto fail; -#endif + /* Reinitialise flow control settings */ + efx_link_set_wanted_fc(efx, efx->wanted_fc); + efx_mdio_an_reconfigure(efx); schedule_timeout_uninterruptible(HZ / 5); /* 200ms */ - /* Let XGXS and SerDes out of reset and resets 10XPress */ + /* Let XGXS and SerDes out of reset */ falcon_reset_xaui(efx); return 0; - - fail: - kfree(efx->phy_data); - efx->phy_data = NULL; - return rc; } +/* Perform a "special software reset" on the PHY. The caller is + * responsible for saving and restoring the PHY hardware registers + * properly, and masking/unmasking LASI */ static int tenxpress_special_reset(struct efx_nic *efx) { int rc, reg; - EFX_TRACE(efx, "%s\n", __func__); + /* The XGMAC clock is driven from the SFC7101/SFT9001 312MHz clock, so + * a special software reset can glitch the XGMAC sufficiently for stats + * requests to fail. */ + falcon_stop_nic_stats(efx); /* Initiate reset */ - reg = mdio_clause45_read(efx, efx->mii.phy_id, - MDIO_MMD_PMAPMD, PMA_PMD_EXT_CTRL_REG); + reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_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); + efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG, reg); - msleep(200); + mdelay(200); /* Wait for the blocks to come out of reset */ - rc = mdio_clause45_wait_reset_mmds(efx, - TENXPRESS_REQUIRED_DEVS); + rc = efx_mdio_wait_reset_mmds(efx, TENXPRESS_REQUIRED_DEVS); if (rc < 0) - return rc; + goto out; /* Try and reconfigure the device */ rc = tenxpress_init(efx); if (rc < 0) - return rc; + goto out; + + /* Wait for the XGXS state machine to churn */ + mdelay(10); +out: + falcon_start_nic_stats(efx); + return rc; +} + +static void sfx7101_check_bad_lp(struct efx_nic *efx, bool link_ok) +{ + struct tenxpress_phy_data *pd = efx->phy_data; + bool bad_lp; + int reg; + + if (link_ok) { + bad_lp = false; + } else { + /* Check that AN has started but not completed. */ + reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_STAT1); + if (!(reg & MDIO_AN_STAT1_LPABLE)) + return; /* LP status is unknown */ + bad_lp = !(reg & MDIO_AN_STAT1_COMPLETE); + if (bad_lp) + pd->bad_lp_tries++; + } + + /* Nothing to do if all is well and was previously so. */ + if (!pd->bad_lp_tries) + return; + + /* Use the RX (red) LED as an error indicator once we've seen AN + * failure several times in a row, and also log a message. */ + if (!bad_lp || pd->bad_lp_tries == MAX_BAD_LP_TRIES) { + reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, + PMA_PMD_LED_OVERR_REG); + reg &= ~(PMA_PMD_LED_MASK << PMA_PMD_LED_RX_LBN); + if (!bad_lp) { + reg |= PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN; + } else { + reg |= PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN; + EFX_ERR(efx, "appears to be plugged into a port" + " that is not 10GBASE-T capable. The PHY" + " supports 10GBASE-T ONLY, so no link can" + " be established\n"); + } + efx_mdio_write(efx, MDIO_MMD_PMAPMD, + PMA_PMD_LED_OVERR_REG, reg); + pd->bad_lp_tries = bad_lp; + } +} + +static bool sfx7101_link_ok(struct efx_nic *efx) +{ + return efx_mdio_links_ok(efx, + MDIO_DEVS_PMAPMD | + MDIO_DEVS_PCS | + MDIO_DEVS_PHYXS); +} + +static bool sft9001_link_ok(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + u32 reg; + + if (efx_phy_mode_disabled(efx->phy_mode)) + return false; + else if (efx->loopback_mode == LOOPBACK_GPHY) + return true; + else if (efx->loopback_mode) + return efx_mdio_links_ok(efx, + MDIO_DEVS_PMAPMD | + MDIO_DEVS_PHYXS); + + /* We must use the same definition of link state as LASI, + * otherwise we can miss a link state transition + */ + if (ecmd->speed == 10000) { + reg = efx_mdio_read(efx, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT1); + return reg & MDIO_PCS_10GBRT_STAT1_BLKLK; + } else { + reg = efx_mdio_read(efx, MDIO_MMD_C22EXT, C22EXT_STATUS_REG); + return reg & (1 << C22EXT_STATUS_LINK_LBN); + } +} + +static void tenxpress_ext_loopback(struct efx_nic *efx) +{ + efx_mdio_set_flag(efx, MDIO_MMD_PHYXS, PHYXS_TEST1, + 1 << LOOPBACK_NEAR_LBN, + efx->loopback_mode == LOOPBACK_PHYXS); + if (efx->phy_type != PHY_TYPE_SFX7101) + efx_mdio_set_flag(efx, MDIO_MMD_C22EXT, GPHY_XCONTROL_REG, + 1 << GPHY_LOOPBACK_NEAR_LBN, + efx->loopback_mode == LOOPBACK_GPHY); +} + +static void tenxpress_low_power(struct efx_nic *efx) +{ + if (efx->phy_type == PHY_TYPE_SFX7101) + efx_mdio_set_mmds_lpower( + efx, !!(efx->phy_mode & PHY_MODE_LOW_POWER), + TENXPRESS_REQUIRED_DEVS); + else + efx_mdio_set_flag( + efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG, + 1 << PMA_PMD_EXT_LPOWER_LBN, + !!(efx->phy_mode & PHY_MODE_LOW_POWER)); +} + +static int tenxpress_phy_reconfigure(struct efx_nic *efx) +{ + struct tenxpress_phy_data *phy_data = efx->phy_data; + bool phy_mode_change, loop_reset; + + if (efx->phy_mode & (PHY_MODE_OFF | PHY_MODE_SPECIAL)) { + phy_data->phy_mode = efx->phy_mode; + return 0; + } + + phy_mode_change = (efx->phy_mode == PHY_MODE_NORMAL && + phy_data->phy_mode != PHY_MODE_NORMAL); + loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, LOOPBACKS_EXTERNAL(efx)) || + LOOPBACK_CHANGED(phy_data, efx, 1 << LOOPBACK_GPHY)); + + if (loop_reset || phy_mode_change) { + tenxpress_special_reset(efx); + + /* Reset XAUI if we were in 10G, and are staying + * in 10G. If we're moving into and out of 10G + * then xaui will be reset anyway */ + if (EFX_IS10G(efx)) + falcon_reset_xaui(efx); + } + + tenxpress_low_power(efx); + efx_mdio_transmit_disable(efx); + efx_mdio_phy_reconfigure(efx); + tenxpress_ext_loopback(efx); + efx_mdio_an_reconfigure(efx); + + phy_data->loopback_mode = efx->loopback_mode; + phy_data->phy_mode = efx->phy_mode; return 0; } -static void tenxpress_set_bad_lp(struct efx_nic *efx, int bad_lp) +static void +tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd); + +/* Poll for link state changes */ +static bool tenxpress_phy_poll(struct efx_nic *efx) { - struct tenxpress_phy_data *pd = efx->phy_data; - int reg; + struct efx_link_state old_state = efx->link_state; - /* Nothing to do if all is well and was previously so. */ - if (!(bad_lp || pd->bad_lp_tries)) - return; + if (efx->phy_type == PHY_TYPE_SFX7101) { + efx->link_state.up = sfx7101_link_ok(efx); + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->link_state.fc = efx_mdio_get_pause(efx); - reg = mdio_clause45_read(efx, efx->mii.phy_id, - MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG); + sfx7101_check_bad_lp(efx, efx->link_state.up); + } else { + struct ethtool_cmd ecmd; - if (bad_lp) - pd->bad_lp_tries++; - else - pd->bad_lp_tries = 0; + /* Check the LASI alarm first */ + if (efx->loopback_mode == LOOPBACK_NONE && + !(efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT) & + MDIO_PMA_LASI_LSALARM)) + return false; - if (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); + tenxpress_get_settings(efx, &ecmd); + + efx->link_state.up = sft9001_link_ok(efx, &ecmd); + efx->link_state.speed = ecmd.speed; + efx->link_state.fd = (ecmd.duplex == DUPLEX_FULL); + efx->link_state.fc = efx_mdio_get_pause(efx); } - mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, - PMA_PMD_LED_OVERR_REG, reg); + + return !efx_link_state_equal(&efx->link_state, &old_state); } -/* 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->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) +static void sfx7101_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); + efx_mdio_write(efx, 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. */ + /* 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 */ +} + +static void tenxpress_phy_remove(struct efx_nic *efx) +{ + if (efx->phy_type == PHY_TYPE_SFT9001B) + device_remove_file(&efx->pci_dev->dev, + &dev_attr_phy_short_reach); 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) +/* Override the RX, TX and link LEDs */ +void tenxpress_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) { 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; + switch (mode) { + case EFX_LED_OFF: + reg = (PMA_PMD_LED_OFF << PMA_PMD_LED_TX_LBN) | + (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) | + (PMA_PMD_LED_OFF << PMA_PMD_LED_LINK_LBN); + break; + case EFX_LED_ON: + reg = (PMA_PMD_LED_ON << PMA_PMD_LED_TX_LBN) | + (PMA_PMD_LED_ON << PMA_PMD_LED_RX_LBN) | + (PMA_PMD_LED_ON << PMA_PMD_LED_LINK_LBN); + break; + default: + if (efx->phy_type == PHY_TYPE_SFX7101) + reg = SFX7101_PMA_PMD_LED_DEFAULT; + else + reg = SFT9001_PMA_PMD_LED_DEFAULT; + break; + } - mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, - PMA_PMD_LED_OVERR_REG, reg); + efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG, reg); } -static void tenxpress_reset_xaui(struct efx_nic *efx) +static const char *const sfx7101_test_names[] = { + "bist" +}; + +static const char *sfx7101_test_name(struct efx_nic *efx, unsigned int index) { - 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); - - /* 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); - - /* 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); + if (index < ARRAY_SIZE(sfx7101_test_names)) + return sfx7101_test_names[index]; + return NULL; } +static int +sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags) +{ + int rc; -struct efx_phy_operations falcon_tenxpress_phy_ops = { + if (!(flags & ETH_TEST_FL_OFFLINE)) + return 0; + + /* BIST is automatically run after a special software reset */ + rc = tenxpress_special_reset(efx); + results[0] = rc ? -1 : 1; + + efx_mdio_an_reconfigure(efx); + + return rc; +} + +static const char *const sft9001_test_names[] = { + "bist", + "cable.pairA.status", + "cable.pairB.status", + "cable.pairC.status", + "cable.pairD.status", + "cable.pairA.length", + "cable.pairB.length", + "cable.pairC.length", + "cable.pairD.length", +}; + +static const char *sft9001_test_name(struct efx_nic *efx, unsigned int index) +{ + if (index < ARRAY_SIZE(sft9001_test_names)) + return sft9001_test_names[index]; + return NULL; +} + +static int sft9001_run_tests(struct efx_nic *efx, int *results, unsigned flags) +{ + int rc = 0, rc2, i, ctrl_reg, res_reg; + + /* Initialise cable diagnostic results to unknown failure */ + for (i = 1; i < 9; ++i) + results[i] = -1; + + /* Run cable diagnostics; wait up to 5 seconds for them to complete. + * A cable fault is not a self-test failure, but a timeout is. */ + ctrl_reg = ((1 << CDIAG_CTRL_IMMED_LBN) | + (CDIAG_CTRL_LEN_METRES << CDIAG_CTRL_LEN_UNIT_LBN)); + if (flags & ETH_TEST_FL_OFFLINE) { + /* Break the link in order to run full diagnostics. We + * must reset the PHY to resume normal service. */ + ctrl_reg |= (1 << CDIAG_CTRL_BRK_LINK_LBN); + } + efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_CDIAG_CTRL_REG, + ctrl_reg); + i = 0; + while (efx_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_CDIAG_CTRL_REG) & + (1 << CDIAG_CTRL_IN_PROG_LBN)) { + if (++i == 50) { + rc = -ETIMEDOUT; + goto out; + } + msleep(100); + } + res_reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_CDIAG_RES_REG); + for (i = 0; i < 4; i++) { + int pair_res = + (res_reg >> (CDIAG_RES_A_LBN - i * CDIAG_RES_WIDTH)) + & ((1 << CDIAG_RES_WIDTH) - 1); + int len_reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, + PMA_PMD_CDIAG_LEN_REG + i); + if (pair_res == CDIAG_RES_OK) + results[1 + i] = 1; + else if (pair_res == CDIAG_RES_INVALID) + results[1 + i] = -1; + else + results[1 + i] = -pair_res; + if (pair_res != CDIAG_RES_INVALID && + pair_res != CDIAG_RES_OPEN && + len_reg != 0xffff) + results[5 + i] = len_reg; + } + +out: + if (flags & ETH_TEST_FL_OFFLINE) { + /* Reset, running the BIST and then resuming normal service. */ + rc2 = tenxpress_special_reset(efx); + results[0] = rc2 ? -1 : 1; + if (!rc) + rc = rc2; + + efx_mdio_an_reconfigure(efx); + } + + return rc; +} + +static void +tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + u32 adv = 0, lpa = 0; + int reg; + + if (efx->phy_type != PHY_TYPE_SFX7101) { + reg = efx_mdio_read(efx, MDIO_MMD_C22EXT, C22EXT_MSTSLV_CTRL); + if (reg & (1 << C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN)) + adv |= ADVERTISED_1000baseT_Full; + reg = efx_mdio_read(efx, MDIO_MMD_C22EXT, C22EXT_MSTSLV_STATUS); + if (reg & (1 << C22EXT_MSTSLV_STATUS_LP_1000_HD_LBN)) + lpa |= ADVERTISED_1000baseT_Half; + if (reg & (1 << C22EXT_MSTSLV_STATUS_LP_1000_FD_LBN)) + lpa |= ADVERTISED_1000baseT_Full; + } + reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL); + if (reg & MDIO_AN_10GBT_CTRL_ADV10G) + adv |= ADVERTISED_10000baseT_Full; + reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); + if (reg & MDIO_AN_10GBT_STAT_LP10G) + lpa |= ADVERTISED_10000baseT_Full; + +#if defined(EFX_USE_KCOMPAT) && defined(EFX_NEED_MDIO45_FLOW_CONTROL_HACKS) + /* Old versions of the intree mdio45 layer don't set the pause + * capabilities in the ecmd structure properly */ + { + const struct mdio_if_info *mdio = &efx->mdio; + u32 reg; + + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN, + MDIO_AN_ADVERTISE); + if (reg & ADVERTISE_PAUSE_CAP) + adv |= ADVERTISED_Pause; + if (reg & ADVERTISE_PAUSE_ASYM) + adv |= ADVERTISED_Asym_Pause; + } +#endif + + mdio45_ethtool_gset_npage(&efx->mdio, ecmd, adv, lpa); + + if (efx->phy_type != PHY_TYPE_SFX7101) { + ecmd->supported |= (SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full); +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_ETHTOOL_ETH_TP_MDIX) + if (ecmd->speed != SPEED_10000) { + ecmd->eth_tp_mdix = + (efx_mdio_read(efx, MDIO_MMD_PMAPMD, + PMA_PMD_XSTATUS_REG) & + (1 << PMA_PMD_XSTAT_MDIX_LBN)) + ? ETH_TP_MDI_X : ETH_TP_MDI; + } +#endif + } + + /* In loopback, the PHY automatically brings up the correct interface, + * but doesn't advertise the correct speed. So override it */ + if (efx->loopback_mode == LOOPBACK_GPHY) + ecmd->speed = SPEED_1000; + else if (LOOPBACK_EXTERNAL(efx)) + ecmd->speed = SPEED_10000; +} + +static int tenxpress_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + if (!ecmd->autoneg) + return -EINVAL; + + return efx_mdio_set_settings(efx, ecmd); +} + +static void sfx7101_set_npage_adv(struct efx_nic *efx, u32 advertising) +{ + efx_mdio_set_flag(efx, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, + MDIO_AN_10GBT_CTRL_ADV10G, + advertising & ADVERTISED_10000baseT_Full); +} + +static void sft9001_set_npage_adv(struct efx_nic *efx, u32 advertising) +{ + efx_mdio_set_flag(efx, MDIO_MMD_C22EXT, C22EXT_MSTSLV_CTRL, + 1 << C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN, + advertising & ADVERTISED_1000baseT_Full); + efx_mdio_set_flag(efx, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, + MDIO_AN_10GBT_CTRL_ADV10G, + advertising & ADVERTISED_10000baseT_Full); +} + +struct efx_phy_operations falcon_sfx7101_phy_ops = { + .probe = tenxpress_phy_probe, .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, + .poll = tenxpress_phy_poll, + .fini = sfx7101_phy_fini, + .remove = tenxpress_phy_remove, + .get_settings = tenxpress_get_settings, + .set_settings = tenxpress_set_settings, + .set_npage_adv = sfx7101_set_npage_adv, + .test_alive = efx_mdio_test_alive, + .test_name = sfx7101_test_name, + .run_tests = sfx7101_run_tests, }; + +struct efx_phy_operations falcon_sft9001_phy_ops = { + .probe = tenxpress_phy_probe, + .init = tenxpress_phy_init, + .reconfigure = tenxpress_phy_reconfigure, + .poll = tenxpress_phy_poll, + .fini = efx_port_dummy_op_void, + .remove = tenxpress_phy_remove, + .get_settings = tenxpress_get_settings, + .set_settings = tenxpress_set_settings, + .set_npage_adv = sft9001_set_npage_adv, + .test_alive = efx_mdio_test_alive, + .test_name = sft9001_test_name, + .run_tests = sft9001_run_tests, +}; diff -r aa133dee7792 drivers/net/sfc/tx.c --- a/drivers/net/sfc/tx.c +++ b/drivers/net/sfc/tx.c @@ -1,56 +1,36 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2005-2006 Fen Systems Ltd. + * Copyright 2005-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #include #include #include #include +#include +#include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) #include #endif #include "net_driver.h" -#include "tx.h" #include "efx.h" -#include "falcon.h" +#include "nic.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) - - +#define EFX_TXQ_THRESHOLD (EFX_TXQ_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. @@ -70,7 +50,7 @@ * 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) +void efx_wake_queue(struct efx_nic *efx) { local_bh_disable(); if (atomic_dec_and_lock(&efx->netif_stop_count, @@ -82,29 +62,87 @@ local_bh_enable(); } -static inline void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, - struct efx_tx_buffer *buffer) +static void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, + struct efx_tx_buffer *buffer) { if (buffer->unmap_len) { struct pci_dev *pci_dev = tx_queue->efx->pci_dev; + dma_addr_t unmap_addr = (buffer->dma_addr + buffer->len - + buffer->unmap_len); if (buffer->unmap_single) - pci_unmap_single(pci_dev, buffer->unmap_addr, - buffer->unmap_len, PCI_DMA_TODEVICE); + pci_unmap_single(pci_dev, unmap_addr, buffer->unmap_len, + PCI_DMA_TODEVICE); else - pci_unmap_page(pci_dev, buffer->unmap_addr, - buffer->unmap_len, PCI_DMA_TODEVICE); + pci_unmap_page(pci_dev, unmap_addr, buffer->unmap_len, + PCI_DMA_TODEVICE); buffer->unmap_len = 0; - buffer->unmap_single = 0; + buffer->unmap_single = false; } 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); } } +/** + * struct efx_tso_header - a DMA mapped buffer for packet headers + * @next: Linked list of free ones. + * The list is protected by the TX queue lock. + * @dma_unmap_len: Length to unmap for an oversize buffer, or 0. + * @dma_addr: The DMA address of the header below. + * + * This controls the memory used for a TSO header. Use TSOH_DATA() + * to find the packet header data. Use TSOH_SIZE() to calculate the + * total size required for a given packet header length. TSO headers + * in the free list are exactly %TSOH_STD_SIZE bytes in size. + */ +struct efx_tso_header { + union { + struct efx_tso_header *next; + size_t unmap_len; + }; + dma_addr_t dma_addr; +}; + +static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, + struct sk_buff *skb); +static void efx_fini_tso(struct efx_tx_queue *tx_queue); +static void efx_tsoh_heap_free(struct efx_tx_queue *tx_queue, + struct efx_tso_header *tsoh); + +static void efx_tsoh_free(struct efx_tx_queue *tx_queue, + struct efx_tx_buffer *buffer) +{ + if (buffer->tsoh) { + if (likely(!buffer->tsoh->unmap_len)) { + buffer->tsoh->next = tx_queue->tso_headers_free; + tx_queue->tso_headers_free = buffer->tsoh; + } else { + efx_tsoh_heap_free(tx_queue, buffer->tsoh); + } + buffer->tsoh = NULL; + } +} + + +static inline unsigned +efx_max_tx_len(struct efx_nic *efx, dma_addr_t dma_addr) +{ + /* Depending on the NIC revision, we can use descriptor + * lengths up to 8K or 8K-1. However, since PCI Express + * devices must split read requests at 4K boundaries, there is + * little benefit from using descriptors that cross those + * boundaries and we keep things simple by not doing so. + */ + unsigned len = (~dma_addr & 0xfff) + 1; + + /* Work around hardware bug for unaligned buffers. */ + if (EFX_WORKAROUND_5391(efx) && (dma_addr & 0xf)) + len = min_t(unsigned, len, 512 - (dma_addr & 0xf)); + + return len; +} /* * Add a socket buffer to a TX queue @@ -116,11 +154,13 @@ * If any DMA mapping fails, any mapped fragments will be unmapped, * the queue's insert pointer will be restored to its original value. * + * This function is split out from efx_hard_start_xmit to allow the + * loopback test to direct packets via specific TX queues. + * * 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) +netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) { struct efx_nic *efx = tx_queue->efx; struct pci_dev *pci_dev = efx->pci_dev; @@ -128,31 +168,42 @@ skb_frag_t *fragment; struct page *page; int page_offset; - unsigned int len, unmap_len = 0, fill_level, insert_ptr, misalign; + unsigned int len, unmap_len = 0, fill_level, insert_ptr; dma_addr_t dma_addr, unmap_addr = 0; unsigned int dma_len; - unsigned unmap_single; + bool unmap_single; int q_space, i = 0; - int rc = NETDEV_TX_OK; + netdev_tx_t rc = NETDEV_TX_OK; EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); + if (skb_shinfo(skb)->gso_size) + return efx_enqueue_skb_tso(tx_queue, skb); + /* Get size of the initial fragment */ len = skb_headlen(skb); + /* Pad if necessary */ + if (EFX_WORKAROUND_15592(efx) && skb->len <= 32) { + EFX_BUG_ON_PARANOID(skb->data_len); + len = 32 + 1; + if (skb_pad(skb, len - skb->len)) + return NETDEV_TX_OK; + } + fill_level = tx_queue->insert_count - tx_queue->old_read_count; - q_space = efx->type->txd_ring_mask - 1 - fill_level; + q_space = EFX_TXQ_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; + unmap_single = true; 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))) + if (unlikely(pci_dma_mapping_error(pci_dev, dma_addr))) goto pci_err; /* Store fields for marking in the per-fragment final @@ -178,30 +229,26 @@ &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); + q_space = EFX_TXQ_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); + insert_ptr = tx_queue->insert_count & EFX_TXQ_MASK; buffer = &tx_queue->buffer[insert_ptr]; + efx_tsoh_free(tx_queue, buffer); + EFX_BUG_ON_PARANOID(buffer->tsoh); EFX_BUG_ON_PARANOID(buffer->skb); EFX_BUG_ON_PARANOID(buffer->len); - EFX_BUG_ON_PARANOID(buffer->continuation != 1); + EFX_BUG_ON_PARANOID(!buffer->continuation); EFX_BUG_ON_PARANOID(buffer->unmap_len); - dma_len = (((~dma_addr) & efx->type->tx_dma_mask) + 1); - if (likely(dma_len > len)) + dma_len = efx_max_tx_len(efx, dma_addr); + 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; @@ -211,7 +258,6 @@ } 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; @@ -225,17 +271,17 @@ page_offset = fragment->page_offset; i++; /* Map for DMA */ - unmap_single = 0; + unmap_single = false; 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; + buffer->continuation = false; /* Pass off to hardware */ - falcon_push_buffers(tx_queue); + efx_nic_push_buffers(tx_queue); return NETDEV_TX_OK; @@ -245,7 +291,7 @@ skb_shinfo(skb)->nr_frags + 1); /* Mark the packet as transmitted, and free the SKB ourselves */ - dev_kfree_skb_any((struct sk_buff *)skb); + dev_kfree_skb_any(skb); goto unwind; stop: @@ -259,16 +305,21 @@ /* 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; + insert_ptr = tx_queue->insert_count & EFX_TXQ_MASK; buffer = &tx_queue->buffer[insert_ptr]; efx_dequeue_buffer(tx_queue, buffer); 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); + if (unmap_len) { + if (unmap_single) + pci_unmap_single(pci_dev, unmap_addr, unmap_len, + PCI_DMA_TODEVICE); + else + pci_unmap_page(pci_dev, unmap_addr, unmap_len, + PCI_DMA_TODEVICE); + } return rc; } @@ -278,15 +329,14 @@ * 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) +static void efx_dequeue_buffers(struct efx_tx_queue *tx_queue, + unsigned int index) { struct efx_nic *efx = tx_queue->efx; unsigned int stop_index, read_ptr; - unsigned int mask = tx_queue->efx->type->txd_ring_mask; - stop_index = (index + 1) & mask; - read_ptr = tx_queue->read_count & mask; + stop_index = (index + 1) & EFX_TXQ_MASK; + read_ptr = tx_queue->read_count & EFX_TXQ_MASK; while (read_ptr != stop_index) { struct efx_tx_buffer *buffer = &tx_queue->buffer[read_ptr]; @@ -299,43 +349,17 @@ return; } + EFX_TRACE(tx_queue->efx, "TX queue %d transmission id %x " + "complete\n", tx_queue->queue, read_ptr); efx_dequeue_buffer(tx_queue, buffer); - buffer->continuation = 1; + buffer->continuation = true; buffer->len = 0; ++tx_queue->read_count; - read_ptr = tx_queue->read_count & mask; + read_ptr = tx_queue->read_count & EFX_TXQ_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 @@ -345,15 +369,18 @@ * 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) +netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, + struct net_device *net_dev) { - struct efx_nic *efx = net_dev->priv; + struct efx_nic *efx = netdev_priv(net_dev); struct efx_tx_queue *tx_queue; enum efx_veto veto; - int rc = NETDEV_TX_OK; + int rc; - /* We have one TX queue. */ - tx_queue = &efx->tx_queue[0]; + if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) + tx_queue = &efx->tx_queue[EFX_TX_QUEUE_OFFLOAD_CSUM]; + else + tx_queue = &efx->tx_queue[EFX_TX_QUEUE_NO_CSUM]; /* See if driverlink wants to veto the packet. */ veto = EFX_DL_CALLBACK(efx, tx_packet, skb); @@ -362,33 +389,41 @@ "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; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; } - rc = efx_xmit(efx, tx_queue, skb); -out: + rc = efx_enqueue_skb(tx_queue, skb); +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_NET_DEVICE_TRANS_START) + if (likely(rc == NETDEV_TX_OK)) { + /* Update last TX timer */ + efx->net_dev->trans_start = jiffies; + } +#endif return rc; } +#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL) void fastcall efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) +#else +void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) +#endif { unsigned fill_level; struct efx_nic *efx = tx_queue->efx; - EFX_BUG_ON_PARANOID(index > efx->type->txd_ring_mask); + EFX_BUG_ON_PARANOID(index > EFX_TXQ_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)) { + if (unlikely(tx_queue->stopped) && likely(efx->port_enabled)) { fill_level = tx_queue->insert_count - tx_queue->read_count; - if (fill_level < EFX_NETDEV_TX_THRESHOLD(tx_queue)) { - EFX_BUG_ON_PARANOID(!NET_DEV_REGISTERED(efx)); + if (fill_level < EFX_TXQ_THRESHOLD) { + EFX_BUG_ON_PARANOID(!efx_dev_registered(efx)); /* Do this under netif_tx_lock(), to avoid racing * with efx_xmit(). */ @@ -411,37 +446,30 @@ 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); + txq_size = EFX_TXQ_SIZE * 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; + if (!tx_queue->buffer) + return -ENOMEM; + for (i = 0; i <= EFX_TXQ_MASK; ++i) + tx_queue->buffer[i].continuation = true; /* Allocate hardware ring */ - rc = falcon_probe_tx(tx_queue); + rc = efx_nic_probe_tx(tx_queue); if (rc) - goto fail2; + goto fail; return 0; - fail2: + fail: 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) +void efx_init_tx_queue(struct efx_tx_queue *tx_queue) { EFX_LOG(tx_queue->efx, "initialising TX queue %d\n", tx_queue->queue); - /* Initialise fields */ tx_queue->insert_count = 0; tx_queue->write_count = 0; tx_queue->read_count = 0; @@ -449,7 +477,7 @@ BUG_ON(tx_queue->stopped); /* Set up TX descriptor ring */ - return falcon_init_tx(tx_queue); + efx_nic_init_tx(tx_queue); } void efx_release_tx_buffers(struct efx_tx_queue *tx_queue) @@ -461,10 +489,9 @@ /* Free any buffers left in the ring */ while (tx_queue->read_count != tx_queue->write_count) { - buffer = &tx_queue->buffer[tx_queue->read_count & - tx_queue->efx->type->txd_ring_mask]; + buffer = &tx_queue->buffer[tx_queue->read_count & EFX_TXQ_MASK]; efx_dequeue_buffer(tx_queue, buffer); - buffer->continuation = 1; + buffer->continuation = true; buffer->len = 0; ++tx_queue->read_count; @@ -476,11 +503,13 @@ EFX_LOG(tx_queue->efx, "shutting down TX queue %d\n", tx_queue->queue); /* Flush TX queue, remove descriptor ring */ - falcon_fini_tx(tx_queue); + efx_nic_fini_tx(tx_queue); - /* Release TX buffers */ efx_release_tx_buffers(tx_queue); + /* Free up TSO header cache */ + efx_fini_tso(tx_queue); + /* Release queue's stop on port, if any */ if (tx_queue->stopped) { tx_queue->stopped = 0; @@ -491,11 +520,683 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) { EFX_LOG(tx_queue->efx, "destroying TX queue %d\n", tx_queue->queue); - falcon_remove_tx(tx_queue); + efx_nic_remove_tx(tx_queue); kfree(tx_queue->buffer); tx_queue->buffer = NULL; - tx_queue->used = 0; } +/* Efx TCP segmentation acceleration. + * + * Why? Because by doing it here in the driver we can go significantly + * faster than the GSO. + * + * Requires TX checksum offload support. + */ + +/* Number of bytes inserted at the start of a TSO header buffer, + * similar to NET_IP_ALIGN. + */ +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +#define TSOH_OFFSET 0 +#else +#define TSOH_OFFSET NET_IP_ALIGN +#endif + +#define TSOH_BUFFER(tsoh) ((u8 *)(tsoh + 1) + TSOH_OFFSET) + +/* Total size of struct efx_tso_header, buffer and padding */ +#define TSOH_SIZE(hdr_len) \ + (sizeof(struct efx_tso_header) + TSOH_OFFSET + hdr_len) + +/* Size of blocks on free list. Larger blocks must be allocated from + * the heap. + */ +#define TSOH_STD_SIZE 128 + +#define PTR_DIFF(p1, p2) ((u8 *)(p1) - (u8 *)(p2)) +#define ETH_HDR_LEN(skb) (skb_network_header(skb) - (skb)->data) +#define SKB_TCP_OFF(skb) PTR_DIFF(tcp_hdr(skb), (skb)->data) +#define SKB_IPV4_OFF(skb) PTR_DIFF(ip_hdr(skb), (skb)->data) +#define SKB_IPV6_OFF(skb) PTR_DIFF(ipv6_hdr(skb), (skb)->data) + +/** + * struct tso_state - TSO state for an SKB + * @out_len: Remaining length in current segment + * @seqnum: Current sequence number + * @ipv4_id: Current IPv4 ID, host endian + * @packet_space: Remaining space in current packet + * @dma_addr: DMA address of current position + * @in_len: Remaining length in current SKB fragment + * @unmap_len: Length of SKB fragment + * @unmap_addr: DMA address of SKB fragment + * @unmap_single: DMA single vs page mapping flag + * @protocol: Network protocol (after any VLAN header) + * @header_len: Number of bytes of header + * @full_packet_size: Number of bytes to put in each outgoing segment + * + * The state used during segmentation. It is put into this data structure + * just to make it easy to pass into inline functions. + */ +struct tso_state { + /* Output position */ + unsigned out_len; + unsigned seqnum; + unsigned ipv4_id; + unsigned packet_space; + + /* Input position */ + dma_addr_t dma_addr; + unsigned in_len; + unsigned unmap_len; + dma_addr_t unmap_addr; + bool unmap_single; + + __be16 protocol; + unsigned header_len; + int full_packet_size; +}; + + +static inline void prefetch_ptr(struct efx_tx_queue *tx_queue) +{ + unsigned insert_ptr = tx_queue->insert_count & EFX_TXQ_MASK; + char *ptr; + + ptr = (char *) (tx_queue->buffer + insert_ptr); + prefetch(ptr); + prefetch(ptr + 0x80); + + ptr = (char *) (((efx_qword_t *)tx_queue->txd.addr) + insert_ptr); + prefetch(ptr); + prefetch(ptr + 0x80); +} + +/* + * Verify that our various assumptions about sk_buffs and the conditions + * under which TSO will be attempted hold true. Return the protocol number. + */ +static __be16 efx_tso_check_protocol(struct sk_buff *skb) +{ + __be16 protocol = skb->protocol; + + EFX_BUG_ON_PARANOID(((struct ethhdr *)skb->data)->h_proto != + protocol); +#if !defined(EFX_USE_KCOMPAT) || defined(EFX_USE_NETDEV_VLAN_FEATURES) + if (protocol == htons(ETH_P_8021Q)) { + /* Find the encapsulated protocol; reset network header + * and transport header based on that. */ + struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; + protocol = veh->h_vlan_encapsulated_proto; + skb_set_network_header(skb, sizeof(*veh)); + if (protocol == htons(ETH_P_IP)) + skb_set_transport_header(skb, sizeof(*veh) + + 4 * ip_hdr(skb)->ihl); + else if (protocol == htons(ETH_P_IPV6)) + skb_set_transport_header(skb, sizeof(*veh) + + sizeof(struct ipv6hdr)); + } +#endif + + if (protocol == htons(ETH_P_IP)) { + EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP); + } else { + EFX_BUG_ON_PARANOID(protocol != htons(ETH_P_IPV6)); + EFX_BUG_ON_PARANOID(ipv6_hdr(skb)->nexthdr != NEXTHDR_TCP); + } + EFX_BUG_ON_PARANOID((PTR_DIFF(tcp_hdr(skb), skb->data) + + (tcp_hdr(skb)->doff << 2u)) > + skb_headlen(skb)); + + return protocol; +} + + +/* + * Allocate a page worth of efx_tso_header structures, and string them + * into the tx_queue->tso_headers_free linked list. Return 0 or -ENOMEM. + */ +static int efx_tsoh_block_alloc(struct efx_tx_queue *tx_queue) +{ + + struct pci_dev *pci_dev = tx_queue->efx->pci_dev; + struct efx_tso_header *tsoh; + dma_addr_t dma_addr; + u8 *base_kva, *kva; + + base_kva = pci_alloc_consistent(pci_dev, PAGE_SIZE, &dma_addr); + if (base_kva == NULL) { + EFX_ERR(tx_queue->efx, "Unable to allocate page for TSO" + " headers\n"); + return -ENOMEM; + } + + /* pci_alloc_consistent() allocates pages. */ + EFX_BUG_ON_PARANOID(dma_addr & (PAGE_SIZE - 1u)); + + for (kva = base_kva; kva < base_kva + PAGE_SIZE; kva += TSOH_STD_SIZE) { + tsoh = (struct efx_tso_header *)kva; + tsoh->dma_addr = dma_addr + (TSOH_BUFFER(tsoh) - base_kva); + tsoh->next = tx_queue->tso_headers_free; + tx_queue->tso_headers_free = tsoh; + } + + return 0; +} + + +/* Free up a TSO header, and all others in the same page. */ +static void efx_tsoh_block_free(struct efx_tx_queue *tx_queue, + struct efx_tso_header *tsoh, + struct pci_dev *pci_dev) +{ + struct efx_tso_header **p; + unsigned long base_kva; + dma_addr_t base_dma; + + base_kva = (unsigned long)tsoh & PAGE_MASK; + base_dma = tsoh->dma_addr & PAGE_MASK; + + p = &tx_queue->tso_headers_free; + while (*p != NULL) { + if (((unsigned long)*p & PAGE_MASK) == base_kva) + *p = (*p)->next; + else + p = &(*p)->next; + } + + pci_free_consistent(pci_dev, PAGE_SIZE, (void *)base_kva, base_dma); +} + +static struct efx_tso_header * +efx_tsoh_heap_alloc(struct efx_tx_queue *tx_queue, size_t header_len) +{ + struct efx_tso_header *tsoh; + + tsoh = kmalloc(TSOH_SIZE(header_len), GFP_ATOMIC | GFP_DMA); + if (unlikely(!tsoh)) + return NULL; + + tsoh->dma_addr = pci_map_single(tx_queue->efx->pci_dev, + TSOH_BUFFER(tsoh), header_len, + PCI_DMA_TODEVICE); + if (unlikely(pci_dma_mapping_error(tx_queue->efx->pci_dev, + tsoh->dma_addr))) { + kfree(tsoh); + return NULL; + } + + tsoh->unmap_len = header_len; + return tsoh; +} + +static void +efx_tsoh_heap_free(struct efx_tx_queue *tx_queue, struct efx_tso_header *tsoh) +{ + pci_unmap_single(tx_queue->efx->pci_dev, + tsoh->dma_addr, tsoh->unmap_len, + PCI_DMA_TODEVICE); + kfree(tsoh); +} + +/** + * efx_tx_queue_insert - push descriptors onto the TX queue + * @tx_queue: Efx TX queue + * @dma_addr: DMA address of fragment + * @len: Length of fragment + * @final_buffer: The final buffer inserted into the queue + * + * Push descriptors onto the TX queue. Return 0 on success or 1 if + * @tx_queue full. + */ +static int efx_tx_queue_insert(struct efx_tx_queue *tx_queue, + dma_addr_t dma_addr, unsigned len, + struct efx_tx_buffer **final_buffer) +{ + struct efx_tx_buffer *buffer; + struct efx_nic *efx = tx_queue->efx; + unsigned dma_len, fill_level, insert_ptr; + int q_space; + + EFX_BUG_ON_PARANOID(len <= 0); + + fill_level = tx_queue->insert_count - tx_queue->old_read_count; + /* -1 as there is no way to represent all descriptors used */ + q_space = EFX_TXQ_MASK - 1 - fill_level; + + while (1) { + 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_TXQ_MASK - 1 - fill_level; + if (unlikely(q_space-- <= 0)) { + *final_buffer = NULL; + return 1; + } + smp_mb(); + --tx_queue->stopped; + } + + insert_ptr = tx_queue->insert_count & EFX_TXQ_MASK; + buffer = &tx_queue->buffer[insert_ptr]; + ++tx_queue->insert_count; + + EFX_BUG_ON_PARANOID(tx_queue->insert_count - + tx_queue->read_count > + EFX_TXQ_MASK); + + efx_tsoh_free(tx_queue, buffer); + EFX_BUG_ON_PARANOID(buffer->len); + EFX_BUG_ON_PARANOID(buffer->unmap_len); + EFX_BUG_ON_PARANOID(buffer->skb); + EFX_BUG_ON_PARANOID(!buffer->continuation); + EFX_BUG_ON_PARANOID(buffer->tsoh); + + buffer->dma_addr = dma_addr; + + dma_len = efx_max_tx_len(efx, dma_addr); + + /* If there is enough space to send then do so */ + if (dma_len >= len) + break; + + buffer->len = dma_len; /* Don't set the other members */ + dma_addr += dma_len; + len -= dma_len; + } + + EFX_BUG_ON_PARANOID(!len); + buffer->len = len; + *final_buffer = buffer; + return 0; +} + + +/* + * Put a TSO header into the TX queue. + * + * This is special-cased because we know that it is small enough to fit in + * a single fragment, and we know it doesn't cross a page boundary. It + * also allows us to not worry about end-of-packet etc. + */ +static void efx_tso_put_header(struct efx_tx_queue *tx_queue, + struct efx_tso_header *tsoh, unsigned len) +{ + struct efx_tx_buffer *buffer; + + buffer = &tx_queue->buffer[tx_queue->insert_count & EFX_TXQ_MASK]; + efx_tsoh_free(tx_queue, buffer); + EFX_BUG_ON_PARANOID(buffer->len); + EFX_BUG_ON_PARANOID(buffer->unmap_len); + EFX_BUG_ON_PARANOID(buffer->skb); + EFX_BUG_ON_PARANOID(!buffer->continuation); + EFX_BUG_ON_PARANOID(buffer->tsoh); + buffer->len = len; + buffer->dma_addr = tsoh->dma_addr; + buffer->tsoh = tsoh; + + ++tx_queue->insert_count; +} + + +/* Remove descriptors put into a tx_queue. */ +static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue) +{ + struct efx_tx_buffer *buffer; + dma_addr_t unmap_addr; + + /* Work backwards until we hit the original insert pointer value */ + while (tx_queue->insert_count != tx_queue->write_count) { + --tx_queue->insert_count; + buffer = &tx_queue->buffer[tx_queue->insert_count & + EFX_TXQ_MASK]; + efx_tsoh_free(tx_queue, buffer); + EFX_BUG_ON_PARANOID(buffer->skb); + if (buffer->unmap_len) { + unmap_addr = (buffer->dma_addr + buffer->len - + buffer->unmap_len); + if (buffer->unmap_single) + pci_unmap_single(tx_queue->efx->pci_dev, + unmap_addr, buffer->unmap_len, + PCI_DMA_TODEVICE); + else + pci_unmap_page(tx_queue->efx->pci_dev, + unmap_addr, buffer->unmap_len, + PCI_DMA_TODEVICE); + buffer->unmap_len = 0; + } + buffer->len = 0; + buffer->continuation = true; + } +} + + +/* Parse the SKB header and initialise state. */ +static void tso_start(struct tso_state *st, const struct sk_buff *skb) +{ + /* All ethernet/IP/TCP headers combined size is TCP header size + * plus offset of TCP header relative to start of packet. + */ + st->header_len = ((tcp_hdr(skb)->doff << 2u) + + PTR_DIFF(tcp_hdr(skb), skb->data)); + st->full_packet_size = st->header_len + skb_shinfo(skb)->gso_size; + + if (st->protocol == htons(ETH_P_IP)) + st->ipv4_id = ntohs(ip_hdr(skb)->id); + else + st->ipv4_id = 0; + st->seqnum = ntohl(tcp_hdr(skb)->seq); + + EFX_BUG_ON_PARANOID(tcp_hdr(skb)->urg); + EFX_BUG_ON_PARANOID(tcp_hdr(skb)->syn); + EFX_BUG_ON_PARANOID(tcp_hdr(skb)->rst); + + st->packet_space = st->full_packet_size; + st->out_len = skb->len - st->header_len; + st->unmap_len = 0; + st->unmap_single = false; +} + +static int tso_get_fragment(struct tso_state *st, struct efx_nic *efx, + skb_frag_t *frag) +{ + st->unmap_addr = pci_map_page(efx->pci_dev, frag->page, + frag->page_offset, frag->size, + PCI_DMA_TODEVICE); + if (likely(!pci_dma_mapping_error(efx->pci_dev, st->unmap_addr))) { + st->unmap_single = false; + st->unmap_len = frag->size; + st->in_len = frag->size; + st->dma_addr = st->unmap_addr; + return 0; + } + return -ENOMEM; +} + +static int tso_get_head_fragment(struct tso_state *st, struct efx_nic *efx, + const struct sk_buff *skb) +{ + int hl = st->header_len; + int len = skb_headlen(skb) - hl; + + st->unmap_addr = pci_map_single(efx->pci_dev, skb->data + hl, + len, PCI_DMA_TODEVICE); + if (likely(!pci_dma_mapping_error(efx->pci_dev, st->unmap_addr))) { + st->unmap_single = true; + st->unmap_len = len; + st->in_len = len; + st->dma_addr = st->unmap_addr; + return 0; + } + return -ENOMEM; +} + + +/** + * tso_fill_packet_with_fragment - form descriptors for the current fragment + * @tx_queue: Efx TX queue + * @skb: Socket buffer + * @st: TSO state + * + * Form descriptors for the current fragment, until we reach the end + * of fragment or end-of-packet. Return 0 on success, 1 if not enough + * space in @tx_queue. + */ +static int tso_fill_packet_with_fragment(struct efx_tx_queue *tx_queue, + const struct sk_buff *skb, + struct tso_state *st) +{ + struct efx_tx_buffer *buffer; + int n, end_of_packet, rc; + + if (st->in_len == 0) + return 0; + if (st->packet_space == 0) + return 0; + + EFX_BUG_ON_PARANOID(st->in_len <= 0); + EFX_BUG_ON_PARANOID(st->packet_space <= 0); + + n = min(st->in_len, st->packet_space); + + st->packet_space -= n; + st->out_len -= n; + st->in_len -= n; + + rc = efx_tx_queue_insert(tx_queue, st->dma_addr, n, &buffer); + if (likely(rc == 0)) { + if (st->out_len == 0) + /* Transfer ownership of the skb */ + buffer->skb = skb; + + end_of_packet = st->out_len == 0 || st->packet_space == 0; + buffer->continuation = !end_of_packet; + + if (st->in_len == 0) { + /* Transfer ownership of the pci mapping */ + buffer->unmap_len = st->unmap_len; + buffer->unmap_single = st->unmap_single; + st->unmap_len = 0; + } + } + + st->dma_addr += n; + return rc; +} + + +/** + * tso_start_new_packet - generate a new header and prepare for the new packet + * @tx_queue: Efx TX queue + * @skb: Socket buffer + * @st: TSO state + * + * Generate a new header and prepare for the new packet. Return 0 on + * success, or -1 if failed to alloc header. + */ +static int tso_start_new_packet(struct efx_tx_queue *tx_queue, + const struct sk_buff *skb, + struct tso_state *st) +{ + struct efx_tso_header *tsoh; + struct tcphdr *tsoh_th; + unsigned ip_length; + u8 *header; + + /* Allocate a DMA-mapped header buffer. */ + if (likely(TSOH_SIZE(st->header_len) <= TSOH_STD_SIZE)) { + if (tx_queue->tso_headers_free == NULL) { + if (efx_tsoh_block_alloc(tx_queue)) + return -1; + } + EFX_BUG_ON_PARANOID(!tx_queue->tso_headers_free); + tsoh = tx_queue->tso_headers_free; + tx_queue->tso_headers_free = tsoh->next; + tsoh->unmap_len = 0; + } else { + tx_queue->tso_long_headers++; + tsoh = efx_tsoh_heap_alloc(tx_queue, st->header_len); + if (unlikely(!tsoh)) + return -1; + } + + header = TSOH_BUFFER(tsoh); + tsoh_th = (struct tcphdr *)(header + SKB_TCP_OFF(skb)); + + /* Copy and update the headers. */ + memcpy(header, skb->data, st->header_len); + + tsoh_th->seq = htonl(st->seqnum); + st->seqnum += skb_shinfo(skb)->gso_size; + if (st->out_len > skb_shinfo(skb)->gso_size) { + /* This packet will not finish the TSO burst. */ + ip_length = st->full_packet_size - ETH_HDR_LEN(skb); + tsoh_th->fin = 0; + tsoh_th->psh = 0; + } else { + /* This packet will be the last in the TSO burst. */ + ip_length = st->header_len - ETH_HDR_LEN(skb) + st->out_len; + tsoh_th->fin = tcp_hdr(skb)->fin; + tsoh_th->psh = tcp_hdr(skb)->psh; + } + + if (st->protocol == htons(ETH_P_IP)) { + struct iphdr *tsoh_iph = + (struct iphdr *)(header + SKB_IPV4_OFF(skb)); + + tsoh_iph->tot_len = htons(ip_length); + + /* Linux leaves suitable gaps in the IP ID space for us to fill. */ + tsoh_iph->id = htons(st->ipv4_id); + st->ipv4_id++; + } else { + struct ipv6hdr *tsoh_iph = + (struct ipv6hdr *)(header + SKB_IPV6_OFF(skb)); + + tsoh_iph->payload_len = htons(ip_length - sizeof(*tsoh_iph)); + } + + st->packet_space = skb_shinfo(skb)->gso_size; + ++tx_queue->tso_packets; + + /* Form a descriptor for this header. */ + efx_tso_put_header(tx_queue, tsoh, st->header_len); + + return 0; +} + +/** + * efx_enqueue_skb_tso - segment and transmit a TSO socket buffer + * @tx_queue: Efx TX queue + * @skb: Socket buffer + * + * Context: You must hold netif_tx_lock() to call this function. + * + * Add socket buffer @skb to @tx_queue, doing TSO or return != 0 if + * @skb was not enqueued. In all cases @skb is consumed. Return + * %NETDEV_TX_OK or %NETDEV_TX_BUSY. + */ +static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, + struct sk_buff *skb) +{ + struct efx_nic *efx = tx_queue->efx; + int frag_i, rc, rc2 = NETDEV_TX_OK; + struct tso_state state; + + prefetch(skb->data); + + /* Find the packet protocol and sanity-check it */ + state.protocol = efx_tso_check_protocol(skb); + + EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); + + tso_start(&state, skb); + + /* Assume that skb header area contains exactly the headers, and + * all payload is in the frag list. + */ + if (skb_headlen(skb) == state.header_len) { + /* Grab the first payload fragment. */ + EFX_BUG_ON_PARANOID(skb_shinfo(skb)->nr_frags < 1); + frag_i = 0; + rc = tso_get_fragment(&state, efx, + skb_shinfo(skb)->frags + frag_i); + if (rc) + goto mem_err; + } else { + rc = tso_get_head_fragment(&state, efx, skb); + if (rc) + goto mem_err; + frag_i = -1; + } + + if (tso_start_new_packet(tx_queue, skb, &state) < 0) + goto mem_err; + + prefetch_ptr(tx_queue); + + while (1) { + rc = tso_fill_packet_with_fragment(tx_queue, skb, &state); + if (unlikely(rc)) + goto stop; + + /* Move onto the next fragment? */ + if (state.in_len == 0) { + if (++frag_i >= skb_shinfo(skb)->nr_frags) + /* End of payload reached. */ + break; + rc = tso_get_fragment(&state, efx, + skb_shinfo(skb)->frags + frag_i); + if (rc) + goto mem_err; + } + + /* Start at new packet? */ + if (state.packet_space == 0 && + tso_start_new_packet(tx_queue, skb, &state) < 0) + goto mem_err; + } + + /* Pass off to hardware */ + efx_nic_push_buffers(tx_queue); + + tx_queue->tso_bursts++; + return NETDEV_TX_OK; + + mem_err: + EFX_ERR(efx, "Out of memory for TSO headers, or PCI mapping error\n"); + dev_kfree_skb_any(skb); + goto unwind; + + stop: + rc2 = NETDEV_TX_BUSY; + + /* Stop the queue if it wasn't stopped before. */ + if (tx_queue->stopped == 1) + efx_stop_queue(efx); + + unwind: + /* Free the DMA mapping we were in the process of writing out */ + if (state.unmap_len) { + if (state.unmap_single) + pci_unmap_single(efx->pci_dev, state.unmap_addr, + state.unmap_len, PCI_DMA_TODEVICE); + else + pci_unmap_page(efx->pci_dev, state.unmap_addr, + state.unmap_len, PCI_DMA_TODEVICE); + } + + efx_enqueue_unwind(tx_queue); + return rc2; +} + + +/* + * Free up all TSO datastructures associated with tx_queue. This + * routine should be called only once the tx_queue is both empty and + * will no longer be used. + */ +static void efx_fini_tso(struct efx_tx_queue *tx_queue) +{ + unsigned i; + + if (tx_queue->buffer) { + for (i = 0; i <= EFX_TXQ_MASK; ++i) + efx_tsoh_free(tx_queue, &tx_queue->buffer[i]); + } + + while (tx_queue->tso_headers_free != NULL) + efx_tsoh_block_free(tx_queue, tx_queue->tso_headers_free, + tx_queue->efx->pci_dev); +} diff -r aa133dee7792 drivers/net/sfc/tx.h --- a/drivers/net/sfc/tx.h +++ /dev/null @@ -1,41 +0,0 @@ -/**************************************************************************** - * 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 */ diff -r aa133dee7792 drivers/net/sfc/txc43128_phy.c --- a/drivers/net/sfc/txc43128_phy.c +++ b/drivers/net/sfc/txc43128_phy.c @@ -1,27 +1,12 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ + /* * Driver for Transwitch/Mysticom CX4 retimer * see www.transwitch.com, part is TXC-43128 @@ -31,22 +16,19 @@ #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 "nic.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_REQUIRED_DEVS (MDIO_DEVS_PCS | \ + MDIO_DEVS_PMAPMD | \ + MDIO_DEVS_PHYXS) #define TXC_LOOPBACKS ((1 << LOOPBACK_PCS) | \ (1 << LOOPBACK_PMAPMD) | \ - (1 << LOOPBACK_NETWORK)) + (1 << LOOPBACK_PHYXS_WS)) /************************************************************************** * @@ -217,16 +199,8 @@ #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 unsigned bug10934_timer; - int phy_powered; - int tx_disabled; + enum efx_phy_mode phy_mode; enum efx_loopback_mode loopback_mode; }; @@ -240,48 +214,32 @@ /* 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); + efx_mdio_set_flag(efx, MDIO_MMD_PHYXS, TXC_GPIO_OUTPUT, 1 << pin, on); } /* Set up the GPIO direction register */ void txc_set_gpio_dir(struct efx_nic *efx, int pin, int dir) { - int dirs; + struct falcon_board *board = falcon_board(efx); - if (efx->board_info.minor < 3 && - efx->board_info.major == 0) + if (board->minor < 3 && board->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); - + efx_mdio_set_flag(efx, MDIO_MMD_PHYXS, TXC_GPIO_DIR, 1 << pin, dir); } /* 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); + int rc = efx_mdio_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); + /* Check that all the MMDs we expect are present and responding. */ + rc = efx_mdio_check_mmds(efx, TXC_REQUIRED_DEVS, 0); if (rc < 0) goto fail; @@ -295,58 +253,52 @@ /* 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)); + efx_mdio_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 = efx_mdio_read(efx, 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); - + efx_mdio_write(efx, 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); + efx_mdio_write(efx, 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); + efx_mdio_write(efx, 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)); + efx_mdio_write(efx, 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); + efx_mdio_write(efx, 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); + bctl = efx_mdio_read(efx, 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); + int count = efx_mdio_read(efx, 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); + count = efx_mdio_read(efx, mmd, TXC_BIST_RX0FRMCNT + lane); if (count == 0) { EFX_ERR(efx, ""TXCNAME": BIST error. " "Lane %d got 0 frames\n", lane); @@ -358,12 +310,11 @@ EFX_INFO(efx, ""TXCNAME": BIST pass\n"); /* Disable BIST */ - mdio_clause45_write(efx, phy, mmd, TXC_BIST_CTL, 0); + efx_mdio_write(efx, 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); + efx_mdio_write(efx, MDIO_MMD_PCS, TXC_MTDIABLO_CTRL, ctrl); return rc; } @@ -378,22 +329,6 @@ 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) @@ -406,111 +341,109 @@ * 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); + efx_mdio_write(efx, MDIO_MMD_PHYXS, TXC_ALRGS_ATXPRE0, TXC_ATXPRE_NONE); + efx_mdio_write(efx, 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); + efx_mdio_write(efx, MDIO_MMD_PHYXS, + TXC_ALRGS_ATXAMP0, TXC_ATXAMP_0820_BOTH); + efx_mdio_write(efx, 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); + efx_mdio_write(efx, MDIO_MMD_PMAPMD, + TXC_ALRGS_ATXPRE0, TXC_ATXPRE_DEFAULT); + efx_mdio_write(efx, MDIO_MMD_PMAPMD, + TXC_ALRGS_ATXPRE1, TXC_ATXPRE_DEFAULT); + efx_mdio_write(efx, MDIO_MMD_PMAPMD, + TXC_ALRGS_ATXAMP0, TXC_ATXAMP_DEFAULT); + efx_mdio_write(efx, 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); + mctrl = efx_mdio_read(efx, 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); + efx_mdio_write(efx, MDIO_MMD_PHYXS, TXC_MRGS_CTL, mctrl); /* Databook recommends doing this after configuration changes */ txc_reset_logic(efx); - efx->board_info.init_leds(efx); + falcon_board(efx)->type->init_phy(efx); +} + +static int txc43128_phy_probe(struct efx_nic *efx) +{ + struct txc43128_data *phy_data; + int rc; + + /* Allocate phy private storage */ + phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); + if (!phy_data) + return -ENOMEM; + efx->phy_data = phy_data; + phy_data->phy_mode = efx->phy_mode; + + efx->mdio.mmds = TXC_REQUIRED_DEVS; + efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; + + efx->loopback_modes = TXC_LOOPBACKS | FALCON_XMAC_LOOPBACKS; + efx->startup_loopback_mode = LOOPBACK_PMAPMD; + + strlcpy(efx->phy_name, "Mysticom CX4", sizeof(efx->phy_name)); + + return 0; + +fail: + kfree(efx->phy_data); + efx->phy_data = NULL; + + return rc; } /* 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; + int rc; - 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)); + devid = efx_mdio_read_id(efx, MDIO_MMD_PHYXS); + EFX_INFO(efx, ""TXCNAME ": PHY ID reg %x (OUI %06x model %02x " + "revision %x)\n", devid, efx_mdio_id_oui(devid), + efx_mdio_id_model(devid), efx_mdio_id_rev(devid)); 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); + efx_mdio_read(efx, MDIO_MMD_PHYXS, TXC_GLRGS_SLID) & + TXC_GLRGS_SLID_MASK); rc = txc_reset_phy(efx); if (rc < 0) - goto fail2; + return rc; rc = txc_bist(efx); if (rc < 0) - goto fail2; + return rc; 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); + int ctl = efx_mdio_read(efx, mmd, TXC_GLRGS_GLCMD); - if (efx->phy_powered) + if (!(efx->phy_mode & PHY_MODE_LOW_POWER)) ctl &= ~pd; else ctl |= pd; - mdio_clause45_write(efx, efx->mii.phy_id, - mmd, TXC_GLRGS_GLCMD, ctl); + efx_mdio_write(efx, mmd, TXC_GLRGS_GLCMD, ctl); } /* Set the lane power down state in the analog control registers */ @@ -522,12 +455,10 @@ 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); + int txctl = efx_mdio_read(efx, mmd, TXC_ALRGS_ATXCTL); + int rxctl = efx_mdio_read(efx, mmd, TXC_ALRGS_ARXCTL); - if (efx->phy_powered) { + if (!(efx->phy_mode & PHY_MODE_LOW_POWER)) { txctl &= ~txpd; rxctl &= ~rxpd; } else { @@ -535,16 +466,15 @@ 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); + efx_mdio_write(efx, mmd, TXC_ALRGS_ATXCTL, txctl); + efx_mdio_write(efx, 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, + efx_mdio_set_mmds_lpower(efx, + !!(efx->phy_mode & PHY_MODE_LOW_POWER), TXC_REQUIRED_DEVS); /* Global register bank is in PCS, PHY XS. These control the host @@ -560,14 +490,12 @@ 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 val = efx_mdio_read(efx, mmd, TXC_GLRGS_GLCMD); int tries = 50; val |= (1 << TXC_GLCMD_LMTSWRST_LBN); - mdio_clause45_write(efx, portid, mmd, TXC_GLRGS_GLCMD, val); + efx_mdio_write(efx, mmd, TXC_GLRGS_GLCMD, val); while (tries--) { - val = mdio_clause45_read(efx, portid, mmd, - TXC_GLRGS_GLCMD); + val = efx_mdio_read(efx, mmd, TXC_GLRGS_GLCMD); if (!(val & (1 << TXC_GLCMD_LMTSWRST_LBN))) break; udelay(1); @@ -587,76 +515,68 @@ txc_reset_logic_mmd(efx, MDIO_MMD_PCS); } -static int txc43128_phy_read_link(struct efx_nic *efx) +static bool txc43128_phy_read_link(struct efx_nic *efx) { - return mdio_clause45_links_ok(efx, TXC_REQUIRED_DEVS); + return efx_mdio_links_ok(efx, TXC_REQUIRED_DEVS); } -static void txc43128_phy_reconfigure(struct efx_nic *efx) +static int 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); + enum efx_phy_mode mode_change = efx->phy_mode ^ phy_data->phy_mode; + bool loop_change = LOOPBACK_CHANGED(phy_data, efx, TXC_LOOPBACKS); - if (!phy_data->tx_disabled && efx->tx_disabled) { + if (efx->phy_mode & mode_change & PHY_MODE_TX_DISABLED) { txc_reset_phy(efx); txc_apply_defaults(efx); falcon_reset_xaui(efx); - disable_change = 0; + mode_change &= ~PHY_MODE_TX_DISABLED; } - mdio_clause45_transmit_disable(efx, efx->tx_disabled); - mdio_clause45_phy_reconfigure(efx); - if (power_change) + efx_mdio_transmit_disable(efx); + efx_mdio_phy_reconfigure(efx); + if (mode_change & PHY_MODE_LOW_POWER) 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) + if (loop_change || mode_change) txc_reset_logic(efx); - phy_data->phy_powered = efx->phy_powered; + phy_data->phy_mode = efx->phy_mode; 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; + + return 0; } static void txc43128_phy_fini(struct efx_nic *efx) { - efx->board_info.blink(efx, 0); + /* Disable link events */ + efx_mdio_write(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 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 +static void txc43128_phy_remove(struct efx_nic *efx) +{ 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) +static bool txc43128_phy_poll(struct efx_nic *efx) { struct txc43128_data *data = efx->phy_data; -#ifdef CONFIG_SFC_DEBUGFS - int phy = efx->mii.phy_id; - int timer, count, i, mmd; -#endif - int rc = 0; - int link_up = txc43128_phy_read_link(efx); + bool was_up = efx->link_state.up; - /* Simulate a PHY event if link state has changed */ - if (link_up != efx->link_up) - efx->mac_op->fake_phy_event(efx); - else if (EFX_WORKAROUND_10934(efx)) { - if (link_up || (efx->loopback_mode != LOOPBACK_NONE)) + efx->link_state.up = txc43128_phy_read_link(efx); + efx->link_state.speed = 10000; + efx->link_state.fd = true; + efx->link_state.fc = efx->wanted_fc; + + if (EFX_WORKAROUND_10934(efx)) { + if (efx->link_state.up || (efx->loopback_mode != LOOPBACK_NONE)) data->bug10934_timer = jiffies; else { int delta = jiffies - data->bug10934_timer; @@ -667,58 +587,52 @@ } } - 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); - } + return efx->link_state.up != was_up; +} -#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 */ +static const char *txc43128_test_names[] = { + "bist" +}; + +static const char *txc43128_test_name(struct efx_nic *efx, unsigned int index) +{ + if (index < ARRAY_SIZE(txc43128_test_names)) + return txc43128_test_names[index]; + return NULL; +} + +static int txc43128_run_tests(struct efx_nic *efx, int *results, unsigned flags) +{ + int rc; + + if (!(flags & ETH_TEST_FL_OFFLINE)) + return 0; + + rc = txc_reset_phy(efx); + if (rc < 0) + return rc; + + rc = txc_bist(efx); + txc_apply_defaults(efx); + results[0] = rc ? -1 : 1; return rc; } +static void txc43128_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +{ + mdio45_ethtool_gset(&efx->mdio, ecmd); +} + 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, + .probe = txc43128_phy_probe, + .init = txc43128_phy_init, + .reconfigure = txc43128_phy_reconfigure, + .poll = txc43128_phy_poll, + .fini = txc43128_phy_fini, + .remove = txc43128_phy_remove, + .get_settings = txc43128_get_settings, + .set_settings = efx_mdio_set_settings, + .test_alive = efx_mdio_test_alive, + .run_tests = txc43128_run_tests, + .test_name = txc43128_test_name, }; diff -r aa133dee7792 drivers/net/sfc/workarounds.h --- a/drivers/net/sfc/workarounds.h +++ b/drivers/net/sfc/workarounds.h @@ -1,26 +1,10 @@ /**************************************************************************** - * 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 + * Driver for Solarflare Solarstorm network controllers and boards + * Copyright 2006-2009 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - **************************************************************************** */ #ifndef EFX_WORKAROUNDS_H @@ -32,47 +16,42 @@ */ #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) +#define EFX_WORKAROUND_NEVER(efx) 0 +#define EFX_WORKAROUND_FALCON_A(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) +#define EFX_WORKAROUND_FALCON_B(efx) (efx_nic_rev(efx) == EFX_REV_FALCON_B0) +#define EFX_WORKAROUND_FALCON_AB(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_B0) +#define EFX_WORKAROUND_SIENA(efx) (efx_nic_rev(efx) == EFX_REV_SIENA_A0) +#define EFX_WORKAROUND_10G(efx) EFX_IS10G(efx) +#define EFX_WORKAROUND_SFT9001(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A || \ + (efx)->phy_type == PHY_TYPE_SFT9001B) /* 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 +#define EFX_WORKAROUND_6943 EFX_WORKAROUND_FALCON_B /* 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 +#define EFX_WORKAROUND_7884 EFX_WORKAROUND_10G /* Selftests need to be retried */ -#define EFX_WORKAROUND_8909 EFX_WORKAROUND_ALWAYS +#define EFX_WORKAROUND_8568 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 +#define EFX_WORKAROUND_9096 EFX_WORKAROUND_FALCON_B /* TX_EV_PKT_ERR can be caused by a dangling TX descriptor * or a PCIe error (bug 11028) */ #define EFX_WORKAROUND_10727 EFX_WORKAROUND_ALWAYS -/* 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 -/* Flush events can take a very long time to appear */ -#define EFX_WORKAROUND_11557 EFX_WORKAROUND_ALWAYS -/* 10Xpress is sensitive to unstable XAUI sync when going into loopback */ -#define EFX_WORKAROUND_11667 EFX_WORKAROUND_ALWAYS +#define EFX_WORKAROUND_11482 EFX_WORKAROUND_FALCON_AB +/* Truncated IPv4 packets can confuse the TX packet parser */ +#define EFX_WORKAROUND_15592 EFX_WORKAROUND_FALCON_AB +/* Legacy ISR read can return zero once */ +#define EFX_WORKAROUND_15783 EFX_WORKAROUND_SIENA +/* Legacy interrupt storm when interrupt fifo fills */ +#define EFX_WORKAROUND_17213 EFX_WORKAROUND_SIENA /* Spurious parity errors in TSORT buffers */ #define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A -/* No unaligned TX over 512 byte boundaries */ +/* Unaligned read request >512 bytes after aligning may break TSORT */ #define EFX_WORKAROUND_5391 EFX_WORKAROUND_FALCON_A /* iSCSI parsing errors */ #define EFX_WORKAROUND_5583 EFX_WORKAROUND_FALCON_A @@ -80,8 +59,6 @@ #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 /* Flushes may never complete */ @@ -89,11 +66,12 @@ /* 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 +/* Invalid BAR accesses may not be completed, leading to NMI */ +#define EFX_WORKAROUND_11368 EFX_WORKAROUND_FALCON_B + +/* Need to send XNP pages for 100BaseT */ +#define EFX_WORKAROUND_13204 EFX_WORKAROUND_SFT9001 +/* Don't restart AN in near-side loopback */ +#define EFX_WORKAROUND_15195 EFX_WORKAROUND_SFT9001 #endif /* EFX_WORKAROUNDS_H */ diff -r aa133dee7792 drivers/net/sfc/xenpack.h --- a/drivers/net/sfc/xenpack.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************** - * 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 */ diff -r aa133dee7792 drivers/net/sfc/xfp_phy.c --- a/drivers/net/sfc/xfp_phy.c +++ /dev/null @@ -1,203 +0,0 @@ -/**************************************************************************** - * 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); - - EFX_INFO(efx, "XFP: PHY init %s.\n", - rc ? "failed" : "successful"); - if (rc < 0) - goto fail; - - return 0; - - fail: - kfree(efx->phy_data); - efx->phy_data = NULL; - return rc; -} - -static void 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->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, - .startup_loopback = LOOPBACK_PCS, -};