1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28static const char version[] =
29 "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
30
31#include <linux/module.h>
32
33#include <linux/kernel.h>
34#include <linux/sched.h>
35#include <linux/errno.h>
36#include <linux/string.h>
37#include <linux/init.h>
38#include <linux/delay.h>
39#include <asm/io.h>
40#include <asm/system.h>
41
42#include <linux/netdevice.h>
43#include <linux/etherdevice.h>
44#include "8390.h"
45
46
47static unsigned int wd_portlist[] __initdata =
48{0x300, 0x280, 0x380, 0x240, 0};
49
50int wd_probe(struct net_device *dev);
51static int wd_probe1(struct net_device *dev, int ioaddr);
52
53static int wd_open(struct net_device *dev);
54static void wd_reset_8390(struct net_device *dev);
55static void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
56 int ring_page);
57static void wd_block_input(struct net_device *dev, int count,
58 struct sk_buff *skb, int ring_offset);
59static void wd_block_output(struct net_device *dev, int count,
60 const unsigned char *buf, int start_page);
61static int wd_close(struct net_device *dev);
62
63
64#define WD_START_PG 0x00
65#define WD03_STOP_PG 0x20
66#define WD13_STOP_PG 0x40
67
68#define WD_CMDREG 0
69#define WD_RESET 0x80
70#define WD_MEMENB 0x40
71#define WD_CMDREG5 5
72#define ISA16 0x80
73#define NIC16 0x40
74#define WD_NIC_OFFSET 16
75#define WD_IO_EXTENT 32
76
77
78
79
80
81
82
83
84
85
86int __init wd_probe(struct net_device *dev)
87{
88 int i;
89 struct resource *r;
90 int base_addr = dev->base_addr;
91
92 SET_MODULE_OWNER(dev);
93
94 if (base_addr > 0x1ff) {
95 r = request_region(base_addr, WD_IO_EXTENT, "wd-probe");
96 if ( r == NULL)
97 return -EBUSY;
98 i = wd_probe1(dev, base_addr);
99 if (i != 0)
100 release_region(base_addr, WD_IO_EXTENT);
101 else
102 r->name = dev->name;
103 return i;
104 }
105 else if (base_addr != 0)
106 return -ENXIO;
107
108 for (i = 0; wd_portlist[i]; i++) {
109 int ioaddr = wd_portlist[i];
110 r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe");
111 if (r == NULL)
112 continue;
113 if (wd_probe1(dev, ioaddr) == 0) {
114 r->name = dev->name;
115 return 0;
116 }
117 release_region(ioaddr, WD_IO_EXTENT);
118 }
119
120 return -ENODEV;
121}
122
123static int __init wd_probe1(struct net_device *dev, int ioaddr)
124{
125 int i;
126 int checksum = 0;
127 int ancient = 0;
128 int word16 = 0;
129 const char *model_name;
130 static unsigned version_printed;
131
132 for (i = 0; i < 8; i++)
133 checksum += inb(ioaddr + 8 + i);
134 if (inb(ioaddr + 8) == 0xff
135 || inb(ioaddr + 9) == 0xff
136 || (checksum & 0xff) != 0xFF)
137 return -ENODEV;
138
139
140 if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
141 printk(KERN_WARNING "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
142 dev->mem_start = 0;
143 dev->mem_end = 0;
144 }
145
146 if (ei_debug && version_printed++ == 0)
147 printk(version);
148
149 printk("%s: WD80x3 at %#3x,", dev->name, ioaddr);
150 for (i = 0; i < 6; i++)
151 printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
152
153
154
155
156
157
158 if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
159 unsigned char reg5 = inb(ioaddr+5);
160
161 switch (inb(ioaddr+2)) {
162 case 0x03: word16 = 0; model_name = "PDI8023-8"; break;
163 case 0x05: word16 = 0; model_name = "PDUC8023"; break;
164 case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
165
166 default: word16 = 0; model_name = "PDI8023"; break;
167 }
168 dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
169 dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
170 } else {
171
172
173
174
175
176
177 for (i = 0; i < 6; i++)
178 if (inb(ioaddr+i) != inb(ioaddr+8+i))
179 break;
180 if (i >= 6) {
181 ancient = 1;
182 model_name = "WD8003-old";
183 word16 = 0;
184 } else {
185 int tmp = inb(ioaddr+1);
186 outb( tmp ^ 0x01, ioaddr+1 );
187 if (((inb( ioaddr+1) & 0x01) == 0x01)
188 && (tmp & 0x01) == 0x01 ) {
189 int asic_reg5 = inb(ioaddr+WD_CMDREG5);
190
191 outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
192 outb(tmp, ioaddr+1);
193 model_name = "WD8013";
194 word16 = 1;
195 } else {
196 model_name = "WD8003";
197 word16 = 0;
198 }
199 outb(tmp, ioaddr+1);
200 }
201#ifndef final_version
202 if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
203 printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
204 word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
205#endif
206 }
207
208#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
209
210 dev->mem_start = WD_SHMEM;
211#else
212 if (dev->mem_start == 0) {
213
214 int reg0 = inb(ioaddr);
215 if (reg0 == 0xff || reg0 == 0) {
216
217 dev->mem_start = 0xd0000;
218 printk(" assigning address %#lx", dev->mem_start);
219 } else {
220 int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
221
222 if (high_addr_bits == 0x1f || word16 == 0)
223 high_addr_bits = 0x01;
224 dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
225 }
226 }
227#endif
228
229
230 dev->base_addr = ioaddr+WD_NIC_OFFSET;
231
232 if (dev->irq < 2) {
233 int irqmap[] = {9,3,5,7,10,11,15,4};
234 int reg1 = inb(ioaddr+1);
235 int reg4 = inb(ioaddr+4);
236 if (ancient || reg1 == 0xff) {
237 short nic_addr = ioaddr+WD_NIC_OFFSET;
238
239
240
241
242 outb_p(E8390_NODMA + E8390_STOP, nic_addr);
243 outb(0x00, nic_addr+EN0_IMR);
244 autoirq_setup(0);
245 outb_p(0xff, nic_addr + EN0_IMR);
246 outb_p(0x00, nic_addr + EN0_RCNTLO);
247 outb_p(0x00, nic_addr + EN0_RCNTHI);
248 outb(E8390_RREAD+E8390_START, nic_addr);
249 dev->irq = autoirq_report(2);
250 outb_p(0x00, nic_addr+EN0_IMR);
251
252 if (ei_debug > 2)
253 printk(" autoirq is %d", dev->irq);
254 if (dev->irq < 2)
255 dev->irq = word16 ? 10 : 5;
256 } else
257 dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
258 } else if (dev->irq == 2)
259 dev->irq = 9;
260
261
262 if (ethdev_init(dev)) {
263 printk (" unable to get memory for dev->priv.\n");
264 return -ENOMEM;
265 }
266
267
268
269 i = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev);
270 if (i) {
271 printk (" unable to get IRQ %d.\n", dev->irq);
272 kfree(dev->priv);
273 dev->priv = NULL;
274 return i;
275 }
276
277
278 ei_status.name = model_name;
279 ei_status.word16 = word16;
280 ei_status.tx_start_page = WD_START_PG;
281 ei_status.rx_start_page = WD_START_PG + TX_PAGES;
282
283
284 dev->rmem_start = dev->mem_start + TX_PAGES*256;
285
286
287 if (dev->mem_end != 0) {
288 ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
289 } else {
290 ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
291 dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
292 }
293 dev->rmem_end = dev->mem_end;
294
295 printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
296 model_name, dev->irq, dev->mem_start, dev->mem_end-1);
297
298 ei_status.reset_8390 = &wd_reset_8390;
299 ei_status.block_input = &wd_block_input;
300 ei_status.block_output = &wd_block_output;
301 ei_status.get_8390_hdr = &wd_get_8390_hdr;
302 dev->open = &wd_open;
303 dev->stop = &wd_close;
304 NS8390_init(dev, 0);
305
306#if 1
307
308
309 if (inb(ioaddr+14) & 0x20)
310 outb(inb(ioaddr+4)|0x80, ioaddr+4);
311#endif
312
313 return 0;
314}
315
316static int
317wd_open(struct net_device *dev)
318{
319 int ioaddr = dev->base_addr - WD_NIC_OFFSET;
320
321
322
323 ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
324 ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
325
326 if (ei_status.word16)
327 outb(ei_status.reg5, ioaddr+WD_CMDREG5);
328 outb(ei_status.reg0, ioaddr);
329
330 ei_open(dev);
331 return 0;
332}
333
334static void
335wd_reset_8390(struct net_device *dev)
336{
337 int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET;
338
339 outb(WD_RESET, wd_cmd_port);
340 if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies);
341 ei_status.txing = 0;
342
343
344 outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
345 if (ei_status.word16)
346 outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
347
348 if (ei_debug > 1) printk("reset done\n");
349 return;
350}
351
352
353
354
355
356static void
357wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
358{
359
360 int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET;
361 unsigned long hdr_start = dev->mem_start + ((ring_page - WD_START_PG)<<8);
362
363
364
365 if (ei_status.word16)
366 outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
367
368#ifdef __BIG_ENDIAN
369
370
371 isa_memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
372 hdr->count = le16_to_cpu(hdr->count);
373#else
374 ((unsigned int*)hdr)[0] = isa_readl(hdr_start);
375#endif
376}
377
378
379
380
381
382
383static void
384wd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
385{
386 int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET;
387 unsigned long xfer_start = dev->mem_start + ring_offset - (WD_START_PG<<8);
388
389 if (xfer_start + count > dev->rmem_end) {
390
391 int semi_count = dev->rmem_end - xfer_start;
392 isa_memcpy_fromio(skb->data, xfer_start, semi_count);
393 count -= semi_count;
394 isa_memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
395 } else {
396
397 isa_eth_io_copy_and_sum(skb, xfer_start, count, 0);
398 }
399
400
401 if (ei_status.word16)
402 outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
403}
404
405static void
406wd_block_output(struct net_device *dev, int count, const unsigned char *buf,
407 int start_page)
408{
409 int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET;
410 long shmem = dev->mem_start + ((start_page - WD_START_PG)<<8);
411
412
413 if (ei_status.word16) {
414
415 outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
416 isa_memcpy_toio(shmem, buf, count);
417 outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
418 } else
419 isa_memcpy_toio(shmem, buf, count);
420}
421
422
423static int
424wd_close(struct net_device *dev)
425{
426 int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET;
427
428 if (ei_debug > 1)
429 printk("%s: Shutting down ethercard.\n", dev->name);
430 ei_close(dev);
431
432
433 if (ei_status.word16)
434 outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
435
436
437 outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
438
439 return 0;
440}
441
442
443#ifdef MODULE
444#define MAX_WD_CARDS 4
445static struct net_device dev_wd[MAX_WD_CARDS];
446static int io[MAX_WD_CARDS];
447static int irq[MAX_WD_CARDS];
448static int mem[MAX_WD_CARDS];
449static int mem_end[MAX_WD_CARDS];
450
451MODULE_PARM(io, "1-" __MODULE_STRING(MAX_WD_CARDS) "i");
452MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_WD_CARDS) "i");
453MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_WD_CARDS) "i");
454MODULE_PARM(mem_end, "1-" __MODULE_STRING(MAX_WD_CARDS) "i");
455MODULE_PARM_DESC(io, "I/O base address(es)");
456MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)");
457MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)");
458MODULE_PARM_DESC(mem_end, "memory end address(es)");
459MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver");
460MODULE_LICENSE("GPL");
461
462
463
464int
465init_module(void)
466{
467 int this_dev, found = 0;
468
469 for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
470 struct net_device *dev = &dev_wd[this_dev];
471 dev->irq = irq[this_dev];
472 dev->base_addr = io[this_dev];
473 dev->mem_start = mem[this_dev];
474 dev->mem_end = mem_end[this_dev];
475 dev->init = wd_probe;
476 if (io[this_dev] == 0) {
477 if (this_dev != 0) break;
478 printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
479 }
480 if (register_netdev(dev) != 0) {
481 printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
482 if (found != 0) {
483 return 0;
484 }
485 return -ENXIO;
486 }
487 found++;
488 }
489 return 0;
490}
491
492void
493cleanup_module(void)
494{
495 int this_dev;
496
497 for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
498 struct net_device *dev = &dev_wd[this_dev];
499 if (dev->priv != NULL) {
500 void *priv = dev->priv;
501 int ioaddr = dev->base_addr - WD_NIC_OFFSET;
502 free_irq(dev->irq, dev);
503 release_region(ioaddr, WD_IO_EXTENT);
504 unregister_netdev(dev);
505 kfree(priv);
506 }
507 }
508}
509#endif
510
511
512
513
514
515
516
517
518
519
520