11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 	smc-ultra32.c: An SMC Ultra32 EISA ethernet driver for linux.
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsSources:
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This driver is based on (cloned from) the ISA SMC Ultra driver
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	written by Donald Becker. Modifications to support the EISA
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	version of the card by Paul Gortmaker and Leonard N. Zubkoff.
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This software may be used and distributed according to the terms
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	of the GNU General Public License, incorporated herein by reference.
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsTheory of Operation:
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	The SMC Ultra32C card uses the SMC 83c790 chip which is also
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	found on the ISA SMC Ultra cards. It has a shared memory mode of
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	operation that makes it similar to the ISA version of the card.
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	The main difference is that the EISA card has 32KB of RAM, but
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	only an 8KB window into that memory. The EISA card also can be
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set for a bus-mastering mode of operation via the ECU, but that
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	is not (and probably will never be) supported by this driver.
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	The ECU should be run to enable shared memory and to disable the
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	bus-mastering feature for use with linux.
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	By programming the 8390 to use only 8KB RAM, the modifications
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	to the ISA driver can be limited to the probe and initialization
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	code. This allows easy integration of EISA support into the ISA
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver. However, the driver development kit from SMC provided the
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	register information for sliding the 8KB window, and hence the 8390
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	is programmed to use the full 32KB RAM.
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Unfortunately this required code changes outside the probe/init
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	routines, and thus we decided to separate the EISA driver from
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	the ISA one. In this way, ISA users don't end up with a larger
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver due to the EISA code, and EISA users don't end up with a
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	larger driver due to the ISA EtherEZ PIO code. The driver is
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	similar to the 3c503/16 driver, in that the window must be set
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	back to the 1st 8KB of space for access to the two 8390 Tx slots.
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	In testing, using only 8KB RAM (3 Tx / 5 Rx) didn't appear to
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	be a limiting factor, since the EISA bus could get packets off
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	the card fast enough, but having the use of lots of RAM as Rx
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	space is extra insurance if interrupt latencies become excessive.
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char *version = "smc-ultra32.c: 06/97 v1.00\n";
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/eisa.h>
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/etherdevice.h>
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "8390.h"
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRV_NAME "smc-ultra32"
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ultra32_probe1(struct net_device *dev, int ioaddr);
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ultra32_open(struct net_device *dev);
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra32_reset_8390(struct net_device *dev);
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra32_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 int ring_page);
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra32_block_input(struct net_device *dev, int count,
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				struct sk_buff *skb, int ring_offset);
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra32_block_output(struct net_device *dev, int count,
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 const unsigned char *buf,
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 const int start_page);
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ultra32_close(struct net_device *dev);
766aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_CMDREG	0	/* Offset to ASIC command register. */
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	 ULTRA32_RESET	0x80	/* Board reset, in ULTRA32_CMDREG. */
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	 ULTRA32_MEMENB	0x40	/* Enable the shared memory. */
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_NIC_OFFSET 16	/* NIC register offset from the base_addr. */
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_IO_EXTENT 32
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define EN0_ERWCNT		0x08	/* Early receive warning count. */
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Defines that apply only to the Ultra32 EISA card. Note that
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * "smc" = 10011 01101 00011 = 0x4da3, and hence !smc8010.cfg translates
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * into an EISA ID of 0x1080A34D
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_BASE	0xca0
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_ID	0x1080a34d
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_IDPORT	(-0x20)	/* 0xc80 */
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Config regs 1->7 from the EISA !SMC8010.CFG file. */
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_CFG1	0x04	/* 0xca4 */
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_CFG2	0x05	/* 0xca5 */
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_CFG3	(-0x18)	/* 0xc88 */
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_CFG4	(-0x17)	/* 0xc89 */
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_CFG5	(-0x16)	/* 0xc8a */
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_CFG6	(-0x15)	/* 0xc8b */
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ULTRA32_CFG7	0x0d	/* 0xcad */
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cleanup_card(struct net_device *dev)
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET;
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* NB: ultra32_close_card() does free_irq */
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(ioaddr, ULTRA32_IO_EXTENT);
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iounmap(ei_status.mem);
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*	Probe for the Ultra32.  This looks like a 8013 with the station
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	following.
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct net_device * __init ultra32_probe(int unit)
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int base;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int irq;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err = -ENODEV;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!EISA_bus)
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ERR_PTR(-ENODEV);
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev = alloc_ei_netdev();
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ERR_PTR(-ENOMEM);
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (unit >= 0) {
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sprintf(dev->name, "eth%d", unit);
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		netdev_boot_setup_check(dev);
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irq = dev->irq;
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* EISA spec allows for up to 16 slots, but 8 is typical. */
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (base = 0x1000 + ULTRA32_BASE; base < 0x9000; base += 0x1000) {
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ultra32_probe1(dev, base) == 0)
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->irq = irq;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (base >= 0x9000)
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = register_netdev(dev);
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out1;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return dev;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout1:
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cleanup_card(dev);
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_netdev(dev);
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ERR_PTR(err);
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1555f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger
1565f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemmingerstatic const struct net_device_ops ultra32_netdev_ops = {
1575f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger	.ndo_open 		= ultra32_open,
1585f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger	.ndo_stop 		= ultra32_close,
1595f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger	.ndo_start_xmit		= ei_start_xmit,
1605f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger	.ndo_tx_timeout		= ei_tx_timeout,
1615f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger	.ndo_get_stats		= ei_get_stats,
162afc4b13df143122f99a0eb10bfefb216c2806de0Jiri Pirko	.ndo_set_rx_mode	= ei_set_multicast_list,
1635f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger	.ndo_validate_addr	= eth_validate_addr,
1645f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger	.ndo_set_mac_address 	= eth_mac_addr,
1655f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger	.ndo_change_mtu		= eth_change_mtu,
1665f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger#ifdef CONFIG_NET_POLL_CONTROLLER
1675f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger	.ndo_poll_controller	= ei_poll,
1685f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger#endif
1695f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger};
1705f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init ultra32_probe1(struct net_device *dev, int ioaddr)
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, edge, media, retval;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int checksum = 0;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	const char *model_name;
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static unsigned version_printed;
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Values from various config regs. */
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char idreg;
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char reg4;
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"};
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region(ioaddr, ULTRA32_IO_EXTENT, DRV_NAME))
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (inb(ioaddr + ULTRA32_IDPORT) == 0xff ||
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    inl(ioaddr + ULTRA32_IDPORT) != ULTRA32_ID) {
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENODEV;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	media = inb(ioaddr + ULTRA32_CFG7) & 0x03;
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	edge = inb(ioaddr + ULTRA32_CFG5) & 0x08;
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n",
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ioaddr >> 12, ifmap[media],
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(edge ? "Edge Triggered" : "Level Sensitive"));
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	idreg = inb(ioaddr + 7);
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	reg4 = inb(ioaddr + 4) & 0x7f;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check the ID nibble. */
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((idreg & 0xf0) != 0x20) {			/* SMC Ultra */
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENODEV;
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Select the station address register set. */
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(reg4, ioaddr + 4);
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 8; i++)
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		checksum += inb(ioaddr + 8 + i);
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((checksum & 0xff) != 0xff) {
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENODEV;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_debug  &&  version_printed++ == 0)
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(version);
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	model_name = "SMC Ultra32";
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 6; i++)
2220795af5729b18218767fab27c44b1384f72dc9adJoe Perches		dev->dev_addr[i] = inb(ioaddr + 8 + i);
2230795af5729b18218767fab27c44b1384f72dc9adJoe Perches
224e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	printk("%s: %s at 0x%X, %pM",
225e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	       dev->name, model_name, ioaddr, dev->dev_addr);
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Switch from the station address to the alternate register set and
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   read the useful registers there. */
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x80 | reg4, ioaddr + 4);
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Reset RAM addr. */
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x00, ioaddr + 0x0b);
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Switch back to the station address register set so that the
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   MS-DOS driver can find the card after a warm boot. */
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(reg4, ioaddr + 4);
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((inb(ioaddr + ULTRA32_CFG5) & 0x40) == 0) {
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("\nsmc-ultra32: Card RAM is disabled!  "
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "Run EISA config utility.\n");
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENODEV;
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((inb(ioaddr + ULTRA32_CFG2) & 0x04) == 0)
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("\nsmc-ultra32: Ignoring Bus-Master enable bit.  "
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "Run EISA config utility.\n");
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->irq < 2) {
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int irq = irqmap[inb(ioaddr + ULTRA32_CFG5) & 0x07];
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (irq == 0) {
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(", failed to detect IRQ line.\n");
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			retval = -EAGAIN;
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->irq = irq;
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The 8390 isn't at the base address, so fake the offset */
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->base_addr = ioaddr + ULTRA32_NIC_OFFSET;
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Save RAM address in the unused reg0 to avoid excess inb's. */
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.reg0 = inb(ioaddr + ULTRA32_CFG3) & 0xfc;
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->mem_start =  0xc0000 + ((ei_status.reg0 & 0x7c) << 11);
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.name = model_name;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.word16 = 1;
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.tx_start_page = 0;
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.rx_start_page = TX_PAGES;
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* All Ultra32 cards have 32KB memory with an 8KB window. */
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.stop_page = 128;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.mem = ioremap(dev->mem_start, 0x2000);
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ei_status.mem) {
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(", failed to ioremap.\n");
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -ENOMEM;
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->mem_end = dev->mem_start + 0x1fff;
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(", IRQ %d, 32KB memory, 8KB window at 0x%lx-0x%lx.\n",
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       dev->irq, dev->mem_start, dev->mem_end);
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.block_input = &ultra32_block_input;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.block_output = &ultra32_block_output;
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.get_8390_hdr = &ultra32_get_8390_hdr;
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.reset_8390 = &ultra32_reset_8390;
2915f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger
2925f352f9a1c8d53270970f4efcf5496cb9b01c4a8Stephen Hemminger	dev->netdev_ops = &ultra32_netdev_ops;
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NS8390_init(dev, 0);
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(ioaddr, ULTRA32_IO_EXTENT);
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ultra32_open(struct net_device *dev)
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */
3041fb9df5d3069064c037c81c0ab8bf783ffa5e373Thomas Gleixner	int irq_flags = (inb(ioaddr + ULTRA32_CFG5) & 0x08) ? 0 : IRQF_SHARED;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = request_irq(dev->irq, ei_interrupt, irq_flags, dev->name, dev);
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x84, ioaddr + 5);	/* Enable MEM16 & Disable Bus Master. */
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x01, ioaddr + 6);	/* Enable Interrupts. */
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Set the early receive warning level in window 0 high enough not
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   to receive ERW interrupts. */
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0xff, dev->base_addr + EN0_ERWCNT);
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_open(dev);
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ultra32_close(struct net_device *dev)
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* CMDREG */
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netif_stop_queue(dev);
3286aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_debug > 1)
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("%s: Shutting down ethercard.\n", dev->name);
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x00, ioaddr + ULTRA32_CFG6); /* Disable Interrupts. */
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x00, ioaddr + 6);		/* Disable interrupts. */
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(dev->irq, dev);
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NS8390_init(dev, 0);
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra32_reset_8390(struct net_device *dev)
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC base addr */
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ULTRA32_RESET, ioaddr);
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_debug > 1) printk("resetting Ultra32, t=%ld...", jiffies);
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.txing = 0;
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x84, ioaddr + 5);	/* Enable MEM16 & Disable Bus Master. */
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x01, ioaddr + 6);	/* Enable Interrupts. */
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_debug > 1) printk("reset done\n");
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Grab the 8390 specific header. Similar to the block_input routine, but
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   we don't need to be concerned with ring wrap as the header will be at
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   the start of a page, so we optimize accordingly. */
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra32_get_8390_hdr(struct net_device *dev,
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 struct e8390_pkt_hdr *hdr,
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 int ring_page)
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *hdr_start = ei_status.mem + ((ring_page & 0x1f) << 8);
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Select correct 8KB Window. */
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ei_status.reg0 | ((ring_page & 0x60) >> 5), RamReg);
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef __BIG_ENDIAN
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Officially this is what we are doing, but the readl() is faster */
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* unfortunately it isn't endian aware of the struct               */
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hdr->count = le16_to_cpu(hdr->count);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	((unsigned int*)hdr)[0] = readl(hdr_start);
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Block input and output are easy on shared memory ethercards, the only
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   complication is when the ring buffer wraps, or in this case, when a
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   packet spans an 8KB boundary. Note that the current 8KB segment is
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   already set by the get_8390_hdr routine. */
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra32_block_input(struct net_device *dev,
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				int count,
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				struct sk_buff *skb,
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				int ring_offset)
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *xfer_start = ei_status.mem + (ring_offset & 0x1fff);
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ring_offset & ~0x1fff) != ((ring_offset + count - 1) & ~0x1fff)) {
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int semi_count = 8192 - (ring_offset & 0x1FFF);
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy_fromio(skb->data, xfer_start, semi_count);
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count -= semi_count;
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ring_offset < 96*256) {
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Select next 8KB Window. */
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ring_offset += semi_count;
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(ei_status.reg0 | ((ring_offset & 0x6000) >> 13), RamReg);
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy_fromio(skb->data + semi_count, ei_status.mem, count);
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Select first 8KB Window. */
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(ei_status.reg0, RamReg);
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
4084ec031166f6a466a443f462e567f7551096b1741Al Viro		memcpy_fromio(skb->data, xfer_start, count);
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ultra32_block_output(struct net_device *dev,
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 int count,
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 const unsigned char *buf,
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 int start_page)
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *xfer_start = ei_status.mem + (start_page<<8);
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Select first 8KB Window. */
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ei_status.reg0, RamReg);
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy_toio(xfer_start, buf, count);
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4256aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef MODULE
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAX_ULTRA32_CARDS   4	/* Max number of Ultra cards per module */
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct net_device *dev_ultra[MAX_ULTRA32_CARDS];
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("SMC Ultra32 EISA ethernet driver");
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
433a2bd2ec8d1bef7479d26d375162963106757e8e9Randy Dunlapint __init init_module(void)
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int this_dev, found = 0;
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct net_device *dev = ultra32_probe(-1);
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (IS_ERR(dev))
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_ultra[found++] = dev;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (found)
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_WARNING "smc-ultra32.c: No SMC Ultra32 found.\n");
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENXIO;
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
449afc8eb46c0ea2cab8bc28713b2e0614f015a7516Al Virovoid __exit cleanup_module(void)
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int this_dev;
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct net_device *dev = dev_ultra[this_dev];
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (dev) {
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			unregister_netdev(dev);
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cleanup_card(dev);
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			free_netdev(dev);
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* MODULE */
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
464