1 /* wd.c: A WD80x3 ethernet driver for linux. */
2 /*
3 Written 1993-94 by Donald Becker.
4
5 Copyright 1993 United States Government as represented by the
6 Director, National Security Agency.
7
8 This software may be used and distributed according to the terms
9 of the GNU Public License, incorporated herein by reference.
10
11 The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
12 Center of Excellence in Space Data and Information Sciences
13 Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
14
15 This is a driver for WD8003 and WD8013 "compatible" ethercards.
16
17 Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
18 */
19
20 static char *version =
21 "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
22
23 #include <linux/config.h>
24 #include <linux/kernel.h>
25 #include <linux/sched.h>
26 #include <linux/errno.h>
27 #include <linux/string.h>
28 #include <asm/io.h>
29 #include <asm/system.h>
30
31 #include <linux/netdevice.h>
32 #include "8390.h"
33 extern struct device *init_etherdev(struct device *dev, int sizeof_private,
34 unsigned long *mem_startp);
35
36 /* A zero-terminated list of I/O addresses to be probed. */
37 static unsigned int wd_portlist[] =
38 {0x300, 0x280, 0x380, 0x240, 0};
39
40 int wd_probe(struct device *dev);
41 int wd_probe1(struct device *dev, int ioaddr);
42
43 static int wd_open(struct device *dev);
44 static void wd_reset_8390(struct device *dev);
45 static int wd_block_input(struct device *dev, int count,
46 char *buf, int ring_offset);
47 static void wd_block_output(struct device *dev, int count,
48 const unsigned char *buf, const start_page);
49 static int wd_close_card(struct device *dev);
50
51
52 #define WD_START_PG 0x00 /* First page of TX buffer */
53 #define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
54 #define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
55
56 #define WD_CMDREG 0 /* Offset to ASIC command register. */
57 #define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
58 #define WD_MEMENB 0x40 /* Enable the shared memory. */
59 #define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
60 #define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
61 #define NIC16 0x40 /* Enable 16 bit access from the 8390. */
62 #define WD_NIC_OFFSET 16 /* Offset to the 8390 from the base_addr. */
63 #define WD_IO_EXTENT 32
64
65
66 /* Probe for the WD8003 and WD8013. These cards have the station
67 address PROM at I/O ports <base>+8 to <base>+13, with a checksum
68 following. A Soundblaster can have the same checksum as an WDethercard,
69 so we have an extra exclusionary check for it.
70
71 The wd_probe1() routine initializes the card and fills the
72 station address field. */
73
74 #ifdef HAVE_DEVLIST
75 struct netdev_entry wd_drv =
76 {"wd", wd_probe1, WD_IO_EXTENT, wd_portlist};
77 #else
78
79 int wd_probe(struct device *dev)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
80 {
81 int i;
82 int base_addr = dev ? dev->base_addr : 0;
83
84 if (base_addr > 0x1ff) /* Check a single specified location. */
85 return wd_probe1(dev, base_addr);
86 else if (base_addr != 0) /* Don't probe at all. */
87 return ENXIO;
88
89 for (i = 0; wd_portlist[i]; i++) {
90 int ioaddr = wd_portlist[i];
91 if (check_region(ioaddr, WD_IO_EXTENT))
92 continue;
93 if (wd_probe1(dev, ioaddr) == 0)
94 return 0;
95 }
96
97 return ENODEV;
98 }
99 #endif
100
101 int wd_probe1(struct device *dev, int ioaddr)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
102 {
103 int i;
104 int checksum = 0;
105 int ancient = 0; /* An old card without config registers. */
106 int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */
107 char *model_name;
108
109 for (i = 0; i < 8; i++)
110 checksum += inb(ioaddr + 8 + i);
111 if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */
112 || inb(ioaddr + 9) == 0xff
113 || (checksum & 0xff) != 0xFF)
114 return ENODEV;
115
116 if (dev == NULL)
117 dev = init_etherdev(0, sizeof(struct ei_device), 0);
118
119 printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr);
120 for (i = 0; i < 6; i++)
121 printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
122
123 /* The following PureData probe code was contributed by
124 Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
125 configuration differently from others so we have to check for them.
126 This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
127 */
128 if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
129 unsigned char reg5 = inb(ioaddr+5);
130
131 switch (inb(ioaddr+2)) {
132 case 0x03: word16 = 0; model_name = "PDI8023-8"; break;
133 case 0x05: word16 = 0; model_name = "PDUC8023"; break;
134 case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
135 /* Either 0x01 (dumb) or they've released a new version. */
136 default: word16 = 0; model_name = "PDI8023"; break;
137 }
138 dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
139 dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
140 } else { /* End of PureData probe */
141 /* This method of checking for a 16-bit board is borrowed from the
142 we.c driver. A simpler method is just to look in ASIC reg. 0x03.
143 I'm comparing the two method in alpha test to make certain they
144 return the same result. */
145 /* Check for the old 8 bit board - it has register 0/8 aliasing.
146 Do NOT check i>=6 here -- it hangs the old 8003 boards! */
147 for (i = 0; i < 6; i++)
148 if (inb(ioaddr+i) != inb(ioaddr+8+i))
149 break;
150 if (i >= 6) {
151 ancient = 1;
152 model_name = "WD8003-old";
153 word16 = 0;
154 } else {
155 int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
156 outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
157 if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
158 && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */
159 int asic_reg5 = inb(ioaddr+WD_CMDREG5);
160 /* Magic to set ASIC to word-wide mode. */
161 outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
162 outb(tmp, ioaddr+1);
163 model_name = "WD8013";
164 word16 = 1; /* We have a 16bit board here! */
165 } else {
166 model_name = "WD8003";
167 word16 = 0;
168 }
169 outb(tmp, ioaddr+1); /* Restore original reg1 value. */
170 }
171 #ifndef final_version
172 if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
173 printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
174 word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
175 #endif
176 }
177
178 #if defined(WD_SHMEM) && WD_SHMEM > 0x80000
179 /* Allow a compile-time override. */
180 dev->mem_start = WD_SHMEM;
181 #else
182 if (dev->mem_start == 0) {
183 /* Sanity and old 8003 check */
184 int reg0 = inb(ioaddr);
185 if (reg0 == 0xff || reg0 == 0) {
186 /* Future plan: this could check a few likely locations first. */
187 dev->mem_start = 0xd0000;
188 printk(" assigning address %#lx", dev->mem_start);
189 } else {
190 int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
191 /* Some boards don't have the register 5 -- it returns 0xff. */
192 if (high_addr_bits == 0x1f || word16 == 0)
193 high_addr_bits = 0x01;
194 dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
195 }
196 }
197 #endif
198
199 /* The 8390 isn't at the base address -- the ASIC regs are there! */
200 dev->base_addr = ioaddr+WD_NIC_OFFSET;
201
202 if (dev->irq < 2) {
203 int irqmap[] = {9,3,5,7,10,11,15,4};
204 int reg1 = inb(ioaddr+1);
205 int reg4 = inb(ioaddr+4);
206 if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */
207 short nic_addr = ioaddr+WD_NIC_OFFSET;
208
209 /* We have an old-style ethercard that doesn't report its IRQ
210 line. Do autoirq to find the IRQ line. Note that this IS NOT
211 a reliable way to trigger an interrupt. */
212 outb_p(E8390_NODMA + E8390_STOP, nic_addr);
213 outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */
214 autoirq_setup(0);
215 outb_p(0xff, nic_addr + EN0_IMR); /* Enable all interrupts. */
216 outb_p(0x00, nic_addr + EN0_RCNTLO);
217 outb_p(0x00, nic_addr + EN0_RCNTHI);
218 outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
219 dev->irq = autoirq_report(2);
220 outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */
221
222 if (ei_debug > 2)
223 printk(" autoirq is %d", dev->irq);
224 if (dev->irq < 2)
225 dev->irq = word16 ? 10 : 5;
226 } else
227 dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
228 } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */
229 dev->irq = 9;
230
231 /* Snarf the interrupt now. There's no point in waiting since we cannot
232 share and the board will usually be enabled. */
233 if (request_irq(dev->irq, ei_interrupt, 0, "wd")) {
234 printk (" unable to get IRQ %d.\n", dev->irq);
235 return EAGAIN;
236 }
237
238 /* OK, were are certain this is going to work. Setup the device. */
239 snarf_region(ioaddr, WD_IO_EXTENT);
240 ethdev_init(dev);
241
242 ei_status.name = model_name;
243 ei_status.word16 = word16;
244 ei_status.tx_start_page = WD_START_PG;
245 ei_status.rx_start_page = WD_START_PG + TX_PAGES;
246 ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
247
248 /* Don't map in the shared memory until the board is actually opened. */
249 dev->rmem_start = dev->mem_start + TX_PAGES*256;
250 dev->mem_end = dev->rmem_end
251 = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
252
253 printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
254 model_name, dev->irq, dev->mem_start, dev->mem_end-1);
255 if (ei_debug > 0)
256 printk(version);
257
258 ei_status.reset_8390 = &wd_reset_8390;
259 ei_status.block_input = &wd_block_input;
260 ei_status.block_output = &wd_block_output;
261 dev->open = &wd_open;
262 dev->stop = &wd_close_card;
263 NS8390_init(dev, 0);
264
265 return 0;
266 }
267
268 static int
269 wd_open(struct device *dev)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
270 {
271 int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
272
273 /* Map in the shared memory. Always set register 0 last to remain
274 compatible with very old boards. */
275 ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
276 ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
277
278 if (ei_status.word16)
279 outb(ei_status.reg5, ioaddr+WD_CMDREG5);
280 outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
281
282 return ei_open(dev);
283 }
284
285 static void
286 wd_reset_8390(struct device *dev)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
287 {
288 int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
289
290 outb(WD_RESET, wd_cmd_port);
291 if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies);
292 ei_status.txing = 0;
293
294 /* Set up the ASIC registers, just in case something changed them. */
295 outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
296 if (ei_status.word16)
297 outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
298
299 if (ei_debug > 1) printk("reset done\n");
300 return;
301 }
302
303 /* Block input and output are easy on shared memory ethercards, and trivial
304 on the Western digital card where there is no choice of how to do it.
305 The only complications are that the ring buffer wraps, and need to map
306 switch between 8- and 16-bit modes. */
307
308 static int
309 wd_block_input(struct device *dev, int count, char *buf, int ring_offset)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
310 {
311 int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
312 long xfer_start = dev->mem_start + ring_offset - (WD_START_PG<<8);
313
314 /* We'll always get a 4 byte header read followed by a packet read, so
315 we enable 16 bit mode before the header, and disable after the body. */
316 if (count == 4) {
317 if (ei_status.word16)
318 outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
319 ((int*)buf)[0] = ((int*)xfer_start)[0];
320 return 0;
321 }
322
323 if (xfer_start + count > dev->rmem_end) {
324 /* We must wrap the input move. */
325 int semi_count = dev->rmem_end - xfer_start;
326 memcpy(buf, (char *)xfer_start, semi_count);
327 count -= semi_count;
328 memcpy(buf + semi_count, (char *)dev->rmem_start, count);
329 } else
330 memcpy(buf, (char *)xfer_start, count);
331
332 /* Turn off 16 bit access so that reboot works. ISA brain-damage */
333 if (ei_status.word16)
334 outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
335
336 return 0;
337 }
338
339 static void
340 wd_block_output(struct device *dev, int count, const unsigned char *buf,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
341 int start_page)
342 {
343 int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
344 long shmem = dev->mem_start + ((start_page - WD_START_PG)<<8);
345
346
347 if (ei_status.word16) {
348 /* Turn on and off 16 bit access so that reboot works. */
349 outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
350 memcpy((char *)shmem, buf, count);
351 outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
352 } else
353 memcpy((char *)shmem, buf, count);
354 }
355
356
357 static int
358 wd_close_card(struct device *dev)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
359 {
360 int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
361
362 if (ei_debug > 1)
363 printk("%s: Shutting down ethercard.\n", dev->name);
364 NS8390_init(dev, 0);
365
366 /* Change from 16-bit to 8-bit shared memory so reboot works. */
367 outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
368
369 /* And disable the shared memory. */
370 outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
371
372 return 0;
373 }
374
375
376 /*
377 * Local variables:
378 * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c wd.c"
379 * version-control: t
380 * tab-width: 4
381 * kept-new-versions: 5
382 * End:
383 */