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