11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* wd.c: A WD80x3 ethernet driver for linux. */
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Written 1993-94 by Donald Becker.
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Copyright 1993 United States Government as represented by the
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Director, National Security Agency.
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This software may be used and distributed according to the terms
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	of the GNU General Public License, incorporated herein by reference.
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	The author may be reached as becker@scyld.com, or C/O
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Scyld Computing Corporation
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	410 Severn Ave., Suite 210
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Annapolis MD 21403
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This is a driver for WD8003 and WD8013 "compatible" ethercards.
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Changelog:
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Paul Gortmaker	: multiple card support for module users, support
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			  for non-standard memory sizes.
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char version[] =
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/etherdevice.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "8390.h"
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRV_NAME "wd"
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* A zero-terminated list of I/O addresses to be probed. */
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int wd_portlist[] __initdata =
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{0x300, 0x280, 0x380, 0x240, 0};
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wd_probe1(struct net_device *dev, int ioaddr);
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wd_open(struct net_device *dev);
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void wd_reset_8390(struct net_device *dev);
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						int ring_page);
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void wd_block_input(struct net_device *dev, int count,
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						  struct sk_buff *skb, int ring_offset);
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void wd_block_output(struct net_device *dev, int count,
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							const unsigned char *buf, int start_page);
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wd_close(struct net_device *dev);
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
63c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whiteheadstatic u32 wd_msg_enable;
646aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_START_PG		0x00	/* First page of TX buffer */
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD03_STOP_PG	0x20	/* Last page +1 of RX ring */
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD13_STOP_PG	0x40	/* Last page +1 of RX ring */
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_CMDREG		0		/* Offset to ASIC command register. */
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	 WD_RESET		0x80	/* Board reset, in WD_CMDREG. */
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	 WD_MEMENB		0x40	/* Enable the shared memory. */
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_CMDREG5		5		/* Offset to 16-bit-only ASIC register 5. */
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	 ISA16			0x80	/* Enable 16 bit access from the ISA bus. */
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	 NIC16			0x40	/* Enable 16 bit access from the 8390. */
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_NIC_OFFSET	16		/* Offset to the 8390 from the base_addr. */
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_IO_EXTENT	32
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
786aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*	Probe for the WD8003 and WD8013.  These cards have the station
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	following. A Soundblaster can have the same checksum as an WDethercard,
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	so we have an extra exclusionary check for it.
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	The wd_probe1() routine initializes the card and fills the
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	station address field. */
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init do_wd_probe(struct net_device *dev)
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct resource *r;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int base_addr = dev->base_addr;
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int irq = dev->irq;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int mem_start = dev->mem_start;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int mem_end = dev->mem_end;
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (base_addr > 0x1ff) {	/* Check a user specified location. */
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		r = request_region(base_addr, WD_IO_EXTENT, "wd-probe");
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ( r == NULL)
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EBUSY;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		i = wd_probe1(dev, base_addr);
1016aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik		if (i != 0)
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			release_region(base_addr, WD_IO_EXTENT);
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			r->name = dev->name;
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return i;
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (base_addr != 0)	/* Don't probe at all. */
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENXIO;
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; wd_portlist[i]; i++) {
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int ioaddr = wd_portlist[i];
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe");
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (r == NULL)
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (wd_probe1(dev, ioaddr) == 0) {
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			r->name = dev->name;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_region(ioaddr, WD_IO_EXTENT);
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->irq = irq;
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_start = mem_start;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_end = mem_end;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENODEV;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef MODULE
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct net_device * __init wd_probe(int unit)
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev = alloc_ei_netdev();
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ERR_PTR(-ENOMEM);
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sprintf(dev->name, "eth%d", unit);
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netdev_boot_setup_check(dev);
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = do_wd_probe(dev);
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return dev;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_netdev(dev);
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ERR_PTR(err);
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
150458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemmingerstatic const struct net_device_ops wd_netdev_ops = {
151458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger	.ndo_open		= wd_open,
152458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger	.ndo_stop		= wd_close,
153458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger	.ndo_start_xmit		= ei_start_xmit,
154458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger	.ndo_tx_timeout		= ei_tx_timeout,
155458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger	.ndo_get_stats		= ei_get_stats,
156afc4b13df143122f99a0eb10bfefb216c2806de0Jiri Pirko	.ndo_set_rx_mode	= ei_set_multicast_list,
157458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger	.ndo_validate_addr	= eth_validate_addr,
158fe96aaa14f553f0eb7af0e3502563a5400c65257Stephen Hemminger	.ndo_set_mac_address 	= eth_mac_addr,
159458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger	.ndo_change_mtu		= eth_change_mtu,
160458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger#ifdef CONFIG_NET_POLL_CONTROLLER
161458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger	.ndo_poll_controller 	= ei_poll,
162458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger#endif
163458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger};
164458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init wd_probe1(struct net_device *dev, int ioaddr)
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
168b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>	int err;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int checksum = 0;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ancient = 0;			/* An old card without config registers. */
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int word16 = 0;				/* 0 = 8 bit, 1 = 16 bit */
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	const char *model_name;
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static unsigned version_printed;
174c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead	struct ei_device *ei_local = netdev_priv(dev);
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 8; i++)
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		checksum += inb(ioaddr + 8 + i);
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (inb(ioaddr + 8) == 0xff 	/* Extra check to avoid soundcard. */
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		|| inb(ioaddr + 9) == 0xff
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		|| (checksum & 0xff) != 0xFF)
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check for semi-valid mem_start/end values if supplied. */
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
185c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead		netdev_warn(dev,
186c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead			    "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_start = 0;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_end = 0;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
191c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead	if ((wd_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
192c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead		netdev_info(dev, version);
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 6; i++)
1950795af5729b18218767fab27c44b1384f72dc9adJoe Perches		dev->dev_addr[i] = inb(ioaddr + 8 + i);
1960795af5729b18218767fab27c44b1384f72dc9adJoe Perches
197c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead	netdev_info(dev, "WD80x3 at %#3x, %pM", ioaddr, dev->dev_addr);
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The following PureData probe code was contributed by
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   configuration differently from others so we have to check for them.
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   */
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned char reg5 = inb(ioaddr+5);
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (inb(ioaddr+2)) {
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0x03: word16 = 0; model_name = "PDI8023-8";	break;
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0x05: word16 = 0; model_name = "PDUC8023";	break;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Either 0x01 (dumb) or they've released a new version. */
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:	 word16 = 0; model_name = "PDI8023";	break;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {								/* End of PureData probe */
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* This method of checking for a 16-bit board is borrowed from the
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   we.c driver.  A simpler method is just to look in ASIC reg. 0x03.
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   I'm comparing the two method in alpha test to make certain they
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   return the same result. */
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Check for the old 8 bit board - it has register 0/8 aliasing.
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   Do NOT check i>=6 here -- it hangs the old 8003 boards! */
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < 6; i++)
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (inb(ioaddr+i) != inb(ioaddr+8+i))
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (i >= 6) {
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ancient = 1;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			model_name = "WD8003-old";
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			word16 = 0;
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				&& (tmp & 0x01) == 0x01	) {				/* In a 16 slot. */
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				int asic_reg5 = inb(ioaddr+WD_CMDREG5);
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* Magic to set ASIC to word-wide mode. */
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				outb(tmp, ioaddr+1);
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				model_name = "WD8013";
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				word16 = 1;		/* We have a 16bit board here! */
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else {
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				model_name = "WD8003";
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				word16 = 0;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(tmp, ioaddr+1);			/* Restore original reg1 value. */
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef final_version
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
249c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead			pr_cont("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
250c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead				word16 ? 16 : 8,
251c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead				(inb(ioaddr+1) & 0x01) ? 16 : 8);
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Allow a compile-time override.	 */
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->mem_start = WD_SHMEM;
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->mem_start == 0) {
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Sanity and old 8003 check */
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int reg0 = inb(ioaddr);
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (reg0 == 0xff || reg0 == 0) {
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Future plan: this could check a few likely locations first. */
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->mem_start = 0xd0000;
265c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead			pr_cont(" assigning address %#lx", dev->mem_start);
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Some boards don't have the register 5 -- it returns 0xff. */
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (high_addr_bits == 0x1f || word16 == 0)
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				high_addr_bits = 0x01;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The 8390 isn't at the base address -- the ASIC regs are there! */
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->base_addr = ioaddr+WD_NIC_OFFSET;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->irq < 2) {
280b6bc765067ece933cc3dc7f5e95665a89100b1d5Joe Perches		static const int irqmap[] = {9, 3, 5, 7, 10, 11, 15, 4};
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int reg1 = inb(ioaddr+1);
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int reg4 = inb(ioaddr+4);
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ancient || reg1 == 0xff) {	/* Ack!! No way to read the IRQ! */
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			short nic_addr = ioaddr+WD_NIC_OFFSET;
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			unsigned long irq_mask;
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* We have an old-style ethercard that doesn't report its IRQ
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   line.  Do autoirq to find the IRQ line. Note that this IS NOT
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   a reliable way to trigger an interrupt. */
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb_p(E8390_NODMA + E8390_STOP, nic_addr);
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(0x00, nic_addr+EN0_IMR);	/* Disable all intrs. */
2926aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			irq_mask = probe_irq_on();
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb_p(0xff, nic_addr + EN0_IMR);	/* Enable all interrupts. */
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb_p(0x00, nic_addr + EN0_RCNTLO);
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb_p(0x00, nic_addr + EN0_RCNTHI);
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			mdelay(20);
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->irq = probe_irq_off(irq_mask);
3006aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb_p(0x00, nic_addr+EN0_IMR);	/* Mask all intrs. again. */
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
303c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead			if (netif_msg_drv(ei_local))
304c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead				pr_cont(" autoirq is %d", dev->irq);
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (dev->irq < 2)
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dev->irq = word16 ? 10 : 5;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (dev->irq == 2)		/* Fixup bogosity: IRQ2 is really IRQ9 */
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->irq = 9;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Snarf the interrupt now.  There's no point in waiting since we cannot
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   share and the board will usually be enabled. */
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev);
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i) {
316c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead		pr_cont(" unable to get IRQ %d.\n", dev->irq);
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return i;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* OK, were are certain this is going to work.  Setup the device. */
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.name = model_name;
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.word16 = word16;
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.tx_start_page = WD_START_PG;
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.rx_start_page = WD_START_PG + TX_PAGES;
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Don't map in the shared memory until the board is actually opened. */
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->mem_end != 0) {
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.priv = dev->mem_end - dev->mem_start;
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.priv = (ei_status.stop_page - WD_START_PG)*256;
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.mem = ioremap(dev->mem_start, ei_status.priv);
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ei_status.mem) {
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_irq(dev->irq, dev);
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
344c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead	pr_cont(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
345c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead		model_name, dev->irq, dev->mem_start, dev->mem_end-1);
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
347c061b18df0f1fe3f50fe451dbbdc9ede3c19701aJoe Perches	ei_status.reset_8390 = wd_reset_8390;
348c061b18df0f1fe3f50fe451dbbdc9ede3c19701aJoe Perches	ei_status.block_input = wd_block_input;
349c061b18df0f1fe3f50fe451dbbdc9ede3c19701aJoe Perches	ei_status.block_output = wd_block_output;
350c061b18df0f1fe3f50fe451dbbdc9ede3c19701aJoe Perches	ei_status.get_8390_hdr = wd_get_8390_hdr;
351458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger
352458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger	dev->netdev_ops = &wd_netdev_ops;
353d3d7b53d1ae46534cd73e1073a5c29e3b61a0552Alan Cox	NS8390_init(dev, 0);
354c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead	ei_local->msg_enable = wd_msg_enable;
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 1
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Enable interrupt generation on softconfig cards -- M.U */
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* .. but possibly potentially unsafe - Donald */
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (inb(ioaddr+14) & 0x20)
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(inb(ioaddr+4)|0x80, ioaddr+4);
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
363b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>	err = register_netdev(dev);
364bdb0f8672ff6f601a32df5af40f11526b741985cKulikov Vasiliy	if (err) {
365b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>		free_irq(dev->irq, dev);
366bdb0f8672ff6f601a32df5af40f11526b741985cKulikov Vasiliy		iounmap(ei_status.mem);
367bdb0f8672ff6f601a32df5af40f11526b741985cKulikov Vasiliy	}
368b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>	return err;
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswd_open(struct net_device *dev)
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  /* Map in the shared memory. Always set register 0 last to remain
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 compatible with very old boards. */
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (ei_status.word16)
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  outb(ei_status.reg5, ioaddr+WD_CMDREG5);
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
385458228bc4c1370de1dae4ba0f78469092ca21d75Stephen Hemminger  return ei_open(dev);
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswd_reset_8390(struct net_device *dev)
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
392c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead	struct ei_device *ei_local = netdev_priv(dev);
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(WD_RESET, wd_cmd_port);
395c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead	netif_dbg(ei_local, hw, dev, "resetting the WD80x3 t=%lu...\n",
396c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead		  jiffies);
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.txing = 0;
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Set up the ASIC registers, just in case something changed them. */
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.word16)
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
404c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead	netif_dbg(ei_local, hw, dev, "reset done\n");
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Grab the 8390 specific header. Similar to the block_input routine, but
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   we don't need to be concerned with ring wrap as the header will be at
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   the start of a page, so we optimize accordingly. */
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8);
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* We'll always get a 4 byte header read followed by a packet read, so
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   we enable 16 bit mode before the header, and disable after the body. */
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.word16)
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef __BIG_ENDIAN
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Officially this is what we are doing, but the readl() is faster */
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* unfortunately it isn't endian aware of the struct               */
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hdr->count = le16_to_cpu(hdr->count);
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	((unsigned int*)hdr)[0] = readl(hdr_start);
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Block input and output are easy on shared memory ethercards, and trivial
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   on the Western digital card where there is no choice of how to do it.
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   The only complications are that the ring buffer wraps, and need to map
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   switch between 8- and 16-bit modes. */
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long offset = ring_offset - (WD_START_PG<<8);
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *xfer_start = ei_status.mem + offset;
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (offset + count > ei_status.priv) {
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* We must wrap the input move. */
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int semi_count = ei_status.priv - offset;
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy_fromio(skb->data, xfer_start, semi_count);
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count -= semi_count;
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Packet is in one chunk -- we can copy + cksum. */
4534ec031166f6a466a443f462e567f7551096b1741Al Viro		memcpy_fromio(skb->data, xfer_start, count);
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Turn off 16 bit access so that reboot works.	 ISA brain-damage */
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.word16)
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswd_block_output(struct net_device *dev, int count, const unsigned char *buf,
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				int start_page)
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8);
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.word16) {
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Turn on and off 16 bit access so that reboot works. */
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy_toio(shmem, buf, count);
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy_toio(shmem, buf, count);
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswd_close(struct net_device *dev)
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
483c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead	struct ei_device *ei_local = netdev_priv(dev);
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
485c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whitehead	netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n");
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_close(dev);
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Change from 16-bit to 8-bit shared memory so reboot works. */
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ei_status.word16)
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* And disable the shared memory. */
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4986aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef MODULE
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAX_WD_CARDS	4	/* Max number of wd cards per module */
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct net_device *dev_wd[MAX_WD_CARDS];
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int io[MAX_WD_CARDS];
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int irq[MAX_WD_CARDS];
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mem[MAX_WD_CARDS];
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mem_end[MAX_WD_CARDS];	/* for non std. mem size */
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(io, int, NULL, 0);
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(irq, int, NULL, 0);
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(mem, int, NULL, 0);
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(mem_end, int, NULL, 0);
511c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew Whiteheadmodule_param_named(msg_enable, wd_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(io, "I/O base address(es)");
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)");
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)");
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(mem_end, "memory end address(es)");
516c45f812f0280c13f1b7992be5e0de512312a9e8fMatthew WhiteheadMODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver");
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This is set up so that only a single autoprobe takes place per call.
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsISA device autoprobes on a running machine are not recommended. */
5225e5fa01d55e9949660a89b23f4a43884eaf49097Andrew Morton
5235e5fa01d55e9949660a89b23f4a43884eaf49097Andrew Mortonint __init init_module(void)
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int this_dev, found = 0;
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (io[this_dev] == 0)  {
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (this_dev != 0) break; /* only autoprobe 1st one */
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev = alloc_ei_netdev();
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!dev)
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->irq = irq[this_dev];
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->base_addr = io[this_dev];
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_start = mem[this_dev];
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_end = mem_end[this_dev];
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (do_wd_probe(dev) == 0) {
541b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>			dev_wd[found++] = dev;
542b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>			continue;
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_netdev(dev);
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (found)
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENXIO;
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
55364916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenkostatic void cleanup_card(struct net_device *dev)
55464916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko{
55564916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	free_irq(dev->irq, dev);
55664916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT);
55764916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	iounmap(ei_status.mem);
55864916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko}
55964916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko
560afc8eb46c0ea2cab8bc28713b2e0614f015a7516Al Virovoid __exit
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldscleanup_module(void)
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int this_dev;
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct net_device *dev = dev_wd[this_dev];
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (dev) {
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			unregister_netdev(dev);
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cleanup_card(dev);
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			free_netdev(dev);
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* MODULE */
575