/* yellowfin.c: A Packet Engines G-NIC ethernet driver for linux. */ /* Written 1997-2001 by Donald Becker. 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 driver is for the Packet Engines G-NIC PCI Gigabit Ethernet adapter. It also supports the Symbios Logic version of the same chip core. The author may be reached as [email protected], or C/O Scyld Computing Corporation 410 Severn Ave., Suite 210 Annapolis MD 21403 Support and updates available at http://www.scyld.com/network/yellowfin.html [link no longer provides useful info -jgarzik] */ #define pr_fmt(fmt) … #define DRV_NAME … #define DRV_VERSION … #define DRV_RELDATE … /* The user-configurable values. These may be modified when a driver module is loaded.*/ static int debug = …; /* 1 normal messages, 0 quiet .. 7 verbose. */ /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ static int max_interrupt_work = …; static int mtu; #ifdef YF_PROTOTYPE /* Support for prototype hardware errata. */ /* System-wide count of bogus-rx frames. */ static int bogus_rx; static int dma_ctrl = 0x004A0263; /* Constrained by errata */ static int fifo_cfg = 0x0020; /* Bypass external Tx FIFO. */ #elif defined(YF_NEW) /* A future perfect board :->. */ static int dma_ctrl = 0x00CAC277; /* Override when loading module! */ static int fifo_cfg = 0x0028; #else static const int dma_ctrl = …; /* Constrained by errata */ static const int fifo_cfg = …; /* Bypass external Tx FIFO. */ #endif /* Set the copy breakpoint for the copy-only-tiny-frames scheme. Setting to > 1514 effectively disables this feature. */ static int rx_copybreak; /* Used to pass the media type, etc. No media types are currently defined. These exist for driver interoperability. */ #define MAX_UNITS … static int options[MAX_UNITS] = …; static int full_duplex[MAX_UNITS] = …; /* Do ugly workaround for GX server chipset errata. */ static int gx_fix; /* Operational parameters that are set at compile time. */ /* Keep the ring sizes a power of two for efficiency. Making the Tx ring too long decreases the effectiveness of channel bonding and packet priority. There are no ill effects from too-large receive rings. */ #define TX_RING_SIZE … #define TX_QUEUE_SIZE … #define RX_RING_SIZE … #define STATUS_TOTAL_SIZE … #define TX_TOTAL_SIZE … #define RX_TOTAL_SIZE … /* Operational parameters that usually are not changed. */ /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT … #define PKT_BUF_SZ … #define yellowfin_debug … #include <linux/module.h> #include <linux/kernel.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/init.h> #include <linux/mii.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/ethtool.h> #include <linux/crc32.h> #include <linux/bitops.h> #include <linux/uaccess.h> #include <asm/processor.h> /* Processor type for cache alignment. */ #include <asm/unaligned.h> #include <asm/io.h> /* These identify the driver base version and may not be removed. */ static const char version[] = …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …; module_param(max_interrupt_work, int, 0); module_param(mtu, int, 0); module_param(debug, int, 0); module_param(rx_copybreak, int, 0); module_param_array(…); module_param_array(…); module_param(gx_fix, int, 0); MODULE_PARM_DESC(…) …; MODULE_PARM_DESC(…) …; MODULE_PARM_DESC(…) …; MODULE_PARM_DESC(…) …; MODULE_PARM_DESC(…) …; MODULE_PARM_DESC(…) …; MODULE_PARM_DESC(…) …; /* Theory of Operation I. Board Compatibility This device driver is designed for the Packet Engines "Yellowfin" Gigabit Ethernet adapter. The G-NIC 64-bit PCI card is supported, as well as the Symbios 53C885E dual function chip. II. Board-specific settings PCI bus devices are configured by the system at boot time, so no jumpers need to be set on the board. The system BIOS preferably should assign the PCI INTA signal to an otherwise unused system IRQ line. Note: Kernel versions earlier than 1.3.73 do not support shared PCI interrupt lines. III. Driver operation IIIa. Ring buffers The Yellowfin uses the Descriptor Based DMA Architecture specified by Apple. This is a descriptor list scheme similar to that used by the EEPro100 and Tulip. This driver uses two statically allocated fixed-size descriptor lists formed into rings by a branch from the final descriptor to the beginning of the list. The ring sizes are set at compile time by RX/TX_RING_SIZE. The driver allocates full frame size skbuffs for the Rx ring buffers at open() time and passes the skb->data field to the Yellowfin as receive data buffers. When an incoming frame is less than RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is copied to the new skbuff. When the incoming frame is larger, the skbuff is passed directly up the protocol stack and replaced by a newly allocated skbuff. The RX_COPYBREAK value is chosen to trade-off the memory wasted by using a full-sized skbuff for small frames vs. the copying costs of larger frames. For small frames the copying cost is negligible (esp. considering that we are pre-loading the cache with immediately useful header information). For large frames the copying cost is non-trivial, and the larger copy might flush the cache of useful data. IIIC. Synchronization The driver runs as two independent, single-threaded flows of control. One is the send-packet routine, which enforces single-threaded use by the dev->tbusy flag. The other thread is the interrupt handler, which is single threaded by the hardware and other software. The send packet thread has partial control over the Tx ring and 'dev->tbusy' flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next queue slot is empty, it clears the tbusy flag when finished otherwise it sets the 'yp->tx_full' flag. The interrupt handler has exclusive control over the Rx ring and records stats from the Tx ring. After reaping the stats, it marks the Tx queue entry as empty by incrementing the dirty_tx mark. Iff the 'yp->tx_full' flag is set, it clears both the tx_full and tbusy flags. IV. Notes Thanks to Kim Stearns of Packet Engines for providing a pair of G-NIC boards. Thanks to Bruce Faust of Digitalscape for providing both their SYM53C885 board and an AlphaStation to verify the Alpha port! IVb. References Yellowfin Engineering Design Specification, 4/23/97 Preliminary/Confidential Symbios SYM53C885 PCI-SCSI/Fast Ethernet Multifunction Controller Preliminary Data Manual v3.0 http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html IVc. Errata See Packet Engines confidential appendix (prototype chips only). */ enum capability_flags { … }; /* The PCI I/O space extent. */ enum { … }; struct pci_id_info { … }; static const struct pci_id_info pci_id_tbl[] = …; static const struct pci_device_id yellowfin_pci_tbl[] = …; MODULE_DEVICE_TABLE (pci, yellowfin_pci_tbl); /* Offsets to the Yellowfin registers. Various sizes and alignments. */ enum yellowfin_offsets { … }; /* The Yellowfin Rx and Tx buffer descriptors. Elements are written as 32 bit for endian portability. */ struct yellowfin_desc { … }; struct tx_status_words { … }; /* Bits in yellowfin_desc.cmd */ enum desc_cmd_bits { … }; /* Bits in yellowfin_desc.status */ enum desc_status_bits { … }; /* Bits in the interrupt status/mask registers. */ enum intr_status_bits { … }; #define PRIV_ALIGN … #define MII_CNT … struct yellowfin_private { … }; static int read_eeprom(void __iomem *ioaddr, int location); static int mdio_read(void __iomem *ioaddr, int phy_id, int location); static void mdio_write(void __iomem *ioaddr, int phy_id, int location, int value); static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static int yellowfin_open(struct net_device *dev); static void yellowfin_timer(struct timer_list *t); static void yellowfin_tx_timeout(struct net_device *dev, unsigned int txqueue); static int yellowfin_init_ring(struct net_device *dev); static netdev_tx_t yellowfin_start_xmit(struct sk_buff *skb, struct net_device *dev); static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance); static int yellowfin_rx(struct net_device *dev); static void yellowfin_error(struct net_device *dev, int intr_status); static int yellowfin_close(struct net_device *dev); static void set_rx_mode(struct net_device *dev); static const struct ethtool_ops ethtool_ops; static const struct net_device_ops netdev_ops = …; static int yellowfin_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { … } static int read_eeprom(void __iomem *ioaddr, int location) { … } /* MII Managemen Data I/O accesses. These routines assume the MDIO controller is idle, and do not exit until the command is finished. */ static int mdio_read(void __iomem *ioaddr, int phy_id, int location) { … } static void mdio_write(void __iomem *ioaddr, int phy_id, int location, int value) { … } static int yellowfin_open(struct net_device *dev) { … } static void yellowfin_timer(struct timer_list *t) { … } static void yellowfin_tx_timeout(struct net_device *dev, unsigned int txqueue) { … } /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ static int yellowfin_init_ring(struct net_device *dev) { … } static netdev_tx_t yellowfin_start_xmit(struct sk_buff *skb, struct net_device *dev) { … } /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance) { … } /* This routine is logically part of the interrupt handler, but separated for clarity and better register allocation. */ static int yellowfin_rx(struct net_device *dev) { … } static void yellowfin_error(struct net_device *dev, int intr_status) { … } static int yellowfin_close(struct net_device *dev) { … } /* Set or clear the multicast filter for this adaptor. */ static void set_rx_mode(struct net_device *dev) { … } static void yellowfin_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { … } static const struct ethtool_ops ethtool_ops = …; static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { … } static void yellowfin_remove_one(struct pci_dev *pdev) { … } static struct pci_driver yellowfin_driver = …; static int __init yellowfin_init (void) { … } static void __exit yellowfin_cleanup (void) { … } module_init(…) …; module_exit(yellowfin_cleanup);