1 /* wd.c: A WD80x3 ethernet driver for linux. */
3 Written 1993 by Donald Becker.
4 Copyright 1993 United States Government as represented by the
5 Director, National Security Agency. This software may be used and
6 distributed according to the terms of the GNU Public License,
7 incorporated herein by reference.
9 This is a driver for WD8003 and WD8013 "compatible" ethercards.
11 The Author may be reached as becker@super.org or
12 C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
14 Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
17 static char *version
=
18 "wd.c:v0.99-14 11/21/93 Donald Becker (becker@super.org)\n";
20 #include <linux/config.h>
21 #include <linux/kernel.h>
22 #include <linux/sched.h>
23 #include <linux/errno.h>
24 #include <linux/string.h>
26 #include <asm/system.h>
31 /* Compatibility definitions for earlier kernel versions. */
32 #ifndef HAVE_PORTRESERVE
33 #define check_region(ioaddr, size) 0
34 #define snarf_region(ioaddr, size) do ; while (0)
37 int wd_probe(struct device
*dev
);
38 int wdprobe1(int ioaddr
, struct device
*dev
);
40 static int wd_open(struct device
*dev
);
41 static void wd_reset_8390(struct device
*dev
);
42 static int wd_block_input(struct device
*dev
, int count
,
43 char *buf
, int ring_offset
);
44 static void wd_block_output(struct device
*dev
, int count
,
45 const unsigned char *buf
, const start_page
);
46 static int wd_close_card(struct device
*dev
);
49 #define WD_START_PG 0x00 /* First page of TX buffer */
50 #define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
51 #define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
53 #define WD_CMDREG 0 /* Offset to ASIC command register. */
54 #define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
55 #define WD_MEMENB 0x40 /* Enable the shared memory. */
56 #define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
57 #define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
58 #define NIC16 0x40 /* Enable 16 bit access from the 8390. */
59 #define WD_NIC_OFFSET 16 /* Offset to the 8390 NIC from the base_addr. */
61 /* Probe for the WD8003 and WD8013. These cards have the station
62 address PROM at I/O ports <base>+8 to <base>+13, with a checksum
63 following. A Soundblaster can have the same checksum as an WDethercard,
64 so we have an extra exclusionary check for it.
66 The wdprobe1() routine initializes the card and fills the
67 station address field. */
69 int wd_probe(struct device
*dev
)
71 int *port
, ports
[] = {0x300, 0x280, 0x380, 0x240, 0};
72 short ioaddr
= dev
->base_addr
;
75 return ENXIO
; /* Don't probe at all. */
77 return ! wdprobe1(ioaddr
, dev
);
79 for (port
= &ports
[0]; *port
; port
++) {
80 if (check_region(*port
, 32))
82 if (inb(*port
+ 8) != 0xff
83 && inb(*port
+ 9) != 0xff /* Extra check to avoid soundcard. */
84 && wdprobe1(*port
, dev
))
87 dev
->base_addr
= ioaddr
;
91 int wdprobe1(int ioaddr
, struct device
*dev
)
94 unsigned char *station_addr
= dev
->dev_addr
;
96 int ancient
= 0; /* An old card without config registers. */
97 int word16
= 0; /* 0 = 8 bit, 1 = 16 bit */
100 for (i
= 0; i
< 8; i
++)
101 checksum
+= inb(ioaddr
+ 8 + i
);
102 if ((checksum
& 0xff) != 0xFF)
105 printk("%s: WD80x3 at %#3x, ", dev
->name
, ioaddr
);
106 for (i
= 0; i
< 6; i
++)
107 printk(" %2.2X", station_addr
[i
] = inb(ioaddr
+ 8 + i
));
109 /* The following PureData probe code was contributed by
110 Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
111 configuration differently from others so we have to check for them.
112 This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
114 if (inb(ioaddr
+0) == 'P' && inb(ioaddr
+1) == 'D') {
115 unsigned char reg5
= inb(ioaddr
+5);
117 switch (inb(ioaddr
+2)) {
118 case 0x03: word16
= 0; model_name
= "PDI8023-8"; break;
119 case 0x05: word16
= 0; model_name
= "PDUC8023"; break;
120 case 0x0a: word16
= 1; model_name
= "PDI8023-16"; break;
121 /* Either 0x01 (dumb) or they've released a new version. */
122 default: word16
= 0; model_name
= "PDI8023"; break;
124 dev
->mem_start
= ((reg5
& 0x1c) + 0xc0) << 12;
125 dev
->irq
= (reg5
& 0xe0) == 0xe0 ? 10 : (reg5
>> 5) + 1;
126 } else { /* End of PureData probe */
127 /* This method of checking for a 16-bit board is borrowed from the
128 we.c driver. A simpler method is just to look in ASIC reg. 0x03.
129 I'm comparing the two method in alpha test to make certain they
130 return the same result. */
131 /* Check for the old 8 bit board - it has register 0/8 aliasing.
132 Do NOT check i>=6 here -- it hangs the old 8003 boards! */
133 for (i
= 0; i
< 6; i
++)
134 if (inb(ioaddr
+i
) != inb(ioaddr
+8+i
))
138 model_name
= "WD8003-old";
141 int tmp
= inb(ioaddr
+1); /* fiddle with 16bit bit */
142 outb( tmp
^ 0x01, ioaddr
+1 ); /* attempt to clear 16bit bit */
143 if (((inb( ioaddr
+1) & 0x01) == 0x01) /* A 16 bit card */
144 && (tmp
& 0x01) == 0x01 ) { /* In a 16 slot. */
145 int asic_reg5
= inb(ioaddr
+WD_CMDREG5
);
146 /* Magic to set ASIC to word-wide mode. */
147 outb( NIC16
| (asic_reg5
&0x1f), ioaddr
+WD_CMDREG5
);
149 model_name
= "WD8013";
150 word16
= 1; /* We have a 16bit board here! */
152 model_name
= "WD8003";
155 outb(tmp
, ioaddr
+1); /* Restore original reg1 value. */
157 #ifndef final_version
158 if ( !ancient
&& (inb(ioaddr
+1) & 0x01) != (word16
& 0x01))
159 printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
160 word16
? 16 : 8, (inb(ioaddr
+1) & 0x01) ? 16 : 8);
164 #if defined(WD_SHMEM) && WD_SHMEM > 0x80000
165 /* Allow a compile-time override. */
166 dev
->mem_start
= WD_SHMEM
;
168 if (dev
->mem_start
== 0) {
169 /* Sanity and old 8003 check */
170 int reg0
= inb(ioaddr
);
171 if (reg0
== 0xff || reg0
== 0) {
172 /* Future plan: this could check a few likely locations first. */
173 dev
->mem_start
= 0xd0000;
174 printk(" assigning address %#lx", dev
->mem_start
);
176 int high_addr_bits
= inb(ioaddr
+WD_CMDREG5
) & 0x1f;
177 /* Some boards don't have the register 5 -- it returns 0xff. */
178 if (high_addr_bits
== 0x1f || word16
== 0)
179 high_addr_bits
= 0x01;
180 dev
->mem_start
= ((reg0
&0x3f) << 13) + (high_addr_bits
<< 19);
185 /* The 8390 isn't at the base address -- the ASIC regs are there! */
186 dev
->base_addr
= ioaddr
+WD_NIC_OFFSET
;
189 int irqmap
[] = {9,3,5,7,10,11,15,4};
190 int reg1
= inb(ioaddr
+1);
191 int reg4
= inb(ioaddr
+4);
192 if (ancient
|| reg1
== 0xff) { /* Ack!! No way to read the IRQ! */
193 short nic_addr
= ioaddr
+WD_NIC_OFFSET
;
195 /* We have an old-style ethercard that doesn't report its IRQ
196 line. Do autoirq to find the IRQ line. Note that this IS NOT
197 a reliable way to trigger an interrupt. */
198 outb_p(E8390_NODMA
+ E8390_STOP
, nic_addr
);
199 outb(0x00, nic_addr
+EN0_IMR
); /* Disable all intrs. */
201 outb_p(0xff, nic_addr
+ EN0_IMR
); /* Enable all interrupts. */
202 outb_p(0x00, nic_addr
+ EN0_RCNTLO
);
203 outb_p(0x00, nic_addr
+ EN0_RCNTHI
);
204 outb(E8390_RREAD
+E8390_START
, nic_addr
); /* Trigger it... */
205 dev
->irq
= autoirq_report(2);
206 outb_p(0x00, nic_addr
+EN0_IMR
); /* Mask all intrs. again. */
209 printk(" autoirq is %d", dev
->irq
);
211 dev
->irq
= word16
? 10 : 5;
213 dev
->irq
= irqmap
[((reg4
>> 5) & 0x03) + (reg1
& 0x04)];
214 } else if (dev
->irq
== 2) /* Fixup bogosity: IRQ2 is really IRQ9 */
217 /* Snarf the interrupt now. There's no point in waiting since we cannot
218 share and the board will usually be enabled. */
219 if (irqaction (dev
->irq
, &ei_sigaction
)) {
220 printk (" unable to get IRQ %d.\n", dev
->irq
);
224 /* OK, were are certain this is going to work. Setup the device. */
225 snarf_region(ioaddr
, 32);
228 ei_status
.name
= model_name
;
229 ei_status
.word16
= word16
;
230 ei_status
.tx_start_page
= WD_START_PG
;
231 ei_status
.rx_start_page
= WD_START_PG
+ TX_PAGES
;
232 ei_status
.stop_page
= word16
? WD13_STOP_PG
: WD03_STOP_PG
;
234 /* Don't map in the shared memory until the board is actually opened. */
235 dev
->rmem_start
= dev
->mem_start
+ TX_PAGES
*256;
236 dev
->mem_end
= dev
->rmem_end
237 = dev
->mem_start
+ (ei_status
.stop_page
- WD_START_PG
)*256;
239 printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
240 model_name
, dev
->irq
, dev
->mem_start
, dev
->mem_end
-1);
244 ei_status
.reset_8390
= &wd_reset_8390
;
245 ei_status
.block_input
= &wd_block_input
;
246 ei_status
.block_output
= &wd_block_output
;
247 dev
->open
= &wd_open
;
248 dev
->stop
= &wd_close_card
;
251 return dev
->base_addr
;
255 wd_open(struct device
*dev
)
257 int ioaddr
= dev
->base_addr
- WD_NIC_OFFSET
; /* WD_CMDREG */
259 /* Map in the shared memory. Always set register 0 last to remain
260 compatible with very old boards. */
261 ei_status
.reg0
= ((dev
->mem_start
>>13) & 0x3f) | WD_MEMENB
;
262 ei_status
.reg5
= ((dev
->mem_start
>>19) & 0x1f) | NIC16
;
264 if (ei_status
.word16
)
265 outb(ei_status
.reg5
, ioaddr
+WD_CMDREG5
);
266 outb(ei_status
.reg0
, ioaddr
); /* WD_CMDREG */
272 wd_reset_8390(struct device
*dev
)
274 int wd_cmd_port
= dev
->base_addr
- WD_NIC_OFFSET
; /* WD_CMDREG */
276 outb(WD_RESET
, wd_cmd_port
);
277 if (ei_debug
> 1) printk("resetting the WD80x3 t=%lu...", jiffies
);
280 /* Set up the ASIC registers, just in case something changed them. */
281 outb((((dev
->mem_start
>>13) & 0x3f)|WD_MEMENB
), wd_cmd_port
);
282 if (ei_status
.word16
)
283 outb(NIC16
| ((dev
->mem_start
>>19) & 0x1f), wd_cmd_port
+WD_CMDREG5
);
285 if (ei_debug
> 1) printk("reset done\n");
289 /* Block input and output are easy on shared memory ethercards, and trivial
290 on the Western digital card where there is no choice of how to do it.
291 The only complications are that the ring buffer wraps, and need to map
292 switch between 8- and 16-bit modes. */
295 wd_block_input(struct device
*dev
, int count
, char *buf
, int ring_offset
)
297 int wd_cmdreg
= dev
->base_addr
- WD_NIC_OFFSET
; /* WD_CMDREG */
298 long xfer_start
= dev
->mem_start
+ ring_offset
- (WD_START_PG
<<8);
300 /* We'll always get a 4 byte header read followed by a packet read, so
301 we enable 16 bit mode before the header, and disable after the body. */
303 if (ei_status
.word16
)
304 outb(ISA16
| ei_status
.reg5
, wd_cmdreg
+WD_CMDREG5
);
305 ((int*)buf
)[0] = ((int*)xfer_start
)[0];
309 if (xfer_start
+ count
> dev
->rmem_end
) {
310 /* We must wrap the input move. */
311 int semi_count
= dev
->rmem_end
- xfer_start
;
312 memcpy(buf
, (char *)xfer_start
, semi_count
);
314 memcpy(buf
+ semi_count
, (char *)dev
->rmem_start
, count
);
316 memcpy(buf
, (char *)xfer_start
, count
);
318 /* Turn off 16 bit access so that reboot works. ISA brain-damage */
319 if (ei_status
.word16
)
320 outb(ei_status
.reg5
, wd_cmdreg
+WD_CMDREG5
);
326 wd_block_output(struct device
*dev
, int count
, const unsigned char *buf
,
329 int wd_cmdreg
= dev
->base_addr
- WD_NIC_OFFSET
; /* WD_CMDREG */
330 long shmem
= dev
->mem_start
+ ((start_page
- WD_START_PG
)<<8);
333 if (ei_status
.word16
) {
334 /* Turn on and off 16 bit access so that reboot works. */
335 outb(ISA16
| ei_status
.reg5
, wd_cmdreg
+WD_CMDREG5
);
336 memcpy((char *)shmem
, buf
, count
);
337 outb(ei_status
.reg5
, wd_cmdreg
+WD_CMDREG5
);
339 memcpy((char *)shmem
, buf
, count
);
344 wd_close_card(struct device
*dev
)
346 int wd_cmdreg
= dev
->base_addr
- WD_NIC_OFFSET
; /* WD_CMDREG */
349 printk("%s: Shutting down ethercard.\n", dev
->name
);
352 /* Change from 16-bit to 8-bit shared memory so reboot works. */
353 outb(ei_status
.reg5
, wd_cmdreg
+ WD_CMDREG5
);
355 /* And disable the shared memory. */
356 outb(ei_status
.reg0
& ~WD_MEMENB
, wd_cmdreg
);
364 * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c wd.c"
367 * kept-new-versions: 5