11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Written 1993-2000 by Donald Becker.
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Copyright 1994-2000 by Donald Becker.
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Copyright 1993 United States Government as represented by the
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Director, National Security Agency.	 This software may be used and
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	distributed according to the terms of the GNU General Public License,
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	incorporated herein by reference.
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This driver is for the 3Com EtherLinkIII series.
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	The author may be reached as becker@scyld.com, or C/O
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Scyld Computing Corporation
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	410 Severn Ave., Suite 210
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Annapolis MD 21403
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Known limitations:
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Because of the way 3c509 ISA detection works it's difficult to predict
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	a priori which of several ISA-mode cards will be detected first.
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This driver does not use predictive interrupt mode, resulting in higher
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	packet latency but lower overhead.  If interrupts are disabled for an
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unusually long time it could also result in missed packets, but in
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	practice this rarely happens.
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	FIXES:
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		Alan Cox:       Removed the 'Unexpected interrupt' bug.
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		Michael Meskes:	Upgraded to Donald Becker's version 1.07.
316aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik		Alan Cox:	Increased the eeprom delay. Regardless of
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				what the docs say some people definitely
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				get problems with lower (but in card spec)
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				delays
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				other cleanups.  -djb
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		Andrea Arcangeli:	Upgraded to Donald Becker's version 1.12.
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		Rick Payne:	Fixed SMP race condition
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v1.15 1/31/98 Faster recovery for Tx errors. -djb
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb
43e1f8e87449147ffe5ea3de64a46af7de450ce279Francois Cami		v1.18 12Mar2001 Andrew Morton
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			- Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz)
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			- Reviewed against 1.18 from scyld.com
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v1.18a 17Nov2001 Jeff Garzik <jgarzik@pobox.com>
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			- ethtool support
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v1.18b 1Mar2002 Zwane Mwaikambo <zwane@commfireservices.com>
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			- Power Management support
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v1.18c 1Mar2002 David Ruggiero <jdr@farfalle.com>
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			- Full duplex support
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v1.19  16Oct2002 Zwane Mwaikambo <zwane@linuxpower.ca>
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			- Additional ethtool features
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v1.19a 28Oct2002 Davud Ruggiero <jdr@farfalle.com>
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			- Increase *read_eeprom udelay to workaround oops with 2 cards.
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v1.19b 08Nov2002 Marc Zyngier <maz@wild-wind.fr.eu.org>
57ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary			- Introduce driver model for EISA cards.
58ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		v1.20  04Feb2008 Ondrej Zary <linux@rainbow-software.org>
59ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary			- convert to isa_driver and pnp_driver and some cleanups
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRV_NAME	"3c509"
63ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#define DRV_VERSION	"1.20"
64ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#define DRV_RELDATE	"04Feb2008"
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* A few values that may be tweaked. */
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Time in jiffies before concluding the transmitter is hung. */
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TX_TIMEOUT  (400*HZ/1000)
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mca.h>
73ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#include <linux/isa.h>
74ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#include <linux/pnp.h>
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/in.h>
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/etherdevice.h>
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pm.h>
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/skbuff.h>
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>	/* for udelay() */
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ethtool.h>
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/device.h>
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/eisa.h>
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/bitops.h>
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/irq.h>
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9637b2a1791c8b8d630944afbe0745a08c8e8ae091Al Virostatic char version[] __devinitdata = DRV_NAME ".c:" DRV_VERSION " " DRV_RELDATE " becker@scyld.com\n";
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef EL3_DEBUG
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el3_debug = EL3_DEBUG;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el3_debug = 2;
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Used to do a global count of all the cards in the system.  Must be
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * a global variable so that the mca/eisa probe routines can increment
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it */
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el3_cards = 0;
108ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#define EL3_MAX_CARDS 8
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* To minimize the size of the driver source I only define operating
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   constants if they are used several times.  You'll need the manual
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   anyway if you want to understand driver details. */
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Offsets from base I/O address. */
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define EL3_DATA 0x00
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define EL3_CMD 0x0e
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define EL3_STATUS 0x0e
117ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#define	EEPROM_READ 0x80
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define EL3_IO_EXTENT	16
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The top five bits written to EL3_CMD are a command, the lower
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   11 bits are the parameter, if applicable. */
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsenum c509cmd {
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	StatsDisable = 22<<11, StopCoax = 23<<11, PowerUp = 27<<11,
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PowerDown = 28<<11, PowerAuto = 29<<11};
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsenum c509status {
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, };
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The SetRxFilter command accepts the following classes: */
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsenum RxFilter {
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Register window 1 offsets, the window used in normal operation. */
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TX_FIFO		0x00
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RX_FIFO		0x00
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RX_STATUS 	0x08
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TX_STATUS 	0x0B
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TX_FREE		0x0C		/* Remaining free bytes in Tx buffer. */
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WN0_CONF_CTRL	0x04		/* Window 0: Configuration control register */
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WN0_ADDR_CONF	0x06		/* Window 0: Address configuration register */
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WN0_IRQ		0x08		/* Window 0: Set IRQ line in bits 12-15. */
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WN4_MEDIA	0x0A		/* Window 4: Various transcvr/media bits. */
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	MEDIA_TP	0x00C0		/* Enable link beat and jabber for 10baseT. */
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WN4_NETDIAG	0x06		/* Window 4: Net diagnostic */
1586aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik#define FD_ENABLE	0x8000		/* Enable full-duplex ("external loopback") */
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Must be a power of two (we use a binary and in the
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * circular queue)
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define SKB_QUEUE_SIZE	64
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
166ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zaryenum el3_cardtype { EL3_ISA, EL3_PNP, EL3_MCA, EL3_EISA };
167ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct el3_private {
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spinlock_t lock;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* skb send-queue */
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int head, size;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sk_buff *queue[SKB_QUEUE_SIZE];
173ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	enum el3_cardtype type;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
175ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int id_port;
176ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int current_tag;
177ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic struct net_device *el3_devs[EL3_MAX_CARDS];
178ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
179ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary/* Parameters that may be passed into the module. */
180ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int debug = -1;
181ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
182ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
183ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int max_interrupt_work = 10;
184ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PNP
185ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int nopnp;
186ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
188948252cb9e01d65a89ecadf67be5018351eee15eDavid S. Millerstatic int __devinit el3_common_init(struct net_device *dev);
189ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic void el3_common_remove(struct net_device *dev);
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ushort id_read_eeprom(int index);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ushort read_eeprom(int ioaddr, int index);
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el3_open(struct net_device *dev);
19327a1de95a1461ec0589005c293d6ac23a46cb72dStephen Hemmingerstatic netdev_tx_t el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
1947d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t el3_interrupt(int irq, void *dev_id);
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void update_stats(struct net_device *dev);
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct net_device_stats *el3_get_stats(struct net_device *dev);
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el3_rx(struct net_device *dev);
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el3_close(struct net_device *dev);
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void set_multicast_list(struct net_device *dev);
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el3_tx_timeout (struct net_device *dev);
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el3_down(struct net_device *dev);
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el3_up(struct net_device *dev);
2037282d491ecaee9883233a0e27283c4c79486279aJeff Garzikstatic const struct ethtool_ops ethtool_ops;
204ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PM
20560a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enbergstatic int el3_suspend(struct device *, pm_message_t);
20660a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enbergstatic int el3_resume(struct device *);
20760a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enberg#else
20860a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enberg#define el3_suspend NULL
20960a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enberg#define el3_resume NULL
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
21160a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enberg
21260a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enberg
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* generic device remove for all device types */
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el3_device_remove (struct device *device);
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_NET_POLL_CONTROLLER
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el3_poll_controller(struct net_device *dev);
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
219ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary/* Return 0 on success, 1 on error, 2 when found already detected PnP card */
220ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int el3_isa_id_sequence(__be16 *phys_addr)
221ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary{
222ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	short lrs_state = 0xff;
223ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	int i;
224ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
225ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	/* ISA boards are detected by sending the ID sequence to the
226ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	   ID_PORT.  We find cards past the first by setting the 'current_tag'
227ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	   on cards as they are found.  Cards with their tag set will not
228ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	   respond to subsequent ID sequences. */
229ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
230ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	outb(0x00, id_port);
231ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	outb(0x00, id_port);
232ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	for (i = 0; i < 255; i++) {
233ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		outb(lrs_state, id_port);
234ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		lrs_state <<= 1;
235ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
236ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	}
237ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	/* For the first probe, clear all board's tag registers. */
238ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (current_tag == 0)
239ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		outb(0xd0, id_port);
240ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	else			/* Otherwise kill off already-found boards. */
241ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		outb(0xd8, id_port);
242ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (id_read_eeprom(7) != 0x6d50)
243ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		return 1;
244ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	/* Read in EEPROM data, which does contention-select.
245ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	   Only the lowest address board will stay "on-line".
246ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	   3Com got the byte order backwards. */
247ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	for (i = 0; i < 3; i++)
248ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		phys_addr[i] = htons(id_read_eeprom(i));
249ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PNP
250ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (!nopnp) {
251ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		/* The ISA PnP 3c509 cards respond to the ID sequence too.
252ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		   This check is needed in order not to register them twice. */
253ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		for (i = 0; i < el3_cards; i++) {
254ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary			struct el3_private *lp = netdev_priv(el3_devs[i]);
2558e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches			if (lp->type == EL3_PNP &&
2568e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches			    !memcmp(phys_addr, el3_devs[i]->dev_addr,
2578e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches				    ETH_ALEN)) {
258ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary				if (el3_debug > 3)
259646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov					pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n",
260ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary						phys_addr[0] & 0xff, phys_addr[0] >> 8,
261ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary						phys_addr[1] & 0xff, phys_addr[1] >> 8,
262ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary						phys_addr[2] & 0xff, phys_addr[2] >> 8);
263ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary				/* Set the adaptor tag so that the next card can be found. */
264ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary				outb(0xd0 + ++current_tag, id_port);
265ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary				return 2;
266ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary			}
267ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		}
268ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	}
269ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif /* CONFIG_PNP */
270ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	return 0;
271ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
272ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary}
273ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
274ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic void __devinit el3_dev_fill(struct net_device *dev, __be16 *phys_addr,
275ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary				   int ioaddr, int irq, int if_port,
276ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary				   enum el3_cardtype type)
277ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary{
278ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	struct el3_private *lp = netdev_priv(dev);
279ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
280ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	memcpy(dev->dev_addr, phys_addr, ETH_ALEN);
281ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	dev->base_addr = ioaddr;
282ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	dev->irq = irq;
283ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	dev->if_port = if_port;
284ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	lp->type = type;
285ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary}
286ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
287ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int __devinit el3_isa_match(struct device *pdev,
288ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary				   unsigned int ndev)
289ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary{
290ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	struct net_device *dev;
291ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	int ioaddr, isa_irq, if_port, err;
292ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	unsigned int iobase;
293ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	__be16 phys_addr[3];
294ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
295ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	while ((err = el3_isa_id_sequence(phys_addr)) == 2)
296ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		;	/* Skip to next card when PnP card found */
297ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (err == 1)
298ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		return 0;
299ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
300ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	iobase = id_read_eeprom(8);
301ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if_port = iobase >> 14;
302ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	ioaddr = 0x200 + ((iobase & 0x1f) << 4);
303ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (irq[el3_cards] > 1 && irq[el3_cards] < 16)
304ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		isa_irq = irq[el3_cards];
305ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	else
306ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		isa_irq = id_read_eeprom(9) >> 12;
307ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
308ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	dev = alloc_etherdev(sizeof(struct el3_private));
309ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (!dev)
310ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		return -ENOMEM;
311ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
312ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	netdev_boot_setup_check(dev);
313ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
314ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-isa")) {
315ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		free_netdev(dev);
316ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		return 0;
317ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	}
318ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
319ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	/* Set the adaptor tag so that the next card can be found. */
320ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	outb(0xd0 + ++current_tag, id_port);
321ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
322ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	/* Activate the adaptor at the EEPROM location. */
323ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	outb((ioaddr >> 4) | 0xe0, id_port);
324ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
325ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	EL3WINDOW(0);
326ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (inw(ioaddr) != 0x6d50) {
327ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		free_netdev(dev);
328ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		return 0;
329ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	}
330ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
331ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	/* Free the interrupt so that some other card can use it. */
332ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	outw(0x0f00, ioaddr + WN0_IRQ);
333ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
334ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	el3_dev_fill(dev, phys_addr, ioaddr, isa_irq, if_port, EL3_ISA);
335ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	dev_set_drvdata(pdev, dev);
336ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (el3_common_init(dev)) {
337ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		free_netdev(dev);
338ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		return 0;
339ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	}
340ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
341ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	el3_devs[el3_cards++] = dev;
342ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	return 1;
343ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary}
344ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
345ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int __devexit el3_isa_remove(struct device *pdev,
346ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary				    unsigned int ndev)
347ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary{
348ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	el3_device_remove(pdev);
349ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	dev_set_drvdata(pdev, NULL);
350ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	return 0;
351ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary}
352ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
353ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PM
354ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int el3_isa_suspend(struct device *dev, unsigned int n,
355ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary			   pm_message_t state)
356ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary{
357ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	current_tag = 0;
358ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	return el3_suspend(dev, state);
359ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary}
360ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
361ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int el3_isa_resume(struct device *dev, unsigned int n)
362ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary{
363ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	struct net_device *ndev = dev_get_drvdata(dev);
364ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	int ioaddr = ndev->base_addr, err;
365ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	__be16 phys_addr[3];
366ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
367ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	while ((err = el3_isa_id_sequence(phys_addr)) == 2)
368ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		;	/* Skip to next card when PnP card found */
369ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (err == 1)
370ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		return 0;
371ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	/* Set the adaptor tag so that the next card can be found. */
372ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	outb(0xd0 + ++current_tag, id_port);
373ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	/* Enable the card */
374ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	outb((ioaddr >> 4) | 0xe0, id_port);
375ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	EL3WINDOW(0);
376ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (inw(ioaddr) != 0x6d50)
377ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		return 1;
378ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	/* Free the interrupt so that some other card can use it. */
379ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	outw(0x0f00, ioaddr + WN0_IRQ);
380ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	return el3_resume(dev);
381ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary}
382ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif
383ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
384ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic struct isa_driver el3_isa_driver = {
385ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	.match		= el3_isa_match,
386ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	.remove		= __devexit_p(el3_isa_remove),
387ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PM
388ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	.suspend	= el3_isa_suspend,
389ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	.resume		= el3_isa_resume,
390ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif
391ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	.driver		= {
392ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		.name	= "3c509"
393ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	},
394ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary};
395ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int isa_registered;
396ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
397ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PNP
398948252cb9e01d65a89ecadf67be5018351eee15eDavid S. Millerstatic struct pnp_device_id el3_pnp_ids[] = {
399ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	{ .id = "TCM5090" }, /* 3Com Etherlink III (TP) */
400ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	{ .id = "TCM5091" }, /* 3Com Etherlink III */
401ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	{ .id = "TCM5094" }, /* 3Com Etherlink III (combo) */
402ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	{ .id = "TCM5095" }, /* 3Com Etherlink III (TPO) */
403ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	{ .id = "TCM5098" }, /* 3Com Etherlink III (TPC) */
404ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	{ .id = "PNP80f7" }, /* 3Com Etherlink III compatible */
405ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	{ .id = "PNP80f8" }, /* 3Com Etherlink III compatible */
406ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	{ .id = "" }
407ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary};
408ac4bed1375c06af7c76b4615ae661791b62e93efOndrej ZaryMODULE_DEVICE_TABLE(pnp, el3_pnp_ids);
409ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
410ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int __devinit el3_pnp_probe(struct pnp_dev *pdev,
411ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary				    const struct pnp_device_id *id)
412ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary{
413ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	short i;
414ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	int ioaddr, irq, if_port;
4154ec7ffa2df247054d422b48148ad82369a45e986Al Viro	__be16 phys_addr[3];
416ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	struct net_device *dev = NULL;
417ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	int err;
418ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
419ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	ioaddr = pnp_port_start(pdev, 0);
420ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-pnp"))
421ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		return -EBUSY;
422ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	irq = pnp_irq(pdev, 0);
423ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	EL3WINDOW(0);
424ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	for (i = 0; i < 3; i++)
425ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		phys_addr[i] = htons(read_eeprom(ioaddr, i));
426ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if_port = read_eeprom(ioaddr, 8) >> 14;
427ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	dev = alloc_etherdev(sizeof(struct el3_private));
428ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (!dev) {
429ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		release_region(ioaddr, EL3_IO_EXTENT);
430ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		return -ENOMEM;
431ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	}
432ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	SET_NETDEV_DEV(dev, &pdev->dev);
433ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	netdev_boot_setup_check(dev);
434ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
435ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_PNP);
436ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	pnp_set_drvdata(pdev, dev);
437ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	err = el3_common_init(dev);
438ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
439ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (err) {
440ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		pnp_set_drvdata(pdev, NULL);
441ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		free_netdev(dev);
442ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		return err;
443ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	}
444ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
445ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	el3_devs[el3_cards++] = dev;
446ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	return 0;
447ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary}
448ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
449ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic void __devexit el3_pnp_remove(struct pnp_dev *pdev)
450ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary{
451ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	el3_common_remove(pnp_get_drvdata(pdev));
452ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	pnp_set_drvdata(pdev, NULL);
453ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary}
454ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
455ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PM
456ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int el3_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
457ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary{
458ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	return el3_suspend(&pdev->dev, state);
459ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary}
460ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
461ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int el3_pnp_resume(struct pnp_dev *pdev)
462ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary{
463ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	return el3_resume(&pdev->dev);
464ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary}
465ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif
466ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
467ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic struct pnp_driver el3_pnp_driver = {
468ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	.name		= "3c509",
469ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	.id_table	= el3_pnp_ids,
470ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	.probe		= el3_pnp_probe,
471ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	.remove		= __devexit_p(el3_pnp_remove),
472ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PM
473ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	.suspend	= el3_pnp_suspend,
474ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	.resume		= el3_pnp_resume,
475ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif
476ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary};
477ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int pnp_registered;
478ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif /* CONFIG_PNP */
479ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_EISA
481948252cb9e01d65a89ecadf67be5018351eee15eDavid S. Millerstatic struct eisa_device_id el3_eisa_ids[] = {
482cf9f6e21c155d5add733b969c695837ead79eeabMaciej W. Rozycki		{ "TCM5090" },
483cf9f6e21c155d5add733b969c695837ead79eeabMaciej W. Rozycki		{ "TCM5091" },
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{ "TCM5092" },
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{ "TCM5093" },
486cf9f6e21c155d5add733b969c695837ead79eeabMaciej W. Rozycki		{ "TCM5094" },
487f04e3f092a855ce798f274b38712b90d51b73bcaAdrian Bunk		{ "TCM5095" },
488cf9f6e21c155d5add733b969c695837ead79eeabMaciej W. Rozycki		{ "TCM5098" },
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{ "" }
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
49107563c711fbc25389e58ab9c9f0b9de2fce56760Michael TokarevMODULE_DEVICE_TABLE(eisa, el3_eisa_ids);
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el3_eisa_probe (struct device *device);
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct eisa_driver el3_eisa_driver = {
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.id_table = el3_eisa_ids,
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver   = {
498ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary				.name    = "3c579",
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				.probe   = el3_eisa_probe,
50060a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enberg				.remove  = __devexit_p (el3_device_remove),
50160a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enberg				.suspend = el3_suspend,
50260a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enberg				.resume  = el3_resume,
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
505ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int eisa_registered;
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_MCA
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el3_mca_probe(struct device *dev);
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511948252cb9e01d65a89ecadf67be5018351eee15eDavid S. Millerstatic short el3_mca_adapter_ids[] __initdata = {
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		0x627c,
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		0x627d,
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		0x62db,
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		0x62f6,
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		0x62f7,
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		0x0000
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
520948252cb9e01d65a89ecadf67be5018351eee15eDavid S. Millerstatic char *el3_mca_adapter_names[] __initdata = {
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		"3Com 3c529 EtherLink III (10base2)",
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		"3Com 3c529 EtherLink III (10baseT)",
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		"3Com 3c529 EtherLink III (test mode)",
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		"3Com 3c529 EtherLink III (TP or coax)",
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		"3Com 3c529 EtherLink III (TP)",
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		NULL
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mca_driver el3_mca_driver = {
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.id_table = el3_mca_adapter_ids,
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver = {
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				.name = "3c529",
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				.bus = &mca_bus_type,
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				.probe = el3_mca_probe,
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				.remove = __devexit_p(el3_device_remove),
53660a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enberg				.suspend = el3_suspend,
53760a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enberg				.resume  = el3_resume,
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		},
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
540ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic int mca_registered;
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* CONFIG_MCA */
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5433186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemmingerstatic const struct net_device_ops netdev_ops = {
5443186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger	.ndo_open 		= el3_open,
5453186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger	.ndo_stop	 	= el3_close,
5463186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger	.ndo_start_xmit 	= el3_start_xmit,
5473186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger	.ndo_get_stats 		= el3_get_stats,
548afc4b13df143122f99a0eb10bfefb216c2806de0Jiri Pirko	.ndo_set_rx_mode	= set_multicast_list,
5493186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger	.ndo_tx_timeout 	= el3_tx_timeout,
5503186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger	.ndo_change_mtu		= eth_change_mtu,
5513186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger	.ndo_set_mac_address 	= eth_mac_addr,
5523186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger	.ndo_validate_addr	= eth_validate_addr,
5533186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger#ifdef CONFIG_NET_POLL_CONTROLLER
5543186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger	.ndo_poll_controller	= el3_poll_controller,
5553186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger#endif
5563186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger};
5573186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger
55837b2a1791c8b8d630944afbe0745a08c8e8ae091Al Virostatic int __devinit el3_common_init(struct net_device *dev)
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct el3_private *lp = netdev_priv(dev);
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
5620795af5729b18218767fab27c44b1384f72dc9adJoe Perches	const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&lp->lock);
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->mem_start & 0x05) { /* xcvr codes 1/3/4/12 */
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->if_port = (dev->mem_start & 0x0f);
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else { /* xcvr codes 0/8 */
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* use eeprom value, but save user's full-duplex selection */
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->if_port |= (dev->mem_start & 0x08);
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The EL3-specific entries in the device structure. */
5743186ae8f3f5a30ecfed9faa76ce113830da39fbdStephen Hemminger	dev->netdev_ops = &netdev_ops;
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->watchdog_timeo = TX_TIMEOUT;
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	SET_ETHTOOL_OPS(dev, &ethtool_ops);
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = register_netdev(dev);
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err) {
580646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_err("Failed to register 3c5x9 at %#3.3lx, IRQ %d.\n",
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->base_addr, dev->irq);
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_region(dev->base_addr, EL3_IO_EXTENT);
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return err;
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
586646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov	pr_info("%s: 3c5x9 found at %#3.3lx, %s port, address %pM, IRQ %d.\n",
5870795af5729b18218767fab27c44b1384f72dc9adJoe Perches	       dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)],
588e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	       dev->dev_addr, dev->irq);
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el3_debug > 0)
591646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_info("%s", version);
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el3_common_remove (struct net_device *dev)
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_netdev (dev);
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(dev->base_addr, EL3_IO_EXTENT);
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_netdev (dev);
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_MCA
604948252cb9e01d65a89ecadf67be5018351eee15eDavid S. Millerstatic int __init el3_mca_probe(struct device *device)
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Based on Erik Nygren's (nygren@mit.edu) 3c529 patch,
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * heavily modified by Chris Beauregard
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * (cpbeaure@csclub.uwaterloo.ca) to support standard MCA
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * probing.
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * redone for multi-card detection by ZP Gu (zpg@castle.net)
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * now works as a module */
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short i;
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr, irq, if_port;
6164ec7ffa2df247054d422b48148ad82369a45e986Al Viro	__be16 phys_addr[3];
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev = NULL;
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u_char pos4, pos5;
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct mca_device *mdev = to_mca_device(device);
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int slot = mdev->slot;
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pos4 = mca_device_read_stored_pos(mdev, 4);
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pos5 = mca_device_read_stored_pos(mdev, 5);
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ioaddr = ((short)((pos4&0xfc)|0x02)) << 8;
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irq = pos5 & 0x0f;
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
630646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov	pr_info("3c529: found %s at slot %d\n",
631646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		el3_mca_adapter_names[mdev->index], slot + 1);
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* claim the slot */
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	strncpy(mdev->name, el3_mca_adapter_names[mdev->index],
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sizeof(mdev->name));
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mca_device_set_claim(mdev, 1);
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if_port = pos4 & 0x03;
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irq = mca_device_transform_irq(mdev, irq);
6416aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	ioaddr = mca_device_transform_ioport(mdev, ioaddr);
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el3_debug > 2) {
643646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_debug("3c529: irq %d  ioaddr 0x%x  ifport %d\n", irq, ioaddr, if_port);
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(0);
6464ec7ffa2df247054d422b48148ad82369a45e986Al Viro	for (i = 0; i < 3; i++)
6474ec7ffa2df247054d422b48148ad82369a45e986Al Viro		phys_addr[i] = htons(read_eeprom(ioaddr, i));
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev = alloc_etherdev(sizeof (struct el3_private));
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev == NULL) {
6514ec7ffa2df247054d422b48148ad82369a45e986Al Viro		release_region(ioaddr, EL3_IO_EXTENT);
6524ec7ffa2df247054d422b48148ad82369a45e986Al Viro		return -ENOMEM;
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netdev_boot_setup_check(dev);
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
657ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_MCA);
6581aec5bdfed91b50aedbcad43393bcb05033c7ef3Greg Kroah-Hartman	dev_set_drvdata(device, dev);
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = el3_common_init(dev);
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err) {
6621aec5bdfed91b50aedbcad43393bcb05033c7ef3Greg Kroah-Hartman		dev_set_drvdata(device, NULL);
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_netdev(dev);
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
667ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	el3_devs[el3_cards++] = dev;
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6706aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* CONFIG_MCA */
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_EISA
674948252cb9e01d65a89ecadf67be5018351eee15eDavid S. Millerstatic int __init el3_eisa_probe (struct device *device)
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short i;
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr, irq, if_port;
6784ec7ffa2df247054d422b48148ad82369a45e986Al Viro	__be16 phys_addr[3];
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev = NULL;
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct eisa_device *edev;
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Yeepee, The driver framework is calling us ! */
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	edev = to_eisa_device (device);
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ioaddr = edev->base_addr;
6866aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
687ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (!request_region(ioaddr, EL3_IO_EXTENT, "3c579-eisa"))
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Change the register set to the configuration window 0. */
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irq = inw(ioaddr + WN0_IRQ) >> 12;
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if_port = inw(ioaddr + 6)>>14;
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 3; i++)
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		phys_addr[i] = htons(read_eeprom(ioaddr, i));
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Restore the "Product ID" to the EEPROM read register. */
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	read_eeprom(ioaddr, 3);
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev = alloc_etherdev(sizeof (struct el3_private));
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev == NULL) {
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_region(ioaddr, EL3_IO_EXTENT);
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netdev_boot_setup_check(dev);
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
709ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_EISA);
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	eisa_set_drvdata (edev, dev);
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = el3_common_init(dev);
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err) {
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		eisa_set_drvdata (edev, NULL);
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_netdev(dev);
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return err;
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
719ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	el3_devs[el3_cards++] = dev;
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This remove works for all device types.
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7261aec5bdfed91b50aedbcad43393bcb05033c7ef3Greg Kroah-Hartman * The net dev must be stored in the driver data field */
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __devexit el3_device_remove (struct device *device)
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7311aec5bdfed91b50aedbcad43393bcb05033c7ef3Greg Kroah-Hartman	dev = dev_get_drvdata(device);
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	el3_common_remove (dev);
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Read a word from the EEPROM using the regular EEPROM access register.
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Assume that we are in register window zero.
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ushort read_eeprom(int ioaddr, int index)
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(EEPROM_READ + index, ioaddr + 10);
7436aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	/* Pause for at least 162 us. for the read to take place.
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Some chips seem to require much longer */
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mdelay(2);
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return inw(ioaddr + 12);
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Read a word from the EEPROM when in the ISA ID probe state. */
750ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zarystatic ushort id_read_eeprom(int index)
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int bit, word = 0;
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Issue read command, and pause for at least 162 us. for it to complete.
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Assume extra-fast 16Mhz bus. */
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(EEPROM_READ + index, id_port);
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Pause for at least 162 us. for the read to take place. */
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Some chips seem to require much longer */
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mdelay(4);
7616aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (bit = 15; bit >= 0; bit--)
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		word = (word << 1) + (inb(id_port) & 0x01);
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el3_debug > 3)
766646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_debug("  3c509 EEPROM word %d %#4.4x.\n", index, word);
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return word;
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel3_open(struct net_device *dev)
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(TxReset, ioaddr + EL3_CMD);
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(RxReset, ioaddr + EL3_CMD);
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
782a0607fd3a25ba1848a63a0d925e36d914735ab47Joe Perches	i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev);
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i)
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return i;
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(0);
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el3_debug > 3)
788646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_debug("%s: Opening, IRQ %d	 status@%x %4.4x.\n", dev->name,
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	el3_up(dev);
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el3_debug > 3)
794646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_debug("%s: Opened 3c509  IRQ %d  status %4.4x.\n",
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel3_tx_timeout (struct net_device *dev)
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Transmitter timeout, serious problems. */
806646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov	pr_warning("%s: transmit timed out, Tx_status %2.2x status %4.4x Tx FIFO room %d.\n",
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS),
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   inw(ioaddr + TX_FREE));
809815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas	dev->stats.tx_errors++;
8101ae5dc342ac78d7a42965fd1f323815f6f5ef2c1Eric Dumazet	dev->trans_start = jiffies; /* prevent tx timeout */
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Issue TX_RESET and TX_START commands. */
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(TxReset, ioaddr + EL3_CMD);
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(TxEnable, ioaddr + EL3_CMD);
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netif_wake_queue(dev);
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
81827a1de95a1461ec0589005c293d6ac23a46cb72dStephen Hemmingerstatic netdev_tx_t
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel3_start_xmit(struct sk_buff *skb, struct net_device *dev)
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct el3_private *lp = netdev_priv(dev);
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netif_stop_queue (dev);
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
827815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas	dev->stats.tx_bytes += skb->len;
8286aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el3_debug > 4) {
830646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n",
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   dev->name, skb->len, inw(ioaddr + EL3_STATUS));
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef final_version
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{	/* Error-checking code, delete someday. */
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ushort status = inw(ioaddr + EL3_STATUS);
8378e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		if (status & 0x0001 && 		/* IRQ line active, missed one. */
8388e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		    inw(ioaddr + EL3_STATUS) & 1) { 			/* Make sure. */
839646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov			pr_debug("%s: Missed interrupt, status then %04x now %04x"
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   "  Tx %2.2x Rx %4.4x.\n", dev->name, status,
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   inw(ioaddr + RX_STATUS));
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Fake interrupt trigger by masking, acknowledge interrupts. */
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 ioaddr + EL3_CMD);
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	We lock the driver against other processors. Note
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	we don't need to lock versus the IRQ as we suspended
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	that. This means that we lose the ability to take
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	an RX during a TX upload. That sucks a bit with SMP
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	on an original 3c509 (2K buffer)
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	Using disable_irq stops us crapping on other
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	time sensitive devices.
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&lp->lock, flags);
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Put out the doubleword header... */
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(skb->len, ioaddr + TX_FIFO);
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(0x00, ioaddr + TX_FIFO);
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* ... and the packet rounded to a doubleword. */
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (inw(ioaddr + TX_FREE) > 1536)
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		netif_start_queue(dev);
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Interrupt us when the FIFO has room for max-sized packet. */
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&lp->lock, flags);
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_kfree_skb (skb);
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Clear the Tx status stack. */
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		short tx_status;
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int i = 4;
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (--i > 0	&&	(tx_status = inb(ioaddr + TX_STATUS)) > 0) {
887815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas			if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8936ed106549d17474ca17a16057f4c0ed4eba5a7caPatrick McHardy	return NETDEV_TX_OK;
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The EL3 interrupt handler. */
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic irqreturn_t
8987d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsel3_interrupt(int irq, void *dev_id)
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
900c31f28e778ab299a5035ea2bda64f245b8915d7cJeff Garzik	struct net_device *dev = dev_id;
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct el3_private *lp;
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr, status;
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i = max_interrupt_work;
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	lp = netdev_priv(dev);
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock(&lp->lock);
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ioaddr = dev->base_addr;
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el3_debug > 4) {
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = inw(ioaddr + EL3_STATUS);
912646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_debug("%s: interrupt, status %4.4x.\n", dev->name, status);
9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while ((status = inw(ioaddr + EL3_STATUS)) &
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   (IntLatch | RxComplete | StatsFull)) {
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (status & RxComplete)
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			el3_rx(dev);
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (status & TxAvailable) {
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (el3_debug > 5)
923646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov				pr_debug("	TX room bit was handled.\n");
9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* There's room in the FIFO for a full-sized packet. */
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			netif_wake_queue (dev);
9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) {
9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Handle all uncommon interrupts. */
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (status & StatsFull)				/* Empty statistics. */
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				update_stats(dev);
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (status & RxEarly) {				/* Rx early is unused. */
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				el3_rx(dev);
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (status & TxComplete) {			/* Really Tx error. */
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				short tx_status;
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				int i = 4;
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
941815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas					if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (status & AdapterFailure) {
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* Adapter failure requires Rx reset and reinit. */
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				outw(RxReset, ioaddr + EL3_CMD);
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* Set the Rx filter to the current state. */
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				outw(SetRxFilter | RxStation | RxBroadcast
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					 | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					 | (dev->flags & IFF_PROMISC ? RxProm : 0),
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					 ioaddr + EL3_CMD);
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (--i < 0) {
961646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov			pr_err("%s: Infinite loop in interrupt, status %4.4x.\n",
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   dev->name, status);
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Clear all interrupts. */
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Acknowledge the IRQ. */
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el3_debug > 4) {
972646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name,
9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   inw(ioaddr + EL3_STATUS));
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock(&lp->lock);
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_HANDLED;
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_NET_POLL_CONTROLLER
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Polling receive - used by netconsole and other diagnostic tools
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to allow network i/o with interrupts disabled.
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el3_poll_controller(struct net_device *dev)
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	disable_irq(dev->irq);
9887d12e780e003f93433d49ce78cfedf4b4c52adc5David Howells	el3_interrupt(dev->irq, dev);
9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	enable_irq(dev->irq);
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct net_device_stats *
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel3_get_stats(struct net_device *dev)
9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct el3_private *lp = netdev_priv(dev);
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	This is fast enough not to bother with disable IRQ
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	stuff.
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
10036aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&lp->lock, flags);
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	update_stats(dev);
10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&lp->lock, flags);
1007815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas	return &dev->stats;
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*  Update statistics.  We change to register window 6, so this should be run
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	single-threaded if the device is active. This is expected to be a rare
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	operation, and it's simpler for the rest of the driver to assume that
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	window 1 is always valid rather than use a special window-state variable.
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void update_stats(struct net_device *dev)
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el3_debug > 5)
1020646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_debug("   Updating the statistics.\n");
10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Turn off statistics updates while reading. */
10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(StatsDisable, ioaddr + EL3_CMD);
10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Switch to the stats window, and read everything. */
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(6);
1025815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas	dev->stats.tx_carrier_errors 	+= inb(ioaddr + 0);
1026815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas	dev->stats.tx_heartbeat_errors	+= inb(ioaddr + 1);
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Multiple collisions. */	   inb(ioaddr + 2);
1028815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas	dev->stats.collisions		+= inb(ioaddr + 3);
1029815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas	dev->stats.tx_window_errors	+= inb(ioaddr + 4);
1030815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas	dev->stats.rx_fifo_errors	+= inb(ioaddr + 5);
1031815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas	dev->stats.tx_packets		+= inb(ioaddr + 6);
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Rx packets	*/		   inb(ioaddr + 7);
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Tx deferrals */		   inb(ioaddr + 8);
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	inw(ioaddr + 10);	/* Total Rx and Tx octets. */
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	inw(ioaddr + 12);
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Back to window 1, and turn statistics back on. */
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(1);
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(StatsEnable, ioaddr + EL3_CMD);
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel3_rx(struct net_device *dev)
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short rx_status;
10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el3_debug > 5)
1049646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_debug("   In rx_packet(), status %4.4x, rx_status %4.4x.\n",
10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rx_status & 0x4000) { /* Error, update stats. */
10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			short error = rx_status & 0x3800;
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outw(RxDiscard, ioaddr + EL3_CMD);
1056815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas			dev->stats.rx_errors++;
10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch (error) {
1058815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas			case 0x0000:		dev->stats.rx_over_errors++; break;
1059815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas			case 0x0800:		dev->stats.rx_length_errors++; break;
1060815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas			case 0x1000:		dev->stats.rx_frame_errors++; break;
1061815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas			case 0x1800:		dev->stats.rx_length_errors++; break;
1062815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas			case 0x2000:		dev->stats.rx_frame_errors++; break;
1063815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas			case 0x2800:		dev->stats.rx_crc_errors++; break;
10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			short pkt_len = rx_status & 0x7ff;
10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct sk_buff *skb;
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10691d266430546acf01438ae42d0a7370db4817e2adPradeep A Dalvi			skb = netdev_alloc_skb(dev, pkt_len + 5);
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (el3_debug > 4)
1071646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov				pr_debug("Receiving packet size %d status %4.4x.\n",
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   pkt_len, rx_status);
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (skb != NULL) {
10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				skb_reserve(skb, 2);     /* Align IP on 16 byte */
10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* 'skb->data' points to the start of sk_buff data area. */
10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len),
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					 (pkt_len + 3) >> 2);
10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				skb->protocol = eth_type_trans(skb,dev);
10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				netif_rx(skb);
1083f7f312a0c7e7a1947cf193e0e94a257ad7742cb2Wang Chen				dev->stats.rx_bytes += pkt_len;
1084815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas				dev->stats.rx_packets++;
10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				continue;
10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outw(RxDiscard, ioaddr + EL3_CMD);
1088815f8802d201aba1ce343ba832daf639165f01a1Paulius Zaleckas			dev->stats.rx_dropped++;
10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (el3_debug)
1090646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov				pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n",
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   dev->name, pkt_len);
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		inw(ioaddr + EL3_STATUS); 				/* Delay. */
10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (inw(ioaddr + EL3_STATUS) & 0x1000)
1095646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov			pr_debug("	Waiting for 3c509 to discard packet, status %x.\n",
10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   inw(ioaddr + EL3_STATUS) );
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *     Set or clear the multicast filter for this adaptor.
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsset_multicast_list(struct net_device *dev)
11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct el3_private *lp = netdev_priv(dev);
11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
11114cd24eaf0c6ee7f0242e34ee77ec899f255e66b5Jiri Pirko	int mc_count = netdev_mc_count(dev);
11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el3_debug > 1) {
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		static int old;
11154cd24eaf0c6ee7f0242e34ee77ec899f255e66b5Jiri Pirko		if (old != mc_count) {
11164cd24eaf0c6ee7f0242e34ee77ec899f255e66b5Jiri Pirko			old = mc_count;
11174cd24eaf0c6ee7f0242e34ee77ec899f255e66b5Jiri Pirko			pr_debug("%s: Setting Rx mode to %d addresses.\n",
11184cd24eaf0c6ee7f0242e34ee77ec899f255e66b5Jiri Pirko				 dev->name, mc_count);
11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&lp->lock, flags);
11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->flags&IFF_PROMISC) {
11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 ioaddr + EL3_CMD);
11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11264cd24eaf0c6ee7f0242e34ee77ec899f255e66b5Jiri Pirko	else if (mc_count || (dev->flags&IFF_ALLMULTI)) {
11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD);
11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&lp->lock, flags);
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel3_close(struct net_device *dev)
11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct el3_private *lp = netdev_priv(dev);
11396aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el3_debug > 2)
1141646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_debug("%s: Shutting down ethercard.\n", dev->name);
11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	el3_down(dev);
11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(dev->irq, dev);
11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Switching back to window 0 disables the IRQ. */
11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(0);
11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (lp->type != EL3_EISA) {
11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* But we explicitly zero the IRQ line select anyway. Don't do
11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * it on EISA cards, it prevents the module from getting an
11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * IRQ after unload+reload... */
11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outw(0x0f00, ioaddr + WN0_IRQ);
11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11586aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzikstatic int
11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel3_link_ok(struct net_device *dev)
11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 tmp;
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(4);
11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tmp = inw(ioaddr + WN4_MEDIA);
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(1);
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return tmp & (1<<11);
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel3_netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 tmp;
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
11756aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(0);
11776aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	/* obtain current transceiver via WN4_MEDIA? */
11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tmp = inw(ioaddr + WN0_ADDR_CONF);
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ecmd->transceiver = XCVR_INTERNAL;
11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (tmp >> 14) {
11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ecmd->port = PORT_TP;
11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
11841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 1:
11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ecmd->port = PORT_AUI;
11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ecmd->transceiver = XCVR_EXTERNAL;
11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
11881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 3:
11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ecmd->port = PORT_BNC;
11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
11911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ecmd->duplex = DUPLEX_HALF;
11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ecmd->supported = 0;
11961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tmp = inw(ioaddr + WN0_CONF_CTRL);
11971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tmp & (1<<13))
11981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ecmd->supported |= SUPPORTED_AUI;
11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tmp & (1<<12))
12001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ecmd->supported |= SUPPORTED_BNC;
12011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tmp & (1<<9)) {
12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ecmd->supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half |
12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				SUPPORTED_10baseT_Full;	/* hmm... */
12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		EL3WINDOW(4);
12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp = inw(ioaddr + WN4_NETDIAG);
12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tmp & FD_ENABLE)
12071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ecmd->duplex = DUPLEX_FULL;
12081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1210707394972093e2056e1e8cc39be19cf9bcb3e7b3David Decotigny	ethtool_cmd_speed_set(ecmd, SPEED_10);
12111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(1);
12121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
12131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
12161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel3_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
12171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
12181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 tmp;
12191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ecmd->speed != SPEED_10)
12221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
12231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ecmd->duplex != DUPLEX_HALF) && (ecmd->duplex != DUPLEX_FULL))
12241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
12251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ecmd->transceiver != XCVR_INTERNAL) && (ecmd->transceiver != XCVR_EXTERNAL))
12261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
12271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* change XCVR type */
12291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(0);
12301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tmp = inw(ioaddr + WN0_ADDR_CONF);
12311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (ecmd->port) {
12321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case PORT_TP:
12331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp &= ~(3<<14);
12341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->if_port = 0;
12351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
12361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case PORT_AUI:
12371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp |= (1<<14);
12381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->if_port = 1;
12391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
12401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case PORT_BNC:
12411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp |= (3<<14);
12421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->if_port = 3;
12431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
12441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
12451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
12461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(tmp, ioaddr + WN0_ADDR_CONF);
12491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->if_port == 3) {
12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* fire up the DC-DC convertor if BNC gets enabled */
12511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp = inw(ioaddr + WN0_ADDR_CONF);
12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tmp & (3 << 14)) {
12531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outw(StartCoax, ioaddr + EL3_CMD);
12541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			udelay(800);
12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else
12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EIO;
12571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(4);
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tmp = inw(ioaddr + WN4_NETDIAG);
12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ecmd->duplex == DUPLEX_FULL)
12621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp |= FD_ENABLE;
12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp &= ~FD_ENABLE;
12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(tmp, ioaddr + WN4_NETDIAG);
12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(1);
12671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
12691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
12721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
12731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	strcpy(info->driver, DRV_NAME);
12741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	strcpy(info->version, DRV_VERSION);
12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el3_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
12791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct el3_private *lp = netdev_priv(dev);
12801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irq(&lp->lock);
12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = el3_netdev_get_ecmd(dev, ecmd);
12841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irq(&lp->lock);
12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el3_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct el3_private *lp = netdev_priv(dev);
12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irq(&lp->lock);
12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = el3_netdev_set_ecmd(dev, ecmd);
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irq(&lp->lock);
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic u32 el3_get_link(struct net_device *dev)
13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
13011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct el3_private *lp = netdev_priv(dev);
13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 ret;
13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irq(&lp->lock);
13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = el3_link_ok(dev);
13061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irq(&lp->lock);
13071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic u32 el3_get_msglevel(struct net_device *dev)
13111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
13121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return el3_debug;
13131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el3_set_msglevel(struct net_device *dev, u32 v)
13161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
13171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	el3_debug = v;
13181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13207282d491ecaee9883233a0e27283c4c79486279aJeff Garzikstatic const struct ethtool_ops ethtool_ops = {
13211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.get_drvinfo = el3_get_drvinfo,
13221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.get_settings = el3_get_settings,
13231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_settings = el3_set_settings,
13241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.get_link = el3_get_link,
13251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.get_msglevel = el3_get_msglevel,
13261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_msglevel = el3_set_msglevel,
13271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
13281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
13301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel3_down(struct net_device *dev)
13311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
13321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
13331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netif_stop_queue(dev);
13351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Turn off statistics ASAP.  We update lp->stats below. */
13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(StatsDisable, ioaddr + EL3_CMD);
13381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Disable the receiver and transmitter. */
13401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(RxDisable, ioaddr + EL3_CMD);
13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(TxDisable, ioaddr + EL3_CMD);
13421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->if_port == 3)
13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Turn off thinnet power.  Green! */
13451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outw(StopCoax, ioaddr + EL3_CMD);
13461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (dev->if_port == 0) {
13471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Disable link beat and jabber, if_port may change here next open(). */
13481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		EL3WINDOW(4);
13491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
13501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
13511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
13531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	update_stats(dev);
13551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
13581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel3_up(struct net_device *dev)
13591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
13601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, sw_info, net_diag;
13611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
13626aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
13631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Activating the board required and does no harm otherwise */
13641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(0x0001, ioaddr + 4);
13651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Set the IRQ line. */
13671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
13681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Set the station address in window 2 each time opened. */
13701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(2);
13711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 6; i++)
13731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(dev->dev_addr[i], ioaddr + i);
13741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((dev->if_port & 0x03) == 3) /* BNC interface */
13761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Start the thinnet transceiver. We should really wait 50ms...*/
13771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outw(StartCoax, ioaddr + EL3_CMD);
13781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if ((dev->if_port & 0x03) == 0) { /* 10baseT interface */
13791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Combine secondary sw_info word (the adapter level) and primary
13801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sw_info word (duplex setting plus other useless bits) */
13811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		EL3WINDOW(0);
13826aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik		sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) |
13831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			(read_eeprom(ioaddr, 0x0d) & 0xBff0);
13841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		EL3WINDOW(4);
13861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		net_diag = inw(ioaddr + WN4_NETDIAG);
13871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */
1388646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_info("%s: ", dev->name);
13891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (dev->if_port & 0x0c) {
13901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case 12:
13911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* force full-duplex mode if 3c5x9b */
13921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (sw_info & 0x000f) {
1393646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov					pr_cont("Forcing 3c5x9b full-duplex mode");
13941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
13951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
13961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case 8:
13971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* set full-duplex mode based on eeprom config setting */
13981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if ((sw_info & 0x000f) && (sw_info & 0x8000)) {
1399646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov					pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)");
14001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
14011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
14021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
14031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */
1404646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov				pr_cont("Setting 3c5x9/3c5x9B half-duplex mode");
14051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */
14061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
14071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outw(net_diag, ioaddr + WN4_NETDIAG);
1409646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_cont(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info);
14101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (el3_debug > 3)
1411646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov			pr_debug("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag);
14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Enable link beat and jabber check. */
14131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Switch to the stats window, and clear all stats by reading. */
14171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(StatsDisable, ioaddr + EL3_CMD);
14181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(6);
14191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 9; i++)
14201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		inb(ioaddr + i);
14211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	inw(ioaddr + 10);
14221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	inw(ioaddr + 12);
14231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Switch to register set 1 for normal use. */
14251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	EL3WINDOW(1);
14261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Accept b-case and phys addr only. */
14281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
14291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
14301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
14321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
14331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Allow status bits to be seen. */
14341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
14351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Ack all pending events, and set active indicator mask. */
14361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
14371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 ioaddr + EL3_CMD);
14381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull,
14391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 ioaddr + EL3_CMD);
14401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netif_start_queue(dev);
14421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Power Management support functions */
1445ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PM
14461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
144860a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enbergel3_suspend(struct device *pdev, pm_message_t state)
14491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
14511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
14521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct el3_private *lp;
14531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr;
14546aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
14551aec5bdfed91b50aedbcad43393bcb05033c7ef3Greg Kroah-Hartman	dev = dev_get_drvdata(pdev);
14561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	lp = netdev_priv(dev);
14571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ioaddr = dev->base_addr;
14581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&lp->lock, flags);
14601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (netif_running(dev))
14621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		netif_device_detach(dev);
14631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	el3_down(dev);
14651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(PowerDown, ioaddr + EL3_CMD);
14661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&lp->lock, flags);
14681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
14691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
147260a89ff6d2681029b3d46b5d23dccf2903a254b4Pekka Enbergel3_resume(struct device *pdev)
14731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
14751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
14761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct el3_private *lp;
14771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr;
14786aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
14791aec5bdfed91b50aedbcad43393bcb05033c7ef3Greg Kroah-Hartman	dev = dev_get_drvdata(pdev);
14801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	lp = netdev_priv(dev);
14811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ioaddr = dev->base_addr;
14821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&lp->lock, flags);
14841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(PowerUp, ioaddr + EL3_CMD);
1486152abd139cca049c9b559a7cca762fa7fd9fd264Ondrej Zary	EL3WINDOW(0);
14871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	el3_up(dev);
14881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (netif_running(dev))
14901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		netif_device_attach(dev);
14916aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
14921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&lp->lock, flags);
14931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
14941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1496ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif /* CONFIG_PM */
14971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(debug,int, 0);
14991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(irq, int, NULL, 0);
15001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(max_interrupt_work, int, 0);
15011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(debug, "debug level (0-6)");
15021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
15031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
1504ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PNP
15051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(nopnp, int, 0);
15061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)");
1507ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif	/* CONFIG_PNP */
1508ac4bed1375c06af7c76b4615ae661791b62e93efOndrej ZaryMODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B, 3c529, 3c579) ethernet driver");
15091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
15101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init el3_init_module(void)
15121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
15130992a5d029181421877a716eaf99145828ff7eaeAndrew Morton	int ret = 0;
15141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (debug >= 0)
15161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		el3_debug = debug;
15171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1518ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PNP
1519ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (!nopnp) {
1520ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		ret = pnp_register_driver(&el3_pnp_driver);
1521ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		if (!ret)
1522ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary			pnp_registered = 1;
1523ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	}
1524ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif
1525ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	/* Select an open I/O location at 0x1*0 to do ISA contention select. */
1526ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	/* Start with 0x110 to avoid some sound cards.*/
1527ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	for (id_port = 0x110 ; id_port < 0x200; id_port += 0x10) {
1528ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		if (!request_region(id_port, 1, "3c509-control"))
1529ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary			continue;
1530ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		outb(0x00, id_port);
1531ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		outb(0xff, id_port);
1532ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		if (inb(id_port) & 0x01)
1533ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary			break;
1534ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		else
1535ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary			release_region(id_port, 1);
1536ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	}
1537ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (id_port >= 0x200) {
1538ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		id_port = 0;
1539646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_err("No I/O port available for 3c509 activation.\n");
1540ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	} else {
1541ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		ret = isa_register_driver(&el3_isa_driver, EL3_MAX_CARDS);
1542ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		if (!ret)
1543ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary			isa_registered = 1;
15441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
15451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_EISA
15460992a5d029181421877a716eaf99145828ff7eaeAndrew Morton	ret = eisa_driver_register(&el3_eisa_driver);
1547ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (!ret)
1548ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		eisa_registered = 1;
15491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
15501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_MCA
1551ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	ret = mca_register_driver(&el3_mca_driver);
1552ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (!ret)
1553ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		mca_registered = 1;
1554ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif
1555ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary
1556ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PNP
1557ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (pnp_registered)
1558ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		ret = 0;
1559ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif
1560ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (isa_registered)
1561ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		ret = 0;
1562ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_EISA
1563ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (eisa_registered)
1564ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		ret = 0;
1565ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif
1566ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_MCA
1567ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (mca_registered)
1568ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		ret = 0;
15691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
15700992a5d029181421877a716eaf99145828ff7eaeAndrew Morton	return ret;
15711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
15721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit el3_cleanup_module(void)
15741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1575ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#ifdef CONFIG_PNP
1576ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (pnp_registered)
1577ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		pnp_unregister_driver(&el3_pnp_driver);
1578ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary#endif
1579ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (isa_registered)
1580ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		isa_unregister_driver(&el3_isa_driver);
1581ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (id_port)
1582ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		release_region(id_port, 1);
15831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_EISA
1584ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (eisa_registered)
1585ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		eisa_driver_unregister(&el3_eisa_driver);
15861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
15871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_MCA
1588ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary	if (mca_registered)
1589ac4bed1375c06af7c76b4615ae661791b62e93efOndrej Zary		mca_unregister_driver(&el3_mca_driver);
15901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
15911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
15921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init (el3_init_module);
15941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit (el3_cleanup_module);
1595