11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* ne2.c: A NE/2 Ethernet Driver for Linux. */
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Based on the NE2000 driver written by Donald Becker (1992-94).
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   modified by Wim Dumon (Apr 1996)
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   This software may be used and distributed according to the terms
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   of the GNU General Public License, incorporated herein by reference.
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   The author may be reached as wimpie@linux.cc.kuleuven.ac.be
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Currently supported: NE/2
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   This patch was never tested on other MCA-ethernet adapters, but it
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   might work. Just give it a try and let me know if you have problems.
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Also mail me if it really works, please!
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Changelog:
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Mon Feb  3 16:26:02 MET 1997
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - adapted the driver to work with the 2.1.25 kernel
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - multiple ne2 support (untested)
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - module support (untested)
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Fri Aug 28 00:18:36 CET 1998 (David Weinehall)
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - fixed a few minor typos
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - made the MODULE_PARM conditional (it only works with the v2.1.x kernels)
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - fixed the module support (Now it's working...)
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Mon Sep  7 19:01:44 CET 1998 (David Weinehall)
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - added support for Arco Electronics AE/2-card (experimental)
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Mon Sep 14 09:53:42 CET 1998 (David Weinehall)
316aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik   - added support for Compex ENET-16MC/P (experimental)
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Tue Sep 15 16:21:12 CET 1998 (David Weinehall, Magnus Jonsson, Tomas Ogren)
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Miscellaneous bugfixes
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Tue Sep 19 16:21:12 CET 1998 (Magnus Jonsson)
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Cleanup
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Wed Sep 23 14:33:34 CET 1998 (David Weinehall)
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Restructuring and rewriting for v2.1.x compliance
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Wed Oct 14 17:19:21 CET 1998 (David Weinehall)
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Added code that unregisters irq and proc-info
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Version# bump
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Mon Nov 16 15:28:23 CET 1998 (Wim Dumon)
476aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik   - pass 'dev' as last parameter of request_irq in stead of 'NULL'
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Wed Feb  7 21:24:00 CET 2001 (Alfred Arnold)
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - added support for the D-Link DE-320CT
516aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   *    WARNING
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	-------
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This is alpha-test software.  It is not guaranteed to work. As a
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	matter of fact, I'm quite sure there are *LOTS* of bugs in here. I
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	would like to hear from you if you use this driver, even if it works.
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	If it doesn't work, be sure to send me a mail with the problems !
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char *version = "ne2.c:v0.91 Nov 16 1998 Wim Dumon <wimpie@kotnet.org>\n";
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fcntl.h>
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/in.h>
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mca-legacy.h>
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/etherdevice.h>
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/skbuff.h>
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/bitops.h>
77ff5688ae1cedfb175b5ed0f319d03ad2e5ee005dMarcelo Feitoza Parisi#include <linux/jiffies.h>
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/dma.h>
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "8390.h"
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRV_NAME "ne2"
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Some defines that people can play with if so inclined. */
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Do we perform extra sanity checks on stuff ? */
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* #define NE_SANITY_CHECK */
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Do we implement the read before write bugfix ? */
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* #define NE_RW_BUGFIX */
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* #define PACKETBUF_MEMSIZE	0x40 */
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* ---- No user-serviceable parts below ---- */
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NE_BASE	 (dev->base_addr)
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NE_CMD	 	0x00
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NE_DATAPORT	0x10	/* NatSemi-defined port window offset. */
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NE_RESET	0x20	/* Issue a read to reset, a write to clear. */
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NE_IO_EXTENT	0x30
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NE1SM_START_PG	0x20	/* First page of TX buffer */
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NE1SM_STOP_PG 	0x40	/* Last page +1 of RX ring */
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NESM_START_PG	0x40	/* First page of TX buffer */
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* From the .ADF file: */
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int addresses[7] __initdata =
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{0x1000, 0x2020, 0x8020, 0xa0a0, 0xb0b0, 0xc0c0, 0xc3d0};
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int irqs[4] __initdata = {3, 4, 5, 9};
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* From the D-Link ADF file: */
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int dlink_addresses[4] __initdata =
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                {0x300, 0x320, 0x340, 0x360};
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dlink_irqs[8] __initdata = {3, 4, 5, 9, 10, 11, 14, 15};
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct ne2_adapters_t {
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int	id;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char		*name;
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct ne2_adapters_t ne2_adapters[] __initdata = {
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0x6354, "Arco Ethernet Adapter AE/2" },
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0x70DE, "Compex ENET-16 MC/P" },
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0x7154, "Novell Ethernet Adapter NE/2" },
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        { 0x56ea, "D-Link DE-320CT" },
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0x0000, NULL }
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsextern int netcard_probe(struct net_device *dev);
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ne2_probe1(struct net_device *dev, int slot);
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ne_reset_8390(struct net_device *dev);
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int ring_page);
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ne_block_input(struct net_device *dev, int count,
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct sk_buff *skb, int ring_offset);
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ne_block_output(struct net_device *dev, const int count,
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		const unsigned char *buf, const int start_page);
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1486aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik * special code to read the DE-320's MAC address EEPROM.  In contrast to a
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * standard NE design, this is a serial EEPROM (93C46) that has to be read
1506aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik * bit by bit.  The EEPROM cotrol port at base + 0x1e has the following
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * layout:
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Bit 0 = Data out (read from EEPROM)
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Bit 1 = Data in  (write to EEPROM)
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Bit 2 = Clock
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Bit 3 = Chip Select
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Bit 7 = ~50 kHz clock for defined delays
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __init dlink_put_eeprom(unsigned char value, unsigned int addr)
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int z;
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char v1, v2;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* write the value to the NIC EEPROM register */
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(value, addr + 0x1e);
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* now wait the clock line to toggle twice.  Effectively, we are
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   waiting (at least) for one clock cycle */
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (z = 0; z < 2; z++) {
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		do {
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			v1 = inb(addr + 0x1e);
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			v2 = inb(addr + 0x1e);
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (!((v1 ^ v2) & 0x80));
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __init dlink_send_eeprom_bit(unsigned int bit, unsigned int addr)
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* shift data bit into correct position */
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	bit = bit << 1;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* write value, keep clock line high for two cycles */
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dlink_put_eeprom(0x09 | bit, addr);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dlink_put_eeprom(0x0d | bit, addr);
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dlink_put_eeprom(0x0d | bit, addr);
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dlink_put_eeprom(0x09 | bit, addr);
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __init dlink_send_eeprom_word(unsigned int value, unsigned int len, unsigned int addr)
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int z;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* adjust bits so that they are left-aligned in a 16-bit-word */
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	value = value << (16 - len);
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* shift bits out to the EEPROM */
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (z = 0; z < len; z++) {
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dlink_send_eeprom_bit((value & 0x8000) >> 15, addr);
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		value = value << 1;
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int __init dlink_get_eeprom(unsigned int eeaddr, unsigned int addr)
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int z;
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int value = 0;
2166aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* pull the CS line low for a moment.  This resets the EEPROM-
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   internal logic, and makes it ready for a new command. */
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dlink_put_eeprom(0x01, addr);
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dlink_put_eeprom(0x09, addr);
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* send one start bit, read command (1 - 0), plus the address to
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds           the EEPROM */
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dlink_send_eeprom_word(0x0180 | (eeaddr & 0x3f), 9, addr);
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* get the data word.  We clock by sending 0s to the EEPROM, which
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   get ignored during the read process */
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (z = 0; z < 16; z++) {
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dlink_send_eeprom_bit(0, addr);
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		value = (value << 1) | (inb(addr + 0x1e) & 0x01);
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return value;
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Note that at boot, this probe only picks up one card at a time.
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init do_ne2_probe(struct net_device *dev)
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static int current_mca_slot = -1;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int adapter_found = 0;
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2496aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	/* Do not check any supplied i/o locations.
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   POS registers usually don't fail :) */
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2526aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	/* MCA cards have POS registers.
2536aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	   Autodetecting MCA cards is extremely simple.
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Just search for the card. */
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for(i = 0; (ne2_adapters[i].name != NULL) && !adapter_found; i++) {
2576aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik		current_mca_slot =
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			mca_find_unused_adapter(ne2_adapters[i].id, 0);
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if((current_mca_slot != MCA_NOTFOUND) && !adapter_found) {
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int res;
2626aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik			mca_set_adapter_name(current_mca_slot,
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ne2_adapters[i].name);
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			mca_mark_as_used(current_mca_slot);
2656aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = ne2_probe1(dev, current_mca_slot);
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (res)
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				mca_mark_as_unused(current_mca_slot);
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return res;
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENODEV;
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef MODULE
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct net_device * __init ne2_probe(int unit)
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
278055e5110ae0c0c1176a75b78d789294f2ff2f7afAlan Cox	struct net_device *dev = alloc_eip_netdev();
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ERR_PTR(-ENOMEM);
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sprintf(dev->name, "eth%d", unit);
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netdev_boot_setup_check(dev);
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = do_ne2_probe(dev);
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return dev;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_netdev(dev);
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ERR_PTR(err);
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ne2_procinfo(char *buf, int slot, struct net_device *dev)
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int len=0;
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len += sprintf(buf+len, "The NE/2 Ethernet Adapter\n" );
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len += sprintf(buf+len, "Driver written by Wim Dumon ");
3036aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	len += sprintf(buf+len, "<wimpie@kotnet.org>\n");
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len += sprintf(buf+len, "Modified by ");
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len += sprintf(buf+len, "David Weinehall <tao@acc.umu.se>\n");
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len += sprintf(buf+len, "and by Magnus Jonsson <bigfoot@acc.umu.se>\n");
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len += sprintf(buf+len, "Based on the original NE2000 drivers\n" );
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len += sprintf(buf+len, "Base IO: %#x\n", (unsigned int)dev->base_addr);
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len += sprintf(buf+len, "IRQ    : %d\n", dev->irq);
310e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	len += sprintf(buf+len, "HW addr : %pM\n", dev->dev_addr);
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return len;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init ne2_probe1(struct net_device *dev, int slot)
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, base_addr, irq, retval;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char POS;
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char SA_prom[32];
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	const char *name = "NE/2";
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int start_page, stop_page;
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static unsigned version_printed;
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_debug && version_printed++ == 0)
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(version);
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("NE/2 ethercard found in slot %d:", slot);
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read base IO and IRQ from the POS-registers */
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	POS = mca_read_stored_pos(slot, 2);
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if(!(POS % 2)) {
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(" disabled.\n");
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* handle different POS register structure for D-Link card */
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (mca_read_stored_pos(slot, 0) == 0xea) {
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		base_addr = dlink_addresses[(POS >> 5) & 0x03];
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irq = dlink_irqs[(POS >> 2) & 0x07];
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        else {
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		i = (POS & 0xE)>>1;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* printk("Halleluja sdog, als er na de pijl een 1 staat is 1 - 1 == 0"
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   	" en zou het moeten werken -> %d\n", i);
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   	The above line was for remote testing, thanx to sdog ... */
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		base_addr = addresses[i - 1];
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irq = irqs[(POS & 0x60)>>5];
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region(base_addr, NE_IO_EXTENT, DRV_NAME))
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("POS info : pos 2 = %#x ; base = %#x ; irq = %ld\n", POS,
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			base_addr, irq);
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef CRYNWR_WAY
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Reset the card the way they do it in the Crynwr packet driver */
3616aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	for (i=0; i<8; i++)
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(0x0, base_addr + NE_RESET);
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	inb(base_addr + NE_RESET);
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x21, base_addr + NE_CMD);
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (inb(base_addr + NE_CMD) != 0x21) {
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("NE/2 adapter not responding\n");
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENODEV;
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* In the crynwr sources they do a RAM-test here. I skip it. I suppose
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   my RAM is okay.  Suppose your memory is broken.  Then this test
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   should fail and you won't be able to use your card.  But if I do not
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   test, you won't be able to use your card, neither.  So this test
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   won't help you. */
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else  /* _I_ never tested it this way .. Go ahead and try ...*/
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Reset card. Who knows what dain-bramaged state it was left in. */
3796aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	{
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned long reset_start_time = jiffies;
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3826aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik		/* DON'T change these to inb_p/outb_p or reset will fail on
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   clones.. */
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(inb(base_addr + NE_RESET), base_addr + NE_RESET);
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while ((inb_p(base_addr + EN0_ISR) & ENISR_RESET) == 0)
387ff5688ae1cedfb175b5ed0f319d03ad2e5ee005dMarcelo Feitoza Parisi			if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk(" not found (no reset ack).\n");
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				retval = -ENODEV;
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto out;
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb_p(0xff, base_addr + EN0_ISR);         /* Ack all intr. */
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read the 16 bytes of station address PROM.
3996aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	   We must first initialize registers, similar to
400f0084a36d4d799c024a5211555334d56c91d236dIngo Molnar	   NS8390p_init(eifdev, 0).
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   We can't reliably read the SAPROM address without this.
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   (I learned the hard way!). */
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
4046aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik		struct {
4056aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik			unsigned char value, offset;
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} program_seq[] = {
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						/* Select page 0 */
4086aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik			{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD},
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{0x49,	EN0_DCFG},  /* Set WORD-wide (0x49) access. */
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{0x00,	EN0_RCNTLO},  /* Clear the count regs. */
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{0x00,	EN0_RCNTHI},
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{0x00,	EN0_IMR},  /* Mask completion irq. */
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{0xFF,	EN0_ISR},
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{E8390_RXOFF, EN0_RXCR},  /* 0x20  Set to monitor */
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{E8390_TXOFF, EN0_TXCR},  /* 0x02  and loopback mode. */
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{32,	EN0_RCNTLO},
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{0x00,	EN0_RCNTHI},
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{0x00,	EN0_RSARLO},  /* DMA starting at 0x0000. */
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{0x00,	EN0_RSARHI},
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{E8390_RREAD+E8390_START, E8390_CMD},
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		};
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
423ff8ac60948ba819b89e9c87083e8050fc2f89999Denis Cheng		for (i = 0; i < ARRAY_SIZE(program_seq); i++)
4246aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik			outb_p(program_seq[i].value, base_addr +
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				program_seq[i].offset);
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for(i = 0; i < 6 /*sizeof(SA_prom)*/; i+=1) {
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		SA_prom[i] = inb(base_addr + NE_DATAPORT);
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* I don't know whether the previous sequence includes the general
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds           board reset procedure, so better don't omit it and just overwrite
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds           the garbage read from a DE-320 with correct stuff. */
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (mca_read_stored_pos(slot, 0) == 0xea) {
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned int v;
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < 3; i++) {
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 			v = dlink_get_eeprom(i, base_addr);
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			SA_prom[(i << 1)    ] = v & 0xff;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			SA_prom[(i << 1) + 1] = (v >> 8) & 0xff;
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	start_page = NESM_START_PG;
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	stop_page = NESM_STOP_PG;
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->irq=irq;
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Snarf the interrupt now.  There's no point in waiting since we cannot
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   share and the board will usually be enabled. */
453055e5110ae0c0c1176a75b78d789294f2ff2f7afAlan Cox	retval = request_irq(dev->irq, eip_interrupt, 0, DRV_NAME, dev);
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
4556aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik		printk (" unable to get IRQ %d (irqval=%d).\n",
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dev->irq, retval);
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->base_addr = base_addr;
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
462104bf3fb963cbc39c6675b23d46d2c9ab3f311d8Joe Perches	for (i = 0; i < ETH_ALEN; i++)
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->dev_addr[i] = SA_prom[i];
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
465e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	printk(" %pM\n", dev->dev_addr);
4660795af5729b18218767fab27c44b1384f72dc9adJoe Perches
4670795af5729b18218767fab27c44b1384f72dc9adJoe Perches	printk("%s: %s found at %#x, using IRQ %d.\n",
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->name, name, base_addr, dev->irq);
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mca_set_adapter_procfn(slot, (MCA_ProcFn) ne2_procinfo, dev);
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.name = name;
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.tx_start_page = start_page;
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.stop_page = stop_page;
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.word16 = (2 == 2);
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.rx_start_page = start_page + TX_PAGES;
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef PACKETBUF_MEMSIZE
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Allow the packet buffer size to be overridden by know-it-alls. */
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.reset_8390 = &ne_reset_8390;
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.block_input = &ne_block_input;
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.block_output = &ne_block_output;
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.get_8390_hdr = &ne_get_8390_hdr;
4876aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.priv = slot;
4896aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
49050014f1b939dff5e591e8aea9ec2932d146d2996Stephen Hemminger	dev->netdev_ops = &eip_netdev_ops;
491055e5110ae0c0c1176a75b78d789294f2ff2f7afAlan Cox	NS8390p_init(dev, 0);
492b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>
493b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>	retval = register_netdev(dev);
494b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>	if (retval)
495b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>		goto out1;
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
497b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>out1:
498b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>	mca_set_adapter_procfn( ei_status.priv, NULL, NULL);
499b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>	free_irq(dev->irq, dev);
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(base_addr, NE_IO_EXTENT);
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Hard reset the card.  This used to pause for the same period that a
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   8390 reset command required, but that shouldn't be necessary. */
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ne_reset_8390(struct net_device *dev)
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long reset_start_time = jiffies;
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5116aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	if (ei_debug > 1)
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("resetting the 8390 t=%ld...", jiffies);
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* DON'T change these to inb_p/outb_p or reset will fail on clones. */
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.txing = 0;
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.dmaing = 0;
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* This check _should_not_ be necessary, omit eventually. */
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
522ff5688ae1cedfb175b5ed0f319d03ad2e5ee005dMarcelo Feitoza Parisi		if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
5236aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik			printk("%s: ne_reset_8390() did not complete.\n",
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dev->name);
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(ENISR_RESET, NE_BASE + EN0_ISR);	/* Ack intr. */
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Grab the 8390 specific header. Similar to the block_input routine, but
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   we don't need to be concerned with ring wrap as the header will be at
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   the start of a page, so we optimize accordingly. */
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5346aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzikstatic void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int ring_page)
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int nic_base = dev->base_addr;
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5406aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	/* This *shouldn't* happen.
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   If it does, it's the last thing you'll see */
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.dmaing) {
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("%s: DMAing conflict in ne_get_8390_hdr "
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"[DMAstat:%d][irqlock:%d].\n",
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dev->name, ei_status.dmaing, ei_status.irqlock);
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.dmaing |= 0x01;
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(0, nic_base + EN0_RCNTHI);
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(0, nic_base + EN0_RSARLO);		/* On page boundary */
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(ring_page, nic_base + EN0_RSARHI);
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.word16)
5586aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik		insw(NE_BASE + NE_DATAPORT, hdr,
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				sizeof(struct e8390_pkt_hdr)>>1);
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5616aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik		insb(NE_BASE + NE_DATAPORT, hdr,
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				sizeof(struct e8390_pkt_hdr));
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.dmaing &= ~0x01;
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Block input and output, similar to the Crynwr packet driver.  If you
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   are porting to a new ethercard, look at the packet driver source for
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   hints. The NEx000 doesn't share the on-board packet memory -- you have
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   to put the packet out through the "remote DMA" dataport using outb. */
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5736aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzikstatic void ne_block_input(struct net_device *dev, int count, struct sk_buff *skb,
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int ring_offset)
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef NE_SANITY_CHECK
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int xfer_count = count;
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int nic_base = dev->base_addr;
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *buf = skb->data;
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5826aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	/* This *shouldn't* happen.
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   If it does, it's the last thing you'll see */
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.dmaing) {
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("%s: DMAing conflict in ne_block_input "
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"[DMAstat:%d][irqlock:%d].\n",
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dev->name, ei_status.dmaing, ei_status.irqlock);
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.dmaing |= 0x01;
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(count & 0xff, nic_base + EN0_RCNTLO);
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(count >> 8, nic_base + EN0_RCNTHI);
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.word16) {
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		insw(NE_BASE + NE_DATAPORT,buf,count>>1);
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (count & 0x01) {
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buf[count-1] = inb(NE_BASE + NE_DATAPORT);
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef NE_SANITY_CHECK
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			xfer_count++;
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		insb(NE_BASE + NE_DATAPORT, buf, count);
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef NE_SANITY_CHECK
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* This was for the ALPHA version only, but enough people have
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   been encountering problems so it is still here.  If you see
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   this message you either 1) have a slightly incompatible clone
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   or 2) have noise/speed problems with your bus. */
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_debug > 1) {	/* DMA termination address check... */
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int addr, tries = 20;
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		do {
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   -- it's broken for Rx on some cards! */
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int high = inb_p(nic_base + EN0_RSARHI);
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int low = inb_p(nic_base + EN0_RSARLO);
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			addr = (high << 8) + low;
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (((ring_offset + xfer_count) & 0xff) == low)
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} while (--tries > 0);
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tries <= 0)
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("%s: RX transfer address mismatch,"
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"%#4.4x (expected) vs. %#4.4x (actual).\n",
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dev->name, ring_offset + xfer_count, addr);
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.dmaing &= ~0x01;
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ne_block_output(struct net_device *dev, int count,
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		const unsigned char *buf, const int start_page)
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int nic_base = NE_BASE;
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long dma_start;
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef NE_SANITY_CHECK
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retries = 0;
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Round the count up for word writes. Do we need to do this?
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   What effect will an odd byte count have on the 8390?
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   I should check someday. */
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.word16 && (count & 0x01))
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count++;
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6506aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	/* This *shouldn't* happen.
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   If it does, it's the last thing you'll see */
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.dmaing) {
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("%s: DMAing conflict in ne_block_output."
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"[DMAstat:%d][irqlock:%d]\n",
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dev->name, ei_status.dmaing, ei_status.irqlock);
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.dmaing |= 0x01;
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* We should already be in page 0, but to be safe... */
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef NE_SANITY_CHECK
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsretry:
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef NE8390_RW_BUGFIX
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Handle the read-before-write bug the same way as the
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Crynwr packet driver -- the NatSemi method doesn't work.
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Actually this doesn't always work either, but if you have
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   problems with your NEx000 this is better than nothing! */
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(0x42, nic_base + EN0_RCNTLO);
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(0x00, nic_base + EN0_RCNTHI);
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(0x42, nic_base + EN0_RSARLO);
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(0x00, nic_base + EN0_RSARHI);
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Make certain that the dummy read has occurred. */
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	SLOW_DOWN_IO;
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	SLOW_DOWN_IO;
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	SLOW_DOWN_IO;
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(ENISR_RDC, nic_base + EN0_ISR);
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Now the normal output. */
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(count & 0xff, nic_base + EN0_RCNTLO);
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(count >> 8,   nic_base + EN0_RCNTHI);
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(0x00, nic_base + EN0_RSARLO);
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(start_page, nic_base + EN0_RSARHI);
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.word16) {
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outsb(NE_BASE + NE_DATAPORT, buf, count);
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dma_start = jiffies;
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef NE_SANITY_CHECK
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* This was for the ALPHA version only, but enough people have
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   been encountering problems so it is still here. */
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_debug > 1) {		/* DMA termination address check... */
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int addr, tries = 20;
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		do {
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int high = inb_p(nic_base + EN0_RSARHI);
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int low = inb_p(nic_base + EN0_RSARLO);
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			addr = (high << 8) + low;
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((start_page << 8) + count == addr)
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} while (--tries > 0);
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tries <= 0) {
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("%s: Tx packet transfer address mismatch,"
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					"%#4.4x (expected) vs. %#4.4x (actual).\n",
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dev->name, (start_page << 8) + count, addr);
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (retries++ == 0)
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto retry;
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
723ff5688ae1cedfb175b5ed0f319d03ad2e5ee005dMarcelo Feitoza Parisi		if (time_after(jiffies, dma_start + 2*HZ/100)) {		/* 20ms */
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("%s: timeout waiting for Tx RDC.\n", dev->name);
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ne_reset_8390(dev);
726055e5110ae0c0c1176a75b78d789294f2ff2f7afAlan Cox			NS8390p_init(dev, 1);
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.dmaing &= ~0x01;
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef MODULE
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAX_NE_CARDS	4	/* Max number of NE cards per module */
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct net_device *dev_ne[MAX_NE_CARDS];
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int io[MAX_NE_CARDS];
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int irq[MAX_NE_CARDS];
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int bad[MAX_NE_CARDS];	/* 0xbad = bad sig or no reset ack */
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(io, int, NULL, 0);
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(irq, int, NULL, 0);
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(bad, int, NULL, 0);
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(io, "(ignored)");
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(irq, "(ignored)");
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(bad, "(ignored)");
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Module code fixed by David Weinehall */
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7525d1f16c6c4d64d1e4f53e277e39c0ada89f00a48Randy.Dunlapint __init init_module(void)
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int this_dev, found = 0;
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
758055e5110ae0c0c1176a75b78d789294f2ff2f7afAlan Cox		dev = alloc_eip_netdev();
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!dev)
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->irq = irq[this_dev];
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_end = bad[this_dev];
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->base_addr = io[this_dev];
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (do_ne2_probe(dev) == 0) {
765b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>			dev_ne[found++] = dev;
766b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>			continue;
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_netdev(dev);
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (found)
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_WARNING "ne2.c: No NE/2 card found\n");
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENXIO;
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
77764916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenkostatic void cleanup_card(struct net_device *dev)
77864916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko{
77964916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	mca_mark_as_unused(ei_status.priv);
78064916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	mca_set_adapter_procfn( ei_status.priv, NULL, NULL);
78164916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	free_irq(dev->irq, dev);
78264916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	release_region(dev->base_addr, NE_IO_EXTENT);
78364916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko}
78464916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko
785afc8eb46c0ea2cab8bc28713b2e0614f015a7516Al Virovoid __exit cleanup_module(void)
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int this_dev;
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct net_device *dev = dev_ne[this_dev];
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (dev) {
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			unregister_netdev(dev);
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cleanup_card(dev);
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			free_netdev(dev);
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* MODULE */
799