Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 25 Oct 2011 11:25:22 +0000 (13:25 +0200)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 25 Oct 2011 11:25:22 +0000 (13:25 +0200)
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1745 commits)
  dp83640: free packet queues on remove
  dp83640: use proper function to free transmit time stamping packets
  ipv6: Do not use routes from locally generated RAs
  |PATCH net-next] tg3: add tx_dropped counter
  be2net: don't create multiple RX/TX rings in multi channel mode
  be2net: don't create multiple TXQs in BE2
  be2net: refactor VF setup/teardown code into be_vf_setup/clear()
  be2net: add vlan/rx-mode/flow-control config to be_setup()
  net_sched: cls_flow: use skb_header_pointer()
  ipv4: avoid useless call of the function check_peer_pmtu
  TCP: remove TCP_DEBUG
  net: Fix driver name for mdio-gpio.c
  ipv4: tcp: fix TOS value in ACK messages sent from TIME_WAIT
  rtnetlink: Add missing manual netlink notification in dev_change_net_namespaces
  ipv4: fix ipsec forward performance regression
  jme: fix irq storm after suspend/resume
  route: fix ICMP redirect validation
  net: hold sock reference while processing tx timestamps
  tcp: md5: add more const attributes
  Add ethtool -g support to virtio_net
  ...

Fix up conflicts in:
 - drivers/net/Kconfig:
The split-up generated a trivial conflict with removal of a
stale reference to Documentation/networking/net-modules.txt.
Remove it from the new location instead.
 - fs/sysfs/dir.c:
Fairly nasty conflicts with the sysfs rb-tree usage, conflicting
with Eric Biederman's changes for tagged directories.

28 files changed:
1  2 
MAINTAINERS
drivers/net/ethernet/atheros/atlx/atl1.c
drivers/net/ethernet/broadcom/bnx2.h
drivers/net/ethernet/dec/tulip/21142.c
drivers/net/ethernet/dec/tulip/eeprom.c
drivers/net/ethernet/dec/tulip/interrupt.c
drivers/net/ethernet/dec/tulip/media.c
drivers/net/ethernet/dec/tulip/pnic.c
drivers/net/ethernet/dec/tulip/pnic2.c
drivers/net/ethernet/dec/tulip/timer.c
drivers/net/ethernet/dec/tulip/tulip.h
drivers/net/ethernet/dec/tulip/tulip_core.c
drivers/net/ethernet/intel/igb/e1000_mbx.c
drivers/net/ethernet/intel/igbvf/mbx.c
drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c
drivers/net/ethernet/intel/ixgbevf/mbx.c
drivers/net/ethernet/marvell/sky2.c
drivers/net/ethernet/micrel/ks8695net.c
drivers/net/ethernet/smsc/Kconfig
drivers/net/ethernet/tile/tilepro.c
drivers/net/wireless/rtlwifi/pci.c
drivers/scsi/fcoe/fcoe.c
drivers/xen/xen-pciback/xenbus.c
fs/sysfs/dir.c
fs/sysfs/inode.c
include/linux/device.h
include/linux/netdevice.h
net/core/dev.c

diff --cc MAINTAINERS
Simple merge
index 0000000,0405261..33a4e35
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,3668 +1,3668 @@@
 -              /* enable GPHY LinkChange Interrrupt */
+ /*
+  * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
+  * Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com>
+  * Copyright(c) 2006 - 2008 Jay Cliburn <jcliburn@gmail.com>
+  *
+  * Derived from Intel e1000 driver
+  * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
+  *
+  * 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., 59
+  * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+  *
+  * The full GNU General Public License is included in this distribution in the
+  * file called COPYING.
+  *
+  * Contact Information:
+  * Xiong Huang <xiong.huang@atheros.com>
+  * Jie Yang <jie.yang@atheros.com>
+  * Chris Snook <csnook@redhat.com>
+  * Jay Cliburn <jcliburn@gmail.com>
+  *
+  * This version is adapted from the Attansic reference driver.
+  *
+  * TODO:
+  * Add more ethtool functions.
+  * Fix abstruse irq enable/disable condition described here:
+  *    http://marc.theaimsgroup.com/?l=linux-netdev&m=116398508500553&w=2
+  *
+  * NEEDS TESTING:
+  * VLAN
+  * multicast
+  * promiscuous mode
+  * interrupt coalescing
+  * SMP torture testing
+  */
+ #include <linux/atomic.h>
+ #include <asm/byteorder.h>
+ #include <linux/compiler.h>
+ #include <linux/crc32.h>
+ #include <linux/delay.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/etherdevice.h>
+ #include <linux/hardirq.h>
+ #include <linux/if_ether.h>
+ #include <linux/if_vlan.h>
+ #include <linux/in.h>
+ #include <linux/interrupt.h>
+ #include <linux/ip.h>
+ #include <linux/irqflags.h>
+ #include <linux/irqreturn.h>
+ #include <linux/jiffies.h>
+ #include <linux/mii.h>
+ #include <linux/module.h>
+ #include <linux/moduleparam.h>
+ #include <linux/net.h>
+ #include <linux/netdevice.h>
+ #include <linux/pci.h>
+ #include <linux/pci_ids.h>
+ #include <linux/pm.h>
+ #include <linux/skbuff.h>
+ #include <linux/slab.h>
+ #include <linux/spinlock.h>
+ #include <linux/string.h>
+ #include <linux/tcp.h>
+ #include <linux/timer.h>
+ #include <linux/types.h>
+ #include <linux/workqueue.h>
+ #include <net/checksum.h>
+ #include "atl1.h"
+ #define ATLX_DRIVER_VERSION "2.1.3"
+ MODULE_AUTHOR("Xiong Huang <xiong.huang@atheros.com>, "
+             "Chris Snook <csnook@redhat.com>, "
+             "Jay Cliburn <jcliburn@gmail.com>");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(ATLX_DRIVER_VERSION);
+ /* Temporary hack for merging atl1 and atl2 */
+ #include "atlx.c"
+ static const struct ethtool_ops atl1_ethtool_ops;
+ /*
+  * This is the only thing that needs to be changed to adjust the
+  * maximum number of ports that the driver can manage.
+  */
+ #define ATL1_MAX_NIC 4
+ #define OPTION_UNSET    -1
+ #define OPTION_DISABLED 0
+ #define OPTION_ENABLED  1
+ #define ATL1_PARAM_INIT { [0 ... ATL1_MAX_NIC] = OPTION_UNSET }
+ /*
+  * Interrupt Moderate Timer in units of 2 us
+  *
+  * Valid Range: 10-65535
+  *
+  * Default Value: 100 (200us)
+  */
+ static int __devinitdata int_mod_timer[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT;
+ static unsigned int num_int_mod_timer;
+ module_param_array_named(int_mod_timer, int_mod_timer, int,
+       &num_int_mod_timer, 0);
+ MODULE_PARM_DESC(int_mod_timer, "Interrupt moderator timer");
+ #define DEFAULT_INT_MOD_CNT   100     /* 200us */
+ #define MAX_INT_MOD_CNT               65000
+ #define MIN_INT_MOD_CNT               50
+ struct atl1_option {
+       enum { enable_option, range_option, list_option } type;
+       char *name;
+       char *err;
+       int def;
+       union {
+               struct {        /* range_option info */
+                       int min;
+                       int max;
+               } r;
+               struct {        /* list_option info */
+                       int nr;
+                       struct atl1_opt_list {
+                               int i;
+                               char *str;
+                       } *p;
+               } l;
+       } arg;
+ };
+ static int __devinit atl1_validate_option(int *value, struct atl1_option *opt,
+       struct pci_dev *pdev)
+ {
+       if (*value == OPTION_UNSET) {
+               *value = opt->def;
+               return 0;
+       }
+       switch (opt->type) {
+       case enable_option:
+               switch (*value) {
+               case OPTION_ENABLED:
+                       dev_info(&pdev->dev, "%s enabled\n", opt->name);
+                       return 0;
+               case OPTION_DISABLED:
+                       dev_info(&pdev->dev, "%s disabled\n", opt->name);
+                       return 0;
+               }
+               break;
+       case range_option:
+               if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) {
+                       dev_info(&pdev->dev, "%s set to %i\n", opt->name,
+                               *value);
+                       return 0;
+               }
+               break;
+       case list_option:{
+                       int i;
+                       struct atl1_opt_list *ent;
+                       for (i = 0; i < opt->arg.l.nr; i++) {
+                               ent = &opt->arg.l.p[i];
+                               if (*value == ent->i) {
+                                       if (ent->str[0] != '\0')
+                                               dev_info(&pdev->dev, "%s\n",
+                                                       ent->str);
+                                       return 0;
+                               }
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+       dev_info(&pdev->dev, "invalid %s specified (%i) %s\n",
+               opt->name, *value, opt->err);
+       *value = opt->def;
+       return -1;
+ }
+ /*
+  * atl1_check_options - Range Checking for Command Line Parameters
+  * @adapter: board private structure
+  *
+  * This routine checks all command line parameters for valid user
+  * input.  If an invalid value is given, or if no user specified
+  * value exists, a default value is used.  The final value is stored
+  * in a variable in the adapter structure.
+  */
+ static void __devinit atl1_check_options(struct atl1_adapter *adapter)
+ {
+       struct pci_dev *pdev = adapter->pdev;
+       int bd = adapter->bd_number;
+       if (bd >= ATL1_MAX_NIC) {
+               dev_notice(&pdev->dev, "no configuration for board#%i\n", bd);
+               dev_notice(&pdev->dev, "using defaults for all values\n");
+       }
+       {                       /* Interrupt Moderate Timer */
+               struct atl1_option opt = {
+                       .type = range_option,
+                       .name = "Interrupt Moderator Timer",
+                       .err = "using default of "
+                               __MODULE_STRING(DEFAULT_INT_MOD_CNT),
+                       .def = DEFAULT_INT_MOD_CNT,
+                       .arg = {.r = {.min = MIN_INT_MOD_CNT,
+                                       .max = MAX_INT_MOD_CNT} }
+               };
+               int val;
+               if (num_int_mod_timer > bd) {
+                       val = int_mod_timer[bd];
+                       atl1_validate_option(&val, &opt, pdev);
+                       adapter->imt = (u16) val;
+               } else
+                       adapter->imt = (u16) (opt.def);
+       }
+ }
+ /*
+  * atl1_pci_tbl - PCI Device ID Table
+  */
+ static DEFINE_PCI_DEVICE_TABLE(atl1_pci_tbl) = {
+       {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1)},
+       /* required last entry */
+       {0,}
+ };
+ MODULE_DEVICE_TABLE(pci, atl1_pci_tbl);
+ static const u32 atl1_default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE |
+       NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP;
+ static int debug = -1;
+ module_param(debug, int, 0);
+ MODULE_PARM_DESC(debug, "Message level (0=none,...,16=all)");
+ /*
+  * Reset the transmit and receive units; mask and clear all interrupts.
+  * hw - Struct containing variables accessed by shared code
+  * return : 0  or  idle status (if error)
+  */
+ static s32 atl1_reset_hw(struct atl1_hw *hw)
+ {
+       struct pci_dev *pdev = hw->back->pdev;
+       struct atl1_adapter *adapter = hw->back;
+       u32 icr;
+       int i;
+       /*
+        * Clear Interrupt mask to stop board from generating
+        * interrupts & Clear any pending interrupt events
+        */
+       /*
+        * iowrite32(0, hw->hw_addr + REG_IMR);
+        * iowrite32(0xffffffff, hw->hw_addr + REG_ISR);
+        */
+       /*
+        * Issue Soft Reset to the MAC.  This will reset the chip's
+        * transmit, receive, DMA.  It will not effect
+        * the current PCI configuration.  The global reset bit is self-
+        * clearing, and should clear within a microsecond.
+        */
+       iowrite32(MASTER_CTRL_SOFT_RST, hw->hw_addr + REG_MASTER_CTRL);
+       ioread32(hw->hw_addr + REG_MASTER_CTRL);
+       iowrite16(1, hw->hw_addr + REG_PHY_ENABLE);
+       ioread16(hw->hw_addr + REG_PHY_ENABLE);
+       /* delay about 1ms */
+       msleep(1);
+       /* Wait at least 10ms for All module to be Idle */
+       for (i = 0; i < 10; i++) {
+               icr = ioread32(hw->hw_addr + REG_IDLE_STATUS);
+               if (!icr)
+                       break;
+               /* delay 1 ms */
+               msleep(1);
+               /* FIXME: still the right way to do this? */
+               cpu_relax();
+       }
+       if (icr) {
+               if (netif_msg_hw(adapter))
+                       dev_dbg(&pdev->dev, "ICR = 0x%x\n", icr);
+               return icr;
+       }
+       return 0;
+ }
+ /* function about EEPROM
+  *
+  * check_eeprom_exist
+  * return 0 if eeprom exist
+  */
+ static int atl1_check_eeprom_exist(struct atl1_hw *hw)
+ {
+       u32 value;
+       value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL);
+       if (value & SPI_FLASH_CTRL_EN_VPD) {
+               value &= ~SPI_FLASH_CTRL_EN_VPD;
+               iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL);
+       }
+       value = ioread16(hw->hw_addr + REG_PCIE_CAP_LIST);
+       return ((value & 0xFF00) == 0x6C00) ? 0 : 1;
+ }
+ static bool atl1_read_eeprom(struct atl1_hw *hw, u32 offset, u32 *p_value)
+ {
+       int i;
+       u32 control;
+       if (offset & 3)
+               /* address do not align */
+               return false;
+       iowrite32(0, hw->hw_addr + REG_VPD_DATA);
+       control = (offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT;
+       iowrite32(control, hw->hw_addr + REG_VPD_CAP);
+       ioread32(hw->hw_addr + REG_VPD_CAP);
+       for (i = 0; i < 10; i++) {
+               msleep(2);
+               control = ioread32(hw->hw_addr + REG_VPD_CAP);
+               if (control & VPD_CAP_VPD_FLAG)
+                       break;
+       }
+       if (control & VPD_CAP_VPD_FLAG) {
+               *p_value = ioread32(hw->hw_addr + REG_VPD_DATA);
+               return true;
+       }
+       /* timeout */
+       return false;
+ }
+ /*
+  * Reads the value from a PHY register
+  * hw - Struct containing variables accessed by shared code
+  * reg_addr - address of the PHY register to read
+  */
+ static s32 atl1_read_phy_reg(struct atl1_hw *hw, u16 reg_addr, u16 *phy_data)
+ {
+       u32 val;
+       int i;
+       val = ((u32) (reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT |
+               MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW | MDIO_CLK_25_4 <<
+               MDIO_CLK_SEL_SHIFT;
+       iowrite32(val, hw->hw_addr + REG_MDIO_CTRL);
+       ioread32(hw->hw_addr + REG_MDIO_CTRL);
+       for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+               udelay(2);
+               val = ioread32(hw->hw_addr + REG_MDIO_CTRL);
+               if (!(val & (MDIO_START | MDIO_BUSY)))
+                       break;
+       }
+       if (!(val & (MDIO_START | MDIO_BUSY))) {
+               *phy_data = (u16) val;
+               return 0;
+       }
+       return ATLX_ERR_PHY;
+ }
+ #define CUSTOM_SPI_CS_SETUP   2
+ #define CUSTOM_SPI_CLK_HI     2
+ #define CUSTOM_SPI_CLK_LO     2
+ #define CUSTOM_SPI_CS_HOLD    2
+ #define CUSTOM_SPI_CS_HI      3
+ static bool atl1_spi_read(struct atl1_hw *hw, u32 addr, u32 *buf)
+ {
+       int i;
+       u32 value;
+       iowrite32(0, hw->hw_addr + REG_SPI_DATA);
+       iowrite32(addr, hw->hw_addr + REG_SPI_ADDR);
+       value = SPI_FLASH_CTRL_WAIT_READY |
+           (CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) <<
+           SPI_FLASH_CTRL_CS_SETUP_SHIFT | (CUSTOM_SPI_CLK_HI &
+                                            SPI_FLASH_CTRL_CLK_HI_MASK) <<
+           SPI_FLASH_CTRL_CLK_HI_SHIFT | (CUSTOM_SPI_CLK_LO &
+                                          SPI_FLASH_CTRL_CLK_LO_MASK) <<
+           SPI_FLASH_CTRL_CLK_LO_SHIFT | (CUSTOM_SPI_CS_HOLD &
+                                          SPI_FLASH_CTRL_CS_HOLD_MASK) <<
+           SPI_FLASH_CTRL_CS_HOLD_SHIFT | (CUSTOM_SPI_CS_HI &
+                                           SPI_FLASH_CTRL_CS_HI_MASK) <<
+           SPI_FLASH_CTRL_CS_HI_SHIFT | (1 & SPI_FLASH_CTRL_INS_MASK) <<
+           SPI_FLASH_CTRL_INS_SHIFT;
+       iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL);
+       value |= SPI_FLASH_CTRL_START;
+       iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL);
+       ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL);
+       for (i = 0; i < 10; i++) {
+               msleep(1);
+               value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL);
+               if (!(value & SPI_FLASH_CTRL_START))
+                       break;
+       }
+       if (value & SPI_FLASH_CTRL_START)
+               return false;
+       *buf = ioread32(hw->hw_addr + REG_SPI_DATA);
+       return true;
+ }
+ /*
+  * get_permanent_address
+  * return 0 if get valid mac address,
+  */
+ static int atl1_get_permanent_address(struct atl1_hw *hw)
+ {
+       u32 addr[2];
+       u32 i, control;
+       u16 reg;
+       u8 eth_addr[ETH_ALEN];
+       bool key_valid;
+       if (is_valid_ether_addr(hw->perm_mac_addr))
+               return 0;
+       /* init */
+       addr[0] = addr[1] = 0;
+       if (!atl1_check_eeprom_exist(hw)) {
+               reg = 0;
+               key_valid = false;
+               /* Read out all EEPROM content */
+               i = 0;
+               while (1) {
+                       if (atl1_read_eeprom(hw, i + 0x100, &control)) {
+                               if (key_valid) {
+                                       if (reg == REG_MAC_STA_ADDR)
+                                               addr[0] = control;
+                                       else if (reg == (REG_MAC_STA_ADDR + 4))
+                                               addr[1] = control;
+                                       key_valid = false;
+                               } else if ((control & 0xff) == 0x5A) {
+                                       key_valid = true;
+                                       reg = (u16) (control >> 16);
+                               } else
+                                       break;
+                       } else
+                               /* read error */
+                               break;
+                       i += 4;
+               }
+               *(u32 *) &eth_addr[2] = swab32(addr[0]);
+               *(u16 *) &eth_addr[0] = swab16(*(u16 *) &addr[1]);
+               if (is_valid_ether_addr(eth_addr)) {
+                       memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN);
+                       return 0;
+               }
+       }
+       /* see if SPI FLAGS exist ? */
+       addr[0] = addr[1] = 0;
+       reg = 0;
+       key_valid = false;
+       i = 0;
+       while (1) {
+               if (atl1_spi_read(hw, i + 0x1f000, &control)) {
+                       if (key_valid) {
+                               if (reg == REG_MAC_STA_ADDR)
+                                       addr[0] = control;
+                               else if (reg == (REG_MAC_STA_ADDR + 4))
+                                       addr[1] = control;
+                               key_valid = false;
+                       } else if ((control & 0xff) == 0x5A) {
+                               key_valid = true;
+                               reg = (u16) (control >> 16);
+                       } else
+                               /* data end */
+                               break;
+               } else
+                       /* read error */
+                       break;
+               i += 4;
+       }
+       *(u32 *) &eth_addr[2] = swab32(addr[0]);
+       *(u16 *) &eth_addr[0] = swab16(*(u16 *) &addr[1]);
+       if (is_valid_ether_addr(eth_addr)) {
+               memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN);
+               return 0;
+       }
+       /*
+        * On some motherboards, the MAC address is written by the
+        * BIOS directly to the MAC register during POST, and is
+        * not stored in eeprom.  If all else thus far has failed
+        * to fetch the permanent MAC address, try reading it directly.
+        */
+       addr[0] = ioread32(hw->hw_addr + REG_MAC_STA_ADDR);
+       addr[1] = ioread16(hw->hw_addr + (REG_MAC_STA_ADDR + 4));
+       *(u32 *) &eth_addr[2] = swab32(addr[0]);
+       *(u16 *) &eth_addr[0] = swab16(*(u16 *) &addr[1]);
+       if (is_valid_ether_addr(eth_addr)) {
+               memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN);
+               return 0;
+       }
+       return 1;
+ }
+ /*
+  * Reads the adapter's MAC address from the EEPROM
+  * hw - Struct containing variables accessed by shared code
+  */
+ static s32 atl1_read_mac_addr(struct atl1_hw *hw)
+ {
+       u16 i;
+       if (atl1_get_permanent_address(hw))
+               random_ether_addr(hw->perm_mac_addr);
+       for (i = 0; i < ETH_ALEN; i++)
+               hw->mac_addr[i] = hw->perm_mac_addr[i];
+       return 0;
+ }
+ /*
+  * Hashes an address to determine its location in the multicast table
+  * hw - Struct containing variables accessed by shared code
+  * mc_addr - the multicast address to hash
+  *
+  * atl1_hash_mc_addr
+  *  purpose
+  *      set hash value for a multicast address
+  *      hash calcu processing :
+  *          1. calcu 32bit CRC for multicast address
+  *          2. reverse crc with MSB to LSB
+  */
+ static u32 atl1_hash_mc_addr(struct atl1_hw *hw, u8 *mc_addr)
+ {
+       u32 crc32, value = 0;
+       int i;
+       crc32 = ether_crc_le(6, mc_addr);
+       for (i = 0; i < 32; i++)
+               value |= (((crc32 >> i) & 1) << (31 - i));
+       return value;
+ }
+ /*
+  * Sets the bit in the multicast table corresponding to the hash value.
+  * hw - Struct containing variables accessed by shared code
+  * hash_value - Multicast address hash value
+  */
+ static void atl1_hash_set(struct atl1_hw *hw, u32 hash_value)
+ {
+       u32 hash_bit, hash_reg;
+       u32 mta;
+       /*
+        * The HASH Table  is a register array of 2 32-bit registers.
+        * It is treated like an array of 64 bits.  We want to set
+        * bit BitArray[hash_value]. So we figure out what register
+        * the bit is in, read it, OR in the new bit, then write
+        * back the new value.  The register is determined by the
+        * upper 7 bits of the hash value and the bit within that
+        * register are determined by the lower 5 bits of the value.
+        */
+       hash_reg = (hash_value >> 31) & 0x1;
+       hash_bit = (hash_value >> 26) & 0x1F;
+       mta = ioread32((hw->hw_addr + REG_RX_HASH_TABLE) + (hash_reg << 2));
+       mta |= (1 << hash_bit);
+       iowrite32(mta, (hw->hw_addr + REG_RX_HASH_TABLE) + (hash_reg << 2));
+ }
+ /*
+  * Writes a value to a PHY register
+  * hw - Struct containing variables accessed by shared code
+  * reg_addr - address of the PHY register to write
+  * data - data to write to the PHY
+  */
+ static s32 atl1_write_phy_reg(struct atl1_hw *hw, u32 reg_addr, u16 phy_data)
+ {
+       int i;
+       u32 val;
+       val = ((u32) (phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT |
+           (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT |
+           MDIO_SUP_PREAMBLE |
+           MDIO_START | MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
+       iowrite32(val, hw->hw_addr + REG_MDIO_CTRL);
+       ioread32(hw->hw_addr + REG_MDIO_CTRL);
+       for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+               udelay(2);
+               val = ioread32(hw->hw_addr + REG_MDIO_CTRL);
+               if (!(val & (MDIO_START | MDIO_BUSY)))
+                       break;
+       }
+       if (!(val & (MDIO_START | MDIO_BUSY)))
+               return 0;
+       return ATLX_ERR_PHY;
+ }
+ /*
+  * Make L001's PHY out of Power Saving State (bug)
+  * hw - Struct containing variables accessed by shared code
+  * when power on, L001's PHY always on Power saving State
+  * (Gigabit Link forbidden)
+  */
+ static s32 atl1_phy_leave_power_saving(struct atl1_hw *hw)
+ {
+       s32 ret;
+       ret = atl1_write_phy_reg(hw, 29, 0x0029);
+       if (ret)
+               return ret;
+       return atl1_write_phy_reg(hw, 30, 0);
+ }
+ /*
+  * Resets the PHY and make all config validate
+  * hw - Struct containing variables accessed by shared code
+  *
+  * Sets bit 15 and 12 of the MII Control regiser (for F001 bug)
+  */
+ static s32 atl1_phy_reset(struct atl1_hw *hw)
+ {
+       struct pci_dev *pdev = hw->back->pdev;
+       struct atl1_adapter *adapter = hw->back;
+       s32 ret_val;
+       u16 phy_data;
+       if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+           hw->media_type == MEDIA_TYPE_1000M_FULL)
+               phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
+       else {
+               switch (hw->media_type) {
+               case MEDIA_TYPE_100M_FULL:
+                       phy_data =
+                           MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 |
+                           MII_CR_RESET;
+                       break;
+               case MEDIA_TYPE_100M_HALF:
+                       phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
+                       break;
+               case MEDIA_TYPE_10M_FULL:
+                       phy_data =
+                           MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET;
+                       break;
+               default:
+                       /* MEDIA_TYPE_10M_HALF: */
+                       phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
+                       break;
+               }
+       }
+       ret_val = atl1_write_phy_reg(hw, MII_BMCR, phy_data);
+       if (ret_val) {
+               u32 val;
+               int i;
+               /* pcie serdes link may be down! */
+               if (netif_msg_hw(adapter))
+                       dev_dbg(&pdev->dev, "pcie phy link down\n");
+               for (i = 0; i < 25; i++) {
+                       msleep(1);
+                       val = ioread32(hw->hw_addr + REG_MDIO_CTRL);
+                       if (!(val & (MDIO_START | MDIO_BUSY)))
+                               break;
+               }
+               if ((val & (MDIO_START | MDIO_BUSY)) != 0) {
+                       if (netif_msg_hw(adapter))
+                               dev_warn(&pdev->dev,
+                                       "pcie link down at least 25ms\n");
+                       return ret_val;
+               }
+       }
+       return 0;
+ }
+ /*
+  * Configures PHY autoneg and flow control advertisement settings
+  * hw - Struct containing variables accessed by shared code
+  */
+ static s32 atl1_phy_setup_autoneg_adv(struct atl1_hw *hw)
+ {
+       s32 ret_val;
+       s16 mii_autoneg_adv_reg;
+       s16 mii_1000t_ctrl_reg;
+       /* Read the MII Auto-Neg Advertisement Register (Address 4). */
+       mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK;
+       /* Read the MII 1000Base-T Control Register (Address 9). */
+       mii_1000t_ctrl_reg = MII_ATLX_CR_1000T_DEFAULT_CAP_MASK;
+       /*
+        * First we clear all the 10/100 mb speed bits in the Auto-Neg
+        * Advertisement Register (Address 4) and the 1000 mb speed bits in
+        * the  1000Base-T Control Register (Address 9).
+        */
+       mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK;
+       mii_1000t_ctrl_reg &= ~MII_ATLX_CR_1000T_SPEED_MASK;
+       /*
+        * Need to parse media_type  and set up
+        * the appropriate PHY registers.
+        */
+       switch (hw->media_type) {
+       case MEDIA_TYPE_AUTO_SENSOR:
+               mii_autoneg_adv_reg |= (MII_AR_10T_HD_CAPS |
+                                       MII_AR_10T_FD_CAPS |
+                                       MII_AR_100TX_HD_CAPS |
+                                       MII_AR_100TX_FD_CAPS);
+               mii_1000t_ctrl_reg |= MII_ATLX_CR_1000T_FD_CAPS;
+               break;
+       case MEDIA_TYPE_1000M_FULL:
+               mii_1000t_ctrl_reg |= MII_ATLX_CR_1000T_FD_CAPS;
+               break;
+       case MEDIA_TYPE_100M_FULL:
+               mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS;
+               break;
+       case MEDIA_TYPE_100M_HALF:
+               mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS;
+               break;
+       case MEDIA_TYPE_10M_FULL:
+               mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS;
+               break;
+       default:
+               mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS;
+               break;
+       }
+       /* flow control fixed to enable all */
+       mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE);
+       hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg;
+       hw->mii_1000t_ctrl_reg = mii_1000t_ctrl_reg;
+       ret_val = atl1_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg);
+       if (ret_val)
+               return ret_val;
+       ret_val = atl1_write_phy_reg(hw, MII_ATLX_CR, mii_1000t_ctrl_reg);
+       if (ret_val)
+               return ret_val;
+       return 0;
+ }
+ /*
+  * Configures link settings.
+  * hw - Struct containing variables accessed by shared code
+  * Assumes the hardware has previously been reset and the
+  * transmitter and receiver are not enabled.
+  */
+ static s32 atl1_setup_link(struct atl1_hw *hw)
+ {
+       struct pci_dev *pdev = hw->back->pdev;
+       struct atl1_adapter *adapter = hw->back;
+       s32 ret_val;
+       /*
+        * Options:
+        *  PHY will advertise value(s) parsed from
+        *  autoneg_advertised and fc
+        *  no matter what autoneg is , We will not wait link result.
+        */
+       ret_val = atl1_phy_setup_autoneg_adv(hw);
+       if (ret_val) {
+               if (netif_msg_link(adapter))
+                       dev_dbg(&pdev->dev,
+                               "error setting up autonegotiation\n");
+               return ret_val;
+       }
+       /* SW.Reset , En-Auto-Neg if needed */
+       ret_val = atl1_phy_reset(hw);
+       if (ret_val) {
+               if (netif_msg_link(adapter))
+                       dev_dbg(&pdev->dev, "error resetting phy\n");
+               return ret_val;
+       }
+       hw->phy_configured = true;
+       return ret_val;
+ }
+ static void atl1_init_flash_opcode(struct atl1_hw *hw)
+ {
+       if (hw->flash_vendor >= ARRAY_SIZE(flash_table))
+               /* Atmel */
+               hw->flash_vendor = 0;
+       /* Init OP table */
+       iowrite8(flash_table[hw->flash_vendor].cmd_program,
+               hw->hw_addr + REG_SPI_FLASH_OP_PROGRAM);
+       iowrite8(flash_table[hw->flash_vendor].cmd_sector_erase,
+               hw->hw_addr + REG_SPI_FLASH_OP_SC_ERASE);
+       iowrite8(flash_table[hw->flash_vendor].cmd_chip_erase,
+               hw->hw_addr + REG_SPI_FLASH_OP_CHIP_ERASE);
+       iowrite8(flash_table[hw->flash_vendor].cmd_rdid,
+               hw->hw_addr + REG_SPI_FLASH_OP_RDID);
+       iowrite8(flash_table[hw->flash_vendor].cmd_wren,
+               hw->hw_addr + REG_SPI_FLASH_OP_WREN);
+       iowrite8(flash_table[hw->flash_vendor].cmd_rdsr,
+               hw->hw_addr + REG_SPI_FLASH_OP_RDSR);
+       iowrite8(flash_table[hw->flash_vendor].cmd_wrsr,
+               hw->hw_addr + REG_SPI_FLASH_OP_WRSR);
+       iowrite8(flash_table[hw->flash_vendor].cmd_read,
+               hw->hw_addr + REG_SPI_FLASH_OP_READ);
+ }
+ /*
+  * Performs basic configuration of the adapter.
+  * hw - Struct containing variables accessed by shared code
+  * Assumes that the controller has previously been reset and is in a
+  * post-reset uninitialized state. Initializes multicast table,
+  * and  Calls routines to setup link
+  * Leaves the transmit and receive units disabled and uninitialized.
+  */
+ static s32 atl1_init_hw(struct atl1_hw *hw)
+ {
+       u32 ret_val = 0;
+       /* Zero out the Multicast HASH table */
+       iowrite32(0, hw->hw_addr + REG_RX_HASH_TABLE);
+       /* clear the old settings from the multicast hash table */
+       iowrite32(0, (hw->hw_addr + REG_RX_HASH_TABLE) + (1 << 2));
+       atl1_init_flash_opcode(hw);
+       if (!hw->phy_configured) {
++              /* enable GPHY LinkChange Interrupt */
+               ret_val = atl1_write_phy_reg(hw, 18, 0xC00);
+               if (ret_val)
+                       return ret_val;
+               /* make PHY out of power-saving state */
+               ret_val = atl1_phy_leave_power_saving(hw);
+               if (ret_val)
+                       return ret_val;
+               /* Call a subroutine to configure the link */
+               ret_val = atl1_setup_link(hw);
+       }
+       return ret_val;
+ }
+ /*
+  * Detects the current speed and duplex settings of the hardware.
+  * hw - Struct containing variables accessed by shared code
+  * speed - Speed of the connection
+  * duplex - Duplex setting of the connection
+  */
+ static s32 atl1_get_speed_and_duplex(struct atl1_hw *hw, u16 *speed, u16 *duplex)
+ {
+       struct pci_dev *pdev = hw->back->pdev;
+       struct atl1_adapter *adapter = hw->back;
+       s32 ret_val;
+       u16 phy_data;
+       /* ; --- Read   PHY Specific Status Register (17) */
+       ret_val = atl1_read_phy_reg(hw, MII_ATLX_PSSR, &phy_data);
+       if (ret_val)
+               return ret_val;
+       if (!(phy_data & MII_ATLX_PSSR_SPD_DPLX_RESOLVED))
+               return ATLX_ERR_PHY_RES;
+       switch (phy_data & MII_ATLX_PSSR_SPEED) {
+       case MII_ATLX_PSSR_1000MBS:
+               *speed = SPEED_1000;
+               break;
+       case MII_ATLX_PSSR_100MBS:
+               *speed = SPEED_100;
+               break;
+       case MII_ATLX_PSSR_10MBS:
+               *speed = SPEED_10;
+               break;
+       default:
+               if (netif_msg_hw(adapter))
+                       dev_dbg(&pdev->dev, "error getting speed\n");
+               return ATLX_ERR_PHY_SPEED;
+               break;
+       }
+       if (phy_data & MII_ATLX_PSSR_DPLX)
+               *duplex = FULL_DUPLEX;
+       else
+               *duplex = HALF_DUPLEX;
+       return 0;
+ }
+ static void atl1_set_mac_addr(struct atl1_hw *hw)
+ {
+       u32 value;
+       /*
+        * 00-0B-6A-F6-00-DC
+        * 0:  6AF600DC   1: 000B
+        * low dword
+        */
+       value = (((u32) hw->mac_addr[2]) << 24) |
+           (((u32) hw->mac_addr[3]) << 16) |
+           (((u32) hw->mac_addr[4]) << 8) | (((u32) hw->mac_addr[5]));
+       iowrite32(value, hw->hw_addr + REG_MAC_STA_ADDR);
+       /* high dword */
+       value = (((u32) hw->mac_addr[0]) << 8) | (((u32) hw->mac_addr[1]));
+       iowrite32(value, (hw->hw_addr + REG_MAC_STA_ADDR) + (1 << 2));
+ }
+ /*
+  * atl1_sw_init - Initialize general software structures (struct atl1_adapter)
+  * @adapter: board private structure to initialize
+  *
+  * atl1_sw_init initializes the Adapter private data structure.
+  * Fields are initialized based on PCI device information and
+  * OS network device settings (MTU size).
+  */
+ static int __devinit atl1_sw_init(struct atl1_adapter *adapter)
+ {
+       struct atl1_hw *hw = &adapter->hw;
+       struct net_device *netdev = adapter->netdev;
+       hw->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+       hw->min_frame_size = ETH_ZLEN + ETH_FCS_LEN;
+       adapter->wol = 0;
+       device_set_wakeup_enable(&adapter->pdev->dev, false);
+       adapter->rx_buffer_len = (hw->max_frame_size + 7) & ~7;
+       adapter->ict = 50000;           /* 100ms */
+       adapter->link_speed = SPEED_0;  /* hardware init */
+       adapter->link_duplex = FULL_DUPLEX;
+       hw->phy_configured = false;
+       hw->preamble_len = 7;
+       hw->ipgt = 0x60;
+       hw->min_ifg = 0x50;
+       hw->ipgr1 = 0x40;
+       hw->ipgr2 = 0x60;
+       hw->max_retry = 0xf;
+       hw->lcol = 0x37;
+       hw->jam_ipg = 7;
+       hw->rfd_burst = 8;
+       hw->rrd_burst = 8;
+       hw->rfd_fetch_gap = 1;
+       hw->rx_jumbo_th = adapter->rx_buffer_len / 8;
+       hw->rx_jumbo_lkah = 1;
+       hw->rrd_ret_timer = 16;
+       hw->tpd_burst = 4;
+       hw->tpd_fetch_th = 16;
+       hw->txf_burst = 0x100;
+       hw->tx_jumbo_task_th = (hw->max_frame_size + 7) >> 3;
+       hw->tpd_fetch_gap = 1;
+       hw->rcb_value = atl1_rcb_64;
+       hw->dma_ord = atl1_dma_ord_enh;
+       hw->dmar_block = atl1_dma_req_256;
+       hw->dmaw_block = atl1_dma_req_256;
+       hw->cmb_rrd = 4;
+       hw->cmb_tpd = 4;
+       hw->cmb_rx_timer = 1;   /* about 2us */
+       hw->cmb_tx_timer = 1;   /* about 2us */
+       hw->smb_timer = 100000; /* about 200ms */
+       spin_lock_init(&adapter->lock);
+       spin_lock_init(&adapter->mb_lock);
+       return 0;
+ }
+ static int mdio_read(struct net_device *netdev, int phy_id, int reg_num)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       u16 result;
+       atl1_read_phy_reg(&adapter->hw, reg_num & 0x1f, &result);
+       return result;
+ }
+ static void mdio_write(struct net_device *netdev, int phy_id, int reg_num,
+       int val)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       atl1_write_phy_reg(&adapter->hw, reg_num, val);
+ }
+ /*
+  * atl1_mii_ioctl -
+  * @netdev:
+  * @ifreq:
+  * @cmd:
+  */
+ static int atl1_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       unsigned long flags;
+       int retval;
+       if (!netif_running(netdev))
+               return -EINVAL;
+       spin_lock_irqsave(&adapter->lock, flags);
+       retval = generic_mii_ioctl(&adapter->mii, if_mii(ifr), cmd, NULL);
+       spin_unlock_irqrestore(&adapter->lock, flags);
+       return retval;
+ }
+ /*
+  * atl1_setup_mem_resources - allocate Tx / RX descriptor resources
+  * @adapter: board private structure
+  *
+  * Return 0 on success, negative on failure
+  */
+ static s32 atl1_setup_ring_resources(struct atl1_adapter *adapter)
+ {
+       struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+       struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+       struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring;
+       struct atl1_ring_header *ring_header = &adapter->ring_header;
+       struct pci_dev *pdev = adapter->pdev;
+       int size;
+       u8 offset = 0;
+       size = sizeof(struct atl1_buffer) * (tpd_ring->count + rfd_ring->count);
+       tpd_ring->buffer_info = kzalloc(size, GFP_KERNEL);
+       if (unlikely(!tpd_ring->buffer_info)) {
+               if (netif_msg_drv(adapter))
+                       dev_err(&pdev->dev, "kzalloc failed , size = D%d\n",
+                               size);
+               goto err_nomem;
+       }
+       rfd_ring->buffer_info =
+               (struct atl1_buffer *)(tpd_ring->buffer_info + tpd_ring->count);
+       /*
+        * real ring DMA buffer
+        * each ring/block may need up to 8 bytes for alignment, hence the
+        * additional 40 bytes tacked onto the end.
+        */
+       ring_header->size = size =
+               sizeof(struct tx_packet_desc) * tpd_ring->count
+               + sizeof(struct rx_free_desc) * rfd_ring->count
+               + sizeof(struct rx_return_desc) * rrd_ring->count
+               + sizeof(struct coals_msg_block)
+               + sizeof(struct stats_msg_block)
+               + 40;
+       ring_header->desc = pci_alloc_consistent(pdev, ring_header->size,
+               &ring_header->dma);
+       if (unlikely(!ring_header->desc)) {
+               if (netif_msg_drv(adapter))
+                       dev_err(&pdev->dev, "pci_alloc_consistent failed\n");
+               goto err_nomem;
+       }
+       memset(ring_header->desc, 0, ring_header->size);
+       /* init TPD ring */
+       tpd_ring->dma = ring_header->dma;
+       offset = (tpd_ring->dma & 0x7) ? (8 - (ring_header->dma & 0x7)) : 0;
+       tpd_ring->dma += offset;
+       tpd_ring->desc = (u8 *) ring_header->desc + offset;
+       tpd_ring->size = sizeof(struct tx_packet_desc) * tpd_ring->count;
+       /* init RFD ring */
+       rfd_ring->dma = tpd_ring->dma + tpd_ring->size;
+       offset = (rfd_ring->dma & 0x7) ? (8 - (rfd_ring->dma & 0x7)) : 0;
+       rfd_ring->dma += offset;
+       rfd_ring->desc = (u8 *) tpd_ring->desc + (tpd_ring->size + offset);
+       rfd_ring->size = sizeof(struct rx_free_desc) * rfd_ring->count;
+       /* init RRD ring */
+       rrd_ring->dma = rfd_ring->dma + rfd_ring->size;
+       offset = (rrd_ring->dma & 0x7) ? (8 - (rrd_ring->dma & 0x7)) : 0;
+       rrd_ring->dma += offset;
+       rrd_ring->desc = (u8 *) rfd_ring->desc + (rfd_ring->size + offset);
+       rrd_ring->size = sizeof(struct rx_return_desc) * rrd_ring->count;
+       /* init CMB */
+       adapter->cmb.dma = rrd_ring->dma + rrd_ring->size;
+       offset = (adapter->cmb.dma & 0x7) ? (8 - (adapter->cmb.dma & 0x7)) : 0;
+       adapter->cmb.dma += offset;
+       adapter->cmb.cmb = (struct coals_msg_block *)
+               ((u8 *) rrd_ring->desc + (rrd_ring->size + offset));
+       /* init SMB */
+       adapter->smb.dma = adapter->cmb.dma + sizeof(struct coals_msg_block);
+       offset = (adapter->smb.dma & 0x7) ? (8 - (adapter->smb.dma & 0x7)) : 0;
+       adapter->smb.dma += offset;
+       adapter->smb.smb = (struct stats_msg_block *)
+               ((u8 *) adapter->cmb.cmb +
+               (sizeof(struct coals_msg_block) + offset));
+       return 0;
+ err_nomem:
+       kfree(tpd_ring->buffer_info);
+       return -ENOMEM;
+ }
+ static void atl1_init_ring_ptrs(struct atl1_adapter *adapter)
+ {
+       struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+       struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+       struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring;
+       atomic_set(&tpd_ring->next_to_use, 0);
+       atomic_set(&tpd_ring->next_to_clean, 0);
+       rfd_ring->next_to_clean = 0;
+       atomic_set(&rfd_ring->next_to_use, 0);
+       rrd_ring->next_to_use = 0;
+       atomic_set(&rrd_ring->next_to_clean, 0);
+ }
+ /*
+  * atl1_clean_rx_ring - Free RFD Buffers
+  * @adapter: board private structure
+  */
+ static void atl1_clean_rx_ring(struct atl1_adapter *adapter)
+ {
+       struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+       struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring;
+       struct atl1_buffer *buffer_info;
+       struct pci_dev *pdev = adapter->pdev;
+       unsigned long size;
+       unsigned int i;
+       /* Free all the Rx ring sk_buffs */
+       for (i = 0; i < rfd_ring->count; i++) {
+               buffer_info = &rfd_ring->buffer_info[i];
+               if (buffer_info->dma) {
+                       pci_unmap_page(pdev, buffer_info->dma,
+                               buffer_info->length, PCI_DMA_FROMDEVICE);
+                       buffer_info->dma = 0;
+               }
+               if (buffer_info->skb) {
+                       dev_kfree_skb(buffer_info->skb);
+                       buffer_info->skb = NULL;
+               }
+       }
+       size = sizeof(struct atl1_buffer) * rfd_ring->count;
+       memset(rfd_ring->buffer_info, 0, size);
+       /* Zero out the descriptor ring */
+       memset(rfd_ring->desc, 0, rfd_ring->size);
+       rfd_ring->next_to_clean = 0;
+       atomic_set(&rfd_ring->next_to_use, 0);
+       rrd_ring->next_to_use = 0;
+       atomic_set(&rrd_ring->next_to_clean, 0);
+ }
+ /*
+  * atl1_clean_tx_ring - Free Tx Buffers
+  * @adapter: board private structure
+  */
+ static void atl1_clean_tx_ring(struct atl1_adapter *adapter)
+ {
+       struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+       struct atl1_buffer *buffer_info;
+       struct pci_dev *pdev = adapter->pdev;
+       unsigned long size;
+       unsigned int i;
+       /* Free all the Tx ring sk_buffs */
+       for (i = 0; i < tpd_ring->count; i++) {
+               buffer_info = &tpd_ring->buffer_info[i];
+               if (buffer_info->dma) {
+                       pci_unmap_page(pdev, buffer_info->dma,
+                               buffer_info->length, PCI_DMA_TODEVICE);
+                       buffer_info->dma = 0;
+               }
+       }
+       for (i = 0; i < tpd_ring->count; i++) {
+               buffer_info = &tpd_ring->buffer_info[i];
+               if (buffer_info->skb) {
+                       dev_kfree_skb_any(buffer_info->skb);
+                       buffer_info->skb = NULL;
+               }
+       }
+       size = sizeof(struct atl1_buffer) * tpd_ring->count;
+       memset(tpd_ring->buffer_info, 0, size);
+       /* Zero out the descriptor ring */
+       memset(tpd_ring->desc, 0, tpd_ring->size);
+       atomic_set(&tpd_ring->next_to_use, 0);
+       atomic_set(&tpd_ring->next_to_clean, 0);
+ }
+ /*
+  * atl1_free_ring_resources - Free Tx / RX descriptor Resources
+  * @adapter: board private structure
+  *
+  * Free all transmit software resources
+  */
+ static void atl1_free_ring_resources(struct atl1_adapter *adapter)
+ {
+       struct pci_dev *pdev = adapter->pdev;
+       struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+       struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+       struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring;
+       struct atl1_ring_header *ring_header = &adapter->ring_header;
+       atl1_clean_tx_ring(adapter);
+       atl1_clean_rx_ring(adapter);
+       kfree(tpd_ring->buffer_info);
+       pci_free_consistent(pdev, ring_header->size, ring_header->desc,
+               ring_header->dma);
+       tpd_ring->buffer_info = NULL;
+       tpd_ring->desc = NULL;
+       tpd_ring->dma = 0;
+       rfd_ring->buffer_info = NULL;
+       rfd_ring->desc = NULL;
+       rfd_ring->dma = 0;
+       rrd_ring->desc = NULL;
+       rrd_ring->dma = 0;
+       adapter->cmb.dma = 0;
+       adapter->cmb.cmb = NULL;
+       adapter->smb.dma = 0;
+       adapter->smb.smb = NULL;
+ }
+ static void atl1_setup_mac_ctrl(struct atl1_adapter *adapter)
+ {
+       u32 value;
+       struct atl1_hw *hw = &adapter->hw;
+       struct net_device *netdev = adapter->netdev;
+       /* Config MAC CTRL Register */
+       value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN;
+       /* duplex */
+       if (FULL_DUPLEX == adapter->link_duplex)
+               value |= MAC_CTRL_DUPLX;
+       /* speed */
+       value |= ((u32) ((SPEED_1000 == adapter->link_speed) ?
+                        MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100) <<
+                 MAC_CTRL_SPEED_SHIFT);
+       /* flow control */
+       value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW);
+       /* PAD & CRC */
+       value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD);
+       /* preamble length */
+       value |= (((u32) adapter->hw.preamble_len
+                  & MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
+       /* vlan */
+       __atlx_vlan_mode(netdev->features, &value);
+       /* rx checksum
+          if (adapter->rx_csum)
+          value |= MAC_CTRL_RX_CHKSUM_EN;
+        */
+       /* filter mode */
+       value |= MAC_CTRL_BC_EN;
+       if (netdev->flags & IFF_PROMISC)
+               value |= MAC_CTRL_PROMIS_EN;
+       else if (netdev->flags & IFF_ALLMULTI)
+               value |= MAC_CTRL_MC_ALL_EN;
+       /* value |= MAC_CTRL_LOOPBACK; */
+       iowrite32(value, hw->hw_addr + REG_MAC_CTRL);
+ }
+ static u32 atl1_check_link(struct atl1_adapter *adapter)
+ {
+       struct atl1_hw *hw = &adapter->hw;
+       struct net_device *netdev = adapter->netdev;
+       u32 ret_val;
+       u16 speed, duplex, phy_data;
+       int reconfig = 0;
+       /* MII_BMSR must read twice */
+       atl1_read_phy_reg(hw, MII_BMSR, &phy_data);
+       atl1_read_phy_reg(hw, MII_BMSR, &phy_data);
+       if (!(phy_data & BMSR_LSTATUS)) {
+               /* link down */
+               if (netif_carrier_ok(netdev)) {
+                       /* old link state: Up */
+                       if (netif_msg_link(adapter))
+                               dev_info(&adapter->pdev->dev, "link is down\n");
+                       adapter->link_speed = SPEED_0;
+                       netif_carrier_off(netdev);
+               }
+               return 0;
+       }
+       /* Link Up */
+       ret_val = atl1_get_speed_and_duplex(hw, &speed, &duplex);
+       if (ret_val)
+               return ret_val;
+       switch (hw->media_type) {
+       case MEDIA_TYPE_1000M_FULL:
+               if (speed != SPEED_1000 || duplex != FULL_DUPLEX)
+                       reconfig = 1;
+               break;
+       case MEDIA_TYPE_100M_FULL:
+               if (speed != SPEED_100 || duplex != FULL_DUPLEX)
+                       reconfig = 1;
+               break;
+       case MEDIA_TYPE_100M_HALF:
+               if (speed != SPEED_100 || duplex != HALF_DUPLEX)
+                       reconfig = 1;
+               break;
+       case MEDIA_TYPE_10M_FULL:
+               if (speed != SPEED_10 || duplex != FULL_DUPLEX)
+                       reconfig = 1;
+               break;
+       case MEDIA_TYPE_10M_HALF:
+               if (speed != SPEED_10 || duplex != HALF_DUPLEX)
+                       reconfig = 1;
+               break;
+       }
+       /* link result is our setting */
+       if (!reconfig) {
+               if (adapter->link_speed != speed ||
+                   adapter->link_duplex != duplex) {
+                       adapter->link_speed = speed;
+                       adapter->link_duplex = duplex;
+                       atl1_setup_mac_ctrl(adapter);
+                       if (netif_msg_link(adapter))
+                               dev_info(&adapter->pdev->dev,
+                                       "%s link is up %d Mbps %s\n",
+                                       netdev->name, adapter->link_speed,
+                                       adapter->link_duplex == FULL_DUPLEX ?
+                                       "full duplex" : "half duplex");
+               }
+               if (!netif_carrier_ok(netdev)) {
+                       /* Link down -> Up */
+                       netif_carrier_on(netdev);
+               }
+               return 0;
+       }
+       /* change original link status */
+       if (netif_carrier_ok(netdev)) {
+               adapter->link_speed = SPEED_0;
+               netif_carrier_off(netdev);
+               netif_stop_queue(netdev);
+       }
+       if (hw->media_type != MEDIA_TYPE_AUTO_SENSOR &&
+           hw->media_type != MEDIA_TYPE_1000M_FULL) {
+               switch (hw->media_type) {
+               case MEDIA_TYPE_100M_FULL:
+                       phy_data = MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 |
+                                  MII_CR_RESET;
+                       break;
+               case MEDIA_TYPE_100M_HALF:
+                       phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
+                       break;
+               case MEDIA_TYPE_10M_FULL:
+                       phy_data =
+                           MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET;
+                       break;
+               default:
+                       /* MEDIA_TYPE_10M_HALF: */
+                       phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
+                       break;
+               }
+               atl1_write_phy_reg(hw, MII_BMCR, phy_data);
+               return 0;
+       }
+       /* auto-neg, insert timer to re-config phy */
+       if (!adapter->phy_timer_pending) {
+               adapter->phy_timer_pending = true;
+               mod_timer(&adapter->phy_config_timer,
+                         round_jiffies(jiffies + 3 * HZ));
+       }
+       return 0;
+ }
+ static void set_flow_ctrl_old(struct atl1_adapter *adapter)
+ {
+       u32 hi, lo, value;
+       /* RFD Flow Control */
+       value = adapter->rfd_ring.count;
+       hi = value / 16;
+       if (hi < 2)
+               hi = 2;
+       lo = value * 7 / 8;
+       value = ((hi & RXQ_RXF_PAUSE_TH_HI_MASK) << RXQ_RXF_PAUSE_TH_HI_SHIFT) |
+               ((lo & RXQ_RXF_PAUSE_TH_LO_MASK) << RXQ_RXF_PAUSE_TH_LO_SHIFT);
+       iowrite32(value, adapter->hw.hw_addr + REG_RXQ_RXF_PAUSE_THRESH);
+       /* RRD Flow Control */
+       value = adapter->rrd_ring.count;
+       lo = value / 16;
+       hi = value * 7 / 8;
+       if (lo < 2)
+               lo = 2;
+       value = ((hi & RXQ_RRD_PAUSE_TH_HI_MASK) << RXQ_RRD_PAUSE_TH_HI_SHIFT) |
+               ((lo & RXQ_RRD_PAUSE_TH_LO_MASK) << RXQ_RRD_PAUSE_TH_LO_SHIFT);
+       iowrite32(value, adapter->hw.hw_addr + REG_RXQ_RRD_PAUSE_THRESH);
+ }
+ static void set_flow_ctrl_new(struct atl1_hw *hw)
+ {
+       u32 hi, lo, value;
+       /* RXF Flow Control */
+       value = ioread32(hw->hw_addr + REG_SRAM_RXF_LEN);
+       lo = value / 16;
+       if (lo < 192)
+               lo = 192;
+       hi = value * 7 / 8;
+       if (hi < lo)
+               hi = lo + 16;
+       value = ((hi & RXQ_RXF_PAUSE_TH_HI_MASK) << RXQ_RXF_PAUSE_TH_HI_SHIFT) |
+               ((lo & RXQ_RXF_PAUSE_TH_LO_MASK) << RXQ_RXF_PAUSE_TH_LO_SHIFT);
+       iowrite32(value, hw->hw_addr + REG_RXQ_RXF_PAUSE_THRESH);
+       /* RRD Flow Control */
+       value = ioread32(hw->hw_addr + REG_SRAM_RRD_LEN);
+       lo = value / 8;
+       hi = value * 7 / 8;
+       if (lo < 2)
+               lo = 2;
+       if (hi < lo)
+               hi = lo + 3;
+       value = ((hi & RXQ_RRD_PAUSE_TH_HI_MASK) << RXQ_RRD_PAUSE_TH_HI_SHIFT) |
+               ((lo & RXQ_RRD_PAUSE_TH_LO_MASK) << RXQ_RRD_PAUSE_TH_LO_SHIFT);
+       iowrite32(value, hw->hw_addr + REG_RXQ_RRD_PAUSE_THRESH);
+ }
+ /*
+  * atl1_configure - Configure Transmit&Receive Unit after Reset
+  * @adapter: board private structure
+  *
+  * Configure the Tx /Rx unit of the MAC after a reset.
+  */
+ static u32 atl1_configure(struct atl1_adapter *adapter)
+ {
+       struct atl1_hw *hw = &adapter->hw;
+       u32 value;
+       /* clear interrupt status */
+       iowrite32(0xffffffff, adapter->hw.hw_addr + REG_ISR);
+       /* set MAC Address */
+       value = (((u32) hw->mac_addr[2]) << 24) |
+               (((u32) hw->mac_addr[3]) << 16) |
+               (((u32) hw->mac_addr[4]) << 8) |
+               (((u32) hw->mac_addr[5]));
+       iowrite32(value, hw->hw_addr + REG_MAC_STA_ADDR);
+       value = (((u32) hw->mac_addr[0]) << 8) | (((u32) hw->mac_addr[1]));
+       iowrite32(value, hw->hw_addr + (REG_MAC_STA_ADDR + 4));
+       /* tx / rx ring */
+       /* HI base address */
+       iowrite32((u32) ((adapter->tpd_ring.dma & 0xffffffff00000000ULL) >> 32),
+               hw->hw_addr + REG_DESC_BASE_ADDR_HI);
+       /* LO base address */
+       iowrite32((u32) (adapter->rfd_ring.dma & 0x00000000ffffffffULL),
+               hw->hw_addr + REG_DESC_RFD_ADDR_LO);
+       iowrite32((u32) (adapter->rrd_ring.dma & 0x00000000ffffffffULL),
+               hw->hw_addr + REG_DESC_RRD_ADDR_LO);
+       iowrite32((u32) (adapter->tpd_ring.dma & 0x00000000ffffffffULL),
+               hw->hw_addr + REG_DESC_TPD_ADDR_LO);
+       iowrite32((u32) (adapter->cmb.dma & 0x00000000ffffffffULL),
+               hw->hw_addr + REG_DESC_CMB_ADDR_LO);
+       iowrite32((u32) (adapter->smb.dma & 0x00000000ffffffffULL),
+               hw->hw_addr + REG_DESC_SMB_ADDR_LO);
+       /* element count */
+       value = adapter->rrd_ring.count;
+       value <<= 16;
+       value += adapter->rfd_ring.count;
+       iowrite32(value, hw->hw_addr + REG_DESC_RFD_RRD_RING_SIZE);
+       iowrite32(adapter->tpd_ring.count, hw->hw_addr +
+               REG_DESC_TPD_RING_SIZE);
+       /* Load Ptr */
+       iowrite32(1, hw->hw_addr + REG_LOAD_PTR);
+       /* config Mailbox */
+       value = ((atomic_read(&adapter->tpd_ring.next_to_use)
+                 & MB_TPD_PROD_INDX_MASK) << MB_TPD_PROD_INDX_SHIFT) |
+               ((atomic_read(&adapter->rrd_ring.next_to_clean)
+               & MB_RRD_CONS_INDX_MASK) << MB_RRD_CONS_INDX_SHIFT) |
+               ((atomic_read(&adapter->rfd_ring.next_to_use)
+               & MB_RFD_PROD_INDX_MASK) << MB_RFD_PROD_INDX_SHIFT);
+       iowrite32(value, hw->hw_addr + REG_MAILBOX);
+       /* config IPG/IFG */
+       value = (((u32) hw->ipgt & MAC_IPG_IFG_IPGT_MASK)
+                << MAC_IPG_IFG_IPGT_SHIFT) |
+               (((u32) hw->min_ifg & MAC_IPG_IFG_MIFG_MASK)
+               << MAC_IPG_IFG_MIFG_SHIFT) |
+               (((u32) hw->ipgr1 & MAC_IPG_IFG_IPGR1_MASK)
+               << MAC_IPG_IFG_IPGR1_SHIFT) |
+               (((u32) hw->ipgr2 & MAC_IPG_IFG_IPGR2_MASK)
+               << MAC_IPG_IFG_IPGR2_SHIFT);
+       iowrite32(value, hw->hw_addr + REG_MAC_IPG_IFG);
+       /* config  Half-Duplex Control */
+       value = ((u32) hw->lcol & MAC_HALF_DUPLX_CTRL_LCOL_MASK) |
+               (((u32) hw->max_retry & MAC_HALF_DUPLX_CTRL_RETRY_MASK)
+               << MAC_HALF_DUPLX_CTRL_RETRY_SHIFT) |
+               MAC_HALF_DUPLX_CTRL_EXC_DEF_EN |
+               (0xa << MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT) |
+               (((u32) hw->jam_ipg & MAC_HALF_DUPLX_CTRL_JAMIPG_MASK)
+               << MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT);
+       iowrite32(value, hw->hw_addr + REG_MAC_HALF_DUPLX_CTRL);
+       /* set Interrupt Moderator Timer */
+       iowrite16(adapter->imt, hw->hw_addr + REG_IRQ_MODU_TIMER_INIT);
+       iowrite32(MASTER_CTRL_ITIMER_EN, hw->hw_addr + REG_MASTER_CTRL);
+       /* set Interrupt Clear Timer */
+       iowrite16(adapter->ict, hw->hw_addr + REG_CMBDISDMA_TIMER);
+       /* set max frame size hw will accept */
+       iowrite32(hw->max_frame_size, hw->hw_addr + REG_MTU);
+       /* jumbo size & rrd retirement timer */
+       value = (((u32) hw->rx_jumbo_th & RXQ_JMBOSZ_TH_MASK)
+                << RXQ_JMBOSZ_TH_SHIFT) |
+               (((u32) hw->rx_jumbo_lkah & RXQ_JMBO_LKAH_MASK)
+               << RXQ_JMBO_LKAH_SHIFT) |
+               (((u32) hw->rrd_ret_timer & RXQ_RRD_TIMER_MASK)
+               << RXQ_RRD_TIMER_SHIFT);
+       iowrite32(value, hw->hw_addr + REG_RXQ_JMBOSZ_RRDTIM);
+       /* Flow Control */
+       switch (hw->dev_rev) {
+       case 0x8001:
+       case 0x9001:
+       case 0x9002:
+       case 0x9003:
+               set_flow_ctrl_old(adapter);
+               break;
+       default:
+               set_flow_ctrl_new(hw);
+               break;
+       }
+       /* config TXQ */
+       value = (((u32) hw->tpd_burst & TXQ_CTRL_TPD_BURST_NUM_MASK)
+                << TXQ_CTRL_TPD_BURST_NUM_SHIFT) |
+               (((u32) hw->txf_burst & TXQ_CTRL_TXF_BURST_NUM_MASK)
+               << TXQ_CTRL_TXF_BURST_NUM_SHIFT) |
+               (((u32) hw->tpd_fetch_th & TXQ_CTRL_TPD_FETCH_TH_MASK)
+               << TXQ_CTRL_TPD_FETCH_TH_SHIFT) | TXQ_CTRL_ENH_MODE |
+               TXQ_CTRL_EN;
+       iowrite32(value, hw->hw_addr + REG_TXQ_CTRL);
+       /* min tpd fetch gap & tx jumbo packet size threshold for taskoffload */
+       value = (((u32) hw->tx_jumbo_task_th & TX_JUMBO_TASK_TH_MASK)
+               << TX_JUMBO_TASK_TH_SHIFT) |
+               (((u32) hw->tpd_fetch_gap & TX_TPD_MIN_IPG_MASK)
+               << TX_TPD_MIN_IPG_SHIFT);
+       iowrite32(value, hw->hw_addr + REG_TX_JUMBO_TASK_TH_TPD_IPG);
+       /* config RXQ */
+       value = (((u32) hw->rfd_burst & RXQ_CTRL_RFD_BURST_NUM_MASK)
+               << RXQ_CTRL_RFD_BURST_NUM_SHIFT) |
+               (((u32) hw->rrd_burst & RXQ_CTRL_RRD_BURST_THRESH_MASK)
+               << RXQ_CTRL_RRD_BURST_THRESH_SHIFT) |
+               (((u32) hw->rfd_fetch_gap & RXQ_CTRL_RFD_PREF_MIN_IPG_MASK)
+               << RXQ_CTRL_RFD_PREF_MIN_IPG_SHIFT) | RXQ_CTRL_CUT_THRU_EN |
+               RXQ_CTRL_EN;
+       iowrite32(value, hw->hw_addr + REG_RXQ_CTRL);
+       /* config DMA Engine */
+       value = ((((u32) hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK)
+               << DMA_CTRL_DMAR_BURST_LEN_SHIFT) |
+               ((((u32) hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK)
+               << DMA_CTRL_DMAW_BURST_LEN_SHIFT) | DMA_CTRL_DMAR_EN |
+               DMA_CTRL_DMAW_EN;
+       value |= (u32) hw->dma_ord;
+       if (atl1_rcb_128 == hw->rcb_value)
+               value |= DMA_CTRL_RCB_VALUE;
+       iowrite32(value, hw->hw_addr + REG_DMA_CTRL);
+       /* config CMB / SMB */
+       value = (hw->cmb_tpd > adapter->tpd_ring.count) ?
+               hw->cmb_tpd : adapter->tpd_ring.count;
+       value <<= 16;
+       value |= hw->cmb_rrd;
+       iowrite32(value, hw->hw_addr + REG_CMB_WRITE_TH);
+       value = hw->cmb_rx_timer | ((u32) hw->cmb_tx_timer << 16);
+       iowrite32(value, hw->hw_addr + REG_CMB_WRITE_TIMER);
+       iowrite32(hw->smb_timer, hw->hw_addr + REG_SMB_TIMER);
+       /* --- enable CMB / SMB */
+       value = CSMB_CTRL_CMB_EN | CSMB_CTRL_SMB_EN;
+       iowrite32(value, hw->hw_addr + REG_CSMB_CTRL);
+       value = ioread32(adapter->hw.hw_addr + REG_ISR);
+       if (unlikely((value & ISR_PHY_LINKDOWN) != 0))
+               value = 1;      /* config failed */
+       else
+               value = 0;
+       /* clear all interrupt status */
+       iowrite32(0x3fffffff, adapter->hw.hw_addr + REG_ISR);
+       iowrite32(0, adapter->hw.hw_addr + REG_ISR);
+       return value;
+ }
+ /*
+  * atl1_pcie_patch - Patch for PCIE module
+  */
+ static void atl1_pcie_patch(struct atl1_adapter *adapter)
+ {
+       u32 value;
+       /* much vendor magic here */
+       value = 0x6500;
+       iowrite32(value, adapter->hw.hw_addr + 0x12FC);
+       /* pcie flow control mode change */
+       value = ioread32(adapter->hw.hw_addr + 0x1008);
+       value |= 0x8000;
+       iowrite32(value, adapter->hw.hw_addr + 0x1008);
+ }
+ /*
+  * When ACPI resume on some VIA MotherBoard, the Interrupt Disable bit/0x400
+  * on PCI Command register is disable.
+  * The function enable this bit.
+  * Brackett, 2006/03/15
+  */
+ static void atl1_via_workaround(struct atl1_adapter *adapter)
+ {
+       unsigned long value;
+       value = ioread16(adapter->hw.hw_addr + PCI_COMMAND);
+       if (value & PCI_COMMAND_INTX_DISABLE)
+               value &= ~PCI_COMMAND_INTX_DISABLE;
+       iowrite32(value, adapter->hw.hw_addr + PCI_COMMAND);
+ }
+ static void atl1_inc_smb(struct atl1_adapter *adapter)
+ {
+       struct net_device *netdev = adapter->netdev;
+       struct stats_msg_block *smb = adapter->smb.smb;
+       /* Fill out the OS statistics structure */
+       adapter->soft_stats.rx_packets += smb->rx_ok;
+       adapter->soft_stats.tx_packets += smb->tx_ok;
+       adapter->soft_stats.rx_bytes += smb->rx_byte_cnt;
+       adapter->soft_stats.tx_bytes += smb->tx_byte_cnt;
+       adapter->soft_stats.multicast += smb->rx_mcast;
+       adapter->soft_stats.collisions += (smb->tx_1_col + smb->tx_2_col * 2 +
+               smb->tx_late_col + smb->tx_abort_col * adapter->hw.max_retry);
+       /* Rx Errors */
+       adapter->soft_stats.rx_errors += (smb->rx_frag + smb->rx_fcs_err +
+               smb->rx_len_err + smb->rx_sz_ov + smb->rx_rxf_ov +
+               smb->rx_rrd_ov + smb->rx_align_err);
+       adapter->soft_stats.rx_fifo_errors += smb->rx_rxf_ov;
+       adapter->soft_stats.rx_length_errors += smb->rx_len_err;
+       adapter->soft_stats.rx_crc_errors += smb->rx_fcs_err;
+       adapter->soft_stats.rx_frame_errors += smb->rx_align_err;
+       adapter->soft_stats.rx_missed_errors += (smb->rx_rrd_ov +
+               smb->rx_rxf_ov);
+       adapter->soft_stats.rx_pause += smb->rx_pause;
+       adapter->soft_stats.rx_rrd_ov += smb->rx_rrd_ov;
+       adapter->soft_stats.rx_trunc += smb->rx_sz_ov;
+       /* Tx Errors */
+       adapter->soft_stats.tx_errors += (smb->tx_late_col +
+               smb->tx_abort_col + smb->tx_underrun + smb->tx_trunc);
+       adapter->soft_stats.tx_fifo_errors += smb->tx_underrun;
+       adapter->soft_stats.tx_aborted_errors += smb->tx_abort_col;
+       adapter->soft_stats.tx_window_errors += smb->tx_late_col;
+       adapter->soft_stats.excecol += smb->tx_abort_col;
+       adapter->soft_stats.deffer += smb->tx_defer;
+       adapter->soft_stats.scc += smb->tx_1_col;
+       adapter->soft_stats.mcc += smb->tx_2_col;
+       adapter->soft_stats.latecol += smb->tx_late_col;
+       adapter->soft_stats.tx_underun += smb->tx_underrun;
+       adapter->soft_stats.tx_trunc += smb->tx_trunc;
+       adapter->soft_stats.tx_pause += smb->tx_pause;
+       netdev->stats.rx_packets = adapter->soft_stats.rx_packets;
+       netdev->stats.tx_packets = adapter->soft_stats.tx_packets;
+       netdev->stats.rx_bytes = adapter->soft_stats.rx_bytes;
+       netdev->stats.tx_bytes = adapter->soft_stats.tx_bytes;
+       netdev->stats.multicast = adapter->soft_stats.multicast;
+       netdev->stats.collisions = adapter->soft_stats.collisions;
+       netdev->stats.rx_errors = adapter->soft_stats.rx_errors;
+       netdev->stats.rx_over_errors =
+               adapter->soft_stats.rx_missed_errors;
+       netdev->stats.rx_length_errors =
+               adapter->soft_stats.rx_length_errors;
+       netdev->stats.rx_crc_errors = adapter->soft_stats.rx_crc_errors;
+       netdev->stats.rx_frame_errors =
+               adapter->soft_stats.rx_frame_errors;
+       netdev->stats.rx_fifo_errors = adapter->soft_stats.rx_fifo_errors;
+       netdev->stats.rx_missed_errors =
+               adapter->soft_stats.rx_missed_errors;
+       netdev->stats.tx_errors = adapter->soft_stats.tx_errors;
+       netdev->stats.tx_fifo_errors = adapter->soft_stats.tx_fifo_errors;
+       netdev->stats.tx_aborted_errors =
+               adapter->soft_stats.tx_aborted_errors;
+       netdev->stats.tx_window_errors =
+               adapter->soft_stats.tx_window_errors;
+       netdev->stats.tx_carrier_errors =
+               adapter->soft_stats.tx_carrier_errors;
+ }
+ static void atl1_update_mailbox(struct atl1_adapter *adapter)
+ {
+       unsigned long flags;
+       u32 tpd_next_to_use;
+       u32 rfd_next_to_use;
+       u32 rrd_next_to_clean;
+       u32 value;
+       spin_lock_irqsave(&adapter->mb_lock, flags);
+       tpd_next_to_use = atomic_read(&adapter->tpd_ring.next_to_use);
+       rfd_next_to_use = atomic_read(&adapter->rfd_ring.next_to_use);
+       rrd_next_to_clean = atomic_read(&adapter->rrd_ring.next_to_clean);
+       value = ((rfd_next_to_use & MB_RFD_PROD_INDX_MASK) <<
+               MB_RFD_PROD_INDX_SHIFT) |
+               ((rrd_next_to_clean & MB_RRD_CONS_INDX_MASK) <<
+               MB_RRD_CONS_INDX_SHIFT) |
+               ((tpd_next_to_use & MB_TPD_PROD_INDX_MASK) <<
+               MB_TPD_PROD_INDX_SHIFT);
+       iowrite32(value, adapter->hw.hw_addr + REG_MAILBOX);
+       spin_unlock_irqrestore(&adapter->mb_lock, flags);
+ }
+ static void atl1_clean_alloc_flag(struct atl1_adapter *adapter,
+       struct rx_return_desc *rrd, u16 offset)
+ {
+       struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+       while (rfd_ring->next_to_clean != (rrd->buf_indx + offset)) {
+               rfd_ring->buffer_info[rfd_ring->next_to_clean].alloced = 0;
+               if (++rfd_ring->next_to_clean == rfd_ring->count) {
+                       rfd_ring->next_to_clean = 0;
+               }
+       }
+ }
+ static void atl1_update_rfd_index(struct atl1_adapter *adapter,
+       struct rx_return_desc *rrd)
+ {
+       u16 num_buf;
+       num_buf = (rrd->xsz.xsum_sz.pkt_size + adapter->rx_buffer_len - 1) /
+               adapter->rx_buffer_len;
+       if (rrd->num_buf == num_buf)
+               /* clean alloc flag for bad rrd */
+               atl1_clean_alloc_flag(adapter, rrd, num_buf);
+ }
+ static void atl1_rx_checksum(struct atl1_adapter *adapter,
+       struct rx_return_desc *rrd, struct sk_buff *skb)
+ {
+       struct pci_dev *pdev = adapter->pdev;
+       /*
+        * The L1 hardware contains a bug that erroneously sets the
+        * PACKET_FLAG_ERR and ERR_FLAG_L4_CHKSUM bits whenever a
+        * fragmented IP packet is received, even though the packet
+        * is perfectly valid and its checksum is correct. There's
+        * no way to distinguish between one of these good packets
+        * and a packet that actually contains a TCP/UDP checksum
+        * error, so all we can do is allow it to be handed up to
+        * the higher layers and let it be sorted out there.
+        */
+       skb_checksum_none_assert(skb);
+       if (unlikely(rrd->pkt_flg & PACKET_FLAG_ERR)) {
+               if (rrd->err_flg & (ERR_FLAG_CRC | ERR_FLAG_TRUNC |
+                                       ERR_FLAG_CODE | ERR_FLAG_OV)) {
+                       adapter->hw_csum_err++;
+                       if (netif_msg_rx_err(adapter))
+                               dev_printk(KERN_DEBUG, &pdev->dev,
+                                       "rx checksum error\n");
+                       return;
+               }
+       }
+       /* not IPv4 */
+       if (!(rrd->pkt_flg & PACKET_FLAG_IPV4))
+               /* checksum is invalid, but it's not an IPv4 pkt, so ok */
+               return;
+       /* IPv4 packet */
+       if (likely(!(rrd->err_flg &
+               (ERR_FLAG_IP_CHKSUM | ERR_FLAG_L4_CHKSUM)))) {
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               adapter->hw_csum_good++;
+               return;
+       }
+ }
+ /*
+  * atl1_alloc_rx_buffers - Replace used receive buffers
+  * @adapter: address of board private structure
+  */
+ static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter)
+ {
+       struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+       struct pci_dev *pdev = adapter->pdev;
+       struct page *page;
+       unsigned long offset;
+       struct atl1_buffer *buffer_info, *next_info;
+       struct sk_buff *skb;
+       u16 num_alloc = 0;
+       u16 rfd_next_to_use, next_next;
+       struct rx_free_desc *rfd_desc;
+       next_next = rfd_next_to_use = atomic_read(&rfd_ring->next_to_use);
+       if (++next_next == rfd_ring->count)
+               next_next = 0;
+       buffer_info = &rfd_ring->buffer_info[rfd_next_to_use];
+       next_info = &rfd_ring->buffer_info[next_next];
+       while (!buffer_info->alloced && !next_info->alloced) {
+               if (buffer_info->skb) {
+                       buffer_info->alloced = 1;
+                       goto next;
+               }
+               rfd_desc = ATL1_RFD_DESC(rfd_ring, rfd_next_to_use);
+               skb = netdev_alloc_skb_ip_align(adapter->netdev,
+                                               adapter->rx_buffer_len);
+               if (unlikely(!skb)) {
+                       /* Better luck next round */
+                       adapter->netdev->stats.rx_dropped++;
+                       break;
+               }
+               buffer_info->alloced = 1;
+               buffer_info->skb = skb;
+               buffer_info->length = (u16) adapter->rx_buffer_len;
+               page = virt_to_page(skb->data);
+               offset = (unsigned long)skb->data & ~PAGE_MASK;
+               buffer_info->dma = pci_map_page(pdev, page, offset,
+                                               adapter->rx_buffer_len,
+                                               PCI_DMA_FROMDEVICE);
+               rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
+               rfd_desc->buf_len = cpu_to_le16(adapter->rx_buffer_len);
+               rfd_desc->coalese = 0;
+ next:
+               rfd_next_to_use = next_next;
+               if (unlikely(++next_next == rfd_ring->count))
+                       next_next = 0;
+               buffer_info = &rfd_ring->buffer_info[rfd_next_to_use];
+               next_info = &rfd_ring->buffer_info[next_next];
+               num_alloc++;
+       }
+       if (num_alloc) {
+               /*
+                * Force memory writes to complete before letting h/w
+                * know there are new descriptors to fetch.  (Only
+                * applicable for weak-ordered memory model archs,
+                * such as IA-64).
+                */
+               wmb();
+               atomic_set(&rfd_ring->next_to_use, (int)rfd_next_to_use);
+       }
+       return num_alloc;
+ }
+ static void atl1_intr_rx(struct atl1_adapter *adapter)
+ {
+       int i, count;
+       u16 length;
+       u16 rrd_next_to_clean;
+       u32 value;
+       struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring;
+       struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring;
+       struct atl1_buffer *buffer_info;
+       struct rx_return_desc *rrd;
+       struct sk_buff *skb;
+       count = 0;
+       rrd_next_to_clean = atomic_read(&rrd_ring->next_to_clean);
+       while (1) {
+               rrd = ATL1_RRD_DESC(rrd_ring, rrd_next_to_clean);
+               i = 1;
+               if (likely(rrd->xsz.valid)) {   /* packet valid */
+ chk_rrd:
+                       /* check rrd status */
+                       if (likely(rrd->num_buf == 1))
+                               goto rrd_ok;
+                       else if (netif_msg_rx_err(adapter)) {
+                               dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+                                       "unexpected RRD buffer count\n");
+                               dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+                                       "rx_buf_len = %d\n",
+                                       adapter->rx_buffer_len);
+                               dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+                                       "RRD num_buf = %d\n",
+                                       rrd->num_buf);
+                               dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+                                       "RRD pkt_len = %d\n",
+                                       rrd->xsz.xsum_sz.pkt_size);
+                               dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+                                       "RRD pkt_flg = 0x%08X\n",
+                                       rrd->pkt_flg);
+                               dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+                                       "RRD err_flg = 0x%08X\n",
+                                       rrd->err_flg);
+                               dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+                                       "RRD vlan_tag = 0x%08X\n",
+                                       rrd->vlan_tag);
+                       }
+                       /* rrd seems to be bad */
+                       if (unlikely(i-- > 0)) {
+                               /* rrd may not be DMAed completely */
+                               udelay(1);
+                               goto chk_rrd;
+                       }
+                       /* bad rrd */
+                       if (netif_msg_rx_err(adapter))
+                               dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+                                       "bad RRD\n");
+                       /* see if update RFD index */
+                       if (rrd->num_buf > 1)
+                               atl1_update_rfd_index(adapter, rrd);
+                       /* update rrd */
+                       rrd->xsz.valid = 0;
+                       if (++rrd_next_to_clean == rrd_ring->count)
+                               rrd_next_to_clean = 0;
+                       count++;
+                       continue;
+               } else {        /* current rrd still not be updated */
+                       break;
+               }
+ rrd_ok:
+               /* clean alloc flag for bad rrd */
+               atl1_clean_alloc_flag(adapter, rrd, 0);
+               buffer_info = &rfd_ring->buffer_info[rrd->buf_indx];
+               if (++rfd_ring->next_to_clean == rfd_ring->count)
+                       rfd_ring->next_to_clean = 0;
+               /* update rrd next to clean */
+               if (++rrd_next_to_clean == rrd_ring->count)
+                       rrd_next_to_clean = 0;
+               count++;
+               if (unlikely(rrd->pkt_flg & PACKET_FLAG_ERR)) {
+                       if (!(rrd->err_flg &
+                               (ERR_FLAG_IP_CHKSUM | ERR_FLAG_L4_CHKSUM
+                               | ERR_FLAG_LEN))) {
+                               /* packet error, don't need upstream */
+                               buffer_info->alloced = 0;
+                               rrd->xsz.valid = 0;
+                               continue;
+                       }
+               }
+               /* Good Receive */
+               pci_unmap_page(adapter->pdev, buffer_info->dma,
+                              buffer_info->length, PCI_DMA_FROMDEVICE);
+               buffer_info->dma = 0;
+               skb = buffer_info->skb;
+               length = le16_to_cpu(rrd->xsz.xsum_sz.pkt_size);
+               skb_put(skb, length - ETH_FCS_LEN);
+               /* Receive Checksum Offload */
+               atl1_rx_checksum(adapter, rrd, skb);
+               skb->protocol = eth_type_trans(skb, adapter->netdev);
+               if (rrd->pkt_flg & PACKET_FLAG_VLAN_INS) {
+                       u16 vlan_tag = (rrd->vlan_tag >> 4) |
+                                       ((rrd->vlan_tag & 7) << 13) |
+                                       ((rrd->vlan_tag & 8) << 9);
+                       __vlan_hwaccel_put_tag(skb, vlan_tag);
+               }
+               netif_rx(skb);
+               /* let protocol layer free skb */
+               buffer_info->skb = NULL;
+               buffer_info->alloced = 0;
+               rrd->xsz.valid = 0;
+       }
+       atomic_set(&rrd_ring->next_to_clean, rrd_next_to_clean);
+       atl1_alloc_rx_buffers(adapter);
+       /* update mailbox ? */
+       if (count) {
+               u32 tpd_next_to_use;
+               u32 rfd_next_to_use;
+               spin_lock(&adapter->mb_lock);
+               tpd_next_to_use = atomic_read(&adapter->tpd_ring.next_to_use);
+               rfd_next_to_use =
+                   atomic_read(&adapter->rfd_ring.next_to_use);
+               rrd_next_to_clean =
+                   atomic_read(&adapter->rrd_ring.next_to_clean);
+               value = ((rfd_next_to_use & MB_RFD_PROD_INDX_MASK) <<
+                       MB_RFD_PROD_INDX_SHIFT) |
+                         ((rrd_next_to_clean & MB_RRD_CONS_INDX_MASK) <<
+                       MB_RRD_CONS_INDX_SHIFT) |
+                         ((tpd_next_to_use & MB_TPD_PROD_INDX_MASK) <<
+                       MB_TPD_PROD_INDX_SHIFT);
+               iowrite32(value, adapter->hw.hw_addr + REG_MAILBOX);
+               spin_unlock(&adapter->mb_lock);
+       }
+ }
+ static void atl1_intr_tx(struct atl1_adapter *adapter)
+ {
+       struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+       struct atl1_buffer *buffer_info;
+       u16 sw_tpd_next_to_clean;
+       u16 cmb_tpd_next_to_clean;
+       sw_tpd_next_to_clean = atomic_read(&tpd_ring->next_to_clean);
+       cmb_tpd_next_to_clean = le16_to_cpu(adapter->cmb.cmb->tpd_cons_idx);
+       while (cmb_tpd_next_to_clean != sw_tpd_next_to_clean) {
+               buffer_info = &tpd_ring->buffer_info[sw_tpd_next_to_clean];
+               if (buffer_info->dma) {
+                       pci_unmap_page(adapter->pdev, buffer_info->dma,
+                                      buffer_info->length, PCI_DMA_TODEVICE);
+                       buffer_info->dma = 0;
+               }
+               if (buffer_info->skb) {
+                       dev_kfree_skb_irq(buffer_info->skb);
+                       buffer_info->skb = NULL;
+               }
+               if (++sw_tpd_next_to_clean == tpd_ring->count)
+                       sw_tpd_next_to_clean = 0;
+       }
+       atomic_set(&tpd_ring->next_to_clean, sw_tpd_next_to_clean);
+       if (netif_queue_stopped(adapter->netdev) &&
+           netif_carrier_ok(adapter->netdev))
+               netif_wake_queue(adapter->netdev);
+ }
+ static u16 atl1_tpd_avail(struct atl1_tpd_ring *tpd_ring)
+ {
+       u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean);
+       u16 next_to_use = atomic_read(&tpd_ring->next_to_use);
+       return (next_to_clean > next_to_use) ?
+               next_to_clean - next_to_use - 1 :
+               tpd_ring->count + next_to_clean - next_to_use - 1;
+ }
+ static int atl1_tso(struct atl1_adapter *adapter, struct sk_buff *skb,
+       struct tx_packet_desc *ptpd)
+ {
+       u8 hdr_len, ip_off;
+       u32 real_len;
+       int err;
+       if (skb_shinfo(skb)->gso_size) {
+               if (skb_header_cloned(skb)) {
+                       err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+                       if (unlikely(err))
+                               return -1;
+               }
+               if (skb->protocol == htons(ETH_P_IP)) {
+                       struct iphdr *iph = ip_hdr(skb);
+                       real_len = (((unsigned char *)iph - skb->data) +
+                               ntohs(iph->tot_len));
+                       if (real_len < skb->len)
+                               pskb_trim(skb, real_len);
+                       hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb));
+                       if (skb->len == hdr_len) {
+                               iph->check = 0;
+                               tcp_hdr(skb)->check =
+                                       ~csum_tcpudp_magic(iph->saddr,
+                                       iph->daddr, tcp_hdrlen(skb),
+                                       IPPROTO_TCP, 0);
+                               ptpd->word3 |= (iph->ihl & TPD_IPHL_MASK) <<
+                                       TPD_IPHL_SHIFT;
+                               ptpd->word3 |= ((tcp_hdrlen(skb) >> 2) &
+                                       TPD_TCPHDRLEN_MASK) <<
+                                       TPD_TCPHDRLEN_SHIFT;
+                               ptpd->word3 |= 1 << TPD_IP_CSUM_SHIFT;
+                               ptpd->word3 |= 1 << TPD_TCP_CSUM_SHIFT;
+                               return 1;
+                       }
+                       iph->check = 0;
+                       tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+                                       iph->daddr, 0, IPPROTO_TCP, 0);
+                       ip_off = (unsigned char *)iph -
+                               (unsigned char *) skb_network_header(skb);
+                       if (ip_off == 8) /* 802.3-SNAP frame */
+                               ptpd->word3 |= 1 << TPD_ETHTYPE_SHIFT;
+                       else if (ip_off != 0)
+                               return -2;
+                       ptpd->word3 |= (iph->ihl & TPD_IPHL_MASK) <<
+                               TPD_IPHL_SHIFT;
+                       ptpd->word3 |= ((tcp_hdrlen(skb) >> 2) &
+                               TPD_TCPHDRLEN_MASK) << TPD_TCPHDRLEN_SHIFT;
+                       ptpd->word3 |= (skb_shinfo(skb)->gso_size &
+                               TPD_MSS_MASK) << TPD_MSS_SHIFT;
+                       ptpd->word3 |= 1 << TPD_SEGMENT_EN_SHIFT;
+                       return 3;
+               }
+       }
+       return false;
+ }
+ static int atl1_tx_csum(struct atl1_adapter *adapter, struct sk_buff *skb,
+       struct tx_packet_desc *ptpd)
+ {
+       u8 css, cso;
+       if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
+               css = skb_checksum_start_offset(skb);
+               cso = css + (u8) skb->csum_offset;
+               if (unlikely(css & 0x1)) {
+                       /* L1 hardware requires an even number here */
+                       if (netif_msg_tx_err(adapter))
+                               dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+                                       "payload offset not an even number\n");
+                       return -1;
+               }
+               ptpd->word3 |= (css & TPD_PLOADOFFSET_MASK) <<
+                       TPD_PLOADOFFSET_SHIFT;
+               ptpd->word3 |= (cso & TPD_CCSUMOFFSET_MASK) <<
+                       TPD_CCSUMOFFSET_SHIFT;
+               ptpd->word3 |= 1 << TPD_CUST_CSUM_EN_SHIFT;
+               return true;
+       }
+       return 0;
+ }
+ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb,
+       struct tx_packet_desc *ptpd)
+ {
+       struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+       struct atl1_buffer *buffer_info;
+       u16 buf_len = skb->len;
+       struct page *page;
+       unsigned long offset;
+       unsigned int nr_frags;
+       unsigned int f;
+       int retval;
+       u16 next_to_use;
+       u16 data_len;
+       u8 hdr_len;
+       buf_len -= skb->data_len;
+       nr_frags = skb_shinfo(skb)->nr_frags;
+       next_to_use = atomic_read(&tpd_ring->next_to_use);
+       buffer_info = &tpd_ring->buffer_info[next_to_use];
+       BUG_ON(buffer_info->skb);
+       /* put skb in last TPD */
+       buffer_info->skb = NULL;
+       retval = (ptpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK;
+       if (retval) {
+               /* TSO */
+               hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+               buffer_info->length = hdr_len;
+               page = virt_to_page(skb->data);
+               offset = (unsigned long)skb->data & ~PAGE_MASK;
+               buffer_info->dma = pci_map_page(adapter->pdev, page,
+                                               offset, hdr_len,
+                                               PCI_DMA_TODEVICE);
+               if (++next_to_use == tpd_ring->count)
+                       next_to_use = 0;
+               if (buf_len > hdr_len) {
+                       int i, nseg;
+                       data_len = buf_len - hdr_len;
+                       nseg = (data_len + ATL1_MAX_TX_BUF_LEN - 1) /
+                               ATL1_MAX_TX_BUF_LEN;
+                       for (i = 0; i < nseg; i++) {
+                               buffer_info =
+                                   &tpd_ring->buffer_info[next_to_use];
+                               buffer_info->skb = NULL;
+                               buffer_info->length =
+                                   (ATL1_MAX_TX_BUF_LEN >=
+                                    data_len) ? ATL1_MAX_TX_BUF_LEN : data_len;
+                               data_len -= buffer_info->length;
+                               page = virt_to_page(skb->data +
+                                       (hdr_len + i * ATL1_MAX_TX_BUF_LEN));
+                               offset = (unsigned long)(skb->data +
+                                       (hdr_len + i * ATL1_MAX_TX_BUF_LEN)) &
+                                       ~PAGE_MASK;
+                               buffer_info->dma = pci_map_page(adapter->pdev,
+                                       page, offset, buffer_info->length,
+                                       PCI_DMA_TODEVICE);
+                               if (++next_to_use == tpd_ring->count)
+                                       next_to_use = 0;
+                       }
+               }
+       } else {
+               /* not TSO */
+               buffer_info->length = buf_len;
+               page = virt_to_page(skb->data);
+               offset = (unsigned long)skb->data & ~PAGE_MASK;
+               buffer_info->dma = pci_map_page(adapter->pdev, page,
+                       offset, buf_len, PCI_DMA_TODEVICE);
+               if (++next_to_use == tpd_ring->count)
+                       next_to_use = 0;
+       }
+       for (f = 0; f < nr_frags; f++) {
+               const struct skb_frag_struct *frag;
+               u16 i, nseg;
+               frag = &skb_shinfo(skb)->frags[f];
+               buf_len = skb_frag_size(frag);
+               nseg = (buf_len + ATL1_MAX_TX_BUF_LEN - 1) /
+                       ATL1_MAX_TX_BUF_LEN;
+               for (i = 0; i < nseg; i++) {
+                       buffer_info = &tpd_ring->buffer_info[next_to_use];
+                       BUG_ON(buffer_info->skb);
+                       buffer_info->skb = NULL;
+                       buffer_info->length = (buf_len > ATL1_MAX_TX_BUF_LEN) ?
+                               ATL1_MAX_TX_BUF_LEN : buf_len;
+                       buf_len -= buffer_info->length;
+                       buffer_info->dma = skb_frag_dma_map(&adapter->pdev->dev,
+                               frag, i * ATL1_MAX_TX_BUF_LEN,
+                               buffer_info->length, DMA_TO_DEVICE);
+                       if (++next_to_use == tpd_ring->count)
+                               next_to_use = 0;
+               }
+       }
+       /* last tpd's buffer-info */
+       buffer_info->skb = skb;
+ }
+ static void atl1_tx_queue(struct atl1_adapter *adapter, u16 count,
+        struct tx_packet_desc *ptpd)
+ {
+       struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+       struct atl1_buffer *buffer_info;
+       struct tx_packet_desc *tpd;
+       u16 j;
+       u32 val;
+       u16 next_to_use = (u16) atomic_read(&tpd_ring->next_to_use);
+       for (j = 0; j < count; j++) {
+               buffer_info = &tpd_ring->buffer_info[next_to_use];
+               tpd = ATL1_TPD_DESC(&adapter->tpd_ring, next_to_use);
+               if (tpd != ptpd)
+                       memcpy(tpd, ptpd, sizeof(struct tx_packet_desc));
+               tpd->buffer_addr = cpu_to_le64(buffer_info->dma);
+               tpd->word2 &= ~(TPD_BUFLEN_MASK << TPD_BUFLEN_SHIFT);
+               tpd->word2 |= (cpu_to_le16(buffer_info->length) &
+                       TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT;
+               /*
+                * if this is the first packet in a TSO chain, set
+                * TPD_HDRFLAG, otherwise, clear it.
+                */
+               val = (tpd->word3 >> TPD_SEGMENT_EN_SHIFT) &
+                       TPD_SEGMENT_EN_MASK;
+               if (val) {
+                       if (!j)
+                               tpd->word3 |= 1 << TPD_HDRFLAG_SHIFT;
+                       else
+                               tpd->word3 &= ~(1 << TPD_HDRFLAG_SHIFT);
+               }
+               if (j == (count - 1))
+                       tpd->word3 |= 1 << TPD_EOP_SHIFT;
+               if (++next_to_use == tpd_ring->count)
+                       next_to_use = 0;
+       }
+       /*
+        * Force memory writes to complete before letting h/w
+        * know there are new descriptors to fetch.  (Only
+        * applicable for weak-ordered memory model archs,
+        * such as IA-64).
+        */
+       wmb();
+       atomic_set(&tpd_ring->next_to_use, next_to_use);
+ }
+ static netdev_tx_t atl1_xmit_frame(struct sk_buff *skb,
+                                        struct net_device *netdev)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
+       int len;
+       int tso;
+       int count = 1;
+       int ret_val;
+       struct tx_packet_desc *ptpd;
+       u16 vlan_tag;
+       unsigned int nr_frags = 0;
+       unsigned int mss = 0;
+       unsigned int f;
+       unsigned int proto_hdr_len;
+       len = skb_headlen(skb);
+       if (unlikely(skb->len <= 0)) {
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
+       nr_frags = skb_shinfo(skb)->nr_frags;
+       for (f = 0; f < nr_frags; f++) {
+               unsigned int f_size = skb_frag_size(&skb_shinfo(skb)->frags[f]);
+               count += (f_size + ATL1_MAX_TX_BUF_LEN - 1) /
+                        ATL1_MAX_TX_BUF_LEN;
+       }
+       mss = skb_shinfo(skb)->gso_size;
+       if (mss) {
+               if (skb->protocol == htons(ETH_P_IP)) {
+                       proto_hdr_len = (skb_transport_offset(skb) +
+                                        tcp_hdrlen(skb));
+                       if (unlikely(proto_hdr_len > len)) {
+                               dev_kfree_skb_any(skb);
+                               return NETDEV_TX_OK;
+                       }
+                       /* need additional TPD ? */
+                       if (proto_hdr_len != len)
+                               count += (len - proto_hdr_len +
+                                       ATL1_MAX_TX_BUF_LEN - 1) /
+                                       ATL1_MAX_TX_BUF_LEN;
+               }
+       }
+       if (atl1_tpd_avail(&adapter->tpd_ring) < count) {
+               /* not enough descriptors */
+               netif_stop_queue(netdev);
+               if (netif_msg_tx_queued(adapter))
+                       dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+                               "tx busy\n");
+               return NETDEV_TX_BUSY;
+       }
+       ptpd = ATL1_TPD_DESC(tpd_ring,
+               (u16) atomic_read(&tpd_ring->next_to_use));
+       memset(ptpd, 0, sizeof(struct tx_packet_desc));
+       if (vlan_tx_tag_present(skb)) {
+               vlan_tag = vlan_tx_tag_get(skb);
+               vlan_tag = (vlan_tag << 4) | (vlan_tag >> 13) |
+                       ((vlan_tag >> 9) & 0x8);
+               ptpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT;
+               ptpd->word2 |= (vlan_tag & TPD_VLANTAG_MASK) <<
+                       TPD_VLANTAG_SHIFT;
+       }
+       tso = atl1_tso(adapter, skb, ptpd);
+       if (tso < 0) {
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
+       if (!tso) {
+               ret_val = atl1_tx_csum(adapter, skb, ptpd);
+               if (ret_val < 0) {
+                       dev_kfree_skb_any(skb);
+                       return NETDEV_TX_OK;
+               }
+       }
+       atl1_tx_map(adapter, skb, ptpd);
+       atl1_tx_queue(adapter, count, ptpd);
+       atl1_update_mailbox(adapter);
+       mmiowb();
+       return NETDEV_TX_OK;
+ }
+ /*
+  * atl1_intr - Interrupt Handler
+  * @irq: interrupt number
+  * @data: pointer to a network interface device structure
+  * @pt_regs: CPU registers structure
+  */
+ static irqreturn_t atl1_intr(int irq, void *data)
+ {
+       struct atl1_adapter *adapter = netdev_priv(data);
+       u32 status;
+       int max_ints = 10;
+       status = adapter->cmb.cmb->int_stats;
+       if (!status)
+               return IRQ_NONE;
+       do {
+               /* clear CMB interrupt status at once */
+               adapter->cmb.cmb->int_stats = 0;
+               if (status & ISR_GPHY)  /* clear phy status */
+                       atlx_clear_phy_int(adapter);
+               /* clear ISR status, and Enable CMB DMA/Disable Interrupt */
+               iowrite32(status | ISR_DIS_INT, adapter->hw.hw_addr + REG_ISR);
+               /* check if SMB intr */
+               if (status & ISR_SMB)
+                       atl1_inc_smb(adapter);
+               /* check if PCIE PHY Link down */
+               if (status & ISR_PHY_LINKDOWN) {
+                       if (netif_msg_intr(adapter))
+                               dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+                                       "pcie phy link down %x\n", status);
+                       if (netif_running(adapter->netdev)) {   /* reset MAC */
+                               iowrite32(0, adapter->hw.hw_addr + REG_IMR);
+                               schedule_work(&adapter->pcie_dma_to_rst_task);
+                               return IRQ_HANDLED;
+                       }
+               }
+               /* check if DMA read/write error ? */
+               if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) {
+                       if (netif_msg_intr(adapter))
+                               dev_printk(KERN_DEBUG, &adapter->pdev->dev,
+                                       "pcie DMA r/w error (status = 0x%x)\n",
+                                       status);
+                       iowrite32(0, adapter->hw.hw_addr + REG_IMR);
+                       schedule_work(&adapter->pcie_dma_to_rst_task);
+                       return IRQ_HANDLED;
+               }
+               /* link event */
+               if (status & ISR_GPHY) {
+                       adapter->soft_stats.tx_carrier_errors++;
+                       atl1_check_for_link(adapter);
+               }
+               /* transmit event */
+               if (status & ISR_CMB_TX)
+                       atl1_intr_tx(adapter);
+               /* rx exception */
+               if (unlikely(status & (ISR_RXF_OV | ISR_RFD_UNRUN |
+                       ISR_RRD_OV | ISR_HOST_RFD_UNRUN |
+                       ISR_HOST_RRD_OV | ISR_CMB_RX))) {
+                       if (status & (ISR_RXF_OV | ISR_RFD_UNRUN |
+                               ISR_RRD_OV | ISR_HOST_RFD_UNRUN |
+                               ISR_HOST_RRD_OV))
+                               if (netif_msg_intr(adapter))
+                                       dev_printk(KERN_DEBUG,
+                                               &adapter->pdev->dev,
+                                               "rx exception, ISR = 0x%x\n",
+                                               status);
+                       atl1_intr_rx(adapter);
+               }
+               if (--max_ints < 0)
+                       break;
+       } while ((status = adapter->cmb.cmb->int_stats));
+       /* re-enable Interrupt */
+       iowrite32(ISR_DIS_SMB | ISR_DIS_DMA, adapter->hw.hw_addr + REG_ISR);
+       return IRQ_HANDLED;
+ }
+ /*
+  * atl1_phy_config - Timer Call-back
+  * @data: pointer to netdev cast into an unsigned long
+  */
+ static void atl1_phy_config(unsigned long data)
+ {
+       struct atl1_adapter *adapter = (struct atl1_adapter *)data;
+       struct atl1_hw *hw = &adapter->hw;
+       unsigned long flags;
+       spin_lock_irqsave(&adapter->lock, flags);
+       adapter->phy_timer_pending = false;
+       atl1_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg);
+       atl1_write_phy_reg(hw, MII_ATLX_CR, hw->mii_1000t_ctrl_reg);
+       atl1_write_phy_reg(hw, MII_BMCR, MII_CR_RESET | MII_CR_AUTO_NEG_EN);
+       spin_unlock_irqrestore(&adapter->lock, flags);
+ }
+ /*
+  * Orphaned vendor comment left intact here:
+  * <vendor comment>
+  * If TPD Buffer size equal to 0, PCIE DMAR_TO_INT
+  * will assert. We do soft reset <0x1400=1> according
+  * with the SPEC. BUT, it seemes that PCIE or DMA
+  * state-machine will not be reset. DMAR_TO_INT will
+  * assert again and again.
+  * </vendor comment>
+  */
+ static int atl1_reset(struct atl1_adapter *adapter)
+ {
+       int ret;
+       ret = atl1_reset_hw(&adapter->hw);
+       if (ret)
+               return ret;
+       return atl1_init_hw(&adapter->hw);
+ }
+ static s32 atl1_up(struct atl1_adapter *adapter)
+ {
+       struct net_device *netdev = adapter->netdev;
+       int err;
+       int irq_flags = 0;
+       /* hardware has been reset, we need to reload some things */
+       atlx_set_multi(netdev);
+       atl1_init_ring_ptrs(adapter);
+       atlx_restore_vlan(adapter);
+       err = atl1_alloc_rx_buffers(adapter);
+       if (unlikely(!err))
+               /* no RX BUFFER allocated */
+               return -ENOMEM;
+       if (unlikely(atl1_configure(adapter))) {
+               err = -EIO;
+               goto err_up;
+       }
+       err = pci_enable_msi(adapter->pdev);
+       if (err) {
+               if (netif_msg_ifup(adapter))
+                       dev_info(&adapter->pdev->dev,
+                               "Unable to enable MSI: %d\n", err);
+               irq_flags |= IRQF_SHARED;
+       }
+       err = request_irq(adapter->pdev->irq, atl1_intr, irq_flags,
+                       netdev->name, netdev);
+       if (unlikely(err))
+               goto err_up;
+       atlx_irq_enable(adapter);
+       atl1_check_link(adapter);
+       netif_start_queue(netdev);
+       return 0;
+ err_up:
+       pci_disable_msi(adapter->pdev);
+       /* free rx_buffers */
+       atl1_clean_rx_ring(adapter);
+       return err;
+ }
+ static void atl1_down(struct atl1_adapter *adapter)
+ {
+       struct net_device *netdev = adapter->netdev;
+       netif_stop_queue(netdev);
+       del_timer_sync(&adapter->phy_config_timer);
+       adapter->phy_timer_pending = false;
+       atlx_irq_disable(adapter);
+       free_irq(adapter->pdev->irq, netdev);
+       pci_disable_msi(adapter->pdev);
+       atl1_reset_hw(&adapter->hw);
+       adapter->cmb.cmb->int_stats = 0;
+       adapter->link_speed = SPEED_0;
+       adapter->link_duplex = -1;
+       netif_carrier_off(netdev);
+       atl1_clean_tx_ring(adapter);
+       atl1_clean_rx_ring(adapter);
+ }
+ static void atl1_tx_timeout_task(struct work_struct *work)
+ {
+       struct atl1_adapter *adapter =
+               container_of(work, struct atl1_adapter, tx_timeout_task);
+       struct net_device *netdev = adapter->netdev;
+       netif_device_detach(netdev);
+       atl1_down(adapter);
+       atl1_up(adapter);
+       netif_device_attach(netdev);
+ }
+ /*
+  * atl1_change_mtu - Change the Maximum Transfer Unit
+  * @netdev: network interface device structure
+  * @new_mtu: new value for maximum frame size
+  *
+  * Returns 0 on success, negative on failure
+  */
+ static int atl1_change_mtu(struct net_device *netdev, int new_mtu)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       int old_mtu = netdev->mtu;
+       int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+       if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) ||
+           (max_frame > MAX_JUMBO_FRAME_SIZE)) {
+               if (netif_msg_link(adapter))
+                       dev_warn(&adapter->pdev->dev, "invalid MTU setting\n");
+               return -EINVAL;
+       }
+       adapter->hw.max_frame_size = max_frame;
+       adapter->hw.tx_jumbo_task_th = (max_frame + 7) >> 3;
+       adapter->rx_buffer_len = (max_frame + 7) & ~7;
+       adapter->hw.rx_jumbo_th = adapter->rx_buffer_len / 8;
+       netdev->mtu = new_mtu;
+       if ((old_mtu != new_mtu) && netif_running(netdev)) {
+               atl1_down(adapter);
+               atl1_up(adapter);
+       }
+       return 0;
+ }
+ /*
+  * atl1_open - Called when a network interface is made active
+  * @netdev: network interface device structure
+  *
+  * Returns 0 on success, negative value on failure
+  *
+  * The open entry point is called when a network interface is made
+  * active by the system (IFF_UP).  At this point all resources needed
+  * for transmit and receive operations are allocated, the interrupt
+  * handler is registered with the OS, the watchdog timer is started,
+  * and the stack is notified that the interface is ready.
+  */
+ static int atl1_open(struct net_device *netdev)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       int err;
+       netif_carrier_off(netdev);
+       /* allocate transmit descriptors */
+       err = atl1_setup_ring_resources(adapter);
+       if (err)
+               return err;
+       err = atl1_up(adapter);
+       if (err)
+               goto err_up;
+       return 0;
+ err_up:
+       atl1_reset(adapter);
+       return err;
+ }
+ /*
+  * atl1_close - Disables a network interface
+  * @netdev: network interface device structure
+  *
+  * Returns 0, this is not allowed to fail
+  *
+  * The close entry point is called when an interface is de-activated
+  * by the OS.  The hardware is still under the drivers control, but
+  * needs to be disabled.  A global MAC reset is issued to stop the
+  * hardware, and all transmit and receive resources are freed.
+  */
+ static int atl1_close(struct net_device *netdev)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       atl1_down(adapter);
+       atl1_free_ring_resources(adapter);
+       return 0;
+ }
+ #ifdef CONFIG_PM
+ static int atl1_suspend(struct device *dev)
+ {
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct net_device *netdev = pci_get_drvdata(pdev);
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       struct atl1_hw *hw = &adapter->hw;
+       u32 ctrl = 0;
+       u32 wufc = adapter->wol;
+       u32 val;
+       u16 speed;
+       u16 duplex;
+       netif_device_detach(netdev);
+       if (netif_running(netdev))
+               atl1_down(adapter);
+       atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl);
+       atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl);
+       val = ctrl & BMSR_LSTATUS;
+       if (val)
+               wufc &= ~ATLX_WUFC_LNKC;
+       if (!wufc)
+               goto disable_wol;
+       if (val) {
+               val = atl1_get_speed_and_duplex(hw, &speed, &duplex);
+               if (val) {
+                       if (netif_msg_ifdown(adapter))
+                               dev_printk(KERN_DEBUG, &pdev->dev,
+                                       "error getting speed/duplex\n");
+                       goto disable_wol;
+               }
+               ctrl = 0;
+               /* enable magic packet WOL */
+               if (wufc & ATLX_WUFC_MAG)
+                       ctrl |= (WOL_MAGIC_EN | WOL_MAGIC_PME_EN);
+               iowrite32(ctrl, hw->hw_addr + REG_WOL_CTRL);
+               ioread32(hw->hw_addr + REG_WOL_CTRL);
+               /* configure the mac */
+               ctrl = MAC_CTRL_RX_EN;
+               ctrl |= ((u32)((speed == SPEED_1000) ? MAC_CTRL_SPEED_1000 :
+                       MAC_CTRL_SPEED_10_100) << MAC_CTRL_SPEED_SHIFT);
+               if (duplex == FULL_DUPLEX)
+                       ctrl |= MAC_CTRL_DUPLX;
+               ctrl |= (((u32)adapter->hw.preamble_len &
+                       MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT);
+               __atlx_vlan_mode(netdev->features, &ctrl);
+               if (wufc & ATLX_WUFC_MAG)
+                       ctrl |= MAC_CTRL_BC_EN;
+               iowrite32(ctrl, hw->hw_addr + REG_MAC_CTRL);
+               ioread32(hw->hw_addr + REG_MAC_CTRL);
+               /* poke the PHY */
+               ctrl = ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
+               ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
+               iowrite32(ctrl, hw->hw_addr + REG_PCIE_PHYMISC);
+               ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
+       } else {
+               ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN);
+               iowrite32(ctrl, hw->hw_addr + REG_WOL_CTRL);
+               ioread32(hw->hw_addr + REG_WOL_CTRL);
+               iowrite32(0, hw->hw_addr + REG_MAC_CTRL);
+               ioread32(hw->hw_addr + REG_MAC_CTRL);
+               hw->phy_configured = false;
+       }
+       return 0;
+  disable_wol:
+       iowrite32(0, hw->hw_addr + REG_WOL_CTRL);
+       ioread32(hw->hw_addr + REG_WOL_CTRL);
+       ctrl = ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
+       ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
+       iowrite32(ctrl, hw->hw_addr + REG_PCIE_PHYMISC);
+       ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
+       hw->phy_configured = false;
+       return 0;
+ }
+ static int atl1_resume(struct device *dev)
+ {
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct net_device *netdev = pci_get_drvdata(pdev);
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       iowrite32(0, adapter->hw.hw_addr + REG_WOL_CTRL);
+       atl1_reset_hw(&adapter->hw);
+       if (netif_running(netdev)) {
+               adapter->cmb.cmb->int_stats = 0;
+               atl1_up(adapter);
+       }
+       netif_device_attach(netdev);
+       return 0;
+ }
+ static SIMPLE_DEV_PM_OPS(atl1_pm_ops, atl1_suspend, atl1_resume);
+ #define ATL1_PM_OPS   (&atl1_pm_ops)
+ #else
+ static int atl1_suspend(struct device *dev) { return 0; }
+ #define ATL1_PM_OPS   NULL
+ #endif
+ static void atl1_shutdown(struct pci_dev *pdev)
+ {
+       struct net_device *netdev = pci_get_drvdata(pdev);
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       atl1_suspend(&pdev->dev);
+       pci_wake_from_d3(pdev, adapter->wol);
+       pci_set_power_state(pdev, PCI_D3hot);
+ }
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+ static void atl1_poll_controller(struct net_device *netdev)
+ {
+       disable_irq(netdev->irq);
+       atl1_intr(netdev->irq, netdev);
+       enable_irq(netdev->irq);
+ }
+ #endif
+ static const struct net_device_ops atl1_netdev_ops = {
+       .ndo_open               = atl1_open,
+       .ndo_stop               = atl1_close,
+       .ndo_start_xmit         = atl1_xmit_frame,
+       .ndo_set_rx_mode        = atlx_set_multi,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = atl1_set_mac,
+       .ndo_change_mtu         = atl1_change_mtu,
+       .ndo_fix_features       = atlx_fix_features,
+       .ndo_set_features       = atlx_set_features,
+       .ndo_do_ioctl           = atlx_ioctl,
+       .ndo_tx_timeout         = atlx_tx_timeout,
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = atl1_poll_controller,
+ #endif
+ };
+ /*
+  * atl1_probe - Device Initialization Routine
+  * @pdev: PCI device information struct
+  * @ent: entry in atl1_pci_tbl
+  *
+  * Returns 0 on success, negative on failure
+  *
+  * atl1_probe initializes an adapter identified by a pci_dev structure.
+  * The OS initialization, configuring of the adapter private structure,
+  * and a hardware reset occur.
+  */
+ static int __devinit atl1_probe(struct pci_dev *pdev,
+       const struct pci_device_id *ent)
+ {
+       struct net_device *netdev;
+       struct atl1_adapter *adapter;
+       static int cards_found = 0;
+       int err;
+       err = pci_enable_device(pdev);
+       if (err)
+               return err;
+       /*
+        * The atl1 chip can DMA to 64-bit addresses, but it uses a single
+        * shared register for the high 32 bits, so only a single, aligned,
+        * 4 GB physical address range can be used at a time.
+        *
+        * Supporting 64-bit DMA on this hardware is more trouble than it's
+        * worth.  It is far easier to limit to 32-bit DMA than update
+        * various kernel subsystems to support the mechanics required by a
+        * fixed-high-32-bit system.
+        */
+       err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+       if (err) {
+               dev_err(&pdev->dev, "no usable DMA configuration\n");
+               goto err_dma;
+       }
+       /*
+        * Mark all PCI regions associated with PCI device
+        * pdev as being reserved by owner atl1_driver_name
+        */
+       err = pci_request_regions(pdev, ATLX_DRIVER_NAME);
+       if (err)
+               goto err_request_regions;
+       /*
+        * Enables bus-mastering on the device and calls
+        * pcibios_set_master to do the needed arch specific settings
+        */
+       pci_set_master(pdev);
+       netdev = alloc_etherdev(sizeof(struct atl1_adapter));
+       if (!netdev) {
+               err = -ENOMEM;
+               goto err_alloc_etherdev;
+       }
+       SET_NETDEV_DEV(netdev, &pdev->dev);
+       pci_set_drvdata(pdev, netdev);
+       adapter = netdev_priv(netdev);
+       adapter->netdev = netdev;
+       adapter->pdev = pdev;
+       adapter->hw.back = adapter;
+       adapter->msg_enable = netif_msg_init(debug, atl1_default_msg);
+       adapter->hw.hw_addr = pci_iomap(pdev, 0, 0);
+       if (!adapter->hw.hw_addr) {
+               err = -EIO;
+               goto err_pci_iomap;
+       }
+       /* get device revision number */
+       adapter->hw.dev_rev = ioread16(adapter->hw.hw_addr +
+               (REG_MASTER_CTRL + 2));
+       if (netif_msg_probe(adapter))
+               dev_info(&pdev->dev, "version %s\n", ATLX_DRIVER_VERSION);
+       /* set default ring resource counts */
+       adapter->rfd_ring.count = adapter->rrd_ring.count = ATL1_DEFAULT_RFD;
+       adapter->tpd_ring.count = ATL1_DEFAULT_TPD;
+       adapter->mii.dev = netdev;
+       adapter->mii.mdio_read = mdio_read;
+       adapter->mii.mdio_write = mdio_write;
+       adapter->mii.phy_id_mask = 0x1f;
+       adapter->mii.reg_num_mask = 0x1f;
+       netdev->netdev_ops = &atl1_netdev_ops;
+       netdev->watchdog_timeo = 5 * HZ;
+       netdev->ethtool_ops = &atl1_ethtool_ops;
+       adapter->bd_number = cards_found;
+       /* setup the private structure */
+       err = atl1_sw_init(adapter);
+       if (err)
+               goto err_common;
+       netdev->features = NETIF_F_HW_CSUM;
+       netdev->features |= NETIF_F_SG;
+       netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX);
+       netdev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_TSO |
+                             NETIF_F_HW_VLAN_RX;
+       /* is this valid? see atl1_setup_mac_ctrl() */
+       netdev->features |= NETIF_F_RXCSUM;
+       /*
+        * patch for some L1 of old version,
+        * the final version of L1 may not need these
+        * patches
+        */
+       /* atl1_pcie_patch(adapter); */
+       /* really reset GPHY core */
+       iowrite16(0, adapter->hw.hw_addr + REG_PHY_ENABLE);
+       /*
+        * reset the controller to
+        * put the device in a known good starting state
+        */
+       if (atl1_reset_hw(&adapter->hw)) {
+               err = -EIO;
+               goto err_common;
+       }
+       /* copy the MAC address out of the EEPROM */
+       atl1_read_mac_addr(&adapter->hw);
+       memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len);
+       if (!is_valid_ether_addr(netdev->dev_addr)) {
+               err = -EIO;
+               goto err_common;
+       }
+       atl1_check_options(adapter);
+       /* pre-init the MAC, and setup link */
+       err = atl1_init_hw(&adapter->hw);
+       if (err) {
+               err = -EIO;
+               goto err_common;
+       }
+       atl1_pcie_patch(adapter);
+       /* assume we have no link for now */
+       netif_carrier_off(netdev);
+       setup_timer(&adapter->phy_config_timer, atl1_phy_config,
+                   (unsigned long)adapter);
+       adapter->phy_timer_pending = false;
+       INIT_WORK(&adapter->tx_timeout_task, atl1_tx_timeout_task);
+       INIT_WORK(&adapter->link_chg_task, atlx_link_chg_task);
+       INIT_WORK(&adapter->pcie_dma_to_rst_task, atl1_tx_timeout_task);
+       err = register_netdev(netdev);
+       if (err)
+               goto err_common;
+       cards_found++;
+       atl1_via_workaround(adapter);
+       return 0;
+ err_common:
+       pci_iounmap(pdev, adapter->hw.hw_addr);
+ err_pci_iomap:
+       free_netdev(netdev);
+ err_alloc_etherdev:
+       pci_release_regions(pdev);
+ err_dma:
+ err_request_regions:
+       pci_disable_device(pdev);
+       return err;
+ }
+ /*
+  * atl1_remove - Device Removal Routine
+  * @pdev: PCI device information struct
+  *
+  * atl1_remove is called by the PCI subsystem to alert the driver
+  * that it should release a PCI device.  The could be caused by a
+  * Hot-Plug event, or because the driver is going to be removed from
+  * memory.
+  */
+ static void __devexit atl1_remove(struct pci_dev *pdev)
+ {
+       struct net_device *netdev = pci_get_drvdata(pdev);
+       struct atl1_adapter *adapter;
+       /* Device not available. Return. */
+       if (!netdev)
+               return;
+       adapter = netdev_priv(netdev);
+       /*
+        * Some atl1 boards lack persistent storage for their MAC, and get it
+        * from the BIOS during POST.  If we've been messing with the MAC
+        * address, we need to save the permanent one.
+        */
+       if (memcmp(adapter->hw.mac_addr, adapter->hw.perm_mac_addr, ETH_ALEN)) {
+               memcpy(adapter->hw.mac_addr, adapter->hw.perm_mac_addr,
+                       ETH_ALEN);
+               atl1_set_mac_addr(&adapter->hw);
+       }
+       iowrite16(0, adapter->hw.hw_addr + REG_PHY_ENABLE);
+       unregister_netdev(netdev);
+       pci_iounmap(pdev, adapter->hw.hw_addr);
+       pci_release_regions(pdev);
+       free_netdev(netdev);
+       pci_disable_device(pdev);
+ }
+ static struct pci_driver atl1_driver = {
+       .name = ATLX_DRIVER_NAME,
+       .id_table = atl1_pci_tbl,
+       .probe = atl1_probe,
+       .remove = __devexit_p(atl1_remove),
+       .shutdown = atl1_shutdown,
+       .driver.pm = ATL1_PM_OPS,
+ };
+ /*
+  * atl1_exit_module - Driver Exit Cleanup Routine
+  *
+  * atl1_exit_module is called just before the driver is removed
+  * from memory.
+  */
+ static void __exit atl1_exit_module(void)
+ {
+       pci_unregister_driver(&atl1_driver);
+ }
+ /*
+  * atl1_init_module - Driver Registration Routine
+  *
+  * atl1_init_module is the first routine called when the driver is
+  * loaded. All it does is register with the PCI subsystem.
+  */
+ static int __init atl1_init_module(void)
+ {
+       return pci_register_driver(&atl1_driver);
+ }
+ module_init(atl1_init_module);
+ module_exit(atl1_exit_module);
+ struct atl1_stats {
+       char stat_string[ETH_GSTRING_LEN];
+       int sizeof_stat;
+       int stat_offset;
+ };
+ #define ATL1_STAT(m) \
+       sizeof(((struct atl1_adapter *)0)->m), offsetof(struct atl1_adapter, m)
+ static struct atl1_stats atl1_gstrings_stats[] = {
+       {"rx_packets", ATL1_STAT(soft_stats.rx_packets)},
+       {"tx_packets", ATL1_STAT(soft_stats.tx_packets)},
+       {"rx_bytes", ATL1_STAT(soft_stats.rx_bytes)},
+       {"tx_bytes", ATL1_STAT(soft_stats.tx_bytes)},
+       {"rx_errors", ATL1_STAT(soft_stats.rx_errors)},
+       {"tx_errors", ATL1_STAT(soft_stats.tx_errors)},
+       {"multicast", ATL1_STAT(soft_stats.multicast)},
+       {"collisions", ATL1_STAT(soft_stats.collisions)},
+       {"rx_length_errors", ATL1_STAT(soft_stats.rx_length_errors)},
+       {"rx_over_errors", ATL1_STAT(soft_stats.rx_missed_errors)},
+       {"rx_crc_errors", ATL1_STAT(soft_stats.rx_crc_errors)},
+       {"rx_frame_errors", ATL1_STAT(soft_stats.rx_frame_errors)},
+       {"rx_fifo_errors", ATL1_STAT(soft_stats.rx_fifo_errors)},
+       {"rx_missed_errors", ATL1_STAT(soft_stats.rx_missed_errors)},
+       {"tx_aborted_errors", ATL1_STAT(soft_stats.tx_aborted_errors)},
+       {"tx_carrier_errors", ATL1_STAT(soft_stats.tx_carrier_errors)},
+       {"tx_fifo_errors", ATL1_STAT(soft_stats.tx_fifo_errors)},
+       {"tx_window_errors", ATL1_STAT(soft_stats.tx_window_errors)},
+       {"tx_abort_exce_coll", ATL1_STAT(soft_stats.excecol)},
+       {"tx_abort_late_coll", ATL1_STAT(soft_stats.latecol)},
+       {"tx_deferred_ok", ATL1_STAT(soft_stats.deffer)},
+       {"tx_single_coll_ok", ATL1_STAT(soft_stats.scc)},
+       {"tx_multi_coll_ok", ATL1_STAT(soft_stats.mcc)},
+       {"tx_underun", ATL1_STAT(soft_stats.tx_underun)},
+       {"tx_trunc", ATL1_STAT(soft_stats.tx_trunc)},
+       {"tx_pause", ATL1_STAT(soft_stats.tx_pause)},
+       {"rx_pause", ATL1_STAT(soft_stats.rx_pause)},
+       {"rx_rrd_ov", ATL1_STAT(soft_stats.rx_rrd_ov)},
+       {"rx_trunc", ATL1_STAT(soft_stats.rx_trunc)}
+ };
+ static void atl1_get_ethtool_stats(struct net_device *netdev,
+       struct ethtool_stats *stats, u64 *data)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       int i;
+       char *p;
+       for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) {
+               p = (char *)adapter+atl1_gstrings_stats[i].stat_offset;
+               data[i] = (atl1_gstrings_stats[i].sizeof_stat ==
+                       sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+       }
+ }
+ static int atl1_get_sset_count(struct net_device *netdev, int sset)
+ {
+       switch (sset) {
+       case ETH_SS_STATS:
+               return ARRAY_SIZE(atl1_gstrings_stats);
+       default:
+               return -EOPNOTSUPP;
+       }
+ }
+ static int atl1_get_settings(struct net_device *netdev,
+       struct ethtool_cmd *ecmd)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       struct atl1_hw *hw = &adapter->hw;
+       ecmd->supported = (SUPPORTED_10baseT_Half |
+                          SUPPORTED_10baseT_Full |
+                          SUPPORTED_100baseT_Half |
+                          SUPPORTED_100baseT_Full |
+                          SUPPORTED_1000baseT_Full |
+                          SUPPORTED_Autoneg | SUPPORTED_TP);
+       ecmd->advertising = ADVERTISED_TP;
+       if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+           hw->media_type == MEDIA_TYPE_1000M_FULL) {
+               ecmd->advertising |= ADVERTISED_Autoneg;
+               if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR) {
+                       ecmd->advertising |= ADVERTISED_Autoneg;
+                       ecmd->advertising |=
+                           (ADVERTISED_10baseT_Half |
+                            ADVERTISED_10baseT_Full |
+                            ADVERTISED_100baseT_Half |
+                            ADVERTISED_100baseT_Full |
+                            ADVERTISED_1000baseT_Full);
+               } else
+                       ecmd->advertising |= (ADVERTISED_1000baseT_Full);
+       }
+       ecmd->port = PORT_TP;
+       ecmd->phy_address = 0;
+       ecmd->transceiver = XCVR_INTERNAL;
+       if (netif_carrier_ok(adapter->netdev)) {
+               u16 link_speed, link_duplex;
+               atl1_get_speed_and_duplex(hw, &link_speed, &link_duplex);
+               ethtool_cmd_speed_set(ecmd, link_speed);
+               if (link_duplex == FULL_DUPLEX)
+                       ecmd->duplex = DUPLEX_FULL;
+               else
+                       ecmd->duplex = DUPLEX_HALF;
+       } else {
+               ethtool_cmd_speed_set(ecmd, -1);
+               ecmd->duplex = -1;
+       }
+       if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+           hw->media_type == MEDIA_TYPE_1000M_FULL)
+               ecmd->autoneg = AUTONEG_ENABLE;
+       else
+               ecmd->autoneg = AUTONEG_DISABLE;
+       return 0;
+ }
+ static int atl1_set_settings(struct net_device *netdev,
+       struct ethtool_cmd *ecmd)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       struct atl1_hw *hw = &adapter->hw;
+       u16 phy_data;
+       int ret_val = 0;
+       u16 old_media_type = hw->media_type;
+       if (netif_running(adapter->netdev)) {
+               if (netif_msg_link(adapter))
+                       dev_dbg(&adapter->pdev->dev,
+                               "ethtool shutting down adapter\n");
+               atl1_down(adapter);
+       }
+       if (ecmd->autoneg == AUTONEG_ENABLE)
+               hw->media_type = MEDIA_TYPE_AUTO_SENSOR;
+       else {
+               u32 speed = ethtool_cmd_speed(ecmd);
+               if (speed == SPEED_1000) {
+                       if (ecmd->duplex != DUPLEX_FULL) {
+                               if (netif_msg_link(adapter))
+                                       dev_warn(&adapter->pdev->dev,
+                                               "1000M half is invalid\n");
+                               ret_val = -EINVAL;
+                               goto exit_sset;
+                       }
+                       hw->media_type = MEDIA_TYPE_1000M_FULL;
+               } else if (speed == SPEED_100) {
+                       if (ecmd->duplex == DUPLEX_FULL)
+                               hw->media_type = MEDIA_TYPE_100M_FULL;
+                       else
+                               hw->media_type = MEDIA_TYPE_100M_HALF;
+               } else {
+                       if (ecmd->duplex == DUPLEX_FULL)
+                               hw->media_type = MEDIA_TYPE_10M_FULL;
+                       else
+                               hw->media_type = MEDIA_TYPE_10M_HALF;
+               }
+       }
+       switch (hw->media_type) {
+       case MEDIA_TYPE_AUTO_SENSOR:
+               ecmd->advertising =
+                   ADVERTISED_10baseT_Half |
+                   ADVERTISED_10baseT_Full |
+                   ADVERTISED_100baseT_Half |
+                   ADVERTISED_100baseT_Full |
+                   ADVERTISED_1000baseT_Full |
+                   ADVERTISED_Autoneg | ADVERTISED_TP;
+               break;
+       case MEDIA_TYPE_1000M_FULL:
+               ecmd->advertising =
+                   ADVERTISED_1000baseT_Full |
+                   ADVERTISED_Autoneg | ADVERTISED_TP;
+               break;
+       default:
+               ecmd->advertising = 0;
+               break;
+       }
+       if (atl1_phy_setup_autoneg_adv(hw)) {
+               ret_val = -EINVAL;
+               if (netif_msg_link(adapter))
+                       dev_warn(&adapter->pdev->dev,
+                               "invalid ethtool speed/duplex setting\n");
+               goto exit_sset;
+       }
+       if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+           hw->media_type == MEDIA_TYPE_1000M_FULL)
+               phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
+       else {
+               switch (hw->media_type) {
+               case MEDIA_TYPE_100M_FULL:
+                       phy_data =
+                           MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 |
+                           MII_CR_RESET;
+                       break;
+               case MEDIA_TYPE_100M_HALF:
+                       phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
+                       break;
+               case MEDIA_TYPE_10M_FULL:
+                       phy_data =
+                           MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET;
+                       break;
+               default:
+                       /* MEDIA_TYPE_10M_HALF: */
+                       phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
+                       break;
+               }
+       }
+       atl1_write_phy_reg(hw, MII_BMCR, phy_data);
+ exit_sset:
+       if (ret_val)
+               hw->media_type = old_media_type;
+       if (netif_running(adapter->netdev)) {
+               if (netif_msg_link(adapter))
+                       dev_dbg(&adapter->pdev->dev,
+                               "ethtool starting adapter\n");
+               atl1_up(adapter);
+       } else if (!ret_val) {
+               if (netif_msg_link(adapter))
+                       dev_dbg(&adapter->pdev->dev,
+                               "ethtool resetting adapter\n");
+               atl1_reset(adapter);
+       }
+       return ret_val;
+ }
+ static void atl1_get_drvinfo(struct net_device *netdev,
+       struct ethtool_drvinfo *drvinfo)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       strlcpy(drvinfo->driver, ATLX_DRIVER_NAME, sizeof(drvinfo->driver));
+       strlcpy(drvinfo->version, ATLX_DRIVER_VERSION,
+               sizeof(drvinfo->version));
+       strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
+       strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
+               sizeof(drvinfo->bus_info));
+       drvinfo->eedump_len = ATL1_EEDUMP_LEN;
+ }
+ static void atl1_get_wol(struct net_device *netdev,
+       struct ethtool_wolinfo *wol)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       wol->supported = WAKE_MAGIC;
+       wol->wolopts = 0;
+       if (adapter->wol & ATLX_WUFC_MAG)
+               wol->wolopts |= WAKE_MAGIC;
+ }
+ static int atl1_set_wol(struct net_device *netdev,
+       struct ethtool_wolinfo *wol)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       if (wol->wolopts & (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST |
+               WAKE_ARP | WAKE_MAGICSECURE))
+               return -EOPNOTSUPP;
+       adapter->wol = 0;
+       if (wol->wolopts & WAKE_MAGIC)
+               adapter->wol |= ATLX_WUFC_MAG;
+       device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
+       return 0;
+ }
+ static u32 atl1_get_msglevel(struct net_device *netdev)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       return adapter->msg_enable;
+ }
+ static void atl1_set_msglevel(struct net_device *netdev, u32 value)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       adapter->msg_enable = value;
+ }
+ static int atl1_get_regs_len(struct net_device *netdev)
+ {
+       return ATL1_REG_COUNT * sizeof(u32);
+ }
+ static void atl1_get_regs(struct net_device *netdev, struct ethtool_regs *regs,
+       void *p)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       struct atl1_hw *hw = &adapter->hw;
+       unsigned int i;
+       u32 *regbuf = p;
+       for (i = 0; i < ATL1_REG_COUNT; i++) {
+               /*
+                * This switch statement avoids reserved regions
+                * of register space.
+                */
+               switch (i) {
+               case 6 ... 9:
+               case 14:
+               case 29 ... 31:
+               case 34 ... 63:
+               case 75 ... 127:
+               case 136 ... 1023:
+               case 1027 ... 1087:
+               case 1091 ... 1151:
+               case 1194 ... 1195:
+               case 1200 ... 1201:
+               case 1206 ... 1213:
+               case 1216 ... 1279:
+               case 1290 ... 1311:
+               case 1323 ... 1343:
+               case 1358 ... 1359:
+               case 1368 ... 1375:
+               case 1378 ... 1383:
+               case 1388 ... 1391:
+               case 1393 ... 1395:
+               case 1402 ... 1403:
+               case 1410 ... 1471:
+               case 1522 ... 1535:
+                       /* reserved region; don't read it */
+                       regbuf[i] = 0;
+                       break;
+               default:
+                       /* unreserved region */
+                       regbuf[i] = ioread32(hw->hw_addr + (i * sizeof(u32)));
+               }
+       }
+ }
+ static void atl1_get_ringparam(struct net_device *netdev,
+       struct ethtool_ringparam *ring)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       struct atl1_tpd_ring *txdr = &adapter->tpd_ring;
+       struct atl1_rfd_ring *rxdr = &adapter->rfd_ring;
+       ring->rx_max_pending = ATL1_MAX_RFD;
+       ring->tx_max_pending = ATL1_MAX_TPD;
+       ring->rx_pending = rxdr->count;
+       ring->tx_pending = txdr->count;
+ }
+ static int atl1_set_ringparam(struct net_device *netdev,
+       struct ethtool_ringparam *ring)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       struct atl1_tpd_ring *tpdr = &adapter->tpd_ring;
+       struct atl1_rrd_ring *rrdr = &adapter->rrd_ring;
+       struct atl1_rfd_ring *rfdr = &adapter->rfd_ring;
+       struct atl1_tpd_ring tpd_old, tpd_new;
+       struct atl1_rfd_ring rfd_old, rfd_new;
+       struct atl1_rrd_ring rrd_old, rrd_new;
+       struct atl1_ring_header rhdr_old, rhdr_new;
+       struct atl1_smb smb;
+       struct atl1_cmb cmb;
+       int err;
+       tpd_old = adapter->tpd_ring;
+       rfd_old = adapter->rfd_ring;
+       rrd_old = adapter->rrd_ring;
+       rhdr_old = adapter->ring_header;
+       if (netif_running(adapter->netdev))
+               atl1_down(adapter);
+       rfdr->count = (u16) max(ring->rx_pending, (u32) ATL1_MIN_RFD);
+       rfdr->count = rfdr->count > ATL1_MAX_RFD ? ATL1_MAX_RFD :
+                       rfdr->count;
+       rfdr->count = (rfdr->count + 3) & ~3;
+       rrdr->count = rfdr->count;
+       tpdr->count = (u16) max(ring->tx_pending, (u32) ATL1_MIN_TPD);
+       tpdr->count = tpdr->count > ATL1_MAX_TPD ? ATL1_MAX_TPD :
+                       tpdr->count;
+       tpdr->count = (tpdr->count + 3) & ~3;
+       if (netif_running(adapter->netdev)) {
+               /* try to get new resources before deleting old */
+               err = atl1_setup_ring_resources(adapter);
+               if (err)
+                       goto err_setup_ring;
+               /*
+                * save the new, restore the old in order to free it,
+                * then restore the new back again
+                */
+               rfd_new = adapter->rfd_ring;
+               rrd_new = adapter->rrd_ring;
+               tpd_new = adapter->tpd_ring;
+               rhdr_new = adapter->ring_header;
+               adapter->rfd_ring = rfd_old;
+               adapter->rrd_ring = rrd_old;
+               adapter->tpd_ring = tpd_old;
+               adapter->ring_header = rhdr_old;
+               /*
+                * Save SMB and CMB, since atl1_free_ring_resources
+                * will clear them.
+                */
+               smb = adapter->smb;
+               cmb = adapter->cmb;
+               atl1_free_ring_resources(adapter);
+               adapter->rfd_ring = rfd_new;
+               adapter->rrd_ring = rrd_new;
+               adapter->tpd_ring = tpd_new;
+               adapter->ring_header = rhdr_new;
+               adapter->smb = smb;
+               adapter->cmb = cmb;
+               err = atl1_up(adapter);
+               if (err)
+                       return err;
+       }
+       return 0;
+ err_setup_ring:
+       adapter->rfd_ring = rfd_old;
+       adapter->rrd_ring = rrd_old;
+       adapter->tpd_ring = tpd_old;
+       adapter->ring_header = rhdr_old;
+       atl1_up(adapter);
+       return err;
+ }
+ static void atl1_get_pauseparam(struct net_device *netdev,
+       struct ethtool_pauseparam *epause)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       struct atl1_hw *hw = &adapter->hw;
+       if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+           hw->media_type == MEDIA_TYPE_1000M_FULL) {
+               epause->autoneg = AUTONEG_ENABLE;
+       } else {
+               epause->autoneg = AUTONEG_DISABLE;
+       }
+       epause->rx_pause = 1;
+       epause->tx_pause = 1;
+ }
+ static int atl1_set_pauseparam(struct net_device *netdev,
+       struct ethtool_pauseparam *epause)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       struct atl1_hw *hw = &adapter->hw;
+       if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+           hw->media_type == MEDIA_TYPE_1000M_FULL) {
+               epause->autoneg = AUTONEG_ENABLE;
+       } else {
+               epause->autoneg = AUTONEG_DISABLE;
+       }
+       epause->rx_pause = 1;
+       epause->tx_pause = 1;
+       return 0;
+ }
+ static void atl1_get_strings(struct net_device *netdev, u32 stringset,
+       u8 *data)
+ {
+       u8 *p = data;
+       int i;
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) {
+                       memcpy(p, atl1_gstrings_stats[i].stat_string,
+                               ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+               break;
+       }
+ }
+ static int atl1_nway_reset(struct net_device *netdev)
+ {
+       struct atl1_adapter *adapter = netdev_priv(netdev);
+       struct atl1_hw *hw = &adapter->hw;
+       if (netif_running(netdev)) {
+               u16 phy_data;
+               atl1_down(adapter);
+               if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+                       hw->media_type == MEDIA_TYPE_1000M_FULL) {
+                       phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
+               } else {
+                       switch (hw->media_type) {
+                       case MEDIA_TYPE_100M_FULL:
+                               phy_data = MII_CR_FULL_DUPLEX |
+                                       MII_CR_SPEED_100 | MII_CR_RESET;
+                               break;
+                       case MEDIA_TYPE_100M_HALF:
+                               phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
+                               break;
+                       case MEDIA_TYPE_10M_FULL:
+                               phy_data = MII_CR_FULL_DUPLEX |
+                                       MII_CR_SPEED_10 | MII_CR_RESET;
+                               break;
+                       default:
+                               /* MEDIA_TYPE_10M_HALF */
+                               phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
+                       }
+               }
+               atl1_write_phy_reg(hw, MII_BMCR, phy_data);
+               atl1_up(adapter);
+       }
+       return 0;
+ }
+ static const struct ethtool_ops atl1_ethtool_ops = {
+       .get_settings           = atl1_get_settings,
+       .set_settings           = atl1_set_settings,
+       .get_drvinfo            = atl1_get_drvinfo,
+       .get_wol                = atl1_get_wol,
+       .set_wol                = atl1_set_wol,
+       .get_msglevel           = atl1_get_msglevel,
+       .set_msglevel           = atl1_set_msglevel,
+       .get_regs_len           = atl1_get_regs_len,
+       .get_regs               = atl1_get_regs,
+       .get_ringparam          = atl1_get_ringparam,
+       .set_ringparam          = atl1_set_ringparam,
+       .get_pauseparam         = atl1_get_pauseparam,
+       .set_pauseparam         = atl1_set_pauseparam,
+       .get_link               = ethtool_op_get_link,
+       .get_strings            = atl1_get_strings,
+       .nway_reset             = atl1_nway_reset,
+       .get_ethtool_stats      = atl1_get_ethtool_stats,
+       .get_sset_count         = atl1_get_sset_count,
+ };
Simple merge
Simple merge
index 0000000,011f67c..9656dd0
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,2011 +1,2008 @@@
 -      Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html}
 -      for more information on this driver.
 -
+ /*    tulip_core.c: A DEC 21x4x-family ethernet driver for Linux.
+       Copyright 2000,2001  The Linux Kernel Team
+       Written/copyright 1994-2001 by Donald Becker.
+       This software may be used and distributed according to the terms
+       of the GNU General Public License, incorporated herein by reference.
+       Please submit bugs to http://bugzilla.kernel.org/ .
+ */
+ #define pr_fmt(fmt) "tulip: " fmt
+ #define DRV_NAME      "tulip"
+ #ifdef CONFIG_TULIP_NAPI
+ #define DRV_VERSION    "1.1.15-NAPI" /* Keep at least for test */
+ #else
+ #define DRV_VERSION   "1.1.15"
+ #endif
+ #define DRV_RELDATE   "Feb 27, 2007"
+ #include <linux/module.h>
+ #include <linux/pci.h>
+ #include <linux/slab.h>
+ #include "tulip.h"
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/etherdevice.h>
+ #include <linux/delay.h>
+ #include <linux/mii.h>
+ #include <linux/crc32.h>
+ #include <asm/unaligned.h>
+ #include <asm/uaccess.h>
+ #ifdef CONFIG_SPARC
+ #include <asm/prom.h>
+ #endif
+ static char version[] __devinitdata =
+       "Linux Tulip driver version " DRV_VERSION " (" DRV_RELDATE ")\n";
+ /* A few user-configurable values. */
+ /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+ static unsigned int max_interrupt_work = 25;
+ #define MAX_UNITS 8
+ /* Used to pass the full-duplex flag, etc. */
+ static int full_duplex[MAX_UNITS];
+ static int options[MAX_UNITS];
+ static int mtu[MAX_UNITS];                    /* Jumbo MTU for interfaces. */
+ /*  The possible media types that can be set in options[] are: */
+ const char * const medianame[32] = {
+       "10baseT", "10base2", "AUI", "100baseTx",
+       "10baseT-FDX", "100baseTx-FDX", "100baseT4", "100baseFx",
+       "100baseFx-FDX", "MII 10baseT", "MII 10baseT-FDX", "MII",
+       "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FDX", "MII 100baseT4",
+       "MII 100baseFx-HDX", "MII 100baseFx-FDX", "Home-PNA 1Mbps", "Invalid-19",
+       "","","","", "","","","",  "","","","Transceiver reset",
+ };
+ /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
+ #if defined(__alpha__) || defined(__arm__) || defined(__hppa__) || \
+       defined(CONFIG_SPARC) || defined(__ia64__) || \
+       defined(__sh__) || defined(__mips__)
+ static int rx_copybreak = 1518;
+ #else
+ static int rx_copybreak = 100;
+ #endif
+ /*
+   Set the bus performance register.
+       Typical: Set 16 longword cache alignment, no burst limit.
+       Cache alignment bits 15:14           Burst length 13:8
+               0000    No alignment  0x00000000 unlimited              0800 8 longwords
+               4000    8  longwords            0100 1 longword         1000 16 longwords
+               8000    16 longwords            0200 2 longwords        2000 32 longwords
+               C000    32  longwords           0400 4 longwords
+       Warning: many older 486 systems are broken and require setting 0x00A04800
+          8 longword cache alignment, 8 longword burst.
+       ToDo: Non-Intel setting could be better.
+ */
+ #if defined(__alpha__) || defined(__ia64__)
+ static int csr0 = 0x01A00000 | 0xE000;
+ #elif defined(__i386__) || defined(__powerpc__) || defined(__x86_64__)
+ static int csr0 = 0x01A00000 | 0x8000;
+ #elif defined(CONFIG_SPARC) || defined(__hppa__)
+ /* The UltraSparc PCI controllers will disconnect at every 64-byte
+  * crossing anyways so it makes no sense to tell Tulip to burst
+  * any more than that.
+  */
+ static int csr0 = 0x01A00000 | 0x9000;
+ #elif defined(__arm__) || defined(__sh__)
+ static int csr0 = 0x01A00000 | 0x4800;
+ #elif defined(__mips__)
+ static int csr0 = 0x00200000 | 0x4000;
+ #else
+ #warning Processor architecture undefined!
+ static int csr0 = 0x00A00000 | 0x4800;
+ #endif
+ /* Operational parameters that usually are not changed. */
+ /* Time in jiffies before concluding the transmitter is hung. */
+ #define TX_TIMEOUT  (4*HZ)
+ MODULE_AUTHOR("The Linux Kernel Team");
+ MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(DRV_VERSION);
+ module_param(tulip_debug, int, 0);
+ module_param(max_interrupt_work, int, 0);
+ module_param(rx_copybreak, int, 0);
+ module_param(csr0, int, 0);
+ module_param_array(options, int, NULL, 0);
+ module_param_array(full_duplex, int, NULL, 0);
+ #ifdef TULIP_DEBUG
+ int tulip_debug = TULIP_DEBUG;
+ #else
+ int tulip_debug = 1;
+ #endif
+ static void tulip_timer(unsigned long data)
+ {
+       struct net_device *dev = (struct net_device *)data;
+       struct tulip_private *tp = netdev_priv(dev);
+       if (netif_running(dev))
+               schedule_work(&tp->media_work);
+ }
+ /*
+  * This table use during operation for capabilities and media timer.
+  *
+  * It is indexed via the values in 'enum chips'
+  */
+ struct tulip_chip_table tulip_tbl[] = {
+   { }, /* placeholder for array, slot unused currently */
+   { }, /* placeholder for array, slot unused currently */
+   /* DC21140 */
+   { "Digital DS21140 Tulip", 128, 0x0001ebef,
+       HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | HAS_PCI_MWI, tulip_timer,
+       tulip_media_task },
+   /* DC21142, DC21143 */
+   { "Digital DS21142/43 Tulip", 128, 0x0801fbff,
+       HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY
+       | HAS_INTR_MITIGATION | HAS_PCI_MWI, tulip_timer, t21142_media_task },
+   /* LC82C168 */
+   { "Lite-On 82c168 PNIC", 256, 0x0001fbef,
+       HAS_MII | HAS_PNICNWAY, pnic_timer, },
+   /* MX98713 */
+   { "Macronix 98713 PMAC", 128, 0x0001ebef,
+       HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer, },
+   /* MX98715 */
+   { "Macronix 98715 PMAC", 256, 0x0001ebef,
+       HAS_MEDIA_TABLE, mxic_timer, },
+   /* MX98725 */
+   { "Macronix 98725 PMAC", 256, 0x0001ebef,
+       HAS_MEDIA_TABLE, mxic_timer, },
+   /* AX88140 */
+   { "ASIX AX88140", 128, 0x0001fbff,
+       HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY
+       | IS_ASIX, tulip_timer, tulip_media_task },
+   /* PNIC2 */
+   { "Lite-On PNIC-II", 256, 0x0801fbff,
+       HAS_MII | HAS_NWAY | HAS_8023X | HAS_PCI_MWI, pnic2_timer, },
+   /* COMET */
+   { "ADMtek Comet", 256, 0x0001abef,
+       HAS_MII | MC_HASH_ONLY | COMET_MAC_ADDR, comet_timer, },
+   /* COMPEX9881 */
+   { "Compex 9881 PMAC", 128, 0x0001ebef,
+       HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer, },
+   /* I21145 */
+   { "Intel DS21145 Tulip", 128, 0x0801fbff,
+       HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI
+       | HAS_NWAY | HAS_PCI_MWI, tulip_timer, tulip_media_task },
+   /* DM910X */
+ #ifdef CONFIG_TULIP_DM910X
+   { "Davicom DM9102/DM9102A", 128, 0x0001ebef,
+       HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | HAS_ACPI,
+       tulip_timer, tulip_media_task },
+ #else
+   { NULL },
+ #endif
+   /* RS7112 */
+   { "Conexant LANfinity", 256, 0x0001ebef,
+       HAS_MII | HAS_ACPI, tulip_timer, tulip_media_task },
+ };
+ static DEFINE_PCI_DEVICE_TABLE(tulip_pci_tbl) = {
+       { 0x1011, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 },
+       { 0x1011, 0x0019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21143 },
+       { 0x11AD, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, LC82C168 },
+       { 0x10d9, 0x0512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98713 },
+       { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 },
+ /*    { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98725 },*/
+       { 0x125B, 0x1400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AX88140 },
+       { 0x11AD, 0xc115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PNIC2 },
+       { 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x1317, 0x0985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x1317, 0x1985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x1317, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x13D1, 0xAB02, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x13D1, 0xAB03, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x13D1, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x104A, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x104A, 0x2774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x1259, 0xa120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 },
+       { 0x8086, 0x0039, PCI_ANY_ID, PCI_ANY_ID, 0, 0, I21145 },
+ #ifdef CONFIG_TULIP_DM910X
+       { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DM910X },
+       { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DM910X },
+ #endif
+       { 0x1113, 0x1216, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x1113, 0x1217, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 },
+       { 0x1113, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x1186, 0x1541, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x1186, 0x1561, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x1186, 0x1591, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x14f1, 0x1803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CONEXANT },
+       { 0x1626, 0x8410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x1737, 0xAB09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x1737, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x17B3, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { 0x10b7, 0x9300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* 3Com 3CSOHO100B-TX */
+       { 0x14ea, 0xab08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* Planex FNW-3602-TX */
+       { 0x1414, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* Microsoft MN-120 */
+       { 0x1414, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+       { } /* terminate list */
+ };
+ MODULE_DEVICE_TABLE(pci, tulip_pci_tbl);
+ /* A full-duplex map for media types. */
+ const char tulip_media_cap[32] =
+ {0,0,0,16,  3,19,16,24,  27,4,7,5, 0,20,23,20,  28,31,0,0, };
+ static void tulip_tx_timeout(struct net_device *dev);
+ static void tulip_init_ring(struct net_device *dev);
+ static void tulip_free_ring(struct net_device *dev);
+ static netdev_tx_t tulip_start_xmit(struct sk_buff *skb,
+                                         struct net_device *dev);
+ static int tulip_open(struct net_device *dev);
+ static int tulip_close(struct net_device *dev);
+ static void tulip_up(struct net_device *dev);
+ static void tulip_down(struct net_device *dev);
+ static struct net_device_stats *tulip_get_stats(struct net_device *dev);
+ static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+ static void set_rx_mode(struct net_device *dev);
+ static void tulip_set_wolopts(struct pci_dev *pdev, u32 wolopts);
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+ static void poll_tulip(struct net_device *dev);
+ #endif
+ static void tulip_set_power_state (struct tulip_private *tp,
+                                  int sleep, int snooze)
+ {
+       if (tp->flags & HAS_ACPI) {
+               u32 tmp, newtmp;
+               pci_read_config_dword (tp->pdev, CFDD, &tmp);
+               newtmp = tmp & ~(CFDD_Sleep | CFDD_Snooze);
+               if (sleep)
+                       newtmp |= CFDD_Sleep;
+               else if (snooze)
+                       newtmp |= CFDD_Snooze;
+               if (tmp != newtmp)
+                       pci_write_config_dword (tp->pdev, CFDD, newtmp);
+       }
+ }
+ static void tulip_up(struct net_device *dev)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->base_addr;
+       int next_tick = 3*HZ;
+       u32 reg;
+       int i;
+ #ifdef CONFIG_TULIP_NAPI
+       napi_enable(&tp->napi);
+ #endif
+       /* Wake the chip from sleep/snooze mode. */
+       tulip_set_power_state (tp, 0, 0);
+       /* Disable all WOL events */
+       pci_enable_wake(tp->pdev, PCI_D3hot, 0);
+       pci_enable_wake(tp->pdev, PCI_D3cold, 0);
+       tulip_set_wolopts(tp->pdev, 0);
+       /* On some chip revs we must set the MII/SYM port before the reset!? */
+       if (tp->mii_cnt  ||  (tp->mtable  &&  tp->mtable->has_mii))
+               iowrite32(0x00040000, ioaddr + CSR6);
+       /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
+       iowrite32(0x00000001, ioaddr + CSR0);
+       pci_read_config_dword(tp->pdev, PCI_COMMAND, &reg);  /* flush write */
+       udelay(100);
+       /* Deassert reset.
+          Wait the specified 50 PCI cycles after a reset by initializing
+          Tx and Rx queues and the address filter list. */
+       iowrite32(tp->csr0, ioaddr + CSR0);
+       pci_read_config_dword(tp->pdev, PCI_COMMAND, &reg);  /* flush write */
+       udelay(100);
+       if (tulip_debug > 1)
+               netdev_dbg(dev, "tulip_up(), irq==%d\n", dev->irq);
+       iowrite32(tp->rx_ring_dma, ioaddr + CSR3);
+       iowrite32(tp->tx_ring_dma, ioaddr + CSR4);
+       tp->cur_rx = tp->cur_tx = 0;
+       tp->dirty_rx = tp->dirty_tx = 0;
+       if (tp->flags & MC_HASH_ONLY) {
+               u32 addr_low = get_unaligned_le32(dev->dev_addr);
+               u32 addr_high = get_unaligned_le16(dev->dev_addr + 4);
+               if (tp->chip_id == AX88140) {
+                       iowrite32(0, ioaddr + CSR13);
+                       iowrite32(addr_low,  ioaddr + CSR14);
+                       iowrite32(1, ioaddr + CSR13);
+                       iowrite32(addr_high, ioaddr + CSR14);
+               } else if (tp->flags & COMET_MAC_ADDR) {
+                       iowrite32(addr_low,  ioaddr + 0xA4);
+                       iowrite32(addr_high, ioaddr + 0xA8);
+                       iowrite32(0, ioaddr + CSR27);
+                       iowrite32(0, ioaddr + CSR28);
+               }
+       } else {
+               /* This is set_rx_mode(), but without starting the transmitter. */
+               u16 *eaddrs = (u16 *)dev->dev_addr;
+               u16 *setup_frm = &tp->setup_frame[15*6];
+               dma_addr_t mapping;
+               /* 21140 bug: you must add the broadcast address. */
+               memset(tp->setup_frame, 0xff, sizeof(tp->setup_frame));
+               /* Fill the final entry of the table with our physical address. */
+               *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
+               *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
+               *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
+               mapping = pci_map_single(tp->pdev, tp->setup_frame,
+                                        sizeof(tp->setup_frame),
+                                        PCI_DMA_TODEVICE);
+               tp->tx_buffers[tp->cur_tx].skb = NULL;
+               tp->tx_buffers[tp->cur_tx].mapping = mapping;
+               /* Put the setup frame on the Tx list. */
+               tp->tx_ring[tp->cur_tx].length = cpu_to_le32(0x08000000 | 192);
+               tp->tx_ring[tp->cur_tx].buffer1 = cpu_to_le32(mapping);
+               tp->tx_ring[tp->cur_tx].status = cpu_to_le32(DescOwned);
+               tp->cur_tx++;
+       }
+       tp->saved_if_port = dev->if_port;
+       if (dev->if_port == 0)
+               dev->if_port = tp->default_port;
+       /* Allow selecting a default media. */
+       i = 0;
+       if (tp->mtable == NULL)
+               goto media_picked;
+       if (dev->if_port) {
+               int looking_for = tulip_media_cap[dev->if_port] & MediaIsMII ? 11 :
+                       (dev->if_port == 12 ? 0 : dev->if_port);
+               for (i = 0; i < tp->mtable->leafcount; i++)
+                       if (tp->mtable->mleaf[i].media == looking_for) {
+                               dev_info(&dev->dev,
+                                        "Using user-specified media %s\n",
+                                        medianame[dev->if_port]);
+                               goto media_picked;
+                       }
+       }
+       if ((tp->mtable->defaultmedia & 0x0800) == 0) {
+               int looking_for = tp->mtable->defaultmedia & MEDIA_MASK;
+               for (i = 0; i < tp->mtable->leafcount; i++)
+                       if (tp->mtable->mleaf[i].media == looking_for) {
+                               dev_info(&dev->dev,
+                                        "Using EEPROM-set media %s\n",
+                                        medianame[looking_for]);
+                               goto media_picked;
+                       }
+       }
+       /* Start sensing first non-full-duplex media. */
+       for (i = tp->mtable->leafcount - 1;
+                (tulip_media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--)
+               ;
+ media_picked:
+       tp->csr6 = 0;
+       tp->cur_index = i;
+       tp->nwayset = 0;
+       if (dev->if_port) {
+               if (tp->chip_id == DC21143  &&
+                   (tulip_media_cap[dev->if_port] & MediaIsMII)) {
+                       /* We must reset the media CSRs when we force-select MII mode. */
+                       iowrite32(0x0000, ioaddr + CSR13);
+                       iowrite32(0x0000, ioaddr + CSR14);
+                       iowrite32(0x0008, ioaddr + CSR15);
+               }
+               tulip_select_media(dev, 1);
+       } else if (tp->chip_id == DC21142) {
+               if (tp->mii_cnt) {
+                       tulip_select_media(dev, 1);
+                       if (tulip_debug > 1)
+                               dev_info(&dev->dev,
+                                        "Using MII transceiver %d, status %04x\n",
+                                        tp->phys[0],
+                                        tulip_mdio_read(dev, tp->phys[0], 1));
+                       iowrite32(csr6_mask_defstate, ioaddr + CSR6);
+                       tp->csr6 = csr6_mask_hdcap;
+                       dev->if_port = 11;
+                       iowrite32(0x0000, ioaddr + CSR13);
+                       iowrite32(0x0000, ioaddr + CSR14);
+               } else
+                       t21142_start_nway(dev);
+       } else if (tp->chip_id == PNIC2) {
+               /* for initial startup advertise 10/100 Full and Half */
+               tp->sym_advertise = 0x01E0;
+                 /* enable autonegotiate end interrupt */
+               iowrite32(ioread32(ioaddr+CSR5)| 0x00008010, ioaddr + CSR5);
+               iowrite32(ioread32(ioaddr+CSR7)| 0x00008010, ioaddr + CSR7);
+               pnic2_start_nway(dev);
+       } else if (tp->chip_id == LC82C168  &&  ! tp->medialock) {
+               if (tp->mii_cnt) {
+                       dev->if_port = 11;
+                       tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0);
+                       iowrite32(0x0001, ioaddr + CSR15);
+               } else if (ioread32(ioaddr + CSR5) & TPLnkPass)
+                       pnic_do_nway(dev);
+               else {
+                       /* Start with 10mbps to do autonegotiation. */
+                       iowrite32(0x32, ioaddr + CSR12);
+                       tp->csr6 = 0x00420000;
+                       iowrite32(0x0001B078, ioaddr + 0xB8);
+                       iowrite32(0x0201B078, ioaddr + 0xB8);
+                       next_tick = 1*HZ;
+               }
+       } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) &&
+                  ! tp->medialock) {
+               dev->if_port = 0;
+               tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0);
+               iowrite32(0x0f370000 | ioread16(ioaddr + 0x80), ioaddr + 0x80);
+       } else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) {
+               /* Provided by BOLO, Macronix - 12/10/1998. */
+               dev->if_port = 0;
+               tp->csr6 = 0x01a80200;
+               iowrite32(0x0f370000 | ioread16(ioaddr + 0x80), ioaddr + 0x80);
+               iowrite32(0x11000 | ioread16(ioaddr + 0xa0), ioaddr + 0xa0);
+       } else if (tp->chip_id == COMET || tp->chip_id == CONEXANT) {
+               /* Enable automatic Tx underrun recovery. */
+               iowrite32(ioread32(ioaddr + 0x88) | 1, ioaddr + 0x88);
+               dev->if_port = tp->mii_cnt ? 11 : 0;
+               tp->csr6 = 0x00040000;
+       } else if (tp->chip_id == AX88140) {
+               tp->csr6 = tp->mii_cnt ? 0x00040100 : 0x00000100;
+       } else
+               tulip_select_media(dev, 1);
+       /* Start the chip's Tx to process setup frame. */
+       tulip_stop_rxtx(tp);
+       barrier();
+       udelay(5);
+       iowrite32(tp->csr6 | TxOn, ioaddr + CSR6);
+       /* Enable interrupts by setting the interrupt mask. */
+       iowrite32(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5);
+       iowrite32(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
+       tulip_start_rxtx(tp);
+       iowrite32(0, ioaddr + CSR2);            /* Rx poll demand */
+       if (tulip_debug > 2) {
+               netdev_dbg(dev, "Done tulip_up(), CSR0 %08x, CSR5 %08x CSR6 %08x\n",
+                          ioread32(ioaddr + CSR0),
+                          ioread32(ioaddr + CSR5),
+                          ioread32(ioaddr + CSR6));
+       }
+       /* Set the timer to switch to check for link beat and perhaps switch
+          to an alternate media type. */
+       tp->timer.expires = RUN_AT(next_tick);
+       add_timer(&tp->timer);
+ #ifdef CONFIG_TULIP_NAPI
+       init_timer(&tp->oom_timer);
+         tp->oom_timer.data = (unsigned long)dev;
+         tp->oom_timer.function = oom_timer;
+ #endif
+ }
+ static int
+ tulip_open(struct net_device *dev)
+ {
+       int retval;
+       tulip_init_ring (dev);
+       retval = request_irq(dev->irq, tulip_interrupt, IRQF_SHARED, dev->name, dev);
+       if (retval)
+               goto free_ring;
+       tulip_up (dev);
+       netif_start_queue (dev);
+       return 0;
+ free_ring:
+       tulip_free_ring (dev);
+       return retval;
+ }
+ static void tulip_tx_timeout(struct net_device *dev)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->base_addr;
+       unsigned long flags;
+       spin_lock_irqsave (&tp->lock, flags);
+       if (tulip_media_cap[dev->if_port] & MediaIsMII) {
+               /* Do nothing -- the media monitor should handle this. */
+               if (tulip_debug > 1)
+                       dev_warn(&dev->dev,
+                                "Transmit timeout using MII device\n");
+       } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 ||
+                  tp->chip_id == MX98713 || tp->chip_id == COMPEX9881 ||
+                  tp->chip_id == DM910X) {
+               dev_warn(&dev->dev,
+                        "21140 transmit timed out, status %08x, SIA %08x %08x %08x %08x, resetting...\n",
+                        ioread32(ioaddr + CSR5), ioread32(ioaddr + CSR12),
+                        ioread32(ioaddr + CSR13), ioread32(ioaddr + CSR14),
+                        ioread32(ioaddr + CSR15));
+               tp->timeout_recovery = 1;
+               schedule_work(&tp->media_work);
+               goto out_unlock;
+       } else if (tp->chip_id == PNIC2) {
+               dev_warn(&dev->dev,
+                        "PNIC2 transmit timed out, status %08x, CSR6/7 %08x / %08x CSR12 %08x, resetting...\n",
+                        (int)ioread32(ioaddr + CSR5),
+                        (int)ioread32(ioaddr + CSR6),
+                        (int)ioread32(ioaddr + CSR7),
+                        (int)ioread32(ioaddr + CSR12));
+       } else {
+               dev_warn(&dev->dev,
+                        "Transmit timed out, status %08x, CSR12 %08x, resetting...\n",
+                        ioread32(ioaddr + CSR5), ioread32(ioaddr + CSR12));
+               dev->if_port = 0;
+       }
+ #if defined(way_too_many_messages)
+       if (tulip_debug > 3) {
+               int i;
+               for (i = 0; i < RX_RING_SIZE; i++) {
+                       u8 *buf = (u8 *)(tp->rx_ring[i].buffer1);
+                       int j;
+                       printk(KERN_DEBUG
+                              "%2d: %08x %08x %08x %08x  %02x %02x %02x\n",
+                              i,
+                              (unsigned int)tp->rx_ring[i].status,
+                              (unsigned int)tp->rx_ring[i].length,
+                              (unsigned int)tp->rx_ring[i].buffer1,
+                              (unsigned int)tp->rx_ring[i].buffer2,
+                              buf[0], buf[1], buf[2]);
+                       for (j = 0; buf[j] != 0xee && j < 1600; j++)
+                               if (j < 100)
+                                       pr_cont(" %02x", buf[j]);
+                       pr_cont(" j=%d\n", j);
+               }
+               printk(KERN_DEBUG "  Rx ring %p: ", tp->rx_ring);
+               for (i = 0; i < RX_RING_SIZE; i++)
+                       pr_cont(" %08x", (unsigned int)tp->rx_ring[i].status);
+               printk(KERN_DEBUG "  Tx ring %p: ", tp->tx_ring);
+               for (i = 0; i < TX_RING_SIZE; i++)
+                       pr_cont(" %08x", (unsigned int)tp->tx_ring[i].status);
+               pr_cont("\n");
+       }
+ #endif
+       tulip_tx_timeout_complete(tp, ioaddr);
+ out_unlock:
+       spin_unlock_irqrestore (&tp->lock, flags);
+       dev->trans_start = jiffies; /* prevent tx timeout */
+       netif_wake_queue (dev);
+ }
+ /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+ static void tulip_init_ring(struct net_device *dev)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       int i;
+       tp->susp_rx = 0;
+       tp->ttimer = 0;
+       tp->nir = 0;
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               tp->rx_ring[i].status = 0x00000000;
+               tp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ);
+               tp->rx_ring[i].buffer2 = cpu_to_le32(tp->rx_ring_dma + sizeof(struct tulip_rx_desc) * (i + 1));
+               tp->rx_buffers[i].skb = NULL;
+               tp->rx_buffers[i].mapping = 0;
+       }
+       /* Mark the last entry as wrapping the ring. */
+       tp->rx_ring[i-1].length = cpu_to_le32(PKT_BUF_SZ | DESC_RING_WRAP);
+       tp->rx_ring[i-1].buffer2 = cpu_to_le32(tp->rx_ring_dma);
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               dma_addr_t mapping;
+               /* Note the receive buffer must be longword aligned.
+                  dev_alloc_skb() provides 16 byte alignment.  But do *not*
+                  use skb_reserve() to align the IP header! */
+               struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ);
+               tp->rx_buffers[i].skb = skb;
+               if (skb == NULL)
+                       break;
+               mapping = pci_map_single(tp->pdev, skb->data,
+                                        PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+               tp->rx_buffers[i].mapping = mapping;
+               skb->dev = dev;                 /* Mark as being used by this device. */
+               tp->rx_ring[i].status = cpu_to_le32(DescOwned); /* Owned by Tulip chip */
+               tp->rx_ring[i].buffer1 = cpu_to_le32(mapping);
+       }
+       tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+       /* The Tx buffer descriptor is filled in as needed, but we
+          do need to clear the ownership bit. */
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               tp->tx_buffers[i].skb = NULL;
+               tp->tx_buffers[i].mapping = 0;
+               tp->tx_ring[i].status = 0x00000000;
+               tp->tx_ring[i].buffer2 = cpu_to_le32(tp->tx_ring_dma + sizeof(struct tulip_tx_desc) * (i + 1));
+       }
+       tp->tx_ring[i-1].buffer2 = cpu_to_le32(tp->tx_ring_dma);
+ }
+ static netdev_tx_t
+ tulip_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       int entry;
+       u32 flag;
+       dma_addr_t mapping;
+       unsigned long flags;
+       spin_lock_irqsave(&tp->lock, flags);
+       /* Calculate the next Tx descriptor entry. */
+       entry = tp->cur_tx % TX_RING_SIZE;
+       tp->tx_buffers[entry].skb = skb;
+       mapping = pci_map_single(tp->pdev, skb->data,
+                                skb->len, PCI_DMA_TODEVICE);
+       tp->tx_buffers[entry].mapping = mapping;
+       tp->tx_ring[entry].buffer1 = cpu_to_le32(mapping);
+       if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
+               flag = 0x60000000; /* No interrupt */
+       } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
+               flag = 0xe0000000; /* Tx-done intr. */
+       } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
+               flag = 0x60000000; /* No Tx-done intr. */
+       } else {                /* Leave room for set_rx_mode() to fill entries. */
+               flag = 0xe0000000; /* Tx-done intr. */
+               netif_stop_queue(dev);
+       }
+       if (entry == TX_RING_SIZE-1)
+               flag = 0xe0000000 | DESC_RING_WRAP;
+       tp->tx_ring[entry].length = cpu_to_le32(skb->len | flag);
+       /* if we were using Transmit Automatic Polling, we would need a
+        * wmb() here. */
+       tp->tx_ring[entry].status = cpu_to_le32(DescOwned);
+       wmb();
+       tp->cur_tx++;
+       /* Trigger an immediate transmit demand. */
+       iowrite32(0, tp->base_addr + CSR1);
+       spin_unlock_irqrestore(&tp->lock, flags);
+       return NETDEV_TX_OK;
+ }
+ static void tulip_clean_tx_ring(struct tulip_private *tp)
+ {
+       unsigned int dirty_tx;
+       for (dirty_tx = tp->dirty_tx ; tp->cur_tx - dirty_tx > 0;
+               dirty_tx++) {
+               int entry = dirty_tx % TX_RING_SIZE;
+               int status = le32_to_cpu(tp->tx_ring[entry].status);
+               if (status < 0) {
+                       tp->dev->stats.tx_errors++;     /* It wasn't Txed */
+                       tp->tx_ring[entry].status = 0;
+               }
+               /* Check for Tx filter setup frames. */
+               if (tp->tx_buffers[entry].skb == NULL) {
+                       /* test because dummy frames not mapped */
+                       if (tp->tx_buffers[entry].mapping)
+                               pci_unmap_single(tp->pdev,
+                                       tp->tx_buffers[entry].mapping,
+                                       sizeof(tp->setup_frame),
+                                       PCI_DMA_TODEVICE);
+                       continue;
+               }
+               pci_unmap_single(tp->pdev, tp->tx_buffers[entry].mapping,
+                               tp->tx_buffers[entry].skb->len,
+                               PCI_DMA_TODEVICE);
+               /* Free the original skb. */
+               dev_kfree_skb_irq(tp->tx_buffers[entry].skb);
+               tp->tx_buffers[entry].skb = NULL;
+               tp->tx_buffers[entry].mapping = 0;
+       }
+ }
+ static void tulip_down (struct net_device *dev)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->base_addr;
+       unsigned long flags;
+       cancel_work_sync(&tp->media_work);
+ #ifdef CONFIG_TULIP_NAPI
+       napi_disable(&tp->napi);
+ #endif
+       del_timer_sync (&tp->timer);
+ #ifdef CONFIG_TULIP_NAPI
+       del_timer_sync (&tp->oom_timer);
+ #endif
+       spin_lock_irqsave (&tp->lock, flags);
+       /* Disable interrupts by clearing the interrupt mask. */
+       iowrite32 (0x00000000, ioaddr + CSR7);
+       /* Stop the Tx and Rx processes. */
+       tulip_stop_rxtx(tp);
+       /* prepare receive buffers */
+       tulip_refill_rx(dev);
+       /* release any unconsumed transmit buffers */
+       tulip_clean_tx_ring(tp);
+       if (ioread32(ioaddr + CSR6) != 0xffffffff)
+               dev->stats.rx_missed_errors += ioread32(ioaddr + CSR8) & 0xffff;
+       spin_unlock_irqrestore (&tp->lock, flags);
+       init_timer(&tp->timer);
+       tp->timer.data = (unsigned long)dev;
+       tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
+       dev->if_port = tp->saved_if_port;
+       /* Leave the driver in snooze, not sleep, mode. */
+       tulip_set_power_state (tp, 0, 1);
+ }
+ static void tulip_free_ring (struct net_device *dev)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       int i;
+       /* Free all the skbuffs in the Rx queue. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               struct sk_buff *skb = tp->rx_buffers[i].skb;
+               dma_addr_t mapping = tp->rx_buffers[i].mapping;
+               tp->rx_buffers[i].skb = NULL;
+               tp->rx_buffers[i].mapping = 0;
+               tp->rx_ring[i].status = 0;      /* Not owned by Tulip chip. */
+               tp->rx_ring[i].length = 0;
+               /* An invalid address. */
+               tp->rx_ring[i].buffer1 = cpu_to_le32(0xBADF00D0);
+               if (skb) {
+                       pci_unmap_single(tp->pdev, mapping, PKT_BUF_SZ,
+                                        PCI_DMA_FROMDEVICE);
+                       dev_kfree_skb (skb);
+               }
+       }
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               struct sk_buff *skb = tp->tx_buffers[i].skb;
+               if (skb != NULL) {
+                       pci_unmap_single(tp->pdev, tp->tx_buffers[i].mapping,
+                                        skb->len, PCI_DMA_TODEVICE);
+                       dev_kfree_skb (skb);
+               }
+               tp->tx_buffers[i].skb = NULL;
+               tp->tx_buffers[i].mapping = 0;
+       }
+ }
+ static int tulip_close (struct net_device *dev)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->base_addr;
+       netif_stop_queue (dev);
+       tulip_down (dev);
+       if (tulip_debug > 1)
+               netdev_dbg(dev, "Shutting down ethercard, status was %02x\n",
+                          ioread32 (ioaddr + CSR5));
+       free_irq (dev->irq, dev);
+       tulip_free_ring (dev);
+       return 0;
+ }
+ static struct net_device_stats *tulip_get_stats(struct net_device *dev)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->base_addr;
+       if (netif_running(dev)) {
+               unsigned long flags;
+               spin_lock_irqsave (&tp->lock, flags);
+               dev->stats.rx_missed_errors += ioread32(ioaddr + CSR8) & 0xffff;
+               spin_unlock_irqrestore(&tp->lock, flags);
+       }
+       return &dev->stats;
+ }
+ static void tulip_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+ {
+       struct tulip_private *np = netdev_priv(dev);
+       strcpy(info->driver, DRV_NAME);
+       strcpy(info->version, DRV_VERSION);
+       strcpy(info->bus_info, pci_name(np->pdev));
+ }
+ static int tulip_ethtool_set_wol(struct net_device *dev,
+                                struct ethtool_wolinfo *wolinfo)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       if (wolinfo->wolopts & (~tp->wolinfo.supported))
+                  return -EOPNOTSUPP;
+       tp->wolinfo.wolopts = wolinfo->wolopts;
+       device_set_wakeup_enable(&tp->pdev->dev, tp->wolinfo.wolopts);
+       return 0;
+ }
+ static void tulip_ethtool_get_wol(struct net_device *dev,
+                                 struct ethtool_wolinfo *wolinfo)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       wolinfo->supported = tp->wolinfo.supported;
+       wolinfo->wolopts = tp->wolinfo.wolopts;
+       return;
+ }
+ static const struct ethtool_ops ops = {
+       .get_drvinfo = tulip_get_drvinfo,
+       .set_wol     = tulip_ethtool_set_wol,
+       .get_wol     = tulip_ethtool_get_wol,
+ };
+ /* Provide ioctl() calls to examine the MII xcvr state. */
+ static int private_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->base_addr;
+       struct mii_ioctl_data *data = if_mii(rq);
+       const unsigned int phy_idx = 0;
+       int phy = tp->phys[phy_idx] & 0x1f;
+       unsigned int regnum = data->reg_num;
+       switch (cmd) {
+       case SIOCGMIIPHY:               /* Get address of MII PHY in use. */
+               if (tp->mii_cnt)
+                       data->phy_id = phy;
+               else if (tp->flags & HAS_NWAY)
+                       data->phy_id = 32;
+               else if (tp->chip_id == COMET)
+                       data->phy_id = 1;
+               else
+                       return -ENODEV;
+       case SIOCGMIIREG:               /* Read MII PHY register. */
+               if (data->phy_id == 32 && (tp->flags & HAS_NWAY)) {
+                       int csr12 = ioread32 (ioaddr + CSR12);
+                       int csr14 = ioread32 (ioaddr + CSR14);
+                       switch (regnum) {
+                       case 0:
+                                 if (((csr14<<5) & 0x1000) ||
+                                         (dev->if_port == 5 && tp->nwayset))
+                                         data->val_out = 0x1000;
+                                 else
+                                         data->val_out = (tulip_media_cap[dev->if_port]&MediaIs100 ? 0x2000 : 0)
+                                                 | (tulip_media_cap[dev->if_port]&MediaIsFD ? 0x0100 : 0);
+                               break;
+                       case 1:
+                                 data->val_out =
+                                       0x1848 +
+                                       ((csr12&0x7000) == 0x5000 ? 0x20 : 0) +
+                                       ((csr12&0x06) == 6 ? 0 : 4);
+                                 data->val_out |= 0x6048;
+                               break;
+                       case 4:
+                                 /* Advertised value, bogus 10baseTx-FD value from CSR6. */
+                                 data->val_out =
+                                       ((ioread32(ioaddr + CSR6) >> 3) & 0x0040) +
+                                       ((csr14 >> 1) & 0x20) + 1;
+                                 data->val_out |= ((csr14 >> 9) & 0x03C0);
+                               break;
+                       case 5: data->val_out = tp->lpar; break;
+                       default: data->val_out = 0; break;
+                       }
+               } else {
+                       data->val_out = tulip_mdio_read (dev, data->phy_id & 0x1f, regnum);
+               }
+               return 0;
+       case SIOCSMIIREG:               /* Write MII PHY register. */
+               if (regnum & ~0x1f)
+                       return -EINVAL;
+               if (data->phy_id == phy) {
+                       u16 value = data->val_in;
+                       switch (regnum) {
+                       case 0: /* Check for autonegotiation on or reset. */
+                               tp->full_duplex_lock = (value & 0x9000) ? 0 : 1;
+                               if (tp->full_duplex_lock)
+                                       tp->full_duplex = (value & 0x0100) ? 1 : 0;
+                               break;
+                       case 4:
+                               tp->advertising[phy_idx] =
+                               tp->mii_advertise = data->val_in;
+                               break;
+                       }
+               }
+               if (data->phy_id == 32 && (tp->flags & HAS_NWAY)) {
+                       u16 value = data->val_in;
+                       if (regnum == 0) {
+                         if ((value & 0x1200) == 0x1200) {
+                           if (tp->chip_id == PNIC2) {
+                                    pnic2_start_nway (dev);
+                             } else {
+                                  t21142_start_nway (dev);
+                             }
+                         }
+                       } else if (regnum == 4)
+                               tp->sym_advertise = value;
+               } else {
+                       tulip_mdio_write (dev, data->phy_id & 0x1f, regnum, data->val_in);
+               }
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return -EOPNOTSUPP;
+ }
+ /* Set or clear the multicast filter for this adaptor.
+    Note that we only use exclusion around actually queueing the
+    new frame, not around filling tp->setup_frame.  This is non-deterministic
+    when re-entered but still correct. */
+ #undef set_bit_le
+ #define set_bit_le(i,p) do { ((char *)(p))[(i)/8] |= (1<<((i)%8)); } while(0)
+ static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       u16 hash_table[32];
+       struct netdev_hw_addr *ha;
+       int i;
+       u16 *eaddrs;
+       memset(hash_table, 0, sizeof(hash_table));
+       set_bit_le(255, hash_table);                    /* Broadcast entry */
+       /* This should work on big-endian machines as well. */
+       netdev_for_each_mc_addr(ha, dev) {
+               int index = ether_crc_le(ETH_ALEN, ha->addr) & 0x1ff;
+               set_bit_le(index, hash_table);
+       }
+       for (i = 0; i < 32; i++) {
+               *setup_frm++ = hash_table[i];
+               *setup_frm++ = hash_table[i];
+       }
+       setup_frm = &tp->setup_frame[13*6];
+       /* Fill the final entry with our physical address. */
+       eaddrs = (u16 *)dev->dev_addr;
+       *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
+       *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
+       *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
+ }
+ static void build_setup_frame_perfect(u16 *setup_frm, struct net_device *dev)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       struct netdev_hw_addr *ha;
+       u16 *eaddrs;
+       /* We have <= 14 addresses so we can use the wonderful
+          16 address perfect filtering of the Tulip. */
+       netdev_for_each_mc_addr(ha, dev) {
+               eaddrs = (u16 *) ha->addr;
+               *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++;
+               *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++;
+               *setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++;
+       }
+       /* Fill the unused entries with the broadcast address. */
+       memset(setup_frm, 0xff, (15 - netdev_mc_count(dev)) * 12);
+       setup_frm = &tp->setup_frame[15*6];
+       /* Fill the final entry with our physical address. */
+       eaddrs = (u16 *)dev->dev_addr;
+       *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
+       *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
+       *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
+ }
+ static void set_rx_mode(struct net_device *dev)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->base_addr;
+       int csr6;
+       csr6 = ioread32(ioaddr + CSR6) & ~0x00D5;
+       tp->csr6 &= ~0x00D5;
+       if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous. */
+               tp->csr6 |= AcceptAllMulticast | AcceptAllPhys;
+               csr6 |= AcceptAllMulticast | AcceptAllPhys;
+       } else if ((netdev_mc_count(dev) > 1000) ||
+                  (dev->flags & IFF_ALLMULTI)) {
+               /* Too many to filter well -- accept all multicasts. */
+               tp->csr6 |= AcceptAllMulticast;
+               csr6 |= AcceptAllMulticast;
+       } else  if (tp->flags & MC_HASH_ONLY) {
+               /* Some work-alikes have only a 64-entry hash filter table. */
+               /* Should verify correctness on big-endian/__powerpc__ */
+               struct netdev_hw_addr *ha;
+               if (netdev_mc_count(dev) > 64) {
+                       /* Arbitrary non-effective limit. */
+                       tp->csr6 |= AcceptAllMulticast;
+                       csr6 |= AcceptAllMulticast;
+               } else {
+                       u32 mc_filter[2] = {0, 0};               /* Multicast hash filter */
+                       int filterbit;
+                       netdev_for_each_mc_addr(ha, dev) {
+                               if (tp->flags & COMET_MAC_ADDR)
+                                       filterbit = ether_crc_le(ETH_ALEN,
+                                                                ha->addr);
+                               else
+                                       filterbit = ether_crc(ETH_ALEN,
+                                                             ha->addr) >> 26;
+                               filterbit &= 0x3f;
+                               mc_filter[filterbit >> 5] |= 1 << (filterbit & 31);
+                               if (tulip_debug > 2)
+                                       dev_info(&dev->dev,
+                                                "Added filter for %pM  %08x bit %d\n",
+                                                ha->addr,
+                                                ether_crc(ETH_ALEN, ha->addr),
+                                                filterbit);
+                       }
+                       if (mc_filter[0] == tp->mc_filter[0]  &&
+                               mc_filter[1] == tp->mc_filter[1])
+                               ;                               /* No change. */
+                       else if (tp->flags & IS_ASIX) {
+                               iowrite32(2, ioaddr + CSR13);
+                               iowrite32(mc_filter[0], ioaddr + CSR14);
+                               iowrite32(3, ioaddr + CSR13);
+                               iowrite32(mc_filter[1], ioaddr + CSR14);
+                       } else if (tp->flags & COMET_MAC_ADDR) {
+                               iowrite32(mc_filter[0], ioaddr + CSR27);
+                               iowrite32(mc_filter[1], ioaddr + CSR28);
+                       }
+                       tp->mc_filter[0] = mc_filter[0];
+                       tp->mc_filter[1] = mc_filter[1];
+               }
+       } else {
+               unsigned long flags;
+               u32 tx_flags = 0x08000000 | 192;
+               /* Note that only the low-address shortword of setup_frame is valid!
+                  The values are doubled for big-endian architectures. */
+               if (netdev_mc_count(dev) > 14) {
+                       /* Must use a multicast hash table. */
+                       build_setup_frame_hash(tp->setup_frame, dev);
+                       tx_flags = 0x08400000 | 192;
+               } else {
+                       build_setup_frame_perfect(tp->setup_frame, dev);
+               }
+               spin_lock_irqsave(&tp->lock, flags);
+               if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
+                       /* Same setup recently queued, we need not add it. */
+               } else {
+                       unsigned int entry;
+                       int dummy = -1;
+                       /* Now add this frame to the Tx list. */
+                       entry = tp->cur_tx++ % TX_RING_SIZE;
+                       if (entry != 0) {
+                               /* Avoid a chip errata by prefixing a dummy entry. */
+                               tp->tx_buffers[entry].skb = NULL;
+                               tp->tx_buffers[entry].mapping = 0;
+                               tp->tx_ring[entry].length =
+                                       (entry == TX_RING_SIZE-1) ? cpu_to_le32(DESC_RING_WRAP) : 0;
+                               tp->tx_ring[entry].buffer1 = 0;
+                               /* Must set DescOwned later to avoid race with chip */
+                               dummy = entry;
+                               entry = tp->cur_tx++ % TX_RING_SIZE;
+                       }
+                       tp->tx_buffers[entry].skb = NULL;
+                       tp->tx_buffers[entry].mapping =
+                               pci_map_single(tp->pdev, tp->setup_frame,
+                                              sizeof(tp->setup_frame),
+                                              PCI_DMA_TODEVICE);
+                       /* Put the setup frame on the Tx list. */
+                       if (entry == TX_RING_SIZE-1)
+                               tx_flags |= DESC_RING_WRAP;             /* Wrap ring. */
+                       tp->tx_ring[entry].length = cpu_to_le32(tx_flags);
+                       tp->tx_ring[entry].buffer1 =
+                               cpu_to_le32(tp->tx_buffers[entry].mapping);
+                       tp->tx_ring[entry].status = cpu_to_le32(DescOwned);
+                       if (dummy >= 0)
+                               tp->tx_ring[dummy].status = cpu_to_le32(DescOwned);
+                       if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2)
+                               netif_stop_queue(dev);
+                       /* Trigger an immediate transmit demand. */
+                       iowrite32(0, ioaddr + CSR1);
+               }
+               spin_unlock_irqrestore(&tp->lock, flags);
+       }
+       iowrite32(csr6, ioaddr + CSR6);
+ }
+ #ifdef CONFIG_TULIP_MWI
+ static void __devinit tulip_mwi_config (struct pci_dev *pdev,
+                                       struct net_device *dev)
+ {
+       struct tulip_private *tp = netdev_priv(dev);
+       u8 cache;
+       u16 pci_command;
+       u32 csr0;
+       if (tulip_debug > 3)
+               netdev_dbg(dev, "tulip_mwi_config()\n");
+       tp->csr0 = csr0 = 0;
+       /* if we have any cache line size at all, we can do MRM and MWI */
+       csr0 |= MRM | MWI;
+       /* Enable MWI in the standard PCI command bit.
+        * Check for the case where MWI is desired but not available
+        */
+       pci_try_set_mwi(pdev);
+       /* read result from hardware (in case bit refused to enable) */
+       pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+       if ((csr0 & MWI) && (!(pci_command & PCI_COMMAND_INVALIDATE)))
+               csr0 &= ~MWI;
+       /* if cache line size hardwired to zero, no MWI */
+       pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache);
+       if ((csr0 & MWI) && (cache == 0)) {
+               csr0 &= ~MWI;
+               pci_clear_mwi(pdev);
+       }
+       /* assign per-cacheline-size cache alignment and
+        * burst length values
+        */
+       switch (cache) {
+       case 8:
+               csr0 |= MRL | (1 << CALShift) | (16 << BurstLenShift);
+               break;
+       case 16:
+               csr0 |= MRL | (2 << CALShift) | (16 << BurstLenShift);
+               break;
+       case 32:
+               csr0 |= MRL | (3 << CALShift) | (32 << BurstLenShift);
+               break;
+       default:
+               cache = 0;
+               break;
+       }
+       /* if we have a good cache line size, we by now have a good
+        * csr0, so save it and exit
+        */
+       if (cache)
+               goto out;
+       /* we don't have a good csr0 or cache line size, disable MWI */
+       if (csr0 & MWI) {
+               pci_clear_mwi(pdev);
+               csr0 &= ~MWI;
+       }
+       /* sane defaults for burst length and cache alignment
+        * originally from de4x5 driver
+        */
+       csr0 |= (8 << BurstLenShift) | (1 << CALShift);
+ out:
+       tp->csr0 = csr0;
+       if (tulip_debug > 2)
+               netdev_dbg(dev, "MWI config cacheline=%d, csr0=%08x\n",
+                          cache, csr0);
+ }
+ #endif
+ /*
+  *    Chips that have the MRM/reserved bit quirk and the burst quirk. That
+  *    is the DM910X and the on chip ULi devices
+  */
+ static int tulip_uli_dm_quirk(struct pci_dev *pdev)
+ {
+       if (pdev->vendor == 0x1282 && pdev->device == 0x9102)
+               return 1;
+       return 0;
+ }
+ static const struct net_device_ops tulip_netdev_ops = {
+       .ndo_open               = tulip_open,
+       .ndo_start_xmit         = tulip_start_xmit,
+       .ndo_tx_timeout         = tulip_tx_timeout,
+       .ndo_stop               = tulip_close,
+       .ndo_get_stats          = tulip_get_stats,
+       .ndo_do_ioctl           = private_ioctl,
+       .ndo_set_rx_mode        = set_rx_mode,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller     = poll_tulip,
+ #endif
+ };
+ DEFINE_PCI_DEVICE_TABLE(early_486_chipsets) = {
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82424) },
+       { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496) },
+       { },
+ };
+ static int __devinit tulip_init_one (struct pci_dev *pdev,
+                                    const struct pci_device_id *ent)
+ {
+       struct tulip_private *tp;
+       /* See note below on the multiport cards. */
+       static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
+       static int last_irq;
+       static int multiport_cnt;       /* For four-port boards w/one EEPROM */
+       int i, irq;
+       unsigned short sum;
+       unsigned char *ee_data;
+       struct net_device *dev;
+       void __iomem *ioaddr;
+       static int board_idx = -1;
+       int chip_idx = ent->driver_data;
+       const char *chip_name = tulip_tbl[chip_idx].chip_name;
+       unsigned int eeprom_missing = 0;
+       unsigned int force_csr0 = 0;
+ #ifndef MODULE
+       if (tulip_debug > 0)
+               printk_once(KERN_INFO "%s", version);
+ #endif
+       board_idx++;
+       /*
+        *      Lan media wire a tulip chip to a wan interface. Needs a very
+        *      different driver (lmc driver)
+        */
+         if (pdev->subsystem_vendor == PCI_VENDOR_ID_LMC) {
+               pr_err("skipping LMC card\n");
+               return -ENODEV;
+       } else if (pdev->subsystem_vendor == PCI_VENDOR_ID_SBE &&
+                  (pdev->subsystem_device == PCI_SUBDEVICE_ID_SBE_T3E3 ||
+                   pdev->subsystem_device == PCI_SUBDEVICE_ID_SBE_2T3E3_P0 ||
+                   pdev->subsystem_device == PCI_SUBDEVICE_ID_SBE_2T3E3_P1)) {
+               pr_err("skipping SBE T3E3 port\n");
+               return -ENODEV;
+       }
+       /*
+        *      DM910x chips should be handled by the dmfe driver, except
+        *      on-board chips on SPARC systems.  Also, early DM9100s need
+        *      software CRC which only the dmfe driver supports.
+        */
+ #ifdef CONFIG_TULIP_DM910X
+       if (chip_idx == DM910X) {
+               struct device_node *dp;
+               if (pdev->vendor == 0x1282 && pdev->device == 0x9100 &&
+                   pdev->revision < 0x30) {
+                       pr_info("skipping early DM9100 with Crc bug (use dmfe)\n");
+                       return -ENODEV;
+               }
+               dp = pci_device_to_OF_node(pdev);
+               if (!(dp && of_get_property(dp, "local-mac-address", NULL))) {
+                       pr_info("skipping DM910x expansion card (use dmfe)\n");
+                       return -ENODEV;
+               }
+       }
+ #endif
+       /*
+        *      Looks for early PCI chipsets where people report hangs
+        *      without the workarounds being on.
+        */
+       /* 1. Intel Saturn. Switch to 8 long words burst, 8 long word cache
+             aligned.  Aries might need this too. The Saturn errata are not
+             pretty reading but thankfully it's an old 486 chipset.
+          2. The dreaded SiS496 486 chipset. Same workaround as Intel
+             Saturn.
+       */
+       if (pci_dev_present(early_486_chipsets)) {
+               csr0 = MRL | MRM | (8 << BurstLenShift) | (1 << CALShift);
+               force_csr0 = 1;
+       }
+       /* bugfix: the ASIX must have a burst limit or horrible things happen. */
+       if (chip_idx == AX88140) {
+               if ((csr0 & 0x3f00) == 0)
+                       csr0 |= 0x2000;
+       }
+       /* PNIC doesn't have MWI/MRL/MRM... */
+       if (chip_idx == LC82C168)
+               csr0 &= ~0xfff10000; /* zero reserved bits 31:20, 16 */
+       /* DM9102A has troubles with MRM & clear reserved bits 24:22, 20, 16, 7:1 */
+       if (tulip_uli_dm_quirk(pdev)) {
+               csr0 &= ~0x01f100ff;
+ #if defined(CONFIG_SPARC)
+                 csr0 = (csr0 & ~0xff00) | 0xe000;
+ #endif
+       }
+       /*
+        *      And back to business
+        */
+       i = pci_enable_device(pdev);
+       if (i) {
+               pr_err("Cannot enable tulip board #%d, aborting\n", board_idx);
+               return i;
+       }
+       /* The chip will fail to enter a low-power state later unless
+        * first explicitly commanded into D0 */
+       if (pci_set_power_state(pdev, PCI_D0)) {
+               pr_notice("Failed to set power state to D0\n");
+       }
+       irq = pdev->irq;
+       /* alloc_etherdev ensures aligned and zeroed private structures */
+       dev = alloc_etherdev (sizeof (*tp));
+       if (!dev) {
+               pr_err("ether device alloc failed, aborting\n");
+               return -ENOMEM;
+       }
+       SET_NETDEV_DEV(dev, &pdev->dev);
+       if (pci_resource_len (pdev, 0) < tulip_tbl[chip_idx].io_size) {
+               pr_err("%s: I/O region (0x%llx@0x%llx) too small, aborting\n",
+                      pci_name(pdev),
+                      (unsigned long long)pci_resource_len (pdev, 0),
+                      (unsigned long long)pci_resource_start (pdev, 0));
+               goto err_out_free_netdev;
+       }
+       /* grab all resources from both PIO and MMIO regions, as we
+        * don't want anyone else messing around with our hardware */
+       if (pci_request_regions (pdev, DRV_NAME))
+               goto err_out_free_netdev;
+       ioaddr =  pci_iomap(pdev, TULIP_BAR, tulip_tbl[chip_idx].io_size);
+       if (!ioaddr)
+               goto err_out_free_res;
+       /*
+        * initialize private data structure 'tp'
+        * it is zeroed and aligned in alloc_etherdev
+        */
+       tp = netdev_priv(dev);
+       tp->dev = dev;
+       tp->rx_ring = pci_alloc_consistent(pdev,
+                                          sizeof(struct tulip_rx_desc) * RX_RING_SIZE +
+                                          sizeof(struct tulip_tx_desc) * TX_RING_SIZE,
+                                          &tp->rx_ring_dma);
+       if (!tp->rx_ring)
+               goto err_out_mtable;
+       tp->tx_ring = (struct tulip_tx_desc *)(tp->rx_ring + RX_RING_SIZE);
+       tp->tx_ring_dma = tp->rx_ring_dma + sizeof(struct tulip_rx_desc) * RX_RING_SIZE;
+       tp->chip_id = chip_idx;
+       tp->flags = tulip_tbl[chip_idx].flags;
+       tp->wolinfo.supported = 0;
+       tp->wolinfo.wolopts = 0;
+       /* COMET: Enable power management only for AN983B */
+       if (chip_idx == COMET ) {
+               u32 sig;
+               pci_read_config_dword (pdev, 0x80, &sig);
+               if (sig == 0x09811317) {
+                       tp->flags |= COMET_PM;
+                       tp->wolinfo.supported = WAKE_PHY | WAKE_MAGIC;
+                       pr_info("%s: Enabled WOL support for AN983B\n",
+                               __func__);
+               }
+       }
+       tp->pdev = pdev;
+       tp->base_addr = ioaddr;
+       tp->revision = pdev->revision;
+       tp->csr0 = csr0;
+       spin_lock_init(&tp->lock);
+       spin_lock_init(&tp->mii_lock);
+       init_timer(&tp->timer);
+       tp->timer.data = (unsigned long)dev;
+       tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
+       INIT_WORK(&tp->media_work, tulip_tbl[tp->chip_id].media_task);
+       dev->base_addr = (unsigned long)ioaddr;
+ #ifdef CONFIG_TULIP_MWI
+       if (!force_csr0 && (tp->flags & HAS_PCI_MWI))
+               tulip_mwi_config (pdev, dev);
+ #endif
+       /* Stop the chip's Tx and Rx processes. */
+       tulip_stop_rxtx(tp);
+       pci_set_master(pdev);
+ #ifdef CONFIG_GSC
+       if (pdev->subsystem_vendor == PCI_VENDOR_ID_HP) {
+               switch (pdev->subsystem_device) {
+               default:
+                       break;
+               case 0x1061:
+               case 0x1062:
+               case 0x1063:
+               case 0x1098:
+               case 0x1099:
+               case 0x10EE:
+                       tp->flags |= HAS_SWAPPED_SEEPROM | NEEDS_FAKE_MEDIA_TABLE;
+                       chip_name = "GSC DS21140 Tulip";
+               }
+       }
+ #endif
+       /* Clear the missed-packet counter. */
+       ioread32(ioaddr + CSR8);
+       /* The station address ROM is read byte serially.  The register must
+          be polled, waiting for the value to be read bit serially from the
+          EEPROM.
+          */
+       ee_data = tp->eeprom;
+       memset(ee_data, 0, sizeof(tp->eeprom));
+       sum = 0;
+       if (chip_idx == LC82C168) {
+               for (i = 0; i < 3; i++) {
+                       int value, boguscnt = 100000;
+                       iowrite32(0x600 | i, ioaddr + 0x98);
+                       do {
+                               value = ioread32(ioaddr + CSR9);
+                       } while (value < 0  && --boguscnt > 0);
+                       put_unaligned_le16(value, ((__le16 *)dev->dev_addr) + i);
+                       sum += value & 0xffff;
+               }
+       } else if (chip_idx == COMET) {
+               /* No need to read the EEPROM. */
+               put_unaligned_le32(ioread32(ioaddr + 0xA4), dev->dev_addr);
+               put_unaligned_le16(ioread32(ioaddr + 0xA8), dev->dev_addr + 4);
+               for (i = 0; i < 6; i ++)
+                       sum += dev->dev_addr[i];
+       } else {
+               /* A serial EEPROM interface, we read now and sort it out later. */
+               int sa_offset = 0;
+               int ee_addr_size = tulip_read_eeprom(dev, 0xff, 8) & 0x40000 ? 8 : 6;
+               int ee_max_addr = ((1 << ee_addr_size) - 1) * sizeof(u16);
+               if (ee_max_addr > sizeof(tp->eeprom))
+                       ee_max_addr = sizeof(tp->eeprom);
+               for (i = 0; i < ee_max_addr ; i += sizeof(u16)) {
+                       u16 data = tulip_read_eeprom(dev, i/2, ee_addr_size);
+                       ee_data[i] = data & 0xff;
+                       ee_data[i + 1] = data >> 8;
+               }
+               /* DEC now has a specification (see Notes) but early board makers
+                  just put the address in the first EEPROM locations. */
+               /* This does  memcmp(ee_data, ee_data+16, 8) */
+               for (i = 0; i < 8; i ++)
+                       if (ee_data[i] != ee_data[16+i])
+                               sa_offset = 20;
+               if (chip_idx == CONEXANT) {
+                       /* Check that the tuple type and length is correct. */
+                       if (ee_data[0x198] == 0x04  &&  ee_data[0x199] == 6)
+                               sa_offset = 0x19A;
+               } else if (ee_data[0] == 0xff  &&  ee_data[1] == 0xff &&
+                                  ee_data[2] == 0) {
+                       sa_offset = 2;          /* Grrr, damn Matrox boards. */
+                       multiport_cnt = 4;
+               }
+ #ifdef CONFIG_MIPS_COBALT
+                if ((pdev->bus->number == 0) &&
+                    ((PCI_SLOT(pdev->devfn) == 7) ||
+                     (PCI_SLOT(pdev->devfn) == 12))) {
+                        /* Cobalt MAC address in first EEPROM locations. */
+                        sa_offset = 0;
+                      /* Ensure our media table fixup get's applied */
+                      memcpy(ee_data + 16, ee_data, 8);
+                }
+ #endif
+ #ifdef CONFIG_GSC
+               /* Check to see if we have a broken srom */
+               if (ee_data[0] == 0x61 && ee_data[1] == 0x10) {
+                       /* pci_vendor_id and subsystem_id are swapped */
+                       ee_data[0] = ee_data[2];
+                       ee_data[1] = ee_data[3];
+                       ee_data[2] = 0x61;
+                       ee_data[3] = 0x10;
+                       /* HSC-PCI boards need to be byte-swaped and shifted
+                        * up 1 word.  This shift needs to happen at the end
+                        * of the MAC first because of the 2 byte overlap.
+                        */
+                       for (i = 4; i >= 0; i -= 2) {
+                               ee_data[17 + i + 3] = ee_data[17 + i];
+                               ee_data[16 + i + 5] = ee_data[16 + i];
+                       }
+               }
+ #endif
+               for (i = 0; i < 6; i ++) {
+                       dev->dev_addr[i] = ee_data[i + sa_offset];
+                       sum += ee_data[i + sa_offset];
+               }
+       }
+       /* Lite-On boards have the address byte-swapped. */
+       if ((dev->dev_addr[0] == 0xA0 ||
+            dev->dev_addr[0] == 0xC0 ||
+            dev->dev_addr[0] == 0x02) &&
+           dev->dev_addr[1] == 0x00)
+               for (i = 0; i < 6; i+=2) {
+                       char tmp = dev->dev_addr[i];
+                       dev->dev_addr[i] = dev->dev_addr[i+1];
+                       dev->dev_addr[i+1] = tmp;
+               }
+       /* On the Zynx 315 Etherarray and other multiport boards only the
+          first Tulip has an EEPROM.
+          On Sparc systems the mac address is held in the OBP property
+          "local-mac-address".
+          The addresses of the subsequent ports are derived from the first.
+          Many PCI BIOSes also incorrectly report the IRQ line, so we correct
+          that here as well. */
+       if (sum == 0  || sum == 6*0xff) {
+ #if defined(CONFIG_SPARC)
+               struct device_node *dp = pci_device_to_OF_node(pdev);
+               const unsigned char *addr;
+               int len;
+ #endif
+               eeprom_missing = 1;
+               for (i = 0; i < 5; i++)
+                       dev->dev_addr[i] = last_phys_addr[i];
+               dev->dev_addr[i] = last_phys_addr[i] + 1;
+ #if defined(CONFIG_SPARC)
+               addr = of_get_property(dp, "local-mac-address", &len);
+               if (addr && len == 6)
+                       memcpy(dev->dev_addr, addr, 6);
+ #endif
+ #if defined(__i386__) || defined(__x86_64__)  /* Patch up x86 BIOS bug. */
+               if (last_irq)
+                       irq = last_irq;
+ #endif
+       }
+       for (i = 0; i < 6; i++)
+               last_phys_addr[i] = dev->dev_addr[i];
+       last_irq = irq;
+       dev->irq = irq;
+       /* The lower four bits are the media type. */
+       if (board_idx >= 0  &&  board_idx < MAX_UNITS) {
+               if (options[board_idx] & MEDIA_MASK)
+                       tp->default_port = options[board_idx] & MEDIA_MASK;
+               if ((options[board_idx] & FullDuplex) || full_duplex[board_idx] > 0)
+                       tp->full_duplex = 1;
+               if (mtu[board_idx] > 0)
+                       dev->mtu = mtu[board_idx];
+       }
+       if (dev->mem_start & MEDIA_MASK)
+               tp->default_port = dev->mem_start & MEDIA_MASK;
+       if (tp->default_port) {
+               pr_info(DRV_NAME "%d: Transceiver selection forced to %s\n",
+                       board_idx, medianame[tp->default_port & MEDIA_MASK]);
+               tp->medialock = 1;
+               if (tulip_media_cap[tp->default_port] & MediaAlwaysFD)
+                       tp->full_duplex = 1;
+       }
+       if (tp->full_duplex)
+               tp->full_duplex_lock = 1;
+       if (tulip_media_cap[tp->default_port] & MediaIsMII) {
+               static const u16 media2advert[] = {
+                       0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200
+               };
+               tp->mii_advertise = media2advert[tp->default_port - 9];
+               tp->mii_advertise |= (tp->flags & HAS_8023X); /* Matching bits! */
+       }
+       if (tp->flags & HAS_MEDIA_TABLE) {
+               sprintf(dev->name, DRV_NAME "%d", board_idx);   /* hack */
+               tulip_parse_eeprom(dev);
+               strcpy(dev->name, "eth%d");                     /* un-hack */
+       }
+       if ((tp->flags & ALWAYS_CHECK_MII) ||
+               (tp->mtable  &&  tp->mtable->has_mii) ||
+               ( ! tp->mtable  &&  (tp->flags & HAS_MII))) {
+               if (tp->mtable  &&  tp->mtable->has_mii) {
+                       for (i = 0; i < tp->mtable->leafcount; i++)
+                               if (tp->mtable->mleaf[i].media == 11) {
+                                       tp->cur_index = i;
+                                       tp->saved_if_port = dev->if_port;
+                                       tulip_select_media(dev, 2);
+                                       dev->if_port = tp->saved_if_port;
+                                       break;
+                               }
+               }
+               /* Find the connected MII xcvrs.
+                  Doing this in open() would allow detecting external xcvrs
+                  later, but takes much time. */
+               tulip_find_mii (dev, board_idx);
+       }
+       /* The Tulip-specific entries in the device structure. */
+       dev->netdev_ops = &tulip_netdev_ops;
+       dev->watchdog_timeo = TX_TIMEOUT;
+ #ifdef CONFIG_TULIP_NAPI
+       netif_napi_add(dev, &tp->napi, tulip_poll, 16);
+ #endif
+       SET_ETHTOOL_OPS(dev, &ops);
+       if (register_netdev(dev))
+               goto err_out_free_ring;
+       pci_set_drvdata(pdev, dev);
+       dev_info(&dev->dev,
+ #ifdef CONFIG_TULIP_MMIO
+                "%s rev %d at MMIO %#llx,%s %pM, IRQ %d\n",
+ #else
+                "%s rev %d at Port %#llx,%s %pM, IRQ %d\n",
+ #endif
+                chip_name, pdev->revision,
+                (unsigned long long)pci_resource_start(pdev, TULIP_BAR),
+                eeprom_missing ? " EEPROM not present," : "",
+                dev->dev_addr, irq);
+         if (tp->chip_id == PNIC2)
+               tp->link_change = pnic2_lnk_change;
+       else if (tp->flags & HAS_NWAY)
+               tp->link_change = t21142_lnk_change;
+       else if (tp->flags & HAS_PNICNWAY)
+               tp->link_change = pnic_lnk_change;
+       /* Reset the xcvr interface and turn on heartbeat. */
+       switch (chip_idx) {
+       case DC21140:
+       case DM910X:
+       default:
+               if (tp->mtable)
+                       iowrite32(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
+               break;
+       case DC21142:
+               if (tp->mii_cnt  ||  tulip_media_cap[dev->if_port] & MediaIsMII) {
+                       iowrite32(csr6_mask_defstate, ioaddr + CSR6);
+                       iowrite32(0x0000, ioaddr + CSR13);
+                       iowrite32(0x0000, ioaddr + CSR14);
+                       iowrite32(csr6_mask_hdcap, ioaddr + CSR6);
+               } else
+                       t21142_start_nway(dev);
+               break;
+       case PNIC2:
+               /* just do a reset for sanity sake */
+               iowrite32(0x0000, ioaddr + CSR13);
+               iowrite32(0x0000, ioaddr + CSR14);
+               break;
+       case LC82C168:
+               if ( ! tp->mii_cnt) {
+                       tp->nway = 1;
+                       tp->nwayset = 0;
+                       iowrite32(csr6_ttm | csr6_ca, ioaddr + CSR6);
+                       iowrite32(0x30, ioaddr + CSR12);
+                       iowrite32(0x0001F078, ioaddr + CSR6);
+                       iowrite32(0x0201F078, ioaddr + CSR6); /* Turn on autonegotiation. */
+               }
+               break;
+       case MX98713:
+       case COMPEX9881:
+               iowrite32(0x00000000, ioaddr + CSR6);
+               iowrite32(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
+               iowrite32(0x00000001, ioaddr + CSR13);
+               break;
+       case MX98715:
+       case MX98725:
+               iowrite32(0x01a80000, ioaddr + CSR6);
+               iowrite32(0xFFFFFFFF, ioaddr + CSR14);
+               iowrite32(0x00001000, ioaddr + CSR12);
+               break;
+       case COMET:
+               /* No initialization necessary. */
+               break;
+       }
+       /* put the chip in snooze mode until opened */
+       tulip_set_power_state (tp, 0, 1);
+       return 0;
+ err_out_free_ring:
+       pci_free_consistent (pdev,
+                            sizeof (struct tulip_rx_desc) * RX_RING_SIZE +
+                            sizeof (struct tulip_tx_desc) * TX_RING_SIZE,
+                            tp->rx_ring, tp->rx_ring_dma);
+ err_out_mtable:
+       kfree (tp->mtable);
+       pci_iounmap(pdev, ioaddr);
+ err_out_free_res:
+       pci_release_regions (pdev);
+ err_out_free_netdev:
+       free_netdev (dev);
+       return -ENODEV;
+ }
+ /* set the registers according to the given wolopts */
+ static void tulip_set_wolopts (struct pci_dev *pdev, u32 wolopts)
+ {
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct tulip_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->base_addr;
+       if (tp->flags & COMET_PM) {
+         
+               unsigned int tmp;
+                       
+               tmp = ioread32(ioaddr + CSR18);
+               tmp &= ~(comet_csr18_pmes_sticky | comet_csr18_apm_mode | comet_csr18_d3a);
+               tmp |= comet_csr18_pm_mode;
+               iowrite32(tmp, ioaddr + CSR18);
+                       
+               /* Set the Wake-up Control/Status Register to the given WOL options*/
+               tmp = ioread32(ioaddr + CSR13);
+               tmp &= ~(comet_csr13_linkoffe | comet_csr13_linkone | comet_csr13_wfre | comet_csr13_lsce | comet_csr13_mpre);
+               if (wolopts & WAKE_MAGIC)
+                       tmp |= comet_csr13_mpre;
+               if (wolopts & WAKE_PHY)
+                       tmp |= comet_csr13_linkoffe | comet_csr13_linkone | comet_csr13_lsce;
+               /* Clear the event flags */
+               tmp |= comet_csr13_wfr | comet_csr13_mpr | comet_csr13_lsc;
+               iowrite32(tmp, ioaddr + CSR13);
+       }
+ }
+ #ifdef CONFIG_PM
+ static int tulip_suspend (struct pci_dev *pdev, pm_message_t state)
+ {
+       pci_power_t pstate;
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct tulip_private *tp = netdev_priv(dev);
+       if (!dev)
+               return -EINVAL;
+       if (!netif_running(dev))
+               goto save_state;
+       tulip_down(dev);
+       netif_device_detach(dev);
+       free_irq(dev->irq, dev);
+ save_state:
+       pci_save_state(pdev);
+       pci_disable_device(pdev);
+       pstate = pci_choose_state(pdev, state);
+       if (state.event == PM_EVENT_SUSPEND && pstate != PCI_D0) {
+               int rc;
+               tulip_set_wolopts(pdev, tp->wolinfo.wolopts);
+               rc = pci_enable_wake(pdev, pstate, tp->wolinfo.wolopts);
+               if (rc)
+                       pr_err("pci_enable_wake failed (%d)\n", rc);
+       }
+       pci_set_power_state(pdev, pstate);
+       return 0;
+ }
+ static int tulip_resume(struct pci_dev *pdev)
+ {
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct tulip_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->base_addr;
+       int retval;
+       unsigned int tmp;
+       if (!dev)
+               return -EINVAL;
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       if (!netif_running(dev))
+               return 0;
+       if ((retval = pci_enable_device(pdev))) {
+               pr_err("pci_enable_device failed in resume\n");
+               return retval;
+       }
+       if ((retval = request_irq(dev->irq, tulip_interrupt, IRQF_SHARED, dev->name, dev))) {
+               pr_err("request_irq failed in resume\n");
+               return retval;
+       }
+       if (tp->flags & COMET_PM) {
+               pci_enable_wake(pdev, PCI_D3hot, 0);
+               pci_enable_wake(pdev, PCI_D3cold, 0);
+               /* Clear the PMES flag */
+               tmp = ioread32(ioaddr + CSR20);
+               tmp |= comet_csr20_pmes;
+               iowrite32(tmp, ioaddr + CSR20);
+               /* Disable all wake-up events */
+               tulip_set_wolopts(pdev, 0);
+       }
+       netif_device_attach(dev);
+       if (netif_running(dev))
+               tulip_up(dev);
+       return 0;
+ }
+ #endif /* CONFIG_PM */
+ static void __devexit tulip_remove_one (struct pci_dev *pdev)
+ {
+       struct net_device *dev = pci_get_drvdata (pdev);
+       struct tulip_private *tp;
+       if (!dev)
+               return;
+       tp = netdev_priv(dev);
+       unregister_netdev(dev);
+       pci_free_consistent (pdev,
+                            sizeof (struct tulip_rx_desc) * RX_RING_SIZE +
+                            sizeof (struct tulip_tx_desc) * TX_RING_SIZE,
+                            tp->rx_ring, tp->rx_ring_dma);
+       kfree (tp->mtable);
+       pci_iounmap(pdev, tp->base_addr);
+       free_netdev (dev);
+       pci_release_regions (pdev);
+       pci_set_drvdata (pdev, NULL);
+       /* pci_power_off (pdev, -1); */
+ }
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+ /*
+  * Polling 'interrupt' - used by things like netconsole to send skbs
+  * without having to re-enable interrupts. It's not called while
+  * the interrupt routine is executing.
+  */
+ static void poll_tulip (struct net_device *dev)
+ {
+       /* disable_irq here is not very nice, but with the lockless
+          interrupt handler we have no other choice. */
+       disable_irq(dev->irq);
+       tulip_interrupt (dev->irq, dev);
+       enable_irq(dev->irq);
+ }
+ #endif
+ static struct pci_driver tulip_driver = {
+       .name           = DRV_NAME,
+       .id_table       = tulip_pci_tbl,
+       .probe          = tulip_init_one,
+       .remove         = __devexit_p(tulip_remove_one),
+ #ifdef CONFIG_PM
+       .suspend        = tulip_suspend,
+       .resume         = tulip_resume,
+ #endif /* CONFIG_PM */
+ };
+ static int __init tulip_init (void)
+ {
+ #ifdef MODULE
+       pr_info("%s", version);
+ #endif
+       /* copy module parms into globals */
+       tulip_rx_copybreak = rx_copybreak;
+       tulip_max_interrupt_work = max_interrupt_work;
+       /* probe for and init boards */
+       return pci_register_driver(&tulip_driver);
+ }
+ static void __exit tulip_cleanup (void)
+ {
+       pci_unregister_driver (&tulip_driver);
+ }
+ module_init(tulip_init);
+ module_exit(tulip_cleanup);
index 0000000,7b083c4..cbd026f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,5158 +1,5158 @@@
 -      /* Force any delayed status interrrupt and NAPI */
+ /*
+  * New driver for Marvell Yukon 2 chipset.
+  * Based on earlier sk98lin, and skge driver.
+  *
+  * This driver intentionally does not support all the features
+  * of the original driver such as link fail-over and link management because
+  * those should be done at higher levels.
+  *
+  * Copyright (C) 2005 Stephen Hemminger <shemminger@osdl.org>
+  *
+  * 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.
+  *
+  * 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.
+  */
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+ #include <linux/crc32.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/netdevice.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/etherdevice.h>
+ #include <linux/ethtool.h>
+ #include <linux/pci.h>
+ #include <linux/interrupt.h>
+ #include <linux/ip.h>
+ #include <linux/slab.h>
+ #include <net/ip.h>
+ #include <linux/tcp.h>
+ #include <linux/in.h>
+ #include <linux/delay.h>
+ #include <linux/workqueue.h>
+ #include <linux/if_vlan.h>
+ #include <linux/prefetch.h>
+ #include <linux/debugfs.h>
+ #include <linux/mii.h>
+ #include <asm/irq.h>
+ #include "sky2.h"
+ #define DRV_NAME              "sky2"
+ #define DRV_VERSION           "1.29"
+ /*
+  * The Yukon II chipset takes 64 bit command blocks (called list elements)
+  * that are organized into three (receive, transmit, status) different rings
+  * similar to Tigon3.
+  */
+ #define RX_LE_SIZE            1024
+ #define RX_LE_BYTES           (RX_LE_SIZE*sizeof(struct sky2_rx_le))
+ #define RX_MAX_PENDING                (RX_LE_SIZE/6 - 2)
+ #define RX_DEF_PENDING                RX_MAX_PENDING
+ /* This is the worst case number of transmit list elements for a single skb:
+    VLAN:GSO + CKSUM + Data + skb_frags * DMA */
+ #define MAX_SKB_TX_LE (2 + (sizeof(dma_addr_t)/sizeof(u32))*(MAX_SKB_FRAGS+1))
+ #define TX_MIN_PENDING                (MAX_SKB_TX_LE+1)
+ #define TX_MAX_PENDING                1024
+ #define TX_DEF_PENDING                127
+ #define TX_WATCHDOG           (5 * HZ)
+ #define NAPI_WEIGHT           64
+ #define PHY_RETRIES           1000
+ #define SKY2_EEPROM_MAGIC     0x9955aabb
+ #define RING_NEXT(x, s)       (((x)+1) & ((s)-1))
+ static const u32 default_msg =
+     NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK
+     | NETIF_MSG_TIMER | NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR
+     | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN;
+ static int debug = -1;                /* defaults above */
+ module_param(debug, int, 0);
+ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+ static int copybreak __read_mostly = 128;
+ module_param(copybreak, int, 0);
+ MODULE_PARM_DESC(copybreak, "Receive copy threshold");
+ static int disable_msi = 0;
+ module_param(disable_msi, int, 0);
+ MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");
+ static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = {
+       { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, /* SK-9Sxx */
+       { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, /* SK-9Exx */
+       { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E01) }, /* SK-9E21M */
+       { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b00) },    /* DGE-560T */
+       { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4001) },    /* DGE-550SX */
+       { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4B02) },    /* DGE-560SX */
+       { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4B03) },    /* DGE-550T */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4340) }, /* 88E8021 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4341) }, /* 88E8022 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4342) }, /* 88E8061 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4343) }, /* 88E8062 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4344) }, /* 88E8021 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4345) }, /* 88E8022 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4346) }, /* 88E8061 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4347) }, /* 88E8062 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4350) }, /* 88E8035 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4351) }, /* 88E8036 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4352) }, /* 88E8038 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4353) }, /* 88E8039 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4354) }, /* 88E8040 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4355) }, /* 88E8040T */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4356) }, /* 88EC033 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4357) }, /* 88E8042 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x435A) }, /* 88E8048 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4360) }, /* 88E8052 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4361) }, /* 88E8050 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4362) }, /* 88E8053 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4363) }, /* 88E8055 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4364) }, /* 88E8056 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4365) }, /* 88E8070 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4366) }, /* 88EC036 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4367) }, /* 88EC032 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4368) }, /* 88EC034 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4369) }, /* 88EC042 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436A) }, /* 88E8058 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436B) }, /* 88E8071 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436C) }, /* 88E8072 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436D) }, /* 88E8055 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4370) }, /* 88E8075 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4380) }, /* 88E8057 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4381) }, /* 88E8059 */
+       { 0 }
+ };
+ MODULE_DEVICE_TABLE(pci, sky2_id_table);
+ /* Avoid conditionals by using array */
+ static const unsigned txqaddr[] = { Q_XA1, Q_XA2 };
+ static const unsigned rxqaddr[] = { Q_R1, Q_R2 };
+ static const u32 portirq_msk[] = { Y2_IS_PORT_1, Y2_IS_PORT_2 };
+ static void sky2_set_multicast(struct net_device *dev);
+ static irqreturn_t sky2_intr(int irq, void *dev_id);
+ /* Access to PHY via serial interconnect */
+ static int gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val)
+ {
+       int i;
+       gma_write16(hw, port, GM_SMI_DATA, val);
+       gma_write16(hw, port, GM_SMI_CTRL,
+                   GM_SMI_CT_PHY_AD(PHY_ADDR_MARV) | GM_SMI_CT_REG_AD(reg));
+       for (i = 0; i < PHY_RETRIES; i++) {
+               u16 ctrl = gma_read16(hw, port, GM_SMI_CTRL);
+               if (ctrl == 0xffff)
+                       goto io_error;
+               if (!(ctrl & GM_SMI_CT_BUSY))
+                       return 0;
+               udelay(10);
+       }
+       dev_warn(&hw->pdev->dev, "%s: phy write timeout\n", hw->dev[port]->name);
+       return -ETIMEDOUT;
+ io_error:
+       dev_err(&hw->pdev->dev, "%s: phy I/O error\n", hw->dev[port]->name);
+       return -EIO;
+ }
+ static int __gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg, u16 *val)
+ {
+       int i;
+       gma_write16(hw, port, GM_SMI_CTRL, GM_SMI_CT_PHY_AD(PHY_ADDR_MARV)
+                   | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD);
+       for (i = 0; i < PHY_RETRIES; i++) {
+               u16 ctrl = gma_read16(hw, port, GM_SMI_CTRL);
+               if (ctrl == 0xffff)
+                       goto io_error;
+               if (ctrl & GM_SMI_CT_RD_VAL) {
+                       *val = gma_read16(hw, port, GM_SMI_DATA);
+                       return 0;
+               }
+               udelay(10);
+       }
+       dev_warn(&hw->pdev->dev, "%s: phy read timeout\n", hw->dev[port]->name);
+       return -ETIMEDOUT;
+ io_error:
+       dev_err(&hw->pdev->dev, "%s: phy I/O error\n", hw->dev[port]->name);
+       return -EIO;
+ }
+ static inline u16 gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg)
+ {
+       u16 v;
+       __gm_phy_read(hw, port, reg, &v);
+       return v;
+ }
+ static void sky2_power_on(struct sky2_hw *hw)
+ {
+       /* switch power to VCC (WA for VAUX problem) */
+       sky2_write8(hw, B0_POWER_CTRL,
+                   PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON);
+       /* disable Core Clock Division, */
+       sky2_write32(hw, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS);
+       if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > CHIP_REV_YU_XL_A1)
+               /* enable bits are inverted */
+               sky2_write8(hw, B2_Y2_CLK_GATE,
+                           Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS |
+                           Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS |
+                           Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS);
+       else
+               sky2_write8(hw, B2_Y2_CLK_GATE, 0);
+       if (hw->flags & SKY2_HW_ADV_POWER_CTL) {
+               u32 reg;
+               sky2_pci_write32(hw, PCI_DEV_REG3, 0);
+               reg = sky2_pci_read32(hw, PCI_DEV_REG4);
+               /* set all bits to 0 except bits 15..12 and 8 */
+               reg &= P_ASPM_CONTROL_MSK;
+               sky2_pci_write32(hw, PCI_DEV_REG4, reg);
+               reg = sky2_pci_read32(hw, PCI_DEV_REG5);
+               /* set all bits to 0 except bits 28 & 27 */
+               reg &= P_CTL_TIM_VMAIN_AV_MSK;
+               sky2_pci_write32(hw, PCI_DEV_REG5, reg);
+               sky2_pci_write32(hw, PCI_CFG_REG_1, 0);
+               sky2_write16(hw, B0_CTST, Y2_HW_WOL_ON);
+               /* Enable workaround for dev 4.107 on Yukon-Ultra & Extreme */
+               reg = sky2_read32(hw, B2_GP_IO);
+               reg |= GLB_GPIO_STAT_RACE_DIS;
+               sky2_write32(hw, B2_GP_IO, reg);
+               sky2_read32(hw, B2_GP_IO);
+       }
+       /* Turn on "driver loaded" LED */
+       sky2_write16(hw, B0_CTST, Y2_LED_STAT_ON);
+ }
+ static void sky2_power_aux(struct sky2_hw *hw)
+ {
+       if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > CHIP_REV_YU_XL_A1)
+               sky2_write8(hw, B2_Y2_CLK_GATE, 0);
+       else
+               /* enable bits are inverted */
+               sky2_write8(hw, B2_Y2_CLK_GATE,
+                           Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS |
+                           Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS |
+                           Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS);
+       /* switch power to VAUX if supported and PME from D3cold */
+       if ( (sky2_read32(hw, B0_CTST) & Y2_VAUX_AVAIL) &&
+            pci_pme_capable(hw->pdev, PCI_D3cold))
+               sky2_write8(hw, B0_POWER_CTRL,
+                           (PC_VAUX_ENA | PC_VCC_ENA |
+                            PC_VAUX_ON | PC_VCC_OFF));
+       /* turn off "driver loaded LED" */
+       sky2_write16(hw, B0_CTST, Y2_LED_STAT_OFF);
+ }
+ static void sky2_gmac_reset(struct sky2_hw *hw, unsigned port)
+ {
+       u16 reg;
+       /* disable all GMAC IRQ's */
+       sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0);
+       gma_write16(hw, port, GM_MC_ADDR_H1, 0);        /* clear MC hash */
+       gma_write16(hw, port, GM_MC_ADDR_H2, 0);
+       gma_write16(hw, port, GM_MC_ADDR_H3, 0);
+       gma_write16(hw, port, GM_MC_ADDR_H4, 0);
+       reg = gma_read16(hw, port, GM_RX_CTRL);
+       reg |= GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA;
+       gma_write16(hw, port, GM_RX_CTRL, reg);
+ }
+ /* flow control to advertise bits */
+ static const u16 copper_fc_adv[] = {
+       [FC_NONE]       = 0,
+       [FC_TX]         = PHY_M_AN_ASP,
+       [FC_RX]         = PHY_M_AN_PC,
+       [FC_BOTH]       = PHY_M_AN_PC | PHY_M_AN_ASP,
+ };
+ /* flow control to advertise bits when using 1000BaseX */
+ static const u16 fiber_fc_adv[] = {
+       [FC_NONE] = PHY_M_P_NO_PAUSE_X,
+       [FC_TX]   = PHY_M_P_ASYM_MD_X,
+       [FC_RX]   = PHY_M_P_SYM_MD_X,
+       [FC_BOTH] = PHY_M_P_BOTH_MD_X,
+ };
+ /* flow control to GMA disable bits */
+ static const u16 gm_fc_disable[] = {
+       [FC_NONE] = GM_GPCR_FC_RX_DIS | GM_GPCR_FC_TX_DIS,
+       [FC_TX]   = GM_GPCR_FC_RX_DIS,
+       [FC_RX]   = GM_GPCR_FC_TX_DIS,
+       [FC_BOTH] = 0,
+ };
+ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
+ {
+       struct sky2_port *sky2 = netdev_priv(hw->dev[port]);
+       u16 ctrl, ct1000, adv, pg, ledctrl, ledover, reg;
+       if ( (sky2->flags & SKY2_FLAG_AUTO_SPEED) &&
+           !(hw->flags & SKY2_HW_NEWER_PHY)) {
+               u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
+               ectrl &= ~(PHY_M_EC_M_DSC_MSK | PHY_M_EC_S_DSC_MSK |
+                          PHY_M_EC_MAC_S_MSK);
+               ectrl |= PHY_M_EC_MAC_S(MAC_TX_CLK_25_MHZ);
+               /* on PHY 88E1040 Rev.D0 (and newer) downshift control changed */
+               if (hw->chip_id == CHIP_ID_YUKON_EC)
+                       /* set downshift counter to 3x and enable downshift */
+                       ectrl |= PHY_M_EC_DSC_2(2) | PHY_M_EC_DOWN_S_ENA;
+               else
+                       /* set master & slave downshift counter to 1x */
+                       ectrl |= PHY_M_EC_M_DSC(0) | PHY_M_EC_S_DSC(1);
+               gm_phy_write(hw, port, PHY_MARV_EXT_CTRL, ectrl);
+       }
+       ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+       if (sky2_is_copper(hw)) {
+               if (!(hw->flags & SKY2_HW_GIGABIT)) {
+                       /* enable automatic crossover */
+                       ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO) >> 1;
+                       if (hw->chip_id == CHIP_ID_YUKON_FE_P &&
+                           hw->chip_rev == CHIP_REV_YU_FE2_A0) {
+                               u16 spec;
+                               /* Enable Class A driver for FE+ A0 */
+                               spec = gm_phy_read(hw, port, PHY_MARV_FE_SPEC_2);
+                               spec |= PHY_M_FESC_SEL_CL_A;
+                               gm_phy_write(hw, port, PHY_MARV_FE_SPEC_2, spec);
+                       }
+               } else {
+                       if (hw->chip_id >= CHIP_ID_YUKON_OPT) {
+                               u16 ctrl2 = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL_2);
+                               /* enable PHY Reverse Auto-Negotiation */
+                               ctrl2 |= 1u << 13;
+                               /* Write PHY changes (SW-reset must follow) */
+                               gm_phy_write(hw, port, PHY_MARV_EXT_CTRL_2, ctrl2);
+                       }
+                       /* disable energy detect */
+                       ctrl &= ~PHY_M_PC_EN_DET_MSK;
+                       /* enable automatic crossover */
+                       ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO);
+                       /* downshift on PHY 88E1112 and 88E1149 is changed */
+                       if ( (sky2->flags & SKY2_FLAG_AUTO_SPEED) &&
+                            (hw->flags & SKY2_HW_NEWER_PHY)) {
+                               /* set downshift counter to 3x and enable downshift */
+                               ctrl &= ~PHY_M_PC_DSC_MSK;
+                               ctrl |= PHY_M_PC_DSC(2) | PHY_M_PC_DOWN_S_ENA;
+                       }
+               }
+       } else {
+               /* workaround for deviation #4.88 (CRC errors) */
+               /* disable Automatic Crossover */
+               ctrl &= ~PHY_M_PC_MDIX_MSK;
+       }
+       gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+       /* special setup for PHY 88E1112 Fiber */
+       if (hw->chip_id == CHIP_ID_YUKON_XL && (hw->flags & SKY2_HW_FIBRE_PHY)) {
+               pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
+               /* Fiber: select 1000BASE-X only mode MAC Specific Ctrl Reg. */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2);
+               ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+               ctrl &= ~PHY_M_MAC_MD_MSK;
+               ctrl |= PHY_M_MAC_MODE_SEL(PHY_M_MAC_MD_1000BX);
+               gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+               if (hw->pmd_type  == 'P') {
+                       /* select page 1 to access Fiber registers */
+                       gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 1);
+                       /* for SFP-module set SIGDET polarity to low */
+                       ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+                       ctrl |= PHY_M_FIB_SIGD_POL;
+                       gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+               }
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
+       }
+       ctrl = PHY_CT_RESET;
+       ct1000 = 0;
+       adv = PHY_AN_CSMA;
+       reg = 0;
+       if (sky2->flags & SKY2_FLAG_AUTO_SPEED) {
+               if (sky2_is_copper(hw)) {
+                       if (sky2->advertising & ADVERTISED_1000baseT_Full)
+                               ct1000 |= PHY_M_1000C_AFD;
+                       if (sky2->advertising & ADVERTISED_1000baseT_Half)
+                               ct1000 |= PHY_M_1000C_AHD;
+                       if (sky2->advertising & ADVERTISED_100baseT_Full)
+                               adv |= PHY_M_AN_100_FD;
+                       if (sky2->advertising & ADVERTISED_100baseT_Half)
+                               adv |= PHY_M_AN_100_HD;
+                       if (sky2->advertising & ADVERTISED_10baseT_Full)
+                               adv |= PHY_M_AN_10_FD;
+                       if (sky2->advertising & ADVERTISED_10baseT_Half)
+                               adv |= PHY_M_AN_10_HD;
+               } else {        /* special defines for FIBER (88E1040S only) */
+                       if (sky2->advertising & ADVERTISED_1000baseT_Full)
+                               adv |= PHY_M_AN_1000X_AFD;
+                       if (sky2->advertising & ADVERTISED_1000baseT_Half)
+                               adv |= PHY_M_AN_1000X_AHD;
+               }
+               /* Restart Auto-negotiation */
+               ctrl |= PHY_CT_ANE | PHY_CT_RE_CFG;
+       } else {
+               /* forced speed/duplex settings */
+               ct1000 = PHY_M_1000C_MSE;
+               /* Disable auto update for duplex flow control and duplex */
+               reg |= GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_SPD_DIS;
+               switch (sky2->speed) {
+               case SPEED_1000:
+                       ctrl |= PHY_CT_SP1000;
+                       reg |= GM_GPCR_SPEED_1000;
+                       break;
+               case SPEED_100:
+                       ctrl |= PHY_CT_SP100;
+                       reg |= GM_GPCR_SPEED_100;
+                       break;
+               }
+               if (sky2->duplex == DUPLEX_FULL) {
+                       reg |= GM_GPCR_DUP_FULL;
+                       ctrl |= PHY_CT_DUP_MD;
+               } else if (sky2->speed < SPEED_1000)
+                       sky2->flow_mode = FC_NONE;
+       }
+       if (sky2->flags & SKY2_FLAG_AUTO_PAUSE) {
+               if (sky2_is_copper(hw))
+                       adv |= copper_fc_adv[sky2->flow_mode];
+               else
+                       adv |= fiber_fc_adv[sky2->flow_mode];
+       } else {
+               reg |= GM_GPCR_AU_FCT_DIS;
+               reg |= gm_fc_disable[sky2->flow_mode];
+               /* Forward pause packets to GMAC? */
+               if (sky2->flow_mode & FC_RX)
+                       sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON);
+               else
+                       sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
+       }
+       gma_write16(hw, port, GM_GP_CTRL, reg);
+       if (hw->flags & SKY2_HW_GIGABIT)
+               gm_phy_write(hw, port, PHY_MARV_1000T_CTRL, ct1000);
+       gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, adv);
+       gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
+       /* Setup Phy LED's */
+       ledctrl = PHY_M_LED_PULS_DUR(PULS_170MS);
+       ledover = 0;
+       switch (hw->chip_id) {
+       case CHIP_ID_YUKON_FE:
+               /* on 88E3082 these bits are at 11..9 (shifted left) */
+               ledctrl |= PHY_M_LED_BLINK_RT(BLINK_84MS) << 1;
+               ctrl = gm_phy_read(hw, port, PHY_MARV_FE_LED_PAR);
+               /* delete ACT LED control bits */
+               ctrl &= ~PHY_M_FELP_LED1_MSK;
+               /* change ACT LED control to blink mode */
+               ctrl |= PHY_M_FELP_LED1_CTRL(LED_PAR_CTRL_ACT_BL);
+               gm_phy_write(hw, port, PHY_MARV_FE_LED_PAR, ctrl);
+               break;
+       case CHIP_ID_YUKON_FE_P:
+               /* Enable Link Partner Next Page */
+               ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+               ctrl |= PHY_M_PC_ENA_LIP_NP;
+               /* disable Energy Detect and enable scrambler */
+               ctrl &= ~(PHY_M_PC_ENA_ENE_DT | PHY_M_PC_DIS_SCRAMB);
+               gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+               /* set LED2 -> ACT, LED1 -> LINK, LED0 -> SPEED */
+               ctrl = PHY_M_FELP_LED2_CTRL(LED_PAR_CTRL_ACT_BL) |
+                       PHY_M_FELP_LED1_CTRL(LED_PAR_CTRL_LINK) |
+                       PHY_M_FELP_LED0_CTRL(LED_PAR_CTRL_SPEED);
+               gm_phy_write(hw, port, PHY_MARV_FE_LED_PAR, ctrl);
+               break;
+       case CHIP_ID_YUKON_XL:
+               pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
+               /* select page 3 to access LED control register */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
+               /* set LED Function Control register */
+               gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+                            (PHY_M_LEDC_LOS_CTRL(1) |  /* LINK/ACT */
+                             PHY_M_LEDC_INIT_CTRL(7) | /* 10 Mbps */
+                             PHY_M_LEDC_STA1_CTRL(7) | /* 100 Mbps */
+                             PHY_M_LEDC_STA0_CTRL(7)));        /* 1000 Mbps */
+               /* set Polarity Control register */
+               gm_phy_write(hw, port, PHY_MARV_PHY_STAT,
+                            (PHY_M_POLC_LS1_P_MIX(4) |
+                             PHY_M_POLC_IS0_P_MIX(4) |
+                             PHY_M_POLC_LOS_CTRL(2) |
+                             PHY_M_POLC_INIT_CTRL(2) |
+                             PHY_M_POLC_STA1_CTRL(2) |
+                             PHY_M_POLC_STA0_CTRL(2)));
+               /* restore page register */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
+               break;
+       case CHIP_ID_YUKON_EC_U:
+       case CHIP_ID_YUKON_EX:
+       case CHIP_ID_YUKON_SUPR:
+               pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
+               /* select page 3 to access LED control register */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
+               /* set LED Function Control register */
+               gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+                            (PHY_M_LEDC_LOS_CTRL(1) |  /* LINK/ACT */
+                             PHY_M_LEDC_INIT_CTRL(8) | /* 10 Mbps */
+                             PHY_M_LEDC_STA1_CTRL(7) | /* 100 Mbps */
+                             PHY_M_LEDC_STA0_CTRL(7)));/* 1000 Mbps */
+               /* set Blink Rate in LED Timer Control Register */
+               gm_phy_write(hw, port, PHY_MARV_INT_MASK,
+                            ledctrl | PHY_M_LED_BLINK_RT(BLINK_84MS));
+               /* restore page register */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
+               break;
+       default:
+               /* set Tx LED (LED_TX) to blink mode on Rx OR Tx activity */
+               ledctrl |= PHY_M_LED_BLINK_RT(BLINK_84MS) | PHY_M_LEDC_TX_CTRL;
+               /* turn off the Rx LED (LED_RX) */
+               ledover |= PHY_M_LED_MO_RX(MO_LED_OFF);
+       }
+       if (hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_UL_2) {
+               /* apply fixes in PHY AFE */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 255);
+               /* increase differential signal amplitude in 10BASE-T */
+               gm_phy_write(hw, port, 0x18, 0xaa99);
+               gm_phy_write(hw, port, 0x17, 0x2011);
+               if (hw->chip_id == CHIP_ID_YUKON_EC_U) {
+                       /* fix for IEEE A/B Symmetry failure in 1000BASE-T */
+                       gm_phy_write(hw, port, 0x18, 0xa204);
+                       gm_phy_write(hw, port, 0x17, 0x2002);
+               }
+               /* set page register to 0 */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+       } else if (hw->chip_id == CHIP_ID_YUKON_FE_P &&
+                  hw->chip_rev == CHIP_REV_YU_FE2_A0) {
+               /* apply workaround for integrated resistors calibration */
+               gm_phy_write(hw, port, PHY_MARV_PAGE_ADDR, 17);
+               gm_phy_write(hw, port, PHY_MARV_PAGE_DATA, 0x3f60);
+       } else if (hw->chip_id == CHIP_ID_YUKON_OPT && hw->chip_rev == 0) {
+               /* apply fixes in PHY AFE */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00ff);
+               /* apply RDAC termination workaround */
+               gm_phy_write(hw, port, 24, 0x2800);
+               gm_phy_write(hw, port, 23, 0x2001);
+               /* set page register back to 0 */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+       } else if (hw->chip_id != CHIP_ID_YUKON_EX &&
+                  hw->chip_id < CHIP_ID_YUKON_SUPR) {
+               /* no effect on Yukon-XL */
+               gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl);
+               if (!(sky2->flags & SKY2_FLAG_AUTO_SPEED) ||
+                   sky2->speed == SPEED_100) {
+                       /* turn on 100 Mbps LED (LED_LINK100) */
+                       ledover |= PHY_M_LED_MO_100(MO_LED_ON);
+               }
+               if (ledover)
+                       gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover);
+       } else if (hw->chip_id == CHIP_ID_YUKON_PRM &&
+                  (sky2_read8(hw, B2_MAC_CFG) & 0xf) == 0x7) {
+               int i;
+               /* This a phy register setup workaround copied from vendor driver. */
+               static const struct {
+                       u16 reg, val;
+               } eee_afe[] = {
+                       { 0x156, 0x58ce },
+                       { 0x153, 0x99eb },
+                       { 0x141, 0x8064 },
+                       /* { 0x155, 0x130b },*/
+                       { 0x000, 0x0000 },
+                       { 0x151, 0x8433 },
+                       { 0x14b, 0x8c44 },
+                       { 0x14c, 0x0f90 },
+                       { 0x14f, 0x39aa },
+                       /* { 0x154, 0x2f39 },*/
+                       { 0x14d, 0xba33 },
+                       { 0x144, 0x0048 },
+                       { 0x152, 0x2010 },
+                       /* { 0x158, 0x1223 },*/
+                       { 0x140, 0x4444 },
+                       { 0x154, 0x2f3b },
+                       { 0x158, 0xb203 },
+                       { 0x157, 0x2029 },
+               };
+               /* Start Workaround for OptimaEEE Rev.Z0 */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00fb);
+               gm_phy_write(hw, port,  1, 0x4099);
+               gm_phy_write(hw, port,  3, 0x1120);
+               gm_phy_write(hw, port, 11, 0x113c);
+               gm_phy_write(hw, port, 14, 0x8100);
+               gm_phy_write(hw, port, 15, 0x112a);
+               gm_phy_write(hw, port, 17, 0x1008);
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00fc);
+               gm_phy_write(hw, port,  1, 0x20b0);
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00ff);
+               for (i = 0; i < ARRAY_SIZE(eee_afe); i++) {
+                       /* apply AFE settings */
+                       gm_phy_write(hw, port, 17, eee_afe[i].val);
+                       gm_phy_write(hw, port, 16, eee_afe[i].reg | 1u<<13);
+               }
+               /* End Workaround for OptimaEEE */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+               /* Enable 10Base-Te (EEE) */
+               if (hw->chip_id >= CHIP_ID_YUKON_PRM) {
+                       reg = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
+                       gm_phy_write(hw, port, PHY_MARV_EXT_CTRL,
+                                    reg | PHY_M_10B_TE_ENABLE);
+               }
+       }
+       /* Enable phy interrupt on auto-negotiation complete (or link up) */
+       if (sky2->flags & SKY2_FLAG_AUTO_SPEED)
+               gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_COMPL);
+       else
+               gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
+ }
+ static const u32 phy_power[] = { PCI_Y2_PHY1_POWD, PCI_Y2_PHY2_POWD };
+ static const u32 coma_mode[] = { PCI_Y2_PHY1_COMA, PCI_Y2_PHY2_COMA };
+ static void sky2_phy_power_up(struct sky2_hw *hw, unsigned port)
+ {
+       u32 reg1;
+       sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+       reg1 = sky2_pci_read32(hw, PCI_DEV_REG1);
+       reg1 &= ~phy_power[port];
+       if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > CHIP_REV_YU_XL_A1)
+               reg1 |= coma_mode[port];
+       sky2_pci_write32(hw, PCI_DEV_REG1, reg1);
+       sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+       sky2_pci_read32(hw, PCI_DEV_REG1);
+       if (hw->chip_id == CHIP_ID_YUKON_FE)
+               gm_phy_write(hw, port, PHY_MARV_CTRL, PHY_CT_ANE);
+       else if (hw->flags & SKY2_HW_ADV_POWER_CTL)
+               sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR);
+ }
+ static void sky2_phy_power_down(struct sky2_hw *hw, unsigned port)
+ {
+       u32 reg1;
+       u16 ctrl;
+       /* release GPHY Control reset */
+       sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR);
+       /* release GMAC reset */
+       sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR);
+       if (hw->flags & SKY2_HW_NEWER_PHY) {
+               /* select page 2 to access MAC control register */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2);
+               ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+               /* allow GMII Power Down */
+               ctrl &= ~PHY_M_MAC_GMIF_PUP;
+               gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+               /* set page register back to 0 */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+       }
+       /* setup General Purpose Control Register */
+       gma_write16(hw, port, GM_GP_CTRL,
+                   GM_GPCR_FL_PASS | GM_GPCR_SPEED_100 |
+                   GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_FCT_DIS |
+                   GM_GPCR_AU_SPD_DIS);
+       if (hw->chip_id != CHIP_ID_YUKON_EC) {
+               if (hw->chip_id == CHIP_ID_YUKON_EC_U) {
+                       /* select page 2 to access MAC control register */
+                       gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2);
+                       ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+                       /* enable Power Down */
+                       ctrl |= PHY_M_PC_POW_D_ENA;
+                       gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+                       /* set page register back to 0 */
+                       gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+               }
+               /* set IEEE compatible Power Down Mode (dev. #4.99) */
+               gm_phy_write(hw, port, PHY_MARV_CTRL, PHY_CT_PDOWN);
+       }
+       sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+       reg1 = sky2_pci_read32(hw, PCI_DEV_REG1);
+       reg1 |= phy_power[port];                /* set PHY to PowerDown/COMA Mode */
+       sky2_pci_write32(hw, PCI_DEV_REG1, reg1);
+       sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+ }
+ /* configure IPG according to used link speed */
+ static void sky2_set_ipg(struct sky2_port *sky2)
+ {
+       u16 reg;
+       reg = gma_read16(sky2->hw, sky2->port, GM_SERIAL_MODE);
+       reg &= ~GM_SMOD_IPG_MSK;
+       if (sky2->speed > SPEED_100)
+               reg |= IPG_DATA_VAL(IPG_DATA_DEF_1000);
+       else
+               reg |= IPG_DATA_VAL(IPG_DATA_DEF_10_100);
+       gma_write16(sky2->hw, sky2->port, GM_SERIAL_MODE, reg);
+ }
+ /* Enable Rx/Tx */
+ static void sky2_enable_rx_tx(struct sky2_port *sky2)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       u16 reg;
+       reg = gma_read16(hw, port, GM_GP_CTRL);
+       reg |= GM_GPCR_RX_ENA | GM_GPCR_TX_ENA;
+       gma_write16(hw, port, GM_GP_CTRL, reg);
+ }
+ /* Force a renegotiation */
+ static void sky2_phy_reinit(struct sky2_port *sky2)
+ {
+       spin_lock_bh(&sky2->phy_lock);
+       sky2_phy_init(sky2->hw, sky2->port);
+       sky2_enable_rx_tx(sky2);
+       spin_unlock_bh(&sky2->phy_lock);
+ }
+ /* Put device in state to listen for Wake On Lan */
+ static void sky2_wol_init(struct sky2_port *sky2)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       enum flow_control save_mode;
+       u16 ctrl;
+       /* Bring hardware out of reset */
+       sky2_write16(hw, B0_CTST, CS_RST_CLR);
+       sky2_write16(hw, SK_REG(port, GMAC_LINK_CTRL), GMLC_RST_CLR);
+       sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR);
+       sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR);
+       /* Force to 10/100
+        * sky2_reset will re-enable on resume
+        */
+       save_mode = sky2->flow_mode;
+       ctrl = sky2->advertising;
+       sky2->advertising &= ~(ADVERTISED_1000baseT_Half|ADVERTISED_1000baseT_Full);
+       sky2->flow_mode = FC_NONE;
+       spin_lock_bh(&sky2->phy_lock);
+       sky2_phy_power_up(hw, port);
+       sky2_phy_init(hw, port);
+       spin_unlock_bh(&sky2->phy_lock);
+       sky2->flow_mode = save_mode;
+       sky2->advertising = ctrl;
+       /* Set GMAC to no flow control and auto update for speed/duplex */
+       gma_write16(hw, port, GM_GP_CTRL,
+                   GM_GPCR_FC_TX_DIS|GM_GPCR_TX_ENA|GM_GPCR_RX_ENA|
+                   GM_GPCR_DUP_FULL|GM_GPCR_FC_RX_DIS|GM_GPCR_AU_FCT_DIS);
+       /* Set WOL address */
+       memcpy_toio(hw->regs + WOL_REGS(port, WOL_MAC_ADDR),
+                   sky2->netdev->dev_addr, ETH_ALEN);
+       /* Turn on appropriate WOL control bits */
+       sky2_write16(hw, WOL_REGS(port, WOL_CTRL_STAT), WOL_CTL_CLEAR_RESULT);
+       ctrl = 0;
+       if (sky2->wol & WAKE_PHY)
+               ctrl |= WOL_CTL_ENA_PME_ON_LINK_CHG|WOL_CTL_ENA_LINK_CHG_UNIT;
+       else
+               ctrl |= WOL_CTL_DIS_PME_ON_LINK_CHG|WOL_CTL_DIS_LINK_CHG_UNIT;
+       if (sky2->wol & WAKE_MAGIC)
+               ctrl |= WOL_CTL_ENA_PME_ON_MAGIC_PKT|WOL_CTL_ENA_MAGIC_PKT_UNIT;
+       else
+               ctrl |= WOL_CTL_DIS_PME_ON_MAGIC_PKT|WOL_CTL_DIS_MAGIC_PKT_UNIT;
+       ctrl |= WOL_CTL_DIS_PME_ON_PATTERN|WOL_CTL_DIS_PATTERN_UNIT;
+       sky2_write16(hw, WOL_REGS(port, WOL_CTRL_STAT), ctrl);
+       /* Disable PiG firmware */
+       sky2_write16(hw, B0_CTST, Y2_HW_WOL_OFF);
+       /* block receiver */
+       sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
+ }
+ static void sky2_set_tx_stfwd(struct sky2_hw *hw, unsigned port)
+ {
+       struct net_device *dev = hw->dev[port];
+       if ( (hw->chip_id == CHIP_ID_YUKON_EX &&
+             hw->chip_rev != CHIP_REV_YU_EX_A0) ||
+            hw->chip_id >= CHIP_ID_YUKON_FE_P) {
+               /* Yukon-Extreme B0 and further Extreme devices */
+               sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_STFW_ENA);
+       } else if (dev->mtu > ETH_DATA_LEN) {
+               /* set Tx GMAC FIFO Almost Empty Threshold */
+               sky2_write32(hw, SK_REG(port, TX_GMF_AE_THR),
+                            (ECU_JUMBO_WM << 16) | ECU_AE_THR);
+               sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_STFW_DIS);
+       } else
+               sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_STFW_ENA);
+ }
+ static void sky2_mac_init(struct sky2_hw *hw, unsigned port)
+ {
+       struct sky2_port *sky2 = netdev_priv(hw->dev[port]);
+       u16 reg;
+       u32 rx_reg;
+       int i;
+       const u8 *addr = hw->dev[port]->dev_addr;
+       sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
+       sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR);
+       sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR);
+       if (hw->chip_id == CHIP_ID_YUKON_XL &&
+           hw->chip_rev == CHIP_REV_YU_XL_A0 &&
+           port == 1) {
+               /* WA DEV_472 -- looks like crossed wires on port 2 */
+               /* clear GMAC 1 Control reset */
+               sky2_write8(hw, SK_REG(0, GMAC_CTRL), GMC_RST_CLR);
+               do {
+                       sky2_write8(hw, SK_REG(1, GMAC_CTRL), GMC_RST_SET);
+                       sky2_write8(hw, SK_REG(1, GMAC_CTRL), GMC_RST_CLR);
+               } while (gm_phy_read(hw, 1, PHY_MARV_ID0) != PHY_MARV_ID0_VAL ||
+                        gm_phy_read(hw, 1, PHY_MARV_ID1) != PHY_MARV_ID1_Y2 ||
+                        gm_phy_read(hw, 1, PHY_MARV_INT_MASK) != 0);
+       }
+       sky2_read16(hw, SK_REG(port, GMAC_IRQ_SRC));
+       /* Enable Transmit FIFO Underrun */
+       sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK);
+       spin_lock_bh(&sky2->phy_lock);
+       sky2_phy_power_up(hw, port);
+       sky2_phy_init(hw, port);
+       spin_unlock_bh(&sky2->phy_lock);
+       /* MIB clear */
+       reg = gma_read16(hw, port, GM_PHY_ADDR);
+       gma_write16(hw, port, GM_PHY_ADDR, reg | GM_PAR_MIB_CLR);
+       for (i = GM_MIB_CNT_BASE; i <= GM_MIB_CNT_END; i += 4)
+               gma_read16(hw, port, i);
+       gma_write16(hw, port, GM_PHY_ADDR, reg);
+       /* transmit control */
+       gma_write16(hw, port, GM_TX_CTRL, TX_COL_THR(TX_COL_DEF));
+       /* receive control reg: unicast + multicast + no FCS  */
+       gma_write16(hw, port, GM_RX_CTRL,
+                   GM_RXCR_UCF_ENA | GM_RXCR_CRC_DIS | GM_RXCR_MCF_ENA);
+       /* transmit flow control */
+       gma_write16(hw, port, GM_TX_FLOW_CTRL, 0xffff);
+       /* transmit parameter */
+       gma_write16(hw, port, GM_TX_PARAM,
+                   TX_JAM_LEN_VAL(TX_JAM_LEN_DEF) |
+                   TX_JAM_IPG_VAL(TX_JAM_IPG_DEF) |
+                   TX_IPG_JAM_DATA(TX_IPG_JAM_DEF) |
+                   TX_BACK_OFF_LIM(TX_BOF_LIM_DEF));
+       /* serial mode register */
+       reg = DATA_BLIND_VAL(DATA_BLIND_DEF) |
+               GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF_1000);
+       if (hw->dev[port]->mtu > ETH_DATA_LEN)
+               reg |= GM_SMOD_JUMBO_ENA;
+       if (hw->chip_id == CHIP_ID_YUKON_EC_U &&
+           hw->chip_rev == CHIP_REV_YU_EC_U_B1)
+               reg |= GM_NEW_FLOW_CTRL;
+       gma_write16(hw, port, GM_SERIAL_MODE, reg);
+       /* virtual address for data */
+       gma_set_addr(hw, port, GM_SRC_ADDR_2L, addr);
+       /* physical address: used for pause frames */
+       gma_set_addr(hw, port, GM_SRC_ADDR_1L, addr);
+       /* ignore counter overflows */
+       gma_write16(hw, port, GM_TX_IRQ_MSK, 0);
+       gma_write16(hw, port, GM_RX_IRQ_MSK, 0);
+       gma_write16(hw, port, GM_TR_IRQ_MSK, 0);
+       /* Configure Rx MAC FIFO */
+       sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_CLR);
+       rx_reg = GMF_OPER_ON | GMF_RX_F_FL_ON;
+       if (hw->chip_id == CHIP_ID_YUKON_EX ||
+           hw->chip_id == CHIP_ID_YUKON_FE_P)
+               rx_reg |= GMF_RX_OVER_ON;
+       sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), rx_reg);
+       if (hw->chip_id == CHIP_ID_YUKON_XL) {
+               /* Hardware errata - clear flush mask */
+               sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), 0);
+       } else {
+               /* Flush Rx MAC FIFO on any flow control or error */
+               sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), GMR_FS_ANY_ERR);
+       }
+       /* Set threshold to 0xa (64 bytes) + 1 to workaround pause bug  */
+       reg = RX_GMF_FL_THR_DEF + 1;
+       /* Another magic mystery workaround from sk98lin */
+       if (hw->chip_id == CHIP_ID_YUKON_FE_P &&
+           hw->chip_rev == CHIP_REV_YU_FE2_A0)
+               reg = 0x178;
+       sky2_write16(hw, SK_REG(port, RX_GMF_FL_THR), reg);
+       /* Configure Tx MAC FIFO */
+       sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_CLR);
+       sky2_write16(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON);
+       /* On chips without ram buffer, pause is controlled by MAC level */
+       if (!(hw->flags & SKY2_HW_RAM_BUFFER)) {
+               /* Pause threshold is scaled by 8 in bytes */
+               if (hw->chip_id == CHIP_ID_YUKON_FE_P &&
+                   hw->chip_rev == CHIP_REV_YU_FE2_A0)
+                       reg = 1568 / 8;
+               else
+                       reg = 1024 / 8;
+               sky2_write16(hw, SK_REG(port, RX_GMF_UP_THR), reg);
+               sky2_write16(hw, SK_REG(port, RX_GMF_LP_THR), 768 / 8);
+               sky2_set_tx_stfwd(hw, port);
+       }
+       if (hw->chip_id == CHIP_ID_YUKON_FE_P &&
+           hw->chip_rev == CHIP_REV_YU_FE2_A0) {
+               /* disable dynamic watermark */
+               reg = sky2_read16(hw, SK_REG(port, TX_GMF_EA));
+               reg &= ~TX_DYN_WM_ENA;
+               sky2_write16(hw, SK_REG(port, TX_GMF_EA), reg);
+       }
+ }
+ /* Assign Ram Buffer allocation to queue */
+ static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, u32 space)
+ {
+       u32 end;
+       /* convert from K bytes to qwords used for hw register */
+       start *= 1024/8;
+       space *= 1024/8;
+       end = start + space - 1;
+       sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_RST_CLR);
+       sky2_write32(hw, RB_ADDR(q, RB_START), start);
+       sky2_write32(hw, RB_ADDR(q, RB_END), end);
+       sky2_write32(hw, RB_ADDR(q, RB_WP), start);
+       sky2_write32(hw, RB_ADDR(q, RB_RP), start);
+       if (q == Q_R1 || q == Q_R2) {
+               u32 tp = space - space/4;
+               /* On receive queue's set the thresholds
+                * give receiver priority when > 3/4 full
+                * send pause when down to 2K
+                */
+               sky2_write32(hw, RB_ADDR(q, RB_RX_UTHP), tp);
+               sky2_write32(hw, RB_ADDR(q, RB_RX_LTHP), space/2);
+               tp = space - 2048/8;
+               sky2_write32(hw, RB_ADDR(q, RB_RX_UTPP), tp);
+               sky2_write32(hw, RB_ADDR(q, RB_RX_LTPP), space/4);
+       } else {
+               /* Enable store & forward on Tx queue's because
+                * Tx FIFO is only 1K on Yukon
+                */
+               sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_STFWD);
+       }
+       sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_OP_MD);
+       sky2_read8(hw, RB_ADDR(q, RB_CTRL));
+ }
+ /* Setup Bus Memory Interface */
+ static void sky2_qset(struct sky2_hw *hw, u16 q)
+ {
+       sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_RESET);
+       sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_OPER_INIT);
+       sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_FIFO_OP_ON);
+       sky2_write32(hw, Q_ADDR(q, Q_WM),  BMU_WM_DEFAULT);
+ }
+ /* Setup prefetch unit registers. This is the interface between
+  * hardware and driver list elements
+  */
+ static void sky2_prefetch_init(struct sky2_hw *hw, u32 qaddr,
+                              dma_addr_t addr, u32 last)
+ {
+       sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
+       sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_CLR);
+       sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_HI), upper_32_bits(addr));
+       sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_LO), lower_32_bits(addr));
+       sky2_write16(hw, Y2_QADDR(qaddr, PREF_UNIT_LAST_IDX), last);
+       sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_OP_ON);
+       sky2_read32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL));
+ }
+ static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2, u16 *slot)
+ {
+       struct sky2_tx_le *le = sky2->tx_le + *slot;
+       *slot = RING_NEXT(*slot, sky2->tx_ring_size);
+       le->ctrl = 0;
+       return le;
+ }
+ static void tx_init(struct sky2_port *sky2)
+ {
+       struct sky2_tx_le *le;
+       sky2->tx_prod = sky2->tx_cons = 0;
+       sky2->tx_tcpsum = 0;
+       sky2->tx_last_mss = 0;
+       le = get_tx_le(sky2, &sky2->tx_prod);
+       le->addr = 0;
+       le->opcode = OP_ADDR64 | HW_OWNER;
+       sky2->tx_last_upper = 0;
+ }
+ /* Update chip's next pointer */
+ static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, u16 idx)
+ {
+       /* Make sure write' to descriptors are complete before we tell hardware */
+       wmb();
+       sky2_write16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX), idx);
+       /* Synchronize I/O on since next processor may write to tail */
+       mmiowb();
+ }
+ static inline struct sky2_rx_le *sky2_next_rx(struct sky2_port *sky2)
+ {
+       struct sky2_rx_le *le = sky2->rx_le + sky2->rx_put;
+       sky2->rx_put = RING_NEXT(sky2->rx_put, RX_LE_SIZE);
+       le->ctrl = 0;
+       return le;
+ }
+ static unsigned sky2_get_rx_threshold(struct sky2_port *sky2)
+ {
+       unsigned size;
+       /* Space needed for frame data + headers rounded up */
+       size = roundup(sky2->netdev->mtu + ETH_HLEN + VLAN_HLEN, 8);
+       /* Stopping point for hardware truncation */
+       return (size - 8) / sizeof(u32);
+ }
+ static unsigned sky2_get_rx_data_size(struct sky2_port *sky2)
+ {
+       struct rx_ring_info *re;
+       unsigned size;
+       /* Space needed for frame data + headers rounded up */
+       size = roundup(sky2->netdev->mtu + ETH_HLEN + VLAN_HLEN, 8);
+       sky2->rx_nfrags = size >> PAGE_SHIFT;
+       BUG_ON(sky2->rx_nfrags > ARRAY_SIZE(re->frag_addr));
+       /* Compute residue after pages */
+       size -= sky2->rx_nfrags << PAGE_SHIFT;
+       /* Optimize to handle small packets and headers */
+       if (size < copybreak)
+               size = copybreak;
+       if (size < ETH_HLEN)
+               size = ETH_HLEN;
+       return size;
+ }
+ /* Build description to hardware for one receive segment */
+ static void sky2_rx_add(struct sky2_port *sky2, u8 op,
+                       dma_addr_t map, unsigned len)
+ {
+       struct sky2_rx_le *le;
+       if (sizeof(dma_addr_t) > sizeof(u32)) {
+               le = sky2_next_rx(sky2);
+               le->addr = cpu_to_le32(upper_32_bits(map));
+               le->opcode = OP_ADDR64 | HW_OWNER;
+       }
+       le = sky2_next_rx(sky2);
+       le->addr = cpu_to_le32(lower_32_bits(map));
+       le->length = cpu_to_le16(len);
+       le->opcode = op | HW_OWNER;
+ }
+ /* Build description to hardware for one possibly fragmented skb */
+ static void sky2_rx_submit(struct sky2_port *sky2,
+                          const struct rx_ring_info *re)
+ {
+       int i;
+       sky2_rx_add(sky2, OP_PACKET, re->data_addr, sky2->rx_data_size);
+       for (i = 0; i < skb_shinfo(re->skb)->nr_frags; i++)
+               sky2_rx_add(sky2, OP_BUFFER, re->frag_addr[i], PAGE_SIZE);
+ }
+ static int sky2_rx_map_skb(struct pci_dev *pdev, struct rx_ring_info *re,
+                           unsigned size)
+ {
+       struct sk_buff *skb = re->skb;
+       int i;
+       re->data_addr = pci_map_single(pdev, skb->data, size, PCI_DMA_FROMDEVICE);
+       if (pci_dma_mapping_error(pdev, re->data_addr))
+               goto mapping_error;
+       dma_unmap_len_set(re, data_size, size);
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+               re->frag_addr[i] = skb_frag_dma_map(&pdev->dev, frag, 0,
+                                                   skb_frag_size(frag),
+                                                   DMA_FROM_DEVICE);
+               if (dma_mapping_error(&pdev->dev, re->frag_addr[i]))
+                       goto map_page_error;
+       }
+       return 0;
+ map_page_error:
+       while (--i >= 0) {
+               pci_unmap_page(pdev, re->frag_addr[i],
+                              skb_frag_size(&skb_shinfo(skb)->frags[i]),
+                              PCI_DMA_FROMDEVICE);
+       }
+       pci_unmap_single(pdev, re->data_addr, dma_unmap_len(re, data_size),
+                        PCI_DMA_FROMDEVICE);
+ mapping_error:
+       if (net_ratelimit())
+               dev_warn(&pdev->dev, "%s: rx mapping error\n",
+                        skb->dev->name);
+       return -EIO;
+ }
+ static void sky2_rx_unmap_skb(struct pci_dev *pdev, struct rx_ring_info *re)
+ {
+       struct sk_buff *skb = re->skb;
+       int i;
+       pci_unmap_single(pdev, re->data_addr, dma_unmap_len(re, data_size),
+                        PCI_DMA_FROMDEVICE);
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+               pci_unmap_page(pdev, re->frag_addr[i],
+                              skb_frag_size(&skb_shinfo(skb)->frags[i]),
+                              PCI_DMA_FROMDEVICE);
+ }
+ /* Tell chip where to start receive checksum.
+  * Actually has two checksums, but set both same to avoid possible byte
+  * order problems.
+  */
+ static void rx_set_checksum(struct sky2_port *sky2)
+ {
+       struct sky2_rx_le *le = sky2_next_rx(sky2);
+       le->addr = cpu_to_le32((ETH_HLEN << 16) | ETH_HLEN);
+       le->ctrl = 0;
+       le->opcode = OP_TCPSTART | HW_OWNER;
+       sky2_write32(sky2->hw,
+                    Q_ADDR(rxqaddr[sky2->port], Q_CSR),
+                    (sky2->netdev->features & NETIF_F_RXCSUM)
+                    ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
+ }
+ /* Enable/disable receive hash calculation (RSS) */
+ static void rx_set_rss(struct net_device *dev, u32 features)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       int i, nkeys = 4;
+       /* Supports IPv6 and other modes */
+       if (hw->flags & SKY2_HW_NEW_LE) {
+               nkeys = 10;
+               sky2_write32(hw, SK_REG(sky2->port, RSS_CFG), HASH_ALL);
+       }
+       /* Program RSS initial values */
+       if (features & NETIF_F_RXHASH) {
+               u32 key[nkeys];
+               get_random_bytes(key, nkeys * sizeof(u32));
+               for (i = 0; i < nkeys; i++)
+                       sky2_write32(hw, SK_REG(sky2->port, RSS_KEY + i * 4),
+                                    key[i]);
+               /* Need to turn on (undocumented) flag to make hashing work  */
+               sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T),
+                            RX_STFW_ENA);
+               sky2_write32(hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR),
+                            BMU_ENA_RX_RSS_HASH);
+       } else
+               sky2_write32(hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR),
+                            BMU_DIS_RX_RSS_HASH);
+ }
+ /*
+  * The RX Stop command will not work for Yukon-2 if the BMU does not
+  * reach the end of packet and since we can't make sure that we have
+  * incoming data, we must reset the BMU while it is not doing a DMA
+  * transfer. Since it is possible that the RX path is still active,
+  * the RX RAM buffer will be stopped first, so any possible incoming
+  * data will not trigger a DMA. After the RAM buffer is stopped, the
+  * BMU is polled until any DMA in progress is ended and only then it
+  * will be reset.
+  */
+ static void sky2_rx_stop(struct sky2_port *sky2)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       unsigned rxq = rxqaddr[sky2->port];
+       int i;
+       /* disable the RAM Buffer receive queue */
+       sky2_write8(hw, RB_ADDR(rxq, RB_CTRL), RB_DIS_OP_MD);
+       for (i = 0; i < 0xffff; i++)
+               if (sky2_read8(hw, RB_ADDR(rxq, Q_RSL))
+                   == sky2_read8(hw, RB_ADDR(rxq, Q_RL)))
+                       goto stopped;
+       netdev_warn(sky2->netdev, "receiver stop failed\n");
+ stopped:
+       sky2_write32(hw, Q_ADDR(rxq, Q_CSR), BMU_RST_SET | BMU_FIFO_RST);
+       /* reset the Rx prefetch unit */
+       sky2_write32(hw, Y2_QADDR(rxq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
+       mmiowb();
+ }
+ /* Clean out receive buffer area, assumes receiver hardware stopped */
+ static void sky2_rx_clean(struct sky2_port *sky2)
+ {
+       unsigned i;
+       memset(sky2->rx_le, 0, RX_LE_BYTES);
+       for (i = 0; i < sky2->rx_pending; i++) {
+               struct rx_ring_info *re = sky2->rx_ring + i;
+               if (re->skb) {
+                       sky2_rx_unmap_skb(sky2->hw->pdev, re);
+                       kfree_skb(re->skb);
+                       re->skb = NULL;
+               }
+       }
+ }
+ /* Basic MII support */
+ static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ {
+       struct mii_ioctl_data *data = if_mii(ifr);
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       int err = -EOPNOTSUPP;
+       if (!netif_running(dev))
+               return -ENODEV; /* Phy still in reset */
+       switch (cmd) {
+       case SIOCGMIIPHY:
+               data->phy_id = PHY_ADDR_MARV;
+               /* fallthru */
+       case SIOCGMIIREG: {
+               u16 val = 0;
+               spin_lock_bh(&sky2->phy_lock);
+               err = __gm_phy_read(hw, sky2->port, data->reg_num & 0x1f, &val);
+               spin_unlock_bh(&sky2->phy_lock);
+               data->val_out = val;
+               break;
+       }
+       case SIOCSMIIREG:
+               spin_lock_bh(&sky2->phy_lock);
+               err = gm_phy_write(hw, sky2->port, data->reg_num & 0x1f,
+                                  data->val_in);
+               spin_unlock_bh(&sky2->phy_lock);
+               break;
+       }
+       return err;
+ }
+ #define SKY2_VLAN_OFFLOADS (NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO)
+ static void sky2_vlan_mode(struct net_device *dev, u32 features)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       u16 port = sky2->port;
+       if (features & NETIF_F_HW_VLAN_RX)
+               sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T),
+                            RX_VLAN_STRIP_ON);
+       else
+               sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T),
+                            RX_VLAN_STRIP_OFF);
+       if (features & NETIF_F_HW_VLAN_TX) {
+               sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
+                            TX_VLAN_TAG_ON);
+               dev->vlan_features |= SKY2_VLAN_OFFLOADS;
+       } else {
+               sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
+                            TX_VLAN_TAG_OFF);
+               /* Can't do transmit offload of vlan without hw vlan */
+               dev->vlan_features &= ~SKY2_VLAN_OFFLOADS;
+       }
+ }
+ /* Amount of required worst case padding in rx buffer */
+ static inline unsigned sky2_rx_pad(const struct sky2_hw *hw)
+ {
+       return (hw->flags & SKY2_HW_RAM_BUFFER) ? 8 : 2;
+ }
+ /*
+  * Allocate an skb for receiving. If the MTU is large enough
+  * make the skb non-linear with a fragment list of pages.
+  */
+ static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2, gfp_t gfp)
+ {
+       struct sk_buff *skb;
+       int i;
+       skb = __netdev_alloc_skb(sky2->netdev,
+                                sky2->rx_data_size + sky2_rx_pad(sky2->hw),
+                                gfp);
+       if (!skb)
+               goto nomem;
+       if (sky2->hw->flags & SKY2_HW_RAM_BUFFER) {
+               unsigned char *start;
+               /*
+                * Workaround for a bug in FIFO that cause hang
+                * if the FIFO if the receive buffer is not 64 byte aligned.
+                * The buffer returned from netdev_alloc_skb is
+                * aligned except if slab debugging is enabled.
+                */
+               start = PTR_ALIGN(skb->data, 8);
+               skb_reserve(skb, start - skb->data);
+       } else
+               skb_reserve(skb, NET_IP_ALIGN);
+       for (i = 0; i < sky2->rx_nfrags; i++) {
+               struct page *page = alloc_page(gfp);
+               if (!page)
+                       goto free_partial;
+               skb_fill_page_desc(skb, i, page, 0, PAGE_SIZE);
+       }
+       return skb;
+ free_partial:
+       kfree_skb(skb);
+ nomem:
+       return NULL;
+ }
+ static inline void sky2_rx_update(struct sky2_port *sky2, unsigned rxq)
+ {
+       sky2_put_idx(sky2->hw, rxq, sky2->rx_put);
+ }
+ static int sky2_alloc_rx_skbs(struct sky2_port *sky2)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       unsigned i;
+       sky2->rx_data_size = sky2_get_rx_data_size(sky2);
+       /* Fill Rx ring */
+       for (i = 0; i < sky2->rx_pending; i++) {
+               struct rx_ring_info *re = sky2->rx_ring + i;
+               re->skb = sky2_rx_alloc(sky2, GFP_KERNEL);
+               if (!re->skb)
+                       return -ENOMEM;
+               if (sky2_rx_map_skb(hw->pdev, re, sky2->rx_data_size)) {
+                       dev_kfree_skb(re->skb);
+                       re->skb = NULL;
+                       return -ENOMEM;
+               }
+       }
+       return 0;
+ }
+ /*
+  * Setup receiver buffer pool.
+  * Normal case this ends up creating one list element for skb
+  * in the receive ring. Worst case if using large MTU and each
+  * allocation falls on a different 64 bit region, that results
+  * in 6 list elements per ring entry.
+  * One element is used for checksum enable/disable, and one
+  * extra to avoid wrap.
+  */
+ static void sky2_rx_start(struct sky2_port *sky2)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       struct rx_ring_info *re;
+       unsigned rxq = rxqaddr[sky2->port];
+       unsigned i, thresh;
+       sky2->rx_put = sky2->rx_next = 0;
+       sky2_qset(hw, rxq);
+       /* On PCI express lowering the watermark gives better performance */
+       if (pci_is_pcie(hw->pdev))
+               sky2_write32(hw, Q_ADDR(rxq, Q_WM), BMU_WM_PEX);
+       /* These chips have no ram buffer?
+        * MAC Rx RAM Read is controlled by hardware */
+       if (hw->chip_id == CHIP_ID_YUKON_EC_U &&
+           hw->chip_rev > CHIP_REV_YU_EC_U_A0)
+               sky2_write32(hw, Q_ADDR(rxq, Q_TEST), F_M_RX_RAM_DIS);
+       sky2_prefetch_init(hw, rxq, sky2->rx_le_map, RX_LE_SIZE - 1);
+       if (!(hw->flags & SKY2_HW_NEW_LE))
+               rx_set_checksum(sky2);
+       if (!(hw->flags & SKY2_HW_RSS_BROKEN))
+               rx_set_rss(sky2->netdev, sky2->netdev->features);
+       /* submit Rx ring */
+       for (i = 0; i < sky2->rx_pending; i++) {
+               re = sky2->rx_ring + i;
+               sky2_rx_submit(sky2, re);
+       }
+       /*
+        * The receiver hangs if it receives frames larger than the
+        * packet buffer. As a workaround, truncate oversize frames, but
+        * the register is limited to 9 bits, so if you do frames > 2052
+        * you better get the MTU right!
+        */
+       thresh = sky2_get_rx_threshold(sky2);
+       if (thresh > 0x1ff)
+               sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_OFF);
+       else {
+               sky2_write16(hw, SK_REG(sky2->port, RX_GMF_TR_THR), thresh);
+               sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_ON);
+       }
+       /* Tell chip about available buffers */
+       sky2_rx_update(sky2, rxq);
+       if (hw->chip_id == CHIP_ID_YUKON_EX ||
+           hw->chip_id == CHIP_ID_YUKON_SUPR) {
+               /*
+                * Disable flushing of non ASF packets;
+                * must be done after initializing the BMUs;
+                * drivers without ASF support should do this too, otherwise
+                * it may happen that they cannot run on ASF devices;
+                * remember that the MAC FIFO isn't reset during initialization.
+                */
+               sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_MACSEC_FLUSH_OFF);
+       }
+       if (hw->chip_id >= CHIP_ID_YUKON_SUPR) {
+               /* Enable RX Home Address & Routing Header checksum fix */
+               sky2_write16(hw, SK_REG(sky2->port, RX_GMF_FL_CTRL),
+                            RX_IPV6_SA_MOB_ENA | RX_IPV6_DA_MOB_ENA);
+               /* Enable TX Home Address & Routing Header checksum fix */
+               sky2_write32(hw, Q_ADDR(txqaddr[sky2->port], Q_TEST),
+                            TBMU_TEST_HOME_ADD_FIX_EN | TBMU_TEST_ROUTING_ADD_FIX_EN);
+       }
+ }
+ static int sky2_alloc_buffers(struct sky2_port *sky2)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       /* must be power of 2 */
+       sky2->tx_le = pci_alloc_consistent(hw->pdev,
+                                          sky2->tx_ring_size *
+                                          sizeof(struct sky2_tx_le),
+                                          &sky2->tx_le_map);
+       if (!sky2->tx_le)
+               goto nomem;
+       sky2->tx_ring = kcalloc(sky2->tx_ring_size, sizeof(struct tx_ring_info),
+                               GFP_KERNEL);
+       if (!sky2->tx_ring)
+               goto nomem;
+       sky2->rx_le = pci_alloc_consistent(hw->pdev, RX_LE_BYTES,
+                                          &sky2->rx_le_map);
+       if (!sky2->rx_le)
+               goto nomem;
+       memset(sky2->rx_le, 0, RX_LE_BYTES);
+       sky2->rx_ring = kcalloc(sky2->rx_pending, sizeof(struct rx_ring_info),
+                               GFP_KERNEL);
+       if (!sky2->rx_ring)
+               goto nomem;
+       return sky2_alloc_rx_skbs(sky2);
+ nomem:
+       return -ENOMEM;
+ }
+ static void sky2_free_buffers(struct sky2_port *sky2)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       sky2_rx_clean(sky2);
+       if (sky2->rx_le) {
+               pci_free_consistent(hw->pdev, RX_LE_BYTES,
+                                   sky2->rx_le, sky2->rx_le_map);
+               sky2->rx_le = NULL;
+       }
+       if (sky2->tx_le) {
+               pci_free_consistent(hw->pdev,
+                                   sky2->tx_ring_size * sizeof(struct sky2_tx_le),
+                                   sky2->tx_le, sky2->tx_le_map);
+               sky2->tx_le = NULL;
+       }
+       kfree(sky2->tx_ring);
+       kfree(sky2->rx_ring);
+       sky2->tx_ring = NULL;
+       sky2->rx_ring = NULL;
+ }
+ static void sky2_hw_up(struct sky2_port *sky2)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       u32 ramsize;
+       int cap;
+       struct net_device *otherdev = hw->dev[sky2->port^1];
+       tx_init(sky2);
+       /*
+        * On dual port PCI-X card, there is an problem where status
+        * can be received out of order due to split transactions
+        */
+       if (otherdev && netif_running(otherdev) &&
+           (cap = pci_find_capability(hw->pdev, PCI_CAP_ID_PCIX))) {
+               u16 cmd;
+               cmd = sky2_pci_read16(hw, cap + PCI_X_CMD);
+               cmd &= ~PCI_X_CMD_MAX_SPLIT;
+               sky2_pci_write16(hw, cap + PCI_X_CMD, cmd);
+       }
+       sky2_mac_init(hw, port);
+       /* Register is number of 4K blocks on internal RAM buffer. */
+       ramsize = sky2_read8(hw, B2_E_0) * 4;
+       if (ramsize > 0) {
+               u32 rxspace;
+               netdev_dbg(sky2->netdev, "ram buffer %dK\n", ramsize);
+               if (ramsize < 16)
+                       rxspace = ramsize / 2;
+               else
+                       rxspace = 8 + (2*(ramsize - 16))/3;
+               sky2_ramset(hw, rxqaddr[port], 0, rxspace);
+               sky2_ramset(hw, txqaddr[port], rxspace, ramsize - rxspace);
+               /* Make sure SyncQ is disabled */
+               sky2_write8(hw, RB_ADDR(port == 0 ? Q_XS1 : Q_XS2, RB_CTRL),
+                           RB_RST_SET);
+       }
+       sky2_qset(hw, txqaddr[port]);
+       /* This is copied from sk98lin 10.0.5.3; no one tells me about erratta's */
+       if (hw->chip_id == CHIP_ID_YUKON_EX && hw->chip_rev == CHIP_REV_YU_EX_B0)
+               sky2_write32(hw, Q_ADDR(txqaddr[port], Q_TEST), F_TX_CHK_AUTO_OFF);
+       /* Set almost empty threshold */
+       if (hw->chip_id == CHIP_ID_YUKON_EC_U &&
+           hw->chip_rev == CHIP_REV_YU_EC_U_A0)
+               sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), ECU_TXFF_LEV);
+       sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map,
+                          sky2->tx_ring_size - 1);
+       sky2_vlan_mode(sky2->netdev, sky2->netdev->features);
+       netdev_update_features(sky2->netdev);
+       sky2_rx_start(sky2);
+ }
+ /* Setup device IRQ and enable napi to process */
+ static int sky2_setup_irq(struct sky2_hw *hw, const char *name)
+ {
+       struct pci_dev *pdev = hw->pdev;
+       int err;
+       err = request_irq(pdev->irq, sky2_intr,
+                         (hw->flags & SKY2_HW_USE_MSI) ? 0 : IRQF_SHARED,
+                         name, hw);
+       if (err)
+               dev_err(&pdev->dev, "cannot assign irq %d\n", pdev->irq);
+       else {
+               napi_enable(&hw->napi);
+               sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
+               sky2_read32(hw, B0_IMSK);
+       }
+       return err;
+ }
+ /* Bring up network interface. */
+ static int sky2_up(struct net_device *dev)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       u32 imask;
+       int err;
+       netif_carrier_off(dev);
+       err = sky2_alloc_buffers(sky2);
+       if (err)
+               goto err_out;
+       /* With single port, IRQ is setup when device is brought up */
+       if (hw->ports == 1 && (err = sky2_setup_irq(hw, dev->name)))
+               goto err_out;
+       sky2_hw_up(sky2);
+       /* Enable interrupts from phy/mac for port */
+       imask = sky2_read32(hw, B0_IMSK);
+       imask |= portirq_msk[port];
+       sky2_write32(hw, B0_IMSK, imask);
+       sky2_read32(hw, B0_IMSK);
+       netif_info(sky2, ifup, dev, "enabling interface\n");
+       return 0;
+ err_out:
+       sky2_free_buffers(sky2);
+       return err;
+ }
+ /* Modular subtraction in ring */
+ static inline int tx_inuse(const struct sky2_port *sky2)
+ {
+       return (sky2->tx_prod - sky2->tx_cons) & (sky2->tx_ring_size - 1);
+ }
+ /* Number of list elements available for next tx */
+ static inline int tx_avail(const struct sky2_port *sky2)
+ {
+       return sky2->tx_pending - tx_inuse(sky2);
+ }
+ /* Estimate of number of transmit list elements required */
+ static unsigned tx_le_req(const struct sk_buff *skb)
+ {
+       unsigned count;
+       count = (skb_shinfo(skb)->nr_frags + 1)
+               * (sizeof(dma_addr_t) / sizeof(u32));
+       if (skb_is_gso(skb))
+               ++count;
+       else if (sizeof(dma_addr_t) == sizeof(u32))
+               ++count;        /* possible vlan */
+       if (skb->ip_summed == CHECKSUM_PARTIAL)
+               ++count;
+       return count;
+ }
+ static void sky2_tx_unmap(struct pci_dev *pdev, struct tx_ring_info *re)
+ {
+       if (re->flags & TX_MAP_SINGLE)
+               pci_unmap_single(pdev, dma_unmap_addr(re, mapaddr),
+                                dma_unmap_len(re, maplen),
+                                PCI_DMA_TODEVICE);
+       else if (re->flags & TX_MAP_PAGE)
+               pci_unmap_page(pdev, dma_unmap_addr(re, mapaddr),
+                              dma_unmap_len(re, maplen),
+                              PCI_DMA_TODEVICE);
+       re->flags = 0;
+ }
+ /*
+  * Put one packet in ring for transmit.
+  * A single packet can generate multiple list elements, and
+  * the number of ring elements will probably be less than the number
+  * of list elements used.
+  */
+ static netdev_tx_t sky2_xmit_frame(struct sk_buff *skb,
+                                  struct net_device *dev)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       struct sky2_tx_le *le = NULL;
+       struct tx_ring_info *re;
+       unsigned i, len;
+       dma_addr_t mapping;
+       u32 upper;
+       u16 slot;
+       u16 mss;
+       u8 ctrl;
+       if (unlikely(tx_avail(sky2) < tx_le_req(skb)))
+               return NETDEV_TX_BUSY;
+       len = skb_headlen(skb);
+       mapping = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE);
+       if (pci_dma_mapping_error(hw->pdev, mapping))
+               goto mapping_error;
+       slot = sky2->tx_prod;
+       netif_printk(sky2, tx_queued, KERN_DEBUG, dev,
+                    "tx queued, slot %u, len %d\n", slot, skb->len);
+       /* Send high bits if needed */
+       upper = upper_32_bits(mapping);
+       if (upper != sky2->tx_last_upper) {
+               le = get_tx_le(sky2, &slot);
+               le->addr = cpu_to_le32(upper);
+               sky2->tx_last_upper = upper;
+               le->opcode = OP_ADDR64 | HW_OWNER;
+       }
+       /* Check for TCP Segmentation Offload */
+       mss = skb_shinfo(skb)->gso_size;
+       if (mss != 0) {
+               if (!(hw->flags & SKY2_HW_NEW_LE))
+                       mss += ETH_HLEN + ip_hdrlen(skb) + tcp_hdrlen(skb);
+               if (mss != sky2->tx_last_mss) {
+                       le = get_tx_le(sky2, &slot);
+                       le->addr = cpu_to_le32(mss);
+                       if (hw->flags & SKY2_HW_NEW_LE)
+                               le->opcode = OP_MSS | HW_OWNER;
+                       else
+                               le->opcode = OP_LRGLEN | HW_OWNER;
+                       sky2->tx_last_mss = mss;
+               }
+       }
+       ctrl = 0;
+       /* Add VLAN tag, can piggyback on LRGLEN or ADDR64 */
+       if (vlan_tx_tag_present(skb)) {
+               if (!le) {
+                       le = get_tx_le(sky2, &slot);
+                       le->addr = 0;
+                       le->opcode = OP_VLAN|HW_OWNER;
+               } else
+                       le->opcode |= OP_VLAN;
+               le->length = cpu_to_be16(vlan_tx_tag_get(skb));
+               ctrl |= INS_VLAN;
+       }
+       /* Handle TCP checksum offload */
+       if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               /* On Yukon EX (some versions) encoding change. */
+               if (hw->flags & SKY2_HW_AUTO_TX_SUM)
+                       ctrl |= CALSUM; /* auto checksum */
+               else {
+                       const unsigned offset = skb_transport_offset(skb);
+                       u32 tcpsum;
+                       tcpsum = offset << 16;                  /* sum start */
+                       tcpsum |= offset + skb->csum_offset;    /* sum write */
+                       ctrl |= CALSUM | WR_SUM | INIT_SUM | LOCK_SUM;
+                       if (ip_hdr(skb)->protocol == IPPROTO_UDP)
+                               ctrl |= UDPTCP;
+                       if (tcpsum != sky2->tx_tcpsum) {
+                               sky2->tx_tcpsum = tcpsum;
+                               le = get_tx_le(sky2, &slot);
+                               le->addr = cpu_to_le32(tcpsum);
+                               le->length = 0; /* initial checksum value */
+                               le->ctrl = 1;   /* one packet */
+                               le->opcode = OP_TCPLISW | HW_OWNER;
+                       }
+               }
+       }
+       re = sky2->tx_ring + slot;
+       re->flags = TX_MAP_SINGLE;
+       dma_unmap_addr_set(re, mapaddr, mapping);
+       dma_unmap_len_set(re, maplen, len);
+       le = get_tx_le(sky2, &slot);
+       le->addr = cpu_to_le32(lower_32_bits(mapping));
+       le->length = cpu_to_le16(len);
+       le->ctrl = ctrl;
+       le->opcode = mss ? (OP_LARGESEND | HW_OWNER) : (OP_PACKET | HW_OWNER);
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+               mapping = skb_frag_dma_map(&hw->pdev->dev, frag, 0,
+                                          skb_frag_size(frag), DMA_TO_DEVICE);
+               if (dma_mapping_error(&hw->pdev->dev, mapping))
+                       goto mapping_unwind;
+               upper = upper_32_bits(mapping);
+               if (upper != sky2->tx_last_upper) {
+                       le = get_tx_le(sky2, &slot);
+                       le->addr = cpu_to_le32(upper);
+                       sky2->tx_last_upper = upper;
+                       le->opcode = OP_ADDR64 | HW_OWNER;
+               }
+               re = sky2->tx_ring + slot;
+               re->flags = TX_MAP_PAGE;
+               dma_unmap_addr_set(re, mapaddr, mapping);
+               dma_unmap_len_set(re, maplen, skb_frag_size(frag));
+               le = get_tx_le(sky2, &slot);
+               le->addr = cpu_to_le32(lower_32_bits(mapping));
+               le->length = cpu_to_le16(skb_frag_size(frag));
+               le->ctrl = ctrl;
+               le->opcode = OP_BUFFER | HW_OWNER;
+       }
+       re->skb = skb;
+       le->ctrl |= EOP;
+       sky2->tx_prod = slot;
+       if (tx_avail(sky2) <= MAX_SKB_TX_LE)
+               netif_stop_queue(dev);
+       sky2_put_idx(hw, txqaddr[sky2->port], sky2->tx_prod);
+       return NETDEV_TX_OK;
+ mapping_unwind:
+       for (i = sky2->tx_prod; i != slot; i = RING_NEXT(i, sky2->tx_ring_size)) {
+               re = sky2->tx_ring + i;
+               sky2_tx_unmap(hw->pdev, re);
+       }
+ mapping_error:
+       if (net_ratelimit())
+               dev_warn(&hw->pdev->dev, "%s: tx mapping error\n", dev->name);
+       dev_kfree_skb(skb);
+       return NETDEV_TX_OK;
+ }
+ /*
+  * Free ring elements from starting at tx_cons until "done"
+  *
+  * NB:
+  *  1. The hardware will tell us about partial completion of multi-part
+  *     buffers so make sure not to free skb to early.
+  *  2. This may run in parallel start_xmit because the it only
+  *     looks at the tail of the queue of FIFO (tx_cons), not
+  *     the head (tx_prod)
+  */
+ static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
+ {
+       struct net_device *dev = sky2->netdev;
+       unsigned idx;
+       BUG_ON(done >= sky2->tx_ring_size);
+       for (idx = sky2->tx_cons; idx != done;
+            idx = RING_NEXT(idx, sky2->tx_ring_size)) {
+               struct tx_ring_info *re = sky2->tx_ring + idx;
+               struct sk_buff *skb = re->skb;
+               sky2_tx_unmap(sky2->hw->pdev, re);
+               if (skb) {
+                       netif_printk(sky2, tx_done, KERN_DEBUG, dev,
+                                    "tx done %u\n", idx);
+                       u64_stats_update_begin(&sky2->tx_stats.syncp);
+                       ++sky2->tx_stats.packets;
+                       sky2->tx_stats.bytes += skb->len;
+                       u64_stats_update_end(&sky2->tx_stats.syncp);
+                       re->skb = NULL;
+                       dev_kfree_skb_any(skb);
+                       sky2->tx_next = RING_NEXT(idx, sky2->tx_ring_size);
+               }
+       }
+       sky2->tx_cons = idx;
+       smp_mb();
+ }
+ static void sky2_tx_reset(struct sky2_hw *hw, unsigned port)
+ {
+       /* Disable Force Sync bit and Enable Alloc bit */
+       sky2_write8(hw, SK_REG(port, TXA_CTRL),
+                   TXA_DIS_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC);
+       /* Stop Interval Timer and Limit Counter of Tx Arbiter */
+       sky2_write32(hw, SK_REG(port, TXA_ITI_INI), 0L);
+       sky2_write32(hw, SK_REG(port, TXA_LIM_INI), 0L);
+       /* Reset the PCI FIFO of the async Tx queue */
+       sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR),
+                    BMU_RST_SET | BMU_FIFO_RST);
+       /* Reset the Tx prefetch units */
+       sky2_write32(hw, Y2_QADDR(txqaddr[port], PREF_UNIT_CTRL),
+                    PREF_UNIT_RST_SET);
+       sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET);
+       sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET);
+ }
+ static void sky2_hw_down(struct sky2_port *sky2)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       u16 ctrl;
+       /* Force flow control off */
+       sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
+       /* Stop transmitter */
+       sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_STOP);
+       sky2_read32(hw, Q_ADDR(txqaddr[port], Q_CSR));
+       sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL),
+                    RB_RST_SET | RB_DIS_OP_MD);
+       ctrl = gma_read16(hw, port, GM_GP_CTRL);
+       ctrl &= ~(GM_GPCR_TX_ENA | GM_GPCR_RX_ENA);
+       gma_write16(hw, port, GM_GP_CTRL, ctrl);
+       sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
+       /* Workaround shared GMAC reset */
+       if (!(hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 &&
+             port == 0 && hw->dev[1] && netif_running(hw->dev[1])))
+               sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET);
+       sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
++      /* Force any delayed status interrupt and NAPI */
+       sky2_write32(hw, STAT_LEV_TIMER_CNT, 0);
+       sky2_write32(hw, STAT_TX_TIMER_CNT, 0);
+       sky2_write32(hw, STAT_ISR_TIMER_CNT, 0);
+       sky2_read8(hw, STAT_ISR_TIMER_CTRL);
+       sky2_rx_stop(sky2);
+       spin_lock_bh(&sky2->phy_lock);
+       sky2_phy_power_down(hw, port);
+       spin_unlock_bh(&sky2->phy_lock);
+       sky2_tx_reset(hw, port);
+       /* Free any pending frames stuck in HW queue */
+       sky2_tx_complete(sky2, sky2->tx_prod);
+ }
+ /* Network shutdown */
+ static int sky2_down(struct net_device *dev)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       /* Never really got started! */
+       if (!sky2->tx_le)
+               return 0;
+       netif_info(sky2, ifdown, dev, "disabling interface\n");
+       /* Disable port IRQ */
+       sky2_write32(hw, B0_IMSK,
+                    sky2_read32(hw, B0_IMSK) & ~portirq_msk[sky2->port]);
+       sky2_read32(hw, B0_IMSK);
+       if (hw->ports == 1) {
+               napi_disable(&hw->napi);
+               free_irq(hw->pdev->irq, hw);
+       } else {
+               synchronize_irq(hw->pdev->irq);
+               napi_synchronize(&hw->napi);
+       }
+       sky2_hw_down(sky2);
+       sky2_free_buffers(sky2);
+       return 0;
+ }
+ static u16 sky2_phy_speed(const struct sky2_hw *hw, u16 aux)
+ {
+       if (hw->flags & SKY2_HW_FIBRE_PHY)
+               return SPEED_1000;
+       if (!(hw->flags & SKY2_HW_GIGABIT)) {
+               if (aux & PHY_M_PS_SPEED_100)
+                       return SPEED_100;
+               else
+                       return SPEED_10;
+       }
+       switch (aux & PHY_M_PS_SPEED_MSK) {
+       case PHY_M_PS_SPEED_1000:
+               return SPEED_1000;
+       case PHY_M_PS_SPEED_100:
+               return SPEED_100;
+       default:
+               return SPEED_10;
+       }
+ }
+ static void sky2_link_up(struct sky2_port *sky2)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       static const char *fc_name[] = {
+               [FC_NONE]       = "none",
+               [FC_TX]         = "tx",
+               [FC_RX]         = "rx",
+               [FC_BOTH]       = "both",
+       };
+       sky2_set_ipg(sky2);
+       sky2_enable_rx_tx(sky2);
+       gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
+       netif_carrier_on(sky2->netdev);
+       mod_timer(&hw->watchdog_timer, jiffies + 1);
+       /* Turn on link LED */
+       sky2_write8(hw, SK_REG(port, LNK_LED_REG),
+                   LINKLED_ON | LINKLED_BLINK_OFF | LINKLED_LINKSYNC_OFF);
+       netif_info(sky2, link, sky2->netdev,
+                  "Link is up at %d Mbps, %s duplex, flow control %s\n",
+                  sky2->speed,
+                  sky2->duplex == DUPLEX_FULL ? "full" : "half",
+                  fc_name[sky2->flow_status]);
+ }
+ static void sky2_link_down(struct sky2_port *sky2)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       u16 reg;
+       gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0);
+       reg = gma_read16(hw, port, GM_GP_CTRL);
+       reg &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
+       gma_write16(hw, port, GM_GP_CTRL, reg);
+       netif_carrier_off(sky2->netdev);
+       /* Turn off link LED */
+       sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
+       netif_info(sky2, link, sky2->netdev, "Link is down\n");
+       sky2_phy_init(hw, port);
+ }
+ static enum flow_control sky2_flow(int rx, int tx)
+ {
+       if (rx)
+               return tx ? FC_BOTH : FC_RX;
+       else
+               return tx ? FC_TX : FC_NONE;
+ }
+ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       u16 advert, lpa;
+       advert = gm_phy_read(hw, port, PHY_MARV_AUNE_ADV);
+       lpa = gm_phy_read(hw, port, PHY_MARV_AUNE_LP);
+       if (lpa & PHY_M_AN_RF) {
+               netdev_err(sky2->netdev, "remote fault\n");
+               return -1;
+       }
+       if (!(aux & PHY_M_PS_SPDUP_RES)) {
+               netdev_err(sky2->netdev, "speed/duplex mismatch\n");
+               return -1;
+       }
+       sky2->speed = sky2_phy_speed(hw, aux);
+       sky2->duplex = (aux & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF;
+       /* Since the pause result bits seem to in different positions on
+        * different chips. look at registers.
+        */
+       if (hw->flags & SKY2_HW_FIBRE_PHY) {
+               /* Shift for bits in fiber PHY */
+               advert &= ~(ADVERTISE_PAUSE_CAP|ADVERTISE_PAUSE_ASYM);
+               lpa &= ~(LPA_PAUSE_CAP|LPA_PAUSE_ASYM);
+               if (advert & ADVERTISE_1000XPAUSE)
+                       advert |= ADVERTISE_PAUSE_CAP;
+               if (advert & ADVERTISE_1000XPSE_ASYM)
+                       advert |= ADVERTISE_PAUSE_ASYM;
+               if (lpa & LPA_1000XPAUSE)
+                       lpa |= LPA_PAUSE_CAP;
+               if (lpa & LPA_1000XPAUSE_ASYM)
+                       lpa |= LPA_PAUSE_ASYM;
+       }
+       sky2->flow_status = FC_NONE;
+       if (advert & ADVERTISE_PAUSE_CAP) {
+               if (lpa & LPA_PAUSE_CAP)
+                       sky2->flow_status = FC_BOTH;
+               else if (advert & ADVERTISE_PAUSE_ASYM)
+                       sky2->flow_status = FC_RX;
+       } else if (advert & ADVERTISE_PAUSE_ASYM) {
+               if ((lpa & LPA_PAUSE_CAP) && (lpa & LPA_PAUSE_ASYM))
+                       sky2->flow_status = FC_TX;
+       }
+       if (sky2->duplex == DUPLEX_HALF && sky2->speed < SPEED_1000 &&
+           !(hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX))
+               sky2->flow_status = FC_NONE;
+       if (sky2->flow_status & FC_TX)
+               sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON);
+       else
+               sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
+       return 0;
+ }
+ /* Interrupt from PHY */
+ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port)
+ {
+       struct net_device *dev = hw->dev[port];
+       struct sky2_port *sky2 = netdev_priv(dev);
+       u16 istatus, phystat;
+       if (!netif_running(dev))
+               return;
+       spin_lock(&sky2->phy_lock);
+       istatus = gm_phy_read(hw, port, PHY_MARV_INT_STAT);
+       phystat = gm_phy_read(hw, port, PHY_MARV_PHY_STAT);
+       netif_info(sky2, intr, sky2->netdev, "phy interrupt status 0x%x 0x%x\n",
+                  istatus, phystat);
+       if (istatus & PHY_M_IS_AN_COMPL) {
+               if (sky2_autoneg_done(sky2, phystat) == 0 &&
+                   !netif_carrier_ok(dev))
+                       sky2_link_up(sky2);
+               goto out;
+       }
+       if (istatus & PHY_M_IS_LSP_CHANGE)
+               sky2->speed = sky2_phy_speed(hw, phystat);
+       if (istatus & PHY_M_IS_DUP_CHANGE)
+               sky2->duplex =
+                   (phystat & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF;
+       if (istatus & PHY_M_IS_LST_CHANGE) {
+               if (phystat & PHY_M_PS_LINK_UP)
+                       sky2_link_up(sky2);
+               else
+                       sky2_link_down(sky2);
+       }
+ out:
+       spin_unlock(&sky2->phy_lock);
+ }
+ /* Special quick link interrupt (Yukon-2 Optima only) */
+ static void sky2_qlink_intr(struct sky2_hw *hw)
+ {
+       struct sky2_port *sky2 = netdev_priv(hw->dev[0]);
+       u32 imask;
+       u16 phy;
+       /* disable irq */
+       imask = sky2_read32(hw, B0_IMSK);
+       imask &= ~Y2_IS_PHY_QLNK;
+       sky2_write32(hw, B0_IMSK, imask);
+       /* reset PHY Link Detect */
+       phy = sky2_pci_read16(hw, PSM_CONFIG_REG4);
+       sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+       sky2_pci_write16(hw, PSM_CONFIG_REG4, phy | 1);
+       sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+       sky2_link_up(sky2);
+ }
+ /* Transmit timeout is only called if we are running, carrier is up
+  * and tx queue is full (stopped).
+  */
+ static void sky2_tx_timeout(struct net_device *dev)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       netif_err(sky2, timer, dev, "tx timeout\n");
+       netdev_printk(KERN_DEBUG, dev, "transmit ring %u .. %u report=%u done=%u\n",
+                     sky2->tx_cons, sky2->tx_prod,
+                     sky2_read16(hw, sky2->port == 0 ? STAT_TXA1_RIDX : STAT_TXA2_RIDX),
+                     sky2_read16(hw, Q_ADDR(txqaddr[sky2->port], Q_DONE)));
+       /* can't restart safely under softirq */
+       schedule_work(&hw->restart_work);
+ }
+ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       int err;
+       u16 ctl, mode;
+       u32 imask;
+       /* MTU size outside the spec */
+       if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
+               return -EINVAL;
+       /* MTU > 1500 on yukon FE and FE+ not allowed */
+       if (new_mtu > ETH_DATA_LEN &&
+           (hw->chip_id == CHIP_ID_YUKON_FE ||
+            hw->chip_id == CHIP_ID_YUKON_FE_P))
+               return -EINVAL;
+       if (!netif_running(dev)) {
+               dev->mtu = new_mtu;
+               netdev_update_features(dev);
+               return 0;
+       }
+       imask = sky2_read32(hw, B0_IMSK);
+       sky2_write32(hw, B0_IMSK, 0);
+       dev->trans_start = jiffies;     /* prevent tx timeout */
+       napi_disable(&hw->napi);
+       netif_tx_disable(dev);
+       synchronize_irq(hw->pdev->irq);
+       if (!(hw->flags & SKY2_HW_RAM_BUFFER))
+               sky2_set_tx_stfwd(hw, port);
+       ctl = gma_read16(hw, port, GM_GP_CTRL);
+       gma_write16(hw, port, GM_GP_CTRL, ctl & ~GM_GPCR_RX_ENA);
+       sky2_rx_stop(sky2);
+       sky2_rx_clean(sky2);
+       dev->mtu = new_mtu;
+       netdev_update_features(dev);
+       mode = DATA_BLIND_VAL(DATA_BLIND_DEF) | GM_SMOD_VLAN_ENA;
+       if (sky2->speed > SPEED_100)
+               mode |= IPG_DATA_VAL(IPG_DATA_DEF_1000);
+       else
+               mode |= IPG_DATA_VAL(IPG_DATA_DEF_10_100);
+       if (dev->mtu > ETH_DATA_LEN)
+               mode |= GM_SMOD_JUMBO_ENA;
+       gma_write16(hw, port, GM_SERIAL_MODE, mode);
+       sky2_write8(hw, RB_ADDR(rxqaddr[port], RB_CTRL), RB_ENA_OP_MD);
+       err = sky2_alloc_rx_skbs(sky2);
+       if (!err)
+               sky2_rx_start(sky2);
+       else
+               sky2_rx_clean(sky2);
+       sky2_write32(hw, B0_IMSK, imask);
+       sky2_read32(hw, B0_Y2_SP_LISR);
+       napi_enable(&hw->napi);
+       if (err)
+               dev_close(dev);
+       else {
+               gma_write16(hw, port, GM_GP_CTRL, ctl);
+               netif_wake_queue(dev);
+       }
+       return err;
+ }
+ /* For small just reuse existing skb for next receive */
+ static struct sk_buff *receive_copy(struct sky2_port *sky2,
+                                   const struct rx_ring_info *re,
+                                   unsigned length)
+ {
+       struct sk_buff *skb;
+       skb = netdev_alloc_skb_ip_align(sky2->netdev, length);
+       if (likely(skb)) {
+               pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->data_addr,
+                                           length, PCI_DMA_FROMDEVICE);
+               skb_copy_from_linear_data(re->skb, skb->data, length);
+               skb->ip_summed = re->skb->ip_summed;
+               skb->csum = re->skb->csum;
+               pci_dma_sync_single_for_device(sky2->hw->pdev, re->data_addr,
+                                              length, PCI_DMA_FROMDEVICE);
+               re->skb->ip_summed = CHECKSUM_NONE;
+               skb_put(skb, length);
+       }
+       return skb;
+ }
+ /* Adjust length of skb with fragments to match received data */
+ static void skb_put_frags(struct sk_buff *skb, unsigned int hdr_space,
+                         unsigned int length)
+ {
+       int i, num_frags;
+       unsigned int size;
+       /* put header into skb */
+       size = min(length, hdr_space);
+       skb->tail += size;
+       skb->len += size;
+       length -= size;
+       num_frags = skb_shinfo(skb)->nr_frags;
+       for (i = 0; i < num_frags; i++) {
+               skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+               if (length == 0) {
+                       /* don't need this page */
+                       __skb_frag_unref(frag);
+                       --skb_shinfo(skb)->nr_frags;
+               } else {
+                       size = min(length, (unsigned) PAGE_SIZE);
+                       skb_frag_size_set(frag, size);
+                       skb->data_len += size;
+                       skb->truesize += PAGE_SIZE;
+                       skb->len += size;
+                       length -= size;
+               }
+       }
+ }
+ /* Normal packet - take skb from ring element and put in a new one  */
+ static struct sk_buff *receive_new(struct sky2_port *sky2,
+                                  struct rx_ring_info *re,
+                                  unsigned int length)
+ {
+       struct sk_buff *skb;
+       struct rx_ring_info nre;
+       unsigned hdr_space = sky2->rx_data_size;
+       nre.skb = sky2_rx_alloc(sky2, GFP_ATOMIC);
+       if (unlikely(!nre.skb))
+               goto nobuf;
+       if (sky2_rx_map_skb(sky2->hw->pdev, &nre, hdr_space))
+               goto nomap;
+       skb = re->skb;
+       sky2_rx_unmap_skb(sky2->hw->pdev, re);
+       prefetch(skb->data);
+       *re = nre;
+       if (skb_shinfo(skb)->nr_frags)
+               skb_put_frags(skb, hdr_space, length);
+       else
+               skb_put(skb, length);
+       return skb;
+ nomap:
+       dev_kfree_skb(nre.skb);
+ nobuf:
+       return NULL;
+ }
+ /*
+  * Receive one packet.
+  * For larger packets, get new buffer.
+  */
+ static struct sk_buff *sky2_receive(struct net_device *dev,
+                                   u16 length, u32 status)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct rx_ring_info *re = sky2->rx_ring + sky2->rx_next;
+       struct sk_buff *skb = NULL;
+       u16 count = (status & GMR_FS_LEN) >> 16;
+       if (status & GMR_FS_VLAN)
+               count -= VLAN_HLEN;     /* Account for vlan tag */
+       netif_printk(sky2, rx_status, KERN_DEBUG, dev,
+                    "rx slot %u status 0x%x len %d\n",
+                    sky2->rx_next, status, length);
+       sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending;
+       prefetch(sky2->rx_ring + sky2->rx_next);
+       /* This chip has hardware problems that generates bogus status.
+        * So do only marginal checking and expect higher level protocols
+        * to handle crap frames.
+        */
+       if (sky2->hw->chip_id == CHIP_ID_YUKON_FE_P &&
+           sky2->hw->chip_rev == CHIP_REV_YU_FE2_A0 &&
+           length != count)
+               goto okay;
+       if (status & GMR_FS_ANY_ERR)
+               goto error;
+       if (!(status & GMR_FS_RX_OK))
+               goto resubmit;
+       /* if length reported by DMA does not match PHY, packet was truncated */
+       if (length != count)
+               goto error;
+ okay:
+       if (length < copybreak)
+               skb = receive_copy(sky2, re, length);
+       else
+               skb = receive_new(sky2, re, length);
+       dev->stats.rx_dropped += (skb == NULL);
+ resubmit:
+       sky2_rx_submit(sky2, re);
+       return skb;
+ error:
+       ++dev->stats.rx_errors;
+       if (net_ratelimit())
+               netif_info(sky2, rx_err, dev,
+                          "rx error, status 0x%x length %d\n", status, length);
+       goto resubmit;
+ }
+ /* Transmit complete */
+ static inline void sky2_tx_done(struct net_device *dev, u16 last)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       if (netif_running(dev)) {
+               sky2_tx_complete(sky2, last);
+               /* Wake unless it's detached, and called e.g. from sky2_down() */
+               if (tx_avail(sky2) > MAX_SKB_TX_LE + 4)
+                       netif_wake_queue(dev);
+       }
+ }
+ static inline void sky2_skb_rx(const struct sky2_port *sky2,
+                              u32 status, struct sk_buff *skb)
+ {
+       if (status & GMR_FS_VLAN)
+               __vlan_hwaccel_put_tag(skb, be16_to_cpu(sky2->rx_tag));
+       if (skb->ip_summed == CHECKSUM_NONE)
+               netif_receive_skb(skb);
+       else
+               napi_gro_receive(&sky2->hw->napi, skb);
+ }
+ static inline void sky2_rx_done(struct sky2_hw *hw, unsigned port,
+                               unsigned packets, unsigned bytes)
+ {
+       struct net_device *dev = hw->dev[port];
+       struct sky2_port *sky2 = netdev_priv(dev);
+       if (packets == 0)
+               return;
+       u64_stats_update_begin(&sky2->rx_stats.syncp);
+       sky2->rx_stats.packets += packets;
+       sky2->rx_stats.bytes += bytes;
+       u64_stats_update_end(&sky2->rx_stats.syncp);
+       dev->last_rx = jiffies;
+       sky2_rx_update(netdev_priv(dev), rxqaddr[port]);
+ }
+ static void sky2_rx_checksum(struct sky2_port *sky2, u32 status)
+ {
+       /* If this happens then driver assuming wrong format for chip type */
+       BUG_ON(sky2->hw->flags & SKY2_HW_NEW_LE);
+       /* Both checksum counters are programmed to start at
+        * the same offset, so unless there is a problem they
+        * should match. This failure is an early indication that
+        * hardware receive checksumming won't work.
+        */
+       if (likely((u16)(status >> 16) == (u16)status)) {
+               struct sk_buff *skb = sky2->rx_ring[sky2->rx_next].skb;
+               skb->ip_summed = CHECKSUM_COMPLETE;
+               skb->csum = le16_to_cpu(status);
+       } else {
+               dev_notice(&sky2->hw->pdev->dev,
+                          "%s: receive checksum problem (status = %#x)\n",
+                          sky2->netdev->name, status);
+               /* Disable checksum offload
+                * It will be reenabled on next ndo_set_features, but if it's
+                * really broken, will get disabled again
+                */
+               sky2->netdev->features &= ~NETIF_F_RXCSUM;
+               sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR),
+                            BMU_DIS_RX_CHKSUM);
+       }
+ }
+ static void sky2_rx_hash(struct sky2_port *sky2, u32 status)
+ {
+       struct sk_buff *skb;
+       skb = sky2->rx_ring[sky2->rx_next].skb;
+       skb->rxhash = le32_to_cpu(status);
+ }
+ /* Process status response ring */
+ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
+ {
+       int work_done = 0;
+       unsigned int total_bytes[2] = { 0 };
+       unsigned int total_packets[2] = { 0 };
+       rmb();
+       do {
+               struct sky2_port *sky2;
+               struct sky2_status_le *le  = hw->st_le + hw->st_idx;
+               unsigned port;
+               struct net_device *dev;
+               struct sk_buff *skb;
+               u32 status;
+               u16 length;
+               u8 opcode = le->opcode;
+               if (!(opcode & HW_OWNER))
+                       break;
+               hw->st_idx = RING_NEXT(hw->st_idx, hw->st_size);
+               port = le->css & CSS_LINK_BIT;
+               dev = hw->dev[port];
+               sky2 = netdev_priv(dev);
+               length = le16_to_cpu(le->length);
+               status = le32_to_cpu(le->status);
+               le->opcode = 0;
+               switch (opcode & ~HW_OWNER) {
+               case OP_RXSTAT:
+                       total_packets[port]++;
+                       total_bytes[port] += length;
+                       skb = sky2_receive(dev, length, status);
+                       if (!skb)
+                               break;
+                       /* This chip reports checksum status differently */
+                       if (hw->flags & SKY2_HW_NEW_LE) {
+                               if ((dev->features & NETIF_F_RXCSUM) &&
+                                   (le->css & (CSS_ISIPV4 | CSS_ISIPV6)) &&
+                                   (le->css & CSS_TCPUDPCSOK))
+                                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+                               else
+                                       skb->ip_summed = CHECKSUM_NONE;
+                       }
+                       skb->protocol = eth_type_trans(skb, dev);
+                       sky2_skb_rx(sky2, status, skb);
+                       /* Stop after net poll weight */
+                       if (++work_done >= to_do)
+                               goto exit_loop;
+                       break;
+               case OP_RXVLAN:
+                       sky2->rx_tag = length;
+                       break;
+               case OP_RXCHKSVLAN:
+                       sky2->rx_tag = length;
+                       /* fall through */
+               case OP_RXCHKS:
+                       if (likely(dev->features & NETIF_F_RXCSUM))
+                               sky2_rx_checksum(sky2, status);
+                       break;
+               case OP_RSS_HASH:
+                       sky2_rx_hash(sky2, status);
+                       break;
+               case OP_TXINDEXLE:
+                       /* TX index reports status for both ports */
+                       sky2_tx_done(hw->dev[0], status & 0xfff);
+                       if (hw->dev[1])
+                               sky2_tx_done(hw->dev[1],
+                                    ((status >> 24) & 0xff)
+                                            | (u16)(length & 0xf) << 8);
+                       break;
+               default:
+                       if (net_ratelimit())
+                               pr_warning("unknown status opcode 0x%x\n", opcode);
+               }
+       } while (hw->st_idx != idx);
+       /* Fully processed status ring so clear irq */
+       sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ);
+ exit_loop:
+       sky2_rx_done(hw, 0, total_packets[0], total_bytes[0]);
+       sky2_rx_done(hw, 1, total_packets[1], total_bytes[1]);
+       return work_done;
+ }
+ static void sky2_hw_error(struct sky2_hw *hw, unsigned port, u32 status)
+ {
+       struct net_device *dev = hw->dev[port];
+       if (net_ratelimit())
+               netdev_info(dev, "hw error interrupt status 0x%x\n", status);
+       if (status & Y2_IS_PAR_RD1) {
+               if (net_ratelimit())
+                       netdev_err(dev, "ram data read parity error\n");
+               /* Clear IRQ */
+               sky2_write16(hw, RAM_BUFFER(port, B3_RI_CTRL), RI_CLR_RD_PERR);
+       }
+       if (status & Y2_IS_PAR_WR1) {
+               if (net_ratelimit())
+                       netdev_err(dev, "ram data write parity error\n");
+               sky2_write16(hw, RAM_BUFFER(port, B3_RI_CTRL), RI_CLR_WR_PERR);
+       }
+       if (status & Y2_IS_PAR_MAC1) {
+               if (net_ratelimit())
+                       netdev_err(dev, "MAC parity error\n");
+               sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_CLI_TX_PE);
+       }
+       if (status & Y2_IS_PAR_RX1) {
+               if (net_ratelimit())
+                       netdev_err(dev, "RX parity error\n");
+               sky2_write32(hw, Q_ADDR(rxqaddr[port], Q_CSR), BMU_CLR_IRQ_PAR);
+       }
+       if (status & Y2_IS_TCP_TXA1) {
+               if (net_ratelimit())
+                       netdev_err(dev, "TCP segmentation error\n");
+               sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_CLR_IRQ_TCP);
+       }
+ }
+ static void sky2_hw_intr(struct sky2_hw *hw)
+ {
+       struct pci_dev *pdev = hw->pdev;
+       u32 status = sky2_read32(hw, B0_HWE_ISRC);
+       u32 hwmsk = sky2_read32(hw, B0_HWE_IMSK);
+       status &= hwmsk;
+       if (status & Y2_IS_TIST_OV)
+               sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ);
+       if (status & (Y2_IS_MST_ERR | Y2_IS_IRQ_STAT)) {
+               u16 pci_err;
+               sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+               pci_err = sky2_pci_read16(hw, PCI_STATUS);
+               if (net_ratelimit())
+                       dev_err(&pdev->dev, "PCI hardware error (0x%x)\n",
+                               pci_err);
+               sky2_pci_write16(hw, PCI_STATUS,
+                                     pci_err | PCI_STATUS_ERROR_BITS);
+               sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+       }
+       if (status & Y2_IS_PCI_EXP) {
+               /* PCI-Express uncorrectable Error occurred */
+               u32 err;
+               sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+               err = sky2_read32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS);
+               sky2_write32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS,
+                            0xfffffffful);
+               if (net_ratelimit())
+                       dev_err(&pdev->dev, "PCI Express error (0x%x)\n", err);
+               sky2_read32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS);
+               sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+       }
+       if (status & Y2_HWE_L1_MASK)
+               sky2_hw_error(hw, 0, status);
+       status >>= 8;
+       if (status & Y2_HWE_L1_MASK)
+               sky2_hw_error(hw, 1, status);
+ }
+ static void sky2_mac_intr(struct sky2_hw *hw, unsigned port)
+ {
+       struct net_device *dev = hw->dev[port];
+       struct sky2_port *sky2 = netdev_priv(dev);
+       u8 status = sky2_read8(hw, SK_REG(port, GMAC_IRQ_SRC));
+       netif_info(sky2, intr, dev, "mac interrupt status 0x%x\n", status);
+       if (status & GM_IS_RX_CO_OV)
+               gma_read16(hw, port, GM_RX_IRQ_SRC);
+       if (status & GM_IS_TX_CO_OV)
+               gma_read16(hw, port, GM_TX_IRQ_SRC);
+       if (status & GM_IS_RX_FF_OR) {
+               ++dev->stats.rx_fifo_errors;
+               sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_CLI_RX_FO);
+       }
+       if (status & GM_IS_TX_FF_UR) {
+               ++dev->stats.tx_fifo_errors;
+               sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_CLI_TX_FU);
+       }
+ }
+ /* This should never happen it is a bug. */
+ static void sky2_le_error(struct sky2_hw *hw, unsigned port, u16 q)
+ {
+       struct net_device *dev = hw->dev[port];
+       u16 idx = sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_GET_IDX));
+       dev_err(&hw->pdev->dev, "%s: descriptor error q=%#x get=%u put=%u\n",
+               dev->name, (unsigned) q, (unsigned) idx,
+               (unsigned) sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX)));
+       sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_IRQ_CHK);
+ }
+ static int sky2_rx_hung(struct net_device *dev)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       unsigned rxq = rxqaddr[port];
+       u32 mac_rp = sky2_read32(hw, SK_REG(port, RX_GMF_RP));
+       u8 mac_lev = sky2_read8(hw, SK_REG(port, RX_GMF_RLEV));
+       u8 fifo_rp = sky2_read8(hw, Q_ADDR(rxq, Q_RP));
+       u8 fifo_lev = sky2_read8(hw, Q_ADDR(rxq, Q_RL));
+       /* If idle and MAC or PCI is stuck */
+       if (sky2->check.last == dev->last_rx &&
+           ((mac_rp == sky2->check.mac_rp &&
+             mac_lev != 0 && mac_lev >= sky2->check.mac_lev) ||
+            /* Check if the PCI RX hang */
+            (fifo_rp == sky2->check.fifo_rp &&
+             fifo_lev != 0 && fifo_lev >= sky2->check.fifo_lev))) {
+               netdev_printk(KERN_DEBUG, dev,
+                             "hung mac %d:%d fifo %d (%d:%d)\n",
+                             mac_lev, mac_rp, fifo_lev,
+                             fifo_rp, sky2_read8(hw, Q_ADDR(rxq, Q_WP)));
+               return 1;
+       } else {
+               sky2->check.last = dev->last_rx;
+               sky2->check.mac_rp = mac_rp;
+               sky2->check.mac_lev = mac_lev;
+               sky2->check.fifo_rp = fifo_rp;
+               sky2->check.fifo_lev = fifo_lev;
+               return 0;
+       }
+ }
+ static void sky2_watchdog(unsigned long arg)
+ {
+       struct sky2_hw *hw = (struct sky2_hw *) arg;
+       /* Check for lost IRQ once a second */
+       if (sky2_read32(hw, B0_ISRC)) {
+               napi_schedule(&hw->napi);
+       } else {
+               int i, active = 0;
+               for (i = 0; i < hw->ports; i++) {
+                       struct net_device *dev = hw->dev[i];
+                       if (!netif_running(dev))
+                               continue;
+                       ++active;
+                       /* For chips with Rx FIFO, check if stuck */
+                       if ((hw->flags & SKY2_HW_RAM_BUFFER) &&
+                            sky2_rx_hung(dev)) {
+                               netdev_info(dev, "receiver hang detected\n");
+                               schedule_work(&hw->restart_work);
+                               return;
+                       }
+               }
+               if (active == 0)
+                       return;
+       }
+       mod_timer(&hw->watchdog_timer, round_jiffies(jiffies + HZ));
+ }
+ /* Hardware/software error handling */
+ static void sky2_err_intr(struct sky2_hw *hw, u32 status)
+ {
+       if (net_ratelimit())
+               dev_warn(&hw->pdev->dev, "error interrupt status=%#x\n", status);
+       if (status & Y2_IS_HW_ERR)
+               sky2_hw_intr(hw);
+       if (status & Y2_IS_IRQ_MAC1)
+               sky2_mac_intr(hw, 0);
+       if (status & Y2_IS_IRQ_MAC2)
+               sky2_mac_intr(hw, 1);
+       if (status & Y2_IS_CHK_RX1)
+               sky2_le_error(hw, 0, Q_R1);
+       if (status & Y2_IS_CHK_RX2)
+               sky2_le_error(hw, 1, Q_R2);
+       if (status & Y2_IS_CHK_TXA1)
+               sky2_le_error(hw, 0, Q_XA1);
+       if (status & Y2_IS_CHK_TXA2)
+               sky2_le_error(hw, 1, Q_XA2);
+ }
+ static int sky2_poll(struct napi_struct *napi, int work_limit)
+ {
+       struct sky2_hw *hw = container_of(napi, struct sky2_hw, napi);
+       u32 status = sky2_read32(hw, B0_Y2_SP_EISR);
+       int work_done = 0;
+       u16 idx;
+       if (unlikely(status & Y2_IS_ERROR))
+               sky2_err_intr(hw, status);
+       if (status & Y2_IS_IRQ_PHY1)
+               sky2_phy_intr(hw, 0);
+       if (status & Y2_IS_IRQ_PHY2)
+               sky2_phy_intr(hw, 1);
+       if (status & Y2_IS_PHY_QLNK)
+               sky2_qlink_intr(hw);
+       while ((idx = sky2_read16(hw, STAT_PUT_IDX)) != hw->st_idx) {
+               work_done += sky2_status_intr(hw, work_limit - work_done, idx);
+               if (work_done >= work_limit)
+                       goto done;
+       }
+       napi_complete(napi);
+       sky2_read32(hw, B0_Y2_SP_LISR);
+ done:
+       return work_done;
+ }
+ static irqreturn_t sky2_intr(int irq, void *dev_id)
+ {
+       struct sky2_hw *hw = dev_id;
+       u32 status;
+       /* Reading this mask interrupts as side effect */
+       status = sky2_read32(hw, B0_Y2_SP_ISRC2);
+       if (status == 0 || status == ~0)
+               return IRQ_NONE;
+       prefetch(&hw->st_le[hw->st_idx]);
+       napi_schedule(&hw->napi);
+       return IRQ_HANDLED;
+ }
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+ static void sky2_netpoll(struct net_device *dev)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       napi_schedule(&sky2->hw->napi);
+ }
+ #endif
+ /* Chip internal frequency for clock calculations */
+ static u32 sky2_mhz(const struct sky2_hw *hw)
+ {
+       switch (hw->chip_id) {
+       case CHIP_ID_YUKON_EC:
+       case CHIP_ID_YUKON_EC_U:
+       case CHIP_ID_YUKON_EX:
+       case CHIP_ID_YUKON_SUPR:
+       case CHIP_ID_YUKON_UL_2:
+       case CHIP_ID_YUKON_OPT:
+       case CHIP_ID_YUKON_PRM:
+       case CHIP_ID_YUKON_OP_2:
+               return 125;
+       case CHIP_ID_YUKON_FE:
+               return 100;
+       case CHIP_ID_YUKON_FE_P:
+               return 50;
+       case CHIP_ID_YUKON_XL:
+               return 156;
+       default:
+               BUG();
+       }
+ }
+ static inline u32 sky2_us2clk(const struct sky2_hw *hw, u32 us)
+ {
+       return sky2_mhz(hw) * us;
+ }
+ static inline u32 sky2_clk2us(const struct sky2_hw *hw, u32 clk)
+ {
+       return clk / sky2_mhz(hw);
+ }
+ static int __devinit sky2_init(struct sky2_hw *hw)
+ {
+       u8 t8;
+       /* Enable all clocks and check for bad PCI access */
+       sky2_pci_write32(hw, PCI_DEV_REG3, 0);
+       sky2_write8(hw, B0_CTST, CS_RST_CLR);
+       hw->chip_id = sky2_read8(hw, B2_CHIP_ID);
+       hw->chip_rev = (sky2_read8(hw, B2_MAC_CFG) & CFG_CHIP_R_MSK) >> 4;
+       switch (hw->chip_id) {
+       case CHIP_ID_YUKON_XL:
+               hw->flags = SKY2_HW_GIGABIT | SKY2_HW_NEWER_PHY;
+               if (hw->chip_rev < CHIP_REV_YU_XL_A2)
+                       hw->flags |= SKY2_HW_RSS_BROKEN;
+               break;
+       case CHIP_ID_YUKON_EC_U:
+               hw->flags = SKY2_HW_GIGABIT
+                       | SKY2_HW_NEWER_PHY
+                       | SKY2_HW_ADV_POWER_CTL;
+               break;
+       case CHIP_ID_YUKON_EX:
+               hw->flags = SKY2_HW_GIGABIT
+                       | SKY2_HW_NEWER_PHY
+                       | SKY2_HW_NEW_LE
+                       | SKY2_HW_ADV_POWER_CTL
+                       | SKY2_HW_RSS_CHKSUM;
+               /* New transmit checksum */
+               if (hw->chip_rev != CHIP_REV_YU_EX_B0)
+                       hw->flags |= SKY2_HW_AUTO_TX_SUM;
+               break;
+       case CHIP_ID_YUKON_EC:
+               /* This rev is really old, and requires untested workarounds */
+               if (hw->chip_rev == CHIP_REV_YU_EC_A1) {
+                       dev_err(&hw->pdev->dev, "unsupported revision Yukon-EC rev A1\n");
+                       return -EOPNOTSUPP;
+               }
+               hw->flags = SKY2_HW_GIGABIT | SKY2_HW_RSS_BROKEN;
+               break;
+       case CHIP_ID_YUKON_FE:
+               hw->flags = SKY2_HW_RSS_BROKEN;
+               break;
+       case CHIP_ID_YUKON_FE_P:
+               hw->flags = SKY2_HW_NEWER_PHY
+                       | SKY2_HW_NEW_LE
+                       | SKY2_HW_AUTO_TX_SUM
+                       | SKY2_HW_ADV_POWER_CTL;
+               /* The workaround for status conflicts VLAN tag detection. */
+               if (hw->chip_rev == CHIP_REV_YU_FE2_A0)
+                       hw->flags |= SKY2_HW_VLAN_BROKEN | SKY2_HW_RSS_CHKSUM;
+               break;
+       case CHIP_ID_YUKON_SUPR:
+               hw->flags = SKY2_HW_GIGABIT
+                       | SKY2_HW_NEWER_PHY
+                       | SKY2_HW_NEW_LE
+                       | SKY2_HW_AUTO_TX_SUM
+                       | SKY2_HW_ADV_POWER_CTL;
+               if (hw->chip_rev == CHIP_REV_YU_SU_A0)
+                       hw->flags |= SKY2_HW_RSS_CHKSUM;
+               break;
+       case CHIP_ID_YUKON_UL_2:
+               hw->flags = SKY2_HW_GIGABIT
+                       | SKY2_HW_ADV_POWER_CTL;
+               break;
+       case CHIP_ID_YUKON_OPT:
+       case CHIP_ID_YUKON_PRM:
+       case CHIP_ID_YUKON_OP_2:
+               hw->flags = SKY2_HW_GIGABIT
+                       | SKY2_HW_NEW_LE
+                       | SKY2_HW_ADV_POWER_CTL;
+               break;
+       default:
+               dev_err(&hw->pdev->dev, "unsupported chip type 0x%x\n",
+                       hw->chip_id);
+               return -EOPNOTSUPP;
+       }
+       hw->pmd_type = sky2_read8(hw, B2_PMD_TYP);
+       if (hw->pmd_type == 'L' || hw->pmd_type == 'S' || hw->pmd_type == 'P')
+               hw->flags |= SKY2_HW_FIBRE_PHY;
+       hw->ports = 1;
+       t8 = sky2_read8(hw, B2_Y2_HW_RES);
+       if ((t8 & CFG_DUAL_MAC_MSK) == CFG_DUAL_MAC_MSK) {
+               if (!(sky2_read8(hw, B2_Y2_CLK_GATE) & Y2_STATUS_LNK2_INAC))
+                       ++hw->ports;
+       }
+       if (sky2_read8(hw, B2_E_0))
+               hw->flags |= SKY2_HW_RAM_BUFFER;
+       return 0;
+ }
+ static void sky2_reset(struct sky2_hw *hw)
+ {
+       struct pci_dev *pdev = hw->pdev;
+       u16 status;
+       int i;
+       u32 hwe_mask = Y2_HWE_ALL_MASK;
+       /* disable ASF */
+       if (hw->chip_id == CHIP_ID_YUKON_EX
+           || hw->chip_id == CHIP_ID_YUKON_SUPR) {
+               sky2_write32(hw, CPU_WDOG, 0);
+               status = sky2_read16(hw, HCU_CCSR);
+               status &= ~(HCU_CCSR_AHB_RST | HCU_CCSR_CPU_RST_MODE |
+                           HCU_CCSR_UC_STATE_MSK);
+               /*
+                * CPU clock divider shouldn't be used because
+                * - ASF firmware may malfunction
+                * - Yukon-Supreme: Parallel FLASH doesn't support divided clocks
+                */
+               status &= ~HCU_CCSR_CPU_CLK_DIVIDE_MSK;
+               sky2_write16(hw, HCU_CCSR, status);
+               sky2_write32(hw, CPU_WDOG, 0);
+       } else
+               sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET);
+       sky2_write16(hw, B0_CTST, Y2_ASF_DISABLE);
+       /* do a SW reset */
+       sky2_write8(hw, B0_CTST, CS_RST_SET);
+       sky2_write8(hw, B0_CTST, CS_RST_CLR);
+       /* allow writes to PCI config */
+       sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+       /* clear PCI errors, if any */
+       status = sky2_pci_read16(hw, PCI_STATUS);
+       status |= PCI_STATUS_ERROR_BITS;
+       sky2_pci_write16(hw, PCI_STATUS, status);
+       sky2_write8(hw, B0_CTST, CS_MRST_CLR);
+       if (pci_is_pcie(pdev)) {
+               sky2_write32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS,
+                            0xfffffffful);
+               /* If error bit is stuck on ignore it */
+               if (sky2_read32(hw, B0_HWE_ISRC) & Y2_IS_PCI_EXP)
+                       dev_info(&pdev->dev, "ignoring stuck error report bit\n");
+               else
+                       hwe_mask |= Y2_IS_PCI_EXP;
+       }
+       sky2_power_on(hw);
+       sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+       for (i = 0; i < hw->ports; i++) {
+               sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET);
+               sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_CLR);
+               if (hw->chip_id == CHIP_ID_YUKON_EX ||
+                   hw->chip_id == CHIP_ID_YUKON_SUPR)
+                       sky2_write16(hw, SK_REG(i, GMAC_CTRL),
+                                    GMC_BYP_MACSECRX_ON | GMC_BYP_MACSECTX_ON
+                                    | GMC_BYP_RETR_ON);
+       }
+       if (hw->chip_id == CHIP_ID_YUKON_SUPR && hw->chip_rev > CHIP_REV_YU_SU_B0) {
+               /* enable MACSec clock gating */
+               sky2_pci_write32(hw, PCI_DEV_REG3, P_CLK_MACSEC_DIS);
+       }
+       if (hw->chip_id == CHIP_ID_YUKON_OPT ||
+           hw->chip_id == CHIP_ID_YUKON_PRM ||
+           hw->chip_id == CHIP_ID_YUKON_OP_2) {
+               u16 reg;
+               u32 msk;
+               if (hw->chip_id == CHIP_ID_YUKON_OPT && hw->chip_rev == 0) {
+                       /* disable PCI-E PHY power down (set PHY reg 0x80, bit 7 */
+                       sky2_write32(hw, Y2_PEX_PHY_DATA, (0x80UL << 16) | (1 << 7));
+                       /* set PHY Link Detect Timer to 1.1 second (11x 100ms) */
+                       reg = 10;
+                       /* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */
+                       sky2_write32(hw, Y2_PEX_PHY_DATA, PEX_DB_ACCESS | (0x08UL << 16));
+               } else {
+                       /* set PHY Link Detect Timer to 0.4 second (4x 100ms) */
+                       reg = 3;
+               }
+               reg <<= PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_BASE;
+               reg |= PSM_CONFIG_REG4_RST_PHY_LINK_DETECT;
+               /* reset PHY Link Detect */
+               sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+               sky2_pci_write16(hw, PSM_CONFIG_REG4, reg);
+               /* enable PHY Quick Link */
+               msk = sky2_read32(hw, B0_IMSK);
+               msk |= Y2_IS_PHY_QLNK;
+               sky2_write32(hw, B0_IMSK, msk);
+               /* check if PSMv2 was running before */
+               reg = sky2_pci_read16(hw, PSM_CONFIG_REG3);
+               if (reg & PCI_EXP_LNKCTL_ASPMC)
+                       /* restore the PCIe Link Control register */
+                       sky2_pci_write16(hw, pdev->pcie_cap + PCI_EXP_LNKCTL,
+                                        reg);
+               sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+               /* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */
+               sky2_write32(hw, Y2_PEX_PHY_DATA, PEX_DB_ACCESS | (0x08UL << 16));
+       }
+       /* Clear I2C IRQ noise */
+       sky2_write32(hw, B2_I2C_IRQ, 1);
+       /* turn off hardware timer (unused) */
+       sky2_write8(hw, B2_TI_CTRL, TIM_STOP);
+       sky2_write8(hw, B2_TI_CTRL, TIM_CLR_IRQ);
+       /* Turn off descriptor polling */
+       sky2_write32(hw, B28_DPT_CTRL, DPT_STOP);
+       /* Turn off receive timestamp */
+       sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_STOP);
+       sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ);
+       /* enable the Tx Arbiters */
+       for (i = 0; i < hw->ports; i++)
+               sky2_write8(hw, SK_REG(i, TXA_CTRL), TXA_ENA_ARB);
+       /* Initialize ram interface */
+       for (i = 0; i < hw->ports; i++) {
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_CTRL), RI_RST_CLR);
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_R1), SK_RI_TO_53);
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XA1), SK_RI_TO_53);
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XS1), SK_RI_TO_53);
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_R1), SK_RI_TO_53);
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XA1), SK_RI_TO_53);
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XS1), SK_RI_TO_53);
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_R2), SK_RI_TO_53);
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XA2), SK_RI_TO_53);
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XS2), SK_RI_TO_53);
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_R2), SK_RI_TO_53);
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XA2), SK_RI_TO_53);
+               sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XS2), SK_RI_TO_53);
+       }
+       sky2_write32(hw, B0_HWE_IMSK, hwe_mask);
+       for (i = 0; i < hw->ports; i++)
+               sky2_gmac_reset(hw, i);
+       memset(hw->st_le, 0, hw->st_size * sizeof(struct sky2_status_le));
+       hw->st_idx = 0;
+       sky2_write32(hw, STAT_CTRL, SC_STAT_RST_SET);
+       sky2_write32(hw, STAT_CTRL, SC_STAT_RST_CLR);
+       sky2_write32(hw, STAT_LIST_ADDR_LO, hw->st_dma);
+       sky2_write32(hw, STAT_LIST_ADDR_HI, (u64) hw->st_dma >> 32);
+       /* Set the list last index */
+       sky2_write16(hw, STAT_LAST_IDX, hw->st_size - 1);
+       sky2_write16(hw, STAT_TX_IDX_TH, 10);
+       sky2_write8(hw, STAT_FIFO_WM, 16);
+       /* set Status-FIFO ISR watermark */
+       if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0)
+               sky2_write8(hw, STAT_FIFO_ISR_WM, 4);
+       else
+               sky2_write8(hw, STAT_FIFO_ISR_WM, 16);
+       sky2_write32(hw, STAT_TX_TIMER_INI, sky2_us2clk(hw, 1000));
+       sky2_write32(hw, STAT_ISR_TIMER_INI, sky2_us2clk(hw, 20));
+       sky2_write32(hw, STAT_LEV_TIMER_INI, sky2_us2clk(hw, 100));
+       /* enable status unit */
+       sky2_write32(hw, STAT_CTRL, SC_STAT_OP_ON);
+       sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START);
+       sky2_write8(hw, STAT_LEV_TIMER_CTRL, TIM_START);
+       sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START);
+ }
+ /* Take device down (offline).
+  * Equivalent to doing dev_stop() but this does not
+  * inform upper layers of the transition.
+  */
+ static void sky2_detach(struct net_device *dev)
+ {
+       if (netif_running(dev)) {
+               netif_tx_lock(dev);
+               netif_device_detach(dev);       /* stop txq */
+               netif_tx_unlock(dev);
+               sky2_down(dev);
+       }
+ }
+ /* Bring device back after doing sky2_detach */
+ static int sky2_reattach(struct net_device *dev)
+ {
+       int err = 0;
+       if (netif_running(dev)) {
+               err = sky2_up(dev);
+               if (err) {
+                       netdev_info(dev, "could not restart %d\n", err);
+                       dev_close(dev);
+               } else {
+                       netif_device_attach(dev);
+                       sky2_set_multicast(dev);
+               }
+       }
+       return err;
+ }
+ static void sky2_all_down(struct sky2_hw *hw)
+ {
+       int i;
+       sky2_read32(hw, B0_IMSK);
+       sky2_write32(hw, B0_IMSK, 0);
+       synchronize_irq(hw->pdev->irq);
+       napi_disable(&hw->napi);
+       for (i = 0; i < hw->ports; i++) {
+               struct net_device *dev = hw->dev[i];
+               struct sky2_port *sky2 = netdev_priv(dev);
+               if (!netif_running(dev))
+                       continue;
+               netif_carrier_off(dev);
+               netif_tx_disable(dev);
+               sky2_hw_down(sky2);
+       }
+ }
+ static void sky2_all_up(struct sky2_hw *hw)
+ {
+       u32 imask = Y2_IS_BASE;
+       int i;
+       for (i = 0; i < hw->ports; i++) {
+               struct net_device *dev = hw->dev[i];
+               struct sky2_port *sky2 = netdev_priv(dev);
+               if (!netif_running(dev))
+                       continue;
+               sky2_hw_up(sky2);
+               sky2_set_multicast(dev);
+               imask |= portirq_msk[i];
+               netif_wake_queue(dev);
+       }
+       sky2_write32(hw, B0_IMSK, imask);
+       sky2_read32(hw, B0_IMSK);
+       sky2_read32(hw, B0_Y2_SP_LISR);
+       napi_enable(&hw->napi);
+ }
+ static void sky2_restart(struct work_struct *work)
+ {
+       struct sky2_hw *hw = container_of(work, struct sky2_hw, restart_work);
+       rtnl_lock();
+       sky2_all_down(hw);
+       sky2_reset(hw);
+       sky2_all_up(hw);
+       rtnl_unlock();
+ }
+ static inline u8 sky2_wol_supported(const struct sky2_hw *hw)
+ {
+       return sky2_is_copper(hw) ? (WAKE_PHY | WAKE_MAGIC) : 0;
+ }
+ static void sky2_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+ {
+       const struct sky2_port *sky2 = netdev_priv(dev);
+       wol->supported = sky2_wol_supported(sky2->hw);
+       wol->wolopts = sky2->wol;
+ }
+ static int sky2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       bool enable_wakeup = false;
+       int i;
+       if ((wol->wolopts & ~sky2_wol_supported(sky2->hw)) ||
+           !device_can_wakeup(&hw->pdev->dev))
+               return -EOPNOTSUPP;
+       sky2->wol = wol->wolopts;
+       for (i = 0; i < hw->ports; i++) {
+               struct net_device *dev = hw->dev[i];
+               struct sky2_port *sky2 = netdev_priv(dev);
+               if (sky2->wol)
+                       enable_wakeup = true;
+       }
+       device_set_wakeup_enable(&hw->pdev->dev, enable_wakeup);
+       return 0;
+ }
+ static u32 sky2_supported_modes(const struct sky2_hw *hw)
+ {
+       if (sky2_is_copper(hw)) {
+               u32 modes = SUPPORTED_10baseT_Half
+                       | SUPPORTED_10baseT_Full
+                       | SUPPORTED_100baseT_Half
+                       | SUPPORTED_100baseT_Full;
+               if (hw->flags & SKY2_HW_GIGABIT)
+                       modes |= SUPPORTED_1000baseT_Half
+                               | SUPPORTED_1000baseT_Full;
+               return modes;
+       } else
+               return SUPPORTED_1000baseT_Half
+                       | SUPPORTED_1000baseT_Full;
+ }
+ static int sky2_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       ecmd->transceiver = XCVR_INTERNAL;
+       ecmd->supported = sky2_supported_modes(hw);
+       ecmd->phy_address = PHY_ADDR_MARV;
+       if (sky2_is_copper(hw)) {
+               ecmd->port = PORT_TP;
+               ethtool_cmd_speed_set(ecmd, sky2->speed);
+               ecmd->supported |=  SUPPORTED_Autoneg | SUPPORTED_TP;
+       } else {
+               ethtool_cmd_speed_set(ecmd, SPEED_1000);
+               ecmd->port = PORT_FIBRE;
+               ecmd->supported |=  SUPPORTED_Autoneg | SUPPORTED_FIBRE;
+       }
+       ecmd->advertising = sky2->advertising;
+       ecmd->autoneg = (sky2->flags & SKY2_FLAG_AUTO_SPEED)
+               ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+       ecmd->duplex = sky2->duplex;
+       return 0;
+ }
+ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       const struct sky2_hw *hw = sky2->hw;
+       u32 supported = sky2_supported_modes(hw);
+       if (ecmd->autoneg == AUTONEG_ENABLE) {
+               if (ecmd->advertising & ~supported)
+                       return -EINVAL;
+               if (sky2_is_copper(hw))
+                       sky2->advertising = ecmd->advertising |
+                                           ADVERTISED_TP |
+                                           ADVERTISED_Autoneg;
+               else
+                       sky2->advertising = ecmd->advertising |
+                                           ADVERTISED_FIBRE |
+                                           ADVERTISED_Autoneg;
+               sky2->flags |= SKY2_FLAG_AUTO_SPEED;
+               sky2->duplex = -1;
+               sky2->speed = -1;
+       } else {
+               u32 setting;
+               u32 speed = ethtool_cmd_speed(ecmd);
+               switch (speed) {
+               case SPEED_1000:
+                       if (ecmd->duplex == DUPLEX_FULL)
+                               setting = SUPPORTED_1000baseT_Full;
+                       else if (ecmd->duplex == DUPLEX_HALF)
+                               setting = SUPPORTED_1000baseT_Half;
+                       else
+                               return -EINVAL;
+                       break;
+               case SPEED_100:
+                       if (ecmd->duplex == DUPLEX_FULL)
+                               setting = SUPPORTED_100baseT_Full;
+                       else if (ecmd->duplex == DUPLEX_HALF)
+                               setting = SUPPORTED_100baseT_Half;
+                       else
+                               return -EINVAL;
+                       break;
+               case SPEED_10:
+                       if (ecmd->duplex == DUPLEX_FULL)
+                               setting = SUPPORTED_10baseT_Full;
+                       else if (ecmd->duplex == DUPLEX_HALF)
+                               setting = SUPPORTED_10baseT_Half;
+                       else
+                               return -EINVAL;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               if ((setting & supported) == 0)
+                       return -EINVAL;
+               sky2->speed = speed;
+               sky2->duplex = ecmd->duplex;
+               sky2->flags &= ~SKY2_FLAG_AUTO_SPEED;
+       }
+       if (netif_running(dev)) {
+               sky2_phy_reinit(sky2);
+               sky2_set_multicast(dev);
+       }
+       return 0;
+ }
+ static void sky2_get_drvinfo(struct net_device *dev,
+                            struct ethtool_drvinfo *info)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       strcpy(info->driver, DRV_NAME);
+       strcpy(info->version, DRV_VERSION);
+       strcpy(info->fw_version, "N/A");
+       strcpy(info->bus_info, pci_name(sky2->hw->pdev));
+ }
+ static const struct sky2_stat {
+       char name[ETH_GSTRING_LEN];
+       u16 offset;
+ } sky2_stats[] = {
+       { "tx_bytes",      GM_TXO_OK_HI },
+       { "rx_bytes",      GM_RXO_OK_HI },
+       { "tx_broadcast",  GM_TXF_BC_OK },
+       { "rx_broadcast",  GM_RXF_BC_OK },
+       { "tx_multicast",  GM_TXF_MC_OK },
+       { "rx_multicast",  GM_RXF_MC_OK },
+       { "tx_unicast",    GM_TXF_UC_OK },
+       { "rx_unicast",    GM_RXF_UC_OK },
+       { "tx_mac_pause",  GM_TXF_MPAUSE },
+       { "rx_mac_pause",  GM_RXF_MPAUSE },
+       { "collisions",    GM_TXF_COL },
+       { "late_collision",GM_TXF_LAT_COL },
+       { "aborted",       GM_TXF_ABO_COL },
+       { "single_collisions", GM_TXF_SNG_COL },
+       { "multi_collisions", GM_TXF_MUL_COL },
+       { "rx_short",      GM_RXF_SHT },
+       { "rx_runt",       GM_RXE_FRAG },
+       { "rx_64_byte_packets", GM_RXF_64B },
+       { "rx_65_to_127_byte_packets", GM_RXF_127B },
+       { "rx_128_to_255_byte_packets", GM_RXF_255B },
+       { "rx_256_to_511_byte_packets", GM_RXF_511B },
+       { "rx_512_to_1023_byte_packets", GM_RXF_1023B },
+       { "rx_1024_to_1518_byte_packets", GM_RXF_1518B },
+       { "rx_1518_to_max_byte_packets", GM_RXF_MAX_SZ },
+       { "rx_too_long",   GM_RXF_LNG_ERR },
+       { "rx_fifo_overflow", GM_RXE_FIFO_OV },
+       { "rx_jabber",     GM_RXF_JAB_PKT },
+       { "rx_fcs_error",   GM_RXF_FCS_ERR },
+       { "tx_64_byte_packets", GM_TXF_64B },
+       { "tx_65_to_127_byte_packets", GM_TXF_127B },
+       { "tx_128_to_255_byte_packets", GM_TXF_255B },
+       { "tx_256_to_511_byte_packets", GM_TXF_511B },
+       { "tx_512_to_1023_byte_packets", GM_TXF_1023B },
+       { "tx_1024_to_1518_byte_packets", GM_TXF_1518B },
+       { "tx_1519_to_max_byte_packets", GM_TXF_MAX_SZ },
+       { "tx_fifo_underrun", GM_TXE_FIFO_UR },
+ };
+ static u32 sky2_get_msglevel(struct net_device *netdev)
+ {
+       struct sky2_port *sky2 = netdev_priv(netdev);
+       return sky2->msg_enable;
+ }
+ static int sky2_nway_reset(struct net_device *dev)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       if (!netif_running(dev) || !(sky2->flags & SKY2_FLAG_AUTO_SPEED))
+               return -EINVAL;
+       sky2_phy_reinit(sky2);
+       sky2_set_multicast(dev);
+       return 0;
+ }
+ static void sky2_phy_stats(struct sky2_port *sky2, u64 * data, unsigned count)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       int i;
+       data[0] = get_stats64(hw, port, GM_TXO_OK_LO);
+       data[1] = get_stats64(hw, port, GM_RXO_OK_LO);
+       for (i = 2; i < count; i++)
+               data[i] = get_stats32(hw, port, sky2_stats[i].offset);
+ }
+ static void sky2_set_msglevel(struct net_device *netdev, u32 value)
+ {
+       struct sky2_port *sky2 = netdev_priv(netdev);
+       sky2->msg_enable = value;
+ }
+ static int sky2_get_sset_count(struct net_device *dev, int sset)
+ {
+       switch (sset) {
+       case ETH_SS_STATS:
+               return ARRAY_SIZE(sky2_stats);
+       default:
+               return -EOPNOTSUPP;
+       }
+ }
+ static void sky2_get_ethtool_stats(struct net_device *dev,
+                                  struct ethtool_stats *stats, u64 * data)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       sky2_phy_stats(sky2, data, ARRAY_SIZE(sky2_stats));
+ }
+ static void sky2_get_strings(struct net_device *dev, u32 stringset, u8 * data)
+ {
+       int i;
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < ARRAY_SIZE(sky2_stats); i++)
+                       memcpy(data + i * ETH_GSTRING_LEN,
+                              sky2_stats[i].name, ETH_GSTRING_LEN);
+               break;
+       }
+ }
+ static int sky2_set_mac_address(struct net_device *dev, void *p)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       const struct sockaddr *addr = p;
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+       memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+       memcpy_toio(hw->regs + B2_MAC_1 + port * 8,
+                   dev->dev_addr, ETH_ALEN);
+       memcpy_toio(hw->regs + B2_MAC_2 + port * 8,
+                   dev->dev_addr, ETH_ALEN);
+       /* virtual address for data */
+       gma_set_addr(hw, port, GM_SRC_ADDR_2L, dev->dev_addr);
+       /* physical address: used for pause frames */
+       gma_set_addr(hw, port, GM_SRC_ADDR_1L, dev->dev_addr);
+       return 0;
+ }
+ static inline void sky2_add_filter(u8 filter[8], const u8 *addr)
+ {
+       u32 bit;
+       bit = ether_crc(ETH_ALEN, addr) & 63;
+       filter[bit >> 3] |= 1 << (bit & 7);
+ }
+ static void sky2_set_multicast(struct net_device *dev)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       struct netdev_hw_addr *ha;
+       u16 reg;
+       u8 filter[8];
+       int rx_pause;
+       static const u8 pause_mc_addr[ETH_ALEN] = { 0x1, 0x80, 0xc2, 0x0, 0x0, 0x1 };
+       rx_pause = (sky2->flow_status == FC_RX || sky2->flow_status == FC_BOTH);
+       memset(filter, 0, sizeof(filter));
+       reg = gma_read16(hw, port, GM_RX_CTRL);
+       reg |= GM_RXCR_UCF_ENA;
+       if (dev->flags & IFF_PROMISC)   /* promiscuous */
+               reg &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA);
+       else if (dev->flags & IFF_ALLMULTI)
+               memset(filter, 0xff, sizeof(filter));
+       else if (netdev_mc_empty(dev) && !rx_pause)
+               reg &= ~GM_RXCR_MCF_ENA;
+       else {
+               reg |= GM_RXCR_MCF_ENA;
+               if (rx_pause)
+                       sky2_add_filter(filter, pause_mc_addr);
+               netdev_for_each_mc_addr(ha, dev)
+                       sky2_add_filter(filter, ha->addr);
+       }
+       gma_write16(hw, port, GM_MC_ADDR_H1,
+                   (u16) filter[0] | ((u16) filter[1] << 8));
+       gma_write16(hw, port, GM_MC_ADDR_H2,
+                   (u16) filter[2] | ((u16) filter[3] << 8));
+       gma_write16(hw, port, GM_MC_ADDR_H3,
+                   (u16) filter[4] | ((u16) filter[5] << 8));
+       gma_write16(hw, port, GM_MC_ADDR_H4,
+                   (u16) filter[6] | ((u16) filter[7] << 8));
+       gma_write16(hw, port, GM_RX_CTRL, reg);
+ }
+ static struct rtnl_link_stats64 *sky2_get_stats(struct net_device *dev,
+                                               struct rtnl_link_stats64 *stats)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       unsigned int start;
+       u64 _bytes, _packets;
+       do {
+               start = u64_stats_fetch_begin_bh(&sky2->rx_stats.syncp);
+               _bytes = sky2->rx_stats.bytes;
+               _packets = sky2->rx_stats.packets;
+       } while (u64_stats_fetch_retry_bh(&sky2->rx_stats.syncp, start));
+       stats->rx_packets = _packets;
+       stats->rx_bytes = _bytes;
+       do {
+               start = u64_stats_fetch_begin_bh(&sky2->tx_stats.syncp);
+               _bytes = sky2->tx_stats.bytes;
+               _packets = sky2->tx_stats.packets;
+       } while (u64_stats_fetch_retry_bh(&sky2->tx_stats.syncp, start));
+       stats->tx_packets = _packets;
+       stats->tx_bytes = _bytes;
+       stats->multicast = get_stats32(hw, port, GM_RXF_MC_OK)
+               + get_stats32(hw, port, GM_RXF_BC_OK);
+       stats->collisions = get_stats32(hw, port, GM_TXF_COL);
+       stats->rx_length_errors = get_stats32(hw, port, GM_RXF_LNG_ERR);
+       stats->rx_crc_errors = get_stats32(hw, port, GM_RXF_FCS_ERR);
+       stats->rx_frame_errors = get_stats32(hw, port, GM_RXF_SHT)
+               + get_stats32(hw, port, GM_RXE_FRAG);
+       stats->rx_over_errors = get_stats32(hw, port, GM_RXE_FIFO_OV);
+       stats->rx_dropped = dev->stats.rx_dropped;
+       stats->rx_fifo_errors = dev->stats.rx_fifo_errors;
+       stats->tx_fifo_errors = dev->stats.tx_fifo_errors;
+       return stats;
+ }
+ /* Can have one global because blinking is controlled by
+  * ethtool and that is always under RTNL mutex
+  */
+ static void sky2_led(struct sky2_port *sky2, enum led_mode mode)
+ {
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       spin_lock_bh(&sky2->phy_lock);
+       if (hw->chip_id == CHIP_ID_YUKON_EC_U ||
+           hw->chip_id == CHIP_ID_YUKON_EX ||
+           hw->chip_id == CHIP_ID_YUKON_SUPR) {
+               u16 pg;
+               pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
+               switch (mode) {
+               case MO_LED_OFF:
+                       gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+                                    PHY_M_LEDC_LOS_CTRL(8) |
+                                    PHY_M_LEDC_INIT_CTRL(8) |
+                                    PHY_M_LEDC_STA1_CTRL(8) |
+                                    PHY_M_LEDC_STA0_CTRL(8));
+                       break;
+               case MO_LED_ON:
+                       gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+                                    PHY_M_LEDC_LOS_CTRL(9) |
+                                    PHY_M_LEDC_INIT_CTRL(9) |
+                                    PHY_M_LEDC_STA1_CTRL(9) |
+                                    PHY_M_LEDC_STA0_CTRL(9));
+                       break;
+               case MO_LED_BLINK:
+                       gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+                                    PHY_M_LEDC_LOS_CTRL(0xa) |
+                                    PHY_M_LEDC_INIT_CTRL(0xa) |
+                                    PHY_M_LEDC_STA1_CTRL(0xa) |
+                                    PHY_M_LEDC_STA0_CTRL(0xa));
+                       break;
+               case MO_LED_NORM:
+                       gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+                                    PHY_M_LEDC_LOS_CTRL(1) |
+                                    PHY_M_LEDC_INIT_CTRL(8) |
+                                    PHY_M_LEDC_STA1_CTRL(7) |
+                                    PHY_M_LEDC_STA0_CTRL(7));
+               }
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
+       } else
+               gm_phy_write(hw, port, PHY_MARV_LED_OVER,
+                                    PHY_M_LED_MO_DUP(mode) |
+                                    PHY_M_LED_MO_10(mode) |
+                                    PHY_M_LED_MO_100(mode) |
+                                    PHY_M_LED_MO_1000(mode) |
+                                    PHY_M_LED_MO_RX(mode) |
+                                    PHY_M_LED_MO_TX(mode));
+       spin_unlock_bh(&sky2->phy_lock);
+ }
+ /* blink LED's for finding board */
+ static int sky2_set_phys_id(struct net_device *dev,
+                           enum ethtool_phys_id_state state)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       switch (state) {
+       case ETHTOOL_ID_ACTIVE:
+               return 1;       /* cycle on/off once per second */
+       case ETHTOOL_ID_INACTIVE:
+               sky2_led(sky2, MO_LED_NORM);
+               break;
+       case ETHTOOL_ID_ON:
+               sky2_led(sky2, MO_LED_ON);
+               break;
+       case ETHTOOL_ID_OFF:
+               sky2_led(sky2, MO_LED_OFF);
+               break;
+       }
+       return 0;
+ }
+ static void sky2_get_pauseparam(struct net_device *dev,
+                               struct ethtool_pauseparam *ecmd)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       switch (sky2->flow_mode) {
+       case FC_NONE:
+               ecmd->tx_pause = ecmd->rx_pause = 0;
+               break;
+       case FC_TX:
+               ecmd->tx_pause = 1, ecmd->rx_pause = 0;
+               break;
+       case FC_RX:
+               ecmd->tx_pause = 0, ecmd->rx_pause = 1;
+               break;
+       case FC_BOTH:
+               ecmd->tx_pause = ecmd->rx_pause = 1;
+       }
+       ecmd->autoneg = (sky2->flags & SKY2_FLAG_AUTO_PAUSE)
+               ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+ }
+ static int sky2_set_pauseparam(struct net_device *dev,
+                              struct ethtool_pauseparam *ecmd)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       if (ecmd->autoneg == AUTONEG_ENABLE)
+               sky2->flags |= SKY2_FLAG_AUTO_PAUSE;
+       else
+               sky2->flags &= ~SKY2_FLAG_AUTO_PAUSE;
+       sky2->flow_mode = sky2_flow(ecmd->rx_pause, ecmd->tx_pause);
+       if (netif_running(dev))
+               sky2_phy_reinit(sky2);
+       return 0;
+ }
+ static int sky2_get_coalesce(struct net_device *dev,
+                            struct ethtool_coalesce *ecmd)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       if (sky2_read8(hw, STAT_TX_TIMER_CTRL) == TIM_STOP)
+               ecmd->tx_coalesce_usecs = 0;
+       else {
+               u32 clks = sky2_read32(hw, STAT_TX_TIMER_INI);
+               ecmd->tx_coalesce_usecs = sky2_clk2us(hw, clks);
+       }
+       ecmd->tx_max_coalesced_frames = sky2_read16(hw, STAT_TX_IDX_TH);
+       if (sky2_read8(hw, STAT_LEV_TIMER_CTRL) == TIM_STOP)
+               ecmd->rx_coalesce_usecs = 0;
+       else {
+               u32 clks = sky2_read32(hw, STAT_LEV_TIMER_INI);
+               ecmd->rx_coalesce_usecs = sky2_clk2us(hw, clks);
+       }
+       ecmd->rx_max_coalesced_frames = sky2_read8(hw, STAT_FIFO_WM);
+       if (sky2_read8(hw, STAT_ISR_TIMER_CTRL) == TIM_STOP)
+               ecmd->rx_coalesce_usecs_irq = 0;
+       else {
+               u32 clks = sky2_read32(hw, STAT_ISR_TIMER_INI);
+               ecmd->rx_coalesce_usecs_irq = sky2_clk2us(hw, clks);
+       }
+       ecmd->rx_max_coalesced_frames_irq = sky2_read8(hw, STAT_FIFO_ISR_WM);
+       return 0;
+ }
+ /* Note: this affect both ports */
+ static int sky2_set_coalesce(struct net_device *dev,
+                            struct ethtool_coalesce *ecmd)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       const u32 tmax = sky2_clk2us(hw, 0x0ffffff);
+       if (ecmd->tx_coalesce_usecs > tmax ||
+           ecmd->rx_coalesce_usecs > tmax ||
+           ecmd->rx_coalesce_usecs_irq > tmax)
+               return -EINVAL;
+       if (ecmd->tx_max_coalesced_frames >= sky2->tx_ring_size-1)
+               return -EINVAL;
+       if (ecmd->rx_max_coalesced_frames > RX_MAX_PENDING)
+               return -EINVAL;
+       if (ecmd->rx_max_coalesced_frames_irq > RX_MAX_PENDING)
+               return -EINVAL;
+       if (ecmd->tx_coalesce_usecs == 0)
+               sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP);
+       else {
+               sky2_write32(hw, STAT_TX_TIMER_INI,
+                            sky2_us2clk(hw, ecmd->tx_coalesce_usecs));
+               sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START);
+       }
+       sky2_write16(hw, STAT_TX_IDX_TH, ecmd->tx_max_coalesced_frames);
+       if (ecmd->rx_coalesce_usecs == 0)
+               sky2_write8(hw, STAT_LEV_TIMER_CTRL, TIM_STOP);
+       else {
+               sky2_write32(hw, STAT_LEV_TIMER_INI,
+                            sky2_us2clk(hw, ecmd->rx_coalesce_usecs));
+               sky2_write8(hw, STAT_LEV_TIMER_CTRL, TIM_START);
+       }
+       sky2_write8(hw, STAT_FIFO_WM, ecmd->rx_max_coalesced_frames);
+       if (ecmd->rx_coalesce_usecs_irq == 0)
+               sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_STOP);
+       else {
+               sky2_write32(hw, STAT_ISR_TIMER_INI,
+                            sky2_us2clk(hw, ecmd->rx_coalesce_usecs_irq));
+               sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START);
+       }
+       sky2_write8(hw, STAT_FIFO_ISR_WM, ecmd->rx_max_coalesced_frames_irq);
+       return 0;
+ }
+ static void sky2_get_ringparam(struct net_device *dev,
+                              struct ethtool_ringparam *ering)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       ering->rx_max_pending = RX_MAX_PENDING;
+       ering->tx_max_pending = TX_MAX_PENDING;
+       ering->rx_pending = sky2->rx_pending;
+       ering->tx_pending = sky2->tx_pending;
+ }
+ static int sky2_set_ringparam(struct net_device *dev,
+                             struct ethtool_ringparam *ering)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       if (ering->rx_pending > RX_MAX_PENDING ||
+           ering->rx_pending < 8 ||
+           ering->tx_pending < TX_MIN_PENDING ||
+           ering->tx_pending > TX_MAX_PENDING)
+               return -EINVAL;
+       sky2_detach(dev);
+       sky2->rx_pending = ering->rx_pending;
+       sky2->tx_pending = ering->tx_pending;
+       sky2->tx_ring_size = roundup_pow_of_two(sky2->tx_pending+1);
+       return sky2_reattach(dev);
+ }
+ static int sky2_get_regs_len(struct net_device *dev)
+ {
+       return 0x4000;
+ }
+ static int sky2_reg_access_ok(struct sky2_hw *hw, unsigned int b)
+ {
+       /* This complicated switch statement is to make sure and
+        * only access regions that are unreserved.
+        * Some blocks are only valid on dual port cards.
+        */
+       switch (b) {
+       /* second port */
+       case 5:         /* Tx Arbiter 2 */
+       case 9:         /* RX2 */
+       case 14 ... 15: /* TX2 */
+       case 17: case 19: /* Ram Buffer 2 */
+       case 22 ... 23: /* Tx Ram Buffer 2 */
+       case 25:        /* Rx MAC Fifo 1 */
+       case 27:        /* Tx MAC Fifo 2 */
+       case 31:        /* GPHY 2 */
+       case 40 ... 47: /* Pattern Ram 2 */
+       case 52: case 54: /* TCP Segmentation 2 */
+       case 112 ... 116: /* GMAC 2 */
+               return hw->ports > 1;
+       case 0:         /* Control */
+       case 2:         /* Mac address */
+       case 4:         /* Tx Arbiter 1 */
+       case 7:         /* PCI express reg */
+       case 8:         /* RX1 */
+       case 12 ... 13: /* TX1 */
+       case 16: case 18:/* Rx Ram Buffer 1 */
+       case 20 ... 21: /* Tx Ram Buffer 1 */
+       case 24:        /* Rx MAC Fifo 1 */
+       case 26:        /* Tx MAC Fifo 1 */
+       case 28 ... 29: /* Descriptor and status unit */
+       case 30:        /* GPHY 1*/
+       case 32 ... 39: /* Pattern Ram 1 */
+       case 48: case 50: /* TCP Segmentation 1 */
+       case 56 ... 60: /* PCI space */
+       case 80 ... 84: /* GMAC 1 */
+               return 1;
+       default:
+               return 0;
+       }
+ }
+ /*
+  * Returns copy of control register region
+  * Note: ethtool_get_regs always provides full size (16k) buffer
+  */
+ static void sky2_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+                         void *p)
+ {
+       const struct sky2_port *sky2 = netdev_priv(dev);
+       const void __iomem *io = sky2->hw->regs;
+       unsigned int b;
+       regs->version = 1;
+       for (b = 0; b < 128; b++) {
+               /* skip poisonous diagnostic ram region in block 3 */
+               if (b == 3)
+                       memcpy_fromio(p + 0x10, io + 0x10, 128 - 0x10);
+               else if (sky2_reg_access_ok(sky2->hw, b))
+                       memcpy_fromio(p, io, 128);
+               else
+                       memset(p, 0, 128);
+               p += 128;
+               io += 128;
+       }
+ }
+ static int sky2_get_eeprom_len(struct net_device *dev)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       u16 reg2;
+       reg2 = sky2_pci_read16(hw, PCI_DEV_REG2);
+       return 1 << ( ((reg2 & PCI_VPD_ROM_SZ) >> 14) + 8);
+ }
+ static int sky2_vpd_wait(const struct sky2_hw *hw, int cap, u16 busy)
+ {
+       unsigned long start = jiffies;
+       while ( (sky2_pci_read16(hw, cap + PCI_VPD_ADDR) & PCI_VPD_ADDR_F) == busy) {
+               /* Can take up to 10.6 ms for write */
+               if (time_after(jiffies, start + HZ/4)) {
+                       dev_err(&hw->pdev->dev, "VPD cycle timed out\n");
+                       return -ETIMEDOUT;
+               }
+               mdelay(1);
+       }
+       return 0;
+ }
+ static int sky2_vpd_read(struct sky2_hw *hw, int cap, void *data,
+                        u16 offset, size_t length)
+ {
+       int rc = 0;
+       while (length > 0) {
+               u32 val;
+               sky2_pci_write16(hw, cap + PCI_VPD_ADDR, offset);
+               rc = sky2_vpd_wait(hw, cap, 0);
+               if (rc)
+                       break;
+               val = sky2_pci_read32(hw, cap + PCI_VPD_DATA);
+               memcpy(data, &val, min(sizeof(val), length));
+               offset += sizeof(u32);
+               data += sizeof(u32);
+               length -= sizeof(u32);
+       }
+       return rc;
+ }
+ static int sky2_vpd_write(struct sky2_hw *hw, int cap, const void *data,
+                         u16 offset, unsigned int length)
+ {
+       unsigned int i;
+       int rc = 0;
+       for (i = 0; i < length; i += sizeof(u32)) {
+               u32 val = *(u32 *)(data + i);
+               sky2_pci_write32(hw, cap + PCI_VPD_DATA, val);
+               sky2_pci_write32(hw, cap + PCI_VPD_ADDR, offset | PCI_VPD_ADDR_F);
+               rc = sky2_vpd_wait(hw, cap, PCI_VPD_ADDR_F);
+               if (rc)
+                       break;
+       }
+       return rc;
+ }
+ static int sky2_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
+                          u8 *data)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       int cap = pci_find_capability(sky2->hw->pdev, PCI_CAP_ID_VPD);
+       if (!cap)
+               return -EINVAL;
+       eeprom->magic = SKY2_EEPROM_MAGIC;
+       return sky2_vpd_read(sky2->hw, cap, data, eeprom->offset, eeprom->len);
+ }
+ static int sky2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
+                          u8 *data)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       int cap = pci_find_capability(sky2->hw->pdev, PCI_CAP_ID_VPD);
+       if (!cap)
+               return -EINVAL;
+       if (eeprom->magic != SKY2_EEPROM_MAGIC)
+               return -EINVAL;
+       /* Partial writes not supported */
+       if ((eeprom->offset & 3) || (eeprom->len & 3))
+               return -EINVAL;
+       return sky2_vpd_write(sky2->hw, cap, data, eeprom->offset, eeprom->len);
+ }
+ static u32 sky2_fix_features(struct net_device *dev, u32 features)
+ {
+       const struct sky2_port *sky2 = netdev_priv(dev);
+       const struct sky2_hw *hw = sky2->hw;
+       /* In order to do Jumbo packets on these chips, need to turn off the
+        * transmit store/forward. Therefore checksum offload won't work.
+        */
+       if (dev->mtu > ETH_DATA_LEN && hw->chip_id == CHIP_ID_YUKON_EC_U) {
+               netdev_info(dev, "checksum offload not possible with jumbo frames\n");
+               features &= ~(NETIF_F_TSO|NETIF_F_SG|NETIF_F_ALL_CSUM);
+       }
+       /* Some hardware requires receive checksum for RSS to work. */
+       if ( (features & NETIF_F_RXHASH) &&
+            !(features & NETIF_F_RXCSUM) &&
+            (sky2->hw->flags & SKY2_HW_RSS_CHKSUM)) {
+               netdev_info(dev, "receive hashing forces receive checksum\n");
+               features |= NETIF_F_RXCSUM;
+       }
+       return features;
+ }
+ static int sky2_set_features(struct net_device *dev, u32 features)
+ {
+       struct sky2_port *sky2 = netdev_priv(dev);
+       u32 changed = dev->features ^ features;
+       if (changed & NETIF_F_RXCSUM) {
+               u32 on = features & NETIF_F_RXCSUM;
+               sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR),
+                            on ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
+       }
+       if (changed & NETIF_F_RXHASH)
+               rx_set_rss(dev, features);
+       if (changed & (NETIF_F_HW_VLAN_TX|NETIF_F_HW_VLAN_RX))
+               sky2_vlan_mode(dev, features);
+       return 0;
+ }
+ static const struct ethtool_ops sky2_ethtool_ops = {
+       .get_settings   = sky2_get_settings,
+       .set_settings   = sky2_set_settings,
+       .get_drvinfo    = sky2_get_drvinfo,
+       .get_wol        = sky2_get_wol,
+       .set_wol        = sky2_set_wol,
+       .get_msglevel   = sky2_get_msglevel,
+       .set_msglevel   = sky2_set_msglevel,
+       .nway_reset     = sky2_nway_reset,
+       .get_regs_len   = sky2_get_regs_len,
+       .get_regs       = sky2_get_regs,
+       .get_link       = ethtool_op_get_link,
+       .get_eeprom_len = sky2_get_eeprom_len,
+       .get_eeprom     = sky2_get_eeprom,
+       .set_eeprom     = sky2_set_eeprom,
+       .get_strings    = sky2_get_strings,
+       .get_coalesce   = sky2_get_coalesce,
+       .set_coalesce   = sky2_set_coalesce,
+       .get_ringparam  = sky2_get_ringparam,
+       .set_ringparam  = sky2_set_ringparam,
+       .get_pauseparam = sky2_get_pauseparam,
+       .set_pauseparam = sky2_set_pauseparam,
+       .set_phys_id    = sky2_set_phys_id,
+       .get_sset_count = sky2_get_sset_count,
+       .get_ethtool_stats = sky2_get_ethtool_stats,
+ };
+ #ifdef CONFIG_SKY2_DEBUG
+ static struct dentry *sky2_debug;
+ /*
+  * Read and parse the first part of Vital Product Data
+  */
+ #define VPD_SIZE      128
+ #define VPD_MAGIC     0x82
+ static const struct vpd_tag {
+       char tag[2];
+       char *label;
+ } vpd_tags[] = {
+       { "PN", "Part Number" },
+       { "EC", "Engineering Level" },
+       { "MN", "Manufacturer" },
+       { "SN", "Serial Number" },
+       { "YA", "Asset Tag" },
+       { "VL", "First Error Log Message" },
+       { "VF", "Second Error Log Message" },
+       { "VB", "Boot Agent ROM Configuration" },
+       { "VE", "EFI UNDI Configuration" },
+ };
+ static void sky2_show_vpd(struct seq_file *seq, struct sky2_hw *hw)
+ {
+       size_t vpd_size;
+       loff_t offs;
+       u8 len;
+       unsigned char *buf;
+       u16 reg2;
+       reg2 = sky2_pci_read16(hw, PCI_DEV_REG2);
+       vpd_size = 1 << ( ((reg2 & PCI_VPD_ROM_SZ) >> 14) + 8);
+       seq_printf(seq, "%s Product Data\n", pci_name(hw->pdev));
+       buf = kmalloc(vpd_size, GFP_KERNEL);
+       if (!buf) {
+               seq_puts(seq, "no memory!\n");
+               return;
+       }
+       if (pci_read_vpd(hw->pdev, 0, vpd_size, buf) < 0) {
+               seq_puts(seq, "VPD read failed\n");
+               goto out;
+       }
+       if (buf[0] != VPD_MAGIC) {
+               seq_printf(seq, "VPD tag mismatch: %#x\n", buf[0]);
+               goto out;
+       }
+       len = buf[1];
+       if (len == 0 || len > vpd_size - 4) {
+               seq_printf(seq, "Invalid id length: %d\n", len);
+               goto out;
+       }
+       seq_printf(seq, "%.*s\n", len, buf + 3);
+       offs = len + 3;
+       while (offs < vpd_size - 4) {
+               int i;
+               if (!memcmp("RW", buf + offs, 2))       /* end marker */
+                       break;
+               len = buf[offs + 2];
+               if (offs + len + 3 >= vpd_size)
+                       break;
+               for (i = 0; i < ARRAY_SIZE(vpd_tags); i++) {
+                       if (!memcmp(vpd_tags[i].tag, buf + offs, 2)) {
+                               seq_printf(seq, " %s: %.*s\n",
+                                          vpd_tags[i].label, len, buf + offs + 3);
+                               break;
+                       }
+               }
+               offs += len + 3;
+       }
+ out:
+       kfree(buf);
+ }
+ static int sky2_debug_show(struct seq_file *seq, void *v)
+ {
+       struct net_device *dev = seq->private;
+       const struct sky2_port *sky2 = netdev_priv(dev);
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       unsigned idx, last;
+       int sop;
+       sky2_show_vpd(seq, hw);
+       seq_printf(seq, "\nIRQ src=%x mask=%x control=%x\n",
+                  sky2_read32(hw, B0_ISRC),
+                  sky2_read32(hw, B0_IMSK),
+                  sky2_read32(hw, B0_Y2_SP_ICR));
+       if (!netif_running(dev)) {
+               seq_printf(seq, "network not running\n");
+               return 0;
+       }
+       napi_disable(&hw->napi);
+       last = sky2_read16(hw, STAT_PUT_IDX);
+       seq_printf(seq, "Status ring %u\n", hw->st_size);
+       if (hw->st_idx == last)
+               seq_puts(seq, "Status ring (empty)\n");
+       else {
+               seq_puts(seq, "Status ring\n");
+               for (idx = hw->st_idx; idx != last && idx < hw->st_size;
+                    idx = RING_NEXT(idx, hw->st_size)) {
+                       const struct sky2_status_le *le = hw->st_le + idx;
+                       seq_printf(seq, "[%d] %#x %d %#x\n",
+                                  idx, le->opcode, le->length, le->status);
+               }
+               seq_puts(seq, "\n");
+       }
+       seq_printf(seq, "Tx ring pending=%u...%u report=%d done=%d\n",
+                  sky2->tx_cons, sky2->tx_prod,
+                  sky2_read16(hw, port == 0 ? STAT_TXA1_RIDX : STAT_TXA2_RIDX),
+                  sky2_read16(hw, Q_ADDR(txqaddr[port], Q_DONE)));
+       /* Dump contents of tx ring */
+       sop = 1;
+       for (idx = sky2->tx_next; idx != sky2->tx_prod && idx < sky2->tx_ring_size;
+            idx = RING_NEXT(idx, sky2->tx_ring_size)) {
+               const struct sky2_tx_le *le = sky2->tx_le + idx;
+               u32 a = le32_to_cpu(le->addr);
+               if (sop)
+                       seq_printf(seq, "%u:", idx);
+               sop = 0;
+               switch (le->opcode & ~HW_OWNER) {
+               case OP_ADDR64:
+                       seq_printf(seq, " %#x:", a);
+                       break;
+               case OP_LRGLEN:
+                       seq_printf(seq, " mtu=%d", a);
+                       break;
+               case OP_VLAN:
+                       seq_printf(seq, " vlan=%d", be16_to_cpu(le->length));
+                       break;
+               case OP_TCPLISW:
+                       seq_printf(seq, " csum=%#x", a);
+                       break;
+               case OP_LARGESEND:
+                       seq_printf(seq, " tso=%#x(%d)", a, le16_to_cpu(le->length));
+                       break;
+               case OP_PACKET:
+                       seq_printf(seq, " %#x(%d)", a, le16_to_cpu(le->length));
+                       break;
+               case OP_BUFFER:
+                       seq_printf(seq, " frag=%#x(%d)", a, le16_to_cpu(le->length));
+                       break;
+               default:
+                       seq_printf(seq, " op=%#x,%#x(%d)", le->opcode,
+                                  a, le16_to_cpu(le->length));
+               }
+               if (le->ctrl & EOP) {
+                       seq_putc(seq, '\n');
+                       sop = 1;
+               }
+       }
+       seq_printf(seq, "\nRx ring hw get=%d put=%d last=%d\n",
+                  sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_GET_IDX)),
+                  sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX)),
+                  sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_LAST_IDX)));
+       sky2_read32(hw, B0_Y2_SP_LISR);
+       napi_enable(&hw->napi);
+       return 0;
+ }
+ static int sky2_debug_open(struct inode *inode, struct file *file)
+ {
+       return single_open(file, sky2_debug_show, inode->i_private);
+ }
+ static const struct file_operations sky2_debug_fops = {
+       .owner          = THIS_MODULE,
+       .open           = sky2_debug_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+ };
+ /*
+  * Use network device events to create/remove/rename
+  * debugfs file entries
+  */
+ static int sky2_device_event(struct notifier_block *unused,
+                            unsigned long event, void *ptr)
+ {
+       struct net_device *dev = ptr;
+       struct sky2_port *sky2 = netdev_priv(dev);
+       if (dev->netdev_ops->ndo_open != sky2_up || !sky2_debug)
+               return NOTIFY_DONE;
+       switch (event) {
+       case NETDEV_CHANGENAME:
+               if (sky2->debugfs) {
+                       sky2->debugfs = debugfs_rename(sky2_debug, sky2->debugfs,
+                                                      sky2_debug, dev->name);
+               }
+               break;
+       case NETDEV_GOING_DOWN:
+               if (sky2->debugfs) {
+                       netdev_printk(KERN_DEBUG, dev, "remove debugfs\n");
+                       debugfs_remove(sky2->debugfs);
+                       sky2->debugfs = NULL;
+               }
+               break;
+       case NETDEV_UP:
+               sky2->debugfs = debugfs_create_file(dev->name, S_IRUGO,
+                                                   sky2_debug, dev,
+                                                   &sky2_debug_fops);
+               if (IS_ERR(sky2->debugfs))
+                       sky2->debugfs = NULL;
+       }
+       return NOTIFY_DONE;
+ }
+ static struct notifier_block sky2_notifier = {
+       .notifier_call = sky2_device_event,
+ };
+ static __init void sky2_debug_init(void)
+ {
+       struct dentry *ent;
+       ent = debugfs_create_dir("sky2", NULL);
+       if (!ent || IS_ERR(ent))
+               return;
+       sky2_debug = ent;
+       register_netdevice_notifier(&sky2_notifier);
+ }
+ static __exit void sky2_debug_cleanup(void)
+ {
+       if (sky2_debug) {
+               unregister_netdevice_notifier(&sky2_notifier);
+               debugfs_remove(sky2_debug);
+               sky2_debug = NULL;
+       }
+ }
+ #else
+ #define sky2_debug_init()
+ #define sky2_debug_cleanup()
+ #endif
+ /* Two copies of network device operations to handle special case of
+    not allowing netpoll on second port */
+ static const struct net_device_ops sky2_netdev_ops[2] = {
+   {
+       .ndo_open               = sky2_up,
+       .ndo_stop               = sky2_down,
+       .ndo_start_xmit         = sky2_xmit_frame,
+       .ndo_do_ioctl           = sky2_ioctl,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = sky2_set_mac_address,
+       .ndo_set_rx_mode        = sky2_set_multicast,
+       .ndo_change_mtu         = sky2_change_mtu,
+       .ndo_fix_features       = sky2_fix_features,
+       .ndo_set_features       = sky2_set_features,
+       .ndo_tx_timeout         = sky2_tx_timeout,
+       .ndo_get_stats64        = sky2_get_stats,
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = sky2_netpoll,
+ #endif
+   },
+   {
+       .ndo_open               = sky2_up,
+       .ndo_stop               = sky2_down,
+       .ndo_start_xmit         = sky2_xmit_frame,
+       .ndo_do_ioctl           = sky2_ioctl,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = sky2_set_mac_address,
+       .ndo_set_rx_mode        = sky2_set_multicast,
+       .ndo_change_mtu         = sky2_change_mtu,
+       .ndo_fix_features       = sky2_fix_features,
+       .ndo_set_features       = sky2_set_features,
+       .ndo_tx_timeout         = sky2_tx_timeout,
+       .ndo_get_stats64        = sky2_get_stats,
+   },
+ };
+ /* Initialize network device */
+ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
+                                                    unsigned port,
+                                                    int highmem, int wol)
+ {
+       struct sky2_port *sky2;
+       struct net_device *dev = alloc_etherdev(sizeof(*sky2));
+       if (!dev) {
+               dev_err(&hw->pdev->dev, "etherdev alloc failed\n");
+               return NULL;
+       }
+       SET_NETDEV_DEV(dev, &hw->pdev->dev);
+       dev->irq = hw->pdev->irq;
+       SET_ETHTOOL_OPS(dev, &sky2_ethtool_ops);
+       dev->watchdog_timeo = TX_WATCHDOG;
+       dev->netdev_ops = &sky2_netdev_ops[port];
+       sky2 = netdev_priv(dev);
+       sky2->netdev = dev;
+       sky2->hw = hw;
+       sky2->msg_enable = netif_msg_init(debug, default_msg);
+       /* Auto speed and flow control */
+       sky2->flags = SKY2_FLAG_AUTO_SPEED | SKY2_FLAG_AUTO_PAUSE;
+       if (hw->chip_id != CHIP_ID_YUKON_XL)
+               dev->hw_features |= NETIF_F_RXCSUM;
+       sky2->flow_mode = FC_BOTH;
+       sky2->duplex = -1;
+       sky2->speed = -1;
+       sky2->advertising = sky2_supported_modes(hw);
+       sky2->wol = wol;
+       spin_lock_init(&sky2->phy_lock);
+       sky2->tx_pending = TX_DEF_PENDING;
+       sky2->tx_ring_size = roundup_pow_of_two(TX_DEF_PENDING+1);
+       sky2->rx_pending = RX_DEF_PENDING;
+       hw->dev[port] = dev;
+       sky2->port = port;
+       dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO;
+       if (highmem)
+               dev->features |= NETIF_F_HIGHDMA;
+       /* Enable receive hashing unless hardware is known broken */
+       if (!(hw->flags & SKY2_HW_RSS_BROKEN))
+               dev->hw_features |= NETIF_F_RXHASH;
+       if (!(hw->flags & SKY2_HW_VLAN_BROKEN)) {
+               dev->hw_features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+               dev->vlan_features |= SKY2_VLAN_OFFLOADS;
+       }
+       dev->features |= dev->hw_features;
+       /* read the mac address */
+       memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN);
+       memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
+       return dev;
+ }
+ static void __devinit sky2_show_addr(struct net_device *dev)
+ {
+       const struct sky2_port *sky2 = netdev_priv(dev);
+       netif_info(sky2, probe, dev, "addr %pM\n", dev->dev_addr);
+ }
+ /* Handle software interrupt used during MSI test */
+ static irqreturn_t __devinit sky2_test_intr(int irq, void *dev_id)
+ {
+       struct sky2_hw *hw = dev_id;
+       u32 status = sky2_read32(hw, B0_Y2_SP_ISRC2);
+       if (status == 0)
+               return IRQ_NONE;
+       if (status & Y2_IS_IRQ_SW) {
+               hw->flags |= SKY2_HW_USE_MSI;
+               wake_up(&hw->msi_wait);
+               sky2_write8(hw, B0_CTST, CS_CL_SW_IRQ);
+       }
+       sky2_write32(hw, B0_Y2_SP_ICR, 2);
+       return IRQ_HANDLED;
+ }
+ /* Test interrupt path by forcing a a software IRQ */
+ static int __devinit sky2_test_msi(struct sky2_hw *hw)
+ {
+       struct pci_dev *pdev = hw->pdev;
+       int err;
+       init_waitqueue_head(&hw->msi_wait);
+       sky2_write32(hw, B0_IMSK, Y2_IS_IRQ_SW);
+       err = request_irq(pdev->irq, sky2_test_intr, 0, DRV_NAME, hw);
+       if (err) {
+               dev_err(&pdev->dev, "cannot assign irq %d\n", pdev->irq);
+               return err;
+       }
+       sky2_write8(hw, B0_CTST, CS_ST_SW_IRQ);
+       sky2_read8(hw, B0_CTST);
+       wait_event_timeout(hw->msi_wait, (hw->flags & SKY2_HW_USE_MSI), HZ/10);
+       if (!(hw->flags & SKY2_HW_USE_MSI)) {
+               /* MSI test failed, go back to INTx mode */
+               dev_info(&pdev->dev, "No interrupt generated using MSI, "
+                        "switching to INTx mode.\n");
+               err = -EOPNOTSUPP;
+               sky2_write8(hw, B0_CTST, CS_CL_SW_IRQ);
+       }
+       sky2_write32(hw, B0_IMSK, 0);
+       sky2_read32(hw, B0_IMSK);
+       free_irq(pdev->irq, hw);
+       return err;
+ }
+ /* This driver supports yukon2 chipset only */
+ static const char *sky2_name(u8 chipid, char *buf, int sz)
+ {
+       const char *name[] = {
+               "XL",           /* 0xb3 */
+               "EC Ultra",     /* 0xb4 */
+               "Extreme",      /* 0xb5 */
+               "EC",           /* 0xb6 */
+               "FE",           /* 0xb7 */
+               "FE+",          /* 0xb8 */
+               "Supreme",      /* 0xb9 */
+               "UL 2",         /* 0xba */
+               "Unknown",      /* 0xbb */
+               "Optima",       /* 0xbc */
+               "Optima Prime", /* 0xbd */
+               "Optima 2",     /* 0xbe */
+       };
+       if (chipid >= CHIP_ID_YUKON_XL && chipid <= CHIP_ID_YUKON_OP_2)
+               strncpy(buf, name[chipid - CHIP_ID_YUKON_XL], sz);
+       else
+               snprintf(buf, sz, "(chip %#x)", chipid);
+       return buf;
+ }
+ static int __devinit sky2_probe(struct pci_dev *pdev,
+                               const struct pci_device_id *ent)
+ {
+       struct net_device *dev, *dev1;
+       struct sky2_hw *hw;
+       int err, using_dac = 0, wol_default;
+       u32 reg;
+       char buf1[16];
+       err = pci_enable_device(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "cannot enable PCI device\n");
+               goto err_out;
+       }
+       /* Get configuration information
+        * Note: only regular PCI config access once to test for HW issues
+        *       other PCI access through shared memory for speed and to
+        *       avoid MMCONFIG problems.
+        */
+       err = pci_read_config_dword(pdev, PCI_DEV_REG2, &reg);
+       if (err) {
+               dev_err(&pdev->dev, "PCI read config failed\n");
+               goto err_out;
+       }
+       if (~reg == 0) {
+               dev_err(&pdev->dev, "PCI configuration read error\n");
+               goto err_out;
+       }
+       err = pci_request_regions(pdev, DRV_NAME);
+       if (err) {
+               dev_err(&pdev->dev, "cannot obtain PCI resources\n");
+               goto err_out_disable;
+       }
+       pci_set_master(pdev);
+       if (sizeof(dma_addr_t) > sizeof(u32) &&
+           !(err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))) {
+               using_dac = 1;
+               err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+               if (err < 0) {
+                       dev_err(&pdev->dev, "unable to obtain 64 bit DMA "
+                               "for consistent allocations\n");
+                       goto err_out_free_regions;
+               }
+       } else {
+               err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+               if (err) {
+                       dev_err(&pdev->dev, "no usable DMA configuration\n");
+                       goto err_out_free_regions;
+               }
+       }
+ #ifdef __BIG_ENDIAN
+       /* The sk98lin vendor driver uses hardware byte swapping but
+        * this driver uses software swapping.
+        */
+       reg &= ~PCI_REV_DESC;
+       err = pci_write_config_dword(pdev, PCI_DEV_REG2, reg);
+       if (err) {
+               dev_err(&pdev->dev, "PCI write config failed\n");
+               goto err_out_free_regions;
+       }
+ #endif
+       wol_default = device_may_wakeup(&pdev->dev) ? WAKE_MAGIC : 0;
+       err = -ENOMEM;
+       hw = kzalloc(sizeof(*hw) + strlen(DRV_NAME "@pci:")
+                    + strlen(pci_name(pdev)) + 1, GFP_KERNEL);
+       if (!hw) {
+               dev_err(&pdev->dev, "cannot allocate hardware struct\n");
+               goto err_out_free_regions;
+       }
+       hw->pdev = pdev;
+       sprintf(hw->irq_name, DRV_NAME "@pci:%s", pci_name(pdev));
+       hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000);
+       if (!hw->regs) {
+               dev_err(&pdev->dev, "cannot map device registers\n");
+               goto err_out_free_hw;
+       }
+       err = sky2_init(hw);
+       if (err)
+               goto err_out_iounmap;
+       /* ring for status responses */
+       hw->st_size = hw->ports * roundup_pow_of_two(3*RX_MAX_PENDING + TX_MAX_PENDING);
+       hw->st_le = pci_alloc_consistent(pdev, hw->st_size * sizeof(struct sky2_status_le),
+                                        &hw->st_dma);
+       if (!hw->st_le)
+               goto err_out_reset;
+       dev_info(&pdev->dev, "Yukon-2 %s chip revision %d\n",
+                sky2_name(hw->chip_id, buf1, sizeof(buf1)), hw->chip_rev);
+       sky2_reset(hw);
+       dev = sky2_init_netdev(hw, 0, using_dac, wol_default);
+       if (!dev) {
+               err = -ENOMEM;
+               goto err_out_free_pci;
+       }
+       if (!disable_msi && pci_enable_msi(pdev) == 0) {
+               err = sky2_test_msi(hw);
+               if (err == -EOPNOTSUPP)
+                       pci_disable_msi(pdev);
+               else if (err)
+                       goto err_out_free_netdev;
+       }
+       err = register_netdev(dev);
+       if (err) {
+               dev_err(&pdev->dev, "cannot register net device\n");
+               goto err_out_free_netdev;
+       }
+       netif_carrier_off(dev);
+       netif_napi_add(dev, &hw->napi, sky2_poll, NAPI_WEIGHT);
+       sky2_show_addr(dev);
+       if (hw->ports > 1) {
+               dev1 = sky2_init_netdev(hw, 1, using_dac, wol_default);
+               if (!dev1) {
+                       err = -ENOMEM;
+                       goto err_out_unregister;
+               }
+               err = register_netdev(dev1);
+               if (err) {
+                       dev_err(&pdev->dev, "cannot register second net device\n");
+                       goto err_out_free_dev1;
+               }
+               err = sky2_setup_irq(hw, hw->irq_name);
+               if (err)
+                       goto err_out_unregister_dev1;
+               sky2_show_addr(dev1);
+       }
+       setup_timer(&hw->watchdog_timer, sky2_watchdog, (unsigned long) hw);
+       INIT_WORK(&hw->restart_work, sky2_restart);
+       pci_set_drvdata(pdev, hw);
+       pdev->d3_delay = 150;
+       return 0;
+ err_out_unregister_dev1:
+       unregister_netdev(dev1);
+ err_out_free_dev1:
+       free_netdev(dev1);
+ err_out_unregister:
+       if (hw->flags & SKY2_HW_USE_MSI)
+               pci_disable_msi(pdev);
+       unregister_netdev(dev);
+ err_out_free_netdev:
+       free_netdev(dev);
+ err_out_free_pci:
+       pci_free_consistent(pdev, hw->st_size * sizeof(struct sky2_status_le),
+                           hw->st_le, hw->st_dma);
+ err_out_reset:
+       sky2_write8(hw, B0_CTST, CS_RST_SET);
+ err_out_iounmap:
+       iounmap(hw->regs);
+ err_out_free_hw:
+       kfree(hw);
+ err_out_free_regions:
+       pci_release_regions(pdev);
+ err_out_disable:
+       pci_disable_device(pdev);
+ err_out:
+       pci_set_drvdata(pdev, NULL);
+       return err;
+ }
+ static void __devexit sky2_remove(struct pci_dev *pdev)
+ {
+       struct sky2_hw *hw = pci_get_drvdata(pdev);
+       int i;
+       if (!hw)
+               return;
+       del_timer_sync(&hw->watchdog_timer);
+       cancel_work_sync(&hw->restart_work);
+       for (i = hw->ports-1; i >= 0; --i)
+               unregister_netdev(hw->dev[i]);
+       sky2_write32(hw, B0_IMSK, 0);
+       sky2_read32(hw, B0_IMSK);
+       sky2_power_aux(hw);
+       sky2_write8(hw, B0_CTST, CS_RST_SET);
+       sky2_read8(hw, B0_CTST);
+       if (hw->ports > 1) {
+               napi_disable(&hw->napi);
+               free_irq(pdev->irq, hw);
+       }
+       if (hw->flags & SKY2_HW_USE_MSI)
+               pci_disable_msi(pdev);
+       pci_free_consistent(pdev, hw->st_size * sizeof(struct sky2_status_le),
+                           hw->st_le, hw->st_dma);
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+       for (i = hw->ports-1; i >= 0; --i)
+               free_netdev(hw->dev[i]);
+       iounmap(hw->regs);
+       kfree(hw);
+       pci_set_drvdata(pdev, NULL);
+ }
+ static int sky2_suspend(struct device *dev)
+ {
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct sky2_hw *hw = pci_get_drvdata(pdev);
+       int i;
+       if (!hw)
+               return 0;
+       del_timer_sync(&hw->watchdog_timer);
+       cancel_work_sync(&hw->restart_work);
+       rtnl_lock();
+       sky2_all_down(hw);
+       for (i = 0; i < hw->ports; i++) {
+               struct net_device *dev = hw->dev[i];
+               struct sky2_port *sky2 = netdev_priv(dev);
+               if (sky2->wol)
+                       sky2_wol_init(sky2);
+       }
+       sky2_power_aux(hw);
+       rtnl_unlock();
+       return 0;
+ }
+ #ifdef CONFIG_PM_SLEEP
+ static int sky2_resume(struct device *dev)
+ {
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct sky2_hw *hw = pci_get_drvdata(pdev);
+       int err;
+       if (!hw)
+               return 0;
+       /* Re-enable all clocks */
+       err = pci_write_config_dword(pdev, PCI_DEV_REG3, 0);
+       if (err) {
+               dev_err(&pdev->dev, "PCI write config failed\n");
+               goto out;
+       }
+       rtnl_lock();
+       sky2_reset(hw);
+       sky2_all_up(hw);
+       rtnl_unlock();
+       return 0;
+ out:
+       dev_err(&pdev->dev, "resume failed (%d)\n", err);
+       pci_disable_device(pdev);
+       return err;
+ }
+ static SIMPLE_DEV_PM_OPS(sky2_pm_ops, sky2_suspend, sky2_resume);
+ #define SKY2_PM_OPS (&sky2_pm_ops)
+ #else
+ #define SKY2_PM_OPS NULL
+ #endif
+ static void sky2_shutdown(struct pci_dev *pdev)
+ {
+       sky2_suspend(&pdev->dev);
+       pci_wake_from_d3(pdev, device_may_wakeup(&pdev->dev));
+       pci_set_power_state(pdev, PCI_D3hot);
+ }
+ static struct pci_driver sky2_driver = {
+       .name = DRV_NAME,
+       .id_table = sky2_id_table,
+       .probe = sky2_probe,
+       .remove = __devexit_p(sky2_remove),
+       .shutdown = sky2_shutdown,
+       .driver.pm = SKY2_PM_OPS,
+ };
+ static int __init sky2_init_module(void)
+ {
+       pr_info("driver version " DRV_VERSION "\n");
+       sky2_debug_init();
+       return pci_register_driver(&sky2_driver);
+ }
+ static void __exit sky2_cleanup_module(void)
+ {
+       pci_unregister_driver(&sky2_driver);
+       sky2_debug_cleanup();
+ }
+ module_init(sky2_init_module);
+ module_exit(sky2_cleanup_module);
+ MODULE_DESCRIPTION("Marvell Yukon 2 Gigabit Ethernet driver");
+ MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(DRV_VERSION);
index 0000000,7078840..ab81c0d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1656 +1,1656 @@@
 - *    So, this Rx interrrupt enable/status bit number is equal
+ /*
+  * Micrel KS8695 (Centaur) Ethernet.
+  *
+  * 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.
+  *
+  * Copyright 2008 Simtec Electronics
+  *              Daniel Silverstone <dsilvers@simtec.co.uk>
+  *              Vincent Sanders <vince@simtec.co.uk>
+  */
+ #include <linux/dma-mapping.h>
+ #include <linux/module.h>
+ #include <linux/ioport.h>
+ #include <linux/netdevice.h>
+ #include <linux/etherdevice.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/skbuff.h>
+ #include <linux/spinlock.h>
+ #include <linux/crc32.h>
+ #include <linux/mii.h>
+ #include <linux/ethtool.h>
+ #include <linux/delay.h>
+ #include <linux/platform_device.h>
+ #include <linux/irq.h>
+ #include <linux/io.h>
+ #include <linux/slab.h>
+ #include <asm/irq.h>
+ #include <mach/regs-switch.h>
+ #include <mach/regs-misc.h>
+ #include <asm/mach/irq.h>
+ #include <mach/regs-irq.h>
+ #include "ks8695net.h"
+ #define MODULENAME    "ks8695_ether"
+ #define MODULEVERSION "1.02"
+ /*
+  * Transmit and device reset timeout, default 5 seconds.
+  */
+ static int watchdog = 5000;
+ /* Hardware structures */
+ /**
+  *    struct rx_ring_desc - Receive descriptor ring element
+  *    @status: The status of the descriptor element (E.g. who owns it)
+  *    @length: The number of bytes in the block pointed to by data_ptr
+  *    @data_ptr: The physical address of the data block to receive into
+  *    @next_desc: The physical address of the next descriptor element.
+  */
+ struct rx_ring_desc {
+       __le32  status;
+       __le32  length;
+       __le32  data_ptr;
+       __le32  next_desc;
+ };
+ /**
+  *    struct tx_ring_desc - Transmit descriptor ring element
+  *    @owner: Who owns the descriptor
+  *    @status: The number of bytes in the block pointed to by data_ptr
+  *    @data_ptr: The physical address of the data block to receive into
+  *    @next_desc: The physical address of the next descriptor element.
+  */
+ struct tx_ring_desc {
+       __le32  owner;
+       __le32  status;
+       __le32  data_ptr;
+       __le32  next_desc;
+ };
+ /**
+  *    struct ks8695_skbuff - sk_buff wrapper for rx/tx rings.
+  *    @skb: The buffer in the ring
+  *    @dma_ptr: The mapped DMA pointer of the buffer
+  *    @length: The number of bytes mapped to dma_ptr
+  */
+ struct ks8695_skbuff {
+       struct sk_buff  *skb;
+       dma_addr_t      dma_ptr;
+       u32             length;
+ };
+ /* Private device structure */
+ #define MAX_TX_DESC 8
+ #define MAX_TX_DESC_MASK 0x7
+ #define MAX_RX_DESC 16
+ #define MAX_RX_DESC_MASK 0xf
+ /*napi_weight have better more than rx DMA buffers*/
+ #define NAPI_WEIGHT   64
+ #define MAX_RXBUF_SIZE 0x700
+ #define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC)
+ #define RX_RING_DMA_SIZE (sizeof(struct rx_ring_desc) * MAX_RX_DESC)
+ #define RING_DMA_SIZE (TX_RING_DMA_SIZE + RX_RING_DMA_SIZE)
+ /**
+  *    enum ks8695_dtype - Device type
+  *    @KS8695_DTYPE_WAN: This device is a WAN interface
+  *    @KS8695_DTYPE_LAN: This device is a LAN interface
+  *    @KS8695_DTYPE_HPNA: This device is an HPNA interface
+  */
+ enum ks8695_dtype {
+       KS8695_DTYPE_WAN,
+       KS8695_DTYPE_LAN,
+       KS8695_DTYPE_HPNA,
+ };
+ /**
+  *    struct ks8695_priv - Private data for the KS8695 Ethernet
+  *    @in_suspend: Flag to indicate if we're suspending/resuming
+  *    @ndev: The net_device for this interface
+  *    @dev: The platform device object for this interface
+  *    @dtype: The type of this device
+  *    @io_regs: The ioremapped registers for this interface
+  *      @napi : Add support NAPI for Rx
+  *    @rx_irq_name: The textual name of the RX IRQ from the platform data
+  *    @tx_irq_name: The textual name of the TX IRQ from the platform data
+  *    @link_irq_name: The textual name of the link IRQ from the
+  *                    platform data if available
+  *    @rx_irq: The IRQ number for the RX IRQ
+  *    @tx_irq: The IRQ number for the TX IRQ
+  *    @link_irq: The IRQ number for the link IRQ if available
+  *    @regs_req: The resource request for the registers region
+  *    @phyiface_req: The resource request for the phy/switch region
+  *                   if available
+  *    @phyiface_regs: The ioremapped registers for the phy/switch if available
+  *    @ring_base: The base pointer of the dma coherent memory for the rings
+  *    @ring_base_dma: The DMA mapped equivalent of ring_base
+  *    @tx_ring: The pointer in ring_base of the TX ring
+  *    @tx_ring_used: The number of slots in the TX ring which are occupied
+  *    @tx_ring_next_slot: The next slot to fill in the TX ring
+  *    @tx_ring_dma: The DMA mapped equivalent of tx_ring
+  *    @tx_buffers: The sk_buff mappings for the TX ring
+  *    @txq_lock: A lock to protect the tx_buffers tx_ring_used etc variables
+  *    @rx_ring: The pointer in ring_base of the RX ring
+  *    @rx_ring_dma: The DMA mapped equivalent of rx_ring
+  *    @rx_buffers: The sk_buff mappings for the RX ring
+  *    @next_rx_desc_read: The next RX descriptor to read from on IRQ
+  *      @rx_lock: A lock to protect Rx irq function
+  *    @msg_enable: The flags for which messages to emit
+  */
+ struct ks8695_priv {
+       int in_suspend;
+       struct net_device *ndev;
+       struct device *dev;
+       enum ks8695_dtype dtype;
+       void __iomem *io_regs;
+       struct napi_struct      napi;
+       const char *rx_irq_name, *tx_irq_name, *link_irq_name;
+       int rx_irq, tx_irq, link_irq;
+       struct resource *regs_req, *phyiface_req;
+       void __iomem *phyiface_regs;
+       void *ring_base;
+       dma_addr_t ring_base_dma;
+       struct tx_ring_desc *tx_ring;
+       int tx_ring_used;
+       int tx_ring_next_slot;
+       dma_addr_t tx_ring_dma;
+       struct ks8695_skbuff tx_buffers[MAX_TX_DESC];
+       spinlock_t txq_lock;
+       struct rx_ring_desc *rx_ring;
+       dma_addr_t rx_ring_dma;
+       struct ks8695_skbuff rx_buffers[MAX_RX_DESC];
+       int next_rx_desc_read;
+       spinlock_t rx_lock;
+       int msg_enable;
+ };
+ /* Register access */
+ /**
+  *    ks8695_readreg - Read from a KS8695 ethernet register
+  *    @ksp: The device to read from
+  *    @reg: The register to read
+  */
+ static inline u32
+ ks8695_readreg(struct ks8695_priv *ksp, int reg)
+ {
+       return readl(ksp->io_regs + reg);
+ }
+ /**
+  *    ks8695_writereg - Write to a KS8695 ethernet register
+  *    @ksp: The device to write to
+  *    @reg: The register to write
+  *    @value: The value to write to the register
+  */
+ static inline void
+ ks8695_writereg(struct ks8695_priv *ksp, int reg, u32 value)
+ {
+       writel(value, ksp->io_regs + reg);
+ }
+ /* Utility functions */
+ /**
+  *    ks8695_port_type - Retrieve port-type as user-friendly string
+  *    @ksp: The device to return the type for
+  *
+  *    Returns a string indicating which of the WAN, LAN or HPNA
+  *    ports this device is likely to represent.
+  */
+ static const char *
+ ks8695_port_type(struct ks8695_priv *ksp)
+ {
+       switch (ksp->dtype) {
+       case KS8695_DTYPE_LAN:
+               return "LAN";
+       case KS8695_DTYPE_WAN:
+               return "WAN";
+       case KS8695_DTYPE_HPNA:
+               return "HPNA";
+       }
+       return "UNKNOWN";
+ }
+ /**
+  *    ks8695_update_mac - Update the MAC registers in the device
+  *    @ksp: The device to update
+  *
+  *    Updates the MAC registers in the KS8695 device from the address in the
+  *    net_device structure associated with this interface.
+  */
+ static void
+ ks8695_update_mac(struct ks8695_priv *ksp)
+ {
+       /* Update the HW with the MAC from the net_device */
+       struct net_device *ndev = ksp->ndev;
+       u32 machigh, maclow;
+       maclow  = ((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
+                  (ndev->dev_addr[4] <<  8) | (ndev->dev_addr[5] <<  0));
+       machigh = ((ndev->dev_addr[0] <<  8) | (ndev->dev_addr[1] <<  0));
+       ks8695_writereg(ksp, KS8695_MAL, maclow);
+       ks8695_writereg(ksp, KS8695_MAH, machigh);
+ }
+ /**
+  *    ks8695_refill_rxbuffers - Re-fill the RX buffer ring
+  *    @ksp: The device to refill
+  *
+  *    Iterates the RX ring of the device looking for empty slots.
+  *    For each empty slot, we allocate and map a new SKB and give it
+  *    to the hardware.
+  *    This can be called from interrupt context safely.
+  */
+ static void
+ ks8695_refill_rxbuffers(struct ks8695_priv *ksp)
+ {
+       /* Run around the RX ring, filling in any missing sk_buff's */
+       int buff_n;
+       for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+               if (!ksp->rx_buffers[buff_n].skb) {
+                       struct sk_buff *skb = dev_alloc_skb(MAX_RXBUF_SIZE);
+                       dma_addr_t mapping;
+                       ksp->rx_buffers[buff_n].skb = skb;
+                       if (skb == NULL) {
+                               /* Failed to allocate one, perhaps
+                                * we'll try again later.
+                                */
+                               break;
+                       }
+                       mapping = dma_map_single(ksp->dev, skb->data,
+                                                MAX_RXBUF_SIZE,
+                                                DMA_FROM_DEVICE);
+                       if (unlikely(dma_mapping_error(ksp->dev, mapping))) {
+                               /* Failed to DMA map this SKB, try later */
+                               dev_kfree_skb_irq(skb);
+                               ksp->rx_buffers[buff_n].skb = NULL;
+                               break;
+                       }
+                       ksp->rx_buffers[buff_n].dma_ptr = mapping;
+                       skb->dev = ksp->ndev;
+                       ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE;
+                       /* Record this into the DMA ring */
+                       ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping);
+                       ksp->rx_ring[buff_n].length =
+                               cpu_to_le32(MAX_RXBUF_SIZE);
+                       wmb();
+                       /* And give ownership over to the hardware */
+                       ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+               }
+       }
+ }
+ /* Maximum number of multicast addresses which the KS8695 HW supports */
+ #define KS8695_NR_ADDRESSES   16
+ /**
+  *    ks8695_init_partial_multicast - Init the mcast addr registers
+  *    @ksp: The device to initialise
+  *    @addr: The multicast address list to use
+  *    @nr_addr: The number of addresses in the list
+  *
+  *    This routine is a helper for ks8695_set_multicast - it writes
+  *    the additional-address registers in the KS8695 ethernet device
+  *    and cleans up any others left behind.
+  */
+ static void
+ ks8695_init_partial_multicast(struct ks8695_priv *ksp,
+                             struct net_device *ndev)
+ {
+       u32 low, high;
+       int i;
+       struct netdev_hw_addr *ha;
+       i = 0;
+       netdev_for_each_mc_addr(ha, ndev) {
+               /* Ran out of space in chip? */
+               BUG_ON(i == KS8695_NR_ADDRESSES);
+               low = (ha->addr[2] << 24) | (ha->addr[3] << 16) |
+                     (ha->addr[4] << 8) | (ha->addr[5]);
+               high = (ha->addr[0] << 8) | (ha->addr[1]);
+               ks8695_writereg(ksp, KS8695_AAL_(i), low);
+               ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high);
+               i++;
+       }
+       /* Clear the remaining Additional Station Addresses */
+       for (; i < KS8695_NR_ADDRESSES; i++) {
+               ks8695_writereg(ksp, KS8695_AAL_(i), 0);
+               ks8695_writereg(ksp, KS8695_AAH_(i), 0);
+       }
+ }
+ /* Interrupt handling */
+ /**
+  *    ks8695_tx_irq - Transmit IRQ handler
+  *    @irq: The IRQ which went off (ignored)
+  *    @dev_id: The net_device for the interrupt
+  *
+  *    Process the TX ring, clearing out any transmitted slots.
+  *    Allows the net_device to pass us new packets once slots are
+  *    freed.
+  */
+ static irqreturn_t
+ ks8695_tx_irq(int irq, void *dev_id)
+ {
+       struct net_device *ndev = (struct net_device *)dev_id;
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       int buff_n;
+       for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+               if (ksp->tx_buffers[buff_n].skb &&
+                   !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) {
+                       rmb();
+                       /* An SKB which is not owned by HW is present */
+                       /* Update the stats for the net_device */
+                       ndev->stats.tx_packets++;
+                       ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length;
+                       /* Free the packet from the ring */
+                       ksp->tx_ring[buff_n].data_ptr = 0;
+                       /* Free the sk_buff */
+                       dma_unmap_single(ksp->dev,
+                                        ksp->tx_buffers[buff_n].dma_ptr,
+                                        ksp->tx_buffers[buff_n].length,
+                                        DMA_TO_DEVICE);
+                       dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+                       ksp->tx_buffers[buff_n].skb = NULL;
+                       ksp->tx_ring_used--;
+               }
+       }
+       netif_wake_queue(ndev);
+       return IRQ_HANDLED;
+ }
+ /**
+  *    ks8695_get_rx_enable_bit - Get rx interrupt enable/status bit
+  *    @ksp: Private data for the KS8695 Ethernet
+  *
+  *    For KS8695 document:
+  *    Interrupt Enable Register (offset 0xE204)
+  *        Bit29 : WAN MAC Receive Interrupt Enable
+  *        Bit16 : LAN MAC Receive Interrupt Enable
+  *    Interrupt Status Register (Offset 0xF208)
+  *        Bit29: WAN MAC Receive Status
+  *        Bit16: LAN MAC Receive Status
++ *    So, this Rx interrupt enable/status bit number is equal
+  *    as Rx IRQ number.
+  */
+ static inline u32 ks8695_get_rx_enable_bit(struct ks8695_priv *ksp)
+ {
+       return ksp->rx_irq;
+ }
+ /**
+  *    ks8695_rx_irq - Receive IRQ handler
+  *    @irq: The IRQ which went off (ignored)
+  *    @dev_id: The net_device for the interrupt
+  *
+  *    Inform NAPI that packet reception needs to be scheduled
+  */
+ static irqreturn_t
+ ks8695_rx_irq(int irq, void *dev_id)
+ {
+       struct net_device *ndev = (struct net_device *)dev_id;
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       spin_lock(&ksp->rx_lock);
+       if (napi_schedule_prep(&ksp->napi)) {
+               unsigned long status = readl(KS8695_IRQ_VA + KS8695_INTEN);
+               unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp);
+               /*disable rx interrupt*/
+               status &= ~mask_bit;
+               writel(status , KS8695_IRQ_VA + KS8695_INTEN);
+               __napi_schedule(&ksp->napi);
+       }
+       spin_unlock(&ksp->rx_lock);
+       return IRQ_HANDLED;
+ }
+ /**
+  *    ks8695_rx - Receive packets called by NAPI poll method
+  *    @ksp: Private data for the KS8695 Ethernet
+  *    @budget: Number of packets allowed to process
+  */
+ static int ks8695_rx(struct ks8695_priv *ksp, int budget)
+ {
+       struct net_device *ndev = ksp->ndev;
+       struct sk_buff *skb;
+       int buff_n;
+       u32 flags;
+       int pktlen;
+       int received = 0;
+       buff_n = ksp->next_rx_desc_read;
+       while (received < budget
+                       && ksp->rx_buffers[buff_n].skb
+                       && (!(ksp->rx_ring[buff_n].status &
+                                       cpu_to_le32(RDES_OWN)))) {
+                       rmb();
+                       flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
+                       /* Found an SKB which we own, this means we
+                        * received a packet
+                        */
+                       if ((flags & (RDES_FS | RDES_LS)) !=
+                           (RDES_FS | RDES_LS)) {
+                               /* This packet is not the first and
+                                * the last segment.  Therefore it is
+                                * a "spanning" packet and we can't
+                                * handle it
+                                */
+                               goto rx_failure;
+                       }
+                       if (flags & (RDES_ES | RDES_RE)) {
+                               /* It's an error packet */
+                               ndev->stats.rx_errors++;
+                               if (flags & RDES_TL)
+                                       ndev->stats.rx_length_errors++;
+                               if (flags & RDES_RF)
+                                       ndev->stats.rx_length_errors++;
+                               if (flags & RDES_CE)
+                                       ndev->stats.rx_crc_errors++;
+                               if (flags & RDES_RE)
+                                       ndev->stats.rx_missed_errors++;
+                               goto rx_failure;
+                       }
+                       pktlen = flags & RDES_FLEN;
+                       pktlen -= 4; /* Drop the CRC */
+                       /* Retrieve the sk_buff */
+                       skb = ksp->rx_buffers[buff_n].skb;
+                       /* Clear it from the ring */
+                       ksp->rx_buffers[buff_n].skb = NULL;
+                       ksp->rx_ring[buff_n].data_ptr = 0;
+                       /* Unmap the SKB */
+                       dma_unmap_single(ksp->dev,
+                                        ksp->rx_buffers[buff_n].dma_ptr,
+                                        ksp->rx_buffers[buff_n].length,
+                                        DMA_FROM_DEVICE);
+                       /* Relinquish the SKB to the network layer */
+                       skb_put(skb, pktlen);
+                       skb->protocol = eth_type_trans(skb, ndev);
+                       netif_receive_skb(skb);
+                       /* Record stats */
+                       ndev->stats.rx_packets++;
+                       ndev->stats.rx_bytes += pktlen;
+                       goto rx_finished;
+ rx_failure:
+                       /* This ring entry is an error, but we can
+                        * re-use the skb
+                        */
+                       /* Give the ring entry back to the hardware */
+                       ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+ rx_finished:
+                       received++;
+                       buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
+       }
+       /* And note which RX descriptor we last did */
+       ksp->next_rx_desc_read = buff_n;
+       /* And refill the buffers */
+       ks8695_refill_rxbuffers(ksp);
+       /* Kick the RX DMA engine, in case it became suspended */
+       ks8695_writereg(ksp, KS8695_DRSC, 0);
+       return received;
+ }
+ /**
+  *    ks8695_poll - Receive packet by NAPI poll method
+  *    @ksp: Private data for the KS8695 Ethernet
+  *    @budget: The remaining number packets for network subsystem
+  *
+  *     Invoked by the network core when it requests for new
+  *     packets from the driver
+  */
+ static int ks8695_poll(struct napi_struct *napi, int budget)
+ {
+       struct ks8695_priv *ksp = container_of(napi, struct ks8695_priv, napi);
+       unsigned long  work_done;
+       unsigned long isr = readl(KS8695_IRQ_VA + KS8695_INTEN);
+       unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp);
+       work_done = ks8695_rx(ksp, budget);
+       if (work_done < budget) {
+               unsigned long flags;
+               spin_lock_irqsave(&ksp->rx_lock, flags);
+               __napi_complete(napi);
+               /*enable rx interrupt*/
+               writel(isr | mask_bit, KS8695_IRQ_VA + KS8695_INTEN);
+               spin_unlock_irqrestore(&ksp->rx_lock, flags);
+       }
+       return work_done;
+ }
+ /**
+  *    ks8695_link_irq - Link change IRQ handler
+  *    @irq: The IRQ which went off (ignored)
+  *    @dev_id: The net_device for the interrupt
+  *
+  *    The WAN interface can generate an IRQ when the link changes,
+  *    report this to the net layer and the user.
+  */
+ static irqreturn_t
+ ks8695_link_irq(int irq, void *dev_id)
+ {
+       struct net_device *ndev = (struct net_device *)dev_id;
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       u32 ctrl;
+       ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+       if (ctrl & WMC_WLS) {
+               netif_carrier_on(ndev);
+               if (netif_msg_link(ksp))
+                       dev_info(ksp->dev,
+                                "%s: Link is now up (10%sMbps/%s-duplex)\n",
+                                ndev->name,
+                                (ctrl & WMC_WSS) ? "0" : "",
+                                (ctrl & WMC_WDS) ? "Full" : "Half");
+       } else {
+               netif_carrier_off(ndev);
+               if (netif_msg_link(ksp))
+                       dev_info(ksp->dev, "%s: Link is now down.\n",
+                                ndev->name);
+       }
+       return IRQ_HANDLED;
+ }
+ /* KS8695 Device functions */
+ /**
+  *    ks8695_reset - Reset a KS8695 ethernet interface
+  *    @ksp: The interface to reset
+  *
+  *    Perform an engine reset of the interface and re-program it
+  *    with sensible defaults.
+  */
+ static void
+ ks8695_reset(struct ks8695_priv *ksp)
+ {
+       int reset_timeout = watchdog;
+       /* Issue the reset via the TX DMA control register */
+       ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST);
+       while (reset_timeout--) {
+               if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST))
+                       break;
+               msleep(1);
+       }
+       if (reset_timeout < 0) {
+               dev_crit(ksp->dev,
+                        "Timeout waiting for DMA engines to reset\n");
+               /* And blithely carry on */
+       }
+       /* Definitely wait long enough before attempting to program
+        * the engines
+        */
+       msleep(10);
+       /* RX: unicast and broadcast */
+       ks8695_writereg(ksp, KS8695_DRXC, DRXC_RU | DRXC_RB);
+       /* TX: pad and add CRC */
+       ks8695_writereg(ksp, KS8695_DTXC, DTXC_TEP | DTXC_TAC);
+ }
+ /**
+  *    ks8695_shutdown - Shut down a KS8695 ethernet interface
+  *    @ksp: The interface to shut down
+  *
+  *    This disables packet RX/TX, cleans up IRQs, drains the rings,
+  *    and basically places the interface into a clean shutdown
+  *    state.
+  */
+ static void
+ ks8695_shutdown(struct ks8695_priv *ksp)
+ {
+       u32 ctrl;
+       int buff_n;
+       /* Disable packet transmission */
+       ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+       ks8695_writereg(ksp, KS8695_DTXC, ctrl & ~DTXC_TE);
+       /* Disable packet reception */
+       ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+       ks8695_writereg(ksp, KS8695_DRXC, ctrl & ~DRXC_RE);
+       /* Release the IRQs */
+       free_irq(ksp->rx_irq, ksp->ndev);
+       free_irq(ksp->tx_irq, ksp->ndev);
+       if (ksp->link_irq != -1)
+               free_irq(ksp->link_irq, ksp->ndev);
+       /* Throw away any pending TX packets */
+       for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+               if (ksp->tx_buffers[buff_n].skb) {
+                       /* Remove this SKB from the TX ring */
+                       ksp->tx_ring[buff_n].owner = 0;
+                       ksp->tx_ring[buff_n].status = 0;
+                       ksp->tx_ring[buff_n].data_ptr = 0;
+                       /* Unmap and bin this SKB */
+                       dma_unmap_single(ksp->dev,
+                                        ksp->tx_buffers[buff_n].dma_ptr,
+                                        ksp->tx_buffers[buff_n].length,
+                                        DMA_TO_DEVICE);
+                       dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+                       ksp->tx_buffers[buff_n].skb = NULL;
+               }
+       }
+       /* Purge the RX buffers */
+       for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+               if (ksp->rx_buffers[buff_n].skb) {
+                       /* Remove the SKB from the RX ring */
+                       ksp->rx_ring[buff_n].status = 0;
+                       ksp->rx_ring[buff_n].data_ptr = 0;
+                       /* Unmap and bin the SKB */
+                       dma_unmap_single(ksp->dev,
+                                        ksp->rx_buffers[buff_n].dma_ptr,
+                                        ksp->rx_buffers[buff_n].length,
+                                        DMA_FROM_DEVICE);
+                       dev_kfree_skb_irq(ksp->rx_buffers[buff_n].skb);
+                       ksp->rx_buffers[buff_n].skb = NULL;
+               }
+       }
+ }
+ /**
+  *    ks8695_setup_irq - IRQ setup helper function
+  *    @irq: The IRQ number to claim
+  *    @irq_name: The name to give the IRQ claimant
+  *    @handler: The function to call to handle the IRQ
+  *    @ndev: The net_device to pass in as the dev_id argument to the handler
+  *
+  *    Return 0 on success.
+  */
+ static int
+ ks8695_setup_irq(int irq, const char *irq_name,
+                irq_handler_t handler, struct net_device *ndev)
+ {
+       int ret;
+       ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev);
+       if (ret) {
+               dev_err(&ndev->dev, "failure to request IRQ %d\n", irq);
+               return ret;
+       }
+       return 0;
+ }
+ /**
+  *    ks8695_init_net - Initialise a KS8695 ethernet interface
+  *    @ksp: The interface to initialise
+  *
+  *    This routine fills the RX ring, initialises the DMA engines,
+  *    allocates the IRQs and then starts the packet TX and RX
+  *    engines.
+  */
+ static int
+ ks8695_init_net(struct ks8695_priv *ksp)
+ {
+       int ret;
+       u32 ctrl;
+       ks8695_refill_rxbuffers(ksp);
+       /* Initialise the DMA engines */
+       ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma);
+       ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma);
+       /* Request the IRQs */
+       ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name,
+                              ks8695_rx_irq, ksp->ndev);
+       if (ret)
+               return ret;
+       ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name,
+                              ks8695_tx_irq, ksp->ndev);
+       if (ret)
+               return ret;
+       if (ksp->link_irq != -1) {
+               ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name,
+                                      ks8695_link_irq, ksp->ndev);
+               if (ret)
+                       return ret;
+       }
+       /* Set up the ring indices */
+       ksp->next_rx_desc_read = 0;
+       ksp->tx_ring_next_slot = 0;
+       ksp->tx_ring_used = 0;
+       /* Bring up transmission */
+       ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+       /* Enable packet transmission */
+       ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE);
+       /* Bring up the reception */
+       ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+       /* Enable packet reception */
+       ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE);
+       /* And start the DMA engine */
+       ks8695_writereg(ksp, KS8695_DRSC, 0);
+       /* All done */
+       return 0;
+ }
+ /**
+  *    ks8695_release_device - HW resource release for KS8695 e-net
+  *    @ksp: The device to be freed
+  *
+  *    This unallocates io memory regions, dma-coherent regions etc
+  *    which were allocated in ks8695_probe.
+  */
+ static void
+ ks8695_release_device(struct ks8695_priv *ksp)
+ {
+       /* Unmap the registers */
+       iounmap(ksp->io_regs);
+       if (ksp->phyiface_regs)
+               iounmap(ksp->phyiface_regs);
+       /* And release the request */
+       release_resource(ksp->regs_req);
+       kfree(ksp->regs_req);
+       if (ksp->phyiface_req) {
+               release_resource(ksp->phyiface_req);
+               kfree(ksp->phyiface_req);
+       }
+       /* Free the ring buffers */
+       dma_free_coherent(ksp->dev, RING_DMA_SIZE,
+                         ksp->ring_base, ksp->ring_base_dma);
+ }
+ /* Ethtool support */
+ /**
+  *    ks8695_get_msglevel - Get the messages enabled for emission
+  *    @ndev: The network device to read from
+  */
+ static u32
+ ks8695_get_msglevel(struct net_device *ndev)
+ {
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       return ksp->msg_enable;
+ }
+ /**
+  *    ks8695_set_msglevel - Set the messages enabled for emission
+  *    @ndev: The network device to configure
+  *    @value: The messages to set for emission
+  */
+ static void
+ ks8695_set_msglevel(struct net_device *ndev, u32 value)
+ {
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       ksp->msg_enable = value;
+ }
+ /**
+  *    ks8695_wan_get_settings - Get device-specific settings.
+  *    @ndev: The network device to read settings from
+  *    @cmd: The ethtool structure to read into
+  */
+ static int
+ ks8695_wan_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+ {
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       u32 ctrl;
+       /* All ports on the KS8695 support these... */
+       cmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+                         SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+                         SUPPORTED_TP | SUPPORTED_MII);
+       cmd->transceiver = XCVR_INTERNAL;
+       cmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
+       cmd->port = PORT_MII;
+       cmd->supported |= (SUPPORTED_Autoneg | SUPPORTED_Pause);
+       cmd->phy_address = 0;
+       ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+       if ((ctrl & WMC_WAND) == 0) {
+               /* auto-negotiation is enabled */
+               cmd->advertising |= ADVERTISED_Autoneg;
+               if (ctrl & WMC_WANA100F)
+                       cmd->advertising |= ADVERTISED_100baseT_Full;
+               if (ctrl & WMC_WANA100H)
+                       cmd->advertising |= ADVERTISED_100baseT_Half;
+               if (ctrl & WMC_WANA10F)
+                       cmd->advertising |= ADVERTISED_10baseT_Full;
+               if (ctrl & WMC_WANA10H)
+                       cmd->advertising |= ADVERTISED_10baseT_Half;
+               if (ctrl & WMC_WANAP)
+                       cmd->advertising |= ADVERTISED_Pause;
+               cmd->autoneg = AUTONEG_ENABLE;
+               ethtool_cmd_speed_set(cmd,
+                                     (ctrl & WMC_WSS) ? SPEED_100 : SPEED_10);
+               cmd->duplex = (ctrl & WMC_WDS) ?
+                       DUPLEX_FULL : DUPLEX_HALF;
+       } else {
+               /* auto-negotiation is disabled */
+               cmd->autoneg = AUTONEG_DISABLE;
+               ethtool_cmd_speed_set(cmd, ((ctrl & WMC_WANF100) ?
+                                           SPEED_100 : SPEED_10));
+               cmd->duplex = (ctrl & WMC_WANFF) ?
+                       DUPLEX_FULL : DUPLEX_HALF;
+       }
+       return 0;
+ }
+ /**
+  *    ks8695_wan_set_settings - Set device-specific settings.
+  *    @ndev: The network device to configure
+  *    @cmd: The settings to configure
+  */
+ static int
+ ks8695_wan_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+ {
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       u32 ctrl;
+       if ((cmd->speed != SPEED_10) && (cmd->speed != SPEED_100))
+               return -EINVAL;
+       if ((cmd->duplex != DUPLEX_HALF) && (cmd->duplex != DUPLEX_FULL))
+               return -EINVAL;
+       if (cmd->port != PORT_MII)
+               return -EINVAL;
+       if (cmd->transceiver != XCVR_INTERNAL)
+               return -EINVAL;
+       if ((cmd->autoneg != AUTONEG_DISABLE) &&
+           (cmd->autoneg != AUTONEG_ENABLE))
+               return -EINVAL;
+       if (cmd->autoneg == AUTONEG_ENABLE) {
+               if ((cmd->advertising & (ADVERTISED_10baseT_Half |
+                               ADVERTISED_10baseT_Full |
+                               ADVERTISED_100baseT_Half |
+                               ADVERTISED_100baseT_Full)) == 0)
+                       return -EINVAL;
+               ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+               ctrl &= ~(WMC_WAND | WMC_WANA100F | WMC_WANA100H |
+                         WMC_WANA10F | WMC_WANA10H);
+               if (cmd->advertising & ADVERTISED_100baseT_Full)
+                       ctrl |= WMC_WANA100F;
+               if (cmd->advertising & ADVERTISED_100baseT_Half)
+                       ctrl |= WMC_WANA100H;
+               if (cmd->advertising & ADVERTISED_10baseT_Full)
+                       ctrl |= WMC_WANA10F;
+               if (cmd->advertising & ADVERTISED_10baseT_Half)
+                       ctrl |= WMC_WANA10H;
+               /* force a re-negotiation */
+               ctrl |= WMC_WANR;
+               writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+       } else {
+               ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+               /* disable auto-negotiation */
+               ctrl |= WMC_WAND;
+               ctrl &= ~(WMC_WANF100 | WMC_WANFF);
+               if (cmd->speed == SPEED_100)
+                       ctrl |= WMC_WANF100;
+               if (cmd->duplex == DUPLEX_FULL)
+                       ctrl |= WMC_WANFF;
+               writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+       }
+       return 0;
+ }
+ /**
+  *    ks8695_wan_nwayreset - Restart the autonegotiation on the port.
+  *    @ndev: The network device to restart autoneotiation on
+  */
+ static int
+ ks8695_wan_nwayreset(struct net_device *ndev)
+ {
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       u32 ctrl;
+       ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+       if ((ctrl & WMC_WAND) == 0)
+               writel(ctrl | WMC_WANR,
+                      ksp->phyiface_regs + KS8695_WMC);
+       else
+               /* auto-negotiation not enabled */
+               return -EINVAL;
+       return 0;
+ }
+ /**
+  *    ks8695_wan_get_pause - Retrieve network pause/flow-control advertising
+  *    @ndev: The device to retrieve settings from
+  *    @param: The structure to fill out with the information
+  */
+ static void
+ ks8695_wan_get_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+ {
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       u32 ctrl;
+       ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+       /* advertise Pause */
+       param->autoneg = (ctrl & WMC_WANAP);
+       /* current Rx Flow-control */
+       ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+       param->rx_pause = (ctrl & DRXC_RFCE);
+       /* current Tx Flow-control */
+       ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+       param->tx_pause = (ctrl & DTXC_TFCE);
+ }
+ /**
+  *    ks8695_get_drvinfo - Retrieve driver information
+  *    @ndev: The network device to retrieve info about
+  *    @info: The info structure to fill out.
+  */
+ static void
+ ks8695_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
+ {
+       strlcpy(info->driver, MODULENAME, sizeof(info->driver));
+       strlcpy(info->version, MODULEVERSION, sizeof(info->version));
+       strlcpy(info->bus_info, dev_name(ndev->dev.parent),
+               sizeof(info->bus_info));
+ }
+ static const struct ethtool_ops ks8695_ethtool_ops = {
+       .get_msglevel   = ks8695_get_msglevel,
+       .set_msglevel   = ks8695_set_msglevel,
+       .get_drvinfo    = ks8695_get_drvinfo,
+ };
+ static const struct ethtool_ops ks8695_wan_ethtool_ops = {
+       .get_msglevel   = ks8695_get_msglevel,
+       .set_msglevel   = ks8695_set_msglevel,
+       .get_settings   = ks8695_wan_get_settings,
+       .set_settings   = ks8695_wan_set_settings,
+       .nway_reset     = ks8695_wan_nwayreset,
+       .get_link       = ethtool_op_get_link,
+       .get_pauseparam = ks8695_wan_get_pause,
+       .get_drvinfo    = ks8695_get_drvinfo,
+ };
+ /* Network device interface functions */
+ /**
+  *    ks8695_set_mac - Update MAC in net dev and HW
+  *    @ndev: The network device to update
+  *    @addr: The new MAC address to set
+  */
+ static int
+ ks8695_set_mac(struct net_device *ndev, void *addr)
+ {
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       struct sockaddr *address = addr;
+       if (!is_valid_ether_addr(address->sa_data))
+               return -EADDRNOTAVAIL;
+       memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+       ks8695_update_mac(ksp);
+       dev_dbg(ksp->dev, "%s: Updated MAC address to %pM\n",
+               ndev->name, ndev->dev_addr);
+       return 0;
+ }
+ /**
+  *    ks8695_set_multicast - Set up the multicast behaviour of the interface
+  *    @ndev: The net_device to configure
+  *
+  *    This routine, called by the net layer, configures promiscuity
+  *    and multicast reception behaviour for the interface.
+  */
+ static void
+ ks8695_set_multicast(struct net_device *ndev)
+ {
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       u32 ctrl;
+       ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+       if (ndev->flags & IFF_PROMISC) {
+               /* enable promiscuous mode */
+               ctrl |= DRXC_RA;
+       } else if (ndev->flags & ~IFF_PROMISC) {
+               /* disable promiscuous mode */
+               ctrl &= ~DRXC_RA;
+       }
+       if (ndev->flags & IFF_ALLMULTI) {
+               /* enable all multicast mode */
+               ctrl |= DRXC_RM;
+       } else if (netdev_mc_count(ndev) > KS8695_NR_ADDRESSES) {
+               /* more specific multicast addresses than can be
+                * handled in hardware
+                */
+               ctrl |= DRXC_RM;
+       } else {
+               /* enable specific multicasts */
+               ctrl &= ~DRXC_RM;
+               ks8695_init_partial_multicast(ksp, ndev);
+       }
+       ks8695_writereg(ksp, KS8695_DRXC, ctrl);
+ }
+ /**
+  *    ks8695_timeout - Handle a network tx/rx timeout.
+  *    @ndev: The net_device which timed out.
+  *
+  *    A network transaction timed out, reset the device.
+  */
+ static void
+ ks8695_timeout(struct net_device *ndev)
+ {
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       netif_stop_queue(ndev);
+       ks8695_shutdown(ksp);
+       ks8695_reset(ksp);
+       ks8695_update_mac(ksp);
+       /* We ignore the return from this since it managed to init
+        * before it probably will be okay to init again.
+        */
+       ks8695_init_net(ksp);
+       /* Reconfigure promiscuity etc */
+       ks8695_set_multicast(ndev);
+       /* And start the TX queue once more */
+       netif_start_queue(ndev);
+ }
+ /**
+  *    ks8695_start_xmit - Start a packet transmission
+  *    @skb: The packet to transmit
+  *    @ndev: The network device to send the packet on
+  *
+  *    This routine, called by the net layer, takes ownership of the
+  *    sk_buff and adds it to the TX ring. It then kicks the TX DMA
+  *    engine to ensure transmission begins.
+  */
+ static int
+ ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+ {
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       int buff_n;
+       dma_addr_t dmap;
+       spin_lock_irq(&ksp->txq_lock);
+       if (ksp->tx_ring_used == MAX_TX_DESC) {
+               /* Somehow we got entered when we have no room */
+               spin_unlock_irq(&ksp->txq_lock);
+               return NETDEV_TX_BUSY;
+       }
+       buff_n = ksp->tx_ring_next_slot;
+       BUG_ON(ksp->tx_buffers[buff_n].skb);
+       dmap = dma_map_single(ksp->dev, skb->data, skb->len, DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(ksp->dev, dmap))) {
+               /* Failed to DMA map this SKB, give it back for now */
+               spin_unlock_irq(&ksp->txq_lock);
+               dev_dbg(ksp->dev, "%s: Could not map DMA memory for "\
+                       "transmission, trying later\n", ndev->name);
+               return NETDEV_TX_BUSY;
+       }
+       ksp->tx_buffers[buff_n].dma_ptr = dmap;
+       /* Mapped okay, store the buffer pointer and length for later */
+       ksp->tx_buffers[buff_n].skb = skb;
+       ksp->tx_buffers[buff_n].length = skb->len;
+       /* Fill out the TX descriptor */
+       ksp->tx_ring[buff_n].data_ptr =
+               cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr);
+       ksp->tx_ring[buff_n].status =
+               cpu_to_le32(TDES_IC | TDES_FS | TDES_LS |
+                           (skb->len & TDES_TBS));
+       wmb();
+       /* Hand it over to the hardware */
+       ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN);
+       if (++ksp->tx_ring_used == MAX_TX_DESC)
+               netif_stop_queue(ndev);
+       /* Kick the TX DMA in case it decided to go IDLE */
+       ks8695_writereg(ksp, KS8695_DTSC, 0);
+       /* And update the next ring slot */
+       ksp->tx_ring_next_slot = (buff_n + 1) & MAX_TX_DESC_MASK;
+       spin_unlock_irq(&ksp->txq_lock);
+       return NETDEV_TX_OK;
+ }
+ /**
+  *    ks8695_stop - Stop (shutdown) a KS8695 ethernet interface
+  *    @ndev: The net_device to stop
+  *
+  *    This disables the TX queue and cleans up a KS8695 ethernet
+  *    device.
+  */
+ static int
+ ks8695_stop(struct net_device *ndev)
+ {
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       netif_stop_queue(ndev);
+       napi_disable(&ksp->napi);
+       ks8695_shutdown(ksp);
+       return 0;
+ }
+ /**
+  *    ks8695_open - Open (bring up) a KS8695 ethernet interface
+  *    @ndev: The net_device to open
+  *
+  *    This resets, configures the MAC, initialises the RX ring and
+  *    DMA engines and starts the TX queue for a KS8695 ethernet
+  *    device.
+  */
+ static int
+ ks8695_open(struct net_device *ndev)
+ {
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       int ret;
+       if (!is_valid_ether_addr(ndev->dev_addr))
+               return -EADDRNOTAVAIL;
+       ks8695_reset(ksp);
+       ks8695_update_mac(ksp);
+       ret = ks8695_init_net(ksp);
+       if (ret) {
+               ks8695_shutdown(ksp);
+               return ret;
+       }
+       napi_enable(&ksp->napi);
+       netif_start_queue(ndev);
+       return 0;
+ }
+ /* Platform device driver */
+ /**
+  *    ks8695_init_switch - Init LAN switch to known good defaults.
+  *    @ksp: The device to initialise
+  *
+  *    This initialises the LAN switch in the KS8695 to a known-good
+  *    set of defaults.
+  */
+ static void __devinit
+ ks8695_init_switch(struct ks8695_priv *ksp)
+ {
+       u32 ctrl;
+       /* Default value for SEC0 according to datasheet */
+       ctrl = 0x40819e00;
+       /* LED0 = Speed  LED1 = Link/Activity */
+       ctrl &= ~(SEC0_LLED1S | SEC0_LLED0S);
+       ctrl |= (LLED0S_LINK | LLED1S_LINK_ACTIVITY);
+       /* Enable Switch */
+       ctrl |= SEC0_ENABLE;
+       writel(ctrl, ksp->phyiface_regs + KS8695_SEC0);
+       /* Defaults for SEC1 */
+       writel(0x9400100, ksp->phyiface_regs + KS8695_SEC1);
+ }
+ /**
+  *    ks8695_init_wan_phy - Initialise the WAN PHY to sensible defaults
+  *    @ksp: The device to initialise
+  *
+  *    This initialises a KS8695's WAN phy to sensible values for
+  *    autonegotiation etc.
+  */
+ static void __devinit
+ ks8695_init_wan_phy(struct ks8695_priv *ksp)
+ {
+       u32 ctrl;
+       /* Support auto-negotiation */
+       ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H |
+               WMC_WANA10F | WMC_WANA10H);
+       /* LED0 = Activity , LED1 = Link */
+       ctrl |= (WLED0S_ACTIVITY | WLED1S_LINK);
+       /* Restart Auto-negotiation */
+       ctrl |= WMC_WANR;
+       writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+       writel(0, ksp->phyiface_regs + KS8695_WPPM);
+       writel(0, ksp->phyiface_regs + KS8695_PPS);
+ }
+ static const struct net_device_ops ks8695_netdev_ops = {
+       .ndo_open               = ks8695_open,
+       .ndo_stop               = ks8695_stop,
+       .ndo_start_xmit         = ks8695_start_xmit,
+       .ndo_tx_timeout         = ks8695_timeout,
+       .ndo_set_mac_address    = ks8695_set_mac,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_rx_mode        = ks8695_set_multicast,
+ };
+ /**
+  *    ks8695_probe - Probe and initialise a KS8695 ethernet interface
+  *    @pdev: The platform device to probe
+  *
+  *    Initialise a KS8695 ethernet device from platform data.
+  *
+  *    This driver requires at least one IORESOURCE_MEM for the
+  *    registers and two IORESOURCE_IRQ for the RX and TX IRQs
+  *    respectively. It can optionally take an additional
+  *    IORESOURCE_MEM for the switch or phy in the case of the lan or
+  *    wan ports, and an IORESOURCE_IRQ for the link IRQ for the wan
+  *    port.
+  */
+ static int __devinit
+ ks8695_probe(struct platform_device *pdev)
+ {
+       struct ks8695_priv *ksp;
+       struct net_device *ndev;
+       struct resource *regs_res, *phyiface_res;
+       struct resource *rxirq_res, *txirq_res, *linkirq_res;
+       int ret = 0;
+       int buff_n;
+       u32 machigh, maclow;
+       /* Initialise a net_device */
+       ndev = alloc_etherdev(sizeof(struct ks8695_priv));
+       if (!ndev) {
+               dev_err(&pdev->dev, "could not allocate device.\n");
+               return -ENOMEM;
+       }
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+       dev_dbg(&pdev->dev, "ks8695_probe() called\n");
+       /* Configure our private structure a little */
+       ksp = netdev_priv(ndev);
+       ksp->dev = &pdev->dev;
+       ksp->ndev = ndev;
+       ksp->msg_enable = NETIF_MSG_LINK;
+       /* Retrieve resources */
+       regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       phyiface_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       rxirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       txirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+       linkirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+       if (!(regs_res && rxirq_res && txirq_res)) {
+               dev_err(ksp->dev, "insufficient resources\n");
+               ret = -ENOENT;
+               goto failure;
+       }
+       ksp->regs_req = request_mem_region(regs_res->start,
+                                          resource_size(regs_res),
+                                          pdev->name);
+       if (!ksp->regs_req) {
+               dev_err(ksp->dev, "cannot claim register space\n");
+               ret = -EIO;
+               goto failure;
+       }
+       ksp->io_regs = ioremap(regs_res->start, resource_size(regs_res));
+       if (!ksp->io_regs) {
+               dev_err(ksp->dev, "failed to ioremap registers\n");
+               ret = -EINVAL;
+               goto failure;
+       }
+       if (phyiface_res) {
+               ksp->phyiface_req =
+                       request_mem_region(phyiface_res->start,
+                                          resource_size(phyiface_res),
+                                          phyiface_res->name);
+               if (!ksp->phyiface_req) {
+                       dev_err(ksp->dev,
+                               "cannot claim switch register space\n");
+                       ret = -EIO;
+                       goto failure;
+               }
+               ksp->phyiface_regs = ioremap(phyiface_res->start,
+                                            resource_size(phyiface_res));
+               if (!ksp->phyiface_regs) {
+                       dev_err(ksp->dev,
+                               "failed to ioremap switch registers\n");
+                       ret = -EINVAL;
+                       goto failure;
+               }
+       }
+       ksp->rx_irq = rxirq_res->start;
+       ksp->rx_irq_name = rxirq_res->name ? rxirq_res->name : "Ethernet RX";
+       ksp->tx_irq = txirq_res->start;
+       ksp->tx_irq_name = txirq_res->name ? txirq_res->name : "Ethernet TX";
+       ksp->link_irq = (linkirq_res ? linkirq_res->start : -1);
+       ksp->link_irq_name = (linkirq_res && linkirq_res->name) ?
+               linkirq_res->name : "Ethernet Link";
+       /* driver system setup */
+       ndev->netdev_ops = &ks8695_netdev_ops;
+       ndev->watchdog_timeo     = msecs_to_jiffies(watchdog);
+       netif_napi_add(ndev, &ksp->napi, ks8695_poll, NAPI_WEIGHT);
+       /* Retrieve the default MAC addr from the chip. */
+       /* The bootloader should have left it in there for us. */
+       machigh = ks8695_readreg(ksp, KS8695_MAH);
+       maclow = ks8695_readreg(ksp, KS8695_MAL);
+       ndev->dev_addr[0] = (machigh >> 8) & 0xFF;
+       ndev->dev_addr[1] = machigh & 0xFF;
+       ndev->dev_addr[2] = (maclow >> 24) & 0xFF;
+       ndev->dev_addr[3] = (maclow >> 16) & 0xFF;
+       ndev->dev_addr[4] = (maclow >> 8) & 0xFF;
+       ndev->dev_addr[5] = maclow & 0xFF;
+       if (!is_valid_ether_addr(ndev->dev_addr))
+               dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please "
+                        "set using ifconfig\n", ndev->name);
+       /* In order to be efficient memory-wise, we allocate both
+        * rings in one go.
+        */
+       ksp->ring_base = dma_alloc_coherent(&pdev->dev, RING_DMA_SIZE,
+                                           &ksp->ring_base_dma, GFP_KERNEL);
+       if (!ksp->ring_base) {
+               ret = -ENOMEM;
+               goto failure;
+       }
+       /* Specify the TX DMA ring buffer */
+       ksp->tx_ring = ksp->ring_base;
+       ksp->tx_ring_dma = ksp->ring_base_dma;
+       /* And initialise the queue's lock */
+       spin_lock_init(&ksp->txq_lock);
+       spin_lock_init(&ksp->rx_lock);
+       /* Specify the RX DMA ring buffer */
+       ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE;
+       ksp->rx_ring_dma = ksp->ring_base_dma + TX_RING_DMA_SIZE;
+       /* Zero the descriptor rings */
+       memset(ksp->tx_ring, 0, TX_RING_DMA_SIZE);
+       memset(ksp->rx_ring, 0, RX_RING_DMA_SIZE);
+       /* Build the rings */
+       for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+               ksp->tx_ring[buff_n].next_desc =
+                       cpu_to_le32(ksp->tx_ring_dma +
+                                   (sizeof(struct tx_ring_desc) *
+                                    ((buff_n + 1) & MAX_TX_DESC_MASK)));
+       }
+       for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+               ksp->rx_ring[buff_n].next_desc =
+                       cpu_to_le32(ksp->rx_ring_dma +
+                                   (sizeof(struct rx_ring_desc) *
+                                    ((buff_n + 1) & MAX_RX_DESC_MASK)));
+       }
+       /* Initialise the port (physically) */
+       if (ksp->phyiface_regs && ksp->link_irq == -1) {
+               ks8695_init_switch(ksp);
+               ksp->dtype = KS8695_DTYPE_LAN;
+               SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+       } else if (ksp->phyiface_regs && ksp->link_irq != -1) {
+               ks8695_init_wan_phy(ksp);
+               ksp->dtype = KS8695_DTYPE_WAN;
+               SET_ETHTOOL_OPS(ndev, &ks8695_wan_ethtool_ops);
+       } else {
+               /* No initialisation since HPNA does not have a PHY */
+               ksp->dtype = KS8695_DTYPE_HPNA;
+               SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+       }
+       /* And bring up the net_device with the net core */
+       platform_set_drvdata(pdev, ndev);
+       ret = register_netdev(ndev);
+       if (ret == 0) {
+               dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %pM\n",
+                        ks8695_port_type(ksp), ndev->dev_addr);
+       } else {
+               /* Report the failure to register the net_device */
+               dev_err(ksp->dev, "ks8695net: failed to register netdev.\n");
+               goto failure;
+       }
+       /* All is well */
+       return 0;
+       /* Error exit path */
+ failure:
+       ks8695_release_device(ksp);
+       free_netdev(ndev);
+       return ret;
+ }
+ /**
+  *    ks8695_drv_suspend - Suspend a KS8695 ethernet platform device.
+  *    @pdev: The device to suspend
+  *    @state: The suspend state
+  *
+  *    This routine detaches and shuts down a KS8695 ethernet device.
+  */
+ static int
+ ks8695_drv_suspend(struct platform_device *pdev, pm_message_t state)
+ {
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       ksp->in_suspend = 1;
+       if (netif_running(ndev)) {
+               netif_device_detach(ndev);
+               ks8695_shutdown(ksp);
+       }
+       return 0;
+ }
+ /**
+  *    ks8695_drv_resume - Resume a KS8695 ethernet platform device.
+  *    @pdev: The device to resume
+  *
+  *    This routine re-initialises and re-attaches a KS8695 ethernet
+  *    device.
+  */
+ static int
+ ks8695_drv_resume(struct platform_device *pdev)
+ {
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       if (netif_running(ndev)) {
+               ks8695_reset(ksp);
+               ks8695_init_net(ksp);
+               ks8695_set_multicast(ndev);
+               netif_device_attach(ndev);
+       }
+       ksp->in_suspend = 0;
+       return 0;
+ }
+ /**
+  *    ks8695_drv_remove - Remove a KS8695 net device on driver unload.
+  *    @pdev: The platform device to remove
+  *
+  *    This unregisters and releases a KS8695 ethernet device.
+  */
+ static int __devexit
+ ks8695_drv_remove(struct platform_device *pdev)
+ {
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       platform_set_drvdata(pdev, NULL);
+       netif_napi_del(&ksp->napi);
+       unregister_netdev(ndev);
+       ks8695_release_device(ksp);
+       free_netdev(ndev);
+       dev_dbg(&pdev->dev, "released and freed device\n");
+       return 0;
+ }
+ static struct platform_driver ks8695_driver = {
+       .driver = {
+               .name   = MODULENAME,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ks8695_probe,
+       .remove         = __devexit_p(ks8695_drv_remove),
+       .suspend        = ks8695_drv_suspend,
+       .resume         = ks8695_drv_resume,
+ };
+ /* Module interface */
+ static int __init
+ ks8695_init(void)
+ {
+       printk(KERN_INFO "%s Ethernet driver, V%s\n",
+              MODULENAME, MODULEVERSION);
+       return platform_driver_register(&ks8695_driver);
+ }
+ static void __exit
+ ks8695_cleanup(void)
+ {
+       platform_driver_unregister(&ks8695_driver);
+ }
+ module_init(ks8695_init);
+ module_exit(ks8695_cleanup);
+ MODULE_AUTHOR("Simtec Electronics");
+ MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver");
+ MODULE_LICENSE("GPL");
+ MODULE_ALIAS("platform:" MODULENAME);
+ module_param(watchdog, int, 0400);
+ MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
index 0000000,1854c88..5a689af
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,137 +1,136 @@@
 -        To compile this driver as a module, choose M here and read
 -        <file:Documentation/networking/net-modules.txt>. The module
+ #
+ # Western Digital/SMC network device configuration
+ #
+ config NET_VENDOR_SMSC
+       bool "SMC (SMSC)/Western Digital devices"
+       default y
+       depends on ARM || ISA || MAC || ARM || MIPS || M32R || SUPERH || \
+               BLACKFIN || MN10300 || COLDFIRE || PCI || PCMCIA
+       ---help---
+         If you have a network (Ethernet) card belonging to this class, say Y
+         and read the Ethernet-HOWTO, available from
+         <http://www.tldp.org/docs.html#howto>.
+         Note that the answer to this question doesn't directly affect the
+         kernel: saying N will just cause the configurator to skip all
+         the questions about SMC/Western Digital cards. If you say Y, you will
+         be asked for your specific card in the following questions.
+ if NET_VENDOR_SMSC
+ config SMC9194
+       tristate "SMC 9194 support"
+       depends on (ISA || MAC && BROKEN)
+       select CRC32
+       ---help---
+         This is support for the SMC9xxx based Ethernet cards. Choose this
+         option if you have a DELL laptop with the docking station, or
+         another SMC9192/9194 based chipset.  Say Y if you want it compiled
+         into the kernel, and read the file
+         <file:Documentation/networking/smc9.txt> and the Ethernet-HOWTO,
+         available from <http://www.tldp.org/docs.html#howto>.
+         To compile this driver as a module, choose M here. The module
+         will be called smc9194.
+ config SMC91X
+       tristate "SMC 91C9x/91C1xxx support"
+       select CRC32
+       select NET_CORE
+       select MII
+       depends on (ARM || M32R || SUPERH || MIPS || BLACKFIN || \
+                   MN10300 || COLDFIRE)
+       ---help---
+         This is a driver for SMC's 91x series of Ethernet chipsets,
+         including the SMC91C94 and the SMC91C111. Say Y if you want it
+         compiled into the kernel, and read the file
+         <file:Documentation/networking/smc9.txt>  and the Ethernet-HOWTO,
+         available from  <http://www.tldp.org/docs.html#howto>.
+         This driver is also available as a module ( = code which can be
+         inserted in and removed from the running kernel whenever you want).
+         The module will be called smc91x.  If you want to compile it as a
+         module, say M here and read <file:Documentation/kbuild/modules.txt>.
+ config PCMCIA_SMC91C92
+       tristate "SMC 91Cxx PCMCIA support"
+       depends on PCMCIA
+       select CRC32
+       select NET_CORE
+       select MII
+       ---help---
+         Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA
+         (PC-card) Ethernet or Fast Ethernet card to your computer.
+         To compile this driver as a module, choose M here: the module will be
+         called smc91c92_cs.  If unsure, say N.
+ config EPIC100
+       tristate "SMC EtherPower II"
+       depends on PCI
+       select CRC32
+       select NET_CORE
+       select MII
+       ---help---
+         This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC,
+         which is based on the SMC83c17x (EPIC/100).
+         More specific information and updates are available from
+         <http://www.scyld.com/network/epic100.html>.
+ config SMC911X
+       tristate "SMSC LAN911[5678] support"
+       select CRC32
+       select NET_CORE
+       select MII
+       depends on (ARM || SUPERH || MN10300)
+       ---help---
+         This is a driver for SMSC's LAN911x series of Ethernet chipsets
+         including the new LAN9115, LAN9116, LAN9117, and LAN9118.
+         Say Y if you want it compiled into the kernel,
+         and read the Ethernet-HOWTO, available from
+         <http://www.tldp.org/docs.html#howto>.
+         This driver is also available as a module. The module will be
+         called smc911x.  If you want to compile it as a module, say M
+         here and read <file:Documentation/kbuild/modules.txt>
+ config SMSC911X
+       tristate "SMSC LAN911x/LAN921x families embedded ethernet support"
+       depends on (ARM || SUPERH || BLACKFIN || MIPS || MN10300)
+       select CRC32
+       select NET_CORE
+       select MII
+       select PHYLIB
+       ---help---
+         Say Y here if you want support for SMSC LAN911x and LAN921x families
+         of ethernet controllers.
++        To compile this driver as a module, choose M here. The module
+         will be called smsc911x.
+ config SMSC911X_ARCH_HOOKS
+       def_bool n
+       depends on SMSC911X
+       ---help---
+         If the arch enables this, it allows the arch to implement various
+         hooks for more comprehensive interrupt control and also to override
+         the source of the MAC address.
+ config SMSC9420
+       tristate "SMSC LAN9420 PCI ethernet adapter support"
+       depends on PCI
+       select CRC32
+       select PHYLIB
+       select SMSC_PHY
+       ---help---
+         This is a driver for SMSC's LAN9420 PCI ethernet adapter.
+         Say Y if you want it compiled into the kernel,
+         and read the Ethernet-HOWTO, available from
+         <http://www.tldp.org/docs.html#howto>.
+         This driver is also available as a module. The module will be
+         called smsc9420.  If you want to compile it as a module, say M
+         here and read <file:Documentation/kbuild/modules.txt>
+ endif # NET_VENDOR_SMSC
index 0000000,78e3fb2..10826d8
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,2465 +1,2465 @@@
 -      /* True if this tile has succcessfully registered with the IPP. */
+ /*
+  * Copyright 2011 Tilera Corporation. All Rights Reserved.
+  *
+  *   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, version 2.
+  *
+  *   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, GOOD TITLE or
+  *   NON INFRINGEMENT.  See the GNU General Public License for
+  *   more details.
+  */
+ #include <linux/module.h>
+ #include <linux/init.h>
+ #include <linux/moduleparam.h>
+ #include <linux/sched.h>
+ #include <linux/kernel.h>      /* printk() */
+ #include <linux/slab.h>        /* kmalloc() */
+ #include <linux/errno.h>       /* error codes */
+ #include <linux/types.h>       /* size_t */
+ #include <linux/interrupt.h>
+ #include <linux/in.h>
+ #include <linux/netdevice.h>   /* struct device, and other headers */
+ #include <linux/etherdevice.h> /* eth_type_trans */
+ #include <linux/skbuff.h>
+ #include <linux/ioctl.h>
+ #include <linux/cdev.h>
+ #include <linux/hugetlb.h>
+ #include <linux/in6.h>
+ #include <linux/timer.h>
+ #include <linux/io.h>
+ #include <asm/checksum.h>
+ #include <asm/homecache.h>
+ #include <hv/drv_xgbe_intf.h>
+ #include <hv/drv_xgbe_impl.h>
+ #include <hv/hypervisor.h>
+ #include <hv/netio_intf.h>
+ /* For TSO */
+ #include <linux/ip.h>
+ #include <linux/tcp.h>
+ /*
+  * First, "tile_net_init_module()" initializes all four "devices" which
+  * can be used by linux.
+  *
+  * Then, "ifconfig DEVICE up" calls "tile_net_open()", which analyzes
+  * the network cpus, then uses "tile_net_open_aux()" to initialize
+  * LIPP/LEPP, and then uses "tile_net_open_inner()" to register all
+  * the tiles, provide buffers to LIPP, allow ingress to start, and
+  * turn on hypervisor interrupt handling (and NAPI) on all tiles.
+  *
+  * If registration fails due to the link being down, then "retry_work"
+  * is used to keep calling "tile_net_open_inner()" until it succeeds.
+  *
+  * If "ifconfig DEVICE down" is called, it uses "tile_net_stop()" to
+  * stop egress, drain the LIPP buffers, unregister all the tiles, stop
+  * LIPP/LEPP, and wipe the LEPP queue.
+  *
+  * We start out with the ingress interrupt enabled on each CPU.  When
+  * this interrupt fires, we disable it, and call "napi_schedule()".
+  * This will cause "tile_net_poll()" to be called, which will pull
+  * packets from the netio queue, filtering them out, or passing them
+  * to "netif_receive_skb()".  If our budget is exhausted, we will
+  * return, knowing we will be called again later.  Otherwise, we
+  * reenable the ingress interrupt, and call "napi_complete()".
+  *
+  * HACK: Since disabling the ingress interrupt is not reliable, we
+  * ignore the interrupt if the global "active" flag is false.
+  *
+  *
+  * NOTE: The use of "native_driver" ensures that EPP exists, and that
+  * we are using "LIPP" and "LEPP".
+  *
+  * NOTE: Failing to free completions for an arbitrarily long time
+  * (which is defined to be illegal) does in fact cause bizarre
+  * problems.  The "egress_timer" helps prevent this from happening.
+  */
+ /* HACK: Allow use of "jumbo" packets. */
+ /* This should be 1500 if "jumbo" is not set in LIPP. */
+ /* This should be at most 10226 (10240 - 14) if "jumbo" is set in LIPP. */
+ /* ISSUE: This has not been thoroughly tested (except at 1500). */
+ #define TILE_NET_MTU 1500
+ /* HACK: Define to support GSO. */
+ /* ISSUE: This may actually hurt performance of the TCP blaster. */
+ /* #define TILE_NET_GSO */
+ /* Define this to collapse "duplicate" acks. */
+ /* #define IGNORE_DUP_ACKS */
+ /* HACK: Define this to verify incoming packets. */
+ /* #define TILE_NET_VERIFY_INGRESS */
+ /* Use 3000 to enable the Linux Traffic Control (QoS) layer, else 0. */
+ #define TILE_NET_TX_QUEUE_LEN 0
+ /* Define to dump packets (prints out the whole packet on tx and rx). */
+ /* #define TILE_NET_DUMP_PACKETS */
+ /* Define to enable debug spew (all PDEBUG's are enabled). */
+ /* #define TILE_NET_DEBUG */
+ /* Define to activate paranoia checks. */
+ /* #define TILE_NET_PARANOIA */
+ /* Default transmit lockup timeout period, in jiffies. */
+ #define TILE_NET_TIMEOUT (5 * HZ)
+ /* Default retry interval for bringing up the NetIO interface, in jiffies. */
+ #define TILE_NET_RETRY_INTERVAL (5 * HZ)
+ /* Number of ports (xgbe0, xgbe1, gbe0, gbe1). */
+ #define TILE_NET_DEVS 4
+ /* Paranoia. */
+ #if NET_IP_ALIGN != LIPP_PACKET_PADDING
+ #error "NET_IP_ALIGN must match LIPP_PACKET_PADDING."
+ #endif
+ /* Debug print. */
+ #ifdef TILE_NET_DEBUG
+ #define PDEBUG(fmt, args...) net_printk(fmt, ## args)
+ #else
+ #define PDEBUG(fmt, args...)
+ #endif
+ MODULE_AUTHOR("Tilera");
+ MODULE_LICENSE("GPL");
+ /*
+  * Queue of incoming packets for a specific cpu and device.
+  *
+  * Includes a pointer to the "system" data, and the actual "user" data.
+  */
+ struct tile_netio_queue {
+       netio_queue_impl_t *__system_part;
+       netio_queue_user_impl_t __user_part;
+ };
+ /*
+  * Statistics counters for a specific cpu and device.
+  */
+ struct tile_net_stats_t {
+       u32 rx_packets;
+       u32 rx_bytes;
+       u32 tx_packets;
+       u32 tx_bytes;
+ };
+ /*
+  * Info for a specific cpu and device.
+  *
+  * ISSUE: There is a "dev" pointer in "napi" as well.
+  */
+ struct tile_net_cpu {
+       /* The NAPI struct. */
+       struct napi_struct napi;
+       /* Packet queue. */
+       struct tile_netio_queue queue;
+       /* Statistics. */
+       struct tile_net_stats_t stats;
+       /* True iff NAPI is enabled. */
+       bool napi_enabled;
++      /* True if this tile has successfully registered with the IPP. */
+       bool registered;
+       /* True if the link was down last time we tried to register. */
+       bool link_down;
+       /* True if "egress_timer" is scheduled. */
+       bool egress_timer_scheduled;
+       /* Number of small sk_buffs which must still be provided. */
+       unsigned int num_needed_small_buffers;
+       /* Number of large sk_buffs which must still be provided. */
+       unsigned int num_needed_large_buffers;
+       /* A timer for handling egress completions. */
+       struct timer_list egress_timer;
+ };
+ /*
+  * Info for a specific device.
+  */
+ struct tile_net_priv {
+       /* Our network device. */
+       struct net_device *dev;
+       /* Pages making up the egress queue. */
+       struct page *eq_pages;
+       /* Address of the actual egress queue. */
+       lepp_queue_t *eq;
+       /* Protects "eq". */
+       spinlock_t eq_lock;
+       /* The hypervisor handle for this interface. */
+       int hv_devhdl;
+       /* The intr bit mask that IDs this device. */
+       u32 intr_id;
+       /* True iff "tile_net_open_aux()" has succeeded. */
+       bool partly_opened;
+       /* True iff the device is "active". */
+       bool active;
+       /* Effective network cpus. */
+       struct cpumask network_cpus_map;
+       /* Number of network cpus. */
+       int network_cpus_count;
+       /* Credits per network cpu. */
+       int network_cpus_credits;
+       /* Network stats. */
+       struct net_device_stats stats;
+       /* For NetIO bringup retries. */
+       struct delayed_work retry_work;
+       /* Quick access to per cpu data. */
+       struct tile_net_cpu *cpu[NR_CPUS];
+ };
+ /* Log2 of the number of small pages needed for the egress queue. */
+ #define EQ_ORDER  get_order(sizeof(lepp_queue_t))
+ /* Size of the egress queue's pages. */
+ #define EQ_SIZE   (1 << (PAGE_SHIFT + EQ_ORDER))
+ /*
+  * The actual devices (xgbe0, xgbe1, gbe0, gbe1).
+  */
+ static struct net_device *tile_net_devs[TILE_NET_DEVS];
+ /*
+  * The "tile_net_cpu" structures for each device.
+  */
+ static DEFINE_PER_CPU(struct tile_net_cpu, hv_xgbe0);
+ static DEFINE_PER_CPU(struct tile_net_cpu, hv_xgbe1);
+ static DEFINE_PER_CPU(struct tile_net_cpu, hv_gbe0);
+ static DEFINE_PER_CPU(struct tile_net_cpu, hv_gbe1);
+ /*
+  * True if "network_cpus" was specified.
+  */
+ static bool network_cpus_used;
+ /*
+  * The actual cpus in "network_cpus".
+  */
+ static struct cpumask network_cpus_map;
+ #ifdef TILE_NET_DEBUG
+ /*
+  * printk with extra stuff.
+  *
+  * We print the CPU we're running in brackets.
+  */
+ static void net_printk(char *fmt, ...)
+ {
+       int i;
+       int len;
+       va_list args;
+       static char buf[256];
+       len = sprintf(buf, "tile_net[%2.2d]: ", smp_processor_id());
+       va_start(args, fmt);
+       i = vscnprintf(buf + len, sizeof(buf) - len - 1, fmt, args);
+       va_end(args);
+       buf[255] = '\0';
+       pr_notice(buf);
+ }
+ #endif
+ #ifdef TILE_NET_DUMP_PACKETS
+ /*
+  * Dump a packet.
+  */
+ static void dump_packet(unsigned char *data, unsigned long length, char *s)
+ {
+       int my_cpu = smp_processor_id();
+       unsigned long i;
+       char buf[128];
+       static unsigned int count;
+       pr_info("dump_packet(data %p, length 0x%lx s %s count 0x%x)\n",
+              data, length, s, count++);
+       pr_info("\n");
+       for (i = 0; i < length; i++) {
+               if ((i & 0xf) == 0)
+                       sprintf(buf, "[%02d] %8.8lx:", my_cpu, i);
+               sprintf(buf + strlen(buf), " %2.2x", data[i]);
+               if ((i & 0xf) == 0xf || i == length - 1) {
+                       strcat(buf, "\n");
+                       pr_info("%s", buf);
+               }
+       }
+ }
+ #endif
+ /*
+  * Provide support for the __netio_fastio1() swint
+  * (see <hv/drv_xgbe_intf.h> for how it is used).
+  *
+  * The fastio swint2 call may clobber all the caller-saved registers.
+  * It rarely clobbers memory, but we allow for the possibility in
+  * the signature just to be on the safe side.
+  *
+  * Also, gcc doesn't seem to allow an input operand to be
+  * clobbered, so we fake it with dummy outputs.
+  *
+  * This function can't be static because of the way it is declared
+  * in the netio header.
+  */
+ inline int __netio_fastio1(u32 fastio_index, u32 arg0)
+ {
+       long result, clobber_r1, clobber_r10;
+       asm volatile("swint2"
+                    : "=R00" (result),
+                      "=R01" (clobber_r1), "=R10" (clobber_r10)
+                    : "R10" (fastio_index), "R01" (arg0)
+                    : "memory", "r2", "r3", "r4",
+                      "r5", "r6", "r7", "r8", "r9",
+                      "r11", "r12", "r13", "r14",
+                      "r15", "r16", "r17", "r18", "r19",
+                      "r20", "r21", "r22", "r23", "r24",
+                      "r25", "r26", "r27", "r28", "r29");
+       return result;
+ }
+ /*
+  * Provide a linux buffer to LIPP.
+  */
+ static void tile_net_provide_linux_buffer(struct tile_net_cpu *info,
+                                         void *va, bool small)
+ {
+       struct tile_netio_queue *queue = &info->queue;
+       /* Convert "va" and "small" to "linux_buffer_t". */
+       unsigned int buffer = ((unsigned int)(__pa(va) >> 7) << 1) + small;
+       __netio_fastio_free_buffer(queue->__user_part.__fastio_index, buffer);
+ }
+ /*
+  * Provide a linux buffer for LIPP.
+  *
+  * Note that the ACTUAL allocation for each buffer is a "struct sk_buff",
+  * plus a chunk of memory that includes not only the requested bytes, but
+  * also NET_SKB_PAD bytes of initial padding, and a "struct skb_shared_info".
+  *
+  * Note that "struct skb_shared_info" is 88 bytes with 64K pages and
+  * 268 bytes with 4K pages (since the frags[] array needs 18 entries).
+  *
+  * Without jumbo packets, the maximum packet size will be 1536 bytes,
+  * and we use 2 bytes (NET_IP_ALIGN) of padding.  ISSUE: If we told
+  * the hardware to clip at 1518 bytes instead of 1536 bytes, then we
+  * could save an entire cache line, but in practice, we don't need it.
+  *
+  * Since CPAs are 38 bits, and we can only encode the high 31 bits in
+  * a "linux_buffer_t", the low 7 bits must be zero, and thus, we must
+  * align the actual "va" mod 128.
+  *
+  * We assume that the underlying "head" will be aligned mod 64.  Note
+  * that in practice, we have seen "head" NOT aligned mod 128 even when
+  * using 2048 byte allocations, which is surprising.
+  *
+  * If "head" WAS always aligned mod 128, we could change LIPP to
+  * assume that the low SIX bits are zero, and the 7th bit is one, that
+  * is, align the actual "va" mod 128 plus 64, which would be "free".
+  *
+  * For now, the actual "head" pointer points at NET_SKB_PAD bytes of
+  * padding, plus 28 or 92 bytes of extra padding, plus the sk_buff
+  * pointer, plus the NET_IP_ALIGN padding, plus 126 or 1536 bytes for
+  * the actual packet, plus 62 bytes of empty padding, plus some
+  * padding and the "struct skb_shared_info".
+  *
+  * With 64K pages, a large buffer thus needs 32+92+4+2+1536+62+88
+  * bytes, or 1816 bytes, which fits comfortably into 2048 bytes.
+  *
+  * With 64K pages, a small buffer thus needs 32+92+4+2+126+88
+  * bytes, or 344 bytes, which means we are wasting 64+ bytes, and
+  * could presumably increase the size of small buffers.
+  *
+  * With 4K pages, a large buffer thus needs 32+92+4+2+1536+62+268
+  * bytes, or 1996 bytes, which fits comfortably into 2048 bytes.
+  *
+  * With 4K pages, a small buffer thus needs 32+92+4+2+126+268
+  * bytes, or 524 bytes, which is annoyingly wasteful.
+  *
+  * Maybe we should increase LIPP_SMALL_PACKET_SIZE to 192?
+  *
+  * ISSUE: Maybe we should increase "NET_SKB_PAD" to 64?
+  */
+ static bool tile_net_provide_needed_buffer(struct tile_net_cpu *info,
+                                          bool small)
+ {
+ #if TILE_NET_MTU <= 1536
+       /* Without "jumbo", 2 + 1536 should be sufficient. */
+       unsigned int large_size = NET_IP_ALIGN + 1536;
+ #else
+       /* ISSUE: This has not been tested. */
+       unsigned int large_size = NET_IP_ALIGN + TILE_NET_MTU + 100;
+ #endif
+       /* Avoid "false sharing" with last cache line. */
+       /* ISSUE: This is already done by "dev_alloc_skb()". */
+       unsigned int len =
+                (((small ? LIPP_SMALL_PACKET_SIZE : large_size) +
+                  CHIP_L2_LINE_SIZE() - 1) & -CHIP_L2_LINE_SIZE());
+       unsigned int padding = 128 - NET_SKB_PAD;
+       unsigned int align;
+       struct sk_buff *skb;
+       void *va;
+       struct sk_buff **skb_ptr;
+       /* Request 96 extra bytes for alignment purposes. */
+       skb = dev_alloc_skb(len + padding);
+       if (skb == NULL)
+               return false;
+       /* Skip 32 or 96 bytes to align "data" mod 128. */
+       align = -(long)skb->data & (128 - 1);
+       BUG_ON(align > padding);
+       skb_reserve(skb, align);
+       /* This address is given to IPP. */
+       va = skb->data;
+       /* Buffers must not span a huge page. */
+       BUG_ON(((((long)va & ~HPAGE_MASK) + len) & HPAGE_MASK) != 0);
+ #ifdef TILE_NET_PARANOIA
+ #if CHIP_HAS_CBOX_HOME_MAP()
+       if (hash_default) {
+               HV_PTE pte = *virt_to_pte(current->mm, (unsigned long)va);
+               if (hv_pte_get_mode(pte) != HV_PTE_MODE_CACHE_HASH_L3)
+                       panic("Non-HFH ingress buffer! VA=%p Mode=%d PTE=%llx",
+                             va, hv_pte_get_mode(pte), hv_pte_val(pte));
+       }
+ #endif
+ #endif
+       /* Invalidate the packet buffer. */
+       if (!hash_default)
+               __inv_buffer(va, len);
+       /* Skip two bytes to satisfy LIPP assumptions. */
+       /* Note that this aligns IP on a 16 byte boundary. */
+       /* ISSUE: Do this when the packet arrives? */
+       skb_reserve(skb, NET_IP_ALIGN);
+       /* Save a back-pointer to 'skb'. */
+       skb_ptr = va - sizeof(*skb_ptr);
+       *skb_ptr = skb;
+       /* Make sure "skb_ptr" has been flushed. */
+       __insn_mf();
+       /* Provide the new buffer. */
+       tile_net_provide_linux_buffer(info, va, small);
+       return true;
+ }
+ /*
+  * Provide linux buffers for LIPP.
+  */
+ static void tile_net_provide_needed_buffers(struct tile_net_cpu *info)
+ {
+       while (info->num_needed_small_buffers != 0) {
+               if (!tile_net_provide_needed_buffer(info, true))
+                       goto oops;
+               info->num_needed_small_buffers--;
+       }
+       while (info->num_needed_large_buffers != 0) {
+               if (!tile_net_provide_needed_buffer(info, false))
+                       goto oops;
+               info->num_needed_large_buffers--;
+       }
+       return;
+ oops:
+       /* Add a description to the page allocation failure dump. */
+       pr_notice("Could not provide a linux buffer to LIPP.\n");
+ }
+ /*
+  * Grab some LEPP completions, and store them in "comps", of size
+  * "comps_size", and return the number of completions which were
+  * stored, so the caller can free them.
+  */
+ static unsigned int tile_net_lepp_grab_comps(lepp_queue_t *eq,
+                                            struct sk_buff *comps[],
+                                            unsigned int comps_size,
+                                            unsigned int min_size)
+ {
+       unsigned int n = 0;
+       unsigned int comp_head = eq->comp_head;
+       unsigned int comp_busy = eq->comp_busy;
+       while (comp_head != comp_busy && n < comps_size) {
+               comps[n++] = eq->comps[comp_head];
+               LEPP_QINC(comp_head);
+       }
+       if (n < min_size)
+               return 0;
+       eq->comp_head = comp_head;
+       return n;
+ }
+ /*
+  * Free some comps, and return true iff there are still some pending.
+  */
+ static bool tile_net_lepp_free_comps(struct net_device *dev, bool all)
+ {
+       struct tile_net_priv *priv = netdev_priv(dev);
+       lepp_queue_t *eq = priv->eq;
+       struct sk_buff *olds[64];
+       unsigned int wanted = 64;
+       unsigned int i, n;
+       bool pending;
+       spin_lock(&priv->eq_lock);
+       if (all)
+               eq->comp_busy = eq->comp_tail;
+       n = tile_net_lepp_grab_comps(eq, olds, wanted, 0);
+       pending = (eq->comp_head != eq->comp_tail);
+       spin_unlock(&priv->eq_lock);
+       for (i = 0; i < n; i++)
+               kfree_skb(olds[i]);
+       return pending;
+ }
+ /*
+  * Make sure the egress timer is scheduled.
+  *
+  * Note that we use "schedule if not scheduled" logic instead of the more
+  * obvious "reschedule" logic, because "reschedule" is fairly expensive.
+  */
+ static void tile_net_schedule_egress_timer(struct tile_net_cpu *info)
+ {
+       if (!info->egress_timer_scheduled) {
+               mod_timer_pinned(&info->egress_timer, jiffies + 1);
+               info->egress_timer_scheduled = true;
+       }
+ }
+ /*
+  * The "function" for "info->egress_timer".
+  *
+  * This timer will reschedule itself as long as there are any pending
+  * completions expected (on behalf of any tile).
+  *
+  * ISSUE: Realistically, will the timer ever stop scheduling itself?
+  *
+  * ISSUE: This timer is almost never actually needed, so just use a global
+  * timer that can run on any tile.
+  *
+  * ISSUE: Maybe instead track number of expected completions, and free
+  * only that many, resetting to zero if "pending" is ever false.
+  */
+ static void tile_net_handle_egress_timer(unsigned long arg)
+ {
+       struct tile_net_cpu *info = (struct tile_net_cpu *)arg;
+       struct net_device *dev = info->napi.dev;
+       /* The timer is no longer scheduled. */
+       info->egress_timer_scheduled = false;
+       /* Free comps, and reschedule timer if more are pending. */
+       if (tile_net_lepp_free_comps(dev, false))
+               tile_net_schedule_egress_timer(info);
+ }
+ #ifdef IGNORE_DUP_ACKS
+ /*
+  * Help detect "duplicate" ACKs.  These are sequential packets (for a
+  * given flow) which are exactly 66 bytes long, sharing everything but
+  * ID=2@0x12, Hsum=2@0x18, Ack=4@0x2a, WinSize=2@0x30, Csum=2@0x32,
+  * Tstamps=10@0x38.  The ID's are +1, the Hsum's are -1, the Ack's are
+  * +N, and the Tstamps are usually identical.
+  *
+  * NOTE: Apparently truly duplicate acks (with identical "ack" values),
+  * should not be collapsed, as they are used for some kind of flow control.
+  */
+ static bool is_dup_ack(char *s1, char *s2, unsigned int len)
+ {
+       int i;
+       unsigned long long ignorable = 0;
+       /* Identification. */
+       ignorable |= (1ULL << 0x12);
+       ignorable |= (1ULL << 0x13);
+       /* Header checksum. */
+       ignorable |= (1ULL << 0x18);
+       ignorable |= (1ULL << 0x19);
+       /* ACK. */
+       ignorable |= (1ULL << 0x2a);
+       ignorable |= (1ULL << 0x2b);
+       ignorable |= (1ULL << 0x2c);
+       ignorable |= (1ULL << 0x2d);
+       /* WinSize. */
+       ignorable |= (1ULL << 0x30);
+       ignorable |= (1ULL << 0x31);
+       /* Checksum. */
+       ignorable |= (1ULL << 0x32);
+       ignorable |= (1ULL << 0x33);
+       for (i = 0; i < len; i++, ignorable >>= 1) {
+               if ((ignorable & 1) || (s1[i] == s2[i]))
+                       continue;
+ #ifdef TILE_NET_DEBUG
+               /* HACK: Mention non-timestamp diffs. */
+               if (i < 0x38 && i != 0x2f &&
+                   net_ratelimit())
+                       pr_info("Diff at 0x%x\n", i);
+ #endif
+               return false;
+       }
+ #ifdef TILE_NET_NO_SUPPRESS_DUP_ACKS
+       /* HACK: Do not suppress truly duplicate ACKs. */
+       /* ISSUE: Is this actually necessary or helpful? */
+       if (s1[0x2a] == s2[0x2a] &&
+           s1[0x2b] == s2[0x2b] &&
+           s1[0x2c] == s2[0x2c] &&
+           s1[0x2d] == s2[0x2d]) {
+               return false;
+       }
+ #endif
+       return true;
+ }
+ #endif
+ static void tile_net_discard_aux(struct tile_net_cpu *info, int index)
+ {
+       struct tile_netio_queue *queue = &info->queue;
+       netio_queue_impl_t *qsp = queue->__system_part;
+       netio_queue_user_impl_t *qup = &queue->__user_part;
+       int index2_aux = index + sizeof(netio_pkt_t);
+       int index2 =
+               ((index2_aux ==
+                 qsp->__packet_receive_queue.__last_packet_plus_one) ?
+                0 : index2_aux);
+       netio_pkt_t *pkt = (netio_pkt_t *)((unsigned long) &qsp[1] + index);
+       /* Extract the "linux_buffer_t". */
+       unsigned int buffer = pkt->__packet.word;
+       /* Convert "linux_buffer_t" to "va". */
+       void *va = __va((phys_addr_t)(buffer >> 1) << 7);
+       /* Acquire the associated "skb". */
+       struct sk_buff **skb_ptr = va - sizeof(*skb_ptr);
+       struct sk_buff *skb = *skb_ptr;
+       kfree_skb(skb);
+       /* Consume this packet. */
+       qup->__packet_receive_read = index2;
+ }
+ /*
+  * Like "tile_net_poll()", but just discard packets.
+  */
+ static void tile_net_discard_packets(struct net_device *dev)
+ {
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int my_cpu = smp_processor_id();
+       struct tile_net_cpu *info = priv->cpu[my_cpu];
+       struct tile_netio_queue *queue = &info->queue;
+       netio_queue_impl_t *qsp = queue->__system_part;
+       netio_queue_user_impl_t *qup = &queue->__user_part;
+       while (qup->__packet_receive_read !=
+              qsp->__packet_receive_queue.__packet_write) {
+               int index = qup->__packet_receive_read;
+               tile_net_discard_aux(info, index);
+       }
+ }
+ /*
+  * Handle the next packet.  Return true if "processed", false if "filtered".
+  */
+ static bool tile_net_poll_aux(struct tile_net_cpu *info, int index)
+ {
+       struct net_device *dev = info->napi.dev;
+       struct tile_netio_queue *queue = &info->queue;
+       netio_queue_impl_t *qsp = queue->__system_part;
+       netio_queue_user_impl_t *qup = &queue->__user_part;
+       struct tile_net_stats_t *stats = &info->stats;
+       int filter;
+       int index2_aux = index + sizeof(netio_pkt_t);
+       int index2 =
+               ((index2_aux ==
+                 qsp->__packet_receive_queue.__last_packet_plus_one) ?
+                0 : index2_aux);
+       netio_pkt_t *pkt = (netio_pkt_t *)((unsigned long) &qsp[1] + index);
+       netio_pkt_metadata_t *metadata = NETIO_PKT_METADATA(pkt);
+       /* Extract the packet size.  FIXME: Shouldn't the second line */
+       /* get subtracted?  Mostly moot, since it should be "zero". */
+       unsigned long len =
+               (NETIO_PKT_CUSTOM_LENGTH(pkt) +
+                NET_IP_ALIGN - NETIO_PACKET_PADDING);
+       /* Extract the "linux_buffer_t". */
+       unsigned int buffer = pkt->__packet.word;
+       /* Extract "small" (vs "large"). */
+       bool small = ((buffer & 1) != 0);
+       /* Convert "linux_buffer_t" to "va". */
+       void *va = __va((phys_addr_t)(buffer >> 1) << 7);
+       /* Extract the packet data pointer. */
+       /* Compare to "NETIO_PKT_CUSTOM_DATA(pkt)". */
+       unsigned char *buf = va + NET_IP_ALIGN;
+       /* Invalidate the packet buffer. */
+       if (!hash_default)
+               __inv_buffer(buf, len);
+       /* ISSUE: Is this needed? */
+       dev->last_rx = jiffies;
+ #ifdef TILE_NET_DUMP_PACKETS
+       dump_packet(buf, len, "rx");
+ #endif /* TILE_NET_DUMP_PACKETS */
+ #ifdef TILE_NET_VERIFY_INGRESS
+       if (!NETIO_PKT_L4_CSUM_CORRECT_M(metadata, pkt) &&
+           NETIO_PKT_L4_CSUM_CALCULATED_M(metadata, pkt)) {
+               /* Bug 6624: Includes UDP packets with a "zero" checksum. */
+               pr_warning("Bad L4 checksum on %d byte packet.\n", len);
+       }
+       if (!NETIO_PKT_L3_CSUM_CORRECT_M(metadata, pkt) &&
+           NETIO_PKT_L3_CSUM_CALCULATED_M(metadata, pkt)) {
+               dump_packet(buf, len, "rx");
+               panic("Bad L3 checksum.");
+       }
+       switch (NETIO_PKT_STATUS_M(metadata, pkt)) {
+       case NETIO_PKT_STATUS_OVERSIZE:
+               if (len >= 64) {
+                       dump_packet(buf, len, "rx");
+                       panic("Unexpected OVERSIZE.");
+               }
+               break;
+       case NETIO_PKT_STATUS_BAD:
+               pr_warning("Unexpected BAD %ld byte packet.\n", len);
+       }
+ #endif
+       filter = 0;
+       /* ISSUE: Filter TCP packets with "bad" checksums? */
+       if (!(dev->flags & IFF_UP)) {
+               /* Filter packets received before we're up. */
+               filter = 1;
+       } else if (NETIO_PKT_STATUS_M(metadata, pkt) == NETIO_PKT_STATUS_BAD) {
+               /* Filter "truncated" packets. */
+               filter = 1;
+       } else if (!(dev->flags & IFF_PROMISC)) {
+               /* FIXME: Implement HW multicast filter. */
+               if (!is_multicast_ether_addr(buf)) {
+                       /* Filter packets not for our address. */
+                       const u8 *mine = dev->dev_addr;
+                       filter = compare_ether_addr(mine, buf);
+               }
+       }
+       if (filter) {
+               /* ISSUE: Update "drop" statistics? */
+               tile_net_provide_linux_buffer(info, va, small);
+       } else {
+               /* Acquire the associated "skb". */
+               struct sk_buff **skb_ptr = va - sizeof(*skb_ptr);
+               struct sk_buff *skb = *skb_ptr;
+               /* Paranoia. */
+               if (skb->data != buf)
+                       panic("Corrupt linux buffer from LIPP! "
+                             "VA=%p, skb=%p, skb->data=%p\n",
+                             va, skb, skb->data);
+               /* Encode the actual packet length. */
+               skb_put(skb, len);
+               /* NOTE: This call also sets "skb->dev = dev". */
+               skb->protocol = eth_type_trans(skb, dev);
+               /* Avoid recomputing "good" TCP/UDP checksums. */
+               if (NETIO_PKT_L4_CSUM_CORRECT_M(metadata, pkt))
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+               netif_receive_skb(skb);
+               stats->rx_packets++;
+               stats->rx_bytes += len;
+               if (small)
+                       info->num_needed_small_buffers++;
+               else
+                       info->num_needed_large_buffers++;
+       }
+       /* Return four credits after every fourth packet. */
+       if (--qup->__receive_credit_remaining == 0) {
+               u32 interval = qup->__receive_credit_interval;
+               qup->__receive_credit_remaining = interval;
+               __netio_fastio_return_credits(qup->__fastio_index, interval);
+       }
+       /* Consume this packet. */
+       qup->__packet_receive_read = index2;
+       return !filter;
+ }
+ /*
+  * Handle some packets for the given device on the current CPU.
+  *
+  * If "tile_net_stop()" is called on some other tile while this
+  * function is running, we will return, hopefully before that
+  * other tile asks us to call "napi_disable()".
+  *
+  * The "rotting packet" race condition occurs if a packet arrives
+  * during the extremely narrow window between the queue appearing to
+  * be empty, and the ingress interrupt being re-enabled.  This happens
+  * a LOT under heavy network load.
+  */
+ static int tile_net_poll(struct napi_struct *napi, int budget)
+ {
+       struct net_device *dev = napi->dev;
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int my_cpu = smp_processor_id();
+       struct tile_net_cpu *info = priv->cpu[my_cpu];
+       struct tile_netio_queue *queue = &info->queue;
+       netio_queue_impl_t *qsp = queue->__system_part;
+       netio_queue_user_impl_t *qup = &queue->__user_part;
+       unsigned int work = 0;
+       while (priv->active) {
+               int index = qup->__packet_receive_read;
+               if (index == qsp->__packet_receive_queue.__packet_write)
+                       break;
+               if (tile_net_poll_aux(info, index)) {
+                       if (++work >= budget)
+                               goto done;
+               }
+       }
+       napi_complete(&info->napi);
+       if (!priv->active)
+               goto done;
+       /* Re-enable the ingress interrupt. */
+       enable_percpu_irq(priv->intr_id);
+       /* HACK: Avoid the "rotting packet" problem (see above). */
+       if (qup->__packet_receive_read !=
+           qsp->__packet_receive_queue.__packet_write) {
+               /* ISSUE: Sometimes this returns zero, presumably */
+               /* because an interrupt was handled for this tile. */
+               (void)napi_reschedule(&info->napi);
+       }
+ done:
+       if (priv->active)
+               tile_net_provide_needed_buffers(info);
+       return work;
+ }
+ /*
+  * Handle an ingress interrupt for the given device on the current cpu.
+  *
+  * ISSUE: Sometimes this gets called after "disable_percpu_irq()" has
+  * been called!  This is probably due to "pending hypervisor downcalls".
+  *
+  * ISSUE: Is there any race condition between the "napi_schedule()" here
+  * and the "napi_complete()" call above?
+  */
+ static irqreturn_t tile_net_handle_ingress_interrupt(int irq, void *dev_ptr)
+ {
+       struct net_device *dev = (struct net_device *)dev_ptr;
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int my_cpu = smp_processor_id();
+       struct tile_net_cpu *info = priv->cpu[my_cpu];
+       /* Disable the ingress interrupt. */
+       disable_percpu_irq(priv->intr_id);
+       /* Ignore unwanted interrupts. */
+       if (!priv->active)
+               return IRQ_HANDLED;
+       /* ISSUE: Sometimes "info->napi_enabled" is false here. */
+       napi_schedule(&info->napi);
+       return IRQ_HANDLED;
+ }
+ /*
+  * One time initialization per interface.
+  */
+ static int tile_net_open_aux(struct net_device *dev)
+ {
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int ret;
+       int dummy;
+       unsigned int epp_lotar;
+       /*
+        * Find out where EPP memory should be homed.
+        */
+       ret = hv_dev_pread(priv->hv_devhdl, 0,
+                          (HV_VirtAddr)&epp_lotar, sizeof(epp_lotar),
+                          NETIO_EPP_SHM_OFF);
+       if (ret < 0) {
+               pr_err("could not read epp_shm_queue lotar.\n");
+               return -EIO;
+       }
+       /*
+        * Home the page on the EPP.
+        */
+       {
+               int epp_home = hv_lotar_to_cpu(epp_lotar);
+               homecache_change_page_home(priv->eq_pages, EQ_ORDER, epp_home);
+       }
+       /*
+        * Register the EPP shared memory queue.
+        */
+       {
+               netio_ipp_address_t ea = {
+                       .va = 0,
+                       .pa = __pa(priv->eq),
+                       .pte = hv_pte(0),
+                       .size = EQ_SIZE,
+               };
+               ea.pte = hv_pte_set_lotar(ea.pte, epp_lotar);
+               ea.pte = hv_pte_set_mode(ea.pte, HV_PTE_MODE_CACHE_TILE_L3);
+               ret = hv_dev_pwrite(priv->hv_devhdl, 0,
+                                   (HV_VirtAddr)&ea,
+                                   sizeof(ea),
+                                   NETIO_EPP_SHM_OFF);
+               if (ret < 0)
+                       return -EIO;
+       }
+       /*
+        * Start LIPP/LEPP.
+        */
+       if (hv_dev_pwrite(priv->hv_devhdl, 0, (HV_VirtAddr)&dummy,
+                         sizeof(dummy), NETIO_IPP_START_SHIM_OFF) < 0) {
+               pr_warning("Failed to start LIPP/LEPP.\n");
+               return -EIO;
+       }
+       return 0;
+ }
+ /*
+  * Register with hypervisor on the current CPU.
+  *
+  * Strangely, this function does important things even if it "fails",
+  * which is especially common if the link is not up yet.  Hopefully
+  * these things are all "harmless" if done twice!
+  */
+ static void tile_net_register(void *dev_ptr)
+ {
+       struct net_device *dev = (struct net_device *)dev_ptr;
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int my_cpu = smp_processor_id();
+       struct tile_net_cpu *info;
+       struct tile_netio_queue *queue;
+       /* Only network cpus can receive packets. */
+       int queue_id =
+               cpumask_test_cpu(my_cpu, &priv->network_cpus_map) ? 0 : 255;
+       netio_input_config_t config = {
+               .flags = 0,
+               .num_receive_packets = priv->network_cpus_credits,
+               .queue_id = queue_id
+       };
+       int ret = 0;
+       netio_queue_impl_t *queuep;
+       PDEBUG("tile_net_register(queue_id %d)\n", queue_id);
+       if (!strcmp(dev->name, "xgbe0"))
+               info = &__get_cpu_var(hv_xgbe0);
+       else if (!strcmp(dev->name, "xgbe1"))
+               info = &__get_cpu_var(hv_xgbe1);
+       else if (!strcmp(dev->name, "gbe0"))
+               info = &__get_cpu_var(hv_gbe0);
+       else if (!strcmp(dev->name, "gbe1"))
+               info = &__get_cpu_var(hv_gbe1);
+       else
+               BUG();
+       /* Initialize the egress timer. */
+       init_timer(&info->egress_timer);
+       info->egress_timer.data = (long)info;
+       info->egress_timer.function = tile_net_handle_egress_timer;
+       priv->cpu[my_cpu] = info;
+       /*
+        * Register ourselves with LIPP.  This does a lot of stuff,
+        * including invoking the LIPP registration code.
+        */
+       ret = hv_dev_pwrite(priv->hv_devhdl, 0,
+                           (HV_VirtAddr)&config,
+                           sizeof(netio_input_config_t),
+                           NETIO_IPP_INPUT_REGISTER_OFF);
+       PDEBUG("hv_dev_pwrite(NETIO_IPP_INPUT_REGISTER_OFF) returned %d\n",
+              ret);
+       if (ret < 0) {
+               if (ret != NETIO_LINK_DOWN) {
+                       printk(KERN_DEBUG "hv_dev_pwrite "
+                              "NETIO_IPP_INPUT_REGISTER_OFF failure %d\n",
+                              ret);
+               }
+               info->link_down = (ret == NETIO_LINK_DOWN);
+               return;
+       }
+       /*
+        * Get the pointer to our queue's system part.
+        */
+       ret = hv_dev_pread(priv->hv_devhdl, 0,
+                          (HV_VirtAddr)&queuep,
+                          sizeof(netio_queue_impl_t *),
+                          NETIO_IPP_INPUT_REGISTER_OFF);
+       PDEBUG("hv_dev_pread(NETIO_IPP_INPUT_REGISTER_OFF) returned %d\n",
+              ret);
+       PDEBUG("queuep %p\n", queuep);
+       if (ret <= 0) {
+               /* ISSUE: Shouldn't this be a fatal error? */
+               pr_err("hv_dev_pread NETIO_IPP_INPUT_REGISTER_OFF failure\n");
+               return;
+       }
+       queue = &info->queue;
+       queue->__system_part = queuep;
+       memset(&queue->__user_part, 0, sizeof(netio_queue_user_impl_t));
+       /* This is traditionally "config.num_receive_packets / 2". */
+       queue->__user_part.__receive_credit_interval = 4;
+       queue->__user_part.__receive_credit_remaining =
+               queue->__user_part.__receive_credit_interval;
+       /*
+        * Get a fastio index from the hypervisor.
+        * ISSUE: Shouldn't this check the result?
+        */
+       ret = hv_dev_pread(priv->hv_devhdl, 0,
+                          (HV_VirtAddr)&queue->__user_part.__fastio_index,
+                          sizeof(queue->__user_part.__fastio_index),
+                          NETIO_IPP_GET_FASTIO_OFF);
+       PDEBUG("hv_dev_pread(NETIO_IPP_GET_FASTIO_OFF) returned %d\n", ret);
+       /* Now we are registered. */
+       info->registered = true;
+ }
+ /*
+  * Deregister with hypervisor on the current CPU.
+  *
+  * This simply discards all our credits, so no more packets will be
+  * delivered to this tile.  There may still be packets in our queue.
+  *
+  * Also, disable the ingress interrupt.
+  */
+ static void tile_net_deregister(void *dev_ptr)
+ {
+       struct net_device *dev = (struct net_device *)dev_ptr;
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int my_cpu = smp_processor_id();
+       struct tile_net_cpu *info = priv->cpu[my_cpu];
+       /* Disable the ingress interrupt. */
+       disable_percpu_irq(priv->intr_id);
+       /* Do nothing else if not registered. */
+       if (info == NULL || !info->registered)
+               return;
+       {
+               struct tile_netio_queue *queue = &info->queue;
+               netio_queue_user_impl_t *qup = &queue->__user_part;
+               /* Discard all our credits. */
+               __netio_fastio_return_credits(qup->__fastio_index, -1);
+       }
+ }
+ /*
+  * Unregister with hypervisor on the current CPU.
+  *
+  * Also, disable the ingress interrupt.
+  */
+ static void tile_net_unregister(void *dev_ptr)
+ {
+       struct net_device *dev = (struct net_device *)dev_ptr;
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int my_cpu = smp_processor_id();
+       struct tile_net_cpu *info = priv->cpu[my_cpu];
+       int ret;
+       int dummy = 0;
+       /* Disable the ingress interrupt. */
+       disable_percpu_irq(priv->intr_id);
+       /* Do nothing else if not registered. */
+       if (info == NULL || !info->registered)
+               return;
+       /* Unregister ourselves with LIPP/LEPP. */
+       ret = hv_dev_pwrite(priv->hv_devhdl, 0, (HV_VirtAddr)&dummy,
+                           sizeof(dummy), NETIO_IPP_INPUT_UNREGISTER_OFF);
+       if (ret < 0)
+               panic("Failed to unregister with LIPP/LEPP!\n");
+       /* Discard all packets still in our NetIO queue. */
+       tile_net_discard_packets(dev);
+       /* Reset state. */
+       info->num_needed_small_buffers = 0;
+       info->num_needed_large_buffers = 0;
+       /* Cancel egress timer. */
+       del_timer(&info->egress_timer);
+       info->egress_timer_scheduled = false;
+ }
+ /*
+  * Helper function for "tile_net_stop()".
+  *
+  * Also used to handle registration failure in "tile_net_open_inner()",
+  * when the various extra steps in "tile_net_stop()" are not necessary.
+  */
+ static void tile_net_stop_aux(struct net_device *dev)
+ {
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int i;
+       int dummy = 0;
+       /*
+        * Unregister all tiles, so LIPP will stop delivering packets.
+        * Also, delete all the "napi" objects (sequentially, to protect
+        * "dev->napi_list").
+        */
+       on_each_cpu(tile_net_unregister, (void *)dev, 1);
+       for_each_online_cpu(i) {
+               struct tile_net_cpu *info = priv->cpu[i];
+               if (info != NULL && info->registered) {
+                       netif_napi_del(&info->napi);
+                       info->registered = false;
+               }
+       }
+       /* Stop LIPP/LEPP. */
+       if (hv_dev_pwrite(priv->hv_devhdl, 0, (HV_VirtAddr)&dummy,
+                         sizeof(dummy), NETIO_IPP_STOP_SHIM_OFF) < 0)
+               panic("Failed to stop LIPP/LEPP!\n");
+       priv->partly_opened = 0;
+ }
+ /*
+  * Disable NAPI for the given device on the current cpu.
+  */
+ static void tile_net_stop_disable(void *dev_ptr)
+ {
+       struct net_device *dev = (struct net_device *)dev_ptr;
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int my_cpu = smp_processor_id();
+       struct tile_net_cpu *info = priv->cpu[my_cpu];
+       /* Disable NAPI if needed. */
+       if (info != NULL && info->napi_enabled) {
+               napi_disable(&info->napi);
+               info->napi_enabled = false;
+       }
+ }
+ /*
+  * Enable NAPI and the ingress interrupt for the given device
+  * on the current cpu.
+  *
+  * ISSUE: Only do this for "network cpus"?
+  */
+ static void tile_net_open_enable(void *dev_ptr)
+ {
+       struct net_device *dev = (struct net_device *)dev_ptr;
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int my_cpu = smp_processor_id();
+       struct tile_net_cpu *info = priv->cpu[my_cpu];
+       /* Enable NAPI. */
+       napi_enable(&info->napi);
+       info->napi_enabled = true;
+       /* Enable the ingress interrupt. */
+       enable_percpu_irq(priv->intr_id);
+ }
+ /*
+  * tile_net_open_inner does most of the work of bringing up the interface.
+  * It's called from tile_net_open(), and also from tile_net_retry_open().
+  * The return value is 0 if the interface was brought up, < 0 if
+  * tile_net_open() should return the return value as an error, and > 0 if
+  * tile_net_open() should return success and schedule a work item to
+  * periodically retry the bringup.
+  */
+ static int tile_net_open_inner(struct net_device *dev)
+ {
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int my_cpu = smp_processor_id();
+       struct tile_net_cpu *info;
+       struct tile_netio_queue *queue;
+       int result = 0;
+       int i;
+       int dummy = 0;
+       /*
+        * First try to register just on the local CPU, and handle any
+        * semi-expected "link down" failure specially.  Note that we
+        * do NOT call "tile_net_stop_aux()", unlike below.
+        */
+       tile_net_register(dev);
+       info = priv->cpu[my_cpu];
+       if (!info->registered) {
+               if (info->link_down)
+                       return 1;
+               return -EAGAIN;
+       }
+       /*
+        * Now register everywhere else.  If any registration fails,
+        * even for "link down" (which might not be possible), we
+        * clean up using "tile_net_stop_aux()".  Also, add all the
+        * "napi" objects (sequentially, to protect "dev->napi_list").
+        * ISSUE: Only use "netif_napi_add()" for "network cpus"?
+        */
+       smp_call_function(tile_net_register, (void *)dev, 1);
+       for_each_online_cpu(i) {
+               struct tile_net_cpu *info = priv->cpu[i];
+               if (info->registered)
+                       netif_napi_add(dev, &info->napi, tile_net_poll, 64);
+               else
+                       result = -EAGAIN;
+       }
+       if (result != 0) {
+               tile_net_stop_aux(dev);
+               return result;
+       }
+       queue = &info->queue;
+       if (priv->intr_id == 0) {
+               unsigned int irq;
+               /*
+                * Acquire the irq allocated by the hypervisor.  Every
+                * queue gets the same irq.  The "__intr_id" field is
+                * "1 << irq", so we use "__ffs()" to extract "irq".
+                */
+               priv->intr_id = queue->__system_part->__intr_id;
+               BUG_ON(priv->intr_id == 0);
+               irq = __ffs(priv->intr_id);
+               /*
+                * Register the ingress interrupt handler for this
+                * device, permanently.
+                *
+                * We used to call "free_irq()" in "tile_net_stop()",
+                * and then re-register the handler here every time,
+                * but that caused DNP errors in "handle_IRQ_event()"
+                * because "desc->action" was NULL.  See bug 9143.
+                */
+               tile_irq_activate(irq, TILE_IRQ_PERCPU);
+               BUG_ON(request_irq(irq, tile_net_handle_ingress_interrupt,
+                                  0, dev->name, (void *)dev) != 0);
+       }
+       {
+               /* Allocate initial buffers. */
+               int max_buffers =
+                       priv->network_cpus_count * priv->network_cpus_credits;
+               info->num_needed_small_buffers =
+                       min(LIPP_SMALL_BUFFERS, max_buffers);
+               info->num_needed_large_buffers =
+                       min(LIPP_LARGE_BUFFERS, max_buffers);
+               tile_net_provide_needed_buffers(info);
+               if (info->num_needed_small_buffers != 0 ||
+                   info->num_needed_large_buffers != 0)
+                       panic("Insufficient memory for buffer stack!");
+       }
+       /* We are about to be active. */
+       priv->active = true;
+       /* Make sure "active" is visible to all tiles. */
+       mb();
+       /* On each tile, enable NAPI and the ingress interrupt. */
+       on_each_cpu(tile_net_open_enable, (void *)dev, 1);
+       /* Start LIPP/LEPP and activate "ingress" at the shim. */
+       if (hv_dev_pwrite(priv->hv_devhdl, 0, (HV_VirtAddr)&dummy,
+                         sizeof(dummy), NETIO_IPP_INPUT_INIT_OFF) < 0)
+               panic("Failed to activate the LIPP Shim!\n");
+       /* Start our transmit queue. */
+       netif_start_queue(dev);
+       return 0;
+ }
+ /*
+  * Called periodically to retry bringing up the NetIO interface,
+  * if it doesn't come up cleanly during tile_net_open().
+  */
+ static void tile_net_open_retry(struct work_struct *w)
+ {
+       struct delayed_work *dw =
+               container_of(w, struct delayed_work, work);
+       struct tile_net_priv *priv =
+               container_of(dw, struct tile_net_priv, retry_work);
+       /*
+        * Try to bring the NetIO interface up.  If it fails, reschedule
+        * ourselves to try again later; otherwise, tell Linux we now have
+        * a working link.  ISSUE: What if the return value is negative?
+        */
+       if (tile_net_open_inner(priv->dev) != 0)
+               schedule_delayed_work(&priv->retry_work,
+                                     TILE_NET_RETRY_INTERVAL);
+       else
+               netif_carrier_on(priv->dev);
+ }
+ /*
+  * Called when a network interface is made active.
+  *
+  * Returns 0 on success, negative value on failure.
+  *
+  * The open entry point is called when a network interface is made
+  * active by the system (IFF_UP).  At this point all resources needed
+  * for transmit and receive operations are allocated, the interrupt
+  * handler is registered with the OS (if needed), the watchdog timer
+  * is started, and the stack is notified that the interface is ready.
+  *
+  * If the actual link is not available yet, then we tell Linux that
+  * we have no carrier, and we keep checking until the link comes up.
+  */
+ static int tile_net_open(struct net_device *dev)
+ {
+       int ret = 0;
+       struct tile_net_priv *priv = netdev_priv(dev);
+       /*
+        * We rely on priv->partly_opened to tell us if this is the
+        * first time this interface is being brought up. If it is
+        * set, the IPP was already initialized and should not be
+        * initialized again.
+        */
+       if (!priv->partly_opened) {
+               int count;
+               int credits;
+               /* Initialize LIPP/LEPP, and start the Shim. */
+               ret = tile_net_open_aux(dev);
+               if (ret < 0) {
+                       pr_err("tile_net_open_aux failed: %d\n", ret);
+                       return ret;
+               }
+               /* Analyze the network cpus. */
+               if (network_cpus_used)
+                       cpumask_copy(&priv->network_cpus_map,
+                                    &network_cpus_map);
+               else
+                       cpumask_copy(&priv->network_cpus_map, cpu_online_mask);
+               count = cpumask_weight(&priv->network_cpus_map);
+               /* Limit credits to available buffers, and apply min. */
+               credits = max(16, (LIPP_LARGE_BUFFERS / count) & ~1);
+               /* Apply "GBE" max limit. */
+               /* ISSUE: Use higher limit for XGBE? */
+               credits = min(NETIO_MAX_RECEIVE_PKTS, credits);
+               priv->network_cpus_count = count;
+               priv->network_cpus_credits = credits;
+ #ifdef TILE_NET_DEBUG
+               pr_info("Using %d network cpus, with %d credits each\n",
+                      priv->network_cpus_count, priv->network_cpus_credits);
+ #endif
+               priv->partly_opened = 1;
+       } else {
+               /* FIXME: Is this possible? */
+               /* printk("Already partly opened.\n"); */
+       }
+       /*
+        * Attempt to bring up the link.
+        */
+       ret = tile_net_open_inner(dev);
+       if (ret <= 0) {
+               if (ret == 0)
+                       netif_carrier_on(dev);
+               return ret;
+       }
+       /*
+        * We were unable to bring up the NetIO interface, but we want to
+        * try again in a little bit.  Tell Linux that we have no carrier
+        * so it doesn't try to use the interface before the link comes up
+        * and then remember to try again later.
+        */
+       netif_carrier_off(dev);
+       schedule_delayed_work(&priv->retry_work, TILE_NET_RETRY_INTERVAL);
+       return 0;
+ }
+ static int tile_net_drain_lipp_buffers(struct tile_net_priv *priv)
+ {
+       int n = 0;
+       /* Drain all the LIPP buffers. */
+       while (true) {
+               int buffer;
+               /* NOTE: This should never fail. */
+               if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&buffer,
+                                sizeof(buffer), NETIO_IPP_DRAIN_OFF) < 0)
+                       break;
+               /* Stop when done. */
+               if (buffer == 0)
+                       break;
+               {
+                       /* Convert "linux_buffer_t" to "va". */
+                       void *va = __va((phys_addr_t)(buffer >> 1) << 7);
+                       /* Acquire the associated "skb". */
+                       struct sk_buff **skb_ptr = va - sizeof(*skb_ptr);
+                       struct sk_buff *skb = *skb_ptr;
+                       kfree_skb(skb);
+               }
+               n++;
+       }
+       return n;
+ }
+ /*
+  * Disables a network interface.
+  *
+  * Returns 0, this is not allowed to fail.
+  *
+  * The close entry point is called when an interface is de-activated
+  * by the OS.  The hardware is still under the drivers control, but
+  * needs to be disabled.  A global MAC reset is issued to stop the
+  * hardware, and all transmit and receive resources are freed.
+  *
+  * ISSUE: How closely does "netif_running(dev)" mirror "priv->active"?
+  *
+  * Before we are called by "__dev_close()", "netif_running()" will
+  * have been cleared, so no NEW calls to "tile_net_poll()" will be
+  * made by "netpoll_poll_dev()".
+  *
+  * Often, this can cause some tiles to still have packets in their
+  * queues, so we must call "tile_net_discard_packets()" later.
+  *
+  * Note that some other tile may still be INSIDE "tile_net_poll()",
+  * and in fact, many will be, if there is heavy network load.
+  *
+  * Calling "on_each_cpu(tile_net_stop_disable, (void *)dev, 1)" when
+  * any tile is still "napi_schedule()"'d will induce a horrible crash
+  * when "msleep()" is called.  This includes tiles which are inside
+  * "tile_net_poll()" which have not yet called "napi_complete()".
+  *
+  * So, we must first try to wait long enough for other tiles to finish
+  * with any current "tile_net_poll()" call, and, hopefully, to clear
+  * the "scheduled" flag.  ISSUE: It is unclear what happens to tiles
+  * which have called "napi_schedule()" but which had not yet tried to
+  * call "tile_net_poll()", or which exhausted their budget inside
+  * "tile_net_poll()" just before this function was called.
+  */
+ static int tile_net_stop(struct net_device *dev)
+ {
+       struct tile_net_priv *priv = netdev_priv(dev);
+       PDEBUG("tile_net_stop()\n");
+       /* Start discarding packets. */
+       priv->active = false;
+       /* Make sure "active" is visible to all tiles. */
+       mb();
+       /*
+        * On each tile, make sure no NEW packets get delivered, and
+        * disable the ingress interrupt.
+        *
+        * Note that the ingress interrupt can fire AFTER this,
+        * presumably due to packets which were recently delivered,
+        * but it will have no effect.
+        */
+       on_each_cpu(tile_net_deregister, (void *)dev, 1);
+       /* Optimistically drain LIPP buffers. */
+       (void)tile_net_drain_lipp_buffers(priv);
+       /* ISSUE: Only needed if not yet fully open. */
+       cancel_delayed_work_sync(&priv->retry_work);
+       /* Can't transmit any more. */
+       netif_stop_queue(dev);
+       /* Disable NAPI on each tile. */
+       on_each_cpu(tile_net_stop_disable, (void *)dev, 1);
+       /*
+        * Drain any remaining LIPP buffers.  NOTE: This "printk()"
+        * has never been observed, but in theory it could happen.
+        */
+       if (tile_net_drain_lipp_buffers(priv) != 0)
+               printk("Had to drain some extra LIPP buffers!\n");
+       /* Stop LIPP/LEPP. */
+       tile_net_stop_aux(dev);
+       /*
+        * ISSUE: It appears that, in practice anyway, by the time we
+        * get here, there are no pending completions, but just in case,
+        * we free (all of) them anyway.
+        */
+       while (tile_net_lepp_free_comps(dev, true))
+               /* loop */;
+       /* Wipe the EPP queue, and wait till the stores hit the EPP. */
+       memset(priv->eq, 0, sizeof(lepp_queue_t));
+       mb();
+       return 0;
+ }
+ /*
+  * Prepare the "frags" info for the resulting LEPP command.
+  *
+  * If needed, flush the memory used by the frags.
+  */
+ static unsigned int tile_net_tx_frags(lepp_frag_t *frags,
+                                     struct sk_buff *skb,
+                                     void *b_data, unsigned int b_len)
+ {
+       unsigned int i, n = 0;
+       struct skb_shared_info *sh = skb_shinfo(skb);
+       phys_addr_t cpa;
+       if (b_len != 0) {
+               if (!hash_default)
+                       finv_buffer_remote(b_data, b_len, 0);
+               cpa = __pa(b_data);
+               frags[n].cpa_lo = cpa;
+               frags[n].cpa_hi = cpa >> 32;
+               frags[n].length = b_len;
+               frags[n].hash_for_home = hash_default;
+               n++;
+       }
+       for (i = 0; i < sh->nr_frags; i++) {
+               skb_frag_t *f = &sh->frags[i];
+               unsigned long pfn = page_to_pfn(f->page);
+               /* FIXME: Compute "hash_for_home" properly. */
+               /* ISSUE: The hypervisor checks CHIP_HAS_REV1_DMA_PACKETS(). */
+               int hash_for_home = hash_default;
+               /* FIXME: Hmmm. */
+               if (!hash_default) {
+                       void *va = pfn_to_kaddr(pfn) + f->page_offset;
+                       BUG_ON(PageHighMem(f->page));
+                       finv_buffer_remote(va, f->size, 0);
+               }
+               cpa = ((phys_addr_t)pfn << PAGE_SHIFT) + f->page_offset;
+               frags[n].cpa_lo = cpa;
+               frags[n].cpa_hi = cpa >> 32;
+               frags[n].length = skb_frag_size(f);
+               frags[n].hash_for_home = hash_for_home;
+               n++;
+       }
+       return n;
+ }
+ /*
+  * This function takes "skb", consisting of a header template and a
+  * payload, and hands it to LEPP, to emit as one or more segments,
+  * each consisting of a possibly modified header, plus a piece of the
+  * payload, via a process known as "tcp segmentation offload".
+  *
+  * Usually, "data" will contain the header template, of size "sh_len",
+  * and "sh->frags" will contain "skb->data_len" bytes of payload, and
+  * there will be "sh->gso_segs" segments.
+  *
+  * Sometimes, if "sendfile()" requires copying, we will be called with
+  * "data" containing the header and payload, with "frags" being empty.
+  *
+  * In theory, "sh->nr_frags" could be 3, but in practice, it seems
+  * that this will never actually happen.
+  *
+  * See "emulate_large_send_offload()" for some reference code, which
+  * does not handle checksumming.
+  *
+  * ISSUE: How do we make sure that high memory DMA does not migrate?
+  */
+ static int tile_net_tx_tso(struct sk_buff *skb, struct net_device *dev)
+ {
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int my_cpu = smp_processor_id();
+       struct tile_net_cpu *info = priv->cpu[my_cpu];
+       struct tile_net_stats_t *stats = &info->stats;
+       struct skb_shared_info *sh = skb_shinfo(skb);
+       unsigned char *data = skb->data;
+       /* The ip header follows the ethernet header. */
+       struct iphdr *ih = ip_hdr(skb);
+       unsigned int ih_len = ih->ihl * 4;
+       /* Note that "nh == ih", by definition. */
+       unsigned char *nh = skb_network_header(skb);
+       unsigned int eh_len = nh - data;
+       /* The tcp header follows the ip header. */
+       struct tcphdr *th = (struct tcphdr *)(nh + ih_len);
+       unsigned int th_len = th->doff * 4;
+       /* The total number of header bytes. */
+       /* NOTE: This may be less than skb_headlen(skb). */
+       unsigned int sh_len = eh_len + ih_len + th_len;
+       /* The number of payload bytes at "skb->data + sh_len". */
+       /* This is non-zero for sendfile() without HIGHDMA. */
+       unsigned int b_len = skb_headlen(skb) - sh_len;
+       /* The total number of payload bytes. */
+       unsigned int d_len = b_len + skb->data_len;
+       /* The maximum payload size. */
+       unsigned int p_len = sh->gso_size;
+       /* The total number of segments. */
+       unsigned int num_segs = sh->gso_segs;
+       /* The temporary copy of the command. */
+       u32 cmd_body[(LEPP_MAX_CMD_SIZE + 3) / 4];
+       lepp_tso_cmd_t *cmd = (lepp_tso_cmd_t *)cmd_body;
+       /* Analyze the "frags". */
+       unsigned int num_frags =
+               tile_net_tx_frags(cmd->frags, skb, data + sh_len, b_len);
+       /* The size of the command, including frags and header. */
+       size_t cmd_size = LEPP_TSO_CMD_SIZE(num_frags, sh_len);
+       /* The command header. */
+       lepp_tso_cmd_t cmd_init = {
+               .tso = true,
+               .header_size = sh_len,
+               .ip_offset = eh_len,
+               .tcp_offset = eh_len + ih_len,
+               .payload_size = p_len,
+               .num_frags = num_frags,
+       };
+       unsigned long irqflags;
+       lepp_queue_t *eq = priv->eq;
+       struct sk_buff *olds[8];
+       unsigned int wanted = 8;
+       unsigned int i, nolds = 0;
+       unsigned int cmd_head, cmd_tail, cmd_next;
+       unsigned int comp_tail;
+       /* Paranoia. */
+       BUG_ON(skb->protocol != htons(ETH_P_IP));
+       BUG_ON(ih->protocol != IPPROTO_TCP);
+       BUG_ON(skb->ip_summed != CHECKSUM_PARTIAL);
+       BUG_ON(num_frags > LEPP_MAX_FRAGS);
+       /*--BUG_ON(num_segs != (d_len + (p_len - 1)) / p_len); */
+       BUG_ON(num_segs <= 1);
+       /* Finish preparing the command. */
+       /* Copy the command header. */
+       *cmd = cmd_init;
+       /* Copy the "header". */
+       memcpy(&cmd->frags[num_frags], data, sh_len);
+       /* Prefetch and wait, to minimize time spent holding the spinlock. */
+       prefetch_L1(&eq->comp_tail);
+       prefetch_L1(&eq->cmd_tail);
+       mb();
+       /* Enqueue the command. */
+       spin_lock_irqsave(&priv->eq_lock, irqflags);
+       /*
+        * Handle completions if needed to make room.
+        * HACK: Spin until there is sufficient room.
+        */
+       if (lepp_num_free_comp_slots(eq) == 0) {
+               nolds = tile_net_lepp_grab_comps(eq, olds, wanted, 0);
+               if (nolds == 0) {
+ busy:
+                       spin_unlock_irqrestore(&priv->eq_lock, irqflags);
+                       return NETDEV_TX_BUSY;
+               }
+       }
+       cmd_head = eq->cmd_head;
+       cmd_tail = eq->cmd_tail;
+       /* Prepare to advance, detecting full queue. */
+       cmd_next = cmd_tail + cmd_size;
+       if (cmd_tail < cmd_head && cmd_next >= cmd_head)
+               goto busy;
+       if (cmd_next > LEPP_CMD_LIMIT) {
+               cmd_next = 0;
+               if (cmd_next == cmd_head)
+                       goto busy;
+       }
+       /* Copy the command. */
+       memcpy(&eq->cmds[cmd_tail], cmd, cmd_size);
+       /* Advance. */
+       cmd_tail = cmd_next;
+       /* Record "skb" for eventual freeing. */
+       comp_tail = eq->comp_tail;
+       eq->comps[comp_tail] = skb;
+       LEPP_QINC(comp_tail);
+       eq->comp_tail = comp_tail;
+       /* Flush before allowing LEPP to handle the command. */
+       /* ISSUE: Is this the optimal location for the flush? */
+       __insn_mf();
+       eq->cmd_tail = cmd_tail;
+       /* NOTE: Using "4" here is more efficient than "0" or "2", */
+       /* and, strangely, more efficient than pre-checking the number */
+       /* of available completions, and comparing it to 4. */
+       if (nolds == 0)
+               nolds = tile_net_lepp_grab_comps(eq, olds, wanted, 4);
+       spin_unlock_irqrestore(&priv->eq_lock, irqflags);
+       /* Handle completions. */
+       for (i = 0; i < nolds; i++)
+               kfree_skb(olds[i]);
+       /* Update stats. */
+       stats->tx_packets += num_segs;
+       stats->tx_bytes += (num_segs * sh_len) + d_len;
+       /* Make sure the egress timer is scheduled. */
+       tile_net_schedule_egress_timer(info);
+       return NETDEV_TX_OK;
+ }
+ /*
+  * Transmit a packet (called by the kernel via "hard_start_xmit" hook).
+  */
+ static int tile_net_tx(struct sk_buff *skb, struct net_device *dev)
+ {
+       struct tile_net_priv *priv = netdev_priv(dev);
+       int my_cpu = smp_processor_id();
+       struct tile_net_cpu *info = priv->cpu[my_cpu];
+       struct tile_net_stats_t *stats = &info->stats;
+       unsigned long irqflags;
+       struct skb_shared_info *sh = skb_shinfo(skb);
+       unsigned int len = skb->len;
+       unsigned char *data = skb->data;
+       unsigned int csum_start = skb_checksum_start_offset(skb);
+       lepp_frag_t frags[LEPP_MAX_FRAGS];
+       unsigned int num_frags;
+       lepp_queue_t *eq = priv->eq;
+       struct sk_buff *olds[8];
+       unsigned int wanted = 8;
+       unsigned int i, nolds = 0;
+       unsigned int cmd_size = sizeof(lepp_cmd_t);
+       unsigned int cmd_head, cmd_tail, cmd_next;
+       unsigned int comp_tail;
+       lepp_cmd_t cmds[LEPP_MAX_FRAGS];
+       /*
+        * This is paranoia, since we think that if the link doesn't come
+        * up, telling Linux we have no carrier will keep it from trying
+        * to transmit.  If it does, though, we can't execute this routine,
+        * since data structures we depend on aren't set up yet.
+        */
+       if (!info->registered)
+               return NETDEV_TX_BUSY;
+       /* Save the timestamp. */
+       dev->trans_start = jiffies;
+ #ifdef TILE_NET_PARANOIA
+ #if CHIP_HAS_CBOX_HOME_MAP()
+       if (hash_default) {
+               HV_PTE pte = *virt_to_pte(current->mm, (unsigned long)data);
+               if (hv_pte_get_mode(pte) != HV_PTE_MODE_CACHE_HASH_L3)
+                       panic("Non-HFH egress buffer! VA=%p Mode=%d PTE=%llx",
+                             data, hv_pte_get_mode(pte), hv_pte_val(pte));
+       }
+ #endif
+ #endif
+ #ifdef TILE_NET_DUMP_PACKETS
+       /* ISSUE: Does not dump the "frags". */
+       dump_packet(data, skb_headlen(skb), "tx");
+ #endif /* TILE_NET_DUMP_PACKETS */
+       if (sh->gso_size != 0)
+               return tile_net_tx_tso(skb, dev);
+       /* Prepare the commands. */
+       num_frags = tile_net_tx_frags(frags, skb, data, skb_headlen(skb));
+       for (i = 0; i < num_frags; i++) {
+               bool final = (i == num_frags - 1);
+               lepp_cmd_t cmd = {
+                       .cpa_lo = frags[i].cpa_lo,
+                       .cpa_hi = frags[i].cpa_hi,
+                       .length = frags[i].length,
+                       .hash_for_home = frags[i].hash_for_home,
+                       .send_completion = final,
+                       .end_of_packet = final
+               };
+               if (i == 0 && skb->ip_summed == CHECKSUM_PARTIAL) {
+                       cmd.compute_checksum = 1;
+                       cmd.checksum_data.bits.start_byte = csum_start;
+                       cmd.checksum_data.bits.count = len - csum_start;
+                       cmd.checksum_data.bits.destination_byte =
+                               csum_start + skb->csum_offset;
+               }
+               cmds[i] = cmd;
+       }
+       /* Prefetch and wait, to minimize time spent holding the spinlock. */
+       prefetch_L1(&eq->comp_tail);
+       prefetch_L1(&eq->cmd_tail);
+       mb();
+       /* Enqueue the commands. */
+       spin_lock_irqsave(&priv->eq_lock, irqflags);
+       /*
+        * Handle completions if needed to make room.
+        * HACK: Spin until there is sufficient room.
+        */
+       if (lepp_num_free_comp_slots(eq) == 0) {
+               nolds = tile_net_lepp_grab_comps(eq, olds, wanted, 0);
+               if (nolds == 0) {
+ busy:
+                       spin_unlock_irqrestore(&priv->eq_lock, irqflags);
+                       return NETDEV_TX_BUSY;
+               }
+       }
+       cmd_head = eq->cmd_head;
+       cmd_tail = eq->cmd_tail;
+       /* Copy the commands, or fail. */
+       for (i = 0; i < num_frags; i++) {
+               /* Prepare to advance, detecting full queue. */
+               cmd_next = cmd_tail + cmd_size;
+               if (cmd_tail < cmd_head && cmd_next >= cmd_head)
+                       goto busy;
+               if (cmd_next > LEPP_CMD_LIMIT) {
+                       cmd_next = 0;
+                       if (cmd_next == cmd_head)
+                               goto busy;
+               }
+               /* Copy the command. */
+               *(lepp_cmd_t *)&eq->cmds[cmd_tail] = cmds[i];
+               /* Advance. */
+               cmd_tail = cmd_next;
+       }
+       /* Record "skb" for eventual freeing. */
+       comp_tail = eq->comp_tail;
+       eq->comps[comp_tail] = skb;
+       LEPP_QINC(comp_tail);
+       eq->comp_tail = comp_tail;
+       /* Flush before allowing LEPP to handle the command. */
+       /* ISSUE: Is this the optimal location for the flush? */
+       __insn_mf();
+       eq->cmd_tail = cmd_tail;
+       /* NOTE: Using "4" here is more efficient than "0" or "2", */
+       /* and, strangely, more efficient than pre-checking the number */
+       /* of available completions, and comparing it to 4. */
+       if (nolds == 0)
+               nolds = tile_net_lepp_grab_comps(eq, olds, wanted, 4);
+       spin_unlock_irqrestore(&priv->eq_lock, irqflags);
+       /* Handle completions. */
+       for (i = 0; i < nolds; i++)
+               kfree_skb(olds[i]);
+       /* HACK: Track "expanded" size for short packets (e.g. 42 < 60). */
+       stats->tx_packets++;
+       stats->tx_bytes += ((len >= ETH_ZLEN) ? len : ETH_ZLEN);
+       /* Make sure the egress timer is scheduled. */
+       tile_net_schedule_egress_timer(info);
+       return NETDEV_TX_OK;
+ }
+ /*
+  * Deal with a transmit timeout.
+  */
+ static void tile_net_tx_timeout(struct net_device *dev)
+ {
+       PDEBUG("tile_net_tx_timeout()\n");
+       PDEBUG("Transmit timeout at %ld, latency %ld\n", jiffies,
+              jiffies - dev->trans_start);
+       /* XXX: ISSUE: This doesn't seem useful for us. */
+       netif_wake_queue(dev);
+ }
+ /*
+  * Ioctl commands.
+  */
+ static int tile_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+ {
+       return -EOPNOTSUPP;
+ }
+ /*
+  * Get System Network Statistics.
+  *
+  * Returns the address of the device statistics structure.
+  */
+ static struct net_device_stats *tile_net_get_stats(struct net_device *dev)
+ {
+       struct tile_net_priv *priv = netdev_priv(dev);
+       u32 rx_packets = 0;
+       u32 tx_packets = 0;
+       u32 rx_bytes = 0;
+       u32 tx_bytes = 0;
+       int i;
+       for_each_online_cpu(i) {
+               if (priv->cpu[i]) {
+                       rx_packets += priv->cpu[i]->stats.rx_packets;
+                       rx_bytes += priv->cpu[i]->stats.rx_bytes;
+                       tx_packets += priv->cpu[i]->stats.tx_packets;
+                       tx_bytes += priv->cpu[i]->stats.tx_bytes;
+               }
+       }
+       priv->stats.rx_packets = rx_packets;
+       priv->stats.rx_bytes = rx_bytes;
+       priv->stats.tx_packets = tx_packets;
+       priv->stats.tx_bytes = tx_bytes;
+       return &priv->stats;
+ }
+ /*
+  * Change the "mtu".
+  *
+  * The "change_mtu" method is usually not needed.
+  * If you need it, it must be like this.
+  */
+ static int tile_net_change_mtu(struct net_device *dev, int new_mtu)
+ {
+       PDEBUG("tile_net_change_mtu()\n");
+       /* Check ranges. */
+       if ((new_mtu < 68) || (new_mtu > 1500))
+               return -EINVAL;
+       /* Accept the value. */
+       dev->mtu = new_mtu;
+       return 0;
+ }
+ /*
+  * Change the Ethernet Address of the NIC.
+  *
+  * The hypervisor driver does not support changing MAC address.  However,
+  * the IPP does not do anything with the MAC address, so the address which
+  * gets used on outgoing packets, and which is accepted on incoming packets,
+  * is completely up to the NetIO program or kernel driver which is actually
+  * handling them.
+  *
+  * Returns 0 on success, negative on failure.
+  */
+ static int tile_net_set_mac_address(struct net_device *dev, void *p)
+ {
+       struct sockaddr *addr = p;
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EINVAL;
+       /* ISSUE: Note that "dev_addr" is now a pointer. */
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+       return 0;
+ }
+ /*
+  * Obtain the MAC address from the hypervisor.
+  * This must be done before opening the device.
+  */
+ static int tile_net_get_mac(struct net_device *dev)
+ {
+       struct tile_net_priv *priv = netdev_priv(dev);
+       char hv_dev_name[32];
+       int len;
+       __netio_getset_offset_t offset = { .word = NETIO_IPP_PARAM_OFF };
+       int ret;
+       /* For example, "xgbe0". */
+       strcpy(hv_dev_name, dev->name);
+       len = strlen(hv_dev_name);
+       /* For example, "xgbe/0". */
+       hv_dev_name[len] = hv_dev_name[len - 1];
+       hv_dev_name[len - 1] = '/';
+       len++;
+       /* For example, "xgbe/0/native_hash". */
+       strcpy(hv_dev_name + len, hash_default ? "/native_hash" : "/native");
+       /* Get the hypervisor handle for this device. */
+       priv->hv_devhdl = hv_dev_open((HV_VirtAddr)hv_dev_name, 0);
+       PDEBUG("hv_dev_open(%s) returned %d %p\n",
+              hv_dev_name, priv->hv_devhdl, &priv->hv_devhdl);
+       if (priv->hv_devhdl < 0) {
+               if (priv->hv_devhdl == HV_ENODEV)
+                       printk(KERN_DEBUG "Ignoring unconfigured device %s\n",
+                                hv_dev_name);
+               else
+                       printk(KERN_DEBUG "hv_dev_open(%s) returned %d\n",
+                                hv_dev_name, priv->hv_devhdl);
+               return -1;
+       }
+       /*
+        * Read the hardware address from the hypervisor.
+        * ISSUE: Note that "dev_addr" is now a pointer.
+        */
+       offset.bits.class = NETIO_PARAM;
+       offset.bits.addr = NETIO_PARAM_MAC;
+       ret = hv_dev_pread(priv->hv_devhdl, 0,
+                          (HV_VirtAddr)dev->dev_addr, dev->addr_len,
+                          offset.word);
+       PDEBUG("hv_dev_pread(NETIO_PARAM_MAC) returned %d\n", ret);
+       if (ret <= 0) {
+               printk(KERN_DEBUG "hv_dev_pread(NETIO_PARAM_MAC) %s failed\n",
+                      dev->name);
+               /*
+                * Since the device is configured by the hypervisor but we
+                * can't get its MAC address, we are most likely running
+                * the simulator, so let's generate a random MAC address.
+                */
+               random_ether_addr(dev->dev_addr);
+       }
+       return 0;
+ }
+ static struct net_device_ops tile_net_ops = {
+       .ndo_open = tile_net_open,
+       .ndo_stop = tile_net_stop,
+       .ndo_start_xmit = tile_net_tx,
+       .ndo_do_ioctl = tile_net_ioctl,
+       .ndo_get_stats = tile_net_get_stats,
+       .ndo_change_mtu = tile_net_change_mtu,
+       .ndo_tx_timeout = tile_net_tx_timeout,
+       .ndo_set_mac_address = tile_net_set_mac_address
+ };
+ /*
+  * The setup function.
+  *
+  * This uses ether_setup() to assign various fields in dev, including
+  * setting IFF_BROADCAST and IFF_MULTICAST, then sets some extra fields.
+  */
+ static void tile_net_setup(struct net_device *dev)
+ {
+       PDEBUG("tile_net_setup()\n");
+       ether_setup(dev);
+       dev->netdev_ops = &tile_net_ops;
+       dev->watchdog_timeo = TILE_NET_TIMEOUT;
+       /* We want lockless xmit. */
+       dev->features |= NETIF_F_LLTX;
+       /* We support hardware tx checksums. */
+       dev->features |= NETIF_F_HW_CSUM;
+       /* We support scatter/gather. */
+       dev->features |= NETIF_F_SG;
+       /* We support TSO. */
+       dev->features |= NETIF_F_TSO;
+ #ifdef TILE_NET_GSO
+       /* We support GSO. */
+       dev->features |= NETIF_F_GSO;
+ #endif
+       if (hash_default)
+               dev->features |= NETIF_F_HIGHDMA;
+       /* ISSUE: We should support NETIF_F_UFO. */
+       dev->tx_queue_len = TILE_NET_TX_QUEUE_LEN;
+       dev->mtu = TILE_NET_MTU;
+ }
+ /*
+  * Allocate the device structure, register the device, and obtain the
+  * MAC address from the hypervisor.
+  */
+ static struct net_device *tile_net_dev_init(const char *name)
+ {
+       int ret;
+       struct net_device *dev;
+       struct tile_net_priv *priv;
+       /*
+        * Allocate the device structure.  This allocates "priv", calls
+        * tile_net_setup(), and saves "name".  Normally, "name" is a
+        * template, instantiated by register_netdev(), but not for us.
+        */
+       dev = alloc_netdev(sizeof(*priv), name, tile_net_setup);
+       if (!dev) {
+               pr_err("alloc_netdev(%s) failed\n", name);
+               return NULL;
+       }
+       priv = netdev_priv(dev);
+       /* Initialize "priv". */
+       memset(priv, 0, sizeof(*priv));
+       /* Save "dev" for "tile_net_open_retry()". */
+       priv->dev = dev;
+       INIT_DELAYED_WORK(&priv->retry_work, tile_net_open_retry);
+       spin_lock_init(&priv->eq_lock);
+       /* Allocate "eq". */
+       priv->eq_pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, EQ_ORDER);
+       if (!priv->eq_pages) {
+               free_netdev(dev);
+               return NULL;
+       }
+       priv->eq = page_address(priv->eq_pages);
+       /* Register the network device. */
+       ret = register_netdev(dev);
+       if (ret) {
+               pr_err("register_netdev %s failed %d\n", dev->name, ret);
+               __free_pages(priv->eq_pages, EQ_ORDER);
+               free_netdev(dev);
+               return NULL;
+       }
+       /* Get the MAC address. */
+       ret = tile_net_get_mac(dev);
+       if (ret < 0) {
+               unregister_netdev(dev);
+               __free_pages(priv->eq_pages, EQ_ORDER);
+               free_netdev(dev);
+               return NULL;
+       }
+       return dev;
+ }
+ /*
+  * Module cleanup.
+  *
+  * FIXME: If compiled as a module, this module cannot be "unloaded",
+  * because the "ingress interrupt handler" is registered permanently.
+  */
+ static void tile_net_cleanup(void)
+ {
+       int i;
+       for (i = 0; i < TILE_NET_DEVS; i++) {
+               if (tile_net_devs[i]) {
+                       struct net_device *dev = tile_net_devs[i];
+                       struct tile_net_priv *priv = netdev_priv(dev);
+                       unregister_netdev(dev);
+                       finv_buffer_remote(priv->eq, EQ_SIZE, 0);
+                       __free_pages(priv->eq_pages, EQ_ORDER);
+                       free_netdev(dev);
+               }
+       }
+ }
+ /*
+  * Module initialization.
+  */
+ static int tile_net_init_module(void)
+ {
+       pr_info("Tilera IPP Net Driver\n");
+       tile_net_devs[0] = tile_net_dev_init("xgbe0");
+       tile_net_devs[1] = tile_net_dev_init("xgbe1");
+       tile_net_devs[2] = tile_net_dev_init("gbe0");
+       tile_net_devs[3] = tile_net_dev_init("gbe1");
+       return 0;
+ }
+ module_init(tile_net_init_module);
+ module_exit(tile_net_cleanup);
+ #ifndef MODULE
+ /*
+  * The "network_cpus" boot argument specifies the cpus that are dedicated
+  * to handle ingress packets.
+  *
+  * The parameter should be in the form "network_cpus=m-n[,x-y]", where
+  * m, n, x, y are integer numbers that represent the cpus that can be
+  * neither a dedicated cpu nor a dataplane cpu.
+  */
+ static int __init network_cpus_setup(char *str)
+ {
+       int rc = cpulist_parse_crop(str, &network_cpus_map);
+       if (rc != 0) {
+               pr_warning("network_cpus=%s: malformed cpu list\n",
+                      str);
+       } else {
+               /* Remove dedicated cpus. */
+               cpumask_and(&network_cpus_map, &network_cpus_map,
+                           cpu_possible_mask);
+               if (cpumask_empty(&network_cpus_map)) {
+                       pr_warning("Ignoring network_cpus='%s'.\n",
+                              str);
+               } else {
+                       char buf[1024];
+                       cpulist_scnprintf(buf, sizeof(buf), &network_cpus_map);
+                       pr_info("Linux network CPUs: %s\n", buf);
+                       network_cpus_used = true;
+               }
+       }
+       return 0;
+ }
+ __setup("network_cpus=", network_cpus_setup);
+ #endif
Simple merge
Simple merge
Simple merge
diff --cc fs/sysfs/dir.c
@@@ -558,36 -547,22 +565,43 @@@ struct sysfs_dirent *sysfs_find_dirent(
                                       const void *ns,
                                       const unsigned char *name)
  {
 -      struct sysfs_dirent *sd;
 +      struct rb_node *p = parent_sd->s_dir.name_tree.rb_node;
 +      struct sysfs_dirent *found = NULL;
  
 -      for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) {
 -              if (sd->s_ns != ns)
 -                      continue;
 -              if (!strcmp(sd->s_name, name))
 -                      return sd;
+       if (!!sysfs_ns_type(parent_sd) != !!ns) {
+               WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
+                       sysfs_ns_type(parent_sd)? "required": "invalid",
+                       parent_sd->s_name, name);
+               return NULL;
+       }
 +      while (p) {
 +              int c;
 +#define node  rb_entry(p, struct sysfs_dirent, name_node)
 +              c = strcmp(name, node->s_name);
 +              if (c < 0) {
 +                      p = node->name_node.rb_left;
 +              } else if (c > 0) {
 +                      p = node->name_node.rb_right;
 +              } else {
 +                      found = node;
 +                      p = node->name_node.rb_left;
 +              }
 +#undef node
        }
 -      return NULL;
 +
 +      if (found && ns) {
 +              while (found->s_ns && found->s_ns != ns) {
 +                      p = rb_next(&found->name_node);
 +                      if (!p)
 +                              return NULL;
 +                      found = rb_entry(p, struct sysfs_dirent, name_node);
 +                      if (strcmp(name, found->s_name))
 +                              return NULL;
 +              }
 +      }
 +
 +      return found;
  }
  
  /**
Simple merge
Simple merge
Simple merge
diff --cc net/core/dev.c
Simple merge