wd.c revision bdb0f8672ff6f601a32df5af40f11526b741985c
1470decc613ab2048b619a01028072d932d9086eeDave Kleikamp/* wd.c: A WD80x3 ethernet driver for linux. */
25886269962f94fa9185c32db3ec936c612503235Uwe Kleine-König/*
3470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	Written 1993-94 by Donald Becker.
4470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
5470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	Copyright 1993 United States Government as represented by the
6470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	Director, National Security Agency.
7470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
8470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	This software may be used and distributed according to the terms
9470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	of the GNU General Public License, incorporated herein by reference.
10470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
11470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	The author may be reached as becker@scyld.com, or C/O
12470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	Scyld Computing Corporation
13470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	410 Severn Ave., Suite 210
14470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	Annapolis MD 21403
15470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
16470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	This is a driver for WD8003 and WD8013 "compatible" ethercards.
17470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
18470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
19470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
20470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	Changelog:
21470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
22f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao	Paul Gortmaker	: multiple card support for module users, support
23470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			  for non-standard memory sizes.
24470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
25470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
26470decc613ab2048b619a01028072d932d9086eeDave Kleikamp*/
27470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
28470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic const char version[] =
29470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	"wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
30470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
31470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/module.h>
32470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/kernel.h>
33470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/errno.h>
34470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/string.h>
35470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/init.h>
36470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/interrupt.h>
37470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/delay.h>
38470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/netdevice.h>
39470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <linux/etherdevice.h>
40470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
41470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <asm/io.h>
42470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include <asm/system.h>
43470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
44470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#include "8390.h"
45470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
46470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define DRV_NAME "wd"
47470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
48470decc613ab2048b619a01028072d932d9086eeDave Kleikamp/* A zero-terminated list of I/O addresses to be probed. */
49470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic unsigned int wd_portlist[] __initdata =
50470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{0x300, 0x280, 0x380, 0x240, 0};
51470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
52470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int wd_probe1(struct net_device *dev, int ioaddr);
53470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
54470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int wd_open(struct net_device *dev);
55470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void wd_reset_8390(struct net_device *dev);
56470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
57470decc613ab2048b619a01028072d932d9086eeDave Kleikamp						int ring_page);
58470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void wd_block_input(struct net_device *dev, int count,
59470decc613ab2048b619a01028072d932d9086eeDave Kleikamp						  struct sk_buff *skb, int ring_offset);
60470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void wd_block_output(struct net_device *dev, int count,
61470decc613ab2048b619a01028072d932d9086eeDave Kleikamp							const unsigned char *buf, int start_page);
62470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int wd_close(struct net_device *dev);
63470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
64470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
65470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD_START_PG		0x00	/* First page of TX buffer */
66470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD03_STOP_PG	0x20	/* Last page +1 of RX ring */
67470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD13_STOP_PG	0x40	/* Last page +1 of RX ring */
68470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
69470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD_CMDREG		0		/* Offset to ASIC command register. */
70470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define	 WD_RESET		0x80	/* Board reset, in WD_CMDREG. */
71470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define	 WD_MEMENB		0x40	/* Enable the shared memory. */
72470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD_CMDREG5		5		/* Offset to 16-bit-only ASIC register 5. */
73470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define	 ISA16			0x80	/* Enable 16 bit access from the ISA bus. */
74470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define	 NIC16			0x40	/* Enable 16 bit access from the 8390. */
75470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD_NIC_OFFSET	16		/* Offset to the 8390 from the base_addr. */
76470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define WD_IO_EXTENT	32
77470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
78470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
79470decc613ab2048b619a01028072d932d9086eeDave Kleikamp/*	Probe for the WD8003 and WD8013.  These cards have the station
80470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
81470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	following. A Soundblaster can have the same checksum as an WDethercard,
82470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	so we have an extra exclusionary check for it.
83470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
84470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	The wd_probe1() routine initializes the card and fills the
85470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	station address field. */
86470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
87470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int __init do_wd_probe(struct net_device *dev)
88470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{
89470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int i;
90470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	struct resource *r;
91470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int base_addr = dev->base_addr;
92470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int irq = dev->irq;
93470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int mem_start = dev->mem_start;
94470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int mem_end = dev->mem_end;
95470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
96470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (base_addr > 0x1ff) {	/* Check a user specified location. */
97470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		r = request_region(base_addr, WD_IO_EXTENT, "wd-probe");
98f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao		if ( r == NULL)
99470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			return -EBUSY;
100f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao		i = wd_probe1(dev, base_addr);
101470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		if (i != 0)
102470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			release_region(base_addr, WD_IO_EXTENT);
103470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		else
104470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			r->name = dev->name;
105470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		return i;
106470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	}
107470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	else if (base_addr != 0)	/* Don't probe at all. */
108470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		return -ENXIO;
109470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
110f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao	for (i = 0; wd_portlist[i]; i++) {
111470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		int ioaddr = wd_portlist[i];
112470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe");
113470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		if (r == NULL)
114470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			continue;
115f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao		if (wd_probe1(dev, ioaddr) == 0) {
116470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			r->name = dev->name;
117470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			return 0;
118470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		}
119470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		release_region(ioaddr, WD_IO_EXTENT);
120470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev->irq = irq;
121f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao		dev->mem_start = mem_start;
122f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao		dev->mem_end = mem_end;
123470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	}
124470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
125470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	return -ENODEV;
126470decc613ab2048b619a01028072d932d9086eeDave Kleikamp}
127470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
128470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#ifndef MODULE
129470decc613ab2048b619a01028072d932d9086eeDave Kleikampstruct net_device * __init wd_probe(int unit)
130470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{
131470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	struct net_device *dev = alloc_ei_netdev();
132470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int err;
133f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao
134470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (!dev)
135f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao		return ERR_PTR(-ENOMEM);
136470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
137470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	sprintf(dev->name, "eth%d", unit);
138470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	netdev_boot_setup_check(dev);
139470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
140470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	err = do_wd_probe(dev);
141470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (err)
142470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		goto out;
143470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	return dev;
144470decc613ab2048b619a01028072d932d9086eeDave Kleikampout:
145470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	free_netdev(dev);
146470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	return ERR_PTR(err);
147470decc613ab2048b619a01028072d932d9086eeDave Kleikamp}
148470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif
149470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
150470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic const struct net_device_ops wd_netdev_ops = {
151470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	.ndo_open		= wd_open,
152470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	.ndo_stop		= wd_close,
153470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	.ndo_start_xmit		= ei_start_xmit,
154470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	.ndo_tx_timeout		= ei_tx_timeout,
155470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	.ndo_get_stats		= ei_get_stats,
156470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	.ndo_set_multicast_list = ei_set_multicast_list,
157470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	.ndo_validate_addr	= eth_validate_addr,
158470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	.ndo_set_mac_address 	= eth_mac_addr,
159470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	.ndo_change_mtu		= eth_change_mtu,
160470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#ifdef CONFIG_NET_POLL_CONTROLLER
161470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	.ndo_poll_controller 	= ei_poll,
162470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif
163470decc613ab2048b619a01028072d932d9086eeDave Kleikamp};
164470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
165470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int __init wd_probe1(struct net_device *dev, int ioaddr)
166470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{
167470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int i;
168470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int err;
169470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int checksum = 0;
170470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int ancient = 0;			/* An old card without config registers. */
171470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int word16 = 0;				/* 0 = 8 bit, 1 = 16 bit */
172470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	const char *model_name;
173470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	static unsigned version_printed;
174470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
175470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	for (i = 0; i < 8; i++)
176470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		checksum += inb(ioaddr + 8 + i);
177470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (inb(ioaddr + 8) == 0xff 	/* Extra check to avoid soundcard. */
178470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		|| inb(ioaddr + 9) == 0xff
179470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		|| (checksum & 0xff) != 0xFF)
180470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		return -ENODEV;
181470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
182470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* Check for semi-valid mem_start/end values if supplied. */
183470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
184470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		printk(KERN_WARNING "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
185470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev->mem_start = 0;
186470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev->mem_end = 0;
187470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	}
188470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
189470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (ei_debug  &&  version_printed++ == 0)
190470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		printk(version);
191470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
192470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	for (i = 0; i < 6; i++)
193470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev->dev_addr[i] = inb(ioaddr + 8 + i);
194470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
195470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	printk("%s: WD80x3 at %#3x, %pM",
196470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	       dev->name, ioaddr, dev->dev_addr);
197470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
198470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* The following PureData probe code was contributed by
199470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	   Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
200470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	   configuration differently from others so we have to check for them.
201f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao	   This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
202470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	   */
203f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao	if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
204470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		unsigned char reg5 = inb(ioaddr+5);
205470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
206470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		switch (inb(ioaddr+2)) {
207470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		case 0x03: word16 = 0; model_name = "PDI8023-8";	break;
208470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		case 0x05: word16 = 0; model_name = "PDUC8023";	break;
209470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
210470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			/* Either 0x01 (dumb) or they've released a new version. */
211470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		default:	 word16 = 0; model_name = "PDI8023";	break;
212470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		}
213470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
214470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
215470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	} else {								/* End of PureData probe */
216470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		/* This method of checking for a 16-bit board is borrowed from the
217470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		   we.c driver.  A simpler method is just to look in ASIC reg. 0x03.
218470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		   I'm comparing the two method in alpha test to make certain they
219470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		   return the same result. */
220470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		/* Check for the old 8 bit board - it has register 0/8 aliasing.
221470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		   Do NOT check i>=6 here -- it hangs the old 8003 boards! */
222470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		for (i = 0; i < 6; i++)
223470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			if (inb(ioaddr+i) != inb(ioaddr+8+i))
224470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				break;
225470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		if (i >= 6) {
226470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			ancient = 1;
227470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			model_name = "WD8003-old";
228470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			word16 = 0;
229470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		} else {
230470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
231470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
232470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
233470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				&& (tmp & 0x01) == 0x01	) {				/* In a 16 slot. */
234470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				int asic_reg5 = inb(ioaddr+WD_CMDREG5);
235470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				/* Magic to set ASIC to word-wide mode. */
236470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
237470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				outb(tmp, ioaddr+1);
238470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				model_name = "WD8013";
239470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				word16 = 1;		/* We have a 16bit board here! */
240470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			} else {
241470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				model_name = "WD8003";
242470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				word16 = 0;
243470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			}
244470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			outb(tmp, ioaddr+1);			/* Restore original reg1 value. */
245470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		}
246470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#ifndef final_version
247470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
248470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
249470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				   word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
250470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif
251470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	}
252470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
253470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
254470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* Allow a compile-time override.	 */
255f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao	dev->mem_start = WD_SHMEM;
256f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao#else
257470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (dev->mem_start == 0) {
258470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		/* Sanity and old 8003 check */
259470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		int reg0 = inb(ioaddr);
260470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		if (reg0 == 0xff || reg0 == 0) {
261f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao			/* Future plan: this could check a few likely locations first. */
262470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			dev->mem_start = 0xd0000;
263470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			printk(" assigning address %#lx", dev->mem_start);
264f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao		} else {
265470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
266470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			/* Some boards don't have the register 5 -- it returns 0xff. */
267470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			if (high_addr_bits == 0x1f || word16 == 0)
268470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				high_addr_bits = 0x01;
269470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
270470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		}
271470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	}
272470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif
273470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
274470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* The 8390 isn't at the base address -- the ASIC regs are there! */
275470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	dev->base_addr = ioaddr+WD_NIC_OFFSET;
276470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
277470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (dev->irq < 2) {
278470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		int irqmap[] = {9,3,5,7,10,11,15,4};
279470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		int reg1 = inb(ioaddr+1);
280470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		int reg4 = inb(ioaddr+4);
281470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		if (ancient || reg1 == 0xff) {	/* Ack!! No way to read the IRQ! */
282470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			short nic_addr = ioaddr+WD_NIC_OFFSET;
283470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			unsigned long irq_mask;
284470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
285470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			/* We have an old-style ethercard that doesn't report its IRQ
286470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			   line.  Do autoirq to find the IRQ line. Note that this IS NOT
287470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			   a reliable way to trigger an interrupt. */
288470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			outb_p(E8390_NODMA + E8390_STOP, nic_addr);
289470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			outb(0x00, nic_addr+EN0_IMR);	/* Disable all intrs. */
290470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
291470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			irq_mask = probe_irq_on();
292470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			outb_p(0xff, nic_addr + EN0_IMR);	/* Enable all interrupts. */
293470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			outb_p(0x00, nic_addr + EN0_RCNTLO);
294470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			outb_p(0x00, nic_addr + EN0_RCNTHI);
295470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
296470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			mdelay(20);
297470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			dev->irq = probe_irq_off(irq_mask);
298470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
299f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao			outb_p(0x00, nic_addr+EN0_IMR);	/* Mask all intrs. again. */
300470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
301470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			if (ei_debug > 2)
302470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				printk(" autoirq is %d", dev->irq);
303470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			if (dev->irq < 2)
304470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				dev->irq = word16 ? 10 : 5;
305470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		} else
306470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
307470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	} else if (dev->irq == 2)		/* Fixup bogosity: IRQ2 is really IRQ9 */
308470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev->irq = 9;
309470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
310470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* Snarf the interrupt now.  There's no point in waiting since we cannot
311470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	   share and the board will usually be enabled. */
312f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao	i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev);
313470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (i) {
314470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		printk (" unable to get IRQ %d.\n", dev->irq);
315470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		return i;
316470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	}
317470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
318470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* OK, were are certain this is going to work.  Setup the device. */
319470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	ei_status.name = model_name;
320470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	ei_status.word16 = word16;
321470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	ei_status.tx_start_page = WD_START_PG;
322470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	ei_status.rx_start_page = WD_START_PG + TX_PAGES;
323470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
324470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* Don't map in the shared memory until the board is actually opened. */
325470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
326470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */
327470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (dev->mem_end != 0) {
328470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
329470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		ei_status.priv = dev->mem_end - dev->mem_start;
330470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	} else {
331470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
332470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
333470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		ei_status.priv = (ei_status.stop_page - WD_START_PG)*256;
334470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	}
335470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
336470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	ei_status.mem = ioremap(dev->mem_start, ei_status.priv);
337470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (!ei_status.mem) {
338470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		free_irq(dev->irq, dev);
339470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		return -ENOMEM;
340470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	}
341470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
342470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
343470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		   model_name, dev->irq, dev->mem_start, dev->mem_end-1);
344470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
345470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	ei_status.reset_8390 = &wd_reset_8390;
346470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	ei_status.block_input = &wd_block_input;
347470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	ei_status.block_output = &wd_block_output;
348470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	ei_status.get_8390_hdr = &wd_get_8390_hdr;
349470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
350470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	dev->netdev_ops = &wd_netdev_ops;
351470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	NS8390_init(dev, 0);
352470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
353470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#if 1
354470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* Enable interrupt generation on softconfig cards -- M.U */
355470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* .. but possibly potentially unsafe - Donald */
356470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (inb(ioaddr+14) & 0x20)
357470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		outb(inb(ioaddr+4)|0x80, ioaddr+4);
358470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif
359470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
360470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	err = register_netdev(dev);
361470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (err) {
362470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		free_irq(dev->irq, dev);
363470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		iounmap(ei_status.mem);
364470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	}
365470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	return err;
366470decc613ab2048b619a01028072d932d9086eeDave Kleikamp}
367470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
368470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int
369470decc613ab2048b619a01028072d932d9086eeDave Kleikampwd_open(struct net_device *dev)
370470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{
371470decc613ab2048b619a01028072d932d9086eeDave Kleikamp  int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
372470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
373470decc613ab2048b619a01028072d932d9086eeDave Kleikamp  /* Map in the shared memory. Always set register 0 last to remain
374470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	 compatible with very old boards. */
375470decc613ab2048b619a01028072d932d9086eeDave Kleikamp  ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
376470decc613ab2048b619a01028072d932d9086eeDave Kleikamp  ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
377f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao
378470decc613ab2048b619a01028072d932d9086eeDave Kleikamp  if (ei_status.word16)
379470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	  outb(ei_status.reg5, ioaddr+WD_CMDREG5);
380470decc613ab2048b619a01028072d932d9086eeDave Kleikamp  outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
381470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
382470decc613ab2048b619a01028072d932d9086eeDave Kleikamp  return ei_open(dev);
383470decc613ab2048b619a01028072d932d9086eeDave Kleikamp}
384470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
385470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void
386470decc613ab2048b619a01028072d932d9086eeDave Kleikampwd_reset_8390(struct net_device *dev)
387470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{
388470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
389470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
390470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	outb(WD_RESET, wd_cmd_port);
391470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies);
392470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	ei_status.txing = 0;
393470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
394470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* Set up the ASIC registers, just in case something changed them. */
395470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
396470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (ei_status.word16)
397470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
398470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
399470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (ei_debug > 1) printk("reset done\n");
400f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao}
401470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
402470decc613ab2048b619a01028072d932d9086eeDave Kleikamp/* Grab the 8390 specific header. Similar to the block_input routine, but
403470decc613ab2048b619a01028072d932d9086eeDave Kleikamp   we don't need to be concerned with ring wrap as the header will be at
404470decc613ab2048b619a01028072d932d9086eeDave Kleikamp   the start of a page, so we optimize accordingly. */
405470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
406470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void
407470decc613ab2048b619a01028072d932d9086eeDave Kleikampwd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
408470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{
409470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
410470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
411470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8);
412470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
413470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* We'll always get a 4 byte header read followed by a packet read, so
414470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	   we enable 16 bit mode before the header, and disable after the body. */
415470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (ei_status.word16)
416470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
417470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
418470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#ifdef __BIG_ENDIAN
419470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* Officially this is what we are doing, but the readl() is faster */
420470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* unfortunately it isn't endian aware of the struct               */
421470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
422470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	hdr->count = le16_to_cpu(hdr->count);
423470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#else
424470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	((unsigned int*)hdr)[0] = readl(hdr_start);
425470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif
426470decc613ab2048b619a01028072d932d9086eeDave Kleikamp}
427470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
428470decc613ab2048b619a01028072d932d9086eeDave Kleikamp/* Block input and output are easy on shared memory ethercards, and trivial
429470decc613ab2048b619a01028072d932d9086eeDave Kleikamp   on the Western digital card where there is no choice of how to do it.
430470decc613ab2048b619a01028072d932d9086eeDave Kleikamp   The only complications are that the ring buffer wraps, and need to map
431470decc613ab2048b619a01028072d932d9086eeDave Kleikamp   switch between 8- and 16-bit modes. */
432470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
433470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void
434470decc613ab2048b619a01028072d932d9086eeDave Kleikampwd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
435470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{
436470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
437470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	unsigned long offset = ring_offset - (WD_START_PG<<8);
438470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	void __iomem *xfer_start = ei_status.mem + offset;
439470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
440470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (offset + count > ei_status.priv) {
441470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		/* We must wrap the input move. */
442470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		int semi_count = ei_status.priv - offset;
443470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		memcpy_fromio(skb->data, xfer_start, semi_count);
444470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		count -= semi_count;
445470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
446470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	} else {
447470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		/* Packet is in one chunk -- we can copy + cksum. */
448470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		memcpy_fromio(skb->data, xfer_start, count);
449470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	}
450470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
451470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* Turn off 16 bit access so that reboot works.	 ISA brain-damage */
452470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (ei_status.word16)
453470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
454470decc613ab2048b619a01028072d932d9086eeDave Kleikamp}
455f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao
456f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Caostatic void
457470decc613ab2048b619a01028072d932d9086eeDave Kleikampwd_block_output(struct net_device *dev, int count, const unsigned char *buf,
458470decc613ab2048b619a01028072d932d9086eeDave Kleikamp				int start_page)
459470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{
460470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
461470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8);
462470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
463470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
464470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (ei_status.word16) {
465470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		/* Turn on and off 16 bit access so that reboot works. */
466470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
467470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		memcpy_toio(shmem, buf, count);
468470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
469470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	} else
470470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		memcpy_toio(shmem, buf, count);
471470decc613ab2048b619a01028072d932d9086eeDave Kleikamp}
472470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
473470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
474470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int
475470decc613ab2048b619a01028072d932d9086eeDave Kleikampwd_close(struct net_device *dev)
476470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{
477470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
478470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
479470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (ei_debug > 1)
480470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		printk("%s: Shutting down ethercard.\n", dev->name);
481470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	ei_close(dev);
482470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
483470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* Change from 16-bit to 8-bit shared memory so reboot works. */
484470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (ei_status.word16)
485470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
486470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
487470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	/* And disable the shared memory. */
488470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
489470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
490470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	return 0;
491470decc613ab2048b619a01028072d932d9086eeDave Kleikamp}
492470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
493470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
494470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#ifdef MODULE
495470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#define MAX_WD_CARDS	4	/* Max number of wd cards per module */
496470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic struct net_device *dev_wd[MAX_WD_CARDS];
497470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int io[MAX_WD_CARDS];
498470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int irq[MAX_WD_CARDS];
499470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int mem[MAX_WD_CARDS];
500470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic int mem_end[MAX_WD_CARDS];	/* for non std. mem size */
501470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
502470decc613ab2048b619a01028072d932d9086eeDave Kleikampmodule_param_array(io, int, NULL, 0);
503470decc613ab2048b619a01028072d932d9086eeDave Kleikampmodule_param_array(irq, int, NULL, 0);
504470decc613ab2048b619a01028072d932d9086eeDave Kleikampmodule_param_array(mem, int, NULL, 0);
505470decc613ab2048b619a01028072d932d9086eeDave Kleikampmodule_param_array(mem_end, int, NULL, 0);
506470decc613ab2048b619a01028072d932d9086eeDave KleikampMODULE_PARM_DESC(io, "I/O base address(es)");
507470decc613ab2048b619a01028072d932d9086eeDave KleikampMODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)");
508470decc613ab2048b619a01028072d932d9086eeDave KleikampMODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)");
509470decc613ab2048b619a01028072d932d9086eeDave KleikampMODULE_PARM_DESC(mem_end, "memory end address(es)");
510470decc613ab2048b619a01028072d932d9086eeDave KleikampMODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver");
511470decc613ab2048b619a01028072d932d9086eeDave KleikampMODULE_LICENSE("GPL");
512470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
513470decc613ab2048b619a01028072d932d9086eeDave Kleikamp/* This is set up so that only a single autoprobe takes place per call.
514470decc613ab2048b619a01028072d932d9086eeDave KleikampISA device autoprobes on a running machine are not recommended. */
515470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
516470decc613ab2048b619a01028072d932d9086eeDave Kleikampint __init init_module(void)
517470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{
518470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	struct net_device *dev;
519470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int this_dev, found = 0;
520470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
521f7f4bccb729844a0fa873e224e3a6f7eeed095bbMingming Cao	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
522470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		if (io[this_dev] == 0)  {
523470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			if (this_dev != 0) break; /* only autoprobe 1st one */
524470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
525470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		}
526470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev = alloc_ei_netdev();
527470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		if (!dev)
528470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			break;
529470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev->irq = irq[this_dev];
530470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev->base_addr = io[this_dev];
531470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev->mem_start = mem[this_dev];
532470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		dev->mem_end = mem_end[this_dev];
533470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		if (do_wd_probe(dev) == 0) {
534470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			dev_wd[found++] = dev;
535470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			continue;
536470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		}
537470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		free_netdev(dev);
538470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
539470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		break;
540470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	}
541470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	if (found)
542470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		return 0;
543470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	return -ENXIO;
544470decc613ab2048b619a01028072d932d9086eeDave Kleikamp}
545470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
546470decc613ab2048b619a01028072d932d9086eeDave Kleikampstatic void cleanup_card(struct net_device *dev)
547470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{
548470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	free_irq(dev->irq, dev);
549470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT);
550470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	iounmap(ei_status.mem);
551470decc613ab2048b619a01028072d932d9086eeDave Kleikamp}
552470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
553470decc613ab2048b619a01028072d932d9086eeDave Kleikampvoid __exit
554470decc613ab2048b619a01028072d932d9086eeDave Kleikampcleanup_module(void)
555470decc613ab2048b619a01028072d932d9086eeDave Kleikamp{
556470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	int this_dev;
557470decc613ab2048b619a01028072d932d9086eeDave Kleikamp
558470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
559470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		struct net_device *dev = dev_wd[this_dev];
560470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		if (dev) {
561470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			unregister_netdev(dev);
562470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			cleanup_card(dev);
563470decc613ab2048b619a01028072d932d9086eeDave Kleikamp			free_netdev(dev);
564470decc613ab2048b619a01028072d932d9086eeDave Kleikamp		}
565470decc613ab2048b619a01028072d932d9086eeDave Kleikamp	}
566470decc613ab2048b619a01028072d932d9086eeDave Kleikamp}
567470decc613ab2048b619a01028072d932d9086eeDave Kleikamp#endif /* MODULE */
568470decc613ab2048b619a01028072d932d9086eeDave Kleikamp