18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci	Written 1997-1998 by Donald Becker.
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci	This software may be used and distributed according to the terms
58c2ecf20Sopenharmony_ci	of the GNU General Public License, incorporated herein by reference.
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci	This driver is for the 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard.
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci	The author may be reached as becker@scyld.com, or C/O
108c2ecf20Sopenharmony_ci	Scyld Computing Corporation
118c2ecf20Sopenharmony_ci	410 Severn Ave., Suite 210
128c2ecf20Sopenharmony_ci	Annapolis MD 21403
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci	2000/2/2- Added support for kernel-level ISAPnP
168c2ecf20Sopenharmony_ci		by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo
178c2ecf20Sopenharmony_ci	Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox.
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	2001/11/17 - Added ethtool support (jgarzik)
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk)
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci*/
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define DRV_NAME		"3c515"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define CORKSCREW 1
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* "Knobs" that adjust features and parameters. */
308c2ecf20Sopenharmony_ci/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
318c2ecf20Sopenharmony_ci   Setting to > 1512 effectively disables this feature. */
328c2ecf20Sopenharmony_cistatic int rx_copybreak = 200;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
358c2ecf20Sopenharmony_cistatic const int mtu = 1500;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
388c2ecf20Sopenharmony_cistatic int max_interrupt_work = 20;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* Enable the automatic media selection code -- usually set. */
418c2ecf20Sopenharmony_ci#define AUTOMEDIA 1
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* Allow the use of fragment bus master transfers instead of only
448c2ecf20Sopenharmony_ci   programmed-I/O for Vortex cards.  Full-bus-master transfers are always
458c2ecf20Sopenharmony_ci   enabled by default on Boomerang cards.  If VORTEX_BUS_MASTER is defined,
468c2ecf20Sopenharmony_ci   the feature may be turned on using 'options'. */
478c2ecf20Sopenharmony_ci#define VORTEX_BUS_MASTER
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* A few values that may be tweaked. */
508c2ecf20Sopenharmony_ci/* Keep the ring sizes a power of two for efficiency. */
518c2ecf20Sopenharmony_ci#define TX_RING_SIZE	16
528c2ecf20Sopenharmony_ci#define RX_RING_SIZE	16
538c2ecf20Sopenharmony_ci#define PKT_BUF_SZ		1536	/* Size of each temporary Rx buffer. */
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#include <linux/module.h>
568c2ecf20Sopenharmony_ci#include <linux/isapnp.h>
578c2ecf20Sopenharmony_ci#include <linux/kernel.h>
588c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
598c2ecf20Sopenharmony_ci#include <linux/string.h>
608c2ecf20Sopenharmony_ci#include <linux/errno.h>
618c2ecf20Sopenharmony_ci#include <linux/in.h>
628c2ecf20Sopenharmony_ci#include <linux/ioport.h>
638c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
648c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
658c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
668c2ecf20Sopenharmony_ci#include <linux/timer.h>
678c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
688c2ecf20Sopenharmony_ci#include <linux/bitops.h>
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
718c2ecf20Sopenharmony_ci#include <asm/io.h>
728c2ecf20Sopenharmony_ci#include <asm/dma.h>
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define NEW_MULTICAST
758c2ecf20Sopenharmony_ci#include <linux/delay.h>
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci#define MAX_UNITS 8
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Donald Becker <becker@scyld.com>");
808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("3Com 3c515 Corkscrew driver");
818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/* "Knobs" for adjusting internal parameters. */
848c2ecf20Sopenharmony_ci/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
858c2ecf20Sopenharmony_ci#define DRIVER_DEBUG 1
868c2ecf20Sopenharmony_ci/* Some values here only for performance evaluation and path-coverage
878c2ecf20Sopenharmony_ci   debugging. */
888c2ecf20Sopenharmony_cistatic int rx_nocopy, rx_copy, queued_packet;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* Number of times to check to see if the Tx FIFO has space, used in some
918c2ecf20Sopenharmony_ci   limited cases. */
928c2ecf20Sopenharmony_ci#define WAIT_TX_AVAIL 200
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/* Operational parameter that usually are not changed. */
958c2ecf20Sopenharmony_ci#define TX_TIMEOUT  ((4*HZ)/10)	/* Time in jiffies before concluding Tx hung */
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/* The size here is somewhat misleading: the Corkscrew also uses the ISA
988c2ecf20Sopenharmony_ci   aliased registers at <base>+0x400.
998c2ecf20Sopenharmony_ci   */
1008c2ecf20Sopenharmony_ci#define CORKSCREW_TOTAL_SIZE 0x20
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#ifdef DRIVER_DEBUG
1038c2ecf20Sopenharmony_cistatic int corkscrew_debug = DRIVER_DEBUG;
1048c2ecf20Sopenharmony_ci#else
1058c2ecf20Sopenharmony_cistatic int corkscrew_debug = 1;
1068c2ecf20Sopenharmony_ci#endif
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#define CORKSCREW_ID 10
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/*
1118c2ecf20Sopenharmony_ci				Theory of Operation
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ciI. Board Compatibility
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciThis device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL,
1168c2ecf20Sopenharmony_ci3Com's ISA bus adapter for Fast Ethernet.  Due to the unique I/O port layout,
1178c2ecf20Sopenharmony_ciit's not practical to integrate this driver with the other EtherLink drivers.
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ciII. Board-specific settings
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ciThe Corkscrew has an EEPROM for configuration, but no special settings are
1228c2ecf20Sopenharmony_cineeded for Linux.
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ciIII. Driver operation
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ciThe 3c515 series use an interface that's very similar to the 3c900 "Boomerang"
1278c2ecf20Sopenharmony_ciPCI cards, with the bus master interface extensively modified to work with
1288c2ecf20Sopenharmony_cithe ISA bus.
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ciThe card is capable of full-bus-master transfers with separate
1318c2ecf20Sopenharmony_cilists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
1328c2ecf20Sopenharmony_ciDEC Tulip and Intel Speedo3.
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ciThis driver uses a "RX_COPYBREAK" scheme rather than a fixed intermediate
1358c2ecf20Sopenharmony_cireceive buffer.  This scheme allocates full-sized skbuffs as receive
1368c2ecf20Sopenharmony_cibuffers.  The value RX_COPYBREAK is used as the copying breakpoint: it is
1378c2ecf20Sopenharmony_cichosen to trade-off the memory wasted by passing the full-sized skbuff to
1388c2ecf20Sopenharmony_cithe queue layer for all frames vs. the copying cost of copying a frame to a
1398c2ecf20Sopenharmony_cicorrectly-sized skbuff.
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ciIIIC. Synchronization
1438c2ecf20Sopenharmony_ciThe driver runs as two independent, single-threaded flows of control.  One
1448c2ecf20Sopenharmony_ciis the send-packet routine, which enforces single-threaded use by the netif
1458c2ecf20Sopenharmony_cilayer.  The other thread is the interrupt handler, which is single
1468c2ecf20Sopenharmony_cithreaded by the hardware and other software.
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ciIV. Notes
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ciThanks to Terry Murphy of 3Com for providing documentation and a development
1518c2ecf20Sopenharmony_ciboard.
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ciThe names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com
1548c2ecf20Sopenharmony_ciproject names.  I use these names to eliminate confusion -- 3Com product
1558c2ecf20Sopenharmony_cinumbers and names are very similar and often confused.
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ciThe new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes!
1588c2ecf20Sopenharmony_ciThis driver only supports ethernet frames because of the recent MTU limit
1598c2ecf20Sopenharmony_ciof 1.5K, but the changes to support 4.5K are minimal.
1608c2ecf20Sopenharmony_ci*/
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci/* Operational definitions.
1638c2ecf20Sopenharmony_ci   These are not used by other compilation units and thus are not
1648c2ecf20Sopenharmony_ci   exported in a ".h" file.
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci   First the windows.  There are eight register windows, with the command
1678c2ecf20Sopenharmony_ci   and status registers available in each.
1688c2ecf20Sopenharmony_ci   */
1698c2ecf20Sopenharmony_ci#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
1708c2ecf20Sopenharmony_ci#define EL3_CMD 0x0e
1718c2ecf20Sopenharmony_ci#define EL3_STATUS 0x0e
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci/* The top five bits written to EL3_CMD are a command, the lower
1748c2ecf20Sopenharmony_ci   11 bits are the parameter, if applicable.
1758c2ecf20Sopenharmony_ci   Note that 11 parameters bits was fine for ethernet, but the new chips
1768c2ecf20Sopenharmony_ci   can handle FDDI length frames (~4500 octets) and now parameters count
1778c2ecf20Sopenharmony_ci   32-bit 'Dwords' rather than octets. */
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cienum corkscrew_cmd {
1808c2ecf20Sopenharmony_ci	TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11,
1818c2ecf20Sopenharmony_ci	RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11,
1828c2ecf20Sopenharmony_ci	UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2,
1838c2ecf20Sopenharmony_ci	DownUnstall = (6 << 11) + 3, RxDiscard = 8 << 11, TxEnable = 9 << 11,
1848c2ecf20Sopenharmony_ci	TxDisable = 10 << 11, TxReset = 11 << 11, FakeIntr = 12 << 11,
1858c2ecf20Sopenharmony_ci	AckIntr = 13 << 11, SetIntrEnb = 14 << 11, SetStatusEnb = 15 << 11,
1868c2ecf20Sopenharmony_ci	SetRxFilter = 16 << 11, SetRxThreshold = 17 << 11,
1878c2ecf20Sopenharmony_ci	SetTxThreshold = 18 << 11, SetTxStart = 19 << 11, StartDMAUp = 20 << 11,
1888c2ecf20Sopenharmony_ci	StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11,
1898c2ecf20Sopenharmony_ci	StatsDisable = 22 << 11, StopCoax = 23 << 11,
1908c2ecf20Sopenharmony_ci};
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/* The SetRxFilter command accepts the following classes: */
1938c2ecf20Sopenharmony_cienum RxFilter {
1948c2ecf20Sopenharmony_ci	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
1958c2ecf20Sopenharmony_ci};
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/* Bits in the general status register. */
1988c2ecf20Sopenharmony_cienum corkscrew_status {
1998c2ecf20Sopenharmony_ci	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
2008c2ecf20Sopenharmony_ci	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
2018c2ecf20Sopenharmony_ci	IntReq = 0x0040, StatsFull = 0x0080,
2028c2ecf20Sopenharmony_ci	DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10,
2038c2ecf20Sopenharmony_ci	DMAInProgress = 1 << 11,	/* DMA controller is still busy. */
2048c2ecf20Sopenharmony_ci	CmdInProgress = 1 << 12,	/* EL3_CMD is still busy. */
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/* Register window 1 offsets, the window used in normal operation.
2088c2ecf20Sopenharmony_ci   On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */
2098c2ecf20Sopenharmony_cienum Window1 {
2108c2ecf20Sopenharmony_ci	TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
2118c2ecf20Sopenharmony_ci	RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B,
2128c2ecf20Sopenharmony_ci	TxFree = 0x1C,		/* Remaining free bytes in Tx buffer. */
2138c2ecf20Sopenharmony_ci};
2148c2ecf20Sopenharmony_cienum Window0 {
2158c2ecf20Sopenharmony_ci	Wn0IRQ = 0x08,
2168c2ecf20Sopenharmony_ci#if defined(CORKSCREW)
2178c2ecf20Sopenharmony_ci	Wn0EepromCmd = 0x200A,	/* Corkscrew EEPROM command register. */
2188c2ecf20Sopenharmony_ci	Wn0EepromData = 0x200C,	/* Corkscrew EEPROM results register. */
2198c2ecf20Sopenharmony_ci#else
2208c2ecf20Sopenharmony_ci	Wn0EepromCmd = 10,	/* Window 0: EEPROM command register. */
2218c2ecf20Sopenharmony_ci	Wn0EepromData = 12,	/* Window 0: EEPROM results register. */
2228c2ecf20Sopenharmony_ci#endif
2238c2ecf20Sopenharmony_ci};
2248c2ecf20Sopenharmony_cienum Win0_EEPROM_bits {
2258c2ecf20Sopenharmony_ci	EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
2268c2ecf20Sopenharmony_ci	EEPROM_EWENB = 0x30,	/* Enable erasing/writing for 10 msec. */
2278c2ecf20Sopenharmony_ci	EEPROM_EWDIS = 0x00,	/* Disable EWENB before 10 msec timeout. */
2288c2ecf20Sopenharmony_ci};
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/* EEPROM locations. */
2318c2ecf20Sopenharmony_cienum eeprom_offset {
2328c2ecf20Sopenharmony_ci	PhysAddr01 = 0, PhysAddr23 = 1, PhysAddr45 = 2, ModelID = 3,
2338c2ecf20Sopenharmony_ci	EtherLink3ID = 7,
2348c2ecf20Sopenharmony_ci};
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cienum Window3 {			/* Window 3: MAC/config bits. */
2378c2ecf20Sopenharmony_ci	Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8,
2388c2ecf20Sopenharmony_ci};
2398c2ecf20Sopenharmony_cienum wn3_config {
2408c2ecf20Sopenharmony_ci	Ram_size = 7,
2418c2ecf20Sopenharmony_ci	Ram_width = 8,
2428c2ecf20Sopenharmony_ci	Ram_speed = 0x30,
2438c2ecf20Sopenharmony_ci	Rom_size = 0xc0,
2448c2ecf20Sopenharmony_ci	Ram_split_shift = 16,
2458c2ecf20Sopenharmony_ci	Ram_split = 3 << Ram_split_shift,
2468c2ecf20Sopenharmony_ci	Xcvr_shift = 20,
2478c2ecf20Sopenharmony_ci	Xcvr = 7 << Xcvr_shift,
2488c2ecf20Sopenharmony_ci	Autoselect = 0x1000000,
2498c2ecf20Sopenharmony_ci};
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cienum Window4 {
2528c2ecf20Sopenharmony_ci	Wn4_NetDiag = 6, Wn4_Media = 10,	/* Window 4: Xcvr/media bits. */
2538c2ecf20Sopenharmony_ci};
2548c2ecf20Sopenharmony_cienum Win4_Media_bits {
2558c2ecf20Sopenharmony_ci	Media_SQE = 0x0008,	/* Enable SQE error counting for AUI. */
2568c2ecf20Sopenharmony_ci	Media_10TP = 0x00C0,	/* Enable link beat and jabber for 10baseT. */
2578c2ecf20Sopenharmony_ci	Media_Lnk = 0x0080,	/* Enable just link beat for 100TX/100FX. */
2588c2ecf20Sopenharmony_ci	Media_LnkBeat = 0x0800,
2598c2ecf20Sopenharmony_ci};
2608c2ecf20Sopenharmony_cienum Window7 {			/* Window 7: Bus Master control. */
2618c2ecf20Sopenharmony_ci	Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci/* Boomerang-style bus master control registers.  Note ISA aliases! */
2658c2ecf20Sopenharmony_cienum MasterCtrl {
2668c2ecf20Sopenharmony_ci	PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen =
2678c2ecf20Sopenharmony_ci	    0x40c,
2688c2ecf20Sopenharmony_ci	TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418,
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci/* The Rx and Tx descriptor lists.
2728c2ecf20Sopenharmony_ci   Caution Alpha hackers: these types are 32 bits!  Note also the 8 byte
2738c2ecf20Sopenharmony_ci   alignment contraint on tx_ring[] and rx_ring[]. */
2748c2ecf20Sopenharmony_cistruct boom_rx_desc {
2758c2ecf20Sopenharmony_ci	u32 next;
2768c2ecf20Sopenharmony_ci	s32 status;
2778c2ecf20Sopenharmony_ci	u32 addr;
2788c2ecf20Sopenharmony_ci	s32 length;
2798c2ecf20Sopenharmony_ci};
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci/* Values for the Rx status entry. */
2828c2ecf20Sopenharmony_cienum rx_desc_status {
2838c2ecf20Sopenharmony_ci	RxDComplete = 0x00008000, RxDError = 0x4000,
2848c2ecf20Sopenharmony_ci	/* See boomerang_rx() for actual error bits */
2858c2ecf20Sopenharmony_ci};
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistruct boom_tx_desc {
2888c2ecf20Sopenharmony_ci	u32 next;
2898c2ecf20Sopenharmony_ci	s32 status;
2908c2ecf20Sopenharmony_ci	u32 addr;
2918c2ecf20Sopenharmony_ci	s32 length;
2928c2ecf20Sopenharmony_ci};
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistruct corkscrew_private {
2958c2ecf20Sopenharmony_ci	const char *product_name;
2968c2ecf20Sopenharmony_ci	struct list_head list;
2978c2ecf20Sopenharmony_ci	struct net_device *our_dev;
2988c2ecf20Sopenharmony_ci	/* The Rx and Tx rings are here to keep them quad-word-aligned. */
2998c2ecf20Sopenharmony_ci	struct boom_rx_desc rx_ring[RX_RING_SIZE];
3008c2ecf20Sopenharmony_ci	struct boom_tx_desc tx_ring[TX_RING_SIZE];
3018c2ecf20Sopenharmony_ci	/* The addresses of transmit- and receive-in-place skbuffs. */
3028c2ecf20Sopenharmony_ci	struct sk_buff *rx_skbuff[RX_RING_SIZE];
3038c2ecf20Sopenharmony_ci	struct sk_buff *tx_skbuff[TX_RING_SIZE];
3048c2ecf20Sopenharmony_ci	unsigned int cur_rx, cur_tx;	/* The next free ring entry */
3058c2ecf20Sopenharmony_ci	unsigned int dirty_rx, dirty_tx;/* The ring entries to be free()ed. */
3068c2ecf20Sopenharmony_ci	struct sk_buff *tx_skb;	/* Packet being eaten by bus master ctrl.  */
3078c2ecf20Sopenharmony_ci	struct timer_list timer;	/* Media selection timer. */
3088c2ecf20Sopenharmony_ci	int capabilities	;	/* Adapter capabilities word. */
3098c2ecf20Sopenharmony_ci	int options;			/* User-settable misc. driver options. */
3108c2ecf20Sopenharmony_ci	int last_rx_packets;		/* For media autoselection. */
3118c2ecf20Sopenharmony_ci	unsigned int available_media:8,	/* From Wn3_Options */
3128c2ecf20Sopenharmony_ci		media_override:3,	/* Passed-in media type. */
3138c2ecf20Sopenharmony_ci		default_media:3,	/* Read from the EEPROM. */
3148c2ecf20Sopenharmony_ci		full_duplex:1, autoselect:1, bus_master:1,	/* Vortex can only do a fragment bus-m. */
3158c2ecf20Sopenharmony_ci		full_bus_master_tx:1, full_bus_master_rx:1,	/* Boomerang  */
3168c2ecf20Sopenharmony_ci		tx_full:1;
3178c2ecf20Sopenharmony_ci	spinlock_t lock;
3188c2ecf20Sopenharmony_ci	struct device *dev;
3198c2ecf20Sopenharmony_ci};
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci/* The action to take with a media selection timer tick.
3228c2ecf20Sopenharmony_ci   Note that we deviate from the 3Com order by checking 10base2 before AUI.
3238c2ecf20Sopenharmony_ci */
3248c2ecf20Sopenharmony_cienum xcvr_types {
3258c2ecf20Sopenharmony_ci	XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
3268c2ecf20Sopenharmony_ci	XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8,
3278c2ecf20Sopenharmony_ci};
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic struct media_table {
3308c2ecf20Sopenharmony_ci	char *name;
3318c2ecf20Sopenharmony_ci	unsigned int media_bits:16,	/* Bits to set in Wn4_Media register. */
3328c2ecf20Sopenharmony_ci		mask:8,			/* The transceiver-present bit in Wn3_Config. */
3338c2ecf20Sopenharmony_ci		next:8;			/* The media type to try next. */
3348c2ecf20Sopenharmony_ci	short wait;			/* Time before we check media status. */
3358c2ecf20Sopenharmony_ci} media_tbl[] = {
3368c2ecf20Sopenharmony_ci	{ "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10 },
3378c2ecf20Sopenharmony_ci	{ "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10},
3388c2ecf20Sopenharmony_ci	{ "undefined", 0, 0x80, XCVR_10baseT, 10000},
3398c2ecf20Sopenharmony_ci	{ "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10},
3408c2ecf20Sopenharmony_ci	{ "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14 * HZ) / 10},
3418c2ecf20Sopenharmony_ci	{ "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10},
3428c2ecf20Sopenharmony_ci	{ "MII", 0, 0x40, XCVR_10baseT, 3 * HZ},
3438c2ecf20Sopenharmony_ci	{ "undefined", 0, 0x01, XCVR_10baseT, 10000},
3448c2ecf20Sopenharmony_ci	{ "Default", 0, 0xFF, XCVR_10baseT, 10000},
3458c2ecf20Sopenharmony_ci};
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci#ifdef __ISAPNP__
3488c2ecf20Sopenharmony_cistatic struct isapnp_device_id corkscrew_isapnp_adapters[] = {
3498c2ecf20Sopenharmony_ci	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
3508c2ecf20Sopenharmony_ci		ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5051),
3518c2ecf20Sopenharmony_ci		(long) "3Com Fast EtherLink ISA" },
3528c2ecf20Sopenharmony_ci	{ }	/* terminate list */
3538c2ecf20Sopenharmony_ci};
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(isapnp, corkscrew_isapnp_adapters);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic int nopnp;
3588c2ecf20Sopenharmony_ci#endif /* __ISAPNP__ */
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic struct net_device *corkscrew_scan(int unit);
3618c2ecf20Sopenharmony_cistatic int corkscrew_setup(struct net_device *dev, int ioaddr,
3628c2ecf20Sopenharmony_ci			    struct pnp_dev *idev, int card_number);
3638c2ecf20Sopenharmony_cistatic int corkscrew_open(struct net_device *dev);
3648c2ecf20Sopenharmony_cistatic void corkscrew_timer(struct timer_list *t);
3658c2ecf20Sopenharmony_cistatic netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb,
3668c2ecf20Sopenharmony_ci					struct net_device *dev);
3678c2ecf20Sopenharmony_cistatic int corkscrew_rx(struct net_device *dev);
3688c2ecf20Sopenharmony_cistatic void corkscrew_timeout(struct net_device *dev, unsigned int txqueue);
3698c2ecf20Sopenharmony_cistatic int boomerang_rx(struct net_device *dev);
3708c2ecf20Sopenharmony_cistatic irqreturn_t corkscrew_interrupt(int irq, void *dev_id);
3718c2ecf20Sopenharmony_cistatic int corkscrew_close(struct net_device *dev);
3728c2ecf20Sopenharmony_cistatic void update_stats(int addr, struct net_device *dev);
3738c2ecf20Sopenharmony_cistatic struct net_device_stats *corkscrew_get_stats(struct net_device *dev);
3748c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev);
3758c2ecf20Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci/*
3798c2ecf20Sopenharmony_ci   Unfortunately maximizing the shared code between the integrated and
3808c2ecf20Sopenharmony_ci   module version of the driver results in a complicated set of initialization
3818c2ecf20Sopenharmony_ci   procedures.
3828c2ecf20Sopenharmony_ci   init_module() -- modules /  tc59x_init()  -- built-in
3838c2ecf20Sopenharmony_ci		The wrappers for corkscrew_scan()
3848c2ecf20Sopenharmony_ci   corkscrew_scan()  		 The common routine that scans for PCI and EISA cards
3858c2ecf20Sopenharmony_ci   corkscrew_found_device() Allocate a device structure when we find a card.
3868c2ecf20Sopenharmony_ci					Different versions exist for modules and built-in.
3878c2ecf20Sopenharmony_ci   corkscrew_probe1()		Fill in the device structure -- this is separated
3888c2ecf20Sopenharmony_ci					so that the modules code can put it in dev->init.
3898c2ecf20Sopenharmony_ci*/
3908c2ecf20Sopenharmony_ci/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
3918c2ecf20Sopenharmony_ci/* Note: this is the only limit on the number of cards supported!! */
3928c2ecf20Sopenharmony_cistatic int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, };
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci#ifdef MODULE
3958c2ecf20Sopenharmony_cistatic int debug = -1;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cimodule_param(debug, int, 0);
3988c2ecf20Sopenharmony_cimodule_param_array(options, int, NULL, 0);
3998c2ecf20Sopenharmony_cimodule_param(rx_copybreak, int, 0);
4008c2ecf20Sopenharmony_cimodule_param(max_interrupt_work, int, 0);
4018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "3c515 debug level (0-6)");
4028c2ecf20Sopenharmony_ciMODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering");
4038c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames");
4048c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt");
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci/* A list of all installed Vortex devices, for removing the driver module. */
4078c2ecf20Sopenharmony_ci/* we will need locking (and refcounting) if we ever use it for more */
4088c2ecf20Sopenharmony_cistatic LIST_HEAD(root_corkscrew_dev);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ciint init_module(void)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	int found = 0;
4138c2ecf20Sopenharmony_ci	if (debug >= 0)
4148c2ecf20Sopenharmony_ci		corkscrew_debug = debug;
4158c2ecf20Sopenharmony_ci	while (corkscrew_scan(-1))
4168c2ecf20Sopenharmony_ci		found++;
4178c2ecf20Sopenharmony_ci	return found ? 0 : -ENODEV;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci#else
4218c2ecf20Sopenharmony_cistruct net_device *tc515_probe(int unit)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct net_device *dev = corkscrew_scan(unit);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (!dev)
4268c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return dev;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci#endif				/* not MODULE */
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic int check_device(unsigned ioaddr)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	int timer;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515"))
4378c2ecf20Sopenharmony_ci		return 0;
4388c2ecf20Sopenharmony_ci	/* Check the resource configuration for a matching ioaddr. */
4398c2ecf20Sopenharmony_ci	if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) {
4408c2ecf20Sopenharmony_ci		release_region(ioaddr, CORKSCREW_TOTAL_SIZE);
4418c2ecf20Sopenharmony_ci		return 0;
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci	/* Verify by reading the device ID from the EEPROM. */
4448c2ecf20Sopenharmony_ci	outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd);
4458c2ecf20Sopenharmony_ci	/* Pause for at least 162 us. for the read to take place. */
4468c2ecf20Sopenharmony_ci	for (timer = 4; timer >= 0; timer--) {
4478c2ecf20Sopenharmony_ci		udelay(162);
4488c2ecf20Sopenharmony_ci		if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
4498c2ecf20Sopenharmony_ci			break;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci	if (inw(ioaddr + Wn0EepromData) != 0x6d50) {
4528c2ecf20Sopenharmony_ci		release_region(ioaddr, CORKSCREW_TOTAL_SIZE);
4538c2ecf20Sopenharmony_ci		return 0;
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci	return 1;
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic void cleanup_card(struct net_device *dev)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct corkscrew_private *vp = netdev_priv(dev);
4618c2ecf20Sopenharmony_ci	list_del_init(&vp->list);
4628c2ecf20Sopenharmony_ci	if (dev->dma)
4638c2ecf20Sopenharmony_ci		free_dma(dev->dma);
4648c2ecf20Sopenharmony_ci	outw(TotalReset, dev->base_addr + EL3_CMD);
4658c2ecf20Sopenharmony_ci	release_region(dev->base_addr, CORKSCREW_TOTAL_SIZE);
4668c2ecf20Sopenharmony_ci	if (vp->dev)
4678c2ecf20Sopenharmony_ci		pnp_device_detach(to_pnp_dev(vp->dev));
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic struct net_device *corkscrew_scan(int unit)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	struct net_device *dev;
4738c2ecf20Sopenharmony_ci	static int cards_found = 0;
4748c2ecf20Sopenharmony_ci	static int ioaddr;
4758c2ecf20Sopenharmony_ci	int err;
4768c2ecf20Sopenharmony_ci#ifdef __ISAPNP__
4778c2ecf20Sopenharmony_ci	short i;
4788c2ecf20Sopenharmony_ci	static int pnp_cards;
4798c2ecf20Sopenharmony_ci#endif
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct corkscrew_private));
4828c2ecf20Sopenharmony_ci	if (!dev)
4838c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (unit >= 0) {
4868c2ecf20Sopenharmony_ci		sprintf(dev->name, "eth%d", unit);
4878c2ecf20Sopenharmony_ci		netdev_boot_setup_check(dev);
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci#ifdef __ISAPNP__
4918c2ecf20Sopenharmony_ci	if(nopnp == 1)
4928c2ecf20Sopenharmony_ci		goto no_pnp;
4938c2ecf20Sopenharmony_ci	for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) {
4948c2ecf20Sopenharmony_ci		struct pnp_dev *idev = NULL;
4958c2ecf20Sopenharmony_ci		int irq;
4968c2ecf20Sopenharmony_ci		while((idev = pnp_find_dev(NULL,
4978c2ecf20Sopenharmony_ci					   corkscrew_isapnp_adapters[i].vendor,
4988c2ecf20Sopenharmony_ci					   corkscrew_isapnp_adapters[i].function,
4998c2ecf20Sopenharmony_ci					   idev))) {
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci			if (pnp_device_attach(idev) < 0)
5028c2ecf20Sopenharmony_ci				continue;
5038c2ecf20Sopenharmony_ci			if (pnp_activate_dev(idev) < 0) {
5048c2ecf20Sopenharmony_ci				pr_warn("pnp activate failed (out of resources?)\n");
5058c2ecf20Sopenharmony_ci				pnp_device_detach(idev);
5068c2ecf20Sopenharmony_ci				continue;
5078c2ecf20Sopenharmony_ci			}
5088c2ecf20Sopenharmony_ci			if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) {
5098c2ecf20Sopenharmony_ci				pnp_device_detach(idev);
5108c2ecf20Sopenharmony_ci				continue;
5118c2ecf20Sopenharmony_ci			}
5128c2ecf20Sopenharmony_ci			ioaddr = pnp_port_start(idev, 0);
5138c2ecf20Sopenharmony_ci			irq = pnp_irq(idev, 0);
5148c2ecf20Sopenharmony_ci			if (!check_device(ioaddr)) {
5158c2ecf20Sopenharmony_ci				pnp_device_detach(idev);
5168c2ecf20Sopenharmony_ci				continue;
5178c2ecf20Sopenharmony_ci			}
5188c2ecf20Sopenharmony_ci			if(corkscrew_debug)
5198c2ecf20Sopenharmony_ci				pr_debug("ISAPNP reports %s at i/o 0x%x, irq %d\n",
5208c2ecf20Sopenharmony_ci					(char*) corkscrew_isapnp_adapters[i].driver_data, ioaddr, irq);
5218c2ecf20Sopenharmony_ci			pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
5228c2ecf20Sopenharmony_ci		     		inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
5238c2ecf20Sopenharmony_ci			/* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */
5248c2ecf20Sopenharmony_ci			SET_NETDEV_DEV(dev, &idev->dev);
5258c2ecf20Sopenharmony_ci			pnp_cards++;
5268c2ecf20Sopenharmony_ci			err = corkscrew_setup(dev, ioaddr, idev, cards_found++);
5278c2ecf20Sopenharmony_ci			if (!err)
5288c2ecf20Sopenharmony_ci				return dev;
5298c2ecf20Sopenharmony_ci			cleanup_card(dev);
5308c2ecf20Sopenharmony_ci		}
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_cino_pnp:
5338c2ecf20Sopenharmony_ci#endif /* __ISAPNP__ */
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	/* Check all locations on the ISA bus -- evil! */
5368c2ecf20Sopenharmony_ci	for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) {
5378c2ecf20Sopenharmony_ci		if (!check_device(ioaddr))
5388c2ecf20Sopenharmony_ci			continue;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci		pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
5418c2ecf20Sopenharmony_ci		     inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
5428c2ecf20Sopenharmony_ci		err = corkscrew_setup(dev, ioaddr, NULL, cards_found++);
5438c2ecf20Sopenharmony_ci		if (!err)
5448c2ecf20Sopenharmony_ci			return dev;
5458c2ecf20Sopenharmony_ci		cleanup_card(dev);
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci	free_netdev(dev);
5488c2ecf20Sopenharmony_ci	return NULL;
5498c2ecf20Sopenharmony_ci}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic const struct net_device_ops netdev_ops = {
5538c2ecf20Sopenharmony_ci	.ndo_open		= corkscrew_open,
5548c2ecf20Sopenharmony_ci	.ndo_stop		= corkscrew_close,
5558c2ecf20Sopenharmony_ci	.ndo_start_xmit		= corkscrew_start_xmit,
5568c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= corkscrew_timeout,
5578c2ecf20Sopenharmony_ci	.ndo_get_stats		= corkscrew_get_stats,
5588c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= set_rx_mode,
5598c2ecf20Sopenharmony_ci	.ndo_set_mac_address 	= eth_mac_addr,
5608c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
5618c2ecf20Sopenharmony_ci};
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic int corkscrew_setup(struct net_device *dev, int ioaddr,
5658c2ecf20Sopenharmony_ci			    struct pnp_dev *idev, int card_number)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	struct corkscrew_private *vp = netdev_priv(dev);
5688c2ecf20Sopenharmony_ci	unsigned int eeprom[0x40], checksum = 0;	/* EEPROM contents */
5698c2ecf20Sopenharmony_ci	int i;
5708c2ecf20Sopenharmony_ci	int irq;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci#ifdef __ISAPNP__
5738c2ecf20Sopenharmony_ci	if (idev) {
5748c2ecf20Sopenharmony_ci		irq = pnp_irq(idev, 0);
5758c2ecf20Sopenharmony_ci		vp->dev = &idev->dev;
5768c2ecf20Sopenharmony_ci	} else {
5778c2ecf20Sopenharmony_ci		irq = inw(ioaddr + 0x2002) & 15;
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci#else
5808c2ecf20Sopenharmony_ci	irq = inw(ioaddr + 0x2002) & 15;
5818c2ecf20Sopenharmony_ci#endif
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	dev->base_addr = ioaddr;
5848c2ecf20Sopenharmony_ci	dev->irq = irq;
5858c2ecf20Sopenharmony_ci	dev->dma = inw(ioaddr + 0x2000) & 7;
5868c2ecf20Sopenharmony_ci	vp->product_name = "3c515";
5878c2ecf20Sopenharmony_ci	vp->options = dev->mem_start;
5888c2ecf20Sopenharmony_ci	vp->our_dev = dev;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (!vp->options) {
5918c2ecf20Sopenharmony_ci		 if (card_number >= MAX_UNITS)
5928c2ecf20Sopenharmony_ci			vp->options = -1;
5938c2ecf20Sopenharmony_ci		else
5948c2ecf20Sopenharmony_ci			vp->options = options[card_number];
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	if (vp->options >= 0) {
5988c2ecf20Sopenharmony_ci		vp->media_override = vp->options & 7;
5998c2ecf20Sopenharmony_ci		if (vp->media_override == 2)
6008c2ecf20Sopenharmony_ci			vp->media_override = 0;
6018c2ecf20Sopenharmony_ci		vp->full_duplex = (vp->options & 8) ? 1 : 0;
6028c2ecf20Sopenharmony_ci		vp->bus_master = (vp->options & 16) ? 1 : 0;
6038c2ecf20Sopenharmony_ci	} else {
6048c2ecf20Sopenharmony_ci		vp->media_override = 7;
6058c2ecf20Sopenharmony_ci		vp->full_duplex = 0;
6068c2ecf20Sopenharmony_ci		vp->bus_master = 0;
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci#ifdef MODULE
6098c2ecf20Sopenharmony_ci	list_add(&vp->list, &root_corkscrew_dev);
6108c2ecf20Sopenharmony_ci#endif
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	pr_info("%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	spin_lock_init(&vp->lock);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	timer_setup(&vp->timer, corkscrew_timer, 0);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	/* Read the station address from the EEPROM. */
6198c2ecf20Sopenharmony_ci	EL3WINDOW(0);
6208c2ecf20Sopenharmony_ci	for (i = 0; i < 0x18; i++) {
6218c2ecf20Sopenharmony_ci		__be16 *phys_addr = (__be16 *) dev->dev_addr;
6228c2ecf20Sopenharmony_ci		int timer;
6238c2ecf20Sopenharmony_ci		outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
6248c2ecf20Sopenharmony_ci		/* Pause for at least 162 us. for the read to take place. */
6258c2ecf20Sopenharmony_ci		for (timer = 4; timer >= 0; timer--) {
6268c2ecf20Sopenharmony_ci			udelay(162);
6278c2ecf20Sopenharmony_ci			if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
6288c2ecf20Sopenharmony_ci				break;
6298c2ecf20Sopenharmony_ci		}
6308c2ecf20Sopenharmony_ci		eeprom[i] = inw(ioaddr + Wn0EepromData);
6318c2ecf20Sopenharmony_ci		checksum ^= eeprom[i];
6328c2ecf20Sopenharmony_ci		if (i < 3)
6338c2ecf20Sopenharmony_ci			phys_addr[i] = htons(eeprom[i]);
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci	checksum = (checksum ^ (checksum >> 8)) & 0xff;
6368c2ecf20Sopenharmony_ci	if (checksum != 0x00)
6378c2ecf20Sopenharmony_ci		pr_cont(" ***INVALID CHECKSUM %4.4x*** ", checksum);
6388c2ecf20Sopenharmony_ci	pr_cont(" %pM", dev->dev_addr);
6398c2ecf20Sopenharmony_ci	if (eeprom[16] == 0x11c7) {	/* Corkscrew */
6408c2ecf20Sopenharmony_ci		if (request_dma(dev->dma, "3c515")) {
6418c2ecf20Sopenharmony_ci			pr_cont(", DMA %d allocation failed", dev->dma);
6428c2ecf20Sopenharmony_ci			dev->dma = 0;
6438c2ecf20Sopenharmony_ci		} else
6448c2ecf20Sopenharmony_ci			pr_cont(", DMA %d", dev->dma);
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci	pr_cont(", IRQ %d\n", dev->irq);
6478c2ecf20Sopenharmony_ci	/* Tell them about an invalid IRQ. */
6488c2ecf20Sopenharmony_ci	if (corkscrew_debug && (dev->irq <= 0 || dev->irq > 15))
6498c2ecf20Sopenharmony_ci		pr_warn(" *** Warning: this IRQ is unlikely to work! ***\n");
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	{
6528c2ecf20Sopenharmony_ci		static const char * const ram_split[] = {
6538c2ecf20Sopenharmony_ci			"5:3", "3:1", "1:1", "3:5"
6548c2ecf20Sopenharmony_ci		};
6558c2ecf20Sopenharmony_ci		__u32 config;
6568c2ecf20Sopenharmony_ci		EL3WINDOW(3);
6578c2ecf20Sopenharmony_ci		vp->available_media = inw(ioaddr + Wn3_Options);
6588c2ecf20Sopenharmony_ci		config = inl(ioaddr + Wn3_Config);
6598c2ecf20Sopenharmony_ci		if (corkscrew_debug > 1)
6608c2ecf20Sopenharmony_ci			pr_info("  Internal config register is %4.4x, transceivers %#x.\n",
6618c2ecf20Sopenharmony_ci				config, inw(ioaddr + Wn3_Options));
6628c2ecf20Sopenharmony_ci		pr_info("  %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
6638c2ecf20Sopenharmony_ci			8 << config & Ram_size,
6648c2ecf20Sopenharmony_ci			config & Ram_width ? "word" : "byte",
6658c2ecf20Sopenharmony_ci			ram_split[(config & Ram_split) >> Ram_split_shift],
6668c2ecf20Sopenharmony_ci			config & Autoselect ? "autoselect/" : "",
6678c2ecf20Sopenharmony_ci			media_tbl[(config & Xcvr) >> Xcvr_shift].name);
6688c2ecf20Sopenharmony_ci		vp->default_media = (config & Xcvr) >> Xcvr_shift;
6698c2ecf20Sopenharmony_ci		vp->autoselect = config & Autoselect ? 1 : 0;
6708c2ecf20Sopenharmony_ci		dev->if_port = vp->default_media;
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci	if (vp->media_override != 7) {
6738c2ecf20Sopenharmony_ci		pr_info("  Media override to transceiver type %d (%s).\n",
6748c2ecf20Sopenharmony_ci		       vp->media_override,
6758c2ecf20Sopenharmony_ci		       media_tbl[vp->media_override].name);
6768c2ecf20Sopenharmony_ci		dev->if_port = vp->media_override;
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	vp->capabilities = eeprom[16];
6808c2ecf20Sopenharmony_ci	vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0;
6818c2ecf20Sopenharmony_ci	/* Rx is broken at 10mbps, so we always disable it. */
6828c2ecf20Sopenharmony_ci	/* vp->full_bus_master_rx = 0; */
6838c2ecf20Sopenharmony_ci	vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* The 3c51x-specific entries in the device structure. */
6868c2ecf20Sopenharmony_ci	dev->netdev_ops = &netdev_ops;
6878c2ecf20Sopenharmony_ci	dev->watchdog_timeo = (400 * HZ) / 1000;
6888c2ecf20Sopenharmony_ci	dev->ethtool_ops = &netdev_ethtool_ops;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	return register_netdev(dev);
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic int corkscrew_open(struct net_device *dev)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	int ioaddr = dev->base_addr;
6978c2ecf20Sopenharmony_ci	struct corkscrew_private *vp = netdev_priv(dev);
6988c2ecf20Sopenharmony_ci	bool armtimer = false;
6998c2ecf20Sopenharmony_ci	__u32 config;
7008c2ecf20Sopenharmony_ci	int i;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	/* Before initializing select the active media port. */
7038c2ecf20Sopenharmony_ci	EL3WINDOW(3);
7048c2ecf20Sopenharmony_ci	if (vp->full_duplex)
7058c2ecf20Sopenharmony_ci		outb(0x20, ioaddr + Wn3_MAC_Ctrl);	/* Set the full-duplex bit. */
7068c2ecf20Sopenharmony_ci	config = inl(ioaddr + Wn3_Config);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	if (vp->media_override != 7) {
7098c2ecf20Sopenharmony_ci		if (corkscrew_debug > 1)
7108c2ecf20Sopenharmony_ci			pr_info("%s: Media override to transceiver %d (%s).\n",
7118c2ecf20Sopenharmony_ci				dev->name, vp->media_override,
7128c2ecf20Sopenharmony_ci				media_tbl[vp->media_override].name);
7138c2ecf20Sopenharmony_ci		dev->if_port = vp->media_override;
7148c2ecf20Sopenharmony_ci	} else if (vp->autoselect) {
7158c2ecf20Sopenharmony_ci		/* Find first available media type, starting with 100baseTx. */
7168c2ecf20Sopenharmony_ci		dev->if_port = 4;
7178c2ecf20Sopenharmony_ci		while (!(vp->available_media & media_tbl[dev->if_port].mask))
7188c2ecf20Sopenharmony_ci			dev->if_port = media_tbl[dev->if_port].next;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci		if (corkscrew_debug > 1)
7218c2ecf20Sopenharmony_ci			pr_debug("%s: Initial media type %s.\n",
7228c2ecf20Sopenharmony_ci			       dev->name, media_tbl[dev->if_port].name);
7238c2ecf20Sopenharmony_ci		armtimer = true;
7248c2ecf20Sopenharmony_ci	} else
7258c2ecf20Sopenharmony_ci		dev->if_port = vp->default_media;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);
7288c2ecf20Sopenharmony_ci	outl(config, ioaddr + Wn3_Config);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	if (corkscrew_debug > 1) {
7318c2ecf20Sopenharmony_ci		pr_debug("%s: corkscrew_open() InternalConfig %8.8x.\n",
7328c2ecf20Sopenharmony_ci		       dev->name, config);
7338c2ecf20Sopenharmony_ci	}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	outw(TxReset, ioaddr + EL3_CMD);
7368c2ecf20Sopenharmony_ci	for (i = 20; i >= 0; i--)
7378c2ecf20Sopenharmony_ci		if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
7388c2ecf20Sopenharmony_ci			break;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	outw(RxReset, ioaddr + EL3_CMD);
7418c2ecf20Sopenharmony_ci	/* Wait a few ticks for the RxReset command to complete. */
7428c2ecf20Sopenharmony_ci	for (i = 20; i >= 0; i--)
7438c2ecf20Sopenharmony_ci		if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
7448c2ecf20Sopenharmony_ci			break;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	/* Use the now-standard shared IRQ implementation. */
7498c2ecf20Sopenharmony_ci	if (vp->capabilities == 0x11c7) {
7508c2ecf20Sopenharmony_ci		/* Corkscrew: Cannot share ISA resources. */
7518c2ecf20Sopenharmony_ci		if (dev->irq == 0 ||
7528c2ecf20Sopenharmony_ci		    dev->dma == 0 ||
7538c2ecf20Sopenharmony_ci		    request_irq(dev->irq, corkscrew_interrupt, 0,
7548c2ecf20Sopenharmony_ci				vp->product_name, dev))
7558c2ecf20Sopenharmony_ci			return -EAGAIN;
7568c2ecf20Sopenharmony_ci		enable_dma(dev->dma);
7578c2ecf20Sopenharmony_ci		set_dma_mode(dev->dma, DMA_MODE_CASCADE);
7588c2ecf20Sopenharmony_ci	} else if (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED,
7598c2ecf20Sopenharmony_ci			       vp->product_name, dev)) {
7608c2ecf20Sopenharmony_ci		return -EAGAIN;
7618c2ecf20Sopenharmony_ci	}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	if (armtimer)
7648c2ecf20Sopenharmony_ci		mod_timer(&vp->timer, jiffies + media_tbl[dev->if_port].wait);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	if (corkscrew_debug > 1) {
7678c2ecf20Sopenharmony_ci		EL3WINDOW(4);
7688c2ecf20Sopenharmony_ci		pr_debug("%s: corkscrew_open() irq %d media status %4.4x.\n",
7698c2ecf20Sopenharmony_ci		       dev->name, dev->irq, inw(ioaddr + Wn4_Media));
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	/* Set the station address and mask in window 2 each time opened. */
7738c2ecf20Sopenharmony_ci	EL3WINDOW(2);
7748c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++)
7758c2ecf20Sopenharmony_ci		outb(dev->dev_addr[i], ioaddr + i);
7768c2ecf20Sopenharmony_ci	for (; i < 12; i += 2)
7778c2ecf20Sopenharmony_ci		outw(0, ioaddr + i);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	if (dev->if_port == 3)
7808c2ecf20Sopenharmony_ci		/* Start the thinnet transceiver. We should really wait 50ms... */
7818c2ecf20Sopenharmony_ci		outw(StartCoax, ioaddr + EL3_CMD);
7828c2ecf20Sopenharmony_ci	EL3WINDOW(4);
7838c2ecf20Sopenharmony_ci	outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) |
7848c2ecf20Sopenharmony_ci	     media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	/* Switch to the stats window, and clear all stats by reading. */
7878c2ecf20Sopenharmony_ci	outw(StatsDisable, ioaddr + EL3_CMD);
7888c2ecf20Sopenharmony_ci	EL3WINDOW(6);
7898c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++)
7908c2ecf20Sopenharmony_ci		inb(ioaddr + i);
7918c2ecf20Sopenharmony_ci	inw(ioaddr + 10);
7928c2ecf20Sopenharmony_ci	inw(ioaddr + 12);
7938c2ecf20Sopenharmony_ci	/* New: On the Vortex we must also clear the BadSSD counter. */
7948c2ecf20Sopenharmony_ci	EL3WINDOW(4);
7958c2ecf20Sopenharmony_ci	inb(ioaddr + 12);
7968c2ecf20Sopenharmony_ci	/* ..and on the Boomerang we enable the extra statistics bits. */
7978c2ecf20Sopenharmony_ci	outw(0x0040, ioaddr + Wn4_NetDiag);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	/* Switch to register set 7 for normal use. */
8008c2ecf20Sopenharmony_ci	EL3WINDOW(7);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	if (vp->full_bus_master_rx) {	/* Boomerang bus master. */
8038c2ecf20Sopenharmony_ci		vp->cur_rx = vp->dirty_rx = 0;
8048c2ecf20Sopenharmony_ci		if (corkscrew_debug > 2)
8058c2ecf20Sopenharmony_ci			pr_debug("%s:  Filling in the Rx ring.\n", dev->name);
8068c2ecf20Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++) {
8078c2ecf20Sopenharmony_ci			struct sk_buff *skb;
8088c2ecf20Sopenharmony_ci			if (i < (RX_RING_SIZE - 1))
8098c2ecf20Sopenharmony_ci				vp->rx_ring[i].next =
8108c2ecf20Sopenharmony_ci				    isa_virt_to_bus(&vp->rx_ring[i + 1]);
8118c2ecf20Sopenharmony_ci			else
8128c2ecf20Sopenharmony_ci				vp->rx_ring[i].next = 0;
8138c2ecf20Sopenharmony_ci			vp->rx_ring[i].status = 0;	/* Clear complete bit. */
8148c2ecf20Sopenharmony_ci			vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000;
8158c2ecf20Sopenharmony_ci			skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
8168c2ecf20Sopenharmony_ci			vp->rx_skbuff[i] = skb;
8178c2ecf20Sopenharmony_ci			if (skb == NULL)
8188c2ecf20Sopenharmony_ci				break;	/* Bad news!  */
8198c2ecf20Sopenharmony_ci			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
8208c2ecf20Sopenharmony_ci			vp->rx_ring[i].addr = isa_virt_to_bus(skb->data);
8218c2ecf20Sopenharmony_ci		}
8228c2ecf20Sopenharmony_ci		if (i != 0)
8238c2ecf20Sopenharmony_ci			vp->rx_ring[i - 1].next =
8248c2ecf20Sopenharmony_ci				isa_virt_to_bus(&vp->rx_ring[0]);	/* Wrap the ring. */
8258c2ecf20Sopenharmony_ci		outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci	if (vp->full_bus_master_tx) {	/* Boomerang bus master Tx. */
8288c2ecf20Sopenharmony_ci		vp->cur_tx = vp->dirty_tx = 0;
8298c2ecf20Sopenharmony_ci		outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold);	/* Room for a packet. */
8308c2ecf20Sopenharmony_ci		/* Clear the Tx ring. */
8318c2ecf20Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++)
8328c2ecf20Sopenharmony_ci			vp->tx_skbuff[i] = NULL;
8338c2ecf20Sopenharmony_ci		outl(0, ioaddr + DownListPtr);
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci	/* Set receiver mode: presumably accept b-case and phys addr only. */
8368c2ecf20Sopenharmony_ci	set_rx_mode(dev);
8378c2ecf20Sopenharmony_ci	outw(StatsEnable, ioaddr + EL3_CMD);	/* Turn on statistics. */
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	netif_start_queue(dev);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	outw(RxEnable, ioaddr + EL3_CMD);	/* Enable the receiver. */
8428c2ecf20Sopenharmony_ci	outw(TxEnable, ioaddr + EL3_CMD);	/* Enable transmitter. */
8438c2ecf20Sopenharmony_ci	/* Allow status bits to be seen. */
8448c2ecf20Sopenharmony_ci	outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull |
8458c2ecf20Sopenharmony_ci	     (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
8468c2ecf20Sopenharmony_ci	     (vp->full_bus_master_rx ? UpComplete : RxComplete) |
8478c2ecf20Sopenharmony_ci	     (vp->bus_master ? DMADone : 0), ioaddr + EL3_CMD);
8488c2ecf20Sopenharmony_ci	/* Ack all pending events, and set active indicator mask. */
8498c2ecf20Sopenharmony_ci	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
8508c2ecf20Sopenharmony_ci	     ioaddr + EL3_CMD);
8518c2ecf20Sopenharmony_ci	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
8528c2ecf20Sopenharmony_ci	     | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
8538c2ecf20Sopenharmony_ci	     ioaddr + EL3_CMD);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	return 0;
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_cistatic void corkscrew_timer(struct timer_list *t)
8598c2ecf20Sopenharmony_ci{
8608c2ecf20Sopenharmony_ci#ifdef AUTOMEDIA
8618c2ecf20Sopenharmony_ci	struct corkscrew_private *vp = from_timer(vp, t, timer);
8628c2ecf20Sopenharmony_ci	struct net_device *dev = vp->our_dev;
8638c2ecf20Sopenharmony_ci	int ioaddr = dev->base_addr;
8648c2ecf20Sopenharmony_ci	unsigned long flags;
8658c2ecf20Sopenharmony_ci	int ok = 0;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	if (corkscrew_debug > 1)
8688c2ecf20Sopenharmony_ci		pr_debug("%s: Media selection timer tick happened, %s.\n",
8698c2ecf20Sopenharmony_ci		       dev->name, media_tbl[dev->if_port].name);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vp->lock, flags);
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	{
8748c2ecf20Sopenharmony_ci		int old_window = inw(ioaddr + EL3_CMD) >> 13;
8758c2ecf20Sopenharmony_ci		int media_status;
8768c2ecf20Sopenharmony_ci		EL3WINDOW(4);
8778c2ecf20Sopenharmony_ci		media_status = inw(ioaddr + Wn4_Media);
8788c2ecf20Sopenharmony_ci		switch (dev->if_port) {
8798c2ecf20Sopenharmony_ci		case 0:
8808c2ecf20Sopenharmony_ci		case 4:
8818c2ecf20Sopenharmony_ci		case 5:	/* 10baseT, 100baseTX, 100baseFX  */
8828c2ecf20Sopenharmony_ci			if (media_status & Media_LnkBeat) {
8838c2ecf20Sopenharmony_ci				ok = 1;
8848c2ecf20Sopenharmony_ci				if (corkscrew_debug > 1)
8858c2ecf20Sopenharmony_ci					pr_debug("%s: Media %s has link beat, %x.\n",
8868c2ecf20Sopenharmony_ci						dev->name,
8878c2ecf20Sopenharmony_ci						media_tbl[dev->if_port].name,
8888c2ecf20Sopenharmony_ci						media_status);
8898c2ecf20Sopenharmony_ci			} else if (corkscrew_debug > 1)
8908c2ecf20Sopenharmony_ci				pr_debug("%s: Media %s is has no link beat, %x.\n",
8918c2ecf20Sopenharmony_ci					dev->name,
8928c2ecf20Sopenharmony_ci					media_tbl[dev->if_port].name,
8938c2ecf20Sopenharmony_ci					media_status);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci			break;
8968c2ecf20Sopenharmony_ci		default:	/* Other media types handled by Tx timeouts. */
8978c2ecf20Sopenharmony_ci			if (corkscrew_debug > 1)
8988c2ecf20Sopenharmony_ci				pr_debug("%s: Media %s is has no indication, %x.\n",
8998c2ecf20Sopenharmony_ci					dev->name,
9008c2ecf20Sopenharmony_ci					media_tbl[dev->if_port].name,
9018c2ecf20Sopenharmony_ci					media_status);
9028c2ecf20Sopenharmony_ci			ok = 1;
9038c2ecf20Sopenharmony_ci		}
9048c2ecf20Sopenharmony_ci		if (!ok) {
9058c2ecf20Sopenharmony_ci			__u32 config;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci			do {
9088c2ecf20Sopenharmony_ci				dev->if_port =
9098c2ecf20Sopenharmony_ci				    media_tbl[dev->if_port].next;
9108c2ecf20Sopenharmony_ci			}
9118c2ecf20Sopenharmony_ci			while (!(vp->available_media & media_tbl[dev->if_port].mask));
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci			if (dev->if_port == 8) {	/* Go back to default. */
9148c2ecf20Sopenharmony_ci				dev->if_port = vp->default_media;
9158c2ecf20Sopenharmony_ci				if (corkscrew_debug > 1)
9168c2ecf20Sopenharmony_ci					pr_debug("%s: Media selection failing, using default %s port.\n",
9178c2ecf20Sopenharmony_ci						dev->name,
9188c2ecf20Sopenharmony_ci						media_tbl[dev->if_port].name);
9198c2ecf20Sopenharmony_ci			} else {
9208c2ecf20Sopenharmony_ci				if (corkscrew_debug > 1)
9218c2ecf20Sopenharmony_ci					pr_debug("%s: Media selection failed, now trying %s port.\n",
9228c2ecf20Sopenharmony_ci						dev->name,
9238c2ecf20Sopenharmony_ci						media_tbl[dev->if_port].name);
9248c2ecf20Sopenharmony_ci				vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
9258c2ecf20Sopenharmony_ci				add_timer(&vp->timer);
9268c2ecf20Sopenharmony_ci			}
9278c2ecf20Sopenharmony_ci			outw((media_status & ~(Media_10TP | Media_SQE)) |
9288c2ecf20Sopenharmony_ci			     media_tbl[dev->if_port].media_bits,
9298c2ecf20Sopenharmony_ci			     ioaddr + Wn4_Media);
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci			EL3WINDOW(3);
9328c2ecf20Sopenharmony_ci			config = inl(ioaddr + Wn3_Config);
9338c2ecf20Sopenharmony_ci			config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);
9348c2ecf20Sopenharmony_ci			outl(config, ioaddr + Wn3_Config);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci			outw(dev->if_port == 3 ? StartCoax : StopCoax,
9378c2ecf20Sopenharmony_ci			     ioaddr + EL3_CMD);
9388c2ecf20Sopenharmony_ci		}
9398c2ecf20Sopenharmony_ci		EL3WINDOW(old_window);
9408c2ecf20Sopenharmony_ci	}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vp->lock, flags);
9438c2ecf20Sopenharmony_ci	if (corkscrew_debug > 1)
9448c2ecf20Sopenharmony_ci		pr_debug("%s: Media selection timer finished, %s.\n",
9458c2ecf20Sopenharmony_ci		       dev->name, media_tbl[dev->if_port].name);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci#endif				/* AUTOMEDIA */
9488c2ecf20Sopenharmony_ci}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_cistatic void corkscrew_timeout(struct net_device *dev, unsigned int txqueue)
9518c2ecf20Sopenharmony_ci{
9528c2ecf20Sopenharmony_ci	int i;
9538c2ecf20Sopenharmony_ci	struct corkscrew_private *vp = netdev_priv(dev);
9548c2ecf20Sopenharmony_ci	int ioaddr = dev->base_addr;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	pr_warn("%s: transmit timed out, tx_status %2.2x status %4.4x\n",
9578c2ecf20Sopenharmony_ci		dev->name, inb(ioaddr + TxStatus),
9588c2ecf20Sopenharmony_ci		inw(ioaddr + EL3_STATUS));
9598c2ecf20Sopenharmony_ci	/* Slight code bloat to be user friendly. */
9608c2ecf20Sopenharmony_ci	if ((inb(ioaddr + TxStatus) & 0x88) == 0x88)
9618c2ecf20Sopenharmony_ci		pr_warn("%s: Transmitter encountered 16 collisions -- network cable problem?\n",
9628c2ecf20Sopenharmony_ci			dev->name);
9638c2ecf20Sopenharmony_ci#ifndef final_version
9648c2ecf20Sopenharmony_ci	pr_debug("  Flags; bus-master %d, full %d; dirty %d current %d.\n",
9658c2ecf20Sopenharmony_ci	       vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx,
9668c2ecf20Sopenharmony_ci	       vp->cur_tx);
9678c2ecf20Sopenharmony_ci	pr_debug("  Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr),
9688c2ecf20Sopenharmony_ci	       &vp->tx_ring[0]);
9698c2ecf20Sopenharmony_ci	for (i = 0; i < TX_RING_SIZE; i++) {
9708c2ecf20Sopenharmony_ci		pr_debug("  %d: %p  length %8.8x status %8.8x\n", i,
9718c2ecf20Sopenharmony_ci		       &vp->tx_ring[i],
9728c2ecf20Sopenharmony_ci		       vp->tx_ring[i].length, vp->tx_ring[i].status);
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci#endif
9758c2ecf20Sopenharmony_ci	/* Issue TX_RESET and TX_START commands. */
9768c2ecf20Sopenharmony_ci	outw(TxReset, ioaddr + EL3_CMD);
9778c2ecf20Sopenharmony_ci	for (i = 20; i >= 0; i--)
9788c2ecf20Sopenharmony_ci		if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
9798c2ecf20Sopenharmony_ci			break;
9808c2ecf20Sopenharmony_ci	outw(TxEnable, ioaddr + EL3_CMD);
9818c2ecf20Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
9828c2ecf20Sopenharmony_ci	dev->stats.tx_errors++;
9838c2ecf20Sopenharmony_ci	dev->stats.tx_dropped++;
9848c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
9858c2ecf20Sopenharmony_ci}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_cistatic netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb,
9888c2ecf20Sopenharmony_ci					struct net_device *dev)
9898c2ecf20Sopenharmony_ci{
9908c2ecf20Sopenharmony_ci	struct corkscrew_private *vp = netdev_priv(dev);
9918c2ecf20Sopenharmony_ci	int ioaddr = dev->base_addr;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	/* Block a timer-based transmit from overlapping. */
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	if (vp->full_bus_master_tx) {	/* BOOMERANG bus-master */
9988c2ecf20Sopenharmony_ci		/* Calculate the next Tx descriptor entry. */
9998c2ecf20Sopenharmony_ci		int entry = vp->cur_tx % TX_RING_SIZE;
10008c2ecf20Sopenharmony_ci		struct boom_tx_desc *prev_entry;
10018c2ecf20Sopenharmony_ci		unsigned long flags;
10028c2ecf20Sopenharmony_ci		int i;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci		if (vp->tx_full)	/* No room to transmit with */
10058c2ecf20Sopenharmony_ci			return NETDEV_TX_BUSY;
10068c2ecf20Sopenharmony_ci		if (vp->cur_tx != 0)
10078c2ecf20Sopenharmony_ci			prev_entry = &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE];
10088c2ecf20Sopenharmony_ci		else
10098c2ecf20Sopenharmony_ci			prev_entry = NULL;
10108c2ecf20Sopenharmony_ci		if (corkscrew_debug > 3)
10118c2ecf20Sopenharmony_ci			pr_debug("%s: Trying to send a packet, Tx index %d.\n",
10128c2ecf20Sopenharmony_ci				dev->name, vp->cur_tx);
10138c2ecf20Sopenharmony_ci		/* vp->tx_full = 1; */
10148c2ecf20Sopenharmony_ci		vp->tx_skbuff[entry] = skb;
10158c2ecf20Sopenharmony_ci		vp->tx_ring[entry].next = 0;
10168c2ecf20Sopenharmony_ci		vp->tx_ring[entry].addr = isa_virt_to_bus(skb->data);
10178c2ecf20Sopenharmony_ci		vp->tx_ring[entry].length = skb->len | 0x80000000;
10188c2ecf20Sopenharmony_ci		vp->tx_ring[entry].status = skb->len | 0x80000000;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci		spin_lock_irqsave(&vp->lock, flags);
10218c2ecf20Sopenharmony_ci		outw(DownStall, ioaddr + EL3_CMD);
10228c2ecf20Sopenharmony_ci		/* Wait for the stall to complete. */
10238c2ecf20Sopenharmony_ci		for (i = 20; i >= 0; i--)
10248c2ecf20Sopenharmony_ci			if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
10258c2ecf20Sopenharmony_ci				break;
10268c2ecf20Sopenharmony_ci		if (prev_entry)
10278c2ecf20Sopenharmony_ci			prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]);
10288c2ecf20Sopenharmony_ci		if (inl(ioaddr + DownListPtr) == 0) {
10298c2ecf20Sopenharmony_ci			outl(isa_virt_to_bus(&vp->tx_ring[entry]),
10308c2ecf20Sopenharmony_ci			     ioaddr + DownListPtr);
10318c2ecf20Sopenharmony_ci			queued_packet++;
10328c2ecf20Sopenharmony_ci		}
10338c2ecf20Sopenharmony_ci		outw(DownUnstall, ioaddr + EL3_CMD);
10348c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vp->lock, flags);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci		vp->cur_tx++;
10378c2ecf20Sopenharmony_ci		if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
10388c2ecf20Sopenharmony_ci			vp->tx_full = 1;
10398c2ecf20Sopenharmony_ci		else {		/* Clear previous interrupt enable. */
10408c2ecf20Sopenharmony_ci			if (prev_entry)
10418c2ecf20Sopenharmony_ci				prev_entry->status &= ~0x80000000;
10428c2ecf20Sopenharmony_ci			netif_wake_queue(dev);
10438c2ecf20Sopenharmony_ci		}
10448c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci	/* Put out the doubleword header... */
10478c2ecf20Sopenharmony_ci	outl(skb->len, ioaddr + TX_FIFO);
10488c2ecf20Sopenharmony_ci	dev->stats.tx_bytes += skb->len;
10498c2ecf20Sopenharmony_ci#ifdef VORTEX_BUS_MASTER
10508c2ecf20Sopenharmony_ci	if (vp->bus_master) {
10518c2ecf20Sopenharmony_ci		/* Set the bus-master controller to transfer the packet. */
10528c2ecf20Sopenharmony_ci		outl((int) (skb->data), ioaddr + Wn7_MasterAddr);
10538c2ecf20Sopenharmony_ci		outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
10548c2ecf20Sopenharmony_ci		vp->tx_skb = skb;
10558c2ecf20Sopenharmony_ci		outw(StartDMADown, ioaddr + EL3_CMD);
10568c2ecf20Sopenharmony_ci		/* queue will be woken at the DMADone interrupt. */
10578c2ecf20Sopenharmony_ci	} else {
10588c2ecf20Sopenharmony_ci		/* ... and the packet rounded to a doubleword. */
10598c2ecf20Sopenharmony_ci		outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
10608c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
10618c2ecf20Sopenharmony_ci		if (inw(ioaddr + TxFree) > 1536) {
10628c2ecf20Sopenharmony_ci			netif_wake_queue(dev);
10638c2ecf20Sopenharmony_ci		} else
10648c2ecf20Sopenharmony_ci			/* Interrupt us when the FIFO has room for max-sized packet. */
10658c2ecf20Sopenharmony_ci			outw(SetTxThreshold + (1536 >> 2),
10668c2ecf20Sopenharmony_ci			     ioaddr + EL3_CMD);
10678c2ecf20Sopenharmony_ci	}
10688c2ecf20Sopenharmony_ci#else
10698c2ecf20Sopenharmony_ci	/* ... and the packet rounded to a doubleword. */
10708c2ecf20Sopenharmony_ci	outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
10718c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
10728c2ecf20Sopenharmony_ci	if (inw(ioaddr + TxFree) > 1536) {
10738c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
10748c2ecf20Sopenharmony_ci	} else
10758c2ecf20Sopenharmony_ci		/* Interrupt us when the FIFO has room for max-sized packet. */
10768c2ecf20Sopenharmony_ci		outw(SetTxThreshold + (1536 >> 2), ioaddr + EL3_CMD);
10778c2ecf20Sopenharmony_ci#endif				/* bus master */
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	/* Clear the Tx status stack. */
10818c2ecf20Sopenharmony_ci	{
10828c2ecf20Sopenharmony_ci		short tx_status;
10838c2ecf20Sopenharmony_ci		int i = 4;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci		while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) {
10868c2ecf20Sopenharmony_ci			if (tx_status & 0x3C) {	/* A Tx-disabling error occurred.  */
10878c2ecf20Sopenharmony_ci				if (corkscrew_debug > 2)
10888c2ecf20Sopenharmony_ci					pr_debug("%s: Tx error, status %2.2x.\n",
10898c2ecf20Sopenharmony_ci						dev->name, tx_status);
10908c2ecf20Sopenharmony_ci				if (tx_status & 0x04)
10918c2ecf20Sopenharmony_ci					dev->stats.tx_fifo_errors++;
10928c2ecf20Sopenharmony_ci				if (tx_status & 0x38)
10938c2ecf20Sopenharmony_ci					dev->stats.tx_aborted_errors++;
10948c2ecf20Sopenharmony_ci				if (tx_status & 0x30) {
10958c2ecf20Sopenharmony_ci					int j;
10968c2ecf20Sopenharmony_ci					outw(TxReset, ioaddr + EL3_CMD);
10978c2ecf20Sopenharmony_ci					for (j = 20; j >= 0; j--)
10988c2ecf20Sopenharmony_ci						if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
10998c2ecf20Sopenharmony_ci							break;
11008c2ecf20Sopenharmony_ci				}
11018c2ecf20Sopenharmony_ci				outw(TxEnable, ioaddr + EL3_CMD);
11028c2ecf20Sopenharmony_ci			}
11038c2ecf20Sopenharmony_ci			outb(0x00, ioaddr + TxStatus);	/* Pop the status stack. */
11048c2ecf20Sopenharmony_ci		}
11058c2ecf20Sopenharmony_ci	}
11068c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci/* The interrupt handler does all of the Rx thread work and cleans up
11108c2ecf20Sopenharmony_ci   after the Tx thread. */
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_cistatic irqreturn_t corkscrew_interrupt(int irq, void *dev_id)
11138c2ecf20Sopenharmony_ci{
11148c2ecf20Sopenharmony_ci	/* Use the now-standard shared IRQ implementation. */
11158c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
11168c2ecf20Sopenharmony_ci	struct corkscrew_private *lp = netdev_priv(dev);
11178c2ecf20Sopenharmony_ci	int ioaddr, status;
11188c2ecf20Sopenharmony_ci	int latency;
11198c2ecf20Sopenharmony_ci	int i = max_interrupt_work;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	ioaddr = dev->base_addr;
11228c2ecf20Sopenharmony_ci	latency = inb(ioaddr + Timer);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	spin_lock(&lp->lock);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	status = inw(ioaddr + EL3_STATUS);
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	if (corkscrew_debug > 4)
11298c2ecf20Sopenharmony_ci		pr_debug("%s: interrupt, status %4.4x, timer %d.\n",
11308c2ecf20Sopenharmony_ci			dev->name, status, latency);
11318c2ecf20Sopenharmony_ci	if ((status & 0xE000) != 0xE000) {
11328c2ecf20Sopenharmony_ci		static int donedidthis;
11338c2ecf20Sopenharmony_ci		/* Some interrupt controllers store a bogus interrupt from boot-time.
11348c2ecf20Sopenharmony_ci		   Ignore a single early interrupt, but don't hang the machine for
11358c2ecf20Sopenharmony_ci		   other interrupt problems. */
11368c2ecf20Sopenharmony_ci		if (donedidthis++ > 100) {
11378c2ecf20Sopenharmony_ci			pr_err("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
11388c2ecf20Sopenharmony_ci				   dev->name, status, netif_running(dev));
11398c2ecf20Sopenharmony_ci			free_irq(dev->irq, dev);
11408c2ecf20Sopenharmony_ci			dev->irq = -1;
11418c2ecf20Sopenharmony_ci		}
11428c2ecf20Sopenharmony_ci	}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	do {
11458c2ecf20Sopenharmony_ci		if (corkscrew_debug > 5)
11468c2ecf20Sopenharmony_ci			pr_debug("%s: In interrupt loop, status %4.4x.\n",
11478c2ecf20Sopenharmony_ci			       dev->name, status);
11488c2ecf20Sopenharmony_ci		if (status & RxComplete)
11498c2ecf20Sopenharmony_ci			corkscrew_rx(dev);
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci		if (status & TxAvailable) {
11528c2ecf20Sopenharmony_ci			if (corkscrew_debug > 5)
11538c2ecf20Sopenharmony_ci				pr_debug("	TX room bit was handled.\n");
11548c2ecf20Sopenharmony_ci			/* There's room in the FIFO for a full-sized packet. */
11558c2ecf20Sopenharmony_ci			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
11568c2ecf20Sopenharmony_ci			netif_wake_queue(dev);
11578c2ecf20Sopenharmony_ci		}
11588c2ecf20Sopenharmony_ci		if (status & DownComplete) {
11598c2ecf20Sopenharmony_ci			unsigned int dirty_tx = lp->dirty_tx;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci			while (lp->cur_tx - dirty_tx > 0) {
11628c2ecf20Sopenharmony_ci				int entry = dirty_tx % TX_RING_SIZE;
11638c2ecf20Sopenharmony_ci				if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry]))
11648c2ecf20Sopenharmony_ci					break;	/* It still hasn't been processed. */
11658c2ecf20Sopenharmony_ci				if (lp->tx_skbuff[entry]) {
11668c2ecf20Sopenharmony_ci					dev_consume_skb_irq(lp->tx_skbuff[entry]);
11678c2ecf20Sopenharmony_ci					lp->tx_skbuff[entry] = NULL;
11688c2ecf20Sopenharmony_ci				}
11698c2ecf20Sopenharmony_ci				dirty_tx++;
11708c2ecf20Sopenharmony_ci			}
11718c2ecf20Sopenharmony_ci			lp->dirty_tx = dirty_tx;
11728c2ecf20Sopenharmony_ci			outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
11738c2ecf20Sopenharmony_ci			if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
11748c2ecf20Sopenharmony_ci				lp->tx_full = 0;
11758c2ecf20Sopenharmony_ci				netif_wake_queue(dev);
11768c2ecf20Sopenharmony_ci			}
11778c2ecf20Sopenharmony_ci		}
11788c2ecf20Sopenharmony_ci#ifdef VORTEX_BUS_MASTER
11798c2ecf20Sopenharmony_ci		if (status & DMADone) {
11808c2ecf20Sopenharmony_ci			outw(0x1000, ioaddr + Wn7_MasterStatus);	/* Ack the event. */
11818c2ecf20Sopenharmony_ci			dev_consume_skb_irq(lp->tx_skb);	/* Release the transferred buffer */
11828c2ecf20Sopenharmony_ci			netif_wake_queue(dev);
11838c2ecf20Sopenharmony_ci		}
11848c2ecf20Sopenharmony_ci#endif
11858c2ecf20Sopenharmony_ci		if (status & UpComplete) {
11868c2ecf20Sopenharmony_ci			boomerang_rx(dev);
11878c2ecf20Sopenharmony_ci			outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
11888c2ecf20Sopenharmony_ci		}
11898c2ecf20Sopenharmony_ci		if (status & (AdapterFailure | RxEarly | StatsFull)) {
11908c2ecf20Sopenharmony_ci			/* Handle all uncommon interrupts at once. */
11918c2ecf20Sopenharmony_ci			if (status & RxEarly) {	/* Rx early is unused. */
11928c2ecf20Sopenharmony_ci				corkscrew_rx(dev);
11938c2ecf20Sopenharmony_ci				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
11948c2ecf20Sopenharmony_ci			}
11958c2ecf20Sopenharmony_ci			if (status & StatsFull) {	/* Empty statistics. */
11968c2ecf20Sopenharmony_ci				static int DoneDidThat;
11978c2ecf20Sopenharmony_ci				if (corkscrew_debug > 4)
11988c2ecf20Sopenharmony_ci					pr_debug("%s: Updating stats.\n", dev->name);
11998c2ecf20Sopenharmony_ci				update_stats(ioaddr, dev);
12008c2ecf20Sopenharmony_ci				/* DEBUG HACK: Disable statistics as an interrupt source. */
12018c2ecf20Sopenharmony_ci				/* This occurs when we have the wrong media type! */
12028c2ecf20Sopenharmony_ci				if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) {
12038c2ecf20Sopenharmony_ci					int win, reg;
12048c2ecf20Sopenharmony_ci					pr_notice("%s: Updating stats failed, disabling stats as an interrupt source.\n",
12058c2ecf20Sopenharmony_ci						dev->name);
12068c2ecf20Sopenharmony_ci					for (win = 0; win < 8; win++) {
12078c2ecf20Sopenharmony_ci						EL3WINDOW(win);
12088c2ecf20Sopenharmony_ci						pr_notice("Vortex window %d:", win);
12098c2ecf20Sopenharmony_ci						for (reg = 0; reg < 16; reg++)
12108c2ecf20Sopenharmony_ci							pr_cont(" %2.2x", inb(ioaddr + reg));
12118c2ecf20Sopenharmony_ci						pr_cont("\n");
12128c2ecf20Sopenharmony_ci					}
12138c2ecf20Sopenharmony_ci					EL3WINDOW(7);
12148c2ecf20Sopenharmony_ci					outw(SetIntrEnb | TxAvailable |
12158c2ecf20Sopenharmony_ci					     RxComplete | AdapterFailure |
12168c2ecf20Sopenharmony_ci					     UpComplete | DownComplete |
12178c2ecf20Sopenharmony_ci					     TxComplete, ioaddr + EL3_CMD);
12188c2ecf20Sopenharmony_ci					DoneDidThat++;
12198c2ecf20Sopenharmony_ci				}
12208c2ecf20Sopenharmony_ci			}
12218c2ecf20Sopenharmony_ci			if (status & AdapterFailure) {
12228c2ecf20Sopenharmony_ci				/* Adapter failure requires Rx reset and reinit. */
12238c2ecf20Sopenharmony_ci				outw(RxReset, ioaddr + EL3_CMD);
12248c2ecf20Sopenharmony_ci				/* Set the Rx filter to the current state. */
12258c2ecf20Sopenharmony_ci				set_rx_mode(dev);
12268c2ecf20Sopenharmony_ci				outw(RxEnable, ioaddr + EL3_CMD);	/* Re-enable the receiver. */
12278c2ecf20Sopenharmony_ci				outw(AckIntr | AdapterFailure,
12288c2ecf20Sopenharmony_ci				     ioaddr + EL3_CMD);
12298c2ecf20Sopenharmony_ci			}
12308c2ecf20Sopenharmony_ci		}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci		if (--i < 0) {
12338c2ecf20Sopenharmony_ci			pr_err("%s: Too much work in interrupt, status %4.4x. Disabling functions (%4.4x).\n",
12348c2ecf20Sopenharmony_ci				dev->name, status, SetStatusEnb | ((~status) & 0x7FE));
12358c2ecf20Sopenharmony_ci			/* Disable all pending interrupts. */
12368c2ecf20Sopenharmony_ci			outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
12378c2ecf20Sopenharmony_ci			outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
12388c2ecf20Sopenharmony_ci			break;
12398c2ecf20Sopenharmony_ci		}
12408c2ecf20Sopenharmony_ci		/* Acknowledge the IRQ. */
12418c2ecf20Sopenharmony_ci		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	} while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	spin_unlock(&lp->lock);
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	if (corkscrew_debug > 4)
12488c2ecf20Sopenharmony_ci		pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
12498c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
12508c2ecf20Sopenharmony_ci}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_cistatic int corkscrew_rx(struct net_device *dev)
12538c2ecf20Sopenharmony_ci{
12548c2ecf20Sopenharmony_ci	int ioaddr = dev->base_addr;
12558c2ecf20Sopenharmony_ci	int i;
12568c2ecf20Sopenharmony_ci	short rx_status;
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	if (corkscrew_debug > 5)
12598c2ecf20Sopenharmony_ci		pr_debug("   In rx_packet(), status %4.4x, rx_status %4.4x.\n",
12608c2ecf20Sopenharmony_ci		     inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus));
12618c2ecf20Sopenharmony_ci	while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
12628c2ecf20Sopenharmony_ci		if (rx_status & 0x4000) {	/* Error, update stats. */
12638c2ecf20Sopenharmony_ci			unsigned char rx_error = inb(ioaddr + RxErrors);
12648c2ecf20Sopenharmony_ci			if (corkscrew_debug > 2)
12658c2ecf20Sopenharmony_ci				pr_debug(" Rx error: status %2.2x.\n",
12668c2ecf20Sopenharmony_ci				       rx_error);
12678c2ecf20Sopenharmony_ci			dev->stats.rx_errors++;
12688c2ecf20Sopenharmony_ci			if (rx_error & 0x01)
12698c2ecf20Sopenharmony_ci				dev->stats.rx_over_errors++;
12708c2ecf20Sopenharmony_ci			if (rx_error & 0x02)
12718c2ecf20Sopenharmony_ci				dev->stats.rx_length_errors++;
12728c2ecf20Sopenharmony_ci			if (rx_error & 0x04)
12738c2ecf20Sopenharmony_ci				dev->stats.rx_frame_errors++;
12748c2ecf20Sopenharmony_ci			if (rx_error & 0x08)
12758c2ecf20Sopenharmony_ci				dev->stats.rx_crc_errors++;
12768c2ecf20Sopenharmony_ci			if (rx_error & 0x10)
12778c2ecf20Sopenharmony_ci				dev->stats.rx_length_errors++;
12788c2ecf20Sopenharmony_ci		} else {
12798c2ecf20Sopenharmony_ci			/* The packet length: up to 4.5K!. */
12808c2ecf20Sopenharmony_ci			short pkt_len = rx_status & 0x1fff;
12818c2ecf20Sopenharmony_ci			struct sk_buff *skb;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci			skb = netdev_alloc_skb(dev, pkt_len + 5 + 2);
12848c2ecf20Sopenharmony_ci			if (corkscrew_debug > 4)
12858c2ecf20Sopenharmony_ci				pr_debug("Receiving packet size %d status %4.4x.\n",
12868c2ecf20Sopenharmony_ci				     pkt_len, rx_status);
12878c2ecf20Sopenharmony_ci			if (skb != NULL) {
12888c2ecf20Sopenharmony_ci				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
12898c2ecf20Sopenharmony_ci				/* 'skb_put()' points to the start of sk_buff data area. */
12908c2ecf20Sopenharmony_ci				insl(ioaddr + RX_FIFO,
12918c2ecf20Sopenharmony_ci				     skb_put(skb, pkt_len),
12928c2ecf20Sopenharmony_ci				     (pkt_len + 3) >> 2);
12938c2ecf20Sopenharmony_ci				outw(RxDiscard, ioaddr + EL3_CMD);	/* Pop top Rx packet. */
12948c2ecf20Sopenharmony_ci				skb->protocol = eth_type_trans(skb, dev);
12958c2ecf20Sopenharmony_ci				netif_rx(skb);
12968c2ecf20Sopenharmony_ci				dev->stats.rx_packets++;
12978c2ecf20Sopenharmony_ci				dev->stats.rx_bytes += pkt_len;
12988c2ecf20Sopenharmony_ci				/* Wait a limited time to go to next packet. */
12998c2ecf20Sopenharmony_ci				for (i = 200; i >= 0; i--)
13008c2ecf20Sopenharmony_ci					if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
13018c2ecf20Sopenharmony_ci						break;
13028c2ecf20Sopenharmony_ci				continue;
13038c2ecf20Sopenharmony_ci			} else if (corkscrew_debug)
13048c2ecf20Sopenharmony_ci				pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len);
13058c2ecf20Sopenharmony_ci		}
13068c2ecf20Sopenharmony_ci		outw(RxDiscard, ioaddr + EL3_CMD);
13078c2ecf20Sopenharmony_ci		dev->stats.rx_dropped++;
13088c2ecf20Sopenharmony_ci		/* Wait a limited time to skip this packet. */
13098c2ecf20Sopenharmony_ci		for (i = 200; i >= 0; i--)
13108c2ecf20Sopenharmony_ci			if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
13118c2ecf20Sopenharmony_ci				break;
13128c2ecf20Sopenharmony_ci	}
13138c2ecf20Sopenharmony_ci	return 0;
13148c2ecf20Sopenharmony_ci}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_cistatic int boomerang_rx(struct net_device *dev)
13178c2ecf20Sopenharmony_ci{
13188c2ecf20Sopenharmony_ci	struct corkscrew_private *vp = netdev_priv(dev);
13198c2ecf20Sopenharmony_ci	int entry = vp->cur_rx % RX_RING_SIZE;
13208c2ecf20Sopenharmony_ci	int ioaddr = dev->base_addr;
13218c2ecf20Sopenharmony_ci	int rx_status;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	if (corkscrew_debug > 5)
13248c2ecf20Sopenharmony_ci		pr_debug("   In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
13258c2ecf20Sopenharmony_ci			inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus));
13268c2ecf20Sopenharmony_ci	while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) {
13278c2ecf20Sopenharmony_ci		if (rx_status & RxDError) {	/* Error, update stats. */
13288c2ecf20Sopenharmony_ci			unsigned char rx_error = rx_status >> 16;
13298c2ecf20Sopenharmony_ci			if (corkscrew_debug > 2)
13308c2ecf20Sopenharmony_ci				pr_debug(" Rx error: status %2.2x.\n",
13318c2ecf20Sopenharmony_ci				       rx_error);
13328c2ecf20Sopenharmony_ci			dev->stats.rx_errors++;
13338c2ecf20Sopenharmony_ci			if (rx_error & 0x01)
13348c2ecf20Sopenharmony_ci				dev->stats.rx_over_errors++;
13358c2ecf20Sopenharmony_ci			if (rx_error & 0x02)
13368c2ecf20Sopenharmony_ci				dev->stats.rx_length_errors++;
13378c2ecf20Sopenharmony_ci			if (rx_error & 0x04)
13388c2ecf20Sopenharmony_ci				dev->stats.rx_frame_errors++;
13398c2ecf20Sopenharmony_ci			if (rx_error & 0x08)
13408c2ecf20Sopenharmony_ci				dev->stats.rx_crc_errors++;
13418c2ecf20Sopenharmony_ci			if (rx_error & 0x10)
13428c2ecf20Sopenharmony_ci				dev->stats.rx_length_errors++;
13438c2ecf20Sopenharmony_ci		} else {
13448c2ecf20Sopenharmony_ci			/* The packet length: up to 4.5K!. */
13458c2ecf20Sopenharmony_ci			short pkt_len = rx_status & 0x1fff;
13468c2ecf20Sopenharmony_ci			struct sk_buff *skb;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci			dev->stats.rx_bytes += pkt_len;
13498c2ecf20Sopenharmony_ci			if (corkscrew_debug > 4)
13508c2ecf20Sopenharmony_ci				pr_debug("Receiving packet size %d status %4.4x.\n",
13518c2ecf20Sopenharmony_ci				     pkt_len, rx_status);
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci			/* Check if the packet is long enough to just accept without
13548c2ecf20Sopenharmony_ci			   copying to a properly sized skbuff. */
13558c2ecf20Sopenharmony_ci			if (pkt_len < rx_copybreak &&
13568c2ecf20Sopenharmony_ci			    (skb = netdev_alloc_skb(dev, pkt_len + 4)) != NULL) {
13578c2ecf20Sopenharmony_ci				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
13588c2ecf20Sopenharmony_ci				/* 'skb_put()' points to the start of sk_buff data area. */
13598c2ecf20Sopenharmony_ci				skb_put_data(skb,
13608c2ecf20Sopenharmony_ci					     isa_bus_to_virt(vp->rx_ring[entry].addr),
13618c2ecf20Sopenharmony_ci					     pkt_len);
13628c2ecf20Sopenharmony_ci				rx_copy++;
13638c2ecf20Sopenharmony_ci			} else {
13648c2ecf20Sopenharmony_ci				void *temp;
13658c2ecf20Sopenharmony_ci				/* Pass up the skbuff already on the Rx ring. */
13668c2ecf20Sopenharmony_ci				skb = vp->rx_skbuff[entry];
13678c2ecf20Sopenharmony_ci				vp->rx_skbuff[entry] = NULL;
13688c2ecf20Sopenharmony_ci				temp = skb_put(skb, pkt_len);
13698c2ecf20Sopenharmony_ci				/* Remove this checking code for final release. */
13708c2ecf20Sopenharmony_ci				if (isa_bus_to_virt(vp->rx_ring[entry].addr) != temp)
13718c2ecf20Sopenharmony_ci					pr_warn("%s: Warning -- the skbuff addresses do not match in boomerang_rx: %p vs. %p / %p\n",
13728c2ecf20Sopenharmony_ci						dev->name,
13738c2ecf20Sopenharmony_ci						isa_bus_to_virt(vp->rx_ring[entry].addr),
13748c2ecf20Sopenharmony_ci						skb->head, temp);
13758c2ecf20Sopenharmony_ci				rx_nocopy++;
13768c2ecf20Sopenharmony_ci			}
13778c2ecf20Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
13788c2ecf20Sopenharmony_ci			netif_rx(skb);
13798c2ecf20Sopenharmony_ci			dev->stats.rx_packets++;
13808c2ecf20Sopenharmony_ci		}
13818c2ecf20Sopenharmony_ci		entry = (++vp->cur_rx) % RX_RING_SIZE;
13828c2ecf20Sopenharmony_ci	}
13838c2ecf20Sopenharmony_ci	/* Refill the Rx ring buffers. */
13848c2ecf20Sopenharmony_ci	for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) {
13858c2ecf20Sopenharmony_ci		struct sk_buff *skb;
13868c2ecf20Sopenharmony_ci		entry = vp->dirty_rx % RX_RING_SIZE;
13878c2ecf20Sopenharmony_ci		if (vp->rx_skbuff[entry] == NULL) {
13888c2ecf20Sopenharmony_ci			skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
13898c2ecf20Sopenharmony_ci			if (skb == NULL)
13908c2ecf20Sopenharmony_ci				break;	/* Bad news!  */
13918c2ecf20Sopenharmony_ci			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
13928c2ecf20Sopenharmony_ci			vp->rx_ring[entry].addr = isa_virt_to_bus(skb->data);
13938c2ecf20Sopenharmony_ci			vp->rx_skbuff[entry] = skb;
13948c2ecf20Sopenharmony_ci		}
13958c2ecf20Sopenharmony_ci		vp->rx_ring[entry].status = 0;	/* Clear complete bit. */
13968c2ecf20Sopenharmony_ci	}
13978c2ecf20Sopenharmony_ci	return 0;
13988c2ecf20Sopenharmony_ci}
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_cistatic int corkscrew_close(struct net_device *dev)
14018c2ecf20Sopenharmony_ci{
14028c2ecf20Sopenharmony_ci	struct corkscrew_private *vp = netdev_priv(dev);
14038c2ecf20Sopenharmony_ci	int ioaddr = dev->base_addr;
14048c2ecf20Sopenharmony_ci	int i;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	if (corkscrew_debug > 1) {
14098c2ecf20Sopenharmony_ci		pr_debug("%s: corkscrew_close() status %4.4x, Tx status %2.2x.\n",
14108c2ecf20Sopenharmony_ci		     dev->name, inw(ioaddr + EL3_STATUS),
14118c2ecf20Sopenharmony_ci		     inb(ioaddr + TxStatus));
14128c2ecf20Sopenharmony_ci		pr_debug("%s: corkscrew close stats: rx_nocopy %d rx_copy %d tx_queued %d.\n",
14138c2ecf20Sopenharmony_ci			dev->name, rx_nocopy, rx_copy, queued_packet);
14148c2ecf20Sopenharmony_ci	}
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	del_timer_sync(&vp->timer);
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	/* Turn off statistics ASAP.  We update lp->stats below. */
14198c2ecf20Sopenharmony_ci	outw(StatsDisable, ioaddr + EL3_CMD);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	/* Disable the receiver and transmitter. */
14228c2ecf20Sopenharmony_ci	outw(RxDisable, ioaddr + EL3_CMD);
14238c2ecf20Sopenharmony_ci	outw(TxDisable, ioaddr + EL3_CMD);
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	if (dev->if_port == XCVR_10base2)
14268c2ecf20Sopenharmony_ci		/* Turn off thinnet power.  Green! */
14278c2ecf20Sopenharmony_ci		outw(StopCoax, ioaddr + EL3_CMD);
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	free_irq(dev->irq, dev);
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	update_stats(ioaddr, dev);
14348c2ecf20Sopenharmony_ci	if (vp->full_bus_master_rx) {	/* Free Boomerang bus master Rx buffers. */
14358c2ecf20Sopenharmony_ci		outl(0, ioaddr + UpListPtr);
14368c2ecf20Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++)
14378c2ecf20Sopenharmony_ci			if (vp->rx_skbuff[i]) {
14388c2ecf20Sopenharmony_ci				dev_kfree_skb(vp->rx_skbuff[i]);
14398c2ecf20Sopenharmony_ci				vp->rx_skbuff[i] = NULL;
14408c2ecf20Sopenharmony_ci			}
14418c2ecf20Sopenharmony_ci	}
14428c2ecf20Sopenharmony_ci	if (vp->full_bus_master_tx) {	/* Free Boomerang bus master Tx buffers. */
14438c2ecf20Sopenharmony_ci		outl(0, ioaddr + DownListPtr);
14448c2ecf20Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++)
14458c2ecf20Sopenharmony_ci			if (vp->tx_skbuff[i]) {
14468c2ecf20Sopenharmony_ci				dev_kfree_skb(vp->tx_skbuff[i]);
14478c2ecf20Sopenharmony_ci				vp->tx_skbuff[i] = NULL;
14488c2ecf20Sopenharmony_ci			}
14498c2ecf20Sopenharmony_ci	}
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	return 0;
14528c2ecf20Sopenharmony_ci}
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_cistatic struct net_device_stats *corkscrew_get_stats(struct net_device *dev)
14558c2ecf20Sopenharmony_ci{
14568c2ecf20Sopenharmony_ci	struct corkscrew_private *vp = netdev_priv(dev);
14578c2ecf20Sopenharmony_ci	unsigned long flags;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	if (netif_running(dev)) {
14608c2ecf20Sopenharmony_ci		spin_lock_irqsave(&vp->lock, flags);
14618c2ecf20Sopenharmony_ci		update_stats(dev->base_addr, dev);
14628c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vp->lock, flags);
14638c2ecf20Sopenharmony_ci	}
14648c2ecf20Sopenharmony_ci	return &dev->stats;
14658c2ecf20Sopenharmony_ci}
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci/*  Update statistics.
14688c2ecf20Sopenharmony_ci	Unlike with the EL3 we need not worry about interrupts changing
14698c2ecf20Sopenharmony_ci	the window setting from underneath us, but we must still guard
14708c2ecf20Sopenharmony_ci	against a race condition with a StatsUpdate interrupt updating the
14718c2ecf20Sopenharmony_ci	table.  This is done by checking that the ASM (!) code generated uses
14728c2ecf20Sopenharmony_ci	atomic updates with '+='.
14738c2ecf20Sopenharmony_ci	*/
14748c2ecf20Sopenharmony_cistatic void update_stats(int ioaddr, struct net_device *dev)
14758c2ecf20Sopenharmony_ci{
14768c2ecf20Sopenharmony_ci	/* Unlike the 3c5x9 we need not turn off stats updates while reading. */
14778c2ecf20Sopenharmony_ci	/* Switch to the stats window, and read everything. */
14788c2ecf20Sopenharmony_ci	EL3WINDOW(6);
14798c2ecf20Sopenharmony_ci	dev->stats.tx_carrier_errors += inb(ioaddr + 0);
14808c2ecf20Sopenharmony_ci	dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
14818c2ecf20Sopenharmony_ci	/* Multiple collisions. */ inb(ioaddr + 2);
14828c2ecf20Sopenharmony_ci	dev->stats.collisions += inb(ioaddr + 3);
14838c2ecf20Sopenharmony_ci	dev->stats.tx_window_errors += inb(ioaddr + 4);
14848c2ecf20Sopenharmony_ci	dev->stats.rx_fifo_errors += inb(ioaddr + 5);
14858c2ecf20Sopenharmony_ci	dev->stats.tx_packets += inb(ioaddr + 6);
14868c2ecf20Sopenharmony_ci	dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4;
14878c2ecf20Sopenharmony_ci						/* Rx packets   */ inb(ioaddr + 7);
14888c2ecf20Sopenharmony_ci						/* Must read to clear */
14898c2ecf20Sopenharmony_ci	/* Tx deferrals */ inb(ioaddr + 8);
14908c2ecf20Sopenharmony_ci	/* Don't bother with register 9, an extension of registers 6&7.
14918c2ecf20Sopenharmony_ci	   If we do use the 6&7 values the atomic update assumption above
14928c2ecf20Sopenharmony_ci	   is invalid. */
14938c2ecf20Sopenharmony_ci	inw(ioaddr + 10);	/* Total Rx and Tx octets. */
14948c2ecf20Sopenharmony_ci	inw(ioaddr + 12);
14958c2ecf20Sopenharmony_ci	/* New: On the Vortex we must also clear the BadSSD counter. */
14968c2ecf20Sopenharmony_ci	EL3WINDOW(4);
14978c2ecf20Sopenharmony_ci	inb(ioaddr + 12);
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	/* We change back to window 7 (not 1) with the Vortex. */
15008c2ecf20Sopenharmony_ci	EL3WINDOW(7);
15018c2ecf20Sopenharmony_ci}
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci/* This new version of set_rx_mode() supports v1.4 kernels.
15048c2ecf20Sopenharmony_ci   The Vortex chip has no documented multicast filter, so the only
15058c2ecf20Sopenharmony_ci   multicast setting is to receive all multicast frames.  At least
15068c2ecf20Sopenharmony_ci   the chip has a very clean way to set the mode, unlike many others. */
15078c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev)
15088c2ecf20Sopenharmony_ci{
15098c2ecf20Sopenharmony_ci	int ioaddr = dev->base_addr;
15108c2ecf20Sopenharmony_ci	unsigned short new_mode;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
15138c2ecf20Sopenharmony_ci		if (corkscrew_debug > 3)
15148c2ecf20Sopenharmony_ci			pr_debug("%s: Setting promiscuous mode.\n",
15158c2ecf20Sopenharmony_ci			       dev->name);
15168c2ecf20Sopenharmony_ci		new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm;
15178c2ecf20Sopenharmony_ci	} else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) {
15188c2ecf20Sopenharmony_ci		new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast;
15198c2ecf20Sopenharmony_ci	} else
15208c2ecf20Sopenharmony_ci		new_mode = SetRxFilter | RxStation | RxBroadcast;
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	outw(new_mode, ioaddr + EL3_CMD);
15238c2ecf20Sopenharmony_ci}
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_cistatic void netdev_get_drvinfo(struct net_device *dev,
15268c2ecf20Sopenharmony_ci			       struct ethtool_drvinfo *info)
15278c2ecf20Sopenharmony_ci{
15288c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
15298c2ecf20Sopenharmony_ci	snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx",
15308c2ecf20Sopenharmony_ci		 dev->base_addr);
15318c2ecf20Sopenharmony_ci}
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_cistatic u32 netdev_get_msglevel(struct net_device *dev)
15348c2ecf20Sopenharmony_ci{
15358c2ecf20Sopenharmony_ci	return corkscrew_debug;
15368c2ecf20Sopenharmony_ci}
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_cistatic void netdev_set_msglevel(struct net_device *dev, u32 level)
15398c2ecf20Sopenharmony_ci{
15408c2ecf20Sopenharmony_ci	corkscrew_debug = level;
15418c2ecf20Sopenharmony_ci}
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops = {
15448c2ecf20Sopenharmony_ci	.get_drvinfo		= netdev_get_drvinfo,
15458c2ecf20Sopenharmony_ci	.get_msglevel		= netdev_get_msglevel,
15468c2ecf20Sopenharmony_ci	.set_msglevel		= netdev_set_msglevel,
15478c2ecf20Sopenharmony_ci};
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci#ifdef MODULE
15518c2ecf20Sopenharmony_civoid cleanup_module(void)
15528c2ecf20Sopenharmony_ci{
15538c2ecf20Sopenharmony_ci	while (!list_empty(&root_corkscrew_dev)) {
15548c2ecf20Sopenharmony_ci		struct net_device *dev;
15558c2ecf20Sopenharmony_ci		struct corkscrew_private *vp;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci		vp = list_entry(root_corkscrew_dev.next,
15588c2ecf20Sopenharmony_ci				struct corkscrew_private, list);
15598c2ecf20Sopenharmony_ci		dev = vp->our_dev;
15608c2ecf20Sopenharmony_ci		unregister_netdev(dev);
15618c2ecf20Sopenharmony_ci		cleanup_card(dev);
15628c2ecf20Sopenharmony_ci		free_netdev(dev);
15638c2ecf20Sopenharmony_ci	}
15648c2ecf20Sopenharmony_ci}
15658c2ecf20Sopenharmony_ci#endif				/* MODULE */
1566