|
|
1.1 root 1: /*
2: * Copyright (c) 1994 Shantanu Goel
3: * All Rights Reserved.
4: *
5: * Permission to use, copy, modify and distribute this software and its
6: * documentation is hereby granted, provided that both the copyright
7: * notice and this permission notice appear in all copies of the
8: * software, derivative works or modified versions, and any portions
9: * thereof, and that both notices appear in supporting documentation.
10: *
11: * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
12: * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
13: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
14: */
15:
16: /*
17: * Written 1993 by Donald Becker.
18: *
19: * Copyright 1993 United States Government as represented by the
20: * Director, National Security Agency. This software may be used and
21: * distributed according to the terms of the GNU Public License,
22: * incorporated herein by reference.
23: *
24: * The Author may be reached as [email protected] or
25: * C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
26: */
27:
28: #include <wd.h>
29: #if NWD > 0
30: /*
31: * Driver for SMC/Western Digital Ethernet adaptors.
32: * Derived from the Linux driver by Donald Becker.
33: *
34: * Shantanu Goel ([email protected])
35: */
36: #include <mach/sa/sys/types.h>
37: #include "vm_param.h"
38: #include <kern/time_out.h>
39: #include <device/device_types.h>
40: #include <device/errno.h>
41: #include <device/io_req.h>
42: #include <device/if_hdr.h>
43: #include <device/if_ether.h>
44: #include <device/net_status.h>
45: #include <device/net_io.h>
46: #include <chips/busses.h>
47: #include <i386/machspl.h>
48: #include <i386/pio.h>
49: #include <i386at/gpl/if_nsreg.h>
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 of ASIC command register */
56: #define WD_RESET 0x80 /* board reset in WDTRA_CMDREG */
57: #define WD_MEMEN 0x40 /* enable shared memory */
58: #define WD_CMDREG5 5 /* offset of 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_OFF 16 /* NIC register offset */
62:
63: #define wdunit(dev) minor(dev)
64:
65: /*
66: * Autoconfiguration stuff.
67: */
68: int wdprobe();
69: void wdattach();
70: int wdstd[] = { 0x300, 0x280, 0x380, 0x240, 0 };
71: struct bus_device *wdinfo[NWD];
72: struct bus_driver wddriver = {
73: wdprobe, 0, wdattach, 0, wdstd, "wd", wdinfo, 0, 0, 0
74: };
75:
76: /*
77: * NS8390 state.
78: */
79: struct nssoftc wdnssoftc[NWD];
80:
81: /*
82: * Board state.
83: */
84: struct wdsoftc {
85: int sc_mstart; /* start of board's RAM */
86: int sc_mend; /* end of board's RAM */
87: int sc_rmstart; /* start of receive RAM */
88: int sc_rmend; /* end of receive RAM */
89: int sc_reg0; /* copy of register 0 of ASIC */
90: int sc_reg5; /* copy of register 5 of ASIC */
91: } wdsoftc[NWD];
92:
93: void wdstart(int);
94: void wd_reset(struct nssoftc *sc);
95: void wd_input(struct nssoftc *sc, int, char *, int);
96: int wd_output(struct nssoftc *sc, int, char *, int);
97:
98: /*
99: * Watchdog.
100: */
101: int wdwstart = 0;
102: void wdwatch(void);
103:
104: #define WDDEBUG
105: #ifdef WDDEBUG
106: int wddebug = 0;
107: #define DEBUGF(stmt) { if (wddebug) stmt; }
108: #else
109: #define DEBUGF(stmt)
110: #endif
111:
112: /*
113: * Probe for the WD8003 and WD8013.
114: * These cards have the station address PROM at I/O ports <base>+8
115: * to <base>+13, with a checksum following. A Soundblaster can have
116: * the same checksum as a WD ethercard, so we have an extra exclusionary
117: * check for it.
118: */
119: int
120: wdprobe(xxx, ui)
121: int xxx;
122: struct bus_device *ui;
123: {
124: int *port;
125:
126: if (ui->unit >= NWD) {
127: printf("wd%d: not configured\n", ui->unit);
128: return (0);
129: }
130: for (port = wdstd; *port; port++) {
131: if (*port < 0)
132: continue;
133: if (inb(*port + 8) != 0xff
134: && inb(*port + 9) != 0xff
135: && wdprobe1(*port, ui)) {
136: ui->address = *port;
137: *port = -1;
138: return (1);
139: }
140: }
141: return (0);
142: }
143:
144: int
145: wdprobe1(port, ui)
146: int port;
147: struct bus_device *ui;
148: {
149:
150: int i, irq = 0, checksum = 0, ancient = 0, word16 = 0;
151: struct wdsoftc *wd = &wdsoftc[ui->unit];
152: struct nssoftc *ns = &wdnssoftc[ui->unit];
153: struct ifnet *ifp = &ns->sc_if;
154:
155: for (i = 0; i < 8; i++)
156: checksum += inb(port + 8 + i);
157: if ((checksum & 0xff) != 0xff)
158: return (0);
159:
160: printf("wd%d: WD80x3 at 0x%03x, ", ui->unit, port);
161: for (i = 0; i < ETHER_ADDR_LEN; i++) {
162: if (i == 0)
163: printf("%02x", ns->sc_addr[i] = inb(port + 8 + i));
164: else
165: printf(":%02x", ns->sc_addr[i] = inb(port + 8 + i));
166: }
167: /*
168: * Check for PureData.
169: */
170: if (inb(port) == 'P' && inb(port + 1) == 'D') {
171: u_char reg5 = inb(port + 5);
172:
173: switch (inb(port + 2)) {
174:
175: case 0x03:
176: case 0x05:
177: word16 = 0;
178: break;
179:
180: case 0x0a:
181: word16 = 1;
182: break;
183:
184: default:
185: word16 = 0;
186: break;
187: }
188: wd->sc_mstart = ((reg5 & 0x1c) + 0xc0) << 12;
189: irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
190: } else {
191: /*
192: * Check for 8 bit vs 16 bit card.
193: */
194: for (i = 0; i < ETHER_ADDR_LEN; i++)
195: if (inb(port + i) != inb(port + 8 + i))
196: break;
197: if (i >= ETHER_ADDR_LEN) {
198: ancient = 1;
199: word16 = 0;
200: } else {
201: int tmp = inb(port + 1);
202:
203: /*
204: * Attempt to clear 16bit bit.
205: */
206: outb(port + 1, tmp ^ 0x01);
207: if (((inb(port + 1) & 0x01) == 0x01) /* 16 bit */
208: && (tmp & 0x01) == 0x01) { /* in 16 bit slot */
209: int asic_reg5 = inb(port + WD_CMDREG5);
210:
211: /*
212: * Magic to set ASIC to word-wide mode.
213: */
214: outb(port+WD_CMDREG5, NIC16|(asic_reg5&0x1f));
215: outb(port + 1, tmp);
216: word16 = 1;
217: } else
218: word16 = 0;
219: outb(port + 1, tmp);
220: }
221: if (!ancient && (inb(port + 1) & 0x01) != (word16 & 0x01))
222: printf("\nwd%d: bus width conflict, "
223: "%d (probe) != %d (reg report)", ui->unit,
224: word16 ? 16 : 8, (inb(port+1) & 0x01) ? 16 : 8);
225: }
226: /*
227: * Determine board's RAM location.
228: */
229: if (wd->sc_mstart == 0) {
230: int reg0 = inb(port);
231:
232: if (reg0 == 0xff || reg0 == 0)
233: wd->sc_mstart = 0xd0000;
234: else {
235: int high_addr_bits = inb(port + WD_CMDREG5) & 0x1f;
236:
237: if (high_addr_bits == 0x1f || word16 == 0)
238: high_addr_bits = 0x01;
239: wd->sc_mstart = ((reg0&0x3f)<<13)+(high_addr_bits<<19);
240: }
241: }
242: /*
243: * Determine irq.
244: */
245: if (irq == 0) {
246: int irqmap[] = { 9, 3, 5, 7, 10, 11, 15, 4 };
247: int reg1 = inb(port + 1);
248: int reg4 = inb(port + 4);
249:
250: /*
251: * For old card, irq must be supplied.
252: */
253: if (ancient || reg1 == 0xff) {
254: if (ui->sysdep1 == 0) {
255: printf("\nwd%d: must specify IRQ for card\n",
256: ui->unit);
257: return (0);
258: }
259: irq = ui->sysdep1;
260: } else {
261: DEBUGF({
262: int i = ((reg4 >> 5) & 0x03) + (reg1 & 0x04);
263:
264: printf("\nwd%d: irq index %d\n", ui->unit, i);
265: printf("wd%d:", ui->unit);
266: })
267: irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
268: }
269: } else if (irq == 2)
270: irq = 9;
271: ui->sysdep1 = irq;
272: take_dev_irq(ui);
273: printf(", irq %d", irq);
274:
275: /*
276: * Initialize 8390 state.
277: */
278: ns->sc_name = ui->name;
279: ns->sc_unit = ui->unit;
280: ns->sc_port = port + WD_NIC_OFF;
281: ns->sc_reset = wd_reset;
282: ns->sc_input = wd_input;
283: ns->sc_output = wd_output;
284: ns->sc_pingpong = 1;
285: ns->sc_word16 = word16;
286: ns->sc_txstrtpg = WD_START_PG;
287: ns->sc_rxstrtpg = WD_START_PG + TX_PAGES(ns);
288: ns->sc_stoppg = word16 ? WD13_STOP_PG : WD03_STOP_PG;
289:
290: wd->sc_rmstart = wd->sc_mstart + TX_PAGES(ns) * 256;
291: wd->sc_mend = wd->sc_rmend
292: = wd->sc_mstart + (ns->sc_stoppg - WD_START_PG) * 256;
293: printf(", memory 0x%05x-0x%05x", wd->sc_mstart, wd->sc_mend);
294:
295: if (word16)
296: printf(", 16 bit");
297: printf("\n");
298:
299: DEBUGF(printf("wd%d: txstrtpg %d rxstrtpg %d num_pages %d\n",
300: ui->unit, ns->sc_txstrtpg, ns->sc_rxstrtpg,
301: (wd->sc_mend - wd->sc_mstart) / 256));
302:
303: /*
304: * Initialize interface header.
305: */
306: ifp->if_unit = ui->unit;
307: ifp->if_mtu = ETHERMTU;
308: ifp->if_flags = IFF_BROADCAST;
309: ifp->if_header_size = sizeof(struct ether_header);
310: ifp->if_header_format = HDR_ETHERNET;
311: ifp->if_address_size = ETHER_ADDR_LEN;
312: ifp->if_address = ns->sc_addr;
313: if_init_queues(ifp);
314:
315: return (1);
316: }
317:
318: void
319: wdattach(ui)
320: struct bus_device *ui;
321: {
322: /*
323: * void
324: */
325: }
326:
327: int
328: wdopen(dev, flag)
329: dev_t dev;
330: int flag;
331: {
332: int unit = wdunit(dev), s;
333: struct bus_device *ui;
334: struct wdsoftc *wd;
335: struct nssoftc *ns;
336:
337: if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
338: return (ENXIO);
339:
340: /*
341: * Start watchdog.
342: */
343: if (!wdwstart) {
344: wdwstart++;
345: timeout(wdwatch, 0, hz);
346: }
347: wd = &wdsoftc[unit];
348: ns = &wdnssoftc[unit];
349: ns->sc_if.if_flags |= IFF_UP;
350: s = splimp();
351: wd->sc_reg0 = ((wd->sc_mstart >> 13) & 0x3f) | WD_MEMEN;
352: wd->sc_reg5 = ((wd->sc_mstart >> 19) & 0x1f) | NIC16;
353: if (ns->sc_word16)
354: outb(ui->address + WD_CMDREG5, wd->sc_reg5);
355: outb(ui->address, wd->sc_reg0);
356: nsinit(ns);
357: splx(s);
358: return (0);
359: }
360:
361: int
362: wdoutput(dev, ior)
363: dev_t dev;
364: io_req_t ior;
365: {
366: int unit = wdunit(dev);
367: struct bus_device *ui;
368:
369: if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
370: return (ENXIO);
371:
372: return (net_write(&wdnssoftc[unit].sc_if, wdstart, ior));
373: }
374:
375: int
376: wdsetinput(dev, receive_port, priority, filter, filter_count)
377: dev_t dev;
378: mach_port_t receive_port;
379: int priority;
380: filter_t *filter;
381: unsigned filter_count;
382: {
383: int unit = wdunit(dev);
384: struct bus_device *ui;
385:
386: if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
387: return (ENXIO);
388:
389: return (net_set_filter(&wdnssoftc[unit].sc_if, receive_port,
390: priority, filter, filter_count));
391: }
392:
393: int
394: wdgetstat(dev, flavor, status, count)
395: dev_t dev;
396: int flavor;
397: dev_status_t status;
398: unsigned *count;
399: {
400: int unit = wdunit(dev);
401: struct bus_device *ui;
402:
403: if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
404: return (ENXIO);
405:
406: return (net_getstat(&wdnssoftc[unit].sc_if, flavor, status, count));
407: }
408:
409: int
410: wdsetstat(dev, flavor, status, count)
411: dev_t dev;
412: int flavor;
413: dev_status_t status;
414: unsigned count;
415: {
416: int unit = wdunit(dev), oflags, s;
417: struct bus_device *ui;
418: struct ifnet *ifp;
419: struct net_status *ns;
420:
421: if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
422: return (ENXIO);
423:
424: ifp = &wdnssoftc[unit].sc_if;
425:
426: switch (flavor) {
427:
428: case NET_STATUS:
429: if (count < NET_STATUS_COUNT)
430: return (D_INVALID_SIZE);
431: ns = (struct net_status *)status;
432: oflags = ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC);
433: ifp->if_flags &= ~(IFF_ALLMULTI|IFF_PROMISC);
434: ifp->if_flags |= ns->flags & (IFF_ALLMULTI|IFF_PROMISC);
435: if ((ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC)) != oflags) {
436: s = splimp();
437: nsinit(&wdnssoftc[unit]);
438: splx(s);
439: }
440: break;
441:
442: default:
443: return (D_INVALID_OPERATION);
444: }
445: return (D_SUCCESS);
446: }
447:
448: void
449: wdintr(unit)
450: int unit;
451: {
452: nsintr(&wdnssoftc[unit]);
453: }
454:
455: void
456: wdstart(unit)
457: int unit;
458: {
459: nsstart(&wdnssoftc[unit]);
460: }
461:
462: void
463: wd_reset(ns)
464: struct nssoftc *ns;
465: {
466: int port = ns->sc_port - WD_NIC_OFF; /* ASIC base address */
467: struct wdsoftc *wd = &wdsoftc[ns->sc_unit];
468:
469: outb(port, WD_RESET);
470: outb(0x80, 0); /* I/O delay */
471: /*
472: * Set up the ASIC registers, just in case something changed them.
473: */
474: outb(port, ((wd->sc_mstart >> 13) & 0x3f) | WD_MEMEN);
475: if (ns->sc_word16)
476: outb(port + WD_CMDREG5, NIC16 | ((wd->sc_mstart>>19) & 0x1f));
477: }
478:
479: void
480: wd_input(ns, count, buf, ring_offset)
481: struct nssoftc *ns;
482: int count;
483: char *buf;
484: int ring_offset;
485: {
486: int port = ns->sc_port - WD_NIC_OFF;
487: int xfer_start;
488: struct wdsoftc *wd = &wdsoftc[ns->sc_unit];
489:
490: DEBUGF(printf("wd%d: ring_offset = %d\n", ns->sc_unit, ring_offset));
491:
492: xfer_start = wd->sc_mstart + ring_offset - (WD_START_PG << 8);
493:
494: /*
495: * The NIC driver calls us 3 times. Once to read the NIC 4 byte
496: * header, next to read the Ethernet header and finally to read
497: * the actual data. We enable 16 bit mode before the NIC header
498: * and disable it after the packet body.
499: */
500: if (count == 4) {
501: if (ns->sc_word16)
502: outb(port + WD_CMDREG5, ISA16 | wd->sc_reg5);
503: ((int *)buf)[0] = ((int *)phystokv(xfer_start))[0];
504: return;
505: }
506: if (count == sizeof(struct ether_header)) {
507: xfer_start = (int)phystokv(xfer_start);
508: ((int *)buf)[0] = ((int *)xfer_start)[0];
509: ((int *)buf)[1] = ((int *)xfer_start)[1];
510: ((int *)buf)[2] = ((int *)xfer_start)[2];
511: ((short *)(buf + 12))[0] = ((short *)(xfer_start + 12))[0];
512: return;
513: }
514: if (xfer_start + count > wd->sc_rmend) {
515: int semi_count = wd->sc_rmend - xfer_start;
516:
517: /*
518: * Input move must be wrapped.
519: */
520: bcopy((char *)phystokv(xfer_start), buf, semi_count);
521: count -= semi_count;
522: bcopy((char *)phystokv(wd->sc_rmstart),buf+semi_count,count);
523: } else
524: bcopy((char *)phystokv(xfer_start), buf, count);
525: if (ns->sc_word16)
526: outb(port + WD_CMDREG5, wd->sc_reg5);
527: }
528:
529: int
530: wd_output(ns, count, buf, start_page)
531: struct nssoftc *ns;
532: int count;
533: char *buf;
534: int start_page;
535: {
536: char *shmem;
537: int i, port = ns->sc_port - WD_NIC_OFF;
538: struct wdsoftc *wd = &wdsoftc[ns->sc_unit];
539:
540: DEBUGF(printf("wd%d: start_page = %d\n", ns->sc_unit, start_page));
541:
542: shmem = (char *)phystokv(wd->sc_mstart+((start_page-WD_START_PG)<<8));
543: if (ns->sc_word16) {
544: outb(port + WD_CMDREG5, ISA16 | wd->sc_reg5);
545: bcopy(buf, shmem, count);
546: outb(port + WD_CMDREG5, wd->sc_reg5);
547: } else
548: bcopy(buf, shmem, count);
549: while (count < ETHERMIN + sizeof(struct ether_header)) {
550: *(shmem + count) = 0;
551: count++;
552: }
553: return (count);
554: }
555:
556: /*
557: * Watchdog.
558: * Check for hung transmissions.
559: */
560: void
561: wdwatch()
562: {
563: int unit, s;
564: struct nssoftc *ns;
565:
566: timeout(wdwatch, 0, hz);
567:
568: s = splimp();
569: for (unit = 0; unit < NWD; unit++) {
570: if (wdinfo[unit] == 0 || wdinfo[unit]->alive == 0)
571: continue;
572: ns = &wdnssoftc[unit];
573: if (ns->sc_timer && --ns->sc_timer == 0) {
574: printf("wd%d: transmission timeout\n", unit);
575: nsinit(ns);
576: }
577: }
578: splx(s);
579: }
580:
581: #endif /* NWD > 0 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.