11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards.
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Written 1993-1998 by Donald Becker.
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Copyright 1993 United States Government as represented by the
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Director, National Security Agency.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This software may be used and distributed according to the terms
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	of the GNU General Public License, incorporated herein by reference.
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	This driver uses the cards in the 8390-compatible mode.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Most of the run-time complexity is handled by the generic code in
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	8390.c.  The code in this file is responsible for
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ultra_probe()	 	Detecting and initializing the card.
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ultra_probe1()
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ultra_probe_isapnp()
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ultra_open()		The card-specific details of starting, stopping
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ultra_reset_8390()	and resetting the 8390 NIC core.
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ultra_close()
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ultra_block_input()		Routines for reading and writing blocks of
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ultra_block_output()	packet buffer memory.
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ultra_pio_input()
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ultra_pio_output()
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This driver enables the shared memory only when doing the actual data
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	transfers to avoid a bug in early version of the card that corrupted
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	data transferred by a AHA1542.
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This driver now supports the programmed-I/O (PIO) data transfer mode of
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	the EtherEZ. It does not use the non-8390-compatible "Altego" mode.
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	That support (if available) is in smc-ez.c.
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Changelog:
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Paul Gortmaker	: multiple card support for module users.
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Donald Becker	: 4/17/96 PIO support, minor potential problems avoided.
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Donald Becker	: 6/6/96 correctly set auto-wrap bit.
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Alexander Sotirov : 1/20/01 Added support for ISAPnP cards
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Note about the ISA PnP support:
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This driver can not autoprobe for more than one SMC EtherEZ PnP card.
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	You have to configure the second card manually through the /proc/isapnp
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	interface and then load the module with an explicit io=0x___ option.
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char version[] =
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
65a6b7a407865aab9f849dd99a71072b7cd1175116Alexey Dobriyan#include <linux/interrupt.h>
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/isapnp.h>
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/etherdevice.h>
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
71bf4e70e54cf31dcca48d279c7f7e71328eebe749Al Viro#include <asm/irq.h>
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "8390.h"
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRV_NAME "smc-ultra"
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* A zero-terminated list of I/O addresses to be probed. */
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int ultra_portlist[] __initdata =
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0};
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ultra_probe1(struct net_device *dev, int ioaddr);
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef __ISAPNP__
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ultra_probe_isapnp(struct net_device *dev);
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ultra_open(struct net_device *dev);
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra_reset_8390(struct net_device *dev);
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						int ring_page);
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra_block_input(struct net_device *dev, int count,
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						  struct sk_buff *skb, int ring_offset);
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra_block_output(struct net_device *dev, int count,
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							const unsigned char *buf, const int start_page);
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						int ring_page);
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra_pio_input(struct net_device *dev, int count,
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						  struct sk_buff *skb, int ring_offset);
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra_pio_output(struct net_device *dev, int count,
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							 const unsigned char *buf, const int start_page);
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ultra_close_card(struct net_device *dev);
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef __ISAPNP__
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct isapnp_device_id ultra_device_ids[] __initdata = {
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {       ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416),
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416),
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                (long) "SMC EtherEZ (8416)" },
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        { }	/* terminate list */
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE(isapnp, ultra_device_ids);
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1146aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define START_PG		0x00	/* First page of TX buffer */
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA_CMDREG	0		/* Offset to ASIC command register. */
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	 ULTRA_RESET	0x80	/* Board reset, in ULTRA_CMDREG. */
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	 ULTRA_MEMENB	0x40	/* Enable the shared memory. */
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOPD	0x02			/* I/O Pipe Data (16 bits), PIO operation. */
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOPA	0x07			/* I/O Pipe Address for PIO operation. */
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA_NIC_OFFSET  16	/* NIC register offset from the base_addr. */
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA_IO_EXTENT 32
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define EN0_ERWCNT		0x08	/* Early receive warning count. */
1256aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_NET_POLL_CONTROLLER
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra_poll(struct net_device *dev)
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	disable_irq(dev->irq);
1307d12e780e003f93433d49ce78cfedf4b4c52adc5David Howells	ei_interrupt(dev->irq, dev);
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	enable_irq(dev->irq);
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*	Probe for the Ultra.  This looks like a 8013 with the station
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	following.
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init do_ultra_probe(struct net_device *dev)
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int base_addr = dev->base_addr;
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int irq = dev->irq;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (base_addr > 0x1ff)		/* Check a single specified location. */
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ultra_probe1(dev, base_addr);
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (base_addr != 0)	/* Don't probe at all. */
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENXIO;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef __ISAPNP__
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Look for any installed ISAPnP cards */
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (isapnp_present() && (ultra_probe_isapnp(dev) == 0))
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; ultra_portlist[i]; i++) {
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->irq = irq;
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ultra_probe1(dev, ultra_portlist[i]) == 0)
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENODEV;
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef MODULE
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct net_device * __init ultra_probe(int unit)
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev = alloc_ei_netdev();
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ERR_PTR(-ENOMEM);
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sprintf(dev->name, "eth%d", unit);
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netdev_boot_setup_check(dev);
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = do_ultra_probe(dev);
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return dev;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_netdev(dev);
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ERR_PTR(err);
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
187d079841d795b83822357ef058ce8d7979dad854cStephen Hemmingerstatic const struct net_device_ops ultra_netdev_ops = {
188d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger	.ndo_open		= ultra_open,
189d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger	.ndo_stop		= ultra_close_card,
190d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger
191d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger	.ndo_start_xmit		= ei_start_xmit,
192d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger	.ndo_tx_timeout		= ei_tx_timeout,
193d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger	.ndo_get_stats		= ei_get_stats,
194afc4b13df143122f99a0eb10bfefb216c2806de0Jiri Pirko	.ndo_set_rx_mode	= ei_set_multicast_list,
195d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger	.ndo_validate_addr	= eth_validate_addr,
196fe96aaa14f553f0eb7af0e3502563a5400c65257Stephen Hemminger	.ndo_set_mac_address 	= eth_mac_addr,
197d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger	.ndo_change_mtu		= eth_change_mtu,
198d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger#ifdef CONFIG_NET_POLL_CONTROLLER
19906e884031702f06b32ce20750ab3c46c596dc776Stephen Hemminger	.ndo_poll_controller 	= ultra_poll,
200d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger#endif
201d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger};
202d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init ultra_probe1(struct net_device *dev, int ioaddr)
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, retval;
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int checksum = 0;
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	const char *model_name;
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char eeprom_irq = 0;
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static unsigned version_printed;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Values from various config regs. */
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char num_pages, irqreg, addr, piomode;
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char idreg = inb(ioaddr + 7);
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME))
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check the ID nibble. */
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((idreg & 0xF0) != 0x20 			/* SMC Ultra */
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		&& (idreg & 0xF0) != 0x40) {		/* SMC EtherEZ */
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENODEV;
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Select the station address register set. */
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(reg4, ioaddr + 4);
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 8; i++)
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		checksum += inb(ioaddr + 8 + i);
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((checksum & 0xff) != 0xFF) {
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENODEV;
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_debug  &&  version_printed++ == 0)
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(version);
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ";
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 6; i++)
2410795af5729b18218767fab27c44b1384f72dc9adJoe Perches		dev->dev_addr[i] = inb(ioaddr + 8 + i);
2420795af5729b18218767fab27c44b1384f72dc9adJoe Perches
243e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	printk("%s: %s at %#3x, %pM", dev->name, model_name,
244e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	       ioaddr, dev->dev_addr);
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Switch from the station address to the alternate register set and
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   read the useful registers there. */
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x80 | reg4, ioaddr + 4);
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	piomode = inb(ioaddr + 0x8);
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	addr = inb(ioaddr + 0xb);
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irqreg = inb(ioaddr + 0xd);
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Switch back to the station address register set so that the MS-DOS driver
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   can find the card after a warm boot. */
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(reg4, ioaddr + 4);
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->irq < 2) {
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int irq;
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* The IRQ bits are split. */
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)];
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (irq == 0) {
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(", failed to detect IRQ line.\n");
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			retval =  -EAGAIN;
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->irq = irq;
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		eeprom_irq = 1;
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The 8390 isn't at the base address, so fake the offset */
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
280b6bc765067ece933cc3dc7f5e95665a89100b1d5Joe Perches		static const int addr_tbl[4] = {
281b6bc765067ece933cc3dc7f5e95665a89100b1d5Joe Perches			0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000
282b6bc765067ece933cc3dc7f5e95665a89100b1d5Joe Perches		};
283b6bc765067ece933cc3dc7f5e95665a89100b1d5Joe Perches		static const short num_pages_tbl[4] = {
284b6bc765067ece933cc3dc7f5e95665a89100b1d5Joe Perches			0x20, 0x40, 0x80, 0xff
285b6bc765067ece933cc3dc7f5e95665a89100b1d5Joe Perches		};
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		num_pages = num_pages_tbl[(addr >> 4) & 3];
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.name = model_name;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.word16 = 1;
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.tx_start_page = START_PG;
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.rx_start_page = START_PG + TX_PAGES;
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.stop_page = num_pages;
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG)*256);
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ei_status.mem) {
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(", failed to ioremap.\n");
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval =  -ENOMEM;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG)*256;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (piomode) {
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(",%s IRQ %d programmed-I/O mode.\n",
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   eeprom_irq ? "EEPROM" : "assigned ", dev->irq);
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.block_input = &ultra_pio_input;
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.block_output = &ultra_pio_output;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.get_8390_hdr = &ultra_pio_get_hdr;
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(",%s IRQ %d memory %#lx-%#lx.\n", eeprom_irq ? "" : "assigned ",
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   dev->irq, dev->mem_start, dev->mem_end-1);
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.block_input = &ultra_block_input;
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.block_output = &ultra_block_output;
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.get_8390_hdr = &ultra_get_8390_hdr;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.reset_8390 = &ultra_reset_8390;
320d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger
321d079841d795b83822357ef058ce8d7979dad854cStephen Hemminger	dev->netdev_ops = &ultra_netdev_ops;
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NS8390_init(dev, 0);
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
324b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>	retval = register_netdev(dev);
325b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>	if (retval)
326b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>		goto out;
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(ioaddr, ULTRA_IO_EXTENT);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef __ISAPNP__
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init ultra_probe_isapnp(struct net_device *dev)
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        int i;
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        for (i = 0; ultra_device_ids[i].vendor != 0; i++) {
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct pnp_dev *idev = NULL;
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                while ((idev = pnp_find_dev(NULL,
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                                            ultra_device_ids[i].vendor,
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                                            ultra_device_ids[i].function,
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                                            idev))) {
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        /* Avoid already found cards from previous calls */
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        if (pnp_device_attach(idev) < 0)
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        	continue;
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        if (pnp_activate_dev(idev) < 0) {
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                              __again:
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        	pnp_device_detach(idev);
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        	continue;
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        }
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* if no io and irq, search for next */
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0))
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto __again;
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        /* found it */
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->base_addr = pnp_port_start(idev, 0);
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->irq = pnp_irq(idev, 0);
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        printk(KERN_INFO "smc-ultra.c: ISAPnP reports %s at i/o %#lx, irq %d.\n",
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                                (char *) ultra_device_ids[i].driver_data,
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                                dev->base_addr, dev->irq);
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        if (ultra_probe1(dev, dev->base_addr) != 0) {      /* Shouldn't happen. */
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                                printk(KERN_ERR "smc-ultra.c: Probe of ISAPnP card at %#lx failed.\n", dev->base_addr);
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                                pnp_device_detach(idev);
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return -ENXIO;
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        }
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        ei_status.priv = (unsigned long)idev;
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        break;
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                }
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                if (!idev)
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        continue;
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                return 0;
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        return -ENODEV;
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsultra_open(struct net_device *dev)
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40,
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, };
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev);
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x00, ioaddr);	/* Disable shared memory for safety. */
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x80, ioaddr + 5);
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Set the IRQ line. */
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(inb(ioaddr + 4) | 0x80, ioaddr + 4);
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13);
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4);
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.block_input == &ultra_pio_input) {
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(0x11, ioaddr + 6);		/* Enable interrupts and PIO. */
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(0x01, ioaddr + 0x19);  	/* Enable ring read auto-wrap. */
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(0x01, ioaddr + 6);		/* Enable interrupts and memory. */
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Set the early receive warning level in window 0 high enough not
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   to receive ERW interrupts. */
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0xff, dev->base_addr + EN0_ERWCNT);
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_open(dev);
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsultra_reset_8390(struct net_device *dev)
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ULTRA_RESET, cmd_port);
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_debug > 1) printk("resetting Ultra, t=%ld...", jiffies);
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.txing = 0;
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x00, cmd_port);	/* Disable shared memory for safety. */
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x80, cmd_port + 5);
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.block_input == &ultra_pio_input)
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(0x11, cmd_port + 6);		/* Enable interrupts and PIO. */
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(0x01, cmd_port + 6);		/* Enable interrupts and memory. */
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_debug > 1) printk("reset done\n");
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Grab the 8390 specific header. Similar to the block_input routine, but
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   we don't need to be concerned with ring wrap as the header will be at
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   the start of a page, so we optimize accordingly. */
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG)<<8);
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);	/* shmem on */
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef __BIG_ENDIAN
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Officially this is what we are doing, but the readl() is faster */
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* unfortunately it isn't endian aware of the struct               */
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hdr->count = le16_to_cpu(hdr->count);
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	((unsigned int*)hdr)[0] = readl(hdr_start);
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Block input and output are easy on shared memory ethercards, the only
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   complication is when the ring buffer wraps. */
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsultra_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *xfer_start = ei_status.mem + ring_offset - (START_PG<<8);
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Enable shared memory. */
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ring_offset + count > ei_status.stop_page*256) {
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* We must wrap the input move. */
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int semi_count = ei_status.stop_page*256 - ring_offset;
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy_fromio(skb->data, xfer_start, semi_count);
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count -= semi_count;
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
4694ec031166f6a466a443f462e567f7551096b1741Al Viro		memcpy_fromio(skb->data, xfer_start, count);
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET);	/* Disable memory. */
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsultra_block_output(struct net_device *dev, int count, const unsigned char *buf,
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				int start_page)
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *shmem = ei_status.mem + ((start_page - START_PG)<<8);
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Enable shared memory. */
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy_toio(shmem, buf, count);
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The identical operations for programmed I/O cards.
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   The PIO model is trivial to use: the 16 bit start address is written
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   byte-sequentially to IOPA, with no intervening I/O operations, and the
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   data is read or written to the IOPD data port.
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   The only potential complication is that the address register is shared
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   and must be always be rewritten between each read/write direction change.
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   This is no problem for us, as the 8390 code ensures that we are single
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   threaded. */
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						int ring_page)
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x00, ioaddr + IOPA);	/* Set the address, LSB first. */
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ring_page, ioaddr + IOPA);
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1);
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra_pio_input(struct net_device *dev, int count,
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						  struct sk_buff *skb, int ring_offset)
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    char *buf = skb->data;
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* For now set the address again, although it should already be correct. */
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ring_offset, ioaddr + IOPA);	/* Set the address, LSB first. */
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ring_offset >> 8, ioaddr + IOPA);
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* We know skbuffs are padded to at least word alignment. */
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	insw(ioaddr + IOPD, buf, (count+1)>>1);
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra_pio_output(struct net_device *dev, int count,
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							const unsigned char *buf, const int start_page)
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x00, ioaddr + IOPA);	/* Set the address, LSB first. */
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(start_page, ioaddr + IOPA);
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* An extra odd byte is OK here as well. */
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outsw(ioaddr + IOPD, buf, (count+1)>>1);
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsultra_close_card(struct net_device *dev)
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netif_stop_queue(dev);
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_debug > 1)
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("%s: Shutting down ethercard.\n", dev->name);
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x00, ioaddr + 6);		/* Disable interrupts. */
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(dev->irq, dev);
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NS8390_init(dev, 0);
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* We should someday disable shared memory and change to 8-bit mode
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   "just in case"... */
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5506aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef MODULE
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAX_ULTRA_CARDS	4	/* Max number of Ultra cards per module */
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct net_device *dev_ultra[MAX_ULTRA_CARDS];
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int io[MAX_ULTRA_CARDS];
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int irq[MAX_ULTRA_CARDS];
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(io, int, NULL, 0);
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(irq, int, NULL, 0);
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(io, "I/O base address(es)");
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver");
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This is set up so that only a single autoprobe takes place per call.
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsISA device autoprobes on a running machine are not recommended. */
566a2bd2ec8d1bef7479d26d375162963106757e8e9Randy Dunlapint __init
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsinit_module(void)
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int this_dev, found = 0;
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (io[this_dev] == 0)  {
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (this_dev != 0) break; /* only autoprobe 1st one */
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n");
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev = alloc_ei_netdev();
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!dev)
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->irq = irq[this_dev];
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->base_addr = io[this_dev];
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (do_ultra_probe(dev) == 0) {
583b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>			dev_ultra[found++] = dev;
584b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>			continue;
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_netdev(dev);
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (found)
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENXIO;
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
59564916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenkostatic void cleanup_card(struct net_device *dev)
59664916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko{
59764916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	/* NB: ultra_close_card() does free_irq */
59864916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko#ifdef __ISAPNP__
59964916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
60064916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	if (idev)
60164916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko		pnp_device_detach(idev);
60264916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko#endif
60364916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	release_region(dev->base_addr - ULTRA_NIC_OFFSET, ULTRA_IO_EXTENT);
60464916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	iounmap(ei_status.mem);
60564916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko}
60664916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko
607afc8eb46c0ea2cab8bc28713b2e0614f015a7516Al Virovoid __exit
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldscleanup_module(void)
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int this_dev;
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct net_device *dev = dev_ultra[this_dev];
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (dev) {
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			unregister_netdev(dev);
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cleanup_card(dev);
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			free_netdev(dev);
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* MODULE */
622