summaryrefslogtreecommitdiff
path: root/drivers/net/typhoon.c
diff options
context:
space:
mode:
authorJeff Kirsher <jeffrey.t.kirsher@intel.com>2011-03-30 03:47:06 -0700
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>2011-08-10 19:53:41 -0700
commitca7a8e85262e93065b2a49dfb96a24d4a534a049 (patch)
tree6fa505e3e6597548edd53b8faa559c6dff5a2bb8 /drivers/net/typhoon.c
parentc1abc95b157fe4574919942018af143203ecca8e (diff)
downloadlinux-ca7a8e85262e93065b2a49dfb96a24d4a534a049.tar.gz
linux-ca7a8e85262e93065b2a49dfb96a24d4a534a049.tar.bz2
linux-ca7a8e85262e93065b2a49dfb96a24d4a534a049.zip
3c*/acenic/typhoon: Move 3Com Ethernet drivers
Moves the 3Com drivers into drivers/net/ethernet/3com/ and the necessary Kconfig and Makefile changes. Did not move the following drivers becuase they use a non-3Com chipset: 3c503, 3c505, 3c507, 3c523 and 3c527 CC: Steffen Klassert <klassert@mathematik.tu-chemnitz.de> CC: David Dillow <dave@thedillows.org> CC: Jes Sorensen <jes@trained-monkey.org> CC: Alan Cox <alan@linux.intel.com> CC: David Hinds <dahinds@users.sourceforge.net> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com> Acked-by: David Dillow <dave@thedillows.org>
Diffstat (limited to 'drivers/net/typhoon.c')
-rw-r--r--drivers/net/typhoon.c2574
1 files changed, 0 insertions, 2574 deletions
diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c
deleted file mode 100644
index 1d5091a1e49a..000000000000
--- a/drivers/net/typhoon.c
+++ /dev/null
@@ -1,2574 +0,0 @@
-/* typhoon.c: A Linux Ethernet device driver for 3Com 3CR990 family of NICs */
-/*
- Written 2002-2004 by David Dillow <dave@thedillows.org>
- Based on code written 1998-2000 by Donald Becker <becker@scyld.com> and
- Linux 2.2.x driver by David P. McLean <davidpmclean@yahoo.com>.
-
- This software may be used and distributed according to the terms of
- the GNU General Public License (GPL), incorporated herein by reference.
- Drivers based on or derived from this code fall under the GPL and must
- retain the authorship, copyright and license notice. This file is not
- a complete program and may only be used when the entire operating
- system is licensed under the GPL.
-
- This software is available on a public web site. It may enable
- cryptographic capabilities of the 3Com hardware, and may be
- exported from the United States under License Exception "TSU"
- pursuant to 15 C.F.R. Section 740.13(e).
-
- This work was funded by the National Library of Medicine under
- the Department of Energy project number 0274DD06D1 and NLM project
- number Y1-LM-2015-01.
-
- This driver is designed for the 3Com 3CR990 Family of cards with the
- 3XP Processor. It has been tested on x86 and sparc64.
-
- KNOWN ISSUES:
- *) Cannot DMA Rx packets to a 2 byte aligned address. Also firmware
- issue. Hopefully 3Com will fix it.
- *) Waiting for a command response takes 8ms due to non-preemptable
- polling. Only significant for getting stats and creating
- SAs, but an ugly wart never the less.
-
- TODO:
- *) Doesn't do IPSEC offloading. Yet. Keep yer pants on, it's coming.
- *) Add more support for ethtool (especially for NIC stats)
- *) Allow disabling of RX checksum offloading
- *) Fix MAC changing to work while the interface is up
- (Need to put commands on the TX ring, which changes
- the locking)
- *) Add in FCS to {rx,tx}_bytes, since the hardware doesn't. See
- http://oss.sgi.com/cgi-bin/mesg.cgi?a=netdev&i=20031215152211.7003fe8e.rddunlap%40osdl.org
-*/
-
-/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
- * Setting to > 1518 effectively disables this feature.
- */
-static int rx_copybreak = 200;
-
-/* Should we use MMIO or Port IO?
- * 0: Port IO
- * 1: MMIO
- * 2: Try MMIO, fallback to Port IO
- */
-static unsigned int use_mmio = 2;
-
-/* end user-configurable values */
-
-/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
- */
-static const int multicast_filter_limit = 32;
-
-/* Operational parameters that are set at compile time. */
-
-/* Keep the ring sizes a power of two for compile efficiency.
- * The compiler will convert <unsigned>'%'<2^N> into a bit mask.
- * Making the Tx ring too large decreases the effectiveness of channel
- * bonding and packet priority.
- * There are no ill effects from too-large receive rings.
- *
- * We don't currently use the Hi Tx ring so, don't make it very big.
- *
- * Beware that if we start using the Hi Tx ring, we will need to change
- * typhoon_num_free_tx() and typhoon_tx_complete() to account for that.
- */
-#define TXHI_ENTRIES 2
-#define TXLO_ENTRIES 128
-#define RX_ENTRIES 32
-#define COMMAND_ENTRIES 16
-#define RESPONSE_ENTRIES 32
-
-#define COMMAND_RING_SIZE (COMMAND_ENTRIES * sizeof(struct cmd_desc))
-#define RESPONSE_RING_SIZE (RESPONSE_ENTRIES * sizeof(struct resp_desc))
-
-/* The 3XP will preload and remove 64 entries from the free buffer
- * list, and we need one entry to keep the ring from wrapping, so
- * to keep this a power of two, we use 128 entries.
- */
-#define RXFREE_ENTRIES 128
-#define RXENT_ENTRIES (RXFREE_ENTRIES - 1)
-
-/* Operational parameters that usually are not changed. */
-
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT (2*HZ)
-
-#define PKT_BUF_SZ 1536
-#define FIRMWARE_NAME "3com/typhoon.bin"
-
-#define pr_fmt(fmt) KBUILD_MODNAME " " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/ethtool.h>
-#include <linux/if_vlan.h>
-#include <linux/crc32.h>
-#include <linux/bitops.h>
-#include <asm/processor.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/in6.h>
-#include <linux/dma-mapping.h>
-#include <linux/firmware.h>
-
-#include "typhoon.h"
-
-MODULE_AUTHOR("David Dillow <dave@thedillows.org>");
-MODULE_VERSION("1.0");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(FIRMWARE_NAME);
-MODULE_DESCRIPTION("3Com Typhoon Family (3C990, 3CR990, and variants)");
-MODULE_PARM_DESC(rx_copybreak, "Packets smaller than this are copied and "
- "the buffer given back to the NIC. Default "
- "is 200.");
-MODULE_PARM_DESC(use_mmio, "Use MMIO (1) or PIO(0) to access the NIC. "
- "Default is to try MMIO and fallback to PIO.");
-module_param(rx_copybreak, int, 0);
-module_param(use_mmio, int, 0);
-
-#if defined(NETIF_F_TSO) && MAX_SKB_FRAGS > 32
-#warning Typhoon only supports 32 entries in its SG list for TSO, disabling TSO
-#undef NETIF_F_TSO
-#endif
-
-#if TXLO_ENTRIES <= (2 * MAX_SKB_FRAGS)
-#error TX ring too small!
-#endif
-
-struct typhoon_card_info {
- const char *name;
- const int capabilities;
-};
-
-#define TYPHOON_CRYPTO_NONE 0x00
-#define TYPHOON_CRYPTO_DES 0x01
-#define TYPHOON_CRYPTO_3DES 0x02
-#define TYPHOON_CRYPTO_VARIABLE 0x04
-#define TYPHOON_FIBER 0x08
-#define TYPHOON_WAKEUP_NEEDS_RESET 0x10
-
-enum typhoon_cards {
- TYPHOON_TX = 0, TYPHOON_TX95, TYPHOON_TX97, TYPHOON_SVR,
- TYPHOON_SVR95, TYPHOON_SVR97, TYPHOON_TXM, TYPHOON_BSVR,
- TYPHOON_FX95, TYPHOON_FX97, TYPHOON_FX95SVR, TYPHOON_FX97SVR,
- TYPHOON_FXM,
-};
-
-/* directly indexed by enum typhoon_cards, above */
-static struct typhoon_card_info typhoon_card_info[] __devinitdata = {
- { "3Com Typhoon (3C990-TX)",
- TYPHOON_CRYPTO_NONE},
- { "3Com Typhoon (3CR990-TX-95)",
- TYPHOON_CRYPTO_DES},
- { "3Com Typhoon (3CR990-TX-97)",
- TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES},
- { "3Com Typhoon (3C990SVR)",
- TYPHOON_CRYPTO_NONE},
- { "3Com Typhoon (3CR990SVR95)",
- TYPHOON_CRYPTO_DES},
- { "3Com Typhoon (3CR990SVR97)",
- TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES},
- { "3Com Typhoon2 (3C990B-TX-M)",
- TYPHOON_CRYPTO_VARIABLE},
- { "3Com Typhoon2 (3C990BSVR)",
- TYPHOON_CRYPTO_VARIABLE},
- { "3Com Typhoon (3CR990-FX-95)",
- TYPHOON_CRYPTO_DES | TYPHOON_FIBER},
- { "3Com Typhoon (3CR990-FX-97)",
- TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES | TYPHOON_FIBER},
- { "3Com Typhoon (3CR990-FX-95 Server)",
- TYPHOON_CRYPTO_DES | TYPHOON_FIBER},
- { "3Com Typhoon (3CR990-FX-97 Server)",
- TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES | TYPHOON_FIBER},
- { "3Com Typhoon2 (3C990B-FX-97)",
- TYPHOON_CRYPTO_VARIABLE | TYPHOON_FIBER},
-};
-
-/* Notes on the new subsystem numbering scheme:
- * bits 0-1 indicate crypto capabilities: (0) variable, (1) DES, or (2) 3DES
- * bit 4 indicates if this card has secured firmware (we don't support it)
- * bit 8 indicates if this is a (0) copper or (1) fiber card
- * bits 12-16 indicate card type: (0) client and (1) server
- */
-static DEFINE_PCI_DEVICE_TABLE(typhoon_pci_tbl) = {
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,TYPHOON_TX },
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_TX_95,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_TX95 },
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_TX_97,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_TX97 },
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B,
- PCI_ANY_ID, 0x1000, 0, 0, TYPHOON_TXM },
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B,
- PCI_ANY_ID, 0x1102, 0, 0, TYPHOON_FXM },
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B,
- PCI_ANY_ID, 0x2000, 0, 0, TYPHOON_BSVR },
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
- PCI_ANY_ID, 0x1101, 0, 0, TYPHOON_FX95 },
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
- PCI_ANY_ID, 0x1102, 0, 0, TYPHOON_FX97 },
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
- PCI_ANY_ID, 0x2101, 0, 0, TYPHOON_FX95SVR },
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
- PCI_ANY_ID, 0x2102, 0, 0, TYPHOON_FX97SVR },
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR95,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR95 },
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR97,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR97 },
- { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR },
- { 0, }
-};
-MODULE_DEVICE_TABLE(pci, typhoon_pci_tbl);
-
-/* Define the shared memory area
- * Align everything the 3XP will normally be using.
- * We'll need to move/align txHi if we start using that ring.
- */
-#define __3xp_aligned ____cacheline_aligned
-struct typhoon_shared {
- struct typhoon_interface iface;
- struct typhoon_indexes indexes __3xp_aligned;
- struct tx_desc txLo[TXLO_ENTRIES] __3xp_aligned;
- struct rx_desc rxLo[RX_ENTRIES] __3xp_aligned;
- struct rx_desc rxHi[RX_ENTRIES] __3xp_aligned;
- struct cmd_desc cmd[COMMAND_ENTRIES] __3xp_aligned;
- struct resp_desc resp[RESPONSE_ENTRIES] __3xp_aligned;
- struct rx_free rxBuff[RXFREE_ENTRIES] __3xp_aligned;
- u32 zeroWord;
- struct tx_desc txHi[TXHI_ENTRIES];
-} __packed;
-
-struct rxbuff_ent {
- struct sk_buff *skb;
- dma_addr_t dma_addr;
-};
-
-struct typhoon {
- /* Tx cache line section */
- struct transmit_ring txLoRing ____cacheline_aligned;
- struct pci_dev * tx_pdev;
- void __iomem *tx_ioaddr;
- u32 txlo_dma_addr;
-
- /* Irq/Rx cache line section */
- void __iomem *ioaddr ____cacheline_aligned;
- struct typhoon_indexes *indexes;
- u8 awaiting_resp;
- u8 duplex;
- u8 speed;
- u8 card_state;
- struct basic_ring rxLoRing;
- struct pci_dev * pdev;
- struct net_device * dev;
- struct napi_struct napi;
- struct basic_ring rxHiRing;
- struct basic_ring rxBuffRing;
- struct rxbuff_ent rxbuffers[RXENT_ENTRIES];
-
- /* general section */
- spinlock_t command_lock ____cacheline_aligned;
- struct basic_ring cmdRing;
- struct basic_ring respRing;
- struct net_device_stats stats;
- struct net_device_stats stats_saved;
- struct typhoon_shared * shared;
- dma_addr_t shared_dma;
- __le16 xcvr_select;
- __le16 wol_events;
- __le32 offload;
-
- /* unused stuff (future use) */
- int capabilities;
- struct transmit_ring txHiRing;
-};
-
-enum completion_wait_values {
- NoWait = 0, WaitNoSleep, WaitSleep,
-};
-
-/* These are the values for the typhoon.card_state variable.
- * These determine where the statistics will come from in get_stats().
- * The sleep image does not support the statistics we need.
- */
-enum state_values {
- Sleeping = 0, Running,
-};
-
-/* PCI writes are not guaranteed to be posted in order, but outstanding writes
- * cannot pass a read, so this forces current writes to post.
- */
-#define typhoon_post_pci_writes(x) \
- do { if(likely(use_mmio)) ioread32(x+TYPHOON_REG_HEARTBEAT); } while(0)
-
-/* We'll wait up to six seconds for a reset, and half a second normally.
- */
-#define TYPHOON_UDELAY 50
-#define TYPHOON_RESET_TIMEOUT_SLEEP (6 * HZ)
-#define TYPHOON_RESET_TIMEOUT_NOSLEEP ((6 * 1000000) / TYPHOON_UDELAY)
-#define TYPHOON_WAIT_TIMEOUT ((1000000 / 2) / TYPHOON_UDELAY)
-
-#if defined(NETIF_F_TSO)
-#define skb_tso_size(x) (skb_shinfo(x)->gso_size)
-#define TSO_NUM_DESCRIPTORS 2
-#define TSO_OFFLOAD_ON TYPHOON_OFFLOAD_TCP_SEGMENT
-#else
-#define NETIF_F_TSO 0
-#define skb_tso_size(x) 0
-#define TSO_NUM_DESCRIPTORS 0
-#define TSO_OFFLOAD_ON 0
-#endif
-
-static inline void
-typhoon_inc_index(u32 *index, const int count, const int num_entries)
-{
- /* Increment a ring index -- we can use this for all rings execept
- * the Rx rings, as they use different size descriptors
- * otherwise, everything is the same size as a cmd_desc
- */
- *index += count * sizeof(struct cmd_desc);
- *index %= num_entries * sizeof(struct cmd_desc);
-}
-
-static inline void
-typhoon_inc_cmd_index(u32 *index, const int count)
-{
- typhoon_inc_index(index, count, COMMAND_ENTRIES);
-}
-
-static inline void
-typhoon_inc_resp_index(u32 *index, const int count)
-{
- typhoon_inc_index(index, count, RESPONSE_ENTRIES);
-}
-
-static inline void
-typhoon_inc_rxfree_index(u32 *index, const int count)
-{
- typhoon_inc_index(index, count, RXFREE_ENTRIES);
-}
-
-static inline void
-typhoon_inc_tx_index(u32 *index, const int count)
-{
- /* if we start using the Hi Tx ring, this needs updateing */
- typhoon_inc_index(index, count, TXLO_ENTRIES);
-}
-
-static inline void
-typhoon_inc_rx_index(u32 *index, const int count)
-{
- /* sizeof(struct rx_desc) != sizeof(struct cmd_desc) */
- *index += count * sizeof(struct rx_desc);
- *index %= RX_ENTRIES * sizeof(struct rx_desc);
-}
-
-static int
-typhoon_reset(void __iomem *ioaddr, int wait_type)
-{
- int i, err = 0;
- int timeout;
-
- if(wait_type == WaitNoSleep)
- timeout = TYPHOON_RESET_TIMEOUT_NOSLEEP;
- else
- timeout = TYPHOON_RESET_TIMEOUT_SLEEP;
-
- iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
- iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
-
- iowrite32(TYPHOON_RESET_ALL, ioaddr + TYPHOON_REG_SOFT_RESET);
- typhoon_post_pci_writes(ioaddr);
- udelay(1);
- iowrite32(TYPHOON_RESET_NONE, ioaddr + TYPHOON_REG_SOFT_RESET);
-
- if(wait_type != NoWait) {
- for(i = 0; i < timeout; i++) {
- if(ioread32(ioaddr + TYPHOON_REG_STATUS) ==
- TYPHOON_STATUS_WAITING_FOR_HOST)
- goto out;
-
- if(wait_type == WaitSleep)
- schedule_timeout_uninterruptible(1);
- else
- udelay(TYPHOON_UDELAY);
- }
-
- err = -ETIMEDOUT;
- }
-
-out:
- iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
- iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
-
- /* The 3XP seems to need a little extra time to complete the load
- * of the sleep image before we can reliably boot it. Failure to
- * do this occasionally results in a hung adapter after boot in
- * typhoon_init_one() while trying to read the MAC address or
- * putting the card to sleep. 3Com's driver waits 5ms, but
- * that seems to be overkill. However, if we can sleep, we might
- * as well give it that much time. Otherwise, we'll give it 500us,
- * which should be enough (I've see it work well at 100us, but still
- * saw occasional problems.)
- */
- if(wait_type == WaitSleep)
- msleep(5);
- else
- udelay(500);
- return err;
-}
-
-static int
-typhoon_wait_status(void __iomem *ioaddr, u32 wait_value)
-{
- int i, err = 0;
-
- for(i = 0; i < TYPHOON_WAIT_TIMEOUT; i++) {
- if(ioread32(ioaddr + TYPHOON_REG_STATUS) == wait_value)
- goto out;
- udelay(TYPHOON_UDELAY);
- }
-
- err = -ETIMEDOUT;
-
-out:
- return err;
-}
-
-static inline void
-typhoon_media_status(struct net_device *dev, struct resp_desc *resp)
-{
- if(resp->parm1 & TYPHOON_MEDIA_STAT_NO_LINK)
- netif_carrier_off(dev);
- else
- netif_carrier_on(dev);
-}
-
-static inline void
-typhoon_hello(struct typhoon *tp)
-{
- struct basic_ring *ring = &tp->cmdRing;
- struct cmd_desc *cmd;
-
- /* We only get a hello request if we've not sent anything to the
- * card in a long while. If the lock is held, then we're in the
- * process of issuing a command, so we don't need to respond.
- */
- if(spin_trylock(&tp->command_lock)) {
- cmd = (struct cmd_desc *)(ring->ringBase + ring->lastWrite);
- typhoon_inc_cmd_index(&ring->lastWrite, 1);
-
- INIT_COMMAND_NO_RESPONSE(cmd, TYPHOON_CMD_HELLO_RESP);
- wmb();
- iowrite32(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);
- spin_unlock(&tp->command_lock);
- }
-}
-
-static int
-typhoon_process_response(struct typhoon *tp, int resp_size,
- struct resp_desc *resp_save)
-{
- struct typhoon_indexes *indexes = tp->indexes;
- struct resp_desc *resp;
- u8 *base = tp->respRing.ringBase;
- int count, len, wrap_len;
- u32 cleared;
- u32 ready;
-
- cleared = le32_to_cpu(indexes->respCleared);
- ready = le32_to_cpu(indexes->respReady);
- while(cleared != ready) {
- resp = (struct resp_desc *)(base + cleared);
- count = resp->numDesc + 1;
- if(resp_save && resp->seqNo) {
- if(count > resp_size) {
- resp_save->flags = TYPHOON_RESP_ERROR;
- goto cleanup;
- }
-
- wrap_len = 0;
- len = count * sizeof(*resp);
- if(unlikely(cleared + len > RESPONSE_RING_SIZE)) {
- wrap_len = cleared + len - RESPONSE_RING_SIZE;
- len = RESPONSE_RING_SIZE - cleared;
- }
-
- memcpy(resp_save, resp, len);
- if(unlikely(wrap_len)) {
- resp_save += len / sizeof(*resp);
- memcpy(resp_save, base, wrap_len);
- }
-
- resp_save = NULL;
- } else if(resp->cmd == TYPHOON_CMD_READ_MEDIA_STATUS) {
- typhoon_media_status(tp->dev, resp);
- } else if(resp->cmd == TYPHOON_CMD_HELLO_RESP) {
- typhoon_hello(tp);
- } else {
- netdev_err(tp->dev,
- "dumping unexpected response 0x%04x:%d:0x%02x:0x%04x:%08x:%08x\n",
- le16_to_cpu(resp->cmd),
- resp->numDesc, resp->flags,
- le16_to_cpu(resp->parm1),
- le32_to_cpu(resp->parm2),
- le32_to_cpu(resp->parm3));
- }
-
-cleanup:
- typhoon_inc_resp_index(&cleared, count);
- }
-
- indexes->respCleared = cpu_to_le32(cleared);
- wmb();
- return resp_save == NULL;
-}
-
-static inline int
-typhoon_num_free(int lastWrite, int lastRead, int ringSize)
-{
- /* this works for all descriptors but rx_desc, as they are a
- * different size than the cmd_desc -- everyone else is the same
- */
- lastWrite /= sizeof(struct cmd_desc);
- lastRead /= sizeof(struct cmd_desc);
- return (ringSize + lastRead - lastWrite - 1) % ringSize;
-}
-
-static inline int
-typhoon_num_free_cmd(struct typhoon *tp)
-{
- int lastWrite = tp->cmdRing.lastWrite;
- int cmdCleared = le32_to_cpu(tp->indexes->cmdCleared);
-
- return typhoon_num_free(lastWrite, cmdCleared, COMMAND_ENTRIES);
-}
-
-static inline int
-typhoon_num_free_resp(struct typhoon *tp)
-{
- int respReady = le32_to_cpu(tp->indexes->respReady);
- int respCleared = le32_to_cpu(tp->indexes->respCleared);
-
- return typhoon_num_free(respReady, respCleared, RESPONSE_ENTRIES);
-}
-
-static inline int
-typhoon_num_free_tx(struct transmit_ring *ring)
-{
- /* if we start using the Hi Tx ring, this needs updating */
- return typhoon_num_free(ring->lastWrite, ring->lastRead, TXLO_ENTRIES);
-}
-
-static int
-typhoon_issue_command(struct typhoon *tp, int num_cmd, struct cmd_desc *cmd,
- int num_resp, struct resp_desc *resp)
-{
- struct typhoon_indexes *indexes = tp->indexes;
- struct basic_ring *ring = &tp->cmdRing;
- struct resp_desc local_resp;
- int i, err = 0;
- int got_resp;
- int freeCmd, freeResp;
- int len, wrap_len;
-
- spin_lock(&tp->command_lock);
-
- freeCmd = typhoon_num_free_cmd(tp);
- freeResp = typhoon_num_free_resp(tp);
-
- if(freeCmd < num_cmd || freeResp < num_resp) {
- netdev_err(tp->dev, "no descs for cmd, had (needed) %d (%d) cmd, %d (%d) resp\n",
- freeCmd, num_cmd, freeResp, num_resp);
- err = -ENOMEM;
- goto out;
- }
-
- if(cmd->flags & TYPHOON_CMD_RESPOND) {
- /* If we're expecting a response, but the caller hasn't given
- * us a place to put it, we'll provide one.
- */
- tp->awaiting_resp = 1;
- if(resp == NULL) {
- resp = &local_resp;
- num_resp = 1;
- }
- }
-
- wrap_len = 0;
- len = num_cmd * sizeof(*cmd);
- if(unlikely(ring->lastWrite + len > COMMAND_RING_SIZE)) {
- wrap_len = ring->lastWrite + len - COMMAND_RING_SIZE;
- len = COMMAND_RING_SIZE - ring->lastWrite;
- }
-
- memcpy(ring->ringBase + ring->lastWrite, cmd, len);
- if(unlikely(wrap_len)) {
- struct cmd_desc *wrap_ptr = cmd;
- wrap_ptr += len / sizeof(*cmd);
- memcpy(ring->ringBase, wrap_ptr, wrap_len);
- }
-
- typhoon_inc_cmd_index(&ring->lastWrite, num_cmd);
-
- /* "I feel a presence... another warrior is on the mesa."
- */
- wmb();
- iowrite32(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);
- typhoon_post_pci_writes(tp->ioaddr);
-
- if((cmd->flags & TYPHOON_CMD_RESPOND) == 0)
- goto out;
-
- /* Ugh. We'll be here about 8ms, spinning our thumbs, unable to
- * preempt or do anything other than take interrupts. So, don't
- * wait for a response unless you have to.
- *
- * I've thought about trying to sleep here, but we're called
- * from many contexts that don't allow that. Also, given the way
- * 3Com has implemented irq coalescing, we would likely timeout --
- * this has been observed in real life!
- *
- * The big killer is we have to wait to get stats from the card,
- * though we could go to a periodic refresh of those if we don't
- * mind them getting somewhat stale. The rest of the waiting
- * commands occur during open/close/suspend/resume, so they aren't
- * time critical. Creating SAs in the future will also have to
- * wait here.
- */
- got_resp = 0;
- for(i = 0; i < TYPHOON_WAIT_TIMEOUT && !got_resp; i++) {
- if(indexes->respCleared != indexes->respReady)
- got_resp = typhoon_process_response(tp, num_resp,
- resp);
- udelay(TYPHOON_UDELAY);
- }
-
- if(!got_resp) {
- err = -ETIMEDOUT;
- goto out;
- }
-
- /* Collect the error response even if we don't care about the
- * rest of the response
- */
- if(resp->flags & TYPHOON_RESP_ERROR)
- err = -EIO;
-
-out:
- if(tp->awaiting_resp) {
- tp->awaiting_resp = 0;
- smp_wmb();
-
- /* Ugh. If a response was added to the ring between
- * the call to typhoon_process_response() and the clearing
- * of tp->awaiting_resp, we could have missed the interrupt
- * and it could hang in the ring an indeterminate amount of
- * time. So, check for it, and interrupt ourselves if this
- * is the case.
- */
- if(indexes->respCleared != indexes->respReady)
- iowrite32(1, tp->ioaddr + TYPHOON_REG_SELF_INTERRUPT);
- }
-
- spin_unlock(&tp->command_lock);
- return err;
-}
-
-static inline void
-typhoon_tso_fill(struct sk_buff *skb, struct transmit_ring *txRing,
- u32 ring_dma)
-{
- struct tcpopt_desc *tcpd;
- u32 tcpd_offset = ring_dma;
-
- tcpd = (struct tcpopt_desc *) (txRing->ringBase + txRing->lastWrite);
- tcpd_offset += txRing->lastWrite;
- tcpd_offset += offsetof(struct tcpopt_desc, bytesTx);
- typhoon_inc_tx_index(&txRing->lastWrite, 1);
-
- tcpd->flags = TYPHOON_OPT_DESC | TYPHOON_OPT_TCP_SEG;
- tcpd->numDesc = 1;
- tcpd->mss_flags = cpu_to_le16(skb_tso_size(skb));
- tcpd->mss_flags |= TYPHOON_TSO_FIRST | TYPHOON_TSO_LAST;
- tcpd->respAddrLo = cpu_to_le32(tcpd_offset);
- tcpd->bytesTx = cpu_to_le32(skb->len);
- tcpd->status = 0;
-}
-
-static netdev_tx_t
-typhoon_start_tx(struct sk_buff *skb, struct net_device *dev)
-{
- struct typhoon *tp = netdev_priv(dev);
- struct transmit_ring *txRing;
- struct tx_desc *txd, *first_txd;
- dma_addr_t skb_dma;
- int numDesc;
-
- /* we have two rings to choose from, but we only use txLo for now
- * If we start using the Hi ring as well, we'll need to update
- * typhoon_stop_runtime(), typhoon_interrupt(), typhoon_num_free_tx(),
- * and TXHI_ENTRIES to match, as well as update the TSO code below
- * to get the right DMA address
- */
- txRing = &tp->txLoRing;
-
- /* We need one descriptor for each fragment of the sk_buff, plus the
- * one for the ->data area of it.
- *
- * The docs say a maximum of 16 fragment descriptors per TCP option
- * descriptor, then make a new packet descriptor and option descriptor
- * for the next 16 fragments. The engineers say just an option
- * descriptor is needed. I've tested up to 26 fragments with a single
- * packet descriptor/option descriptor combo, so I use that for now.
- *
- * If problems develop with TSO, check this first.
- */
- numDesc = skb_shinfo(skb)->nr_frags + 1;
- if (skb_is_gso(skb))
- numDesc++;
-
- /* When checking for free space in the ring, we need to also
- * account for the initial Tx descriptor, and we always must leave
- * at least one descriptor unused in the ring so that it doesn't
- * wrap and look empty.
- *
- * The only time we should loop here is when we hit the race
- * between marking the queue awake and updating the cleared index.
- * Just loop and it will appear. This comes from the acenic driver.
- */
- while(unlikely(typhoon_num_free_tx(txRing) < (numDesc + 2)))
- smp_rmb();
-
- first_txd = (struct tx_desc *) (txRing->ringBase + txRing->lastWrite);
- typhoon_inc_tx_index(&txRing->lastWrite, 1);
-
- first_txd->flags = TYPHOON_TX_DESC | TYPHOON_DESC_VALID;
- first_txd->numDesc = 0;
- first_txd->len = 0;
- first_txd->tx_addr = (u64)((unsigned long) skb);
- first_txd->processFlags = 0;
-
- if(skb->ip_summed == CHECKSUM_PARTIAL) {
- /* The 3XP will figure out if this is UDP/TCP */
- first_txd->processFlags |= TYPHOON_TX_PF_TCP_CHKSUM;
- first_txd->processFlags |= TYPHOON_TX_PF_UDP_CHKSUM;
- first_txd->processFlags |= TYPHOON_TX_PF_IP_CHKSUM;
- }
-
- if(vlan_tx_tag_present(skb)) {
- first_txd->processFlags |=
- TYPHOON_TX_PF_INSERT_VLAN | TYPHOON_TX_PF_VLAN_PRIORITY;
- first_txd->processFlags |=
- cpu_to_le32(htons(vlan_tx_tag_get(skb)) <<
- TYPHOON_TX_PF_VLAN_TAG_SHIFT);
- }
-
- if (skb_is_gso(skb)) {
- first_txd->processFlags |= TYPHOON_TX_PF_TCP_SEGMENT;
- first_txd->numDesc++;
-
- typhoon_tso_fill(skb, txRing, tp->txlo_dma_addr);
- }
-
- txd = (struct tx_desc *) (txRing->ringBase + txRing->lastWrite);
- typhoon_inc_tx_index(&txRing->lastWrite, 1);
-
- /* No need to worry about padding packet -- the firmware pads
- * it with zeros to ETH_ZLEN for us.
- */
- if(skb_shinfo(skb)->nr_frags == 0) {
- skb_dma = pci_map_single(tp->tx_pdev, skb->data, skb->len,
- PCI_DMA_TODEVICE);
- txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
- txd->len = cpu_to_le16(skb->len);
- txd->frag.addr = cpu_to_le32(skb_dma);
- txd->frag.addrHi = 0;
- first_txd->numDesc++;
- } else {
- int i, len;
-
- len = skb_headlen(skb);
- skb_dma = pci_map_single(tp->tx_pdev, skb->data, len,
- PCI_DMA_TODEVICE);
- txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
- txd->len = cpu_to_le16(len);
- txd->frag.addr = cpu_to_le32(skb_dma);
- txd->frag.addrHi = 0;
- first_txd->numDesc++;
-
- for(i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- void *frag_addr;
-
- txd = (struct tx_desc *) (txRing->ringBase +
- txRing->lastWrite);
- typhoon_inc_tx_index(&txRing->lastWrite, 1);
-
- len = frag->size;
- frag_addr = (void *) page_address(frag->page) +
- frag->page_offset;
- skb_dma = pci_map_single(tp->tx_pdev, frag_addr, len,
- PCI_DMA_TODEVICE);
- txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
- txd->len = cpu_to_le16(len);
- txd->frag.addr = cpu_to_le32(skb_dma);
- txd->frag.addrHi = 0;
- first_txd->numDesc++;
- }
- }
-
- /* Kick the 3XP
- */
- wmb();
- iowrite32(txRing->lastWrite, tp->tx_ioaddr + txRing->writeRegister);
-
- /* If we don't have room to put the worst case packet on the
- * queue, then we must stop the queue. We need 2 extra
- * descriptors -- one to prevent ring wrap, and one for the
- * Tx header.
- */
- numDesc = MAX_SKB_FRAGS + TSO_NUM_DESCRIPTORS + 1;
-
- if(typhoon_num_free_tx(txRing) < (numDesc + 2)) {
- netif_stop_queue(dev);
-
- /* A Tx complete IRQ could have gotten between, making
- * the ring free again. Only need to recheck here, since
- * Tx is serialized.
- */
- if(typhoon_num_free_tx(txRing) >= (numDesc + 2))
- netif_wake_queue(dev);
- }
-
- return NETDEV_TX_OK;
-}
-
-static void
-typhoon_set_rx_mode(struct net_device *dev)
-{
- struct typhoon *tp = netdev_priv(dev);
- struct cmd_desc xp_cmd;
- u32 mc_filter[2];
- __le16 filter;
-
- filter = TYPHOON_RX_FILTER_DIRECTED | TYPHOON_RX_FILTER_BROADCAST;
- if(dev->flags & IFF_PROMISC) {
- filter |= TYPHOON_RX_FILTER_PROMISCOUS;
- } else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
- (dev->flags & IFF_ALLMULTI)) {
- /* Too many to match, or accept all multicasts. */
- filter |= TYPHOON_RX_FILTER_ALL_MCAST;
- } else if (!netdev_mc_empty(dev)) {
- struct netdev_hw_addr *ha;
-
- memset(mc_filter, 0, sizeof(mc_filter));
- netdev_for_each_mc_addr(ha, dev) {
- int bit = ether_crc(ETH_ALEN, ha->addr) & 0x3f;
- mc_filter[bit >> 5] |= 1 << (bit & 0x1f);
- }
-
- INIT_COMMAND_NO_RESPONSE(&xp_cmd,
- TYPHOON_CMD_SET_MULTICAST_HASH);
- xp_cmd.parm1 = TYPHOON_MCAST_HASH_SET;
- xp_cmd.parm2 = cpu_to_le32(mc_filter[0]);
- xp_cmd.parm3 = cpu_to_le32(mc_filter[1]);
- typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
-
- filter |= TYPHOON_RX_FILTER_MCAST_HASH;
- }
-
- INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_RX_FILTER);
- xp_cmd.parm1 = filter;
- typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
-}
-
-static int
-typhoon_do_get_stats(struct typhoon *tp)
-{
- struct net_device_stats *stats = &tp->stats;
- struct net_device_stats *saved = &tp->stats_saved;
- struct cmd_desc xp_cmd;
- struct resp_desc xp_resp[7];
- struct stats_resp *s = (struct stats_resp *) xp_resp;
- int err;
-
- INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_STATS);
- err = typhoon_issue_command(tp, 1, &xp_cmd, 7, xp_resp);
- if(err < 0)
- return err;
-
- /* 3Com's Linux driver uses txMultipleCollisions as it's
- * collisions value, but there is some other collision info as well...
- *
- * The extra status reported would be a good candidate for
- * ethtool_ops->get_{strings,stats}()
- */
- stats->tx_packets = le32_to_cpu(s->txPackets) +
- saved->tx_packets;
- stats->tx_bytes = le64_to_cpu(s->txBytes) +
- saved->tx_bytes;
- stats->tx_errors = le32_to_cpu(s->txCarrierLost) +
- saved->tx_errors;
- stats->tx_carrier_errors = le32_to_cpu(s->txCarrierLost) +
- saved->tx_carrier_errors;
- stats->collisions = le32_to_cpu(s->txMultipleCollisions) +
- saved->collisions;
- stats->rx_packets = le32_to_cpu(s->rxPacketsGood) +
- saved->rx_packets;
- stats->rx_bytes = le64_to_cpu(s->rxBytesGood) +
- saved->rx_bytes;
- stats->rx_fifo_errors = le32_to_cpu(s->rxFifoOverruns) +
- saved->rx_fifo_errors;
- stats->rx_errors = le32_to_cpu(s->rxFifoOverruns) +
- le32_to_cpu(s->BadSSD) + le32_to_cpu(s->rxCrcErrors) +
- saved->rx_errors;
- stats->rx_crc_errors = le32_to_cpu(s->rxCrcErrors) +
- saved->rx_crc_errors;
- stats->rx_length_errors = le32_to_cpu(s->rxOversized) +
- saved->rx_length_errors;
- tp->speed = (s->linkStatus & TYPHOON_LINK_100MBPS) ?
- SPEED_100 : SPEED_10;
- tp->duplex = (s->linkStatus & TYPHOON_LINK_FULL_DUPLEX) ?
- DUPLEX_FULL : DUPLEX_HALF;
-
- return 0;
-}
-
-static struct net_device_stats *
-typhoon_get_stats(struct net_device *dev)
-{
- struct typhoon *tp = netdev_priv(dev);
- struct net_device_stats *stats = &tp->stats;
- struct net_device_stats *saved = &tp->stats_saved;
-
- smp_rmb();
- if(tp->card_state == Sleeping)
- return saved;
-
- if(typhoon_do_get_stats(tp) < 0) {
- netdev_err(dev, "error getting stats\n");
- return saved;
- }
-
- return stats;
-}
-
-static int
-typhoon_set_mac_address(struct net_device *dev, void *addr)
-{
- struct sockaddr *saddr = (struct sockaddr *) addr;
-
- if(netif_running(dev))
- return -EBUSY;
-
- memcpy(dev->dev_addr, saddr->sa_data, dev->addr_len);
- return 0;
-}
-
-static void
-typhoon_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
- struct typhoon *tp = netdev_priv(dev);
- struct pci_dev *pci_dev = tp->pdev;
- struct cmd_desc xp_cmd;
- struct resp_desc xp_resp[3];
-
- smp_rmb();
- if(tp->card_state == Sleeping) {
- strcpy(info->fw_version, "Sleep image");
- } else {
- INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_VERSIONS);
- if(typhoon_issue_command(tp, 1, &xp_cmd, 3, xp_resp) < 0) {
- strcpy(info->fw_version, "Unknown runtime");
- } else {
- u32 sleep_ver = le32_to_cpu(xp_resp[0].parm2);
- snprintf(info->fw_version, 32, "%02x.%03x.%03x",
- sleep_ver >> 24, (sleep_ver >> 12) & 0xfff,
- sleep_ver & 0xfff);
- }
- }
-
- strcpy(info->driver, KBUILD_MODNAME);
- strcpy(info->bus_info, pci_name(pci_dev));
-}
-
-static int
-typhoon_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
- struct typhoon *tp = netdev_priv(dev);
-
- cmd->supported = SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
- SUPPORTED_Autoneg;
-
- switch (tp->xcvr_select) {
- case TYPHOON_XCVR_10HALF:
- cmd->advertising = ADVERTISED_10baseT_Half;
- break;
- case TYPHOON_XCVR_10FULL:
- cmd->advertising = ADVERTISED_10baseT_Full;
- break;